Skip to content

Add ML-DSA #113502

Open
1 of 1 issue completed
Open
Add ML-DSA#113502
Feature
1 of 1 issue completed
@bartonjs

Description

@bartonjs

As part of the Post-Quantum Cryptography effort, we should add support for Module-Lattice-Based Digital Signature Algorithm (ML-DSA).

As a signature algorithm, ML-DSA should be available as a primitive, as well as integrated into:

  • X509Certificate2
  • SignedXml
  • COSE
  • TLS (no API required, carried with the certificate)

Based on Elliptic-Curve Digital Signature Algorithm's name of "EC-DSA" becoming ECDsa, the class name for ML-DSA is proposed as MLDsa.

namespace System.Security.Cryptography
{
    [Experimental("SYSLIB5006")]
    public abstract class MLDsa : IDisposable
    {
        public static bool IsSupported { get; }

        protected MLDsa(MLDsaAlgorithm algorithm);

        public MLDsaAlgorithm Algorithm { get; }
        public int SignatureSizeInBytes { get; }
        public void Dispose();

        public int SignData(
            ReadOnlySpan<byte> data,
            Span<byte> destination,
            ReadOnlySpan<byte> context = default);
        public bool VerifyData(
            ReadOnlySpan<byte> data,
            ReadOnlySpan<byte> signature,
            ReadOnlySpan<byte> context = default);

        public int SignPreHash(
            ReadOnlySpan<byte> hash,         
            Span<byte> destination,
            HashAlgorithmName preHashAlgorithm,
            ReadOnlySpan<byte> context = default);
        public bool SignPreHash(
            ReadOnlySpan<byte> hash,         
            ReadOnlySpan<byte> signature,
            HashAlgorithmName preHashAlgorithm,
            ReadOnlySpan<byte> context = default);

        public byte[] ExportSubjectPublicKeyInfo();
        public bool TryExportSubjectPublicKeyInfo(Span<byte> destination, out int bytesWritten);
        public string ExportSubjectPublicKeyInfoPem();

        public byte[] ExportPkcs8PrivateKey();
        public bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten);
        public string ExportPkcs8PrivateKeyPem();

        public byte[] ExportEncryptedPkcs8PrivateKey(
            ReadOnlySpan<char> password,
            PbeParameters pbeParameters);
        public byte[] ExportEncryptedPkcs8PrivateKey(
            ReadOnlySpan<byte> passwordBytes,
            PbeParameters pbeParameters);
        public bool TryExportEncryptedPkcs8PrivateKey(
            ReadOnlySpan<char> password,
            PbeParameters pbeParameters,
            Span<byte> destination,
            out int bytesWritten);
        public bool TryExportEncryptedPkcs8PrivateKey(
            ReadOnlySpan<byte> passwordBytes,
            PbeParameters pbeParameters,
            Span<byte> destination,
            out int bytesWritten);
        public string ExportEncryptedPkcs8PrivateKeyPem(
            ReadOnlySpan<char> password,
            PbeParameters pbeParameters);
        public string ExportEncryptedPkcs8PrivateKeyPem(
            ReadOnlySpan<byte> passwordBytes,
            PbeParameters pbeParameters);

        public int ExportMLDsaPublicKey(Span<byte> destination);
        public int ExportMLDsaSecretKey(Span<byte> destination);
        public int ExportMLDsaPrivateSeed(Span<byte> destination);

        public static MLDsa GenerateMLDsa44Key();
        public static MLDsa GenerateMLDsa65Key();
        public static MLDsa GenerateMLDsa87Key();

        public static MLDsa ImportSubjectPublicKeyInfo(ReadOnlySpan<byte> source);
        public static MLDsa ImportPkcs8PrivateKey(ReadOnlySpan<byte> source);
        public static MLDsa ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte> passwordBytes, ReadOnlySpan<byte> source);
        public static MLDsa ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<char> password, ReadOnlySpan<byte> source);
        public static MLDsa ImportFromPem(ReadOnlySpan<char> source);
        public static MLDsa ImportFromEncryptedPem(ReadOnlySpan<char> source, ReadOnlySpan<char> password);
        public static MLDsa ImportFromEncryptedPem(ReadOnlySpan<char> source, ReadOnlySpan<byte> passwordBytes);
        public static MLDsa ImportMLDsaPublicKey(MLDsaAlgorithm algorithm, ReadOnlySpan<byte> source);
        public static MLDsa ImportMLDsaSecretKey(MLDsaAlgorithm algorithm, ReadOnlySpan<byte> source);
        public static MLDsa ImportMLDsaPrivateSeed(MLDsaAlgorithm algorithm, ReadOnlySpan<byte> source);

        protected void ThrowIfDisposed();
        protected virtual void Dispose(bool disposing);

        protected abstract void SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, Span<byte> destination);
        protected abstract bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature);
        protected abstract void SignPreHashCore(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> context, HashAlgorithmName preHashAlgorithm, Span<byte> destination);
        protected abstract bool VerifyPreHashCore(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> context, HashAlgorithmName preHashAlgorithm, ReadOnlySpan<byte> signature);

        protected abstract void ExportMLDsaPublicKeyCore(Span<byte> destination);
        protected abstract void ExportMLDsaSecretKeyCore(Span<byte> destination);
        protected abstract void ExportMLDsaPrivateSeedCore(Span<byte> destination);
    }

    [DebuggerDisplay("{Name,nq}")]
    [Experimental("SYSLIB5006")]
    public sealed class MLDsaAlgorithm
    {
        private MLDsaAlgorithm();

        public static MLDsaAlgorithm MLDsa44 { get; }
        public static MLDsaAlgorithm MLDsa65 { get; }
        public static MLDsaAlgorithm MLDsa87 { get; }

        public string Name { get; }
        public int PublicKeySizeInBytes { get; }
        public int SecretKeySizeInBytes { get; }
        public int SignatureSizeInBytes { get; }
    }

    [Experimental("SYSLIB5006")]
    public class MLDsaCng : MLDsa
    {
         public MLDsaCng(CngKey key);
         // On ECDsaCng this is an allocating property. Changed to a method here.
         public CngKey GetCngKey();
    }

    [Experimental("SYSLIB5006")]
    public class MLDsaOpenSsl : MLDsa
    {
         public MLDsaOpenSsl(SafeEvpPKeyHandle keyHandle);
         public SafeEvpPKeyHandle DuplicateKeyHandle();
    }
}

namespace System.Security.Cryptography.X509Certificates
{
    // Extension class to enable porting to Microsoft.Bcl.Cryptography
    [Experimental("SYSLIB5006")]
    public sealed class MLDsaCertificateExtensions
    {
        public static MLDsa GetMLDsaPublicKey(this X509Certificate2 certificate);
        public static MLDsa GetMLDsaPrivateKey(this X509Certificate2 certificate);
    }

    public partial class CertificateRequest
    {
        [Experimental("SYSLIB5006")]
        public CertificateRequest(string subjectName, MLDsa key);
        [Experimental("SYSLIB5006")]
        public CertificateRequest(X500DistinguishedName subjectName, MLDsa key);
    }

#if NET10_OR_GREATER
    public partial class X509SignatureGenerator
    {
        [Experimental("SYSLIB5006")]
        public static X509SignatureGenerator CreateForMLDsa(MLDsa key);
    }
#endif
}

#if NET10_OR_GREATER
namespace System.Security.Cryptography.Pkcs
{
    public partial class CmsSigner
    {
        [Experimental("SYSLIB5006")]
        public CmsSigner(
            X509Certificate2 certificate,
            MLDsa privateKey,
            SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier);
    }
}
#endif

namespace System.Security.Cryptography.Cose
{
    public partial class CoseSigner
    {
        // As PrivateKey is marked as non-nullable, this will set it to an instance of a non-public type that wraps the key.
        [Experimental("SYSLIB5006")]
        public CoseSigner(MLDsa key);
    }
}

Sub-issues

Metadata

Metadata

Assignees

Labels

area-System.Securitycryptographic-docs-impactIssues impacting cryptographic docs. Cleared and reused after documentation is updated each release.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions