1use ct_codecs::{Base64UrlSafeNoPadding, Decoder, Encoder};
2use serde::{de::DeserializeOwned, Serialize};
3
4use crate::claims::*;
5use crate::common::*;
6use crate::error::*;
7use crate::jwt_header::*;
8
9pub const MAX_HEADER_LENGTH: usize = 8192;
10
11pub struct Token;
13
14#[derive(Debug, Clone, Default)]
16pub struct TokenMetadata {
17 pub(crate) jwt_header: JWTHeader,
18}
19
20impl TokenMetadata {
21 pub fn algorithm(&self) -> &str {
26 &self.jwt_header.algorithm
27 }
28
29 pub fn content_type(&self) -> Option<&str> {
31 self.jwt_header.content_type.as_deref()
32 }
33
34 pub fn key_id(&self) -> Option<&str> {
36 self.jwt_header.key_id.as_deref()
37 }
38
39 pub fn signature_type(&self) -> Option<&str> {
41 self.jwt_header.signature_type.as_deref()
42 }
43
44 pub fn critical(&self) -> Option<&[String]> {
46 self.jwt_header.critical.as_deref()
47 }
48
49 pub fn certificate_chain(&self) -> Option<&[String]> {
53 self.jwt_header.certificate_chain.as_deref()
54 }
55
56 pub fn key_set_url(&self) -> Option<&str> {
61 self.jwt_header.key_set_url.as_deref()
62 }
63
64 pub fn public_key(&self) -> Option<&str> {
69 self.jwt_header.public_key.as_deref()
70 }
71
72 pub fn certificate_url(&self) -> Option<&str> {
77 self.jwt_header.certificate_url.as_deref()
78 }
79
80 pub fn certificate_sha1_thumbprint(&self) -> Option<&str> {
85 self.jwt_header.certificate_sha1_thumbprint.as_deref()
86 }
87
88 pub fn certificate_sha256_thumbprint(&self) -> Option<&str> {
93 self.jwt_header.certificate_sha256_thumbprint.as_deref()
94 }
95
96 pub fn salt(&self) -> Option<Vec<u8>> {
98 match self.jwt_header.salt {
99 Some(ref salt) => {
100 let salt = Base64UrlSafeNoPadding::decode_to_vec(salt, None).unwrap();
101 Some(salt)
102 }
103 None => None,
104 }
105 }
106}
107
108impl Token {
109 pub(crate) fn build<AuthenticationOrSignatureFn, CustomClaims: Serialize + DeserializeOwned>(
110 jwt_header: &JWTHeader,
111 claims: JWTClaims<CustomClaims>,
112 authentication_or_signature_fn: AuthenticationOrSignatureFn,
113 ) -> Result<String, Error>
114 where
115 AuthenticationOrSignatureFn: FnOnce(&str) -> Result<Vec<u8>, Error>,
116 {
117 let jwt_header_json = serde_json::to_string(&jwt_header)?;
118 let claims_json = serde_json::to_string(&claims)?;
119 let authenticated = format!(
120 "{}.{}",
121 Base64UrlSafeNoPadding::encode_to_string(jwt_header_json)?,
122 Base64UrlSafeNoPadding::encode_to_string(claims_json)?
123 );
124 let authentication_tag_or_signature = authentication_or_signature_fn(&authenticated)?;
125 let mut token = authenticated;
126 token.push('.');
127 token.push_str(&Base64UrlSafeNoPadding::encode_to_string(
128 authentication_tag_or_signature,
129 )?);
130 Ok(token)
131 }
132
133 pub(crate) fn verify<
134 AuthenticationOrSignatureFn,
135 SaltCheckFn,
136 CustomClaims: Serialize + DeserializeOwned,
137 >(
138 jwt_alg_name: &'static str,
139 token: &str,
140 options: Option<VerificationOptions>,
141 authentication_or_signature_fn: AuthenticationOrSignatureFn,
142 salt_check_fn: SaltCheckFn,
143 ) -> Result<JWTClaims<CustomClaims>, Error>
144 where
145 AuthenticationOrSignatureFn: FnOnce(&str, &[u8]) -> Result<(), Error>,
146 SaltCheckFn: FnOnce(Option<&[u8]>) -> Result<(), Error>,
147 {
148 let options = options.unwrap_or_default();
149
150 if let Some(max_token_length) = options.max_token_length {
151 ensure!(token.len() <= max_token_length, JWTError::TokenTooLong);
152 }
153
154 let mut parts = token.split('.');
155 let jwt_header_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
156 ensure!(
157 jwt_header_b64.len() <= options.max_header_length.unwrap_or(MAX_HEADER_LENGTH),
158 JWTError::HeaderTooLarge
159 );
160 let claims_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
161 let authentication_tag_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
162 ensure!(parts.next().is_none(), JWTError::CompactEncodingError);
163 let jwt_header: JWTHeader = serde_json::from_slice(
164 &Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)?,
165 )?;
166
167 if let Some(expected_signature_type) = &options.required_signature_type {
168 let expected_signature_type_uc = expected_signature_type.to_uppercase();
169 let signature_type_uc = jwt_header
170 .signature_type
171 .ok_or(JWTError::RequiredSignatureTypeMismatch)?
172 .to_uppercase();
173 ensure!(
174 signature_type_uc == expected_signature_type_uc,
175 JWTError::RequiredSignatureTypeMismatch
176 )
177 } else if let Some(signature_type) = &jwt_header.signature_type {
178 let signature_type_uc = signature_type.to_uppercase();
179 ensure!(
180 signature_type_uc == "JWT" || signature_type_uc.ends_with("+JWT"),
181 JWTError::NotJWT
182 );
183 }
184
185 if let Some(expected_content_type) = &options.required_content_type {
186 let expected_content_type_uc = expected_content_type.to_uppercase();
187 let content_type_uc = jwt_header
188 .content_type
189 .ok_or(JWTError::RequiredContentTypeMismatch)?
190 .to_uppercase();
191 ensure!(
192 content_type_uc == expected_content_type_uc,
193 JWTError::RequiredContentTypeMismatch
194 );
195 }
196
197 ensure!(
198 jwt_header.algorithm == jwt_alg_name,
199 JWTError::AlgorithmMismatch
200 );
201 if let Some(required_key_id) = &options.required_key_id {
202 if let Some(key_id) = &jwt_header.key_id {
203 ensure!(key_id == required_key_id, JWTError::KeyIdentifierMismatch);
204 } else {
205 bail!(JWTError::MissingJWTKeyIdentifier)
206 }
207 }
208 if let Some(salt) = &jwt_header.salt {
209 let salt = Base64UrlSafeNoPadding::decode_to_vec(salt, None)?;
210 salt_check_fn(Some(&salt))?;
211 } else {
212 salt_check_fn(None)?;
213 }
214 let authentication_tag =
215 Base64UrlSafeNoPadding::decode_to_vec(authentication_tag_b64, None)?;
216 let authenticated = &token[..jwt_header_b64.len() + 1 + claims_b64.len()];
217 authentication_or_signature_fn(authenticated, &authentication_tag)?;
218 let claims: JWTClaims<CustomClaims> =
219 serde_json::from_slice(&Base64UrlSafeNoPadding::decode_to_vec(claims_b64, None)?)?;
220 claims.validate(&options)?;
221 Ok(claims)
222 }
223
224 pub fn decode_metadata(token: &str) -> Result<TokenMetadata, Error> {
227 let mut parts = token.split('.');
228 let jwt_header_b64 = parts.next().ok_or(JWTError::CompactEncodingError)?;
229 ensure!(
230 jwt_header_b64.len() <= MAX_HEADER_LENGTH,
231 JWTError::HeaderTooLarge
232 );
233 let jwt_header: JWTHeader = serde_json::from_slice(
234 &Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)?,
235 )?;
236 Ok(TokenMetadata { jwt_header })
237 }
238}
239
240#[test]
241fn should_verify_token() {
242 use crate::prelude::*;
243
244 let key = HS256Key::generate();
245
246 let issuer = "issuer";
247 let audience = "recipient";
248 let mut claims = Claims::create(Duration::from_mins(10))
249 .with_issuer(issuer)
250 .with_audience(audience);
251 let nonce = claims.create_nonce();
252 let token = key.authenticate(claims).unwrap();
253
254 let options = VerificationOptions {
255 required_nonce: Some(nonce),
256 allowed_issuers: Some(HashSet::from_strings(&[issuer])),
257 allowed_audiences: Some(HashSet::from_strings(&[audience])),
258 ..Default::default()
259 };
260 key.verify_token::<NoCustomClaims>(&token, Some(options))
261 .unwrap();
262}
263
264#[test]
265fn multiple_audiences() {
266 use std::collections::HashSet;
267
268 use crate::prelude::*;
269
270 let key = HS256Key::generate();
271
272 let mut audiences = HashSet::new();
273 audiences.insert("audience 1");
274 audiences.insert("audience 2");
275 audiences.insert("audience 3");
276 let claims = Claims::create(Duration::from_mins(10)).with_audiences(audiences);
277 let token = key.authenticate(claims).unwrap();
278
279 let options = VerificationOptions {
280 allowed_audiences: Some(HashSet::from_strings(&["audience 1"])),
281 ..Default::default()
282 };
283 key.verify_token::<NoCustomClaims>(&token, Some(options))
284 .unwrap();
285}
286
287#[test]
288fn explicitly_empty_audiences() {
289 use std::collections::HashSet;
290
291 use crate::prelude::*;
292
293 let key = HS256Key::generate();
294
295 let audiences: HashSet<&str> = HashSet::new();
296 let claims = Claims::create(Duration::from_mins(10)).with_audiences(audiences);
297 let token = key.authenticate(claims).unwrap();
298 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
299 assert!(decoded.audiences.is_some());
300
301 let claims = Claims::create(Duration::from_mins(10)).with_audience("");
302 let token = key.authenticate(claims).unwrap();
303 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
304 assert!(decoded.audiences.is_some());
305
306 let claims = Claims::create(Duration::from_mins(10));
307 let token = key.authenticate(claims).unwrap();
308 let decoded = key.verify_token::<NoCustomClaims>(&token, None).unwrap();
309 assert!(decoded.audiences.is_none());
310}
311
312#[test]
313fn very_old_artificial_time() {
314 use crate::prelude::*;
315 let key = RS256PublicKey::from_pem(
316 r#"-----BEGIN PUBLIC KEY-----
317MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt5N44H1mpb5Wlx/0e7Cd
318oKTY8xt+3yMby8BgNdagVNkeCkZ4pRbmQXRWNC7qn//Zaxx9dnzHbzGCul5W0RLf
319d3oB3PESwsrQh+oiXVEPTYhvUPQkX0vBfCXJtg/zY2mY1DxKOIiXnZ8PaK/7Sx0a
320MmvR//0Yy2a5dIAWCmjPsxn+PcGZOkVUm+D5bH1+ZStcA/68r4ZSPix7Szhgl1Ro
321Hb9Q6JSekyZqM0Qfwhgb7srZVXC/9/m5PEx9wMVNYpYJBrXhD5IQm9RzE9oJS8T+
322Ai+4/5mNTNXI8f1rrYgffWS4wf9cvsEihrvEg9867B2f98L7ux9Llle7jsHCtwgV
3231wIDAQAB
324-----END PUBLIC KEY-----"#,
325 )
326 .unwrap();
327 let jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwiaXNzIjoiaHR0cHM6Ly9jaHJvbm9nZWFycy5jb20vdGVzdCIsImF1ZCI6InRlc3QiLCJhdXRoX3RpbWUiOjEwMCwidXNlcl9pZCI6InVpZDEyMyIsInN1YiI6InNidTEyMyIsImlhdCI6MjAwLCJleHAiOjUwMCwibmJmIjozMDAsImVtYWlsIjoiYWxvdmVsYWNlQGNocm9ub2dlYXJzLmNvbSJ9.eTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
328
329 let mut options = VerificationOptions::default();
330 options.artificial_time = Some(UnixTimeStamp::from_secs(400));
331 let res = key.verify_token::<NoCustomClaims>(jwt, Some(options.clone()));
332 assert!(res.is_err());
333
334 options.time_tolerance = Some(Duration::from_secs(100));
335 key.verify_token::<NoCustomClaims>(jwt, Some(options))
336 .unwrap();
337}
338
339#[test]
340fn content_type() {
341 use crate::prelude::*;
342 let key = HS256Key::generate();
343 let options = VerificationOptions {
344 required_content_type: Some("JWT".into()),
345 ..VerificationOptions::default()
346 };
347 let token = key
348 .authenticate(Claims::create(Duration::from_secs(86400)))
349 .unwrap();
350 let res = key.verify_token::<NoCustomClaims>(&token, Some(options.clone()));
351 assert!(res.is_err());
352
353 let token = key
354 .authenticate_with_options(
355 Claims::create(Duration::from_secs(86400)),
356 &HeaderOptions {
357 content_type: Some("jwt".into()),
358 ..Default::default()
359 },
360 )
361 .unwrap();
362 key.verify_token::<NoCustomClaims>(&token, Some(options.clone()))
363 .unwrap();
364}
365
366#[test]
367fn signature_type() {
368 use crate::prelude::*;
369 let key = ES256KeyPair::generate();
370 let options = VerificationOptions {
371 required_signature_type: Some("dpop+jwt".into()),
372 ..VerificationOptions::default()
373 };
374 let token = key
375 .sign(Claims::create(Duration::from_secs(86400)))
376 .unwrap();
377 let res = key
378 .public_key()
379 .verify_token::<NoCustomClaims>(&token, Some(options.clone()));
380 assert!(res.is_err());
381
382 let token = key
383 .sign_with_options(
384 Claims::create(Duration::from_secs(86400)),
385 &HeaderOptions {
386 signature_type: Some("dpop+jwt".into()),
387 ..Default::default()
388 },
389 )
390 .unwrap();
391 key.public_key()
392 .verify_token::<NoCustomClaims>(&token, Some(options.clone()))
393 .unwrap();
394}