Chapter 5ML-KEM

Compression and Ciphertext Size

August 25, 20265 min readbeginner

Ciphertexts travel on the wire, so their size is a real cost paid on every connection. ML-KEM throws away low-order bits deliberately, and the error budget from…

Ciphertexts travel on the wire, so their size is a real cost paid on every connection. ML-KEM throws away low-order bits deliberately, and the error budget from Why Decryption Works is what says how many it can afford.

01.The operation

Compressq(x,d)  =  ⌈2dq⋅x⌋ mod 2d,Decompressq(y,d)  =  ⌈q2d⋅y⌋.\text{Compress}_q(x, d) \;=\; \left\lceil \frac{2^d}{q} \cdot x \right\rfloor \bmod 2^d, \qquad \text{Decompress}_q(y, d) \;=\; \left\lceil \frac{q}{2^d} \cdot y \right\rfloor .

The half-square brackets denote rounding to the nearest integer.

Read it plainly. Compression rescales a value from the range [0,q)[0, q) down to [0,2d)[0, 2^d) and rounds. Decompression rescales back. The round trip does not return the original, because information was discarded, but it returns something close.

Concretely with q=3329q = 3329 and d=4d = 4, the whole range of 33293329 possible coefficient values is squeezed into 1616 buckets, each about 208208 wide. Decompressing returns the centre of the bucket, so the error is at most about 104104.

02.Where the widths come from

The two parts of the ciphertext are compressed differently: du=10d_u = 10 bits per coefficient for u\mathbf{u}, and dv=4d_v = 4 for vv. That is a factor of sixty-four difference in how much is thrown away, and the reason is in the error equation.

Recall the two extra terms compression added:

δ′  =  ⋯  −  s⊤cu  +  cv.\delta' \;=\; \cdots \;-\; \mathbf{s}^\top \mathbf{c}_u \;+\; c_v .

The noise on u\mathbf{u} arrives multiplied by the secret s\mathbf{s}. That multiplication is a full ring product, summing nn terms, so even though s\mathbf{s} is small the product accumulates. Compression noise on u\mathbf{u} is therefore amplified before it reaches the budget, and u\mathbf{u} has to be treated carefully.

The noise on vv arrives alone. It is added once, at whatever size it is, and nothing multiplies it. So vv can be compressed far harder for the same cost to the budget.

Hence du=10d_u = 10 and dv=4d_v = 4. It is a nice illustration of how a scheme's constants are not arbitrary: each one is the answer to an inequality.

03.The saving

Uncompressed, every coefficient needs 12 bits, since q=3329q = 3329 needs 12. For ML-KEM-768, an uncompressed ciphertext would be

3⋅256⋅128  +  256⋅128  =  1152+384  =  1536 bytes.\frac{3 \cdot 256 \cdot 12}{8} \;+\; \frac{256 \cdot 12}{8} \;=\; 1152 + 384 \;=\; 1536 \text{ bytes}.

Compressed, it is

3⋅256⋅108  +  256⋅48  =  960+128  =  1088 bytes.\frac{3 \cdot 256 \cdot 10}{8} \;+\; \frac{256 \cdot 4}{8} \;=\; 960 + 128 \;=\; 1088 \text{ bytes}.

A saving of 448448 bytes, just under thirty percent, on every ciphertext ever sent.

Across all three parameter sets:

uncompressedcompressedsaved
ML-KEM-5121152 B768 B384 B
ML-KEM-7681536 B1088 B448 B
ML-KEM-10241920 B1568 B352 B

ML-KEM-1024 saves less proportionally because it uses gentler compression, du=11d_u = 11 and dv=5d_v = 5. At the highest security level the error budget is tighter relative to the noise, so less can be discarded.

04.Why the public key is not compressed

The public key is 11841184 bytes for ML-KEM-768 and is sent uncompressed. Given that ciphertexts save thirty percent, the obvious question is why keys do not.

Two reasons, and the second is the real one.

The public key is stored in NTT domain, as Key Generation explained. Transformed coefficients are uniform across the full range with no structure to exploit, and compressing them would discard bits that the arithmetic genuinely needs.

More fundamentally, compressing the public key would inject rounding error into t\mathbf{t}, which appears in the correctness equation on the key generation side. Bob's secret cannot compensate for it, because Bob does not know which way each coefficient was rounded and cannot recover that from sksk. Ciphertext compression works precisely because its error lands in δ′\delta' where the budget absorbs it. Public key compression has no such landing place.

There is also an asymmetry of usage that makes the trade less attractive anyway. A public key is fetched once and cached, often inside a certificate that is itself several kilobytes. A ciphertext is sent on every single connection. Optimising the thing that travels constantly is worth more than optimising the thing that travels once.

05.The encoding, briefly

One implementation detail that causes trouble in practice. Compressed coefficients are dd bits wide and dd is not a multiple of 88, so the serialised ciphertext is a densely packed bitstream rather than a byte array. Ten-bit values pack four to every five bytes.

FIPS 203 specifies the packing exactly, down to bit order, because two implementations that pack differently produce different ciphertexts from identical inputs and fail to interoperate. This is the kind of detail that has no mathematical content and consumes real engineering time, and it is worth knowing it is there before meeting it in a test vector mismatch.

FeedbackBook mode
post-quantum-cryptographycryptographymathematics