-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Enable ML-DSA on Windows #116291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable ML-DSA on Windows #116291
Conversation
Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Adds support for ML-DSA on Windows by implementing the algorithm over BCrypt, updating tests to disable certificate scenarios until NCrypt is available, and introducing helpers for PQC key blob handling.
- Updated tests to conditionally skip certificate- and empty-context tests on Windows.
- Introduced
PqcBlobHelpers
plus new BCrypt interop (SetProperty, SignHashPure, VerifySignaturePure). - Added a full Windows-side
MLDsaImplementation
usingSafeBCryptAlgorithmHandle
.
Reviewed Changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs | Use MLDsaTestHelpers.MLDsaCertificatesAreSupported for certificate tests on Windows |
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs | Added canary IsWindows10Version27858OrGreater for PQC support |
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/MLDsa/MLDsaTestHelpers.cs | New helper flags to disable cert/context tests on Windows |
src/libraries/Common/src/System/Security/Cryptography/PqcBlobHelpers.cs | New helper class to encode/decode ML-DSA blobs |
src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs | Full Windows BCrypt-based implementation for ML-DSA |
src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs | Registered the ML-DSA algorithm name |
Comments suppressed due to low confidence (1)
src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs:34
- The algorithm name passed to BCryptOpenAlgorithmProvider should match the native BCRYPT_MLDSA_ALGORITHM constant (likely "MLDSA" without a hyphen). Using
"ML-DSA"
may cause the provider open call to fail.
public const string MLDsa = "ML-DSA"; // BCRYPT_MLDSA_ALGORITHM
1623d18
to
20b4e85
Compare
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptSetProperty.cs
Outdated
Show resolved
Hide resolved
int cbInput, | ||
int dwFlags); | ||
|
||
internal static unsafe void BCryptSetSZProperty(SafeBCryptHandle hObject, string pszProperty, string pszValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with the helper, I think, but once we're adding it we should use it. So as a followup to this PR we should delete the public static partial NTSTATUS BCryptSetProperty(SafeAlgorithmHandle hObject, string pszProperty, string pbInput, int cbInput, int dwFlags);
import in Interop\Windows\BCrypt\Cng.cs, and change all the callers of that to use this.
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptSignHash.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptVerifySignature.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.NotSupported.cs
Outdated
Show resolved
Hide resolved
...libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateKeyPair.cs
Outdated
Show resolved
Hide resolved
SafeBCryptHandle hObject, | ||
string pszProperty, | ||
void* pbInput, | ||
int cbInput, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cbInput
should be a uint
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most other places use int for cb and dwFlags
. Is the guidance that if we add new interop signatures we should use uint
(when the underlying native API is ULONG
)?
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptSetProperty.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptSignHash.cs
Outdated
Show resolved
Hide resolved
src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
Outdated
Show resolved
Hide resolved
ReadOnlySpan<byte> data, | ||
EncodeBlobFunc<TResult> callback) | ||
{ | ||
int blobHeaderSize = Unsafe.SizeOf<BCRYPT_PQDSA_KEY_BLOB>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be Marshal.SizeOf
since we care about the marshal size, not the managed size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that seems to only be relevant if we let the marshalling be done automatically via Marshal.PtrToStructure
or Marshal.StructureToPtr
. For example:
Console.WriteLine(Unsafe.SizeOf<S>()); // 1
Console.WriteLine(Marshal.SizeOf<S>()); // 4
[StructLayout(LayoutKind.Sequential)]
struct S
{
bool b;
}
That's because Marshal
will marshal a C# bool as a native 32 bit unsigned int. But in our code we're doing the marshalling ourselves by using the C# struct layout as the exact native layout (and projecting the BCRYPT_PQDSA_KEY_BLOB
on top of raw bytes from the rented buffer).
That being said, I don't think Unsafe.SizeOf<BCRYPT_PQDSA_KEY_BLOB>
is also 100% correct. Right now there's no padding, but if the struct was something like:
[StructLayout(LayoutKind.Sequential)]
struct S
{
long Foo;
int Bar;
// Let's say Windows has data starting at offset 12
}
Then sizeof(S)
would give 16 (with padding) and we would be off by 4. The right approach is probably calculating the total size without the struct and only using the struct as a way to project data in the backing array (blob->Bar = 42
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The padding conversation was resolved in #116291 (comment)
int keyLength = blob.cbKey; | ||
index += Unsafe.SizeOf<BCRYPT_PQDSA_KEY_BLOB>(); | ||
|
||
parameterSet = MemoryMarshal.Cast<byte, char>(blobBytes.Slice(index, parameterSetLength)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should use MemoryMarshal
here, one because it's not needed, and two I don't think this will work when you try to light it up for Microsoft.Bcl.Cryptography. All of this marshal logic is going to need to work on .NET Framework.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Microsoft.Bcl.Cryptography already references System.Memory where MemoryMarshal.Cast<,>
is available for netstandard2.0 (and AsRef
can be built as foo.Cast<byte,T>()[0]
). This feels more natural to me rather than unsafe and pinning (maybe because I don't do much interop).
Adds the Windows implementation of ML-DSA using BCrypt as the underlying crypto provider. The MLDsa factory methods will create this implementation when called on Windows.
Certificates are not supported yet so tests requiring them are disabled on Windows. A PR following this one will add NCrypt and re-enable the tests again.
Contributes to #113502