1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! # jwk-simple
//!
//! A Rust library for working with JSON Web Keys (JWK) and JWK Sets (JWKS) as
//! defined in RFC 7517, with full support for WASM environments and optional
//! jwt-simple integration.
//!
//! ## Features
//!
//! - **RFC coverage (JOSE/JWK)**: Supports RFC 7517 (JWK), RFC 7518 (algorithms),
//! RFC 8037 (OKP), RFC 9864 (Ed25519/Ed448 JOSE algorithms), and RFC 7638
//! (thumbprints)
//! - **Multiple key types**: RSA, EC (P-256, P-384, P-521, secp256k1),
//! Symmetric (HMAC), and OKP (Ed25519, Ed448, X25519, X448)
//! - **WASM compatible**: Core functionality works in WebAssembly environments
//! - **Security-first**: Zeroize support for sensitive data, constant-time base64 encoding
//! - **jwt-simple integration**: Optional feature for converting JWKs to jwt-simple key types
//! - **Remote fetching**: Load JWKS from HTTP endpoints with caching support
//! - **Strict selection API**: `KeySet::selector(...).select(...)` with typed errors
//!
//! ## Quick Start
//!
//! Parse a JWKS and strictly select a verification key:
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
//!
//! let json = r#"{
//! "keys": [{
//! "kty": "RSA",
//! "kid": "my-key-id",
//! "use": "sig",
//! "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
//! "e": "AQAB"
//! }]
//! }"#;
//!
//! let jwks = serde_json::from_str::<KeySet>(json)?;
//! let key = jwks
//! .selector(&[Algorithm::Rs256])
//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?;
//! assert!(key.is_public_key_only());
//! # Ok(())
//! # }
//! ```
//!
//! ## Feature Flags
//!
//! Feature definitions live in `Cargo.toml` (`[features]`), while this section
//! documents expected usage and platform constraints.
//!
//! | Feature | Platform | Description |
//! |---------|----------|-------------|
//! | `jwt-simple` | all targets | Integration with the jwt-simple crate (requires a `jwt-simple` backend feature such as `jwt-simple/pure-rust`) |
//! | `http` | all targets | Async HTTP fetching with `HttpKeyStore` |
//! | `web-crypto` | `wasm32` only | WebCrypto integration for browser/WASM environments |
//! | `cloudflare` | `wasm32` only | Cloudflare Workers support (Fetch API + KV cache) |
//! | `moka` | non-`wasm32` only | In-memory `KeyCache` implementation using Moka |
//!
//! Invalid platform/feature combinations fail at compile time with clear
//! `compile_error!` messages.
//!
//! ## Converting to jwt-simple keys
//!
//! With the `jwt-simple` feature enabled, you can convert JWKs to jwt-simple key types.
//! Make sure your crate also enables a concrete `jwt-simple` backend feature
//! (for example `jwt-simple/pure-rust`).
//!
//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
//! use jwt_simple::prelude::*;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let json = "{}";
//! # let token = "";
//! let keyset: KeySet = serde_json::from_str(json)?;
//! let jwk = keyset
//! .selector(&[Algorithm::Rs256])
//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?
//! .clone();
//!
//! // Convert to jwt-simple key
//! // Conversion failures use `JwtSimpleKeyConversionError`.
//! let key: RS256PublicKey = jwk.try_into()?;
//!
//! // Use for JWT verification
//! let claims = key.verify_token::<NoCustomClaims>(&token, None)?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Using with WebCrypto (Browser/WASM)
//!
//! With the `web-crypto` feature enabled, you can use JWKs with the browser's
//! native SubtleCrypto API:
//!
//! use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
//! use std::convert::TryInto;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! # let json = r#"{"keys":[{"kty":"RSA","kid":"my-key-id","n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw","e":"AQAB"}]}"#;
//! // Parse a JWKS
//! let keyset: KeySet = serde_json::from_str(json)?;
//! let key = keyset
//! .selector(&[Algorithm::Rs256])
//! .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-key-id"))?;
//!
//! // Check if the key is WebCrypto compatible
//! if key.is_web_crypto_compatible() {
//! // Import as a CryptoKey for verification.
//! // Use the _for_alg variant because many JWKS keys (especially from OIDC
//! // providers) omit the `alg` field, and WebCrypto requires the algorithm
//! // to be known at import time for RSA and HMAC keys.
//! let alg = Algorithm::Rs256; // typically from the JWT header
//! let crypto_key = key.import_as_verify_key_for_alg(&alg).await?;
//!
//! // Or get the JsonWebKey directly
//! let jwk: web_sys::JsonWebKey = key.try_into()?;
//! }
//! # Ok(())
//! # }
//! ```
//!
//! If the key's `alg` field is present, you can use the simpler
//! `Key::import_as_verify_key` instead. EC keys always work without an explicit
//! algorithm since the curve determines the WebCrypto parameters.
//!
//! **Note:** WebCrypto does not support OKP keys (Ed25519, Ed448, X25519, X448)
//! or the secp256k1 curve. Use `Key::is_web_crypto_compatible()` to check
//! compatibility before attempting to use a key with WebCrypto.
//!
//! ## Security
//!
//! This crate prioritizes security:
//!
//! - Private key parameters are zeroed from memory on drop via `zeroize`
//! - Base64 encoding uses constant-time operations via `base64ct`
//! - Debug output redacts sensitive key material
//! - All fallible operations return `Result` types. The crate avoids panics,
//! though standard trait implementations like `Index` follow normal Rust
//! semantics and may panic on invalid input (e.g., out-of-bounds indexing)
//! - Validation entry points are layered:
//!
//! | API | Structural key params | `use`/`key_ops` consistency | Cert metadata (`x5u`/`x5c`/`x5t`) | Alg suitability/strength | Op/alg compatibility | Private material capability | Selection policy |
//! |-----|------------------------|-----------------------------|-----------------------------------|--------------------------|----------------------|-----------------------------|------------------|
//! | [`Key::validate`] | yes | yes | yes | no | no | no | no |
//! | [`Key::validate_for_use`] | yes | yes | yes | yes | yes | yes | no |
//! | [`KeySet::selector(...).select(...)`](crate::jwks::KeySelector::select) | yes, per candidate | yes, per candidate | yes, per candidate | yes, per candidate | yes, up front | yes, per candidate | yes |
//!
//! PKIX trust validation for `x5c` chains is application-defined and out of
//! scope for this crate.
// ---------------------------------------------------------------------------
// Feature/target compatibility guards
// ---------------------------------------------------------------------------
//
// docs.rs builds with all features enabled on a native target to render API docs.
// We skip hard errors there so docs can still be generated for feature-gated APIs.
compile_error!;
compile_error!;
compile_error!;
// Re-exports for convenience
pub use JwtSimpleKeyConversionError;
pub use ;
pub use ;
pub use ;
pub use web_crypto;