From be823a152f76e9a71a752e41777069d2a47fd34e Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 3 Mar 2023 09:54:13 -0500 Subject: [PATCH] Support raw ECDH key agreements --- .../Common/src/Interop/Windows/BCrypt/Cng.cs | 1 + .../NCrypt/Interop.NCryptDeriveKeyMaterial.cs | 20 ++++ .../ECDiffieHellmanAndroid.Derive.cs | 10 ++ .../Cryptography/ECDiffieHellmanCng.cs | 13 +++ .../ECDiffieHellmanOpenSsl.Derive.cs | 11 +++ .../ECDiffieHellmanSecurityTransforms.cs | 10 ++ .../ECDiffieHellman/ECDiffieHellmanFactory.cs | 3 + .../ECDiffieHellmanTests.NistValidation.cs | 14 ++- .../ECDiffieHellmanTests.Raw.cs | 94 +++++++++++++++++++ .../tests/ECDiffieHellmanCngProvider.cs | 1 + ...tem.Security.Cryptography.Cng.Tests.csproj | 2 + .../tests/EcDiffieHellmanOpenSslProvider.cs | 1 + ...Security.Cryptography.OpenSsl.Tests.csproj | 2 + .../ref/System.Security.Cryptography.cs | 1 + .../Security/Cryptography/ECDiffieHellman.cs | 29 ++++++ .../Cryptography/ECDiffieHellmanWrapper.cs | 3 + .../DefaultECDiffieHellmanProvider.Android.cs | 2 + .../DefaultECDiffieHellmanProvider.Unix.cs | 1 + .../DefaultECDiffieHellmanProvider.Windows.cs | 1 + .../System.Security.Cryptography.Tests.csproj | 2 + 20 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.Raw.cs diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs index 4c0fe5e82ad850..31e827630fbcf1 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs @@ -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 } } diff --git a/src/libraries/Common/src/Interop/Windows/NCrypt/Interop.NCryptDeriveKeyMaterial.cs b/src/libraries/Common/src/Interop/Windows/NCrypt/Interop.NCryptDeriveKeyMaterial.cs index cb290836f3eb42..0bb0ba115eacd2 100644 --- a/src/libraries/Common/src/Interop/Windows/NCrypt/Interop.NCryptDeriveKeyMaterial.cs +++ b/src/libraries/Common/src/Interop/Windows/NCrypt/Interop.NCryptDeriveKeyMaterial.cs @@ -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.Empty, + flags); + + // Win32 returns the result as little endian. So we need to flip it to big endian. + Array.Reverse(result); + return result; + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanAndroid.Derive.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanAndroid.Derive.cs index 7002da719f4874..c3f9fbed2ae4b2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanAndroid.Derive.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanAndroid.Derive.cs @@ -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; + } + /// /// Get the secret agreement generated between two parties /// diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.cs index a25cfcc3a4f6c7..df4d0c62420621 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.cs @@ -130,5 +130,18 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey Interop.NCrypt.SecretAgreementFlags.None); } } + + /// + public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey) + { + ArgumentNullException.ThrowIfNull(otherPartyPublicKey); + + using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey)) + { + return Interop.NCrypt.DeriveKeyMaterialTruncate( + secretAgreement, + Interop.NCrypt.SecretAgreementFlags.None); + } + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs index 6c4a212ec0eac6..f6fc78762e043c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs @@ -69,6 +69,17 @@ 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; + } + /// /// Get the secret agreement generated between two parties /// diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs index c90235163dc8f2..cad96d1017f50b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs @@ -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)) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs index aec1b09fc8e827..6497640809f3ab 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs @@ -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 @@ -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; } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.NistValidation.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.NistValidation.cs index 7e5d408c4c9aea..d79d8de3edba17 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.NistValidation.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.NistValidation.cs @@ -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(() => iut.DeriveRawSecretAgreement(cavsPublic)); + } } } #endif diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.Raw.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.Raw.cs new file mode 100644 index 00000000000000..d8d434248574ee --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.Raw.cs @@ -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() + { + using (ECDiffieHellman ecdh = ECDiffieHellmanFactory.Create()) + { + AssertExtensions.Throws( + "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( + "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(() => alice.DeriveRawSecretAgreement(bobPublic)); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs b/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs index 0470ab527ebfc6..d805dbc6d2b813 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs @@ -35,6 +35,7 @@ public bool ExplicitCurvesSupported } public bool CanDeriveNewPublicKey => true; + public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater; private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 8bd0699feeb6d6..e40ea90436f4ca 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -176,6 +176,8 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" /> + _ecdsaProvider.ExplicitCurvesSupported; public bool CanDeriveNewPublicKey => true; + public bool SupportsRawDerivation => true; } public partial class ECDiffieHellmanFactory diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index 3ed8b1f1e208e4..dedc6bdb4bd7fd 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -37,6 +37,8 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" /> + + /// Derive raw key material. + /// + /// The public key of the party with which to derive a mutual secret. + /// The raw key agreement. + /// + /// 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. + /// + /// + /// is . + /// + /// + /// is over a different curve than this key. + /// + /// + /// A derived implementation has not provided an implementation of the method. + /// + /// + /// The current platform does not support raw key agreement. + /// + /// + /// The object has already been disposed. + /// + public virtual byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey) + { + throw DerivedClassMustOverride(); + } + private static NotImplementedException DerivedClassMustOverride() { return new NotImplementedException(SR.NotSupported_SubclassOverride); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanWrapper.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanWrapper.cs index 58f1c1df6ffee6..b355ccab0a305a 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanWrapper.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanWrapper.cs @@ -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) => diff --git a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Android.cs b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Android.cs index 63ec79d8baeecd..93c0237a8f6cb6 100644 --- a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Android.cs +++ b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Android.cs @@ -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)) diff --git a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Unix.cs b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Unix.cs index 58b7bcfa00f5fd..d00c95b27eaffa 100644 --- a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Unix.cs +++ b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Unix.cs @@ -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) { diff --git a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Windows.cs b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Windows.cs index c458f74cd8931b..49f8ff15761e17 100644 --- a/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography/tests/DefaultECDiffieHellmanProvider.Windows.cs @@ -23,6 +23,7 @@ public bool ExplicitCurvesSupported } public bool CanDeriveNewPublicKey => true; + public bool SupportsRawDerivation => PlatformDetection.IsWindows10OrLater; private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index 86e3d512391a98..eda1865b105c6e 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -127,6 +127,8 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanTests.ImportExport.cs" /> +