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
}
}
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;
+ }
}
}
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>
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);
+ }
+ }
}
}
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>
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))
bool IsCurveValid(Oid oid);
bool ExplicitCurvesSupported { get; }
bool CanDeriveNewPublicKey { get; }
+ bool SupportsRawDerivation { get; }
}
public static partial class ECDiffieHellmanFactory
public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported;
public static bool CanDeriveNewPublicKey => s_provider.CanDeriveNewPublicKey;
+
+ public static bool SupportsRawDerivation => s_provider.SupportsRawDerivation;
}
}
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
--- /dev/null
+// 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()
+ {
+ 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));
+ }
+ }
+ }
+}
}
public bool CanDeriveNewPublicKey => true;
+ public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater;
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
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"
public bool ExplicitCurvesSupported => _ecdsaProvider.ExplicitCurvesSupported;
public bool CanDeriveNewPublicKey => true;
+ public bool SupportsRawDerivation => true;
}
public partial class ECDiffieHellmanFactory
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"
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; }
}
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);
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) =>
public bool CanDeriveNewPublicKey => false;
+ public bool SupportsRawDerivation => true;
+
private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
if (string.IsNullOrEmpty(friendlyNameOrValue))
}
public bool CanDeriveNewPublicKey { get; } = !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst;
+ public bool SupportsRawDerivation => true;
private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
}
public bool CanDeriveNewPublicKey => true;
+ public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater;
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
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"