Skip to main content

🔗 Initial Secret Setup

Complete X3DH Handshake Walkthrough

In 20 minutes: Trace the complete X3DH handshake from Alice to Bob
Prerequisite: Four Types of Keys


🎯 The Simple Story

Alice wants to send Bob a message, but Bob is offline.

X3DH handshake steps:

  1. Bob uploads keys (while offline)
  2. Alice downloads keys (from server)
  3. Alice verifies keys (check signature)
  4. Alice generates ephemeral (per handshake)
  5. Alice computes 4 DH (shared secret derivation)
  6. Alice sends message (with ephemeral included)
  7. Bob receives message (when he comes online)
  8. Bob computes 4 DH (same as Alice!)
  9. Both have shared secret (can start Double Ratchet!)

🧠 Mental Model

Hold this picture in your head:

X3DH Handshake Flow:

Bob (Offline):
┌────────────────────────────────┐
│ Step 1: Create & Upload Keys │
├────────────────────────────────┤
│ • IK_B (Identity Key) │
│ • SPK_B (Signed Pre-Key) │
│ • OPK_B[] (100 One-Time Keys) │
│ • SIG_B (Signature of SPK_B) │
└────────────────────────────────┘

Upload

Bob goes offline

6 months later...

Alice (Online):
┌────────────────────────────────┐
│ Step 2: Download Bob's Keys │
├────────────────────────────────┤
│ • pk(IK_B), pk(SPK_B) │
│ • pk(OPK_B[0]), SIG_B │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 3: Verify SIG_B │
├────────────────────────────────┤
│ verify(pk(SPK_B), IK_B) │
│ ✅ Valid! Bob's keys │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 4: Generate EK_A │
├────────────────────────────────┤
│ • EK_A (Ephemeral Key) │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 5: Compute 4 DH │
├────────────────────────────────┤
│ DH1 = IK_A × SPK_B │
│ DH2 = EK_A × IK_B │
│ DH3 = EK_A × SPK_B │
│ DH4 = EK_A × OPK_B[0] │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 6: Derive Shared Secret │
├────────────────────────────────┤
│ SK = KDF(DH1 || DH2 || DH3) │
│ || || || DH4) │
└────────────────────────────────┘

Send encrypted message
+ pk(EK_A) + pk(OPK_B[0]) to Bob


Bob comes online!

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 7: Bob receives message │
├────────────────────────────────┤
│ • Extract pk(EK_A) │
│ • Retrieve sk(OPK_B[0]) │
│ • Delete OPK_B[0] │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 8: Bob Computes 4 DH │
├────────────────────────────────┤
│ DH1 = SPK_B × IK_A │
│ DH2 = IK_B × EK_A │
│ DH3 = SPK_B × EK_A │
│ DH4 = OPK_B[0] × EK_A │
└────────────────────────────────┘

─────────────────────────────────────────
┌────────────────────────────────┐
│ Step 9: Derive Same Secret │
├────────────────────────────────┤
│ SK = KDF(DH1 || DH2 || DH3) │
│ || || || DH4) │
└────────────────────────────────┘

Alice has SK
Bob has SK
Eve doesn't!

Start Double Ratchet!

📊 See It Happen

Complete X3DH sequence:


🎭 The Story: Alice's First Message

Bob's Setup (6 months ago):

Bob installed Signal. He created:

  • Identity key (IK_B = permanent)
  • Signed pre-key (SPK_B = this week)
  • 100 one-time pre-keys (OPK_B[0] to OPK_B[99])

Bob signed SPK_B with IK_B and uploaded all to server. Then he powered down his computer (offline).

Alice's First Message (today):

Alice needs to tell Bob: "I love you"

Alice opens Signal, enters Bob's phone number.

Signal server sends Alice:

  • pk(IK_B) - Bob's identity key
  • pk(SPK_B) - Bob's signed pre-key
  • pk(OPK_B[0]) - First one-time pre-key
  • SIG_B - Signature of SPK_B

Alice verifies: Is SIG_B valid for pk(SPK_B)? ✅ Yes!

Alice creates: EK_A (ephemeral key just for this message)

Alice computes:

  • DH1 = Alice's identity × Bob's SPK
  • DH2 = Alice's ephemeral × Bob's identity
  • DH3 = Alice's ephemeral × Bob's SPK
  • DH4 = Alice's ephemeral × Bob's OPK[0]

Alice derives: SK (shared secret) = KDF(DH1, DH2, DH3, DH4)

Alice encrypts: "I love you" with SK → "Kj7$mP9q..."

Alice sends: "Kj7$mP9q..." + pk(EK_A) + pk(OPK_B[0])

Bob's Receives (tomorrow):

Bob powers on his computer.

Bob sees Alice's message. Extracts pk(EK_A) and pk(OPK_B[0]).

Bob retrieves sk(OPK_B[0]) from his device, then deletes OPK_B[0].

Bob computes:

  • DH1 = Bob's SPK × Alice's identity (same as Alice's DH1!)
  • DH2 = Bob's identity × Alice's ephemeral (same as Alice's DH2!)
  • DH3 = Bob's SPK × Alice's ephemeral (same as Alice's DH3!)
  • DH4 = Bob's OPK[0] × Alice's ephemeral (same as Alice's DH4!)

Bob derives: SK (shared secret) = KDF(DH1, DH2, DH3, DH4) (same SK!)

Bob decrypts: "Kj7$mP9q..." with SK → "I love you"

Bob reads: "I love you"

Both Alice and Bob now have SK. They can start the Double Ratchet (forward secrecy per message)!


🔢 The Math

Alice's X3DH Steps

# Step 1: Download Bob's keys
pk(IK_B), pk(SPK_B), pk(OPK_B[0]), SIG_B ← Download(BobID)

# Step 2: Verify signature
Verify(pk(SPK_B), SIG_B, pk(IK_B)) → ✅ Valid

# Step 3: Generate ephemeral
sk(EK_A), pk(EK_A) ← GenerateKeyPair()

# Step 4: Compute 4 Diffie-Hellman operations
DH1 = DH(sk(IK_A), pk(SPK_B))
DH2 = DH(sk(EK_A), pk(IK_B))
DH3 = DH(sk(EK_A), pk(SPK_B))
DH4 = DH(sk(EK_A), pk(OPK_B[0]))

# Step 5: Derive shared secret
SK = KDF(DH1 || DH2 || DH3 || DH4)

# Step 6: Encrypt message
Ciphertext = Encrypt(message, SK)

# Step 7: Send
Send(Ciphertext, pk(EK_A), pk(OPK_B[0]))

Bob's X3DH Steps

# Step 1: Receive message
Ciphertext, pk(EK_A), pk(OPK_B[0]) ← Receive()

# Step 2: Retrieve and delete OPK
sk(OPK_B[0]) ← Retrieve(PRIVATE_KEY_STORE)
Delete(sk(OPK_B[0]), pk(OPK_B[0]))

# Step 3: Compute 4 Diffie-Hellman operations
DH1 = DH(sk(SPK_B), pk(IK_A))
DH2 = DH(IK_B, pk(EK_A))
DH3 = DH(sk(SPK_B), pk(EK_A))
DH4 = DH(sk(OPK_B[0]), pk(EK_A))

# Step 4: Derive shared secret
SK = KDF(DH1 || DH2 || DH3 || DH4)

# Step 5: Decrypt message
Message = Decrypt(Ciphertext, SK)

Key Point: DH operations are commutative. Alice's and Bob's computations match!


💡 Why We Care

Why X3DH Works for Offline Users

Traditional DH (online both parties):

  • Alice: "Bob, are you online? Let's do DH!"
  • Bob: "Yes!"
  • Alice and Bob agree: g^(ab) → shared secret
  • Problem: Both must be online simultaneously

X3DH (Bob can be offline):

  • Bob uploads keys to server (while offline)
  • Alice downloads keys, does her part
  • Bob comes online, does his part
  • Alice and Bob still agree: shared secret S

Security Properties

PropertyHow X3DH Provides It
ConfidentialityEve sees public keys only, can't compute S (missing EK_A)
AuthenticationSIG_B verifies Bob's keys (SOPA-resistant)
Forward secrecyOPK deleted after use, EK_A deleted after handshake
DeniabilityOPK provides deniability (DH4)
Message integrityMAC tag on ciphertext (derived from SK)

✅ Quick Check

Can you trace X3DH?

Step-by-step:
  1. Bob uploads IK_B, SPK_B, OPK_B[], SIG_B to server
  2. Alice downloads Bob's keys
  3. Alice verifies SIG_B (is it really Bob's SPK?)
  4. Alice creates EK_A
  5. Alice computes DH1, DH2, DH3, DH4
  6. Alice derives SK = KDF(DH1 || DH2 || DH3 || DH4)
  7. Alice sends message with pk(EK_A) + pk(OPK_B[0])
  8. Bob receives message
  9. Bob computes same DH1-DH4, derives same SK
  10. Bob decrypts
  11. Both have SK!

Why 4 DH operations?

Security benefits:

DH1: Identity × signed pre-key (long-term security) DH2: Ephemeral × identity (forward security) DH3: Ephemeral × signed pre-key (extra entropy) DH4: Ephemeral × one-time pre-key (deniability + non-repudiation)

Combined: All 4 needed to break. Hard!


📋 Key Takeaways

Bob uploads: IK, SPK, OPK[], SIG (when offline)
Alice downloads: Bob's keys and verifies signature
Alice generates: Ephemeral key (EK_A)
4 DH operations: Each provides different security
Shared secret: KDF(DH1 || DH2 || DH3 || DH4)
Alice sends: Encrypted message + pk(EK_A) + pk(OPK_B)
Bob retrieves: sk(OPK) and computes same DH operations
Both derive: Same shared secret SK
Result: Can start Double Ratchet for forward secrecy


🎉 What You'll Learn Next

Now you understand the complete X3DH handshake! Next, we'll learn why verification steps are important.

✓ Continue: Verifying Bob's Keys

We'll learn why signatures matter and how Eve might try to impersonate Bob!


Complete X3DH handshake explained! Next: Why verify keys?