diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs b/src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs
index 112b95c5b01960..cfc3dd1d5682cd 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs
@@ -85,17 +85,15 @@ public void Dispose()
/// The data to sign.
///
///
- /// The buffer to receive the signature.
+ /// The buffer to receive the signature. Its length must be exactly
+ /// .
///
///
/// An optional context-specific value to limit the scope of the signature.
/// The default value is an empty buffer.
///
- ///
- /// The number of bytes written to the buffer.
- ///
///
- /// The buffer in is too small to hold the signature.
+ /// The buffer in is the incorrect length to receive the signature.
///
///
/// has a in excess of
@@ -109,8 +107,17 @@ public void Dispose()
/// -or-
/// An error occurred while signing the data.
///
- public int SignData(ReadOnlySpan data, Span destination, ReadOnlySpan context = default)
+ public void SignData(ReadOnlySpan data, Span destination, ReadOnlySpan context = default)
{
+ int signatureSizeInBytes = Algorithm.SignatureSizeInBytes;
+
+ if (destination.Length != signatureSizeInBytes)
+ {
+ throw new ArgumentException(
+ SR.Format(SR.Argument_DestinationImprecise, signatureSizeInBytes),
+ nameof(destination));
+ }
+
if (context.Length > MaxContextLength)
{
throw new ArgumentOutOfRangeException(
@@ -119,14 +126,47 @@ public int SignData(ReadOnlySpan data, Span destination, ReadOnlySpa
SR.Argument_SignatureContextTooLong255);
}
- if (destination.Length < Algorithm.SignatureSizeInBytes)
- {
- throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
- }
-
ThrowIfDisposed();
- SignDataCore(data, context, destination.Slice(0, Algorithm.SignatureSizeInBytes));
- return Algorithm.SignatureSizeInBytes;
+ SignDataCore(data, context, destination);
+ }
+
+ ///
+ /// Signs the specified data.
+ ///
+ ///
+ /// The data to sign.
+ ///
+ ///
+ /// An optional context-specific value to limit the scope of the signature.
+ /// The default value is .
+ ///
+ ///
+ /// ML-DSA signature for the specified data.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// has a length in excess of 255 bytes.
+ ///
+ ///
+ /// This instance has been disposed.
+ ///
+ ///
+ /// The instance represents only a public key.
+ /// -or-
+ /// An error occurred while signing the data.
+ ///
+ ///
+ /// A context is treated as empty.
+ ///
+ public byte[] SignData(byte[] data, byte[]? context = default)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+
+ byte[] destination = new byte[Algorithm.SignatureSizeInBytes];
+ SignData(new ReadOnlySpan(data), destination.AsSpan(), new ReadOnlySpan(context));
+ return destination;
}
// TODO: SignPreHash
@@ -179,6 +219,45 @@ public bool VerifyData(ReadOnlySpan data, ReadOnlySpan signature, Re
return VerifyDataCore(data, context, signature);
}
+ ///
+ /// Verifies that the specified signature is valid for this key and the provided data.
+ ///
+ ///
+ /// The data to verify.
+ ///
+ ///
+ /// The signature to verify.
+ ///
+ ///
+ /// The context value which was provided during signing.
+ /// The default value is .
+ ///
+ ///
+ /// if the signature validates the data; otherwise, .
+ ///
+ ///
+ /// or is .
+ ///
+ ///
+ /// has a length in excess of 255 bytes.
+ ///
+ ///
+ /// This instance has been disposed.
+ ///
+ ///
+ /// An error occurred while verifying the data.
+ ///
+ ///
+ /// A context is treated as empty.
+ ///
+ public bool VerifyData(byte[] data, byte[] signature, byte[]? context = default)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+ ArgumentNullException.ThrowIfNull(signature);
+
+ return VerifyData(new ReadOnlySpan(data), new ReadOnlySpan(signature), new ReadOnlySpan(context));
+ }
+
// TODO: VerifyPreHash
///
@@ -701,79 +780,151 @@ public string ExportEncryptedPkcs8PrivateKeyPem(
///
/// Exports the public-key portion of the current key in the FIPS 204 public key format.
///
- ///
- /// The buffer to receive the public key.
- ///
///
- /// The number of bytes written to .
+ /// The FIPS 204 public key.
///
+ ///
+ /// An error occurred while exporting the key.
+ ///
+ /// The object has already been disposed.
+ public byte[] ExportMLDsaPublicKey()
+ {
+ ThrowIfDisposed();
+
+ byte[] destination = new byte[Algorithm.PublicKeySizeInBytes];
+ ExportMLDsaPublicKeyCore(destination);
+ return destination;
+ }
+
+ ///
+ /// Exports the public-key portion of the current key in the FIPS 204 public key format.
+ ///
+ ///
+ /// The buffer to receive the public key. Its length must be exactly
+ /// .
+ ///
///
- /// is too small to hold the public key.
+ /// is the incorrect length to receive the public key.
///
- public int ExportMLDsaPublicKey(Span destination)
+ ///
+ /// An error occurred while exporting the key.
+ ///
+ /// The object has already been disposed.
+ ///
+ /// is required to be exactly
+ /// in length.
+ ///
+ public void ExportMLDsaPublicKey(Span destination)
{
- if (destination.Length < Algorithm.PublicKeySizeInBytes)
+ int publicKeySizeInBytes = Algorithm.PublicKeySizeInBytes;
+
+ if (destination.Length != publicKeySizeInBytes)
{
- throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
+ throw new ArgumentException(
+ SR.Format(SR.Argument_DestinationImprecise, publicKeySizeInBytes),
+ nameof(destination));
}
ThrowIfDisposed();
- ExportMLDsaPublicKeyCore(destination.Slice(0, Algorithm.PublicKeySizeInBytes));
- return Algorithm.PublicKeySizeInBytes;
+
+ ExportMLDsaPublicKeyCore(destination);
}
///
/// Exports the current key in the FIPS 204 secret key format.
///
- ///
- /// The buffer to receive the secret key.
- ///
///
- /// The number of bytes written to .
+ /// The FIPS 204 secret key.
///
+ ///
+ /// The current instance cannot export a secret key.
+ /// -or-
+ /// An error occurred while exporting the key.
+ ///
+ /// The object has already been disposed.
+ public byte[] ExportMLDsaSecretKey()
+ {
+ ThrowIfDisposed();
+
+ byte[] destination = new byte[Algorithm.SecretKeySizeInBytes];
+ ExportMLDsaSecretKeyCore(destination);
+ return destination;
+ }
+
+ ///
+ /// Exports the current key in the FIPS 204 secret key format.
+ ///
+ ///
+ /// The buffer to receive the secret key. Its length must be exactly
+ /// .
+ ///
///
- /// is too small to hold the secret key.
+ /// is the incorrect length to receive the secret key.
///
///
/// An error occurred while exporting the key.
///
- public int ExportMLDsaSecretKey(Span destination)
+ public void ExportMLDsaSecretKey(Span destination)
{
- if (destination.Length < Algorithm.SecretKeySizeInBytes)
+ int secretKeySizeInBytes = Algorithm.SecretKeySizeInBytes;
+
+ if (destination.Length != secretKeySizeInBytes)
{
- throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
+ throw new ArgumentException(
+ SR.Format(SR.Argument_DestinationImprecise, secretKeySizeInBytes),
+ nameof(destination));
}
ThrowIfDisposed();
- ExportMLDsaSecretKeyCore(destination.Slice(0, Algorithm.SecretKeySizeInBytes));
- return Algorithm.SecretKeySizeInBytes;
+
+ ExportMLDsaSecretKeyCore(destination);
+ }
+
+ ///
+ /// Exports the private seed in the FIPS 204 private seed format.
+ ///
+ ///
+ /// The FIPS 204 private seed.
+ ///
+ ///
+ /// An error occurred while exporting the key.
+ ///
+ /// The object has already been disposed.
+ public byte[] ExportMLDsaPrivateSeed()
+ {
+ ThrowIfDisposed();
+
+ byte[] destination = new byte[Algorithm.PrivateSeedSizeInBytes];
+ ExportMLDsaPrivateSeedCore(destination);
+ return destination;
}
///
/// Exports the private seed of the current key.
///
///
- /// The buffer to receive the private seed.
+ /// The buffer to receive the private seed. Its length must be exactly
+ /// .
///
- ///
- /// The number of bytes written to .
- ///
///
- /// is too small to hold the private seed.
+ /// is the incorrect length to receive the private seed.
///
///
/// An error occurred while exporting the private seed.
///
- public int ExportMLDsaPrivateSeed(Span destination)
+ public void ExportMLDsaPrivateSeed(Span destination)
{
- if (destination.Length < Algorithm.PrivateSeedSizeInBytes)
+ int privateSeedSizeInBytes = Algorithm.PrivateSeedSizeInBytes;
+ if (destination.Length != privateSeedSizeInBytes)
{
- throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
+ throw new ArgumentException(
+ SR.Format(SR.Argument_DestinationImprecise, privateSeedSizeInBytes),
+ nameof(destination));
}
ThrowIfDisposed();
- ExportMLDsaPrivateSeedCore(destination.Slice(0, Algorithm.PrivateSeedSizeInBytes));
- return Algorithm.PrivateSeedSizeInBytes;
+
+ ExportMLDsaPrivateSeedCore(destination);
}
///
@@ -1296,6 +1447,18 @@ public static MLDsa ImportMLDsaPublicKey(MLDsaAlgorithm algorithm, ReadOnlySpan<
return MLDsaImplementation.ImportPublicKey(algorithm, source);
}
+ ///
+ ///
+ /// or is .
+ ///
+ public static MLDsa ImportMLDsaPublicKey(MLDsaAlgorithm algorithm, byte[] source)
+ {
+ ArgumentNullException.ThrowIfNull(algorithm);
+ ArgumentNullException.ThrowIfNull(source);
+
+ return ImportMLDsaPublicKey(algorithm, new ReadOnlySpan(source));
+ }
+
///
/// Imports an ML-DSA private key in the FIPS 204 secret key format.
///
@@ -1334,6 +1497,18 @@ public static MLDsa ImportMLDsaSecretKey(MLDsaAlgorithm algorithm, ReadOnlySpan<
return MLDsaImplementation.ImportSecretKey(algorithm, source);
}
+ ///
+ ///
+ /// or is .
+ ///
+ public static MLDsa ImportMLDsaSecretKey(MLDsaAlgorithm algorithm, byte[] source)
+ {
+ ArgumentNullException.ThrowIfNull(algorithm);
+ ArgumentNullException.ThrowIfNull(source);
+
+ return ImportMLDsaSecretKey(algorithm, new ReadOnlySpan(source));
+ }
+
///
/// Imports an ML-DSA private key from its private seed value.
///
@@ -1372,6 +1547,18 @@ public static MLDsa ImportMLDsaPrivateSeed(MLDsaAlgorithm algorithm, ReadOnlySpa
return MLDsaImplementation.ImportSeed(algorithm, source);
}
+ ///
+ ///
+ /// or is .
+ ///
+ public static MLDsa ImportMLDsaPrivateSeed(MLDsaAlgorithm algorithm, byte[] source)
+ {
+ ArgumentNullException.ThrowIfNull(algorithm);
+ ArgumentNullException.ThrowIfNull(source);
+
+ return ImportMLDsaPrivateSeed(algorithm, new ReadOnlySpan(source));
+ }
+
///
/// Called by the Dispose() and Finalize() methods to release the managed and unmanaged
/// resources used by the current instance of the class.
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
index bb1f12ebd98284..b3ca249c345ea9 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
@@ -28,22 +28,25 @@ internal static MLDsaImplementation DuplicatePrivateKey(MLDsa key)
Debug.Assert(key is not MLDsaImplementation);
MLDsaAlgorithm alg = key.Algorithm;
+ Debug.Assert(alg.SecretKeySizeInBytes > alg.PrivateSeedSizeInBytes);
byte[] rented = CryptoPool.Rent(alg.SecretKeySizeInBytes);
- int written = 0;
try
{
- written = key.ExportMLDsaPrivateSeed(rented);
- return ImportSeed(alg, new ReadOnlySpan(rented, 0, written));
+ Span seedSpan = rented.AsSpan(0, alg.PrivateSeedSizeInBytes);
+ key.ExportMLDsaPrivateSeed(seedSpan);
+ return ImportSeed(alg, seedSpan);
}
catch (CryptographicException)
{
- written = key.ExportMLDsaSecretKey(rented);
- return ImportSecretKey(alg, new ReadOnlySpan(rented, 0, written));
+ // Rented array may still be larger but we expect exact length
+ Span skSpan = rented.AsSpan(0, alg.SecretKeySizeInBytes);
+ key.ExportMLDsaSecretKey(skSpan);
+ return ImportSecretKey(alg, skSpan);
}
finally
{
- CryptoPool.Return(rented, written);
+ CryptoPool.Return(rented);
}
}
}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/SlhDsaImplementation.cs b/src/libraries/Common/src/System/Security/Cryptography/SlhDsaImplementation.cs
index 7ce51ec7f1e862..3f655e57815a05 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/SlhDsaImplementation.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/SlhDsaImplementation.cs
@@ -27,6 +27,7 @@ internal static SlhDsaImplementation DuplicatePrivateKey(SlhDsa key)
Span secretKey = (stackalloc byte[128])[..key.Algorithm.SecretKeySizeInBytes];
key.ExportSlhDsaSecretKey(secretKey);
+
try
{
return ImportSecretKey(key.Algorithm, secretKey);
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaImplementationTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaImplementationTests.cs
index 2c66bae65052d6..eabcc2f37801a3 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaImplementationTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaImplementationTests.cs
@@ -20,9 +20,21 @@ public class MLDsaImplementationTests : MLDsaTestsBase
public static void GenerateImport_NullAlgorithm()
{
AssertExtensions.Throws("algorithm", static () => MLDsa.GenerateKey(null));
- AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPrivateSeed(null, default));
- AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPublicKey(null, default));
- AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaSecretKey(null, default));
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPrivateSeed(null, default(ReadOnlySpan)));
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPublicKey(null, default(ReadOnlySpan)));
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaSecretKey(null, default(ReadOnlySpan)));
+
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPrivateSeed(null, (byte[]?)null));
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaPublicKey(null, (byte[]?)null));
+ AssertExtensions.Throws("algorithm", static () => MLDsa.ImportMLDsaSecretKey(null, (byte[]?)null));
+ }
+
+ [Fact]
+ public static void Import_NullSource()
+ {
+ AssertExtensions.Throws("source", static () => MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa44, (byte[]?)null));
+ AssertExtensions.Throws("source", static () => MLDsa.ImportMLDsaPublicKey(MLDsaAlgorithm.MLDsa44, (byte[]?)null));
+ AssertExtensions.Throws("source", static () => MLDsa.ImportMLDsaSecretKey(MLDsaAlgorithm.MLDsa44, (byte[]?)null));
}
[Theory]
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestHelpers.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestHelpers.cs
index 5800ef812b767d..a03ec55bca7eed 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestHelpers.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestHelpers.cs
@@ -64,9 +64,11 @@ internal static void AssertImportPublicKey(Action> testDirectCall, A
{
testDirectCall(() => MLDsa.ImportMLDsaPublicKey(algorithm, Array.Empty().AsSpan()));
testDirectCall(() => MLDsa.ImportMLDsaPublicKey(algorithm, ReadOnlySpan.Empty));
+ testDirectCall(() => MLDsa.ImportMLDsaPublicKey(algorithm, default(ReadOnlySpan)));
}
else
{
+ testDirectCall(() => MLDsa.ImportMLDsaPublicKey(algorithm, publicKey));
testDirectCall(() => MLDsa.ImportMLDsaPublicKey(algorithm, publicKey.AsSpan()));
}
@@ -109,9 +111,11 @@ internal static void AssertImportSecretKey(Action> testDirectCall, A
{
testDirectCall(() => MLDsa.ImportMLDsaSecretKey(algorithm, Array.Empty().AsSpan()));
testDirectCall(() => MLDsa.ImportMLDsaSecretKey(algorithm, ReadOnlySpan.Empty));
+ testDirectCall(() => MLDsa.ImportMLDsaSecretKey(algorithm, default(ReadOnlySpan)));
}
else
{
+ testDirectCall(() => MLDsa.ImportMLDsaSecretKey(algorithm, secretKey));
testDirectCall(() => MLDsa.ImportMLDsaSecretKey(algorithm, secretKey.AsSpan()));
}
@@ -147,9 +151,11 @@ internal static void AssertImportPrivateSeed(Action> testDirectCall,
{
testDirectCall(() => MLDsa.ImportMLDsaPrivateSeed(algorithm, Array.Empty().AsSpan()));
testDirectCall(() => MLDsa.ImportMLDsaPrivateSeed(algorithm, ReadOnlySpan.Empty));
+ testDirectCall(() => MLDsa.ImportMLDsaPrivateSeed(algorithm, default(ReadOnlySpan)));
}
else
{
+ testDirectCall(() => MLDsa.ImportMLDsaPrivateSeed(algorithm, privateSeed));
testDirectCall(() => MLDsa.ImportMLDsaPrivateSeed(algorithm, privateSeed.AsSpan()));
}
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTests.cs
index 9b31180db2fc46..6f276d089d798e 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTests.cs
@@ -74,6 +74,10 @@ public static void NullArgumentValidation(MLDsaAlgorithm algorithm, bool shouldD
PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 42);
+ AssertExtensions.Throws("data", () => mldsa.SignData(null));
+ AssertExtensions.Throws("data", () => mldsa.VerifyData(null, null));
+ AssertExtensions.Throws("signature", () => mldsa.VerifyData(Array.Empty(), null));
+
AssertExtensions.Throws("password", () => mldsa.ExportEncryptedPkcs8PrivateKey((string)null, pbeParameters));
AssertExtensions.Throws("password", () => mldsa.ExportEncryptedPkcs8PrivateKeyPem((string)null, pbeParameters));
AssertExtensions.Throws("password", () => mldsa.TryExportEncryptedPkcs8PrivateKey((string)null, pbeParameters, Span.Empty, out _));
@@ -107,9 +111,13 @@ public static void ArgumentValidation(MLDsaAlgorithm algorithm, bool shouldDispo
}
AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaPublicKey(new byte[publicKeySize - 1]));
+ AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaPublicKey(new byte[publicKeySize + 1]));
AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaSecretKey(new byte[secretKeySize - 1]));
+ AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaSecretKey(new byte[secretKeySize + 1]));
AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaPrivateSeed(new byte[privateSeedSize - 1]));
+ AssertExtensions.Throws("destination", () => mldsa.ExportMLDsaPrivateSeed(new byte[privateSeedSize + 1]));
AssertExtensions.Throws("destination", () => mldsa.SignData(ReadOnlySpan.Empty, new byte[signatureSize - 1], ReadOnlySpan.Empty));
+ AssertExtensions.Throws("destination", () => mldsa.SignData(ReadOnlySpan.Empty, new byte[signatureSize + 1], ReadOnlySpan.Empty));
// Context length must be less than 256
AssertExtensions.Throws("context", () => mldsa.SignData(ReadOnlySpan.Empty, new byte[signatureSize], new byte[256]));
@@ -165,6 +173,14 @@ public static void ExportMLDsaPublicKey_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddFillDestination(1);
int publicKeySize = algorithm.PublicKeySizeInBytes;
+
+ // Array overload
+ byte[] exported = mldsa.ExportMLDsaPublicKey();
+ Assert.Equal(1, mldsa.ExportMLDsaPublicKeyCoreCallCount);
+ Assert.Equal(publicKeySize, exported.Length);
+ AssertExpectedFill(exported, fillElement: 1);
+
+ // Span overload
byte[] publicKey = CreatePaddedFilledArray(publicKeySize, 42);
// Extra bytes in destination buffer should not be touched
@@ -172,7 +188,7 @@ public static void ExportMLDsaPublicKey_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddDestinationBufferIsSameAssertion(destination);
mldsa.ExportMLDsaPublicKey(destination.Span);
- Assert.Equal(1, mldsa.ExportMLDsaPublicKeyCoreCallCount);
+ Assert.Equal(2, mldsa.ExportMLDsaPublicKeyCoreCallCount);
AssertExpectedFill(publicKey, fillElement: 1, paddingElement: 42, PaddingSize, publicKeySize);
}
@@ -185,6 +201,14 @@ public static void ExportMLDsaSecretKey_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddFillDestination(1);
int secretKeySize = algorithm.SecretKeySizeInBytes;
+
+ // Array overload
+ byte[] exported = mldsa.ExportMLDsaSecretKey();
+ Assert.Equal(1, mldsa.ExportMLDsaSecretKeyCoreCallCount);
+ Assert.Equal(secretKeySize, exported.Length);
+ AssertExpectedFill(exported, fillElement: 1);
+
+ // Span overload
byte[] secretKey = CreatePaddedFilledArray(secretKeySize, 42);
// Extra bytes in destination buffer should not be touched
@@ -192,10 +216,38 @@ public static void ExportMLDsaSecretKey_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddDestinationBufferIsSameAssertion(destination);
mldsa.ExportMLDsaSecretKey(destination.Span);
- Assert.Equal(1, mldsa.ExportMLDsaSecretKeyCoreCallCount);
+ Assert.Equal(2, mldsa.ExportMLDsaSecretKeyCoreCallCount);
AssertExpectedFill(secretKey, fillElement: 1, paddingElement: 42, PaddingSize, secretKeySize);
}
+ [Theory]
+ [MemberData(nameof(MLDsaTestsData.AllMLDsaAlgorithms), MemberType = typeof(MLDsaTestsData))]
+ public static void ExportMLDsaPrivateSeed_CallsCore(MLDsaAlgorithm algorithm)
+ {
+ using MLDsaTestImplementation mldsa = MLDsaTestImplementation.CreateOverriddenCoreMethodsFail(algorithm);
+ mldsa.ExportMLDsaPrivateSeedHook = _ => { };
+ mldsa.AddFillDestination(1);
+
+ int privateSeedSize = algorithm.PrivateSeedSizeInBytes;
+
+ // Array overload
+ byte[] exported = mldsa.ExportMLDsaPrivateSeed();
+ Assert.Equal(1, mldsa.ExportMLDsaPrivateSeedCoreCallCount);
+ Assert.Equal(privateSeedSize, exported.Length);
+ AssertExpectedFill(exported, fillElement: 1);
+
+ // Span overload
+ byte[] privateSeed = CreatePaddedFilledArray(privateSeedSize, 42);
+
+ // Extra bytes in destination buffer should not be touched
+ Memory destination = privateSeed.AsMemory(PaddingSize, privateSeedSize);
+ mldsa.AddDestinationBufferIsSameAssertion(destination);
+
+ mldsa.ExportMLDsaPrivateSeed(destination.Span);
+ Assert.Equal(2, mldsa.ExportMLDsaPrivateSeedCoreCallCount);
+ AssertExpectedFill(privateSeed, fillElement: 1, paddingElement: 42, PaddingSize, privateSeedSize);
+ }
+
[Theory]
[MemberData(nameof(MLDsaTestsData.AllMLDsaAlgorithms), MemberType = typeof(MLDsaTestsData))]
public static void SignData_CallsCore(MLDsaAlgorithm algorithm)
@@ -210,6 +262,14 @@ public static void SignData_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddFillDestination(1);
int signatureSize = algorithm.SignatureSizeInBytes;
+
+ // Array overload
+ byte[] exported = mldsa.SignData(testData, testContext);
+ Assert.Equal(1, mldsa.SignDataCoreCallCount);
+ Assert.Equal(signatureSize, exported.Length);
+ AssertExpectedFill(exported, fillElement: 1);
+
+ // Span overload
byte[] signature = CreatePaddedFilledArray(signatureSize, 42);
// Extra bytes in destination buffer should not be touched
@@ -217,7 +277,7 @@ public static void SignData_CallsCore(MLDsaAlgorithm algorithm)
mldsa.AddDestinationBufferIsSameAssertion(destination);
mldsa.SignData(testData, destination.Span, testContext);
- Assert.Equal(1, mldsa.SignDataCoreCallCount);
+ Assert.Equal(2, mldsa.SignDataCoreCallCount);
AssertExpectedFill(signature, fillElement: 1, paddingElement: 42, PaddingSize, signatureSize);
}
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestsBase.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestsBase.cs
index 2675718198b45c..00b2b49e327d8c 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestsBase.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestsBase.cs
@@ -30,9 +30,7 @@ public void GenerateSignVerifyNoContext(MLDsaAlgorithm algorithm)
{
using MLDsa mldsa = GenerateKey(algorithm);
byte[] data = [ 1, 2, 3, 4, 5 ];
- byte[] signature = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsa.SignData(data, signature));
-
+ byte[] signature = mldsa.SignData(data);
ExerciseSuccessfulVerify(mldsa, data, signature, []);
}
@@ -43,8 +41,7 @@ public void GenerateSignVerifyWithContext(MLDsaAlgorithm algorithm)
using MLDsa mldsa = GenerateKey(algorithm);
byte[] context = [ 1, 1, 3, 5, 6 ];
byte[] data = [ 1, 2, 3, 4, 5 ];
- byte[] signature = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsa.SignData(data, signature, context));
+ byte[] signature = mldsa.SignData(data, context);
ExerciseSuccessfulVerify(mldsa, data, signature, context);
}
@@ -55,9 +52,7 @@ public void GenerateSignVerifyWithContext(MLDsaAlgorithm algorithm)
public void GenerateSignVerifyEmptyMessageNoContext(MLDsaAlgorithm algorithm)
{
using MLDsa mldsa = GenerateKey(algorithm);
- byte[] signature = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsa.SignData([], signature));
-
+ byte[] signature = mldsa.SignData([]);
ExerciseSuccessfulVerify(mldsa, [], signature, []);
}
@@ -68,9 +63,7 @@ public void GenerateSignVerifyEmptyMessageWithContext(MLDsaAlgorithm algorithm)
{
using MLDsa mldsa = GenerateKey(algorithm);
byte[] context = [1, 1, 3, 5, 6];
- byte[] signature = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsa.SignData([], signature, context));
-
+ byte[] signature = mldsa.SignData([], context);
ExerciseSuccessfulVerify(mldsa, [], signature, context);
}
@@ -84,12 +77,10 @@ public void GenerateSignExportPublicVerifyWithPublicOnly(MLDsaAlgorithm algorith
using (MLDsa mldsa = GenerateKey(algorithm))
{
- signature = new byte[algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsa.SignData(data, signature));
+ signature = mldsa.SignData(data);
AssertExtensions.TrueExpression(mldsa.VerifyData(data, signature));
- publicKey = new byte[algorithm.PublicKeySizeInBytes];
- Assert.Equal(publicKey.Length, mldsa.ExportMLDsaPublicKey(publicKey));
+ publicKey = mldsa.ExportMLDsaPublicKey();
}
using (MLDsa mldsaPub = ImportPublicKey(algorithm, publicKey))
@@ -108,19 +99,17 @@ public void GenerateExportSecretKeySignAndVerify(MLDsaAlgorithm algorithm)
using (MLDsa mldsaTmp = GenerateKey(algorithm))
{
- signature = new byte[algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsaTmp.SignData(data, signature));
-
- secretKey = new byte[algorithm.SecretKeySizeInBytes];
- Assert.Equal(secretKey.Length, mldsaTmp.ExportMLDsaSecretKey(secretKey));
+ signature = mldsaTmp.SignData(data);
+ secretKey = mldsaTmp.ExportMLDsaSecretKey();
}
using (MLDsa mldsa = ImportSecretKey(algorithm, secretKey))
{
AssertExtensions.TrueExpression(mldsa.VerifyData(data, signature));
- signature.AsSpan().Fill(0);
- Assert.Equal(signature.Length, mldsa.SignData(data, signature));
+ Span signatureSpan = signature.AsSpan();
+ signatureSpan.Fill(0);
+ mldsa.SignData(data, signatureSpan);
AssertExtensions.TrueExpression(mldsa.VerifyData(data, signature));
data[0] ^= 1;
@@ -138,19 +127,17 @@ public void GenerateExportPrivateSeedSignAndVerify(MLDsaAlgorithm algorithm)
using (MLDsa mldsaTmp = GenerateKey(algorithm))
{
- signature = new byte[algorithm.SignatureSizeInBytes];
- Assert.Equal(signature.Length, mldsaTmp.SignData(data, signature));
-
- privateSeed = new byte[algorithm.PrivateSeedSizeInBytes];
- Assert.Equal(privateSeed.Length, mldsaTmp.ExportMLDsaPrivateSeed(privateSeed));
+ signature = mldsaTmp.SignData(data);
+ privateSeed = mldsaTmp.ExportMLDsaPrivateSeed();
}
using (MLDsa mldsa = ImportPrivateSeed(algorithm, privateSeed))
{
AssertExtensions.TrueExpression(mldsa.VerifyData(data, signature));
- signature.AsSpan().Fill(0);
- Assert.Equal(signature.Length, mldsa.SignData(data, signature));
+ Span signatureSpan = signature.AsSpan();
+ signatureSpan.Fill(0);
+ mldsa.SignData(data, signatureSpan);
ExerciseSuccessfulVerify(mldsa, data, signature, []);
}
@@ -159,10 +146,10 @@ public void GenerateExportPrivateSeedSignAndVerify(MLDsaAlgorithm algorithm)
[Fact]
public void ImportSecretKey_CannotReconstructSeed()
{
- byte[] secretKey = new byte[MLDsaAlgorithm.MLDsa44.SecretKeySizeInBytes];
+ byte[] secretKey;
using (MLDsa mldsaOriginal = GenerateKey(MLDsaAlgorithm.MLDsa44))
{
- Assert.Equal(secretKey.Length, mldsaOriginal.ExportMLDsaSecretKey(secretKey));
+ secretKey = mldsaOriginal.ExportMLDsaSecretKey();
}
using (MLDsa mldsa = ImportSecretKey(MLDsaAlgorithm.MLDsa44, secretKey))
@@ -174,21 +161,18 @@ public void ImportSecretKey_CannotReconstructSeed()
[Fact]
public void ImportSeed_CanReconstructSecretKey()
{
- byte[] secretKey = new byte[MLDsaAlgorithm.MLDsa44.SecretKeySizeInBytes];
- byte[] seed = new byte[MLDsaAlgorithm.MLDsa44.PrivateSeedSizeInBytes];
+ byte[] secretKey;
+ byte[] seed;
using (MLDsa mldsaOriginal = GenerateKey(MLDsaAlgorithm.MLDsa44))
{
- Assert.Equal(secretKey.Length, mldsaOriginal.ExportMLDsaSecretKey(secretKey));
- Assert.Equal(seed.Length, mldsaOriginal.ExportMLDsaPrivateSeed(seed));
+ secretKey = mldsaOriginal.ExportMLDsaSecretKey();
+ seed = mldsaOriginal.ExportMLDsaPrivateSeed();
}
using (MLDsa mldsa = ImportPrivateSeed(MLDsaAlgorithm.MLDsa44, seed))
{
- byte[] secretKey2 = new byte[MLDsaAlgorithm.MLDsa44.SecretKeySizeInBytes];
- byte[] seed2 = new byte[MLDsaAlgorithm.MLDsa44.PrivateSeedSizeInBytes];
-
- Assert.Equal(secretKey2.Length, mldsa.ExportMLDsaSecretKey(secretKey2));
- Assert.Equal(seed2.Length, mldsa.ExportMLDsaPrivateSeed(seed2));
+ byte[] secretKey2 = mldsa.ExportMLDsaSecretKey();
+ byte[] seed2 = mldsa.ExportMLDsaPrivateSeed();
AssertExtensions.SequenceEqual(secretKey, secretKey2);
AssertExtensions.SequenceEqual(seed, seed2);
@@ -209,15 +193,12 @@ public void NistImportSecretKeyVerifyExportsAndSignature(MLDsaNistTestCase testC
{
using MLDsa mldsa = ImportSecretKey(testCase.Algorithm, testCase.SecretKey);
- byte[] pubKey = new byte[testCase.Algorithm.PublicKeySizeInBytes];
- Assert.Equal(pubKey.Length, mldsa.ExportMLDsaPublicKey(pubKey));
+ byte[] pubKey = mldsa.ExportMLDsaPublicKey();
AssertExtensions.SequenceEqual(testCase.PublicKey, pubKey);
- byte[] secretKey = new byte[testCase.Algorithm.SecretKeySizeInBytes];
- Assert.Equal(secretKey.Length, mldsa.ExportMLDsaSecretKey(secretKey));
-
- byte[] seed = new byte[testCase.Algorithm.PrivateSeedSizeInBytes];
- Assert.Throws(() => mldsa.ExportMLDsaPrivateSeed(seed));
+ byte[] secretKey = mldsa.ExportMLDsaSecretKey();
+ AssertExtensions.SequenceEqual(testCase.SecretKey, secretKey);
+ Assert.Throws(() => mldsa.ExportMLDsaPrivateSeed());
Assert.Equal(testCase.ShouldPass, mldsa.VerifyData(testCase.Message, testCase.Signature, testCase.Context));
}
diff --git a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseKey.cs b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseKey.cs
index 368b10144d239f..fc277572851cbd 100644
--- a/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseKey.cs
+++ b/src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseKey.cs
@@ -207,7 +207,9 @@ internal int Sign(ReadOnlySpan toBeSigned, Span destination)
#pragma warning disable SYSLIB5006
case KeyType.MLDsa:
Debug.Assert(_mldsaKey != null);
- return _mldsaKey.SignData(toBeSigned, destination);
+ Span mldsaSignature = destination.Slice(0, _mldsaKey.Algorithm.SignatureSizeInBytes);
+ _mldsaKey.SignData(toBeSigned, mldsaSignature);
+ return mldsaSignature.Length;
#pragma warning restore SYSLIB5006
default:
Debug.Fail("Unknown key type");
diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs b/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs
index 5537532205105e..8ec09289007a3f 100644
--- a/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs
+++ b/src/libraries/System.Security.Cryptography.Cose/tests/CoseTestHelpers.cs
@@ -464,123 +464,30 @@ private static void AssertHeaders(CborReader reader, List<(CoseHeaderLabel, Read
}
}
- private static ECParameters _ec256Parameters = CreateECParameters("nistP256", "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM");
- private static ECParameters _ec384Parameters = CreateECParameters("nistP384", "kTJyP2KSsBBhnb4kjWmMF7WHVsY55xUPgb7k64rDcjatChoZ1nvjKmYmPh5STRKc", "mM0weMVU2DKsYDxDJkEP9hZiRZtB8fPfXbzINZj_fF7YQRynNWedHEyzAJOX2e8s", "ok3Nq97AXlpEusO7jIy1FZATlBP9PNReMU7DWbkLQ5dU90snHuuHVDjEPmtV0fTo");
- private static ECParameters _ec512Parameters = CreateECParameters("nistP521", "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt");
-
- [ThreadStatic]
- private static ECDsa? t_es256;
- [ThreadStatic]
- private static ECDsa? t_es384;
[ThreadStatic]
- private static ECDsa? t_es512;
+ private static TestKeyRing? t_threadKeys;
+ private static TestKeyRing ThreadKeys => (t_threadKeys ??= new TestKeyRing());
- internal static ECDsa ES256 => t_es256 ??= CreateECDsa(_ec256Parameters, true);
- internal static ECDsa ES384 => t_es384 ??= CreateECDsa(_ec384Parameters, true);
- internal static ECDsa ES512 => t_es512 ??= CreateECDsa(_ec512Parameters, true);
-
- [ThreadStatic]
- private static ECDsa? t_es256WithoutPrivateKey;
- [ThreadStatic]
- private static ECDsa? t_es384WithoutPrivateKey;
- [ThreadStatic]
- private static ECDsa? t_es512WithoutPrivateKey;
+ internal static ECDsa ES256 => ThreadKeys.ES256;
+ internal static ECDsa ES384 => ThreadKeys.ES384;
+ internal static ECDsa ES512 => ThreadKeys.ES512;
- private static ECDsa ES256WithoutPrivateKey => t_es256WithoutPrivateKey ??= CreateECDsa(_ec256Parameters, false);
- private static ECDsa ES384WithoutPrivateKey => t_es384WithoutPrivateKey ??= CreateECDsa(_ec384Parameters, false);
- private static ECDsa ES512WithoutPrivateKey => t_es512WithoutPrivateKey ??= CreateECDsa(_ec512Parameters, false);
+ internal static ECDsa ES256WithoutPrivateKey => ThreadKeys.ES256WithoutPrivateKey;
+ internal static ECDsa ES384WithoutPrivateKey => ThreadKeys.ES384WithoutPrivateKey;
+ internal static ECDsa ES512WithoutPrivateKey => ThreadKeys.ES512WithoutPrivateKey;
internal static ECDsa DefaultKey => ES256;
internal static HashAlgorithmName DefaultHash { get; } = HashAlgorithmName.SHA256;
- [ThreadStatic]
- internal static RSA? t_rsaKey;
- [ThreadStatic]
- internal static RSA? t_rsaKeyWithoutPrivateKey;
-
- internal static RSA RSAKey => t_rsaKey ??= CreateRSA(true);
- internal static RSA RSAKeyWithoutPrivateKey => t_rsaKeyWithoutPrivateKey ??= CreateRSA(false);
-
- [ThreadStatic]
- private static MLDsa? t_mldsa44Key;
- [ThreadStatic]
- private static MLDsa? t_mldsa44KeyWithoutPrivateKey;
- [ThreadStatic]
- private static MLDsa? t_mldsa65Key;
- [ThreadStatic]
- private static MLDsa? t_mldsa65KeyWithoutPrivateKey;
- [ThreadStatic]
- private static MLDsa? t_mldsa87Key;
- [ThreadStatic]
- private static MLDsa? t_mldsa87KeyWithoutPrivateKey;
-
- internal static MLDsa MLDsa44Key => t_mldsa44Key ??= CreateMLDsa(MLDsaAlgorithm.MLDsa44, true);
- internal static MLDsa MLDsa44KeyWithoutPrivateKey => t_mldsa44KeyWithoutPrivateKey ??= CreateMLDsa(MLDsaAlgorithm.MLDsa44, false);
- internal static MLDsa MLDsa65Key => t_mldsa65Key ??= CreateMLDsa(MLDsaAlgorithm.MLDsa65, true);
- internal static MLDsa MLDsa65KeyWithoutPrivateKey => t_mldsa65KeyWithoutPrivateKey ??= CreateMLDsa(MLDsaAlgorithm.MLDsa65, false);
- internal static MLDsa MLDsa87Key => t_mldsa87Key ??= CreateMLDsa(MLDsaAlgorithm.MLDsa87, true);
- internal static MLDsa MLDsa87KeyWithoutPrivateKey => t_mldsa87KeyWithoutPrivateKey ??= CreateMLDsa(MLDsaAlgorithm.MLDsa87, false);
-
- private static ECParameters CreateECParameters(string curveFriendlyName, string base64UrlQx, string base64UrlQy, string base64UrlPrivateKey)
- {
- return new ECParameters()
- {
- Curve = ECCurve.CreateFromFriendlyName(curveFriendlyName),
- Q = new ECPoint
- {
- X = Base64UrlEncoder.DecodeBytes(base64UrlQx),
- Y = Base64UrlEncoder.DecodeBytes(base64UrlQy),
- },
- D = Base64UrlEncoder.DecodeBytes(base64UrlPrivateKey),
- };
- }
-
- private static ECDsa CreateECDsa(ECParameters parameters, bool includePrivateKey)
- {
- ECParameters parametersLocalCopy = parameters;
- if (!includePrivateKey)
- {
- parametersLocalCopy.D = null;
- }
-
- return ECDsa.Create(parametersLocalCopy);
- }
-
- private static RSA CreateRSA(bool includePrivateKey)
- {
- var rsaParameters = new RSAParameters
- {
- Modulus = ByteUtils.HexToByteArray("BC7E29D0DF7E20CC9DC8D509E0F68895922AF0EF452190D402C61B554334A7BF91C9A570240F994FAE1B69035BCFAD4F7E249EB26087C2665E7C958C967B1517413DC3F97A431691A5999B257CC6CD356BAD168D929B8BAE9020750E74CF60F6FD35D6BB3FC93FC28900478694F508B33E7C00E24F90EDF37457FC3E8EFCFD2F42306301A8205AB740515331D5C18F0C64D4A43BE52FC440400F6BFC558A6E32884C2AF56F29E5C52780CEA7285F5C057FC0DFDA232D0ADA681B01495D9D0E32196633588E289E59035FF664F056189F2F10FE05827B796C326E3E748FFA7C589ED273C9C43436CDDB4A6A22523EF8BCB2221615B799966F1ABA5BC84B7A27CF"),
- Exponent = ByteUtils.HexToByteArray("010001"),
- D = ByteUtils.HexToByteArray("0969FF04FCC1E1647C20402CF3F736D4CAE33F264C1C6EE3252CFCC77CDEF533D700570AC09A50D7646EDFB1F86A13BCABCF00BD659F27813D08843597271838BC46ED4743FE741D9BC38E0BF36D406981C7B81FCE54861CEBFB85AD23A8B4833C1BEE18C05E4E436A869636980646EECB839E4DAF434C9C6DFBF3A55CE1DB73E4902F89384BD6F9ECD3399FB1ED4B83F28D356C8E619F1F0DC96BBE8B75C1812CA58F360259EAEB1D17130C3C0A2715A99BE49898E871F6088A29570DC2FFA0CEFFFA27F1F055CBAABFD8894E0CC24F176E34EBAD32278A466F8A34A685ACC8207D9EC1FCBBD094996DC73C6305FCA31668BE57B1699D0BB456CC8871BFFBCD"),
- P = ByteUtils.HexToByteArray("F331593E147FD3A3235675F0D36A06E5426F7C5E78E49B2ACD3E268BA50E48ED2A52F3B4FA492D6BCF70EB3F915A716078A113652E3FA4C6D50AF8606C2D2C28ECAF083B712D6CEE1263C1205DA03BBBFA6F5C2D8B1A96194089CACB306C844A832E2B032B5F96A7EAB6CFE1107299013C8B0E9F089BBABBC504DD8BC138BA4B"),
- Q = ByteUtils.HexToByteArray("C66B5DDCAB7017E14083F2854F61997F35636C86F2F92B172D2555588EE1ED899BA6B6ADEC0A02024B2E78A91C891256A8571E0EFB3BAC3F41724DE036EC8FA0F93E2CFBDDA59C6FF1816EB3DC938D4E45912423F3F34B7E96C39E2E4D65A3DCD6DFD2B4EF527841001272F77855B6D75D40D54BB65BD1DF8538E96EC4DAD60D"),
- DP = ByteUtils.HexToByteArray("1F677CFDBE49EF7B7EA1B8A33BB9D260229F20F1562D373864BEA4DD9D97E5A4F2B53991624CB6D7D836DDBA1CBC102E0405D0EA5CF98CFEBC1E298AD20D5749859EE8B23C604053D1FE1DBF5F37C4DEF66D10FB349E5F49AD82DDB435719DF7BD4EE5F107D5D52FA3E8AD9983B538BAE72591E2C98ACAA75ABED1192DFF7457"),
- DQ = ByteUtils.HexToByteArray("2CCC9F13ACCD9146B57755318E3BBE197FA7642090097C162E86485FC75AF173E965D9C7290D1569092A83E9C2DC9BFC5EE3D490935EE4C41F75BC698C5D1B0CC059AE746B95F1DD408CF5BEBC65C038D4F23153C0C7C4DADF1569C890870B5958568ECF755D8C73389DF1C138353A242414F853B0E7C85A0C4D4E3F4949139D"),
- InverseQ = ByteUtils.HexToByteArray("7B6E2406FD03BC75EA22AB94A8D242506A6BBFE36BC8132DBBCE50B8425425062B697AFA180F5685E90E11EB5712D2E6E2B24E2A1E7C75D5940E08301E824470EF38561BE3E9D05F9FCA8E6F69A028A928E85E58212E789BA577B80378D7A995FA6AFEA74BE364661A679F82776C5905F43F7A35692986271E594E1D11F9668D"),
- };
-
- if (!includePrivateKey)
- {
- return RSA.Create(new RSAParameters { Modulus = rsaParameters.Modulus, Exponent = rsaParameters.Exponent });
- }
-
- return RSA.Create(rsaParameters);
- }
-
- private static MLDsa CreateMLDsa(MLDsaAlgorithm algorithm, bool includePrivateKey)
- {
- MLDsaNistTestCase nistKey = MLDsaTestsData.GetPassingNistTestCase(algorithm);
+ internal static RSA RSAKey => ThreadKeys.RSAKey;
+ internal static RSA RSAKeyWithoutPrivateKey => ThreadKeys.RSAKeyWithoutPrivateKey;
- if (includePrivateKey)
- {
- return MLDsa.ImportMLDsaSecretKey(algorithm, nistKey.SecretKey);
- }
- else
- {
- return MLDsa.ImportMLDsaPublicKey(algorithm, nistKey.PublicKey);
- }
- }
+ internal static MLDsa MLDsa44Key => ThreadKeys.MLDsa44Key;
+ internal static MLDsa MLDsa44KeyWithoutPrivateKey => ThreadKeys.MLDsa44KeyWithoutPrivateKey;
+ internal static MLDsa MLDsa65Key => ThreadKeys.MLDsa65Key;
+ internal static MLDsa MLDsa65KeyWithoutPrivateKey => ThreadKeys.MLDsa65KeyWithoutPrivateKey;
+ internal static MLDsa MLDsa87Key => ThreadKeys.MLDsa87Key;
+ internal static MLDsa MLDsa87KeyWithoutPrivateKey => ThreadKeys.MLDsa87KeyWithoutPrivateKey;
internal static bool AlgorithmNeedsHashAlgorithm(CoseAlgorithm algorithm)
=> algorithm is
@@ -596,7 +503,8 @@ internal static bool AlgorithmIsSupported(CoseAlgorithm algorithm)
case CoseAlgorithm.MLDsa65:
case CoseAlgorithm.MLDsa87:
return MLDsa.IsSupported;
- default: return true;
+ default:
+ return true;
}
}
@@ -960,9 +868,7 @@ private static byte[] GetSignature(IDisposable key, HashAlgorithmName? hash, byt
else if (key is MLDsa mldsa)
{
Assert.Null(hash);
- byte[] sig = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(sig.Length, mldsa.SignData(toBeSigned, sig));
- return sig;
+ return mldsa.SignData(toBeSigned);
}
throw new NotImplementedException($"Unhandled key type: {key.GetType()}");
diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/System.Security.Cryptography.Cose.Tests.csproj b/src/libraries/System.Security.Cryptography.Cose/tests/System.Security.Cryptography.Cose.Tests.csproj
index 5d1346e348d573..c7bd4df0c7f3d6 100644
--- a/src/libraries/System.Security.Cryptography.Cose/tests/System.Security.Cryptography.Cose.Tests.csproj
+++ b/src/libraries/System.Security.Cryptography.Cose/tests/System.Security.Cryptography.Cose.Tests.csproj
@@ -48,6 +48,7 @@
+
diff --git a/src/libraries/System.Security.Cryptography.Cose/tests/TestKeyRing.cs b/src/libraries/System.Security.Cryptography.Cose/tests/TestKeyRing.cs
new file mode 100644
index 00000000000000..ede7573f555ba9
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography.Cose/tests/TestKeyRing.cs
@@ -0,0 +1,102 @@
+// 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.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Formats.Cbor;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography.Tests;
+using Microsoft.IdentityModel.Tokens;
+using Test.Cryptography;
+using Xunit;
+
+namespace System.Security.Cryptography.Cose.Tests
+{
+ public class TestKeyRing
+ {
+ private static ECParameters _ec256Parameters = CreateECParameters("nistP256", "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8", "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4", "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM");
+ private static ECParameters _ec384Parameters = CreateECParameters("nistP384", "kTJyP2KSsBBhnb4kjWmMF7WHVsY55xUPgb7k64rDcjatChoZ1nvjKmYmPh5STRKc", "mM0weMVU2DKsYDxDJkEP9hZiRZtB8fPfXbzINZj_fF7YQRynNWedHEyzAJOX2e8s", "ok3Nq97AXlpEusO7jIy1FZATlBP9PNReMU7DWbkLQ5dU90snHuuHVDjEPmtV0fTo");
+ private static ECParameters _ec512Parameters = CreateECParameters("nistP521", "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt");
+
+ internal ECDsa ES256 => field ??= CreateECDsa(_ec256Parameters, true);
+ internal ECDsa ES384 => field ??= CreateECDsa(_ec384Parameters, true);
+ internal ECDsa ES512 => field ??= CreateECDsa(_ec512Parameters, true);
+
+ internal ECDsa ES256WithoutPrivateKey => field ??= CreateECDsa(_ec256Parameters, false);
+ internal ECDsa ES384WithoutPrivateKey => field ??= CreateECDsa(_ec384Parameters, false);
+ internal ECDsa ES512WithoutPrivateKey => field ??= CreateECDsa(_ec512Parameters, false);
+
+ internal RSA RSAKey => field ??= CreateRSA(true);
+ internal RSA RSAKeyWithoutPrivateKey => field ??= CreateRSA(false);
+
+ internal MLDsa MLDsa44Key => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa44, true);
+ internal MLDsa MLDsa44KeyWithoutPrivateKey => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa44, false);
+ internal MLDsa MLDsa65Key => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa65, true);
+ internal MLDsa MLDsa65KeyWithoutPrivateKey => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa65, false);
+ internal MLDsa MLDsa87Key => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa87, true);
+ internal MLDsa MLDsa87KeyWithoutPrivateKey => field ??= CreateMLDsa(MLDsaAlgorithm.MLDsa87, false);
+
+ private static ECParameters CreateECParameters(string curveFriendlyName, string base64UrlQx, string base64UrlQy, string base64UrlPrivateKey)
+ {
+ return new ECParameters()
+ {
+ Curve = ECCurve.CreateFromFriendlyName(curveFriendlyName),
+ Q = new ECPoint
+ {
+ X = Base64UrlEncoder.DecodeBytes(base64UrlQx),
+ Y = Base64UrlEncoder.DecodeBytes(base64UrlQy),
+ },
+ D = Base64UrlEncoder.DecodeBytes(base64UrlPrivateKey),
+ };
+ }
+
+ private static ECDsa CreateECDsa(ECParameters parameters, bool includePrivateKey)
+ {
+ ECParameters parametersLocalCopy = parameters;
+ if (!includePrivateKey)
+ {
+ parametersLocalCopy.D = null;
+ }
+
+ return ECDsa.Create(parametersLocalCopy);
+ }
+
+ private static RSA CreateRSA(bool includePrivateKey)
+ {
+ var rsaParameters = new RSAParameters
+ {
+ Modulus = ByteUtils.HexToByteArray("BC7E29D0DF7E20CC9DC8D509E0F68895922AF0EF452190D402C61B554334A7BF91C9A570240F994FAE1B69035BCFAD4F7E249EB26087C2665E7C958C967B1517413DC3F97A431691A5999B257CC6CD356BAD168D929B8BAE9020750E74CF60F6FD35D6BB3FC93FC28900478694F508B33E7C00E24F90EDF37457FC3E8EFCFD2F42306301A8205AB740515331D5C18F0C64D4A43BE52FC440400F6BFC558A6E32884C2AF56F29E5C52780CEA7285F5C057FC0DFDA232D0ADA681B01495D9D0E32196633588E289E59035FF664F056189F2F10FE05827B796C326E3E748FFA7C589ED273C9C43436CDDB4A6A22523EF8BCB2221615B799966F1ABA5BC84B7A27CF"),
+ Exponent = ByteUtils.HexToByteArray("010001"),
+ D = ByteUtils.HexToByteArray("0969FF04FCC1E1647C20402CF3F736D4CAE33F264C1C6EE3252CFCC77CDEF533D700570AC09A50D7646EDFB1F86A13BCABCF00BD659F27813D08843597271838BC46ED4743FE741D9BC38E0BF36D406981C7B81FCE54861CEBFB85AD23A8B4833C1BEE18C05E4E436A869636980646EECB839E4DAF434C9C6DFBF3A55CE1DB73E4902F89384BD6F9ECD3399FB1ED4B83F28D356C8E619F1F0DC96BBE8B75C1812CA58F360259EAEB1D17130C3C0A2715A99BE49898E871F6088A29570DC2FFA0CEFFFA27F1F055CBAABFD8894E0CC24F176E34EBAD32278A466F8A34A685ACC8207D9EC1FCBBD094996DC73C6305FCA31668BE57B1699D0BB456CC8871BFFBCD"),
+ P = ByteUtils.HexToByteArray("F331593E147FD3A3235675F0D36A06E5426F7C5E78E49B2ACD3E268BA50E48ED2A52F3B4FA492D6BCF70EB3F915A716078A113652E3FA4C6D50AF8606C2D2C28ECAF083B712D6CEE1263C1205DA03BBBFA6F5C2D8B1A96194089CACB306C844A832E2B032B5F96A7EAB6CFE1107299013C8B0E9F089BBABBC504DD8BC138BA4B"),
+ Q = ByteUtils.HexToByteArray("C66B5DDCAB7017E14083F2854F61997F35636C86F2F92B172D2555588EE1ED899BA6B6ADEC0A02024B2E78A91C891256A8571E0EFB3BAC3F41724DE036EC8FA0F93E2CFBDDA59C6FF1816EB3DC938D4E45912423F3F34B7E96C39E2E4D65A3DCD6DFD2B4EF527841001272F77855B6D75D40D54BB65BD1DF8538E96EC4DAD60D"),
+ DP = ByteUtils.HexToByteArray("1F677CFDBE49EF7B7EA1B8A33BB9D260229F20F1562D373864BEA4DD9D97E5A4F2B53991624CB6D7D836DDBA1CBC102E0405D0EA5CF98CFEBC1E298AD20D5749859EE8B23C604053D1FE1DBF5F37C4DEF66D10FB349E5F49AD82DDB435719DF7BD4EE5F107D5D52FA3E8AD9983B538BAE72591E2C98ACAA75ABED1192DFF7457"),
+ DQ = ByteUtils.HexToByteArray("2CCC9F13ACCD9146B57755318E3BBE197FA7642090097C162E86485FC75AF173E965D9C7290D1569092A83E9C2DC9BFC5EE3D490935EE4C41F75BC698C5D1B0CC059AE746B95F1DD408CF5BEBC65C038D4F23153C0C7C4DADF1569C890870B5958568ECF755D8C73389DF1C138353A242414F853B0E7C85A0C4D4E3F4949139D"),
+ InverseQ = ByteUtils.HexToByteArray("7B6E2406FD03BC75EA22AB94A8D242506A6BBFE36BC8132DBBCE50B8425425062B697AFA180F5685E90E11EB5712D2E6E2B24E2A1E7C75D5940E08301E824470EF38561BE3E9D05F9FCA8E6F69A028A928E85E58212E789BA577B80378D7A995FA6AFEA74BE364661A679F82776C5905F43F7A35692986271E594E1D11F9668D"),
+ };
+
+ if (!includePrivateKey)
+ {
+ return RSA.Create(new RSAParameters { Modulus = rsaParameters.Modulus, Exponent = rsaParameters.Exponent });
+ }
+
+ return RSA.Create(rsaParameters);
+ }
+
+ private static MLDsa CreateMLDsa(MLDsaAlgorithm algorithm, bool includePrivateKey)
+ {
+ MLDsaNistTestCase nistKey = MLDsaTestsData.GetPassingNistTestCase(algorithm);
+
+ if (includePrivateKey)
+ {
+ return MLDsa.ImportMLDsaSecretKey(algorithm, nistKey.SecretKey);
+ }
+ else
+ {
+ return MLDsa.ImportMLDsaPublicKey(algorithm, nistKey.PublicKey);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
index d9c5fb43a079a9..6bc45dacf77113 100644
--- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
+++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
@@ -1819,11 +1819,14 @@ protected virtual void Dispose(bool disposing) { }
public string ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
public string ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
public string ExportEncryptedPkcs8PrivateKeyPem(string password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; }
- public int ExportMLDsaPrivateSeed(System.Span destination) { throw null; }
+ public byte[] ExportMLDsaPrivateSeed() { throw null; }
+ public void ExportMLDsaPrivateSeed(System.Span destination) { }
protected abstract void ExportMLDsaPrivateSeedCore(System.Span destination);
- public int ExportMLDsaPublicKey(System.Span destination) { throw null; }
+ public byte[] ExportMLDsaPublicKey() { throw null; }
+ public void ExportMLDsaPublicKey(System.Span destination) { }
protected abstract void ExportMLDsaPublicKeyCore(System.Span destination);
- public int ExportMLDsaSecretKey(System.Span destination) { throw null; }
+ public byte[] ExportMLDsaSecretKey() { throw null; }
+ public void ExportMLDsaSecretKey(System.Span destination) { }
protected abstract void ExportMLDsaSecretKeyCore(System.Span destination);
public byte[] ExportPkcs8PrivateKey() { throw null; }
public string ExportPkcs8PrivateKeyPem() { throw null; }
@@ -1839,14 +1842,18 @@ protected virtual void Dispose(bool disposing) { }
public static System.Security.Cryptography.MLDsa ImportFromEncryptedPem(string source, string password) { throw null; }
public static System.Security.Cryptography.MLDsa ImportFromPem(System.ReadOnlySpan source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportFromPem(string source) { throw null; }
+ public static System.Security.Cryptography.MLDsa ImportMLDsaPrivateSeed(System.Security.Cryptography.MLDsaAlgorithm algorithm, byte[] source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportMLDsaPrivateSeed(System.Security.Cryptography.MLDsaAlgorithm algorithm, System.ReadOnlySpan source) { throw null; }
+ public static System.Security.Cryptography.MLDsa ImportMLDsaPublicKey(System.Security.Cryptography.MLDsaAlgorithm algorithm, byte[] source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportMLDsaPublicKey(System.Security.Cryptography.MLDsaAlgorithm algorithm, System.ReadOnlySpan source) { throw null; }
+ public static System.Security.Cryptography.MLDsa ImportMLDsaSecretKey(System.Security.Cryptography.MLDsaAlgorithm algorithm, byte[] source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportMLDsaSecretKey(System.Security.Cryptography.MLDsaAlgorithm algorithm, System.ReadOnlySpan source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportPkcs8PrivateKey(byte[] source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportPkcs8PrivateKey(System.ReadOnlySpan source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportSubjectPublicKeyInfo(byte[] source) { throw null; }
public static System.Security.Cryptography.MLDsa ImportSubjectPublicKeyInfo(System.ReadOnlySpan source) { throw null; }
- public int SignData(System.ReadOnlySpan data, System.Span destination, System.ReadOnlySpan context = default(System.ReadOnlySpan)) { throw null; }
+ public byte[] SignData(byte[] data, byte[]? context = null) { throw null; }
+ public void SignData(System.ReadOnlySpan data, System.Span destination, System.ReadOnlySpan context = default(System.ReadOnlySpan)) { throw null; }
protected abstract void SignDataCore(System.ReadOnlySpan data, System.ReadOnlySpan context, System.Span destination);
public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; }
public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; }
@@ -1854,6 +1861,7 @@ protected virtual void Dispose(bool disposing) { }
public bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; }
protected abstract bool TryExportPkcs8PrivateKeyCore(System.Span destination, out int bytesWritten);
public bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; }
+ public bool VerifyData(byte[] data, byte[] signature, byte[]? context = null) { throw null; }
public bool VerifyData(System.ReadOnlySpan data, System.ReadOnlySpan signature, System.ReadOnlySpan context = default(System.ReadOnlySpan)) { throw null; }
protected abstract bool VerifyDataCore(System.ReadOnlySpan data, System.ReadOnlySpan context, System.ReadOnlySpan signature);
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/MLDsaX509SignatureGenerator.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/MLDsaX509SignatureGenerator.cs
index bc5d15737ea833..0dcfc3967c2672 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/MLDsaX509SignatureGenerator.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/MLDsaX509SignatureGenerator.cs
@@ -43,18 +43,13 @@ public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm)
// This generator only supports ML-DSA "Pure" signatures, but the overall design of
// CertificateRequest makes it easy for a hashAlgorithm value to get here.
- byte[] signature = new byte[_key.Algorithm.SignatureSizeInBytes];
- int written = _key.SignData(data, signature);
- Debug.Assert(written == signature.Length);
- return signature;
+ return _key.SignData(data);
}
protected override PublicKey BuildPublicKey()
{
Oid oid = new Oid(_key.Algorithm.Oid, null);
- byte[] pkBytes = new byte[_key.Algorithm.PublicKeySizeInBytes];
- int written = _key.ExportMLDsaPublicKey(pkBytes);
- Debug.Assert(written == pkBytes.Length);
+ byte[] pkBytes = _key.ExportMLDsaPublicKey();
return new PublicKey(
oid,
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
index f99ae1c64bf5f8..ecd9cd58095cf8 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs
@@ -966,13 +966,10 @@ public X509Certificate2 CopyWithPrivateKey(MLDsa privateKey)
throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey));
}
- byte[] pk1 = new byte[publicKey.Algorithm.PublicKeySizeInBytes];
- byte[] pk2 = new byte[pk1.Length];
+ byte[] pk1 = publicKey.ExportMLDsaPublicKey();
+ byte[] pk2 = privateKey.ExportMLDsaPublicKey();
- int w1 = publicKey.ExportMLDsaPublicKey(pk1);
- int w2 = privateKey.ExportMLDsaPublicKey(pk2);
-
- if (w1 != w2 || !pk1.AsSpan().SequenceEqual(pk2))
+ if (pk1.Length != pk2.Length || !pk1.AsSpan().SequenceEqual(pk2))
{
throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey));
}
diff --git a/src/libraries/System.Security.Cryptography/tests/MLDsaOpenSslTests.Unix.cs b/src/libraries/System.Security.Cryptography/tests/MLDsaOpenSslTests.Unix.cs
index d46cc47c17fa6a..8ad67d9a8963dc 100644
--- a/src/libraries/System.Security.Cryptography/tests/MLDsaOpenSslTests.Unix.cs
+++ b/src/libraries/System.Security.Cryptography/tests/MLDsaOpenSslTests.Unix.cs
@@ -94,11 +94,11 @@ public void MLDsaOpenSsl_DuplicateKeyHandleLifetime()
{
byte[] data = [ 1, 1, 2, 3, 5, 8 ];
byte[] context = [ 13, 21 ];
- byte[] oneSignature = new byte[MLDsaAlgorithm.MLDsa44.SignatureSizeInBytes];
+ byte[] oneSignature;
using (one)
{
- Assert.Equal(oneSignature.Length, one.SignData(data, oneSignature, context));
+ oneSignature = one.SignData(data, context);
VerifyInstanceIsUsable(one);
VerifyInstanceIsUsable(two);
}
@@ -116,11 +116,11 @@ public void MLDsaOpenSsl_DuplicateKeyHandleLifetime()
private static void VerifyInstanceIsUsable(MLDsaOpenSsl mldsa)
{
- byte[] seed = new byte[mldsa.Algorithm.PrivateSeedSizeInBytes];
- Assert.Equal(mldsa.Algorithm.PrivateSeedSizeInBytes, mldsa.ExportMLDsaPrivateSeed(seed)); // does not throw
+ byte[] seed = mldsa.ExportMLDsaPrivateSeed();
+ Assert.Equal(mldsa.Algorithm.PrivateSeedSizeInBytes, seed.Length);
- byte[] secretKey = new byte[mldsa.Algorithm.SecretKeySizeInBytes];
- Assert.Equal(mldsa.Algorithm.SecretKeySizeInBytes, mldsa.ExportMLDsaSecretKey(secretKey)); // does not throw
+ byte[] secretKey = mldsa.ExportMLDsaSecretKey();
+ Assert.Equal(mldsa.Algorithm.SecretKeySizeInBytes, secretKey.Length);
// usable
byte[] data = [ 1, 2, 3 ];
diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs
index c6d6aabab01bde..425cd1ed94b3bd 100644
--- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs
@@ -153,8 +153,7 @@ public static void PrivateKey_FromCertificate_CanExportPrivate_MLDsa()
{
Assert.NotNull(certKey);
byte[] expectedKey = MLDsaTestsData.IetfMLDsa44.SecretKey;
- byte[] actualKey = new byte[MLDsaAlgorithm.MLDsa44.SecretKeySizeInBytes];
- Assert.Equal(MLDsaAlgorithm.MLDsa44.SecretKeySizeInBytes, certKey.ExportMLDsaSecretKey(actualKey));
+ byte[] actualKey = certKey.ExportMLDsaSecretKey();
AssertExtensions.SequenceEqual(expectedKey, actualKey);
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertificateCreation/PrivateKeyAssociationTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertificateCreation/PrivateKeyAssociationTests.cs
index 34d3065b494ed2..cb32411d214250 100644
--- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertificateCreation/PrivateKeyAssociationTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertificateCreation/PrivateKeyAssociationTests.cs
@@ -715,13 +715,11 @@ public static void GetMLDsaPublicKeyTest()
using (MLDsa? certKey = GetMLDsaPublicKey(cert))
{
Assert.NotNull(certKey);
- byte[] publicKey = new byte[certKey.Algorithm.PublicKeySizeInBytes];
- Assert.Equal(publicKey.Length, certKey.ExportMLDsaPublicKey(publicKey));
+ byte[] publicKey = certKey.ExportMLDsaPublicKey();
AssertExtensions.SequenceEqual(MLDsaTestsData.IetfMLDsa44.PublicKey, publicKey);
// Verify the key is not actually private
- byte[] signature = new byte[certKey.Algorithm.SignatureSizeInBytes];
- Assert.ThrowsAny(() => certKey.SignData([1, 2, 3], signature));
+ Assert.ThrowsAny(() => certKey.SignData([1, 2, 3]));
}
// Cert with private key
@@ -729,13 +727,11 @@ public static void GetMLDsaPublicKeyTest()
using (MLDsa? certKey = GetMLDsaPublicKey(cert))
{
Assert.NotNull(certKey);
- byte[] publicKey = new byte[certKey.Algorithm.PublicKeySizeInBytes];
- Assert.Equal(publicKey.Length, certKey.ExportMLDsaPublicKey(publicKey));
+ byte[] publicKey = certKey.ExportMLDsaPublicKey();
AssertExtensions.SequenceEqual(MLDsaTestsData.IetfMLDsa44.PublicKey, publicKey);
// Verify the key is not actually private
- byte[] signature = new byte[certKey.Algorithm.SignatureSizeInBytes];
- Assert.ThrowsAny(() => certKey.SignData([1, 2, 3], signature));
+ Assert.ThrowsAny(() => certKey.SignData([1, 2, 3]));
}
}
@@ -759,8 +755,7 @@ public static void GetMLDsaPrivateKeyTest()
Assert.NotNull(certKey);
// Verify the key is actually private
- byte[] privateSeed = new byte[certKey.Algorithm.PrivateSeedSizeInBytes];
- Assert.Equal(privateSeed.Length, certKey.ExportMLDsaPrivateSeed(privateSeed));
+ byte[] privateSeed = certKey.ExportMLDsaPrivateSeed();
AssertExtensions.SequenceEqual(
MLDsaTestsData.IetfMLDsa44.PrivateSeed,
privateSeed);
@@ -803,9 +798,7 @@ public static void CheckCopyWithPrivateKey_MLDSA()
byte[] data = new byte[RandomNumberGenerator.GetInt32(97)];
RandomNumberGenerator.Fill(data);
- byte[] signature = new byte[pub.Algorithm.SignatureSizeInBytes];
- int written = priv.SignData(data, signature);
- Assert.Equal(signature.Length, written);
+ byte[] signature = priv.SignData(data);
Assert.True(pub.VerifyData(data, signature));
});
}
@@ -857,8 +850,7 @@ public static void CheckCopyWithPrivateKey_MLDsa_OtherMLDsa_SecretKey()
using (MLDsa certPrivateMLDsa = GetMLDsaPrivateKey(privCert))
{
- byte[] secretKey = new byte[certPrivateMLDsa.Algorithm.SecretKeySizeInBytes];
- Assert.Equal(secretKey.Length, certPrivateMLDsa.ExportMLDsaSecretKey(secretKey));
+ byte[] secretKey = certPrivateMLDsa.ExportMLDsaSecretKey();
AssertExtensions.SequenceEqual(
MLDsaTestsData.IetfMLDsa44.SecretKey,
secretKey);
@@ -870,7 +862,7 @@ public static void CheckCopyWithPrivateKey_MLDsa_OtherMLDsa_SecretKey()
privateMLDsa.TryExportPkcs8PrivateKeyHook = (_, out w) => { Assert.Fail(); w = 0; return false; };
// Ensure the key is actual a clone
- Assert.Equal(secretKey.Length, certPrivateMLDsa.ExportMLDsaSecretKey(secretKey));
+ secretKey = certPrivateMLDsa.ExportMLDsaSecretKey();
AssertExtensions.SequenceEqual(
MLDsaTestsData.IetfMLDsa44.SecretKey,
secretKey);
@@ -921,8 +913,7 @@ public static void CheckCopyWithPrivateKey_MLDsa_OtherMLDsa_PrivateSeed()
using (MLDsa certPrivateMLDsa = GetMLDsaPrivateKey(privCert))
{
- byte[] secretKey = new byte[certPrivateMLDsa.Algorithm.PrivateSeedSizeInBytes];
- Assert.Equal(secretKey.Length, certPrivateMLDsa.ExportMLDsaPrivateSeed(secretKey));
+ byte[] secretKey = certPrivateMLDsa.ExportMLDsaPrivateSeed();
AssertExtensions.SequenceEqual(
MLDsaTestsData.IetfMLDsa44.PrivateSeed,
secretKey);
@@ -933,7 +924,7 @@ public static void CheckCopyWithPrivateKey_MLDsa_OtherMLDsa_PrivateSeed()
privateMLDsa.TryExportPkcs8PrivateKeyHook = (_, out w) => { Assert.Fail(); w = 0; return false; };
// Ensure the key is actual a clone
- Assert.Equal(secretKey.Length, certPrivateMLDsa.ExportMLDsaPrivateSeed(secretKey));
+ secretKey = certPrivateMLDsa.ExportMLDsaPrivateSeed();
AssertExtensions.SequenceEqual(
MLDsaTestsData.IetfMLDsa44.PrivateSeed,
secretKey);
diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExportTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExportTests.cs
index bad6c553af54aa..70eb3214bf858c 100644
--- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExportTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExportTests.cs
@@ -419,8 +419,7 @@ public static void ExportPkcs12_MLDsa_Generated_Roundtrip(MLDsaKeyInfo info)
Assert.Equal(info.Algorithm, mldsa.Algorithm);
AssertExtensions.SequenceEqual(info.Certificate, reLoaded.RawData);
- byte[] actualSecretKey = new byte[info.SecretKey.Length];
- Assert.Equal(actualSecretKey.Length, mldsa.ExportMLDsaSecretKey(actualSecretKey));
+ byte[] actualSecretKey = mldsa.ExportMLDsaSecretKey();
AssertExtensions.SequenceEqual(info.SecretKey, actualSecretKey);
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs
index 2666f58d247934..922b33c02dab0d 100644
--- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs
@@ -685,8 +685,7 @@ public static void ReadMLDsa512PrivateKey_Seed_Pfx(X509KeyStorageFlags keyStorag
Assert.Equal(info.Algorithm, mldsa.Algorithm);
Assert.Equal("CN=LAMPS WG, O=IETF", cert.Subject);
- byte[] seed = new byte[info.PrivateSeed.Length];
- Assert.Equal(seed.Length, mldsa.ExportMLDsaPrivateSeed(seed));
+ byte[] seed = mldsa.ExportMLDsaPrivateSeed();
AssertExtensions.SequenceEqual(info.PrivateSeed, seed);
}
}
@@ -706,9 +705,8 @@ public static void ReadMLDsa512PrivateKey_ExpandedKey_Pfx(X509KeyStorageFlags ke
Assert.Equal(info.Algorithm, mldsa.Algorithm);
Assert.Equal("CN=LAMPS WG, O=IETF", cert.Subject);
- byte[] secretKey = new byte[info.SecretKey.Length];
- Assert.Throws(() => mldsa.ExportMLDsaPrivateSeed(secretKey));
- Assert.Equal(secretKey.Length, mldsa.ExportMLDsaSecretKey(secretKey));
+ Assert.Throws(() => mldsa.ExportMLDsaPrivateSeed());
+ byte[] secretKey = mldsa.ExportMLDsaSecretKey();
AssertExtensions.SequenceEqual(info.SecretKey, secretKey);
}
}
@@ -728,14 +726,11 @@ public static void ReadMLDsa512PrivateKey_Both_Pfx(X509KeyStorageFlags keyStorag
Assert.Equal(info.Algorithm, mldsa.Algorithm);
Assert.Equal("CN=LAMPS WG, O=IETF", cert.Subject);
- byte[] buffer = new byte[info.SecretKey.Length];
- Assert.Equal(info.PrivateSeed.Length, mldsa.ExportMLDsaPrivateSeed(buffer));
- AssertExtensions.SequenceEqual(info.PrivateSeed.AsSpan(), buffer.AsSpan(0, info.PrivateSeed.Length));
+ byte[] seed = mldsa.ExportMLDsaPrivateSeed();
+ AssertExtensions.SequenceEqual(info.PrivateSeed.AsSpan(), seed.AsSpan());
- Assert.Equal(info.SecretKey.Length, mldsa.ExportMLDsaSecretKey(buffer));
- AssertExtensions.SequenceEqual(
- info.SecretKey,
- buffer);
+ byte[] sk = mldsa.ExportMLDsaSecretKey();
+ AssertExtensions.SequenceEqual(info.SecretKey, sk);
}
}
diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs
index d08a420c20b779..a1e35ee0df9cc0 100644
--- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs
+++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs
@@ -916,8 +916,7 @@ private static void AssertKeysMatch(string keyPem, Func keyLoader, string
AssertExtensions.SequenceEqual(sharedSecret1, sharedSecret2);
break;
case (MLDsa mldsa, MLDsa mldsaPem):
- byte[] mldsaSignature = new byte[mldsa.Algorithm.SignatureSizeInBytes];
- Assert.Equal(mldsaSignature.Length, mldsa.SignData(data, mldsaSignature));
+ byte[] mldsaSignature = mldsa.SignData(data);
Assert.True(mldsaPem.VerifyData(data, mldsaSignature));
break;
case (SlhDsa slhDsa, SlhDsa slhDsaPem):