Add SLH-DSA
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- csharp
- Domain
- cryptography, security
Research direction
Start with the linked Post-Quantum Cryptography effort and the proposed SlhDsa and SlhDsaAlgorithm API in this issue. Review how X509Certificate2, SignedXml, COSE, and TLS currently integrate signature algorithms. Done means SLH-DSA is available as a primitive and integrated into the listed certificate, XML, COSE, and TLS paths.
Written by the indexing model from the issue text.
Description
As part of the Post-Quantum Cryptography effort, we should add support for Stateless Hash-Based Digital Signature Algorithm (SLH-DSA).
As a signature algorithm, SLH-DSA should be available as a primitive, as well as integrated into:
- X509Certificate2
- SignedXml
- COSE
- TLS (no API required, carried with the certificate)
As the algorithm (family) name is generally written as SLH-DSA, the .NET name is composed of the concatenation of two separate three-letter initialisms, so two capital letters and 4 lowercase: SlhDsa
namespace System.Security.Cryptography
{
[Experimental("SYSLIB5006")]
public abstract class SlhDsa : IDisposable
{
public static bool IsSupported { get; }
protected SlhDsa(SlhDsaAlgorithm algorithm);
public SlhDsaAlgorithm 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 ExportSlhDsaPublicKey(Span<byte> destination);
public int ExportSlhDsaSecretKey(Span<byte> destination);
public int ExportSlhDsaPrivateSeed(Span<byte> destination);
public static SlhDsa GenerateKey(SlhDsaAlgorithm algorithm);
public static SlhDsa ImportSubjectPublicKeyInfo(ReadOnlySpan<byte> source);
public static SlhDsa ImportPkcs8PrivateKey(ReadOnlySpan<byte> source);
public static SlhDsa ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<byte> passwordBytes, ReadOnlySpan<byte> source);
public static SlhDsa ImportEncryptedPkcs8PrivateKey(ReadOnlySpan<char> password, ReadOnlySpan<byte> source);
public static SlhDsa ImportFromPem(ReadOnlySpan<char> source);
public static SlhDsa ImportFromEncryptedPem(ReadOnlySpan<char> source, ReadOnlySpan<char> password);
public static SlhDsa ImportFromEncryptedPem(ReadOnlySpan<char> source, ReadOnlySpan<byte> passwordBytes);
public static SlhDsa ImportSlhDsaPublicKey(SlhDsaAlgorithm algorithm, ReadOnlySpan<byte> source);
public static SlhDsa ImportSlhDsaSecretKey(SlhDsaAlgorithm algorithm, ReadOnlySpan<byte> source);
public static SlhDsa ImportSlhDsaPrivateSeed(SlhDsaAlgorithm 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 ExportSlhDsaPublicKeyCore(Span<byte> destination);
protected abstract void ExportSlhDsaSecretKeyCore(Span<byte> destination);
protected abstract void ExportSlhDsaPrivateSeedCore(Span<byte> destination);
}
[DebuggerDisplay("{Name,nq}")]
[Experimental("SYSLIB5006")]
public sealed class SlhDsaAlgorithm
{
private SlhDsaAlgorithm();
public static SlhDsaAlgorithm SlhDsaSha2_128s { get; }
public static SlhDsaAlgorithm SlhDsaSha2_128f { get; }
public static SlhDsaAlgorithm SlhDsaSha2_192s { get; }
public static SlhDsaAlgorithm SlhDsaSha2_192f { get; }
public static SlhDsaAlgorithm SlhDsaSha2_256s { get; }
public static SlhDsaAlgorithm SlhDsaSha2_256f { get; }
public static SlhDsaAlgorithm SlhDsaShake128s { get; }
public static SlhDsaAlgorithm SlhDsaShake128f { get; }
public static SlhDsaAlgorithm SlhDsaShake192s { get; }
public static SlhDsaAlgorithm SlhDsaShake192f { get; }
public static SlhDsaAlgorithm SlhDsaShake256s { get; }
public static SlhDsaAlgorithm SlhDsaShake256f { get; }
public string Name { get; }
public int PublicKeySizeInBytes { get; }
public int SecretKeySizeInBytes { get; }
public int SignatureSizeInBytes { get; }
}
[Experimental("SYSLIB5006")]
public class SlhDsaCng : SlhDsa
{
public MLDsaCng(CngKey key);
// On ECDsaCng this is an allocating property. Changed to a method here.
public CngKey GetCngKey();
}
[Experimental("SYSLIB5006")]
public class SlhDsaOpenSsl : SlhDsa
{
public SlhDsaOpenSsl(SafeEvpPKeyHandle keyHandle);
public SafeEvpPKeyHandle DuplicateKeyHandle();
}
}
namespace System.Security.Cryptography.X509Certificates
{
// Extension class to enable porting to Microsoft.Bcl.Cryptography
[Experimental("SYSLIB5006")]
public sealed class SlhDsaCertificateExtensions
{
public static SlhDsa GetSlhDsaPublicKey(this X509Certificate2 certificate);
public static SlhDsa GetSlhDsaPrivateKey(this X509Certificate2 certificate);
}
public partial class CertificateRequest
{
[Experimental("SYSLIB5006")]
public CertificateRequest(string subjectName, SlhDsa key);
[Experimental("SYSLIB5006")]
public CertificateRequest(X500DistinguishedName subjectName, SlhDsa key);
}
#if NET10_OR_GREATER
public partial class X509SignatureGenerator
{
[Experimental("SYSLIB5006")]
public static X509SignatureGenerator CreateForSlhDsa(SlhDsa key);
}
#endif
}
#if NET10_OR_GREATER
namespace System.Security.Cryptography.Pkcs
{
public partial class CmsSigner
{
[Experimental("SYSLIB5006")]
public CmsSigner(
X509Certificate2 certificate,
SlhDsa 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(SlhDsa key);
}
}
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·