Read this first
Crypto-agility is an engineering property you build from a handful of concrete patterns: negotiate the algorithm at runtime, route every operation through one provider, choose the algorithm from configuration, store a versioned algorithm identifier next to the data, size every field for the largest primitive, and automate rotation with dual-stack support. Apply them and the post-quantum swap — and the one after it — becomes a configuration change rather than a programme of work.
This is the implementation companion to crypto-agility (the concept): that guide argues why agility, not the algorithm swap, is the real deliverable of a post-quantum migration. This one is the how. None of the patterns below are new — they are the mechanisms mature protocols already use to survive algorithm change — but they have to be applied deliberately, because rigidity is what you get by default. Each pattern moves one decision out of hardcoded code and into a place you can change cheaply later.
Negotiate the algorithm at runtime
The most durable pattern is to let the endpoints agree on the algorithm when they connect, rather than fixing it in the wire format. TLS is the model: cipher suites and `supported_groups` let a client and server pick a mutually supported primitive — the same mechanism that makes the TLS hybrid group deployable without a flag day. Design new protocols with an explicit negotiation step and a version field from day one, even when only one algorithm exists, so the second one is an addition rather than a breaking change.
Put every operation behind one provider
Application code should never name a concrete algorithm. Route all cryptographic operations through a single interface — a provider, in the JCA/JCE or PKCS#11 sense — that takes an abstract request ("establish a shared secret", "sign this") and resolves the actual primitive internally. The algorithm becomes an implementation detail behind a stable boundary, so a swap is one change in one place instead of an edit at every call site. This is also what makes negotiation and configuration enforceable: there is exactly one code path to govern.
Select from configuration and policy
With a provider in place, the choice of primitive belongs in configuration or a policy engine, never in identifiers scattered through the source. Centralising it means a change — a new parameter set, a deprecation, an emergency response to a broken algorithm — is a single edit that propagates everywhere, ideally without a redeploy. Keep the policy versioned alongside your migration roadmap so the running configuration and the plan for what changes next stay in step.
Store self-describing, versioned data
Anything cryptographic that outlives a single session must carry its own algorithm identifier, so you can rotate primitives and still read what you wrote before. This is exactly what X.509 `AlgorithmIdentifier`, the JOSE/COSE `alg` header, and message-format version fields already do — mirror them in your own formats. Two rules make it work in practice:
- **Tag the data, don't infer it.** Persist an explicit algorithm-and-parameters identifier next to every key, signature, and ciphertext; never assume the algorithm from context or deployment date.
- **Size for the largest primitive.** Never hardcode key, signature, or ciphertext lengths — post-quantum outputs dwarf classical ones, and SLH-DSA's large signatures can run to tens of kilobytes. Use length-prefixed or variable-length fields in schemas, columns, and buffers.
Agility is measured by the next change, not this one
The common failure is treating post-quantum as a one-time swap — ripping out RSA and hardcoding ML-KEM into exactly the same fixed fields and inline calls. You spend a multi-year effort and rebuild the same rigidity, so the next transition costs just as much. Judge every pattern here by one test: how cheap does it make the change after this one? If the answer is "another migration programme," you have swapped the algorithm but not built agility.
Rotate continuously and run dual-stack
Agility only holds if you exercise it. Automate key and certificate rotation so changing a primitive is a routine, tested operation rather than a rare manual event — a rotation path that is never used is a rotation path that will fail when you need it. And support running old and new algorithms at once: hybrid key exchange, dual signatures, or parallel certificate chains let you cut over gradually and roll back safely, instead of forcing every endpoint to upgrade in lockstep.
How quantakrypto helps
These patterns are cheap to state and hard to retrofit, because the rigidity is spread across code, schemas, and protocols that already ship. We start with an audit that produces a machine-readable inventory — a CBOM — of where cryptography is hardcoded, pinned, or size-locked, so you can find and track what still needs to change. We turn it into a migration roadmap that fixes the boundaries — negotiation, provider layers, config-driven selection, versioned formats — so the post-quantum swap and every swap after it is a configuration change. And we run training so your engineers reach for these patterns by default.