E2EE Chat

🚧 In active development

A chat application with Signal-style end-to-end encryption (X3DH + Double Ratchet) and its own PKI — the server never sees a message in plaintext.

Repository not public yet — happy to walk through the code on request.

Problem

Most chat systems trust the server with message contents. I wanted to understand, by building it, how modern messengers guarantee that only the endpoints can read messages — even when the server stores and relays them.

Solution

A Python client/server system implementing the X3DH handshake and the Double Ratchet algorithm, with an internal certificate authority issuing X.509 certificates per user, encrypted offline delivery queues, an optional peer-to-peer direct mode, and a local web interface built without frameworks.

Tech stack

  • Python
  • Cryptography
  • X3DH
  • Double Ratchet
  • X.509 PKI

Architecture

The server only stores public key bundles and encrypted message queues. Each client runs the full protocol stack locally — X3DH for session establishment, Double Ratchet for message keys — and exposes a local HTTP bridge (JSON API + server-sent events) that a frameworkless web UI consumes. The browser is purely a presentation layer; keys and encryption never leave the client process.

Technical highlights

  • X3DH key agreement + Double Ratchet sessions with forward secrecy and automatic one-time prekey replenishment
  • Group chats via pairwise encrypted sessions per member
  • Own PKI: an internal CA issues X.509 certificates for every user
  • P2P direct mode — clients can talk without going through the server
  • Private keys encrypted at rest with a local password; they never leave the device
  • Client-server transport wrapped in its own encrypted channel (ECDH + HKDF)
  • Local web UI (plain HTML/CSS/JS + SSE) over the Python client — crypto stays in the client process

Technical decisions

  • Implemented the Signal protocol primitives (X3DH, Double Ratchet) instead of using a ready-made library, to actually understand them.
  • Kept the web UI as a thin local bridge over the client so the crypto boundary stays in one process.

Challenges

  • Getting the Double Ratchet state machine right — out-of-order messages, skipped keys and session resets.
  • Designing offline delivery so queued messages stay encrypted and forward secrecy is preserved.

Future improvements

  • Add authenticated group management and safety-number verification UX.

What I learned

  • Cryptographic protocols are unforgiving: the hard part is not the math but the state management and the failure modes.