Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,65 @@ internal static bool MLDsaVerifyPreEncoded(
}
}

[LibraryImport(Libraries.CryptoNative)]
private static partial int CryptoNative_MLDsaSignExternalMu(
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
ReadOnlySpan<byte> mu, int muLength,
Span<byte> destination, int destinationLength);

internal static void MLDsaSignExternalMu(
SafeEvpPKeyHandle pkey,
ReadOnlySpan<byte> mu,
Span<byte> destination)
{
const int Success = 1;
const int SigningFailure = 0;

int ret = CryptoNative_MLDsaSignExternalMu(
pkey, GetExtraHandle(pkey),
mu, mu.Length,
destination, destination.Length);

if (ret != Success)
{
Debug.Assert(ret == SigningFailure, $"Unexpected return value {ret} from {nameof(CryptoNative_MLDsaSignExternalMu)}.");
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
}

[LibraryImport(Libraries.CryptoNative)]
private static partial int CryptoNative_MLDsaVerifyExternalMu(
SafeEvpPKeyHandle pkey, IntPtr extraHandle,
ReadOnlySpan<byte> mu, int muLength,
ReadOnlySpan<byte> signature, int signatureLength);

internal static bool MLDsaVerifyExternalMu(
SafeEvpPKeyHandle pkey,
ReadOnlySpan<byte> mu,
ReadOnlySpan<byte> signature)
{
const int ValidSignature = 1;
const int InvalidSignature = 0;

int ret = CryptoNative_MLDsaVerifyExternalMu(
pkey, GetExtraHandle(pkey),
mu, mu.Length,
signature, signature.Length);

if (ret == ValidSignature)
{
return true;
}
else if (ret == InvalidSignature)
{
return false;
}
else
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}
}

[LibraryImport(Libraries.CryptoNative)]
private static partial int CryptoNative_MLDsaExportSecretKey(SafeEvpPKeyHandle pkey, Span<byte> destination, int destinationLength);

Expand Down
13 changes: 13 additions & 0 deletions src/libraries/Common/src/System/Security/Cryptography/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,18 @@ internal static void ThrowIfAsnInvalidLength(ReadOnlySpan<byte> data)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
}

#if !BUILDING_PKCS
internal static void ThrowIfDestinationWrongLength(
Span<byte> destination,
int expectedLength,
[System.Runtime.CompilerServices.CallerArgumentExpression(nameof(destination))] string? paramName = null)
{
if (destination.Length != expectedLength)
{
throw new ArgumentException(SR.Format(SR.Argument_DestinationImprecise, expectedLength), paramName);
}
}
#endif
}
}
195 changes: 154 additions & 41 deletions src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,7 @@ public void Dispose()
/// </exception>
public void SignData(ReadOnlySpan<byte> data, Span<byte> destination, ReadOnlySpan<byte> context = default)
{
int signatureSizeInBytes = Algorithm.SignatureSizeInBytes;

if (destination.Length != signatureSizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, signatureSizeInBytes),
nameof(destination));
}
Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.SignatureSizeInBytes);

if (context.Length > MaxContextLength)
{
Expand Down Expand Up @@ -309,13 +302,7 @@ public bool VerifyData(byte[] data, byte[] signature, byte[]? context = default)
public void SignPreHash(ReadOnlySpan<byte> hash, Span<byte> destination, string hashAlgorithmOid, ReadOnlySpan<byte> context = default)
{
ArgumentNullException.ThrowIfNull(hashAlgorithmOid);

if (destination.Length != Algorithm.SignatureSizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, Algorithm.SignatureSizeInBytes),
nameof(destination));
}
Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.SignatureSizeInBytes);

if (context.Length > MaxContextLength)
{
Expand Down Expand Up @@ -507,6 +494,155 @@ public bool VerifyPreHash(byte[] hash, byte[] signature, string hashAlgorithmOid
new ReadOnlySpan<byte>(context));
}

/// <inheritdoc cref="SignMu(ReadOnlySpan{byte})"/>
/// <exception cref="ArgumentNullException"><paramref name="externalMu"/> is <see langword="null"/>.</exception>
public byte[] SignMu(byte[] externalMu)
{
ArgumentNullException.ThrowIfNull(externalMu);

return SignMu(new ReadOnlySpan<byte>(externalMu));
}

/// <summary>
/// Signs the specified externally computed signature mu (&#x3BC;) value.
/// </summary>
/// <param name="externalMu">
/// The signature mu value to sign.
/// </param>
/// <returns>
/// ML-DSA signature for the specified mu value.
/// </returns>
/// <exception cref="ArgumentException">
/// The buffer in <paramref name="externalMu"/> is the incorrect length for the signature mu value.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This instance has been disposed.
/// </exception>
/// <exception cref="CryptographicException">
/// <para>The instance represents only a public key.</para>
/// <para>-or-</para>
/// <para>An error occurred while signing the hash.</para>
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support signing with an externally computed mu value.
/// </exception>
/// <seealso cref="VerifyMu(byte[], byte[])"/>
public byte[] SignMu(ReadOnlySpan<byte> externalMu)
{
byte[] destination = new byte[Algorithm.SignatureSizeInBytes];
SignMu(externalMu, destination.AsSpan());
return destination;
}

/// <summary>
/// Signs the specified externally computed signature mu (&#x3BC;) value,
/// writing the signature into the provided buffer.
/// </summary>
/// <param name="externalMu">
/// The signature mu value to sign.
/// </param>
/// <param name="destination">
/// The buffer to receive the signature. Its length must be exactly
/// <see cref="MLDsaAlgorithm.SignatureSizeInBytes"/>.
/// </param>
/// <exception cref="ArgumentException">
/// <para>
/// The buffer in <paramref name="externalMu"/> is the incorrect length for the signature mu value.
/// </para>
/// <para>-or-</para>
/// <para>
/// The buffer in <paramref name="destination"/> is the incorrect length to receive the signature.
/// </para>
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This instance has been disposed.
/// </exception>
/// <exception cref="CryptographicException">
/// <para>The instance represents only a public key.</para>
/// <para>-or-</para>
/// <para>An error occurred while signing the hash.</para>
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support signing with an externally computed mu value.
/// </exception>
/// <seealso cref="VerifyMu(ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>
public void SignMu(ReadOnlySpan<byte> externalMu, Span<byte> destination)
{
if (externalMu.Length != Algorithm.MuSizeInBytes)
throw new ArgumentException(SR.Argument_MLDsaMuInvalidLength, nameof(externalMu));

Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.SignatureSizeInBytes);
ThrowIfDisposed();

SignMuCore(externalMu, destination);
}

/// <summary>
/// When overridden in a derived class, computes the remainder of the signature from the
/// precomputed mu (&#x3BC;) value, writing it into the provided buffer.
/// </summary>
/// <param name="externalMu">
/// The signature mu value to sign.
/// </param>
/// <param name="destination">
/// The buffer to receive the signature, which will always be the exactly correct size for the algorithm.
/// </param>
/// <exception cref="CryptographicException">
/// An error occurred while computing the signature.
/// </exception>
protected abstract void SignMuCore(ReadOnlySpan<byte> externalMu, Span<byte> destination);

/// <inheritdoc cref="VerifyMu(ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>
/// <exception cref="ArgumentNullException">
/// <paramref name="externalMu"/> or <paramref name="signature"/> is <see langword="null"/>.
/// </exception>
public bool VerifyMu(byte[] externalMu, byte[] signature)
{
ArgumentNullException.ThrowIfNull(externalMu);
ArgumentNullException.ThrowIfNull(signature);

return VerifyMu(new ReadOnlySpan<byte>(externalMu), new ReadOnlySpan<byte>(signature));
}

/// <summary>
/// Verifies that a digital signature is valid for the provided externally computed signature mu (&#x3BC;) value.
/// </summary>
/// <param name="externalMu">The signature mu value.</param>
/// <param name="signature">The signature to verify.</param>
/// <returns>
/// <see langword="true"/> if the digital signature is valid for the provided mu value;
/// otherwise, <see langword="false"/>.
/// </returns>
/// <exception cref="ObjectDisposedException">
/// This instance has been disposed.
/// </exception>
/// <exception cref="CryptographicException">An error occurred while verifying the mu value.</exception>
/// <exception cref="PlatformNotSupportedException">
/// The current platform does not support verification with an externally computed mu value.
/// </exception>
public bool VerifyMu(ReadOnlySpan<byte> externalMu, ReadOnlySpan<byte> signature)
{
if (externalMu.Length != Algorithm.MuSizeInBytes || signature.Length != Algorithm.SignatureSizeInBytes)
{
return false;
}

ThrowIfDisposed();

return VerifyMuCore(externalMu, signature);
}

/// <summary>
/// When overridden in a derived class,
/// verifies that a digital signature is valid for the provided externally computed signature mu (&#x3BC;) value.
/// </summary>
/// <param name="externalMu">The signature mu value.</param>
/// <param name="signature">The signature to verify.</param>
/// <returns>
/// <see langword="true"/> if the mu value is valid; otherwise, <see langword="false"/>.
/// </returns>
protected abstract bool VerifyMuCore(ReadOnlySpan<byte> externalMu, ReadOnlySpan<byte> signature);

/// <summary>
/// Exports the public-key portion of the current key in the X.509 SubjectPublicKeyInfo format.
/// </summary>
Expand Down Expand Up @@ -1063,15 +1199,7 @@ public byte[] ExportMLDsaPublicKey()
/// </remarks>
public void ExportMLDsaPublicKey(Span<byte> destination)
{
int publicKeySizeInBytes = Algorithm.PublicKeySizeInBytes;

if (destination.Length != publicKeySizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, publicKeySizeInBytes),
nameof(destination));
}

Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.PublicKeySizeInBytes);
ThrowIfDisposed();

ExportMLDsaPublicKeyCore(destination);
Expand Down Expand Up @@ -1113,15 +1241,7 @@ public byte[] ExportMLDsaSecretKey()
/// </exception>
public void ExportMLDsaSecretKey(Span<byte> destination)
{
int secretKeySizeInBytes = Algorithm.SecretKeySizeInBytes;

if (destination.Length != secretKeySizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, secretKeySizeInBytes),
nameof(destination));
}

Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.SecretKeySizeInBytes);
ThrowIfDisposed();

ExportMLDsaSecretKeyCore(destination);
Expand Down Expand Up @@ -1161,14 +1281,7 @@ public byte[] ExportMLDsaPrivateSeed()
/// </exception>
public void ExportMLDsaPrivateSeed(Span<byte> destination)
{
int privateSeedSizeInBytes = Algorithm.PrivateSeedSizeInBytes;
if (destination.Length != privateSeedSizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, privateSeedSizeInBytes),
nameof(destination));
}

Helpers.ThrowIfDestinationWrongLength(destination, Algorithm.PrivateSeedSizeInBytes);
ThrowIfDisposed();

ExportMLDsaPrivateSeedCore(destination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ public sealed class MLDsaAlgorithm : IEquatable<MLDsaAlgorithm>
/// </value>
public int SignatureSizeInBytes { get; }

/// <summary>
/// Gets the size, in bytes, of the mu (&#x3BC;) value for the current ML-DSA algorithm.
/// </summary>
/// <value>
/// The size, in bytes, of the mu (&#x3BC;) value for the current ML-DSA algorithm.
/// </value>
public int MuSizeInBytes => 64;

internal string Oid { get; }
internal int LambdaCollisionStrength { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ public MLDsaCng(CngKey key)
_key = duplicateKey;
}

/// <inheritdoc />
protected override void SignMuCore(ReadOnlySpan<byte> mu, Span<byte> destination) =>
throw new PlatformNotSupportedException();

/// <inheritdoc />
protected override bool VerifyMuCore(ReadOnlySpan<byte> mu, ReadOnlySpan<byte> signature) =>
throw new PlatformNotSupportedException();

private static MLDsaAlgorithm AlgorithmFromHandleWithPlatformCheck(CngKey key, out CngKey duplicateKey)
{
if (!Helpers.IsOSPlatformWindows)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ protected override void SignPreHashCore(ReadOnlySpan<byte> hash, ReadOnlySpan<by
protected override bool VerifyPreHashCore(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> context, string hashAlgorithmOid, ReadOnlySpan<byte> signature) =>
throw new PlatformNotSupportedException();

protected override void SignMuCore(ReadOnlySpan<byte> mu, Span<byte> destination) =>
throw new PlatformNotSupportedException();

protected override bool VerifyMuCore(ReadOnlySpan<byte> mu, ReadOnlySpan<byte> signature) =>
throw new PlatformNotSupportedException();

protected override void ExportMLDsaPublicKeyCore(Span<byte> destination) =>
throw new PlatformNotSupportedException();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ protected override bool VerifyPreHashCore(
return Interop.BCrypt.BCryptVerifySignaturePqcPreHash(_key, hash, hashAlgorithmIdentifier, context, signature);
}

protected override void SignMuCore(ReadOnlySpan<byte> externalMu, Span<byte> destination) =>
throw new PlatformNotSupportedException();

protected override bool VerifyMuCore(ReadOnlySpan<byte> externalMu, ReadOnlySpan<byte> signature) =>
throw new PlatformNotSupportedException();

internal static partial MLDsaImplementation GenerateKeyImpl(MLDsaAlgorithm algorithm)
{
Debug.Assert(SupportsAny());
Expand Down
Loading
Loading