Skip to content

Appendix D: Solutions for Chapter 8

This page collects solutions and editorial notes for the exercises in Chapter 8: The LWE problem. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.

The fuller versions of these routines are in the lwe package under solutions/ch08-lwe. From a clone of the companion repository, pytest tests/ch08 runs its suite. Appendix C has the setup.

The expected behavior is not a gradient. Over 100 seeds at each of B=1B = 1, 33, 1010 and 3030, solve_mod_q\mathrm{solve\_mod\_q} returns None on 100 of 100 instances at every width, and never returns the true secret. Recovery has already failed completely at B=1B = 1, which is the point the chapter’s own worked block makes when it prints recovered noisy = None. There is no BB at which the solver degrades gracefully. A single unit of noise is enough. The “wrong secret” outcome stays rare because solve_mod_q\mathrm{solve\_mod\_q} either finds a candidate satisfying every consistency row or reports failure. Spurious-but-consistent secrets are unlikely with mn=4m - n = 4 extra check rows.

The residual distribution is the reason, and it also does not change with BB. The consistency check on the last mnm - n rows asks Achecksbcheck(modq)A_{\text{check}} s \equiv b_{\text{check}} \pmod q exactly. Those rows do not carry the raw error: forward elimination replaces each one with a Zq\mathbb{Z}_q-linear combination of every error coordinate, scaled by pivot inverses. Multiplying a small error by an arbitrary element of Zq\mathbb{Z}_q destroys its smallness. Measured over 200 seeds, the mean absolute residual in symmetric representatives is 24.224.2 at B=1B = 1 and 24.324.3 at B=30B = 30. A value drawn uniformly from Z97\mathbb{Z}_{97} averages 24.224.2. The fraction of nonzero consistency rows runs from 98.4%98.4\% to 99.1%99.1\% across the same range. The residuals are indistinguishable from uniform as soon as any noise is present, which is why the failure is immediate rather than progressive.

A strong answer connects that to the final part of the question. The check detects noise but extracts nothing from it, so it is not an attack: it says an instance is noisy without narrowing the search for ss. The regime where a consistent but wrong secret comes back is m=nm = n, where there are no consistency rows left to fire, and it is reached by varying mm rather than BB.

The brute force enumerates qnq^n candidate secrets. For (q,n)=(97,4)(q, n) = (97, 4) the loop runs 974=88,529,28197^4 = 88{,}529{,}281 iterations, each computing Asb\lVert A s - b \rVert_\infty in O(mn)O(m n) work. Write it as a per-candidate Python loop and it takes about fourteen minutes at roughly 10510^5 candidates per second; batch the candidates through numpy and the same enumeration finishes in about thirty seconds. Either is fine for one instance, and neither scales.

On a real LWE pair the minimum residual is at most BB, achieved at the true secret. On a uniform pair it is not q/2q/2, and assuming so would break the distinguisher. The minimum is taken over qnq^n candidates, so it is the smallest of about 88.588.5 million draws. A residual of \infty-norm at most tt has probability ((2t+1)/q)m((2t+1)/q)^m. Setting qn((2t+1)/q)m1q^n \cdot ((2t+1)/q)^m \approx 1 at (n,q,m)=(4,97,8)(n, q, m) = (4, 97, 8) gives t4t \approx 4. Measured over six uniform pairs, the minimum residual came out 44, 44, 55, 55, 55, 66. The distinguisher still separates cleanly at the threshold B=1B = 1, but the margin is one unit against four, not one against forty-eight.

Cost is O(mnqn)O(m n \cdot q^n). For fixed nn this is polynomial in qq. For fixed qq it is O(qn)O(q^n), exponential in nn. ML-KEM-768 is Module-LWE at module rank 33 over a degree-256256 ring, so its flat-LWE dimension is 3256=7683 \cdot 256 = 768. The brute force is infeasible there by many hundreds of orders of magnitude, and lattice cryptography rests on exactly that gap.

print(97 ** 4)
# ==> 88529281

The expected output: detB=974=88,529,281\lvert \det B \rvert = 97^4 = 88{,}529{,}281, and Av0(mod97)A^\top v \equiv 0 \pmod{97} for every row vv of BB. The construction depends on AA having full column rank modulo qq. In the chapter’s notation, Λq(A)={vZm:Av0(modq)}\Lambda_q^\perp(A) = \{v \in \mathbb{Z}^m : A^\top v \equiv 0 \pmod q\} has determinant qnq^n exactly when AA has rank nn modulo qq. If AA is rank-deficient mod qq, the lattice has smaller determinant and the constructed basis fails the orthogonality check on the extra rows.