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

Permit CngKey backed ECDH interoperability from different KSPs #80571

Closed
wants to merge 1 commit into from
Closed
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
Expand Up @@ -191,7 +191,7 @@ public static void HashAlgorithm_SupportsOtherECDHImplementations()

[ConditionalFact(typeof(PlatformSupport), nameof(PlatformSupport.PlatformCryptoProviderFunctional))]
[OuterLoop("Hardware backed key generation takes several seconds.")]
public static void PlatformCryptoProvider_DeriveKeyMaterial()
public static void PlatformCryptoProvider_DeriveKeyMaterial_CngKey()
{
CngKey key1 = null;
CngKey key2 = null;
Expand All @@ -206,12 +206,12 @@ public static void PlatformCryptoProvider_DeriveKeyMaterial()

key1 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyMaterial)}{nameof(key1)}",
$"{nameof(PlatformCryptoProvider_DeriveKeyMaterial_CngKey)}{nameof(key1)}",
cngCreationParameters);

key2 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyMaterial)}{nameof(key2)}",
$"{nameof(PlatformCryptoProvider_DeriveKeyMaterial_CngKey)}{nameof(key2)}",
cngCreationParameters);

using (ECDiffieHellmanCng ecdhCng1 = new ECDiffieHellmanCng(key1))
Expand All @@ -228,5 +228,85 @@ public static void PlatformCryptoProvider_DeriveKeyMaterial()
key2?.Delete();
}
}

[ConditionalFact(typeof(PlatformSupport), nameof(PlatformSupport.PlatformCryptoProviderFunctional))]
[OuterLoop("Hardware backed key generation takes several seconds.")]
public static void PlatformCryptoProvider_DeriveKeyFromHash()
{
CngKey key1 = null;
CngKey key2 = null;

try
{
CngKeyCreationParameters cngCreationParameters = new CngKeyCreationParameters
{
Provider = CngProvider.MicrosoftPlatformCryptoProvider,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
};

key1 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyFromHash)}{nameof(key1)}",
cngCreationParameters);

key2 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyFromHash)}{nameof(key2)}",
cngCreationParameters);

using (ECDiffieHellmanCng ecdhCng1 = new ECDiffieHellmanCng(key1))
using (ECDiffieHellmanCng ecdhCng2 = new ECDiffieHellmanCng(key2))
{
byte[] derivedKey1 = ecdhCng1.DeriveKeyFromHash(ecdhCng2.PublicKey, HashAlgorithmName.SHA256, new byte[] { 0x00 }, new byte[] { 0xFF });
byte[] derivedKey2 = ecdhCng2.DeriveKeyFromHash(ecdhCng1.PublicKey, HashAlgorithmName.SHA256, new byte[] { 0x00 }, new byte[] { 0xFF });
Assert.Equal(derivedKey1, derivedKey2);
}
}
finally
{
key1?.Delete();
key2?.Delete();
}
}

[ConditionalFact(typeof(PlatformSupport), nameof(PlatformSupport.PlatformCryptoProviderFunctional))]
[OuterLoop("Hardware backed key generation takes several seconds.")]
public static void PlatformCryptoProvider_DeriveKeyMaterial_ECDiffieHellmanPublicKey()
{
CngKey key1 = null;
CngKey key2 = null;

try
{
CngKeyCreationParameters cngCreationParameters = new CngKeyCreationParameters
{
Provider = CngProvider.MicrosoftPlatformCryptoProvider,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
};

key1 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyFromHash)}{nameof(key1)}",
cngCreationParameters);

key2 = CngKey.Create(
CngAlgorithm.ECDiffieHellmanP256,
$"{nameof(PlatformCryptoProvider_DeriveKeyFromHash)}{nameof(key2)}",
cngCreationParameters);

using (ECDiffieHellmanCng ecdhCng1 = new ECDiffieHellmanCng(key1))
using (ECDiffieHellmanCng ecdhCng2 = new ECDiffieHellmanCng(key2))
{
byte[] derivedKey1 = ecdhCng1.DeriveKeyMaterial(ecdhCng2.PublicKey);
byte[] derivedKey2 = ecdhCng2.DeriveKeyMaterial(ecdhCng1.PublicKey);
Assert.Equal(derivedKey1, derivedKey2);
}
}
finally
{
key1?.Delete();
key2?.Delete();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPubl

if (otherPartyPublicKey is ECDiffieHellmanCngPublicKey otherKey)
{
using (CngKey import = otherKey.Import())
using (CngKey import = otherKey.NativeCngKey() ?? otherKey.Import())
{
return DeriveKeyMaterial(import);
}
Expand Down Expand Up @@ -101,7 +101,7 @@ public SafeNCryptSecretHandle DeriveSecretAgreementHandle(ECDiffieHellmanPublicK

if (otherPartyPublicKey is ECDiffieHellmanCngPublicKey otherKey)
{
using (CngKey importedKey = otherKey.Import())
using (CngKey importedKey = otherKey.NativeCngKey() ?? otherKey.Import())
{
return DeriveSecretAgreementHandle(importedKey);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Win32.SafeHandles;
using System.Runtime.Versioning;

namespace System.Security.Cryptography
Expand All @@ -13,6 +14,7 @@ public sealed partial class ECDiffieHellmanCngPublicKey : ECDiffieHellmanPublicK
private readonly CngKeyBlobFormat _format;
private readonly string? _curveName;
private bool _disposed;
private SafeNCryptKeyHandle? _cngKeyHandle;

/// <summary>
/// Wrap a CNG key
Expand All @@ -26,10 +28,28 @@ internal ECDiffieHellmanCngPublicKey(byte[] keyBlob, string? curveName, CngKeyBl
_curveName = curveName;
}

/// <summary>
/// Wrap a CNG key
/// </summary>
#pragma warning disable SYSLIB0043 // byte[] constructor on ECDiffieHellmanPublicKey is obsolete
internal ECDiffieHellmanCngPublicKey(
byte[] keyBlob,
string? curveName,
CngKeyBlobFormat format,
CngKey cngKey) : base(keyBlob)
#pragma warning restore SYSLIB0043
{
_format = format;
// Can be null for P256, P384, P521, or an explicit blob
_curveName = curveName;
_cngKeyHandle = cngKey.Handle; // The handle property duplicates the handle.
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_cngKeyHandle?.Dispose();
_disposed = true;
}

Expand Down Expand Up @@ -85,7 +105,7 @@ internal static ECDiffieHellmanCngPublicKey FromKey(CngKey key)
CngKeyBlobFormat format;
string? curveName;
byte[] blob = ECCng.ExportKeyBlob(key, false, out format, out curveName);
return new ECDiffieHellmanCngPublicKey(blob, curveName, format);
return new ECDiffieHellmanCngPublicKey(blob, curveName, format, key);
}

/// <summary>
Expand Down Expand Up @@ -153,5 +173,28 @@ public override ECParameters ExportParameters()
return ecparams;
}
}

internal CngKey? NativeCngKey()
{
if (_cngKeyHandle is null)
{
return null;
}

CngKeyHandleOpenOptions options = CngKeyHandleOpenOptions.None;
byte clrIsEphemeral = 0;
Interop.NCrypt.ErrorCode errorCode = Interop.NCrypt.NCryptGetByteProperty(
_cngKeyHandle,
KeyPropertyName.ClrIsEphemeral,
ref clrIsEphemeral,
CngPropertyOptions.CustomProperty);

if (errorCode == Interop.NCrypt.ErrorCode.ERROR_SUCCESS && clrIsEphemeral == 1)
{
options |= CngKeyHandleOpenOptions.EphemeralKey;
}

return CngKey.Open(_cngKeyHandle, options);
}
}
}