jwt_simple/
common.rs

1use std::collections::HashSet;
2
3use coarsetime::{Duration, UnixTimeStamp};
4use ct_codecs::{Base64UrlSafeNoPadding, Decoder, Encoder, Hex};
5
6use crate::{claims::DEFAULT_TIME_TOLERANCE_SECS, error::*};
7
8pub const DEFAULT_MAX_TOKEN_LENGTH: usize = 1_000_000;
9
10/// Additional features to enable during verification.
11/// Signatures and token expiration are already automatically verified.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct VerificationOptions {
14    /// Reject tokens created before the given date
15    ///
16    /// For a given user, the time of the last successful authentication can be
17    /// kept in a database, and `reject_before` can then be used to reject
18    /// older (replayed) tokens.
19    pub reject_before: Option<UnixTimeStamp>,
20
21    /// Accept tokens created with a date in the future
22    pub accept_future: bool,
23
24    /// Require a specific subject to be present
25    pub required_subject: Option<String>,
26
27    /// Require a specific key identifier to be present
28    pub required_key_id: Option<String>,
29
30    /// Require a specific public key to be present
31    pub required_public_key: Option<String>,
32
33    /// Require a specific nonce to be present
34    pub required_nonce: Option<String>,
35
36    /// Require the issuer to be present in the set
37    pub allowed_issuers: Option<HashSet<String>>,
38
39    /// Require the audience to be present in the set
40    pub allowed_audiences: Option<HashSet<String>>,
41
42    /// How much clock drift to tolerate when verifying token timestamps
43    pub time_tolerance: Option<Duration>,
44
45    /// Reject tokens created more than `max_validity` ago
46    pub max_validity: Option<Duration>,
47
48    /// Maximum token length to accept
49    pub max_token_length: Option<usize>,
50
51    /// Maximum unsafe, untrusted, unverified JWT header length to accept
52    pub max_header_length: Option<usize>,
53
54    /// Change the current time. Only used for testing.
55    pub artificial_time: Option<UnixTimeStamp>,
56}
57
58impl Default for VerificationOptions {
59    fn default() -> Self {
60        Self {
61            reject_before: None,
62            accept_future: false,
63            required_subject: None,
64            required_key_id: None,
65            required_public_key: None,
66            required_nonce: None,
67            allowed_issuers: None,
68            allowed_audiences: None,
69            time_tolerance: Some(Duration::from_secs(DEFAULT_TIME_TOLERANCE_SECS)),
70            max_validity: None,
71            max_token_length: Some(DEFAULT_MAX_TOKEN_LENGTH),
72            max_header_length: None,
73            artificial_time: None,
74        }
75    }
76}
77
78/// Unsigned metadata about a key to be attached to tokens.
79/// This information can be freely tampered with by an intermediate party.
80/// Most applications should not need to use this.
81#[derive(Debug, Clone, Default)]
82pub struct KeyMetadata {
83    pub(crate) key_set_url: Option<String>,
84    pub(crate) public_key: Option<String>,
85    pub(crate) certificate_url: Option<String>,
86    pub(crate) certificate_sha1_thumbprint: Option<String>,
87    pub(crate) certificate_sha256_thumbprint: Option<String>,
88}
89
90impl KeyMetadata {
91    /// Add a key set URL to the metadata ("jku")
92    pub fn with_key_set_url(mut self, key_set_url: impl ToString) -> Self {
93        self.key_set_url = Some(key_set_url.to_string());
94        self
95    }
96
97    /// Add a public key to the metadata ("jwk")
98    pub fn with_public_key(mut self, public_key: impl ToString) -> Self {
99        self.public_key = Some(public_key.to_string());
100        self
101    }
102
103    /// Add a certificate URL to the metadata ("x5u")
104    pub fn with_certificate_url(mut self, certificate_url: impl ToString) -> Self {
105        self.certificate_url = Some(certificate_url.to_string());
106        self
107    }
108
109    /// Add a certificate SHA-1 thumbprint to the metadata ("x5t")
110    pub fn with_certificate_sha1_thumbprint(
111        mut self,
112        certificate_sha1_thumbprint: impl ToString,
113    ) -> Result<Self, Error> {
114        let thumbprint = certificate_sha1_thumbprint.to_string();
115        let mut bin = [0u8; 20];
116        if thumbprint.len() == 40 {
117            ensure!(
118                Hex::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
119                JWTError::InvalidCertThumprint
120            );
121            let thumbprint = Base64UrlSafeNoPadding::encode_to_string(bin)?;
122            self.certificate_sha1_thumbprint = Some(thumbprint);
123            return Ok(self);
124        }
125        ensure!(
126            Base64UrlSafeNoPadding::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
127            JWTError::InvalidCertThumprint
128        );
129        self.certificate_sha1_thumbprint = Some(thumbprint);
130        Ok(self)
131    }
132
133    /// Add a certificate SHA-256 thumbprint to the metadata ("x5t#256")
134    pub fn with_certificate_sha256_thumbprint(
135        mut self,
136        certificate_sha256_thumbprint: impl ToString,
137    ) -> Result<Self, Error> {
138        let thumbprint = certificate_sha256_thumbprint.to_string();
139        let mut bin = [0u8; 32];
140        if thumbprint.len() == 64 {
141            ensure!(
142                Hex::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
143                JWTError::InvalidCertThumprint
144            );
145            let thumbprint = Base64UrlSafeNoPadding::encode_to_string(bin)?;
146            self.certificate_sha256_thumbprint = Some(thumbprint);
147            return Ok(self);
148        }
149        ensure!(
150            Base64UrlSafeNoPadding::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
151            JWTError::InvalidCertThumprint
152        );
153        self.certificate_sha256_thumbprint = Some(thumbprint);
154        Ok(self)
155    }
156}
157
158#[inline(never)]
159pub(crate) fn timingsafe_eq(a: &[u8], b: &[u8]) -> bool {
160    if a.len() != b.len() {
161        return false;
162    }
163    a.iter().zip(b.iter()).fold(0, |c, (x, y)| c | (x ^ y)) == 0
164}