PQC · Lecture 3 · Florida Atlantic University · Fall 2026

Lecture 3: ML-KEM and ML-DSA

ML-KEM hides one message bit per coefficient under noise that only the secret key can remove. ML-DSA is a Schnorr signature built from lattices, with high bits and rejection sampling. Both start here as toy versions with every number shown.

1088bytes in an ML-KEM-768 ciphertext
2−165decryption failure rate of ML-KEM-768
2420bytes in an ML-DSA-44 signature
4.25average signing tries in ML-DSA-44
We have MLWE and fast multiplication. How do we exchange a key and sign a message with them?

Key encapsulation

A KEM instead of Diffie-Hellman

ML-KEM is already in use

Bytes on the wireclient →\rightarrow serverserver →\rightarrow client
X25519 alone3232
ML-KEM-768 alone11841088
hybrid X25519MLKEM76812161120

Toy Kyber

Key generation: the Lecture 1 example

Encryption

Encryption with numbers

Bob's small random values
r=(1+x,  1−x)\mathbf{r} = (1 + x,\; 1 - x)
e1=(1+x,  x3)\mathbf{e}_1 = (1 + x,\; x^3)
e2=x2+x3e_2 = x^2 + x^3
Ciphertext
u0=A00r0+A10r1+e1,0=(3,5,15,5)u_0 = A_{00} r_0 + A_{10} r_1 + e_{1,0} = (3, 5, 15, 5)
u1=A01r0+A11r1+e1,1=(6,0,9,3)u_1 = A_{01} r_0 + A_{11} r_1 + e_{1,1} = (6, 0, 9, 3)
v=t0r0+t1r1+e2+9m=(13,8,15,2)v = t_0 r_0 + t_1 r_1 + e_2 + 9m = (13, 8, 15, 2)

Decryption with numbers

Alice computes
s0u0+s1u1=(5,7,5,11)\Sec{s_0} u_0 + \Sec{s_1} u_1 = (5, 7, 5, 11)
w=v−sTu=(13−5,  8−7,  15−5,  2−11)=(8,1,10,8)w = v - \mathbf{s}^T\mathbf{u} = (13 - 5,\; 8 - 7,\; 15 - 5,\; 2 - 11) = (8, 1, 10, 8)
round: 8→18 \rightarrow 1, 1→01 \rightarrow 0, 10→110 \rightarrow 1, 8→18 \rightarrow 1 m=(1,0,1,1)m = \Out{(1, 0, 1, 1)}

Why decryption works

w=v−sTu=(tTr+e2+9m)−sT(ATr+e1)=sTATr+eTr+e2+9m−sTATr−sTe1=9m+eTr+e2−sTe1⏟small noise\begin{aligned} w = v - \mathbf{s}^T\mathbf{u} &= (\mathbf{t}^T\mathbf{r} + e_2 + 9m) - \mathbf{s}^T(\mathbf{A}^T\mathbf{r} + \mathbf{e}_1) \\ &= \cancel{\mathbf{s}^T\mathbf{A}^T\mathbf{r}} + \mathbf{e}^T\mathbf{r} + e_2 + 9m - \cancel{\mathbf{s}^T\mathbf{A}^T\mathbf{r}} - \mathbf{s}^T\mathbf{e}_1 \\ &= 9m + \underbrace{\mathbf{e}^T\mathbf{r} + e_2 - \mathbf{s}^T\mathbf{e}_1}_{\text{small noise}} \end{aligned}
In our example
noise =(−1,1,1,−1)= (-1, 1, 1, -1), 9m+noise=(8,1,10,8)=w9m + \text{noise} = (8, 1, 10, 8) = w

When decryption fails

SchemeRoom for noise (q/4q/4)Chance a decryption fails
our toy, q=17q = 17454% to 66% (depends on the message)
ML-KEM-5128322−1392^{-139}
ML-KEM-7688322−1652^{-165}
ML-KEM-10248322−1752^{-175}
Try it

Toy Kyber, live

The key is the Lecture 1 instance. Flip message bits, draw new random r, e1, e2, or run many encryptions to see how often the toy fails.

From toy Kyber to ML-KEM

What changes

PartToyML-KEM-768 (FIPS 203)
ringq=17q = 17, n=4n = 4, k=2k = 2q=3329q = 3329, n=256n = 256, k=3k = 3
matrix A\mathbf{A}written outexpanded from a 32-byte seed ρ\rho
small valueschosen by handsampled from a binomial distribution
multiplicationschoolbookNTT, 7 stages
ciphertextfull numberscompressed
useencrypt a messageKEM with a re-encryption check

A seed instead of a matrix

Two details
  • SHAKE128 gives 12-bit numbers. Values of 3329 or more are skipped, so every coefficient is uniform mod qq.
  • A\mathbf{A} is random anyway, so it is read directly as NTT values (Lecture 2) and never transformed.

Small values from coin flips

value−2-2−1-1001122
probability1/161/164/164/166/166/164/164/161/161/16

Compression: fewer bits on the wire

d=10d = 10 (used for u\mathbf{u})
2000→615→19992000 \rightarrow 615 \rightarrow 1999
error 1
d=4d = 4 (used for vv)
2000→10→20812000 \rightarrow 10 \rightarrow 2081
error 81, still far below q/4=832q/4 = 832

From encryption to a KEM

Why the re-encryption check?

With the check
  • A changed ciphertext never re-encrypts to itself, so it is always caught.
  • Alice then returns a fake key Kˉ=J(z,c)\bar K = J(z, c) from a secret seed zz. To the attacker it looks like any other key, so even the failure reveals nothing.

Where the NTT is used

StepComputation
KeyGent^=A^∘NTT(s)+NTT(e)\hat{\mathbf{t}} = \hat{\mathbf{A}} \circ \mathrm{NTT}(\mathbf{s}) + \mathrm{NTT}(\mathbf{e}) (stays in NTT form)
Encryptr^=NTT(r)\hat{\mathbf{r}} = \mathrm{NTT}(\mathbf{r}), u=NTT−1(A^T∘r^)+e1\mathbf{u} = \mathrm{NTT}^{-1}(\hat{\mathbf{A}}^T \circ \hat{\mathbf{r}}) + \mathbf{e}_1, v=NTT−1(t^T∘r^)+e2+μv = \mathrm{NTT}^{-1}(\hat{\mathbf{t}}^T \circ \hat{\mathbf{r}}) + e_2 + \mu
Decryptw=v−NTT−1(s^T∘NTT(u))w = v - \mathrm{NTT}^{-1}(\hat{\mathbf{s}}^T \circ \mathrm{NTT}(\mathbf{u}))

ML-KEM parameter sets

Setkkη\eta(du,dv)(d_u, d_v)ekdkciphertextsecurity
ML-KEM-51223(10, 4)8001632768128-bit
ML-KEM-76832(10, 4)118424001088192-bit
ML-KEM-102442(11, 5)156831681568256-bit
X25519 (ECDH)323232128-bit
RSA-2048256256256112-bit

Signatures: from Schnorr to Dilithium

Recap: Schnorr signatures

The scheme
secret x\Sec{x}, public pk=gx\Pub{\mathrm{pk}} = g^{x}
Sign: random yy, w=gyw = g^{y},
c=H(M,w)c = H(M, w), z=y+c xz = y + c\,x
Verify: w′=gz⋅pk−cw' = g^{z} \cdot \mathrm{pk}^{-c},
accept if H(M,w′)=cH(M, w') = c
Numbers: p=23p = 23, g=5g = 5
x=6\Sec{x = 6}, pk=56=8\Pub{\mathrm{pk} = 5^6 = 8}
y=3y = 3, w=53=10w = 5^3 = 10
suppose HH gives c=7c = 7
z=3+7⋅6=45≡1(mod22)z = 3 + 7 \cdot 6 = 45 \equiv 1 \pmod{22}
w′=51⋅8−7=5⋅2=10w' = 5^1 \cdot 8^{-7} = 5 \cdot 2 = \Out{10} ✓

The same three moves with lattices

SchnorrDilithium (toy)
secretxxsmall s1,s2\mathbf{s}_1, \mathbf{s}_2
public keygxg^{x}t=As1+s2\mathbf{t} = \mathbf{A}\mathbf{s}_1 + \mathbf{s}_2
commitw=gyw = g^{y}w=Ay\mathbf{w} = \mathbf{A}\mathbf{y}
challengec=H(M,w)c = H(M, w)c=H(M,HighBits(w))c = H(M, \mathrm{HighBits}(\mathbf{w})), a small polynomial
responsez=y+c xz = y + c\,xz=y+c s1\mathbf{z} = \mathbf{y} + c\,\mathbf{s}_1
checkgz pk−c=wg^{z}\,\mathrm{pk}^{-c} = wAz−c t=w−c s2≈w\mathbf{A}\mathbf{z} - c\,\mathbf{t} = \mathbf{w} - c\,\mathbf{s}_2 \approx \mathbf{w}

Two problems, two fixes

Problem 1: the verifier gets w−c s2\mathbf{w} - c\,\mathbf{s}_2, not w\mathbf{w}
Fix: hash only the high bits of w\mathbf{w}. A small change (c s2c\,\mathbf{s}_2) leaves the high bits alone, unless w\mathbf{w} sits near a boundary. The signer checks for that.
Problem 2: z=y+c s1\mathbf{z} = \mathbf{y} + c\,\mathbf{s}_1 can reveal s1\mathbf{s}_1
y\mathbf{y} is random in a fixed range. Values of z\mathbf{z} near the edge of that range happen more often for one sign of c s1c\,\mathbf{s}_1 than for the other.
Fix: rejection sampling. Publish z\mathbf{z} only if all its coefficients stay well inside the range. Otherwise start over with a new y\mathbf{y}.

Toy Dilithium

Toy parameters and keys

Parameters
y\mathbf{y} coefficients in (−16,16](-16, 16] (γ1=16\gamma_1 = 16)
high bits in steps of 1616 (γ2=8\gamma_2 = 8)
cc has two coefficients ±1\pm 1 (τ=2\tau = 2)
s1,s2\mathbf{s}_1, \mathbf{s}_2 in {−1,0,1}\{-1, 0, 1\} (η=1\eta = 1)
β=τη=2\beta = \tau\eta = 2: the largest size of c sc\,\mathbf{s}
Keys
a=(23,61,8,45)\Pub{a = (23, 61, 8, 45)}
s1=(1,−1,0,1)\Sec{s_1 = (1, -1, 0, 1)}
s2=(0,1,−1,0)\Sec{s_2 = (0, 1, -1, 0)}
t=a s1+s2=(7,31,95,60)\Pub{t = a\,s_1 + s_2 = (7, 31, 95, 60)}

High bits and low bits

Signing

Signing with numbers: try 1

Message MM = "hello"
y=(0,7,1,14)y = (0, 7, 1, 14)
w=a y=(84,4,14,51)w = a\,y = (84, 4, 14, 51), w1=HighBits(w)=(5,0,1,3)w_1 = \mathrm{HighBits}(w) = (5, 0, 1, 3)
our toy HH gives c=−x+x3c = -x + x^3, c s1=(2,−1,0,1)c\,s_1 = (2, -1, 0, 1)
z=y+c s1=(2,6,1,15)z = y + c\,s_1 = (2, 6, 1, \Bad{15})
Rejected
∣15∣≥14|15| \ge 14. A value this close to the edge could leak the sign of c s1c\,s_1. Throw everything away and draw a new yy.

Signing with numbers: try 2

New yy
y=(−4,−12,1,−14)y = (-4, -12, 1, -14)
w=a y=(33,32,83,45)w = a\,y = (33, 32, 83, 45), w1=(2,2,5,3)w_1 = (2, 2, 5, 3)
c=−x−x2c = -x - x^2, c s1=(1,0,0,1)c\,s_1 = (1, 0, 0, 1), z=(−3,−12,1,−13)z = (-3, -12, 1, -13)
Both checks pass
zz: all coefficients below 14 in size ✓
w−c s2=(34,32,84,45)w - c\,s_2 = (34, 32, 84, 45), low bits (2,0,4,−3)(2, 0, 4, -3): all below 6 in size ✓
Signature: σ=(c,z)=(−x−x2,  (−3,−12,1,−13))\sigma = \Out{(c, z)} = (-x - x^2,\; (-3, -12, 1, -13))

Verification

With numbers
a z=(92,85,46,16)a\,z = (92, 85, 46, 16), c t=(58,53,59,68)c\,t = (58, 53, 59, 68)
w′=a z−c t=(34,32,84,45)w' = a\,z - c\,t = (34, 32, 84, 45), HighBits(w′)=(2,2,5,3)=w1\mathrm{HighBits}(w') = (2, 2, 5, 3) = w_1, so c′=cc' = c ✓

Why verification works

a z−c t=a (y+c s1)−c (a s1+s2)=a y+c a s1−c a s1−c s2=w−c s2\begin{aligned} a\,z - c\,t &= a\,(y + c\,s_1) - c\,(a\,s_1 + s_2) \\ &= a\,y + c\,a\,s_1 - c\,a\,s_1 - c\,s_2 \\ &= w - c\,s_2 \end{aligned}

How many tries?

SchemeAverage tries per signature
our toyabout 9
ML-DSA-444.25
ML-DSA-655.1
ML-DSA-873.85
Try it

Toy Dilithium signer

Each click signs the message with fresh random y and shows every try, accepted or rejected, then verifies the signature. Same keys and toy hash as the slides.

From toy Dilithium to ML-DSA

What changes

PartToyML-DSA-44 (FIPS 204)
ringq=97q = 97, n=4n = 4q=8380417q = 8380417, n=256n = 256, full NTT
sizes1×11 \times 1A\mathbf{A} is 4×44 \times 4, from a 32-byte seed
challenge cctwo ±1\pm 139 coefficients ±1\pm 1 (SampleInBall, SHAKE256)
boundsγ1=16\gamma_1 = 16, γ2=8\gamma_2 = 8γ1=217\gamma_1 = 2^{17}, γ2=(q−1)/88\gamma_2 = (q-1)/88
public keyfull t\mathbf{t}only the high part t1\mathbf{t}_1, plus hints
messageMMμ=H(H(pk) ∥ M)\mu = H(H(\mathrm{pk}) \,\|\, M)

Shrinking the public key with hints

The cost
The verifier now computes Az−c t1213=w−c s2+c t0\mathbf{A}\mathbf{z} - c\,\mathbf{t}_1 2^{13} = \mathbf{w} - c\,\mathbf{s}_2 + c\,\mathbf{t}_0, a little further from w\mathbf{w}.
The signer adds hint bits h\mathbf{h} that say which high bits to move by one. At most ω=80\omega = 80 hints are allowed.

Deterministic or hedged signing

Hedged (default)
rnd = 32 fresh random bytes
same message, different signature each time
harder to attack with side channels and faults
Deterministic
rnd = 32 zero bytes
same message, same signature
for devices without a good random number generator

ML-DSA parameter sets and sizes

Scheme(k,ℓ)(k, \ell)η\etaτ\taupublic keysecret keysignature
ML-DSA-44(4, 4)239131225602420
ML-DSA-65(6, 5)449195240323309
ML-DSA-87(8, 7)260259248964627
ECDSA P-256643264
RSA-3072384384384
FN-DSA-512 (Falcon)897666
SLH-DSA-128s32647856

Summary

Summary

ML-KEM
  • Key: an MLWE sample t=As+e\mathbf{t} = \mathbf{A}\mathbf{s} + \mathbf{e}.
  • Encrypt: hide ⌈q/2⌋⋅m\lceil q/2 \rfloor \cdot m under tTr\mathbf{t}^T\mathbf{r}.
  • Decrypt: v−sTuv - \mathbf{s}^T\mathbf{u}, then round.
  • KEM: random mm, hash to KK, re-encryption check.
ML-DSA
  • Schnorr with lattices: commit, challenge, response.
  • Hash only the high bits of w\mathbf{w}.
  • Reject any z\mathbf{z} that could leak s1\mathbf{s}_1.
  • Verify: HighBits(Az−c t)=w1\mathrm{HighBits}(\mathbf{A}\mathbf{z} - c\,\mathbf{t}) = \mathbf{w}_1.
Under both
Polynomials in Rq\Rq (Lecture 1), multiplied with the NTT (Lecture 2), and the MLWE problem.
Thank you!