Pixieby Sociofabric

The End-to-End Layer: What the Relay Can Never See

Before we can talk about hiding who sent a message, or when, or how often, we have to be sure of the easy-sounding part: that the relay cannot read the message at all. "Easy- sounding" is doing a lot of work in that sentence.

This is the foundation article of the series. Every later piece (sealed sender, unlinkability, the envelope, deniability) assumes that the body of a message is opaque to the relay. Here is how that opacity is built, why the obvious version of it is not enough, and where the one part we did not build ourselves begins.


1. What "end-to-end" actually promises

End-to-end encryption (E2EE) means the keys that can decrypt a message live only on the two end devices (Alice's phone and Bob's phone) and never on the server between them. The relay ferries ciphertext it cannot open. If that were the whole story, this article would be one paragraph.

It is not the whole story, for three reasons that each turned into real engineering:

  1. The two phones are never online together. Alice wants to message Bob while Bob's phone is asleep. You cannot run an interactive key exchange with someone who isn't there. So the session setup has to be asynchronous: it must work while one side is away.
  2. Keys leak eventually. Phones get seized, backed up, malware'd. A good messenger assumes today's key will eventually be compromised and limits the blast radius: a stolen key must not unlock yesterday's messages, nor tomorrow's.
  3. "Encrypted" is not automatically "quantum-safe." The classic key exchange is Diffie–Hellman (each side combines its own private key with the other's public one and both land on the same shared secret, which never crosses the wire), and a quantum computer breaks it. A recorded conversation is a sitting duck for harvest-now-decrypt-later.

The messenger that solved (1) and (2) beautifully is Signal, and we use Signal's library, libsignal, for exactly this layer. Our job was to pick it, wire it correctly, insist on its post-quantum mode, and guard that insistence. Let us walk the three problems.


2. Talking to someone who isn't there: prekeys and PQXDH

To start a conversation with an offline Bob, Alice needs something of Bob's to encrypt to right now, without Bob's participation.

The mechanism is prekeys. When Bob first installs the app, his phone uploads a small stack of public keys to the server and goes to sleep. When Alice wants to message him, she downloads one of those prekeys, does a key agreement against it, and sends. Bob catches up later.

The server stores and hands out public keys only. It is a key vending machine, never a keyholder.

ALICE SERVER BOB ASLEEP · OFFLINE BOB'S PREKEYS · PUBLIC ONLY UPLOADED EARLIER 1 · ALICE FETCHES ONE PREKEY 2 · SEALS AND SENDS — BOB STILL ASLEEP CIPHERTEXT — THE SERVER CANNOT OPEN IT A KEY VENDING MACHINE, NEVER A KEYHOLDER — BOB SLEPT THROUGH IT ALL

The mental model is a wall of lockboxes with fresh padlocks. Bob leaves a stack of open padlocks at the front desk. Anyone can grab one, lock a message in a box, and leave it; only Bob has the matching key. He was asleep the whole time.

The classic version of this dance is Signal's X3DH ("Extended Triple Diffie–Hellman"): it mixes several Diffie–Hellman operations between Alice's and Bob's keys so that the resulting shared secret depends on both long-term identity keys and fresh per-session keys. That last part matters: it is what gives the session its initial forward secrecy, meaning a key stolen later cannot unlock what was already sent.

But every one of those operations is a Diffie–Hellman, and Diffie–Hellman is precisely what Shor's algorithm dismantles. Shor's algorithm is the quantum routine that solves the two math problems under most classical cryptography (factoring large integers and taking discrete logarithms) fast enough to make them useless as locks.

So we use the post-quantum upgrade, PQXDH, which adds a lattice key-encapsulation (ML-KEM) alongside the classical exchanges. A key-encapsulation mechanism, or KEM, is a one-shot way to ship a fresh shared secret to someone using nothing but their public key. This one rests on lattices (the difficulty of finding a short vector in a huge grid of points) rather than on discrete logarithms.

The session secret now depends on a problem believed hard for quantum computers and on the battle-tested classical one. That is a hybrid, so a break of either alone is survivable.

A recording of the setup no longer contains a Diffie–Hellman transcript (the byte-for-byte record of the exchange) that a future machine can unwind. (Signal deployed PQXDH in 2023; the specification is a clean read.)


3. A fresh key for every message: the Double Ratchet

Session setup gives Alice and Bob one shared secret. If they used it directly for every message forever, a single key theft would decrypt the entire history and future of the conversation. That is unacceptable, so the shared secret is only a seed.

From that seed, the Double Ratchet derives a brand-new key for every single message and throws the old ones away. Two properties fall out, and they are the reason the ratchet is famous:

The picture is a hotel that re-keys your door lock after every single entry, and derives each new key partly from a coin flip it makes inside the room where no one is watching. Steal one key card and you get exactly one door opening, in the past, once.

The "double" is because two ratchets turn together: a symmetric one that clicks forward on every message (cheap), and a Diffie–Hellman one that clicks when the direction of conversation changes (the healing step). The full mechanism is one of the most elegant constructions in applied cryptography, and Signal documents it far better than we could re-derive here: The Double Ratchet Algorithm.

ONE NEW KEY PER MESSAGE · OLD KEYS DELETED ALICE BOB K1 K2 K3 K4 K5 PAST KEYS GONE KEY STOLEN HEALED BOUNDARY FRESH RATCHET INPUT SYMMETRIC CLICK · EVERY MESSAGE FRESH INPUT · FUTURE RECOVERS ONE STOLEN KEY DOES NOT UNLOCK THE PAST OR THE HEALED FUTURE

4. The subtle gap: a ratchet that heals classically

Here is the wrinkle that is easy to miss, and it is the reason this article exists rather than just pointing at Signal's docs.

PQXDH makes the setup post-quantum. But the Double Ratchet's self-healing step (the part that re-injects fresh secrecy as the conversation continues) is a Diffie–Hellman.

That means a long conversation keeps folding in classical key material over time.

Why that matters: an attacker who has compromised a device and should be locked back out relies on the healing step to do the locking. If the healing is Diffie–Hellman, a quantum adversary follows along and never gets locked out.

Post-quantum setup with a classical ratchet is a house with a quantum-proof front door and ordinary windows.

The fix is SPQR, the Sparse Post-Quantum Ratchet, which threads lattice key-encapsulation material through the ongoing ratchet so the healing itself becomes quantum-safe.

"Sparse" is the engineering compromise at its heart. Running a full post-quantum KEM on every message would be bandwidth-prohibitive (lattice ciphertexts are kilobytes, not the 32 bytes of an elliptic-curve point), so SPQR spreads the post-quantum refresh across messages. That amortizes the cost, spreading it thinly over many messages, while still guaranteeing the conversation heals post-quantum on a bounded schedule.

It ships in libsignal 0.94.0 and up, and rides in a dedicated field of every steady-state message: every message after the initial session setup, which is nearly all of them.


5. The part that is ours: trust, but verify the ratchet

We did not build PQXDH, the Double Ratchet, or SPQR. We chose them, and then we did the thing a security engineer must do with a dependency this load-bearing: we refused to take it on faith.

The risk is quiet failure. A misconfiguration, a downgraded library version, or a bug could leave the post-quantum ratchet field absent from a steady-state message, and everything would still work perfectly, still encrypt, still deliver, while silently having dropped back to classical-only security. A recording made during that window would be harvest-now-decrypt-later bait, and nobody would notice.

So we added a small, mean guard.

On both the send and the receive path, before a steady-state message is allowed through, a tiny check walks the encrypted frame's structure and asserts that the post-quantum ratchet field is actually present. If it is missing, the message is rejected, fail-closed. We would rather drop a message than send one that lies about being quantum-safe.

A build-time floor pins the libsignal version at or above the release where SPQR is guaranteed, so a dependency downgrade fails the build rather than the users.

It is a dozen lines of code standing guard over the most important assumption in the system.

BUILD FLOOR · LIBSIGNAL WITH SPQR SEND / RECEIVE GUARD PRESENT? SEALED FRAME SPQR FIELD ALLOW PRESENT? SEALED FRAME FIELD MISSING REJECT · FAIL CLOSED NO CLASSICAL-ONLY FALLBACK · NO SILENT DOWNGRADE POST-QUANTUM IS A CHECKED INVARIANT, NOT A CONFIGURATION HOPE

This is the pattern for the whole series: where we consume someone else's excellent cryptography, we consume it and we build a fail-closed tripwire that screams if the guarantee ever silently lapses.


6. What this buys the rest of the series

From here on, every article gets to assume the message body is a sealed, post-quantum, forward-secret, self-healing blob that the relay cannot read and a future quantum computer cannot unwind from a recording. That is the floor.

But notice what it does not give us. Content encryption says nothing about who sent the blob, whether two blobs came from the same person, what kind of message it is, or whether the relay keeps a copy. Those are all metadata, all outside the seal, and all still fully visible to a naive relay. Peeling each of them away is the rest of the series, starting with the most basic: hiding the sender, in Sealed Sender.


References & further reading

← The RelaySealed Sender: Hiding Who Is Talking →