Skip to content
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
1 change: 1 addition & 0 deletions src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal static class KeyDerivationFunction
public const string Hash = "HASH"; // BCRYPT_KDF_HASH
public const string Hmac = "HMAC"; // BCRYPT_KDF_HMAC
public const string Tls = "TLS_PRF"; // BCRYPT_KDF_TLS_PRF
public const string Raw = "TRUNCATE"; // BCRYPT_KDF_RAW_SECRET
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,5 +241,25 @@ internal static unsafe byte[] DeriveKeyMaterialTls(
flags);
}
}

internal static unsafe byte[] DeriveKeyMaterialTruncate(
SafeNCryptSecretHandle secretAgreement,
SecretAgreementFlags flags)
{
if (!OperatingSystem.IsWindowsVersionAtLeast(10))
{
throw new PlatformNotSupportedException();
}

byte[] result = DeriveKeyMaterial(
secretAgreement,
BCryptNative.KeyDerivationFunction.Raw,
ReadOnlySpan<NCryptBuffer>.Empty,
flags);

// Win32 returns the result as little endian. So we need to flip it to big endian.
Array.Reverse(result);
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey
DeriveSecretAgreement);
}

public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey)
{
ArgumentNullException.ThrowIfNull(otherPartyPublicKey);
ThrowIfDisposed();

byte[]? secretAgreement = DeriveSecretAgreement(otherPartyPublicKey, hasher: null);
Debug.Assert(secretAgreement is not null);
return secretAgreement;
}

/// <summary>
/// Get the secret agreement generated between two parties
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,18 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey
Interop.NCrypt.SecretAgreementFlags.None);
}
}

/// <inheritdoc />
public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey)
{
ArgumentNullException.ThrowIfNull(otherPartyPublicKey);

using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey))
{
return Interop.NCrypt.DeriveKeyMaterialTruncate(
secretAgreement,
Interop.NCrypt.SecretAgreementFlags.None);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey
DeriveSecretAgreement);
}

/// <inheritdoc />
public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey)
{
ArgumentNullException.ThrowIfNull(otherPartyPublicKey);
ThrowIfDisposed();

byte[]? secretAgreement = DeriveSecretAgreement(otherPartyPublicKey, hasher: null);
Debug.Assert(secretAgreement is not null);
return secretAgreement;
}

/// <summary>
/// Get the secret agreement generated between two parties
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey
DeriveSecretAgreement);
}

public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey)
{
ArgumentNullException.ThrowIfNull(otherPartyPublicKey);
ThrowIfDisposed();

byte[]? secretAgreement = DeriveSecretAgreement(otherPartyPublicKey, hasher: null);
Debug.Assert(secretAgreement is not null);
return secretAgreement;
}

private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash? hasher)
{
if (!(otherPartyPublicKey is ECDiffieHellmanSecurityTransformsPublicKey secTransPubKey))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface IECDiffieHellmanProvider
bool IsCurveValid(Oid oid);
bool ExplicitCurvesSupported { get; }
bool CanDeriveNewPublicKey { get; }
bool SupportsRawDerivation { get; }
}

public static partial class ECDiffieHellmanFactory
Expand Down Expand Up @@ -42,5 +43,7 @@ public static bool IsCurveValid(Oid oid)
public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported;

public static bool CanDeriveNewPublicKey => s_provider.CanDeriveNewPublicKey;

public static bool SupportsRawDerivation => s_provider.SupportsRawDerivation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,19 @@ private static void Verify(
HashAlgorithmName zHashAlgorithm,
byte[] iutZ)
{
byte[] result = iut.DeriveKeyFromHash(cavsPublic, zHashAlgorithm);
byte[] deriveHash = iut.DeriveKeyFromHash(cavsPublic, zHashAlgorithm);
byte[] hashedZ = zHasher.ComputeHash(iutZ);
Assert.Equal(hashedZ.ByteArrayToHex(), result.ByteArrayToHex());
Assert.Equal(hashedZ.ByteArrayToHex(), deriveHash.ByteArrayToHex());

if (ECDiffieHellmanFactory.SupportsRawDerivation)
{
byte[] rawDerived = iut.DeriveRawSecretAgreement(cavsPublic);
Assert.Equal(iutZ.ByteArrayToHex(), rawDerived.ByteArrayToHex());
}
else
{
Assert.Throws<PlatformNotSupportedException>(() => iut.DeriveRawSecretAgreement(cavsPublic));
}
}
}
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Security.Cryptography;
using Xunit;

namespace System.Security.Cryptography.EcDiffieHellman.Tests
{
public partial class ECDiffieHellmanTests
{
public static bool DoesNotSupportRawDerivation => !ECDiffieHellmanFactory.SupportsRawDerivation;

[ConditionalFact(typeof(ECDiffieHellmanFactory), nameof(ECDiffieHellmanFactory.SupportsRawDerivation))]
public static void RawDerivation_OtherKeyRequired()
Comment thread
vcsjones marked this conversation as resolved.
{
using (ECDiffieHellman ecdh = ECDiffieHellmanFactory.Create())
{
AssertExtensions.Throws<ArgumentNullException>(
"otherPartyPublicKey",
() => ecdh.DeriveRawSecretAgreement(null));
}
}

[ConditionalTheory(typeof(ECDiffieHellmanFactory), nameof(ECDiffieHellmanFactory.SupportsRawDerivation))]
[MemberData(nameof(MismatchedKeysizes))]
public static void RawDerivation_SameSizeOtherKeyRequired(int aliceSize, int bobSize)
{
using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(aliceSize))
using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(bobSize))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
AssertExtensions.Throws<ArgumentException>(
"otherPartyPublicKey",
() => alice.DeriveRawSecretAgreement(bobPublic));
}
}

[ConditionalTheory(typeof(ECDiffieHellmanFactory), nameof(ECDiffieHellmanFactory.SupportsRawDerivation))]
[MemberData(nameof(EveryKeysize))]
public static void RawDerivation_DeriveSharedSecret_Agree(int keySize)
{
using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(keySize))
using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(keySize))
using (ECDiffieHellmanPublicKey alicePublic = alice.PublicKey)
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
byte[] aliceDerived = alice.DeriveRawSecretAgreement(bobPublic);
byte[] bobDerived = bob.DeriveRawSecretAgreement(alicePublic);
Assert.Equal(aliceDerived, bobDerived);
}
}

[ConditionalFact(typeof(ECDiffieHellmanFactory), nameof(ECDiffieHellmanFactory.SupportsRawDerivation))]
public static void RawDerivation_DeriveSharedSecret_Disagree()
{
using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman eve = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
using (ECDiffieHellmanPublicKey evePublic = eve.PublicKey)
{
byte[] aliceDerived = alice.DeriveRawSecretAgreement(bobPublic);
byte[] eveDerived = alice.DeriveRawSecretAgreement(evePublic);

Assert.NotEqual(aliceDerived, eveDerived);
}
}

[ConditionalFact(typeof(ECDiffieHellmanFactory), nameof(ECDiffieHellmanFactory.SupportsRawDerivation))]
public static void RawDerivation_DeriveIsStable()
{
using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
byte[] aliceDerived1 = alice.DeriveRawSecretAgreement(bobPublic);
byte[] aliceDerived2 = alice.DeriveRawSecretAgreement(bobPublic);
Assert.Equal(aliceDerived1, aliceDerived2);
}
}

[ConditionalFact(nameof(DoesNotSupportRawDerivation))]
public static void RawDerivation_NotSupported()
{
using (ECDiffieHellman alice = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellman bob = ECDiffieHellmanFactory.Create(ECCurve.NamedCurves.nistP256))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
Assert.Throws<PlatformNotSupportedException>(() => alice.DeriveRawSecretAgreement(bobPublic));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public bool ExplicitCurvesSupported
}

public bool CanDeriveNewPublicKey => true;
public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater;

private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Xml.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public ECDiffieHellman Create(ECCurve curve)
public bool ExplicitCurvesSupported => _ecdsaProvider.ExplicitCurvesSupported;

public bool CanDeriveNewPublicKey => true;
public bool SupportsRawDerivation => true;
}

public partial class ECDiffieHellmanFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Xml.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ protected ECDiffieHellman() { }
public virtual byte[] DeriveKeyFromHmac(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, byte[]? hmacKey, byte[]? secretPrepend, byte[]? secretAppend) { throw null; }
public virtual byte[] DeriveKeyMaterial(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey) { throw null; }
public virtual byte[] DeriveKeyTls(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed) { throw null; }
public virtual byte[] DeriveRawSecretAgreement(System.Security.Cryptography.ECDiffieHellmanPublicKey otherPartyPublicKey) { throw null; }
public override void FromXmlString(string xmlString) { }
public override string ToXmlString(bool includePrivateParameters) { throw null; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,35 @@ public virtual byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey,
throw DerivedClassMustOverride();
}

/// <summary>
/// Derive raw key material.
/// </summary>
/// <param name="otherPartyPublicKey">The public key of the party with which to derive a mutual secret.</param>
/// <returns>The raw key agreement.</returns>
/// <remarks>
/// Care must be taking when using the raw derived secret agreement value. The raw value is expected to be used
/// as input in to a Key Derivation Function, and not used directly as key material.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// <paramref name="otherPartyPublicKey"/> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="otherPartyPublicKey"/> is over a different curve than this key.
/// </exception>
/// <exception cref="NotImplementedException">
/// A derived implementation has not provided an implementation of the method.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support raw key agreement.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The object has already been disposed.
/// </exception>
public virtual byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey)
{
throw DerivedClassMustOverride();
}

private static NotImplementedException DerivedClassMustOverride()
{
return new NotImplementedException(SR.NotSupported_SubclassOverride);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public override byte[] DeriveKeyFromHmac(
public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed) =>
_wrapped.DeriveKeyTls(Unwrap(otherPartyPublicKey), prfLabel, prfSeed);

public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey) =>
_wrapped.DeriveRawSecretAgreement(Unwrap(otherPartyPublicKey));

public override void FromXmlString(string xmlString) => _wrapped.FromXmlString(xmlString);

public override string ToXmlString(bool includePrivateParameters) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public bool IsCurveValid(Oid oid)

public bool CanDeriveNewPublicKey => false;

public bool SupportsRawDerivation => true;

private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
if (string.IsNullOrEmpty(friendlyNameOrValue))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public bool ExplicitCurvesSupported
}

public bool CanDeriveNewPublicKey { get; } = !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst;
public bool SupportsRawDerivation => true;

private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public bool ExplicitCurvesSupported
}

public bool CanDeriveNewPublicKey => true;
public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater;

private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.NistValidation.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Raw.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Tls.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.Xml.cs"
Expand Down