Post-quantum (Hybrid ML-KEM-768) identity option #28
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
age v1.3.0+ ships built-in hybrid ML-KEM-768 + X25519 post-quantum keys (
age-keygen -pq). The Go library (which we already pin at v1.3.1) exportsGenerateHybridIdentity(),ParseHybridIdentity(),ParseHybridRecipient();HybridRecipient/HybridIdentitysatisfy the sameage.Recipient/age.Identityinterfaces ourKeyProviderreturns — so this is a type swap, not a rewrite.HybridRecipientrefuses to mix with classical recipients (emits a "postquantum" label) → the recovery recipient must also be Hybrid.Corrects our earlier factual error ("age is not post-quantum"). Relevant to public-sector PQC-readiness asks. Sources: age README /
pq.go.Scoping note (2026-07-29, discussion with Nikola) — why this is gated on decisions, not effort.
What it is. All current age identities (Keycloak-profile user keys, deployment key, recovery key) are classical X25519. The threat that matters for storage is harvest-now-decrypt-later: an adversary who copies an encrypted bucket today decrypts it in 10–15 years — exactly the untrusted-third-party-bucket scenario this feature exists for. The fix is age's hybrid ML-KEM-768 + X25519 identity type (config toggle in age 1.3+, per ENCRYPTION-COMPARISON.md). Hybrid = protected by both algorithms, never weaker than today even if ML-KEM turns out flawed. ML-KEM-768 is NIST-standardized (FIPS 203) and in the Go stdlib since 1.24 — mature enough to ship.
The toggle is ~a day of work for a fresh instance. What is actually undecided:
Honest middle path if ever wanted early: a greenfield-only toggle — enabled only on instances that have never written encrypted data (refuse otherwise), no migration story, existing deployments untouched. Genuinely shippable in days and would support a "post-quantum option available" claim. Current decision is to keep it gated: the encryption layer is formally a probe awaiting external sign-off, and locking in an identity format before the custody/rotation decisions land is exactly the double-work the review exists to prevent.
Short version: the toggle is easy; the commitment it represents isn't.
Decision — deferred (Manuel's review, relayed by Nikola 2026-07-31):
Stays open, now explicitly blocked on #18 (group/space multi-recipient): the PQ toggle only makes sense once the multi-recipient model exists, so the hybrid ML-KEM-768 + X25519 interplay can be designed against real recipient sets rather than hypothetically. Revisit when #18 lands.
Shipped in v0.3.29 (PR #107). Nikola's research confirmed the unblocking fact: age wraps one file key into an independent stanza per recipient, so multi-recipient PQ (user+recovery, group spaces #18) needed no redesign — and we already pinned age v1.3.1, so zero new dependencies.
What shipped
storage.encryption.postQuantum: true→ all custody backends (keycloak-profile, openbao, deployment) mint hybrid ML-KEM-768+X25519 identities (AGE-SECRET-KEY-PQ-1…).recoveryIdentityLegacy/deploymentIdentityLegacykeep the old classical keys decrypt-only. A test proves age rejects a mixed recipient set.legacyIdentities). Verified: pre-PQ data decrypts, new data is unreadable by the classical key, hybrid recovery works, andpostQuantum: falserolls back cleanly to the retained classical identity.instanceSeed(#31) + PQ deployment identity refused at startup — HKDF derivation is X25519-only. Follow-up if anyone actually needs both.To flip the dogfood (.249) — your side, config secret edit:
age-keygen -pq→ new hybrid recovery keypair (password manager).cairn-enc-config:"postQuantum": true,recoveryIdentity= new hybrid key,recoveryIdentityLegacy= current classical recovery key.age -dwith the hybrid recovery key opens it and the header showsmlkem768x25519stanzas; confirm an old file still opens in the UI.Closing as shipped.