Introduction
https://yuhi-sa.github.io/en/posts/20260614_cryptography_roadmap/1/ listed “Post-Quantum Cryptography (CRYSTALS-Kyber / Dilithium)” as the final placeholder for a future article. This article implements it. The factoring problem behind https://yuhi-sa.github.io/en/posts/20260225_rsa/1/ and the discrete-log problems behind https://yuhi-sa.github.io/en/posts/20260614_diffie_hellman/1/ and https://yuhi-sa.github.io/en/posts/20260702_elliptic_curve_cryptography/1/ are all known to be solvable in polynomial time on a quantum computer via Shor’s algorithm. This article examines why the Learning With Errors (LWE) problem is believed to resist quantum computers too, by implementing Regev encryption (an LWE-based public-key cryptosystem) from scratch and running numerical experiments.
Why Quantum Computers Are a Threat: Shor’s Algorithm
RSA, Diffie-Hellman, and ECC all derive their security from the assumption that certain number-theoretic problems (factoring, discrete logarithm) cannot be solved efficiently on a classical computer. What Peter Shor showed in 1994 was that efficient (polynomial-time) algorithms for these problems exist on a quantum computer. This means that once a sufficiently large quantum computer exists, RSA-, DH-, and ECC-based cryptography currently in use would in principle all become breakable. This is why the transition to Post-Quantum Cryptography (PQC), designed to resist quantum computers, is underway. NIST finalized lattice-based CRYSTALS-Kyber (key encapsulation, standardized as FIPS 203) and CRYSTALS-Dilithium (signatures, FIPS 204) as standards in 2024.
The Learning With Errors (LWE) Problem
The foundation of many post-quantum schemes, including Kyber, is the LWE problem, proposed by Oded Regev in 2005. Intuitively, it exploits the property that “mixing a small amount of noise into a system of linear equations makes solving it dramatically harder.”
LWE problem definition: given a secret vector \(\mathbf{s} \in \mathbb{Z}_q^n\) , a random matrix \(A \in \mathbb{Z}_q^{m \times n}\) , and a small error vector \(\mathbf{e} \in \mathbb{Z}_q^m\) (each component drawn from, e.g., a normal distribution), compute
\[ \mathbf{b} = A\mathbf{s} + \mathbf{e} \pmod{q} \tag{1} \]Given \((A, \mathbf{b})\) , find \(\mathbf{s}\) . Without the error term \(\mathbf{e}\) , for \(m \geq n\) this is trivial: just solve the linear system (modular linear algebra) to find \(\mathbf{s}\) uniquely. With the error added, this simple approach breaks down, and the problem is instead shown to reduce to a hard lattice problem (such as the shortest vector problem). No known quantum algorithm — including Shor’s — is believed to solve this lattice problem efficiently.
Regev Encryption: LWE-Based Public-Key Encryption (Single-Bit Version)
The LWE problem’s hardness can be used to construct a public-key cryptosystem, as follows (a simplified version of Regev, 2005).
Key generation: choose a secret key \(\mathbf{s} \in \mathbb{Z}_q^n\) at random. The public key is the LWE instance \((A, \mathbf{b} = A\mathbf{s} + \mathbf{e})\) .
Encryption (encrypting a single bit \(b \in \{0,1\}\) ): choose a random subset \(\mathbf{r} \in \{0,1\}^m\) , and compute
\[ \mathbf{u} = \mathbf{r}^\top A \pmod q, \qquad v = \mathbf{r}^\top \mathbf{b} + b \cdot \lfloor q/2 \rfloor \pmod q \tag{2} \]as the ciphertext.
Decryption: compute
\[ v - \mathbf{u}^\top \mathbf{s} = \mathbf{r}^\top\mathbf{e} + b\cdot\lfloor q/2\rfloor \pmod q \tag{3} \]Since \(\mathbf{r}^\top\mathbf{e}\) is small (a sum of error terms), the result is close to \(0\) if \(b=0\) and close to \(q/2\) if \(b=1\) .
Python Implementation
import numpy as np
n, q, m, sigma = 32, 3329, 128, 2.0 # toy parameters (q matches Kyber's modulus)
rng = np.random.default_rng(20260715) # seed fixed for every experiment in this article
def sample_error(size, sigma, q):
e = np.round(rng.normal(0, sigma, size)).astype(np.int64) % q
return e
def keygen():
s = rng.integers(0, q, size=n)
A = rng.integers(0, q, size=(m, n))
e = sample_error(m, sigma, q)
b = (A @ s + e) % q
return (A, b), s
def encrypt(pub, bit, q):
A, b = pub
r = rng.integers(0, 2, size=A.shape[0])
u = (r @ A) % q
v = (int(r @ b) + bit * (q // 2)) % q
return u, v
def decrypt(sk, ct, q):
u, v = ct
raw = (v - int(u @ sk)) % q
return 1 if abs(raw - q // 2) < q // 4 else 0
Deriving Correctness: Noise Accumulation and the Decryption Condition
The decrypt function recovers the bit with the threshold test abs(raw - q // 2) < q // 4. From equation (3), \(\mathtt{raw} = \mathbf{r}^\top\mathbf{e} + b\cdot\lfloor q/2\rfloor \pmod q\)
, so the necessary and sufficient condition for this test to return the correct bit is, as a signed integer before reduction mod \(q\)
,
(for \(b=0\) , \(\mathtt{raw}\approx 0\) must stay under \(q/4\) ; for \(b=1\) , \(\mathtt{raw}\approx\lfloor q/2\rfloor\) must stay within \(q/4\) of \(q/2\) — a symmetric condition). So the probability of correct decryption is exactly the probability that the accumulated error \(\mathbf{r}^\top\mathbf{e}\) stays below \(q/4\) . Let’s evaluate this concretely.
Step 1: the distribution of the accumulated error. With \(\mathbf{r}\in\{0,1\}^m\) and each error component \(e_i\) i.i.d. \(\mathcal{N}(0,\sigma^2)\) (treating the rounding as a continuous approximation), let \(k=\sum_i r_i\) be the Hamming weight of \(\mathbf{r}\) . Conditional on \(\mathbf{r}\) , this is a linear combination of independent Gaussians, so exactly (not merely by CLT):
\[ \mathbf{r}^\top\mathbf{e} \mid \mathbf{r} \sim \mathcal{N}(0,\, k\sigma^2) \tag{5} \]Step 2: a rigorous worst-case bound (provable). Since \(0 \le k \le m\) always holds, applying the standard Gaussian tail bound \(P(|Z|>t) \le 2\exp(-t^2/2v)\) for \(Z\sim\mathcal{N}(0,v)\) with \(t=q/4\) , \(v=k\sigma^2 \le m\sigma^2\) gives, regardless of \(\mathbf{r}\) :
\[ P(\text{decryption fails}\mid \mathbf{r}) \le 2\exp\!\left(-\frac{q^2}{32\,k\,\sigma^2}\right) \le 2\exp\!\left(-\frac{q^2}{32\,m\,\sigma^2}\right) \tag{6} \]To keep the failure probability under \(2^{-\lambda}\) , this gives the design constraint
\[ q \;\gtrsim\; 4\sigma\sqrt{2m\lambda\ln 2} \;=\; \Theta\!\left(\sigma\sqrt{m\lambda}\right) \tag{7} \]— the basic noise-growth law that recurs throughout LWE-based cryptosystem design: the modulus \(q\) must scale with the noise standard deviation \(\sigma\) times roughly the square root of the number of samples \(m\) being combined. The dimension \(n\) doesn’t appear directly, but since \(m\) is usually chosen proportional to \(n\) (this article uses \(m=4n\) ), a larger \(n\) indirectly forces a larger \(q\) to keep the same safety margin.
Step 3: a sharper practical estimate. Equation (6) is a provable bound using the worst case \(k\le m\) , but in practice \(\mathbf{r}\) is drawn uniformly from \(\{0,1\}^m\) , so \(k\) follows \(\mathrm{Binomial}(m, 1/2)\) and concentrates tightly around \(m/2\) (Chernoff bound). Substituting \(k\approx m/2\) gives the closer-to-reality approximation
\[ P(\text{decryption fails}) \approx 2\left(1-\Phi\!\left(\frac{q/4}{\sigma\sqrt{m/2}}\right)\right) \tag{8} \]which we check directly against measurement in Experiment 2 below.
Experiment 1: Verifying Decryption Correctness
Using toy parameters \(n=32\)
(lattice dimension), \(q=3329\)
(the same modulus Kyber uses), \(m=128\)
(LWE samples in the public key), and \(\sigma=2.0\)
(error standard deviation), with seed 20260715 fixed, we ran 2000 trials of encrypting and decrypting a random bit.
correct decryptions: 2000/2000 (100.00%)
All 2000 trials decrypted correctly. At \(\sigma=2.0\) , equation (8) predicts a failure probability far below \(10^{-100}\) (\(q/4\approx 832\) against \(\sigma\sqrt{m/2}=2\times 8=16\) — the threshold is more than 50 standard deviations away), so 0 failures out of 2000 is exactly what the theory predicts.
Experiment 2: The Noise σ vs. Correctness Trade-Off (Theory vs. Measurement)
The central design dilemma in post-quantum cryptography is that “a larger error \(\mathbf{e}\) is more secure (the LWE problem gets harder), but too large and even the legitimate recipient fails to decrypt.” To test the prediction of equation (8), we swept \(\sigma\) across 27 points from 2 to 300, running 20 key generations × 400 encrypt/decrypt trials at each point (8000 trials per point), and overlaid the measured success rate on the theoretical curves from equations (6) and (8).
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
n, q, m = 32, 3329, 128
SEED = 20260715
def sample_error(size, sigma, q, rng):
return np.round(rng.normal(0, sigma, size)).astype(np.int64) % q
def keygen(rng, sigma):
s = rng.integers(0, q, size=n)
A = rng.integers(0, q, size=(m, n))
e = sample_error(m, sigma, q, rng)
b = (A @ s + e) % q
return (A, b), s
def encrypt(pub, bit, rng):
A, b = pub
r = rng.integers(0, 2, size=A.shape[0])
u = (r @ A) % q
v = (int(r @ b) + bit * (q // 2)) % q
return u, v
def decrypt(sk, ct):
u, v = ct
raw = (v - int(u @ sk)) % q
return 1 if abs(raw - q // 2) < q // 4 else 0
def theory_typical_success(sigma): # equation (8)
z = (q / 4) / (sigma * np.sqrt(m / 2))
return 100 * (1 - 2 * norm.sf(z))
sigmas = [2, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112,
120, 128, 140, 150, 165, 175, 190, 200, 225, 250, 275, 300]
n_keygens, trials_per_keygen = 20, 400
rng_master = np.random.default_rng(SEED)
for sigma in sigmas:
correct, total = 0, 0
for _ in range(n_keygens):
rng = np.random.default_rng(rng_master.integers(0, 2**31 - 1))
pub, sk = keygen(rng, sigma)
for _ in range(trials_per_keygen):
bit = int(rng.integers(0, 2))
ct = encrypt(pub, bit, rng)
correct += decrypt(sk, ct) == bit
total += 1
print(sigma, 100 * correct / total, theory_typical_success(sigma))
| \(\sigma\) | Measured success | Eq. (8) prediction |
|---|---|---|
| 32 | 99.99% | 99.88% |
| 64 | 91.50% | 89.59% |
| 96 | 69.65% | 72.15% |
| 128 | 61.83% | 58.36% |
| 150 | 53.50% | 51.20% |
| 200 | 50.89% | 39.70% |
| 300 | 50.02% | 27.12% |

For \(\sigma \lesssim 100\)
, measurement and the theoretical prediction from equation (8) agree closely. For \(\sigma \gtrsim 150\)
, however, the theoretical predictions (equations (6) and (8)) keep decreasing toward 0%, while the measured success rate plateaus at 50% (a coin flip) and never drops further. This is because the “continuous Gaussian \(e_i\)
” approximation of equation (5) breaks down: the implementation’s sample_error reduces the error mod \(q\)
, and once \(\sigma\)
grows to the same order as \(q\)
, the error is no longer “small Gaussian noise” but nearly uniform on \(\mathbb{Z}_q\)
. In that regime raw is also nearly uniform on \(\mathbb{Z}_q\)
, and since the decision interval \((q/4, 3q/4)\)
spans exactly half of \(\mathbb{Z}_q\)
, the probability of returning 1 converges to exactly 1/2 regardless of the true bit. Equation (8) doesn’t model this modular wraparound, so it predicts a pessimistically low success rate at large \(\sigma\)
— a useful failure case, since it shows exactly where the simplified Gaussian approximation stops applying. Real Kyber carefully tunes this trade-off, using a far more sophisticated error distribution and parameter selection than this toy implementation, to keep the decryption failure probability around \(2^{-140}\)
while maintaining strong security.
Edge Case (a): Decryption Actually Fails Once the Threshold Is Crossed
The theory and the figure suggest that pushing \(\sigma\)
far past the safe zone should make decryption actually return the wrong bit. We verify this with a single fixed key. From the same seed 20260715, we first generate a key, then run 1000 encrypt/decrypt trials (since the true-bit sequence is drawn from the RNG state left over after key generation, the following “unsafe” and “safe” runs encrypt the exact same sequence of bits).
def demo(sigma, n_show, n_total, seed):
rng = np.random.default_rng(seed)
pub, sk = keygen(rng, sigma)
mismatches = 0
for t in range(n_total):
bit = int(rng.integers(0, 2))
ct = encrypt(pub, bit, rng)
u, v = ct
raw = (v - int(u @ sk)) % q
dec = 1 if abs(raw - q // 2) < q // 4 else 0
if t < n_show:
print(t, bit, dec, raw, "OK" if dec == bit else "FAIL")
mismatches += dec != bit
print(f"correct: {n_total - mismatches}/{n_total} ({100*(n_total-mismatches)/n_total:.2f}%)")
demo(sigma=150, n_show=20, n_total=1000, seed=20260715) # unsafe regime
demo(sigma=2, n_show=20, n_total=1000, seed=20260715) # safe regime (same seed)
First 20 trials in the unsafe regime (\(\sigma=150\) , equation (8) predicts 51.20% success):
trial true_bit decrypted_bit raw match
0 1 1 1213 OK
1 0 1 1071 FAIL
2 0 1 1819 FAIL
3 1 0 686 FAIL
4 1 0 505 FAIL
5 0 0 312 OK
6 1 0 3128 FAIL
7 1 0 649 FAIL
8 1 0 2807 FAIL
9 1 0 3068 FAIL
10 0 0 667 OK
11 1 0 3119 FAIL
12 1 0 3107 FAIL
13 0 1 1302 FAIL
14 1 0 2626 FAIL
15 0 0 614 OK
16 0 0 635 OK
17 1 0 3301 FAIL
18 1 1 1752 OK
19 0 0 3316 OK
... (1000 trials total)
correct decryptions: 308/1000 (30.80%)
At σ=150, only 308 of 1000 trials (30.80%) decrypted correctly — worse than a coin flip. This happens because the particular error vector \(\mathbf{e}\) generated for this key happens to carry a bias: \(\mathbb{E}_{\mathbf{r}}[\mathbf{r}^\top\mathbf{e}\mid\mathbf{e}] = \frac{1}{2}\sum_i e_i\) is a key-specific systematic shift that, for this particular key, pushed the threshold test the wrong way. (Equation (8) predicts the average behavior across many keys — it doesn’t guarantee the behavior of any single specific key, and this is a concrete example of that gap.)
Rewinding to \(\sigma=2\) (safe regime) with the same seed:
correct decryptions: 1000/1000 (100.00%)
With the same random seed and the same plaintext-bit sequence, dropping σ from 150 to 2 alone brings decryption back to 1000/1000 correct. The importance of keeping the noise in the safe regime is confirmed with concrete numbers.
As an aside, \(\sigma=32\) — which looked like “100.00%” in Experiment 2’s table — showed 4 failures out of 1000 trials (99.60%) once we ran enough trials to see it. A failure event too rare to show up in 300 trials becomes visible at 1000: decryption correctness is a high-probability guarantee, not an absolute one, and this boundary case makes that concrete.
Experiment 3: Without the Error, the Secret Key Is Recovered Instantly
To directly confirm that LWE’s hardness comes specifically from the error term, we tested whether the secret key could be recovered in the error-free case (\(\mathbf{b} = A\mathbf{s}\) , an ordinary linear system).
import sympy
A0 = np.array(rng.integers(1, q, size=(n, n)))
s_true = rng.integers(0, q, size=n)
b0 = (A0 @ s_true) % q # no error term
A0_inv_mod = sympy.Matrix(A0.tolist()).inv_mod(q) # matrix inverse mod q
s_recovered = np.array([int(x) for x in (A0_inv_mod * sympy.Matrix(b0.tolist())) % q]).flatten()
max |recovered - true| (should be 0): 0
s fully recovered: True
Computing the matrix inverse mod \(q\) recovered the secret key completely and instantly. This shows that an error-free system of equations is solvable by simple linear algebra (\(O(n^3)\) ). The error is essential to the LWE problem’s hardness — it’s exactly what enables the reduction to the “shortest vector problem on a lattice,” a problem with no known efficient solution, even on a quantum computer.
Edge Case (b): The Parameter-Size Security/Efficiency Trade-Off
In this article’s Regev scheme, the ciphertext is \((\mathbf{u}, v) \in \mathbb{Z}_q^n \times \mathbb{Z}_q\) and the public key is \((A, \mathbf{b}) \in \mathbb{Z}_q^{m\times n} \times \mathbb{Z}_q^m\) . Counting bits with \(\lceil\log_2 q\rceil = 12\) (\(q=3329\) ):
\[ \begin{aligned} \text{ciphertext size} &= (n+1)\lceil\log_2 q\rceil \text{ bits} \\ \text{public key size} &= m(n+1)\lceil\log_2 q\rceil \text{ bits} \end{aligned} \tag{9} \](keeping this article’s toy ratio \(m=4n\) as \(n\) varies).
ns = np.array([32, 64, 128, 256, 512, 768, 1024])
ms = 4 * ns
bits_q = 12 # ceil(log2(3329))
ciphertext_bytes = (ns + 1) * bits_q / 8
pubkey_bytes = ms * (ns + 1) * bits_q / 8
| \(n\) | \(m\) | Ciphertext size | Public-key size |
|---|---|---|---|
| 32 | 128 | 49.5 B | 6.2 KB |
| 128 | 512 | 193.5 B | 96.8 KB |
| 512 | 2048 | 769.5 B | 1.50 MB |
| 1024 | 4096 | 1537.5 B | 6.01 MB |

Ciphertext size grows only roughly linearly with \(n\) (about 1.5KB even at \(n=1024\) ), but assuming \(m\propto n\) , the public-key size grows quadratically (\(O(n^2)\) ) with \(n\) , reaching about 6MB at \(n=1024\) . This is the concrete number behind the claim in the “Relationship to CRYSTALS-Kyber” section below that “the public key grows as \(O(nm)\) .” The figure also overlays real ML-KEM (Kyber) published figures. ML-KEM-512/768/1024 (\(n=k\times 256\) , \(k=2,3,4\) ) have ciphertext sizes of 768/1088/1568 bytes respectively — remarkably close to this article’s toy ciphertext sizes at the same \(n\) (769.5/1153.5/1537.5 bytes), since we matched \(q\) to Kyber’s value. However, public-key sizes for ML-KEM are 800/1184/1568 bytes — three to four orders of magnitude smaller than this toy scheme’s 1.50MB–6.01MB. That gap is exactly the payoff of regenerating the matrix \(A\) from a short seed via the structure of a polynomial ring, instead of transmitting it in full — one of the core ideas behind Module-LWE.
On the security-cost side, informally, the following is known. Standard methods for estimating the cost of the most practical attacks against LWE/Module-LWE (lattice-basis reduction such as BKZ) — the “core-SVP” methodology — estimate that attack cost grows roughly exponentially with the dimension. The Kyber specification submitted to NIST reports that ML-KEM-512 (\(n=512\) ) offers roughly 118 bits of classical / 107 bits of quantum hardness, ML-KEM-768 (\(n=768\) ) roughly 182/165 bits, and ML-KEM-1024 (\(n=1024\) ) roughly 256/232 bits. Note that this is simply quoting published figures — we have not actually estimated a comparable attack cost for this article’s toy scheme (\(n=32\) ). The toy parameters \(n=32\) , \(q=3329\) are a pedagogical simplification and make no security claim. Still, the qualitative structure — that increasing \(n\) trades off an efficiency cost (larger keys/ciphertexts) against a security gain (harder lattice problem) — is visible both in this figure and in Kyber’s published numbers.
Edge Case (c): Why LWE Is Believed to Resist Shor’s Algorithm
The integer-factoring problem behind RSA and the discrete-log problems behind Diffie-Hellman/ECC all reduce to the same underlying mathematical structure: the Hidden Subgroup Problem (HSP) over abelian groups. The heart of Shor’s algorithm is that the quantum Fourier transform (QFT) solves the abelian HSP in polynomial time. Factoring reduces to order-finding in \((\mathbb{Z}/N\mathbb{Z})^*\) , and discrete log reduces to an HSP over \(\mathbb{Z}\times\mathbb{Z}\) — both are attacked through the same “abelian group + QFT” framework.
The LWE problem (and the underlying lattice problems — shortest vector problem, bounded-distance decoding) has no known efficient reduction to an abelian HSP. The hidden-subgroup analogue for lattice problems is believed to be closer to the HSP over the non-abelian dihedral group (or the closely related “hidden lattice problem”), and because the dihedral group is non-abelian, the standard QFT-based attack does not apply. Indeed, the best known quantum algorithm for the dihedral HSP (Kuperberg, 2005) runs in subexponential time \(2^{O(\sqrt{n})}\) — nowhere near the polynomial time Shor’s algorithm achieves. Regev (2004) also shows a connection between lattice problems and the dihedral coset problem, while arguing that this connection does not translate into an efficient quantum algorithm.
It’s important to be honest here: there is no proof that LWE resists quantum computers. What we do know is (1) no polynomial-time quantum algorithm for lattice problems has been found over more than two decades of effort, and (2) lattice problems don’t naturally fit the “abelian group + QFT” attack pattern that breaks RSA/discrete-log. This is circumstantial evidence that no efficient attack is known — not a mathematical proof that none exists. In fact, improving classical and quantum attacks on lattice problems remains an active research area (see “Recent Research” below), so this circumstantial picture could change in the future.
Relationship to CRYSTALS-Kyber
The Regev encryption in this article is a simplified, pedagogical version: it can only encrypt one bit at a time, and — as confirmed concretely in the previous section — the public key grows as \(O(nm)\) . Real CRYSTALS-Kyber solves this by using LWE over a polynomial ring (Module-LWE). Instead of integer vectors, it uses elements of the polynomial ring \(\mathbb{Z}_q[x]/(x^n+1)\) , and instead of matrix operations, polynomial multiplication (which pairs well with fast NTT — Number Theoretic Transform — acceleration). This dramatically reduces key size and computation while allowing multiple bits to be encrypted at once. The core properties this toy implementation confirmed — that the error is the source of hardness, that error size trades off against correctness, and that key size trades off against security — hold just as much for Module-LWE.
Recent Research (2024-2025)
- Alongside FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA), NIST also finalized FIPS 205 (SLH-DSA, a hash-based signature scheme built on SPHINCS+) in August 2024. It is positioned as a conservative alternative signature scheme that depends only on hash-function hardness rather than lattice problems.
- In March 2025, NIST selected the code-based scheme HQC (Hamming Quasi-Cyclic) as a fifth PQC algorithm, to serve as a backup to ML-KEM. It rests on a completely different mathematical foundation (error-correcting codes) from lattice problems, part of a diversification strategy to have an alternative ready in case ML-KEM’s underlying assumption is ever broken (a draft standard is expected around 2026, with finalization targeted for 2027).
- Wenger, Saxena, Malhou, Thieu, & Lauter (2025), “Benchmarking Attacks on Learning with Errors,” IEEE Symposium on Security and Privacy (S&P) 2025, systematically benchmarks major attack algorithms (uSVP, SALSA, Cool & Cruel, Dual Hybrid Meet-in-the-Middle) against Kyber-style sparse secrets. Traditional uSVP attacks turn out to be ineffective in this setting, while ML-based attacks recover binomial secrets in 28-36 hours under certain configurations — underscoring that estimating the real-world cost of attacking LWE is still very much an open, evolving problem.
- Stevens, Wenger, Li, Nolte, Saxena, Charton, & Lauter (2024), “Salsa Fresca: Angular Embeddings and Pre-Training for ML Attacks on Learning With Errors,” arXiv:2402.01082, improves transformer-based ML attacks for recovering sparse LWE secrets, reporting the first instance of such attacks recovering sparse binary secrets at dimension \(n=1024\) — a practically-sized cryptographic parameter. Together with the previous section’s point that “attack cost is only known through published estimates,” this shows that how ML-based attacks affect LWE’s practical security margin is an actively evolving question.
Related Articles
- Cryptography Roadmap: Classical Ciphers, Symmetric Keys, RSA, Diffie-Hellman, Elliptic Curves, Hashing, Signatures, and TLS in Python - This article implements the final one of this hub’s “planned for the future” placeholders.
- RSA in Python: theory, key generation, encryption, decryption - A factoring-based cryptosystem broken by Shor’s algorithm.
- Diffie-Hellman Key Exchange: Theory and Implementation - A discrete-log-based key exchange broken by Shor’s algorithm.
- Elliptic Curve Cryptography (ECC): Math and Python Implementation - Covers the elliptic-curve discrete-log problem (ECDLP), another target of Shor’s algorithm.
- Dissecting the TLS 1.3 Handshake in Python - Provides context for how today’s ECDHE-based handshake could eventually migrate to Kyber-based key exchange.
- AES Symmetric Cryptography: Theory and Python Implementation - Symmetric ciphers only face a square-root speedup from Grover’s algorithm, which is why AES-256 keeps being used unchanged after the post-quantum transition.
References
- Regev, O. (2005). On lattices, learning with errors, random linear codes, and cryptography. Proceedings of the 37th Annual ACM Symposium on Theory of Computing (STOC).
- Regev, O. (2004). Quantum computation and lattice problems. SIAM Journal on Computing, 33(3), 738-760.
- Kuperberg, G. (2005). A subexponential-time quantum algorithm for the dihedral hidden subgroup problem. SIAM Journal on Computing, 35(1), 170-188.
- Peikert, C. (2016). A decade of lattice cryptography. Foundations and Trends in Theoretical Computer Science, 10(4), 283-424.
- Shor, P. W. (1997). Polynomial-time algorithms for prime factorization and discrete logarithms on a quantum computer. SIAM Journal on Computing, 26(5), 1484-1509.
- National Institute of Standards and Technology (2024). Module-Lattice-Based Key-Encapsulation Mechanism Standard. FIPS 203 (CRYSTALS-Kyber).
- National Institute of Standards and Technology (2024). Module-Lattice-Based Digital Signature Standard. FIPS 204 (CRYSTALS-Dilithium).
- National Institute of Standards and Technology (2024). Stateless Hash-Based Digital Signature Standard. FIPS 205 (SLH-DSA).
- National Institute of Standards and Technology (2025). NIST Selects HQC as Fifth Algorithm for Post-Quantum Encryption. NIST News, March 11, 2025.
- Wenger, E., Saxena, E., Malhou, M., Thieu, E., & Lauter, K. (2025). Benchmarking attacks on learning with errors. IEEE Symposium on Security and Privacy (S&P) 2025.
- Stevens, S., Wenger, E., Li, C., Nolte, N., Saxena, E., Charton, F., & Lauter, K. (2024). Salsa Fresca: Angular embeddings and pre-training for ML attacks on Learning With Errors. arXiv:2402.01082.