Skip to content

Conversation

@bartonjs
Copy link
Member

@bartonjs bartonjs commented Sep 2, 2025

Not experimental:

  • MLDsaAlgorithm (whole type)
  • MLDsa (class name)
  • MLDsa.GenerateKey
  • MLDsa.ImportMLDsaPublicKey
  • MLDsa.ImportMLDsaPrivateKey
  • MLDsa.ImportMLDsaPrivateSeed
  • MLDsa.ExportMLDsaPublicKey
  • MLDsa.ExportMLDsaPrivateKey
  • MLDsa.ExportMLDsaPrivateSeed
  • MLDsa.SignData
  • MLDsa.VerifyData
  • Any *Core method for the above
  • CngAlgorithm.MLDsa
  • CngAlgorithmGroup.MLDsa
  • CngBlobFormat.PQDsaPublicBlob
  • CngBlobFormat.PQDsaPrivateBlob
  • CngBlobFormat.PQDsaPrivateSeedBlob

Experimental:

  • IETF Formats
    • MLDsa.ImportFromPem
    • MLDsa.ImportFromEncryptedPem
    • MLDsa.ImportPkcs8PrivateKey
    • MLDsa.ImportEncryptedPkcs8PrivateKey
    • MLDsa.ImportSubjectPublicKeyInfo
    • MLDsa.ExportPkcs8PrivateKey
    • MLDsa.ExportPkcs8PrivateKeyPem
    • MLDsa.ExportEncryptedPkcs8PrivateKey
    • MLDsa.ExportEncryptedPkcs8PrivateKeyPem
    • MLDsa.ExportSubjectPublicKeyInfo
    • MLDsa.ExportSubjectPublicKeyInfoPem
    • And any related Try* or *Core methods
  • Unsure we have the right shape
    • MLDsa.SignPreHash
    • MLDsa.VerifyPreHash
    • MLDsa.SignMu
    • MLDsa.VerifyMu
    • And any *Core methods powering them

Not experimental:
* MLDsaAlgorithm (whole type)
* MLDsa (class name)
* MLDsa.GenerateKey
* MLDsa.ImportMLDsaPublicKey
* MLDsa.ImportMLDsaPrivateKey
* MLDsa.ImportMLDsaPrivateSeed
* MLDsa.ExportMLDsaPublicKey
* MLDsa.ExportMLDsaPrivateKey
* MLDsa.ExportMLDsaPrivateSeed
* MLDsa.SignData
* MLDsa.VerifyData
* Any *Core method for the above

Experimental:
* IETF Formats
  * MLDsa.ImportFromPem
  * MLDsa.ImportFromEncryptedPem
  * MLDsa.ImportPkcs8PrivateKey
  * MLDsa.ImportEncryptedPkcs8PrivateKey
  * MLDsa.ImportSubjectPublicKeyInfo
  * MLDsa.ExportPkcs8PrivateKey
  * MLDsa.ExportPkcs8PrivateKeyPem
  * MLDsa.ExportEncryptedPkcs8PrivateKey
  * MLDsa.ExportEncryptedPkcs8PrivateKeyPem
  * MLDsa.ExportSubjectPublicKeyInfo
  * MLDsa.ExportSubjectPublicKeyInfoPem
  * And any *Core methods powering them
* Unsure we have the right shape
  * MLDsa.SignPreHash
  * MLDsa.VerifyPreHash
  * MLDsa.SignMu
  * MLDsa.VerifyMu
  * And any *Core methods powering them
Copilot AI review requested due to automatic review settings September 2, 2025 23:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR removes the experimental attribute from the base MLDsa class and MLDsaAlgorithm type while adding the experimental attribute to specific members that are still considered experimental. The primary purpose is to graduate core ML-DSA functionality from experimental status while keeping IETF format methods and certain signing methods as experimental.

  • Removes [Experimental] attribute from the base MLDsa class and MLDsaAlgorithm type
  • Adds [Experimental] attribute to specific IETF format import/export methods
  • Adds [Experimental] attribute to PreHash and Mu signing/verification methods

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs Updates reference assembly to move experimental attributes from class level to specific IETF and PreHash/Mu methods
src/libraries/Common/src/System/Security/Cryptography/MLDsaAlgorithm.cs Removes experimental attribute from the MLDsaAlgorithm class
src/libraries/Common/src/System/Security/Cryptography/MLDsa.cs Removes experimental attribute from MLDsa class and adds it to specific IETF format and PreHash/Mu methods

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@bartonjs
Copy link
Member Author

bartonjs commented Sep 2, 2025

I sanity checked this by adding a quick and dirty test to a library that doesn't already have global suppressions enabled:

private static byte[] Sign(byte[] key, byte[] message)
{
    using (MLDsa mldsa = MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa44, key))
    {
        return mldsa.SignData(message);
    }
}

No warnings.
Then:

-       return mldsa.SignData(message);
+       return mldsa.SignMu(message);

And SYSLIB5006 was correctly emitted.

Copy link
Member

@vcsjones vcsjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the derived types? MLDsaCng and MLDsaOpenSsl? When we did MLKem we also removed it from the whole class on the derived types and some of the "helper" stuff like CngAlgorithm and CngAlgorithmGroup.

@vcsjones
Copy link
Member

vcsjones commented Sep 2, 2025

Here's the MLKem PR for reference. #117831

@bartonjs
Copy link
Member Author

bartonjs commented Sep 3, 2025

What about the derived types?

Whoops.

some of the "helper" stuff like CngAlgorithm and CngAlgorithmGroup

CngAlgorithm and CngAlgorithmGroup I'm pretty solid on. The CngBlobFormats I also did, but I feel less good about... if I were Windows I'd be regretting the "PQ" name and trying to replace it before full GA... but I don't expect them to, so I went ahead and removed it.

If anyone wants to be hesitant on the PQ blobs, I'm happy to put it back. They're not really important for mainline usage.

@vcsjones
Copy link
Member

vcsjones commented Sep 3, 2025

If anyone wants to be hesitant on the PQ blobs, I'm happy to put it back. They're not really important for mainline usage.

I paused and thought about it. But yeah I think I am least concerned about these.

@bartonjs bartonjs added NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) api-ready-for-review API is ready for review, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work labels Sep 4, 2025
@bartonjs bartonjs added this to the 10.0.0 milestone Sep 4, 2025
@vcsjones
Copy link
Member

vcsjones commented Sep 5, 2025

@bartonjs why does this need API review?

@bartonjs
Copy link
Member Author

bartonjs commented Sep 5, 2025

why does this need API review?

It changes the ref.cs file. And no one is answering email 😄

@bartonjs
Copy link
Member Author

bartonjs commented Sep 9, 2025

API Review: Looks good as proposed

namespace System.Security.Cryptography
{
    public partial class CngAlgorithm
    {
-       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.CngAlgorithm MLDsa { get; }
    }
    public partial class CngAlgorithmGroup
    {
-       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.CngAlgorithmGroup MLDsa { get; }
    }
    public partial class CngBlobFormat
    {
-       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.CngKeyBlobFormat PQDsaPrivateBlob { get; }
-       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.CngKeyBlobFormat PQDsaPrivateSeedBlob { get; }
-       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.CngKeyBlobFormat PQDsaPublicBlob { get; }
    }
-   [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
    public partial class MLDsa : System.IDisposable
    {
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] ExportEncryptedPkcs8PrivateKey(string password, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public string ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public string ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public string ExportEncryptedPkcs8PrivateKeyPem(string password, System.Security.Cryptography.PbeParameters pbeParameters);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] ExportPkcs8PrivateKey();
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public string ExportPkcs8PrivateKeyPem();
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] ExportSubjectPublicKeyInfo();
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public string ExportSubjectPublicKeyInfoPem();
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportEncryptedPkcs8PrivateKey(string password, byte[] source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromEncryptedPem(System.ReadOnlySpan<char> source, System.ReadOnlySpan<byte> passwordBytes);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromEncryptedPem(System.ReadOnlySpan<char> source, System.ReadOnlySpan<char> password);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromEncryptedPem(string source, byte[] passwordBytes);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromEncryptedPem(string source, string password);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromPem(System.ReadOnlySpan<char> source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportFromPem(string source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportPkcs8PrivateKey(byte[] source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportPkcs8PrivateKey(System.ReadOnlySpan<byte> source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportSubjectPublicKeyInfo(byte[] source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public static System.Security.Cryptography.MLDsa ImportSubjectPublicKeyInfo(System.ReadOnlySpan<byte> source);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] SignMu(byte[] externalMu);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] SignMu(System.ReadOnlySpan<byte> externalMu);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public void SignMu(System.ReadOnlySpan<byte> externalMu, System.Span<byte> destination) { }
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        protected abstract void SignMuCore(System.ReadOnlySpan<byte> externalMu, System.Span<byte> destination);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public byte[] SignPreHash(byte[] hash, string hashAlgorithmOid, byte[]? context = null);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public void SignPreHash(System.ReadOnlySpan<byte> hash, System.Span<byte> destination, string hashAlgorithmOid, System.ReadOnlySpan<byte> context = default(System.ReadOnlySpan<byte>)) { }
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        protected abstract void SignPreHashCore(System.ReadOnlySpan<byte> hash, System.ReadOnlySpan<byte> context, string hashAlgorithmOid, System.Span<byte> destination);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool TryExportEncryptedPkcs8PrivateKey(string password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        protected abstract bool TryExportPkcs8PrivateKeyCore(System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool VerifyMu(byte[] externalMu, byte[] signature);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool VerifyMu(System.ReadOnlySpan<byte> externalMu, System.ReadOnlySpan<byte> signature);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        protected abstract bool VerifyMuCore(System.ReadOnlySpan<byte> externalMu, System.ReadOnlySpan<byte> signature);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool VerifyPreHash(byte[] hash, byte[] signature, string hashAlgorithmOid, byte[]? context = null);
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        public bool VerifyPreHash(System.ReadOnlySpan<byte> hash, System.ReadOnlySpan<byte> signature, string hashAlgorithmOid, System.ReadOnlySpan<byte> context = default(System.ReadOnlySpan<byte>));
+       [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
        protected abstract bool VerifyPreHashCore(System.ReadOnlySpan<byte> hash, System.ReadOnlySpan<byte> context, string hashAlgorithmOid, System.ReadOnlySpan<byte> signature);
}

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) blocking Marks issues that we want to fast track in order to unblock other important work labels Sep 9, 2025
@bartonjs bartonjs merged commit f839373 into dotnet:main Sep 9, 2025
90 of 96 checks passed
@bartonjs
Copy link
Member Author

/backport to release/10.0

@github-actions
Copy link
Contributor

Started backporting to release/10.0: https://github.com/dotnet/runtime/actions/runs/17598805465

@bartonjs bartonjs deleted the mldsa_less_experimental branch September 10, 2025 00:00
@github-actions github-actions bot locked and limited conversation to collaborators Oct 10, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api-approved API was approved in API review, it can be implemented area-System.Security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants