@zanii/pq
v0.1.0
Published
Post-quantum migration rails - hybrid dual-signing (Ed25519 + ML-DSA-65, both must verify), a PQ key binding signed by BOTH keys (proves possession of each), and a transition record anchored into the Merkle log so the binding provably predates any future
Readme
@zanii/pq
Post-quantum migration rails: dual-sign today, stay provable after Ed25519. Hybrid Ed25519 + ML-DSA-65 (FIPS 204) — the boring package that becomes mandatory the day a regulator says "post-quantum", and the government/health verticals ask early.
npm install @zanii/pq @zanii/coreimport { generatePqKeypair, bindPqKey, verifyPqBinding, dualSign, verifyDual, transitionPayload, verifyTransition } from '@zanii/pq';
// 1. Bind an ML-DSA-65 key to the agent's did:key — BOTH keys sign the same body,
// so possession of both is proven; one key alone cannot forge the binding.
const pq = generatePqKeypair();
const binding = bindPqKey({ did: agent.did, pqPublicKey: pq.publicKey, ts: now }, agent.privateKey, pq.secretKey);
verifyPqBinding(binding); // { ok, reasons }
// 2. Dual-sign anything — verification requires BOTH signatures.
const sigs = dualSign(doc, agent.privateKey, pq.secretKey);
verifyDual(doc, agent.did, binding, sigs); // missing ML-DSA = failure, never a fallback
// 3. Anchor the binding into the log NOW (unsalted — it's public by design):
await zanii.record({ target: 'pq.transition', payload: transitionPayload(binding), salt: false });
// later, prove the binding predates any Ed25519 break:
verifyTransition(binding, receipt, { sth, index, proof });Python (pip install "zanii[pq]"): from zanii.pq import bind_pq_key, dual_sign, verify_dual, ...
— cross-language verified: a Python-signed binding verifies in TypeScript
(@noble/post-quantum) and vice versa; both implement final FIPS 204.
Why the transition record is the point
The Merkle log is SHA-256, which no known quantum algorithm breaks in any practical sense. So anchoring the binding now buys the thing that matters later: when Ed25519 falls, an Ed25519 signature that was provably included in an anchored tree before the break is still evidence of when it was made — the same signature made after the break proves nothing. "This PQ key was this agent's key all along" survives the event it defends against.
The limit, stated up front
Pre-migration receipts are not re-signed and never will be — their post-quantum
protection is the anchored timestamp, not the signature. And hybrid is only hybrid
when the verifier demands both signatures: verifyDual refuses a missing ML-DSA
signature rather than quietly falling back to Ed25519-only, because a hybrid that
degrades silently isn't one.
