Security Architecture
Session Establishment
X3DH Overview
X3DH (Extended Triple Diffie-Hellman) is the session establishment protocol used by SemaFore. It runs once per sender-recipient pair when the sender initiates a conversation. The protocol produces a shared secret that is used to derive the initial encryption keys for the conversation.
The goal of X3DH is to establish a session where both participants are confident in each other’s identity and where forward secrecy is guaranteed (i.e., if a participant’s long-term keys are later compromised, past session keys remain secure because they depend on ephemeral keys that have been deleted).
Protocol Steps
Assume sender Alice wants to initiate a session with recipient Bob.
Step 1: Fetch Bob’s key bundle
Alice queries the server for Bob’s current key bundle. The server responds with:
identity_key: Bob’s long-term Curve25519 public key (X25519 form)identity_signing_key: Bob’s Ed25519 public key, used to verify the SPK signaturesigned_pre_key: Bob’s current Signed Pre-Key (Curve25519 public key)signed_pre_key_signature: An Ed25519 signature of the SPK, created by Bob’s Identity Keyone_time_pre_key: A public Curve25519 key from Bob’s available one-time pre-key pool (if any remain)one_time_pre_key_id: The identifier of the consumed OPK (if one was allocated)
Step 2: Verify the SPK signature
Alice verifies that the SPK signature is valid under Bob’s identity_signing_key (Ed25519 public key). If verification fails, Alice aborts the session—the key bundle may have been tampered with by a network attacker or the server. Verification succeeds only if Bob’s device signed the SPK with its own Identity Key.
Step 3: Generate an ephemeral key pair
Alice generates a fresh Curve25519 key pair (EK_A_private, EK_A_public). This ephemeral key will be used once and then deleted.
Step 4: Perform four DH operations
Alice computes four Diffie-Hellman shared secrets:
DH1 = DH(IK_A_private, SPK_B_public)— Alice’s long-term key with Bob’s pre-keyDH2 = DH(EK_A_private, IK_B_public)— Alice’s ephemeral key with Bob’s long-term keyDH3 = DH(EK_A_private, SPK_B_public)— Alice’s ephemeral key with Bob’s pre-keyDH4 = DH(EK_A_private, OPK_B_public)— Alice’s ephemeral key with Bob’s one-time pre-key (if available; omitted if no OPK)
Each DH operation produces 32 bytes of key material.
Step 5: Combine via HKDF
Alice concatenates the four (or three, if no OPK) DH shared secrets and uses HKDF-SHA256 to derive the initial session secret:
session_secret = HKDF-SHA256(
salt = empty,
input_key_material = DH1 || DH2 || DH3 || DH4,
info = "X3DH session",
length = 32
)
This session secret is then used to derive the initial root key and chain keys for the Double Ratchet (see the Message Encryption page).
First Message Headers
Alice’s first message to Bob must include metadata so Bob can reconstruct the shared session secret:
- Alice’s Identity Key public component (
IK_A_public) - Alice’s ephemeral key public component (
EK_A_public) - The identifier of the OPK that Alice consumed (
OPK_B_id), if an OPK was present
The server does not read these values; it passes them through with the ciphertext to Bob. Bob uses them to perform the same four DH operations on his side and derive the same session secret.
Multi-Device Fan-Out
When Alice sends a message, she may not know how many devices Bob owns. The protocol handles this transparently:
- The server maintains a list of all of Bob’s registered devices
- For each device, the server has a separate key bundle
- Alice queries the server separately for each of Bob’s devices, performing X3DH for each device
- The server routes the message N times (once per device), encrypted under each device’s derived session secret
- Each copy of the message includes the device-specific X3DH headers (IK_A, EK_A, OPK_id for that device)
- Bob’s devices decrypt their respective copies independently
From Alice’s perspective, it appears to be a single send. From the server’s perspective, it is N separately encrypted messages. From Bob’s perspective (on each device), it is one message that was encrypted specifically for that device.
Forward Secrecy
Forward secrecy in X3DH comes from the ephemeral key EK_A. Because:
EK_Ais generated fresh for each new sessionEK_A_privateis deleted after the X3DH computation- Two of the four DH operations involve
EK_A_private
If Bob’s long-term keys (IK or SPK) are later compromised, an attacker cannot use them to recover EK_A_private (because it has been deleted) or EK_A_public is known but deriving the DH shared secret still requires EK_A_private. Thus, past sessions remain secure even if long-term keys are compromised.
Exception: If an attacker compromises Bob’s device before the session is established and steals both the Identity Key and the Signed Pre-Key, the attacker could perform the four DH operations themselves if they intercept or guess Alice’s ephemeral public key. However, in a real-world deployment, this is a physical security event (device theft), not a network compromise, and is outside the threat model of the protocol.