The Thirty-Year Migration Ships in a pip install: How Post-Quantum Cryptography Crossed from Standard to Shipping Code
Post-quantum cryptography spent thirty years reaching a pip install. ML-KEM and ML-DSA shipped in pyca/cryptography v48 weeks ahead of the federal deadline.
Permalink1. The Last Mile Is a pip install
On May 4, 2026, the migration Peter Shor set in motion thirty-two years earlier quietly crossed its last mile -- not in a standards body, not at a White House signing, but in a routine pip install. That day, pyca/cryptography v48.0.0 added ML-KEM and ML-DSA to the default wheels that sit beneath Ansible, Certbot, and a package downloaded more than a billion times a month [1] [2]. Seven weeks later, an executive order would set the federal deadlines for exactly this migration [3] -- but the code had already arrived.
That ordering is the whole story, and it is the opposite of the one most people tell. The usual narrative treats the 2024 NIST standards or the 2026 executive order as the moment post-quantum cryptography "became real." The dated record says otherwise: the primitive shipped first, in a version-bump nobody outside a handful of maintainers noticed.
The pip install beat the policy.
pyca/cryptographyv48.0.0 shipped ML-KEM and ML-DSA on May 4, 2026; Executive Order 14412 set the federal deadlines on June 22, 2026 -- forty-nine days later. The shipping code was already in a billion-download-a-month wheel before the mandate existed [1] [3].
Trail of Bits, which wrote much of the code that made this possible, put the milestone plainly.
Post-quantum cryptography is now one
pip-installaway. -- Trail of Bits, on the arrival of ML-KEM and ML-DSA inpyca/cryptography[2]
This is a strange kind of anticlimax. A threat that dooms RSA, Diffie-Hellman, and elliptic-curve cryptography was described in 1994. It took a decade to find math that plausibly resists it, another decade for that math to become efficient enough to deploy, a six-year public competition to pick winners, and a formal standardization in 2024. And then the thing that actually put quantum-resistant cryptography in front of most working programmers was a distribution change: the same primitives, previously reachable only in specialist builds, landing in the wheel everyone already installs.
Diagram source
timeline
title From a quantum threat to shipping code
1994 : Shor's algorithm breaks RSA, DH, and ECC in theory
1996 : Ajtai proves worst-case lattice hardness
2005 : Regev defines Learning With Errors
2012 : Module-LWE yields kilobyte-scale keys
2016 : NIST opens its post-quantum competition
2022 : NIST selects Kyber, Dilithium, Falcon, and SPHINCS+
2024 : FIPS 203, 204, and 205 finalized on August 13
May 2026 : pyca cryptography v48 ships ML-KEM and ML-DSA
Jun 2026 : Executive Order 14412 sets the federal deadlines There is a catch, and it is the reason this article exists. Shipping the primitive is the easy part. ML-KEM and ML-DSA are not smaller, faster, tidier versions of the algorithms they replace; they are dramatically larger, they have a different shape, and they violate assumptions that thousands of protocols and buffers quietly encoded around 32-byte keys and 64-byte signatures. A standardized, pip-installable primitive is a genuine milestone. It is also not a working TLS handshake, a signed X.509 certificate, or a package-signing scheme. In a phrase this article will earn: a primitive is not a protocol.
So here is the question. If the primitive is a pip install away, why is the migration still a thirty-year project -- and how did the code beat the executive order?
2. The Threat That Started a Thirty-Year Clock
In 1994, Peter Shor showed that a sufficiently large quantum computer could factor integers and compute discrete logarithms in polynomial time [4]. That one result quietly condemned almost every public-key system in deployment. RSA rests on the hardness of factoring; Diffie-Hellman and elliptic-curve cryptography rest on discrete logarithms. Shor's algorithm breaks all three at once. It is not a faster attack that a bigger key can outrun -- it is a correctness failure of the assumption those systems are built on. Shor's 1994 paper is cited under two titles: the FOCS conference version, "Algorithms for Quantum Computation: Discrete Logarithms and Factoring," and the expanded arXiv and SIAM version, "Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer" [4].
Classical cryptographic algorithms -- ones that run on ordinary computers -- designed to remain secure even against an adversary with a large-scale quantum computer. PQC is not quantum cryptography: it uses no quantum hardware. It replaces the hard problems (factoring, discrete log) that quantum computers solve with problems (such as finding short vectors in a lattice) that they are not known to solve efficiently.
A 1994 quantum algorithm that factors integers and solves the discrete-logarithm problem in polynomial time. Its existence means that RSA, Diffie-Hellman, and elliptic-curve cryptography are all breakable by a large enough quantum computer, which is the entire reason the post-quantum migration exists [4].
Why symmetric cryptography survives
The quantum threat is asymmetric in an important way: it targets public-key cryptography, not the symmetric ciphers and hashes underneath it. The relevant quantum tool for symmetric keys is Grover's algorithm, which gives only a quadratic speedup on a brute-force search [5]. The folk summary is that Grover halves the security level, turning a 128-bit key into 64 bits. That halving is a naive query-count bound, not a real cost. A depth-limited quantum search is far more expensive: NIST's own evaluation criteria peg the effective quantum cost of breaking AES-128 near rather than [6], and Grassl and colleagues give the concrete Grover-on-AES resource estimates behind that figure [7]. Because Grover parallelizes only as the square root of the number of machines, serial depth dominates [8]. Even AES-128 keeps a wide margin; moving to AES-256 is a precaution, not a rescue. The practical upshot is clean: symmetric keys need at most a modest bump, so the migration is overwhelmingly a public-key story [6] [7].
Why the migration cannot wait for the hardware
Here is the part that surprises people. You do not need a working quantum computer today for the threat to be operational today. An adversary can record encrypted traffic now and simply store it, then decrypt it years later once a cryptographically relevant quantum computer exists. Anything that must stay confidential past the arrival of that machine is already exposed.
An attack strategy in which an adversary captures and stores encrypted data today, betting on decrypting it in the future once quantum computers can break the key exchange that protected it. HNDL makes the quantum threat a present-day problem for any data with a long confidentiality lifetime, even though the quantum computer does not yet exist.
Thirty-two years after Shor, Executive Order 14412 opens by restating exactly this rationale as national-security fact [3].
Ongoing cyber activity against our Nation also presents the risk of adversaries collecting United States information now, and decrypting it later once large-scale quantum computers are operational. -- Executive Order 14412 [3]
The field even got its name in this period. The PQCrypto workshop series began gathering researchers around the problem in 2006, and the 2009 Springer volume Post-Quantum Cryptography framed the multi-decade replacement thesis that the community has been executing ever since [10] [11]. The coinage of the phrase "post-quantum cryptography" is usually attributed to Daniel J. Bernstein around 2003 to 2006, but that attribution is folk knowledge; the firm anchors are the 2006 workshop and the 2009 book [10] [11].
If factoring and discrete log are dead in the quantum model, the obvious next question is what we build on instead. Several candidate hard problems were on the table. Only one of them turned into the workhorse behind the algorithms that just shipped in your pip cache -- and understanding why the others lost is the fastest way to understand what makes the winner trustworthy.
3. The Math That Survives Quantum
Hash-based signatures are the most conservative option on the menu, because their only assumption is that a hash function behaves like a hash function. Merkle proposed the idea in 1979 [12], and it survives today as SLH-DSA. But hash-based schemes sign; they cannot establish a key, so they can never be the whole answer. The early stateful forms carry a vicious footgun -- reuse a one-time key by losing track of internal state and you leak the private key [13] -- and the stateless successor, SPHINCS+, buys safety with large, slow signatures [14] [2].
Code-based encryption has an even longer unbroken record. McEliece's 1978 cryptosystem hides a structured error-correcting code as a random-looking matrix; half a century and many attack papers later, including quantum ones, its security has held [15]. Its ciphertexts are tiny. The problem is the public key: Classic McEliece keys run from roughly 261 KB to more than 1 MB [15]. That is fine for a niche, fatal for a default that has to fit inside a TLS handshake.
Multivariate and isogeny schemes both looked promising and both broke during the standardization era -- a story worth its own section (Section 4).
That leaves lattices, and the reason they won is a two-step story of beauty followed by engineering.
Step one: beauty (worst-case hardness)
In 1996, Miklos Ajtai proved something remarkable: you can build cryptographic instances whose average-case hardness is guaranteed by the worst-case hardness of a lattice problem [16]. In most of cryptography you hope your random instance is hard; Ajtai showed a family where a random instance is provably as hard as the hardest instance in the class. In 2005, Oded Regev turned this into a usable assumption -- Learning With Errors -- complete with a quantum worst-case-to-average-case reduction and a working public-key encryption scheme [17].
The problem of recovering a secret vector given many noisy linear equations , where is public and random and is small random noise. Without the noise this is trivial linear algebra; with it, recovering is believed hard even for quantum computers. LWE is the assumption underneath ML-KEM and ML-DSA [17].
LWE is beautiful, and in its raw form it is unshippable. A plain-LWE public key is essentially a big random matrix, so its size grows like in the security parameter -- megabytes, not kilobytes. Beauty was not enough.
Step two: engineering (algebraic structure)
The fix was to give LWE algebraic structure. Ring-LWE (2010) works over a ring of polynomials instead of a flat vector space, collapsing the quadratic overhead and letting the Number-Theoretic Transform multiply polynomials quickly; its own authors describe removing LWE's "inherent quadratic overhead" [18]. Module-LWE (2012) generalized this to short vectors of ring elements, giving designers a tunable dial between raw LWE and full Ring-LWE [19]. The size effect is the whole game: from down to , from megabytes to kilobytes. That single step is what turned lattice cryptography from a theorist's toy into the deployable engine behind ML-KEM and ML-DSA.
A middle-ground version of LWE defined over modules -- short vectors whose entries are elements of a polynomial ring. It sits between unstructured LWE (large, most conservative) and Ring-LWE (compact, most structured), letting a scheme trade size against how much algebraic structure it assumes. Module-LWE is the specific hard problem under CRYSTALS-Kyber and CRYSTALS-Dilithium, standardized as ML-KEM and ML-DSA [19].
Diagram source
flowchart TD
A["Classical public key (RSA, DH, ECC)"] -->|Shor 1994 breaks all three| B["Need a quantum-hard problem"]
B --> C["Hash-based (SLH-DSA): conservative backstop, large and slow"]
B --> D["Code-based (McEliece): unbroken, but keys near 1 MB"]
B --> E["Multivariate (Rainbow): broken in a weekend, 2022"]
B --> F["Isogeny (SIKE): broken in about ten minutes, 2022"]
B --> G["Lattice (LWE): worst-case hardness, but O(n^2) keys"]
G --> H["Ring-LWE and Module-LWE: O(n) kilobyte keys"]
H --> I["ML-KEM and ML-DSA: the deployed workhorse"] There is an honest catch in step two, and it matters for the rest of this article. Adding structure is what buys the kilobyte keys, but structure is also extra assumption. Is the algebraic structure "free"? The worst-case reduction for Ring-LWE and Module-LWE runs to ideal or module lattices -- a smaller, more special class than general lattices [20]. No known attack cashes that extra structure in at the standardized parameters; the best attack remains generic lattice sieving at , the same cost as for unstructured LWE [21]. Module-LWE deliberately dilutes the structure, as its own built-in hedge [19]. But "the structure is free" is a conjecture, not a theorem -- which is exactly why the standardized portfolio keeps a hash-only backstop, SLH-DSA, that would survive even if that conjecture failed [22]. The community's answer to that residual doubt is not to pretend it away; it is to keep a scheme from a completely different family in reserve. Hold that thought -- it is the reason the eventual standard is a portfolio, not a single winner.
Kilobyte-scale lattice schemes existed by 2018 [23] [24]. So why did it take a six-year global tournament to decide which ones to trust?
4. From a Problem to a Portfolio
NIST did not crown one algorithm. Starting in 2016, it ran an open, public, break-or-defend competition -- publish your candidate, publish your attacks, let years of adversarial scrutiny do the filtering [25]. Google had already run a taste of the future in 2016, wiring an experimental post-quantum key exchange called CECPQ1 into Chrome and its front ends to see what breaks at internet scale, then retiring it precisely because it was not yet standardized [26]. The lesson was clear: experiments are cheap, but a default needs a standard behind it.
On July 5, 2022, NIST announced its first selections: CRYSTALS-Kyber for key establishment, and CRYSTALS-Dilithium, Falcon, and SPHINCS+ for signatures [27]. Three of the four are lattice schemes; the fourth, SPHINCS+, is hash-based. NIST named Dilithium the primary signature, Falcon the option for applications that need smaller signatures, and SPHINCS+ the backup built on "a different math approach" [27]. That portfolio is a deliberate hedge: a compact, versatile lattice family as the workhorse, plus a signature scheme whose security rests on nothing but hash functions.
The tournament produced casualties
The hedge was not paranoia. It was a lesson the competition taught in real time.
The isogeny KEM SIKE was one of the most elegant candidates, with the smallest keys of anything in the field, and it advanced to the fourth round. Then, in 2022, Wouter Castryck and Thomas Decru broke it -- not with a quantum computer, but on a laptop. Castryck and Decru recovered the SIKE private key by exploiting the torsion-point images the protocol reveals, via Kani's reducibility criterion. Their Magma implementation "breaks SIKEp434, which aims at security level 1, in about ten minutes on a single core" [28]. A years-vetted, fourth-round candidate fell to one new mathematical idea. Around the same time, Ward Beullens broke the multivariate signature scheme Rainbow, a third-round finalist, recovering its secret key in an average of 53 hours -- one weekend -- on a standard laptop [29].
Even NTRU, one of the oldest and most compact lattice schemes (Hoffstein, Pipher, and Silverman, 1996), did not make the cut. It was a finalist, but its security rested on a more heuristic argument than the CRYSTALS schemes' cleaner reductions, and it carried a history of patent encumbrance that only lapsed in 2017 [30]. NIST's rationale was multi-factor, but the outcome was telling: given two workable lattice families, the committee preferred the one with the tidier hardness story and no intellectual-property baggage.
Diagram source
flowchart LR
subgraph LAT["Lattice family (selected)"]
L1["Ring-LWE and Module-LWE"] --> L2["Kyber and Dilithium selected 2022"]
end
subgraph HEDGE["Conservative hedge"]
H1["Hash-based SPHINCS+ selected as backstop"]
end
subgraph DEAD["Broken during standardization"]
D1["Isogeny SIKE broken 2022"]
D2["Multivariate Rainbow broken 2022"]
end
N["NTRU finalist, not selected"] -.-> L2 The following table condenses the generational story: which ideas advanced, which were demoted to a niche or a backstop, and which broke outright.
| Approach | Since | Core idea | Status |
|---|---|---|---|
| RSA / DH / ECC | 1976 | Factoring and discrete log | Quantum-doomed (Shor) [4] |
| Hash-based (Merkle to SLH-DSA) | 1979 | One-time keys over a hash tree | Active backstop, signatures only [13] [14] |
| Code-based (Classic McEliece) | 1978 | Hide a Goppa code as a random matrix | Niche: unbroken but ~1 MB keys [15] |
| Lattice theory (Ajtai; Regev/LWE) | 1996 / 2005 | Worst-case-hard lattices; LWE | Foundation, but O(n^2) raw sizes [16] [17] |
| NTRU | 1996 | Ring-based lattice encryption | Superseded finalist, not selected [30] |
| Ring/Module-LWE to ML-KEM/ML-DSA | 2012 / 2024 | Structured lattices, NTT | Standardized workhorse [19] [31] |
| Isogeny SIDH/SIKE | 2011 | Supersingular isogeny walks | Broken in about ten minutes, 2022 [28] |
| Multivariate Rainbow | 2005 | Multivariate quadratic systems | Broken in a weekend, 2022 [29] |
| Falcon / FN-DSA | 2022 | NTRU lattice with Gaussian sampling | Standard pending as FIPS 206 [27] |
A short list of winning algorithms is still just math on paper. What turns "we chose Kyber" into something a whole software supply chain can build on?
5. Why the Rename Mattered
On August 13, 2024, NIST finalized three standards. Kyber became ML-KEM (FIPS 203), Dilithium became ML-DSA (FIPS 204), and SPHINCS+ became SLH-DSA (FIPS 205) [32] [31] [22]. It is tempting to read this as bureaucratic relabeling. It is the opposite: the rename is the moment the algorithms stopped being research artifacts and became a contract.
A Federal Information Processing Standard: a document published by NIST that fixes exact parameter sets, byte-level encodings, and algorithm behavior so that independent implementations interoperate and can be validated against a common reference. A FIPS standard is a specification -- a shared, machine-checkable contract -- not a piece of software [32].
The value of a FIPS document is that it pins down the boring, load-bearing details. A research paper can leave an encoding ambiguous or offer three parameter options; a standard says these parameter sets, these byte counts, this wire encoding, and nothing else. That is what lets a hardware team, a TLS library, and a Python wheel independently implement ML-KEM and have their outputs match to the byte.
The three standards divide the work cleanly.
The Module-Lattice-Based Key-Encapsulation Mechanism: the standardized key-establishment primitive, derived from CRYSTALS-Kyber, with security tied to Module-LWE. It comes in three parameter sets -- ML-KEM-512, ML-KEM-768, and ML-KEM-1024 -- in increasing order of security strength [32].
The Module-Lattice-Based Digital Signature Algorithm: the standardized general-purpose signature, derived from CRYSTALS-Dilithium. It is a Fiat-Shamir-with-aborts lattice signature that samples from the uniform distribution -- a design the Dilithium team chose specifically because "Gaussian sampling is hard to implement securely and efficiently" [31] [24].
SLH-DSA (FIPS 205) rounds out the set as the hash-only backstop -- slower and larger, but resting on assumptions no lattice attack can touch [22]. Key establishment, everyday signatures, and a conservative signature hedge: three jobs, three standards, one portfolio.
Standardization, not the algorithm, is what lets a whole software community move at once. The algorithm was ready in 2018; what a whole community of libraries, protocols, and hardware needs is a fixed set of byte counts and validated test vectors they can all target independently. FIPS 203 and 204 supplied exactly that -- and that is the "standard" pole of the crossing this article traces [32] [31] [33].
But a standard is still only a specification. It does not compile, it does not ship in a wheel, and it does not integrate itself into a single protocol. FIPS 204 tells you that an ML-DSA-65 signature is exactly 3,309 bytes; it does not tell you what happens to the fifty protocols and hardware buffers that were quietly built around 64-byte signatures.
The standard fixes the byte counts. What happens when those byte counts are fifty times bigger than the ones every protocol was quietly built around?
6. Why the Numbers Break the Protocols
An Ed25519 signature is 64 bytes. Its post-quantum replacement, ML-DSA-65, is 3,309 bytes [2] [31]. Now multiply that by every place a protocol, a certificate format, a hardware buffer, or a database column quietly assumed 64.
This is the pivot of the whole article. The primitive is real, standardized, and about to be a pip install away -- and it does not fit where the old one did.
The size table
| Role | Classical | Post-quantum | Public key | Wire output | Blow-up |
|---|---|---|---|---|---|
| Signature | Ed25519 | ML-DSA-65 | 32 B to 1,952 B | 64 B sig to 3,309 B sig | ~61x key, ~52x sig |
| Key exchange | X25519 | ML-KEM-768 | 32 B to 1,184 B | 32 B shared to 1,088 B ciphertext | ~37x key, ~34x wire |
Those multipliers -- roughly 34x to 61x -- come straight from the Trail of Bits size tables and FIPS 204's Appendix B [2] [31]. Private keys are not the problem. Both ML-DSA-65 and ML-KEM-768 can store their private keys compactly as 32-byte and 64-byte seeds, respectively [2]. The pressure is entirely on the values that travel on the wire: public keys, signatures, and ciphertexts. Those are exactly the objects protocols frame with fixed-width length fields. The size story alone would be a migration headache. But size is only half of what breaks. The other half is shape.
A KEM is not a Diffie-Hellman
The instinct is to treat ML-KEM as "X25519, but bigger." It is not. X25519 is a Diffie-Hellman exchange: both parties publish a share, and both combine their own secret with the other's share to arrive at the same value. ML-KEM is a key-encapsulation mechanism, which has a different message shape entirely.
A key-establishment primitive with three operations. The receiver generates a keypair and publishes the public (encapsulation) key. A sender runs encapsulate on that public key, producing a fresh shared secret together with a ciphertext; the sender keeps the secret and transmits the ciphertext. The receiver runs decapsulate on the ciphertext to recover the same shared secret. Unlike Diffie-Hellman, only one side draws the secret, and there is no "combine my share with yours" step [32].
Trail of Bits states the distinction directly: in a KEM "one party encapsulates a fresh shared secret to the receiver's public key, and the receiver decapsulates it," which is "fundamentally different from Diffie-Hellman" [2]. The consequence for real systems is severe. A protocol that assumed the symmetric, two-share choreography of Diffie-Hellman cannot simply swap in ML-KEM; the message flow itself has to be redesigned, not merely re-parameterized.
Diagram source
sequenceDiagram
participant A as Alice
participant B as Bob
Note over A,B: Diffie-Hellman, both sides contribute a share
A->>B: public share A
B->>A: public share B
Note over A,B: each side combines to reach the same secret
Note over A,B: ML-KEM, only one side encapsulates
B->>A: publishes public key
A->>B: ciphertext from encapsulate
Note over A,B: Bob runs decapsulate to recover the same secret The 4,096-byte wall
The abstract worry becomes concrete at a specific number. Move up one ML-DSA security level, from ML-DSA-65 to ML-DSA-87, and the signature grows from 3,309 bytes to 4,627 bytes [31]. That crosses a boundary that a lot of hardware was built around.
You can check the arithmetic yourself. The snippet below computes the blow-up ratios and tests ML-DSA-87's signature against both a 4,096-byte budget and the tighter 3,968-byte reference budget.
// verified byte counts: FIPS 204 Appendix B and the Trail of Bits size tables
const ed25519 = { key: 32, sig: 64 };
const mldsa65 = { key: 1952, sig: 3309 };
const x25519 = 32;
const mlkem768 = { key: 1184, ct: 1088 };
console.log("ML-DSA-65 signature is " + (mldsa65.sig / ed25519.sig).toFixed(0) + "x an Ed25519 signature");
console.log("ML-DSA-65 public key is " + (mldsa65.key / ed25519.key).toFixed(0) + "x an Ed25519 key");
console.log("ML-KEM-768 public key is " + (mlkem768.key / x25519).toFixed(0) + "x an X25519 key");
// the 4,096-byte wall: does one signature fit one TPM command?
const COMMON_TPM = 4096; // a common maximum command size
const TCG_REF = 4096 - 0x80; // TCG reference MAX_COMMAND_SIZE = 3968
const mldsa87sig = 4627; // ML-DSA-87 signature
const fits = (sig, budget) => (sig > budget ? "OVERFLOWS" : "fits");
console.log("ML-DSA-87 (4627 B) vs 4096 B budget: " + fits(mldsa87sig, COMMON_TPM));
console.log("ML-DSA-87 (4627 B) vs 3968 B budget: " + fits(mldsa87sig, TCG_REF));
console.log("ML-DSA-65 (3309 B) vs 3968 B budget: " + fits(3309, TCG_REF)); Press Run to execute.
Trail of Bits generalizes the warning beyond TPMs: if you maintain a protocol or wire format that hardcodes Ed25519-sized signatures or X25519-sized public keys, "the surrounding fields, length prefixes, and chunking assumptions need to grow with it" [2]. Length prefixes sized for a single byte of count, record framing that assumed a signature fit in one packet, database columns declared CHAR(64) -- each is a small, quiet assumption that the standardized sizes break.
A primitive is not a protocol. Standardizing ML-KEM and ML-DSA -- even shipping them in a default wheel -- does not migrate a single handshake. The primitive is larger, differently shaped, and hostile to the fixed-width assumptions baked into TLS records, X.509 certificates, SSH packets, and hardware command buffers. Installing it is the easy part; making the protocols around it accept it is the migration [2] [31].
So the primitive is real, standardized, and about to be a pip install away -- yet it silently breaks the protocols we would drop it into. Who actually shipped it, and how did they get it into more than a billion downloads a month?
7. The Crossing: How the Primitive Reached a Billion Downloads
The milestone on May 4, 2026 was not a new algorithm. ML-KEM and ML-DSA had existed in specialist builds of pyca/cryptography for over a year -- reachable only if you compiled the library against AWS-LC or BoringSSL. What changed in v48.0.0 was distribution: the primitives landed in the default wheel that everyone already installs.
The changelog says it plainly. Version 48.0.0 added support for mlkem and mldsa "when using OpenSSL 3.5.0 or later, in addition to the existing AWS-LC and BoringSSL support," and concludes: "This means post-quantum algorithms are now available to users of our wheels" [1]. That last clause is the crossing. Not "available if you rebuild against a special backend," but available to users of the ordinary wheels, from an ordinary pip install.
The evolution here is distribution, not mathematics
Diagram source
flowchart TD
S["FIPS 203 and 204 standard (2024)"] --> B1["OpenSSL 3.5.0 (2025)"]
S --> B2["AWS-LC and BoringSSL (earlier)"]
B2 -.->|specialist builds only, 2024 to early 2026| G["Backend-gated access"]
B1 -->|default build| W["pyca cryptography v48 wheel (May 2026)"]
G -.-> W
W --> D["Ansible, Certbot, Airflow, paramiko"]
D --> P["The whole Python software supply chain"] The gate that opened was a backend one. pyca/cryptography delegates its heavy cryptography to a C library, and until recently only the non-default backends AWS-LC and BoringSSL exposed the post-quantum primitives. OpenSSL 3.5.0, released April 8, 2025, added support for ML-KEM, ML-DSA, and SLH-DSA to the mainstream backend [36]. Once the default wheels could be built against an OpenSSL that carried the primitives, the last distribution barrier fell. The sequence is: standard, then backend-gated builds, then default wheels [1] [36].
There is a security point hiding in that sequence. pyca/cryptography does not reimplement ML-KEM or ML-DSA in Python; it delegates to the vetted C backend and exposes a thin, cross-tested binding [2]. That is why the milestone is a packaging event, not a cryptographic one. It is also why the OpenSSL 3.5.0 floor is not an inconvenience but the whole mechanism: the primitive a developer calls is the same backend implementation that already ships in browsers and servers, reached through new Rust bindings rather than rewritten in a memory-unsafe hurry. Distribution, not reimplementation, is what crossed the last mile.
Why does one Python library matter this much? Reach. pyca/cryptography is the foundational cryptography library for the Python community, sitting underneath widely used tools including Ansible, Certbot, Apache Airflow, and paramiko [2] [37]. Because so much of the Python world depends on it, one release put quantum-resistant primitives within a single pip install of an enormous body of software. Trail of Bits reports pyca/cryptography as the eleventh most-downloaded package on PyPI, with about 1.2 billion downloads in the last month [2] [38]. A change to a dependency that widely used is a change to the reachable capability of the entire community above it.
Who built it
The post-quantum work in pyca/cryptography was a funded, credited effort, not a weekend patch. Trail of Bits, with support from the Sovereign Tech Agency, wrote the Rust bindings, the cross-binding API and tests, and the AWS-LC backend support; the library's maintainers, Paul Kehrer and Alex Gaynor, contributed other-backend work and review [2] [39].
What the API looks like
The signature API mirrors the existing asymmetric surface: generate a key, get its public key, sign, verify. The KEM API is where the shape difference from Section 6 becomes code -- encapsulate returns a (shared_secret, ciphertext) pair, not a single shared value both sides derive.
# Signatures (FIPS 204): mirrors the familiar sign / verify shape
from cryptography.hazmat.primitives.asymmetric import mldsa
private_key = mldsa.MLDSA65PrivateKey.generate()
public_key = private_key.public_key()
signature = private_key.sign(b"message")
public_key.verify(signature, b"message") # raises InvalidSignature on failure
# Key establishment (FIPS 203): a KEM, not Diffie-Hellman
from cryptography.hazmat.primitives.asymmetric import mlkem
private_key = mlkem.MLKEM768PrivateKey.generate()
public_key = private_key.public_key()
shared_secret_sender, ciphertext = public_key.encapsulate()
shared_secret_receiver = private_key.decapsulate(ciphertext)
assert shared_secret_sender == shared_secret_receiver Press Run to execute.
Two caveats keep this honest. First, v48 ships ML-KEM and ML-DSA but not SLH-DSA; Trail of Bits notes only that they have "started working on it" [2]. Second, the OpenSSL 3.5.0 requirement is real -- the primitives are exposed only when the wheel is backed by a version that carries them [1]. The availability matrix makes the pattern visible: the primitive columns are nearly all "Yes," while real protocol integration remains sparse.
| Layer | Artifact | ML-KEM | ML-DSA | SLH-DSA | Hybrid KEX default |
|---|---|---|---|---|---|
| Crypto backend | OpenSSL 3.5.0 (2025-04-08) | Yes | Yes | Yes | X25519MLKEM768 (TLS) |
| Crypto backend | AWS-LC / BoringSSL | Yes | Yes | no | n/a |
| Python library | pyca/cryptography v48.0.0 (2026-05-04) | Yes | Yes | no (WIP) | delegates |
| Python library | pyca/cryptography v49.0.0 (2026-06-12) | Yes | Yes (plus X.509) | no | delegates |
| SSH protocol | OpenSSH 9.9 (2024-09-19) | hybrid KEX | no | no | mlkem768x25519-sha256 |
Sources: OpenSSL 3.5.0 release notes [36]; the pyca changelog [1]; OpenSSH 9.9 notes [40]; the Trail of Bits write-up [2].
The code shipped the primitive in May. The federal government set its deadlines in June. Which one was driving the other?
8. The Clock, and Why the Code Got There First
Seven weeks after the wheels shipped, the White House put a clock on the wall. Executive Order 14412, "Securing the Nation Against Advanced Cryptographic Attacks," was signed on June 22, 2026 [3]. It sets hard federal deadlines: migrate key establishment to post-quantum cryptography by December 31, 2030; migrate digital signatures by December 31, 2031; stand up a NIST migration pilot by December 31, 2027; and have CISA issue cryptographic-bill-of-materials minimum-elements guidance within 270 days [3].
Diagram source
timeline
title Two tracks, and the code point lands first
section Code track
OpenSSL 3.5.0 2025-04-08 : PQC in the mainstream backend
pyca v48.0.0 2026-05-04 : ML-KEM and ML-DSA in default wheels
pyca v49.0.0 2026-06-12 : X.509 support, current at EO signing
section Policy track
NSM-10 and CNSA 2.0 2022 : intent set, but no shipping code
OMB M-23-02 2022 : agency inventory guidance
EO 14412 2026-06-22 : deadlines 2027, 2030, 2031 The policy intent is older than the code. The National Security Memorandum 10 (May 2022) [41], the NSA's CNSA 2.0 suite (September 2022) [42], and OMB memorandum M-23-02 (November 2022) [43] had already told federal systems that a post-quantum migration was coming. What none of them could do was make the primitive installable. That is the asymmetry worth sitting with: the intent was set in 2022, but the shipping code arrived in 2026 -- and it arrived ahead of the deadline that finally gave the intent teeth.
The executive order's own definitions are asymmetric
One precise detail here is easy to get wrong, and getting it right sharpens the argument.
The executive order does not define its two migration targets symmetrically. Section 2 defines "key establishment" with the same meaning it has in FIPS 203 -- which is ML-KEM, a post-quantum standard -- so the key-establishment mandate points straight at the standardized primitive. But it defines "digital signature" with the same meaning it has in FIPS 186-5, the classical Digital Signature Standard covering RSA, ECDSA, and EdDSA; the post-quantum signature standard, FIPS 204 (ML-DSA), is not named in the definitions [44] [45] [31]. The signature mandate is therefore operative, not definitional: Section 4(b) still requires migrating signatures to post-quantum cryptography by 2031, but it reaches that goal through the deadline text rather than by defining a signature as a post-quantum object. Key establishment is defined into a post-quantum standard; signatures are mandated toward post-quantum cryptography by a deadline while still being defined against the classical standard [44] [45]. That is not a contradiction -- it reflects that key establishment carries the urgent, harvest-now-decrypt-later confidentiality risk, while signatures do not, a distinction Section 9 develops.
The pip install beat the policy
Stated plainly: the shipping code led, and the policy followed. The full four-date timeline -- and why "the same week" is a misconception -- is in the callout below [1] [3].
This is the article's second turn, paid off. It was not the 2024 standard, and not the 2026 executive order, that put quantum-resistant cryptography in front of ordinary developers. It was two distribution events: standardization fixing the byte counts, and a default-wheel release shipping the code. The second beat the mandate. Federal policy still matters enormously for who must migrate and by when, and the platform axis of that migration on Windows is its own long story in this blog's sibling post. But on the shipping-code axis, decree did not lead. Distribution did.
If the code beat the mandate and the standard is finalized, the migration should be basically done. It is not. Why not?
9. What Shipping the Primitive Does Not Solve
Here is the uncomfortable truth beneath the celebration: nobody can prove that lattice problems are hard, and the assumption is younger than the one it replaces.
The security of ML-KEM and ML-DSA rests on two pillars. The first is the worst-case-to-average-case reduction from Ajtai and Regev, which ties breaking a random instance to solving the hardest instance of a lattice problem [16] [17]. The second is the best-known attack: lattice sieving, whose cost is classically in the relevant blocksize, with no known exponential quantum speedup [21]. The entire security argument lives in the gap between "no proof of hardness" and that attack cost. A proven super-polynomial lower bound would settle , since these lattice problems sit in NP -- so we are not getting a proof soon. This is the same epistemic position RSA has always occupied, except on a younger assumption.
Two threat horizons, not one
A subtle but important point: the quantum threat does not arrive on the same schedule for confidentiality and for authentication.
Harvest-now-decrypt-later is a confidentiality horizon. An adversary can record an encrypted session today and decrypt it whenever a quantum computer arrives, so key establishment must be quantum-resistant now for any secret with a long shelf life. Signature forgery is an authentication threat with no retroactive version: you cannot forge yesterday's signature tomorrow, because a signature only needs to resist forgery until the moment it is verified and trusted [3]. That is why the order's key-establishment deadline (2030) falls a year before its signature deadline (2031): the confidentiality clock is the more urgent one [3].
Why the answer is a portfolio
No single scheme is simultaneously tiny, tightly reduced to a decades-old assumption, and trivially side-channel-safe. That ideal primitive does not exist, which is precisely why the standard is a portfolio rather than a single winner.
| Dimension | ML-KEM (203) | ML-DSA (204) | SLH-DSA (205) | FN-DSA/Falcon (206) | Classic McEliece |
|---|---|---|---|---|---|
| Job | Key establishment | Signature | Signature | Signature | Key establishment |
| Hard problem | Module-LWE | Module-LWE | Hash preimage | NTRU lattice | Goppa decoding |
| Public key | 1,184 B | 1,952 B | 32 to 64 B | compact | 261 KB to 1.3 MB |
| Output | 1,088 B ct | 3,309 B sig | many KB sig | small sig | 128 to 240 B ct |
| Assumption age | ~2012 | ~2012 | decades | ~1996 | 45+ years |
| In pyca v48 | Yes | Yes | no | no (pending) | no |
Sources: FIPS 203, 204, and 205 [32] [31] [22]; the CRYSTALS pages for the lattice schemes [23] [24]; Classic McEliece for the code-based sizes [15]; and the pyca changelog for what v48 includes [1].
Two more standing hedges belong here, because they shape the practical advice in Section 11.
A key-establishment construction that runs a classical exchange (such as X25519) and a post-quantum KEM (such as ML-KEM-768) together and combines both shared secrets, so the session stays secure as long as either component is unbroken. Hybrids protect against both a surprise lattice break and an implementation bug in the new code, at the cost of sending both sets of bytes [36].
The property of a system that can swap cryptographic algorithms without re-architecting the protocols and formats around them. Crypto-agility is the standing hedge against the next migration: build the seams now so that replacing a broken primitive later is a configuration change, not another thirty-year project.
If the math is settled enough to standardize and ship, and the limits are understood, where is the actual remaining work?
10. The Protocols Are the Remaining Work
Here is the thesis in one line: the primitive is a pip install away, and it is still nearly useless to most developers. That is not a paradox. It is the definition of the remaining work.
The defining open problem is protocol integration. TLS, SSH, X.509 and the public-key infrastructure built on it, and package signing all have to be redesigned around KEM-shaped, kilobyte-scale objects without breaking the length fields, buffers, and packet-size assumptions surveyed in Section 6 [2]. Trail of Bits is blunt about what that means for the average engineer.
You're unlikely to use PQ algorithms directly in tools like Certbot or Ansible until common protocols add support. -- Trail of Bits [2]
The good news is that the hardest-to-defer half is already moving.
The portfolio is also still incomplete in the shipping code. SLH-DSA, the conservative hash-based backstop, is not yet in pyca/cryptography; the maintainers have only started on it [2]. Falcon, standardized as FN-DSA, remains pending as FIPS 206 in 2026, held up in part because its floating-point Gaussian sampler is hard to implement safely at standardization quality [2] [27]. So even a developer who wants the full toolbox cannot yet pip install all of it.
And some problems have nothing to do with new code. Long-lived trust anchors -- root certificates and firmware keys with lifetimes measured in decades -- must migrate while still interoperating with everything already deployed, and they run straight into the fixed-size hardware limits from Section 6, where an extra security level can overrun a command buffer that has no chunking [34]. The deeper governance problem is crypto-agility: building systems so that the next forced migration -- prompted by, say, a lattice break of the kind that felled SIKE -- does not take another thirty years [28].
None of this is theoretical anymore. So what should a working engineer actually do on Monday?
11. Using It Today
Concrete and actionable: the one command, the one gotcha, and the three decisions.
The command is pip install "cryptography>=48". The gotcha is that the version pin alone does not guarantee you get anything post-quantum.
The API is small enough to hold in your head. A version pin alone is not enough: as the callout above warns, the primitives appear only when the wheel is built against a supported backend [1]. The mldsa module gives you generate, public_key, sign, and verify -- the familiar signature shape. The mlkem module gives you encapsulate, which returns a (shared_secret, ciphertext) pair, and decapsulate, which takes a ciphertext and returns the shared secret. There is deliberately no separate share-combining step: that is the KEM shape from Section 6, not a Diffie-Hellman exchange [2].
How to confirm your build actually exposes the primitives
Import the module and generate a key; a backend without support raises instead of silently degrading. Running mlkem.MLKEM768PrivateKey.generate() after from cryptography.hazmat.primitives.asymmetric import mlkem should complete without error. A clean run means your wheel is backed by a build that actually carries ML-KEM -- one of the supported backends named in the warning above -- while an exception means it is not [1].
Three decisions follow.
Which parameter set? Default to ML-KEM-768 and ML-DSA-65 -- NIST Category 3, roughly AES-192, and more than 128 bits of security under conservative analysis [23] [24]. Go higher only for data whose confidentiality must outlive decades, and when you do, watch the 4,096-byte wall from Section 6. Prefer ML-DSA over SLH-DSA as your default signature, and do not wait for Falcon in Python -- it is not standardized yet. Performance rarely decides this: even on a constrained ARM Cortex-M4, ML-KEM-768 runs in around a million cycles per operation, and on modern hardware the cost is imperceptible for normal use [46] [2].
Pure or hybrid? Prefer hybrid where a protocol offers it.
Where do you start? Not with code -- with an inventory.
A machine-readable inventory of the cryptography a system uses: which algorithms, key sizes, protocols, and libraries appear where. A CBOM is the migration's first practical step -- you cannot replace what you cannot find -- and Executive Order 14412 directs CISA to publish minimum-elements guidance for exactly this kind of inventory [3].
You can install the primitive in five seconds; you should deploy it with a plan. The last mile after the pip install is yours.
12. Frequently Asked Questions
Frequently asked questions
Does installing cryptography 48 or later make me quantum-safe?
No. It gives you primitives, not a protocol, and only when the wheel is backed by OpenSSL 3.5.0 or later, AWS-LC, or BoringSSL. Being able to call ML-KEM and ML-DSA is not the same as having a quantum-resistant TLS connection or certificate chain, which depend on protocol integration that is still in progress [1] [2].
Is ML-KEM a drop-in replacement for X25519?
No. ML-KEM is a key-encapsulation mechanism, not a Diffie-Hellman exchange: one side encapsulates a fresh secret to the other's public key rather than both sides combining shares. The message shape differs, and the objects are roughly 34 to 37 times larger, so protocols must be redesigned rather than merely re-parameterized [2] [32].
Did pyca v48 invent these algorithms?
No. Earlier versions of the library could already expose ML-KEM and ML-DSA when built against AWS-LC or BoringSSL. What v48.0.0 changed was distribution: it put the primitives in the default OpenSSL-backed wheels, so a plain pip install reaches them [1].
Is quantum resistance the main reason to migrate now?
What is the difference between harvest-now-decrypt-later and signature-forgery risk?
Harvest-now-decrypt-later is a confidentiality horizon: recorded ciphertext can be broken retroactively, so key establishment is urgent now. Signature forgery is an authentication threat that only bites once a quantum computer exists, because you cannot forge a signature that was already verified and trusted in the past. That is why the federal key-establishment deadline precedes the signature deadline [3].
Did the library and the Executive Order ship the same week?
Is post-quantum cryptography the same as quantum key distribution?
No. Post-quantum cryptography is classical software -- ordinary algorithms on ordinary computers -- designed to resist quantum attack, which is exactly what FIPS 203 and 204 standardize [32] [31]. Quantum key distribution is a hardware and physics approach that transmits keys over quantum channels; it is a different technology often confused with PQC.
Conclusion: The Primitive Shipped, the Protocols Remain
A threat named in 1994 set a thirty-year relay in motion. Ajtai and Regev found lattice math that plausibly survives Shor's algorithm; Ring-LWE and Module-LWE made that math small enough to ship; an open competition from 2016 to 2022 chose a deliberately diverse portfolio and watched two serious candidates break along the way; and in August 2024, NIST turned the winners into standards with fixed byte counts and validated test vectors [16] [17] [19] [27] [32] [31].
Then, on May 4, 2026, the primitive quietly crossed its last visible milestone -- not by decree, but by distribution. pyca/cryptography v48.0.0 put ML-KEM and ML-DSA in the default wheels that a billion-download-a-month slice of the Python world already installs, roughly seven weeks before Executive Order 14412 set the federal deadlines [1] [3]. The primitive shipped; the policy set the clock; and the protocols are the work that remains [1] [3] [2].
That last clause is the whole point. ML-KEM and ML-DSA are larger, differently shaped, and hostile to the fixed-width assumptions that TLS, SSH, X.509, and hardware buffers were quietly built around. Installing the primitive took thirty years and now takes five seconds. Making the protocols accept it -- redesigning wire formats around KEM-shaped, kilobyte-scale objects without breaking the length fields and command budgets that assumed 32-byte keys and 64-byte signatures -- is the migration that is actually left. A primitive is not a protocol, and the last mile after the pip install is the one we are still walking.
Study guide
Key terms
- Post-quantum cryptography (PQC)
- Classical algorithms, run on ordinary computers, designed to resist attack by a large-scale quantum computer.
- Shor's algorithm
- A 1994 quantum algorithm that factors integers and solves discrete logarithms in polynomial time, breaking RSA, Diffie-Hellman, and elliptic-curve cryptography.
- Harvest-now, decrypt-later (HNDL)
- Recording encrypted data today to decrypt it later once quantum computers exist, which makes the threat present-day for long-lived secrets.
- Learning With Errors (LWE)
- Recovering a secret from noisy linear equations; believed hard even for quantum computers, and the basis of ML-KEM and ML-DSA.
- Module-LWE
- A structured, tunable version of LWE over modules that yields kilobyte-scale keys; the hard problem under CRYSTALS-Kyber and Dilithium.
- ML-KEM (FIPS 203)
- The standardized key-encapsulation mechanism for post-quantum key establishment, derived from CRYSTALS-Kyber.
- ML-DSA (FIPS 204)
- The standardized general-purpose post-quantum signature, derived from CRYSTALS-Dilithium, using Fiat-Shamir with aborts and uniform sampling.
- Key Encapsulation Mechanism (KEM)
- A key-establishment primitive where one side encapsulates a fresh secret to the other's public key and the other decapsulates it; not a Diffie-Hellman exchange.
Comprehension questions
Why does the quantum threat target public-key cryptography but only mildly affect symmetric ciphers?
Shor's algorithm breaks the factoring and discrete-log assumptions behind public-key schemes outright, while Grover gives only a quadratic speedup against symmetric keys, so a modest key-size bump restores the margin.
Why is ML-KEM not a drop-in replacement for X25519?
A KEM has a different message shape than Diffie-Hellman (encapsulate and decapsulate rather than combining shares), and its outputs are roughly 34 to 37 times larger, so protocols must be redesigned, not just re-parameterized.
What does the 4,096-byte wall illustrate, and why is 4,096 not universal?
ML-DSA-87's 4,627-byte signature overruns a common TPM command budget that has no chunking, while ML-DSA-65 fits; the budget is implementation-defined, and the TCG reference build is tighter at 3,968 bytes.
In what concrete sense did the pip install beat the policy?
pyca/cryptography v48.0.0 shipped ML-KEM and ML-DSA on May 4, 2026, about seven weeks before Executive Order 14412 set the federal deadlines on June 22, 2026.
Why does the standardized portfolio keep a hash-only backstop?
Lattice hardness is unproven and younger than factoring, the ring and module structure is a conjecture rather than a theorem, and SIKE and Rainbow both broke during standardization, so SLH-DSA is kept as a hedge that would survive a lattice break.
References
- (2026). cryptography CHANGELOG (v48.0.0, v48.0.1, v49.0.0). https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst - ML-KEM/ML-DSA added to default OpenSSL-backed wheels in v48.0.0 (2026-05-04). ↩
- (2026). Shipping post-quantum cryptography to Python. https://blog.trailofbits.com/2026/06/30/shipping-post-quantum-cryptography-to-python/ ↩
- (2026). Securing the Nation Against Advanced Cryptographic Attacks (Executive Order 14412). https://www.whitehouse.gov/presidential-actions/2026/06/securing-the-nation-against-advanced-cryptographic-attacks/ ↩
- (1994). Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer. https://arxiv.org/abs/quant-ph/9508027 ↩
- (1996). A fast quantum mechanical algorithm for database search. https://arxiv.org/abs/quant-ph/9605043 - STOC 1996; the quadratic (square-root) speedup for unstructured search underlying the symmetric-key argument. ↩
- (2016). Post-Quantum Cryptography: Security (Evaluation Criteria). https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria) ↩
- (2016). Applying Grovers algorithm to AES: quantum resource estimates. https://arxiv.org/abs/1512.04965 ↩
- (1999). Grovers quantum searching algorithm is optimal. https://arxiv.org/abs/quant-ph/9711070 ↩
- (2024). Quantum is unimportant to post-quantum. https://blog.trailofbits.com/2024/07/01/quantum-is-unimportant-to-post-quantum/ ↩
- (2006). PQCrypto Workshop Series. https://pqcrypto.org/ ↩
- (2009). Post-Quantum Cryptography. Springer. https://doi.org/10.1007/978-3-540-88702-7 ↩
- (2018). RFC 8391: XMSS: eXtended Merkle Signature Scheme. https://www.rfc-editor.org/rfc/rfc8391 - Sec. 1: one-time and many-time hash-based signature schemes were proposed by Merkle in 1979. ↩
- (2020). NIST SP 800-208: Recommendation for Stateful Hash-Based Signature Schemes. https://csrc.nist.gov/pubs/sp/800/208/final ↩
- (2024). SPHINCS+. https://sphincs.org/ ↩
- (2024). Classic McEliece. https://classic.mceliece.org/ ↩
- (1996). Generating Hard Instances of Lattice Problems. https://dl.acm.org/doi/10.1145/237814.237838 ↩
- (2005). On Lattices, Learning with Errors, Random Linear Codes, and Cryptography. https://cims.nyu.edu/~regev/papers/qcrypto.pdf ↩
- (2012). On Ideal Lattices and Learning with Errors over Rings. https://eprint.iacr.org/2012/230 ↩
- (2012). Worst-Case to Average-Case Reductions for Module Lattices. https://eprint.iacr.org/2012/090 ↩
- (2015). A Decade of Lattice Cryptography. https://eprint.iacr.org/2015/939 ↩
- (2016). New Directions in Nearest Neighbor Searching with Applications to Lattice Sieving. https://eprint.iacr.org/2015/1128 ↩
- (2024). FIPS 205: Stateless Hash-Based Digital Signature Standard. https://csrc.nist.gov/pubs/fips/205/final ↩
- (2024). CRYSTALS-Kyber. https://pq-crystals.org/kyber/ ↩
- (2024). CRYSTALS-Dilithium. https://pq-crystals.org/dilithium/index.shtml ↩
- (2024). Post-Quantum Cryptography Project. https://csrc.nist.gov/Projects/Post-Quantum-Cryptography ↩
- (2016). Experimenting with Post-Quantum Cryptography. https://security.googleblog.com/2016/07/experimenting-with-post-quantum.html ↩
- (2022). NIST Announces First Four Quantum-Resistant Cryptographic Algorithms. https://www.nist.gov/news-events/news/2022/07/nist-announces-first-four-quantum-resistant-cryptographic-algorithms ↩
- (2022). An Efficient Key Recovery Attack on SIDH. https://eprint.iacr.org/2022/975 ↩
- (2022). Breaking Rainbow Takes a Weekend on a Laptop. https://eprint.iacr.org/2022/214 ↩
- (2024). NTRU. https://en.wikipedia.org/wiki/NTRU - Background on NTRU history, patent lapse (2017), and NIST Round-3 finalist status. ↩
- (2024). FIPS 204: Module-Lattice-Based Digital Signature Standard. https://csrc.nist.gov/pubs/fips/204/final ↩
- (2024). FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard. https://csrc.nist.gov/pubs/fips/203/final ↩
- (2024). ACVP-Server (Cryptographic Algorithm Validation Program test vectors). https://github.com/usnistgov/ACVP-Server ↩
- (2026). TPM 2.0 Library Specification. https://trustedcomputinggroup.org/resource/tpm-library-specification/ ↩
- (2024). ms-tpm-20-ref: TPM 2.0 reference implementation. https://github.com/microsoft/ms-tpm-20-ref - MAX_COMMAND_SIZE defined as 4096-0x80 = 3,968 B; MAX_DIGEST_BUFFER 1024 B. ↩
- (2025). OpenSSL 3.5 Release Notes. https://openssl-library.org/news/openssl-3.5-notes/ ↩
- (2026). cryptography 48.0.0 dependents. https://deps.dev/pypi/cryptography/48.0.0/dependents ↩
- (2026). Top PyPI Packages. https://pypistats.org/top ↩
- (2026). Sovereign Tech Agency. https://www.sovereign.tech/ ↩
- (2024). OpenSSH 9.9 Release Notes. https://www.openssh.org/txt/release-9.9 ↩
- (2022). National Security Memorandum 10 (NSM-10): Promoting United States Leadership in Quantum Computing While Mitigating Risks to Vulnerable Cryptographic Systems. https://bidenwhitehouse.archives.gov/briefing-room/statements-releases/2022/05/04/national-security-memorandum-on-promoting-united-states-leadership-in-quantum-computing-while-mitigating-risks-to-vulnerable-cryptographic-systems/ - Signed May 4, 2022; directs agencies to begin the multi-year migration to quantum-resistant cryptography. ↩
- (2022). CNSA 2.0 Fact Sheet. https://media.defense.gov/2022/Sep/07/2003066201/-1/-1/0/CSI_CNSA_SUITE_2_0_FACT_SHEET_UOO117573-22.PDF ↩
- (2022). OMB Memorandum M-23-02: Migrating to Post-Quantum Cryptography. https://bidenwhitehouse.archives.gov/wp-content/uploads/2022/11/M-23-02-M-Memo-on-Migrating-to-Post-Quantum-Cryptography.pdf - Dated November 18, 2022; agency post-quantum inventory and migration-planning guidance. ↩
- (2026). Executive Order 14412, full text (91 FR 38483). https://www.federalregister.gov/documents/full_text/text/2026/06/25/2026-12909.txt - Sec. 2(k) key establishment via FIPS 203; Sec. 2(j) digital signature via FIPS 186-5. ↩
- (2023). FIPS 186-5: Digital Signature Standard (DSS). https://csrc.nist.gov/pubs/fips/186-5/final - The classical DSS (RSA/ECDSA/EdDSA) that EO 14412 Sec. 2(j) points to. ↩
- (2026). pqm4 Post-Quantum Benchmarks on Cortex-M4. https://github.com/mupq/pqm4/blob/master/benchmarks.md ↩