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}