Chapter 6ML-DSA

Signing

August 25, 20265 min readbeginner

This is the heart of the scheme, and it is a loop. Everything in it is either the Schnorr skeleton from 02-Schnorr-and-Fiat-Shamir, the rejection rule from 04-Rejection-Sampling…

This is the heart of the scheme, and it is a loop. Everything in it is either the Schnorr skeleton from Schnorr and Fiat-Shamir, the rejection rule from Rejection Sampling, or Fiat-Shamir With Aborts, or machinery for keeping the signature small.

01.The rounding gadgets

Two small routines are needed first. Both are pure integer arithmetic.

With bucket width α=2γ2\alpha = 2\gamma_2, decompose each r∈Zqr \in \mathbb{Z}_q as

r  =  α⋅r1+r0,r0∈(−α/2,  α/2],r \;=\; \alpha \cdot r_1 + r_0, \qquad r_0 \in (-\alpha/2,\; \alpha/2],

and define HighBitsα(r)=r1\text{HighBits}_\alpha(r) = r_1 and LowBitsα(r)=r0\text{LowBits}_\alpha(r) = r_0.

So HighBits\text{HighBits} says which bucket of width α\alpha a value falls in, and LowBits\text{LowBits} says where inside that bucket. Knowing only the high bits locates a value to within α\alpha, which is coarse but often enough.

Both must be branchless, because they are applied to secret-dependent values and a data-dependent branch would leak through timing or cache behaviour.

02.The loop

S1. Compute the message digest. μ=H(tr ∥ m)\mu = H(\textsf{tr} \,\|\, m), where tr\textsf{tr} was precomputed in key generation.

Then repeat the following until it succeeds.

S2. Commit. Sample y∈Rqℓ\mathbf{y} \in R_q^{\ell} with coefficients uniform on (−γ1,γ1](-\gamma_1, \gamma_1], and compute

w  =  Ay  ∈  Rqk.\mathbf{w} \;=\; A\mathbf{y} \;\in\; R_q^{k} .

S3. Take the high bits. w1=HighBits(w)\mathbf{w}_1 = \text{HighBits}(\mathbf{w}).

Only the coarse part is committed to. Sending the whole of w\mathbf{w} would be wasteful, and the verifier only needs to be able to recompute the same coarse value.

S4. Challenge. c~=H(μ ∥ w1)\tilde{c} = H(\mu \,\|\, \mathbf{w}_1), and then c=SampleInBall(c~)c = \textsf{SampleInBall}(\tilde{c}), which places exactly τ\tau coefficients of ±1\pm 1 at pseudorandom positions and leaves the rest zero.

This is Fiat-Shamir: the challenge is a hash of the commitment and the message, so the signer cannot choose it.

S5. Respond.

z  =  y+c s1.\mathbf{z} \;=\; \mathbf{y} + c\,\mathbf{s}_1 .

S6. The rejection checks. Restart if any of these fails.

∥z∥∞<γ1−β\|\mathbf{z}\|_\infty < \gamma_1 - \beta. This is the zero-knowledge check from Rejection Sampling, or Fiat-Shamir With Aborts. It is what makes the released z\mathbf{z} uniform on a box that does not depend on s1\mathbf{s}_1.

∥LowBits(w−cs2)∥∞<γ2−β\|\text{LowBits}(\mathbf{w} - c\mathbf{s}_2)\|_\infty < \gamma_2 - \beta. This one is not about secrecy at all. It is about the verifier being able to reproduce w1\mathbf{w}_1, and it keeps every coefficient a margin of β\beta away from a bucket boundary so that a small later perturbation cannot push it across.

∥c t0∥∞<γ2\|c\,\mathbf{t}_0\|_\infty < \gamma_2. This bounds the discrepancy created by truncating the public key in Key Generation, guaranteeing it can move a coefficient by at most one bucket.

S7. Build the hint. Compute a one-bit flag per coefficient recording whether adding ct0c\mathbf{t}_0 changes the bucket:

MakeHint(z,r)  =  1{HighBits(r+z)≠HighBits(r)}.\text{MakeHint}(z, r) \;=\; \mathbf{1}\bigl\{ \text{HighBits}(r + z) \neq \text{HighBits}(r) \bigr\}.

Restart if more than ω\omega bits are set.

S8. Emit.

σ  =  (c~,  z,  h).\sigma \;=\; (\tilde{c},\; \mathbf{z},\; \mathbf{h}).

03.The three checks, separated

It is easy to read step S6 as one blob of conditions. They serve three distinct purposes and it is worth keeping them apart.

The first check exists so that the signature reveals nothing. Without it the scheme leaks the key, as note 3 demonstrated.

The second exists so that verification is possible at all. It guarantees the signer's w1\mathbf{w}_1 can be reconstructed from what the verifier will have.

The third exists so that the hint stays well defined. It caps the damage from public-key truncation at one bucket, which is what makes a single bit per coefficient sufficient to describe it.

Only the first is about security. The other two are the price of the two size optimisations, committing to high bits only and truncating the public key.

04.The hint vector

The hint deserves its own explanation because it is the least obvious piece of the scheme.

The verifier will reconstruct something close to w\mathbf{w}, but off by ct0c\mathbf{t}_0, because it holds only t1\mathbf{t}_1. It then takes high bits. Almost always the perturbation is too small to change which bucket a coefficient sits in, and the verifier gets the right answer. Occasionally a coefficient was near a boundary and the perturbation pushes it over.

The hint is a list of exactly which coefficients that happened to. One bit each, across k⋅256k \cdot 256 coefficients.

Nearly all of those bits are zero, so serialising the hint as a bitstring would waste space. Instead it is stored as the positions of the set bits, of which there are at most ω\omega, which keeps it under a hundred bytes.

The check "restart if more than ω\omega bits are set" is what makes that encoding safe. Without it a signature could occasionally need more positions than the format allows.

The verifier applies UseHint\text{UseHint}, which returns the high bits as computed if the bit is zero, and the adjacent bucket if it is one.

05.What signing costs

Per attempt: one matrix-vector product AyA\mathbf{y} over RqR_q, which is kℓk\ell ring multiplications, plus two smaller products cs1c\mathbf{s}_1 and cs2c\mathbf{s}_2, plus a hash.

Expected attempts: four to six, from the rejection rate in Rejection Sampling, or Fiat-Shamir With Aborts.

So signing costs roughly five times a verification, and its running time varies from one invocation to the next. Both facts are unusual coming from ECDSA, and both matter for anybody building hardware: the signing datapath needs the same NTT engine as everything else, but it needs to be scheduled for a variable number of passes.

One property is worth stating explicitly because it looks alarming and is not. The number of restarts is public, observable in timing, and reveals nothing. Each attempt uses a fresh y\mathbf{y}, and whether it passes depends on y\mathbf{y} and on the resulting cc, not on any fixed property of s1\mathbf{s}_1. An observer counting restarts learns about the discarded randomness, which is discarded.

FeedbackBook mode
post-quantum-cryptographycryptographymathematics