jwt_simple/
common.rs

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