Post-Quantum Migration Starts With Your Crypto Debt
Post-quantum cryptography is often explained as an algorithm story: Shor’s algorithm threatens RSA and ECC, NIST has standardized ML-KEM, ML-DSA, and SLH-DSA, so everyone should replace old algorithms as soon as possible.
That statement is not wrong, but it sounds too much like a security-training slide. Inside a real company, the problem is rarely as clean as “replace algorithm A with algorithm B.” Where are certificates issued? Who signs the mobile app package? Which KMS wraps backup keys? Is the partner API signature documented? Can the old SDK be upgraded? These small questions decide whether the migration can actually move.
This article follows a fictional but realistic SaaS company called Northwind Notes, a B2B collaboration-documents and approval-workflow product. It has no national secrets and no mysterious hardware. It has public TLS, mobile apps, daily backups, partner APIs, and some historical baggage. The security lead gives the team one target: within 90 days, finish a post-quantum migration pilot. It does not need to replace everything, but it must make the inventory visible and prove one real, rollback-safe path.

The Day-One Misunderstanding: Isn’t Everything Already HTTPS?
Northwind Notes’ first meeting moves quickly. When the business team hears “post-quantum migration,” their first reaction is that all public services already sit behind HTTPS and certificates are managed by the cloud provider. Surely the gateway team can just take a look.
The gateway team does produce a neat list: three public domains, load balancers in two regions, one CDN configuration, and an internal mTLS mesh. It looks tidy until the mobile lead asks, “Does app-package signing count?” The backup owner follows with, “What about the RSA wrapping key used for backups?” The partner-ecosystem team adds the final blow: “Several partner callbacks still use RSA-SHA256 signatures, and the signature-string format is written into contracts.”
That is the real beginning. PQC is not a TLS-only project. It is crypto-asset governance. Crypto assets do not live only with the security team or in infrastructure. They hide in release pipelines, mobile certificates, archive jobs, partner SDKs, old batch scripts, and many “we wrote this signer in a hurry years ago” utilities.
The company rewrites the 90-day goal into three sentences:
| Goal | Not for | Real deliverable |
|---|---|---|
| Inventory crypto assets | A beautiful report | Find assets with long secrecy periods, slow upgrades, and unclear ownership |
| Run a hybrid pilot | Prove that a new algorithm is fashionable | Validate compatibility, observability, rollback, and vendor boundaries |
| Build crypto agility | Replace every algorithm once | Make future algorithm changes avoid another company-wide code excavation |
This shift matters. If post-quantum migration starts with “upgrade everything,” it easily becomes a large but vague security program. If it starts by admitting that the organization does not know where cryptography is used, it lands on executable lists, interfaces, and workflows.
The Inventory Is Not a CMDB; It Must Support Prioritization
In the first week, Northwind Notes does not buy a platform or introduce a new library. The security team sends a plain spreadsheet to every system owner. The fields are not administrative fields such as “system name, owner, note.” They are designed around migration decisions.
| Field | Example | Question it answers |
|---|---|---|
| Asset ID | CRYPTO-API-017 | Can later tests, risks, and tickets point to the same thing? |
| Scenario | Partner order-callback verification | Is this transport encryption, encryption at rest, or digital signature? |
| Algorithm and parameters | RSA-2048 / SHA-256 / PKCS#1 v1.5 | How exposed is it to quantum risk, and does it have classical weaknesses too? |
| Data secrecy period | 7 years | Is this a harvest-now-decrypt-later risk? |
| Key or certificate lifecycle | Rotated every 2 years | Where is the next natural change window? |
| Dependents | 8 partners, 3 internal services | Who will be affected by migration? |
| Code or configuration location | api-signature-service / v2 signer | Can we find the actual implementation? |
| Vendor constraint | HSM firmware does not yet support PQC | Is the bottleneck outside the company? |
| Rollback path | Feature flag back to RSA | Can the pilot retreat if it fails? |
| Current observability | Only 401 count, no algorithm label | Can failures be diagnosed after rollout? |
The spreadsheet quickly exposes several problems. Public TLS is clear because certificate management is centralized. Mobile app signing has expiry dates, but nobody has documented old-device verification behavior. Backup encryption uses cloud KMS, but the team has not checked how the KMS wraps keys internally. Partner RSA signatures are the worst: signing code is scattered across an old service and two SDKs, and some partners have private differences in field ordering.
By the end of week one, the team does not have a “12% migrated” number. It has a risk map that can be sorted.
| Asset | Current practice | Main risk | 90-day action |
|---|---|---|---|
| Public API TLS | Cloud load balancer, ECDHE plus ECDSA/RSA certs | Long-lived sensitive traffic may be collected; middlebox compatibility unknown | Pilot hybrid KEM on an internal entry point, not full public traffic |
| Mobile app signing | Android/iOS platform signing plus internal package signing | Old verification chains may live for years; offline verification changes slowly | Document the signing chain and rehearse dual signing |
| Backup encryption | AES data keys; RSA wrapping for historical archive keys | Backups have 7 to 10-year secrecy periods, a classic harvest-now-decrypt-later case | Make new backups use a pluggable envelope; plan phased re-encryption for old backups |
| Partner API signatures | RSA-SHA256 request-level signatures | External migration is slow, protocol is hard-coded, verification failures are opaque | Design v3 signature protocol and support dual verification first |
| Internal mTLS | Managed by service mesh | Clients are relatively uniform, but traffic is high | Choose a low-risk internal link for handshake-observability pilot |
This table is more valuable than picking algorithms early. It turns the meeting from “Should we switch to ML-KEM?” into “Which real path is worth proving in 90 days?”
Different Cryptographic Uses Need Different Migration Speeds
Northwind Notes divides assets into four groups: key exchange, transport-layer certificates, encryption at rest, and digital signatures. All relate to post-quantum work, but the migration difficulties differ.
TLS and mTLS depend on protocol stacks, clients, gateways, and middleboxes. A pilot can use hybrid key exchange, and failures can be routed back to classical settings. It is risky, but at least it is an online system with canarying and observability.
Backup encryption looks simpler because browser compatibility is not involved. The actual difficulty is the secrecy period. Backups are cold, but their value lasts. If an attacker obtains ciphertext today and can decrypt it years later, the business loss still happens. This scenario deserves earlier attention than many short-lived public sessions.
Mobile app signing and partner signatures are a different kind of trouble. There are many verifiers, and verification may happen years later. An app package, an audit archive, or a contract-callback log may still need to be validated long after signing. Signature migration cannot only ask “Can we verify it today?” It must ask “How will we prove the signature was valid later?” That introduces dual signatures, timestamps, legacy certificate retention, and verifier-version management.
The team first considers using public TLS as the 90-day pilot. It drops the idea for a practical reason: public clients are uncontrollable, the cloud provider’s production support for hybrid KEM is still on the roadmap, and forcing it would turn the project into vendor waiting. Instead, it runs two tracks in parallel:
| Pilot track | Why it was chosen | Why it is not the final state |
|---|---|---|
| Internal backup-envelope refactor | Long data secrecy, controlled path, real reduction of long-term risk | Covers only new backups; old backups still need phased treatment |
| Partner-signature v3 dual verification | Represents the hardest external-coordination problem | Within 90 days, only two sandbox partners join |
This is steadier than choosing the flashiest new algorithm. One pilot validates crypto agility for data at rest. The other validates external compatibility for signature migration. TLS remains on the roadmap, but it is no longer forced into the 90-day window.
Backup Track: Pull the Algorithm Out of the Code
The historical backup system is simple: every day it exports database and object-store snapshots, encrypts them with a random AES-256-GCM data key, wraps the data key with an RSA public key, and stores the ciphertext, wrapped key, and metadata in an archive bucket.
The old metadata looks like this:
{
"version": "backup.v1",
"data_cipher": "AES-256-GCM",
"key_wrap": "RSA-OAEP-SHA256",
"wrapped_data_key": "base64...",
"nonce": "base64...",
"created_at": "2026-01-04T03:10:12Z"
}
The problem is not AES. The problem is that key wrapping is hard-coded as RSA. Worse, the restore tool makes the same assumption: if it sees backup.v1, it unwraps with RSA-OAEP. Before using PQC or hybrid wrapping, the first step is not changing algorithms. It is versioning the envelope.
The second version changes the metadata to this shape:
{
"version": "backup.v2",
"data_cipher": "AES-256-GCM",
"key_envelopes": [
{
"id": "rsa-2026-q1",
"type": "rsa-oaep-sha256",
"recipient": "backup-rsa-prod-01",
"wrapped_data_key": "base64..."
},
{
"id": "mlkem-2026-q1",
"type": "ml-kem-768-hybrid-x25519",
"recipient": "backup-pqc-pilot-01",
"encapsulated_key": "base64...",
"wrapped_data_key": "base64..."
}
],
"nonce": "base64...",
"aad": {
"tenant_region": "us-east-1",
"retention_class": "seven_years"
},
"created_at": "2026-03-18T03:10:12Z"
}
The field names are not the important part. The design principles are: keep symmetric encryption for data; allow multiple key envelopes to coexist; let the restore tool choose the envelope it can use; give every envelope a version, recipient, and audit trail. Even if the first PQC path must be rolled back, the encrypted backup payload remains intact.
The configuration also moves out of application code:
backup_crypto_profile:
name: seven-year-retention-pilot
applies_to:
retention_class: seven_years
regions:
- us-east-1
data_cipher: AES-256-GCM
envelopes:
- id: rsa-2026-q1
type: rsa-oaep-sha256
required_for_restore: true
recipient_key_ref: kms://backup/rsa/prod-01
- id: pqc-hybrid-2026-q1
type: ml-kem-768-hybrid-x25519
required_for_restore: false
recipient_key_ref: kms://backup/pqc/pilot-01
rollout:
mode: shadow
tenant_percent: 5
stop_on_restore_failure: true
In the first phase, mode: shadow means that the system writes the new envelope but still treats the RSA envelope as the primary restore path. A background job regularly performs restore drills with the PQC envelope. The system produces real data without betting production recovery on the new path.
This design also leaves room for later steps. After enough successful runtime, required_for_restore can change. When vendors and auditors are ready, the RSA envelope can move from “primary path” to “compatibility path.” Old backups can be re-encrypted by secrecy period instead of through one dangerous big-bang migration.
First Backup Failure: The Restore Tool Was Fine; The Runbook Was Wrong
On day 32, the team runs its first restore drill. Encryption and decryption unit tests pass. Shadow restore also works. The drill still fails for a small reason: the on-call runbook says “copy wrapped_data_key into the restore command,” but the new format has multiple envelopes. The engineer copies the RSA envelope field while selecting the PQC profile in the new restore tool. The tool throws a low-level “recipient mismatch.”
This is a typical migration failure. The algorithm implementation is not broken. The surrounding operational assumptions are. The tool error messages, runbooks, audit fields, and dashboards all assume “there is one key-wrapping method.” When the format becomes multi-envelope, those assumptions surface together.
The team makes three small changes:
backupctl restore \
--archive s3://backup-archive/2026/03/18/tenant-1042.snapshot \
--crypto-profile seven-year-retention-pilot \
--envelope auto \
--dry-run
--envelope auto lists available envelopes and explains why one was selected. --dry-run validates metadata and key-access permissions without restoring the database. The error message also changes from a low-level exception to an operator-facing explanation:
cannot use envelope pqc-hybrid-2026-q1:
plugin can read archive metadata, but KMS recipient backup-pqc-pilot-01 is not granted to this restore role.
suggestion:
run with --envelope rsa-2026-q1, or request restore role backup-pqc-restore-pilot.
This is not a cryptographic breakthrough, but it determines whether the pilot can enter production. Security projects often underestimate these human-machine interfaces. A real rollback and restore path is not the arrow in an architecture diagram. It is whether the person on call at night can follow the runbook correctly.
Partner Signatures: Dual Verification Before Asking Everyone To Change
Partner APIs live in another world. Northwind Notes has an open callback endpoint where partners push approval-status changes. The old protocol looks roughly like this:
POST /partner/v2/callback HTTP/1.1
X-Partner-Id: acme-sandbox
X-Timestamp: 1771272000
X-Signature-Alg: RSA-SHA256
X-Signature: base64...
Content-Type: application/json
The signature string is:
method + "\n" +
path + "\n" +
timestamp + "\n" +
sha256(body)
This protocol has old problems: the algorithm field is just a string; there is no version negotiation; partner public-key rotation happens through manual email; error logs only say “signature verification failed”; and SDKs contain private ordering logic for some partners. PQC simply shines light on these problems.
The new protocol does not ask partners to switch to post-quantum signatures immediately. The team first designs a v3 manifest where partners declare supported signature capabilities, and the host supports dual verification plus gradual enforcement.
{
"partner_id": "partner-sandbox-17",
"protocol": "northwind.partner-signature.v3",
"valid_from": "2026-04-01T00:00:00Z",
"keys": [
{
"kid": "rsa-2026-01",
"alg": "rsa-pss-sha256",
"use": "verify",
"public_key": "pem-or-jwk-ref",
"expires_at": "2026-12-31T23:59:59Z"
},
{
"kid": "mldsa-pilot-01",
"alg": "ml-dsa-65",
"use": "verify",
"public_key": "jwk-ref",
"expires_at": "2026-09-30T23:59:59Z",
"pilot": true
}
],
"policy": {
"accept": "rsa_or_pqc",
"require_timestamp_skew_seconds": 300,
"log_signature_material": false
}
}
Request headers also support multiple signatures:
X-Partner-Id: partner-sandbox-17
X-Signature-Input: method path timestamp body-sha256
X-Signature-RSA: kid=rsa-2026-01; sig=base64...
X-Signature-PQC: kid=mldsa-pilot-01; sig=base64...
During the pilot, the policy is rsa_or_pqc. If RSA verification passes, the request is still accepted; the PQC verification result goes into logs and dashboards but does not affect business. After two sandbox partners meet the success target for 30 consecutive days, one low-risk endpoint can move to rsa_and_pqc, requiring both signatures. Only much later should some new endpoints accept only PQC or hybrid signatures.
A subtle point matters here: dual signing is not just adding a field. The signature input must be identical. Error codes must say which signature failed. Replay protection must not become ambiguous because two signatures exist. SDKs must be able to generate test vectors. Otherwise partners only see 401, and both sides start pasting logs into chat.
Partner Failure Sample: Signature Failure Is Not One Error
On day 51, a sandbox partner connects v3 and the PQC signature keeps failing. At first everyone suspects incompatible algorithm libraries. The actual issue is path normalization. The partner SDK signs /callback; the gateway verifies /partner/v3/callback. The old RSA SDK had the same problem, but the old service silently rewrote the path before verification, and nobody wrote that behavior down.
The team splits signature errors into finer categories:
| Error code | Meaning | Counts as algorithm failure? |
|---|---|---|
signature.input_mismatch | The two sides used different signature input | No, protocol-integration issue |
signature.key_not_found | No public key for the given kid | No, configuration or rotation issue |
signature.unsupported_alg | Host does not support the algorithm | Yes, capability-negotiation issue |
signature.verify_failed | Input and key match, but verification fails | Yes, implementation issue or tampering |
signature.clock_skew | Timestamp outside the allowed window | No, replay-protection issue |
It also creates public test vectors for SDKs and partner documentation:
{
"case": "v3-path-normalization",
"method": "POST",
"path": "/partner/v3/callback",
"timestamp": "1771272000",
"body_sha256": "d4735e3a265e16eee03f59718b9b5d03...",
"signature_input": "POST\n/partner/v3/callback\n1771272000\nd4735e3a...",
"expected": {
"rsa_pss_sha256": "base64...",
"ml_dsa_65": "base64..."
}
}
The vectors contain no real partner names, keys, or request bodies. They preserve protocol behavior. Their job is to turn “maybe your library is incompatible” into locally reproducible input and output.
TLS Did Not Disappear; It Returned To Its Proper Place
This article is not saying TLS is unimportant. Northwind Notes also inventories TLS. It simply does not treat TLS as the only battlefield for the 90-day pilot.
The TLS inventory includes:
| Item | Checks |
|---|---|
| Public domains | Certificate algorithm, certificate chain, expiry, CDN and load-balancer support roadmap |
| Internal mTLS | Service-mesh version, sidecar update rhythm, failure metrics |
| Client distribution | Browsers, mobile apps, old SDKs, enterprise proxies |
| Middleboxes | Whether WAF, API gateway, or enterprise proxy drops unknown extensions |
| Observability | Whether handshake failures can be grouped by protocol, cipher, and client version |
On day 60, the team chooses one internal path for early research: mTLS from the document service to the audit-archive service. Traffic is stable, clients are controlled sidecars, data has long-term audit value, and failures can fall back to the old target.
The pilot configuration does not go straight to “all PQC.” It has explicit switches:
mesh_tls_policy:
service: audit-archive
client_selector:
namespace: docs
key_exchange:
mode: hybrid
classical: x25519
post_quantum: ml-kem-768
rollout:
percent: 10
fallback: classical_only
abort_if:
handshake_error_rate_gt: 0.2%
p95_handshake_ms_increase_gt: 15
telemetry:
label_algorithm: true
label_client_version: true
The valuable part is telemetry. Previously, a handshake failure was just a TLS error. Now it can be grouped by sidecar version, negotiation mode, and error family. Without that dimension, a hybrid-mode incident can only be handled by global rollback.
Testing: Do Not Only Test Encryption and Decryption
Northwind Notes splits testing into four layers. Algorithm correctness is only the first layer; the other three are closer to engineering reality.
| Test layer | Backup example | Signature example | TLS example |
|---|---|---|---|
| Correctness | The same backup restores through both RSA and PQC envelopes | Test vectors verify successfully | Sidecar completes hybrid handshake |
| Compatibility | Old restore tool rejects new format with a clear error | v2 partners are unaffected by v3 manifests | Old sidecars automatically use classical mode |
| Fault injection | RSA restore works when PQC KMS key is unavailable | Expired kid, inconsistent path, clock skew | Middlebox drops extension; handshake timeout |
| Operational drill | On-call restores a sample from the runbook | Partner generates signature locally from docs | SRE triggers rollback from metrics |
Not everything is declared “complete only if fully automated.” Some tests must be automated, such as test vectors and restore dry-runs. Some tests need people, especially runbook drills. Crypto migration changes operations, and people are part of the system.
The weekly review watches four metrics:
| Metric | Target |
|---|---|
| Owner coverage for high-priority crypto assets | 100% |
| New backup v2 envelope write success | Above 99.99% |
| PQC shadow-restore success | 100% for 14 consecutive days |
| Classifiable v3 signature errors | Above 95% |
There is no large “percentage migrated” number because a 90-day pilot should not pretend to complete a company-wide migration. It must prove that assets can be seen, failures can be explained, and paths can be rolled back.
Rollout And Rollback: Migration Must Allow Failure Without Incident
The rollout plan is conservative.
Backup track:
| Stage | Behavior | Rollback |
|---|---|---|
| 0 | Read only old v1; do not write new format | No rollback needed |
| 1 | 5% tenants write v2; RSA envelope remains the primary restore path | Stop writing v2; new backups return to v1 |
| 2 | Write v2 for all tenants; background shadow-restore through PQC envelope | Disable PQC envelope; keep RSA |
| 3 | Selected tenants’ restore drills prefer PQC envelope | Restore command switches back to RSA envelope |
Partner-signature track:
| Stage | Behavior | Rollback |
|---|---|---|
| 0 | v3 manifest registered only in sandbox | Delete manifest; v2 unaffected |
| 1 | Production accepts v2; v3 dual signature only records results | Disable v3 verification worker |
| 2 | Two low-risk partners use rsa_or_pqc | Change partner policy back to rsa_only |
| 3 | One low-risk endpoint uses rsa_and_pqc | Revert policy and retain failure samples |
The core idea is that every step can return to a known-safe classical path while preserving observability. Rollback is not admitting defeat. It is part of the pilot design. Without rollback, crypto migration encourages delay because nobody wants to own a full-site verification failure.
What Was Actually Delivered After 90 Days
On day 90, Northwind Notes does not announce “we have completed post-quantum migration.” That is the mature conclusion. It delivers a set of simpler, more sustainable artifacts:
| Deliverable | Value |
|---|---|
| 72 crypto assets inventoried, 28 high priority | Budgeting and scheduling now have evidence |
| Backup v2 envelope format and restore tooling | Long-secrecy data now has a migration entry point |
| Partner-signature v3 protocol, manifest, and SDK test vectors | External coordination becomes a contract, not a chat thread |
| mTLS hybrid-handshake telemetry fields | Future TLS pilots can explain failures |
| Vendor issue list | HSM, KMS, CDN, and mobile-platform support timelines are recorded |
| Rollout and rollback templates | Other systems can reuse the same change pattern |
Many things remain unfinished. Old backups are not fully re-encrypted. Public TLS does not enable PQC. Mobile app signing is still at dual-signing rehearsal. Only two sandbox partners have joined. None of that is embarrassing. The 90-day target solves “how do we start?” It does not swallow several years of work.
The most valuable output is a set of engineering habits:
| Habit | Why it matters |
|---|---|
| Do not hard-code algorithms in business code | Standards, libraries, and vendors will change |
| Give every key and certificate an owner | Unowned crypto assets are the most dangerous |
| Support dual tracks for new protocols | External ecosystems need migration buffers |
| Classify errors | A plain “failure” cannot drive collaboration |
| Update runbooks with tools | Migration affects real operations, not just code |
That is what I mean by crypto agility. It is not a framework name or a vendor product. It is an organizational ability: know where cryptography is used, know who owns it, know how to test, and know how to retreat.
A Small Checklist To Start With
If you are starting a similar program, these questions are better than “When can we fully switch to PQC?”
| Question | If you cannot answer it |
|---|---|
| Which data remains valuable five years after being stolen today? | Risk prioritization is not established |
| Which signatures must remain verifiable years later? | Signature migration ignores long-term evidence |
| Which cryptographic capabilities depend on vendors? | The timeline is not purely internal |
| Which algorithms are hard-coded in business repositories? | Migration will become repository-by-repository surgery |
| Which failures currently show only 401 or TLS error? | Compatibility issues will be hard to diagnose after rollout |
| Is there one real link that can be piloted and rolled back quickly? | The project may stay in documents |
Post-quantum cryptography absolutely has algorithmic problems. But inside a company, post-quantum migration first becomes an asset problem, a protocol problem, a rollout problem, and a coordination problem. The best next step is not replacing RSA everywhere, nor waiting until a practical quantum computer forces a meeting. It is exposing crypto debt and running one real path.
When large-scale migration eventually becomes necessary, a prepared team will not search repositories for signing functions at the last minute. It will open the inventory, adjust the profile, canary the change, watch metrics, and roll back if needed. That is the part worth building early.
Follow ZiCode on WeChat
If this post was useful, you can follow later updates on WeChat as well.
X / Twitter
Follow @ax2_zicode
Faster technical notes, short thoughts, and new-post alerts are posted on X.