Expand description
First-order arithmetic masking for ML-DSA secret polynomials
(DPA / template-attack countermeasure). Available with the
sca-protected Cargo feature.
First-order arithmetic masking for ML-DSA polynomials.
Same idea as the ML-KEM masked module but adapted to the
ML-DSA arithmetic (q = 8 380 417, polynomial coefficients held as
i32, NTT goes all the way down to length-1 components).
Each secret polynomial is represented as two additive shares modulo
q: s = (s₀ + s₁) mod q. All operations on secret data manipulate
the shares independently, so a first-order side-channel attacker
observing one share at a time learns nothing about the unmasked
value.
§Sensitive operations protected
In dsa::sign_internal, the secret polynomials s1, s2, t0
are NTT-transformed once before the rejection-sampling loop, then
multiplied by the per-iteration challenge polynomial c (which is
public — verifier recomputes it):
ŝ1, ŝ2, t̂0 ← NTT(s1), NTT(s2), NTT(t0)
for each rejection iteration:
ĉ ← NTT(c)
cs1[i] ← ĉ · ŝ1[i] // secret × public
cs2[i] ← ĉ · ŝ2[i] // secret × public
ct0[i] ← ĉ · t̂0[i] // secret × publicThe masked variants in this module replace ŝ1, ŝ2, t̂0 with
MaskedPoly containers and provide a pointwise_mul_public
that multiplies each share independently. Because ĉ is public,
no secret×secret multiplication occurs and first-order masking is
sufficient.
§Available operations
| Function | Description |
|---|---|
MaskedPoly::mask | Split a plaintext polynomial into two shares |
MaskedPoly::unmask | Reconstruct the polynomial from shares |
MaskedPoly::refresh | Re-randomize shares (prevents correlation buildup) |
MaskedPoly::zeroize | DSE-resistant wipe of both shares |
masked_ntt | Forward NTT applied to each share |
masked_ntt_inv | Inverse NTT applied to each share |
masked_pointwise_mul_public | Masked × public pointwise mul (returns a MaskedPoly) |
§Masked y pipeline (sca-masked-y)
MaskedPoly::expand_mask samples y directly as two shares
drawn from SHAKE256. The shares propagate through
masked_ntt and masked_mat_vec_mul / masked_mat_vec_mul_lazy
so that the intermediate w = A·y stays in masked form until the
rejection loop commits to emitting it. This closes the DPA
recovery of s1 from z = y + c·s1 that exists on any unmasked
implementation. See Side-channel analysis of masked y-sampling
in ML-DSA (IACR ePrint 2025/276) and the countermeasure chapter
at doc/sca/countermeasures/ml_dsa.rst, section DPA on y —
the sca-masked-y pipeline.
§References
- Hardware masking of ML-DSA (IACR ePrint 2024,
doc/papers/eprint2024_mldsa_hw_masking.pdf) — reference construction, we follow the same share topology. - Side-channel analysis of masked y-sampling in ML-DSA
(IACR ePrint 2025/276) — basis for
MaskedPoly::expand_mask+ propagation through the linear stage. - Physical security considerations for ML-DSA (NIST, 2025) — masking recommendation for high-assurance profiles.
§Where to look next
- Countermeasure description and threat analysis:
doc/sca/countermeasures/ml_dsa.rst, sections DPA — first- order masking of secret polynomials and DPA ony— thesca-masked-ypipeline. - Call sites:
crate::ml_dsa::dsa::sign_internal(look for#[cfg(feature = "sca-protected")]and#[cfg(feature = "sca-masked-y")]blocks).
§Scope and residual risk
Masking here is first-order. The shipped Tier-1 item
T1-A (A3, refresh shares at the start of every rejection
iteration, head-of-loop refresh block in dsa.rs) raises the
effort required by a higher-order DPA that combines leakage
across iterations. Going beyond first-order (full higher-order
masking) is tracked as Tier-4 T4-C.
Structs§
- Masked
Poly - A polynomial split into two additive shares modulo
q.
Functions§
- masked_
mat_ vec_ mul - Masked matrix-vector multiplication in the NTT domain: for each
output row
i, computesum_j (A_hat[i][j] · y_hat_m[j])as a masked accumulator. - masked_
mat_ vec_ mul_ lazy - Low-memory variant of
masked_mat_vec_mul: recomputes eacha_hat[i][j]polynomial on-the-fly from the public seedrhovia SHAKE128 instead of holding the full k×l matrix in memory. - masked_
ntt - Apply the forward NTT to each share independently.
- masked_
ntt_ inv - Apply the inverse NTT to each share independently.
- masked_
pointwise_ mul_ public - Pointwise multiply a masked polynomial by a public polynomial
in NTT domain. Returns a fresh
MaskedPolyholding the product.