Skip to content

[Perf] Cache key size in CngKey #89599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;

namespace System.Security.Cryptography.Tests
{
public static class CngKeyTests
{
[Theory]
[MemberData(nameof(AllPublicProperties))]
public static void AllProperties_ThrowOnDisposal(string propertyName)
{
// Create a dummy key. Use a fast-ish algorithm.
// If we can't query the property even on a fresh key, exclude it from the tests.

PropertyInfo propInfo = typeof(CngKey).GetProperty(propertyName);
CngKey theKey = CngKey.Create(CngAlgorithm.ECDsaP256);

try
{
propInfo.GetValue(theKey);
}
catch
{
// Property getter threw an exception. It's nonsensical for us to query
// whether this same getter throws ObjectDisposedException once the object
// is disposed. So we'll just mark this test as success.

return;
}

// We've queried the property. Now dispose the object and query the property again.
// We should see an ObjectDisposedException.

theKey.Dispose();
Assert.ThrowsAny<ObjectDisposedException>(() =>
propInfo.GetValue(theKey, BindingFlags.DoNotWrapExceptions, null, null, null));
}

public static IEnumerable<object[]> AllPublicProperties()
{
foreach (PropertyInfo pi in typeof(CngKey).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (pi.GetMethod is not null && !typeof(SafeHandle).IsAssignableFrom(pi.PropertyType))
{
yield return new[] { pi.Name };
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\TestData.cs" />
<Compile Include="$(CommonTestPath)System\IO\PositionValueStream.cs"
Link="CommonTest\System\IO\PositionValueStream.cs" />
<Compile Include="CngKeyTests.cs" />
<Compile Include="CngPkcs8Tests.cs" />
<Compile Include="DSACngPkcs8Tests.cs" />
<Compile Include="DSACngProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public sealed partial class CngKey : IDisposable
// Key properties
//

private const int CachedKeySizeUninitializedSentinel = -1;
private int _cachedKeySize = CachedKeySizeUninitializedSentinel;

/// <summary>
/// Algorithm group this key can be used with
Expand Down Expand Up @@ -169,6 +171,19 @@ public string? KeyName
public int KeySize
{
get
{
// Key size lookup is a common operation, and we don't want to incur the
// LRPC overhead to query lsass for the information. Since the key size
// cannot be changed on an existing key, we'll cache it. For consistency
// with other properties, we'll still throw if the underlying handle has
// been closed.
if (_cachedKeySize == CachedKeySizeUninitializedSentinel || _keyHandle.IsClosed)
{
_cachedKeySize = ComputeKeySize();
}
return _cachedKeySize;

int ComputeKeySize()
{
int keySize = 0;

Expand Down Expand Up @@ -218,6 +233,7 @@ public int KeySize
return keySize;
}
}
}

/// <summary>
/// Usage restrictions on the key
Expand Down