Skip to main content

sipx_media/ice/
stun.rs

1//! STUN as ICE uses it: connectivity checks over the media port (RFC 5389, RFC 8445 §7).
2//!
3//! [`sipx_transport::stun`] is a Binding *client* with no attributes and no credentials, and its
4//! own header says so: "Anything that needs the full protocol (ICE, RFC 8445) needs a different
5//! module, not more attributes bolted onto this one." This is that module. What it takes from
6//! there it takes unchanged — RFC 5389 §6's header layout, the magic cookie, §7.3's `is_stun`
7//! test and the cryptographically random [`TransactionId`], which is a security decision that
8//! should exist once — and it adds nothing there.
9//!
10//! What it does not take is the `XOR-MAPPED-ADDRESS` reader, for two reasons: that decoder is
11//! reachable only through `parse_reply`, which reads Binding Responses and discards every
12//! attribute ICE needs, and a connectivity check has to *write* the attribute as well as read it.
13//! Exposing the helper would have been extending the module the story was told not to extend.
14//!
15//! Two things in here are worth reading twice, because both fail silently.
16//!
17//! **The order of the two integrity values.** `MESSAGE-INTEGRITY` is HMAC-SHA1 over the message
18//! with the header's length field temporarily set as though the message ended just after it
19//! (RFC 5389 §15.4); `FINGERPRINT` is computed last, over everything including
20//! `MESSAGE-INTEGRITY`, with the length field again adjusted to include *it* (§15.5), and its
21//! value is the CRC-32 XOR `0x5354554e`. Both adjustments are easy to skip and neither is
22//! visible in a self-test: the message round-trips through this module perfectly and every real
23//! peer rejects it. The guard is `a_connectivity_check_encodes_to_the_rfc_5769_sample_request`,
24//! because the IETF computed that tag and not this crate.
25//!
26//! **The direction of `USERNAME`.** See [`Peering`].
27//!
28//! Everything here is handed unauthenticated datagrams from whoever can reach the media port
29//! ([spec] §11.3): no `unwrap`, no raw indexing, no length arithmetic that can wrap. A malformed
30//! message is an [`Error`] and a dropped datagram.
31//!
32//! ## `PRIORITY` is range-checked, and that is a deliberate trade
33//!
34//! A `PRIORITY` outside RFC 8839 §5.1's `1..=2^31−1` makes [`Message::decode`] reject the **whole
35//! datagram**, not just the attribute, because [`Priority`] will not hold the value.
36//!
37//! Chosen with the cost known. [spec] §6.2 is explicit that the range check on parse is what keeps
38//! RFC 8445 §6.1.2.3's pair-priority arithmetic inside a `u64`, and §5.1.2.1's formula cannot
39//! reach 2^31 for a conforming peer, so nothing legitimate is being refused. What is being risked
40//! is a peer that treats the field as a plain `u32` and sets the high bit: **every** check it
41//! sends is dropped, and the failure has precisely the signature [`Peering`] warns about — it
42//! looks like a blocked path and gets diagnosed as a network fault. That is written down here
43//! rather than left to be rediscovered, because the alternative — accepting the value and
44//! range-checking it at the point the arithmetic happens — moves an overflow that a peer chooses
45//! into a crate that cannot see where the number came from. Failing closed at the parser is worth
46//! the interop risk; failing closed silently would not be.
47//!
48//! [spec]: https://github.com/codewandler/sipx/blob/main/docs/specs/ice.md
49
50use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
51
52use hmac::{Hmac, Mac};
53use sha1::Sha1;
54use sipx_sdp::ice::{Credentials, Priority};
55use subtle::ConstantTimeEq as _;
56
57use sipx_transport::stun::{HEADER_LEN, MAGIC_COOKIE, is_stun};
58pub use sipx_transport::stun::{TransactionId, new_transaction_id};
59
60type HmacSha1 = Hmac<Sha1>;
61
62/// RFC 5389 §6: the only method ICE uses. Binding is the whole protocol here.
63const METHOD_BINDING: u16 = 0x0001;
64
65const ATTR_USERNAME: u16 = 0x0006;
66const ATTR_MESSAGE_INTEGRITY: u16 = 0x0008;
67const ATTR_ERROR_CODE: u16 = 0x0009;
68const ATTR_XOR_MAPPED_ADDRESS: u16 = 0x0020;
69const ATTR_PRIORITY: u16 = 0x0024;
70const ATTR_USE_CANDIDATE: u16 = 0x0025;
71const ATTR_SOFTWARE: u16 = 0x8022;
72const ATTR_FINGERPRINT: u16 = 0x8028;
73const ATTR_ICE_CONTROLLED: u16 = 0x8029;
74const ATTR_ICE_CONTROLLING: u16 = 0x802a;
75
76const FAMILY_IPV4: u8 = 0x01;
77const FAMILY_IPV6: u8 = 0x02;
78
79/// RFC 5389 §15.5: the value on the wire is the CRC-32 XOR this constant, "to avoid a fingerprint
80/// of the STUN message being confused with the CRC of an enclosing protocol".
81const FINGERPRINT_XOR: u32 = 0x5354_554e;
82
83/// The top 16 bits of [`MAGIC_COOKIE`], which is what §15.2 XORs a port with. Checked against
84/// the cookie itself by `the_port_key_is_the_top_half_of_the_cookie`.
85const PORT_KEY: u16 = 0x2112;
86
87/// `MESSAGE-INTEGRITY` with its 4-byte attribute header: HMAC-SHA1 is 20 octets (RFC 5389 §15.4).
88const INTEGRITY_ATTR_LEN: usize = 24;
89/// `FINGERPRINT` with its 4-byte attribute header.
90const FINGERPRINT_ATTR_LEN: usize = 8;
91
92/// RFC 8445 §7.3.1.1's error response code: 487 Role Conflict.
93pub const ROLE_CONFLICT: u16 = 487;
94
95/// The error codes RFC 5389 §15.6's three-bit class can carry: "The value MUST be between 3 and
96/// 6." Enforced in both directions, so that neither a code this module sends nor one it reports
97/// having received is a number §15.6 does not define.
98pub const ERROR_CODES: std::ops::RangeInclusive<u16> = 300..=699;
99
100/// The byte an attribute value is padded to a 32-bit boundary with.
101///
102/// RFC 5389 §15 says the padding "may be any value", so this is a free choice — but it is a
103/// choice that shows up in the wire bytes, because `MESSAGE-INTEGRITY` is an HMAC over the
104/// padding as well as the value. RFC 5769's vectors pad with `0x20`: §2.1's `USERNAME` is nine
105/// bytes followed by `20 20 20`, and §2.2's `SOFTWARE` is eleven followed by `20`. An encoder
106/// that pads with zeroes cannot reproduce either published tag, and reproducing them is the only
107/// evidence available that this encoder is right.
108const PAD: u8 = 0x20;
109
110/// What a datagram that was not a STUN message this profile understands turned out to be.
111///
112/// Every variant is a dropped datagram. None of them is a panic, and none of them moves any
113/// state: an off-path attacker who can reach the media port can produce all of them at will.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
115#[non_exhaustive]
116pub enum Error {
117    /// Not STUN at all — the first two bits or the magic cookie say so (RFC 5389 §7.3).
118    #[error("not a STUN message")]
119    NotStun,
120    /// The message ends inside its own header, an attribute, or the length it claims.
121    #[error("the STUN message ends inside what it claims to contain")]
122    Truncated,
123    /// A method other than Binding. ICE uses no others (RFC 8445 §7).
124    #[error("STUN method {0:#06x} is not Binding")]
125    UnsupportedMethod(u16),
126    /// A known attribute whose value is the wrong length, not UTF-8, or out of range.
127    #[error("STUN attribute {0:#06x} is malformed")]
128    MalformedAttribute(u16),
129    /// An [`Attribute::Unknown`] naming a type this module computes for itself.
130    ///
131    /// Encoding only, and only from a caller that assembled the message by hand: the two
132    /// integrity values must be the last two attributes and must be derived from everything
133    /// before them, so a hand-supplied copy of either is refused rather than emitted.
134    #[error("STUN attribute {0:#06x} is computed by the encoder and cannot be supplied")]
135    ReservedAttribute(u16),
136    /// `FINGERPRINT` is present and does not match the message (RFC 5389 §15.5).
137    #[error("FINGERPRINT does not match the message")]
138    Fingerprint,
139    /// More bytes than the 16-bit length field can describe. Unreachable from any credential
140    /// [`Credentials`] admits; it exists so that no encoding path has to panic or truncate.
141    #[error("the message is longer than the STUN length field can describe")]
142    TooLong,
143}
144
145/// RFC 5389 §6's message class: the two bits that say request from response.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
147pub enum Class {
148    /// A Binding Request. A connectivity check is one.
149    Request,
150    /// A Binding Indication. Draws no response; ICE's keepalive is one (RFC 8445 §11).
151    Indication,
152    /// A success response.
153    Success,
154    /// An error response.
155    Error,
156}
157
158impl Class {
159    const fn bits(self) -> u16 {
160        match self {
161            Self::Request => 0,
162            Self::Indication => 1,
163            Self::Success => 2,
164            Self::Error => 3,
165        }
166    }
167
168    const fn from_bits(bits: u16) -> Self {
169        match bits {
170            1 => Self::Indication,
171            2 => Self::Success,
172            3 => Self::Error,
173            _ => Self::Request,
174        }
175    }
176}
177
178/// RFC 5389 §6: the 14-bit message type interleaves the method with the class, `C1` at bit 8 and
179/// `C0` at bit 4, "for backwards compatibility with RFC 3489".
180const fn message_type(class: Class, method: u16) -> u16 {
181    let class = class.bits();
182    (method & 0x000f)
183        | ((method & 0x0070) << 1)
184        | ((method & 0x0f80) << 2)
185        | ((class & 0x1) << 4)
186        | ((class & 0x2) << 7)
187}
188
189/// The inverse of [`message_type`].
190const fn split_type(raw: u16) -> (Class, u16) {
191    let class = ((raw & 0x0100) >> 7) | ((raw & 0x0010) >> 4);
192    let method = (raw & 0x000f) | ((raw & 0x00e0) >> 1) | ((raw & 0x3e00) >> 2);
193    (Class::from_bits(class), method)
194}
195
196/// One STUN attribute, in the profile spec §11.1 lists.
197///
198/// `MESSAGE-INTEGRITY` and `FINGERPRINT` are deliberately not variants. They are not attributes a
199/// caller chooses to add: they are computed over whatever else is present and must come last, in
200/// that order, so [`Message::encode`] appends them and nothing else can.
201///
202/// [`Attribute::Unknown`] is the hole in that sentence, and it is closed rather than trusted:
203/// encoding one whose `kind` is either of those two types is [`Error::ReservedAttribute`]. It is
204/// not reachable from the wire — [`Message::decode`] matches both types before it ever builds an
205/// `Unknown` — but it is reachable from a caller assembling a message by hand, and a second
206/// `MESSAGE-INTEGRITY` in a message is a message that authenticates as nothing.
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub enum Attribute {
209    /// `USERNAME` (RFC 5389 §15.3), in the direction [`Peering`] fixes.
210    Username(String),
211    /// `PRIORITY` (RFC 8445 §7.1.1) — and note §7.1.1's rule that this is *not* the candidate's
212    /// own priority but the one it would have as a peer-reflexive candidate (spec §4).
213    ///
214    /// Carried as [`Priority`] so the RFC 8839 §5.1 range check applies to a value read off the
215    /// wire as much as to one read out of SDP: spec §6.2 shows that an unchecked priority is what
216    /// overflows the pair-priority arithmetic, and a check is a place a peer can put one.
217    Priority(Priority),
218    /// `USE-CANDIDATE` (RFC 8445 §7.1.2): a flag, so a zero-length value.
219    UseCandidate,
220    /// `ICE-CONTROLLED` (§7.1.3) carrying the sender's tiebreaker.
221    IceControlled(u64),
222    /// `ICE-CONTROLLING` (§7.1.3) carrying the sender's tiebreaker.
223    IceControlling(u64),
224    /// `ERROR-CODE` (RFC 5389 §15.6). 487 is the role conflict of RFC 8445 §7.3.1.1.
225    ///
226    /// The code is held to [`ERROR_CODES`] in both directions. On decode that means an error
227    /// response carrying a class §15.6 does not define is a dropped datagram, which costs the
228    /// transaction a retransmission rather than an immediate failure — the same fail-closed trade
229    /// the module documentation records for `PRIORITY`, and only reachable from a peer that is
230    /// already violating §15.6's MUST.
231    ErrorCode {
232        /// The three-digit code, reassembled from §15.6's class and number.
233        code: u16,
234        /// The reason phrase, which is advisory and may be empty.
235        reason: String,
236    },
237    /// `XOR-MAPPED-ADDRESS` (RFC 5389 §15.2): where the responder saw the request come from.
238    XorMappedAddress(SocketAddr),
239    /// `SOFTWARE` (RFC 5389 §15.10).
240    ///
241    /// sipx does not put one on its own checks — spec §11.1 lists what a check carries and this
242    /// is not on it, and a version string on every check is bytes on the wire and a gift to a
243    /// scanner. It is here because a peer may send one and because RFC 5769's vectors carry one,
244    /// and a vector that cannot be encoded is a vector that cannot test the encoder.
245    Software(String),
246    /// An attribute this profile has no meaning for, kept so the caller can decide.
247    ///
248    /// RFC 5389 §7.3.1 wants a comprehension-required unknown attribute in a *request* answered
249    /// with a 420; that is the agent's decision, not the codec's, so the bytes survive to it.
250    Unknown {
251        /// The attribute type.
252        kind: u16,
253        /// Its value, unpadded.
254        value: Vec<u8>,
255    },
256}
257
258/// Which role attribute a check carries, and — for the controlling agent only — whether it
259/// nominates (RFC 8445 §7.1.2, §7.1.3).
260///
261/// The controlled arm has no `nominate`, and that is the point: §7.1.2 says "the controlled agent
262/// MUST NOT include the USE-CANDIDATE attribute in a Binding request", and a shape that cannot
263/// express it cannot send it by accident.
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
265pub enum RoleAttribute {
266    /// `ICE-CONTROLLING`.
267    Controlling {
268        /// The 64-bit value chosen per ICE session (§7.1.3), regenerated on a role switch.
269        tiebreaker: u64,
270        /// Whether this check nominates the pair (§8.1.1's regular nomination).
271        nominate: bool,
272    },
273    /// `ICE-CONTROLLED`.
274    Controlled {
275        /// The 64-bit value chosen per ICE session (§7.1.3).
276        tiebreaker: u64,
277    },
278}
279
280impl RoleAttribute {
281    const fn attribute(self) -> Attribute {
282        match self {
283            Self::Controlling { tiebreaker, .. } => Attribute::IceControlling(tiebreaker),
284            Self::Controlled { tiebreaker } => Attribute::IceControlled(tiebreaker),
285        }
286    }
287
288    /// The tiebreaker the attribute carries.
289    #[must_use]
290    pub const fn tiebreaker(self) -> u64 {
291        match self {
292            Self::Controlling { tiebreaker, .. } | Self::Controlled { tiebreaker } => tiebreaker,
293        }
294    }
295}
296
297/// Our short-term credentials and the peer's, and the two usernames they make (spec §11.2).
298///
299/// This type exists for one reason: the direction. A check sipx **sends** carries
300/// `<peer-ufrag>:<our-ufrag>` and is keyed with the **peer's** password; a check sipx
301/// **receives** carries `<our-ufrag>:<peer-ufrag>` and is keyed with **ours**, as is the response
302/// sipx sends back to it. Reverse the two and every check sipx sends is rejected for a bad
303/// credential and every check it receives goes unanswered — which on the wire is
304/// indistinguishable from a blocked path, so it gets diagnosed as a network fault and not as the
305/// four transposed characters it is. Naming the four values rather than formatting a username at
306/// each call site is what makes the mistake reviewable.
307#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct Peering {
309    local: Credentials,
310    remote: Credentials,
311}
312
313impl Peering {
314    /// Pair our credentials with the peer's.
315    #[must_use]
316    pub const fn new(local: Credentials, remote: Credentials) -> Self {
317        Self { local, remote }
318    }
319
320    /// Our `a=ice-ufrag` and `a=ice-pwd`.
321    #[must_use]
322    pub const fn local(&self) -> &Credentials {
323        &self.local
324    }
325
326    /// The peer's.
327    #[must_use]
328    pub const fn remote(&self) -> &Credentials {
329        &self.remote
330    }
331
332    /// The `USERNAME` on a check sipx sends: `<peer-ufrag>:<our-ufrag>`.
333    #[must_use]
334    pub fn outbound_username(&self) -> String {
335        format!("{}:{}", self.remote.ufrag(), self.local.ufrag())
336    }
337
338    /// The key for a check sipx sends, and for the response it expects back: the peer's password.
339    #[must_use]
340    pub fn outbound_key(&self) -> &str {
341        self.remote.pwd()
342    }
343
344    /// The `USERNAME` a check sipx receives must carry: `<our-ufrag>:<peer-ufrag>`.
345    #[must_use]
346    pub fn inbound_username(&self) -> String {
347        format!("{}:{}", self.local.ufrag(), self.remote.ufrag())
348    }
349
350    /// The key for a check sipx receives, and for the response sipx sends to it: our password.
351    #[must_use]
352    pub fn inbound_key(&self) -> &str {
353        self.local.pwd()
354    }
355}
356
357/// A STUN message: the header, the attributes, and — once decoded — what the two integrity
358/// values said.
359#[derive(Debug, Clone, PartialEq, Eq)]
360pub struct Message {
361    class: Class,
362    transaction: TransactionId,
363    attributes: Vec<Attribute>,
364    integrity: Option<ReceivedIntegrity>,
365    fingerprint: bool,
366}
367
368/// A `MESSAGE-INTEGRITY` read off the wire, with the bytes it claims to cover.
369///
370/// The prefix is kept rather than the offsets into the datagram because the key is not known
371/// when the message is decoded — it depends on the `USERNAME` the message itself carries — so
372/// verification happens later, and asking the caller to hand the original datagram back at that
373/// point is an invitation to hand back a different one.
374#[derive(Debug, Clone, PartialEq, Eq)]
375struct ReceivedIntegrity {
376    tag: [u8; 20],
377    covered: Vec<u8>,
378}
379
380impl Message {
381    /// An empty Binding message of this class.
382    #[must_use]
383    pub const fn new(class: Class, transaction: TransactionId) -> Self {
384        Self {
385            class,
386            transaction,
387            attributes: Vec::new(),
388            integrity: None,
389            fingerprint: false,
390        }
391    }
392
393    /// Append an attribute.
394    ///
395    /// Order is preserved, and only two attributes have a normative position: the two
396    /// [`Message::encode`] appends itself.
397    #[must_use]
398    pub fn with(mut self, attribute: Attribute) -> Self {
399        self.attributes.push(attribute);
400        self
401    }
402
403    /// The message class.
404    #[must_use]
405    pub const fn class(&self) -> Class {
406        self.class
407    }
408
409    /// The transaction this message belongs to.
410    #[must_use]
411    pub const fn transaction(&self) -> TransactionId {
412        self.transaction
413    }
414
415    /// The attributes, in the order they appeared.
416    #[must_use]
417    pub fn attributes(&self) -> &[Attribute] {
418        &self.attributes
419    }
420
421    /// Encode, appending `MESSAGE-INTEGRITY` keyed with `key` when there is one, and then
422    /// `FINGERPRINT`, in that order and always last (RFC 5389 §15.4, §15.5).
423    ///
424    /// `key` is `None` only for RFC 8445 §11's keepalive, which "MUST NOT utilize any
425    /// authentication mechanism". `FINGERPRINT` is not optional: spec §11.1 puts it on every
426    /// check, every response and every keepalive, and it is what lets the far end tell a check
427    /// from RTP on the one port they share.
428    pub fn encode(&self, key: Option<&str>) -> Result<Vec<u8>, Error> {
429        let mut out = Vec::with_capacity(HEADER_LEN + 64);
430        out.extend_from_slice(&message_type(self.class, METHOD_BINDING).to_be_bytes());
431        // Patched below, once per integrity value, because both are computed over it.
432        out.extend_from_slice(&0u16.to_be_bytes());
433        out.extend_from_slice(&MAGIC_COOKIE.to_be_bytes());
434        out.extend_from_slice(&self.transaction);
435        for attribute in &self.attributes {
436            attribute.encode_into(&mut out, &self.transaction)?;
437        }
438        if let Some(key) = key {
439            // §15.4: the length must already count MESSAGE-INTEGRITY when the HMAC is taken.
440            set_length(&mut out, INTEGRITY_ATTR_LEN)?;
441            let tag = hmac_sha1(key.as_bytes(), &out);
442            push_attribute(&mut out, ATTR_MESSAGE_INTEGRITY, &tag)?;
443        }
444        // §15.5, and the order is the whole point: the CRC covers MESSAGE-INTEGRITY, so it is
445        // computed after it, with the length adjusted again to count FINGERPRINT itself.
446        set_length(&mut out, FINGERPRINT_ATTR_LEN)?;
447        let crc = crc32(&out) ^ FINGERPRINT_XOR;
448        push_attribute(&mut out, ATTR_FINGERPRINT, &crc.to_be_bytes())?;
449        Ok(out)
450    }
451
452    /// Read a datagram the media port's demultiplexer called STUN (RFC 5764 §5.1.2,
453    /// [`crate::dtls::classify`]).
454    ///
455    /// Nothing here trusts the datagram. A `FINGERPRINT` that does not match is an error, per
456    /// RFC 5389 §15.5, because a message whose CRC is wrong is not addressed to us however well
457    /// formed it is; a `MESSAGE-INTEGRITY` cannot be checked yet, because which key applies
458    /// depends on the `USERNAME` this message carries — see [`Message::verify_integrity`].
459    pub fn decode(datagram: &[u8]) -> Result<Self, Error> {
460        if !is_stun(datagram) {
461            return Err(Error::NotStun);
462        }
463        let (class, method) = split_type(read_u16(datagram, 0).ok_or(Error::Truncated)?);
464        if method != METHOD_BINDING {
465            return Err(Error::UnsupportedMethod(method));
466        }
467        let transaction: TransactionId = datagram
468            .get(8..HEADER_LEN)
469            .and_then(|bytes| <[u8; 12]>::try_from(bytes).ok())
470            .ok_or(Error::Truncated)?;
471        // The stated length is authoritative; a datagram carrying extra bytes is not licence to
472        // read them.
473        let stated = usize::from(read_u16(datagram, 2).ok_or(Error::Truncated)?);
474        let end = HEADER_LEN.checked_add(stated).ok_or(Error::Truncated)?;
475        let body = datagram.get(HEADER_LEN..end).ok_or(Error::Truncated)?;
476
477        let mut message = Self::new(class, transaction);
478        let mut offset = 0usize;
479        while offset < body.len() {
480            let kind = read_u16(body, offset).ok_or(Error::Truncated)?;
481            let length = usize::from(
482                read_u16(body, offset.checked_add(2).ok_or(Error::Truncated)?)
483                    .ok_or(Error::Truncated)?,
484            );
485            let start = offset.checked_add(4).ok_or(Error::Truncated)?;
486            let value = start
487                .checked_add(length)
488                .and_then(|end| body.get(start..end))
489                .ok_or(Error::Truncated)?;
490
491            match kind {
492                ATTR_MESSAGE_INTEGRITY if message.integrity.is_none() => {
493                    message.integrity = Some(ReceivedIntegrity {
494                        tag: <[u8; 20]>::try_from(value)
495                            .map_err(|_| Error::MalformedAttribute(kind))?,
496                        covered: covered_prefix(datagram, offset, INTEGRITY_ATTR_LEN)?,
497                    });
498                }
499                ATTR_FINGERPRINT => {
500                    if value.len() != 4 {
501                        return Err(Error::MalformedAttribute(kind));
502                    }
503                    let stated_crc = read_u32(value, 0).ok_or(Error::MalformedAttribute(kind))?;
504                    let prefix = covered_prefix(datagram, offset, FINGERPRINT_ATTR_LEN)?;
505                    if crc32(&prefix) ^ FINGERPRINT_XOR != stated_crc {
506                        return Err(Error::Fingerprint);
507                    }
508                    message.fingerprint = true;
509                    // §15.5: FINGERPRINT "MUST be the last attribute in the message". Whatever
510                    // follows it is not part of the message and is not read.
511                    break;
512                }
513                _ if message.integrity.is_some() => {
514                    // §15.4: with the exception of FINGERPRINT, "agents MUST ignore all other
515                    // attributes that follow MESSAGE-INTEGRITY". They fall outside the tag, so
516                    // anyone on the path can append them; honouring one would mean honouring an
517                    // unauthenticated instruction.
518                }
519                _ => message
520                    .attributes
521                    .push(Attribute::decode(kind, value, &transaction)?),
522            }
523
524            // §15 aligns attributes on 32-bit boundaries. Skipping without the padding walks
525            // into the middle of the next attribute.
526            let padded = length.checked_add(3).ok_or(Error::Truncated)? & !3;
527            offset = start.checked_add(padded).ok_or(Error::Truncated)?;
528        }
529        Ok(message)
530    }
531
532    /// Whether this message's `MESSAGE-INTEGRITY` was computed with `key`.
533    ///
534    /// `false` when there is none at all: an unauthenticated check is not a check that happens
535    /// to verify, and spec §11.3 requires that it move no state.
536    ///
537    /// Which key to pass is the direction rule — [`Peering::inbound_key`] for a check that
538    /// arrived, [`Peering::outbound_key`] for the response to one sipx sent.
539    #[must_use]
540    pub fn verify_integrity(&self, key: &str) -> bool {
541        let Some(integrity) = self.integrity.as_ref() else {
542            return false;
543        };
544        let computed = hmac_sha1(key.as_bytes(), &integrity.covered);
545        // Constant time. An `==` here is a byte-at-a-time oracle for the tag, offered to anyone
546        // who can reach the media port, and the tag is the only thing between an off-path
547        // attacker and a state change (spec §11.2, §11.3) — the same reason
548        // `sipx_sdp::fingerprint` and `sipx_rtp::srtp` compare this way.
549        computed.ct_eq(&integrity.tag).into()
550    }
551
552    /// Whether a `MESSAGE-INTEGRITY` was present at all.
553    #[must_use]
554    pub const fn has_integrity(&self) -> bool {
555        self.integrity.is_some()
556    }
557
558    /// Whether a `FINGERPRINT` was present. If it was, it matched — [`Message::decode`] rejects
559    /// one that does not.
560    #[must_use]
561    pub const fn has_fingerprint(&self) -> bool {
562        self.fingerprint
563    }
564
565    /// The `USERNAME`, if there is one.
566    #[must_use]
567    pub fn username(&self) -> Option<&str> {
568        self.attributes
569            .iter()
570            .find_map(|attribute| match attribute {
571                Attribute::Username(name) => Some(name.as_str()),
572                _ => None,
573            })
574    }
575
576    /// The `PRIORITY` a check claims for the candidate the peer would learn from it (§7.1.1).
577    #[must_use]
578    pub fn priority(&self) -> Option<Priority> {
579        self.attributes
580            .iter()
581            .find_map(|attribute| match attribute {
582                Attribute::Priority(priority) => Some(*priority),
583                _ => None,
584            })
585    }
586
587    /// Whether `USE-CANDIDATE` is set (§7.1.2).
588    #[must_use]
589    pub fn use_candidate(&self) -> bool {
590        self.attributes
591            .iter()
592            .any(|attribute| matches!(attribute, Attribute::UseCandidate))
593    }
594
595    /// The role attribute and its tiebreaker, if the peer sent one (§7.1.3).
596    ///
597    /// A peer that sends neither is not doing role signalling, which spec §7.3's last row says
598    /// is not a conflict. `nominate` reports `USE-CANDIDATE` alongside `ICE-CONTROLLING`; a
599    /// controlled peer that sets it anyway is violating §7.1.2, and the caller sees that as a
600    /// `Controlled` role with [`Message::use_candidate`] true.
601    #[must_use]
602    pub fn role(&self) -> Option<RoleAttribute> {
603        self.attributes
604            .iter()
605            .find_map(|attribute| match attribute {
606                Attribute::IceControlling(tiebreaker) => Some(RoleAttribute::Controlling {
607                    tiebreaker: *tiebreaker,
608                    nominate: self.use_candidate(),
609                }),
610                Attribute::IceControlled(tiebreaker) => Some(RoleAttribute::Controlled {
611                    tiebreaker: *tiebreaker,
612                }),
613                _ => None,
614            })
615    }
616
617    /// The `ERROR-CODE`, if this is an error response. 487 is the role conflict of §7.3.1.1.
618    #[must_use]
619    pub fn error_code(&self) -> Option<u16> {
620        self.attributes
621            .iter()
622            .find_map(|attribute| match attribute {
623                Attribute::ErrorCode { code, .. } => Some(*code),
624                _ => None,
625            })
626    }
627
628    /// The `XOR-MAPPED-ADDRESS`, unobfuscated.
629    #[must_use]
630    pub fn mapped_address(&self) -> Option<SocketAddr> {
631        self.attributes
632            .iter()
633            .find_map(|attribute| match attribute {
634                Attribute::XorMappedAddress(address) => Some(*address),
635                _ => None,
636            })
637    }
638}
639
640impl Attribute {
641    fn encode_into(&self, out: &mut Vec<u8>, transaction: &TransactionId) -> Result<(), Error> {
642        let (kind, value) = match self {
643            Self::Username(name) => (ATTR_USERNAME, name.as_bytes().to_vec()),
644            Self::Priority(priority) => (ATTR_PRIORITY, priority.get().to_be_bytes().to_vec()),
645            Self::UseCandidate => (ATTR_USE_CANDIDATE, Vec::new()),
646            Self::IceControlled(tiebreaker) => {
647                (ATTR_ICE_CONTROLLED, tiebreaker.to_be_bytes().to_vec())
648            }
649            Self::IceControlling(tiebreaker) => {
650                (ATTR_ICE_CONTROLLING, tiebreaker.to_be_bytes().to_vec())
651            }
652            Self::ErrorCode { code, reason } => {
653                (ATTR_ERROR_CODE, encode_error_code(*code, reason)?)
654            }
655            Self::XorMappedAddress(address) => (
656                ATTR_XOR_MAPPED_ADDRESS,
657                encode_xor_mapped(*address, transaction),
658            ),
659            Self::Software(text) => (ATTR_SOFTWARE, text.as_bytes().to_vec()),
660            Self::Unknown { kind, value } => {
661                if matches!(*kind, ATTR_MESSAGE_INTEGRITY | ATTR_FINGERPRINT) {
662                    return Err(Error::ReservedAttribute(*kind));
663                }
664                (*kind, value.clone())
665            }
666        };
667        push_attribute(out, kind, &value)
668    }
669
670    fn decode(kind: u16, value: &[u8], transaction: &TransactionId) -> Result<Self, Error> {
671        let malformed = || Error::MalformedAttribute(kind);
672        Ok(match kind {
673            ATTR_USERNAME => Self::Username(text(value, kind)?),
674            ATTR_SOFTWARE => Self::Software(text(value, kind)?),
675            ATTR_PRIORITY => {
676                let raw = fixed_u32(value, kind)?;
677                // §5.1 of RFC 8839 bounds a priority at 2^31 − 1, and spec §6.2 shows what an
678                // unchecked one does to the pair-priority arithmetic. A check is a place a peer
679                // can put a ten-digit number, so the bound is enforced here too.
680                Self::Priority(Priority::new(raw).ok_or_else(malformed)?)
681            }
682            ATTR_USE_CANDIDATE => {
683                if !value.is_empty() {
684                    return Err(malformed());
685                }
686                Self::UseCandidate
687            }
688            ATTR_ICE_CONTROLLED => Self::IceControlled(fixed_u64(value, kind)?),
689            ATTR_ICE_CONTROLLING => Self::IceControlling(fixed_u64(value, kind)?),
690            ATTR_ERROR_CODE => {
691                let class = u16::from(*value.get(2).ok_or_else(malformed)? & 0x07);
692                let number = u16::from(*value.get(3).ok_or_else(malformed)?);
693                let code = class
694                    .checked_mul(100)
695                    .and_then(|hundreds| hundreds.checked_add(number))
696                    .filter(|code| ERROR_CODES.contains(code))
697                    .ok_or_else(malformed)?;
698                Self::ErrorCode {
699                    code,
700                    reason: text(value.get(4..).unwrap_or_default(), kind)?,
701                }
702            }
703            ATTR_XOR_MAPPED_ADDRESS => {
704                Self::XorMappedAddress(decode_xor_mapped(value, transaction).ok_or_else(malformed)?)
705            }
706            _ => Self::Unknown {
707                kind,
708                value: value.to_vec(),
709            },
710        })
711    }
712}
713
714/// A UTF-8 attribute value. RFC 5389 §15.3 and §15.10 are both `SASLprep`-able strings, so
715/// anything that is not UTF-8 is malformed rather than lossily converted.
716fn text(value: &[u8], kind: u16) -> Result<String, Error> {
717    std::str::from_utf8(value)
718        .map(str::to_owned)
719        .map_err(|_| Error::MalformedAttribute(kind))
720}
721
722/// A four-byte attribute value, rejecting one that is merely long enough.
723fn fixed_u32(value: &[u8], kind: u16) -> Result<u32, Error> {
724    <[u8; 4]>::try_from(value)
725        .map(u32::from_be_bytes)
726        .map_err(|_| Error::MalformedAttribute(kind))
727}
728
729/// An eight-byte attribute value.
730fn fixed_u64(value: &[u8], kind: u16) -> Result<u64, Error> {
731    <[u8; 8]>::try_from(value)
732        .map(u64::from_be_bytes)
733        .map_err(|_| Error::MalformedAttribute(kind))
734}
735
736/// A big-endian `u16` at `at`, or `None` if the bytes are not there.
737fn read_u16(bytes: &[u8], at: usize) -> Option<u16> {
738    let end = at.checked_add(2)?;
739    <[u8; 2]>::try_from(bytes.get(at..end)?)
740        .ok()
741        .map(u16::from_be_bytes)
742}
743
744/// A big-endian `u32` at `at`, or `None` if the bytes are not there.
745fn read_u32(bytes: &[u8], at: usize) -> Option<u32> {
746    let end = at.checked_add(4)?;
747    <[u8; 4]>::try_from(bytes.get(at..end)?)
748        .ok()
749        .map(u32::from_be_bytes)
750}
751
752/// The bytes an integrity value covers: the header and every attribute before the one at
753/// `offset`, with the length field rewritten as though the message ended just after it.
754///
755/// This is the receiving half of [`set_length`], and it has to make the same adjustment for the
756/// same reason (RFC 5389 §15.4, §15.5). Getting it wrong here rejects every well-formed peer.
757fn covered_prefix(datagram: &[u8], offset: usize, attr_len: usize) -> Result<Vec<u8>, Error> {
758    let end = HEADER_LEN.checked_add(offset).ok_or(Error::Truncated)?;
759    let mut prefix = datagram.get(..end).ok_or(Error::Truncated)?.to_vec();
760    let body = offset.checked_add(attr_len).ok_or(Error::Truncated)?;
761    let length = u16::try_from(body).map_err(|_| Error::Truncated)?;
762    prefix
763        .get_mut(2..4)
764        .ok_or(Error::Truncated)?
765        .copy_from_slice(&length.to_be_bytes());
766    Ok(prefix)
767}
768
769/// Undo the obfuscation §15.2 applies to an address.
770fn decode_xor_mapped(value: &[u8], transaction: &TransactionId) -> Option<SocketAddr> {
771    let family = *value.get(1)?;
772    let port = read_u16(value, 2)? ^ PORT_KEY;
773    match family {
774        FAMILY_IPV4 => {
775            let raw = read_u32(value, 4)?;
776            let address = Ipv4Addr::from(raw ^ MAGIC_COOKIE);
777            Some(SocketAddr::new(IpAddr::V4(address), port))
778        }
779        FAMILY_IPV6 => {
780            let raw = <[u8; 16]>::try_from(value.get(4..20)?).ok()?;
781            let key = xor_key(transaction);
782            let mut octets = [0u8; 16];
783            for (slot, (byte, k)) in octets.iter_mut().zip(raw.into_iter().zip(key)) {
784                *slot = byte ^ k;
785            }
786            Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(octets)), port))
787        }
788        _ => None,
789    }
790}
791
792/// HMAC-SHA1, the `MESSAGE-INTEGRITY` transform (RFC 5389 §15.4).
793///
794/// RFC 8445 cites RFC 5389 and not RFC 8489, so there is no SHA-256 variant to negotiate here.
795/// The key for short-term credentials is `SASLprep(password)`; every character RFC 8839 §5.4's
796/// `ice-char` admits is ASCII alphanumeric, `+` or `/`, all of which `SASLprep` leaves alone, so
797/// the password's own bytes are the key.
798fn hmac_sha1(key: &[u8], data: &[u8]) -> [u8; 20] {
799    let mut mac = <HmacSha1 as Mac>::new_from_slice(key)
800        .unwrap_or_else(|_| unreachable!("HMAC accepts a key of any length"));
801    mac.update(data);
802    let full = mac.finalize().into_bytes();
803    let mut tag = [0u8; 20];
804    for (slot, byte) in tag.iter_mut().zip(full) {
805        *slot = byte;
806    }
807    tag
808}
809
810/// CRC-32 as IEEE 802.3 defines it, which is the one RFC 5389 §15.5 means.
811///
812/// Bit-at-a-time rather than table-driven: a connectivity check is a hundred-odd bytes and one
813/// leaves every 50 ms (spec §9), so a lookup table would cost a kilobyte of static data to save
814/// microseconds nobody is waiting for. Pinned to the standard's own check value by
815/// `the_crc_matches_the_published_check_value`, and to the IETF's by both RFC 5769 vectors.
816fn crc32(data: &[u8]) -> u32 {
817    /// The reversed representation of the IEEE polynomial.
818    const POLYNOMIAL: u32 = 0xedb8_8320;
819    let mut crc = 0xffff_ffff_u32;
820    for byte in data {
821        crc ^= u32::from(*byte);
822        for _ in 0..8 {
823            crc = if crc & 1 == 1 {
824                (crc >> 1) ^ POLYNOMIAL
825            } else {
826                crc >> 1
827            };
828        }
829    }
830    !crc
831}
832
833/// Write one attribute, padded to a 32-bit boundary (RFC 5389 §15).
834fn push_attribute(out: &mut Vec<u8>, kind: u16, value: &[u8]) -> Result<(), Error> {
835    let stated = u16::try_from(value.len()).map_err(|_| Error::TooLong)?;
836    out.extend_from_slice(&kind.to_be_bytes());
837    out.extend_from_slice(&stated.to_be_bytes());
838    out.extend_from_slice(value);
839    out.extend(std::iter::repeat_n(PAD, (4 - value.len() % 4) % 4));
840    Ok(())
841}
842
843/// Rewrite the header's length field as though the message ended `extra` bytes past what has been
844/// written.
845///
846/// RFC 5389 §15.4 and §15.5 both require exactly this before their value is computed: the length
847/// must "point to the length of the message up to, and including, the attribute itself". Leave it
848/// at the length of what has been written so far and both values are wrong — and wrong in a way
849/// that round-trips through this module perfectly and that only a real peer rejects.
850fn set_length(out: &mut [u8], extra: usize) -> Result<(), Error> {
851    let body = out
852        .len()
853        .checked_sub(HEADER_LEN)
854        .and_then(|written| written.checked_add(extra))
855        .ok_or(Error::TooLong)?;
856    let length = u16::try_from(body).map_err(|_| Error::TooLong)?;
857    out.get_mut(2..4)
858        .ok_or(Error::TooLong)?
859        .copy_from_slice(&length.to_be_bytes());
860    Ok(())
861}
862
863/// RFC 5389 §15.6: 21 reserved bits, a 3-bit class holding the hundreds digit, an 8-bit number
864/// holding the rest, then the reason phrase.
865///
866/// Three bits hold the hundreds digit, so §15.6 bounds the code: "The Class represents the
867/// hundreds digit of the error code. The value MUST be between 3 and 6." Anything outside
868/// [`ERROR_CODES`] is refused rather than folded into range — 800 masked to three bits is 0 and
869/// 65535 is 735, and a codec that quietly sends a different number than it was asked for is worse
870/// than one that will not send at all.
871fn encode_error_code(code: u16, reason: &str) -> Result<Vec<u8>, Error> {
872    if !ERROR_CODES.contains(&code) {
873        return Err(Error::MalformedAttribute(ATTR_ERROR_CODE));
874    }
875    let class = u8::try_from(code / 100).unwrap_or_default();
876    let number = u8::try_from(code % 100).unwrap_or_default();
877    let mut value = vec![0, 0, class, number];
878    value.extend_from_slice(reason.as_bytes());
879    Ok(value)
880}
881
882/// The 16-byte key §15.2 XORs an IPv6 address with: the cookie followed by the transaction ID.
883fn xor_key(transaction: &TransactionId) -> [u8; 16] {
884    let mut key = [0u8; 16];
885    let source = MAGIC_COOKIE
886        .to_be_bytes()
887        .into_iter()
888        .chain(transaction.iter().copied());
889    for (slot, byte) in key.iter_mut().zip(source) {
890        *slot = byte;
891    }
892    key
893}
894
895/// Obfuscate an address the way RFC 5389 §15.2 requires.
896///
897/// §15.2's reason, not tidiness: some NATs rewrite anything in a payload that looks like an
898/// address, and that would corrupt the very value the attribute exists to report.
899fn encode_xor_mapped(address: SocketAddr, transaction: &TransactionId) -> Vec<u8> {
900    let mut value = Vec::with_capacity(20);
901    value.push(0);
902    match address.ip() {
903        IpAddr::V4(v4) => {
904            value.push(FAMILY_IPV4);
905            value.extend_from_slice(&(address.port() ^ PORT_KEY).to_be_bytes());
906            let raw = u32::from_be_bytes(v4.octets());
907            value.extend_from_slice(&(raw ^ MAGIC_COOKIE).to_be_bytes());
908        }
909        IpAddr::V6(v6) => {
910            value.push(FAMILY_IPV6);
911            value.extend_from_slice(&(address.port() ^ PORT_KEY).to_be_bytes());
912            let key = xor_key(transaction);
913            value.extend(v6.octets().into_iter().zip(key).map(|(byte, k)| byte ^ k));
914        }
915    }
916    value
917}
918
919/// A connectivity check to send to the peer (RFC 8445 §7.1, spec §11.1).
920///
921/// The attribute order is RFC 5769 §2.1's own — `PRIORITY`, the role attribute, `USERNAME` — so
922/// that what this produces lines up with the published vector attribute for attribute. Nothing
923/// but the two integrity values has a normative position.
924pub fn connectivity_check(
925    transaction: TransactionId,
926    peering: &Peering,
927    priority: Priority,
928    role: RoleAttribute,
929) -> Result<Vec<u8>, Error> {
930    let mut message = Message::new(Class::Request, transaction)
931        .with(Attribute::Priority(priority))
932        .with(role.attribute())
933        .with(Attribute::Username(peering.outbound_username()));
934    if matches!(role, RoleAttribute::Controlling { nominate: true, .. }) {
935        message = message.with(Attribute::UseCandidate);
936    }
937    message.encode(Some(peering.outbound_key()))
938}
939
940/// The success response to a check sipx received (RFC 8445 §7.3.1.2, RFC 5389 §10.1.2).
941///
942/// `mapped` is the address the check arrived from — the peer-reflexive address the peer learns
943/// itself by, so it must be the source of the datagram and not anything out of SDP.
944///
945/// No `USERNAME`: RFC 5389 §10.1.2 asks a short-term-credential response for `MESSAGE-INTEGRITY`
946/// and nothing more, and RFC 5769 §2.2 — the IETF's own response to §2.1's request — carries
947/// none. The key is **ours**, because it is our credential the peer's check was made with.
948pub fn check_success(
949    transaction: TransactionId,
950    peering: &Peering,
951    mapped: SocketAddr,
952) -> Result<Vec<u8>, Error> {
953    Message::new(Class::Success, transaction)
954        .with(Attribute::XorMappedAddress(mapped))
955        .encode(Some(peering.inbound_key()))
956}
957
958/// The 487 Role Conflict error response (RFC 8445 §7.3.1.1).
959pub fn role_conflict(transaction: TransactionId, peering: &Peering) -> Result<Vec<u8>, Error> {
960    Message::new(Class::Error, transaction)
961        .with(Attribute::ErrorCode {
962            code: ROLE_CONFLICT,
963            reason: "Role Conflict".to_owned(),
964        })
965        .encode(Some(peering.inbound_key()))
966}
967
968/// A keepalive on a selected pair (RFC 8445 §11, RFC 8839 §6, spec §10).
969///
970/// A Binding **Indication** with `FINGERPRINT` and nothing else. §11 is unusually specific: it
971/// "MUST NOT utilize any authentication mechanism", it SHOULD carry `FINGERPRINT` so the far end
972/// can demultiplex it from media, and it SHOULD NOT carry anything more. An indication draws no
973/// response, so this proves nothing about the path — it only holds the NAT binding open.
974pub fn keepalive(transaction: TransactionId) -> Result<Vec<u8>, Error> {
975    Message::new(Class::Indication, transaction).encode(None)
976}
977
978#[cfg(test)]
979#[allow(
980    clippy::unwrap_used,
981    clippy::expect_used,
982    clippy::panic,
983    clippy::indexing_slicing
984)]
985mod tests {
986    use super::*;
987
988    /// Decode the hex listings RFC 5769 prints, so the test input is the RFC's bytes rather than
989    /// something transcribed by hand into a different shape.
990    fn hex(text: &str) -> Vec<u8> {
991        text.split_whitespace()
992            .map(|byte| u8::from_str_radix(byte, 16).expect("a hex byte"))
993            .collect()
994    }
995
996    /// RFC 5769 §2.1, the sample request — which §2.2 of the ICE spec notes is itself an ICE
997    /// connectivity check.
998    const SAMPLE_REQUEST: &str = "
999        00 01 00 58  21 12 a4 42  b7 e7 a7 01  bc 34 d6 86
1000        fa 87 df ae  80 22 00 10  53 54 55 4e  20 74 65 73
1001        74 20 63 6c  69 65 6e 74  00 24 00 04  6e 00 01 ff
1002        80 29 00 08  93 2f f9 b1  51 26 3b 36  00 06 00 09
1003        65 76 74 6a  3a 68 36 76  59 20 20 20  00 08 00 14
1004        9a ea a7 0c  bf d8 cb 56  78 1e f2 b5  b2 d3 f2 49
1005        c1 b5 71 a2  80 28 00 04  e5 7a 3b cf";
1006
1007    const SAMPLE_ID: TransactionId = [
1008        0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34, 0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae,
1009    ];
1010
1011    /// RFC 5769 §2.1's stated parameters.
1012    const SAMPLE_SOFTWARE: &str = "STUN test client";
1013    const SAMPLE_UFRAG_SENDER: &str = "h6vY";
1014    const SAMPLE_UFRAG_RECEIVER: &str = "evtj";
1015    const SAMPLE_PASSWORD: &str = "VOkJxbRl1RmTxUk/WvJxBt";
1016    const SAMPLE_PRIORITY: u32 = 0x6e00_01ff;
1017    const SAMPLE_TIEBREAKER: u64 = 0x932f_f9b1_5126_3b36;
1018
1019    /// A password for the side of the vector RFC 5769 does not state one for. Never keys
1020    /// anything the vector asserts on; it is here because [`Credentials`] will not hold a
1021    /// half-populated pair.
1022    const OTHER_PASSWORD: &str = "aPasswordTheRfcNeverStates";
1023
1024    /// The peering as the *sender* of §2.1's request sees it: its own ufrag is `h6vY`, the peer's
1025    /// is `evtj`, and the peer's password is the one the RFC states — because `USERNAME` is
1026    /// `<peer-ufrag>:<our-ufrag>` and the key is the peer's password.
1027    fn sample_sender() -> Peering {
1028        Peering::new(
1029            Credentials::new(SAMPLE_UFRAG_SENDER, OTHER_PASSWORD).expect("valid credentials"),
1030            Credentials::received(SAMPLE_UFRAG_RECEIVER, SAMPLE_PASSWORD)
1031                .expect("valid credentials"),
1032        )
1033    }
1034
1035    /// The same two agents from the other side — the one that receives §2.1's request and sends
1036    /// §2.2's response. `evtj` is now ours and `VOkJxbRl1RmTxUk/WvJxBt` is our password, which is
1037    /// what makes the RFC's two vectors a test of both directions rather than one.
1038    fn sample_receiver() -> Peering {
1039        Peering::new(
1040            Credentials::new(SAMPLE_UFRAG_RECEIVER, SAMPLE_PASSWORD).expect("valid credentials"),
1041            Credentials::received(SAMPLE_UFRAG_SENDER, OTHER_PASSWORD).expect("valid credentials"),
1042        )
1043    }
1044
1045    fn sample_priority() -> Priority {
1046        Priority::new(SAMPLE_PRIORITY).expect("in range")
1047    }
1048
1049    /// Offsets into [`SAMPLE_REQUEST`]. Stated rather than searched for, so that a test asserting
1050    /// on a slice of the vector is asserting on the part of it that it names.
1051    const REQUEST_ICE_ATTRIBUTES: std::ops::Range<usize> = 40..76;
1052    const REQUEST_INTEGRITY_TAG: std::ops::Range<usize> = 80..100;
1053    /// Where `MESSAGE-INTEGRITY` starts, counted from the end of the 20-byte header.
1054    const REQUEST_INTEGRITY_BODY_OFFSET: u16 = 56;
1055
1056    /// RFC 5769 §2.1's sample request, produced by this encoder from the parameters the RFC
1057    /// states and compared byte for byte.
1058    ///
1059    /// This is the assertion the whole module is built around, and it is the only one that is not
1060    /// self-confirming: the IETF computed `MESSAGE-INTEGRITY` and `FINGERPRINT` here, so matching
1061    /// them proves the length adjustments, the ordering, the attribute padding and the direction
1062    /// of `USERNAME` at once. A decoder tested against this encoder would prove none of it.
1063    #[test]
1064    fn a_connectivity_check_encodes_to_the_rfc_5769_sample_request() {
1065        let peering = sample_sender();
1066
1067        let message = Message::new(Class::Request, SAMPLE_ID)
1068            .with(Attribute::Software(SAMPLE_SOFTWARE.to_owned()))
1069            .with(Attribute::Priority(sample_priority()))
1070            .with(Attribute::IceControlled(SAMPLE_TIEBREAKER))
1071            .with(Attribute::Username(peering.outbound_username()));
1072
1073        assert_eq!(
1074            message
1075                .encode(Some(peering.outbound_key()))
1076                .expect("encodes"),
1077            hex(SAMPLE_REQUEST),
1078            "the encoder does not reproduce RFC 5769 §2.1"
1079        );
1080
1081        // The vector carries a SOFTWARE attribute and a check sipx sends does not (spec §11.1),
1082        // so what ties the profile helper to the same bytes is the run of ICE attributes: the
1083        // vector's PRIORITY, ICE-CONTROLLED and USERNAME, in that order and with that padding.
1084        let check = connectivity_check(
1085            SAMPLE_ID,
1086            &peering,
1087            sample_priority(),
1088            RoleAttribute::Controlled {
1089                tiebreaker: SAMPLE_TIEBREAKER,
1090            },
1091        )
1092        .expect("encodes");
1093        let vector = hex(SAMPLE_REQUEST);
1094        assert_eq!(
1095            &check[HEADER_LEN..HEADER_LEN + REQUEST_ICE_ATTRIBUTES.len()],
1096            &vector[REQUEST_ICE_ATTRIBUTES],
1097        );
1098    }
1099
1100    /// RFC 5769 §2.2, the sample IPv4 success response — the answer to §2.1's request, keyed with
1101    /// the same password and so, in ICE's terms, keyed with *ours*.
1102    ///
1103    /// A second published vector, and the only one that exercises `XOR-MAPPED-ADDRESS` in the
1104    /// encoding direction. Its `SOFTWARE` value is eleven bytes, so it also pins the padding byte
1105    /// a second time and independently.
1106    #[test]
1107    fn a_success_response_encodes_to_the_rfc_5769_sample_response() {
1108        const SAMPLE_RESPONSE: &str = "
1109            01 01 00 3c  21 12 a4 42  b7 e7 a7 01  bc 34 d6 86
1110            fa 87 df ae  80 22 00 0b  74 65 73 74  20 76 65 63
1111            74 6f 72 20  00 20 00 08  00 01 a1 47  e1 12 a6 43
1112            00 08 00 14  2b 91 f5 99  fd 9e 90 c3  8c 74 89 f9
1113            2a f9 ba 53  f0 6b e7 d7  80 28 00 04  c0 7d 4c 96";
1114        /// The address the RFC states its own vector carries. Not computed here — the point of a
1115        /// published vector is that the expected value comes from the publisher.
1116        const MAPPED: &str = "192.0.2.1:32853";
1117        const RESPONSE_MAPPED_ADDRESS: std::ops::Range<usize> = 36..48;
1118
1119        let peering = sample_receiver();
1120        let mapped: SocketAddr = MAPPED.parse().expect("valid");
1121
1122        let message = Message::new(Class::Success, SAMPLE_ID)
1123            .with(Attribute::Software("test vector".to_owned()))
1124            .with(Attribute::XorMappedAddress(mapped));
1125
1126        assert_eq!(
1127            message
1128                .encode(Some(peering.inbound_key()))
1129                .expect("encodes"),
1130            hex(SAMPLE_RESPONSE),
1131            "the encoder does not reproduce RFC 5769 §2.2"
1132        );
1133
1134        let response = check_success(SAMPLE_ID, &peering, mapped).expect("encodes");
1135        let vector = hex(SAMPLE_RESPONSE);
1136        assert_eq!(
1137            &response[HEADER_LEN..HEADER_LEN + RESPONSE_MAPPED_ADDRESS.len()],
1138            &vector[RESPONSE_MAPPED_ADDRESS],
1139        );
1140    }
1141
1142    /// RFC 5389 §15.4's length adjustment, shown to be the difference between the IETF's tag and
1143    /// a wrong one.
1144    ///
1145    /// The vector's own length field says 88 — the whole message, `FINGERPRINT` included. The
1146    /// HMAC is taken over 80: everything up to and including `MESSAGE-INTEGRITY`. Skip the
1147    /// adjustment and the message still round-trips through this module; it is only a real peer
1148    /// that rejects it, which is why the assertion is on the published tag.
1149    #[test]
1150    fn the_integrity_is_taken_over_the_adjusted_length_and_not_the_real_one() {
1151        let vector = hex(SAMPLE_REQUEST);
1152        assert_eq!(vector.len(), 108, "RFC 5769 §2.1 is 108 bytes");
1153        assert_eq!(&vector[2..4], &[0x00, 0x58], "the vector's real length, 88");
1154
1155        let mut covered = vector[..76].to_vec();
1156        let real = hmac_sha1(SAMPLE_PASSWORD.as_bytes(), &covered);
1157        assert_ne!(
1158            &real[..],
1159            &vector[REQUEST_INTEGRITY_TAG],
1160            "the real length must not produce the published tag"
1161        );
1162
1163        let adjusted = REQUEST_INTEGRITY_BODY_OFFSET + 24;
1164        assert_eq!(adjusted, 80);
1165        covered[2..4].copy_from_slice(&adjusted.to_be_bytes());
1166        assert_eq!(
1167            &hmac_sha1(SAMPLE_PASSWORD.as_bytes(), &covered)[..],
1168            &vector[REQUEST_INTEGRITY_TAG],
1169            "the adjusted length must"
1170        );
1171    }
1172
1173    /// RFC 5389 §15.5: `FINGERPRINT` is computed last and is last, over a message that already
1174    /// contains `MESSAGE-INTEGRITY`. Swap the two and neither value is right.
1175    #[test]
1176    fn the_integrity_comes_before_the_fingerprint_and_both_come_last() {
1177        let peering = sample_sender();
1178        let check = connectivity_check(
1179            SAMPLE_ID,
1180            &peering,
1181            sample_priority(),
1182            RoleAttribute::Controlling {
1183                tiebreaker: SAMPLE_TIEBREAKER,
1184                nominate: true,
1185            },
1186        )
1187        .expect("encodes");
1188
1189        let integrity = check.len() - INTEGRITY_ATTR_LEN - FINGERPRINT_ATTR_LEN;
1190        let fingerprint = check.len() - FINGERPRINT_ATTR_LEN;
1191        assert_eq!(
1192            read_u16(&check, integrity),
1193            Some(ATTR_MESSAGE_INTEGRITY),
1194            "MESSAGE-INTEGRITY is second to last"
1195        );
1196        assert_eq!(
1197            read_u16(&check, fingerprint),
1198            Some(ATTR_FINGERPRINT),
1199            "FINGERPRINT is last"
1200        );
1201
1202        // The CRC covers MESSAGE-INTEGRITY, so recomputing it over the message as sent must
1203        // reproduce the value on the wire.
1204        let expected = crc32(&check[..fingerprint]) ^ FINGERPRINT_XOR;
1205        assert_eq!(read_u32(&check, fingerprint + 4), Some(expected));
1206
1207        let decoded = Message::decode(&check).expect("our own check decodes");
1208        assert!(decoded.has_integrity() && decoded.has_fingerprint());
1209        assert!(decoded.verify_integrity(peering.outbound_key()));
1210    }
1211
1212    /// The direction rule of spec §11.2, against the IETF's own bytes in both directions.
1213    ///
1214    /// The two peerings are the same pair of agents seen from either end. `evtj` sends nothing in
1215    /// this test; it only reads. Reverse either accessor and one of these four assertions fails —
1216    /// which is the point, because on the wire the reversal looks like a blocked path.
1217    #[test]
1218    fn the_username_and_key_of_a_check_depend_on_which_way_it_travels() {
1219        let sender = sample_sender();
1220        let receiver = sample_receiver();
1221
1222        assert_eq!(sender.outbound_username(), "evtj:h6vY");
1223        assert_eq!(sender.outbound_key(), SAMPLE_PASSWORD);
1224        assert_eq!(receiver.inbound_username(), "evtj:h6vY");
1225        assert_eq!(receiver.inbound_key(), SAMPLE_PASSWORD);
1226        assert_eq!(sender.inbound_username(), "h6vY:evtj");
1227        assert_eq!(sender.inbound_key(), OTHER_PASSWORD);
1228        assert_eq!(receiver.outbound_username(), "h6vY:evtj");
1229        assert_eq!(receiver.outbound_key(), OTHER_PASSWORD);
1230
1231        // What the receiver of RFC 5769 §2.1's request must conclude about it.
1232        let arrived = Message::decode(&hex(SAMPLE_REQUEST)).expect("decodes");
1233        assert_eq!(
1234            arrived.username(),
1235            Some(receiver.inbound_username()).as_deref()
1236        );
1237        assert!(
1238            arrived.verify_integrity(receiver.inbound_key()),
1239            "a check that arrived is keyed with our password"
1240        );
1241        assert!(
1242            !arrived.verify_integrity(receiver.outbound_key()),
1243            "keying an inbound check with the peer's password answers nothing and looks like a \
1244             network fault"
1245        );
1246    }
1247
1248    /// Every attribute spec §11.1 lists, out and back (RFC 5389 §15, RFC 8445 §7.1).
1249    #[test]
1250    fn every_profile_attribute_encodes_as_well_as_decodes() {
1251        let attributes = vec![
1252            Attribute::Username("evtj:h6vY".to_owned()),
1253            Attribute::Priority(sample_priority()),
1254            Attribute::UseCandidate,
1255            Attribute::IceControlling(SAMPLE_TIEBREAKER),
1256            Attribute::ErrorCode {
1257                code: ROLE_CONFLICT,
1258                reason: "Role Conflict".to_owned(),
1259            },
1260            Attribute::XorMappedAddress("192.0.2.1:32853".parse().expect("valid")),
1261            Attribute::Software(SAMPLE_SOFTWARE.to_owned()),
1262            Attribute::Unknown {
1263                kind: 0x8050,
1264                value: vec![1, 2, 3],
1265            },
1266        ];
1267        let message = attributes
1268            .iter()
1269            .cloned()
1270            .fold(Message::new(Class::Request, SAMPLE_ID), Message::with);
1271        let bytes = message.encode(Some(SAMPLE_PASSWORD)).expect("encodes");
1272        let decoded = Message::decode(&bytes).expect("decodes");
1273
1274        assert_eq!(decoded.attributes(), attributes.as_slice());
1275        assert_eq!(decoded.class(), Class::Request);
1276        assert_eq!(decoded.transaction(), SAMPLE_ID);
1277        assert_eq!(decoded.error_code(), Some(487));
1278        assert_eq!(decoded.priority(), Some(sample_priority()));
1279        assert_eq!(
1280            decoded.mapped_address(),
1281            Some("192.0.2.1:32853".parse().expect("valid"))
1282        );
1283        assert_eq!(
1284            decoded.role(),
1285            Some(RoleAttribute::Controlling {
1286                tiebreaker: SAMPLE_TIEBREAKER,
1287                nominate: true,
1288            })
1289        );
1290
1291        // ICE-CONTROLLED is the other half of §7.1.3, and the same message cannot carry both.
1292        let controlled = Message::new(Class::Request, SAMPLE_ID)
1293            .with(Attribute::IceControlled(SAMPLE_TIEBREAKER))
1294            .encode(None)
1295            .expect("encodes");
1296        assert_eq!(
1297            Message::decode(&controlled).expect("decodes").role(),
1298            Some(RoleAttribute::Controlled {
1299                tiebreaker: SAMPLE_TIEBREAKER
1300            })
1301        );
1302    }
1303
1304    /// `XOR-MAPPED-ADDRESS` for IPv6 extends the key with the transaction ID (§15.2). RFC 5769
1305    /// §2.3's IPv6 response is for a different transaction ID than §2.1's, so this is a
1306    /// round-trip rather than a vector — the encoding direction is pinned by §2.2 above.
1307    #[test]
1308    fn an_ipv6_mapped_address_round_trips() {
1309        let address: SocketAddr = "[2001:db8::1]:32853".parse().expect("valid");
1310        let value = encode_xor_mapped(address, &SAMPLE_ID);
1311        assert_eq!(value.len(), 20);
1312        assert_ne!(&value[4..20], &[0u8; 16], "the address must be obfuscated");
1313        assert_eq!(decode_xor_mapped(&value, &SAMPLE_ID), Some(address));
1314        assert_ne!(
1315            decode_xor_mapped(&value, &[0u8; 12]),
1316            Some(address),
1317            "the transaction ID is part of the key"
1318        );
1319    }
1320
1321    /// `USE-CANDIDATE` is a flag: RFC 8445 §7.1.2 gives it no value at all.
1322    #[test]
1323    fn use_candidate_is_a_zero_length_flag_only_the_controlling_agent_can_send() {
1324        let peering = sample_sender();
1325        let nominating = connectivity_check(
1326            SAMPLE_ID,
1327            &peering,
1328            sample_priority(),
1329            RoleAttribute::Controlling {
1330                tiebreaker: SAMPLE_TIEBREAKER,
1331                nominate: true,
1332            },
1333        )
1334        .expect("encodes");
1335        let decoded = Message::decode(&nominating).expect("decodes");
1336        assert!(decoded.use_candidate());
1337        assert!(decoded.attributes().contains(&Attribute::UseCandidate));
1338
1339        // On the wire the flag is four bytes of attribute header and no value.
1340        let flag = Message::new(Class::Request, SAMPLE_ID)
1341            .with(Attribute::UseCandidate)
1342            .encode(None)
1343            .expect("encodes");
1344        assert_eq!(read_u16(&flag, HEADER_LEN), Some(ATTR_USE_CANDIDATE));
1345        assert_eq!(read_u16(&flag, HEADER_LEN + 2), Some(0), "zero length");
1346
1347        // §7.1.2: the controlled agent MUST NOT send it, which `RoleAttribute::Controlled` has
1348        // no way to express.
1349        let controlled = connectivity_check(
1350            SAMPLE_ID,
1351            &peering,
1352            sample_priority(),
1353            RoleAttribute::Controlled {
1354                tiebreaker: SAMPLE_TIEBREAKER,
1355            },
1356        )
1357        .expect("encodes");
1358        assert!(
1359            !Message::decode(&controlled)
1360                .expect("decodes")
1361                .use_candidate()
1362        );
1363    }
1364
1365    /// RFC 8445 §7.3.1.1's answer to a role conflict, and RFC 5389 §15.6's split of the code.
1366    #[test]
1367    fn a_role_conflict_is_a_487_error_response() {
1368        let peering = sample_receiver();
1369        let bytes = role_conflict(SAMPLE_ID, &peering).expect("encodes");
1370        let decoded = Message::decode(&bytes).expect("decodes");
1371
1372        assert_eq!(decoded.class(), Class::Error);
1373        assert_eq!(decoded.error_code(), Some(ROLE_CONFLICT));
1374        assert!(
1375            decoded.verify_integrity(peering.inbound_key()),
1376            "a response to a check that arrived is keyed with our password"
1377        );
1378        // §15.6 puts the hundreds digit in a 3-bit class and the rest in a byte: 487 is 4 then 87.
1379        assert_eq!(&bytes[HEADER_LEN + 4..HEADER_LEN + 8], &[0, 0, 4, 87]);
1380    }
1381
1382    /// Spec §10 and RFC 8445 §11: a keepalive is a Binding Indication with `FINGERPRINT`, no
1383    /// credential, and nothing else.
1384    #[test]
1385    fn a_keepalive_is_a_binding_indication_with_a_fingerprint_and_nothing_else() {
1386        let bytes = keepalive(SAMPLE_ID).expect("encodes");
1387        assert_eq!(
1388            bytes.len(),
1389            HEADER_LEN + FINGERPRINT_ATTR_LEN,
1390            "a header and one attribute"
1391        );
1392        assert_eq!(&bytes[0..2], &[0x00, 0x11], "Binding Indication");
1393
1394        let decoded = Message::decode(&bytes).expect("decodes");
1395        assert_eq!(decoded.class(), Class::Indication);
1396        assert!(decoded.attributes().is_empty(), "§11: nothing else");
1397        assert!(decoded.has_fingerprint(), "§11 SHOULD, for demultiplexing");
1398        assert!(
1399            !decoded.has_integrity(),
1400            "§11: MUST NOT utilize any authentication mechanism"
1401        );
1402        assert!(
1403            !decoded.verify_integrity(SAMPLE_PASSWORD),
1404            "an unauthenticated message never verifies against any key"
1405        );
1406    }
1407
1408    /// RFC 5389 §15.5: a message whose `FINGERPRINT` is wrong is not addressed to us, however
1409    /// well formed the rest of it is.
1410    #[test]
1411    fn a_fingerprint_that_does_not_match_is_a_dropped_datagram() {
1412        let mut bytes = hex(SAMPLE_REQUEST);
1413        let last = bytes.len() - 1;
1414        bytes[last] ^= 0x01;
1415        assert_eq!(Message::decode(&bytes), Err(Error::Fingerprint));
1416    }
1417
1418    /// RFC 5389 §15.4: with the exception of `FINGERPRINT`, everything after `MESSAGE-INTEGRITY`
1419    /// is ignored. It falls outside the tag, so anyone on the path can put it there.
1420    #[test]
1421    fn an_attribute_appended_after_message_integrity_is_ignored() {
1422        let peering = sample_sender();
1423        let honest = connectivity_check(
1424            SAMPLE_ID,
1425            &peering,
1426            sample_priority(),
1427            RoleAttribute::Controlled {
1428                tiebreaker: SAMPLE_TIEBREAKER,
1429            },
1430        )
1431        .expect("encodes");
1432
1433        // Splice USE-CANDIDATE in between MESSAGE-INTEGRITY and FINGERPRINT, and repair the
1434        // length and the CRC so that only the tag can tell.
1435        let split = honest.len() - FINGERPRINT_ATTR_LEN;
1436        let mut forged = honest[..split].to_vec();
1437        push_attribute(&mut forged, ATTR_USE_CANDIDATE, &[]).expect("encodes");
1438        set_length(&mut forged, FINGERPRINT_ATTR_LEN).expect("fits");
1439        let crc = crc32(&forged) ^ FINGERPRINT_XOR;
1440        push_attribute(&mut forged, ATTR_FINGERPRINT, &crc.to_be_bytes()).expect("encodes");
1441
1442        let decoded = Message::decode(&forged).expect("decodes");
1443        assert!(decoded.has_fingerprint(), "the CRC was repaired");
1444        assert!(
1445            decoded.verify_integrity(peering.outbound_key()),
1446            "the bytes the tag covers are untouched"
1447        );
1448        assert!(
1449            !decoded.use_candidate(),
1450            "an unauthenticated USE-CANDIDATE must not nominate a pair"
1451        );
1452    }
1453
1454    /// The two integrity values are computed, never supplied. `Attribute::Unknown` is the only
1455    /// way a caller could name their types, and it is refused.
1456    ///
1457    /// Not reachable from the wire — `Message::decode` matches both types before it builds an
1458    /// `Unknown` — but very reachable from an agent assembling a message by hand, and a message
1459    /// carrying two `MESSAGE-INTEGRITY` attributes authenticates as nothing.
1460    #[test]
1461    fn an_unknown_attribute_cannot_smuggle_in_an_integrity_value() {
1462        for kind in [ATTR_MESSAGE_INTEGRITY, ATTR_FINGERPRINT] {
1463            let forged = Message::new(Class::Request, SAMPLE_ID)
1464                .with(Attribute::Unknown {
1465                    kind,
1466                    value: vec![0; 20],
1467                })
1468                .with(Attribute::Username("evtj:h6vY".to_owned()));
1469            assert_eq!(
1470                forged.encode(Some(SAMPLE_PASSWORD)),
1471                Err(Error::ReservedAttribute(kind))
1472            );
1473        }
1474
1475        // Every other type still passes through untouched.
1476        let passthrough = Message::new(Class::Request, SAMPLE_ID)
1477            .with(Attribute::Unknown {
1478                kind: 0x8050,
1479                value: vec![1, 2, 3],
1480            })
1481            .encode(None)
1482            .expect("encodes");
1483        assert!(Message::decode(&passthrough).is_ok());
1484    }
1485
1486    /// RFC 5389 §15.6 gives the hundreds digit three bits and says "The value MUST be between 3
1487    /// and 6", so a code outside that is not encodable. Folding it into range instead sends a
1488    /// different number than the caller asked for: 800 becomes 0, and 65535 becomes 735.
1489    #[test]
1490    fn an_error_code_outside_rfc_5389s_range_is_refused_rather_than_folded() {
1491        for code in [0, 99, 299, 700, 800, 1000, u16::MAX] {
1492            let message = Message::new(Class::Error, SAMPLE_ID).with(Attribute::ErrorCode {
1493                code,
1494                reason: String::new(),
1495            });
1496            assert_eq!(
1497                message.encode(Some(SAMPLE_PASSWORD)),
1498                Err(Error::MalformedAttribute(ATTR_ERROR_CODE)),
1499                "{code} is not an error code §15.6 defines"
1500            );
1501        }
1502        for code in [*ERROR_CODES.start(), ROLE_CONFLICT, *ERROR_CODES.end()] {
1503            let bytes = Message::new(Class::Error, SAMPLE_ID)
1504                .with(Attribute::ErrorCode {
1505                    code,
1506                    reason: "because".to_owned(),
1507                })
1508                .encode(None)
1509                .expect("encodes");
1510            assert_eq!(
1511                Message::decode(&bytes).expect("decodes").error_code(),
1512                Some(code)
1513            );
1514        }
1515
1516        // And the same bound on the way in: class bits 0 and 7 are not codes §15.6 defines.
1517        let mut bytes = role_conflict(SAMPLE_ID, &sample_receiver()).expect("encodes");
1518        bytes[HEADER_LEN + 6] = 7;
1519        let split = bytes.len() - FINGERPRINT_ATTR_LEN;
1520        let crc = crc32(&bytes[..split]) ^ FINGERPRINT_XOR;
1521        bytes[split + 4..].copy_from_slice(&crc.to_be_bytes());
1522        assert_eq!(
1523            Message::decode(&bytes),
1524            Err(Error::MalformedAttribute(ATTR_ERROR_CODE))
1525        );
1526    }
1527
1528    /// Spec §6.2: an unchecked priority is what overflows the pair-priority arithmetic, and a
1529    /// connectivity check is a place a peer can put one.
1530    #[test]
1531    fn a_priority_outside_rfc_8839s_range_is_rejected() {
1532        let mut bytes = Message::new(Class::Request, SAMPLE_ID)
1533            .with(Attribute::Priority(Priority::MAX))
1534            .encode(None)
1535            .expect("encodes");
1536        assert!(Message::decode(&bytes).is_ok());
1537
1538        // Raise it one past 2^31 − 1 and repair the CRC, so it is the range check that rejects it.
1539        bytes[HEADER_LEN + 4..HEADER_LEN + 8].copy_from_slice(&0x8000_0000_u32.to_be_bytes());
1540        let split = bytes.len() - FINGERPRINT_ATTR_LEN;
1541        let crc = crc32(&bytes[..split]) ^ FINGERPRINT_XOR;
1542        bytes[split + 4..].copy_from_slice(&crc.to_be_bytes());
1543        assert_eq!(
1544            Message::decode(&bytes),
1545            Err(Error::MalformedAttribute(ATTR_PRIORITY))
1546        );
1547    }
1548
1549    /// Acceptance's live invariant: this parser is handed unauthenticated datagrams by anyone who
1550    /// can reach the media port. Every prefix of a message the RFC itself publishes is a
1551    /// plausible truncation, and none of them may panic.
1552    #[test]
1553    fn no_prefix_of_a_real_message_panics() {
1554        let bytes = hex(SAMPLE_REQUEST);
1555        for length in 0..=bytes.len() {
1556            let _ = Message::decode(&bytes[..length]);
1557        }
1558    }
1559
1560    /// Nor may any single-byte corruption of one — which reaches every length field, every
1561    /// attribute type and both integrity values.
1562    #[test]
1563    fn no_single_byte_corruption_of_a_real_message_panics() {
1564        let bytes = hex(SAMPLE_REQUEST);
1565        for index in 0..bytes.len() {
1566            for pattern in [0x00, 0x01, 0x7f, 0x80, 0xff] {
1567                let mut corrupted = bytes.clone();
1568                corrupted[index] = pattern;
1569                let _ = Message::decode(&corrupted);
1570            }
1571        }
1572    }
1573
1574    /// And nor may arbitrary bytes behind a well-formed header, which is what an attacker sends.
1575    ///
1576    /// Deterministic rather than randomised: a fuzz finding that cannot be reproduced from the
1577    /// test file is a fuzz finding nobody fixes.
1578    #[test]
1579    fn arbitrary_bytes_behind_a_valid_stun_header_never_panic() {
1580        let mut seed = 0x5354_554e_u64;
1581        let mut next = || {
1582            seed = seed
1583                .wrapping_mul(6_364_136_223_846_793_005)
1584                .wrapping_add(1_442_695_040_888_963_407);
1585            u8::try_from(seed >> 56).unwrap_or_default()
1586        };
1587        for _ in 0..2_000 {
1588            let body_len = usize::from(next()) * 2;
1589            let mut datagram = Vec::with_capacity(HEADER_LEN + body_len);
1590            datagram.extend_from_slice(&[0x00, 0x01]);
1591            datagram.extend_from_slice(&u16::try_from(body_len).unwrap_or_default().to_be_bytes());
1592            datagram.extend_from_slice(&MAGIC_COOKIE.to_be_bytes());
1593            datagram.extend_from_slice(&SAMPLE_ID);
1594            datagram.extend((0..body_len).map(|_| next()));
1595            let _ = Message::decode(&datagram);
1596        }
1597    }
1598
1599    /// A length field that claims more than arrived is the classic way into a panic.
1600    #[test]
1601    fn a_length_field_past_the_end_is_an_error() {
1602        let mut bytes = hex(SAMPLE_REQUEST);
1603        bytes[2..4].copy_from_slice(&u16::MAX.to_be_bytes());
1604        assert_eq!(Message::decode(&bytes), Err(Error::Truncated));
1605
1606        // The same one attribute down: an attribute claiming to run past the body.
1607        let mut bytes = hex(SAMPLE_REQUEST);
1608        bytes[22..24].copy_from_slice(&u16::MAX.to_be_bytes());
1609        assert_eq!(Message::decode(&bytes), Err(Error::Truncated));
1610    }
1611
1612    /// Anything that is not STUN, and anything that is STUN but not Binding, is refused before a
1613    /// single attribute is read.
1614    #[test]
1615    fn a_datagram_that_is_not_a_binding_message_is_refused() {
1616        assert_eq!(Message::decode(&[]), Err(Error::NotStun));
1617        assert_eq!(
1618            Message::decode(b"INVITE sip:bob@example.com SIP/2.0\r\n\r\n"),
1619            Err(Error::NotStun)
1620        );
1621
1622        let mut allocate = keepalive(SAMPLE_ID).expect("encodes");
1623        allocate[0..2].copy_from_slice(&0x0003_u16.to_be_bytes());
1624        assert_eq!(Message::decode(&allocate), Err(Error::UnsupportedMethod(3)));
1625    }
1626
1627    /// Our own checks must pass the §7.3 test the transport crate already implements — that is
1628    /// what makes them demultiplexable at the far end.
1629    #[test]
1630    fn what_this_module_encodes_is_stun_by_the_transport_crates_own_test() {
1631        let peering = sample_sender();
1632        for bytes in [
1633            connectivity_check(
1634                SAMPLE_ID,
1635                &peering,
1636                sample_priority(),
1637                RoleAttribute::Controlled {
1638                    tiebreaker: SAMPLE_TIEBREAKER,
1639                },
1640            )
1641            .expect("encodes"),
1642            check_success(
1643                SAMPLE_ID,
1644                &peering,
1645                "192.0.2.1:32853".parse().expect("valid"),
1646            )
1647            .expect("encodes"),
1648            role_conflict(SAMPLE_ID, &peering).expect("encodes"),
1649            keepalive(SAMPLE_ID).expect("encodes"),
1650        ] {
1651            assert!(is_stun(&bytes), "{bytes:02x?}");
1652            assert_eq!(crate::dtls::classify(&bytes), crate::dtls::Arriving::Stun);
1653        }
1654    }
1655
1656    /// The check value every description of CRC-32 publishes for the ASCII digits.
1657    #[test]
1658    fn the_crc_matches_the_published_check_value() {
1659        assert_eq!(crc32(b"123456789"), 0xcbf4_3926);
1660    }
1661
1662    #[test]
1663    fn the_port_key_is_the_top_half_of_the_cookie() {
1664        assert_eq!(u32::from(PORT_KEY) << 16, MAGIC_COOKIE & 0xffff_0000);
1665    }
1666
1667    /// RFC 5389 §6's class and method interleave, over the four classes Binding has.
1668    #[test]
1669    fn the_message_type_round_trips_through_the_class_bits() {
1670        for (class, raw) in [
1671            (Class::Request, 0x0001),
1672            (Class::Indication, 0x0011),
1673            (Class::Success, 0x0101),
1674            (Class::Error, 0x0111),
1675        ] {
1676            assert_eq!(message_type(class, METHOD_BINDING), raw);
1677            assert_eq!(split_type(raw), (class, METHOD_BINDING));
1678        }
1679    }
1680
1681    /// Two transaction IDs differ, because the one thing they must not do is repeat.
1682    #[test]
1683    fn a_transaction_id_is_fresh_each_time() {
1684        assert_ne!(new_transaction_id(), new_transaction_id());
1685    }
1686}