Skip to main content

Module code_join

Module code_join 

Source
Expand description

Code-join wrap-key derivation.

A single-use join code lets an owner hand a room’s Megolm session key to a read-only joiner without the passphrase: the joiner sends an ephemeral X25519 public key, the owner ECDHs against it and wraps the session key under an HKDF-derived key, and the joiner derives the same key to unwrap. Both sides compute the identical wrap key from derive_wrap_key; previously this ECDH+HKDF was open-coded twice in AppHandle (the CodeJoinRequest and CodeJoinResponse handlers), so it lives here as one tested function.

Constants§

CODE_JOIN_V2_PREFIX
huddle 2.2 (audit PA-1): the marker a v2 owner prepends to every join code it issues. This is the out-of-band capability anchor that defeats a relay downgrade: the owner hands the joiner the code over a channel the relay does not control (Signal, in person, a QR), so the relay cannot strip this marker the way it can strip a capabilities field from a network announcement. A joiner that sees the marker sends the proof form unconditionally — it never consults the relay-mediated capability for the code-join decision — so the relay can no longer force the cleartext fallback. The marker begins with a lowercase letter, which the join-code alphabet (ABCDEFGHJKMNPQRSTUVWXYZ23456789, uppercase-only) never produces, so a legacy code can never be mistaken for a v2 one. The proof is computed over the FULL code string, marker included.

Functions§

derive_code_proof
huddle 2.2 (audit PA-1): derive a memory-hard proof of knowledge of the join code, bound to the room and the joiner’s ephemeral X25519 public key.
derive_wrap_key
Derive the 32-byte wrap key both sides compute: HKDF-SHA256 over the raw X25519 ECDH shared secret of our_secret and their_pub. The owner uses it to passphrase::wrap the session key; the joiner uses it to unwrap.
verify_code_proof
huddle 2.2 (audit PA-1): constant-time check that proof is the proof for expected_code under this (room_id, joiner_x25519_pub). The owner calls this for each unexpired issued code; the comparison is constant-time so a timing side-channel can’t leak how many proof bytes matched.