Introduction#
Most of the encryption protecting data in transit today — TLS, SSH, JWT signatures, certificate authorities — relies on the hardness of two mathematical problems: factoring large integers (RSA) and computing discrete logarithms (ECDH, ECDSA). Quantum computers running Shor’s algorithm can solve both in polynomial time.
A sufficiently powerful quantum computer does not exist yet. But adversaries are already collecting encrypted traffic today to decrypt it later — a strategy called “harvest now, decrypt later.” Data protected by RSA-2048 today may be readable in 10–15 years.
This post explains the threat, what NIST has standardized, and concrete steps to start migrating now.
The Quantum Threat in Plain Terms#
Classical computers brute-force RSA by factoring the public key’s modulus. RSA-2048 would take classical hardware longer than the age of the universe. A quantum computer running Shor’s algorithm can factor the same number in hours.
The relevant threat timeline:
| Milestone | Estimated Timeframe |
|---|---|
| Quantum advantage on toy problems | Already achieved |
| 1,000 logical qubits (error-corrected) | ~2028–2030 |
| Cryptographically relevant quantum computer (CRQC) | ~2030–2040 (estimates vary widely) |
| RSA-2048 broken in practice | After CRQC |
The uncertainty is large. Some researchers say 2030, some say 2040. The point is: migration takes years, and you should not wait for the threat to materialize before starting.
Symmetric algorithms (AES-256) are not threatened in the same way. Grover’s algorithm provides a quadratic speedup against symmetric keys, effectively halving their security. AES-256 is still considered safe post-quantum. AES-128 is marginal.
What NIST Has Standardized#
NIST finalized its first post-quantum cryptography standards in August 2024:
ML-KEM (FIPS 203) — Key Encapsulation Mechanism
- Based on CRYSTALS-Kyber
- Replaces ECDH/RSA for key exchange
- Variants: ML-KEM-512, ML-KEM-768, ML-KEM-1024 (increasing security level)
ML-DSA (FIPS 204) — Digital Signature Algorithm
- Based on CRYSTALS-Dilithium
- Replaces ECDSA/RSA for signatures
- Variants: ML-DSA-44, ML-DSA-65, ML-DSA-87
SLH-DSA (FIPS 205) — Stateless Hash-Based Digital Signature
- Based on SPHINCS+
- Conservative choice; larger signatures, slower
- Recommended as a backup if lattice-based signatures are ever broken
A fourth standard — FN-DSA (FALCON) — is expected in 2025 for use cases requiring smaller signature sizes.
What is Vulnerable#
Audit your systems for these patterns:
Immediately threatened (asymmetric cryptography):
- RSA (any key size) — key exchange and signatures
- ECDH, ECDSA, EdDSA — elliptic curve key exchange and signatures
- Diffie-Hellman — classic key exchange
- DSA — digital signatures
Not directly threatened:
- AES-256, ChaCha20 — symmetric encryption
- SHA-256, SHA-3 — hash functions (minor impact, double key lengths as precaution)
- HMAC — message authentication
In practice, this means:
- TLS 1.3 (uses ECDH for key exchange) is vulnerable
- SSH (RSA/ECDSA host keys) is vulnerable
- JWT signed with RS256 or ES256 is vulnerable
- X.509 certificates (RSA or EC) are vulnerable
- PGP email encryption is vulnerable
Harvest Now, Decrypt Later#
This is the most immediate concern for engineers. An attacker captures encrypted TLS traffic today and stores it. When quantum computers become capable, they decrypt it retroactively.
Which data is worth harvesting?
- Long-lived secrets (private keys, passwords, credentials)
- Classified or regulated data (health records, financial data)
- Intellectual property
- Government communications
If your system transmits data that will still be sensitive in 10–15 years, you are already at risk.
The Migration Path#
Step 1: Inventory What You Have#
Document all cryptographic usage in your systems:
1
2
3
4
5
6
7
8
9
10
# Find TLS certificate key types in use
openssl s_client -connect yourservice.com:443 </dev/null 2>/dev/null \
| openssl x509 -noout -text \
| grep -E "Public Key Algorithm|RSA Public-Key|Public-Key"
# Check SSH host key types
ssh-keyscan -t rsa,ecdsa,ed25519 yourserver.com 2>/dev/null
# Find JWT signing algorithms in code
grep -r "RS256\|ES256\|RS384\|ES384\|RS512\|ES512" ./src
Create a registry:
| System | Algorithm | Key Size | Rotation Period | Priority |
|---|---|---|---|---|
| API TLS | ECDH P-256 | 256-bit | 1 year | High |
| JWT signing | RS256 | 2048-bit | 90 days | High |
| SSH access | ECDSA | 256-bit | Manual | Medium |
| Database at rest | AES-256 | 256-bit | Annual | Low |
Step 2: Enable Hybrid Key Exchange in TLS#
Before full PQC migration, use hybrid key exchange: combine classical ECDH with a PQC algorithm. If either is broken, the connection remains secure.
TLS 1.3 supports hybrid groups. As of 2024, major implementations support X25519 + ML-KEM-768:
1
2
# nginx - enable hybrid PQC key exchange (requires OpenSSL 3.5+ with oqs-provider)
ssl_ecdh_curve X25519MLKEM768:X25519:P-256;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Go TLS with hybrid key exchange (using cloudflare/circl)
import (
"crypto/tls"
"github.com/cloudflare/circl/kem/hybrid"
)
tlsConfig := &tls.Config{
CurvePreferences: []tls.CurveID{
// Go 1.23+ supports X25519MLKEM768 natively
tls.X25519MLKEM768,
tls.X25519,
tls.CurveP256,
},
MinVersion: tls.VersionTLS13,
}
Chrome, Firefox, and most major browsers already support X25519MLKEM768 in TLS 1.3.
Step 3: Migrate Signatures#
For JWT and internal signing, migrate to ML-DSA:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Using liboqs-python (Open Quantum Safe project)
import oqs
# Generate ML-DSA-65 keypair
with oqs.Signature("ML-DSA-65") as sig:
public_key = sig.generate_keypair()
private_key = sig.export_secret_key()
# Sign
message = b"payload to sign"
signature = sig.sign(message)
# Verify
is_valid = sig.verify(message, signature, public_key)
print(f"Valid: {is_valid}")
print(f"Public key size: {len(public_key)} bytes") # ~1952 bytes for ML-DSA-65
print(f"Signature size: {len(signature)} bytes") # ~3309 bytes for ML-DSA-65
Note: ML-DSA signatures are significantly larger than ECDSA (3309 bytes vs 64 bytes for P-256). For high-volume signed tokens, this affects bandwidth and storage.
Step 4: Update Certificate Infrastructure#
Certificate authorities are beginning to issue PQC certificates. For internal PKI:
1
2
3
4
5
6
7
8
9
10
# Generate ML-DSA-65 key and self-signed cert using OpenSSL with oqs-provider
openssl genpkey -algorithm ml-dsa-65 -out server-pq.key
openssl req -new -x509 -key server-pq.key \
-out server-pq.crt \
-days 365 \
-subj "/CN=internal-service"
# Verify
openssl x509 -in server-pq.crt -noout -text | grep "Public Key Algorithm"
# Should show: Public Key Algorithm: ML-DSA-65
For production-facing certificates, wait for your CA to support PQC issuance. Let’s Encrypt, DigiCert, and others have announced timelines.
Algorithm Selection Guide#
| Use Case | Classical Today | PQC Replacement | Notes |
|---|---|---|---|
| TLS key exchange | ECDH P-256 / X25519 | X25519MLKEM768 (hybrid) | Enable now, widely supported |
| Code signing | ECDSA | ML-DSA-65 | Larger signatures |
| JWT / API tokens | RS256, ES256 | ML-DSA-44 (or hybrid) | Size impact on tokens |
| Certificate signatures | RSA-2048, P-256 | ML-DSA-65 | Wait for CA support |
| SSH host keys | ECDSA, ED25519 | No standard yet | Monitor OpenSSH roadmap |
| Long-term data encryption | AES-256 + ECDH | AES-256 + ML-KEM-768 | Immediate priority for sensitive data |
What to Do Right Now#
Actions ranked by impact:
-
Enable hybrid TLS key exchange on public-facing services. This is the highest-leverage action and requires minimal code change in modern web servers.
-
Inventory all asymmetric key usage — document every place you use RSA, ECDH, ECDSA, and what data they protect.
-
Identify harvest-now targets — which data you transmit that will still be sensitive in 10+ years? Prioritize migrating those connections.
-
Update crypto libraries — ensure your dependencies can be updated to PQC-capable versions when you are ready. Avoid pinning to ancient OpenSSL versions.
-
Build crypto agility into new systems — design so the algorithm is a configuration value, not hard-coded. When you need to rotate to ML-DSA, it should be a config change plus a key rotation, not a code refactor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Example: crypto-agile signing abstraction
from enum import Enum
from dataclasses import dataclass
class SigningAlgorithm(str, Enum):
ES256 = "ES256" # ECDSA P-256
ML_DSA_65 = "ML-DSA-65" # Post-quantum
@dataclass
class CryptoConfig:
signing_algorithm: SigningAlgorithm = SigningAlgorithm.ES256
# Change this config value to migrate; no code changes elsewhere
def sign(payload: bytes, config: CryptoConfig, private_key) -> bytes:
if config.signing_algorithm == SigningAlgorithm.ES256:
return sign_ecdsa(payload, private_key)
elif config.signing_algorithm == SigningAlgorithm.ML_DSA_65:
return sign_ml_dsa(payload, private_key)
raise ValueError(f"Unknown algorithm: {config.signing_algorithm}")
Compliance and Regulatory Direction#
Several regulatory bodies have issued guidance:
- NIST: Final PQC standards published August 2024 (FIPS 203/204/205)
- NSA: Commercial National Security Algorithm Suite 2.0 mandates PQC for national security systems by 2030
- CISA: Recommends organizations begin PQC migration planning now
- EU: ENISA published PQC transition guidance; NIS2 compliance will likely require PQC readiness
If you operate in regulated industries, assume PQC compliance requirements are coming within 3–5 years.
Conclusion#
Post-quantum cryptography is not a theoretical concern. The NIST standards are finalized, major browsers already support hybrid TLS key exchange, and adversaries are collecting traffic now. The migration is complex because cryptography is embedded everywhere.
Start with the highest-leverage action: enable hybrid TLS key exchange on public services. Then build out your inventory and migration plan. The organizations that begin now will have a controlled migration. Those that wait will have an emergency.
The goal is crypto agility — systems that can swap algorithms without architectural rewrites. Build that property into new systems from day one.