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
capabilitiesfield 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-SHA256over the raw X25519 ECDH shared secret ofour_secretandtheir_pub. The owner uses it topassphrase::wrapthe session key; the joiner uses it tounwrap. - verify_
code_ proof - huddle 2.2 (audit PA-1): constant-time check that
proofis the proof forexpected_codeunder 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.