common_access_token/
header.rs

1//! # Header Types for Common Access Token
2//!
3//! This module provides the header structure and related types for Common Access Tokens.
4//!
5//! Headers in Common Access Tokens are divided into two categories:
6//!
7//! - **Protected Headers**: These headers are integrity-protected and are part of the signature input.
8//! - **Unprotected Headers**: These headers are not integrity-protected and can be modified without invalidating the signature.
9//!
10//! The most important header parameters are:
11//!
12//! - **Algorithm (alg)**: Specifies the cryptographic algorithm used to secure the token.
13//! - **Key ID (kid)**: Identifies the key used to secure the token.
14
15use crate::constants::cose_algs;
16use std::collections::BTreeMap;
17
18/// Supported algorithms for token signing and verification.
19///
20/// This enum represents the cryptographic algorithms that can be used
21/// to sign and verify Common Access Tokens.
22///
23/// Currently, only HMAC-SHA256 is supported, but the design allows for
24/// easy extension to support additional algorithms in the future.
25///
26/// # Example
27///
28/// ```
29/// use common_access_token::Algorithm;
30///
31/// // Create a token with HMAC-SHA256 algorithm
32/// let alg = Algorithm::HmacSha256;
33/// assert_eq!(alg.identifier(), 5); // COSE algorithm identifier
34/// ```
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Algorithm {
37    /// HMAC with SHA-256 (COSE algorithm identifier: 5)
38    HmacSha256,
39}
40
41impl Algorithm {
42    /// Get the algorithm identifier as defined in the COSE spec
43    pub fn identifier(&self) -> i32 {
44        match self {
45            Algorithm::HmacSha256 => cose_algs::HMAC_SHA_256,
46        }
47    }
48
49    /// Create an Algorithm from an identifier
50    pub fn from_identifier(id: i32) -> Option<Self> {
51        match id {
52            cose_algs::HMAC_SHA_256 => Some(Algorithm::HmacSha256),
53            _ => None,
54        }
55    }
56}
57
58/// Key identifier that can be either a binary or string value.
59///
60/// The key identifier (kid) is used to indicate which key was used to secure the token.
61/// This allows the recipient to select the appropriate key for verification without
62/// having to try each key.
63///
64/// # Examples
65///
66/// Creating a string key identifier:
67///
68/// ```
69/// use common_access_token::KeyId;
70///
71/// let string_kid = KeyId::string("my-key-2023");
72/// ```
73///
74/// Creating a binary key identifier:
75///
76/// ```
77/// use common_access_token::KeyId;
78///
79/// let binary_kid = KeyId::binary(vec![0x01, 0x02, 0x03, 0x04]);
80/// ```
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub enum KeyId {
83    /// Binary key identifier (for binary key identifiers like UUIDs or hashes)
84    Binary(Vec<u8>),
85    /// String key identifier (for human-readable key identifiers)
86    String(String),
87}
88
89impl KeyId {
90    /// Create a new binary key identifier
91    pub fn binary<T: Into<Vec<u8>>>(data: T) -> Self {
92        KeyId::Binary(data.into())
93    }
94
95    /// Create a new string key identifier
96    pub fn string<T: Into<String>>(data: T) -> Self {
97        KeyId::String(data.into())
98    }
99
100    /// Get the key identifier as bytes
101    pub fn as_bytes(&self) -> &[u8] {
102        match self {
103            KeyId::Binary(data) => data,
104            KeyId::String(data) => data.as_bytes(),
105        }
106    }
107}
108
109/// CBOR value type for header and claim values.
110///
111/// This enum represents the different types of values that can be stored
112/// in token headers and claims. It supports the most common CBOR data types:
113/// integers, byte strings, text strings, and maps (which can contain nested values).
114///
115/// # Examples
116///
117/// Creating different types of CBOR values:
118///
119/// ```
120/// use common_access_token::CborValue;
121/// use std::collections::BTreeMap;
122///
123/// // Integer value
124/// let int_value = CborValue::Integer(42);
125///
126/// // Text string value
127/// let text_value = CborValue::Text("Hello, world!".to_string());
128///
129/// // Byte string value
130/// let bytes_value = CborValue::Bytes(vec![0x01, 0x02, 0x03]);
131///
132/// // Map value (nested CBOR map)
133/// let mut map = BTreeMap::new();
134/// map.insert(1, CborValue::Text("nested value".to_string()));
135/// let map_value = CborValue::Map(map);
136/// ```
137#[derive(Debug, Clone, PartialEq)]
138pub enum CborValue {
139    /// Integer value (signed 64-bit integer)
140    Integer(i64),
141    /// Byte string value (binary data)
142    Bytes(Vec<u8>),
143    /// Text string value (UTF-8 encoded string)
144    Text(String),
145    /// Map value (nested CBOR map with integer keys and CBOR values)
146    Map(BTreeMap<i32, CborValue>),
147    /// Array value (list of CBOR values)
148    Array(Vec<CborValue>),
149    /// Null value
150    Null,
151}
152
153/// Type alias for header maps
154pub type HeaderMap = BTreeMap<i32, CborValue>;
155
156/// COSE header parameter labels
157pub mod labels {
158    use crate::constants::cose_labels;
159
160    /// Algorithm (used in protected header)
161    pub const ALG: i32 = cose_labels::ALG;
162    /// Key identifier (used in protected or unprotected header)
163    pub const KID: i32 = cose_labels::KID;
164}
165
166/// Header for a Common Access Token.
167///
168/// The header contains metadata about the token, such as the algorithm used
169/// for signing and the key identifier. It is divided into two parts:
170///
171/// - **Protected Header**: Contains parameters that are integrity-protected and
172///   included in the signature input. This typically includes the algorithm.
173///
174/// - **Unprotected Header**: Contains parameters that are not integrity-protected
175///   and can be modified without invalidating the signature. This might include
176///   non-critical metadata.
177///
178/// # Examples
179///
180/// Creating a header with algorithm and key identifier:
181///
182/// ```
183/// use common_access_token::{Algorithm, Header, KeyId};
184///
185/// let header = Header::new()
186///     .with_algorithm(Algorithm::HmacSha256)
187///     .with_protected_key_id(KeyId::string("my-key-2023"));
188///
189/// assert_eq!(header.algorithm(), Some(Algorithm::HmacSha256));
190/// ```
191#[derive(Debug, Clone, Default)]
192pub struct Header {
193    /// Protected header parameters (must be integrity protected)
194    pub protected: HeaderMap,
195    /// Unprotected header parameters
196    pub unprotected: HeaderMap,
197}
198
199impl Header {
200    /// Creates a new empty header with no parameters.
201    ///
202    /// # Example
203    ///
204    /// ```
205    /// use common_access_token::Header;
206    ///
207    /// let header = Header::new();
208    /// assert!(header.protected.is_empty());
209    /// assert!(header.unprotected.is_empty());
210    /// ```
211    pub fn new() -> Self {
212        Self::default()
213    }
214
215    /// Sets the algorithm in the protected header.
216    ///
217    /// The algorithm is always placed in the protected header because
218    /// it is a critical parameter that must be integrity-protected.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// use common_access_token::{Algorithm, Header};
224    ///
225    /// let header = Header::new().with_algorithm(Algorithm::HmacSha256);
226    /// assert_eq!(header.algorithm(), Some(Algorithm::HmacSha256));
227    /// ```
228    pub fn with_algorithm(mut self, alg: Algorithm) -> Self {
229        self.protected
230            .insert(labels::ALG, CborValue::Integer(alg.identifier() as i64));
231        self
232    }
233
234    /// Sets the key identifier in the protected header.
235    ///
236    /// The key identifier in the protected header is integrity-protected
237    /// and cannot be modified without invalidating the signature.
238    ///
239    /// # Example
240    ///
241    /// ```
242    /// use common_access_token::{Header, KeyId};
243    ///
244    /// let header = Header::new().with_protected_key_id(KeyId::string("my-key-2023"));
245    /// if let Some(KeyId::String(kid)) = header.key_id() {
246    ///     assert_eq!(kid, "my-key-2023");
247    /// }
248    /// ```
249    pub fn with_protected_key_id(mut self, kid: KeyId) -> Self {
250        match kid {
251            KeyId::Binary(data) => {
252                self.protected.insert(labels::KID, CborValue::Bytes(data));
253            }
254            KeyId::String(data) => {
255                self.protected.insert(labels::KID, CborValue::Text(data));
256            }
257        }
258        self
259    }
260
261    /// Sets the key identifier in the unprotected header.
262    ///
263    /// The key identifier in the unprotected header is not integrity-protected
264    /// and can be modified without invalidating the signature.
265    ///
266    /// # Example
267    ///
268    /// ```
269    /// use common_access_token::{Header, KeyId};
270    ///
271    /// let header = Header::new().with_unprotected_key_id(KeyId::string("my-key-2023"));
272    /// if let Some(KeyId::String(kid)) = header.key_id() {
273    ///     assert_eq!(kid, "my-key-2023");
274    /// }
275    /// ```
276    pub fn with_unprotected_key_id(mut self, kid: KeyId) -> Self {
277        match kid {
278            KeyId::Binary(data) => {
279                self.unprotected.insert(labels::KID, CborValue::Bytes(data));
280            }
281            KeyId::String(data) => {
282                self.unprotected.insert(labels::KID, CborValue::Text(data));
283            }
284        }
285        self
286    }
287
288    /// Gets the algorithm from the protected header.
289    ///
290    /// Returns `None` if the algorithm is not present or is not a valid algorithm.
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// use common_access_token::{Algorithm, Header};
296    ///
297    /// let header = Header::new().with_algorithm(Algorithm::HmacSha256);
298    /// assert_eq!(header.algorithm(), Some(Algorithm::HmacSha256));
299    ///
300    /// let empty_header = Header::new();
301    /// assert_eq!(empty_header.algorithm(), None);
302    /// ```
303    pub fn algorithm(&self) -> Option<Algorithm> {
304        if let Some(CborValue::Integer(alg)) = self.protected.get(&labels::ALG) {
305            Algorithm::from_identifier(*alg as i32)
306        } else {
307            None
308        }
309    }
310
311    /// Gets the key identifier from the protected or unprotected header.
312    ///
313    /// This method first checks the protected header, and if the key identifier
314    /// is not found there, it checks the unprotected header.
315    ///
316    /// Returns `None` if the key identifier is not present in either header.
317    ///
318    /// # Example
319    ///
320    /// ```
321    /// use common_access_token::{Header, KeyId};
322    ///
323    /// let header = Header::new().with_protected_key_id(KeyId::string("my-key-2023"));
324    /// if let Some(KeyId::String(kid)) = header.key_id() {
325    ///     assert_eq!(kid, "my-key-2023");
326    /// }
327    ///
328    /// let empty_header = Header::new();
329    /// assert_eq!(empty_header.key_id(), None);
330    /// ```
331    pub fn key_id(&self) -> Option<KeyId> {
332        // First check protected header
333        if let Some(kid) = self.protected.get(&labels::KID) {
334            return match kid {
335                CborValue::Bytes(data) => Some(KeyId::Binary(data.clone())),
336                CborValue::Text(data) => Some(KeyId::String(data.clone())),
337                _ => None,
338            };
339        }
340
341        // Then check unprotected header
342        if let Some(kid) = self.unprotected.get(&labels::KID) {
343            return match kid {
344                CborValue::Bytes(data) => Some(KeyId::Binary(data.clone())),
345                CborValue::Text(data) => Some(KeyId::String(data.clone())),
346                _ => None,
347            };
348        }
349
350        None
351    }
352}