Designing for crypto-agility
The goal is not one swap to ML-KEM. It is the ability to change cryptographic algorithms again, cheaply, without another multi-year programme
On this page — 6 sections
Ask what success looks like for a post-quantum programme and most answers describe a destination: everything on ML-KEM, everything on ML-DSA, finished. That is the wrong target. The migration you are running now is expensive largely because the last set of algorithm choices was welded into the estate. If the new choices are welded in the same way, you have bought one upgrade at full price and guaranteed you will pay it again.
Crypto-agility is the property that makes the next change cheap. It is an architectural outcome rather than a product you buy, and it is worth designing for while you still have the budget and attention a post-quantum programme provides.
Why the second migration is the real target
There will be a next time. Algorithms weaken, parameter sets get revised, standards bodies update guidance, and regulators change what they will accept. The record of NIST SP 800-131A transitions is a record of algorithms being deprecated on a schedule, and nothing about the post-quantum generation exempts it from that pattern.
Cost is the argument. If changing an algorithm requires touching hundreds of source files, coordinating dozens of teams and renegotiating with every counterparty, you will defer it until a regulator or an incident forces the issue. If it requires a configuration change, a negotiation update and a test pass, you will do it when the evidence says to. The difference between those two organisations is architecture, not diligence.
A system is agile when changing its cryptographic algorithm is a configuration decision with a test attached, not a programme with a budget attached.Theos Quantum, working definition
Centralise cryptographic choice behind an interface
In most estates, cryptographic decisions are scattered across application code. A hundred call sites each name a cipher, a curve, a hash and a padding mode. Every one of those call sites is a place the next migration has to visit, and a place where a default can quietly diverge from policy.
The fix is unglamorous. Application code should express intent, and one component should decide how that intent is satisfied. Callers ask to protect data in transit, to sign an artefact, or to derive a key. They do not choose a curve. Policy then lives in one place, is versioned, is testable, and can change without redeploying every consumer.
// Intent at the call site. Algorithm choice lives in policy.
//
// Not this:
// const sig = signEcdsaP256(payload, key);
//
// This:
const sig = await crypto.sign({
purpose: "artefact-release", // policy maps purpose -> algorithm
payload,
keyRef: "release-signing/current",
});
// The verifier reads the algorithm and policy version from the
// envelope instead of assuming what the signer used.
await crypto.verify(sig, { purpose: "artefact-release" });Two details make this work in practice. Keys are referenced indirectly, so rotation does not change calling code. And the output carries the algorithm and policy version it was produced under, so a verifier never has to assume. That second point is what lets old and new artefacts coexist during a transition.
Remove hard-coded algorithm identifiers
Hard-coded algorithm identifiers are the specific thing that makes migrations expensive, and they hide in more places than source code. Discovery finds them across configuration, schemas, stored records and infrastructure definitions. See What counts as an asset for how we catalogue them.
- Source code naming a cipher suite, curve, hash or key size directly at the call site.
- Configuration and infrastructure as code pinning cipher lists, curve preferences or certificate parameters per environment.
- Data formats where the algorithm is implied by field length or by position rather than declared in a header.
- Stored records that omit which algorithm produced them, so you cannot re-verify or re-encrypt without guessing.
- Tests asserting on one algorithm’s output, which turns a policy change into a wave of unrelated test failures.
- Vendor and partner contracts that specify an algorithm by name and require renegotiation before you can change it.
Unlabelled ciphertext is a migration blocker
Encrypted data stored without a record of the algorithm, parameters and key version used cannot be re-encrypted safely at scale. You end up inferring the algorithm from length, or trying candidates until one works. Adding that metadata to your envelope format is cheap now and very expensive later.
Version and negotiate, on both sides of the wire
Agility across a boundary requires negotiation. Inside your own estate that means every protected artefact declares what produced it, and every consumer can handle more than one answer. Across an external boundary it means the protocol supports algorithm negotiation and both parties can move independently.
The pattern that works is ordinary version tolerance applied to cryptography: accept a defined set of algorithms, produce exactly one, and be able to change which one you produce without changing what you accept. That decoupling is what lets you roll forward, observe, and roll back without a coordinated cutover.
| Capability | Not agile | Agile |
|---|---|---|
| Selecting an algorithm | Named at each call site, changed by editing code | Resolved from versioned policy at runtime |
| Reading old artefacts | One algorithm assumed; older records fail or are guessed at | Algorithm and parameters read from the artefact envelope |
| Rotating keys | Key material referenced directly, so rotation needs a release | Keys referenced by name, so rotation is an operational task |
| Talking to a peer | Fixed algorithm; any change requires a coordinated cutover | Negotiated, with the agreed choice logged per connection |
| Reverting a change | Requires a rebuild and a release cycle | Configuration change, rehearsed, with a known recovery time |
| Knowing what you run | A spreadsheet from the last audit | Continuous inventory, refreshed and diffed against policy |
Inventory as a standing capability
Agility is not durable without visibility. An estate drifts. Services are added, defaults change, a library upgrade quietly alters a cipher preference, an acquisition arrives with its own cryptography. An inventory produced once for a migration programme starts decaying the day it is delivered.
Treat inventory as a running capability with an owner, a refresh cadence and a diff. What matters is not the snapshot but the change since the last one, checked against policy. That is also what turns the next migration into a scoping exercise of days rather than months, and it is what an auditor is actually looking for. See Cryptographic bills of material and What to hand an auditor.
The evidence side matters as much as the technical side. Keep the policy version, the inventory diff and the negotiated-algorithm telemetry together. An organisation that can show what it runs, what policy says it should run, and the gap between the two, holds a defensible position even where remediation is incomplete.
A testable definition of agile
Crypto-agility is a slogan until it is falsifiable. We assess it by asking a team to do things, on a real system, and timing them. The claim is only as good as the last time someone ran the exercise.
- 01Change the algorithm used by one service without editing application source code. If code changes, choice is not centralised.
- 02State the recovery time for reverting that change, measured from a rehearsal rather than estimated from a runbook.
- 03Read a record encrypted under the previous algorithm and one under the current algorithm, through the same code path.
- 04Rotate a key without a release, and show which artefacts still reference the old key version.
- 05Produce a current list of every algorithm in use, with its policy version, from the live estate rather than from a document.
- 06Show, per external peer, which algorithm was negotiated on recent connections.
- 07Name the assets that cannot change without a hardware refresh or a vendor release, with the owner and the constraint recorded.
The last item separates honest architecture from optimistic architecture. Every real estate contains cryptography that cannot move on a software timescale. Agility does not make that constraint disappear. It means the constraint is known, named and owned, so it appears in the plan rather than in an incident. Sequencing a migration explains how those constraints shape the order of work.
We assess and evidence agility. We do not refactor your applications.
AutoPQC identifies hard-coded algorithm choices, maps where cryptographic decisions are made, and tracks the inventory over time so the gap against policy stays visible. We do not rewrite your code, build your cryptographic abstraction layer, operate your key management, or supply a cryptographic library. Those remain your engineering decisions, and any vendor claiming to make an estate agile by installing something is describing a product that does not exist.