Signal Protocol Layer: The Two-Person Phone Booth
Mental Model: The Two-Person Phone Boothβ
Imagine you and your friend want to talk privately in a crowded room. You step into a phone boothβa small, enclosed space where you can talk without being heard.
Signal is like that booth, but with a magic twist:
- The booth ratchets forward with every sentence you speak
- Each ratchet burns the previous conversation (can't go back!)
- The booth's magic key changes after each word you say
Result: Complete forward secrecy. If Eve breaks into the booth now, she can only hear what you're saying right nowβnot earlier, and not future conversations.
Why Use Signal in Cascading Cipher?β
π― The Problemβ
In the DH chapter, we learned we can create a shared secret. But what about:
- Compromise of a private key β Eve decrypts all past messages
- Message ordering β Ensure messages are decrypted in the right order
- Loss of synchronization β Handle when someone goes offline
π‘οΈ The Signal Protocol Solutionβ
Signal solves these with a double ratchet:
Root Ratchet (Like a Master Key)β
- Root Key: Long-term secret from DH handshake
- Chain Key: Temporary key from the Root Key
- Message Key: Per-message encryption key
Sending Ratchet (Like a Forward-Only Counter)β
- Each message gets a new random Message Key
- After sending, the Chain Key updates (can't reverse!)
- Old Message Keys are deleted forever
Receiving Ratchet (Like a Message Queue)β
- Track which message numbers you've decrypted
- Decrypt in order, reject duplicates
- Skip old or out-of-order messages
Result: Perfect forward secrecy + compromise resilience + ordered delivery.
Where It Fits in the Stackβ
βββββββββββββββββββββββββββββββββββ
β π Signal Layer (Ratcheting) β β Per-message key rotation
βββββββββββββββββββββββββββββββββββ€
β π€ DH Layer (Key Exchange) β β Creates the root key
βββββββββββββββββββββββββββββββββββ€
β AES Layer (Foundation) β β Encrypts with each message key
βββββββββββββββββββββββββββββββββββ
Signal takes the DH secret and creates fresh keys for every message so compromise is limited to just that message.
How the Double Ratchet Worksβ
The Ratchet Chainβ
Step-by-Step Message Flowβ
1. Initial Setup (DH Handshake)β
// Alice and Bob do ECDH to create a root key
const rootKey = await deriveRootKey(aliceKp, bobKp)
// rootKey: long-lived shared secret
// Initialize the first chain key
const chainKey0 = await KDF(rootKey, "chainKey0")
2. Sending Message 1β
// Derive Message Key 1 from Chain Key 0
const messageKey1 = await KDF(chainKey0, "messageKey1")
// Encrypt Message 1 with Message Key 1
const encrypted1 = await AES_GCMEncrypt(message1, messageKey1)
// Ratchet the chain key to Chain Key 1
const chainKey1 = await KDF(chainKey0, "ratchet")
// Delete messageKey1 and chainKey0!
await secureDelete([messageKey1, chainKey0])
3. Sending Message 2β
// Derive Message Key 2 from Chain Key 1
const messageKey2 = await KDF(chainKey1, "messageKey2")
// Encrypt Message 2 with Message Key 2
const encrypted2 = await AES_GCMEncrypt(message2, messageKey2)
// Ratchet to Chain Key 2
const chainKey2 = await KDF(chainKey1, "ratchet")
// Delete messageKey2 and chainKey1!
await secureDelete([messageKey2, chainKey1])
4. Receiving and Decryptingβ
Bob receives encrypted1 first:
// Bob has the same rootKey from DH
const bobChainKey0 = await KDF(rootKey, "chainKey0")
const bobMessageKey1 = await KDF(bobChainKey0, "messageKey1")
// Decrypt Message 1
const decrypted1 = await AES_GCMDecrypt(encrypted1, bobMessageKey1)
// Ratchet Bob's chain key to stay in sync
const bobChainKey1 = await KDF(bobChainKey0, "ratchet")
await secureDelete([messageKey1, bobChainKey0])
// Track that we decrypted message 1
await storeMessageNumber(1)
5. Receiving Message 2β
// Derive Message Key 2 from Chain Key 1
const bobMessageKey2 = await KDF(bobChainKey1, "messageKey2")
// Decrypt Message 2
const decrypted2 = await AES_GCMDecrypt(encrypted2, bobMessageKey2)
// Ratchet to Chain Key 2
const bobChainKey2 = await KDF(bobChainKey1, "ratchet")
await secureDelete([messageKey2, bobChainKey1])
// Track that we decrypted message 2
await storeMessageNumber(2)
Why Can't Eve Go Back?β
The Cryptographic Arrowβ
The KDF (Key Derivation Function) is a one-way function:
Chain Key 0 --KDF--> Message Key 1
Chain Key 0 --KDF--> Chain Key 1
Given Chain Key 1, you cannot derive Chain Key 0 or Message Key 1. The chain only moves forward!
If Eve Compromises Alice's Phoneβ
Before Signal (DH + AES only):β
Eve gets Alice's private key
β Decrypts DH shared secret
β Derives AES key
β Decrypts **all** messages (past and future!)