fix(media): rewrite relay re-seal seq so multi-user audio decrypts
The media AEAD nonce is an implicit per-direction monotonic counter; open() reconstructs it from the 14-byte header seq field (the AAD), so the contract is header.seq == the counter seal() used. The SFU relay decrypted inbound frames with the sender key, re-sealed with the recipient send_crypto (its own counter), but forwarded the sender header verbatim -- so seq carried the wrong counter and the recipient rebuilt the wrong nonce, silently dropping every relayed frame. It only worked for a single first-ever sender into a fresh recipient, which is why reverse/3rd-party audio failed. Rewrite the outgoing header seq to the recipient peek_send_counter() before re-sealing so each server->client direction is one contiguous monotonic counter and the nonce always matches. Safe: the jitter buffer orders by timestamp, not seq. No wire-format/proto/ABI change. Adds test_relay_interleaved_reseal regression coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -102,8 +102,17 @@ void MediaRelay::on_udp_frame(const uint8_t* data, size_t len,
|
||||
crypto_aead_chacha20poly1305_ietf_ABYTES);
|
||||
}
|
||||
|
||||
// Copy header (re-use sender's header verbatim — ssrc, seq, ts pass through).
|
||||
// Copy header (ssrc, ts, flags pass through for demux/playout), then rewrite the
|
||||
// seq field to THIS recipient's next send counter. The media AEAD nonce is an
|
||||
// implicit per-direction monotonic counter; open() reconstructs it from the seq in
|
||||
// the header (the AAD). Since we re-seal with the recipient's send_crypto (its own
|
||||
// counter), the verbatim sender seq would no longer match the nonce seal() uses and
|
||||
// every relayed frame would fail auth. Set seq = peek_send_counter() BEFORE sealing
|
||||
// so the header (which is the authenticated AAD) carries the matching counter.
|
||||
std::memcpy(seal_buf_.data(), data, voicecat::net::kVoiceHeaderSize);
|
||||
const uint64_t send_ctr = send_crypto->peek_send_counter();
|
||||
seal_buf_[8] = static_cast<uint8_t>((send_ctr >> 8) & 0xFF);
|
||||
seal_buf_[9] = static_cast<uint8_t>(send_ctr & 0xFF);
|
||||
|
||||
uint8_t* out_payload = seal_buf_.data() + voicecat::net::kVoiceHeaderSize;
|
||||
long sealed_out = send_crypto->seal(
|
||||
|
||||
Reference in New Issue
Block a user