Since static keys are not broadcast in v2 announces, devices use pairwise recognition tags to identify mutual favorites. A tag is a directional 8-byte MAC that only the two participating peers can compute.
Tag Generation (A $\to$ B):
- Compute shared secret
S_AB = X25519(A_noiseStaticPrivate, B_noiseStaticPublic). - Derive
K_AB = HKDF-SHA256(ikm: S_AB, salt: "", info: "bitchat-recognition-v1", length: 32). - Compute
tag_A\to B = HMAC-SHA256(key: K_AB, message: uint32be(epoch) || A_noiseStaticPublic || B_noiseStaticPublic || peerID_e)[0..8].
Implementation Rules:
- Directional: The tag must include the sender's and receiver's public keys in a specific order so that
tag_A\to B \neq tag_B\to A. This prevents observers from linking two rotating IDs via a symmetric tag. - Binding: The
peerID_e must be included in the HMAC message to prevent attackers from replaying a tag from one ID to another. - Padding: The tag list in an announce must be padded with uniform random 8-byte values to a fixed count of
TAG_SLOTS = 8. This hides the actual number of mutual favorites. - Rotation: If a device has more than 8 favorites, it must rotate which favorites occupy the slots across successive announces.
- Unidirectional Caution: Do not include a tag for a one-directional favorite; this would disclose interest to a peer who hasn't reciprocated.
Warning: Recognition is a hint only. Do not perform consequential actions (like routing DMs or showing verified badges) based on a tag match alone. Wait for a completed Noise handshake.
S_AB = X25519(A_noiseStaticPrivate, B_noiseStaticPublic)
K_AB = HKDF-SHA256(ikm: S_AB, salt: "", info: "bitchat-recognition-v1", length: 32)
tag_A\to B = HMAC-SHA256(key: K_AB,
message: uint32be(epoch)
|| A_noiseStaticPublic (32)
|| B_noiseStaticPublic (32)
|| peerID_e (8))[0..8]