Skip to content
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

Fix CngKey.KeySize for keys in the Platform Crypto Provider #77809

Merged
merged 11 commits into from
Jan 4, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,94 @@ namespace System.Security.Cryptography.Cng.Tests
{
public static class PropertyTests
{
private static Lazy<bool> s_lazyPlatformCryptoProviderFunctional = new Lazy<bool>(static () =>
{
CngKey key = null;

try
{
key = CngKey.Create(
CngAlgorithm.ECDsaP256,
$"{nameof(PlatformCryptoProviderFunctional)}Key",
new CngKeyCreationParameters
{
Provider = CngProvider.MicrosoftPlatformCryptoProvider,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
});

return true;
}
catch (CryptographicException)
{
return false;
}
finally
{
key?.Delete();
}
});

public static bool PlatformCryptoProviderFunctional => s_lazyPlatformCryptoProviderFunctional.Value;

[ConditionalTheory(nameof(PlatformCryptoProviderFunctional))]
[InlineData("ECDH_P256", 256)]
[InlineData("ECDH_P384", 384)]
[InlineData("ECDSA_P256", 256)]
[InlineData("ECDSA_P384", 384)]
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
[OuterLoop("Hardware backed key generation takes several seconds.")]
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
public static void CreatePersisted_PlatformEccKeyHasKeySize(string algorithm, int expectedKeySize)
{
CngKey key = null;

try
{
key = CngKey.Create(
new CngAlgorithm(algorithm),
$"{nameof(CreatePersisted_PlatformEccKeyHasKeySize)}_{algorithm}",
new CngKeyCreationParameters
{
Provider = CngProvider.MicrosoftPlatformCryptoProvider,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
});

Assert.Equal(expectedKeySize, key.KeySize);
}
finally
{
key?.Delete(); // Delete does a Dispose for us.
}
}

[ConditionalTheory(nameof(PlatformCryptoProviderFunctional))]
[InlineData(1024)]
[InlineData(2048)]
[OuterLoop("Hardware backed key generation takes several seconds.")]
public static void CreatePersisted_PlatformRsaKeyHasKeySize(int keySize)
{
CngKey key = null;

try
{
CngKeyCreationParameters cngCreationParameters = new CngKeyCreationParameters
{
Provider = CngProvider.MicrosoftPlatformCryptoProvider,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
};
cngCreationParameters.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(keySize), CngPropertyOptions.None));

key = CngKey.Create(
CngAlgorithm.Rsa,
$"{nameof(CreatePersisted_PlatformRsaKeyHasKeySize)}_{keySize}",
cngCreationParameters);

Assert.Equal(keySize, key.KeySize);
}
finally
{
key?.Delete(); // Delete does a Dispose for us.
}
}

[Fact]
public static void GetProperty_NoSuchProperty()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,29 @@ public int KeySize
throw errorCode.ToCryptographicException();
}

// The platform crypto provider always returns "0" for EC keys when asked for a key size. This
// has been observed in Windows 10 and most recently observed in Windows 11 22H2.
// The Algorithm NCrypt Property only returns the Algorithm Group, so that doesn't work either.
// What does work is the ECCCurveName.
CngAlgorithmGroup? algorithmGroup = AlgorithmGroup;

if (keySize == 0 && Provider == CngProvider.MicrosoftPlatformCryptoProvider &&
(algorithmGroup == CngAlgorithmGroup.ECDiffieHellman || algorithmGroup == CngAlgorithmGroup.ECDsa))
{
string? curve = _keyHandle.GetPropertyAsString(KeyPropertyName.ECCCurveName, CngPropertyOptions.None);

switch (curve)
{
// nistP192 and nistP224 don't have named curve accelerators but we can handle them.
// These string values match the names in https://learn.microsoft.com/en-us/windows/win32/seccng/cng-named-elliptic-curves
case "nistP192": return 192;
case "nistP224": return 224;
case nameof(ECCurve.NamedCurves.nistP256): return 256;
case nameof(ECCurve.NamedCurves.nistP384): return 384;
case nameof(ECCurve.NamedCurves.nistP521): return 521;
}
}

return keySize;
}
}
Expand Down