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 signature type
32    pub required_signature_type: Option<String>,
33
34    /// Require a specific content type
35    pub required_content_type: Option<String>,
36
37    /// Require a specific nonce to be present
38    pub required_nonce: Option<String>,
39
40    /// Require the issuer to be present in the set
41    pub allowed_issuers: Option<HashSet<String>>,
42
43    /// Require the audience to be present in the set
44    pub allowed_audiences: Option<HashSet<String>>,
45
46    /// How much clock drift to tolerate when verifying token timestamps
47    /// Default is 15 minutes, to work around common issues with clocks that are not perfectly accurate
48    pub time_tolerance: Option<Duration>,
49
50    /// Reject tokens created more than `max_validity` ago
51    pub max_validity: Option<Duration>,
52
53    /// Maximum token length to accept
54    pub max_token_length: Option<usize>,
55
56    /// Maximum unsafe, untrusted, unverified JWT header length to accept
57    pub max_header_length: Option<usize>,
58
59    /// Change the current time. Only used for testing.
60    pub artificial_time: Option<UnixTimeStamp>,
61}
62
63impl Default for VerificationOptions {
64    fn default() -> Self {
65        Self {
66            reject_before: None,
67            accept_future: false,
68            required_subject: None,
69            required_key_id: None,
70            required_signature_type: None,
71            required_content_type: None,
72            required_nonce: None,
73            allowed_issuers: None,
74            allowed_audiences: None,
75            time_tolerance: Some(Duration::from_secs(DEFAULT_TIME_TOLERANCE_SECS)),
76            max_validity: None,
77            max_token_length: Some(DEFAULT_MAX_TOKEN_LENGTH),
78            max_header_length: None,
79            artificial_time: None,
80        }
81    }
82}
83
84/// Options for header creation when constructing a token.
85#[derive(Debug, Clone, Default)]
86pub struct HeaderOptions {
87    /// The contents of the content type (`cty`) field in the JWT header. If set
88    /// to `None`, this field is not present on the serialized JWT.
89    pub content_type: Option<String>,
90    /// The contents of the signature type (`typ`) field in the JWT header. If
91    /// set to `None`, the serialized JWT's `typ` field will contain the string
92    /// "JWT".
93    pub signature_type: Option<String>,
94}
95
96#[derive(Debug, Clone, Default)]
97pub enum Salt {
98    /// No salt. This is the default.
99    #[default]
100    None,
101    /// A salt to be used for signing tokens.
102    Signer(Vec<u8>),
103    /// A salt to be used for verifying tokens.
104    Verifier(Vec<u8>),
105}
106
107impl Salt {
108    /// Get the length of the salt.
109    pub fn len(&self) -> usize {
110        match self {
111            Salt::None => 0,
112            Salt::Signer(s) => s.len(),
113            Salt::Verifier(s) => s.len(),
114        }
115    }
116
117    /// Check if the salt is empty.
118    pub fn is_empty(&self) -> bool {
119        self.len() == 0
120    }
121
122    /// Generate a new random salt.
123    pub fn generate() -> Self {
124        let mut salt = vec![0u8; 32];
125        rand::thread_rng().fill_bytes(&mut salt);
126        Salt::Signer(salt)
127    }
128}
129
130impl AsRef<[u8]> for Salt {
131    /// Get the salt as a byte slice.
132    fn as_ref(&self) -> &[u8] {
133        match self {
134            Salt::None => &[],
135            Salt::Signer(s) => s,
136            Salt::Verifier(s) => s,
137        }
138    }
139}
140
141/// Unsigned metadata about a key to be attached to tokens.
142/// This information can be freely tampered with by an intermediate party.
143/// Most applications should not need to use this.
144#[derive(Debug, Clone, Default)]
145pub struct KeyMetadata {
146    pub(crate) key_set_url: Option<String>,
147    pub(crate) public_key: Option<String>,
148    pub(crate) certificate_url: Option<String>,
149    pub(crate) certificate_sha1_thumbprint: Option<String>,
150    pub(crate) certificate_sha256_thumbprint: Option<String>,
151    pub(crate) salt: Salt,
152}
153
154impl KeyMetadata {
155    /// Add a salt to the metadata
156    pub fn with_salt(mut self, salt: Salt) -> Self {
157        self.salt = salt;
158        self
159    }
160
161    /// Add a key set URL to the metadata ("jku")
162    pub fn with_key_set_url(mut self, key_set_url: impl ToString) -> Self {
163        self.key_set_url = Some(key_set_url.to_string());
164        self
165    }
166
167    /// Add a public key to the metadata ("jwk")
168    pub fn with_public_key(mut self, public_key: impl ToString) -> Self {
169        self.public_key = Some(public_key.to_string());
170        self
171    }
172
173    /// Add a certificate URL to the metadata ("x5u")
174    pub fn with_certificate_url(mut self, certificate_url: impl ToString) -> Self {
175        self.certificate_url = Some(certificate_url.to_string());
176        self
177    }
178
179    /// Add a certificate SHA-1 thumbprint to the metadata ("x5t")
180    pub fn with_certificate_sha1_thumbprint(
181        mut self,
182        certificate_sha1_thumbprint: impl ToString,
183    ) -> Result<Self, Error> {
184        let thumbprint = certificate_sha1_thumbprint.to_string();
185        let mut bin = [0u8; 20];
186        if thumbprint.len() == 40 {
187            ensure!(
188                Hex::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
189                JWTError::InvalidCertThumprint
190            );
191            let thumbprint = Base64UrlSafeNoPadding::encode_to_string(bin)?;
192            self.certificate_sha1_thumbprint = Some(thumbprint);
193            return Ok(self);
194        }
195        ensure!(
196            Base64UrlSafeNoPadding::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
197            JWTError::InvalidCertThumprint
198        );
199        self.certificate_sha1_thumbprint = Some(thumbprint);
200        Ok(self)
201    }
202
203    /// Add a certificate SHA-256 thumbprint to the metadata ("x5t#256")
204    pub fn with_certificate_sha256_thumbprint(
205        mut self,
206        certificate_sha256_thumbprint: impl ToString,
207    ) -> Result<Self, Error> {
208        let thumbprint = certificate_sha256_thumbprint.to_string();
209        let mut bin = [0u8; 32];
210        if thumbprint.len() == 64 {
211            ensure!(
212                Hex::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
213                JWTError::InvalidCertThumprint
214            );
215            let thumbprint = Base64UrlSafeNoPadding::encode_to_string(bin)?;
216            self.certificate_sha256_thumbprint = Some(thumbprint);
217            return Ok(self);
218        }
219        ensure!(
220            Base64UrlSafeNoPadding::decode(&mut bin, &thumbprint, None)?.len() == bin.len(),
221            JWTError::InvalidCertThumprint
222        );
223        self.certificate_sha256_thumbprint = Some(thumbprint);
224        Ok(self)
225    }
226}
227
228#[inline(never)]
229pub(crate) fn timingsafe_eq(a: &[u8], b: &[u8]) -> bool {
230    if a.len() != b.len() {
231        return false;
232    }
233    a.iter().zip(b.iter()).fold(0, |c, (x, y)| c | (x ^ y)) == 0
234}