Class MLDSAEngine
java.lang.Object
org.bouncycastle.crypto.signers.mldsa.MLDSAEngine
Lightweight ML-DSA (FIPS 204) engine — key generation, signing and verification.
Constant-time note. ML-DSA is designed to admit a constant-time implementation,
and this engine keeps the secret-sensitive arithmetic branchless and free of
secret-indexed memory access: the NTT (Ntt), the Montgomery/Barrett reductions
and conditionalAddQ (Reduce), Rounding.decompose(int, int) and
power2RoundAll all operate over public loop bounds with mask-select rather than
data-dependent branches, and ML-DSA uses no secret-indexed table lookups.
The following operations are deliberately variable-time. Each matches the FIPS 204 / pq-crystals reference and its accepted side-channel model — do not "simplify" them into a shape that leaks more:
- Rejection sampling of
s1/s2(Poly.uniformEta(byte[], short)): the number of SHAKE bytes consumed depends on the secret seed, but only the reject count between accepted coefficients leaks — never an accepted coefficient value. Matches referencerej_eta. - The Fiat-Shamir-with-aborts loop (
generateSignature(byte[], SHAKEDigest, byte[], byte[], byte[], byte[], byte[], byte[])): the iteration count and whichcheckNorm/makeHintbound triggered the restart leak through timing. A rejected attempt discardsyand resamples, so this reveals nothing about the long-term key. Poly.checkNorm(int)early-returns on the first out-of-bound coefficient. The absolute value is computed branchlessly first so a secret coefficient's sign never leaks — only the rejection event does, exactly as the referencepoly_chknormintends.Rounding.makeHint(int, int, MLDSAEngine)branches on its inputs, but the result is the hint bit that ships in the signature — information-equivalent to public output.Poly.challenge(byte[], int, int)(SampleInBall) has a data-dependent rejection loop andc[b]access, butbderives from the public commitment hash c~ (part of the signature / recomputed by the verifier), so no secret is involved.
decompose) preserves all of the above: it adds no
secret-dependent branch, memory index, or variable-latency operation.-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intstatic final intstatic final intstatic final intstatic final intstatic final intstatic final intstatic final intstatic final intstatic final int -
Method Summary
Modifier and TypeMethodDescriptionbyte[]deriveT1(byte[] rho, byte[] key, byte[] tr, byte[] s1Enc, byte[] s2Enc, byte[] t0Enc) byte[][]byte[][]generateKeyPairInternal(byte[] seed) byte[]generateMu(SHAKEDigest shake256Digest) byte[]generateSignature(byte[] mu, SHAKEDigest shake256Digest, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) intintintstatic MLDSAEnginegetInstance(MLDSAParameters mldsaParameters, SecureRandom random) protected org.bouncycastle.crypto.signers.mldsa.SymmetricvoidinitSign(byte[] tr, boolean isPreHash, byte[] ctx) voidinitVerify(byte[] rho, byte[] encT1, boolean isPreHash, byte[] ctx) byte[]signInternal(byte[] msg, int msglen, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) booleanverifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) booleanverifyInternalMu(byte[] providedMu) booleanverifyInternalMuSignature(byte[] mu, byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1)
-
Field Details
-
DilithiumN
public static final int DilithiumN- See Also:
-
DilithiumQ
public static final int DilithiumQ- See Also:
-
DilithiumQinv
public static final int DilithiumQinv- See Also:
-
DilithiumD
public static final int DilithiumD- See Also:
-
SeedBytes
public static final int SeedBytes- See Also:
-
CrhBytes
public static final int CrhBytes- See Also:
-
RndBytes
public static final int RndBytes- See Also:
-
TrBytes
public static final int TrBytes- See Also:
-
DilithiumPolyT1PackedBytes
public static final int DilithiumPolyT1PackedBytes- See Also:
-
DilithiumPolyT0PackedBytes
public static final int DilithiumPolyT0PackedBytes- See Also:
-
-
Method Details
-
GetSymmetric
protected org.bouncycastle.crypto.signers.mldsa.Symmetric GetSymmetric() -
getDilithiumPolyEtaPackedBytes
public int getDilithiumPolyEtaPackedBytes() -
getDilithiumK
public int getDilithiumK() -
getDilithiumL
public int getDilithiumL() -
getInstance
-
generateKeyPairInternal
public byte[][] generateKeyPairInternal(byte[] seed) -
deriveT1
public byte[] deriveT1(byte[] rho, byte[] key, byte[] tr, byte[] s1Enc, byte[] s2Enc, byte[] t0Enc) -
getShake256Digest
-
initSign
public void initSign(byte[] tr, boolean isPreHash, byte[] ctx) -
initVerify
public void initVerify(byte[] rho, byte[] encT1, boolean isPreHash, byte[] ctx) -
signInternal
public byte[] signInternal(byte[] msg, int msglen, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) -
generateMu
-
generateSignature
public byte[] generateSignature(byte[] mu, SHAKEDigest shake256Digest, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) -
verifyInternalMu
public boolean verifyInternalMu(byte[] providedMu) -
verifyInternalMuSignature
public boolean verifyInternalMuSignature(byte[] mu, byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) -
verifyInternal
public boolean verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) -
generateKeyPair
public byte[][] generateKeyPair()
-