04 Science — the ratchet
The key that only turns forward.
A real derivation chain runs in this page. Step it with your thumb, watch a fresh key seal fresh bytes — then try to turn it backward.
01Feel — one-way time, as a shape
A chain where every link devours the last.
Each chain key births exactly two things: one message key, and the next chain key. Then it is destroyed. Hold the newest link and every older one is already gone — the chain gives you the future and burns the past.
The arrow is a hash function. Following it costs one call; reversing it costs a preimage search. That asymmetry is the whole machine.
02Touch — real SHA3-256 steps, real seals
Step it. Then try to go back.
Every press runs a real SHA3-256 derivation and seals a fresh message under the new key — while the old keys are zeroized in memory. Then tap an older link: the live key runs a real open() on it, and the seal refuses.
Real ChaCha20-Poly1305 (RFC 8439), compiled from Rust to WebAssembly, running in your browser. Same primitive family as the QERYX protocol; demo build, not the shipped crypto-core module. The chain steps are real SHA3-256 derivations under distinct labels — demo build, real derivation; the shipped Q-Ratchet advances its symmetric chain with HKDF-SHA-512.
03Understand — two labels, one chain key
Forward secrecy is a hash you cannot invert.
Compromise of the chain at time t does not reveal messages before t: every derivation flows forward only, under labels that separate chain keys from message keys, and per-message keys are zeroized immediately after use.
mk_n = SHA3-256( "MESSAGE-DERIVE" ‖ ck_n ) ck_n+1 = SHA3-256( "CHAIN-ADVANCE" ‖ ck_n ) forward: one hash call backward: a preimage search — ≈2²⁵⁶ work
Two distinct labels split one chain key into a message key and the next chain key — a leaked message key never reveals the chain, and the chain never reveals past message keys. The bench prefixes its labels with QX-DEMO-RATCHET/ so demo output can never collide with a protocol derivation.
ChainKey.advance():
new_ck = HKDF(ck, "CHAIN-ADVANCE", info = "CHAIN-KEY", 32)
msg_key = HKDF(ck, "MESSAGE-DERIVE", info = "MESSAGE-KEY", 32)
Same shape, production primitive: the shipped symmetric chain advances with HKDF-SHA-512 (RFC 5869) under the same two-label split, and intermediate key copies are explicitly zeroized before drop.
| Chain | Primitive | Cadence |
|---|---|---|
| 1 — Classical DH | X25519 | every message |
| 2 — Post-quantum KEM | ML-KEM-1024 | every 100 messages |
| 3 — Symmetric chain | HKDF-SHA-512 | every message |
| 4 — Entropy injection | certified QRNG | every 5 messages (text) |
What you stepped above is chain 3’s principle in isolation. The other three exist so that compromise of any one chain leaves the others standing — the full construction is in the Q-Ratchet paper.
Why can the chain never rewind?
Each step is a full hash of the previous key. Running SHA3-256 backward is a preimage search — FIPS 202 puts it at 2²⁵⁶ work for a 256-bit digest, beyond any published attack, classical or post-quantum. The bench’s refusal is that mathematics wearing a button.
What does a stolen live key actually read?
Everything after the theft, nothing before it — until the chain heals. The symmetric chain alone reads forward from the stolen state; in the full Q-Ratchet the next ML-KEM-1024 epoch and the next QRNG injection re-key from material the thief never had, cutting the forward view off as well. That healing property is post-compromise security, specified in the paper.
Is this bench the shipped ratchet?
It is one of its four chains, on the demo primitive: real SHA3-256 derivations and real ChaCha20-Poly1305 seals in the browser WASM build, where the shipped chain runs HKDF-SHA-512 inside crypto-core. The one-way property you just pressed is the same. The label under the bench states exactly this.
- NIST FIPS 202, "SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions" (2015)
- RFC 5869 — HMAC-based Extract-and-Expand Key Derivation Function (HKDF) (2010)
- RFC 8439 — ChaCha20 and Poly1305 for IETF Protocols (2018)
- QERYX Protocol Specification §6 — docs/SPEC/04-q-ratchet.md; implementation crypto-core/src/quadruple_ratchet.rs
04In QERYX — four parallel chains
Follow the chain into the protocol.
Every message you send rides a key that existed for that message alone. The Q-Ratchet derives it the way the bench does — forward only, distinct labels, immediate zeroization — from the X25519 + ML-KEM-1024 hybrid agreement, and advances four parallel chains so no single compromise opens the conversation.
A device seized today holds today’s chain head. The messages behind it were sealed under keys that no longer exist anywhere — and that the seized state cannot re-derive, for the reason your thumb just verified.
05The record — the questions we get asked hardest
The honest questions.
Is the derivation real?
Yes. The chain steps are SHA3-256 and the seals are ChaCha20-Poly1305 (RFC 8439), compiled from Rust to WebAssembly and self-tested against the RFC’s vector before first use — on any mismatch the bench refuses to run. The keys are demo keys, born in your browser from crypto.getRandomValues; the derivations on them are real.
Does anything leave this page?
No. Chain keys, message keys, nonces, and ciphertext live and die in this tab. The bench makes no network request of any kind.
If the math is one-way, why does the app need more than this chain?
Because a one-way chain protects the past, and only the past. An attacker holding the live state reads forward until the chain re-keys from fresh material — so the Q-Ratchet folds in a new ML-KEM-1024 encapsulation every epoch and fresh QRNG entropy every few messages. And no ratchet defends an unlocked device in an attacker’s hands; the limitations page names that plainly.