Skip to main content

moqtap_codec/
auth_token.rs

1use crate::error::CodecError;
2use crate::varint::{MoqtProfile, VarInt, VarIntError};
3use bytes::{Buf, BufMut};
4
5/// The parameter type carrying an Authorization Token on drafts 12 and later.
6///
7/// Drafts 12 through 19 all number it 0x03, in both the message parameter and
8/// the setup namespace. Draft-11 numbers the message parameter 0x01 and has no
9/// setup parameter of this kind at all, so a draft-11 setup 0x01 is a PATH and
10/// is not a token; [`AUTH_TOKEN_PARAMETER_D11`] is the one it does have.
11pub const AUTH_TOKEN_PARAMETER: u64 = 0x03;
12
13/// The parameter type carrying an Authorization Token on draft-11.
14///
15/// Draft-11 Section 8.2.1.1 gives the AUTHORIZATION TOKEN parameter type 0x01
16/// among the version-specific parameters. Draft-12 renumbered it to
17/// [`AUTH_TOKEN_PARAMETER`] and added it to the setup namespace beside it.
18pub const AUTH_TOKEN_PARAMETER_D11: u64 = 0x01;
19
20/// What a Token's Alias Type says about the fields that follow it.
21///
22/// Drafts 11 through 19 all define the same four code points and describe them
23/// as deciding the serialization, not merely the behaviour: "Alias Type - an
24/// integer defining both the serialization and the processing behavior of the
25/// receiver." A reader that does not recognise the value therefore cannot know
26/// which of the three optional fields are present, which is why an unassigned
27/// Alias Type is a formatting error rather than something to skip past.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum TokenAliasType {
30    /// DELETE (0x0). Carries an Alias and retires it: "This Alias and the Token
31    /// Value it was previously associated with MUST be retired."
32    Delete,
33    /// REGISTER (0x1). Carries an Alias, a Token Type and a Token Value, and
34    /// binds the Alias to that value for the rest of the session.
35    Register,
36    /// USE_ALIAS (0x2). Carries an Alias alone, standing for the Token Type and
37    /// Token Value registered under it earlier.
38    UseAlias,
39    /// USE_VALUE (0x3). Carries a Token Type and Token Value and no Alias: "Use
40    /// the Token Value as provided. The Token Value may be discarded after
41    /// processing."
42    UseValue,
43}
44
45impl TokenAliasType {
46    /// The Alias Type this code point names, or `None` if no draft in the range
47    /// assigns it.
48    pub const fn from_id(id: u64) -> Option<Self> {
49        match id {
50            0x0 => Some(Self::Delete),
51            0x1 => Some(Self::Register),
52            0x2 => Some(Self::UseAlias),
53            0x3 => Some(Self::UseValue),
54            _ => None,
55        }
56    }
57
58    /// The code point for this Alias Type.
59    pub const fn id(self) -> u64 {
60        match self {
61            Self::Delete => 0x0,
62            Self::Register => 0x1,
63            Self::UseAlias => 0x2,
64            Self::UseValue => 0x3,
65        }
66    }
67
68    /// Whether a Token Alias field follows the Alias Type.
69    ///
70    /// Three of the four carry one. USE_VALUE is the exception — "There is no
71    /// Alias and there is a Type and Value" — and it is the only form a sender
72    /// can use before it has registered anything.
73    pub const fn has_alias(self) -> bool {
74        !matches!(self, Self::UseValue)
75    }
76
77    /// Whether a Token Type and a Token Value follow.
78    ///
79    /// The two travel together in every form: REGISTER and USE_VALUE carry
80    /// both, DELETE and USE_ALIAS carry neither, and no form carries one
81    /// without the other.
82    pub const fn has_type_and_value(self) -> bool {
83        matches!(self, Self::Register | Self::UseValue)
84    }
85}
86
87/// The Token structure an AUTHORIZATION TOKEN parameter carries as its value.
88///
89/// Drafts 11 through 19 serialize it as
90///
91/// ```text
92/// Token {
93///   Alias Type (i),
94///   [Token Alias (i),]
95///   [Token Type (i),]
96///   [Token Value (..)]
97/// }
98/// ```
99///
100/// with the Alias Type deciding which of the bracketed fields are present. The
101/// Token Value has no length of its own and runs to the end of the parameter
102/// value, which is what bounds it — draft-19 Section 1.4.3 says so outright:
103/// "Key-Value-Pairs are always parsed with a known byte length, which bounds the
104/// sequence."
105///
106/// The structure has not moved across the nine drafts that define it. Only the
107/// integer encoding has: drafts 11 through 16 write the three integers as QUIC
108/// variable-length integers and drafts 17 and later as MoQT ones, which is the
109/// only difference between [`decode`](Self::decode) and
110/// [`decode_moqt`](Self::decode_moqt).
111///
112/// The contents of the Token Value are deliberately opaque here. "The contents
113/// and serialization of this payload are defined by the Token Type", and that
114/// registry is not this codec's to interpret — Token Type 0 is reserved for a
115/// meaning "negotiated out-of-band between client and receiver", so no reader
116/// can hold the value to a shape without knowing what the two peers agreed.
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct AuthorizationToken {
119    /// Which of the fields below are present, and what the receiver does with
120    /// them.
121    pub alias_type: TokenAliasType,
122    /// The Session-specific Alias, present on every form but USE_VALUE.
123    pub alias: Option<u64>,
124    /// The Token Type, present on REGISTER and USE_VALUE.
125    pub token_type: Option<u64>,
126    /// The Token Value, present on REGISTER and USE_VALUE and running to the
127    /// end of the parameter value. Empty on DELETE and USE_ALIAS.
128    pub value: Vec<u8>,
129}
130
131/// Report a Token that cannot be decoded under the rule its draft states.
132///
133/// Drafts 15 through 19 give this rule its own sentence — "If the Token
134/// structure cannot be decoded, the receiver MUST close the Session with
135/// KEY_VALUE_FORMATTING_ERROR". Drafts 12, 13 and 14 state it in those same
136/// words and name the code in prose instead, as Key-Value Formatting error —
137/// draft-14 doing so even though its own registry and its general
138/// Key-Value-Pair rule already spell KEY_VALUE_FORMATTING_ERROR. Draft-11 has
139/// no sentence of its own and reaches the same answer through the general one
140/// in Section 1.3.2, which covers any Type whose value does not match the
141/// serialization that Type defines.
142fn malformed(key: u64, detail: &'static str) -> CodecError {
143    CodecError::KeyValueFormatting { key, detail }
144}
145
146impl AuthorizationToken {
147    /// Decode a Token from a parameter value written with QUIC variable-length
148    /// integers, as drafts 11 through 16 write them.
149    ///
150    /// `key` is the parameter type the value arrived under. It is carried into
151    /// the error rather than used for the parse: the caller knows which
152    /// namespace it read, and a report naming 0x03 when the frame said 0x01
153    /// sends the reader to the wrong table.
154    pub fn decode(key: u64, bytes: &[u8]) -> Result<Self, CodecError> {
155        Self::decode_with(key, bytes, |buf: &mut &[u8]| VarInt::decode(buf))
156    }
157
158    /// Decode a Token from a parameter value written with MoQT variable-length
159    /// integers, as drafts 17 and later write them.
160    pub fn decode_moqt<P: MoqtProfile>(key: u64, bytes: &[u8]) -> Result<Self, CodecError> {
161        Self::decode_with(key, bytes, |buf: &mut &[u8]| VarInt::decode_moqt::<P>(buf))
162    }
163
164    /// The body both readers share, taking the integer encoding as a parameter.
165    ///
166    /// Every refusal here is one rule seen from a different side, so they answer
167    /// with one variant and differ only in `detail`. A Token that stops early
168    /// and one that runs long are both structures the draft's own serialization
169    /// does not describe, and neither leaves a receiver anything to act on.
170    fn decode_with<F>(key: u64, bytes: &[u8], mut read: F) -> Result<Self, CodecError>
171    where
172        F: FnMut(&mut &[u8]) -> Result<VarInt, VarIntError>,
173    {
174        const NO_ALIAS_TYPE: &str = "it carries no Alias Type";
175        const UNASSIGNED: &str = "its Alias Type is not one this draft assigns";
176        const NO_ALIAS: &str = "its Alias Type promises a Token Alias and the value ends first";
177        const NO_TYPE: &str = "its Alias Type promises a Token Type and the value ends first";
178        const TRAILING: &str =
179            "its Alias Type promises no Token Value and bytes follow the Token Alias";
180
181        let mut buf = bytes;
182        let raw = read(&mut buf).map_err(|_| malformed(key, NO_ALIAS_TYPE))?;
183        let alias_type =
184            TokenAliasType::from_id(raw.into_inner()).ok_or_else(|| malformed(key, UNASSIGNED))?;
185
186        let alias = if alias_type.has_alias() {
187            Some(read(&mut buf).map_err(|_| malformed(key, NO_ALIAS))?.into_inner())
188        } else {
189            None
190        };
191
192        let (token_type, value) = if alias_type.has_type_and_value() {
193            let token_type = read(&mut buf).map_err(|_| malformed(key, NO_TYPE))?.into_inner();
194            (Some(token_type), buf.to_vec())
195        } else {
196            if buf.has_remaining() {
197                return Err(malformed(key, TRAILING));
198            }
199            (None, Vec::new())
200        };
201
202        Ok(AuthorizationToken { alias_type, alias, token_type, value })
203    }
204
205    /// Write this Token as a parameter value, using QUIC variable-length
206    /// integers (drafts 11 through 16).
207    ///
208    /// Fallible where [`encode_moqt`](Self::encode_moqt) is not, for the reason
209    /// the two integer encodings differ: the QUIC one stops at 2^62 - 1, so an
210    /// Alias or Token Type above that has no representation and would otherwise
211    /// be written as an unrelated number with the length bits folded into it.
212    ///
213    /// The fields written are the ones the Alias Type promises, not the ones
214    /// that happen to be populated. A caller that sets a Token Alias on a
215    /// USE_VALUE has described a structure no draft defines, and writing it
216    /// would produce bytes this codec's own reader refuses; the Alias Type is
217    /// the field a receiver parses by, so it is the field that decides.
218    pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
219        let mut out = Vec::with_capacity(24 + self.value.len());
220        VarInt::from_u64(self.alias_type.id())?.encode(&mut out);
221        if self.alias_type.has_alias() {
222            VarInt::from_u64(self.alias.unwrap_or(0))?.encode(&mut out);
223        }
224        if self.alias_type.has_type_and_value() {
225            VarInt::from_u64(self.token_type.unwrap_or(0))?.encode(&mut out);
226            out.extend_from_slice(&self.value);
227        }
228        buf.put_slice(&out);
229        Ok(())
230    }
231
232    /// Write this Token as a parameter value, using MoQT variable-length
233    /// integers (drafts 17 and later).
234    ///
235    /// Infallible: that encoding reaches the whole 64-bit range, so every value
236    /// these fields can hold has a representation.
237    pub fn encode_moqt<P: MoqtProfile>(&self, buf: &mut impl BufMut) {
238        VarInt::from_u64_moqt(self.alias_type.id()).encode_moqt::<P>(buf);
239        if self.alias_type.has_alias() {
240            VarInt::from_u64_moqt(self.alias.unwrap_or(0)).encode_moqt::<P>(buf);
241        }
242        if self.alias_type.has_type_and_value() {
243            VarInt::from_u64_moqt(self.token_type.unwrap_or(0)).encode_moqt::<P>(buf);
244            buf.put_slice(&self.value);
245        }
246    }
247}