Relay
Algorithm: Sealing the Envelope
5 min read
A deep dive into the exact bytes: what goes inside the seal, what stays outside, and the one MAC that ties them together so a message is authentic, replay-proof, and deniable all at once.
This is the wire-format counterpart to Sealed Sender, The Envelope, and Deniability. It assumes you have read those; here we assemble their ideas into the concrete object that travels the network.
1. Two nested structures
Every message is two structures, one inside the other.
The inner payload is what only Bob can read. Before encryption, it is built as three concatenated parts:
InnerPayload = SenderCert ‖ MAC(channel_key, header_preimage) ‖ CBOR(Payload)
SenderCert— Alice's per-conversation, hybrid (Ed25519 + ML-DSA-65) certificate (article 02). It carries her sending public key and the identity blessing, so Bob can verify he is talking to a real, non-banned user.MAC(channel_key, header_preimage)— the deniable per-message authenticator (below).CBOR(Payload)— the actual message, including its true fine-grained type (one of the 27 kinds) and content, serialized with CBOR (a compact binary cousin of JSON, RFC 8949).
This whole InnerPayload is then handed to libsignal's sealed session cipher, which
encrypts it under the PQXDH + Double Ratchet + SPQR session.
The output is the sealed_payload — opaque, post-quantum, forward-secret bytes.
The outer envelope is what the relay reads. It wraps the sealed payload with exactly the routing and handling fields established across the series:
Envelope = { to, wake_class, created_at, cert_id, rate_limit_token, sealed_payload }
to (recipient routing key), wake_class (the coarse 6-value class), created_at
(5-minute-bucketed time), cert_id (the per-conversation coat-check ticket),
rate_limit_token (the single-use admission token). It travels the WebSocket as a binary
frame with a one-byte tag (0x01 for a normal message; a separate 0x02 tags a
first-contact discovery frame). The formal schema lives in the project's CDDL
(RFC 8610) definition.
2. The MAC that replaced a signature
The middle field is where three requirements meet, so it is worth dwelling on.
Earlier designs put an Ed25519 signature here — Alice signing each message. As Deniability explained, that is transferable proof and had to go. The replacement is an HMAC computed under the pairwise channel key — a secret Alice and Bob share for their conversation and no one else holds:
- Authenticity (to Bob). Only Alice and Bob hold
channel_key. Bob knows he didn't compute this tag, so Alice did — the message is genuinely hers. - Deniability (to the world). Because Bob also holds
channel_key, he could have produced the tag himself, so it proves nothing to any third party.
That handles who. The MAC also has to handle replay — stopping the relay, or anyone, from
lifting a valid message and re-injecting it — and it does so by what it is computed over:
the header_preimage. That preimage binds the MAC to this specific envelope — its routing
key, its coarse class, its bucketed time, its certificate — so the tag is only valid in the
exact envelope it was made for. Copy the sealed payload into a different envelope and the
recipient's recomputed MAC won't match; replay the same envelope and the message's
double-spent token is already burned. The MAC is the
glue between the sealed inside and the visible outside: it makes the outer header
tamper-evident to the recipient without making it readable by the relay.
3. What each layer defends
It helps to see the construction as concentric shells, each answering a different attacker:
| Layer | Protects against | Mechanism |
|---|---|---|
sealed_payload (libsignal) |
a relay reading content; a recorder decrypting later | PQXDH + ratchet + SPQR |
SenderCert (inside) |
an impostor claiming to be Alice | hybrid Ed25519 + ML-DSA-65 signature Bob verifies |
MAC(channel_key, …) (inside) |
forgery and replay and courtroom transfer | pairwise-key HMAC over the header preimage |
cert_id / wake_class / created_at (outside) |
the relay learning identity / type / precise time | minimized to a ticket, a coarse class, a fuzzy time |
The design rule visible in that table: anything that could identify, prove, or reveal lives inside the seal; only the irreducible routing-and-handling residue lives outside, and the MAC stitches the two so the outside cannot be tampered with undetected.
4. A machine-checked guarantee
The security-critical claim here is that the v4 MAC is strongly unforgeable: no one
without the channel key can produce a valid (envelope, tag) pair the recipient will accept,
even after seeing many legitimate ones — which is what makes the replay and forgery defenses
real rather than hopeful. That property (SUF-CMA, "strong unforgeability under chosen-message
attack") is proved by machine for this construction, in the computational model
(EasyCrypt) alongside a symbolic model (ProVerif) of the whole sealed-sender envelope. The
sealing is not just assembled and tested; the core unforgeability is a checked theorem.
5. Why this order, and this serialization
A couple of choices that look arbitrary but aren't:
- The certificate is inside, not a separate field. Putting it in the sealed payload is
what makes sealed sender work at all — the relay must not see the identity, so the identity
cannot be an outer field. The only identity residue outside is the opaque
cert_id. - CBOR, not JSON. The payload is serialized with CBOR because it is compact, binary, and has a canonical form — important when bytes are fed into a MAC, because two encoders must agree on the exact preimage or the tag won't verify. A canonical binary encoding removes the "did you sort the JSON keys the same way?" class of bug from a security-critical path.
- One-byte frame tag. The
0x01/0x02prefix lets the relay route normal messages and first-contact frames down different admission paths (the latter has no certificate yet — see Deniability and the handshake) without parsing anything sealed.
References & further reading
- Sealed Sender, The Envelope, Deniability — the three properties this format realizes.
- CBOR, RFC 8949 and CDDL, RFC 8610 — the payload/schema encodings.
- HMAC, RFC 2104 — the MAC primitive.
- "Strong unforgeability" — the property the v4 MAC is proved to have.