Class MLDSAEngine

java.lang.Object
org.bouncycastle.crypto.signers.mldsa.MLDSAEngine

public class MLDSAEngine extends Object
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 reference rej_eta.
  • The Fiat-Shamir-with-aborts loop (generateSignature(byte[], SHAKEDigest, byte[], byte[], byte[], byte[], byte[], byte[])): the iteration count and which checkNorm/makeHint bound triggered the restart leak through timing. A rejected attempt discards y and 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 reference poly_chknorm intends.
  • 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 and c[b] access, but b derives from the public commitment hash c~ (part of the signature / recomputed by the verifier), so no secret is involved.
The performance refactor of this package (in-place NTT, fused pointwise-accumulate, direct coefficient access, packed decompose) preserves all of the above: it adds no secret-dependent branch, memory index, or variable-latency operation.
  • Field Details

  • Method Details

    • GetSymmetric

      protected org.bouncycastle.crypto.signers.mldsa.Symmetric GetSymmetric()
    • getDilithiumPolyEtaPackedBytes

      public int getDilithiumPolyEtaPackedBytes()
    • getDilithiumK

      public int getDilithiumK()
    • getDilithiumL

      public int getDilithiumL()
    • getInstance

      public static MLDSAEngine getInstance(MLDSAParameters mldsaParameters, SecureRandom random)
    • generateKeyPairInternal

      public byte[][] generateKeyPairInternal(byte[] seed)
    • deriveT1

      public byte[] deriveT1(byte[] rho, byte[] key, byte[] tr, byte[] s1Enc, byte[] s2Enc, byte[] t0Enc)
    • getShake256Digest

      public SHAKEDigest 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

      public byte[] generateMu(SHAKEDigest shake256Digest)
    • 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()