Skip to main content

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