Skip to main content

sign_internal

Function sign_internal 

Source
pub fn sign_internal<P: Params>(
    sk: &[u8],
    m_prime: &[u8],
    rnd: &[u8; 32],
) -> Result<Vec<u8>, MlDsaError>
Expand description

Sign a pre-formatted message (deterministic or hedged).

Implements Algorithm 7 of FIPS 204 (ML-DSA.Sign_internal).

This function contains the core rejection sampling loop: candidate signatures (z, h) are generated from a masking vector y and the challenge polynomial c, then tested against the norm bounds ||z||_inf < gamma1 - beta and ||r0||_inf < gamma2 - beta. If any check fails, the counter kappa is incremented and a new attempt begins.

  • sk: encoded secret key bytes.
  • m_prime: pre-formatted message (e.g., 0x00 || len(ctx) || ctx || msg).
  • rnd: 32-byte randomness. Use random bytes for hedged signing or all-zeros for fully deterministic signing.

§Side-channel countermeasures

With the sca-protected Cargo feature enabled (default), this function activates the additional defences described in the crate-level documentation:

  1. Shuffled NTT on s1, s2, t0 — runs once at entry, via super::shuffle::ntt_shuffled. Defends against SPA on the secret-key NTT and disrupts trace alignment for any later DPA campaign that tries to average aligned traces.
  2. First-order additive masking of the NTT-domain secrets, via super::masked::MaskedPoly. Each polynomial is split into two shares mod q = 8 380 417; no single intermediate value reveals the secret to a first-order observer.
  3. Per-iteration c·sₓ multiplications go through super::masked::masked_pointwise_mul_public, which multiplies each share independently by the public challenge ĉ. Because ĉ is public, first-order shares are sufficient — no secret×secret operation is performed.
  4. Mask refresh after every use: the share pair is re-randomized via MaskedPoly::refresh() between rejection iterations, so the same secret never multiplies the same share twice — defeating higher-order correlation attacks that would otherwise become available across many rejection retries on the same key.

All randomness for the SCA layer comes from a deterministic SHAKE256-based ScaRng seeded with (K ‖ rnd ‖ tr ‖ M'), so the function remains deterministic for fixed rnd. The masked path produces signatures bit-identical to the unmasked path — proven by the NIST ACVP siggen vectors, which the SCA build passes unchanged.

§Errors

Returns MlDsaError::InvalidSecretKey if sk has incorrect length (checked by the caller in the public API).