jwt_simple/
lib.rs

1//! [![GitHub CI](https://github.com/jedisct1/rust-jwt-simple/workflows/Rust/badge.svg)](https://github.com/jedisct1/rust-jwt-simple/actions)
2//! [![Docs.rs](https://docs.rs/jwt-simple/badge.svg)](https://docs.rs/jwt-simple/)
3//! [![crates.io](https://img.shields.io/crates/v/jwt-simple.svg)](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}