MLDSAEngine (Bouncy Castle Library 1.85 API Specification)
Date: 2026-07-12

org.bouncycastle.crypto.signers.mldsa

## Class MLDSAEngine

- java.lang.Object
- - org.bouncycastle.crypto.signers.mldsa.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.

- - ### Field Summary

    Fields
    Modifier and Type | Field and Description
    --- | ---
    `static int` | `CrhBytes`
    `static int` | `DilithiumD`
    `static int` | `DilithiumN`
    `static int` | `DilithiumPolyT0PackedBytes`
    `static int` | `DilithiumPolyT1PackedBytes`
    `static int` | `DilithiumQ`
    `static int` | `DilithiumQinv`
    `static int` | `RndBytes`
    `static int` | `SeedBytes`
    `static int` | `TrBytes`

  - ### Method Summary

    All Methods Static Methods Instance Methods Concrete Methods
    Modifier and Type | Method and Description
    --- | ---
    `byte[]` | `deriveT1(byte[] rho, byte[] key, byte[] tr, byte[] s1Enc, byte[] s2Enc, byte[] t0Enc)`
    `byte[][]` | `generateKeyPair()`
    `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)`
    `int` | `getDilithiumK()`
    `int` | `getDilithiumL()`
    `int` | `getDilithiumPolyEtaPackedBytes()`
    `static MLDSAEngine` | `getInstance(MLDSAParameters mldsaParameters, java.security.SecureRandom random)`
    `SHAKEDigest` | `getShake256Digest()`
    `protected org.bouncycastle.crypto.signers.mldsa.Symmetric` | `GetSymmetric()`
    `void` | `initSign(byte[] tr, boolean isPreHash, byte[] ctx)`
    `void` | `initVerify(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)`
    `boolean` | `verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1)`
    `boolean` | `verifyInternalMu(byte[] providedMu)`
    `boolean` | `verifyInternalMuSignature(byte[] mu, byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1)`

    - ### Methods inherited from class java.lang.Object

      `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Field Detail

    - #### DilithiumN

          public static final int DilithiumN

      See Also:
        Constant Field Values

    - #### DilithiumQ

          public static final int DilithiumQ

      See Also:
        Constant Field Values

    - #### DilithiumQinv

          public static final int DilithiumQinv

      See Also:
        Constant Field Values

    - #### DilithiumD

          public static final int DilithiumD

      See Also:
        Constant Field Values

    - #### SeedBytes

          public static final int SeedBytes

      See Also:
        Constant Field Values

    - #### CrhBytes

          public static final int CrhBytes

      See Also:
        Constant Field Values

    - #### RndBytes

          public static final int RndBytes

      See Also:
        Constant Field Values

    - #### TrBytes

          public static final int TrBytes

      See Also:
        Constant Field Values

    - #### DilithiumPolyT1PackedBytes

          public static final int DilithiumPolyT1PackedBytes

      See Also:
        Constant Field Values

    - #### DilithiumPolyT0PackedBytes

          public static final int DilithiumPolyT0PackedBytes

      See Also:
        Constant Field Values

  - ### Method Detail

    - #### 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,
                                                java.security.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()