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.