ARCHIVED from builddistributedsystem.com on 2026-04-28 — URL: https://builddistributedsystem.com/tracks/securitor/tasks/task-24-2-5-end-to-end-encryption
TASK

Implementation

End-to-end encryption ensures only the communicating parties can read messages — not the server, not the network. The X3DH protocol establishes a shared secret without either party transmitting it. The double ratchet algorithm then derives a fresh key for every single message, providing perfect forward secrecy.

Implement a node that handles E2EE session establishment and messaging:

// Alice uses Bob's published public keys to establish a session
{ "type": "initiate_conversation", "msg_id": 1,
  "recipient": "bob",
  "bob_public_key": {"identity_key": "IK", "signed_pre_key": "SPK"} }
-> { "type": "conversation_initiated", "in_reply_to": 1,
    "session_id": "<uuid>",
    "root_key": "<derived shared secret>" }

// Encrypt a message using the double ratchet
{ "type": "send_message", "msg_id": 2,
  "recipient": "bob", "plaintext": "Secret message" }
-> { "type": "message_encrypted", "in_reply_to": 2,
    "encrypted_message": {"ciphertext": "<base64>", "auth_tag": "<hex>"} }

// Bob decrypts and verifies authenticity
{ "type": "receive_message", "msg_id": 3,
  "sender": "alice",
  "encrypted_message": {"ciphertext": "CIPHERTEXT", "auth_tag": "AUTHTAG"} }
-> { "type": "message_decrypted", "in_reply_to": 3,
    "plaintext": "Secret message" }

Sample Test Cases

Initiate E2EE conversationTimeout: 5000ms
Input
{
  "src": "alice",
  "dest": "e2ee",
  "body": {
    "type": "initiate_conversation",
    "msg_id": 1,
    "recipient": "bob",
    "bob_public_key": {
      "identity_key": "IK",
      "signed_pre_key": "SPK"
    }
  }
}
Expected Output
{"type": "conversation_initiated", "in_reply_to": 1, "session_id": ".*", "root_key": ".*"}
Encrypt message with E2EETimeout: 5000ms
Input
{
  "src": "alice",
  "dest": "e2ee",
  "body": {
    "type": "send_message",
    "msg_id": 1,
    "recipient": "bob",
    "plaintext": "Secret message"
  }
}
Expected Output
{"type": "message_encrypted", "in_reply_to": 1, "encrypted_message": {"ciphertext": ".*", "auth_tag": ".*"}}

Hints

Hint 1
X3DH: Alice uses Bob public keys to derive a shared root key without the server learning it
Hint 2
session_id is derived from the X3DH exchange and is unique per conversation
Hint 3
Double ratchet: each message uses a fresh key derived from the previous one
Hint 4
Perfect forward secrecy: past session keys are deleted after use; compromising today's key reveals nothing about past messages
Hint 5
E2EE ensures the server never sees plaintext — only Alice and Bob can decrypt
OVERVIEW

Theoretical Hub

Concept overview coming soon

Key Concepts

E2EEX3DH key agreementdouble ratchetperfect forward secrecysession keys
main.py
python
Implement End-to-End Encryption (E2EE) - The Securitor | Build Distributed Systems