Skip to main content

wechat_protocol/calls/
audio_packet.rs

1//! Selected Live96 envelope and ordinary95 Opus-primary framing. Encoding these
2//! bytes does not establish negotiated capabilities, room ownership or permission
3//! to send. Media transmission requires a separately admitted generation/scope.
4
5use anyhow::{bail, ensure, Result};
6use openssl::{
7    hash::MessageDigest,
8    memcmp,
9    pkey::{Id, PKey},
10    sign::Signer,
11    symm::{Cipher, Crypter, Mode},
12};
13use std::collections::BTreeMap;
14use zeroize::Zeroizing;
15
16const HEADER: usize = 19;
17const MAX_PACKET: usize = 1519;
18const MAX_CONF: usize = 1024;
19const WINDOW: u16 = 64;
20// Leave 60,000 sequences for the current 20-minute limit at 50 audio packets/sec.
21const MAX_LEGACY_AUDIO_START: u16 = u16::MAX - 59_999;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct MediaEpoch {
25    pub account_generation: u64,
26    pub logout_epoch: u64,
27    pub call_generation: u64,
28    pub key_generation: u64,
29}
30impl MediaEpoch {
31    fn validate(self) -> Result<()> {
32        ensure!(
33            self.account_generation != 0 && self.call_generation != 0 && self.key_generation != 0,
34            "Inactive media generation"
35        );
36        Ok(())
37    }
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct MediaContext {
42    pub room_id: u64,
43    pub own_member: u16,
44    pub peer_member: u16,
45    pub connection_id: u16,
46}
47
48/// The owner must establish a fresh media generation before authorizing sends.
49/// The legacy relay scope trades cross-direction audio nonce uniqueness for
50/// interoperability with peers that share media key material.
51pub enum TransmitScope {
52    VerifiedGeneration {
53        key_generation: u64,
54    },
55    LegacyRelayAudio {
56        key_generation: u64,
57    },
58    IsolatedCompatibilityTest {
59        intent_id: [u8; 32],
60        deadline_ms: u64,
61        allow_known_audio_overlap: bool,
62    },
63}
64
65/// Authenticated current-generation payload. Clear headers are not fully in AAD;
66/// protected member equality and external source/room/roster checks remain needed.
67/// Contents have no Debug/Serialize implementation.
68pub struct OpenedPayload {
69    pub packed_kind: u8,
70    pub sequence: u16,
71    payload: Zeroizing<Vec<u8>>,
72    epoch: MediaEpoch,
73    context: MediaContext,
74}
75impl OpenedPayload {
76    pub fn payload(&self) -> &[u8] {
77        &self.payload
78    }
79}
80
81/// One key/base/generation owner. No Clone, reset or implicit rekey is provided.
82/// The 64-sequence RX window is a local policy; rollover requires a verified new
83/// generation. Socket-source validation must happen before `open`.
84pub struct Live96 {
85    key: Zeroizing<[u8; 32]>,
86    base: Zeroizing<u64>,
87    context: MediaContext,
88    epoch: MediaEpoch,
89    tx_scope: Option<TransmitScope>,
90    first_tx_audio: u16,
91    next_audio: u32,
92    next_control: u32,
93    clear_sequence: u32,
94    rx_highest: BTreeMap<u8, u16>,
95    rx_seen: BTreeMap<u8, Box<[u64; 1024]>>,
96    rx: BTreeMap<[u8; 8], [u8; 32]>,
97    audio_overlaps: u64,
98    closed: bool,
99}
100impl Live96 {
101    pub fn new(
102        key: [u8; 32],
103        nonce_base: u64,
104        context: MediaContext,
105        epoch: MediaEpoch,
106    ) -> Result<Self> {
107        epoch.validate()?;
108        ensure!(
109            context.room_id != 0 && nonce_base != 0 && context.own_member != context.peer_member,
110            "Invalid media room/member context"
111        );
112        Ok(Self {
113            key: Zeroizing::new(key),
114            base: Zeroizing::new(nonce_base),
115            context,
116            epoch,
117            tx_scope: None,
118            first_tx_audio: 0,
119            next_audio: 0,
120            next_control: 0,
121            clear_sequence: 0,
122            rx_highest: BTreeMap::new(),
123            rx_seen: BTreeMap::new(),
124            rx: BTreeMap::new(),
125            audio_overlaps: 0,
126            closed: false,
127        })
128    }
129    fn check(&mut self, current: MediaEpoch) -> Result<()> {
130        if self.closed || self.epoch != current {
131            self.close();
132            bail!("Media generation changed or closed");
133        }
134        Ok(())
135    }
136    pub fn authorize_transmit(&mut self, current: MediaEpoch, scope: TransmitScope) -> Result<()> {
137        self.check(current)?;
138        ensure!(
139            self.tx_scope.is_none(),
140            "Media transmit scope already installed"
141        );
142        match &scope {
143            TransmitScope::VerifiedGeneration { key_generation }
144            | TransmitScope::LegacyRelayAudio { key_generation } => ensure!(
145                *key_generation == current.key_generation,
146                "Media key generation mismatch"
147            ),
148            TransmitScope::IsolatedCompatibilityTest {
149                intent_id,
150                deadline_ms,
151                ..
152            } => ensure!(
153                *intent_id != [0; 32] && *deadline_ms != 0,
154                "Missing isolated media test scope"
155            ),
156        };
157        if matches!(scope, TransmitScope::LegacyRelayAudio { .. }) {
158            self.first_tx_audio = self.legacy_audio_start()?;
159            self.next_audio = u32::from(self.first_tx_audio);
160        }
161        self.tx_scope = Some(scope);
162        Ok(())
163    }
164    fn legacy_audio_start(&self) -> Result<u16> {
165        let key = PKey::hmac(&self.key[..])?;
166        let mut mac = Signer::new(MessageDigest::sha256(), &key)?;
167        mac.update(b"agent-wechat/live96/audio-start/v1")?;
168        mac.update(&self.context.room_id.to_le_bytes())?;
169        mac.update(&self.base.to_le_bytes())?;
170        mac.update(&self.context.own_member.to_le_bytes())?;
171        let digest = Zeroizing::new(mac.sign_to_vec()?);
172        let prefix = u32::from_le_bytes(digest[..4].try_into()?);
173        Ok((prefix % u32::from(MAX_LEGACY_AUDIO_START) + 1) as u16)
174    }
175    fn tx_check(&mut self, current: MediaEpoch, now_ms: u64) -> Result<()> {
176        self.check(current)?;
177        match &self.tx_scope {
178            None => bail!("Media transmission is not authorized"),
179            Some(
180                TransmitScope::VerifiedGeneration { .. } | TransmitScope::LegacyRelayAudio { .. },
181            ) => Ok(()),
182            Some(TransmitScope::IsolatedCompatibilityTest { deadline_ms, .. }) => {
183                if now_ms >= *deadline_ms {
184                    self.close();
185                    bail!("Isolated media test expired");
186                }
187                Ok(())
188            }
189        }
190    }
191    fn nonce(&self, kind: u8, sequence: u16) -> [u8; 8] {
192        self.base
193            .wrapping_add(u64::from(kind) << 32)
194            .wrapping_add(u64::from(sequence))
195            .to_le_bytes()
196    }
197    fn diagnostic_audio_overlap(&mut self, kind: u8) -> Result<()> {
198        if kind == 0x10
199            && matches!(
200                self.tx_scope,
201                Some(
202                    TransmitScope::LegacyRelayAudio { .. }
203                        | TransmitScope::IsolatedCompatibilityTest {
204                            allow_known_audio_overlap: true,
205                            ..
206                        }
207                )
208            )
209        {
210            self.audio_overlaps = self
211                .audio_overlaps
212                .checked_add(1)
213                .ok_or_else(|| anyhow::anyhow!("Media diagnostic count exhausted"))?;
214            Ok(())
215        } else {
216            self.close();
217            bail!("Known media cross-direction nonce reuse")
218        }
219    }
220    pub fn known_audio_overlaps(&self) -> u64 {
221        self.audio_overlaps
222    }
223    pub fn seal_audio(
224        &mut self,
225        current: MediaEpoch,
226        now_ms: u64,
227        conf_payload: &[u8],
228    ) -> Result<Vec<u8>> {
229        parse_ordinary95(conf_payload)?;
230        self.seal(current, now_ms, 0x10, conf_payload)
231    }
232    fn seal(
233        &mut self,
234        current: MediaEpoch,
235        now_ms: u64,
236        kind: u8,
237        payload: &[u8],
238    ) -> Result<Vec<u8>> {
239        self.tx_check(current, now_ms)?;
240        ensure!(
241            !payload.is_empty() && payload.len() <= 1482,
242            "Invalid media payload extent"
243        );
244        let next = match kind {
245            0x10 => &mut self.next_audio,
246            0x70 => &mut self.next_control,
247            _ => bail!("Unsupported media sender family"),
248        };
249        ensure!(
250            *next <= u32::from(u16::MAX) && self.clear_sequence < u32::MAX,
251            "Media sequence exhausted; verified rekey required"
252        );
253        let sequence = *next as u16;
254        *next += 1;
255        let nonce = self.nonce(kind, sequence);
256        if self
257            .rx_seen
258            .get(&kind)
259            .is_some_and(|seen| seen[usize::from(sequence) / 64] & (1u64 << (sequence % 64)) != 0)
260        {
261            self.diagnostic_audio_overlap(kind)?;
262        }
263        let mut protected = Zeroizing::new(Vec::with_capacity(payload.len() + 2));
264        protected.extend(self.context.own_member.to_le_bytes());
265        protected.extend(payload);
266        let cipher = seal_original(&self.key, nonce, &protected)?;
267        let mut packet = Vec::with_capacity(HEADER + cipher.len());
268        packet.extend([0x96, 19]);
269        packet.extend(self.context.room_id.to_le_bytes());
270        packet.extend(self.context.own_member.to_le_bytes());
271        packet.extend((self.clear_sequence as u16).to_le_bytes());
272        packet.push(kind);
273        packet.extend(sequence.to_le_bytes());
274        packet.extend([0, self.context.connection_id as u8]);
275        packet.extend(cipher);
276        self.clear_sequence += 1;
277        Ok(packet)
278    }
279    pub fn open(&mut self, current: MediaEpoch, packet: &[u8]) -> Result<Option<OpenedPayload>> {
280        self.check(current)?;
281        ensure!(
282            (38..=MAX_PACKET).contains(&packet.len()) && packet[..2] == [0x96, 19],
283            "Unsupported Live96 packet shape"
284        );
285        ensure!(
286            u64::from_le_bytes(packet[2..10].try_into()?) == self.context.room_id
287                && u16::from_le_bytes(packet[10..12].try_into()?) == self.context.peer_member,
288            "Media room/member mismatch"
289        );
290        let kind = packet[14];
291        ensure!(
292            matches!(kind, 0x10 | 0x20 | 0x70) && packet[17] == 0,
293            "Unsupported Live96 packet family or extension"
294        );
295        let sequence = u16::from_le_bytes(packet[15..17].try_into()?);
296        let nonce = self.nonce(kind, sequence);
297        let clear = open_original(&self.key, nonce, &packet[HEADER..])?;
298        let digest: [u8; 32] = openssl::sha::sha256(&packet[HEADER..]);
299        if let Some(previous) = self.rx.get(&nonce) {
300            if *previous != digest {
301                self.close();
302                bail!("Authenticated media nonce conflict");
303            }
304            return Ok(None);
305        }
306        let own_next = if kind == 0x10 {
307            self.next_audio
308        } else if kind == 0x70 {
309            self.next_control
310        } else {
311            0
312        };
313        if u32::from(sequence) < own_next && (kind != 0x10 || sequence >= self.first_tx_audio) {
314            self.diagnostic_audio_overlap(kind)?;
315        }
316        // Retire every authenticated nonce before payload/member classification.
317        // Fixed 65536-bit ledgers preserve known cross-direction overlap checks
318        // after ciphertext digests leave the smaller reordering window.
319        let seen = self
320            .rx_seen
321            .entry(kind)
322            .or_insert_with(|| Box::new([0; 1024]));
323        seen[usize::from(sequence) / 64] |= 1u64 << (sequence % 64);
324        if self
325            .rx_highest
326            .get(&kind)
327            .is_some_and(|high| sequence < *high && *high - sequence >= WINDOW)
328        {
329            return Ok(None);
330        }
331        let high = self.rx_highest.entry(kind).or_insert(sequence);
332        *high = (*high).max(sequence);
333        let minimum = high.saturating_sub(WINDOW - 1);
334        let base = *self.base;
335        self.rx.retain(|nonce, _| {
336            let combined = u64::from_le_bytes(*nonce).wrapping_sub(base);
337            let k = (combined >> 32) as u8;
338            let s = combined as u16;
339            k != kind || s >= minimum
340        });
341        self.rx.insert(nonce, digest);
342        ensure!(
343            clear.len() > 2
344                && clear.len() <= 1484
345                && u16::from_le_bytes(clear[..2].try_into()?) == self.context.peer_member,
346            "Protected media member mismatch or extent"
347        );
348        // RX connection ID is not assumed equal to the local TX connection ID.
349        Ok(Some(OpenedPayload {
350            packed_kind: kind,
351            sequence,
352            payload: Zeroizing::new(clear[2..].to_vec()),
353            epoch: current,
354            context: self.context,
355        }))
356    }
357    /// Reply only to the exact selected authenticated faking_type1 probe. A
358    /// periodic/control response receives no echo. No general control sender is
359    /// inferred, and retries would consume another semantic operation.
360    pub fn reply_to_probe(
361        &mut self,
362        current: MediaEpoch,
363        now_ms: u64,
364        opened: &OpenedPayload,
365    ) -> Result<Option<Vec<u8>>> {
366        self.check(current)?;
367        ensure!(
368            opened.epoch == current && opened.context == self.context,
369            "Stale authenticated control payload"
370        );
371        if opened.packed_kind != 0x70 {
372            return Ok(None);
373        }
374        match opened.payload() {
375            [8, 1, 18, 2, 8, 1] => {
376                // Leave the probe unanswered if a reply would consume an
377                // inbound nonce. The sender's reuse fence stays unchanged.
378                if self.next_control <= u32::from(u16::MAX)
379                    && self.rx_seen.get(&0x70).is_some_and(|seen| {
380                        let sequence = self.next_control as usize;
381                        seen[sequence / 64] & (1u64 << (sequence % 64)) != 0
382                    })
383                {
384                    return Ok(None);
385                }
386                Ok(Some(self.seal(
387                    current,
388                    now_ms,
389                    0x70,
390                    &[8, 1, 18, 2, 8, 2],
391                )?))
392            }
393            [8, 1, 18, 2, 8, 0 | 2] => Ok(None),
394            // Other authenticated control messages have no implemented reply.
395            // Ignore them without inventing a response or stopping audio.
396            _ => Ok(None),
397        }
398    }
399    pub fn close(&mut self) {
400        self.key = Zeroizing::new([0; 32]);
401        self.base = Zeroizing::new(0);
402        self.tx_scope = None;
403        self.closed = true;
404        self.rx.clear();
405        self.rx_highest.clear();
406        self.rx_seen.clear();
407    }
408}
409
410/// Configured ordinary95/74 N1 sender for format106.
411pub fn ordinary95_single(
412    metadata: u8,
413    sequence: u16,
414    timestamp: u32,
415    frame: &[u8],
416) -> Result<Vec<u8>> {
417    ensure!(
418        !frame.is_empty() && frame.len() <= 1023 && 15 + frame.len() <= MAX_CONF,
419        "Invalid ordinary95 primary extent"
420    );
421    let mut out = Vec::with_capacity(15 + frame.len());
422    out.extend([1, metadata, 0, 0, 0x95, 0x74]);
423    out.extend(sequence.to_be_bytes());
424    out.extend(timestamp.to_be_bytes());
425    out.push(1);
426    out.extend((frame.len() as i16).to_le_bytes());
427    out.extend(frame);
428    Ok(out)
429}
430
431/// Configured ordinary95/74 N2 sender. RTP sequence/time identify the LAST
432/// primary, independent from cryptographic counters and clear room sequence.
433pub fn ordinary95_pair(
434    metadata: u8,
435    last_sequence: u16,
436    last_timestamp: u32,
437    first: &[u8],
438    second: &[u8],
439) -> Result<Vec<u8>> {
440    ensure!(
441        !first.is_empty()
442            && !second.is_empty()
443            && first.len() <= 1023
444            && second.len() <= 1023
445            && 17 + first.len() + second.len() <= MAX_CONF,
446        "Invalid ordinary95 primary extent"
447    );
448    let mut out = Vec::with_capacity(17 + first.len() + second.len());
449    out.extend([1, metadata, 0, 0, 0x95, 0x74]);
450    out.extend(last_sequence.to_be_bytes());
451    out.extend(last_timestamp.to_be_bytes());
452    out.push(2);
453    out.extend((first.len() as i16).to_le_bytes());
454    out.extend((second.len() as i16).to_le_bytes());
455    out.extend(first);
456    out.extend(second);
457    Ok(out)
458}
459
460/// Encoded Opus primaries, not decoded audio. The caller must establish that the
461/// current negotiation selects this codec/layout before decoding.
462pub struct OrdinaryAudio<'a> {
463    pub metadata: [u8; 4],
464    pub last_sequence: u16,
465    pub last_timestamp: u32,
466    pub primaries: Vec<&'a [u8]>,
467}
468pub fn parse_ordinary95(payload: &[u8]) -> Result<OrdinaryAudio<'_>> {
469    ensure!(
470        (16..=MAX_CONF).contains(&payload.len())
471            && payload[0] == 1
472            && payload[4..6] == [0x95, 0x74],
473        "Unsupported ordinary95 audio shape"
474    );
475    let count = usize::from(payload[12]);
476    ensure!(
477        (1..=3).contains(&count) && 13 + 2 * count <= payload.len(),
478        "Invalid ordinary95 primary count"
479    );
480    let mut cursor = 13 + 2 * count;
481    let mut primaries = Vec::with_capacity(count);
482    for n in 0..count {
483        let length = i16::from_le_bytes(payload[13 + 2 * n..15 + 2 * n].try_into()?);
484        ensure!(
485            (1..=1023).contains(&length),
486            "Invalid ordinary95 primary length"
487        );
488        let length = length as usize;
489        ensure!(
490            length <= payload.len() - cursor,
491            "Truncated ordinary95 primary"
492        );
493        primaries.push(&payload[cursor..cursor + length]);
494        cursor += length;
495    }
496    ensure!(cursor == payload.len(), "Trailing ordinary95 bytes");
497    Ok(OrdinaryAudio {
498        metadata: payload[..4].try_into()?,
499        last_sequence: u16::from_be_bytes(payload[6..8].try_into()?),
500        last_timestamp: u32::from_be_bytes(payload[8..12].try_into()?),
501        primaries,
502    })
503}
504fn xor(key: &[u8; 32], nonce: [u8; 8], counter: u64, input: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
505    let mut iv = Zeroizing::new([0; 16]);
506    iv[..8].copy_from_slice(&counter.to_le_bytes());
507    iv[8..].copy_from_slice(&nonce);
508    let cipher = Cipher::chacha20();
509    let mut crypter = Crypter::new(cipher, Mode::Encrypt, key, Some(&*iv))?;
510    crypter.pad(false);
511    let mut out = Zeroizing::new(vec![0; input.len() + cipher.block_size()]);
512    let n = crypter.update(input, &mut out)?;
513    let end = crypter.finalize(&mut out[n..])?;
514    out.truncate(n + end);
515    ensure!(out.len() == input.len(), "Unexpected call cipher size");
516    Ok(out)
517}
518fn tag(key: &[u8; 32], nonce: [u8; 8], cipher: &[u8]) -> Result<[u8; 16]> {
519    let one = xor(key, nonce, 0, &[0; 32])?;
520    let key = PKey::private_key_from_raw_bytes(&one, Id::POLY1305)?;
521    let mut signer = Signer::new_without_digest(&key)?;
522    signer.update(&0u64.to_le_bytes())?;
523    signer.update(cipher)?;
524    signer.update(&(cipher.len() as u64).to_le_bytes())?;
525    let mut tag = [0; 16];
526    ensure!(signer.sign(&mut tag)? == 16, "Unexpected call tag size");
527    Ok(tag)
528}
529fn seal_original(key: &[u8; 32], nonce: [u8; 8], clear: &[u8]) -> Result<Vec<u8>> {
530    ensure!(clear.len() <= 1484, "Call cipher input too large");
531    let cipher = xor(key, nonce, 1, clear)?;
532    let tag = tag(key, nonce, &cipher)?;
533    let mut out = Vec::with_capacity(cipher.len() + 16);
534    out.extend(&*cipher);
535    out.extend(tag);
536    Ok(out)
537}
538fn open_original(key: &[u8; 32], nonce: [u8; 8], sealed: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
539    ensure!(
540        (16..=1500).contains(&sealed.len()),
541        "Invalid call ciphertext extent"
542    );
543    let (cipher, expected) = sealed.split_at(sealed.len() - 16);
544    let actual = tag(key, nonce, cipher)?;
545    ensure!(
546        memcmp::eq(&actual, expected),
547        "Call media authentication failed"
548    );
549    xor(key, nonce, 1, cipher)
550}
551
552#[cfg(test)]
553mod tests {
554    use super::*;
555    fn epoch() -> MediaEpoch {
556        MediaEpoch {
557            account_generation: 1,
558            logout_epoch: 0,
559            call_generation: 1,
560            key_generation: 1,
561        }
562    }
563    fn context() -> MediaContext {
564        MediaContext {
565            room_id: 9,
566            own_member: 0,
567            peer_member: 1,
568            connection_id: 2,
569        }
570    }
571    fn receiver() -> Live96 {
572        Live96::new([7; 32], 11, context(), epoch()).unwrap()
573    }
574    fn peer() -> Live96 {
575        let mut c = context();
576        std::mem::swap(&mut c.own_member, &mut c.peer_member);
577        let mut sender = Live96::new([7; 32], 11, c, epoch()).unwrap();
578        sender
579            .authorize_transmit(
580                epoch(),
581                TransmitScope::VerifiedGeneration { key_generation: 1 },
582            )
583            .unwrap();
584        sender
585    }
586    #[test]
587    fn legacy_audio_start_depends_on_the_authenticated_member() {
588        let mut local = receiver();
589        let mut peer_context = context();
590        std::mem::swap(&mut peer_context.own_member, &mut peer_context.peer_member);
591        let mut remote = Live96::new([7; 32], 11, peer_context, epoch()).unwrap();
592        for channel in [&mut local, &mut remote] {
593            channel
594                .authorize_transmit(
595                    epoch(),
596                    TransmitScope::LegacyRelayAudio { key_generation: 1 },
597                )
598                .unwrap();
599        }
600        // Independent HMAC-SHA256 vectors for the same room and two member IDs.
601        assert_eq!(local.first_tx_audio, 440);
602        assert_eq!(remote.first_tx_audio, 429);
603        assert_eq!(local.next_audio, 440);
604        assert_eq!(remote.next_audio, 429);
605    }
606
607    #[test]
608    fn legacy_relay_allows_opposite_direction_audio_overlap_in_either_order() {
609        let raw = ordinary95_pair(0, 1, 320, &[1], &[2]).unwrap();
610        for transmit_first in [false, true] {
611            let mut local = receiver();
612            local
613                .authorize_transmit(
614                    epoch(),
615                    TransmitScope::LegacyRelayAudio { key_generation: 1 },
616                )
617                .unwrap();
618            let mut remote = peer();
619            remote.next_audio = u32::from(local.first_tx_audio);
620            let inbound = remote.seal_audio(epoch(), 0, &raw).unwrap();
621            if transmit_first {
622                local.seal_audio(epoch(), 0, &raw).unwrap();
623            }
624            assert_eq!(
625                local.open(epoch(), &inbound).unwrap().unwrap().payload(),
626                raw
627            );
628            if !transmit_first {
629                local.seal_audio(epoch(), 0, &raw).unwrap();
630            }
631            assert!(local.open(epoch(), &inbound).unwrap().is_none());
632            assert_eq!(local.known_audio_overlaps(), 1);
633
634            let mut conflicting_peer = peer();
635            conflicting_peer.next_audio = u32::from(local.first_tx_audio);
636            let different = ordinary95_pair(0, 1, 320, &[3], &[4]).unwrap();
637            let conflict = conflicting_peer.seal_audio(epoch(), 0, &different).unwrap();
638            assert!(local.open(epoch(), &conflict).is_err());
639            assert!(local.seal_audio(epoch(), 0, &raw).is_err());
640        }
641    }
642
643    #[test]
644    fn legacy_relay_keeps_control_and_send_exhaustion_fences() {
645        let mut local = receiver();
646        local
647            .authorize_transmit(
648                epoch(),
649                TransmitScope::LegacyRelayAudio { key_generation: 1 },
650            )
651            .unwrap();
652        let mut remote = peer();
653        local.seal(epoch(), 0, 0x70, &[8, 1, 18, 2, 8, 2]).unwrap();
654        let control = remote.seal(epoch(), 0, 0x70, &[8, 1, 18, 2, 8, 1]).unwrap();
655        assert!(local.open(epoch(), &control).is_err());
656
657        let mut local = receiver();
658        local
659            .authorize_transmit(
660                epoch(),
661                TransmitScope::LegacyRelayAudio { key_generation: 1 },
662            )
663            .unwrap();
664        local.next_audio = u32::from(u16::MAX);
665        let raw = ordinary95_pair(0, 1, 320, &[1], &[2]).unwrap();
666        local.seal_audio(epoch(), 0, &raw).unwrap();
667        assert!(local.seal_audio(epoch(), 0, &raw).is_err());
668    }
669
670    #[test]
671    fn original_aead_matches_independent_literal_vectors() {
672        // Independent original eight-byte-nonce ChaCha/Poly1305 known answers.
673        // This tests the primitive only, not a relay envelope or key lifecycle.
674        let vectors = [
675            (
676                "43b718d5a398bf36a5f3904a469febac12f7b5b22efa1ae5cc5deb54e75f5574",
677                "f06be3d7749e299c",
678                "",
679                "a02210edb5c4fd736d7366ae81089103",
680            ),
681            (
682                "9456a10f0c4b0644e9524fedcc329f052c443f55df5d911f4c172e07121e3740",
683                "beb908121cb81960",
684                "67736a080137aef7b92f53145b6ec56622",
685                "4576fc20a665cd99f668062c319677cd82a2bbd02898dbd27db411e002570cf3d5",
686            ),
687            (
688                "4b0fdc9081934247c7e945a10ca2ef52989dad5a2477fe041a6780e06d9254ed",
689                "948fb8afa967f654",
690                "1169e988a0972d513ca73734aad2e21f053d292b78d3eb9d3a6486da320d3e9bc67871bb80fdceb0f18b197aa8862b69b2b83993bab8bff256ff7c87ff9a486beb",
691                "ff673095b4835ccdbbd73719c1351799309fca1063534ccd3fb8a5a5b4492b282ef72fa53cb0af85ed86900773e081dcd78ae54215970a122fef411d62561ad3dd67a359f949c8d666c180287b67f297d3",
692            ),
693        ];
694        for (key_hex, nonce_hex, plaintext_hex, expected_hex) in vectors {
695            let key = hex::decode(key_hex).unwrap().try_into().unwrap();
696            let nonce = hex::decode(nonce_hex).unwrap().try_into().unwrap();
697            let plaintext = hex::decode(plaintext_hex).unwrap();
698            let expected = hex::decode(expected_hex).unwrap();
699            assert_eq!(seal_original(&key, nonce, &plaintext).unwrap(), expected);
700            assert_eq!(&*open_original(&key, nonce, &expected).unwrap(), &plaintext);
701        }
702    }
703    #[test]
704    fn literal_primary_layout_and_extent() {
705        let single = ordinary95_single(3, 1, 320, &[1, 2]).unwrap();
706        assert_eq!(
707            single,
708            [1, 3, 0, 0, 0x95, 0x74, 0, 1, 0, 0, 1, 64, 1, 2, 0, 1, 2]
709        );
710        assert_eq!(
711            parse_ordinary95(&single).unwrap().primaries,
712            vec![&[1, 2][..]]
713        );
714        let raw = ordinary95_pair(3, 1, 320, &[1, 2], &[3]).unwrap();
715        assert_eq!(
716            &raw[..17],
717            &[1, 3, 0, 0, 0x95, 0x74, 0, 1, 0, 0, 1, 64, 2, 2, 0, 1, 0]
718        );
719        let decoded = parse_ordinary95(&raw).unwrap();
720        assert_eq!(decoded.primaries, vec![&[1, 2][..], &[3][..]]);
721        let mut trailing = raw;
722        trailing.push(0);
723        assert!(parse_ordinary95(&trailing).is_err());
724    }
725    #[test]
726    fn unauthorized_sender_fails_and_authenticated_receiver_rejects_replay() {
727        let raw = ordinary95_pair(0, 1, 320, &[1], &[2]).unwrap();
728        let mut receiver = receiver();
729        assert!(receiver.seal_audio(epoch(), 0, &raw).is_err());
730        let mut peer = peer();
731        let packet = peer.seal_audio(epoch(), 0, &raw).unwrap();
732        assert_eq!(&packet[..2], &[0x96, 19]);
733        let clear = receiver.open(epoch(), &packet).unwrap().unwrap();
734        assert_eq!(clear.payload(), raw);
735        assert!(receiver.open(epoch(), &packet).unwrap().is_none());
736        let mut corrupt = packet;
737        *corrupt.last_mut().unwrap() ^= 1;
738        assert!(receiver.open(epoch(), &corrupt).is_err());
739    }
740    #[test]
741    fn clear_header_member_cannot_replace_protected_member() {
742        let raw = ordinary95_pair(0, 1, 320, &[1], &[2]).unwrap();
743        let mut peer = peer();
744        let mut packet = peer.seal_audio(epoch(), 0, &raw).unwrap();
745        packet[10..12].copy_from_slice(&2u16.to_le_bytes());
746        assert!(receiver().open(epoch(), &packet).is_err());
747    }
748    #[test]
749    fn strict_scope_rejects_overlap_and_isolated_scope_expires() {
750        let raw = ordinary95_pair(0, 1, 320, &[1], &[2]).unwrap();
751        let mut peer = peer();
752        let packet = peer.seal_audio(epoch(), 0, &raw).unwrap();
753        let mut receiver = receiver();
754        receiver.open(epoch(), &packet).unwrap();
755        receiver
756            .authorize_transmit(
757                epoch(),
758                TransmitScope::VerifiedGeneration { key_generation: 1 },
759            )
760            .unwrap();
761        assert!(receiver.seal_audio(epoch(), 0, &raw).is_err());
762        // The same collision fails closed when local transmission happens first.
763        let mut sender_first = self::receiver();
764        sender_first
765            .authorize_transmit(
766                epoch(),
767                TransmitScope::VerifiedGeneration { key_generation: 1 },
768            )
769            .unwrap();
770        sender_first.seal_audio(epoch(), 0, &raw).unwrap();
771        assert!(sender_first.open(epoch(), &packet).is_err());
772        assert!(sender_first.seal_audio(epoch(), 1, &raw).is_err());
773        let mut diagnostic = self::receiver();
774        diagnostic
775            .authorize_transmit(
776                epoch(),
777                TransmitScope::IsolatedCompatibilityTest {
778                    intent_id: [1; 32],
779                    deadline_ms: 100,
780                    allow_known_audio_overlap: true,
781                },
782            )
783            .unwrap();
784        diagnostic.open(epoch(), &packet).unwrap();
785        assert!(diagnostic.seal_audio(epoch(), 1, &raw).is_ok());
786        assert_eq!(diagnostic.known_audio_overlaps(), 1);
787        assert!(diagnostic.seal_audio(epoch(), 100, &raw).is_err());
788    }
789    #[test]
790    fn authenticated_controls_preserve_nonce_fence_and_ignore_unhandled_messages() {
791        let mut other_peer = peer();
792        let other = other_peer.seal(epoch(), 0, 0x70, &[8, 7]).unwrap();
793        let mut other_receiver = receiver();
794        let opened = other_receiver.open(epoch(), &other).unwrap().unwrap();
795        assert!(other_receiver
796            .reply_to_probe(epoch(), 0, &opened)
797            .unwrap()
798            .is_none());
799        assert!(other_receiver.check(epoch()).is_ok());
800
801        let mut peer = peer();
802        let request = peer.seal(epoch(), 0, 0x70, &[8, 1, 18, 2, 8, 1]).unwrap();
803        let mut receiver = receiver();
804        let opened = receiver.open(epoch(), &request).unwrap().unwrap();
805        receiver
806            .authorize_transmit(
807                epoch(),
808                TransmitScope::VerifiedGeneration { key_generation: 1 },
809            )
810            .unwrap();
811        assert!(receiver
812            .reply_to_probe(epoch(), 0, &opened)
813            .unwrap()
814            .is_none());
815        assert!(receiver.check(epoch()).is_ok());
816        // Omitting a reply does not disable the underlying reuse fence.
817        assert!(receiver
818            .seal(epoch(), 0, 0x70, &[8, 1, 18, 2, 8, 2])
819            .is_err());
820    }
821}