Call key establishment¶
Crypto - call-key
CRY-01 - status: draft - audio, video, group
The 32-byte call key is the root secret for all per-call media keying, delivered to each recipient device inside the offer's per-device Signal-encrypted
The call key (callKey) is a 32-byte shared secret per 1:1 call. It is the
input keying material for all per-call media keys.
Length. The call key MUST be exactly 32 bytes. A call key shorter than 32 bytes MUST be rejected. Downstream derivations consume the full 32 bytes:
derive_e2e_keys : HKDF IKM = callKey[0..32]
derive_e2e_sframe_key: salt = callKey[0..16], IKM = callKey[16..32]
derive_warp_auth_key : HKDF IKM = callKey[0..32]
Delivery. The caller MUST encrypt the call key to each recipient device's Signal
session and place the ciphertext in an <enc> node (see call-offer):
<enc v="2" type="pkmsg|msg" count="0">CIPHERTEXT</enc>
type="pkmsg"MUST be used when establishing the Signal session (PreKeySignalMessage).type="msg"MUST be used when reusing an existing session.- Single-device callee: the
<enc>node is placed directly in<offer>. - Multi-device callee: each device's
<enc>MUST be wrapped in its own<to jid="...">under a<destination>node, one per device. Every device receives the same call key under its own Signal session and derives identical per-participant keys.
Key generation path (keygen=2). <encopt keygen="2"> selects the v2 keying path.
The Signal-delivered secret is then carried in a <raw_e2e> field that replaces the
call key as the HKDF IKM for end-to-end SRTP derivation. It MUST be at least 32 bytes;
only its first 32 bytes are consumed as IKM.
Plaintext format. The decrypted Signal plaintext yields the call key (and, under
keygen=2, the raw_e2e material). Its serialization is not pinned by this section.
Requires: call-offer, srtp-master-key, sframe-media, warp
Breakdown: srtp-e2e
Implemented by
| Flavor | Status | Source | Notes |
|---|---|---|---|
whatsapp-rust |
working | history - blame - commits 674e851 |
— |
zapo-caller |
working | — | — |
meowcaller |
planned | — | — |
Annotation wacrg:CRY-01 — a flavor marks its implementation site in source with this comment; a script clones the source, finds it, and attaches the commit blame/permalink.
Contributors
| Contributor | Role |
|---|---|
| wrote initial spec |
protocol history / diff - blame
Open questions - How the caller generates the 32-byte call key (CSPRNG source / any structure) is not revealed by the keying code, which only consumes it. - Exact serialization of the Signal plaintext that carries the call key, and how raw_e2e is framed alongside it under keygen=2. - Whether the call key is regenerated per offer or reused across re-offers / call legs.
References - Signal Protocol (X3DH + Double Ratchet) - RFC 5869 — HKDF
Changelog¶
- 2026-06-21 — Initial spec entry.