jwt_simple/lib.rs
1//! [](https://github.com/jedisct1/rust-jwt-simple/actions)
2//! [](https://docs.rs/jwt-simple/)
3//! [](https://crates.io/crates/jwt-simple)
4//!
5//! <!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
6//!
7//! <!-- code_chunk_output -->
8//!
9//! - [JWT-Simple](#jwt-simple)
10//! - [Usage](#usage)
11//! - [Authentication (symmetric, `HS*` JWT algorithms) example](#authentication-symmetric-hs-jwt-algorithms-example)
12//! - [Keys and tokens creation](#keys-and-tokens-creation)
13//! - [Token verification](#token-verification)
14//! - [Signatures (asymmetric, `RS*`, `PS*`, `ES*` and `EdDSA` algorithms) example](#signatures-asymmetric-rs-ps-es-and-eddsa-algorithms-example)
15//! - [Key pairs and tokens creation](#key-pairs-and-tokens-creation)
16//! - [ES256](#es256)
17//! - [ES384](#es384)
18//! - [JWE (Encrypted tokens)](#jwe-encrypted-tokens)
19//! - [RSA-OAEP key management](#rsa-oaep-key-management)
20//! - [AES Key Wrap](#aes-key-wrap)
21//! - [ECDH-ES key agreement](#ecdh-es-key-agreement)
22//! - [Advanced usage](#advanced-usage)
23//! - [Custom claims](#custom-claims)
24//! - [Peeking at metadata before verification](#peeking-at-metadata-before-verification)
25//! - [Creating and attaching key identifiers](#creating-and-attaching-key-identifiers)
26//! - [Mitigations against replay attacks](#mitigations-against-replay-attacks)
27//! - [Salted keys](#salted-keys)
28//! - [CWT (CBOR) support](#cwt-cbor-support)
29//! - [Specifying header options](#specifying-header-options)
30//! - [Validating content and signature types](#validating-content-and-signature-types)
31//! - [Working around compilation issues with the `boring` crate](#working-around-compilation-issues-with-the-boring-crate)
32//! - [Usage in Web browsers](#usage-in-web-browsers)
33//! - [Why yet another JWT crate](#why-yet-another-jwt-crate)
34//!
35//! <!-- /code_chunk_output -->
36//!
37//! # JWT-Simple
38//!
39//! A new JWT (JSON Web Tokens) implementation for Rust that focuses on simplicity, while avoiding common JWT security pitfalls.
40//!
41//! `jwt-simple` is unopinionated and supports all commonly deployed authentication and signature algorithms:
42//!
43//! | JWT algorithm name | Description |
44//! | ------------------ | ------------------------------------- |
45//! | `HS256` | HMAC-SHA-256 |
46//! | `HS384` | HMAC-SHA-384 |
47//! | `HS512` | HMAC-SHA-512 |
48//! | `BLAKE2B` | BLAKE2B-256 |
49//! | `RS256` | RSA with PKCS#1v1.5 padding / SHA-256 |
50//! | `RS384` | RSA with PKCS#1v1.5 padding / SHA-384 |
51//! | `RS512` | RSA with PKCS#1v1.5 padding / SHA-512 |
52//! | `PS256` | RSA with PSS padding / SHA-256 |
53//! | `PS384` | RSA with PSS padding / SHA-384 |
54//! | `PS512` | RSA with PSS padding / SHA-512 |
55//! | `ES256` | ECDSA over p256 / SHA-256 |
56//! | `ES384` | ECDSA over p384 / SHA-384 |
57//! | `ES256K` | ECDSA over secp256k1 / SHA-256 |
58//! | `EdDSA` | Ed25519 |
59//!
60//! JWE (JSON Web Encryption) is also supported with the following key management algorithms:
61//!
62//! | JWE algorithm name | Description |
63//! | ------------------ | ------------------------------------------- |
64//! | `RSA-OAEP` | RSA with OAEP using SHA-1 (not recommended) |
65//! | `A256KW` | AES-256 Key Wrap |
66//! | `A128KW` | AES-128 Key Wrap |
67//! | `ECDH-ES+A256KW` | ECDH with AES-256 Key Wrap |
68//! | `ECDH-ES+A128KW` | ECDH with AES-128 Key Wrap |
69//!
70//! Content encryption uses AES-GCM (A256GCM or A128GCM).
71//!
72//! `jwt-simple` can be compiled out of the box to WebAssembly/WASI. It is fully compatible with Fastly _Compute_ service.
73//!
74//! Important: JWT's purpose is to verify that data has been created by a party knowing a secret key. It does not provide any kind of confidentiality: JWT data is simply encoded as Base64, and is not encrypted.
75//!
76//! ## Usage
77//!
78//! `cargo.toml`:
79//!
80//! ```toml
81//! [dependencies]
82//! jwt-simple = "0.12"
83//! ```
84//!
85//! Rust:
86//!
87//! ```rust
88//! use jwt_simple::prelude::*;
89//! ```
90//!
91//! Errors are returned as `jwt_simple::Error` values (alias for the `Error` type of the `anyhow` crate).
92//!
93//! ## Authentication (symmetric, `HS*` JWT algorithms) example
94//!
95//! Authentication schemes use the same key for creating and verifying tokens. In other words, both parties need to ultimately trust each other, or else the verifier could also create arbitrary tokens.
96//!
97//! ### Keys and tokens creation
98//!
99//! Key creation:
100//!
101//! ```rust
102//! use jwt_simple::prelude::*;
103//!
104//! // create a new key for the `HS256` JWT algorithm
105//! let key = HS256Key::generate();
106//! ```
107//!
108//! A key can be exported as bytes with `key.to_bytes()`, and restored with `HS256Key::from_bytes()`.
109//!
110//! Token creation:
111//!
112//! ```rust,ignore
113//! // create claims valid for 2 hours
114//! let claims = Claims::create(Duration::from_hours(2));
115//! let token = key.authenticate(claims)?;
116//! ```
117//!
118//! -> Done!
119//!
120//! ### Token verification
121//!
122//! ```rust,ignore
123//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
124//! ```
125//!
126//! -> Done! No additional steps required.
127//!
128//! Key expiration, start time, authentication tags, etc. are automatically verified. The function fails with `JWTError::InvalidAuthenticationTag` if the authentication tag is invalid for the given key.
129//!
130//! The full set of claims can be inspected in the `claims` object if necessary. `NoCustomClaims` means that only the standard set of claims is used by the application, but application-defined claims can also be supported.
131//!
132//! Extra verification steps can optionally be enabled via the `VerificationOptions` structure:
133//!
134//! ```rust,ignore
135//! let mut options = VerificationOptions::default();
136//! // Accept tokens that will only be valid in the future
137//! options.accept_future = true;
138//! // Accept tokens even if they have expired up to 15 minutes after the deadline,
139//! // and/or they will be valid within 15 minutes.
140//! // Note that 15 minutes is the default, since it is very common for clocks to be slightly off.
141//! options.time_tolerance = Some(Duration::from_mins(15));
142//! // Reject tokens if they were issued more than 1 hour ago
143//! options.max_validity = Some(Duration::from_hours(1));
144//! // Reject tokens if they don't include an issuer from that set
145//! options.allowed_issuers = Some(HashSet::from_strings(&["example app"]));
146//!
147//! // see the documentation for the full list of available options
148//!
149//! let claims = key.verify_token::<NoCustomClaims>(&token, Some(options))?;
150//! ```
151//!
152//! Note that `allowed_issuers` and `allowed_audiences` are not strings, but sets of strings (using the `HashSet` type from the Rust standard library), as the application can allow multiple values.
153//!
154//! ## Signatures (asymmetric, `RS*`, `PS*`, `ES*` and `EdDSA` algorithms) example
155//!
156//! A signature requires a key pair: a secret key used to create tokens, and a public key, that can only verify them.
157//!
158//! Always use a signature scheme if both parties do not ultimately trust each other, such as tokens exchanged between clients and API providers.
159//!
160//! ### Key pairs and tokens creation
161//!
162//! Key creation:
163//!
164//! #### ES256
165//!
166//! ```rust
167//! use jwt_simple::prelude::*;
168//!
169//! // create a new key pair for the `ES256` JWT algorithm
170//! let key_pair = ES256KeyPair::generate();
171//!
172//! // a public key can be extracted from a key pair:
173//! let public_key = key_pair.public_key();
174//! ```
175//!
176//! #### ES384
177//!
178//! ```rust
179//! use jwt_simple::prelude::*;
180//!
181//! // create a new key pair for the `ES384` JWT algorithm
182//! let key_pair = ES384KeyPair::generate();
183//!
184//! // a public key can be extracted from a key pair:
185//! let public_key = key_pair.public_key();
186//! ```
187//!
188//! Keys can be exported as bytes for later reuse, and imported from bytes or, for RSA, from individual parameters, DER-encoded data or PEM-encoded data.
189//!
190//! RSA key pair creation, using OpenSSL and PEM importation of the secret key:
191//!
192//! ```sh
193//! openssl genrsa -out private.pem 2048
194//! openssl rsa -in private.pem -outform PEM -pubout -out public.pem
195//! ```
196//!
197//! ```rust,ignore
198//! let key_pair = RS384KeyPair::from_pem(private_pem_file_content)?;
199//! let public_key = RS384PublicKey::from_pem(public_pem_file_content)?;
200//! ```
201//!
202//! Token creation and verification work the same way as with `HS*` algorithms, except that tokens are created with a key pair, and verified using the corresponding public key.
203//!
204//! Token creation:
205//!
206//! ```rust,ignore
207//! // create claims valid for 2 hours
208//! let claims = Claims::create(Duration::from_hours(2));
209//! let token = key_pair.sign(claims)?;
210//! ```
211//!
212//! Token verification:
213//!
214//! ```rust,ignore
215//! let claims = public_key.verify_token::<NoCustomClaims>(&token, None)?;
216//! ```
217//!
218//! Available verification options are identical to the ones used with symmetric algorithms.
219//!
220//! ## JWE (Encrypted tokens)
221//!
222//! While JWT signatures provide authenticity (verifying who created the token), JWE provides confidentiality by encrypting the token content. Use JWE when the claims contain sensitive data that should not be visible to intermediaries.
223//!
224//! ### RSA-OAEP key management
225//!
226//! RSA-OAEP uses asymmetric encryption: anyone with the public key can encrypt tokens, but only the private key holder can decrypt them.
227//!
228//! ```rust,ignore
229//! use jwt_simple::prelude::*;
230//!
231//! // Generate a key pair (2048 bits minimum, 4096 recommended for high security)
232//! let decryption_key = RsaOaepDecryptionKey::generate(2048)?;
233//! let encryption_key = decryption_key.encryption_key();
234//!
235//! // Encrypt a token
236//! let claims = Claims::create(Duration::from_hours(1))
237//! .with_subject("user@example.com");
238//! let token = encryption_key.encrypt(claims)?;
239//!
240//! // Decrypt the token
241//! let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None)?;
242//! ```
243//!
244//! Keys can be exported and imported using PEM or DER formats, similar to RSA signature keys.
245//!
246//! ### AES Key Wrap
247//!
248//! For symmetric encryption where the same key is used for both encryption and decryption:
249//!
250//! ```rust,ignore
251//! use jwt_simple::prelude::*;
252//!
253//! // Generate a 256-bit key
254//! let key = A256KWKey::generate();
255//!
256//! // Or create from existing bytes
257//! let key = A256KWKey::from_bytes(&raw_key_bytes)?;
258//!
259//! // Encrypt
260//! let claims = Claims::create(Duration::from_hours(1));
261//! let token = key.encrypt(claims)?;
262//!
263//! // Decrypt
264//! let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None)?;
265//! ```
266//!
267//! `A128KWKey` is also available for 128-bit keys.
268//!
269//! ### ECDH-ES key agreement
270//!
271//! ECDH-ES uses elliptic curve Diffie-Hellman for key agreement. Like RSA-OAEP, it uses asymmetric keys but is more efficient:
272//!
273//! ```rust,ignore
274//! use jwt_simple::prelude::*;
275//!
276//! // Generate a key pair
277//! let decryption_key = EcdhEsA256KWDecryptionKey::generate();
278//! let encryption_key = decryption_key.encryption_key();
279//!
280//! // Encrypt
281//! let claims = Claims::create(Duration::from_hours(1));
282//! let token = encryption_key.encrypt(claims)?;
283//!
284//! // Decrypt
285//! let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None)?;
286//! ```
287//!
288//! JWE tokens support the same claim types and custom claims as JWT signatures. Decryption options allow validating claims, requiring specific key IDs, and limiting token size.
289//!
290//! ## Advanced usage
291//!
292//! ### Custom claims
293//!
294//! Claim objects support all the standard claims by default, and they can be set directly or via convenient helpers:
295//!
296//! ```rust,ignore
297//! let claims = Claims::create(Duration::from_hours(2)).
298//! with_issuer("Example issuer").with_subject("Example subject");
299//! ```
300//!
301//! But application-defined claims can also be used. These simply have to be present in a serializable type (this requires the `serde` crate):
302//!
303//! ```rust,ignore
304//! #[derive(Serialize, Deserialize)]
305//! struct MyAdditionalData {
306//! user_is_admin: bool,
307//! user_country: String,
308//! }
309//! let my_additional_data = MyAdditionalData {
310//! user_is_admin: false,
311//! user_country: "FR".to_string(),
312//! };
313//! ```
314//!
315//! Claim creation with custom data:
316//!
317//! ```rust,ignore
318//! let claims = Claims::with_custom_claims(my_additional_data, Duration::from_secs(30));
319//! ```
320//!
321//! Claim verification with custom data. Note the presence of the custom data type:
322//!
323//! ```rust,ignore
324//! let claims = public_key.verify_token::<MyAdditionalData>(&token, None)?;
325//! let user_is_admin = claims.custom.user_is_admin;
326//! ```
327//!
328//! ### Peeking at metadata before verification
329//!
330//! Properties such as the key identifier can be useful prior to tag or signature verification in order to pick the right key out of a set.
331//!
332//! ```rust,ignore
333//! let metadata = Token::decode_metadata(&token)?;
334//! let key_id = metadata.key_id();
335//! let algorithm = metadata.algorithm();
336//! // all other standard properties are also accessible
337//! ```
338//!
339//! **IMPORTANT:** neither the key ID nor the algorithm can be trusted. This is an unfixable design flaw of the JWT standard.
340//!
341//! As a result, `algorithm` should be used only for debugging purposes, and never to select a key type.
342//! Similarly, `key_id` should be used only to select a key in a set of keys made for the same algorithm.
343//!
344//! At the bare minimum, verification using `HS*` must be prohibited if a signature scheme was originally used to create the token.
345//!
346//! ### Creating and attaching key identifiers
347//!
348//! Key identifiers indicate to verifiers what public key (or shared key) should be used for verification.
349//! They can be attached at any time to existing shared keys, key pairs and public keys:
350//!
351//! ```rust,ignore
352//! let public_key_with_id = public_key.with_key_id(&"unique key identifier");
353//! ```
354//!
355//! Instead of delegating this to applications, `jwt-simple` can also create such an identifier for an existing key:
356//!
357//! ```rust,ignore
358//! let key_id = public_key.create_key_id();
359//! ```
360//!
361//! This creates a text-encoded identifier for the key, attaches it, and returns it.
362//!
363//! If an identifier has been attached to a shared key or a key pair, tokens created with them will include it.
364//!
365//! ### Mitigations against replay attacks
366//!
367//! `jwt-simple` includes mechanisms to mitigate replay attacks:
368//!
369//! - Nonces can be created and attached to new tokens using the `create_nonce()` claim function. The verification procedure can later reject any token that doesn't include the expected nonce (`required_nonce` verification option).
370//! - The verification procedure can reject tokens created too long ago, no matter what their expiration date is. This prevents tokens from malicious (or compromised) signers from being used for too long.
371//! - The verification procedure can reject tokens created before a date. For a given user, the date of the last successful authentication can be stored in a database, and used later along with this option to reject older (replayed) tokens.
372//!
373//! ### Salted keys
374//!
375//! Symmetric keys, such as the ones used with the `HS256`, `HS384`, `HS512` and `BLAKE2B` algorithms, are simple and fast, but have a major downside: signature and verification use the exact same key. Therefore, an adversary having access to the verifier key can forge arbitrary, valid tokens.
376//!
377//! Salted keys mitigate this issue in the following way:
378//!
379//! - A random signer salt is created and attached to the shared key. This salt is meant to be known only by the signer.
380//! - Another salt is computed from the signer salt and is meant to be used for verification.
381//! - The verifier salt is used to verify the signer salt, which is included in tokens in the `salt` JWT header.
382//!
383//! If the verifier has access to tokens, it can forge arbitrary tokens. But given only the verification code and keys, this is impossible. This greatly improves the security of symmetric keys used for verification on 3rd party servers, such as CDNs.
384//!
385//! A salt binds to a key, and can be of any length. The `generate_with_salt()` function generates both a random symmetric key, and a 32-byte salt.
386//!
387//! Example usage:
388//!
389//! ```rust,ignore
390//! // Create a random key and a signer salt
391//! let key = HS256Key::generate_with_salt();
392//! let claims = Claims::create(Duration::from_secs(86400));
393//! let token = key.authenticate(claims).unwrap();
394//! ```
395//!
396//! A salt is a `Salt` enum, because it can be either a salt for signing, or a salt for verification.
397//! It can be saved and restored:
398//!
399//! ```rust,ignore
400//! // Get the salt
401//! let salt = key.salt();
402//! // Attach an existing salt to a key
403//! key.attach_salt(salt)?;
404//! ```
405//!
406//! Given a signer salt, the corresponding verifier salt can be computed:
407//!
408//! ```rust,ignore
409//! // Compute the verifier salt, given a signer salt
410//! let verifier_salt = key.verifier_salt()?;
411//! ```
412//!
413//! The verifier salt doesn't have to be secret, and can even be hard-coded in the verification code.
414//!
415//! Verification:
416//!
417//! ```rust,ignore
418//! let verifier_salt = Salt::Verifier(verifier_salt_bytes);
419//! key.attach_salt(verifier_salt)?;
420//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
421//! ```
422//!
423//! ### CWT (CBOR) support
424//!
425//! The development code includes a `cwt` cargo feature that enables experimental parsing and validation of CWT tokens.
426//!
427//! Please note that CWT doesn't support custom claims. The required identifiers [haven't been standardized yet](https://www.iana.org/assignments/cwt/cwt.xhtml).
428//!
429//! Also, the existing Rust crates for JSON and CBOR deserialization are not safe. An untrusted party can send a serialized object that requires a lot of memory and CPU to deserialize. Band-aids have been added for JSON, but with the current Rust tooling, it would be tricky to implement for CBOR.
430//!
431//! As a mitigation, we highly recommend rejecting tokens that would be too large in the context of your application. That can be done with the `max_token_length` verification option.
432//!
433//! ### Specifying header options
434//!
435//! It is possible to change the content type (`cty`) and signature type (`typ`) fields of a signed JWT by using the `sign_with_options`/`authenticate_with_options` functions and passing in a `HeaderOptions` struct:
436//!
437//! ``` rust,ignore
438//! let options = HeaderOptions {
439//! content_type: Some("foo".into()),
440//! signature_type: Some("foo+JWT".into()),
441//! ..Default::default()
442//! };
443//! key_pair.sign_with_options(claims, &options).unwrap();
444//! ```
445//!
446//! By default, generated JWTs will have a signature type field containing the string "JWT", and the content type field will not be present.
447//!
448//! ### Validating content and signature types
449//!
450//! By default, `jwt_simple` ignores the `content_type` field when doing validation, and checks `signature_type` to ensure it is either exactly `JWT` or ends in `+JWT`, case insensitive, if it is present. Both fields may instead be case-insensitively compared against an expected string:
451//!
452//! ```rust,ignore
453//! options.required_signature_type = Some("JWT".into());
454//! options.required_content_type = Some("foo+jwt".into());
455//! ```
456//!
457//! When validating CWTs, note that CWTs do not have a `content_type` field in their header, and therefore attempting to match a specific one by setting `required_content_type` during validation will **always result in an error**.
458//!
459//!
460//! ## Working around compilation issues with the `boring` crate
461//!
462//! As a temporary workaround for portability issues with one of the dependencies (the `boring` crate), this library can be compiled to use only Rust implementations.
463//!
464//! In order to do so, import the crate with `default-features = false, features = ["pure-rust"]` in your Cargo configuration.
465//!
466//! Do not do it unconditionally. This is only required for very specific setups and targets, and only until issues with the `boring` crate have been solved. The way to configure this in Cargo may also change in future versions.
467//!
468//! Static builds targeting the `musl` library don't require that workaround. Just use [`cargo-zigbuild`](https://github.com/rust-cross/cargo-zigbuild) to build your project.
469//!
470//! ## Usage in Web browsers
471//!
472//! The `wasm32-freestanding` target (still sometimes called `wasm32-unknown-unknown` in Rust) is supported (as in "it compiles").
473//!
474//! However, using a native JavaScript implementation is highly recommended instead. There are high-quality JWT implementations in JavaScript, leveraging the WebCrypto API, that provide better performance and security guarantees than a WebAssembly module.
475//!
476//! ## Why yet another JWT crate
477//!
478//! This crate is not an endorsement of JWT. JWT is [an awful design](https://tools.ietf.org/html/rfc8725), and one of the many examples that "but this is a standard" doesn't necessarily mean that it is good.
479//!
480//! I would highly recommend [PASETO](https://github.com/paragonie/paseto) or [Biscuit](https://github.com/CleverCloud/biscuit) instead if you control both token creation and verification.
481//!
482//! However, JWT is still widely used in the industry, and remains absolutely mandatory to communicate with popular APIs.
483//!
484//! This crate was designed to:
485//!
486//! - Be simple to use, even for people who are new to Rust
487//! - Avoid common JWT API pitfalls
488//! - Support features widely in use. I'd love to limit the algorithm choices to Ed25519, but other methods are required to connect to existing APIs, so we provide them (with the exception of the `None` signature method for obvious reasons).
489//! - Minimize code complexity and external dependencies
490//! - Automatically perform common tasks to prevent misuse. Signature verification and claims validation happen automatically instead of relying on applications.
491//! - Still allow power users to access everything JWT tokens include if they really need to
492//! - Work out of the box in a WebAssembly environment, so that it can be used in function-as-a-service platforms.
493#![forbid(unsafe_code)]
494
495#[cfg(all(feature = "pure-rust", feature = "optimal"))]
496compile_error!("jwt-simple: the `optimal` feature is only available when the `pure-rust` feature is disabled - Consider disabling default Cargo features.");
497
498#[cfg(all(not(feature = "pure-rust"), not(feature = "optimal")))]
499compile_error!("jwt-simple: the `optimal` feature is required when the `pure-rust` feature is disabled - Consider enabling default Cargo features.");
500
501pub mod algorithms;
502pub mod claims;
503pub mod common;
504#[cfg(feature = "cwt")]
505pub mod cwt_token;
506#[cfg(feature = "jwe")]
507pub mod jwe_header;
508#[cfg(feature = "jwe")]
509pub mod jwe_token;
510pub mod token;
511
512mod jwt_header;
513mod serde_additions;
514
515pub mod reexports {
516 pub use anyhow;
517 pub use coarsetime;
518 pub use ct_codecs;
519 pub use rand;
520 pub use serde;
521 pub use serde_json;
522 pub use thiserror;
523 pub use zeroize;
524}
525
526mod error;
527pub use error::{Error, JWTError};
528
529pub mod prelude {
530 pub use std::collections::HashSet;
531
532 pub use coarsetime::{self, Clock, Duration, UnixTimeStamp};
533 pub use ct_codecs::{
534 Base64, Base64NoPadding, Base64UrlSafe, Base64UrlSafeNoPadding, Decoder as _, Encoder as _,
535 };
536 pub use serde::{Deserialize, Serialize};
537
538 pub use crate::algorithms::*;
539 pub use crate::claims::*;
540 pub use crate::common::*;
541 #[cfg(feature = "cwt")]
542 pub use crate::cwt_token::*;
543 #[cfg(feature = "jwe")]
544 pub use crate::jwe_token::{DecryptionOptions, EncryptionOptions, JWEToken, JWETokenMetadata};
545 pub use crate::token::*;
546
547 mod hashset_from_strings {
548 use std::collections::HashSet;
549
550 pub trait HashSetFromStringsT {
551 /// Create a set from a list of strings
552 fn from_strings(strings: &[impl ToString]) -> HashSet<String> {
553 strings.iter().map(|x| x.to_string()).collect()
554 }
555 }
556
557 impl HashSetFromStringsT for HashSet<String> {}
558 }
559
560 pub use hashset_from_strings::HashSetFromStringsT as _;
561}
562
563#[cfg(test)]
564mod tests {
565 use crate::prelude::*;
566
567 const RSA_KP_PEM: &str = r"
568-----BEGIN RSA PRIVATE KEY-----
569MIIEpAIBAAKCAQEAyqq0N5u8Jvl+BLH2VMP/NAv/zY9T8mSq0V2Gk5Ql5H1a+4qi
5703viorUXG3AvIEEccpLsW85ps5+I9itp74jllRjA5HG5smbb+Oym0m2Hovfj6qP/1
571m1drQg8oth6tNmupNqVzlGGWZLsSCBLuMa3pFaPhoxl9lGU3XJIQ1/evMkOb98I3
572hHb4ELn3WGtNlAVkbP20R8sSii/zFjPqrG/NbSPLyAl1ctbG2d8RllQF1uRIqYQj
57385yx73hqQCMpYWU3d9QzpkLf/C35/79qNnSKa3t0cyDKinOY7JGIwh8DWAa4pfEz
574gg56yLcilYSSohXeaQV0nR8+rm9J8GUYXjPK7wIDAQABAoIBAQCpeRPYyHcPFGTH
5754lU9zuQSjtIq/+bP9FRPXWkS8bi6GAVEAUtvLvpGYuoGyidTTVPrgLORo5ncUnjq
576KwebRimlBuBLIR/Zboery5VGthoc+h4JwniMnQ6JIAoIOSDZODA5DSPYeb58n15V
577uBbNHkOiH/eoHsG/nOAtnctN/cXYPenkCfeLXa3se9EzkcmpNGhqCBL/awtLU17P
578Iw7XxsJsRMBOst4Aqiri1GQI8wqjtXWLyfjMpPR8Sqb4UpTDmU1wHhE/w/+2lahC
579Tu0/+sCWj7TlafYkT28+4pAMyMqUT6MjqdmGw8lD7/vXv8TF15NU1cUv3QSKpVGe
58050vlB1QpAoGBAO1BU1evrNvA91q1bliFjxrH3MzkTQAJRMn9PBX29XwxVG7/HlhX
5810tZRSR92ZimT2bAu7tH0Tcl3Bc3NwEQrmqKlIMqiW+1AVYtNjuipIuB7INb/TUM3
582smEh+fn3yhMoVxbbh/klR1FapPUFXlpNv3DJHYM+STqLMhl9tEc/I7bLAoGBANqt
583zR6Kovf2rh7VK/Qyb2w0rLJE7Zh/WI+r9ubCba46sorqkJclE5cocxWuTy8HWyQp
584spxzLP1FQlsI+MESgRLueoH3HtB9lu/pv6/8JlNjU6SzovfUZ0KztVUyUeB4vAcH
585pGcf2CkUtoYc8YL22Ybck3s8ThIdnY5zphCF55PtAoGAf46Go3c05XVKx78R05AD
586D2/y+0mnSGSzUjHPMzPyadIPxhltlCurlERhnwPGC4aNHFcvWTwS8kUGns6HF1+m
587JNnI1okSCW10UI/jTJ1avfwU/OKIBKKWSfi9cDJTt5cRs51V7pKnVEr6sy0uvDhe
588u+G091HuhwY9ak0WNtPwfJ8CgYEAuRdoyZQQso7x/Bj0tiHGW7EOB2n+LRiErj6g
589odspmNIH8zrtHXF9bnEHT++VCDpSs34ztuZpywnHS2SBoHH4HD0MJlszksbqbbDM
5901bk3+1bUIlEF/Hyk1jljn3QTB0tJ4y1dwweaH9NvVn7DENW9cr/aePGnJwA4Lq3G
591fq/IPlUCgYAuqgJQ4ztOq0EaB75xgqtErBM57A/+lMWS9eD/euzCEO5UzWVaiIJ+
592nNDmx/jvSrxA1Ih8TEHjzv4ezLFYpaJrTst4Mjhtx+csXRJU9a2W6HMXJ4Kdn8rk
593PBziuVURslNyLdlFsFlm/kfvX+4Cxrbb+pAGETtRTgmAoCDbvuDGRQ==
594-----END RSA PRIVATE KEY-----
595 ";
596
597 const RSA_PK_PEM: &str = r"
598-----BEGIN PUBLIC KEY-----
599MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqq0N5u8Jvl+BLH2VMP/
600NAv/zY9T8mSq0V2Gk5Ql5H1a+4qi3viorUXG3AvIEEccpLsW85ps5+I9itp74jll
601RjA5HG5smbb+Oym0m2Hovfj6qP/1m1drQg8oth6tNmupNqVzlGGWZLsSCBLuMa3p
602FaPhoxl9lGU3XJIQ1/evMkOb98I3hHb4ELn3WGtNlAVkbP20R8sSii/zFjPqrG/N
603bSPLyAl1ctbG2d8RllQF1uRIqYQj85yx73hqQCMpYWU3d9QzpkLf/C35/79qNnSK
604a3t0cyDKinOY7JGIwh8DWAa4pfEzgg56yLcilYSSohXeaQV0nR8+rm9J8GUYXjPK
6057wIDAQAB
606-----END PUBLIC KEY-----
607 ";
608
609 #[test]
610 fn hs384() {
611 let key = HS384Key::from_bytes(b"your-256-bit-secret").with_key_id("my-key-id");
612 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
613 let token = key.authenticate(claims).unwrap();
614 let options = VerificationOptions {
615 allowed_issuers: Some(HashSet::from_strings(&["test issuer"])),
616 ..Default::default()
617 };
618 let _claims = key
619 .verify_token::<NoCustomClaims>(&token, Some(options))
620 .unwrap();
621 }
622
623 #[test]
624 fn blake2b() {
625 let key = Blake2bKey::from_bytes(b"your-256-bit-secret").with_key_id("my-key-id");
626 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
627 let token = key.authenticate(claims).unwrap();
628 let options = VerificationOptions {
629 allowed_issuers: Some(HashSet::from_strings(&["test issuer"])),
630 ..Default::default()
631 };
632 let _claims = key
633 .verify_token::<NoCustomClaims>(&token, Some(options))
634 .unwrap();
635 }
636
637 #[test]
638 fn rs256() {
639 let key_pair = RS256KeyPair::from_pem(RSA_KP_PEM).unwrap();
640 let claims = Claims::create(Duration::from_secs(86400));
641 let token = key_pair.sign(claims).unwrap();
642 let pk = RS256PublicKey::from_pem(RSA_PK_PEM).unwrap();
643 let _claims = pk.verify_token::<NoCustomClaims>(&token, None).unwrap();
644 let components = pk.to_components();
645 let hex_e = Base64::encode_to_string(components.e).unwrap();
646 let _e = Base64::decode_to_vec(hex_e, None).unwrap();
647 }
648
649 #[test]
650 fn ps384() {
651 let key_pair = PS384KeyPair::generate(2048).unwrap();
652 let claims = Claims::create(Duration::from_secs(86400));
653 let token = key_pair.sign(claims).unwrap();
654 let _claims = key_pair
655 .public_key()
656 .verify_token::<NoCustomClaims>(&token, None)
657 .unwrap();
658 }
659
660 #[test]
661 fn es256() {
662 let key_pair = ES256KeyPair::generate();
663 let claims = Claims::create(Duration::from_secs(86400));
664 let token = key_pair.sign(claims).unwrap();
665 let _claims = key_pair
666 .public_key()
667 .verify_token::<NoCustomClaims>(&token, None)
668 .unwrap();
669 }
670
671 #[test]
672 fn es384() {
673 let key_pair = ES384KeyPair::generate();
674 let claims = Claims::create(Duration::from_secs(86400));
675 let token = key_pair.sign(claims).unwrap();
676 let _claims = key_pair
677 .public_key()
678 .verify_token::<NoCustomClaims>(&token, None)
679 .unwrap();
680 }
681
682 #[test]
683 fn es256k() {
684 let key_pair = ES256kKeyPair::generate();
685 let claims = Claims::create(Duration::from_secs(86400));
686 let token = key_pair.sign(claims).unwrap();
687 let _claims = key_pair
688 .public_key()
689 .verify_token::<NoCustomClaims>(&token, None)
690 .unwrap();
691 }
692
693 #[test]
694 fn ed25519() {
695 #[derive(Serialize, Deserialize)]
696 struct CustomClaims {
697 is_custom: bool,
698 }
699
700 let key_pair = Ed25519KeyPair::generate();
701 let mut pk = key_pair.public_key();
702 let key_id = pk.create_key_id();
703 let key_pair = key_pair.with_key_id(key_id);
704 let public_key = key_pair.public_key(); // Get public key after setting key_id
705 let custom_claims = CustomClaims { is_custom: true };
706 let claims = Claims::with_custom_claims(custom_claims, Duration::from_secs(86400));
707 let token = key_pair.sign(claims).unwrap();
708 let options = VerificationOptions {
709 required_key_id: Some(key_id.to_string()),
710 ..Default::default()
711 };
712 let claims: JWTClaims<CustomClaims> = public_key
713 .verify_token::<CustomClaims>(&token, Some(options))
714 .unwrap();
715 assert!(claims.custom.is_custom);
716 }
717
718 #[test]
719 fn ed25519_der() {
720 let key_pair = Ed25519KeyPair::generate();
721 let der = key_pair.to_der();
722 let key_pair2 = Ed25519KeyPair::from_der(&der).unwrap();
723 assert_eq!(key_pair.to_bytes(), key_pair2.to_bytes());
724 }
725
726 #[test]
727 fn require_nonce() {
728 let key = HS256Key::generate();
729 let mut claims = Claims::create(Duration::from_hours(1));
730 let nonce = claims.create_nonce();
731 let token = key.authenticate(claims).unwrap();
732
733 let options = VerificationOptions {
734 required_nonce: Some(nonce),
735 ..Default::default()
736 };
737 key.verify_token::<NoCustomClaims>(&token, Some(options))
738 .unwrap();
739 }
740
741 #[test]
742 fn eddsa_pem() {
743 let sk_pem = "-----BEGIN PRIVATE KEY-----
744MC4CAQAwBQYDK2VwBCIEIMXY1NUbUe/3dW2YUoKW5evsnCJPMfj60/q0RzGne3gg
745-----END PRIVATE KEY-----
746";
747 let pk_pem = "-----BEGIN PUBLIC KEY-----
748MCowBQYDK2VwAyEAyrRjJfTnhMcW5igzYvPirFW5eUgMdKeClGzQhd4qw+Y=
749-----END PUBLIC KEY-----
750";
751 let kp = Ed25519KeyPair::from_pem(sk_pem).unwrap();
752 assert_eq!(kp.public_key().to_pem(), pk_pem);
753 }
754
755 #[test]
756 fn key_metadata() {
757 let mut key_pair = Ed25519KeyPair::generate();
758 let thumbprint = key_pair.public_key().sha1_thumbprint();
759 let key_metadata = KeyMetadata::default()
760 .with_certificate_sha1_thumbprint(&thumbprint)
761 .unwrap();
762 key_pair.attach_metadata(key_metadata).unwrap();
763
764 let claims = Claims::create(Duration::from_secs(86400));
765 let token = key_pair.sign(claims).unwrap();
766
767 let decoded_metadata = Token::decode_metadata(&token).unwrap();
768 assert_eq!(
769 decoded_metadata.certificate_sha1_thumbprint(),
770 Some(thumbprint.as_ref())
771 );
772 let _ = key_pair
773 .public_key()
774 .verify_token::<NoCustomClaims>(&token, None)
775 .unwrap();
776 }
777
778 #[test]
779 fn set_header_content_type() {
780 let key_pair = Ed25519KeyPair::generate();
781 let claims = Claims::create(Duration::from_secs(86400));
782 let token = key_pair
783 .sign_with_options(
784 claims,
785 &HeaderOptions {
786 content_type: Some("foo".into()),
787 ..Default::default()
788 },
789 )
790 .unwrap();
791 let decoded_metadata = Token::decode_metadata(&token).unwrap();
792 assert_eq!(
793 decoded_metadata.jwt_header.content_type.as_deref(),
794 Some("foo")
795 );
796 let _ = key_pair
797 .public_key()
798 .verify_token::<NoCustomClaims>(&token, None)
799 .unwrap();
800 }
801
802 #[test]
803 fn set_header_signature_type() {
804 let key_pair = Ed25519KeyPair::generate();
805 let claims = Claims::create(Duration::from_secs(86400));
806 let token = key_pair
807 .sign_with_options(
808 claims,
809 &HeaderOptions {
810 signature_type: Some("etc+jwt".into()),
811 ..Default::default()
812 },
813 )
814 .unwrap();
815 let decoded_metadata = Token::decode_metadata(&token).unwrap();
816 assert_eq!(
817 decoded_metadata.jwt_header.signature_type.as_deref(),
818 Some("etc+jwt")
819 );
820 let _ = key_pair
821 .public_key()
822 .verify_token::<NoCustomClaims>(&token, None)
823 .unwrap();
824 }
825
826 #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
827 #[test]
828 fn expired_token() {
829 let key = HS256Key::generate();
830 let claims = Claims::create(Duration::from_secs(1));
831 let token = key.authenticate(claims).unwrap();
832 std::thread::sleep(std::time::Duration::from_secs(2));
833 let options = VerificationOptions {
834 time_tolerance: None,
835 ..Default::default()
836 };
837 let claims = key.verify_token::<NoCustomClaims>(&token, None);
838 assert!(claims.is_ok());
839 let claims = key.verify_token::<NoCustomClaims>(&token, Some(options));
840 assert!(claims.is_err());
841 }
842
843 #[test]
844 fn salt() {
845 let mut key = HS256Key::generate_with_salt();
846 let claims = Claims::create(Duration::from_secs(86400));
847 let token = key.authenticate(claims).unwrap();
848
849 let res = key.verify_token::<NoCustomClaims>(&token, None);
850 assert!(res.is_err());
851
852 let verifier_salt = key.verifier_salt().unwrap();
853 key.attach_salt(verifier_salt).unwrap();
854 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
855 }
856
857 #[test]
858 fn salt2() {
859 let mut key = HS256Key::generate();
860 let claims = Claims::create(Duration::from_secs(86400));
861 let token = key.authenticate(claims).unwrap();
862
863 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
864
865 let verifier_salt = Salt::Verifier(b"salt".to_vec());
866 key.attach_salt(verifier_salt).unwrap();
867 let res = key.verify_token::<NoCustomClaims>(&token, None);
868 assert!(res.is_err());
869 }
870
871 #[test]
872 fn weak_key_rejected_on_authenticate() {
873 let key = HS256Key::from_bytes(b"short");
874 let claims = Claims::create(Duration::from_secs(86400));
875 let res = key.authenticate(claims);
876 assert!(res.is_err());
877 assert!(res.unwrap_err().to_string().contains("Weak key"));
878 }
879
880 #[test]
881 fn weak_key_rejected_on_verify() {
882 let valid_key = HS256Key::generate();
883 let claims = Claims::create(Duration::from_secs(86400));
884 let token = valid_key.authenticate(claims).unwrap();
885
886 let weak_key = HS256Key::from_bytes(b"11-bytes..");
887 let res = weak_key.verify_token::<NoCustomClaims>(&token, None);
888 assert!(res.is_err());
889 assert!(res.unwrap_err().to_string().contains("Weak key"));
890 }
891
892 #[test]
893 fn min_key_length_accepted() {
894 let key = HS256Key::from_bytes(b"12-bytes...!");
895 let claims = Claims::create(Duration::from_secs(86400));
896 let token = key.authenticate(claims).unwrap();
897 key.verify_token::<NoCustomClaims>(&token, None).unwrap();
898 }
899
900 #[test]
901 fn weak_key_rejected_hs384() {
902 let key = HS384Key::from_bytes(b"short");
903 let claims = Claims::create(Duration::from_secs(86400));
904 assert!(key.authenticate(claims).is_err());
905 }
906
907 #[test]
908 fn weak_key_rejected_hs512() {
909 let key = HS512Key::from_bytes(b"short");
910 let claims = Claims::create(Duration::from_secs(86400));
911 assert!(key.authenticate(claims).is_err());
912 }
913
914 #[test]
915 fn weak_key_rejected_blake2b() {
916 let key = Blake2bKey::from_bytes(b"short");
917 let claims = Claims::create(Duration::from_secs(86400));
918 assert!(key.authenticate(claims).is_err());
919 }
920
921 #[cfg(feature = "jwe")]
922 #[test]
923 fn jwe_rsa_oaep() {
924 let decryption_key = RsaOaepDecryptionKey::generate(2048).unwrap();
925 let encryption_key = decryption_key.encryption_key();
926
927 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
928 let token = encryption_key.encrypt(claims).unwrap();
929
930 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
931 assert_eq!(claims.issuer, Some("test issuer".to_string()));
932 }
933
934 #[cfg(feature = "jwe")]
935 #[test]
936 fn jwe_a256kw() {
937 let key = A256KWKey::generate();
938
939 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
940 let token = key.encrypt(claims).unwrap();
941
942 let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
943 assert_eq!(claims.issuer, Some("test issuer".to_string()));
944 }
945
946 #[cfg(feature = "jwe")]
947 #[test]
948 fn jwe_a256kw_from_bytes() {
949 let raw_key = [0u8; 32];
950 let key = A256KWKey::from_bytes(&raw_key).unwrap();
951
952 let claims = Claims::create(Duration::from_secs(86400));
953 let token = key.encrypt(claims).unwrap();
954
955 let key2 = A256KWKey::from_bytes(&raw_key).unwrap();
956 let _claims: JWTClaims<NoCustomClaims> = key2.decrypt_token(&token, None).unwrap();
957 }
958
959 #[cfg(feature = "jwe")]
960 #[test]
961 fn jwe_a128kw() {
962 let key = A128KWKey::generate();
963
964 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
965 let token = key.encrypt(claims).unwrap();
966
967 let claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
968 assert_eq!(claims.issuer, Some("test issuer".to_string()));
969 }
970
971 #[cfg(feature = "jwe")]
972 #[test]
973 fn jwe_a128kw_from_bytes() {
974 let raw_key = [0u8; 16];
975 let key = A128KWKey::from_bytes(&raw_key).unwrap();
976
977 let claims = Claims::create(Duration::from_secs(86400));
978 let token = key.encrypt(claims).unwrap();
979
980 let key2 = A128KWKey::from_bytes(&raw_key).unwrap();
981 let _claims: JWTClaims<NoCustomClaims> = key2.decrypt_token(&token, None).unwrap();
982 }
983
984 #[cfg(feature = "jwe")]
985 #[test]
986 fn jwe_ecdh_es_a256kw() {
987 let decryption_key = EcdhEsA256KWDecryptionKey::generate();
988 let encryption_key = decryption_key.encryption_key();
989
990 let claims = Claims::create(Duration::from_secs(86400))
991 .with_issuer("test issuer")
992 .with_audience("test audience");
993 let token = encryption_key.encrypt(claims).unwrap();
994
995 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
996 assert_eq!(claims.issuer, Some("test issuer".to_string()));
997 }
998
999 #[cfg(feature = "jwe")]
1000 #[test]
1001 fn jwe_ecdh_es_a128kw() {
1002 let decryption_key = EcdhEsA128KWDecryptionKey::generate();
1003 let encryption_key = decryption_key.encryption_key();
1004
1005 let claims = Claims::create(Duration::from_secs(86400)).with_issuer("test issuer");
1006 let token = encryption_key.encrypt(claims).unwrap();
1007
1008 let claims: JWTClaims<NoCustomClaims> = decryption_key.decrypt_token(&token, None).unwrap();
1009 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1010 }
1011
1012 #[cfg(feature = "jwe")]
1013 #[test]
1014 fn jwe_custom_claims() {
1015 #[derive(Serialize, Deserialize, Debug, PartialEq)]
1016 struct CustomClaims {
1017 user_id: u64,
1018 role: String,
1019 }
1020
1021 let key = A256KWKey::generate();
1022
1023 let custom = CustomClaims {
1024 user_id: 12345,
1025 role: "admin".to_string(),
1026 };
1027 let claims = Claims::with_custom_claims(custom, Duration::from_secs(86400))
1028 .with_issuer("test issuer");
1029 let token = key.encrypt(claims).unwrap();
1030
1031 let claims: JWTClaims<CustomClaims> = key.decrypt_token(&token, None).unwrap();
1032 assert_eq!(claims.issuer, Some("test issuer".to_string()));
1033 assert_eq!(claims.custom.user_id, 12345);
1034 assert_eq!(claims.custom.role, "admin");
1035 }
1036
1037 #[cfg(feature = "jwe")]
1038 #[test]
1039 fn jwe_with_content_encryption_a128gcm() {
1040 let key = A256KWKey::generate();
1041
1042 let claims = Claims::create(Duration::from_secs(86400));
1043 let options = EncryptionOptions {
1044 content_encryption: ContentEncryption::A128GCM,
1045 ..Default::default()
1046 };
1047 let token = key.encrypt_with_options(claims, &options).unwrap();
1048
1049 let metadata = A256KWKey::decode_metadata(&token).unwrap();
1050 assert_eq!(metadata.encryption(), "A128GCM");
1051
1052 let _claims: JWTClaims<NoCustomClaims> = key.decrypt_token(&token, None).unwrap();
1053 }
1054
1055 #[cfg(feature = "jwe")]
1056 #[test]
1057 fn jwe_metadata_decode() {
1058 let key = A256KWKey::generate().with_key_id("my-key");
1059
1060 let claims = Claims::create(Duration::from_secs(86400));
1061 let options = EncryptionOptions {
1062 content_type: Some("JWT".to_string()),
1063 ..Default::default()
1064 };
1065 let token = key.encrypt_with_options(claims, &options).unwrap();
1066
1067 let metadata = A256KWKey::decode_metadata(&token).unwrap();
1068 assert_eq!(metadata.algorithm(), "A256KW");
1069 assert_eq!(metadata.encryption(), "A256GCM");
1070 assert_eq!(metadata.key_id(), Some("my-key"));
1071 assert_eq!(metadata.content_type(), Some("JWT"));
1072 }
1073
1074 #[cfg(feature = "jwe")]
1075 #[test]
1076 fn jwe_wrong_key_fails() {
1077 let key1 = A256KWKey::generate();
1078 let key2 = A256KWKey::generate();
1079
1080 let claims = Claims::create(Duration::from_secs(86400));
1081 let token = key1.encrypt(claims).unwrap();
1082
1083 let result: Result<JWTClaims<NoCustomClaims>, _> = key2.decrypt_token(&token, None);
1084 assert!(result.is_err());
1085 }
1086
1087 #[cfg(feature = "jwe")]
1088 #[test]
1089 fn jwe_invalid_key_size_a256kw() {
1090 let result = A256KWKey::from_bytes(&[0u8; 16]);
1091 assert!(result.is_err());
1092 }
1093
1094 #[cfg(feature = "jwe")]
1095 #[test]
1096 fn jwe_invalid_key_size_a128kw() {
1097 let result = A128KWKey::from_bytes(&[0u8; 32]);
1098 assert!(result.is_err());
1099 }
1100
1101 #[cfg(feature = "jwe")]
1102 #[test]
1103 fn jwe_rsa_key_too_small() {
1104 let result = RsaOaepDecryptionKey::generate(1024);
1105 assert!(result.is_err());
1106 }
1107
1108 #[cfg(feature = "jwe")]
1109 #[test]
1110 fn jwe_critical_header_rejected() {
1111 use ct_codecs::{Base64UrlSafeNoPadding, Encoder};
1112
1113 let key = A256KWKey::generate();
1114 let claims = Claims::create(Duration::from_secs(86400));
1115 let token = key.encrypt(claims).unwrap();
1116
1117 let parts: Vec<&str> = token.split('.').collect();
1118 let header_bytes =
1119 ct_codecs::Base64UrlSafeNoPadding::decode_to_vec(parts[0], None).unwrap();
1120 let mut header: serde_json::Value = serde_json::from_slice(&header_bytes).unwrap();
1121 header["crit"] = serde_json::json!(["unknown-extension"]);
1122 let modified_header = serde_json::to_string(&header).unwrap();
1123 let modified_header_b64 =
1124 Base64UrlSafeNoPadding::encode_to_string(&modified_header).unwrap();
1125
1126 let modified_token = format!(
1127 "{}.{}.{}.{}.{}",
1128 modified_header_b64, parts[1], parts[2], parts[3], parts[4]
1129 );
1130
1131 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified_token, None);
1132 assert!(result.is_err());
1133 }
1134
1135 #[cfg(feature = "jwe")]
1136 #[test]
1137 fn jwe_malformed_inputs_no_panic() {
1138 let key = A256KWKey::generate();
1139
1140 let malformed_inputs = [
1141 "",
1142 ".",
1143 "..",
1144 "...",
1145 "....",
1146 ".....",
1147 "a]]]]]",
1148 "a.b.c.d.e",
1149 "?????.?????.?????.?????.?????",
1150 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0",
1151 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.",
1152 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0..",
1153 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0...",
1154 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0....",
1155 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.a.b.c.d",
1156 "not-base64!@#$.valid.valid.valid.valid",
1157 "eyJhbGciOiJBMjU2S1cifQ.a.b.c.d",
1158 "eyJ9.a.b.c.d",
1159 "e30.a.b.c.d",
1160 &"a".repeat(100000),
1161 &format!("{}.{}.{}.{}.{}", "a".repeat(10000), "b", "c", "d", "e"),
1162 "eyJhbGciOiJXUk9ORyIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1163 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJXUk9ORyJ9.a.b.c.d",
1164 " . . . . ",
1165 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.AAAA.AAAA.AAAA.AAAA",
1166 "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.....AAAA",
1167 ];
1168
1169 for input in &malformed_inputs {
1170 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1171 assert!(result.is_err(), "Expected error for input: {}", input);
1172 }
1173
1174 // Also test decode_metadata with malformed inputs
1175 for input in &malformed_inputs {
1176 let _ = A256KWKey::decode_metadata(input);
1177 }
1178 }
1179
1180 #[cfg(feature = "jwe")]
1181 #[test]
1182 fn jwe_truncated_token_no_panic() {
1183 let key = A256KWKey::generate();
1184 let claims = Claims::create(Duration::from_secs(86400));
1185 let token = key.encrypt(claims).unwrap();
1186
1187 // Test progressively truncated tokens
1188 for i in 0..token.len() {
1189 let truncated = &token[..i];
1190 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(truncated, None);
1191 assert!(result.is_err());
1192 }
1193 }
1194
1195 #[cfg(feature = "jwe")]
1196 #[test]
1197 fn jwe_corrupted_parts_no_panic() {
1198 let key = A256KWKey::generate();
1199 let claims = Claims::create(Duration::from_secs(86400));
1200 let token = key.encrypt(claims).unwrap();
1201 let parts: Vec<&str> = token.split('.').collect();
1202
1203 // Corrupt each part individually
1204 for i in 0..5 {
1205 let mut modified_parts: Vec<String> = parts.iter().map(|s| s.to_string()).collect();
1206 modified_parts[i] = "AAAA".to_string();
1207 let modified = modified_parts.join(".");
1208 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified, None);
1209 assert!(result.is_err());
1210 }
1211
1212 // Empty each part individually
1213 for i in 0..5 {
1214 let mut modified_parts: Vec<String> = parts.iter().map(|s| s.to_string()).collect();
1215 modified_parts[i] = "".to_string();
1216 let modified = modified_parts.join(".");
1217 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(&modified, None);
1218 assert!(result.is_err());
1219 }
1220 }
1221
1222 #[cfg(feature = "jwe")]
1223 #[test]
1224 fn jwe_rsa_malformed_inputs_no_panic() {
1225 let key = RsaOaepDecryptionKey::generate(2048).unwrap();
1226
1227 let malformed_inputs = [
1228 "",
1229 ".....",
1230 "a.b.c.d.e",
1231 "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1232 ];
1233
1234 for input in &malformed_inputs {
1235 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1236 assert!(result.is_err());
1237 }
1238 }
1239
1240 #[cfg(feature = "jwe")]
1241 #[test]
1242 fn jwe_ecdh_malformed_inputs_no_panic() {
1243 let key = EcdhEsA256KWDecryptionKey::generate();
1244
1245 let malformed_inputs = [
1246 "",
1247 ".....",
1248 "a.b.c.d.e",
1249 "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00ifQ.a.b.c.d",
1250 // Valid header but missing epk
1251 "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00ifQ.AAAA.AAAA.AAAA.AAAA",
1252 ];
1253
1254 for input in &malformed_inputs {
1255 let result: Result<JWTClaims<NoCustomClaims>, _> = key.decrypt_token(input, None);
1256 assert!(result.is_err());
1257 }
1258 }
1259}