HashMLDsaSigner
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);
}
}
}