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