diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKem.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKem.cs index adc547e2ad39a4..984b0875b866dc 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKem.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKem.cs @@ -135,7 +135,15 @@ public void Encapsulate(out byte[] ciphertext, out byte[] sharedSecret) byte[] localCiphertext = new byte[Algorithm.CiphertextSizeInBytes]; byte[] localSharedSecret = new byte[Algorithm.SharedSecretSizeInBytes]; - EncapsulateCore(localCiphertext, localSharedSecret); + try + { + EncapsulateCore(localCiphertext, localSharedSecret); + } + catch + { + CryptographicOperations.ZeroMemory(localSharedSecret); + throw; + } sharedSecret = localSharedSecret; ciphertext = localCiphertext; @@ -188,7 +196,15 @@ public void Encapsulate(Span ciphertext, Span sharedSecret) ThrowIfDisposed(); - EncapsulateCore(ciphertext, sharedSecret); + try + { + EncapsulateCore(ciphertext, sharedSecret); + } + catch + { + CryptographicOperations.ZeroMemory(sharedSecret); + throw; + } } /// @@ -243,7 +259,17 @@ public byte[] Decapsulate(byte[] ciphertext) ThrowIfDisposed(); byte[] sharedSecret = new byte[Algorithm.SharedSecretSizeInBytes]; - DecapsulateCore(ciphertext, sharedSecret); + + try + { + DecapsulateCore(ciphertext, sharedSecret); + } + catch + { + CryptographicOperations.ZeroMemory(sharedSecret); + throw; + } + return sharedSecret; } @@ -286,7 +312,15 @@ public void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret) ThrowIfDisposed(); - DecapsulateCore(ciphertext, sharedSecret); + try + { + DecapsulateCore(ciphertext, sharedSecret); + } + catch + { + CryptographicOperations.ZeroMemory(sharedSecret); + throw; + } } /// diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.ECDiffieHellman.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.ECDiffieHellman.cs new file mode 100644 index 00000000000000..bd77fa90c49ae4 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.ECDiffieHellman.cs @@ -0,0 +1,244 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography.Asn1; +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + internal sealed partial class CompositeMLKemManaged + { + private sealed class ECDiffieHellmanKem : TraditionalKem +#if DESIGNTIMEINTERFACES +#pragma warning disable SA1001 // Commas should be spaced correctly + , ITraditionalKemFactory +#pragma warning restore SA1001 // Commas should be spaced correctly +#endif + { + private readonly ECDiffieHellmanKemAlgorithm _algorithm; + private ECDiffieHellman _ecdh; + + private ECDiffieHellmanKem(ECDiffieHellman ecdh, ECDiffieHellmanKemAlgorithm algorithm) + { + _ecdh = ecdh; + _algorithm = algorithm; + } + + // While some of our OSes support the brainpool curves, not all do. Supporting them + // only in the managed implementation could make migrating to a native implementation + // that omits them a breaking change. This is a minor concern, but be conservative and + // limit this implementation to the NIST curves until native implementations align. + public static bool IsAlgorithmSupported(ECDiffieHellmanKemAlgorithm algorithm) => + algorithm.CurveOidValue is Oids.secp256r1 or Oids.secp384r1 or Oids.secp521r1; + + public static ECDiffieHellmanKem GenerateKey(ECDiffieHellmanKemAlgorithm algorithm) => + new ECDiffieHellmanKem(ECDiffieHellman.Create(algorithm.Curve), algorithm); + + public static ECDiffieHellmanKem ImportPublicKey(ECDiffieHellmanKemAlgorithm algorithm, ReadOnlySpan source) + { + int fieldWidth = algorithm.FieldSizeInBytes; + + // draft-ietf-lamps-pq-composite-kem-19, 4 + // public key MUST be encoded as an uncompressed elliptic curve point as in + // section 2.2 of [RFC5480], including the leading byte 0x04 + if (source.Length != 1 + 2 * fieldWidth || source[0] != 0x04) + { + throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + } + + ECParameters parameters = new() + { + Curve = algorithm.Curve, + Q = new ECPoint + { + X = source.Slice(1, fieldWidth).ToArray(), + Y = source.Slice(1 + fieldWidth, fieldWidth).ToArray(), + }, + }; + + return new ECDiffieHellmanKem(ECDiffieHellman.Create(parameters), algorithm); + } + + public static ECDiffieHellmanKem ImportPrivateKey(ECDiffieHellmanKemAlgorithm algorithm, ReadOnlySpan source) + { + Helpers.ThrowIfAsnInvalidLength(source); + + ValueECPrivateKey.Decode(source, AsnEncodingRules.DER, out ValueECPrivateKey ecPrivateKey); + + if (ecPrivateKey.Version != 1 || + ecPrivateKey.HasPublicKey || + !ecPrivateKey.HasParameters || + ecPrivateKey.Parameters.Named != algorithm.CurveOidValue || + ecPrivateKey.PrivateKey.Length != algorithm.OrderSizeInBytes) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + byte[] d = new byte[ecPrivateKey.PrivateKey.Length]; + + using (PinAndClear.Track(d)) + { + ecPrivateKey.PrivateKey.CopyTo(d); + + ECParameters parameters = new() + { + Curve = algorithm.Curve, + D = d, + }; + + parameters.Validate(); + + return new ECDiffieHellmanKem(ECDiffieHellman.Create(parameters), algorithm); + } + } + + internal override void Encapsulate(Span ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.2 + // DHKEM.Encaps(pkR): + // (skE, pkE) = GenerateKeyPair() + // ss = DH(skE, pkR) + // ct = SerializePublicKey(pkE) + // + // return ss, ct + + using (ECDiffieHellman ephemeral = ECDiffieHellman.Create(_algorithm.Curve)) + using (ECDiffieHellmanPublicKey recipientPublicKey = _ecdh.PublicKey) + { + ExportPublicKey(ephemeral, _algorithm, ciphertext); + + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + DeriveSecret(ephemeral, recipientPublicKey, sharedSecret); + } + } + + internal override void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.2 + // DHKEM.Decaps(skR, ct): + // pkE = DeserializePublicKey(ct) + // ss = DH(skR, pkE) + // + // return ss + + using (ECDiffieHellmanKem ephemeral = ImportPublicKey(_algorithm, ciphertext)) + using (ECDiffieHellmanPublicKey ephemeralPublicKey = ephemeral._ecdh.PublicKey) + { + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + DeriveSecret(_ecdh, ephemeralPublicKey, sharedSecret); + } + } + + internal override int ExportPublicKey(Span destination) + { + ExportPublicKey(_ecdh, _algorithm, destination); + return 1 + 2 * _algorithm.FieldSizeInBytes; + } + + internal override int ExportPrivateKey(Span destination) + { + ECParameters parameters = _ecdh.ExportParameters(includePrivateParameters: true); + + Debug.Assert(parameters.D is not null); + + using (PinAndClear.Track(parameters.D)) + { + if (parameters.D.Length != _algorithm.OrderSizeInBytes || + !parameters.Curve.IsNamed || + parameters.Curve.Oid.Value != _algorithm.CurveOidValue) + { + Debug.Fail("ECDH exported unexpected private key parameters."); + throw new CryptographicException(); + } + + AsnWriter writer = new(AsnEncodingRules.DER); + + try + { + ValueECPrivateKey ecPrivateKey = new() + { + Version = 1, + PrivateKey = parameters.D, + Parameters = new ValueECDomainParameters + { + Named = _algorithm.CurveOidValue, + }, + }; + + ecPrivateKey.Encode(writer); + + if (!writer.TryEncode(destination, out int bytesWritten)) + { + Debug.Fail("The ECDH private key destination was unexpectedly too small."); + throw new CryptographicException(); + } + + return bytesWritten; + } + finally + { + writer.Reset(); + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _ecdh?.Dispose(); + _ecdh = null!; + } + + base.Dispose(disposing); + } + + private static void DeriveSecret(ECDiffieHellman ownKey, ECDiffieHellmanPublicKey otherParty, Span destination) + { + byte[] secret = ownKey.DeriveRawSecretAgreement(otherParty); + + try + { + if (secret.Length != destination.Length) + { + Debug.Fail("ECDH produced an unexpected shared secret length."); + throw new CryptographicException(); + } + + secret.CopyTo(destination); + } + finally + { + CryptographicOperations.ZeroMemory(secret); + } + } + + private static void ExportPublicKey(ECDiffieHellman key, ECDiffieHellmanKemAlgorithm algorithm, Span destination) + { + int fieldWidth = algorithm.FieldSizeInBytes; + Debug.Assert(destination.Length >= 1 + 2 * fieldWidth); + + ECParameters parameters = key.ExportParameters(includePrivateParameters: false); + byte[]? x = parameters.Q.X; + byte[]? y = parameters.Q.Y; + + if (x is null || + y is null || + x.Length != fieldWidth || + y.Length != fieldWidth || + !parameters.Curve.IsNamed || + parameters.Curve.Oid.Value != algorithm.CurveOidValue) + { + Debug.Fail("ECDH exported unexpected public key parameters."); + throw new CryptographicException(); + } + + destination[0] = 0x04; + x.CopyTo(destination.Slice(1, fieldWidth)); + y.CopyTo(destination.Slice(1 + fieldWidth, fieldWidth)); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.RSA.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.RSA.cs new file mode 100644 index 00000000000000..3d62b4048d0120 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.RSA.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Formats.Asn1; + +namespace System.Security.Cryptography +{ + internal sealed partial class CompositeMLKemManaged + { + private sealed class RsaKem : TraditionalKem +#if DESIGNTIMEINTERFACES +#pragma warning disable SA1001 // Commas should be spaced correctly + , ITraditionalKemFactory +#pragma warning restore SA1001 // Commas should be spaced correctly +#endif + { + private readonly RsaKemAlgorithm _algorithm; + private RSA _rsa; + + private RsaKem(RSA rsa, RsaKemAlgorithm algorithm) + { + _rsa = rsa; + _algorithm = algorithm; + } + + public static bool IsAlgorithmSupported(RsaKemAlgorithm _) => true; + + public static RsaKem GenerateKey(RsaKemAlgorithm algorithm) + { + RSA? rsa = null; + + try + { + rsa = RSA.Create(algorithm.KeySizeInBits); + + // RSA key generation is lazy, so force it to happen eagerly + // so that key generation failures happen in GenerateKey rather than + // the first method to use the key. + _ = rsa.ExportParameters(includePrivateParameters: false); + + return new RsaKem(rsa, algorithm); + } + catch + { + rsa?.Dispose(); + throw; + } + } + + public static RsaKem ImportPublicKey(RsaKemAlgorithm algorithm, ReadOnlySpan source) + { + RSA rsa = RSAKeyFormatHelper.FromPkcs1PublicKey(source, CreateRSA, AsnEncodingRules.DER); + + if (rsa.KeySize != algorithm.KeySizeInBits) + { + rsa.Dispose(); + throw new CryptographicException(SR.Argument_PublicKeyWrongSizeForAlgorithm); + } + + return new RsaKem(rsa, algorithm); + } + + public static RsaKem ImportPrivateKey(RsaKemAlgorithm algorithm, ReadOnlySpan source) + { + RSA rsa = RSAKeyFormatHelper.FromPkcs1PrivateKey(source, CreateRSA, ruleSet: AsnEncodingRules.DER); + + if (rsa.KeySize != algorithm.KeySizeInBits) + { + rsa.Dispose(); + throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm); + } + + return new RsaKem(rsa, algorithm); + } + + internal override void Encapsulate(Span ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.1 + // RSAOAEPKEM.Encaps(pkR): + // shared_secret = SecureRandom(ss_len) + // enc = RSAES-OAEP-ENCRYPT(pkR, shared_secret) + // + // return shared_secret, enc + + Debug.Assert(sharedSecret.Length == RsaKemAlgorithm.SecretSizeInBytes); + Debug.Assert(ciphertext.Length == _algorithm.KeySizeInBits / 8); + + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + RandomNumberGenerator.Fill(sharedSecret); + + if (!_rsa.TryEncrypt(sharedSecret, ciphertext, RSAEncryptionPadding.OaepSHA256, out int bytesWritten) || + bytesWritten != ciphertext.Length) + { + Debug.Fail("RSA encryption produced an unexpected ciphertext length."); + throw new CryptographicException(); + } + } + + internal override void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.1 + // RSAOAEPKEM.Decaps(skR, enc): + // shared_secret = RSAES-OAEP-DECRYPT(skR, enc) + // + // return shared_secret + + Debug.Assert(sharedSecret.Length == RsaKemAlgorithm.SecretSizeInBytes); + Debug.Assert(ciphertext.Length == _algorithm.KeySizeInBits / 8); + + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + if (!_rsa.TryDecrypt(ciphertext, sharedSecret, RSAEncryptionPadding.OaepSHA256, out int bytesWritten) || + bytesWritten != sharedSecret.Length) + { + throw new CryptographicException(SR.Cryptography_CompositeKemRsaDecapsulationFailed); + } + } + + internal override int ExportPublicKey(Span destination) + { + if (!_rsa.TryExportRSAPublicKey(destination, out int bytesWritten)) + { + Debug.Fail("The RSA public key destination was unexpectedly too small."); + throw new CryptographicException(); + } + + return bytesWritten; + } + + internal override int ExportPrivateKey(Span destination) + { + if (!_rsa.TryExportRSAPrivateKey(destination, out int bytesWritten)) + { + Debug.Fail("The RSA private key destination was unexpectedly too small."); + throw new CryptographicException(); + } + + return bytesWritten; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _rsa?.Dispose(); + _rsa = null!; + } + + base.Dispose(disposing); + } + + private static RSA CreateRSA(RSAParameters parameters) + { + RSA rsa = RSA.Create(); + + try + { + rsa.ImportParameters(parameters); + return rsa; + } + catch + { + rsa.Dispose(); + throw; + } + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.XDiffieHellman.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.XDiffieHellman.cs new file mode 100644 index 00000000000000..ae8cf5afa830ba --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.XDiffieHellman.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Security.Cryptography +{ + internal sealed partial class CompositeMLKemManaged + { + private sealed class XDiffieHellmanKem : TraditionalKem + { + private X25519DiffieHellman _key; + + private XDiffieHellmanKem(X25519DiffieHellman key) + { + _key = key; + } + + internal static bool IsAlgorithmSupported(XDiffieHellmanKemAlgorithm algorithm) => + X25519DiffieHellman.IsSupported && algorithm.IsX25519; + + internal static XDiffieHellmanKem GenerateKey(XDiffieHellmanKemAlgorithm algorithm) + { + Debug.Assert(algorithm.IsX25519); + + return new XDiffieHellmanKem(X25519DiffieHellman.GenerateKey()); + } + + internal static XDiffieHellmanKem ImportPublicKey( + XDiffieHellmanKemAlgorithm algorithm, + ReadOnlySpan source) + { + Debug.Assert(algorithm.IsX25519); + + return new XDiffieHellmanKem(X25519DiffieHellman.ImportPublicKey(source)); + } + + internal static XDiffieHellmanKem ImportPrivateKey( + XDiffieHellmanKemAlgorithm algorithm, + ReadOnlySpan source) + { + Debug.Assert(algorithm.IsX25519); + + return new XDiffieHellmanKem(X25519DiffieHellman.ImportPrivateKey(source)); + } + + internal override void Encapsulate(Span ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.2 + // DHKEM.Encaps(pkR): + // (skE, pkE) = GenerateKeyPair() + // ss = DH(skE, pkR) + // ct = SerializePublicKey(pkE) + // + // return ss, ct + + using (X25519DiffieHellman ephemeral = X25519DiffieHellman.GenerateKey()) + { + ephemeral.ExportPublicKey(ciphertext); + + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + ephemeral.DeriveRawSecretAgreement(_key, sharedSecret); + } + } + + internal override void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 2.2 + // DHKEM.Decaps(skR, ct): + // pkE = DeserializePublicKey(ct) + // ss = DH(skR, pkE) + // + // return ss + + // The caller clears sharedSecret, even when this method throws, so no cleanup is needed here. + _key.DeriveRawSecretAgreement(ciphertext, sharedSecret); + } + + internal override int ExportPublicKey(Span destination) + { + _key.ExportPublicKey(destination); + return X25519DiffieHellman.PublicKeySizeInBytes; + } + + internal override int ExportPrivateKey(Span destination) + { + _key.ExportPrivateKey(destination); + return X25519DiffieHellman.PrivateKeySizeInBytes; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _key?.Dispose(); + _key = null!; + } + + base.Dispose(disposing); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.cs new file mode 100644 index 00000000000000..cbbf755fb0458c --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLKemManaged.cs @@ -0,0 +1,569 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Security.Cryptography +{ + internal sealed partial class CompositeMLKemManaged : CompositeMLKem + { + private static readonly Dictionary s_algorithmMetadata = CreateAlgorithmMetadata(); + private static readonly ConcurrentDictionary s_algorithmSupport = new(); + + private MLKem _mlkem; + private TraditionalKem _traditionalKem; + private readonly bool _hasDecapsulationKey; + + private AlgorithmMetadata AlgorithmDetails => field ??= s_algorithmMetadata[Algorithm]; + + private CompositeMLKemManaged(CompositeMLKemAlgorithm algorithm, MLKem mlkem, TraditionalKem traditionalKem, bool hasDecapsulationKey) + : base(algorithm) + { + _mlkem = mlkem; + _traditionalKem = traditionalKem; + _hasDecapsulationKey = hasDecapsulationKey; + } + + internal static bool IsAlgorithmSupportedImpl(CompositeMLKemAlgorithm algorithm) + { + return s_algorithmSupport.GetOrAdd( + algorithm, + static alg => + { + AlgorithmMetadata metadata = s_algorithmMetadata[alg]; + + return MLKemImplementation.IsAlgorithmSupported(metadata.MLKemAlgorithm) && metadata.TraditionalKemAlgorithm switch + { + RsaKemAlgorithm rsaAlgorithm => RsaKem.IsAlgorithmSupported(rsaAlgorithm), + ECDiffieHellmanKemAlgorithm ecdhAlgorithm => ECDiffieHellmanKem.IsAlgorithmSupported(ecdhAlgorithm), + XDiffieHellmanKemAlgorithm xdhAlgorithm => XDiffieHellmanKem.IsAlgorithmSupported(xdhAlgorithm), + }; + }); + } + + internal static CompositeMLKem GenerateKeyImpl(CompositeMLKemAlgorithm algorithm) + { + Debug.Assert(IsAlgorithmSupportedImpl(algorithm)); + + AlgorithmMetadata metadata = s_algorithmMetadata[algorithm]; + + // draft-ietf-lamps-pq-composite-kem-19, 3.1 + // 1. Generate component keys + // + // mlkemSeed = Random(64) + // (mlkemPK, mlkemSK) = ML-KEM.KeyGen_internal( + // mlkemSeed[:32], + // mlkemSeed[32:] ) + // (tradPK, tradSK) = Trad.KeyGen() + // + // 2. Check for component key gen failure + // + // if NOT (mlkemPK, mlkemSK) or NOT (tradPK, tradSK): + // output "Key generation error" + + MLKem mlkem = MLKem.GenerateKey(metadata.MLKemAlgorithm); + + TraditionalKem? tradKey; + + try + { + tradKey = metadata.TraditionalKemAlgorithm switch + { + RsaKemAlgorithm rsaAlgorithm => RsaKem.GenerateKey(rsaAlgorithm), + ECDiffieHellmanKemAlgorithm ecdhAlgorithm => ECDiffieHellmanKem.GenerateKey(ecdhAlgorithm), + XDiffieHellmanKemAlgorithm xdhAlgorithm => XDiffieHellmanKem.GenerateKey(xdhAlgorithm), + }; + } + catch + { + mlkem.Dispose(); + throw; + } + + // 3. Output the composite public and private keys + // + // pk = SerializePublicKey(mlkemPK, tradPK) + // sk = SerializePrivateKey(mlkemSeed, tradSK) + // return (pk, sk) + + return new CompositeMLKemManaged(algorithm, mlkem, tradKey, hasDecapsulationKey: true); + } + + internal static CompositeMLKem ImportEncapsulationKeyImpl(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source) + { + Debug.Assert(IsAlgorithmSupportedImpl(algorithm)); + + AlgorithmMetadata metadata = s_algorithmMetadata[algorithm]; + + // draft-ietf-lamps-pq-composite-kem-19, 4.1 + // 1. Parse each constituent encoded public key. + // The length of the mlkemPK is known based on the size of + // the ML-KEM component key length specified by the Object ID. + // + // switch ML-KEM do + // case ML-KEM-768: + // mlkemPK = bytes[:1184] + // tradPK = bytes[1184:] + // case ML-KEM-1024: + // mlkemPK = bytes[:1568] + // tradPK = bytes[1568:] + // + // Note that while ML-KEM has fixed-length keys, RSA + // may not, depending on encoding, so rigorous length-checking + // of the overall composite key is not always possible. + // + // 2. Output the component public keys + // + // output (mlkemPK, tradPK) + + ReadOnlySpan mlkemKey = source.Slice(0, metadata.MLKemAlgorithm.EncapsulationKeySizeInBytes); + ReadOnlySpan tradKey = source.Slice(metadata.MLKemAlgorithm.EncapsulationKeySizeInBytes); + + MLKem mlkem = MLKem.ImportEncapsulationKey(metadata.MLKemAlgorithm, mlkemKey); + + TraditionalKem? traditionalKem; + + try + { + traditionalKem = metadata.TraditionalKemAlgorithm switch + { + RsaKemAlgorithm rsaAlgorithm => RsaKem.ImportPublicKey(rsaAlgorithm, tradKey), + ECDiffieHellmanKemAlgorithm ecdhAlgorithm => ECDiffieHellmanKem.ImportPublicKey(ecdhAlgorithm, tradKey), + XDiffieHellmanKemAlgorithm xdhAlgorithm => XDiffieHellmanKem.ImportPublicKey(xdhAlgorithm, tradKey), + }; + } + catch + { + mlkem.Dispose(); + throw; + } + + return new CompositeMLKemManaged(algorithm, mlkem, traditionalKem, hasDecapsulationKey: false); + } + + internal static CompositeMLKem ImportDecapsulationKeyImpl(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source) + { + Debug.Assert(IsAlgorithmSupportedImpl(algorithm)); + + AlgorithmMetadata metadata = s_algorithmMetadata[algorithm]; + + // draft-ietf-lamps-pq-composite-kem-19, 4.2 + // 1. Parse the ML-KEM seed, which is always a 64 byte seed + // for all parameter sets. + // + // mlkemSeed = bytes[:64] + // tradSK = bytes[64:] + // + // 2. Output the component private keys + // + // output (mlkemSeed, tradKey) + + ReadOnlySpan mlkemKey = source.Slice(0, metadata.MLKemAlgorithm.PrivateSeedSizeInBytes); + ReadOnlySpan tradKey = source.Slice(metadata.MLKemAlgorithm.PrivateSeedSizeInBytes); + + MLKem mlkem = MLKem.ImportPrivateSeed(metadata.MLKemAlgorithm, mlkemKey); + + TraditionalKem? traditionalKem; + + try + { + traditionalKem = metadata.TraditionalKemAlgorithm switch + { + RsaKemAlgorithm rsaAlgorithm => RsaKem.ImportPrivateKey(rsaAlgorithm, tradKey), + ECDiffieHellmanKemAlgorithm ecdhAlgorithm => ECDiffieHellmanKem.ImportPrivateKey(ecdhAlgorithm, tradKey), + XDiffieHellmanKemAlgorithm xdhAlgorithm => XDiffieHellmanKem.ImportPrivateKey(xdhAlgorithm, tradKey), + }; + } + catch + { + mlkem.Dispose(); + throw; + } + + return new CompositeMLKemManaged(algorithm, mlkem, traditionalKem, hasDecapsulationKey: true); + } + + protected override void EncapsulateCore(Span ciphertext, Span sharedSecret) + { + // draft-ietf-lamps-pq-composite-kem-19, 3.2 + // 1. Separate the public keys. + // + // (mlkemPK, tradPK) = DeserializePublicKey(pk) + + /* no-op */ + + // 2. Perform the respective component Encap operations according to + // their algorithm specifications. + // + // (mlkemCT, mlkemSS) = ML-KEM.Encaps(mlkemPK) + // (tradCT, tradSS) = TradKEM.Encaps(tradPK) + // + // 3. If either ML-KEM.Encaps() or TradKEM.Encaps() return an error, + // then this process must return an error. + // + // if NOT (mlkemCT, mlkemSS) or NOT (tradCT, tradSS): + // output "Encapsulation error" + // + // 4. Encode the ciphertext + // + // ct = SerializeCiphertext(mlkemCT, tradCT) + + int mlkemCiphertextSize = AlgorithmDetails.MLKemAlgorithm.CiphertextSizeInBytes; + + Span mlkemCT = ciphertext.Slice(0, mlkemCiphertextSize); + Span tradCT = ciphertext.Slice(mlkemCiphertextSize); + + const int MaxMLKemSharedSecretSize = 32; + const int MaxTraditionalSharedSecretSize = 66; + + int mlkemSharedSecretSize = AlgorithmDetails.MLKemAlgorithm.SharedSecretSizeInBytes; + int traditionalSharedSecretSize = AlgorithmDetails.TraditionalKemAlgorithm.SharedSecretSizeInBytes; + Debug.Assert(mlkemSharedSecretSize <= MaxMLKemSharedSecretSize, $"Increase {nameof(MaxMLKemSharedSecretSize)} to utilize the stackalloc below."); + Debug.Assert(traditionalSharedSecretSize <= MaxTraditionalSharedSecretSize, $"Increase {nameof(MaxTraditionalSharedSecretSize)} to utilize the stackalloc below."); + + Span mlkemSS = mlkemSharedSecretSize <= MaxMLKemSharedSecretSize + ? (stackalloc byte[MaxMLKemSharedSecretSize]).Slice(0, mlkemSharedSecretSize) + : new byte[mlkemSharedSecretSize]; // We should never get here, but handle it anyway. + + Span tradSS = traditionalSharedSecretSize <= MaxTraditionalSharedSecretSize + ? (stackalloc byte[MaxTraditionalSharedSecretSize]).Slice(0, traditionalSharedSecretSize) + : new byte[traditionalSharedSecretSize]; // We should never get here, but handle it anyway. + + try + { + _mlkem.Encapsulate(mlkemCT, mlkemSS); + _traditionalKem.Encapsulate(tradCT, tradSS); + + // 5. Combine the KEM secrets and additional context to yield the + // composite shared secret key. + // + // ss = KemCombiner(mlkemSS, tradSS, tradCT, tradPK, Label) + + CombineSecrets(mlkemSS, tradSS, tradCT, sharedSecret); + + // 6. Output composite shared secret key and ciphertext. + // + // return (ss, ct) + } + finally + { + CryptographicOperations.ZeroMemory(mlkemSS); + CryptographicOperations.ZeroMemory(tradSS); + } + } + + protected override void DecapsulateCore(ReadOnlySpan ciphertext, Span sharedSecret) + { + if (!_hasDecapsulationKey) + { + throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable); + } + + // draft-ietf-lamps-pq-composite-kem-19, 3.3 + // 1. Separate the private keys and ciphertexts + // + // (mlkemSeed, tradSK) = DeserializePrivateKey(sk) + // (_, mlkemSK) = ML-KEM.KeyGen(mlkemSeed[:32], mlkemSeed[32:]) + // (mlkemCT, tradCT) = DeserializeCiphertext(ct) + + int mlkemCiphertextSize = AlgorithmDetails.MLKemAlgorithm.CiphertextSizeInBytes; + ReadOnlySpan mlkemCT = ciphertext.Slice(0, mlkemCiphertextSize); + ReadOnlySpan tradCT = ciphertext.Slice(mlkemCiphertextSize); + + // 2. Perform the respective component Decap operations according + // to their algorithm specifications. + // + // mlkemSS = ML-KEM.Decaps(mlkemSK, mlkemCT) + // tradSS = TradKEM.Decaps(tradSK, tradCT) + // + // 3. If either ML-KEM.Decaps() or TradKEM.Decaps() return an error, + // then this process must return an error. + // + // if NOT mlkemSS or NOT tradSS: + // output "Decapsulation error" + + const int MaxMLKemSharedSecretSize = 32; + const int MaxTraditionalSharedSecretSize = 66; + + int mlkemSharedSecretSize = AlgorithmDetails.MLKemAlgorithm.SharedSecretSizeInBytes; + int traditionalSharedSecretSize = AlgorithmDetails.TraditionalKemAlgorithm.SharedSecretSizeInBytes; + Debug.Assert(mlkemSharedSecretSize <= MaxMLKemSharedSecretSize, $"Increase {nameof(MaxMLKemSharedSecretSize)} to utilize the stackalloc below."); + Debug.Assert(traditionalSharedSecretSize <= MaxTraditionalSharedSecretSize, $"Increase {nameof(MaxTraditionalSharedSecretSize)} to utilize the stackalloc below."); + + Span mlkemSS = mlkemSharedSecretSize <= MaxMLKemSharedSecretSize + ? (stackalloc byte[MaxMLKemSharedSecretSize]).Slice(0, mlkemSharedSecretSize) + : new byte[mlkemSharedSecretSize]; // We should never get here, but handle it anyway. + + Span tradSS = traditionalSharedSecretSize <= MaxTraditionalSharedSecretSize + ? (stackalloc byte[MaxTraditionalSharedSecretSize]).Slice(0, traditionalSharedSecretSize) + : new byte[traditionalSharedSecretSize]; // We should never get here, but handle it anyway. + + try + { + _mlkem.Decapsulate(mlkemCT, mlkemSS); + _traditionalKem.Decapsulate(tradCT, tradSS); + + // 4. Combine the KEM secrets and additional context to yield the + // composite shared secret key. + // + // ss = KemCombiner(mlkemSS, tradSS, tradCT, tradPK, Label) + + CombineSecrets(mlkemSS, tradSS, tradCT, sharedSecret); + + // 5. Output composite shared secret key. + // + // return ss + } + finally + { + CryptographicOperations.ZeroMemory(mlkemSS); + CryptographicOperations.ZeroMemory(tradSS); + } + } + + protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) + { + if (!_hasDecapsulationKey) + { + throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable); + } + + return TryExportPkcs8FromExportedDecapsulationKey(destination, out bytesWritten); + } + + protected override int ExportEncapsulationKeyCore(Span destination) + { + // draft-ietf-lamps-pq-composite-kem-19, 4.1 + // 1. Combine and output the encoded public key + // + // output mlkemPK || tradPK + + int mlkemEncapsulationKeySize = AlgorithmDetails.MLKemAlgorithm.EncapsulationKeySizeInBytes; + int bytesWritten = 0; + + _mlkem.ExportEncapsulationKey(destination.Slice(0, mlkemEncapsulationKeySize)); + bytesWritten += mlkemEncapsulationKeySize; + + bytesWritten += _traditionalKem.ExportPublicKey(destination.Slice(mlkemEncapsulationKeySize)); + + return bytesWritten; + } + + protected override int ExportDecapsulationKeyCore(Span destination) + { + if (!_hasDecapsulationKey) + { + throw new CryptographicException(SR.Cryptography_NoPrivateKeyAvailable); + } + + // draft-ietf-lamps-pq-composite-kem-19, 4.2 + // 1. Combine and output the encoded private key. + // + // output mlkemSeed || tradSK + + try + { + int mlkemDecapsulationKeySize = AlgorithmDetails.MLKemAlgorithm.PrivateSeedSizeInBytes; + int bytesWritten = 0; + + _mlkem.ExportPrivateSeed(destination.Slice(0, mlkemDecapsulationKeySize)); + bytesWritten += mlkemDecapsulationKeySize; + + bytesWritten += _traditionalKem.ExportPrivateKey(destination.Slice(mlkemDecapsulationKeySize)); + + return bytesWritten; + } + catch + { + CryptographicOperations.ZeroMemory(destination); + throw; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _mlkem?.Dispose(); + _mlkem = null!; + + _traditionalKem?.Dispose(); + _traditionalKem = null!; + } + + base.Dispose(disposing); + } + + private void CombineSecrets( + ReadOnlySpan mlkemSharedSecret, + ReadOnlySpan traditionalSharedSecret, + ReadOnlySpan traditionalCiphertext, + Span destination) + { + // draft-ietf-lamps-pq-composite-kem-19, 3.4 + // ss = SHA3-256(mlkemSS || tradSS || tradCT || tradPK || Label) + // + // return ss + + int maxTraditionalPublicKeySize = + Algorithm.MaxEncapsulationKeySizeInBytes - AlgorithmDetails.MLKemAlgorithm.EncapsulationKeySizeInBytes; + + using (CryptoPoolLease lease = CryptoPoolLease.Rent(maxTraditionalPublicKeySize, skipClear: true)) + using (IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA3_256)) + { + int traditionalPublicKeySize = _traditionalKem.ExportPublicKey(lease.Span); + + hash.AppendData(mlkemSharedSecret); + hash.AppendData(traditionalSharedSecret); + hash.AppendData(traditionalCiphertext); + hash.AppendData(lease.Span.Slice(0, traditionalPublicKeySize)); + hash.AppendData(AlgorithmDetails.Label); + + if (!hash.TryGetHashAndReset(destination, out int bytesWritten) || bytesWritten != destination.Length) + { + Debug.Fail("SHA3-256 produced an unexpected output length."); + throw new CryptographicException(); + } + } + } + +#if DESIGNTIMEINTERFACES + private interface ITraditionalKemFactory + where TTraditionalKem : TraditionalKem, ITraditionalKemFactory + { + internal static abstract bool IsAlgorithmSupported(TAlgorithmKemAlgorithm algorithm); + internal static abstract TTraditionalKem GenerateKey(TAlgorithmKemAlgorithm algorithm); + internal static abstract TTraditionalKem ImportPrivateKey(TAlgorithmKemAlgorithm algorithm, ReadOnlySpan source); + internal static abstract TTraditionalKem ImportPublicKey(TAlgorithmKemAlgorithm algorithm, ReadOnlySpan source); + } +#endif + + private closed class TraditionalKem : IDisposable + { + private bool _disposed; + + internal abstract void Encapsulate(Span ciphertext, Span sharedSecret); + internal abstract void Decapsulate(ReadOnlySpan ciphertext, Span sharedSecret); + internal abstract int ExportPublicKey(Span destination); + internal abstract int ExportPrivateKey(Span destination); + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + Dispose(true); + GC.SuppressFinalize(this); + } + } + + protected virtual void Dispose(bool disposing) + { + } + } + + private static Dictionary CreateAlgorithmMetadata() + { + const int Count = 12; + + Dictionary algorithmMetadata = new(Count) + { + { + CompositeMLKemAlgorithm.MLKem768WithRsaOaep2048, + new(MLKemAlgorithm.MLKem768, new RsaKemAlgorithm(2048), [.."MLKEM768-RSAOAEP2048"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithRsaOaep3072, + new(MLKemAlgorithm.MLKem768, new RsaKemAlgorithm(3072), [.."MLKEM768-RSAOAEP3072"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithRsaOaep4096, + new(MLKemAlgorithm.MLKem768, new RsaKemAlgorithm(4096), [.."MLKEM768-RSAOAEP4096"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithX25519, + new(MLKemAlgorithm.MLKem768, new XDiffieHellmanKemAlgorithm(IsX25519: true), [0x5C, 0x2E, 0x2F, 0x2F, 0x5E, 0x5C]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanP256, + new(MLKemAlgorithm.MLKem768, new ECDiffieHellmanKemAlgorithm(Oids.secp256r1, 256, 256), [.."MLKEM768-P256"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanP384, + new(MLKemAlgorithm.MLKem768, new ECDiffieHellmanKemAlgorithm(Oids.secp384r1, 384, 384), [.."MLKEM768-P384"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanBrainpoolP256r1, + new(MLKemAlgorithm.MLKem768, new ECDiffieHellmanKemAlgorithm(Oids.brainpoolP256r1, 256, 256), [.."MLKEM768-BP256"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem1024WithRsaOaep3072, + new(MLKemAlgorithm.MLKem1024, new RsaKemAlgorithm(3072), [.."MLKEM1024-RSAOAEP3072"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanP384, + new(MLKemAlgorithm.MLKem1024, new ECDiffieHellmanKemAlgorithm(Oids.secp384r1, 384, 384), [.."MLKEM1024-P384"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanBrainpoolP384r1, + new(MLKemAlgorithm.MLKem1024, new ECDiffieHellmanKemAlgorithm(Oids.brainpoolP384r1, 384, 384), [.."MLKEM1024-BP384"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem1024WithX448, + new(MLKemAlgorithm.MLKem1024, new XDiffieHellmanKemAlgorithm(IsX25519: false), [.."MLKEM1024-X448"u8]) + }, + { + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanP521, + new(MLKemAlgorithm.MLKem1024, new ECDiffieHellmanKemAlgorithm(Oids.secp521r1, 521, 521), [.."MLKEM1024-P521"u8]) + }, + }; + + Debug.Assert(algorithmMetadata.Count == Count); + return algorithmMetadata; + } + + private sealed class AlgorithmMetadata( + MLKemAlgorithm mlkemAlgorithm, + TraditionalKemAlgorithm traditionalAlgorithm, + byte[] label) + { + internal MLKemAlgorithm MLKemAlgorithm { get; } = mlkemAlgorithm; + internal TraditionalKemAlgorithm TraditionalKemAlgorithm { get; } = traditionalAlgorithm; + internal byte[] Label { get; } = label; + } + + private closed record TraditionalKemAlgorithm + { + internal abstract int SharedSecretSizeInBytes { get; } + } + + private sealed record RsaKemAlgorithm(int KeySizeInBits) + : TraditionalKemAlgorithm + { + internal const int SecretSizeInBytes = 32; + + internal override int SharedSecretSizeInBytes => SecretSizeInBytes; + } + + private sealed record ECDiffieHellmanKemAlgorithm(string CurveOidValue, int FieldSizeInBits, int OrderSizeInBits) + : TraditionalKemAlgorithm + { + internal ECCurve Curve { get; } = ECCurve.CreateFromValue(CurveOidValue); + internal int FieldSizeInBytes { get; } = (FieldSizeInBits + 7) / 8; + internal int OrderSizeInBytes { get; } = (OrderSizeInBits + 7) / 8; + + internal override int SharedSecretSizeInBytes => FieldSizeInBytes; + } + + private sealed record XDiffieHellmanKemAlgorithm(bool IsX25519) + : TraditionalKemAlgorithm + { + private const int X448SecretSizeInBytes = 56; + + internal override int SharedSecretSizeInBytes => + IsX25519 ? X25519DiffieHellman.SecretAgreementSizeInBytes : X448SecretSizeInBytes; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs index b2f444bf8da27a..e9286111d6751d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs @@ -9,6 +9,8 @@ internal sealed partial class MLKemImplementation : MLKem { internal static new bool IsSupported => false; + internal static bool IsAlgorithmSupported(MLKemAlgorithm _) => false; + private MLKemImplementation(MLKemAlgorithm algorithm) : base(algorithm) { Debug.Fail("Caller should have checked platform availability."); diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.Windows.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.Windows.cs index 345beb64d6e249..cfae3e7b54a4f5 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.Windows.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.Windows.cs @@ -24,6 +24,8 @@ internal sealed partial class MLKemImplementation : MLKem [MemberNotNullWhen(true, nameof(s_algHandle))] internal static new bool IsSupported => s_algHandle is not null; + internal static bool IsAlgorithmSupported(MLKemAlgorithm _) => IsSupported; + private MLKemImplementation(MLKemAlgorithm algorithm, SafeBCryptKeyHandle key, bool hasSeed, bool hasDecapsulationKey) : base(algorithm) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs index 693911e3fe390f..49bcc78db33e0c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs @@ -15,9 +15,10 @@ internal static partial class RSAKeyFormatHelper internal static TRet FromPkcs1PrivateKey( ReadOnlySpan keyData, RSAParametersCallback parametersReader, - bool pinAndClearParameters = true) + bool pinAndClearParameters = true, + AsnEncodingRules ruleSet = AsnEncodingRules.BER) { - ValueRSAPrivateKeyAsn.Decode(keyData, AsnEncodingRules.BER, out ValueRSAPrivateKeyAsn key); + ValueRSAPrivateKeyAsn.Decode(keyData, ruleSet, out ValueRSAPrivateKeyAsn key); const int MaxSupportedVersion = 0; @@ -79,9 +80,10 @@ static TRet ExtractParametersWithCallback(RSAParametersCallback parameters internal static TRet FromPkcs1PublicKey( ReadOnlySpan keyData, - RSAParametersCallback parametersReader) + RSAParametersCallback parametersReader, + AsnEncodingRules ruleSet = AsnEncodingRules.BER) { - ValueRSAPublicKeyAsn.Decode(keyData, AsnEncodingRules.BER, out ValueRSAPublicKeyAsn key); + ValueRSAPublicKeyAsn.Decode(keyData, ruleSet, out ValueRSAPublicKeyAsn key); RSAParameters parameters = new RSAParameters { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemContractTests.cs index 951a10ffb75608..31023a25c4c597 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemContractTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemContractTests.cs @@ -182,7 +182,7 @@ public static void Encapsulate_Span_CallsCore(CompositeMLKemAlgorithm algorithm) [Theory] [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] - public static void Encapsulate_CoreCryptographicException_Propagates(CompositeMLKemAlgorithm algorithm) + public static void Encapsulate_Array_CryptographicExceptionPropagates(CompositeMLKemAlgorithm algorithm) { using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); CryptographicException expected = new(); @@ -190,10 +190,31 @@ public static void Encapsulate_CoreCryptographicException_Propagates(CompositeML kem.EncapsulateCoreHook = (_, _) => throw expected; Assert.Same(expected, Assert.Throws(() => kem.Encapsulate(out _, out _))); + Assert.Equal(1, kem.EncapsulateCoreCallCount); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void Encapsulate_Span_CryptographicExceptionClearsSharedSecret(CompositeMLKemAlgorithm algorithm) + { + using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); + CryptographicException expected = new(); + + byte[] sharedSecret = new byte[algorithm.SharedSecretSizeInBytes]; + sharedSecret.AsSpan().Fill(0xA5); + + kem.EncapsulateCoreHook = (_, destination) => + { + destination.Fill(0xFE); + throw expected; + }; + Assert.Same( expected, Assert.Throws(() => - kem.Encapsulate(new byte[algorithm.CiphertextSizeInBytes], new byte[algorithm.SharedSecretSizeInBytes]))); + kem.Encapsulate(new byte[algorithm.CiphertextSizeInBytes], sharedSecret))); + Assert.Equal(1, kem.EncapsulateCoreCallCount); + AssertExtensions.FilledWith(0, sharedSecret); } [Theory] @@ -240,7 +261,35 @@ public static void Decapsulate_Span_CallsCore(CompositeMLKemAlgorithm algorithm) [Theory] [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] - public static void Decapsulate_CoreCryptographicException_Propagates(CompositeMLKemAlgorithm algorithm) + public static void Decapsulate_Array_ImplicitRejectionReturnsSharedSecret(CompositeMLKemAlgorithm algorithm) + { + using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); + byte[] ciphertext = new byte[algorithm.CiphertextSizeInBytes]; + + kem.DecapsulateCoreHook = (_, sharedSecret) => sharedSecret.Fill(0xA5); + + AssertExtensions.FilledWith(0xA5, kem.Decapsulate(ciphertext)); + Assert.Equal(1, kem.DecapsulateCoreCallCount); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void Decapsulate_Span_ImplicitRejectionReturnsSharedSecret(CompositeMLKemAlgorithm algorithm) + { + using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); + byte[] ciphertext = new byte[algorithm.CiphertextSizeInBytes]; + byte[] sharedSecret = new byte[algorithm.SharedSecretSizeInBytes]; + + kem.DecapsulateCoreHook = (_, destination) => destination.Fill(0xA5); + kem.Decapsulate(ciphertext, sharedSecret); + + Assert.Equal(1, kem.DecapsulateCoreCallCount); + AssertExtensions.FilledWith(0xA5, sharedSecret); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void Decapsulate_Array_CryptographicExceptionPropagates(CompositeMLKemAlgorithm algorithm) { using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); CryptographicException expected = new(); @@ -249,10 +298,31 @@ public static void Decapsulate_CoreCryptographicException_Propagates(CompositeML kem.DecapsulateCoreHook = (_, _) => throw expected; Assert.Same(expected, Assert.Throws(() => kem.Decapsulate(ciphertext))); + Assert.Equal(1, kem.DecapsulateCoreCallCount); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void Decapsulate_Span_CryptographicExceptionClearsSharedSecret(CompositeMLKemAlgorithm algorithm) + { + using CompositeMLKemMockImplementation kem = CompositeMLKemMockImplementation.Create(algorithm); + CryptographicException expected = new(); + byte[] ciphertext = new byte[algorithm.CiphertextSizeInBytes]; + + byte[] sharedSecret = new byte[algorithm.SharedSecretSizeInBytes]; + sharedSecret.AsSpan().Fill(0xA5); + + kem.DecapsulateCoreHook = (_, destination) => + { + destination.Fill(0xFE); + throw expected; + }; + Assert.Same( expected, - Assert.Throws(() => - kem.Decapsulate(ciphertext, new byte[algorithm.SharedSecretSizeInBytes]))); + Assert.Throws(() => kem.Decapsulate(ciphertext, sharedSecret))); + Assert.Equal(1, kem.DecapsulateCoreCallCount); + AssertExtensions.FilledWith(0, sharedSecret); } [Theory] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemFactoryTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemFactoryTests.cs index fbdfd422e9b33f..9e1b426e6a8ac9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemFactoryTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemFactoryTests.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Formats.Asn1; +using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; +using Microsoft.DotNet.RemoteExecutor; using Test.Cryptography; using Xunit; @@ -39,16 +41,35 @@ public static void NullArgumentValidation() [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] public static void IsAlgorithmSupported_AgreesWithPlatform(CompositeMLKemAlgorithm algorithm) { - // No platform implements Composite ML-KEM yet. - AssertExtensions.FalseExpression(CompositeMLKem.IsAlgorithmSupported(algorithm)); + bool supported = + !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + MLKem.IsSupported && + CompositeMLKemTestData.ExecuteComponentFunc( + algorithm, + rsa => true, + ecdh => ecdh.IsSecg, + xdh => xdh.IsX25519 && X25519DiffieHellman.IsSupported); + + Assert.Equal(supported, CompositeMLKem.IsAlgorithmSupported(algorithm)); } - [Theory] - [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] - public static void AlgorithmMatches_GenerateKey(CompositeMLKemAlgorithm algorithm) + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void IsSupported_InitializesCrypto() { - using CompositeMLKem kem = CompositeMLKem.GenerateKey(algorithm); - Assert.Equal(algorithm, kem.Algorithm); + CompositeMLKemAlgorithm algorithm = CompositeMLKemAlgorithm.MLKem768WithX25519; + string arg = CompositeMLKem.IsAlgorithmSupported(algorithm) ? "1" : "0"; + + // This ensures that Composite ML-KEM is the first cryptographic algorithm touched in the process, which kicks off + // the initialization of the crypto layer on some platforms. Running in a remote executor ensures no other + // test has pre-initialized anything. + RemoteExecutor.Invoke(static (string isSupportedStr) => + { + CompositeMLKemAlgorithm algorithm = CompositeMLKemAlgorithm.MLKem768WithX25519; + bool isSupported = isSupportedStr == "1"; + return CompositeMLKem.IsAlgorithmSupported(algorithm) == isSupported ? + RemoteExecutor.SuccessExitCode : + 0; + }, arg).Dispose(); } [Theory] @@ -68,6 +89,24 @@ public static void ImportEncapsulationKey_InvalidSizes(CompositeMLKemAlgorithm a new byte[CompositeMLKemTestData.GetMLKemAlgorithm(algorithm).EncapsulationKeySizeInBytes]); } + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportEncapsulationKey_Success(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportEncapsulationKey( + import => + { + using (CompositeMLKem kem = import()) + { + Assert.Equal(vector.Algorithm, kem.Algorithm); + AssertExtensions.SequenceEqual(vector.EncapsulationKey, kem.ExportEncapsulationKey()); + Assert.Throws(() => kem.ExportDecapsulationKey()); + } + }, + vector.Algorithm, + vector.EncapsulationKey.ToArray()); + } + [Theory] [MemberData(nameof(CompositeMLKemTestData.AllAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] public static void ImportDecapsulationKey_InvalidSizes(CompositeMLKemAlgorithm algorithm) @@ -85,6 +124,24 @@ public static void ImportDecapsulationKey_InvalidSizes(CompositeMLKemAlgorithm a new byte[CompositeMLKemTestData.GetMLKemAlgorithm(algorithm).PrivateSeedSizeInBytes]); } + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportDecapsulationKey_Success(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportDecapsulationKey( + import => + { + using (CompositeMLKem kem = import()) + { + Assert.Equal(vector.Algorithm, kem.Algorithm); + AssertExtensions.SequenceEqual(vector.EncapsulationKey, kem.ExportEncapsulationKey()); + AssertExtensions.SequenceEqual(vector.DecapsulationKey, kem.ExportDecapsulationKey()); + } + }, + vector.Algorithm, + vector.DecapsulationKey.ToArray()); + } + [Fact] public static void ImportSubjectPublicKeyInfo_UnknownAlgorithm() { @@ -195,6 +252,20 @@ public static void Import_EncodedKeyTruncated() AssertPkcs8PrivateKeyImportThrows(TruncateLastByte(CreateEmptyPkcs8PrivateKey())); } + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportPkcs8PrivateKey_AllowsBerEncoding(CompositeMLKemTestVector vector) + { + byte[] ber = AsnUtils.ConvertDerToNonDerBer(vector.Pkcs8); + + using (CompositeMLKem kem = CompositeMLKem.ImportPkcs8PrivateKey(ber)) + { + Assert.Equal(vector.Algorithm, kem.Algorithm); + AssertExtensions.SequenceEqual(vector.EncapsulationKey, kem.ExportEncapsulationKey()); + AssertExtensions.SequenceEqual(vector.DecapsulationKey, kem.ExportDecapsulationKey()); + } + } + [Fact] [SkipOnPlatform(TestPlatforms.Browser, "Password-based encryption requires AES, which is not supported on Browser.")] public static void ImportEncryptedPkcs8PrivateKey_TrailingData() @@ -264,6 +335,54 @@ public static void ImportEncryptedPkcs8PrivateKey_BytePassword_RejectsPkcs12Kdf( CompositeMLKemTestHelpers.EncryptionPasswordType.Byte); } + [Theory] + [SkipOnPlatform(TestPlatforms.Browser, "Password-based encryption requires AES, which is not supported on Browser.")] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportEncryptedPkcs8PrivateKey_BytePassword_Success(CompositeMLKemTestVector vector) + { + byte[] encryptedPkcs8 = CompositeMLKemTestHelpers.CreateEncryptedPkcs8PrivateKey( + CompositeMLKemTestHelpers.AlgorithmToOid(vector.Algorithm), + vector.DecapsulationKey.ToArray(), + new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 42), + CompositeMLKemTestHelpers.EncryptionPasswordType.Byte); + + CompositeMLKemTestHelpers.AssertImportEncryptedPkcs8PrivateKey( + import => + { + using (CompositeMLKem kem = import("PLACEHOLDER", encryptedPkcs8)) + { + Assert.Equal(vector.Algorithm, kem.Algorithm); + AssertExtensions.SequenceEqual(vector.EncapsulationKey, kem.ExportEncapsulationKey()); + AssertExtensions.SequenceEqual(vector.DecapsulationKey, kem.ExportDecapsulationKey()); + } + }, + CompositeMLKemTestHelpers.EncryptionPasswordType.Byte); + } + + [Theory] + [SkipOnPlatform(TestPlatforms.Browser, "Password-based encryption requires AES, which is not supported on Browser.")] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportEncryptedPkcs8PrivateKey_CharPassword_Success(CompositeMLKemTestVector vector) + { + byte[] encryptedPkcs8 = CompositeMLKemTestHelpers.CreateEncryptedPkcs8PrivateKey( + CompositeMLKemTestHelpers.AlgorithmToOid(vector.Algorithm), + vector.DecapsulationKey.ToArray(), + new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 42), + CompositeMLKemTestHelpers.EncryptionPasswordType.Char); + + CompositeMLKemTestHelpers.AssertImportEncryptedPkcs8PrivateKey( + import => + { + using (CompositeMLKem kem = import("PLACEHOLDER", encryptedPkcs8)) + { + Assert.Equal(vector.Algorithm, kem.Algorithm); + AssertExtensions.SequenceEqual(vector.EncapsulationKey, kem.ExportEncapsulationKey()); + AssertExtensions.SequenceEqual(vector.DecapsulationKey, kem.ExportDecapsulationKey()); + } + }, + CompositeMLKemTestHelpers.EncryptionPasswordType.Char); + } + [Fact] public static void Import_WrongAsnType() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemImplementationTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemImplementationTests.cs new file mode 100644 index 00000000000000..83d305ea81fe9f --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemImplementationTests.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + [ConditionalClass(typeof(MLKem), nameof(MLKem.IsSupported))] + public sealed class CompositeMLKemImplementationTests : CompositeMLKemTestsBase + { + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void GenerateKey_TypeIsInternal(CompositeMLKemAlgorithm algorithm) + { + AssertCompositeMLKemIsOnlyPublicAncestor(() => CompositeMLKem.GenerateKey(algorithm)); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportEncapsulationKey_TypeIsInternal(CompositeMLKemTestVector vector) + { + AssertCompositeMLKemIsOnlyPublicAncestor( + () => CompositeMLKem.ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public static void ImportDecapsulationKey_TypeIsInternal(CompositeMLKemTestVector vector) + { + AssertCompositeMLKemIsOnlyPublicAncestor( + () => CompositeMLKem.ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_EncapsulationKey_Ietf(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportEncapsulationKey( + import => + { + using (CompositeMLKem encapsKey = import()) + { + Assert.Equal(vector.Algorithm, encapsKey.Algorithm); + + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(encapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => Assert.Throws(() => export(encapsKey))); + } + }, + vector.Algorithm, + vector.EncapsulationKey.ToArray()); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_SubjectPublicKeyInfo_Ietf(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportSubjectPublicKeyInfo( + import => + { + using (CompositeMLKem encapsKey = import(vector.Spki.ToArray())) + { + Assert.Equal(vector.Algorithm, encapsKey.Algorithm); + + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(encapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => Assert.Throws(() => export(encapsKey))); + } + }); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_DecapsulationKey_Ietf(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportDecapsulationKey( + import => + { + using (CompositeMLKem decapsKey = import()) + { + Assert.Equal(vector.Algorithm, decapsKey.Algorithm); + + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(decapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => AssertExtensions.SequenceEqual(vector.DecapsulationKey, export(decapsKey))); + } + }, + vector.Algorithm, + vector.DecapsulationKey.ToArray()); + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_Pkcs8_Ietf(CompositeMLKemTestVector vector) + { + CompositeMLKemTestHelpers.AssertImportPkcs8PrivateKey( + import => + { + using (CompositeMLKem decapsKey = import(vector.Pkcs8.ToArray())) + { + Assert.Equal(vector.Algorithm, decapsKey.Algorithm); + + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(decapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => AssertExtensions.SequenceEqual(vector.DecapsulationKey, export(decapsKey))); + } + }); + } + + private static void AssertCompositeMLKemIsOnlyPublicAncestor(Func createKey) + { + Type? keyType; + + using (CompositeMLKem kem = createKey()) + { + keyType = kem.GetType(); + } + + while (keyType is not null && keyType != typeof(CompositeMLKem)) + { + AssertExtensions.FalseExpression(keyType.IsPublic); + keyType = keyType.BaseType; + } + + Assert.Equal(typeof(CompositeMLKem), keyType); + } + + protected override CompositeMLKem GenerateKey(CompositeMLKemAlgorithm algorithm) => + CompositeMLKem.GenerateKey(algorithm); + + protected override CompositeMLKem ImportDecapsulationKey(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source) => + CompositeMLKem.ImportDecapsulationKey(algorithm, source); + + protected override CompositeMLKem ImportEncapsulationKey(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source) => + CompositeMLKem.ImportEncapsulationKey(algorithm, source); + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.Raw.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.Raw.cs new file mode 100644 index 00000000000000..bd62a3b67814ce --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.Raw.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Generated from https://github.com/lamps-wg/draft-composite-kem/blob/6f6c8a5601cfe8d66730841a413d209add7dc9ed/src/testvectors.json +namespace System.Security.Cryptography.Tests +{ + public static partial class CompositeMLKemTestData + { + internal static partial CompositeMLKemTestVector[] AllIetfVectors => field ??= + [ + new CompositeMLKemTestVector( + "id-MLKEM768-RSA2048-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithRsaOaep2048, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-RSA3072-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithRsaOaep3072, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-RSA4096-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithRsaOaep4096, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-X25519-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithX25519, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-ECDH-P256-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanP256, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-ECDH-P384-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanP384, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM768-ECDH-brainpoolP256r1-SHA3-256", + CompositeMLKemAlgorithm.MLKem768WithECDiffieHellmanBrainpoolP256r1, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM1024-RSA3072-SHA3-256", + CompositeMLKemAlgorithm.MLKem1024WithRsaOaep3072, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM1024-ECDH-P384-SHA3-256", + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanP384, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM1024-ECDH-brainpoolP384r1-SHA3-256", + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanBrainpoolP384r1, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM1024-X448-SHA3-256", + CompositeMLKemAlgorithm.MLKem1024WithX448, + "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" + ), + new CompositeMLKemTestVector( + "id-MLKEM1024-ECDH-P521-SHA3-256", + CompositeMLKemAlgorithm.MLKem1024WithECDiffieHellmanP521, + "vfNF7dkPOBoqc0tOKCt1ifk8xIY0iFJh70edWLMbJwBTEIGZvBVBsZZgp5MInLJXYFmAHFguzlmJY+WDfKlVJGCPQOgRtCRWSZjMrNhyuOtu6xGLxWW9ayozATitaMSBWXw1vaMQLHyQJ1R2Mat8FhS8MNUiqTVmD6EHJcTPcRMsRzp1gltEFqcIPqN8aILPM5ah6pKYqfSm34TLuHM3UkhtFWRXuyYOseCy8aUMSVh/HYSO2oh2KMNPD1lSTtg7MYSbJhRgV9BNP+Zd2dQOnFzEqXfJB9SAdsIumOx/ASyG2hNKEGo+64WstTZHUDsZVUlZODEE24qQ2QO96altn3xEKtKAaaBI5xy2OLJDz5s27wHJx0RdL2U/cTLPHWWUAVygUZWDREWFDng+6tpbr7spRpUvNVp1ORMFXxJB3BcRePM+PKyHo7qWtLotzvFdhmiJ9MNiyeVXwXs6ovAIpTJRCYq2wkh62lO8CWq6oRqioEKwxJUQ+HRtDXxFYFCW4Ow/a2PBb4wXEZswvaeEkuS60rG5tkqw+VktQDdTSlMkEmF9rExaIlm9sTulPxgol5kfsQqoZDUfnkJVgamSm+HLRAZMf9V870douOCVT+wHz5vNoVJAyoLD2OEIT9RpuAkXveKeEeZGCmsnU6UE7CZbCTcZHeRcuFuGMYwnFvRnJtR0/zqMsXMLV1EwDezGkMECm+sEGTBNfZw6goYKl0IFmAUjrZaLkewb7ch0Jma0S8aaC3hqvcSKncF4detXefABm5uqJlkP1XsBI2ZiW3F6MacjKCF+h5d9VMEm4EBoW8CPpeLPBHGvsUgYzvRR9qE0GjoTAJoZAHpAnecCxmQBOXqZWhqYblVwvtc1timxXzoIV/o4cWiSGDWCq/F9eWqtuAUu2LBKtyhaaLx5t1Mma+NiihgTh1Iha3uZowekoNEvuIGjnAnLwfg37UE6B0ZzM/gwhEp+NpWzvTMjY7QUJ9a1a4ms52gxOzurjRNOOFu9OAKE/NyJKJC6HwmTgRG2vWrPDfitO+xPz9cmdbBP0hdm5fKeqqQLKtO/70IvtROMC3OPPFdti6GDhzNo8GurxfuxOCq6COgr8VC5kpGtBVtZU9GFnoxWo3RHKMlK/ywLwmMHNwV9IBB1oSHDY+kCWGCgniOzhlSQPMFiCPiS1JNvPbiT4EcLiiJpz2aweTiL8jsuhVU7v9oC3XiR8yF6C/wZX5aGNMMMz9xo7sgovqdOK8VpK9YdUFUBsNoYmdUhJ5HGR6kbgGC00mSCCQlICQidqxmPs6RNk8VC4re/VVQ9T6etROQUzdGfEZiMyWaL5DNnmvoXjhtrgMUL0igf97l0yuyIBSMFpiygqKU1zGrFZ1s0+8BiQqop93m5MikoB1sey9Jw4zyVDHpslwYdqhtkOxIk1QPDlmu2zdugVTx+EbMH9dlqWBo4VDarLLogGoNn4BWNTci74LqAf5NHQLIvRzFQZmrH2mklvplY6yBawptghou3lBpZ/eWcPFmZ2uo88JspLHleuoi51hQiaxm/R5uMDGwU+reEoSmm+fs6/6uF07RLkcx8hBWA5ZK1PyCWEkbO2PFdSnWWmwW66UM3DEinqEnD54CkmXkscAGH3qHOXllieyufM+oU2VF72Am0FPS89wkZvvFMa/gx8je3qRSflbd/ert454BQllCtOpfJooXNUzh5e7k+Jeu16bY1jYYCfwtnWLQyX8K6iJtd6dOX6RGH/bAtEUBfqUVctOp2C0e50hEBkftQc3lzA8XPfqh/GrycHQdV4IGQWzBTAnkHpqUVwjIlpACXgaJn+AC8oRmpDwjM+sVtB/PIJec17rIbO/c2AdpA+xKi61U/HCwQqki8wXVPgLrFmRW1ndIw/Gpen4vMfWYpChN7o9xIu9V7s1ENWaRBs3x5U9tvhJe2NoJroRwVHMw5lpRZMvgdr5FWwqJ6vsyA9jWgdeOhwbkzxbIpAJo2pHWZYScSayEQmHNaJVNuQwVAGYlDARZx+OyjB8SSeGKZ5vFHtrmbhsgN6BQsi4xpoLui2NMnOKK/g6F8Vs0nvcVWN+nYdoByE6oyGELwJYIEAe26u7r566cNgGrml3SFBvdPPrAeg30eI5nNvV++WAIctVIa5wdF4qMeE3YXw6q+OVqPVN49i6zIL90i147aorBtAVlufEGf+bdc+wvfj7t0mU5fJA1Kz/Id4FWn002yr1YkvOvSwlYKvEwtZVtYA3eZ4X9avSEiYrVzPwIAezAz30Dh", + "MIIUozCCB6CgAwIBAgIUVJnKaWPmojo+NWlVbA443rfgj5swCwYJYIZIAWUDBAMSMD0xDTALBgNVBAoMBElFVEYxDjAMBgNVBAsMBUxBTVBTMRwwGgYDVQQDDBNDb21wb3NpdGUgTUwtS0VNIENBMB4XDTI2MDExNDEyMTUzN1oXDTM2MDExNTEyMTUzN1owSTENMAsGA1UECgwESUVURjEOMAwGA1UECwwFTEFNUFMxKDAmBgNVBAMMH2lkLU1MS0VNMTAyNC1FQ0RILVA1MjEtU0hBMy0yNTYwgga2MAoGCCsGAQUFBwZCA4IGpgC980Xt2Q84GipzS04oK3WJ+TzEhjSIUmHvR51YsxsnAFMQgZm8FUGxlmCnkwicsldgWYAcWC7OWYlj5YN8qVUkYI9A6BG0JFZJmMys2HK4627rEYvFZb1rKjMBOK1oxIFZfDW9oxAsfJAnVHYxq3wWFLww1SKpNWYPoQclxM9xEyxHOnWCW0QWpwg+o3xogs8zlqHqkpip9KbfhMu4czdSSG0VZFe7Jg6x4LLxpQxJWH8dhI7aiHYow08PWVJO2DsxhJsmFGBX0E0/5l3Z1A6cXMSpd8kH1IB2wi6Y7H8BLIbaE0oQaj7rhay1NkdQOxlVSVk4MQTbipDZA73pqW2ffEQq0oBpoEjnHLY4skPPmzbvAcnHRF0vZT9xMs8dZZQBXKBRlYNERYUOeD7q2luvuylGlS81WnU5EwVfEkHcFxF48z48rIejupa0ui3O8V2GaIn0w2LJ5VfBezqi8AilMlEJirbCSHraU7wJarqhGqKgQrDElRD4dG0NfEVgUJbg7D9rY8FvjBcRmzC9p4SS5LrSsbm2SrD5WS1AN1NKUyQSYX2sTFoiWb2xO6U/GCiXmR+xCqhkNR+eQlWBqZKb4ctEBkx/1XzvR2i44JVP7AfPm82hUkDKgsPY4QhP1Gm4CRe94p4R5kYKaydTpQTsJlsJNxkd5Fy4W4YxjCcW9Gcm1HT/OoyxcwtXUTAN7MaQwQKb6wQZME19nDqChgqXQgWYBSOtlouR7BvtyHQmZrRLxpoLeGq9xIqdwXh161d58AGbm6omWQ/VewEjZmJbcXoxpyMoIX6Hl31UwSbgQGhbwI+l4s8Eca+xSBjO9FH2oTQaOhMAmhkAekCd5wLGZAE5eplaGphuVXC+1zW2KbFfOghX+jhxaJIYNYKr8X15aq24BS7YsEq3KFpovHm3UyZr42KKGBOHUiFre5mjB6Sg0S+4gaOcCcvB+DftQToHRnMz+DCESn42lbO9MyNjtBQn1rVriaznaDE7O6uNE044W704AoT83IkokLofCZOBEba9as8N+K077E/P1yZ1sE/SF2bl8p6qpAsq07/vQi+1E4wLc488V22LoYOHM2jwa6vF+7E4KroI6CvxULmSka0FW1lT0YWejFajdEcoyUr/LAvCYwc3BX0gEHWhIcNj6QJYYKCeI7OGVJA8wWII+JLUk289uJPgRwuKImnPZrB5OIvyOy6FVTu/2gLdeJHzIXoL/BlfloY0wwzP3GjuyCi+p04rxWkr1h1QVQGw2hiZ1SEnkcZHqRuAYLTSZIIJCUgJCJ2rGY+zpE2TxULit79VVD1Pp61E5BTN0Z8RmIzJZovkM2ea+heOG2uAxQvSKB/3uXTK7IgFIwWmLKCopTXMasVnWzT7wGJCqin3ebkyKSgHWx7L0nDjPJUMemyXBh2qG2Q7EiTVA8OWa7bN26BVPH4Rswf12WpYGjhUNqssuiAag2fgFY1NyLvguoB/k0dAsi9HMVBmasfaaSW+mVjrIFrCm2CGi7eUGln95Zw8WZna6jzwmykseV66iLnWFCJrGb9Hm4wMbBT6t4ShKab5+zr/q4XTtEuRzHyEFYDlkrU/IJYSRs7Y8V1KdZabBbrpQzcMSKeoScPngKSZeSxwAYfeoc5eWWJ7K58z6hTZUXvYCbQU9Lz3CRm+8Uxr+DHyN7epFJ+Vt396u3jngFCWUK06l8mihc1TOHl7uT4l67XptjWNhgJ/C2dYtDJfwrqIm13p05fpEYf9sC0RQF+pRVy06nYLR7nSEQGR+1BzeXMDxc9+qH8avJwdB1XggZBbMFMCeQempRXCMiWkAJeBomf4ALyhGakPCMz6xW0H88gl5zXushs79zYB2kD7EqLrVT8cLBCqSLzBdU+AusWZFbWd0jD8al6fi8x9ZikKE3uj3Ei71XuzUQ1ZpEGzfHlT22+El7Y2gmuhHBUczDmWlFky+B2vkVbConq+zID2NaB146HBuTPFsikAmjakdZlhJxJrIRCYc1olU25DBUAZiUMBFnH47KMHxJJ4Ypnm8Ue2uZuGyA3oFCyLjGmgu6LY0yc4or+DoXxWzSe9xVY36dh2gHITqjIYQvAlggQB7bq7uvnrpw2AauaXdIUG908+sB6DfR4jmc29X75YAhy1UhrnB0Xiox4TdhfDqr45Wo9U3j2LrMgv3SLXjtqisG0BWW58QZ/5t1z7C9+Pu3SZTl8kDUrP8h3gVafTTbKvViS869LCVgq8TC1lW1gDd5nhf1q9ISJitXM/AgB7MDPfQOGjEjAQMA4GA1UdDwEB/wQEAwIFIDALBglghkgBZQMEAxIDggzuAASTk297i0ii4Kws/lLQblALubul51ct21SoXqzhXQKGLTT3Fz4cigbvctED/8xPfJHTntsmTBYSsJ5uthlF/RS3ifO0FopmZxK7rc5BJsyuQ8q+1Nt9/lFO7i8lZtuxRsSuRYrSRSmoSaTWMxZmmw0gXJwHQJf5a491yL3lBWtjOhjvdh4jbp9sAc2Z5HRNomQKX7JsmgtNl1kvmWubKqZD0XzRIwz/3Vf1NizhQg5KWK4C3qPOwHrkv4aa5CqXbkfTIi3jgh0VTcBGQsUu0yEN8/OnP7WZD4me21vTmhT7FAy0vg2t7yDGpt8S5Jpcrcfvz9pmIONKJT3sIXNtS45mqOMa58zvTxw8PPY9ALRGXgFwMW2Mwm1Zo+y8PVWIWT/KYb70anCLb6dFgID6saNa4aOgL73cC4I1/cr3dvUWFBMA/Lus1As0uNVLhKeWE1m2S5+4Wl9vSvFqZvJfzxhu1TVcxhvDlBZoJJpjPivKgDzP1iU3dFBazcGVKQSSIMSNjoJGtJOKYQmJ1vudGPHZ1WteYK4092trwbPuVTl6jHlDSypczrBsdUppADC9XB+SfpiRYM+CyXL1AAxMKurhdlW2VSQtwxA7zmYjghja5GO77kEtnOXIBdLhzW2uyFECPvtpB02D0JFIH0CY8WsxVIIFcSiZOpNQ+bHNDRvXqlxpfdgv+UHqIoQ9So1jrF1OtH/E8e+/J0Yv3d1vz7jXJQPetaS8qQyYYe0jjqOTs/wNsCudozYo9FNCFkbaPZhPe0xwCYcr7Jx2kW1avU5Ah4mjnsJRJvCX1ew06aseYvBs51QsWXc3YXWLluaH/XQUeDjudJdbvW1i02ke3O8vsjQAXiHTjTzRuIlZKqHUtv4paPUL4mCiqBKGrwRXnYTlRbvcGRejW/7IXqYUwXDEgzApaMVO7AGtFxpvZTMJhoc/+tMfqcCMRdKAz40CJbgq6APeD0gAhkji078SYuaAZ0vy/Y89xbpt5Ma2BK11aFg0vpL4ZRP9E546oI2Fh96iiPI2Y9z9TWDORDCsbUVKY0zPxWAbHiYlw4AxHd/XxRPloj3AGYE2TwH9pkwln9rQ5Ywj7nTaCpXJ4jg2ERz6B+of9PT8A46G3buQVggGpa52jnXoj4yzTSIjLuWMaagKYCwQx7oykvEDvaWYa9I9PuHCjujv4suN+04/GwYDkMQUhdErztQPyknEK74i05JSBrSP1FuJc3Zke+pwPLJXL3SgrLvB0kLbWY2GQzKgM506aTfAtS0P+moLZzYqvJk1SrqG59l3pW6JAv5x9O+ENZZxjJ/mHRfaXQsn3Mkwn1lUm7MO+Mg6op+InVR6PESIA5UqQPsCmMyWhl7pOhzoC2rDJgv2RfUoplE63d6bKuEb0gUjdZPtXtfRrt3Zs3X+zTjhBmoQcJ6GjAoXfhUs+R2ueuB5Go6dCgd3cW+MakUcpAcLW6iz5PmYPpZlAJzb1N9Vt3X2YgMwd+FBLsKIo7njVcwj47btmiLpooZMFgXkSCnp7xlCOEV+u7O38aT0F/W0kDjatp3wM0cESoARopN/yl+Y9g9tR/hBOutvYh6Rxw+sXR45ujNc7FfDcEWLQQpwEwpnA+eTHnbcWLzsWgJcHNHRJSxND2aZ4k1sDE4jjwaHnO2nHqBgTgByXvZTqSTTUyQSKVBEY/C+hiIet97uiRPFNtASj4gvI/AGHAJ+8f+gO/2FrvJrO6P2ciwjkg8m+1CAc344Znh3coPQaxgMdrR8nrlIzfy2j5CbFJna7xDwwfymPMF+hT0nOIahfJvTcbYuaVJ81mt6QG1X6LJ07/3P8bsqdJuKw790AN2zRd8s0LX9TfFH4qGHSXScfekjcKt0SbH/ap4RoLGUUFTVIyH/35e76iilETtnY1sDwD/86C/K9Oqhar/SFWmelXXSf/+EIsWoWobfTI5GS2tWh58dYfGYQHrdT/oQ1wskJ1SwSvrgv1LpCbAaEmPPgV9TAuemYKC08qNTP2gUU/rMtvRez+NNWWaU/tHlcjK6uygac0i/aEfrc5qXH+N6WYyhTMa6JZ6ThWAzkieICrv9TVspyLiZupPGi8zoLzGpFnIlW80TC8K35SZb3pKkPULlq9UR5bguPITkJdU3owHCiw4V/M/f9sfA31comBqmF+duuCNuCKUvJYrKyAKEtopRa5DqAqfTA0Jel1GGGBawodndNozhq26eRU0yIRL4w7nHDl0BbAE31f+tSLwBqUjHmAhMgpgMoZ8IMiqOh2/Vg3jlnW6dvhqm/MldOW/M/ZfTMQ16QdwRDgc5km8v8PBZ8ZxO3AVzCuV1Chi+cJSWmDS3Z8eWniSsj5X9VC31uPnp7AxRx5LiwelJJy9DAarZETuu9F7iBo9iEYTpng98px9xipr2f1C10M3n9Sa01WEcXvH33yU3m16Tdu9uBiuSNWjLaHiH3hYMfeTE/nugQ4C7QK4eRsNwU0sV1f6ABTbl/irJXtujO71sQRYyd2o74opfiHoC8thRoojXVyG3IwA0I2NnpGScMAvVAFhAmCM0knam4xyJxp2TaMMOy25gm/0TQSMzoBiepsfqbVvuVxDrnW+Q/y2z1A/Rwas9ukwSGSGaSVgp7SsYvOAADpyHwvDwNh7DJbSN69jM5u2OWKW6dek72YZb0cONzIS7hznDb3j5YbtactUOQsve09R1TgHj4DLnvkQzfoQjF6I4boxmYyNpewmAHN8UQqvgSwkfE7u38bK4gn7cZFaaCJtFE/IpelI1lH0Fe134jnMo+ktCvU39/Ix3N2mogA4XVOwtXqHkriFNCmc66zpRH5jw95sXGyqG4dXc1iidus9LDck/QF76uFO2fwQGrvIe5UwhWtKIoFSwZRmTr4DFPBNfZMraf9z2LbPhiC3q0qKDpJpEsK7jeKYDE7RDCz59zW6Vk5d8lmwdx16cQhEIg49HmfgMAKHp1lpqPP2ovUm0dcOZazMVNT30OCBN3VmWbKRg38BdsZp8qC7eOTwP8hdN32leiMGn0J2qOI6GHCzIv0FObvCnFKaeRzvn7ihrEQYmlfuebOpmkVqnAHPaM5WptAvZbwRXAnz5o+EqzH2Az4ULPf8kyBjTR95fBXzhG3t4INerN02jjFaUMYnVfuRMSNtmwqEO/rJ3T9V+e9/1R0DgGG3cO6JdSkT4KF0F1yRHZhnJ4UO2Ew7kFYa+owvfyUkIYyyXcwwQiWKaOQ+J2RDbk/TkJCGYiRKoYnV356nFyHx0efCPuX7GQlZMQhcMZfv8++pUhhie624PF3ytOx4KETo6bzDHUfLospCqnRbtSTuLoWmLGtCVdqrMoTfZFNiwpRSgt/1P6IiGXCyIDZlc77fYkXTPA+QEqa5b+odpejm+FkVpYnAvYcQWLMymE3HVnvy/UKvHd6wMvZbIy50rRNCIHR3ktOQRxU8/rsO6UpDDMI2rz18dEEKbUVA2l3PLA9QtUh9Dk2gFR6G/LohpGriVdY9EHilXA3Gl85qcnYTGgs7bhwouycst2wk3lYj5q6r+TDFnTVSGh4IiP1zpJmtN+DPsndJupUqTb+CgMfYX5BVwvxJ+slh6K7kguANvcwiGr4sDJtM2TWkVo7dWsKrGsJyJ3Knjp+Ae8FvV5qy4O+xRawqoUgtfOMiK9iBwqhUmqPz+7YNCgytKuoPSzwKb/7L/qBJ7Ts2RVlnz3rRskGoeSn26mWwS5TsHuCqurvA6Omvevs9LgMZJ7bL3Z+YLrfGPSCofcdThBgoZNnYHd8lW6GgvNueb8U6mN+WyfzjThTk4pt2PbeCRhHTHMWp2yicfplJ+TSPhH1tbVneADea27cV0hhQAWSl+OInYpEEAAbDAHJiC6txUTZyvjfF65jJXZ5mv+ZdZaWq2YrMAhBfgyYPtH+JdwMoXLrIAi7bkIi48MCyAw+ydAXGi+CdRxQSSPWsE2A3LRq1CatHnC9OM9Bq4UZU0JBFQ0v9LNkmNn/n6x6UpqSMywjwkVd30dGy+LvciY2p7U56mxbOjcrvWhrjN1Yq15PnyNA3NlmnFGw+NbEeyFBvtTDkXzvXzAcy+tw7xIBdR43ZF0+YNFmXCkk+1cTeHgKbI+eF+I1qEKM118ulPZafQV8JAxo/BtPzRclhQeWzzZtoGxcP7CJ2NxLI08JHcCKSE/Pj8x6b/QySUEw4fuOzYpKNc2yGwyxwi4qpLthlplPaoUkKRi/Oao+xiHuZ5TKaO2GyJj99NTv0ab36qmXlCrfaeyXnShMff/bF3evk0fP+Cm4Xa/bNN430O9hdT4WV40JVhTnAchsNUVVKn13XVN/mZxg7ByWcvV3LIECc6WqPBxtHjmZ2fvhBWcMnpBzI+YYOGh4nkAUlZgoOnsFGHAAAAAAAAAAAAAAAAAAAAAAAAAAkNEhsiJA==", + "u4YgUmpfvWLQPC3hn+pLKM2eWoKvO6jjAoxp66F2EJ7Gga3F5nttGEvskQK8TXckzon4VlZYKLXmfw29nDXnpTBQAgEBBEIB0OYX4ibRxumR3zjRFnT4dDrmuEMln3dnrSM31x+VldFf3tOd8dmMW8aovGPYNcHkc0m6/hnPR8dZVz9lfVG98IugBwYFK4EEACM=", + "MIGkAgEAMAoGCCsGAQUFBwZCBIGSu4YgUmpfvWLQPC3hn+pLKM2eWoKvO6jjAoxp66F2EJ7Gga3F5nttGEvskQK8TXckzon4VlZYKLXmfw29nDXnpTBQAgEBBEIB0OYX4ibRxumR3zjRFnT4dDrmuEMln3dnrSM31x+VldFf3tOd8dmMW8aovGPYNcHkc0m6/hnPR8dZVz9lfVG98IugBwYFK4EEACM=", + "RQdmu56wmwGr/NzeML6lkTnkJXaT8Rq/Q0bs5TtcLwSQppM6k6f82lX3hLp3zKRvnQCsXKiqKZXLaDtNqet9vSwdfi2VdJtqVuYA6LJ636+QK1oHxM6sKkdQHzFPv6+efCrxlrqQKe3Qw0YNxPGBXnSfRJ1PxsuBvnxOH7bKbn1hMOjRW80CAD+tOFSLK6KvjbBsuKwdkZgVmqKhygTPMg5Gf8OLYU7lV05On+014RJHxY5g9cjNBYti5Cmflu35sJ+oewbIbF92WJBVpApp7QsN01SK2yzp0O0PiA9LKTO3d/5qlpB+XRJYJunPGiIJLJ7Wwyt56OuHYVoeG7v7ES0K+pqFdE6N12iVyw4zySeQvd6jzn5Gdg2oNkpV9YyviykFXzsJPqcRrbxkNUt3TAj+2JJ+izhD2ibNcMAiFsIZFbo2YVygXfttX/dfuxDEiookE8NC9CrDL4DX+JolXV7Xysds7KW9xTXbNfFrKPF+T8fpY9PFYcNtOdHyK1kbi+U1AO+QbdU9A9A1Xgy+DtYX05asttYzsIrRusfkuaCR8B626aq09YjdccWHcst6SGJipARJfyXHyrE9K5ePWjtviNNv2Z3Ee1f6o0NfAO3vXzQeztckFrxR6Z/tgOYFGAqkT/BcoyL2nwWs6jMHlTJZRqAThhb8beLZ7MjXKlqecaP2PA/oa1BerEUZij4MkWRx+3ocJ+y62q71YIhJYDAdy+eYol2TbvYj6aEoTjmB6jlf5NuF+AFNNdjcFQWsK9czKey0168ApQzcOPek/4oH5bqbljrnyTKO1ZLQvxFepN0FrMST8LqLc5wDQaj0n+hWdG3c6qqYdQ+91agbv8lKLUpo7m+6iz6k3s7pzqDA/gY4r07Jypq2qxDkhWHTPdgnKHG5BQbjW0GQRfiQSvjGrhlbk/6ISK0Cu824jp5yZkS2R6zA1dE/wq39jV+L+hIQgCqCk6gaBRiQFq6XhAGGK8tqb+PWomZFGC60pQJhROoRdaYUimq3dlGXuFzL7kjEmPK7cUfUxo80vo97O1jrrE4ci3N81TzNhkYQo0oSSPk+aA0iL3kCRr+rI13EblS2jQLjjQzQSdkbVcIAKKQ+UQNxWdOhNsv4Q/C5oVbYLbvBRXR/SjQHl28a3JOdijXjj+SH1cwRcKeXDwpl2D6QvXCg70oMfWG/2KUJgzlU3t070bpOBKa8o8nnXc5Mq1AZaWtcGvdBuTfz8+0Gig3PLSAm3MYjdbX6JXzWP/HF0i2wI/w7gcHtq94O8/TqhT/I1YGqmRQvIsDtYU2LVndUJ4r/94sPHlox5XcUHsbP+quKn4IUaFb7XqXn0t1L9uCH5231qR3rUrFYn0Kr1KrmEY2b2m94qp4cNymZ30wT9s5Sm+q/PiyWszmkOfDjMW7qi2X/sUFrgfjVfpmNS4Mp3ArRLLXIxNWZgIuADMqHGv2SAyalrC/GYKzvrrx5zh868nITW9cy6s7sP2E8V1MOKvE3Uxx5p+YMr1MFiqbKCLtldj3sYqQBP5bPpsu+xXRu90knUTuTEBcWNLhQagzirmpcS50lj7y4m+J0EbsXZFPctE+RlHCpe/0pFw6L8qaxMZku6NFXRjJ5jQxyPQc4+YFbXzYFdV6jYCiPTUxXy6E+W+FYYb019puRWPV+Tor4qlEE/pO2egMAmb6K6N6BikKVNMqRgVCAP2WJxtiCn00JCSFnf8meQ0SxG/fuUqAPPbDi0L1l8Cl4YoVg04o3OvoX3cK/vFATn5OqfyFkYDYBnm3F5/PUJJM2d0ABQejvR33o2D1WQX5O+neqh8tsuX0RpoaygO3hXQ3a9c/3siReSL/SPXuPaTfdw8T1cwZx2rlWZhEXWtPKXDimxjrK7wRnjpISiImlRrh9LSPFqDeprK3a+pEp17zIqTEL4jgsa5MJrYoVvLbtXvBuRwEXZY00wH1u186eE3u9FujZsqrgpug0yXINAKniiwGGOVqzXWWSxYro9SQgFdTeth1EHUOVpNka/Ans1x+g/hAZQam5uXhisvaSRQRwQi6K5g2r4Cfbye6Ul0AG2c8SI63qpkwC37YH4qoCKXx6nl4EASFeXt/0mj77s0UiUo5U0j8aIw/lhqzC3SM9cb5juaNhpO5uNSKxVPp6o1b8i2/8+OYG4NSVt9JsfMwiXt/4fWvfAABCa00od7cCixHfIHr62+hWqjwxEQFQ9AwPiqn2Gh04lSO5Shyrc21IRBBVuMaMCxxepkFvqDQVx+Q4JyU1CmCb", + "h66EY21eGjvyrtSZ8a9tVDmBQvf2ENWVi1sss1ne9v4=" + ), + ]; + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.cs index 802c224a4179ef..c1eee8e4b89af3 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestData.cs @@ -7,8 +7,25 @@ namespace System.Security.Cryptography.Tests { - public static class CompositeMLKemTestData + public static partial class CompositeMLKemTestData { + internal static partial CompositeMLKemTestVector[] AllIetfVectors { get; } + + public static IEnumerable AllIetfVectorsTestData => + AllIetfVectors.Select(vector => new object[] { vector }); + + public static IEnumerable SupportedAlgorithmIetfVectorsTestData => + AllIetfVectors + .Where(vector => CompositeMLKem.IsAlgorithmSupported(vector.Algorithm)) + .Select(vector => new object[] { vector }); + + public static IEnumerable SupportedXDiffieHellmanIetfVectorsTestData => + AllIetfVectors + .Where(vector => + CompositeMLKem.IsAlgorithmSupported(vector.Algorithm) && + IsXDiffieHellman(vector.Algorithm)) + .Select(vector => new object[] { vector }); + internal static CompositeMLKemAlgorithm[] AllAlgorithms { get; } = [ CompositeMLKemAlgorithm.MLKem768WithRsaOaep2048, @@ -45,16 +62,20 @@ internal sealed class RsaAlgorithm(int keySizeInBits, int maxPublicKeySizeInByte internal int MaxPrivateKeySizeInBytes { get; } = maxPrivateKeySizeInBytes; } - internal sealed class ECDiffieHellmanAlgorithm(int keySizeInBits, int maxPrivateKeySizeInBytes) + internal sealed class ECDiffieHellmanAlgorithm(int keySizeInBits, int maxPrivateKeySizeInBytes, bool isSecg) { internal int KeySizeInBits { get; } = keySizeInBits; internal int MaxPrivateKeySizeInBytes { get; } = maxPrivateKeySizeInBytes; + + internal bool IsSecg { get; } = isSecg; } - internal sealed class XDiffieHellmanAlgorithm(int keySizeInBits) + internal sealed class XDiffieHellmanAlgorithm(int keySizeInBits, bool isX25519) { internal int KeySizeInBits { get; } = keySizeInBits; + + internal bool IsX25519 { get; } = isX25519; } internal static MLKemAlgorithm GetMLKemAlgorithm(CompositeMLKemAlgorithm algorithm) => @@ -75,14 +96,14 @@ internal static T ExecuteComponentFunc( "MLKEM768-RSA3072-SHA3-256" or "MLKEM1024-RSA3072-SHA3-256" => rsaFunc(new RsaAlgorithm(3072, 428, 1800)), "MLKEM768-RSA4096-SHA3-256" => rsaFunc(new RsaAlgorithm(4096, 556, 2381)), - "MLKEM768-ECDH-P256-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(256, 51)), + "MLKEM768-ECDH-P256-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(256, 51, isSecg: true)), "MLKEM768-ECDH-P384-SHA3-256" or - "MLKEM1024-ECDH-P384-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(384, 64)), - "MLKEM1024-ECDH-P521-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(521, 82)), - "MLKEM768-ECDH-brainpoolP256r1-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(256, 52)), - "MLKEM1024-ECDH-brainpoolP384r1-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(384, 68)), - "MLKEM768-X25519-SHA3-256" => xdhFunc(new XDiffieHellmanAlgorithm(32 * 8)), - "MLKEM1024-X448-SHA3-256" => xdhFunc(new XDiffieHellmanAlgorithm(56 * 8)), + "MLKEM1024-ECDH-P384-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(384, 64, isSecg: true)), + "MLKEM1024-ECDH-P521-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(521, 82, isSecg: true)), + "MLKEM768-ECDH-brainpoolP256r1-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(256, 52, isSecg: false)), + "MLKEM1024-ECDH-brainpoolP384r1-SHA3-256" => ecdhFunc(new ECDiffieHellmanAlgorithm(384, 68, isSecg: false)), + "MLKEM768-X25519-SHA3-256" => xdhFunc(new XDiffieHellmanAlgorithm(32 * 8, isX25519: true)), + "MLKEM1024-X448-SHA3-256" => xdhFunc(new XDiffieHellmanAlgorithm(56 * 8, isX25519: false)), _ => throw new XunitException($"Unsupported algorithm: {algorithm.Name}"), }; } @@ -128,14 +149,7 @@ internal static int ExpectedDecapsulationKeySizeUpperBound(CompositeMLKemAlgorit xdh => xdh.KeySizeInBits / 8); } - internal static int ExpectedCiphertextSize(CompositeMLKemAlgorithm algorithm) - { - return GetMLKemAlgorithm(algorithm).CiphertextSizeInBytes + - ExecuteComponentFunc( - algorithm, - rsa => rsa.KeySizeInBits / 8, // RSA-OAEP ciphertexts are the size of the modulus - ecdh => 1 + 2 * ((ecdh.KeySizeInBits + 7) / 8), - xdh => xdh.KeySizeInBits / 8); - } + internal static bool IsXDiffieHellman(CompositeMLKemAlgorithm algorithm) => + ExecuteComponentFunc(algorithm, rsa => false, ecdh => false, xdh => true); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestVector.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestVector.cs new file mode 100644 index 00000000000000..e18460f929b6bb --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestVector.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Formats.Asn1; + +namespace System.Security.Cryptography.Tests +{ + public sealed class CompositeMLKemTestVector + { + private readonly byte[] _encapsulationKey; + private readonly byte[] _decapsulationKey; + private readonly byte[] _pkcs8; + private readonly byte[] _ciphertext; + private readonly byte[] _sharedSecret; + private readonly byte[] _spki; + + internal string Id { get; } + internal CompositeMLKemAlgorithm Algorithm { get; } + internal ReadOnlySpan EncapsulationKey => _encapsulationKey; + internal ReadOnlySpan DecapsulationKey => _decapsulationKey; + internal ReadOnlySpan Pkcs8 => _pkcs8; + internal ReadOnlySpan Ciphertext => _ciphertext; + internal ReadOnlySpan SharedSecret => _sharedSecret; + internal ReadOnlySpan Spki => _spki; + + internal CompositeMLKemTestVector( + string id, + CompositeMLKemAlgorithm algorithm, + string encapsulationKey, + string certificate, + string decapsulationKey, + string pkcs8, + string ciphertext, + string sharedSecret) + { + Id = id; + Algorithm = algorithm; + _encapsulationKey = Convert.FromBase64String(encapsulationKey); + _decapsulationKey = Convert.FromBase64String(decapsulationKey); + _pkcs8 = Convert.FromBase64String(pkcs8); + _ciphertext = Convert.FromBase64String(ciphertext); + _sharedSecret = Convert.FromBase64String(sharedSecret); + + AsnReader reader = new AsnReader(Convert.FromBase64String(certificate), AsnEncodingRules.DER); + AsnReader certificateReader = reader.ReadSequence(); + AsnReader tbsCertificate = certificateReader.ReadSequence(); + tbsCertificate.ReadEncodedValue(); // Version + tbsCertificate.ReadEncodedValue(); // Serial number + tbsCertificate.ReadEncodedValue(); // Signature + tbsCertificate.ReadEncodedValue(); // Issuer + tbsCertificate.ReadEncodedValue(); // Validity + tbsCertificate.ReadEncodedValue(); // Subject + _spki = tbsCertificate.ReadEncodedValue().ToArray(); + } + + public override string ToString() => Id; + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestsBase.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestsBase.cs new file mode 100644 index 00000000000000..c18620b0532754 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLKem/CompositeMLKemTestsBase.cs @@ -0,0 +1,803 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Formats.Asn1; +using System.Linq; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + public abstract class CompositeMLKemTestsBase + { + protected abstract CompositeMLKem GenerateKey(CompositeMLKemAlgorithm algorithm); + protected abstract CompositeMLKem ImportDecapsulationKey(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source); + protected abstract CompositeMLKem ImportEncapsulationKey(CompositeMLKemAlgorithm algorithm, ReadOnlySpan source); + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void AlgorithmMatches_GenerateKey(CompositeMLKemAlgorithm algorithm) + { + using (CompositeMLKem kem = GenerateKey(algorithm)) + { + Assert.Equal(algorithm, kem.Algorithm); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Generate_Encapsulate_Decapsulate(CompositeMLKemAlgorithm algorithm) + { + byte[] ct; + byte[] expectedSs; + + byte[] encapsKeyBytes; + byte[] decapsKeyBytes; + + using (CompositeMLKem generatedKem = GenerateKey(algorithm)) + { + generatedKem.Encapsulate(out ct, out expectedSs); + byte[] ss = generatedKem.Decapsulate(ct); + + AssertExtensions.SequenceEqual(expectedSs, ss); + + encapsKeyBytes = generatedKem.ExportEncapsulationKey(); + decapsKeyBytes = generatedKem.ExportDecapsulationKey(); + } + + ct = null; + expectedSs = null; + + using (CompositeMLKem encapsKey = ImportEncapsulationKey(algorithm, encapsKeyBytes)) + { + encapsKey.Encapsulate(out ct, out expectedSs); + } + + using (CompositeMLKem decapsKey = ImportDecapsulationKey(algorithm, decapsKeyBytes)) + { + byte[] ss = decapsKey.Decapsulate(ct); + + AssertExtensions.SequenceEqual(expectedSs, ss); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_Encapsulate_Decapsulate(CompositeMLKemTestVector vector) + { + using (CompositeMLKem encapsKey = ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)) + using (CompositeMLKem decapsKey = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + encapsKey.Encapsulate(out byte[] ct, out byte[] expectedSs); + byte[] ss = decapsKey.Decapsulate(ct); + AssertExtensions.SequenceEqual(expectedSs,ss); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_EncapsulationKey_Encapsulate_Decapsulate(CompositeMLKemTestVector vector) + { + using (CompositeMLKem encapsKey = ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)) + { + encapsKey.Encapsulate(out byte[] ct, out byte[] expectedSs); + AssertExtensions.Throws(() => encapsKey.Decapsulate(ct)); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_DecapsulationKey_Encapsulate_Decapsulate(CompositeMLKemTestVector vector) + { + using (CompositeMLKem decapsKey = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + decapsKey.Encapsulate(out byte[] ct, out byte[] expectedSs); + byte[] ss = decapsKey.Decapsulate(ct); + AssertExtensions.SequenceEqual(expectedSs, ss); + + decapsKey.Decapsulate(vector.Ciphertext, ss); + AssertExtensions.SequenceEqual(vector.SharedSecret, ss); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_DecapsulationKey_AlgorithmMatches(CompositeMLKemTestVector vector) + { + using (CompositeMLKem decapsKey = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + Assert.Equal(vector.Algorithm, decapsKey.Algorithm); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_DecapsulationKey_Export(CompositeMLKemTestVector vector) + { + using (CompositeMLKem decapsKey = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(decapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => AssertExtensions.SequenceEqual(vector.DecapsulationKey, export(decapsKey))); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_EncapsulationKey_AlgorithmMatches(CompositeMLKemTestVector vector) + { + using (CompositeMLKem encapsKey = ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)) + { + Assert.Equal(vector.Algorithm, encapsKey.Algorithm); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_EncapsulationKey_Export(CompositeMLKemTestVector vector) + { + using (CompositeMLKem encapsKey = ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)) + { + CompositeMLKemTestHelpers.AssertExportEncapsulationKey( + export => AssertExtensions.SequenceEqual(vector.EncapsulationKey, export(encapsKey))); + + CompositeMLKemTestHelpers.AssertExportDecapsulationKey( + export => Assert.Throws(() => export(encapsKey))); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void Import_EncapsulationKey_Export_Spki(CompositeMLKemTestVector vector) + { + using (CompositeMLKem encapsKey = ImportEncapsulationKey(vector.Algorithm, vector.EncapsulationKey)) + { + // SPKI is DER so the exported SPKI must be identical to the reference SPKI. + CompositeMLKemTestHelpers.AssertExportSubjectPublicKeyInfo( + export => AssertExtensions.SequenceEqual(vector.Spki, export(encapsKey))); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void DifferentKey_DecapsulationRejection(CompositeMLKemAlgorithm algorithm) + { + using (CompositeMLKem one = GenerateKey(algorithm)) + using (CompositeMLKem two = GenerateKey(algorithm)) + { + one.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + AssertDecapsulationRejects(two, ciphertext, sharedSecret); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void DifferentTradKeyComponents_DecapsulationRejection(CompositeMLKemTestVector vector) + { + using (CompositeMLKem orig = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + byte[] otherDecapsKeyBytes; + byte[] otherEncapsKeyBytes; + + using (CompositeMLKem other = GenerateKey(vector.Algorithm)) + { + otherDecapsKeyBytes = other.ExportDecapsulationKey(); + otherEncapsKeyBytes = other.ExportEncapsulationKey(); + } + + // Make decaps key with original ML-KEM component and other traditional component. + byte[] origKeyBytes = vector.DecapsulationKey.ToArray(); + int tradKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + origKeyBytes.AsSpan().Slice(0, tradKeyOffset).CopyTo(otherDecapsKeyBytes); + + using (CompositeMLKem other = ImportDecapsulationKey(vector.Algorithm, otherDecapsKeyBytes)) + { + orig.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + AssertDecapsulationRejects(other, ciphertext, sharedSecret); + + other.Encapsulate(out ciphertext, out sharedSecret); + AssertDecapsulationRejects(orig, ciphertext, sharedSecret); + } + + // Now the encaps key + origKeyBytes = vector.EncapsulationKey.ToArray(); + tradKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + origKeyBytes.AsSpan().Slice(0, tradKeyOffset).CopyTo(otherEncapsKeyBytes); + + using (CompositeMLKem other = ImportEncapsulationKey(vector.Algorithm, otherEncapsKeyBytes)) + { + other.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + AssertDecapsulationRejects(orig, ciphertext, sharedSecret); + } + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void DifferentMLKemComponents_DecapsulationRejection(CompositeMLKemTestVector vector) + { + using (CompositeMLKem orig = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + byte[] otherDecapsKeyBytes; + byte[] otherEncapsKeyBytes; + + using (CompositeMLKem other = GenerateKey(vector.Algorithm)) + { + otherDecapsKeyBytes = other.ExportDecapsulationKey(); + otherEncapsKeyBytes = other.ExportEncapsulationKey(); + } + + // Make decaps key with original TradKey component and other ML-KEM component. + byte[] testKeyBytes = vector.DecapsulationKey.ToArray(); + int tradKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + otherDecapsKeyBytes.AsSpan().Slice(0, tradKeyOffset).CopyTo(testKeyBytes); + + using (CompositeMLKem other = ImportDecapsulationKey(vector.Algorithm, testKeyBytes)) + { + orig.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + AssertDecapsulationRejects(other, ciphertext, sharedSecret); + + other.Encapsulate(out ciphertext, out sharedSecret); + AssertDecapsulationRejects(orig, ciphertext, sharedSecret); + } + + // Now the encaps key + testKeyBytes = vector.EncapsulationKey.ToArray(); + tradKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + otherEncapsKeyBytes.AsSpan().Slice(0, tradKeyOffset).CopyTo(testKeyBytes); + + using (CompositeMLKem other = ImportEncapsulationKey(vector.Algorithm, testKeyBytes)) + { + other.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + AssertDecapsulationRejects(orig, ciphertext, sharedSecret); + } + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void ModifiedMLKemCiphertext_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] ciphertext = vector.Ciphertext.ToArray(); + ciphertext[0] ^= 1; + + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + AssertDecapsulationRejects(kem, ciphertext, vector.SharedSecret); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void ModifiedTraditionalCiphertext_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] ciphertext = vector.Ciphertext.ToArray(); + ciphertext[^1] ^= 1; + + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + AssertDecapsulationRejects(kem, ciphertext, vector.SharedSecret); + } + } + + public static IEnumerable SupportedRsaIetfVectorsTestData => + CompositeMLKemTestData.AllIetfVectors + .Where(vector => + CompositeMLKem.IsAlgorithmSupported(vector.Algorithm) && + CompositeMLKemTestData.ExecuteComponentFunc( + vector.Algorithm, + rsa => true, + ecdh => false, + xdh => false)) + .Select(vector => new object[] { vector }); + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaUndersizedSharedSecret_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] ciphertext = CreateRsaCiphertextWithSharedSecretSize(vector, sharedSecretSizeInBytes: 31); + + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + AssertDecapsulationRejects(kem, ciphertext, vector.SharedSecret); + } + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaOversizedSharedSecret_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] ciphertext = CreateRsaCiphertextWithSharedSecretSize(vector, sharedSecretSizeInBytes: 33); + + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + AssertDecapsulationRejects(kem, ciphertext, vector.SharedSecret); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedAlgorithmIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void ModifiedMLKemDecapsulationKey_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] decapsulationKey = vector.DecapsulationKey.ToArray(); + decapsulationKey[0] ^= 1; + + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, decapsulationKey)) + { + AssertDecapsulationRejects(kem, vector.Ciphertext, vector.SharedSecret); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedXDiffieHellmanIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void ModifiedXDiffieHellmanEncapsulationKey_Failure(CompositeMLKemTestVector vector) + { + byte[] encapsulationKeyBytes = vector.EncapsulationKey.ToArray(); + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + encapsulationKeyBytes[traditionalKeyOffset] ^= 1; + + CompositeMLKem tamperedEncapsulationKey; + + try + { + tamperedEncapsulationKey = ImportEncapsulationKey(vector.Algorithm, encapsulationKeyBytes); + } + catch (CryptographicException) + { + // Some providers notice the tampering and throw immediately. + return; + } + + // Other providers allow import, but decapsulation must result in rejection. + + using (tamperedEncapsulationKey) + using (CompositeMLKem decapsulationKey = ImportDecapsulationKey(vector.Algorithm, vector.DecapsulationKey)) + { + tamperedEncapsulationKey.Encapsulate(out byte[] ciphertext, out byte[] sharedSecret); + + AssertDecapsulationRejects(decapsulationKey, ciphertext, sharedSecret); + } + } + + [Theory] + [MemberData(nameof(CompositeMLKemTestData.SupportedXDiffieHellmanIetfVectorsTestData), MemberType = typeof(CompositeMLKemTestData))] + public void ModifiedXDiffieHellmanDecapsulationKey_DecapsulationRejection(CompositeMLKemTestVector vector) + { + byte[] decapsulationKey = vector.DecapsulationKey.ToArray(); + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + decapsulationKey[traditionalKeyOffset + 1] ^= 1; + + // Tampering doesn't cause import failures, but should cause decapsulation rejection. + using (CompositeMLKem kem = ImportDecapsulationKey(vector.Algorithm, decapsulationKey)) + { + AssertDecapsulationRejects(kem, vector.Ciphertext, vector.SharedSecret); + } + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void InvalidRsaEncoding_ImportEncapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] encapsulationKey = vector.EncapsulationKey.ToArray(); + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + encapsulationKey.AsSpan(traditionalKeyOffset).Clear(); + + AssertCorrectlySizedEncapsulationKeyImportFails(vector.Algorithm, encapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaWrongKeySize_ImportEncapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] encapsulationKey = CreateCompositeRsaKeyWithWrongSize(vector, includePrivateKey: false); + + AssertCorrectlySizedEncapsulationKeyImportFails(vector.Algorithm, encapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaTrailingData_ImportEncapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] encapsulationKey = new byte[vector.EncapsulationKey.Length + 1]; + vector.EncapsulationKey.CopyTo(encapsulationKey); + + AssertCorrectlySizedEncapsulationKeyImportFails(vector.Algorithm, encapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaBerEncoding_ImportEncapsulationKeyFailure(CompositeMLKemTestVector vector) + { + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + byte[] rsaPublicKey = AsnUtils.ConvertDerToNonDerBer(vector.EncapsulationKey.Slice(traditionalKeyOffset)); + byte[] encapsulationKey = ChangeTraditionalEncapsulationKeyComponent(vector, rsaPublicKey); + + AssertCorrectlySizedEncapsulationKeyImportFails(vector.Algorithm, encapsulationKey); + } + + public static IEnumerable SupportedECDiffieHellmanIetfVectorsTestData => + CompositeMLKemTestData.AllIetfVectors + .Where(vector => + CompositeMLKem.IsAlgorithmSupported(vector.Algorithm) && + CompositeMLKemTestData.ExecuteComponentFunc( + vector.Algorithm, + rsa => false, + ecdh => true, + xdh => false)) + .Select(vector => new object[] { vector }); + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanInvalidPoint_ImportEncapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] encapsulationKey = vector.EncapsulationKey.ToArray(); + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + encapsulationKey[traditionalKeyOffset] = 0; + + AssertCorrectlySizedEncapsulationKeyImportFails(vector.Algorithm, encapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaInvalidEncoding_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] decapsulationKey = vector.DecapsulationKey.ToArray(); + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + decapsulationKey.AsSpan(traditionalKeyOffset).Clear(); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaWrongKeySize_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] decapsulationKey = CreateCompositeRsaKeyWithWrongSize(vector, includePrivateKey: true); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaInvalidParameterP_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + byte[] invalidRsaPrivateKey = CreateRsaPrivateKeyWithTamperedParameterP(vector.DecapsulationKey.Slice(traditionalKeyOffset)); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, invalidRsaPrivateKey); + + // Make sure it's correctly sized so it won't trigger early validation failure + Assert.InRange( + decapsulationKey.Length, + CompositeMLKemTestData.ExpectedDecapsulationKeySizeLowerBound(vector.Algorithm), + CompositeMLKemTestData.ExpectedDecapsulationKeySizeUpperBound(vector.Algorithm)); + + Action test = () => ImportDecapsulationKey(vector.Algorithm, decapsulationKey); + + if (PlatformDetection.IsOpenSslSupported) + { + // OpenSSL has a custom message for this. + CryptographicException ex = Assert.ThrowsAny(test); + Assert.Contains("n does not equal p q", ex.Message); + } + else + { + Assert.Throws(test); + } + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaTrailingData_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] decapsulationKey = new byte[vector.DecapsulationKey.Length + 1]; + vector.DecapsulationKey.CopyTo(decapsulationKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedRsaIetfVectorsTestData))] + public void RsaBerEncoding_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + byte[] rsaPrivateKey = AsnUtils.ConvertDerToNonDerBer(vector.DecapsulationKey.Slice(traditionalKeyOffset)); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, rsaPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanInvalidVersion_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] ecPrivateKey = EncodeCorrectlySizedEcPrivateKey(vector.Algorithm, version: 0, GetECDiffieHellmanCurveOid(vector.Algorithm)); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, ecPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanPublicKeyField_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] ecPrivateKey = EncodeCorrectlySizedEcPrivateKey(vector.Algorithm, version: 1, curveOid: null, publicKey: [0x04]); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, ecPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanMissingParameters_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] ecPrivateKey = EncodeCorrectlySizedEcPrivateKey(vector.Algorithm, version: 1, curveOid: null); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, ecPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanWrongCurve_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + string curveOid = GetECDiffieHellmanCurveOid(vector.Algorithm); + string wrongCurveOid = + curveOid == ECCurve.NamedCurves.nistP256.Oid.Value ? + ECCurve.NamedCurves.nistP384.Oid.Value! : + ECCurve.NamedCurves.nistP256.Oid.Value!; + byte[] ecPrivateKey = EncodeCorrectlySizedEcPrivateKey(vector.Algorithm, version: 1, wrongCurveOid); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, ecPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + [Theory] + [MemberData(nameof(SupportedECDiffieHellmanIetfVectorsTestData))] + public void ECDiffieHellmanBerEncoding_ImportDecapsulationKeyFailure(CompositeMLKemTestVector vector) + { + byte[] ecPrivateKey = EncodeBerEcPrivateKey(vector.Algorithm); + byte[] decapsulationKey = ChangeTraditionalDecapsulationKeyComponent(vector, ecPrivateKey); + + AssertCorrectlySizedDecapsulationKeyImportFails(vector.Algorithm, decapsulationKey); + } + + private static void AssertDecapsulationRejects( + CompositeMLKem kem, + ReadOnlySpan ciphertext, + ReadOnlySpan expectedSharedSecret) + { + byte[] sharedSecret = new byte[kem.Algorithm.SharedSecretSizeInBytes]; + sharedSecret.AsSpan().Fill(0xA5); + + try + { + kem.Decapsulate(ciphertext, sharedSecret); + + // Implicit rejection + AssertExtensions.FalseExpression(sharedSecret.AsSpan().SequenceEqual(expectedSharedSecret)); + } + catch (CryptographicException) + { + // Explicit rejection, must clear the shared secret + AssertExtensions.SequenceEqual(new byte[sharedSecret.Length], sharedSecret); + } + } + + private static byte[] CreateRsaCiphertextWithSharedSecretSize(CompositeMLKemTestVector vector, int sharedSecretSizeInBytes) + { + MLKemAlgorithm mlKemAlgorithm = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm); + ReadOnlySpan rsaPublicKey = vector.EncapsulationKey.Slice(mlKemAlgorithm.EncapsulationKeySizeInBytes); + + byte[] rsaCiphertext; + + using (RSA rsa = RSA.Create()) + { + rsa.ImportRSAPublicKey(rsaPublicKey, out int bytesRead); + Assert.Equal(rsaPublicKey.Length, bytesRead); + rsaCiphertext = rsa.Encrypt(new byte[sharedSecretSizeInBytes], RSAEncryptionPadding.OaepSHA256); + } + + byte[] ciphertext = new byte[mlKemAlgorithm.CiphertextSizeInBytes + rsaCiphertext.Length]; + vector.Ciphertext.Slice(0, mlKemAlgorithm.CiphertextSizeInBytes).CopyTo(ciphertext); + rsaCiphertext.CopyTo(ciphertext.AsSpan(mlKemAlgorithm.CiphertextSizeInBytes)); + return ciphertext; + } + + private static byte[] CreateCompositeRsaKeyWithWrongSize(CompositeMLKemTestVector vector, bool includePrivateKey) + { + const int KeySizeDifferenceInBits = 64; + + int expectedKeySizeInBits = + CompositeMLKemTestData.ExecuteComponentFunc( + vector.Algorithm, + rsa => rsa.KeySizeInBits, + ecdh => throw new Xunit.Sdk.XunitException("Expected an RSA algorithm."), + xdh => throw new Xunit.Sdk.XunitException("Expected an RSA algorithm.")); + int wrongKeySizeInBits = expectedKeySizeInBits - KeySizeDifferenceInBits; + + byte[] traditionalKey; + + using (RSA rsa = RSA.Create(wrongKeySizeInBits)) + { + traditionalKey = includePrivateKey ? rsa.ExportRSAPrivateKey() : rsa.ExportRSAPublicKey(); + } + + MLKemAlgorithm mlKemAlgorithm = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm); + ReadOnlySpan mlKemKey = + includePrivateKey ? + vector.DecapsulationKey.Slice(0, mlKemAlgorithm.PrivateSeedSizeInBytes) : + vector.EncapsulationKey.Slice(0, mlKemAlgorithm.EncapsulationKeySizeInBytes); + byte[] compositeKey = new byte[mlKemKey.Length + traditionalKey.Length]; + mlKemKey.CopyTo(compositeKey); + traditionalKey.CopyTo(compositeKey.AsSpan(mlKemKey.Length)); + + return compositeKey; + } + + private static byte[] CreateRsaPrivateKeyWithTamperedParameterP(ReadOnlySpan rsaPrivateKey) + { + RSAParameters parameters; + + using (RSA rsa = RSA.Create()) + { + rsa.ImportRSAPrivateKey(rsaPrivateKey, out int bytesRead); + Assert.Equal(rsaPrivateKey.Length, bytesRead); + parameters = rsa.ExportParameters(includePrivateParameters: true); + } + + parameters.P[0] ^= 1; + + AsnWriter writer = new(AsnEncodingRules.DER); + + using (writer.PushSequence()) + { + writer.WriteInteger(0); + writer.WriteIntegerUnsigned(parameters.Modulus); + writer.WriteIntegerUnsigned(parameters.Exponent); + writer.WriteIntegerUnsigned(parameters.D); + writer.WriteIntegerUnsigned(parameters.P); + writer.WriteIntegerUnsigned(parameters.Q); + writer.WriteIntegerUnsigned(parameters.DP); + writer.WriteIntegerUnsigned(parameters.DQ); + writer.WriteIntegerUnsigned(parameters.InverseQ); + } + + return writer.Encode(); + } + + private void AssertCorrectlySizedEncapsulationKeyImportFails(CompositeMLKemAlgorithm algorithm, byte[] encapsulationKey) + { + // Make sure it's correctly sized so it won't trigger early validation failure + Assert.InRange( + encapsulationKey.Length, + CompositeMLKemTestData.ExpectedEncapsulationKeySizeLowerBound(algorithm), + CompositeMLKemTestData.ExpectedEncapsulationKeySizeUpperBound(algorithm)); + + Assert.Throws(() => ImportEncapsulationKey(algorithm, encapsulationKey)); + } + + private void AssertCorrectlySizedDecapsulationKeyImportFails(CompositeMLKemAlgorithm algorithm, byte[] decapsulationKey) + { + // Make sure it's correctly sized so it won't trigger early validation failure + Assert.InRange( + decapsulationKey.Length, + CompositeMLKemTestData.ExpectedDecapsulationKeySizeLowerBound(algorithm), + CompositeMLKemTestData.ExpectedDecapsulationKeySizeUpperBound(algorithm)); + + Assert.Throws(() => ImportDecapsulationKey(algorithm, decapsulationKey)); + } + + private byte[] ChangeTraditionalEncapsulationKeyComponent(CompositeMLKemTestVector vector, byte[] tradKey) + { + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).EncapsulationKeySizeInBytes; + byte[] encapsulationKey = new byte[traditionalKeyOffset + tradKey.Length]; + vector.EncapsulationKey.Slice(0, traditionalKeyOffset).CopyTo(encapsulationKey); + tradKey.CopyTo(encapsulationKey.AsSpan(traditionalKeyOffset)); + return encapsulationKey; + } + + private byte[] ChangeTraditionalDecapsulationKeyComponent(CompositeMLKemTestVector vector, byte[] tradKey) + { + int traditionalKeyOffset = CompositeMLKemTestData.GetMLKemAlgorithm(vector.Algorithm).PrivateSeedSizeInBytes; + byte[] decapsulationKey = new byte[traditionalKeyOffset + tradKey.Length]; + vector.DecapsulationKey.Slice(0, traditionalKeyOffset).CopyTo(decapsulationKey); + tradKey.CopyTo(decapsulationKey.AsSpan(traditionalKeyOffset)); + return decapsulationKey; + } + + private static byte[] EncodeCorrectlySizedEcPrivateKey( + CompositeMLKemAlgorithm algorithm, + int version, + string? curveOid, + byte[]? publicKey = null) + { + int targetSize = GetECDiffieHellmanPrivateKeyEncodingSize(algorithm); + byte[] emptyPrivateKey = EncodeEcPrivateKey(version, privateKeySize: 0, curveOid, publicKey); + int privateKeySize = targetSize - emptyPrivateKey.Length; + AssertExtensions.GreaterThanOrEqualTo(privateKeySize, 0); + + byte[] key = EncodeEcPrivateKey(version, privateKeySize, curveOid, publicKey); + Assert.Equal(targetSize, key.Length); + return key; + } + + private static byte[] EncodeEcPrivateKey(int version, int privateKeySize, string? curveOid, byte[]? publicKey = null) + { + AsnWriter writer = new(AsnEncodingRules.DER); + + using (writer.PushSequence()) + { + writer.WriteInteger(version); + writer.WriteOctetString(new byte[privateKeySize]); + + if (curveOid is not null) + { + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true))) + { + writer.WriteObjectIdentifier(curveOid); + } + } + + if (publicKey is not null) + { + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true))) + { + writer.WriteBitString(publicKey); + } + } + } + + return writer.Encode(); + } + + private static byte[] EncodeBerEcPrivateKey(CompositeMLKemAlgorithm algorithm) + { + byte[] shortPrivateKey = EncodeEcPrivateKey( + version: 1, + privateKeySize: GetECDiffieHellmanPrivateKeySize(algorithm) - 1, + GetECDiffieHellmanCurveOid(algorithm)); + + Assert.Equal(GetECDiffieHellmanPrivateKeyEncodingSize(algorithm) - 1, shortPrivateKey.Length); + Assert.Equal(0x30, shortPrivateKey[0]); + Assert.Equal(0x02, shortPrivateKey[2]); + Assert.Equal(0x04, shortPrivateKey[5]); + + byte[] ecPrivateKey = new byte[shortPrivateKey.Length + 1]; + ecPrivateKey[0] = shortPrivateKey[0]; + ecPrivateKey[1] = checked((byte)(shortPrivateKey[1] + 1)); + shortPrivateKey.AsSpan(2, 4).CopyTo(ecPrivateKey.AsSpan(2)); + ecPrivateKey[6] = 0x81; + shortPrivateKey.AsSpan(6).CopyTo(ecPrivateKey.AsSpan(7)); + return ecPrivateKey; + } + + private static int GetECDiffieHellmanPrivateKeySize(CompositeMLKemAlgorithm algorithm) => + CompositeMLKemTestData.ExecuteComponentFunc( + algorithm, + rsa => throw new Xunit.Sdk.XunitException("Expected an ECDH algorithm."), + ecdh => (ecdh.KeySizeInBits + 7) / 8, + xdh => throw new Xunit.Sdk.XunitException("Expected an ECDH algorithm.")); + + private static int GetECDiffieHellmanPrivateKeyEncodingSize(CompositeMLKemAlgorithm algorithm) => + CompositeMLKemTestData.ExecuteComponentFunc( + algorithm, + rsa => throw new Xunit.Sdk.XunitException("Expected an ECDH algorithm."), + ecdh => ecdh.MaxPrivateKeySizeInBytes, + xdh => throw new Xunit.Sdk.XunitException("Expected an ECDH algorithm.")); + + private static string GetECDiffieHellmanCurveOid(CompositeMLKemAlgorithm algorithm) => + algorithm.Name switch + { + "MLKEM768-ECDH-P256-SHA3-256" => ECCurve.NamedCurves.nistP256.Oid.Value!, + "MLKEM768-ECDH-P384-SHA3-256" or + "MLKEM1024-ECDH-P384-SHA3-256" => ECCurve.NamedCurves.nistP384.Oid.Value!, + "MLKEM768-ECDH-brainpoolP256r1-SHA3-256" => "1.3.36.3.3.2.8.1.1.7", + "MLKEM1024-ECDH-brainpoolP384r1-SHA3-256" => "1.3.36.3.3.2.8.1.1.11", + "MLKEM1024-ECDH-P521-SHA3-256" => ECCurve.NamedCurves.nistP521.Oid.Value!, + _ => throw new Xunit.Sdk.XunitException($"Expected an ECDH algorithm, got '{algorithm.Name}'."), + }; + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AsnUtils.cs b/src/libraries/Common/tests/System/Security/Cryptography/AsnUtils.cs index 84198a7b89bc68..43b6df57f68fec 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AsnUtils.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AsnUtils.cs @@ -82,7 +82,7 @@ static HashAlgorithmName GetHashAlgorithmFromPbkdf2Params(in ValuePbkdf2Params p } } - internal static byte[] ConvertDerToNonDerBer(byte[] derBytes) + internal static byte[] ConvertDerToNonDerBer(ReadOnlySpan derBytes) { // Convert a valid DER encoding to BER by making the length octets of the first value non-minimal. byte[] berBytes = new byte[derBytes.Length + 1]; @@ -101,7 +101,7 @@ internal static byte[] ConvertDerToNonDerBer(byte[] derBytes) } // Copy the tag - derBytes.AsSpan(0, index + 1).CopyTo(berBytes); + derBytes.Slice(0, index + 1).CopyTo(berBytes); // Advance to the length index++; @@ -112,7 +112,7 @@ internal static byte[] ConvertDerToNonDerBer(byte[] derBytes) // Short form, so just make it long form by adding the length length berBytes[index] = 0x80 | 1; - derBytes.AsSpan(index).CopyTo(berBytes.AsSpan(index + 1)); + derBytes.Slice(index).CopyTo(berBytes.AsSpan(index + 1)); } else { @@ -126,7 +126,7 @@ internal static byte[] ConvertDerToNonDerBer(byte[] derBytes) berBytes[index] = lengthLength; berBytes[index + 1] = 0x00; - derBytes.AsSpan(index + 1).CopyTo(berBytes.AsSpan(index + 2)); + derBytes.Slice(index + 1).CopyTo(berBytes.AsSpan(index + 2)); } return berBytes; diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index 42ee98006c8ece..9092e17f128157 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -236,6 +236,10 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\CompositeMLKem\CompositeMLKemMockImplementation.cs" /> + + The specified CipherMode '{0}' is not supported. + + RSA-OAEP decapsulation failed to produce a shared secret of the expected length. + Composite signature generation failed due to an error in one or both of the components. diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 56055d5928146a..ae83255b308b6b 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -1058,6 +1058,14 @@ Link="Common\System\Security\Cryptography\CompositeMLDsaManaged.ECDsa.cs" /> + + + + - + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CompositeMLKemImplementation.Managed.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CompositeMLKemImplementation.Managed.cs new file mode 100644 index 00000000000000..ced8d9563f4de7 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CompositeMLKemImplementation.Managed.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ + internal sealed partial class CompositeMLKemImplementation : CompositeMLKem + { + private CompositeMLKemImplementation(CompositeMLKemAlgorithm algorithm) + : base(algorithm) + { + throw new PlatformNotSupportedException(); + } + + internal static partial bool IsAlgorithmSupportedImpl(CompositeMLKemAlgorithm algorithm) => + CompositeMLKemManaged.IsAlgorithmSupportedImpl(algorithm); + + internal static partial CompositeMLKem GenerateKeyImpl(CompositeMLKemAlgorithm algorithm) => + CompositeMLKemManaged.GenerateKeyImpl(algorithm); + + internal static partial CompositeMLKem ImportEncapsulationKeyImpl( + CompositeMLKemAlgorithm algorithm, + ReadOnlySpan source) + { + return CompositeMLKemManaged.ImportEncapsulationKeyImpl(algorithm, source); + } + + internal static partial CompositeMLKem ImportDecapsulationKeyImpl( + CompositeMLKemAlgorithm algorithm, + ReadOnlySpan source) + { + return CompositeMLKemManaged.ImportDecapsulationKeyImpl(algorithm, source); + } + + protected override void EncapsulateCore(Span ciphertext, Span sharedSecret) => + throw new PlatformNotSupportedException(); + + protected override void DecapsulateCore(ReadOnlySpan ciphertext, Span sharedSecret) => + throw new PlatformNotSupportedException(); + + protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(); + + protected override int ExportEncapsulationKeyCore(Span destination) => + throw new PlatformNotSupportedException(); + + protected override int ExportDecapsulationKeyCore(Span destination) => + throw new PlatformNotSupportedException(); + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemImplementation.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemImplementation.OpenSsl.cs index 0f493994eadb75..bafc99edd2276e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemImplementation.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemImplementation.OpenSsl.cs @@ -13,6 +13,27 @@ internal sealed partial class MLKemImplementation : MLKem // OpenSSL is expected to give "all or none" support. internal static new bool IsSupported => Interop.Crypto.EvpKemAlgs.MlKem512 is not null; + internal static bool IsAlgorithmSupported(MLKemAlgorithm algorithm) + { + if (algorithm == MLKemAlgorithm.MLKem512) + { + return Interop.Crypto.EvpKemAlgs.MlKem512 is not null; + } + else if (algorithm == MLKemAlgorithm.MLKem768) + { + return Interop.Crypto.EvpKemAlgs.MlKem768 is not null; + } + else if (algorithm == MLKemAlgorithm.MLKem1024) + { + return Interop.Crypto.EvpKemAlgs.MlKem1024 is not null; + } + else + { + Debug.Fail($"Unexpected algorithm: {algorithm}"); + return false; + } + } + private readonly bool _hasSeed; private readonly bool _hasDecapsulationKey; 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 730259ceaf51ab..a6d78e08436f95 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 @@ -318,12 +318,20 @@ Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\CompositeMLKem\CompositeMLKemContractTests.cs" /> + + + +