QERYX COMING SOON
OBSERVATORY

02 Science — authenticated encryption

Break the seal. Then try to forge it.

Real ChaCha20-Poly1305 runs in this page. Decrypt a message, seal your own, flip one byte — and watch Poly1305 refuse the entire frame.

Real WASM crypto ChaCha20-Poly1305 · RFC 8439

Tap to decrypt.

This is a real ciphertext, sealed with the same primitive family the app uses. Your tap runs the actual open() — the resolve you see is the plaintext arriving.

rerun open() on a corrupted frame

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 crypto-core module itself.

Seal your own. Then attack it.

Write anything — it never leaves this page. Seal it under a fresh 256-bit key, decrypt it back, then flip a single byte and watch the authenticator refuse the whole message rather than return a garbled copy.

Sealed and opened entirely in this page. Nothing leaves your browser.

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 crypto-core module itself.

Read the construction under your thumb.

Two primitives, one composition: ChaCha20 turns a key and nonce into keystream; Poly1305 makes the ciphertext unforgeable. RFC 8439 fixes how they combine.

chacha20 quarter-round — RFC 8439 §2.1
a += b;  d ^= a;  d <<<= 16;
c += d;  b ^= c;  b <<<= 12;
a += b;  d ^= a;  d <<<=  8;
c += d;  b ^= c;  b <<<=  7;

Add, rotate, xor — on a 4×4 state of 32-bit words, 20 rounds per block. Three operations every CPU runs in constant time: no lookup tables, no data-dependent branches, nothing for a timing attack to read.

poly1305 — a one-time authenticator
tag = ( ( m₁·rⁿ + m₂·rⁿ⁻¹ + … + mₙ·r )  mod 2¹³⁰ − 5 ) + s   mod 2¹²⁸

The message is read as coefficients of a polynomial, evaluated at a secret point r over the prime field 2¹³⁰−5, then offset by s. Both r and s are used for exactly one message.

the AEAD composition — RFC 8439 §2.8
(r, s)  = first ChaCha20 keystream block of (key, nonce)
ct      = plaintext ⊕ ChaCha20(key, nonce, counter ≥ 1)
tag     = Poly1305( r, s,  AAD ‖ ct ‖ len(AAD) ‖ len(ct) )

open(): recompute tag, compare in constant time — refuse on any mismatch

The tag covers the ciphertext and the associated data. open() verifies before it decrypts — a tampered frame yields a refusal, never plaintext.

ChaCha20-Poly1305 parameters
ParameterValueSource
Key 256 bits RFC 8439
Nonce 96 bits RFC 8439
Tag 128 bits RFC 8439
Cipher rounds 20 RFC 8439
Poly1305 field prime 2¹³⁰ − 5 RFC 8439

Garbled output leaks structure and creates oracles: an attacker who can watch you process corrupted plaintext learns things. An AEAD returns exactly two outcomes — the true plaintext, or a refusal. The flip-a-byte demo above is that property, live.

Everything. Two messages under the same (key, nonce) share keystream, which exposes their xor — and reuse also exposes the Poly1305 key, enabling forgeries. This is the sharpest edge of the construction; the demo draws a fresh random key and nonce for every seal, and QERYX derives fresh message keys through Q-Ratchet so the pair never repeats.

ChaCha20 runs in constant time on every CPU, with no lookup tables and no special instructions required — the same speed and the same timing profile on a flagship phone and a ten-year-old handset. The AES-256-GCM suite is implemented and reserved as suite 0x0002.

  • Y. Nir, A. Langley, "ChaCha20 and Poly1305 for IETF Protocols," RFC 8439 (2018)
  • D. J. Bernstein, "ChaCha, a variant of Salsa20" (2008)
  • D. J. Bernstein, "The Poly1305-AES message-authentication code," FSE (2005)

Follow the seal into the protocol.

Every message frame is sealed with ChaCha20-Poly1305 (RFC 8439) under a key that exists for that message alone — Q-Ratchet derives a fresh one each time from the X25519 + ML-KEM-1024 hybrid agreement.

The relay carries the sealed frame and cannot open it: the key never leaves the two devices. What you just did to a tampered byte is what every QERYX client does to a tampered message — refuse it whole.

The honest questions.

Is this demo the app's crypto?

It is real ChaCha20-Poly1305 (RFC 8439), compiled from Rust to WebAssembly and self-tested against the RFC's test vector before first use — the same primitive family as the QERYX protocol, as a demo build rather than the crypto-core module itself. The label under each demo states exactly this.

Your message never leaves the page. The only request the page makes is for the WebAssembly module itself — one same-origin file, fetched once; the key, the nonce, the plaintext and the ciphertext are created and destroyed in this tab.

The same refusal as a flipped byte: Poly1305 recomputes a tag that cannot match, and open() rejects before a single block is decrypted. Wrong key, wrong nonce, tampered ciphertext, tampered associated data — one indistinguishable refusal for all four.