Chapter 5ML-KEM

A Worked Toy Example

August 25, 20266 min readbeginner

At the real parameters nothing can be checked by hand. This note shrinks every dimension until the whole scheme fits on a page, and runs it.

At the real parameters nothing can be checked by hand. This note shrinks every dimension until the whole scheme fits on a page, and runs it.

Fix the ring to R17\mathcal{R}_{17} with n=4n = 4, module rank k=2k = 2, and errors drawn from {−1,0,1}\{-1, 0, 1\}. The message is a single bit. Every product below is in R17\mathcal{R}_{17}, meaning reduce with X4=−1X^4 = -1 and then modulo 1717.

01.Key generation

Take

A=(3+X2+4X2X+2X25+3X3),s=(1−X2X−X3),e=(X31−X).A = \begin{pmatrix} 3 + X & 2 + 4X^2 \\ X + 2X^2 & 5 + 3X^3 \end{pmatrix}, \quad \mathbf{s} = \begin{pmatrix} 1 - X^2 \\ X - X^3 \end{pmatrix}, \quad \mathbf{e} = \begin{pmatrix} X^3 \\ 1 - X \end{pmatrix} .

Compute t=As+e\mathbf{t} = A\mathbf{s} + \mathbf{e}. Working the first component, (3+X)(1−X2)+(2+4X2)(X−X3)+X3(3+X)(1-X^2) + (2+4X^2)(X-X^3) + X^3:

The first product is 3+X−3X2−X33 + X - 3X^2 - X^3.

The second is 2X−2X3+4X3−4X52X - 2X^3 + 4X^3 - 4X^5. The X5X^5 term reduces: X5=X⋅X4=−XX^5 = X \cdot X^4 = -X, so −4X5=+4X-4X^5 = +4X. That gives 6X+2X36X + 2X^3.

Adding those and the error X3X^3:

t0  =  3+7X−3X2+2X3  ≡  3+7X+14X2+2X3(mod17),t_0 \;=\; 3 + 7X - 3X^2 + 2X^3 \;\equiv\; 3 + 7X + 14X^2 + 2X^3 \pmod{17},

using −3≡14-3 \equiv 14.

The same procedure on the second row gives

t1  =  5X+5X2+11X3.t_1 \;=\; 5X + 5X^2 + 11X^3 .

So the public key is (A,t)(A, \mathbf{t}) and the secret key is s\mathbf{s}.

02.Encryption

Encrypt the bit m=1m = 1. It encodes to ⌈17/2⌉=9\lceil 17/2 \rceil = 9 in the constant coefficient, so Encode(m)=9\text{Encode}(m) = 9.

Take

r=(1+X−X2),e1=(X−X31),e2=1.\mathbf{r} = \begin{pmatrix} 1 + X \\ -X^2 \end{pmatrix}, \qquad \mathbf{e}_1 = \begin{pmatrix} X - X^3 \\ 1 \end{pmatrix}, \qquad e_2 = 1 .

Compute u=A⊤r+e1\mathbf{u} = A^\top \mathbf{r} + \mathbf{e}_1, remembering the transpose, so the first component uses the first column of AA:

u0  =  5+5X+X2+15X3,u1  =  3+5X+16X2+4X3.u_0 \;=\; 5 + 5X + X^2 + 15X^3, \qquad u_1 \;=\; 3 + 5X + 16X^2 + 4X^3 .

Compute v=t⊤r+e2+9v = \mathbf{t}^\top \mathbf{r} + e_2 + 9:

v  =  16+4X+4X2+11X3.v \;=\; 16 + 4X + 4X^2 + 11X^3 .

The ciphertext is (u,v)(\mathbf{u}, v). No compression in this toy version.

03.Decryption

Bob computes s⊤u=s0u0+s1u1\mathbf{s}^\top \mathbf{u} = s_0 u_0 + s_1 u_1, which comes to

s⊤u  =  7+5X+5X2+6X3,\mathbf{s}^\top \mathbf{u} \;=\; 7 + 5X + 5X^2 + 6X^3 ,

and then

w  =  v−s⊤u  =  (16−7)+(4−5)X+(4−5)X2+(11−6)X3  =  9+16X+16X2+5X3,w \;=\; v - \mathbf{s}^\top \mathbf{u} \;=\; (16-7) + (4-5)X + (4-5)X^2 + (11-6)X^3 \;=\; 9 + 16X + 16X^2 + 5X^3 ,

reducing −1≡16-1 \equiv 16.

The constant coefficient is 99. Encode put 99 there for a 11 bit and 00 for a 00 bit, so the distance to 99 is zero and the distance to 00 is eight. The nearer anchor is 99.

Decoded bit: m=1m = 1. Correct.

04.Checking the error equation

Why Decryption Works claimed that w=Encode(m)+δw = \text{Encode}(m) + \delta with δ=e⊤r+e2−s⊤e1\delta = \mathbf{e}^\top \mathbf{r} + e_2 - \mathbf{s}^\top \mathbf{e}_1. Verify it directly on these numbers.

Computing that expression gives

δ  =  0+16X+16X2+5X3,\delta \;=\; 0 + 16X + 16X^2 + 5X^3 ,

whose coefficients written in centred form, taking values above 88 as negative, are

δ  =  (0,  −1,  −1,  5).\delta \;=\; (0,\; -1,\; -1,\; 5) .

And indeed

Encode(m)+δ  =  9+0+16X+16X2+5X3  =  w.✓\text{Encode}(m) + \delta \;=\; 9 + 0 + 16X + 16X^2 + 5X^3 \;=\; w . \qquad\checkmark

The cancellation happened exactly as the algebra promised, on real numbers, with no approximation.

05.The interesting part: a decryption failure

Look at the last coefficient of δ\delta. It is 55.

The tolerance from Why Decryption Works is ∣δj∣<q/4|\delta_j| < q/4, and here

q4  =  174  =  4.25.\frac{q}{4} \;=\; \frac{17}{4} \;=\; 4.25 .

So ∣δ3∣=5>4.25|\delta_3| = 5 > 4.25. That coefficient has broken the bound.

Trace the consequence. The message occupied only the constant coefficient, so coefficient 3 of Encode(m)\text{Encode}(m) is 00 and should decode to a 00 bit. But w3=5w_3 = 5, and on the circle modulo 1717 the distance from 55 to the anchor 99 is 44, while the distance to the anchor 00 is 55. The nearer anchor is 99, so it decodes to 11.

Wrong. Had the message been four bits rather than one, this instance would have decrypted three of them correctly and the fourth incorrectly.

This is a genuine decryption failure, produced by honest parties following the protocol exactly. Nobody cheated and nothing was mis-computed.

It happens because q=17q = 17 leaves a tolerance of 4.254.25, and errors that are sums of several products of values in {−1,0,1}\{-1, 0, 1\} reach 55 without difficulty. The toy parameters have essentially no margin.

Compare the real ones. At q=3329q = 3329 the tolerance is 832.25832.25, and the noise is a sum of products of centred binomial values with η≤3\eta \le 3 over 256256 coefficients. Reaching 832832 requires the far tail of that distribution, which is why the published failure probability is below 2−1392^{-139}.

So the toy example demonstrates two things at once: that the algebra works, and that the parameters are what make it usable. Shrinking qq for legibility broke the scheme, which is the most direct available evidence that the real values were chosen rather than picked.

06.Chapter summary

ML-KEM is Module-LWE with the sharp edges removed.

The narrow job is establishing a shared symmetric key over an open channel, and the KEM formulation returns the key rather than accepting one, which keeps user data out of the public-key primitive and makes the security analysis tractable.

The construction has two layers. The inner one is a public-key encryption scheme where key generation publishes t=As+e\mathbf{t} = A\mathbf{s} + \mathbf{e} and keeps s\mathbf{s}, encryption sends u=A⊤r+e1\mathbf{u} = A^\top\mathbf{r} + \mathbf{e}_1 alongside v=t⊤r+e2+Encode(m)v = \mathbf{t}^\top\mathbf{r} + e_2 + \text{Encode}(m), and decryption computes v−s⊤uv - \mathbf{s}^\top\mathbf{u}. The term s⊤A⊤r\mathbf{s}^\top A^\top \mathbf{r} appears twice with opposite signs and cancels, which is the whole construction, and the transpose in encryption exists precisely to make it happen.

What survives the cancellation is the encoded message plus a small error δ\delta. Decoding is correct exactly when ∣δj∣<q/4|\delta_j| < q/4 for every coefficient, and every parameter in the scheme is calibrated against that inequality. Encoding a bit to 00 or ⌈q/2⌉\lceil q/2 \rceil maximises the tolerance. Compression widths differ because the noise on u\mathbf{u} arrives multiplied by the secret while the noise on vv arrives alone.

The outer layer is the Fujisaki-Okamoto transform, which makes encryption deterministic in the message, verifies ciphertexts by re-encrypting them, and returns a pseudorandom dummy key on mismatch rather than an error. That implicit rejection is what defeats an active attacker, and it costs a full extra encryption on every decapsulation.

Every random value the scheme needs comes from Keccak in one parameterisation or another, so one hash engine serves the whole design.

Three parameter sets share n=256n = 256 and q=3329q = 3329 and differ only in module rank and noise widths, so one arithmetic datapath serves all of them. ML-KEM-768 is the recommended default, at 1184-byte public keys and 1088-byte ciphertexts.

The next chapter builds ML-DSA, which solves the other half of the problem. Encryption protects secrecy. Signatures protect authenticity, and the same lattice machinery does both.

FeedbackBook mode
post-quantum-cryptographycryptographymathematics