MODERN CRYPTOGRAPHY SERIES · PRESENTATION 07

Post-Quantum Cryptography

Lattices · ML-KEM · ML-DSA · SLH-DSA · NIST PQC Standards · Hardware

Navigate with arrow keys · Press Esc for overview · Press F for fullscreen

Why Post-Quantum?

Every public-key cryptosystem in wide deployment today — RSA, Diffie-Hellman, ECDSA, ECDH, EdDSA — derives security from one of two problems: integer factorisation or the discrete logarithm problem.

Shor's algorithm (1994) solves both in polynomial time on a quantum computer, turning attacks that are sub-exponential classically into polynomial ones (roughly O(n³) gates for an n-bit modulus).

⚠ Broken by Shor's Algorithm

  • RSA (integer factorisation)
  • Diffie-Hellman / DSA (DLP over ℤp*)
  • ECDH / ECDSA / EdDSA (ECDLP)
  • ElGamal encryption

✓ Quantum-Resistant

  • AES-256 (Grover halves key strength → 128-bit)
  • SHA-2 / SHA-3 with ≥ 256-bit output
  • Lattice-based: ML-KEM, ML-DSA
  • Hash-based: SLH-DSA, LMS/XMSS

Grover's algorithm gives only a quadratic speedup against symmetric ciphers — doubling key length neutralises the threat.

The Quantum Threat Timeline

1994
Shor publishes factoring algorithm
1996
Grover's search algorithm — quadratic speedup
2016
NIST calls for PQC submissions (82 received)
2022
NIST selects Kyber, Dilithium, Falcon and SPHINCS+ for standardisation (end of round 3)
Aug 2024
FIPS 203, 204, 205 finalised — ML-KEM, ML-DSA, SLH-DSA
Mar 2025
HQC selected as backup KEM (code-based)
2027
CNSA 2.0 — new National Security System acquisitions must be CNSA 2.0-compliant
2035
NIST IR 8547 (draft): RSA/ECC deprecated after 2030, disallowed after 2035

🕐 "Harvest Now, Decrypt Later"

Adversaries record encrypted traffic today, planning to decrypt it once a Cryptographically Relevant Quantum Computer (CRQC) is available. Long-lived secrets are already at risk.

Qubit Estimates to Break RSA-2048

Gidney–Ekerå (2019): ~20 million noisy physical qubits for 8 hours. Gidney (2025): under 1 million noisy qubits, under a week (~1,400 logical qubits). Estimates for CRQC arrival: 2030s–2040s.

Symmetric Impact

Grover reduces AES-128 → ~64-bit security. Mitigation: use AES-256 (→128-bit post-quantum).

PQC Algorithm Families

Post-quantum schemes derive hardness from problems believed resistant to both classical and quantum algorithms.

🔷 Lattice-Based

Hard problem: Learning With Errors (LWE), Module-LWE, Ring-LWE
Standards: ML-KEM (FIPS 203), ML-DSA (FIPS 204), FN-DSA (FIPS 206, pending)
Pros: Small keys, fast operations, versatile
Cons: Relatively newer hardness assumptions

🔶 Hash-Based

Hard problem: Preimage/collision resistance of hash functions
Standards: SLH-DSA (FIPS 205), LMS/XMSS (SP 800-208)
Pros: Very conservative, minimal assumptions, tiny keys
Cons: Large signatures, slower signing

🔵 Code-Based

Hard problem: Decoding random linear codes
Standards: HQC (selected March 2025; draft standard expected ~2026)
Pros: Decades of study (McEliece, 1978), diverse math basis
Cons: Very large public keys (Classic McEliece)

🟣 Multivariate / Isogeny

Hard problems: MQ problem, supersingular isogeny
Status: SIKE broken (2022); Rainbow broken (2022); research continues
Notes: Multivariate (MAYO, UOV, SNOVA, QR-UOV) and isogeny (SQIsign) signatures are in NIST's additional signature round

Lattice Fundamentals

A lattice ℒ(B) is the set of all integer linear combinations of basis vectors B = {b₁, b₂, …, bₙ} ⊂ ℝⁿ:

ℒ(B) = { Σᵢ zᵢ · bᵢ | zᵢ ∈ ℤ }

Interactive 2D lattice — click to see closest vector problem (CVP)

Hard Lattice Problems

  • SVP — Shortest Vector Problem: find the shortest non-zero vector in ℒ
  • CVP — Closest Vector Problem: given a target point, find the nearest lattice point
  • SIVP — Shortest Independent Vectors: find n shortest linearly independent vectors
  • GapSVP — Decision variant: is λ₁(ℒ) ≤ d or > γ·d?

Exact or small-factor SVP: best known algorithms (sieving, enumeration, BKZ) take 2Θ(n) time; LLL is polynomial-time but only finds vectors within an exponential factor. Quantum algorithms only shave the exponent (e.g. sieving 20.292n → ~20.257n).

Learning With Errors (LWE)

Introduced by Oded Regev (2005, Gödel Prize 2018). LWE is the computational foundation for ML-KEM and ML-DSA.

The Problem

Given a random matrix A ∈ ℤqm×n and a secret vector s ∈ ℤqn, compute:

b = A·s + e (mod q)

where e is a "small" error vector sampled from a discrete Gaussian χ.

The search problem: given (A, b), recover s.
The decision problem: distinguish (A, b) from uniform random.

Without noise: trivial Gaussian elimination — O(n³). With noise: as hard as worst-case lattice problems (GapSVP, SIVP) — via a quantum reduction.

Variants

Ring-LWE (RLWE)

Work in Rq = ℤq[x]/(xn+1). Structured — O(n log n) operations via NTT. Basis of the FHE schemes BGV, BFV and CKKS (deck 08).

Module-LWE (MLWE)

Matrices of ring elements: A ∈ Rqk×k. Bridges Ring-LWE efficiency with LWE flexibility. Parameter k controls security/performance trade-off.

Worst-Case to Average-Case

Regev's (quantum) reduction: solving random LWE instances is at least as hard as solving worst-case GapSVP/SIVP; Peikert (2009) gave a classical reduction for GapSVP. Few assumptions have a worst-case guarantee like this.

Interactive: LWE in Action

This demonstrates why noise makes LWE hard. Without error, the system is trivially solvable.

Parameters: n=4, q=97

No noise (e=0) Small noise (σ=1) Medium noise (σ=3) Large noise (σ=8)

Error distribution: discrete samples from e mod q

NIST PQC Standards (Aug 2024)

FIPSAlgorithmOriginal NameTypeHard ProblemPurpose
203ML-KEMCRYSTALS-KyberLatticeModule-LWEKey Encapsulation
204ML-DSACRYSTALS-DilithiumLatticeModule-LWE + SISDigital Signatures
205SLH-DSASPHINCS+HashHash preimageDigital Signatures

Coming: FIPS 206 — FN-DSA

Based on FALCON. FFT over NTRU lattices. Compact signatures (666 B for FN-DSA-512, NIST Category 1). Draft FIPS 206 pending.

Coming: HQC

Code-based KEM selected March 2025 as backup to ML-KEM. Different mathematical basis (Hamming Quasi-Cyclic codes). Draft standard ~2026, final ~2027.

SP 800-208: LMS/XMSS

Stateful hash-based signature schemes. Already approved. Require strict state management — suitable for firmware signing, not TLS.

NIST IR 8547 (initial public draft, Nov 2024): quantum-vulnerable algorithms deprecated after 2030 and disallowed after 2035, with high-risk systems transitioning much earlier.

ML-KEM (FIPS 203) — Deep Dive

Module-Lattice-Based Key-Encapsulation Mechanism, derived from CRYSTALS-Kyber.

Construction Overview

ML-KEM is built in two layers:

1. K-PKE — an IND-CPA public-key encryption scheme based on Module-LWE. Encrypts a fixed 32-byte message.

2. KEM transform — a modified Fujisaki-Okamoto (FO) transform applied to K-PKE, yielding IND-CCA2 security.

KeyGen: (A, t = As + e) where A ∈ Rqk×k, s,e ← CBD(η)
Encaps: c = Compress(ATy + e₁, tTy + e₂ + ⌈q/2⌋·m)
Decaps: m' = Decompress(c₂ − sTc₁), re-encrypt, check

The ring Rq = ℤq[x]/(x256+1) with q = 3329. Polynomial multiplication via Number Theoretic Transform (NTT).

Parameter Sets

Setkη₁,η₂PK (B)SK (B)CT (B)NIST Cat
ML-KEM-51223, 280016327681
ML-KEM-76832, 21184240010883
ML-KEM-102442, 21568316815685

Shared secret: always 32 bytes. NIST Category 1 ≈ AES-128 security; Cat 3 ≈ AES-192; Cat 5 ≈ AES-256.

Why Module-LWE?

Module-LWE generalises Ring-LWE (one polynomial → k×k matrix of polynomials). Tuning k adjusts security without changing ring dimension. ML-KEM-768 (k=3) is the recommended general-purpose parameter set.

ML-KEM Protocol Flow

Alice (Initiator)
KeyGen():
1. Sample A ← ℤq[x]/(x²⁵⁶+1) via SHAKE-128
2. Sample s, e ← CBD(η₁)
3. t̂ = Â∘NTT(s) + NTT(e) (Â sampled in NTT form)
4. pk = (t̂, ρ), sk = (s, pk, H(pk), z)

Decaps(sk, ct):
5. Decrypt ct → m'
6. Re-encrypt m' → ct'
7. (K', r') = G(m' ‖ H(pk)); if ct' = ct: K = K'
   Else: K = J(z ‖ ct) (implicit rejection)
pk (800–1568 B)
→→→
←←←
ct (768–1568 B)
Bob (Responder)
Encaps(pk):
1. m ← random 32 bytes
2. (K̂, r) = G(m ‖ H(pk))
3. Sample y ← CBD(η₁), e₁, e₂ ← CBD(η₂) using r
4. u = NTT⁻¹(ÂT·NTT(y)) + e₁
5. v = NTT⁻¹(t̂T·NTT(y)) + e₂ + Decompress₁(m)
6. ct = (Compress(u), Compress(v))
7. K = K̂ (FIPS 203 drops Kyber's final KDF)

Both parties derive the same 32-byte shared secret K. The FO transform ensures IND-CCA2 security via implicit rejection.

Number Theoretic Transform (NTT)

The NTT is the workhorse of lattice cryptography — it converts polynomial multiplication from O(n²) to O(n log n).

How It Works

The NTT is the finite-field analogue of the FFT, operating over ℤq instead of ℂ.

Given f(x), g(x) ∈ Rq = ℤq[x]/(x256+1), q = 3329:

1. f̂ = NTT(f)   — 7 layers of butterfly operations
2. ĝ = NTT(g)
3. ĥ = f̂ ⊙ ĝ   — 128 products of degree-1 polys mod (X² − γi)
4. h = NTT⁻¹(ĥ) — inverse transform

Root of unity: ζ = 17 (ζ¹²⁸ ≡ −1, ζ²⁵⁶ ≡ 1 mod 3329)
Why q = 3329? Prime, q = 13·256 + 1 ≡ 1 (mod 256) but not (mod 512): no 512th root, so the NTT stops after 7 layers ("incomplete" NTT). Fits in 12 bits.

Butterfly Operation

a[j] a[j+m] ×ζ^k ×ζ^k a[j] + ζᵏ·a[j+m] a[j] − ζᵏ·a[j+m]

ML-KEM uses 7 layers of 128 butterflies. Each butterfly: 1 multiply + 1 add + 1 subtract in ℤ3329. Total: 128 × 7 = 896 butterflies per NTT.

Hardware: one butterfly unit at 200 MHz does an NTT in ~4.5 μs (896 cycles); 4–8 parallel butterflies bring it to ~1 μs or below.

ML-DSA (FIPS 204) — Digital Signatures

Module-Lattice-Based Digital Signature Algorithm, derived from CRYSTALS-Dilithium.

Fiat-Shamir with Aborts

ML-DSA uses the "Fiat-Shamir with Aborts" paradigm (Lyubashevsky, 2009):

KeyGen: A ← Rqk×ℓ, (s₁,s₂) ← small, t = As₁ + s₂
  pk = (ρ, t₁), sk = (ρ, K, tr, s₁, s₂, t₀) — A re-expanded from seed ρ

Sign(sk, M):
  repeat:
    y ← uniform with ‖y‖∞ < γ₁
    w = Ay
    c̃ = H(μ ‖ HighBits(w)), μ = H(tr ‖ M)
    c = SampleInBall(c̃) — sparse polynomial, τ nonzero coeffs
    z = y + c·s₁
  until ‖z‖∞ < γ₁−β and ‖LowBits(w − c·s₂)‖∞ < γ₂−β
  σ = (c̃, z, hints)

Verify(pk, M, σ):
  w' = UseHint(h, Az − c·t₁·2d), check H(μ ‖ w') = c̃

Parameter Sets

Set(k,ℓ)PK (B)SK (B)Sig (B)Cat
ML-DSA-44(4,4)1,3122,5602,4202
ML-DSA-65(6,5)1,9524,0323,3093
ML-DSA-87(8,7)2,5924,8964,6275

Why "Aborts"?

The masking vector y hides s₁ in z = y + c·s₁. If z leaks information about s₁ (by being too large or having suspicious low bits), the signature is rejected and the signer tries again. Expected iterations: 4.25 / 5.1 / 3.85 for ML-DSA-44/65/87 (FIPS 204 Table 1).

Security Basis

Relies on both Module-LWE (key generation) and Module-SIS (Short Integer Solution — signature forgery). Both reduce to worst-case lattice hardness.

SLH-DSA (FIPS 205) — Hash-Based Signatures

Stateless Hash-Based Digital Signature Algorithm, derived from SPHINCS+. The conservative choice — security rests solely on hash function properties.

Architecture

Hypertree Root (Public Key)

↑ XMSS tree layer d−1

↑ ... (d layers of XMSS Merkle trees) ...

↑ XMSS tree layer 0

FORS Signature (Few-time OTS)

↑

Message Hash → index selection

Each layer: WOTS+ one-time signatures authenticate tree roots. FORS (Forest of Random Subsets) signs the message. The hypertree root is the public key.

Key & Signature Sizes

Parameter SetPK (B)SK (B)Sig (B)Cat
SLH-DSA-*-128s32647,8561
SLH-DSA-*-128f326417,0881
SLH-DSA-*-192s489616,2243
SLH-DSA-*-192f489635,6643
SLH-DSA-*-256s6412829,7925
SLH-DSA-*-256f6412849,8565

Hash choice: SHA-2 or SHAKE256 family. Suffix s = smaller sig / slower; f = fast sign / larger sig.

Trade-off: Keys vs Signatures

SLH-DSA has the smallest keys of any PQC signature (32–64 B public key!) but the largest signatures (7–50 KB). Ideal for code signing, firmware verification — not TLS handshakes.

Interactive: Key & Signature Sizes

Compare PQC algorithms against classical schemes. Select a metric to visualise:

Public Key Secret Key Ciphertext / Signature

All sizes in bytes. Classical schemes shown for reference. ML-KEM-768 is the recommended general-purpose KEM.

Performance Benchmarks

PQC algorithms are competitive with — and often faster than — classical schemes in software.

OperationML-KEM-768ECDH (X25519)RSA-2048
KeyGen~30 μs~50 μs~200 ms
Encaps / Encrypt~40 μs~50 μs~30 μs
Decaps / Decrypt~35 μs~50 μs~1 ms
OperationML-DSA-65Ed25519RSA-2048
KeyGen~80 μs~30 μs~200 ms
Sign~250 μs (avg)~50 μs~1 ms
Verify~90 μs~130 μs~30 μs

One x86-64 core. Classical columns: openssl speed (OpenSSL 3.0); PQC columns: indicative portable-C figures (AVX2 code is 2–4× faster). Treat all as orders of magnitude.

✓ PQC Advantage

ML-KEM key generation is thousands of times faster than RSA-2048. Total KEM round-trip is comparable to ECDH. NTT-based arithmetic is highly parallelisable.

⚠ PQC Trade-off

Larger key/signature sizes increase bandwidth. ML-DSA signing requires retry loops (~4–5 iterations on average). SLH-DSA signing is 100–1000× slower than ML-DSA.

Hardware Implementation

PQC accelerators for SoCs, FPGAs, and ASICs.

NTT Accelerator Architecture

Coeff RAM
(256×12-bit)
→
Butterfly Unit
mul + add/sub mod q
→
Result RAM
Twiddle ROM
ζk table
↗
Control FSM
7 stages × 128 ops
  • Core operation: modular multiply in ℤ3329 — 12×12-bit multiplier + Barrett reduction
  • Parallelism: 1 butterfly/cycle → 896 cycles per NTT, or 2–4 parallel butterflies for throughput
  • Keccak/SHAKE co-processor for sampling and hashing

Implementation Results

PlatformAlgorithmKeyGenEncaps / SignArea
Cortex-M4ML-KEM-768~500K cyc~600K cyc—
Artix-7 FPGAML-KEM-768~3 μs~4 μs~5K LUTs
28nm ASICML-KEM-768<1 μs<1 μs~25K GE
Cortex-M4ML-DSA-65~1.5M cyc~5M cyc—

Indicative figures from published designs; they vary several-fold with architecture and clock.

Design Considerations for SoC Integration

  • Shared NTT engine between KEM and signature operations
  • Keccak-f[1600] permutation block — serves SHA3-256/512, SHAKE128 (matrix A) and SHAKE256 (sampling)
  • CBD (Centered Binomial Distribution) sampler — two popcounts and a subtract
  • Constant-time implementation essential — avoid timing side-channels in Barrett reduction and NTT butterfly

Python: Simplified ML-KEM Core (1/2)

A pedagogical implementation of the core NTT and polynomial arithmetic in ℤ3329.


Q = 3329          # ML-KEM modulus
N = 256           # polynomial degree
ZETA = 17         # primitive 256th root of unity mod Q (17^128 = -1)

def bitrev7(i):
    return int(f'{i:07b}'[::-1], 2)

# Twiddle factors ζ^BitRev7(i), as in FIPS 203
ZETAS = [pow(ZETA, bitrev7(i), Q) for i in range(128)]

def ntt(f):
    """FIPS 203 Alg. 9: 7 layers, 128 -> 2; output = 128 degree-1 residues."""
    a = list(f)
    k, length = 1, 128
    while length >= 2:
        for start in range(0, N, 2 * length):
            zeta = ZETAS[k]; k += 1
            for j in range(start, start + length):
                t = (zeta * a[j + length]) % Q
                a[j + length] = (a[j] - t) % Q
                a[j]          = (a[j] + t) % Q
        length //= 2
    return a

Python: Simplified ML-KEM Core (2/2)

def intt(a):
    """FIPS 203 Alg. 10: Gentleman-Sande butterflies, then multiply by 128^-1."""
    a = list(a)
    k, length = 127, 2
    while length <= 128:
        for start in range(0, N, 2 * length):
            zeta = ZETAS[k]; k -= 1
            for j in range(start, start + length):
                t = a[j]
                a[j] = (t + a[j + length]) % Q
                a[j + length] = (zeta * (a[j + length] - t)) % Q
        length *= 2
    return [(x * 3303) % Q for x in a]          # 3303 = 128^-1 mod 3329

def multiply_ntts(f_hat, g_hat):
    """FIPS 203 Alg. 11: 128 products of degree-1 polys mod (X^2 - γ_i)."""
    h = [0] * N
    for i in range(128):
        g = pow(ZETA, 2 * bitrev7(i) + 1, Q)     # γ_i = ζ^(2·BitRev7(i)+1)
        a0, a1, b0, b1 = f_hat[2*i], f_hat[2*i+1], g_hat[2*i], g_hat[2*i+1]
        h[2*i]   = (a0 * b0 + a1 * b1 * g) % Q
        h[2*i+1] = (a0 * b1 + a1 * b0) % Q
    return h
  

Python: K-PKE Key Generation (FIPS 203) (1/2)


import os, hashlib

def cbd(eta, seed, nonce):
    """Centred binomial sample (FIPS 203 Alg. 8): coefficient = x - y."""
    data = hashlib.shake_256(seed + bytes([nonce])).digest(64 * eta)  # PRF
    bits = [(byte >> b) & 1 for byte in data for b in range(8)]
    return [(sum(bits[2*eta*i : 2*eta*i + eta])
             - sum(bits[2*eta*i + eta : 2*eta*(i + 1)])) % Q for i in range(N)]

def sample_ntt(rho, j, i):
    """FIPS 203 Alg. 7: uniform NTT-domain poly by rejection from SHAKE-128."""
    xof = hashlib.shake_128(rho + bytes([j, i])).digest(840)  # ample bytes
    a, p = [], 0
    while len(a) < N:
        d1 = xof[p] | (xof[p+1] & 15) << 8
        d2 = xof[p+1] >> 4 | xof[p+2] << 4
        a += [d for d in (d1, d2) if d < Q]; p += 3
    return a[:N]

def encode12(f):                     # ByteEncode_12: 256 x 12 bits = 384 B
    return b''.join((f[i] | f[i+1] << 12).to_bytes(3, 'little')
                    for i in range(0, N, 2))

Python: K-PKE Key Generation (FIPS 203) (2/2)

def k_pke_keygen(d, k=3, eta1=2):
    """FIPS 203 Alg. 13 (ML-KEM-768 when k=3)."""
    g = hashlib.sha3_512(d + bytes([k])).digest()
    rho, sigma = g[:32], g[32:]      # public seed for A, private seed for s, e
    A_hat = [[sample_ntt(rho, j, i) for j in range(k)] for i in range(k)]
    s_hat = [ntt(cbd(eta1, sigma, n)) for n in range(k)]
    e_hat = [ntt(cbd(eta1, sigma, n + k)) for n in range(k)]
    t_hat = []                       # t = A·s + e, all in the NTT domain
    for i in range(k):
        acc = e_hat[i]
        for j in range(k):
            prod = multiply_ntts(A_hat[i][j], s_hat[j])
            acc = [(x + y) % Q for x, y in zip(acc, prod)]
        t_hat.append(acc)
    ek = b''.join(map(encode12, t_hat)) + rho      # 1152 + 32 = 1184 B
    dk_pke = b''.join(map(encode12, s_hat))        # 1152 B
    return ek, dk_pke     # ML-KEM dk = dk_pke ‖ ek ‖ H(ek) ‖ z = 2400 B
  

Uses ntt and multiply_ntts from the "Simplified ML-KEM Core" slides; for a fixed seed d the output matches the ML-KEM-768 keys of the kyber-py reference byte for byte. Production implementations: use liboqs, pqcrypto (Rust), or OpenSSL 3.5+ with ML-KEM support.

Hybrid Schemes & Migration

The transition strategy: combine classical + PQC for defence-in-depth.

Hybrid Key Exchange

TLS 1.3 Hybrid Handshake

X25519 Key Share
+
ML-KEM-768 Encaps Key

↓ Combined Key Share in ClientHello

SS₁ = X25519(a, B)
‖
SS₂ = ML-KEM.Decaps(ct)

↓ SS₂ ‖ SS₁ → TLS 1.3 key schedule (X25519MLKEM768 puts the ML-KEM secret first)

If either algorithm is broken, the other still protects the session. Already deployed by Cloudflare, Google Chrome, AWS.

Migration Roadmap

Phase 1: Inventory (Now)

Identify all uses of RSA, ECDH, ECDSA in protocols, certificates, and stored data. Prioritise long-lived secrets.

Phase 2: Hybrid Deploy (2025–2027)

Deploy hybrid TLS with ML-KEM + X25519. Update certificate chains. Test interoperability.

Phase 3: Pure PQC (2028–2035)

Transition to PQC-only where hybrid overhead is unacceptable. Deprecate classical public-key algorithms per NIST IR 8547.

Crypto-Agility

Design systems to swap algorithms without rewriting protocols. Use negotiation (like TLS cipher suites) and abstraction layers.

Algorithm Decision Matrix

AlgorithmTypePKSKCT/SigSpeedMaturityBest For
ML-KEM-768KEM1,184 B2,400 B1,088 BVery fastFIPS 203TLS, VPN, general KE
ML-DSA-65Sig1,952 B4,032 B3,309 BFastFIPS 204Code signing, auth, TLS
SLH-DSA-128fSig32 B64 B17,088 BSlow signFIPS 205Firmware, long-term archive
FN-DSA-512Sig897 B1,281 B666 BFastFIPS 206 pendingBandwidth-constrained
HQC-128KEM~2.2 KB~2.3 KB~4.4 KBModerateSelected 2025ML-KEM backup (diversity)
RSA-2048KE+Sig256 B~1.2 KB256 BSlow KGBroken by QCLegacy (deprecate)
ECDSA P-256Sig64 B32 B64 BFastBroken by QCLegacy (deprecate)

Recommendation: Use ML-KEM-768 for key exchange and ML-DSA-65 for general signing. Add SLH-DSA where conservative hash-only assumptions are required. Deploy hybrid initially.

References & Standards

NIST Standards
FIPS 203 — ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism)
FIPS 204 — ML-DSA (Module-Lattice-Based Digital Signature Algorithm)
FIPS 205 — SLH-DSA (Stateless Hash-Based Digital Signature Algorithm)
NIST IR 8547 — Transition to Post-Quantum Cryptography Standards
SP 800-208 — Stateful Hash-Based Signature Schemes (LMS, XMSS)

Foundational Papers
Shor, "Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer" (1994)
Regev, "On Lattices, Learning with Errors, Random Linear Codes, and Cryptography" (2005, Gödel Prize 2018)
Lyubashevsky, Peikert, Regev, "On Ideal Lattices and Learning with Errors Over Rings" (2010)
Bos et al., "CRYSTALS-Kyber: A CCA-Secure Module-Lattice-Based KEM" (2018)
Ducas et al., "CRYSTALS-Dilithium: A Lattice-Based Digital Signature Scheme" (2018)
Bernstein et al., "The SPHINCS+ Signature Framework" (2019)

Implementation Resources
Open Quantum Safe (liboqs) · PQ-Crystals reference code · OpenSSL 3.5+ PQC support · NIST ACVP test vectors