Skip to content

Commit 6afa19d

Browse files
Add stubs for CompositeMLDsa APIs (#118520)
1 parent 60b5538 commit 6afa19d

File tree

14 files changed

+522
-0
lines changed

14 files changed

+522
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Security.Cryptography
5+
{
6+
public sealed partial class CompositeMLDsaCng : CompositeMLDsa
7+
{
8+
public partial CngKey GetKey() =>
9+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
10+
11+
/// <inheritdoc/>
12+
protected override int SignDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, Span<byte> destination) =>
13+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
14+
15+
/// <inheritdoc/>
16+
protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span<byte> destination, out int bytesWritten) =>
17+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
18+
19+
/// <inheritdoc/>
20+
protected override bool TryExportCompositeMLDsaPublicKeyCore(Span<byte> destination, out int bytesWritten) =>
21+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
22+
23+
/// <inheritdoc/>
24+
protected override bool TryExportPkcs8PrivateKeyCore(Span<byte> destination, out int bytesWritten) =>
25+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
26+
27+
/// <inheritdoc/>
28+
protected override bool VerifyDataCore(ReadOnlySpan<byte> data, ReadOnlySpan<byte> context, ReadOnlySpan<byte> signature) =>
29+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
30+
}
31+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Runtime.Versioning;
6+
7+
namespace System.Security.Cryptography
8+
{
9+
/// <summary>
10+
/// Provides a Cryptography Next Generation (CNG) implementation of Composite ML-DSA.
11+
/// </summary>
12+
/// <remarks>
13+
/// <para>
14+
/// Developers are encouraged to program against the <see cref="CompositeMLDsa" /> base class,
15+
/// rather than any specific derived class.
16+
/// The derived classes are intended for interop with the underlying system
17+
/// cryptographic libraries.
18+
/// </para>
19+
/// </remarks>
20+
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
21+
public sealed partial class CompositeMLDsaCng : CompositeMLDsa
22+
{
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="CompositeMLDsaCng"/> class by using the specified <see cref="CngKey"/>.
25+
/// </summary>
26+
/// <param name="key">
27+
/// The key that will be used as input to the cryptographic operations performed by the current object.
28+
/// </param>
29+
/// <exception cref="ArgumentNullException">
30+
/// <paramref name="key"/> is <see langword="null"/>.
31+
/// </exception>
32+
/// <exception cref="ArgumentException">
33+
/// <paramref name="key"/> does not specify a Composite ML-DSA group.
34+
/// </exception>
35+
/// <exception cref="PlatformNotSupportedException">
36+
/// Cryptography Next Generation (CNG) classes are not supported on this system.
37+
/// </exception>
38+
[SupportedOSPlatform("windows")]
39+
public CompositeMLDsaCng(CngKey key)
40+
: base(AlgorithmFromHandle(key))
41+
{
42+
throw new PlatformNotSupportedException();
43+
}
44+
45+
private static CompositeMLDsaAlgorithm AlgorithmFromHandle(CngKey key) =>
46+
throw new PlatformNotSupportedException();
47+
48+
/// <summary>
49+
/// Gets a new <see cref="CngKey" /> representing the key used by the current instance.
50+
/// </summary>
51+
/// <exception cref="ObjectDisposedException">
52+
/// This instance has been disposed.
53+
/// </exception>
54+
/// <remarks>
55+
/// This <see cref="CngKey"/> object is not the same as the one passed to <see cref="CompositeMLDsaCng(CngKey)"/>,
56+
/// if that constructor was used. However, it will point to the same CNG key.
57+
/// </remarks>
58+
public partial CngKey GetKey();
59+
}
60+
}

src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.Forwards.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#if NET10_0_OR_GREATER
1515
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsa))]
1616
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsaAlgorithm))]
17+
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsaCng))]
1718
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsa))]
1819
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsaAlgorithm))]
1920
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsaCng))]

src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@
497497
Link="Common\System\Security\Cryptography\CngHelpers.SignVerify.cs" />
498498
<Compile Include="$(CommonPath)System\Security\Cryptography\CngPkcs8.Shared.cs"
499499
Link="Common\System\Security\Cryptography\CngPkcs8.Shared.cs" />
500+
<Compile Include="$(CommonPath)System\Security\Cryptography\CompositeMLDsaCng.cs"
501+
Link="Common\System\Security\Cryptography\CompositeMLDsaCng.cs" />
502+
<Compile Include="$(CommonPath)System\Security\Cryptography\CompositeMLDsaCng.Windows.cs"
503+
Link="Common\System\Security\Cryptography\CompositeMLDsaCng.Windows.cs" />
500504
<Compile Include="$(CommonPath)System\Security\Cryptography\KeyPropertyName.cs"
501505
Link="Common\System\Security\Cryptography\KeyPropertyName.cs" />
502506
<Compile Include="$(CommonPath)System\Security\Cryptography\MLDsaCng.cs"

src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateKeyAccessors.cs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,154 @@ public static X509Certificate2 CopyWithPrivateKey(this X509Certificate2 certific
395395
#endif
396396
}
397397

398+
/// <summary>
399+
/// Gets the <see cref="CompositeMLDsa"/> public key from this certificate.
400+
/// </summary>
401+
/// <param name="certificate">
402+
/// The X.509 certificate that contains the public key.
403+
/// </param>
404+
/// <returns>
405+
/// The public key, or <see langword="null"/> if this certificate does not have a Composite ML-DSA public key.
406+
/// </returns>
407+
/// <exception cref="ArgumentNullException">
408+
/// <paramref name="certificate"/> is <see langword="null"/>.
409+
/// </exception>
410+
/// <exception cref="PlatformNotSupportedException">
411+
/// The certificate has a Composite ML-DSA public key, but the platform does not support Composite ML-DSA.
412+
/// </exception>
413+
/// <exception cref="CryptographicException">
414+
/// The public key was invalid, or otherwise could not be imported.
415+
/// </exception>
416+
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
417+
public static CompositeMLDsa? GetCompositeMLDsaPublicKey(this X509Certificate2 certificate)
418+
{
419+
ArgumentNullException.ThrowIfNull(certificate);
420+
421+
#if NET10_0_OR_GREATER
422+
return certificate.GetCompositeMLDsaPublicKey();
423+
#else
424+
if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(certificate.GetKeyAlgorithm()) is null)
425+
{
426+
return null;
427+
}
428+
429+
ArraySegment<byte> encoded = GetCertificateSubjectPublicKeyInfo(certificate);
430+
431+
try
432+
{
433+
return CompositeMLDsa.ImportSubjectPublicKeyInfo(encoded);
434+
}
435+
finally
436+
{
437+
// SubjectPublicKeyInfo does not need to clear since it's public
438+
CryptoPool.Return(encoded, clearSize: 0);
439+
}
440+
#endif
441+
}
442+
443+
/// <summary>
444+
/// Gets the <see cref="CompositeMLDsa"/> private key from this certificate.
445+
/// </summary>
446+
/// <param name="certificate">
447+
/// The X.509 certificate that contains the private key.
448+
/// </param>
449+
/// <returns>
450+
/// The private key, or <see langword="null"/> if this certificate does not have a Composite ML-DSA private key.
451+
/// </returns>
452+
/// <exception cref="ArgumentNullException">
453+
/// <paramref name="certificate"/> is <see langword="null"/>.
454+
/// </exception>
455+
/// <exception cref="PlatformNotSupportedException">
456+
/// Retrieving a Composite ML-DSA private key from a certificate is not supported on this platform.
457+
/// </exception>
458+
/// <exception cref="CryptographicException">
459+
/// An error occurred accessing the private key.
460+
/// </exception>
461+
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
462+
public static CompositeMLDsa? GetCompositeMLDsaPrivateKey(this X509Certificate2 certificate)
463+
{
464+
ArgumentNullException.ThrowIfNull(certificate);
465+
466+
#if NET10_0_OR_GREATER
467+
return certificate.GetCompositeMLDsaPrivateKey();
468+
#else
469+
if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(certificate.GetKeyAlgorithm()) is null)
470+
{
471+
return null;
472+
}
473+
474+
throw new PlatformNotSupportedException();
475+
#endif
476+
}
477+
478+
/// <summary>
479+
/// Combines a private key with a certificate containing the associated public key into a
480+
/// new instance that can access the private key.
481+
/// </summary>
482+
/// <param name="certificate">
483+
/// The X.509 certificate that contains the public key.
484+
/// </param>
485+
/// <param name="privateKey">
486+
/// The Composite ML-DSA private key that corresponds to the Composite ML-DSA public key in this certificate.
487+
/// </param>
488+
/// <returns>
489+
/// A new certificate with the <see cref="X509Certificate2.HasPrivateKey" /> property set to <see langword="true"/>.
490+
/// The current certificate isn't modified.
491+
/// </returns>
492+
/// <exception cref="ArgumentNullException">
493+
/// <paramref name="certificate"/> or <paramref name="privateKey"/> is <see langword="null"/>.
494+
/// </exception>
495+
/// <exception cref="ArgumentException">
496+
/// The specified private key doesn't match the public key for this certificate.
497+
/// </exception>
498+
/// <exception cref="InvalidOperationException">
499+
/// The certificate already has an associated private key.
500+
/// </exception>
501+
/// <exception cref="PlatformNotSupportedException">
502+
/// Combining a certificate and a Composite ML-DSA private key is not supported on this platform.
503+
/// </exception>
504+
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
505+
public static X509Certificate2 CopyWithPrivateKey(this X509Certificate2 certificate, CompositeMLDsa privateKey)
506+
{
507+
ArgumentNullException.ThrowIfNull(certificate);
508+
ArgumentNullException.ThrowIfNull(privateKey);
509+
510+
#if NET10_0_OR_GREATER
511+
return certificate.CopyWithPrivateKey(privateKey);
512+
#elif NETSTANDARD
513+
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa)));
514+
#else
515+
if (!Helpers.IsOSPlatformWindows)
516+
throw new PlatformNotSupportedException();
517+
518+
if (certificate.HasPrivateKey)
519+
throw new InvalidOperationException(SR.Cryptography_Cert_AlreadyHasPrivateKey);
520+
521+
using (CompositeMLDsa? publicKey = GetCompositeMLDsaPublicKey(certificate))
522+
{
523+
if (publicKey is null)
524+
{
525+
throw new ArgumentException(SR.Cryptography_PrivateKey_WrongAlgorithm);
526+
}
527+
528+
if (publicKey.Algorithm != privateKey.Algorithm)
529+
{
530+
throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey));
531+
}
532+
533+
byte[] pk1 = publicKey.ExportCompositeMLDsaPublicKey();
534+
byte[] pk2 = privateKey.ExportCompositeMLDsaPublicKey();
535+
536+
if (!pk1.SequenceEqual(pk2))
537+
{
538+
throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey));
539+
}
540+
}
541+
542+
throw new PlatformNotSupportedException();
543+
#endif
544+
}
545+
398546
#if !NET10_0_OR_GREATER
399547
private static ArraySegment<byte> GetCertificateSubjectPublicKeyInfo(X509Certificate2 certificate)
400548
{

src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netstandard21.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public sealed partial class CmsSigner
1616
{
1717
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { }
1818
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
19+
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.CompositeMLDsa? privateKey) { }
20+
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
1921
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.MLDsa? privateKey) { }
2022
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.RSA? privateKey, System.Security.Cryptography.RSASignaturePadding? signaturePadding) { }
2123
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]

src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? c
133133
{
134134
}
135135

136+
#if NET || NETSTANDARD2_1
137+
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
138+
public
139+
#else
140+
private
141+
#endif
142+
CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? certificate, CompositeMLDsa? privateKey)
143+
: this(signerIdentifierType, certificate, privateKey, signaturePadding: null)
144+
{
145+
throw new PlatformNotSupportedException();
146+
}
147+
136148
/// <summary>
137149
/// Initializes a new instance of the CmsSigner class with a specified signer
138150
/// certificate, subject identifier type, private key object, and RSA signature padding.

0 commit comments

Comments
 (0)