Class MLDSAEngine


  • public class MLDSAEngine
    extends java.lang.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[], org.bouncycastle.crypto.digests.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, org.bouncycastle.crypto.signers.mldsa.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.
    • Method Detail

      • GetSymmetric

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

        public int getDilithiumPolyEtaPackedBytes()
      • getDilithiumK

        public int getDilithiumK()
      • getDilithiumL

        public int getDilithiumL()
      • 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()