Heads up: posts on this site are drafted by Claude and fact-checked by Codex. Both can still get things wrong — read with care and verify anything load-bearing before relying on it.
why → how

How end-to-end encryption works

Open WhatsApp and a banner tells you Meta can't read your messages. That claim sits on a specific protocol — Diffie–Hellman key agreement plus a 'double ratchet' that changes the key on every message. Here's the shape of it.

Security intermediate May 24, 2026 · updated Aug 25, 2026 · 13 min read

On this page

The picture version

Five pictures for a reader who has only ever seen the banner, following one conversation: Alice messaging Bob.

1 · The problem

Both say “encrypted.” They differ on where the ciphertext stops.

ordinary TLS Alice server Bob ciphertext ciphertext plaintext here, even if only for a moment end-to-end encryption Alice server Bob ciphertext ciphertext only ciphertext, the whole way “Trust the server operator” stops being part of the threat model.
Anyone who can subpoena, breach, or rogue-employee that middle box can read the top row. The bottom row moves the keys to the two endpoints, so the server’s job shrinks to storing and forwarding blobs it cannot open.

2 · Starting a conversation

You can’t shake hands with a phone that’s in a pocket.

Alice wants to start a conversation. Bob’s phone is in his pocket. Alice fetches the bundle Bob’s bundle, uploaded ahead a long-term identity key a signed prekey a stack of one-time prekeys — all of them public a shared secret, on both sides the server hosts the bundles and never learns the result — it is computed from private keys that never travel
Alice runs several Diffie–Hellman computations against those published keys at once and mixes the results into one secret; Bob does the matching operations whenever he next comes online and lands on the same value. Asynchrony was the constraint that shaped this step, not secrecy.

3 · The part doing the work

The key changes on every single message.

one shared secret would be fragile, so the key keeps moving msg 1msg 2msg 3msg 4msg 5msg 6 C₀C₁C₂Cʹ₀Cʹ₁Cʺ₀ A → BA → BA → BB → AB → AA → B a one-way function steps the chain key after every message — it cannot be run backwards and the accent marks are where the direction reversed, triggering a fresh Diffie–Hellman step which buys two different properties forward secrecy — steal the phone today and yesterday’s messages stay unreadable; those keys are overwritten post-compromise security — once a fresh step runs, a previously stolen key stops being enough the caveat is in the “once”: an attacker with live access learns the new material too
Recovery is from a snapshot, not from an ongoing intruder — worth holding onto, because it is the difference between “they got a copy once” and “they are still inside.” The expensive public-key maths only runs at the direction changes, not on every keystroke.

4 · Where the banner stops

It promises what, not whether.

what the server’s view of each message actually is from device A · to device B · 412 bytes · 02:14 · [opaque blob] the blob is useless without one of the two endpoints’ keys It promises the server can’t read what she said. It doesn’t promise it can’t know she said it, to Bob, at 2am. and three more places the guarantee stops backups — if the cloud copy is readable by the provider, the guarantee ends there; the safe settings are opt-in the endpoint — malware or a forensic extraction sees plaintext, because plaintext is what the user reads client-side scanning — inspect the plaintext before encryption and the property survives while being neutralised
The metadata graph alone is famously informative, and no messenger hides all of it — routing a message requires knowing where to route it. Every item here is a place the guarantee stops at the device rather than at the protocol, which is exactly where the policy fights are aimed.

5 · Keep this card

The whole thing on one index card.

E2EE = a key agreement between the two devices + a per-message key the server never sees + doing most of the work: the key keeps moving ∴ a claim about the design, not the operator without the moving part, one stolen phone retroactively unlocks every message the server ever stored
Picture to keep: the post office still handles every envelope and still reads the address on the front — but the envelope is welded shut, and the only two people with a cutter are the sender and the recipient. Where it breaks: a real envelope uses one lock forever, while the ratchet welds each message with a fresh one and destroys that message’s cutter immediately after use.

Why it exists

Open WhatsApp, start a chat with a friend — call her Alice messaging Bob, since we’ll follow this exact conversation the whole way down — and a small banner tells you “Messages and calls are end-to-end encrypted.” Meta runs the servers the message just travelled through, and the banner is claiming that even Meta, sitting in the middle, cannot read what was typed.

The obvious question is: how is that different from the lock icon next to a URL in your browser? Both say “encrypted.” The difference is where the ciphertext stops. With ordinary TLS, your message is encrypted from your phone to WhatsApp’s server, decrypted there, then re-encrypted from the server to your friend’s phone. The server holds plaintext, even if only for a moment. Anyone who can subpoena, breach, or rogue-employee that server can read messages. End-to-end encryption (E2EE) closes that window: the keys live on the two endpoints, the server only relays ciphertext, and “trust the server operator” stops being part of the threat model.

flowchart LR
    subgraph TLS["Ordinary TLS"]
        direction LR
        A1[Alice's phone] -->|ciphertext| S1["server<br/>holds plaintext briefly"]
        S1 -->|ciphertext| B1[Bob's phone]
    end
    subgraph E2EE["End-to-end encryption"]
        direction LR
        A2[Alice's phone] -->|ciphertext| S2["server<br/>only sees ciphertext"]
        S2 -->|ciphertext| B2[Bob's phone]
    end

Why it matters now

E2EE remains a live policy fight, not a settled feature. Critics — Signal prominently among them — argue that the UK’s Online Safety Act and the EU’s recurring “Chat Control” proposals push encrypted services toward client-side scanning: inspecting messages on the device before they’re encrypted. Signal has publicly said it will not weaken its privacy guarantees to comply. Separately, Apple announced a plan in 2021 that would have matched photos on the device against known-material hashes as they were uploaded to iCloud Photos, and drew heavy criticism from cryptographers on exactly the client-side-scanning grounds below; Apple later introduced optional end-to-end encrypted iCloud backups under Advanced Data Protection in December 2022.

On the deployment side, the design Signal published in 2013–2014 — which picked up the name “Signal Protocol” in 2016 — is now the default across the major messengers: WhatsApp turned Signal-protocol E2EE on by default in 2016, Google Messages rolled out one-to-one RCS E2EE in beta from late 2020 and now applies it by default to eligible RCS conversations, and Facebook Messenger finished defaulting all personal chats to E2EE in December 2023. The mechanism this post describes underlies the major consumer-messaging clients, and is the part legislation keeps trying to put a hole in. What share of the world’s messaging traffic that adds up to isn’t something anyone publishes.

The short answer

E2EE = key agreement between the two devices + a per-message symmetric key that the server never sees

Picture to keep: the post office still handles every envelope and still reads the address on the front — but the envelope is welded shut, and the only two people with a cutter are the sender and the recipient. Where the picture breaks: a real envelope uses one lock forever, while the ratchet described below welds each message with a fresh one and destroys that message’s cutter immediately after use.

Two phones do a public-key handshake to agree on a starting secret without trusting the server in the middle. From that secret they derive a fresh symmetric key for every message, ratcheting forward so that compromising today’s key doesn’t reveal the messages already sent — and a periodic Diffie–Hellman step folds in new entropy so future messages eventually recover from the compromise too. The server’s job shrinks to “store and forward opaque blobs.”

How it works

We’ll use the Signal Protocol as the worked example, because it’s the one WhatsApp, Signal, Messenger, and Google Messages all build on, and because it’s been publicly specified and analysed since around 2013. Each piece below fixes the failure of the simpler version before it.

1. Bootstrapping a shared secret (X3DH)

Naive plan: have the two phones do a live handshake. The hard problem is that when Alice first messages Bob, Bob might be offline. You can’t do an interactive handshake with a phone that’s currently in a pocket. Signal’s answer is X3DH: every user uploads a small bundle of public keys to the server in advance — a long-term identity key, a medium-lived “signed prekey,” and a stack of one-time prekeys. When Alice wants to start a conversation, she fetches Bob’s bundle, performs several Diffie–Hellman computations against those keys at once, and mixes the results into a single shared secret. Bob, when he comes online, does the matching operations on his side and arrives at the same secret.

The server hosts the bundles but never sees the resulting secret — Diffie–Hellman is built so the secret is computable from each side’s private keys plus the other side’s public keys, and never has to traverse the network.

2. Ratcheting forward (Double Ratchet)

Naive plan: agree on one shared secret and keep using it. That would be a fragile thing to lean on for years. If a phone is compromised tomorrow, you want yesterday’s messages to stay unreadable (forward secrecy) and you want the conversation to eventually recover so tomorrow’s stop being readable too (post-compromise security).

The Double Ratchet gives both. It combines two clocks:

sequenceDiagram
    participant A as Alice's phone
    participant B as Bob's phone
    Note over A,B: DH step → chain key C₀
    A->>B: msg 1 (key from C₀)
    A->>B: msg 2 (key from C₁ = KDF(C₀))
    A->>B: msg 3 (key from C₂ = KDF(C₁))
    Note over A,B: Bob's reply triggers a new DH step → C'₀
    B->>A: msg 4 (key from C'₀)
    B->>A: msg 5 (key from C'₁ = KDF(C'₀))
    Note over A,B: Alice's reply triggers another DH step → C''₀
    A->>B: msg 6 (key from C''₀)

The symmetric ratchet is the inside of each run (C₀ → C₁ → C₂ by KDF). The DH ratchet is the jump between runs — a fresh chain key derived from new key material the other side just brought in.

The per-message key that finally encrypts the payload is symmetric. The Double Ratchet spec treats the payload cipher as a pluggable AEAD interface and gives a CBC-plus-HMAC construction — encrypt-then-MAC — as one recommended instantiation, so treat that as “a concrete choice the spec endorses” rather than “the cipher Signal is defined to use.” Either way the expensive public-key math only runs during ratchet steps, not on every keystroke.

3. What the server actually sees

Naive hope: end-to-end encryption hides everything. Once the ratchet is running, the server’s view of each message is roughly: from device A, to device B, this many bytes, at this time, here is an opaque blob and a header. That blob and header are useless without one of the two endpoints’ keys.

Which is also the honest limit of the banner Alice saw in the first paragraph. It promises the server can’t read what she said. It does not promise the server doesn’t know she said it, to Bob, at 2am, for the ninth time this week.

Show the seams

You started with E2EE = key agreement between the two devices + a per-message symmetric key that the server never sees. What did this post add? — + the key keeps moving. That clause is doing almost all the work: without it, one stolen phone retroactively unlocks every message the server ever stored, and the ratchet is the entire reason “we can’t read your messages” is a claim about the design rather than a promise about the operator’s behaviour. Everything in the seams list above is a place where that claim stops at the device, not at the protocol.

Check yourself

Before you go — Alice’s phone is seized and fully imaged, keys and all, right after she sends message 12. The server hands over its complete archive of ciphertext for messages 1 through 12. How much of that archive can be decrypted with what’s on the phone?

Answer

None of the archived ciphertext, assuming what was captured is the ratchet state. Each chain key is derived from the previous one by a one-way KDF and the old one is overwritten, and each message key is discarded once it has been used — including message 12’s, spent at the moment she hit send. Nothing in the captured state runs backwards. That’s forward secrecy, and it’s why the server’s archive is worth much less than its size suggests.

Two things do fall, and they’re the ones that matter in practice. The app’s own local database almost certainly holds the plaintext history, because that’s how you scroll up — no protocol break required. And the captured session state decrypts everything Bob sends from now on, and can send as Alice, until she re-establishes the session from a new device. E2EE protected the archive, not the endpoint holding the plaintext.

And one more — a government requires the messenger to hash every photo on the device against a known-material database before encryption, and report matches. Is the service still end-to-end encrypted?

Answer

Technically yes, and that’s the point of the argument. The wire still carries only ciphertext, the server still can’t decrypt, and every property described in this post still holds. What changed is that “the endpoint” is no longer working solely for its user — the plaintext is inspected at the one place the protocol was designed to trust unconditionally. The security critique isn’t that the math breaks; it’s that the reporting channel is a general-purpose one, and the definition of what to scan for is set by whoever controls the list, not by the protocol.

Going deeper