<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" />

HashMLDsaSigner

public sealed class HashMLDsaSigner : ISigner
using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium; using Org.BouncyCastle.Security; using System; namespace Org.BouncyCastle.Crypto.Signers { public sealed class HashMLDsaSigner : ISigner { private readonly ShakeDigest m_msgRepDigest = DilithiumEngine.MsgRepCreateDigest(); private readonly MLDsaParameters m_parameters; private readonly byte[] m_preHashOidEncoding; private readonly IDigest m_preHashDigest; private readonly bool m_deterministic; private byte[] m_context; private MLDsaPrivateKeyParameters m_privateKey; private MLDsaPublicKeyParameters m_publicKey; private DilithiumEngine m_engine; public string AlgorithmName => m_parameters.Name; public HashMLDsaSigner(MLDsaParameters parameters, bool deterministic) { if (parameters == null) throw new ArgumentNullException("parameters"); if (parameters.PreHashOid == null) throw new ArgumentException("cannot be used for ML-DSA", "parameters"); m_parameters = parameters; m_preHashOidEncoding = parameters.PreHashOid.GetEncoded("DER"); m_preHashDigest = DigestUtilities.GetDigest(parameters.PreHashOid); m_deterministic = deterministic; } public void Init(bool forSigning, ICipherParameters parameters) { byte[] array = null; ParametersWithContext parametersWithContext = parameters as ParametersWithContext; if (parametersWithContext != null) { if (parametersWithContext.ContextLength > 255) throw new ArgumentOutOfRangeException("context too long", "parameters"); array = parametersWithContext.GetContext(); parameters = parametersWithContext.Parameters; } m_context = (array ?? Array.Empty<byte>()); if (forSigning) { SecureRandom secureRandom = null; ParametersWithRandom parametersWithRandom = parameters as ParametersWithRandom; if (parametersWithRandom != null) { secureRandom = parametersWithRandom.Random; parameters = parametersWithRandom.Parameters; } m_privateKey = (MLDsaPrivateKeyParameters)parameters; m_publicKey = null; SecureRandom random = m_deterministic ? null : CryptoServicesRegistrar.GetSecureRandom(secureRandom); m_engine = GetEngine(m_privateKey.Parameters, random); } else { m_privateKey = null; m_publicKey = (MLDsaPublicKeyParameters)parameters; m_engine = GetEngine(m_publicKey.Parameters, null); } m_msgRepDigest.Reset(); byte[] tr = (m_privateKey != null) ? m_privateKey.m_tr : m_publicKey.GetPublicKeyHash(); m_engine.MsgRepBegin(m_msgRepDigest, tr); m_msgRepDigest.Update(1); m_msgRepDigest.Update((byte)m_context.Length); m_msgRepDigest.BlockUpdate(m_context, 0, m_context.Length); m_msgRepDigest.BlockUpdate(m_preHashOidEncoding, 0, m_preHashOidEncoding.Length); Reset(); } public void Update(byte input) { m_preHashDigest.Update(input); } public void BlockUpdate(byte[] input, int inOff, int inLen) { m_preHashDigest.BlockUpdate(input, inOff, inLen); } public void BlockUpdate(ReadOnlySpan<byte> input) { m_preHashDigest.BlockUpdate(input); } public int GetMaxSignatureSize() { return m_engine.CryptoBytes; } public byte[] GenerateSignature() { if (m_privateKey == null) throw new InvalidOperationException("MLDsaSigner not initialised for signature generation."); ShakeDigest d = FinishPreHash(); byte[] array = new byte[m_engine.CryptoBytes]; m_engine.MsgRepEndSign(d, array, array.Length, m_privateKey.m_rho, m_privateKey.m_k, m_privateKey.m_t0, m_privateKey.m_s1, m_privateKey.m_s2, false); return array; } public bool VerifySignature(byte[] signature) { if (m_publicKey == null) throw new InvalidOperationException("MLDsaSigner not initialised for verification"); ShakeDigest d = FinishPreHash(); return m_engine.MsgRepEndVerifyInternal(d, signature, signature.Length, m_publicKey.m_rho, m_publicKey.m_t1); } public void Reset() { m_preHashDigest.Reset(); } private unsafe ShakeDigest FinishPreHash() { ShakeDigest shakeDigest = new ShakeDigest(m_msgRepDigest); int digestSize = shakeDigest.GetDigestSize(); Span<byte> span = new Span<byte>(stackalloc byte[(int)(uint)digestSize], digestSize); m_preHashDigest.DoFinal(span); shakeDigest.BlockUpdate(span); return shakeDigest; } private DilithiumEngine GetEngine(MLDsaParameters keyParameters, SecureRandom random) { MLDsaParameterSet parameterSet = keyParameters.ParameterSet; if (keyParameters.ParameterSet != m_parameters.ParameterSet) throw new ArgumentException("Mismatching key parameter set", "keyParameters"); return parameterSet.GetEngine(random); } } }