Skip to content

Chapter 9: Ring-LWE and Module-LWE

Ring-LWE is the ring-structured analogue of LWE. The secret, the public element, and the error are all polynomials in the ring Rq=Zq[x]/(xn+1)R_q = \mathbb{Z}_q[x]/(x^n + 1), with nn a power of two. A Ring-LWE sample is a pair (a,b)(a, b) of ring elements with b=as+eb = a \cdot s + e. The secret ss is a polynomial drawn uniformly from RqR_q, and the error ee has small coefficients from a short distribution (Lyubashevsky et al., 2010). A flat LWE instance at the same dimension stores an m×nm \times n matrix of field elements. Ring-LWE stores one polynomial aa instead. The implicit n×nn \times n negacyclic circulant that multiplication by aa realizes is never written down.

Module-LWE is the same idea at rank kk. The secret is a vector in RqkR_q^k, the public data is an m×km \times k matrix of ring elements, and the error is a vector in RqmR_q^m. At k=1k = 1, Module-LWE coincides with Ring-LWE. At n=1n = 1, the ring collapses to Zq\mathbb{Z}_q and Module-LWE coincides with flat LWE. ML-KEM uses Module-LWE at module rank k{2,3,4}k \in \{2, 3, 4\} over the single ring Z3329[x]/(x256+1)\mathbb{Z}_{3329}[x]/(x^{256} + 1) for its three security levels (National Institute of Standards and Technology, 2024). The number theoretic transform makes polynomial multiplication in RqR_q run in Θ(nlogn)\Theta(n \log n) time at the parameters practical schemes pick, under a simple arithmetic condition on qq.

The ring RqR_q and its negacyclic multiplication

Section titled “The ring R_q and its negacyclic multiplication”

Fix a prime qq and an integer nn that is a power of two. The ring

Rq=Zq[x]/(xn+1)R_q = \mathbb{Z}_q[x] / (x^n + 1)

consists of equivalence classes of polynomials in Zq[x]\mathbb{Z}_q[x] modulo the polynomial xn+1x^n + 1. A canonical representative is a polynomial of degree strictly less than nn. We write it as a length-nn coefficient vector (f0,f1,,fn1)(f_0, f_1, \ldots, f_{n-1}) with fiZqf_i \in \mathbb{Z}_q. Addition is coefficient-wise modulo qq: (f+g)i=fi+gimodq(f + g)_i = f_i + g_i \bmod q. Multiplication starts in Z[x]\mathbb{Z}[x] and then uses two reductions. The polynomial product fgf \cdot g has degree at most 2n22n - 2. Every coefficient at position n+jn + j with j0j \geq 0 is moved to position jj with a sign flip. The sign flip comes from xn+10x^n + 1 \equiv 0 in RqR_q, which rearranges to xn1x^n \equiv -1. After the fold, every coefficient is reduced modulo qq. The whole operation takes O(n2)O(n^2) integer multiplications (Lang, 2002).

The sign flip is the difference between RqR_q and the cyclic ring Zq[x]/(xn1)\mathbb{Z}_q[x]/(x^n - 1). The cyclic modulus xn1x^n - 1 always carries the visible root x=1x = 1, because xn1=(x1)(xn1++1)x^n - 1 = (x - 1)(x^{n-1} + \cdots + 1). The negacyclic modulus xn+1x^n + 1 avoids that particular factor. It may still factor over Zq\mathbb{Z}_q, and when 2nq12n \mid q - 1 it splits completely into nn distinct linear factors. That full factorization is exactly what the NTT exploits, in the “The number theoretic transform” section below.

One hand example fixes the mechanics. Take n=4n = 4, q=17q = 17, f=1+2x+3x2+4x3f = 1 + 2x + 3x^2 + 4x^3, and g=5+6xg = 5 + 6x. The product in Z[x]\mathbb{Z}[x] is

fg=5+16x+27x2+38x3+24x4Z[x].f \cdot g = 5 + 16 x + 27 x^2 + 38 x^3 + 24 x^4 \in \mathbb{Z}[x].

The single term above degree n1=3n - 1 = 3 is 24x424 x^4. Applying x41x^4 \equiv -1 subtracts 2424 from the constant term and leaves the higher terms alone. The folded polynomial is (19)+16x+27x2+38x3(-19) + 16 x + 27 x^2 + 38 x^3. Reducing each coefficient modulo 1717 gives the final answer in R17R_{17}:

fg15+16x+10x2+4x3(modx4+1,17).f \cdot g \equiv 15 + 16 x + 10 x^2 + 4 x^3 \pmod{x^4 + 1, 17}.

The constant term is 1915(mod17)-19 \equiv 15 \pmod{17}. The other three are 1616, 2717=1027 - 17 = 10, and 3834=438 - 34 = 4. The inline code block below implements schoolbook negacyclic multiplication in numpy. It prints the same four coefficients that the hand calculation produced.

import numpy as np
def ring_mul_naive(f, g, q):
n = len(f)
h = np.zeros(n, dtype=np.int64)
for i in range(n):
for j in range(n):
k = i + j
if k < n:
h[k] += f[i] * g[j]
else:
# x^n = -1 wraps the tail into the head with a sign flip
h[k - n] -= f[i] * g[j]
return h % q
f = np.array([1, 2, 3, 4], dtype=np.int64)
g = np.array([5, 6, 0, 0], dtype=np.int64)
h = ring_mul_naive(f, g, 17)
print("f * g in R_17 =", h.tolist())
# ==> f * g in R_17 = [15, 16, 10, 4]

Every Python block this chapter prints is also a standalone file in the companion repository, under chapter-code/ch09/, one file per block. Appendix C covers the clone and the environment they run on.

The package under solutions/ch09-ring-lwe/ ships the same function, ring_mul_naive, in ring_lwe.ring. It also ships a vector-add helper, NTT routines for the Θ(nlogn)\Theta(n \log n) path, and Ring-LWE and Module-LWE sampling. The tests under tests/ch09/ verify the hand example above, negacyclic wraparound on single-term inputs, and commutativity, associativity, and distributivity on random seeds.

A Ring-LWE instance is specified by a parameter tuple (n,q,m,χR)(n, q, m, \chi_R). Here nn is the ring degree, qq is the prime modulus, mm is the number of samples, and χR\chi_R is a short error distribution over ring elements (Lyubashevsky et al., 2010). In the toy code below, “short” means coefficient-wise small: χR\chi_R draws each coefficient uniformly from {B,B+1,,B}\{-B, -B+1, \ldots, B\} for a nonnegative integer noise bound BB. The original Ring-LWE reduction is stated for continuous Gaussian errors in the canonical embedding, added modulo the dual ideal RR^\vee, and passing from there to a discrete coefficient-wise error is a separate translation (Lyubashevsky et al., 2010, sec. 2.3). ML-KEM specifies its small ring elements through a centered binomial distribution Dη(Rq)D_\eta(R_q), parameterized by an integer η{2,3}\eta \in \{2, 3\} (National Institute of Standards and Technology, 2024). These are all short-noise choices, but they should not be treated as automatically interchangeable inside the worst-case reduction: the proof assumptions and the concrete security estimates have to be tracked separately.

Search Ring-LWE. Fix (n,q,m,χR)(n, q, m, \chi_R) and draw aa uniform in RqR_q, ss uniform in RqR_q, and ee from χR\chi_R. Set b=as+eb = a \cdot s + e in RqR_q. The search problem gives the solver mm independent samples (ai,bi)(a_i, b_i) that all share the same secret ss. The task is to recover ss (Lyubashevsky et al., 2010). The solver sees the mm pairs and the parameters. The secret and the errors are hidden. At m=1m = 1, the advantage is the probability of recovering ss from one sample. At larger mm, the solver gets independent algebraic relations on the same secret.

Decisional Ring-LWE. The decisional problem presents mm samples (ai,bi)(a_i, b_i) drawn from one of two distributions with equal probability. On the Ring-LWE side, each bi=ais+eib_i = a_i \cdot s + e_i for a fixed secret ss and independent eie_i from χR\chi_R. On the uniform side, each bib_i is drawn independently and uniformly from RqR_q. The solver must decide which (Lyubashevsky et al., 2010). Decisional Ring-LWE is the distinguishing game at the heart of every IND-CPA-secure scheme built on the ring. Search and decisional Ring-LWE are equivalent up to polynomial factors for a prime qq bounded by a polynomial in nn with q1(mod2n)q \equiv 1 \pmod{2n}, and for the error family the reduction is stated for. The hardness section below gives both conditions (Lyubashevsky et al., 2010, sec. 4).

The definitions above use a uniform secret sRqs \in R_q, the cleanest textbook form. Many practical schemes including ML-KEM use small secrets as well as small errors (National Institute of Standards and Technology, 2024), and the proof and parameter-estimation story for those small-secret variants is not identical to the uniform-secret one. Chapter 11 picks up the small-secret form when assembling ML-KEM.

The compression from flat LWE to Ring-LWE can be stated as a structural identity. Fix nn and consider one sample (a,b)Rq×Rq(a, b) \in R_q \times R_q with b=as+eb = a \cdot s + e. Write the coefficients of aa as (a0,,an1)(a_0, \ldots, a_{n-1}) and the coefficients of ss as a column vector sZqn\mathbf{s} \in \mathbb{Z}_q^n. The coefficients of the ring product asa \cdot s are the entries of a matrix-vector product MasM_a \mathbf{s}, where MaZqn×nM_a \in \mathbb{Z}_q^{n \times n} is a negacyclic circulant determined by aa.

The matrix MaM_a is built column by column. The first column is (a0,a1,,an1)(a_0, a_1, \ldots, a_{n-1})^\top, which is the coefficient vector of aa. Each next column is the previous one rotated down by one position, with the element that wraps off the bottom reintroduced at the top with a sign flip. The sign flip encodes xn1x^n \equiv -1: when a coefficient is shifted past the top, it comes back negated. The last column is (a1,a2,,an1,a0)(-a_1, -a_2, \ldots, -a_{n-1}, a_0)^\top.

The matrix MaM_a has n2n^2 entries, but only nn of them are free. The other n2nn^2 - n are determined by the first column. One element aRqa \in R_q therefore replaces the full n×nn \times n matrix in the flat-LWE presentation.

The compression factor grows with nn. Flat LWE at secret dimension nn and sample count mm stores an m×nm \times n matrix, which is mnmn field elements of public data. Ring-LWE at the same nn and mm stores mm ring elements, which is also mnmn field elements. The savings appear when compared against the algebraic yield of each sample. Each ring element aiRqa_i \in R_q carries nn linear equations on the coefficients of ss. One Ring-LWE sample is therefore comparable to nn rows of a flat LWE matrix. The public data per linear equation drops from nn field elements in flat LWE to one field element in Ring-LWE. That factor-of-nn compression is the practical appeal of the ring form.

The price of the compression is structure: the nn relations from one Ring-LWE sample are not independent flat-LWE rows, but correlated through the negacyclic-circulant form of MaM_a. Ring-LWE is flat LWE restricted to a special algebraic family of matrices, not flat LWE with fewer bytes. The compression is what lets ML-KEM keep encapsulation keys small at every security level: 800800 bytes for ML-KEM-512, 11841184 bytes for ML-KEM-768, and 15681568 bytes for ML-KEM-1024 (National Institute of Standards and Technology, 2024).

Module-LWE generalizes Ring-LWE from one ring element to a rank-kk vector. A Module-LWE instance is specified by a parameter tuple (n,q,k,m,χR)(n, q, k, m, \chi_R), where kk is the module rank. Draw ARqm×k\mathbf{A} \in R_q^{m \times k} uniform, sRqk\mathbf{s} \in R_q^k uniform, and eRqm\mathbf{e} \in R_q^m with each entry from χR\chi_R. Set b=As+e\mathbf{b} = \mathbf{A} \mathbf{s} + \mathbf{e} in RqmR_q^m. The search problem is to recover s\mathbf{s} from (A,b)(\mathbf{A}, \mathbf{b}). The decisional version distinguishes (A,b)(\mathbf{A}, \mathbf{b}) from (A,u)(\mathbf{A}, \mathbf{u}) with u\mathbf{u} uniform in RqmR_q^m (Langlois & Stehlé, 2015).

Two collapse identities place Module-LWE between the two endpoints. At k=1k = 1, the matrix A\mathbf{A} degenerates to a column of ring elements. Each row is then one Ring-LWE sample sharing the same secret sRqs \in R_q. At n=1n = 1, the ring RqR_q degenerates to Zq\mathbb{Z}_q via the explicit isomorphism Zq[x]/(x+1)Zq\mathbb{Z}_q[x]/(x + 1) \cong \mathbb{Z}_q. A residue class modulo x+1x + 1 is determined by its value at x=1x = -1, which is a single field element. Module-LWE at n=1n = 1 is flat LWE at secret dimension kk. Between the two endpoints, Module-LWE with k2k \geq 2 and n2n \geq 2 carries both ring structure and module structure.

A compact comparison of the three families on public data size, secret shape, and underlying hardness is:

FamilyCoefficients in A\mathbf{A}SecretWorst-case hardness source
Flat LWEmnm n field elementsZqn\mathbb{Z}_q^nGapSVP and SIVP on general lattices (Regev, 2009)
Ring-LWEmnm n field elementsone polynomial in RqR_qideal-lattice problems in Z[ζ2n]\mathbb{Z}[\zeta_{2n}] (Lyubashevsky et al., 2010)
Module-LWEmknm k n field elementsRqkR_q^kmodule-lattice problems such as Mod-SIVP at rank kk (Langlois & Stehlé, 2015)

Those figures count the coefficients of A\mathbf{A} alone, not the full public sample (A,b)(\mathbf{A}, \mathbf{b}). Adding b\mathbf{b} gives m(n+1)m(n+1) field elements for flat LWE, 2mn2 m n for Ring-LWE at mm samples, and m(k+1)nm(k+1)n for Module-LWE. The gap between the families is not in bytes but in the algebraic yield per sample. Each flat LWE row is one linear relation on the secret. Each Ring-LWE sample is nn linear relations, one per coefficient of the ring product. At equal A\mathbf{A} storage that is nn times as many constraints. At equal total public storage the ratio is (n+1)/2(n+1)/2 instead, because Ring-LWE spends half its bytes on b\mathbf{b} where flat LWE spends one part in n+1n+1. Module-LWE interpolates: each row of A\mathbf{A} pairs with one entry of b\mathbf{b} to give nn scalar relations, and those nn relations couple all kk ring-valued secret components rather than constraining each of them separately.

The schoolbook multiplication in RqR_q costs Θ(n2)\Theta(n^2) integer multiplications per ring product. At toy parameters this is invisible. At ML-KEM’s n=256n = 256, and across the many ring products inside the module-matrix arithmetic, this is why the NTT is part of the design rather than a cosmetic optimization (National Institute of Standards and Technology, 2024). The number theoretic transform replaces the Θ(n2)\Theta(n^2) convolution with three Θ(nlogn)\Theta(n \log n) transforms and an Θ(n)\Theta(n) pointwise multiplication. The replacement holds when a single arithmetic condition on qq is satisfied.

The condition is that Zq\mathbb{Z}_q contains a primitive 2n2n-th root of unity ψ\psi. A primitive 2n2n-th root is a field element of multiplicative order exactly 2n2n. That is: ψ2n1(modq)\psi^{2n} \equiv 1 \pmod q, and ψd≢1(modq)\psi^d \not\equiv 1 \pmod q for every proper divisor dd of 2n2n. The multiplicative group Zq×\mathbb{Z}_q^\times is cyclic of order q1q - 1. An element of order 2n2n exists if and only if 2n2n divides q1q - 1. At (n,q)=(4,17)(n, q) = (4, 17) this holds because q1=16=22nq - 1 = 16 = 2 \cdot 2n. At ML-KEM’s (n,q)=(256,3329)(n, q) = (256, 3329) the condition fails: q1=3328=25613q - 1 = 3328 = 256 \cdot 13, and 2n=5122n = 512 does not divide 33283328. ML-KEM uses a partial NTT that Chapter 11 develops. It works around the failure by using a primitive 256256-th root and stopping the factorization one level early.

Negacyclic factorization. When 2nq12n \mid q - 1, the polynomial x2n1x^{2n} - 1 splits into 2n2n distinct linear factors over Zq\mathbb{Z}_q:

x2n1=i=02n1(xψi).x^{2n} - 1 = \prod_{i=0}^{2n - 1} (x - \psi^i).

The factor xn1x^n - 1 carries the even powers: xn1=i=0n1(xψ2i)x^n - 1 = \prod_{i=0}^{n-1}(x - \psi^{2i}). The factor xn+1x^n + 1 carries the odd powers:

xn+1=i=0n1(xψ2i+1).x^n + 1 = \prod_{i=0}^{n-1} \left( x - \psi^{2i+1} \right).

This is the defining identity for the ring RqR_q. The modulus xn+1x^n + 1 is a product of nn distinct linear factors over Zq\mathbb{Z}_q. The Chinese remainder theorem then gives a ring isomorphism

Rq    i=0n1Zq[x]/(xψ2i+1)    Zqn.R_q \;\cong\; \prod_{i=0}^{n-1} \mathbb{Z}_q[x] / (x - \psi^{2i+1}) \;\cong\; \mathbb{Z}_q^n.

Each factor on the left is a copy of Zq\mathbb{Z}_q. The identification is: reducing Zq[x]\mathbb{Z}_q[x] modulo a linear polynomial xcx - c evaluates at x=cx = c (Lyubashevsky et al., 2010). The explicit map from left to right is evaluation at the nn odd powers of ψ\psi. A polynomial f(x)Rqf(x) \in R_q goes to the tuple (f(ψ),f(ψ3),,f(ψ2n1))(f(\psi), f(\psi^3), \ldots, f(\psi^{2n-1})). The map is a ring homomorphism. Ring multiplication on the left becomes coordinate-wise multiplication on the right.

The NTT as an evaluation map. The negacyclic number theoretic transform is this CRT isomorphism written as a matrix. Fix nn, qq, and a primitive 2n2n-th root ψ\psi. The NTT of a coefficient vector (f0,,fn1)(f_0, \ldots, f_{n-1}) is the length-nn vector (f^0,,f^n1)(\hat{f}_0, \ldots, \hat{f}_{n-1}) defined by

f^k=i=0n1fiψi(2k+1)(modq),\hat{f}_k = \sum_{i=0}^{n-1} f_i \cdot \psi^{i(2k+1)} \pmod q,

for k=0,1,,n1k = 0, 1, \ldots, n-1. The kk-th entry is exactly f(ψ2k+1)f(\psi^{2k+1}). Because the isomorphism is a ring homomorphism, the product fgf \cdot g in RqR_q satisfies fg^k=f^kg^k\widehat{f \cdot g}_k = \hat{f}_k \cdot \hat{g}_k pointwise over kk.

The inverse transform. The CRT isomorphism has an explicit inverse from an orthogonality relation. Fix ii and jj in {0,1,,n1}\{0, 1, \ldots, n-1\} and compute

k=0n1ψ(ij)(2k+1).\sum_{k=0}^{n-1} \psi^{(i - j)(2k + 1)}.

Pull the ψ(ij)\psi^{(i - j)} factor out of the sum. What remains is k=0n1ω(ij)k\sum_{k=0}^{n-1} \omega^{(i-j) k}, where ω=ψ2\omega = \psi^2 is a primitive nn-th root of unity. The geometric-series identity for roots of unity gives nn when ij(modn)i \equiv j \pmod n and 00 otherwise. For 0i,j<n0 \leq i, j < n this collapses to n[i=j]n \cdot [i = j]. The original sum is then nn when i=ji = j and 00 otherwise. Rearranging, the inverse NTT is

fj=n1k=0n1f^kψj(2k+1)(modq),f_j = n^{-1} \sum_{k=0}^{n-1} \hat{f}_k \cdot \psi^{-j(2k+1)} \pmod q,

for j=0,1,,n1j = 0, 1, \ldots, n-1. The inverse uses the same sum structure as the forward transform. It uses ψ1\psi^{-1} in place of ψ\psi and a final multiplication by n1n^{-1}. Both ψ1\psi^{-1} and n1n^{-1} exist in Zq\mathbb{Z}_q because qq is prime.

At (n,q)=(4,17)(n, q) = (4, 17), a primitive 88-th root of unity is ψ=2\psi = 2. The verification is a chain of exponentiations. We have 21=22^1 = 2, 22=42^2 = 4, 24=1612^4 = 16 \equiv -1, and 28=(1)2=12^8 = (-1)^2 = 1. So ψ=2\psi = 2 has order exactly 88. At (n,q)=(8,17)(n, q) = (8, 17), a primitive 1616-th root is ψ=3\psi = 3. The check is 381(mod17)3^8 \equiv -1 \pmod{17}, from which 31613^{16} \equiv 1 and no smaller divisor of 1616 gives 11. The package helper primitive_2n_root(n, q) searches ψ{2,3,,q1}\psi \in \{2, 3, \ldots, q-1\} until it finds an element of order 2n2n. It caches the result by (n,q)(n, q).

Figure 9.1 shows the negacyclic factorization at n=4n = 4, q=17q = 17. The polynomial x4+1x^4 + 1 factors over Z17\mathbb{Z}_{17} into four distinct linear factors (xψ)(xψ3)(xψ5)(xψ7)(x - \psi)(x - \psi^3)(x - \psi^5)(x - \psi^7), where ψ=2\psi = 2 is a primitive 88-th root of unity. The NTT evaluates a polynomial fR17f \in R_{17} at these four points. It delivers the tuple (f(ψ),f(ψ3),f(ψ5),f(ψ7))(f(\psi), f(\psi^3), f(\psi^5), f(\psi^7)) as the image of ff in Z174\mathbb{Z}_{17}^4. Pointwise multiplication in Z174\mathbb{Z}_{17}^4 corresponds to multiplication in R17R_{17}. The inverse NTT recovers the coefficient vector from the evaluations.

The negacyclic factorization of $x^4 + 1$ over $\mathbb{Z}_{17}$ The negacyclic NTT decomposes R sub 17 into four copies of Z sub 17 by evaluating polynomials at psi to the first equals 2, psi to the third equals 8, psi to the fifth equals 15, and psi to the seventh equals 9. Pointwise multiplication in the product corresponds to ring multiplication. R_17 = Z_17[x] / (x^4 + 1) x^4 + 1 = (x - 2)(x - 8)(x - 15)(x - 9) f(2) f(8) f(15) f(9) Z_17 at psi^1 = 2 Z_17 at psi^3 = 8 Z_17 at psi^5 = 15 Z_17 at psi^7 = 9 The CRT map f → (f(2), f(8), f(15), f(9)) is the negacyclic NTT at n = 4, q = 17. Pointwise multiplication in Z_17^4 corresponds to multiplication in R_17.
Figure 9.1. The negacyclic factorization of x4+1x^4 + 1 over Z17\mathbb{Z}_{17} into four linear factors at the odd powers of the primitive 88-th root of unity ψ=2\psi = 2. The NTT at (n,q)=(4,17)(n, q) = (4, 17) is the Chinese remainder theorem isomorphism R17Z174R_{17} \cong \mathbb{Z}_{17}^4, realized by evaluating a polynomial at {2,8,15,9}\{2, 8, 15, 9\}.

A tiny NTT, end to end. The inline code block below implements the negacyclic NTT from the direct definition at (n,q,ψ)=(4,17,2)(n, q, \psi) = (4, 17, 2). It transforms f=1+2x+3x2+4x3f = 1 + 2x + 3x^2 + 4x^3 and g=5+6xg = 5 + 6x, multiplies pointwise, inverse-transforms, and prints the result. The output matches the schoolbook product 15+16x+10x2+4x315 + 16x + 10x^2 + 4x^3 computed by hand earlier.

import numpy as np
n, q, psi = 4, 17, 2
psi_inv = pow(psi, -1, q)
n_inv = pow(n, -1, q)
def ntt_forward(f):
fhat = np.zeros(n, dtype=np.int64)
for k in range(n):
acc = 0
for i in range(n):
acc += int(f[i]) * pow(psi, i * (2 * k + 1), q)
fhat[k] = acc % q
return fhat
def ntt_inverse(fhat):
f = np.zeros(n, dtype=np.int64)
for j in range(n):
acc = 0
for k in range(n):
acc += int(fhat[k]) * pow(psi_inv, j * (2 * k + 1), q)
f[j] = (n_inv * acc) % q
return f
f = np.array([1, 2, 3, 4], dtype=np.int64)
g = np.array([5, 6, 0, 0], dtype=np.int64)
fhat = ntt_forward(f)
ghat = ntt_forward(g)
hhat = (fhat * ghat) % q
h = ntt_inverse(hhat)
print("fhat =", fhat.tolist())
# ==> fhat = [15, 13, 11, 16]
print("ghat =", ghat.tolist())
# ==> ghat = [0, 2, 10, 8]
print("f * g in R_17 via NTT =", h.tolist())
# ==> f * g in R_17 via NTT = [15, 16, 10, 4]

The direct-definition form runs in Θ(n2)\Theta(n^2) operations, same as the schoolbook convolution. The asymptotic speedup comes from an iterative Cooley-Tukey decomposition layered with the negacyclic pre-twist. At each layer, the nn-point transform splits into two n/2n/2-point transforms plus a linear combination. The input is pre-multiplied by the appropriate powers of ψ\psi before the DFT layer. The pre-twist makes the final evaluation points the odd powers of ψ\psi rather than the even powers. Production implementations in Kyber (the pre-standard NIST PQC submission) and ML-KEM use this layered form with a cache-friendly byte layout and a constant-time Montgomery reduction schedule (Longa & Naehrig, 2016; Seiler, 2018). Chapter 11 walks the iterative NTT at ML-KEM scale when it is time to assemble the KEM.

The agreement between ring_mul_ntt and ring_mul_naive is an algebraic identity, and what the suite in tests/ch09/test_ntt.py shows is a sample of it. At (n,q)=(4,17)(n, q) = (4, 17) and (n,q)=(8,17)(n, q) = (8, 17) it draws 30 polynomial pairs, multiplies them both ways, and checks the results agree. Each parameter set uses one fixed seed for all 30 draws, 2 and 3 respectively, so this is 60 sampled products rather than an exhaustive check.

Sampling Ring-LWE and Module-LWE in Python

Section titled “Sampling Ring-LWE and Module-LWE in Python”

Fix the toy parameters (n,q,B)=(4,17,1)(n, q, B) = (4, 17, 1). The ring degree is n=4n = 4, the modulus is the prime q=17q = 17, and the noise bound is B=1B = 1. Every error coefficient therefore lies in {1,0,1}\{-1, 0, 1\} before reduction modulo qq. The code block below draws one Ring-LWE sample. It verifies the defining identity b=as+eb = a \cdot s + e in RqR_q using the schoolbook multiplication from the first section.

import numpy as np
n, q, B = 4, 17, 1
rng = np.random.default_rng(seed=0)
def ring_mul_naive(f, g, q):
n = len(f)
h = np.zeros(n, dtype=np.int64)
for i in range(n):
for j in range(n):
k = i + j
if k < n:
h[k] += f[i] * g[j]
else:
h[k - n] -= f[i] * g[j]
return h % q
a = rng.integers(low=0, high=q, size=n, dtype=np.int64)
s = rng.integers(low=0, high=q, size=n, dtype=np.int64)
raw_e = rng.integers(low=-B, high=B + 1, size=n, dtype=np.int64)
e = raw_e % q
b = (ring_mul_naive(a, s, q) + e) % q
print("a =", a.tolist())
# ==> a = [14, 10, 8, 4]
print("s =", s.tolist())
# ==> s = [5, 0, 1, 0]
print("raw e =", raw_e.tolist())
# ==> raw e = [-1, 1, 0, 1]
print("b =", b.tolist())
# ==> b = [10, 13, 3, 14]
# Independent identity check. Compute the Z[x] product via numpy.convolve
# and fold the tail into the head with a sign flip (x^n = -1), yielding
# a second path to a * s in R_q that does not call ring_mul_naive.
raw_prod = np.convolve(a, s).astype(np.int64) # length 2n - 1 = 7
folded = raw_prod[:n].copy()
# raw_prod[n:] has n-1 entries at positions n..2n-2; each subtracts into
# folded[0..n-2] under x^n = -1. The head term at position n-1 (degree
# x^{n-1}) does not wrap and stays in folded[n-1] untouched.
folded[:n - 1] -= raw_prod[n:]
expected_b = (folded + e) % q
print("independent expected b =", expected_b.tolist())
# ==> independent expected b = [10, 13, 3, 14]
print("b == expected :", (b == expected_b).all())
# ==> b == expected : True

The secret ss has four coefficients drawn uniformly from Z17\mathbb{Z}_{17}. The ring element aa has the same shape. The error ee has four coefficients drawn from {1,0,1}\{-1, 0, 1\}, with one zero entry for this seed. The final print confirms that the returned bb matches the formula used to build it. The function sample_ring_lwe in the ch09-ring-lwe package under solutions/ returns the full tuple (a,s,e,b)(a, s, e, b). Tests can then verify the identity directly.

Module-LWE at rank k=2k = 2. ML-KEM-512 uses Module-LWE at rank k=2k = 2 over its n=256n = 256 ring. The toy version at (n,q,k,m,B)=(4,17,2,4,1)(n, q, k, m, B) = (4, 17, 2, 4, 1) has the same shape on a much smaller ring. The code block below samples a matrix A\mathbf{A} of four rows and two columns of ring elements. It also samples a secret s\mathbf{s} of two ring elements, an error vector e\mathbf{e} of four ring elements, and the target b=As+e\mathbf{b} = \mathbf{A} \mathbf{s} + \mathbf{e}.

import numpy as np
n, q, k, m, B = 4, 17, 2, 4, 1
def ring_mul_naive(f, g, q):
n = len(f)
h = np.zeros(n, dtype=np.int64)
for i in range(n):
for j in range(n):
kk = i + j
if kk < n:
h[kk] += f[i] * g[j]
else:
h[kk - n] -= f[i] * g[j]
return h % q
rng = np.random.default_rng(seed=0)
A = rng.integers(low=0, high=q, size=(m, k, n), dtype=np.int64)
s = rng.integers(low=0, high=q, size=(k, n), dtype=np.int64)
raw_e = rng.integers(low=-B, high=B + 1, size=(m, n), dtype=np.int64)
e = raw_e % q
b = np.zeros((m, n), dtype=np.int64)
for i in range(m):
row = np.zeros(n, dtype=np.int64)
for j in range(k):
row = (row + ring_mul_naive(A[i, j], s[j], q)) % q
b[i] = (row + e[i]) % q
print("A shape =", A.shape)
# ==> A shape = (4, 2, 4)
print("s shape =", s.shape)
# ==> s shape = (2, 4)
print("b shape =", b.shape)
# ==> b shape = (4, 4)
print("b[0] =", b[0].tolist())
# ==> b[0] = [1, 15, 16, 9]

Each row of the matrix-vector product sums two ring multiplications and one error vector. At k=1k = 1, the inner loop runs once and Module-LWE reduces to Ring-LWE with mm independent pairs sharing the same secret. At k=2k = 2, each row is the sum of two ring products. At ML-KEM-768’s k=3k = 3, the inner loop runs three times. At ML-KEM-1024’s k=4k = 4, it runs four times. The number of inner ring multiplications is mkm k. At the toy parameters above that is 42=84 \cdot 2 = 8 ring multiplications to assemble b\mathbf{b}.

At ML-KEM-512, the public matrix A\mathbf{A} has shape k×kk \times k with k=2k = 2, which is kkn=22256=1024k \cdot k \cdot n = 2 \cdot 2 \cdot 256 = 1024 ring coefficients after expansion. The encapsulation key does not transmit those coefficients directly: it stores the NTT-domain target t^\hat{\mathbf{t}} together with a 3232-byte seed ρ\rho. The encapsulating party extracts ρ\rho from the encapsulation key and regenerates A^\hat{\mathbf{A}} using ML-KEM’s XOF / SampleNTT procedure (National Institute of Standards and Technology, 2024). The secret is a vector of k=2k = 2 ring elements, 2256=5122 \cdot 256 = 512 integers. The Module-LWE form trades a slightly larger expanded matrix for a secret that is kk ring elements rather than one. That trade gives the designer an extra knob for tuning security against speed by varying kk (National Institute of Standards and Technology, 2024).

Worst-case reductions and structural attacks

Section titled “Worst-case reductions and structural attacks”

A note on the cyclotomic framing. The Ring-LWE reduction uses a slightly different algebraic framing than the RqR_q used in the previous sections. Let ζ2n\zeta_{2n} denote a primitive 2n2n-th root of unity in C\mathbb{C}. For nn a power of two, the minimal polynomial of ζ2n\zeta_{2n} over Q\mathbb{Q} is the cyclotomic polynomial Φ2n(x)=xn+1\Phi_{2n}(x) = x^n + 1. The cyclotomic field Q(ζ2n)\mathbb{Q}(\zeta_{2n}) therefore has ring of integers Z[ζ2n]\mathbb{Z}[\zeta_{2n}], and this ring is isomorphic as a Z\mathbb{Z}-algebra to the polynomial ring Z[x]/(xn+1)\mathbb{Z}[x]/(x^n + 1) we have been computing in (Washington, 1997). Under the isomorphism, a polynomial fZ[x]/(xn+1)f \in \mathbb{Z}[x]/(x^n+1) corresponds to the algebraic integer f(ζ2n)Z[ζ2n]f(\zeta_{2n}) \in \mathbb{Z}[\zeta_{2n}].

An ideal lattice in Z[ζ2n]\mathbb{Z}[\zeta_{2n}] is a nonzero ideal IZ[ζ2n]I \subseteq \mathbb{Z}[\zeta_{2n}] viewed as a rank-nn sublattice of Z[ζ2n]\mathbb{Z}[\zeta_{2n}] and, equivalently, as a rank-nn sublattice of Zn\mathbb{Z}^n via the coefficient representation (Lyubashevsky et al., 2010). For the power-of-two cyclotomic rings used in this chapter, the coefficient representation is well behaved and supports the toy arithmetic examples above.

Ring-LWE hardness from ideal lattices. The Lyubashevsky-Peikert-Regev 2010 theorem gives the hardness story for Ring-LWE. Fix the cyclotomic ring R=Z[ζ2n]R = \mathbb{Z}[\zeta_{2n}] with nn a power of two. The theorem says the following. Fix α(0,1)\alpha \in (0, 1) and a modulus q2q \geq 2 with αqω(logn)\alpha q \geq \omega(\sqrt{\log n}), and ask for a solver that works for every error distribution in the family Ψα\Psi_{\leq \alpha}, the elliptical Gaussians in the canonical embedding whose width along each axis is at most α\alpha. The theorem gives a polynomial-time quantum reduction from approximate shortest-vector problems on worst-case ideal lattices in RR to average-case search Ring-LWE over R/qRR / qR with that error family. Its approximation factor grows with 1/α1/\alpha, and is polynomial in nn when 1/α1/\alpha is (Lyubashevsky et al., 2010, sec. 3.1). A single fixed spherical Gaussian is not what the theorem is stated for. The paper notes that hardness for one costs a slightly super-polynomial approximation factor, modulus, and reduction runtime (Lyubashevsky et al., 2010, sec. 1.1). The reduction transfers worst-case hardness of approximate SVP on this narrower class of lattices to average-case hardness of Ring-LWE over the same ring (Lyubashevsky et al., 2010).

The exact statement tracks the canonical embedding, dual ideals, modulus conditions, and the width of the noise distribution. The proof uses cyclotomic-field structure, the dual-basis theory of ideal lattices, and Regev’s iterative quantum reduction from the flat LWE paper (Lyubashevsky et al., 2010; Regev, 2009).

The search-to-decision side of the LPR result is where the arithmetic conditions on qq arrive. That reduction is stated for a prime qq, bounded by a polynomial in nn, that splits completely in Z[ζ2n]\mathbb{Z}[\zeta_{2n}], which happens precisely when q1(mod2n)q \equiv 1 \pmod{2n} (Lyubashevsky et al., 2010). None of the three is required by the worst-case reduction to search Ring-LWE above.

The splitting condition is the same condition as 2nq12n \mid q - 1, the condition that makes the negacyclic NTT available, from the “The number theoretic transform” section above. The coincidence is not accidental: splitting completely in the ring of integers is the algebraic content of the factorization xn+1=i=0n1(xψ2i+1)x^n + 1 = \prod_{i=0}^{n-1}(x - \psi^{2i+1}) over Zq\mathbb{Z}_q. The full-splitting condition is therefore both the condition for the simple full negacyclic NTT and one of the standard algebraic conditions in the LPR search-to-decision reduction for cyclotomic Ring-LWE. The problem variant and noise distribution still matter, so this is not the whole theorem.

ML-KEM is a useful counterpoint: q=3329q = 3329 admits primitive 256256-th roots but not primitive 512512-th roots, so x256+1x^{256} + 1 factors into 128128 quadratic pieces rather than 256256 linear ones, and Chapter 11’s partial NTT mirrors that incomplete splitting (National Institute of Standards and Technology, 2024).

Module-LWE hardness from module lattices. The Langlois-Stehlé 2015 theorem is the corresponding statement for Module-LWE. Fix the same ring RR, a module rank k1k \geq 1, an α(0,1)\alpha \in (0, 1), and a modulus q2q \geq 2 of known factorization with αq>2kω(logn)\alpha q > 2\sqrt{k} \cdot \omega(\sqrt{\log n}). In informal form, their Theorem 4.7 gives a quantum polynomial-time reduction from approximate module-lattice problems, in particular Mod-SIVP, to average-case search Module-LWE over RqkR_q^k with the same error family Ψα\Psi_{\leq \alpha} as above, at approximation factor k8nω(logn)/αk \sqrt{8 n} \cdot \omega(\sqrt{\log n}) / \alpha, which is polynomial in knkn when 1/α1/\alpha is. The decision form follows by a direct search-to-decision step when qq is a prime bounded by a polynomial in knkn with q1(mod2n)q \equiv 1 \pmod{2n}, and for a modulus of any other shape through their Theorem 4.8, a modulus-switching step that enlarges the noise by a stated factor (Langlois & Stehlé, 2015, sec. 4.1). A module lattice of rank kk in RkR^k is a finitely generated RR-submodule of RkR^k of full rank, one that contains kk RR-linearly independent vectors, viewed as a rank-knkn sublattice of Zkn\mathbb{Z}^{kn} via the coefficient representation. At k=1k = 1, module lattices specialize to ideal lattices, and the module-lattice framework recovers the Ring-LWE endpoint.

Module-LWE sits between flat LWE and Ring-LWE in the amount of algebraic structure exposed. As kk grows, the structure is less concentrated in a single ideal-lattice component and the underlying module-lattice problem looks more like a general lattice problem (Langlois & Stehlé, 2015).

Practical schemes pick Module-LWE for flexibility. The ring Rq=Z3329[x]/(x256+1)R_q = \mathbb{Z}_{3329}[x]/(x^{256}+1) supports three ML-KEM security levels by varying the module rank kk from 22 to 44, rather than three different rings at three different values of nn (National Institute of Standards and Technology, 2024).

Structural attacks. Ideal and module lattices are a narrower class of lattices than the general-lattice family that the flat LWE reduction covers. The narrower class leaves room for attacks that exploit the algebraic structure of the ring. Two structural attack families are worth flagging here, and each bites only in its own parameter regime.

The first targets Ring-LWE over specific non-dual or non-cyclotomic rings. Castryck, Iliashenko, and Vercauteren revisited earlier weak-instance results and showed that distinguishing attacks on these rings succeed at cost far below what flat LWE would face at the same dimension (Castryck et al., 2016). These weak instances exploit non-dual or distorted error embeddings rather than the canonical Gaussian embedding that LPR assumes (Castryck et al., 2016).

The second family targets cyclotomic rings with small proper subfields. Sub-field attacks exploit the factorization of the cyclotomic minimal polynomial over those subfields to reduce a high-dimensional ring problem to a lower-dimensional one on the subfield, where lattice reduction is cheaper. Albrecht, Bai, and Ducas 2016 give the canonical sub-field attack on overstretched NTRU and related ring-based instances (Albrecht et al., 2016). These attacks become efficient in the overstretched regime, where the modulus qq grows super-polynomially in nn and the norm map down to a proper subfield (including, for power-of-two cyclotomics, the maximal real subfield) produces a strictly cheaper lattice problem (Albrecht et al., 2016). Power-of-two cyclotomics are not immune on principle: their subfield tower is rich enough to support the projection.

ML-KEM is not in the overstretched NTRU regime that these attacks target. Its modulus is fixed at q=3329q = 3329 with n=256n = 256, so its security estimates are dominated by the usual primal, dual, and hybrid lattice attacks on Module-LWE rather than by sub-field projection (Albrecht et al., 2016). The lesson is that structural attacks remain highly parameter- and assumption-specific. Chapter 13 walks the best-known primal and dual attacks on ideal and module lattices at cryptographic parameters. It states the cost model and places the three ML-KEM parameter sets of FIPS 203 §8 (National Institute of Standards and Technology, 2024) on the resulting curve.

Flat LWE (Chapter 8) is the source of lattice hardness with a quantum reduction from worst-case general-lattice problems (Regev, 2009). Ring-LWE compresses the public data by a factor of nn per algebraic relation and adds a fast-multiplication algorithm, at the cost of restricting to structured lattices (Lyubashevsky et al., 2010). Module-LWE interpolates between the two by varying the module rank kk (Langlois & Stehlé, 2015). Chapter 10 builds Regev-style public-key encryption over flat LWE, then shows the Ring-LWE descendant that replaces the flat sample (A,b)(A, b) with a ring sample (a,b)(a, b). Chapter 11 replaces the Zq\mathbb{Z}_q secret with a Module-LWE secret, adds the Fujisaki-Okamoto transform from Chapter 5, and lands on ML-KEM (National Institute of Standards and Technology, 2024). Chapter 13 walks the primal and dual attack families on ideal and module lattices. It places the three parameter sets of FIPS 203 §8, ML-KEM-512, ML-KEM-768, and ML-KEM-1024 (National Institute of Standards and Technology, 2024), on the best-known cost curve.

The hash-based signatures of Chapter 14 and Chapter 15 rest on preimage and collision resistance. Those are assumptions about concrete hash functions, not worst-case lattice problems. The code-based KEMs of Chapter 20 (Classic McEliece) and Chapter 21 (HQC) rest on syndrome decoding. The bounded-weight syndrome decoding decision problem is NP-complete (Berlekamp et al., 1978). That is a worst-case theorem. The concrete instances those schemes rest on need an average-case hardness assumption that no reduction from the worst case supplies, which Chapter 3 states as a working conjecture. The isogeny-based signature of Chapter 23 (SQIsign) rests on the endomorphism ring problem on supersingular elliptic curves. Its cryptanalytic surface is still being explored. The lattice family has a worst-case-to-average-case reduction from a well-studied geometric problem, the reductions stated above (Langlois & Stehlé, 2015; Lyubashevsky et al., 2010). Of the Part II through Part IV families surveyed here, it is the only one that does.

Exercise 1. Negacyclic wraparound at n=8n = 8. Using ring_mul_naive from the package or the inline definition in the first code block, multiply f=x5f = x^5 by g=x4g = x^4 in R17=Z17[x]/(x8+1)R_{17} = \mathbb{Z}_{17}[x]/(x^8 + 1). By hand, the product in Z[x]\mathbb{Z}[x] is x9x^9. Reduce using x81x^8 \equiv -1 to get x-x, then reduce modulo 1717 to get 16x16 x. Run the function and verify that it prints [0,16,0,0,0,0,0,0][0, 16, 0, 0, 0, 0, 0, 0]. Now replace gg with x3x^3. The product in Z[x]\mathbb{Z}[x] is x8x^8, which reduces to 1=16-1 = 16. Verify the function prints [16,0,0,0,0,0,0,0][16, 0, 0, 0, 0, 0, 0, 0].

Exercise 2. Forward NTT of f=xf = x. At (n,q,ψ)=(4,17,2)(n, q, \psi) = (4, 17, 2), compute the forward NTT of f=xf = x by hand. The coefficient vector is [0,1,0,0][0, 1, 0, 0]. The result is f^k=ψ2k+1\hat{f}_k = \psi^{2k+1} for k=0,1,2,3k = 0, 1, 2, 3. That is (ψ,ψ3,ψ5,ψ7)=(2,8,32,128)(mod17)(\psi, \psi^3, \psi^5, \psi^7) = (2, 8, 32, 128) \pmod{17}, which reduces to (2,8,15,9)(2, 8, 15, 9). Run ntt_forward([0, 1, 0, 0]) from the package and verify it returns the same four values.

Exercise 3. Module-LWE at rank k=3k = 3. Draw a Module-LWE instance at (n,q,k,m,B)=(4,17,3,4,1)(n, q, k, m, B) = (4, 17, 3, 4, 1) using sample_module_lwe from the package. Verify the defining identity by recomputing b[i]=j=02A[i,j]s[j]+e[i]\mathbf{b}[i] = \sum_{j=0}^{2} \mathbf{A}[i, j] \cdot \mathbf{s}[j] + \mathbf{e}[i] with ring_mul_naive on every term. Compare term by term against the returned b\mathbf{b}. The test file tests/ch09/test_module_collapses_to_ring.py does this check for k=1,2,3k = 1, 2, 3. The exercise is to write the verification yourself.

Exercise 4. Primes admitting the NTT at n=8n = 8. The negacyclic NTT at n=8n = 8 requires a prime qq with 16q116 \mid q - 1. Write a short Python loop that finds every prime q<200q < 200 satisfying this condition. Pick any such qq and use primitive_2n_root(8, q) from the package to find a primitive 1616-th root of unity ψ\psi. Verify by hand that ψ\psi has order 1616: ψ161(modq)\psi^{16} \equiv 1 \pmod q and ψ8≢1(modq)\psi^8 \not\equiv 1 \pmod q. Run ntt_forward and ntt_inverse on a random polynomial and verify the round-trip.

Worked solutions and editorial notes for these exercises are in Appendix D, Chapter 9. A separate track, for rebuilding rather than reading: the package exercises/ch09-ring-lwe has every function the chapter teaches replaced by a stub. Run PQC_IMPL=exercises pytest tests/ch09 to grade your version against the suite that proves the reference one.

Albrecht, M. R., Bai, S., & Ducas, L. (2016). A subfield lattice attack on overstretched NTRU assumptions: Cryptanalysis of some FHE and graded encoding schemes. Advances in Cryptology – CRYPTO 2016, 9814, 153–178. https://doi.org/10.1007/978-3-662-53018-4_6
Berlekamp, E. R., McEliece, R. J., & van Tilborg, H. C. A. (1978). On the inherent intractability of certain coding problems. IEEE Transactions on Information Theory, 24(3), 384–386. https://doi.org/10.1109/TIT.1978.1055873
Castryck, W., Iliashenko, I., & Vercauteren, F. (2016). Provably weak instances of Ring-LWE revisited. In M. Fischlin & J.-S. Coron (Eds.), Advances in Cryptology – EUROCRYPT 2016 (Vol. 9665, pp. 147–167). Springer. https://doi.org/10.1007/978-3-662-49890-3_6
Lang, S. (2002). Algebra (Revised 3rd, Vol. 211). Springer. https://link.springer.com/book/10.1007/978-1-4613-0041-0
Langlois, A., & Stehlé, D. (2015). Worst-case to average-case reductions for module lattices. Designs, Codes and Cryptography, 75(3), 565–599. https://doi.org/10.1007/s10623-014-9938-4
Longa, P., & Naehrig, M. (2016). Speeding up the number theoretic transform for faster ideal lattice-based cryptography. In S. Foresti & G. Persiano (Eds.), Cryptology and Network Security – CANS 2016 (Vol. 10052, pp. 124–139). Springer. https://doi.org/10.1007/978-3-319-48965-0_8
Lyubashevsky, V., Peikert, C., & Regev, O. (2010). On ideal lattices and learning with errors over rings. In H. Gilbert (Ed.), Advances in Cryptology – EUROCRYPT 2010 (Vol. 6110, pp. 1–23). Springer. https://doi.org/10.1007/978-3-642-13190-5_1
National Institute of Standards and Technology. (2024). FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard. Federal Information Processing Standards Publication. https://doi.org/10.6028/NIST.FIPS.203
Regev, O. (2009). On lattices, learning with errors, random linear codes, and cryptography. Journal of the ACM, 56(6), 34:1-34:40. https://doi.org/10.1145/1568318.1568324
Seiler, G. (2018). Faster AVX2 optimized NTT multiplication for Ring-LWE lattice cryptography. IACR Cryptology ePrint Archive. https://eprint.iacr.org/2018/039
Washington, L. C. (1997). Introduction to Cyclotomic Fields (2nd ed., Vol. 83). Springer. https://doi.org/10.1007/978-1-4612-1934-7

Last updated: