The previous scheme built ChaCha20-Poly1305 nonces from an internal
recv_seq counter that incremented once per decrypt() call. Under
in-order delivery recv_seq stayed in sync with the sender's send_seq,
but any out-of-order or lost packet caused them to diverge permanently —
every subsequent packet then used the wrong nonce and AEAD decryption
failed for the rest of the session.
Fix: parse the MediaHeader at the top of both encrypt() and decrypt()
and use header.seq as the nonce input. Both sides now derive the nonce
from the same wire field, surviving reordering by construction.
send_seq / recv_seq are kept as pure packet counters for the rekey
interval trigger; they no longer affect nonce derivation.
All tests updated to pass valid v2 MediaHeader bytes instead of raw
byte literals (the new code requires a parseable header for nonce
derivation). New test decrypt_survives_out_of_order_delivery encrypts
5 packets and delivers them out of order (indices 0,2,1,4,3); this
test would have failed under the old counter-based scheme.
Fixes audit finding C1 from AUDIT-2026-05-25.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
shiguredo_dav1d and shiguredo_svt_av1 build scripts panic with
'unsupported target: os=android, arch=aarch64'. The AV1 SW fallback
is only needed on macOS / Linux desktop — Android uses MediaCodec
for AV1 anyway.
- Cargo.toml: AV1 SW deps moved under cfg(not(target_os = "android"))
- lib.rs: cfg-gate the dav1d and svt_av1 modules and re-exports
- factory.rs: on Android, Av1Main paths return NotInitialized when
HW MediaCodec is also unavailable (only path on Android)
- factory tests: assert NotInitialized on Android, Ok elsewhere
Unblocks T4.3.1.1 (Android target-compile of wzp-video / mediacodec).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Refactor should_forward_pli(room, stream_id) -> should_forward_pli(room, stream_id, now: Instant)
so the 200 ms dedup window is deterministically testable.
- Update the one caller in run_participant_signals to pass Instant::now().
- Add 6 PLI unit tests covering:
* first PLI forwards
* duplicate within 200 ms suppressed
* after 200 ms forwards again
* different streams independent
* different rooms independent
* no stream owner returns None
Addresses reviewer CR on T4.7 (line drawn at T4.6 — stateful relay features must
have state-transition tests).
wzp-relay tests: 93 -> 99 pass.
The broadcast alone wasn't reaching the first client because its
recv loop hadn't started yet when the second client registered.
Now the relay sends PresenceList directly to the new client (right
after RegisterPresenceAck) AND broadcasts to all others.
This guarantees every client gets the full user list:
- New client: via direct send (queued before recv loop starts)
- Existing clients: via broadcast
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>