- Docs
- Bureau — Blue Team (defensive)
- QKD-Witness
Bureau — Blue Team (defensive)
QKD-Witness
Quantum key distribution lets two parties share a key whose secrecy depends on the channel's error rate staying below a threshold. QKD-Witness signs each session's error rate and tracks an operator's migration from classical signatures (Ed25519, RSA) to post-quantum signatures (ML-DSA).
Posture: 🔵 Blue Team (defensive) · Status: alpha (moonshot)
What it does
Quantum Key Distribution (QKD) uses single-photon physics to share a secret key between two parties. The canonical protocol is BB84 (Bennett & Brassard, 1984). It outputs a (key, error rate) pair. The error rate is called QBER (Quantum Bit Error Rate), and the canonical security threshold is 11%: at QBER ≤ 11%, error correction and privacy amplification recover an unconditionally-secret key. Above 11%, an eavesdropper's mutual information is too high and the session must be aborted. There is no industry standard today that attests "this key came from a non-tampered BB84 channel at QBER < 11%." QKD-Witness mints that statement.
The package also tracks the world's quiet migration off classical signatures. Today, almost every signature in production is Ed25519 or RSA – both broken by a future quantum computer running Shor's algorithm. NIST released the FIPS-204 post-quantum signature standard in 2024 (algorithm name ML-DSA). Operators sign migration events as they transition from ed25519 → ml-dsa-65, and QKD-Witness emits a harvest-then-decrypt proof when an asset was harvested by an adversary before the operator migrated – meaning the asset was classical-encrypted when grabbed, regardless of any later "we're post-quantum now" claim.
Who would use it
- A bank, telco, or government agency running QKD links (a US bank piloting IDQ Clavis, a Chinese state telco using Toshiba MU200) who needs an audit trail showing each session was secure at distribution time.
- A CISO running the Ed25519 → ML-DSA-65 migration who needs a public signed timeline of when assets crossed the post-quantum threshold.
- A national-security incident responder investigating a "harvest now, decrypt later" exposure who needs to prove which assets predated migration.
- A NIST FIPS-140 evaluator auditing whether a vendor's QKD device actually maintained the QBER margin it claimed during certification.
What you'll need
- The Pluck CLI installed (
npm i -g @sizls/pluck-cli). - For real deployment: a commercial BB84 device (IDQ Clavis³, Toshiba MU200, Qubitekk QC2 – the alpha simulates these). The hardware bridge plumbing is per-vendor.
- A migration plan if you're going to use the migration ledger – what's your target algorithm (
ml-dsa-65is the alpha's canonical pick), and what assets need re-signing.
Step-by-step
pluck bureau qkd-witness demo
The demo synthesizes three QKD sessions (one valid at 5% QBER, one bad at 18% QBER, and an equivocation pair where the same hardware fingerprint signs two different keys for the same instant), one Ed25519 → ML-DSA-65 migration event, and one harvest marker dated 10 days before the migration.
qkd-witness/demo: ingesting 3 QkdSessions + 1 MigrationEvent + 1 HarvestNowDecryptLaterMarker -> 3 QkdProofs.
[Bureau/QKD-WITNESS] proof=518c9b88… kind=qber-too-high
[Bureau/QKD-WITNESS] proof=8cdc73da… kind=session-equivocation
[Bureau/QKD-WITNESS] proof=5c95adda… kind=harvest-then-decrypt
Production CLI (capture from the QKD device, migrate to record an algorithm transition, harvest to mark a known-grabbed asset, verify to check a published session) lands in a follow-up.
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-qkd-witness @sizls/pluck-bureau-core tsx). The example uses the deterministic readQkdSession stub that ships with the package – real BB84 hardware integration lands in a follow-up.
// index.ts
import { createHash } from "node:crypto";
import {
createQkdWitnessSystem,
fingerprintPrivateKey,
readQkdSession,
signCanonicalBody,
} from "@sizls/pluck-bureau-qkd-witness";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
const sha256 = (s: string) => createHash("sha256").update(s).digest("hex");
const flush = (n = 60) => new Promise<void>((r) => { let i = 0; const tick = () => (++i >= n ? r() : setImmediate(tick)); setImmediate(tick); });
async function main() {
const op = generateOperatorKey();
const opFp = fingerprintPrivateKey(op.privateKeyPem);
const system = createQkdWitnessSystem({
signingKey: op.privateKeyPem,
disablePausePoll: true,
disableLogging: true,
});
// Stub returns deterministic QBER 0.18 – strictly above 11% threshold.
const reading = readQkdSession({ label: "high-qber", qber: 0.18, hardwareLabel: "qkd-device-B" });
const sessionBody = {
schemaVersion: 1 as const,
keyDigest: reading.keyDigest, qber: reading.qber,
hardwareFingerprint: reading.hardwareFingerprint,
operatorFingerprint: opFp,
observedAt: "2026-04-15T10:30:00.000Z",
};
const sessionId = sha256(JSON.stringify(sessionBody));
const { signature } = signCanonicalBody({ ...sessionBody, sessionId }, op.privateKeyPem);
try {
system.observeSession({ ...sessionBody, sessionId, signature });
await flush();
for (const p of system.facts.proofs()) {
console.log(`proof kind=${p.kind} id=${p.proofId.slice(0, 16)}…`);
}
} finally {
await system.shutdown();
}
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
proof kind=qber-too-high id=…
▶ Open in StackBlitz – runs in your browser, no install required.
What you get
A QkdSession envelope per BB84 session containing the key digest (NEVER the key bytes – just sha256 of the key), the QBER, the hardware fingerprint of the BB84 device, and an operator signature. A MigrationEvent per algorithm transition containing the new ML-DSA Subject Public Key digest and timestamp. All Rekor-anchored.
Three classes of red-team proof:
qber-too-high– observed QBER > 11% threshold; channel must be aborted.session-equivocation– the same hardware fingerprint signed two distinct keys for the same instant.harvest-then-decrypt– a harvest marker timestamp precedes the operator's migration. The asset was classical-encrypted when grabbed.
What it can't do
- Real BB84 hardware integration is deferred – the alpha's
readQkdSession()returns deterministic test data. Production deployment requires a per-vendor hardware bridge. - BB84-specific. If your QKD hardware runs E91, BB92, COW, DPS, or measurement-device-independent QKD, the 11% threshold and proof shapes need rewiring. Alpha hard-codes BB84.
- ML-DSA-65 is the alpha's canonical post-quantum target. Other parameter sets (ML-DSA-44, ML-DSA-87, SLH-DSA) are accepted but not exhaustively tested.
- Real Sigstore Rekor
notarizeintegration is stubbed. - Sift rate (raw bits → key bits ratio after basis disclosure) is not committed to the signed body. A NIST evaluator would require this for full BB84 attestation; alpha scope omits it.
- Cannot retroactively un-harvest a classical-encrypted asset. If an adversary already grabbed your TLS-encrypted traffic in 2022, no migration event in 2027 saves you. The proof exists to make that liability legible.
A real-world example
A regional bank deploys an IDQ Clavis³ QKD link between two data centers in 2027. Every session is signed by QKD-Witness with the QBER and the device's hardware fingerprint. In Q3, an internal audit identifies two sessions where the QBER reached 14%; a qber-too-high proof exists for each, and the audit trail shows the operators aborted both sessions as the protocol requires. Two years later, an adversary discloses having captured encrypted traffic between the bank's data centers in 2024, before migration. Because the bank had previously published a harvest-then-decrypt proof to Rekor for affected assets, the timing relationship between the harvest and the migration event is a matter of public record rather than something the bank must reconstruct from internal logs.
For developers
Predicate URIs
| URI | What it attests |
|---|---|
https://pluck.run/QkdWitness.Session/v1 | BB84 session at hardware H produced key with digest D and QBER Q at time T. |
https://pluck.run/QkdWitness.Migration/v1 | Operator O migrated from algorithm A to algorithm B, new SPKI digest D, at time T. |
https://pluck.run/QkdWitness.HarvestMarker/v1 | Operator O acknowledges asset D was harvested at time T. |
https://pluck.run/QkdWitness.Proof/v1 | Class: qber-too-high | session-equivocation | harvest-then-decrypt. |
The signed body never carries the QKD key bytes (only the digest), the harvested asset bytes (only the digest), or any cleartext from the BB84 channel.
Programs composed
- ROTATE – operator-key rotation across the Ed25519 → ML-DSA migration boundary.
- Custody – cryptographic anchoring of the migration timeline.
- Pluck core's DSSE in-toto envelopes + Sigstore Rekor client.
- Directive's facts/constraints/resolvers.
Threat model + adversary
The adversary is a state actor stockpiling classical-encrypted traffic for future quantum decryption ("harvest now, decrypt later"). The protocol does not stop the harvest – it makes the harvest's pre-migration timing publicly attestable, so the operator's later "we migrated in time" claim can be falsified.
What's stubbed (alpha – moonshot)
- Real BB84 hardware (IDQ Clavis³, Toshiba MU200, Qubitekk QC2) bridge deferred.
dsseSign/notarizeAttestationRekor integration stubbed.- Non-BB84 QKD protocols (E91, BB92, COW, DPS, MDI-QKD) deferred.
- Exhaustive ML-DSA-44 / ML-DSA-87 / SLH-DSA testing deferred.
Verify a published cassette
pluck bureau verify <bundle-dir>
cosign verify-blob --key <pubkey.pem> --signature <sig> \
--type https://pluck.run/QkdWitness.Session/v1 <body.json>
See also
- Bureau Foundations
- Threat Model
- Verify a dossier
- ROTATE – algorithm migration
- Custody – timeline anchoring
- BB84 protocol (Bennett & Brassard 1984)
- NIST FIPS 204 (ML-DSA)