common_access_token/
token.rs

1//! Token implementation for Common Access Token
2
3use crate::claims::{Claims, RegisteredClaims};
4use crate::constants::tprint_params;
5use crate::error::Error;
6use crate::header::{Algorithm, CborValue, Header, HeaderMap, KeyId};
7use crate::utils::{compute_hmac_sha256, current_timestamp, verify_hmac_sha256};
8use crate::FingerprintType;
9use minicbor::{Decoder, Encoder};
10use std::collections::BTreeMap;
11use std::ffi::OsStr;
12use std::path::Path;
13
14/// Common Access Token structure
15#[derive(Debug, Clone)]
16pub struct Token {
17    /// Token header
18    pub header: Header,
19    /// Token claims
20    pub claims: Claims,
21    /// Token signature
22    pub signature: Vec<u8>,
23    /// Original payload bytes (for verification)
24    original_payload_bytes: Option<Vec<u8>>,
25}
26
27impl Token {
28    /// Create a new token with the given header, claims, and signature
29    pub fn new(header: Header, claims: Claims, signature: Vec<u8>) -> Self {
30        Self {
31            header,
32            claims,
33            signature,
34            original_payload_bytes: None,
35        }
36    }
37
38    /// Encode the token to CBOR bytes
39    pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
40        let mut buf = Vec::new();
41        let mut enc = Encoder::new(&mut buf);
42
43        // For HMAC algorithms, use COSE_Mac0 format with CWT tag
44        if let Some(Algorithm::HmacSha256) = self.header.algorithm() {
45            // Apply CWT tag (61)
46            enc.tag(minicbor::data::Tag::new(61))?;
47            // Apply COSE_Mac0 tag (17)
48            enc.tag(minicbor::data::Tag::new(17))?;
49        }
50
51        // COSE structure array with 4 items
52        enc.array(4)?;
53
54        // 1. Protected header (encoded as CBOR and then as bstr)
55        let protected_bytes = encode_map(&self.header.protected)?;
56        enc.bytes(&protected_bytes)?;
57
58        // 2. Unprotected header
59        encode_map_direct(&self.header.unprotected, &mut enc)?;
60
61        // 3. Payload (encoded as CBOR and then as bstr)
62        let claims_map = self.claims.to_map();
63        let claims_bytes = encode_map(&claims_map)?;
64        enc.bytes(&claims_bytes)?;
65
66        // 4. Signature/MAC
67        enc.bytes(&self.signature)?;
68
69        Ok(buf)
70    }
71
72    /// Decode a token from CBOR bytes
73    ///
74    /// This function supports both COSE_Sign1 (tag 18) and COSE_Mac0 (tag 17) structures,
75    /// as well as custom tags. It will automatically skip any tags and process the underlying
76    /// CBOR array.
77    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
78        let mut dec = Decoder::new(bytes);
79
80        // Check if the token starts with a tag (COSE_Sign1 tag = 18, COSE_Mac0 tag = 17, or custom tag = 61)
81        if dec.datatype()? == minicbor::data::Type::Tag {
82            // Skip the tag
83            let _ = dec.tag()?;
84
85            // Check for a second tag
86            if dec.datatype()? == minicbor::data::Type::Tag {
87                let _ = dec.tag()?;
88            }
89        }
90
91        // Expect array with 4 items
92        let array_len = dec.array()?.unwrap_or(0);
93        if array_len != 4 {
94            return Err(Error::InvalidFormat(format!(
95                "Expected array of length 4, got {array_len}"
96            )));
97        }
98
99        // 1. Protected header
100        let protected_bytes = dec.bytes()?;
101        let protected = decode_map(protected_bytes)?;
102
103        // 2. Unprotected header
104        let unprotected = decode_map_direct(&mut dec)?;
105
106        // Create header
107        let header = Header {
108            protected,
109            unprotected,
110        };
111
112        // 3. Payload
113        let claims_bytes = dec.bytes()?;
114        let claims_map = decode_map(claims_bytes)?;
115        let claims = Claims::from_map(&claims_map);
116
117        // 4. Signature
118        let signature = dec.bytes()?.to_vec();
119
120        Ok(Self {
121            header,
122            claims,
123            signature,
124            original_payload_bytes: Some(claims_bytes.to_vec()),
125        })
126    }
127
128    /// Verify the token signature
129    ///
130    /// This function supports both COSE_Sign1 and COSE_Mac0 structures.
131    /// It will first try to verify the signature using the COSE_Sign1 structure,
132    /// and if that fails, it will try the COSE_Mac0 structure.
133    pub fn verify(&self, key: &[u8]) -> Result<(), Error> {
134        let alg = self.header.algorithm().ok_or_else(|| {
135            Error::InvalidFormat("Missing algorithm in protected header".to_string())
136        })?;
137
138        match alg {
139            Algorithm::HmacSha256 => {
140                // Try with COSE_Sign1 structure first
141                let sign1_input = self.sign1_input()?;
142                let sign1_result = verify_hmac_sha256(key, &sign1_input, &self.signature);
143
144                if sign1_result.is_ok() {
145                    return Ok(());
146                }
147
148                // If COSE_Sign1 verification fails, try COSE_Mac0 structure
149                let mac0_input = self.mac0_input()?;
150                verify_hmac_sha256(key, &mac0_input, &self.signature)
151            }
152        }
153    }
154
155    /// Verify the token claims
156    pub fn verify_claims(&self, options: &VerificationOptions) -> Result<(), Error> {
157        let now = current_timestamp();
158
159        // Check expiration
160        if options.verify_exp {
161            if let Some(exp) = self.claims.registered.exp {
162                if now >= exp {
163                    return Err(Error::Expired);
164                }
165            } else if options.require_exp {
166                return Err(Error::MissingClaim("exp".to_string()));
167            }
168        }
169
170        // Check not before
171        if options.verify_nbf {
172            if let Some(nbf) = self.claims.registered.nbf {
173                if now < nbf {
174                    return Err(Error::NotYetValid);
175                }
176            }
177        }
178
179        // Check issuer
180        if let Some(expected_iss) = &options.expected_issuer {
181            if let Some(iss) = &self.claims.registered.iss {
182                if iss != expected_iss {
183                    return Err(Error::InvalidIssuer);
184                }
185            } else if options.require_iss {
186                return Err(Error::MissingClaim("iss".to_string()));
187            }
188        }
189
190        // Check audience
191        if let Some(expected_aud) = &options.expected_audience {
192            if let Some(aud) = &self.claims.registered.aud {
193                if aud != expected_aud {
194                    return Err(Error::InvalidAudience);
195                }
196            } else if options.require_aud {
197                return Err(Error::MissingClaim("aud".to_string()));
198            }
199        }
200
201        // Check CAT-specific claims
202        if options.verify_catu {
203            self.verify_catu_claim(options)?;
204        }
205
206        if options.verify_catm {
207            self.verify_catm_claim(options)?;
208        }
209
210        if options.verify_catreplay {
211            self.verify_catreplay_claim(options)?;
212        }
213
214        if options.verify_cattprint {
215            self.verify_cattprint_claim(options)?;
216        }
217
218        Ok(())
219    }
220
221    /// Verify the CATU (URI) claim against the provided URI
222    fn verify_catu_claim(&self, options: &VerificationOptions) -> Result<(), Error> {
223        use crate::constants::{cat_keys, uri_components};
224        use url::Url;
225
226        // Get the URI to verify against
227        let uri = match &options.uri {
228            Some(uri) => uri,
229            None => {
230                return Err(Error::InvalidClaimValue(
231                    "No URI provided for CATU verification".to_string(),
232                ))
233            }
234        };
235
236        // Parse the URI
237        let parsed_uri = match Url::parse(uri) {
238            Ok(url) => url,
239            Err(_) => {
240                return Err(Error::InvalidClaimValue(format!(
241                    "Invalid URI format: {uri}"
242                )))
243            }
244        };
245
246        // Parse the Path from the URI
247        let parsed_path = Path::new(parsed_uri.path());
248
249        // Check if token has CATU claim
250        let catu_claim = match self.claims.custom.get(&cat_keys::CATU) {
251            Some(claim) => claim,
252            None => return Ok(()), // No CATU claim, so nothing to verify
253        };
254
255        // CATU claim should be a map
256        let component_map = match catu_claim {
257            CborValue::Map(map) => map,
258            _ => {
259                return Err(Error::InvalidUriClaim(
260                    "CATU claim is not a map".to_string(),
261                ))
262            }
263        };
264
265        // Verify each component in the CATU claim
266        for (component_key, component_value) in component_map {
267            match *component_key {
268                uri_components::SCHEME => {
269                    self.verify_uri_component(
270                        &parsed_uri.scheme().to_string(),
271                        component_value,
272                        "scheme",
273                    )?;
274                }
275                uri_components::HOST => {
276                    self.verify_uri_component(
277                        &parsed_uri.host_str().unwrap_or("").to_string(),
278                        component_value,
279                        "host",
280                    )?;
281                }
282                uri_components::PORT => {
283                    let port = parsed_uri.port().map(|p| p.to_string()).unwrap_or_default();
284                    self.verify_uri_component(&port, component_value, "port")?;
285                }
286                uri_components::PATH => {
287                    self.verify_uri_component(
288                        &parsed_uri.path().to_string(),
289                        component_value,
290                        "path",
291                    )?;
292                }
293                uri_components::QUERY => {
294                    let query = parsed_uri.query().unwrap_or("").to_string();
295                    self.verify_uri_component(&query, component_value, "query")?;
296                }
297                uri_components::PARENT_PATH => {
298                    // Extract parent directory path from URI path.
299                    // For URI "https://example.com/a/b/file.txt", this extracts "/a/b".
300                    // For root-level files, this returns an empty string.
301                    // Non-UTF8 paths are converted to empty strings.
302                    let parent_path = parsed_path.parent().unwrap_or(Path::new("")).to_str().unwrap_or("").to_string();
303                    self.verify_uri_component(&parent_path, component_value, "parent_path")?;
304                }
305                uri_components::FILENAME => {
306                    // Extract complete filename (with extension) from URI path.
307                    // For URI "https://example.com/path/video.mp4", this extracts "video.mp4".
308                    // For paths without a filename, this returns an empty string.
309                    // Non-UTF8 filenames are converted to empty strings.
310                    let filename = parsed_path.file_name().unwrap_or(OsStr::new("")).to_str().unwrap_or("").to_string();
311                    self.verify_uri_component(&filename, component_value, "filename")?;
312                }
313                uri_components::STEM => {
314                    // Extract filename without extension from URI path.
315                    // For URI "https://example.com/path/video.mp4", this extracts "video".
316                    // For "archive.tar.gz", this extracts "archive.tar" (only last extension removed).
317                    // For files without extension, returns the entire filename.
318                    // Non-UTF8 stems are converted to empty strings.
319                    let stem = parsed_path.file_stem().unwrap_or(OsStr::new("")).to_str().unwrap_or("").to_string();
320                    self.verify_uri_component(&stem, component_value, "stem")?;
321                }
322                uri_components::EXTENSION => {
323                    // Extract file extension from path
324                    let path = parsed_uri.path();
325                    let extension = path.split('.').next_back().unwrap_or("").to_string();
326                    if !path.contains('.') || path.ends_with('.') {
327                        // No extension or ends with dot
328                        self.verify_uri_component(&"".to_string(), component_value, "extension")?;
329                    } else {
330                        self.verify_uri_component(
331                            &format!(".{extension}"),
332                            component_value,
333                            "extension",
334                        )?;
335                    }
336                }
337                _ => {
338                    // Ignore unsupported components
339                }
340            }
341        }
342
343        Ok(())
344    }
345
346    /// Verify a URI component against match conditions
347    fn verify_uri_component(
348        &self,
349        component: &String,
350        match_conditions: &CborValue,
351        component_name: &str,
352    ) -> Result<(), Error> {
353        use crate::constants::match_types;
354        use hmac_sha256::Hash as Sha256Hash;
355        use hmac_sha512::Hash as Sha512Hash;
356        use regex::Regex;
357
358        // Match conditions should be a map
359        let match_map = match match_conditions {
360            CborValue::Map(map) => map,
361            _ => {
362                return Err(Error::InvalidUriClaim(format!(
363                    "Match conditions for {component_name} is not a map"
364                )))
365            }
366        };
367
368        for (match_type, match_value) in match_map {
369            match *match_type {
370                match_types::EXACT => {
371                    if let CborValue::Text(text) = match_value {
372                        if component != text {
373                            return Err(Error::InvalidUriClaim(format!(
374                                "URI component {component_name} '{component}' does not exactly match required value '{text}'"
375                            )));
376                        }
377                    }
378                }
379                match_types::PREFIX => {
380                    if let CborValue::Text(prefix) = match_value {
381                        if !component.starts_with(prefix) {
382                            return Err(Error::InvalidUriClaim(format!(
383                                "URI component {component_name} '{component}' does not start with required prefix '{prefix}'"
384                            )));
385                        }
386                    }
387                }
388                match_types::SUFFIX => {
389                    if let CborValue::Text(suffix) = match_value {
390                        if !component.ends_with(suffix) {
391                            return Err(Error::InvalidUriClaim(format!(
392                                "URI component {component_name} '{component}' does not end with required suffix '{suffix}'"
393                            )));
394                        }
395                    }
396                }
397                match_types::CONTAINS => {
398                    if let CborValue::Text(contained) = match_value {
399                        if !component.contains(contained) {
400                            return Err(Error::InvalidUriClaim(format!(
401                                "URI component {component_name} '{component}' does not contain required text '{contained}'"
402                            )));
403                        }
404                    }
405                }
406                match_types::REGEX => {
407                    if let CborValue::Array(array) = match_value {
408                        if let Some(CborValue::Text(pattern)) = array.first() {
409                            match Regex::new(pattern) {
410                                Ok(regex) => {
411                                    if !regex.is_match(component) {
412                                        return Err(Error::InvalidUriClaim(format!(
413                                            "URI component {component_name} '{component}' does not match required regex pattern '{pattern}'"
414                                        )));
415                                    }
416                                }
417                                Err(_) => {
418                                    return Err(Error::InvalidUriClaim(format!(
419                                        "Invalid regex pattern: {pattern}"
420                                    )))
421                                }
422                            }
423                        }
424                    }
425                }
426                match_types::SHA256 => {
427                    if let CborValue::Bytes(expected_hash) = match_value {
428                        let hash = Sha256Hash::hash(component.as_bytes());
429
430                        if !ct_codecs::verify(&hash, expected_hash.as_slice()) {
431                            return Err(Error::InvalidUriClaim(format!(
432                                "URI component {component_name} '{component}' SHA-256 hash does not match expected value"
433                            )));
434                        }
435                    }
436                }
437                match_types::SHA512_256 => {
438                    if let CborValue::Bytes(expected_hash) = match_value {
439                        let hash = Sha512Hash::hash(component.as_bytes());
440                        let truncated_hash = &hash[0..32]; // Take first 256 bits (32 bytes)
441
442                        if !ct_codecs::verify(truncated_hash, &expected_hash[..]) {
443                            return Err(Error::InvalidUriClaim(format!(
444                                "URI component {component_name} '{component}' SHA-512/256 hash does not match expected value"
445                            )));
446                        }
447                    }
448                }
449                _ => {
450                    // Ignore unsupported match types
451                }
452            }
453        }
454
455        Ok(())
456    }
457
458    /// Verify the CATM (HTTP method) claim against the provided method
459    fn verify_catm_claim(&self, options: &VerificationOptions) -> Result<(), Error> {
460        use crate::constants::cat_keys;
461
462        // Get the HTTP method to verify against
463        let method = match &options.http_method {
464            Some(method) => method,
465            None => {
466                return Err(Error::InvalidClaimValue(
467                    "No HTTP method provided for CATM verification".to_string(),
468                ))
469            }
470        };
471
472        // Check if token has CATM claim
473        let catm_claim = match self.claims.custom.get(&cat_keys::CATM) {
474            Some(claim) => claim,
475            None => return Ok(()), // No CATM claim, so nothing to verify
476        };
477
478        // CATM claim should be an array of allowed methods
479        let allowed_methods = match catm_claim {
480            CborValue::Array(methods) => methods,
481            _ => {
482                return Err(Error::InvalidMethodClaim(
483                    "CATM claim is not an array".to_string(),
484                ))
485            }
486        };
487
488        // Check if the provided method is in the allowed methods list
489        let method_upper = method.to_uppercase();
490        let method_allowed = allowed_methods.iter().any(|m| {
491            if let CborValue::Text(allowed) = m {
492                allowed.to_uppercase() == method_upper
493            } else {
494                false
495            }
496        });
497
498        if !method_allowed {
499            return Err(Error::InvalidMethodClaim(format!(
500                "HTTP method '{}' is not allowed. Permitted methods: {:?}",
501                method,
502                allowed_methods
503                    .iter()
504                    .filter_map(|m| if let CborValue::Text(t) = m {
505                        Some(t.as_str())
506                    } else {
507                        None
508                    })
509                    .collect::<Vec<&str>>()
510            )));
511        }
512
513        Ok(())
514    }
515
516    /// Verify the CATREPLAY claim for token replay protection
517    fn verify_catreplay_claim(&self, options: &VerificationOptions) -> Result<(), Error> {
518        use crate::constants::{cat_keys, replay_values};
519
520        // Check if token has CATREPLAY claim
521        let catreplay_claim = match self.claims.custom.get(&cat_keys::CATREPLAY) {
522            Some(claim) => claim,
523            None => return Ok(()), // No CATREPLAY claim, so nothing to verify
524        };
525
526        // Get the replay protection value
527        let replay_value = match catreplay_claim {
528            CborValue::Integer(value) => *value as i32,
529            _ => {
530                return Err(Error::InvalidClaimValue(
531                    "CATREPLAY claim is not an integer".to_string(),
532                ))
533            }
534        };
535
536        match replay_value {
537            replay_values::PERMITTED => {
538                // Replay is permitted, no verification needed
539                Ok(())
540            }
541            replay_values::PROHIBITED => {
542                // Replay is prohibited, check if token has been seen before
543                if options.token_seen_before {
544                    Err(Error::ReplayViolation(
545                        "Token replay is prohibited".to_string(),
546                    ))
547                } else {
548                    Ok(())
549                }
550            }
551            replay_values::REUSE_DETECTION => {
552                // Reuse is detected but allowed, no error returned
553                // Implementations should log or notify about reuse
554                Ok(())
555            }
556            _ => Err(Error::InvalidClaimValue(format!(
557                "Invalid CATREPLAY value: {replay_value}"
558            ))),
559        }
560    }
561
562    /// Verify the CATTPRINT (TLS Fingerprint) claim against the provided fingerprint type and value
563    fn verify_cattprint_claim(&self, options: &VerificationOptions) -> Result<(), Error> {
564        use crate::constants::cat_keys;
565
566        // Get the Fingerprint type to verify against
567        let fingerprint_type = match &options.fingerprint_type {
568            Some(fingerprint_type) => fingerprint_type,
569            None => {
570                return Err(Error::InvalidClaimValue(
571                    "No Fingerprint Type provided for CATTPRINT verification".to_string(),
572                ))
573            }
574        };
575
576        // Get the Fingerprint value to verify against
577        let fingerprint_value = match &options.fingerprint_value {
578            Some(fingerprint_value) => fingerprint_value,
579            None => {
580                return Err(Error::InvalidClaimValue(
581                    "No Fingerprint Value provided for CATTPRINT verification".to_string(),
582                ))
583            }
584        };
585
586        // Check if token has CATTPRINT claim
587        let cattprint_claim = match self.claims.custom.get(&cat_keys::CATTPRINT) {
588            Some(claim) => claim,
589            None => return Ok(()), // No CATTPRINT claim, so nothing to verify
590        };
591
592        // CATTPRINT claim should be a map of 2 values
593        let cattprint_map = match cattprint_claim {
594            CborValue::Map(cattprint_map) => cattprint_map,
595            _ => {
596                return Err(Error::InvalidTLSFingerprintClaim(
597                    "CATTPRINT claim is not a map".to_string(),
598                ))
599            }
600        };
601
602        // Check if the provided Fingerprint Type matches
603        let claim_fingerprint_type = cattprint_map.get(&tprint_params::FINGERPRINT_TYPE);
604        if let Some(CborValue::Integer(claim_type)) = claim_fingerprint_type {
605            if *claim_type != (*fingerprint_type as i64) {
606                // Convert claim_type (i64) to FingerprintType for human-readable name
607                let claim_type_name = FingerprintType::from_i64(*claim_type)
608                    .map(|ft| ft.as_str())
609                    .unwrap_or("<unknown>");
610                return Err(Error::InvalidTLSFingerprintClaim(format!(
611                    "TLS Fingerprint Type '{}' does not match required value '{}'",
612                    claim_type_name, fingerprint_type.as_str()
613                )));
614            }
615        } else {
616            return Err(Error::InvalidTLSFingerprintClaim(
617                "Missing or invalid Fingerprint Type in CATTPRINT claim".to_string(),
618            ));
619        }
620
621        // Check if the provided Fingerprint Value matches
622        let fingerprint_value_upper = fingerprint_value.to_lowercase();
623        let claim_fingerprint_value = cattprint_map.get(&tprint_params::FINGERPRINT_VALUE);
624        if let Some(CborValue::Text(claim_value)) = claim_fingerprint_value {
625            if claim_value.to_lowercase() != fingerprint_value_upper {
626                return Err(Error::InvalidTLSFingerprintClaim(format!(
627                    "TLS Fingerprint Value '{}' does not match required value '{}'",
628                    claim_value, fingerprint_value
629                )));
630            }
631        } else {
632            return Err(Error::InvalidTLSFingerprintClaim(
633                "Missing or invalid Fingerprint Value in CATTPRINT claim".to_string(),
634            ));
635        }
636
637        Ok(())
638    }
639
640    // Note: signature_input method removed as we now use mac0_input for HMAC algorithms
641
642    /// Get the encoded payload bytes, using original bytes if available
643    fn get_payload_bytes(&self) -> Result<Vec<u8>, Error> {
644        if let Some(ref original) = self.original_payload_bytes {
645            // Use original bytes for verification
646            Ok(original.clone())
647        } else {
648            // Encode claims for newly created tokens
649            let claims_map = self.claims.to_map();
650            encode_map(&claims_map)
651        }
652    }
653
654    /// Get the COSE_Sign1 signature input
655    fn sign1_input(&self) -> Result<Vec<u8>, Error> {
656        // Sig_structure = [
657        //   context : "Signature1",
658        //   protected : bstr .cbor header_map,
659        //   external_aad : bstr,
660        //   payload : bstr .cbor claims
661        // ]
662
663        let mut buf = Vec::new();
664        let mut enc = Encoder::new(&mut buf);
665
666        // Start array with 4 items
667        enc.array(4)?;
668
669        // 1. Context
670        enc.str("Signature1")?;
671
672        // 2. Protected header
673        let protected_bytes = encode_map(&self.header.protected)?;
674        enc.bytes(&protected_bytes)?;
675
676        // 3. External AAD (empty in our case)
677        enc.bytes(&[])?;
678
679        // 4. Payload
680        let claims_bytes = self.get_payload_bytes()?;
681        enc.bytes(&claims_bytes)?;
682
683        Ok(buf)
684    }
685
686    /// Get the COSE_Mac0 signature input
687    fn mac0_input(&self) -> Result<Vec<u8>, Error> {
688        // Mac_structure = [
689        //   context : "MAC0",
690        //   protected : bstr .cbor header_map,
691        //   external_aad : bstr,
692        //   payload : bstr .cbor claims
693        // ]
694
695        let mut buf = Vec::new();
696        let mut enc = Encoder::new(&mut buf);
697
698        // Start array with 4 items
699        enc.array(4)?;
700
701        // 1. Context
702        enc.str("MAC0")?;
703
704        // 2. Protected header
705        let protected_bytes = encode_map(&self.header.protected)?;
706        enc.bytes(&protected_bytes)?;
707
708        // 3. External AAD (empty in our case)
709        enc.bytes(&[])?;
710
711        // 4. Payload
712        let claims_bytes = self.get_payload_bytes()?;
713        enc.bytes(&claims_bytes)?;
714
715        Ok(buf)
716    }
717
718    // Convenience methods for common token operations
719
720    /// Check if the token has expired
721    ///
722    /// Returns `true` if the token has an expiration claim and the current time is at or after it.
723    /// Returns `false` if the token has no expiration claim or if it hasn't expired yet.
724    ///
725    /// # Example
726    ///
727    /// ```
728    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims, current_timestamp};
729    ///
730    /// let key = b"my-secret-key";
731    /// let now = current_timestamp();
732    ///
733    /// // Token that expires in 1 hour
734    /// let token = TokenBuilder::new()
735    ///     .algorithm(Algorithm::HmacSha256)
736    ///     .registered_claims(RegisteredClaims::new().with_expiration(now + 3600))
737    ///     .sign(key)
738    ///     .unwrap();
739    ///
740    /// assert!(!token.is_expired());
741    /// ```
742    pub fn is_expired(&self) -> bool {
743        if let Some(exp) = self.claims.registered.exp {
744            current_timestamp() >= exp
745        } else {
746            false
747        }
748    }
749
750    /// Get the duration until token expiration
751    ///
752    /// Returns `Some(Duration)` if the token has an expiration claim and hasn't expired yet.
753    /// Returns `None` if the token has no expiration claim or has already expired.
754    ///
755    /// # Example
756    ///
757    /// ```
758    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims, current_timestamp};
759    ///
760    /// let key = b"my-secret-key";
761    /// let now = current_timestamp();
762    ///
763    /// let token = TokenBuilder::new()
764    ///     .algorithm(Algorithm::HmacSha256)
765    ///     .registered_claims(RegisteredClaims::new().with_expiration(now + 3600))
766    ///     .sign(key)
767    ///     .unwrap();
768    ///
769    /// if let Some(duration) = token.expires_in() {
770    ///     println!("Token expires in {} seconds", duration.as_secs());
771    /// }
772    /// ```
773    pub fn expires_in(&self) -> Option<std::time::Duration> {
774        if let Some(exp) = self.claims.registered.exp {
775            let now = current_timestamp();
776            if now < exp {
777                Some(std::time::Duration::from_secs(exp - now))
778            } else {
779                None
780            }
781        } else {
782            None
783        }
784    }
785
786    /// Check if the token is valid based on the not-before (nbf) claim
787    ///
788    /// Returns `true` if the token has no nbf claim or if the current time is at or after it.
789    /// Returns `false` if the token has an nbf claim and the current time is before it.
790    ///
791    /// # Example
792    ///
793    /// ```
794    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims, current_timestamp};
795    ///
796    /// let key = b"my-secret-key";
797    /// let now = current_timestamp();
798    ///
799    /// let token = TokenBuilder::new()
800    ///     .algorithm(Algorithm::HmacSha256)
801    ///     .registered_claims(RegisteredClaims::new().with_not_before(now))
802    ///     .sign(key)
803    ///     .unwrap();
804    ///
805    /// assert!(token.is_valid_yet());
806    /// ```
807    pub fn is_valid_yet(&self) -> bool {
808        if let Some(nbf) = self.claims.registered.nbf {
809            current_timestamp() >= nbf
810        } else {
811            true
812        }
813    }
814
815    /// Get the issuer claim value
816    ///
817    /// # Example
818    ///
819    /// ```
820    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims};
821    ///
822    /// let key = b"my-secret-key";
823    /// let token = TokenBuilder::new()
824    ///     .algorithm(Algorithm::HmacSha256)
825    ///     .registered_claims(RegisteredClaims::new().with_issuer("example-issuer"))
826    ///     .sign(key)
827    ///     .unwrap();
828    ///
829    /// assert_eq!(token.issuer(), Some("example-issuer"));
830    /// ```
831    pub fn issuer(&self) -> Option<&str> {
832        self.claims.registered.iss.as_deref()
833    }
834
835    /// Get the subject claim value
836    ///
837    /// # Example
838    ///
839    /// ```
840    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims};
841    ///
842    /// let key = b"my-secret-key";
843    /// let token = TokenBuilder::new()
844    ///     .algorithm(Algorithm::HmacSha256)
845    ///     .registered_claims(RegisteredClaims::new().with_subject("user-123"))
846    ///     .sign(key)
847    ///     .unwrap();
848    ///
849    /// assert_eq!(token.subject(), Some("user-123"));
850    /// ```
851    pub fn subject(&self) -> Option<&str> {
852        self.claims.registered.sub.as_deref()
853    }
854
855    /// Get the audience claim value
856    ///
857    /// # Example
858    ///
859    /// ```
860    /// use common_access_token::{TokenBuilder, Algorithm, RegisteredClaims};
861    ///
862    /// let key = b"my-secret-key";
863    /// let token = TokenBuilder::new()
864    ///     .algorithm(Algorithm::HmacSha256)
865    ///     .registered_claims(RegisteredClaims::new().with_audience("api-service"))
866    ///     .sign(key)
867    ///     .unwrap();
868    ///
869    /// assert_eq!(token.audience(), Some("api-service"));
870    /// ```
871    pub fn audience(&self) -> Option<&str> {
872        self.claims.registered.aud.as_deref()
873    }
874
875    /// Get the expiration timestamp
876    pub fn expiration(&self) -> Option<u64> {
877        self.claims.registered.exp
878    }
879
880    /// Get the not-before timestamp
881    pub fn not_before(&self) -> Option<u64> {
882        self.claims.registered.nbf
883    }
884
885    /// Get the issued-at timestamp
886    pub fn issued_at(&self) -> Option<u64> {
887        self.claims.registered.iat
888    }
889
890    /// Get a custom claim as a string
891    ///
892    /// Returns `Some(&str)` if the claim exists and is a text value, `None` otherwise.
893    ///
894    /// # Example
895    ///
896    /// ```
897    /// use common_access_token::{TokenBuilder, Algorithm};
898    ///
899    /// let key = b"my-secret-key";
900    /// let token = TokenBuilder::new()
901    ///     .algorithm(Algorithm::HmacSha256)
902    ///     .custom_string(100, "custom-value")
903    ///     .sign(key)
904    ///     .unwrap();
905    ///
906    /// assert_eq!(token.get_custom_string(100), Some("custom-value"));
907    /// assert_eq!(token.get_custom_string(999), None);
908    /// ```
909    pub fn get_custom_string(&self, key: i32) -> Option<&str> {
910        match self.claims.custom.get(&key) {
911            Some(CborValue::Text(s)) => Some(s.as_str()),
912            _ => None,
913        }
914    }
915
916    /// Get a custom claim as an integer
917    ///
918    /// Returns `Some(i64)` if the claim exists and is an integer value, `None` otherwise.
919    ///
920    /// # Example
921    ///
922    /// ```
923    /// use common_access_token::{TokenBuilder, Algorithm};
924    ///
925    /// let key = b"my-secret-key";
926    /// let token = TokenBuilder::new()
927    ///     .algorithm(Algorithm::HmacSha256)
928    ///     .custom_int(100, 42)
929    ///     .sign(key)
930    ///     .unwrap();
931    ///
932    /// assert_eq!(token.get_custom_int(100), Some(42));
933    /// assert_eq!(token.get_custom_int(999), None);
934    /// ```
935    pub fn get_custom_int(&self, key: i32) -> Option<i64> {
936        match self.claims.custom.get(&key) {
937            Some(CborValue::Integer(i)) => Some(*i),
938            _ => None,
939        }
940    }
941
942    /// Get a custom claim as binary data
943    ///
944    /// Returns `Some(&[u8])` if the claim exists and is a bytes value, `None` otherwise.
945    ///
946    /// # Example
947    ///
948    /// ```
949    /// use common_access_token::{TokenBuilder, Algorithm};
950    ///
951    /// let key = b"my-secret-key";
952    /// let data = vec![1, 2, 3, 4];
953    /// let token = TokenBuilder::new()
954    ///     .algorithm(Algorithm::HmacSha256)
955    ///     .custom_binary(100, data.clone())
956    ///     .sign(key)
957    ///     .unwrap();
958    ///
959    /// assert_eq!(token.get_custom_binary(100), Some(data.as_slice()));
960    /// assert_eq!(token.get_custom_binary(999), None);
961    /// ```
962    pub fn get_custom_binary(&self, key: i32) -> Option<&[u8]> {
963        match self.claims.custom.get(&key) {
964            Some(CborValue::Bytes(b)) => Some(b.as_slice()),
965            _ => None,
966        }
967    }
968
969    /// Get a reference to a custom claim value
970    ///
971    /// Returns `Some(&CborValue)` if the claim exists, `None` otherwise.
972    ///
973    /// # Example
974    ///
975    /// ```
976    /// use common_access_token::{TokenBuilder, Algorithm, CborValue};
977    ///
978    /// let key = b"my-secret-key";
979    /// let token = TokenBuilder::new()
980    ///     .algorithm(Algorithm::HmacSha256)
981    ///     .custom_string(100, "value")
982    ///     .sign(key)
983    ///     .unwrap();
984    ///
985    /// if let Some(CborValue::Text(s)) = token.get_custom_claim(100) {
986    ///     assert_eq!(s, "value");
987    /// }
988    /// ```
989    pub fn get_custom_claim(&self, key: i32) -> Option<&CborValue> {
990        self.claims.custom.get(&key)
991    }
992
993    /// Check if a custom claim exists
994    ///
995    /// # Example
996    ///
997    /// ```
998    /// use common_access_token::{TokenBuilder, Algorithm};
999    ///
1000    /// let key = b"my-secret-key";
1001    /// let token = TokenBuilder::new()
1002    ///     .algorithm(Algorithm::HmacSha256)
1003    ///     .custom_string(100, "value")
1004    ///     .sign(key)
1005    ///     .unwrap();
1006    ///
1007    /// assert!(token.has_custom_claim(100));
1008    /// assert!(!token.has_custom_claim(999));
1009    /// ```
1010    pub fn has_custom_claim(&self, key: i32) -> bool {
1011        self.claims.custom.contains_key(&key)
1012    }
1013}
1014
1015/// Options for token verification
1016#[derive(Debug, Clone, Default)]
1017pub struct VerificationOptions {
1018    /// Verify expiration claim
1019    pub verify_exp: bool,
1020    /// Require expiration claim
1021    pub require_exp: bool,
1022    /// Verify not before claim
1023    pub verify_nbf: bool,
1024    /// Expected issuer
1025    pub expected_issuer: Option<String>,
1026    /// Require issuer claim
1027    pub require_iss: bool,
1028    /// Expected audience
1029    pub expected_audience: Option<String>,
1030    /// Require audience claim
1031    pub require_aud: bool,
1032    /// Verify CAT-specific URI claim (CATU) against provided URI
1033    pub verify_catu: bool,
1034    /// URI to verify against CATU claim
1035    pub uri: Option<String>,
1036    /// Verify CAT-specific HTTP methods claim (CATM) against provided method
1037    pub verify_catm: bool,
1038    /// HTTP method to verify against CATM claim
1039    pub http_method: Option<String>,
1040    /// Verify CAT-specific replay protection (CATREPLAY)
1041    pub verify_catreplay: bool,
1042    /// Whether the token has been seen before (for replay protection)
1043    pub token_seen_before: bool,
1044    /// Verify CAT-specific TLS Fingerprint claim (CATTPRINT) against provided Fingerprint Type and Value
1045    pub verify_cattprint: bool,
1046    /// Fingerprint Type to verify against CATTPRINT claim
1047    pub fingerprint_type: Option<FingerprintType>,
1048    /// Fingerprint Value to verify against CATTPRINT claim
1049    pub fingerprint_value: Option<String>,
1050}
1051
1052impl VerificationOptions {
1053    /// Create new default verification options
1054    pub fn new() -> Self {
1055        Self {
1056            verify_exp: true,
1057            require_exp: false,
1058            verify_nbf: true,
1059            expected_issuer: None,
1060            require_iss: false,
1061            expected_audience: None,
1062            require_aud: false,
1063            verify_catu: false,
1064            uri: None,
1065            verify_catm: false,
1066            http_method: None,
1067            verify_catreplay: false,
1068            token_seen_before: false,
1069            verify_cattprint: false,
1070            fingerprint_type: None,
1071            fingerprint_value: None,
1072        }
1073    }
1074
1075    /// Set whether to verify expiration
1076    pub fn verify_exp(mut self, verify: bool) -> Self {
1077        self.verify_exp = verify;
1078        self
1079    }
1080
1081    /// Set whether to require expiration
1082    pub fn require_exp(mut self, require: bool) -> Self {
1083        self.require_exp = require;
1084        self
1085    }
1086
1087    /// Set whether to verify not before
1088    pub fn verify_nbf(mut self, verify: bool) -> Self {
1089        self.verify_nbf = verify;
1090        self
1091    }
1092
1093    /// Set expected issuer
1094    pub fn expected_issuer<S: Into<String>>(mut self, issuer: S) -> Self {
1095        self.expected_issuer = Some(issuer.into());
1096        self
1097    }
1098
1099    /// Set whether to require issuer
1100    pub fn require_iss(mut self, require: bool) -> Self {
1101        self.require_iss = require;
1102        self
1103    }
1104
1105    /// Set expected audience
1106    pub fn expected_audience<S: Into<String>>(mut self, audience: S) -> Self {
1107        self.expected_audience = Some(audience.into());
1108        self
1109    }
1110
1111    /// Set whether to require audience
1112    pub fn require_aud(mut self, require: bool) -> Self {
1113        self.require_aud = require;
1114        self
1115    }
1116
1117    /// Set whether to verify CAT-specific URI claim (CATU)
1118    pub fn verify_catu(mut self, verify: bool) -> Self {
1119        self.verify_catu = verify;
1120        self
1121    }
1122
1123    /// Set URI to verify against CATU claim
1124    pub fn uri<S: Into<String>>(mut self, uri: S) -> Self {
1125        self.uri = Some(uri.into());
1126        self
1127    }
1128
1129    /// Set whether to verify CAT-specific HTTP methods claim (CATM)
1130    pub fn verify_catm(mut self, verify: bool) -> Self {
1131        self.verify_catm = verify;
1132        self
1133    }
1134
1135    /// Set HTTP method to verify against CATM claim
1136    pub fn http_method<S: Into<String>>(mut self, method: S) -> Self {
1137        self.http_method = Some(method.into());
1138        self
1139    }
1140
1141    /// Set whether to verify CAT-specific replay protection (CATREPLAY)
1142    pub fn verify_catreplay(mut self, verify: bool) -> Self {
1143        self.verify_catreplay = verify;
1144        self
1145    }
1146
1147    /// Set whether the token has been seen before (for replay protection)
1148    pub fn token_seen_before(mut self, seen: bool) -> Self {
1149        self.token_seen_before = seen;
1150        self
1151    }
1152
1153    /// Set whether to verify CAT-specific TLS Fingerprint claim (CATTPRINT)
1154    pub fn verify_cattprint(mut self, verify: bool) -> Self {
1155        self.verify_cattprint = verify;
1156        self
1157    }
1158
1159    /// Set fingerprint type to verify for the CATTPRINT claim
1160    pub fn fingerprint_type(mut self, fingerprint_type: FingerprintType) -> Self {
1161        self.fingerprint_type = Some(fingerprint_type);
1162        self
1163    }
1164
1165    /// Set fingerprint value to verify for the CATTPRINT claim
1166    pub fn fingerprint_value<S: Into<String>>(mut self, fingerprint_value: S) -> Self {
1167        self.fingerprint_value = Some(fingerprint_value.into());
1168        self
1169    }
1170}
1171
1172/// Builder for creating tokens
1173#[derive(Debug, Clone, Default)]
1174pub struct TokenBuilder {
1175    header: Header,
1176    claims: Claims,
1177}
1178
1179impl TokenBuilder {
1180    /// Create a new token builder
1181    pub fn new() -> Self {
1182        Self::default()
1183    }
1184
1185    /// Set the algorithm
1186    pub fn algorithm(mut self, alg: Algorithm) -> Self {
1187        self.header = self.header.with_algorithm(alg);
1188        self
1189    }
1190
1191    /// Set the key identifier in the protected header
1192    pub fn protected_key_id(mut self, kid: KeyId) -> Self {
1193        self.header = self.header.with_protected_key_id(kid);
1194        self
1195    }
1196
1197    /// Set the key identifier in the unprotected header
1198    pub fn unprotected_key_id(mut self, kid: KeyId) -> Self {
1199        self.header = self.header.with_unprotected_key_id(kid);
1200        self
1201    }
1202
1203    /// Set the registered claims
1204    pub fn registered_claims(mut self, claims: RegisteredClaims) -> Self {
1205        self.claims = self.claims.with_registered_claims(claims);
1206        self
1207    }
1208
1209    /// Add a custom claim with a string value
1210    pub fn custom_string<S: Into<String>>(mut self, key: i32, value: S) -> Self {
1211        self.claims = self.claims.with_custom_string(key, value);
1212        self
1213    }
1214
1215    /// Add a custom claim with a binary value
1216    pub fn custom_binary<B: Into<Vec<u8>>>(mut self, key: i32, value: B) -> Self {
1217        self.claims = self.claims.with_custom_binary(key, value);
1218        self
1219    }
1220
1221    /// Add a custom claim with an integer value
1222    pub fn custom_int(mut self, key: i32, value: i64) -> Self {
1223        self.claims = self.claims.with_custom_int(key, value);
1224        self
1225    }
1226
1227    /// Add a custom claim with a nested map value
1228    pub fn custom_map(mut self, key: i32, value: BTreeMap<i32, CborValue>) -> Self {
1229        self.claims = self.claims.with_custom_map(key, value);
1230        self
1231    }
1232
1233    /// Add a custom claim with a CborValue directly
1234    pub fn custom_cbor(mut self, key: i32, value: CborValue) -> Self {
1235        self.claims.custom.insert(key, value);
1236        self
1237    }
1238
1239    /// Add a custom claim with an array value
1240    pub fn custom_array(mut self, key: i32, value: Vec<CborValue>) -> Self {
1241        self.claims.custom.insert(key, CborValue::Array(value));
1242        self
1243    }
1244
1245    /// Set expiration time relative to now (in seconds)
1246    ///
1247    /// This is a convenience method that sets the expiration claim to the current time plus the specified number of seconds.
1248    ///
1249    /// # Example
1250    ///
1251    /// ```
1252    /// use common_access_token::{TokenBuilder, Algorithm, current_timestamp};
1253    ///
1254    /// let key = b"my-secret-key";
1255    ///
1256    /// // Token expires in 1 hour
1257    /// let token = TokenBuilder::new()
1258    ///     .algorithm(Algorithm::HmacSha256)
1259    ///     .expires_in_secs(3600)
1260    ///     .sign(key)
1261    ///     .unwrap();
1262    ///
1263    /// assert!(!token.is_expired());
1264    /// ```
1265    pub fn expires_in_secs(mut self, seconds: u64) -> Self {
1266        let exp = current_timestamp() + seconds;
1267        self.claims.registered.exp = Some(exp);
1268        self
1269    }
1270
1271    /// Set expiration time relative to now using a Duration
1272    ///
1273    /// This is a convenience method that sets the expiration claim to the current time plus the specified duration.
1274    ///
1275    /// # Example
1276    ///
1277    /// ```
1278    /// use common_access_token::{TokenBuilder, Algorithm};
1279    /// use std::time::Duration;
1280    ///
1281    /// let key = b"my-secret-key";
1282    ///
1283    /// // Token expires in 1 hour
1284    /// let token = TokenBuilder::new()
1285    ///     .algorithm(Algorithm::HmacSha256)
1286    ///     .expires_in(Duration::from_secs(3600))
1287    ///     .sign(key)
1288    ///     .unwrap();
1289    ///
1290    /// assert!(!token.is_expired());
1291    /// ```
1292    pub fn expires_in(self, duration: std::time::Duration) -> Self {
1293        self.expires_in_secs(duration.as_secs())
1294    }
1295
1296    /// Set token lifetime with issued-at and expiration claims
1297    ///
1298    /// This convenience method sets both the `iat` (issued at) and `exp` (expiration) claims.
1299    /// The issued-at is set to the current time, and expiration is set to current time plus the specified seconds.
1300    ///
1301    /// # Example
1302    ///
1303    /// ```
1304    /// use common_access_token::{TokenBuilder, Algorithm};
1305    ///
1306    /// let key = b"my-secret-key";
1307    ///
1308    /// // Token valid for 1 hour
1309    /// let token = TokenBuilder::new()
1310    ///     .algorithm(Algorithm::HmacSha256)
1311    ///     .valid_for_secs(3600)
1312    ///     .sign(key)
1313    ///     .unwrap();
1314    ///
1315    /// assert!(token.issued_at().is_some());
1316    /// assert!(token.expiration().is_some());
1317    /// ```
1318    pub fn valid_for_secs(mut self, seconds: u64) -> Self {
1319        let now = current_timestamp();
1320        self.claims.registered.iat = Some(now);
1321        self.claims.registered.exp = Some(now + seconds);
1322        self
1323    }
1324
1325    /// Set token lifetime with issued-at and expiration claims using a Duration
1326    ///
1327    /// This convenience method sets both the `iat` (issued at) and `exp` (expiration) claims.
1328    /// The issued-at is set to the current time, and expiration is set to current time plus the specified duration.
1329    ///
1330    /// # Example
1331    ///
1332    /// ```
1333    /// use common_access_token::{TokenBuilder, Algorithm};
1334    /// use std::time::Duration;
1335    ///
1336    /// let key = b"my-secret-key";
1337    ///
1338    /// // Token valid for 1 hour
1339    /// let token = TokenBuilder::new()
1340    ///     .algorithm(Algorithm::HmacSha256)
1341    ///     .valid_for(Duration::from_secs(3600))
1342    ///     .sign(key)
1343    ///     .unwrap();
1344    ///
1345    /// assert!(token.issued_at().is_some());
1346    /// assert!(token.expiration().is_some());
1347    /// ```
1348    pub fn valid_for(self, duration: std::time::Duration) -> Self {
1349        self.valid_for_secs(duration.as_secs())
1350    }
1351
1352    /// Build and sign the token
1353    pub fn sign(self, key: &[u8]) -> Result<Token, Error> {
1354        // Ensure we have an algorithm
1355        let alg = self.header.algorithm().ok_or_else(|| {
1356            Error::InvalidFormat("Missing algorithm in protected header".to_string())
1357        })?;
1358
1359        // Create token without signature
1360        let token = Token {
1361            header: self.header,
1362            claims: self.claims,
1363            signature: Vec::new(),
1364            original_payload_bytes: None,
1365        };
1366
1367        // Compute signature input based on algorithm
1368        // HMAC algorithms use COSE_Mac0 structure, others use COSE_Sign1
1369        let (_signature_input, signature) = match alg {
1370            Algorithm::HmacSha256 => {
1371                let mac_input = token.mac0_input()?;
1372                let mac = compute_hmac_sha256(key, &mac_input);
1373                (mac_input, mac)
1374            }
1375        };
1376
1377        // Create final token with signature
1378        Ok(Token {
1379            header: token.header,
1380            claims: token.claims,
1381            signature,
1382            original_payload_bytes: None,
1383        })
1384    }
1385}
1386
1387// Helper functions for CBOR encoding/decoding
1388
1389fn encode_map(map: &HeaderMap) -> Result<Vec<u8>, Error> {
1390    let mut buf = Vec::new();
1391    let mut enc = Encoder::new(&mut buf);
1392
1393    encode_map_direct(map, &mut enc)?;
1394
1395    Ok(buf)
1396}
1397
1398/// Encode a CBOR value directly to the encoder
1399fn encode_cbor_value(value: &CborValue, enc: &mut Encoder<&mut Vec<u8>>) -> Result<(), Error> {
1400    match value {
1401        CborValue::Integer(i) => {
1402            enc.i64(*i)?;
1403        }
1404        CborValue::Bytes(b) => {
1405            enc.bytes(b)?;
1406        }
1407        CborValue::Text(s) => {
1408            enc.str(s)?;
1409        }
1410        CborValue::Map(nested_map) => {
1411            // Create a nested encoder for the map
1412            encode_map_direct(nested_map, enc)?;
1413        }
1414        CborValue::Array(arr) => {
1415            // Create a nested encoder for the array
1416            enc.array(arr.len() as u64)?;
1417            for item in arr {
1418                encode_cbor_value(item, enc)?;
1419            }
1420        }
1421        CborValue::Null => {
1422            enc.null()?;
1423        }
1424    }
1425    Ok(())
1426}
1427
1428fn encode_map_direct(map: &HeaderMap, enc: &mut Encoder<&mut Vec<u8>>) -> Result<(), Error> {
1429    enc.map(map.len() as u64)?;
1430
1431    for (key, value) in map {
1432        enc.i32(*key)?;
1433        encode_cbor_value(value, enc)?;
1434    }
1435
1436    Ok(())
1437}
1438
1439fn decode_map(bytes: &[u8]) -> Result<HeaderMap, Error> {
1440    let mut dec = Decoder::new(bytes);
1441    decode_map_direct(&mut dec)
1442}
1443
1444/// Decode a CBOR array
1445fn decode_array(dec: &mut Decoder<'_>) -> Result<Vec<CborValue>, Error> {
1446    let array_len = dec.array()?.unwrap_or(0);
1447    let mut array = Vec::with_capacity(array_len as usize);
1448
1449    for _ in 0..array_len {
1450        // Try to decode based on the datatype
1451        let datatype = dec.datatype()?;
1452
1453        // Handle each type separately
1454        let value = if datatype == minicbor::data::Type::Int {
1455            // Integer value
1456            let i = dec.i64()?;
1457            CborValue::Integer(i)
1458        } else if datatype == minicbor::data::Type::U8
1459            || datatype == minicbor::data::Type::U16
1460            || datatype == minicbor::data::Type::U32
1461            || datatype == minicbor::data::Type::U64
1462        {
1463            // Unsigned integer value
1464            let i = dec.u64()? as i64;
1465            CborValue::Integer(i)
1466        } else if datatype == minicbor::data::Type::Bytes {
1467            // Byte string
1468            let b = dec.bytes()?;
1469            CborValue::Bytes(b.to_vec())
1470        } else if datatype == minicbor::data::Type::String {
1471            // Text string
1472            let s = dec.str()?;
1473            CborValue::Text(s.to_string())
1474        } else if datatype == minicbor::data::Type::Map {
1475            // Nested map
1476            let nested_map = decode_map_direct(dec)?;
1477            CborValue::Map(nested_map)
1478        } else if datatype == minicbor::data::Type::Array {
1479            // Nested array
1480            let nested_array = decode_array(dec)?;
1481            CborValue::Array(nested_array)
1482        } else if datatype == minicbor::data::Type::Null {
1483            // Null value
1484            dec.null()?;
1485            CborValue::Null
1486        } else {
1487            // Unsupported type
1488            return Err(Error::InvalidFormat(format!(
1489                "Unsupported CBOR type in array: {datatype:?}"
1490            )));
1491        };
1492
1493        array.push(value);
1494    }
1495
1496    Ok(array)
1497}
1498
1499fn decode_map_direct(dec: &mut Decoder<'_>) -> Result<HeaderMap, Error> {
1500    let map_len = dec.map()?.unwrap_or(0);
1501    let mut map = HeaderMap::new();
1502
1503    for _ in 0..map_len {
1504        let key = dec.i32()?;
1505
1506        // Try to decode based on the datatype
1507        let datatype = dec.datatype()?;
1508
1509        // Handle each type separately
1510        let value = if datatype == minicbor::data::Type::Int {
1511            // Integer value
1512            let i = dec.i64()?;
1513            CborValue::Integer(i)
1514        } else if datatype == minicbor::data::Type::U8
1515            || datatype == minicbor::data::Type::U16
1516            || datatype == minicbor::data::Type::U32
1517            || datatype == minicbor::data::Type::U64
1518        {
1519            // Unsigned integer value
1520            let i = dec.u64()? as i64;
1521            CborValue::Integer(i)
1522        } else if datatype == minicbor::data::Type::Bytes {
1523            // Byte string
1524            let b = dec.bytes()?;
1525            CborValue::Bytes(b.to_vec())
1526        } else if datatype == minicbor::data::Type::String {
1527            // Text string
1528            let s = dec.str()?;
1529            CborValue::Text(s.to_string())
1530        } else if datatype == minicbor::data::Type::Map {
1531            // Nested map
1532            let nested_map = decode_map_direct(dec)?;
1533            CborValue::Map(nested_map)
1534        } else if datatype == minicbor::data::Type::Array {
1535            // Array
1536            let array = decode_array(dec)?;
1537            CborValue::Array(array)
1538        } else if datatype == minicbor::data::Type::Null {
1539            // Null value
1540            dec.null()?;
1541            CborValue::Null
1542        } else {
1543            // Unsupported type
1544            return Err(Error::InvalidFormat(format!(
1545                "Unsupported CBOR type: {datatype:?}"
1546            )));
1547        };
1548
1549        map.insert(key, value);
1550    }
1551
1552    Ok(map)
1553}