# How is the first chain key established in Signal's double ratchet?

I have parsed the following documentation: https://signal.org/docs/specifications/doubleratchet/#kdf-chains

My understanding is that any KDF round needs a chain key and a DH output (also called constant).

It's easy for me to understand where the constant comes from (DH exchange), but it is quite hazy when it comes to the very first chain key.

When chain key already exists, the constant is changed when a new public key is used for the DH ratchet and the previous chain key is used, but I could not find any info about how the very first chain key is established between the Alice and Bob.

Can anyone explain this part please?

• Q: how the very first chain key is established between the Alice and Bob? see code snippet of section 3.3, root key and chain keys are established by KDF state.RK, state.CKs = KDF_RK(SK, DH(state.DHs, state.DHr)) section 5.1 states, The SK output from X3DH becomes the SK input to Double Ratchet initialization thats how process starts first an SK is computed by X3DH then chains are established Commented Sep 5 at 6:24

Introduction states

The Double Ratchet algorithm is used by two parties to exchange encrypted messages based on a shared secret key. Typically the parties will use some key agreement protocol (such as X3DH 1) to agree on the shared secret key. Following this, the parties will use the Double Ratchet to send and receive encrypted messages.

so X3DH is first used to compute a shared secret (SK).

Section 3.3 explains use of SK to initialize chain

Prior to initialization both parties must use some key agreement protocol to agree on a 32-byte shared secret key SK and Bob's ratchet public key. These values will be used to populate Alice's sending chain key and Bob's root key. Bob's chain keys and Alice's receiving chain key will be left empty, since they are populated by each party's first DH ratchet step.

def RatchetInitAlice(state, SK, bob_dh_public_key):
state.DHs = GENERATE_DH()
state.DHr = bob_dh_public_key
state.RK, state.CKs = KDF_RK(SK, DH(state.DHs, state.DHr))
state.CKr = None
state.Ns = 0
state.Nr = 0
state.PN = 0
state.MKSKIPPED = {}

def RatchetInitBob(state, SK, bob_dh_key_pair):
state.DHs = bob_dh_key_pair
state.DHr = None
state.RK = SK
state.CKs = None
state.CKr = None
state.Ns = 0
state.Nr = 0
state.PN = 0
state.MKSKIPPED = {}


Section 5.1 explains Integration of X3DH

The Double Ratchet algorithm can be used in combination with the X3DH key agreement protocol 1. The Double Ratchet plays the role of a "post-X3DH" protocol which takes the session key SK negotiated by X3DH and uses it as the Double Ratchet's initial root key.

The following outputs from X3DH are used by the Double Ratchet:

The SK output from X3DH becomes the SK input to Double Ratchet initialization (see Section 3.3).

The AD output from X3DH becomes the AD input to Double Ratchet encryption and decryption (see Section 3.4 and Section 3.5).

Bob's signed prekey from X3DH (SPKB) becomes Bob's initial ratchet public key (and corresponding key pair) for Double Ratchet initialization.

Any Double Ratchet message encrypted using Alice's initial sending chain can serve as an "initial ciphertext" for X3DH. To deal with the possibility of lost or out-of-order messages, a recommended pattern is for Alice to repeatedly send the same X3DH initial message prepended to all of her Double Ratchet messages until she receives Bob's first Double Ratchet response message.

An easy explaination by ChatGPT is

The very first chain key in the Double Ratchet algorithm is established through the initial key agreement protocol, such as X3DH (Extended Triple Diffie-Hellman), before the Double Ratchet process begins. Here's how it works:

1. Initial Key Agreement: Before Alice and Bob start the Double Ratchet process, they use a key agreement protocol (like X3DH) to derive a shared secret key, denoted as ( SK ). This key is shared between both parties and serves as the initial root key.
2. Root Key Initialization: Alice and Bob each initialize their root key (( RK )) using the shared secret key ( SK ). For Alice, the initial chain key for the sending chain (( CK_s )) is derived from the root key and the Diffie-Hellman output computed from her own private key and Bob's public key.
3. First Chain Key: This derived chain key (( CK_s )) becomes the first chain key in the sending chain for Alice. Similarly, Bob derives his first chain key for his receiving chain from the same root key and DH output. Thus, the first chain key is essentially derived from the shared secret ( SK ) established during the initial key agreement and the DH output generated when Alice computes it using Bob's public key. If you'd like to dive deeper into the process or review the documentation directly, you can refer to the Double Ratchet specification.
• It is not very clear how the constant for the Symetric-key ratchet is obtained. "In the Signal protocol, the constant used in the Symmetric-key ratchet is not explicitly shared between Alice and Bob. Instead, it is a predefined value that both parties know and use independently. This constant is part of the protocol specification and is the same for all sessions. Therefore, Alice and Bob do not need to exchange this constant during their communication setup." A predefined value that both parties know: how? Of course this constant should remain secret between Alice and Bob. Commented Sep 4 at 10:20
• I have asked ChatGPT about this constant and it says it is not a secret and known by everyone. I was not expecting that, I don't get why this constant is added if everyone knows it. If you steal the init key from DH exchange, and you have the constant, then you have everything to generate the message keys! I think I need an explanation on this (ELI5). To me, a KDF can issue new message keys and init keys with only the previous key as its input. Commented Sep 4 at 10:30