Skip to main content

jwk_simple/
jwks.rs

1//! JSON Web Key Set (JWKS) as defined in RFC 7517 Section 5.
2//!
3//! A JWKS is a collection of JWK objects, typically used for key distribution
4//! and discovery. This module also defines the [`KeyStore`] trait, which abstracts
5//! over different sources of JWK keys.
6
7use std::fmt::{self, Display};
8
9use serde::{Deserialize, Deserializer, Serialize};
10
11use crate::error::{Error, IncompatibleKeyError, InvalidKeyError, Result};
12use crate::jwk::{
13    Algorithm, Key, KeyOperation, KeyType, KeyUse, is_operation_compatible_with_algorithm,
14};
15
16mod cache;
17#[cfg(all(feature = "cloudflare", target_arch = "wasm32"))]
18pub mod cloudflare;
19mod store;
20
21#[cfg(all(feature = "moka", not(target_arch = "wasm32")))]
22pub use cache::moka::{DEFAULT_MOKA_CACHE_TTL, MokaKeyCache};
23pub use cache::{CachedKeyStore, KeyCache};
24
25#[cfg(feature = "http")]
26pub use store::http::HttpKeyStore;
27
28#[cfg(all(feature = "http", not(target_arch = "wasm32")))]
29pub use store::http::DEFAULT_TIMEOUT;
30
31/// Errors returned by strict key selection.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum SelectionError {
35    /// Verification selection was requested without a configured allowlist.
36    EmptyVerifyAllowlist,
37    /// The requested algorithm is unknown/private and strict selection rejects it.
38    UnknownAlgorithm,
39    /// The requested operation is unknown/private and strict selection rejects it.
40    UnknownOperation,
41    /// The requested operation is not compatible with the requested algorithm.
42    OperationAlgorithmMismatch {
43        /// Operation requested by the caller.
44        operation: KeyOperation,
45        /// Algorithm requested by the caller.
46        algorithm: Algorithm,
47    },
48    /// The requested verification algorithm is not permitted by the allowlist.
49    AlgorithmNotAllowed,
50    /// The requested algorithm conflicts with a key's declared `alg` value.
51    AlgorithmMismatch {
52        /// Algorithm requested by the caller.
53        requested: Algorithm,
54        /// Algorithm declared on the matching key.
55        declared: Algorithm,
56    },
57    /// Key metadata (`use` / `key_ops`) does not permit the requested operation.
58    IntentMismatch,
59    /// The matched key is structurally invalid.
60    InvalidKey(InvalidKeyError),
61    /// Key material type/curve is incompatible with the requested algorithm.
62    IncompatibleKeyType,
63    /// Key failed cryptographic suitability checks (strength/parameter/capability constraints).
64    KeySuitabilityFailed(IncompatibleKeyError),
65    /// More than one key satisfies strict selection criteria.
66    AmbiguousSelection {
67        /// Number of matched keys.
68        count: usize,
69    },
70    /// No key satisfies strict selection criteria.
71    NoMatchingKey,
72}
73
74impl Display for SelectionError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        use crate::error::sanitize_for_display;
77
78        match self {
79            SelectionError::EmptyVerifyAllowlist => {
80                write!(f, "verification allowlist is empty")
81            }
82            SelectionError::UnknownAlgorithm => write!(f, "unknown or unsupported algorithm"),
83            SelectionError::UnknownOperation => write!(f, "unknown or unsupported operation"),
84            SelectionError::OperationAlgorithmMismatch {
85                operation,
86                algorithm,
87            } => {
88                let operation_display = match operation {
89                    KeyOperation::Unknown(value) => {
90                        format!("unknown({})", sanitize_for_display(value))
91                    }
92                    _ => operation.to_string(),
93                };
94
95                let algorithm_display = match algorithm {
96                    Algorithm::Unknown(value) => {
97                        format!("unknown({})", sanitize_for_display(value))
98                    }
99                    _ => algorithm.to_string(),
100                };
101
102                write!(
103                    f,
104                    "operation/algorithm mismatch: operation {} is not valid for algorithm {}",
105                    operation_display, algorithm_display
106                )
107            }
108            SelectionError::AlgorithmNotAllowed => {
109                write!(f, "algorithm is not allowed for verification")
110            }
111            SelectionError::AlgorithmMismatch {
112                requested,
113                declared,
114            } => {
115                // In strict selection, `requested` is guaranteed to be known
116                // because unknown algorithms are rejected upfront.
117                let requested_display = requested.to_string();
118
119                let declared_display = match declared {
120                    Algorithm::Unknown(value) => {
121                        format!("unknown({})", sanitize_for_display(value))
122                    }
123                    _ => declared.to_string(),
124                };
125
126                write!(
127                    f,
128                    "algorithm mismatch: requested {}, key declares {}",
129                    requested_display, declared_display
130                )
131            }
132            SelectionError::IntentMismatch => {
133                write!(f, "key metadata does not permit requested operation")
134            }
135            SelectionError::InvalidKey(e) => {
136                write!(f, "key is invalid: {}", e)
137            }
138            SelectionError::IncompatibleKeyType => {
139                write!(f, "key type/curve is incompatible with requested algorithm")
140            }
141            SelectionError::KeySuitabilityFailed(e) => {
142                write!(f, "key suitability check failed: {}", e)
143            }
144            SelectionError::AmbiguousSelection { count } => {
145                write!(f, "selection is ambiguous: {} matching keys", count)
146            }
147            SelectionError::NoMatchingKey => write!(f, "no matching key found"),
148        }
149    }
150}
151
152impl std::error::Error for SelectionError {
153    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
154        match self {
155            SelectionError::InvalidKey(e) => Some(e),
156            SelectionError::KeySuitabilityFailed(e) => Some(e),
157            _ => None,
158        }
159    }
160}
161
162/// Strict selection request criteria.
163#[derive(Debug, Clone)]
164pub struct KeyMatcher<'a> {
165    /// Requested cryptographic operation.
166    op: KeyOperation,
167    /// Requested JOSE algorithm.
168    alg: Algorithm,
169    /// Optional key identifier (`kid`) constraint.
170    kid: Option<&'a str>,
171}
172
173impl<'a> KeyMatcher<'a> {
174    /// Creates strict selection criteria for an operation and algorithm.
175    #[must_use]
176    pub fn new(op: KeyOperation, alg: Algorithm) -> Self {
177        Self { op, alg, kid: None }
178    }
179
180    /// Sets a key identifier (`kid`) constraint.
181    #[must_use]
182    pub fn with_kid(mut self, kid: &'a str) -> Self {
183        self.kid = Some(kid);
184        self
185    }
186}
187
188/// Discovery filter criteria.
189///
190/// # Construction
191/// This type is `#[non_exhaustive]`. External callers must use [`KeyFilter::new`]
192/// plus builder methods, or convenience constructors such as
193/// [`KeyFilter::for_alg`]. Struct-literal syntax will not compile outside this crate.
194///
195/// Public fields remain readable for inspection/pattern-matching, but builder
196/// methods are the only supported external construction path.
197#[derive(Debug, Clone, Default)]
198#[non_exhaustive]
199pub struct KeyFilter<'a> {
200    /// Optional operation-intent filter.
201    pub op: Option<KeyOperation>,
202    /// Optional algorithm filter (exact `alg` match only).
203    pub alg: Option<Algorithm>,
204    /// Optional key identifier filter.
205    pub kid: Option<&'a str>,
206    /// Optional key-type filter.
207    pub kty: Option<KeyType>,
208    /// Optional key-use filter.
209    pub key_use: Option<KeyUse>,
210}
211
212impl<'a> KeyFilter<'a> {
213    /// Creates an empty discovery filter.
214    #[must_use]
215    pub fn new() -> Self {
216        Self::default()
217    }
218
219    /// Creates a filter for an exact algorithm match.
220    #[must_use]
221    pub fn for_alg(alg: Algorithm) -> Self {
222        Self::new().with_alg(alg)
223    }
224
225    /// Creates a filter for a specific key use.
226    #[must_use]
227    pub fn for_use(key_use: KeyUse) -> Self {
228        Self::new().with_key_use(key_use)
229    }
230
231    /// Creates a filter for a specific key type.
232    #[must_use]
233    pub fn for_kty(kty: KeyType) -> Self {
234        Self::new().with_kty(kty)
235    }
236
237    /// Creates a filter for a specific operation intent.
238    #[must_use]
239    pub fn for_op(op: KeyOperation) -> Self {
240        Self::new().with_op(op)
241    }
242
243    /// Creates a filter for key use + exact algorithm.
244    #[must_use]
245    pub fn for_use_alg(key_use: KeyUse, alg: Algorithm) -> Self {
246        Self::new().with_key_use(key_use).with_alg(alg)
247    }
248
249    /// Creates a filter for operation intent + exact algorithm.
250    #[must_use]
251    pub fn for_op_alg(op: KeyOperation, alg: Algorithm) -> Self {
252        Self::new().with_op(op).with_alg(alg)
253    }
254
255    /// Sets an operation filter.
256    #[must_use]
257    pub fn with_op(mut self, op: KeyOperation) -> Self {
258        self.op = Some(op);
259        self
260    }
261
262    /// Sets an exact algorithm filter.
263    #[must_use]
264    pub fn with_alg(mut self, alg: Algorithm) -> Self {
265        self.alg = Some(alg);
266        self
267    }
268
269    /// Sets a key identifier (`kid`) filter.
270    #[must_use]
271    pub fn with_kid(mut self, kid: &'a str) -> Self {
272        self.kid = Some(kid);
273        self
274    }
275
276    /// Sets a key type filter.
277    #[must_use]
278    pub fn with_kty(mut self, kty: KeyType) -> Self {
279        self.kty = Some(kty);
280        self
281    }
282
283    /// Sets a key use filter.
284    #[must_use]
285    pub fn with_key_use(mut self, key_use: KeyUse) -> Self {
286        self.key_use = Some(key_use);
287        self
288    }
289}
290
291/// Policy-bound strict selector for a [`KeySet`].
292#[derive(Debug, Clone)]
293pub struct KeySelector<'a> {
294    /// Backing key set used for selection.
295    keyset: &'a KeySet,
296    /// Allowed algorithms for verification operations.
297    allowed_verify_algs: Vec<Algorithm>,
298}
299
300impl<'a> KeySelector<'a> {
301    /// Selects exactly one key using strict cryptographic suitability checks.
302    ///
303    /// Conceptually, strict selection applies the same per-key validation layers
304    /// as [`Key::validate_for_use`], but within a JWKS search and with extra
305    /// policy rules such as verification allowlists, declared-`alg` matching,
306    /// and ambiguity handling.
307    ///
308    /// When `kid` is present in the matcher, candidate-level validation failures are
309    /// surfaced with specific diagnostics (`AlgorithmMismatch`, `IntentMismatch`,
310    /// `InvalidKey`, `KeySuitabilityFailed`, `IncompatibleKeyType`) using deterministic precedence.
311    ///
312    /// When `kid` is not present, candidates that fail per-key checks are skipped and
313    /// selection resolves by surviving cardinality (`AmbiguousSelection` / `NoMatchingKey`).
314    ///
315    /// In kid-less mode, candidate-level mismatch diagnostics are intentionally
316    /// suppressed. Early policy errors still surface (`UnknownAlgorithm`,
317    /// `UnknownOperation`, `OperationAlgorithmMismatch`, allowlist failures).
318    ///
319    /// Error precedence is deterministic:
320    /// 1. `UnknownAlgorithm`
321    /// 2. `UnknownOperation`
322    /// 3. `OperationAlgorithmMismatch`
323    /// 4. `EmptyVerifyAllowlist` / `AlgorithmNotAllowed` (verify only)
324    /// 5. Candidate evaluation
325    /// 6. If multiple candidates survive: `AmbiguousSelection`
326    ///
327    /// If `kid` is present and no candidate survives, the most specific error
328    /// is returned in this order: `AlgorithmMismatch` -> `IntentMismatch`
329    /// -> `InvalidKey` -> `KeySuitabilityFailed` -> `IncompatibleKeyType`
330    /// -> `NoMatchingKey`.
331    ///
332    /// If `kid` is absent and no candidate survives, candidate-level
333    /// diagnostics are suppressed and selection returns `NoMatchingKey`.
334    ///
335    /// If `kid` is omitted and selection returns `NoMatchingKey`, use
336    /// [`KeySet::find`] for discovery diagnostics to inspect broad candidates.
337    ///
338    /// # Examples
339    ///
340    /// Verify selection with an explicit allowlist:
341    ///
342    /// ```
343    /// use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
344    ///
345    /// let json = r#"{"keys": [
346    ///   {"kty": "RSA", "kid": "my-kid", "use": "sig", "alg": "RS256", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB"}
347    /// ]}"#;
348    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
349    ///
350    /// let key = jwks
351    ///   .selector(&[Algorithm::Rs256, Algorithm::Es256])
352    ///   .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("my-kid"))
353    ///   .unwrap();
354    /// assert_eq!(key.kid(), Some("my-kid"));
355    /// ```
356    ///
357    /// Sign selection (allowlist is not consulted for signing):
358    ///
359    /// ```
360    /// use jwk_simple::{Algorithm, KeyMatcher, KeyOperation, KeySet};
361    ///
362    /// let json = r#"{"keys": [
363    ///   {"kty": "EC", "kid": "sign-kid", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
364    /// ]}"#;
365    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
366    ///
367    /// let key = jwks
368    ///   .selector(&[])
369    ///   .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("sign-kid"))
370    ///   .unwrap();
371    /// assert_eq!(key.kid(), Some("sign-kid"));
372    /// ```
373    pub fn select(&self, matcher: KeyMatcher<'_>) -> std::result::Result<&'a Key, SelectionError> {
374        if matcher.alg.is_unknown() {
375            return Err(SelectionError::UnknownAlgorithm);
376        }
377
378        if matcher.op.is_unknown() {
379            return Err(SelectionError::UnknownOperation);
380        }
381
382        if !is_operation_compatible_with_algorithm(&matcher.op, &matcher.alg) {
383            return Err(SelectionError::OperationAlgorithmMismatch {
384                operation: matcher.op,
385                algorithm: matcher.alg,
386            });
387        }
388
389        if matcher.op == KeyOperation::Verify {
390            if self.allowed_verify_algs.is_empty() {
391                return Err(SelectionError::EmptyVerifyAllowlist);
392            }
393
394            // Linear scan is acceptable for small verification allowlists.
395            // Revisit with set-backed lookup if large allowlists become common.
396            // Unknown/private algorithms are rejected above (`UnknownAlgorithm`),
397            // so this allowlist check is only meaningful for known variants.
398            if !self.allowed_verify_algs.contains(&matcher.alg) {
399                return Err(SelectionError::AlgorithmNotAllowed);
400            }
401        }
402
403        let mut candidates = Vec::new();
404        let mut incompatible_for_known_kid = false;
405        let mut saw_alg_mismatch: Option<(Algorithm, Algorithm)> = None;
406        let mut saw_intent_mismatch = false;
407        let mut saw_invalid_key: Option<InvalidKeyError> = None;
408        let mut saw_suitability_error: Option<IncompatibleKeyError> = None;
409
410        for key in self.keyset.keys.iter() {
411            // Diagnostics are accumulated only for kid-constrained lookups.
412            // For kid-less selection, failing candidates are skipped and final
413            // outcome is resolved by surviving cardinality.
414            if let Some(kid) = matcher.kid
415                && key.kid() != Some(kid)
416            {
417                continue;
418            }
419
420            if let Some(declared_alg) = key.alg()
421                && declared_alg != &matcher.alg
422            {
423                if matcher.kid.is_some() && saw_alg_mismatch.is_none() {
424                    saw_alg_mismatch = Some((matcher.alg.clone(), declared_alg.clone()));
425                }
426                continue;
427            }
428
429            if !key.is_algorithm_compatible(&matcher.alg) {
430                if matcher.kid.is_some() {
431                    incompatible_for_known_kid = true;
432                }
433                continue;
434            }
435
436            if let Err(err) = key.check_operation_intent(std::slice::from_ref(&matcher.op)) {
437                if matcher.kid.is_some() {
438                    match err {
439                        Error::IncompatibleKey(IncompatibleKeyError::OperationNotPermitted {
440                            ..
441                        }) => saw_intent_mismatch = true,
442                        Error::InvalidKey(invalid) => {
443                            if saw_invalid_key.is_none() {
444                                saw_invalid_key = Some(invalid);
445                            }
446                        }
447                        // Conservatively map any future intent-check error to intent mismatch.
448                        Error::IncompatibleKey(_) => saw_intent_mismatch = true,
449                        _ => incompatible_for_known_kid = true,
450                    }
451                }
452                continue;
453            }
454
455            if let Err(err) = key.validate_certificate_metadata() {
456                if matcher.kid.is_some() {
457                    match err {
458                        Error::InvalidKey(invalid) => {
459                            if saw_invalid_key.is_none() {
460                                saw_invalid_key = Some(invalid);
461                            }
462                        }
463                        _ => incompatible_for_known_kid = true,
464                    }
465                }
466                continue;
467            }
468
469            if let Err(e) = key.check_algorithm_suitability(&matcher.alg) {
470                if matcher.kid.is_some() {
471                    match e {
472                        Error::InvalidKey(invalid) => {
473                            if saw_invalid_key.is_none() {
474                                saw_invalid_key = Some(invalid);
475                            }
476                        }
477                        Error::IncompatibleKey(suitability) => {
478                            if saw_suitability_error.is_none() {
479                                saw_suitability_error = Some(suitability);
480                            }
481                        }
482                        _ => incompatible_for_known_kid = true,
483                    }
484                }
485                continue;
486            }
487
488            if let Err(e) = key.check_operation_capability(std::slice::from_ref(&matcher.op)) {
489                if matcher.kid.is_some() {
490                    match e {
491                        Error::IncompatibleKey(suitability) => {
492                            if saw_suitability_error.is_none() {
493                                saw_suitability_error = Some(suitability);
494                            }
495                        }
496                        // Conservatively map any future non-incompatible errors
497                        // to generic incompatibility in strict selection.
498                        Error::InvalidKey(_) => incompatible_for_known_kid = true,
499                        _ => incompatible_for_known_kid = true,
500                    }
501                }
502                continue;
503            }
504
505            candidates.push(key);
506        }
507
508        if candidates.is_empty() {
509            if let Some((requested, declared)) = saw_alg_mismatch {
510                return Err(SelectionError::AlgorithmMismatch {
511                    requested,
512                    declared,
513                });
514            }
515            if saw_intent_mismatch {
516                return Err(SelectionError::IntentMismatch);
517            }
518            if let Some(invalid) = saw_invalid_key {
519                return Err(SelectionError::InvalidKey(invalid));
520            }
521            if let Some(suitability) = saw_suitability_error {
522                return Err(SelectionError::KeySuitabilityFailed(suitability));
523            }
524            if incompatible_for_known_kid {
525                return Err(SelectionError::IncompatibleKeyType);
526            }
527            return Err(SelectionError::NoMatchingKey);
528        }
529
530        if candidates.len() > 1 {
531            return Err(SelectionError::AmbiguousSelection {
532                count: candidates.len(),
533            });
534        }
535
536        Ok(candidates[0])
537    }
538}
539
540/// A trait for types that can provide JWK keys.
541///
542/// This trait abstracts over different sources of keys, whether from
543/// a static set, a remote HTTP endpoint, or a cached source.
544///
545/// The only required method is [`get_keyset`](KeyStore::get_keyset), which returns
546/// the full key set. A default implementation of [`get_key`](KeyStore::get_key) is
547/// provided that fetches the full set and looks up by key ID.
548///
549/// # Async and Send Bounds
550///
551/// On native targets, the trait requires `Send + Sync` and futures are `Send`.
552/// On WASM targets, these bounds are relaxed since everything is single-threaded.
553///
554/// # Examples
555///
556/// Using a static key set:
557///
558/// ```
559/// use jwk_simple::KeySet;
560/// use jwk_simple::jwks::KeyStore;
561///
562/// # async fn example() -> jwk_simple::Result<()> {
563/// let store: KeySet = serde_json::from_str(r#"{"keys": []}"#)?;
564/// let key = store.get_key("some-kid").await?;
565/// # Ok(())
566/// # }
567/// ```
568///
569/// Generic code that works with any store:
570///
571/// ```ignore
572/// async fn verify_token<S: KeyStore>(store: &S, kid: &str) -> Result<()> {
573///     let key = store.get_key(kid).await?
574///         .ok_or_else(|| Error::Other("key not found".into()))?;
575///     // ... verify with key
576///     Ok(())
577/// }
578/// ```
579#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
580#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
581pub trait KeyStore {
582    /// Gets all available keys as a [`KeySet`].
583    ///
584    /// For remote sources, this may trigger a fetch if the cache is empty or expired.
585    async fn get_keyset(&self) -> Result<KeySet>;
586
587    /// Gets a key by its key ID (`kid`).
588    ///
589    /// Returns `Ok(None)` if no key with the given ID exists.
590    /// Returns `Err` if the lookup failed (e.g., network error for remote sources).
591    ///
592    /// The default implementation fetches the full key set and looks up by key ID.
593    /// Implementations may override this for more efficient lookups (e.g., caching).
594    async fn get_key(&self, kid: &str) -> Result<Option<Key>> {
595        Ok(self.get_keyset().await?.get_by_kid(kid).cloned())
596    }
597}
598
599// Implement KeyStore for KeySet (static, immediate)
600#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
601#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
602impl KeyStore for KeySet {
603    async fn get_keyset(&self) -> Result<KeySet> {
604        Ok(self.clone())
605    }
606
607    async fn get_key(&self, kid: &str) -> Result<Option<Key>> {
608        Ok(self.get_by_kid(kid).cloned())
609    }
610}
611
612/// A JSON Web Key Set (RFC 7517 Section 5).
613///
614/// A KeySet contains a collection of keys that can be looked up by various
615/// criteria such as key ID (`kid`), algorithm, or key use.
616///
617/// # RFC Compliance
618///
619/// Per RFC 7517 Section 5:
620/// > "Implementations SHOULD ignore JWKs within a JWK Set that use 'kty'
621/// > (key type) values that are not understood by them, that are missing
622/// > required members, or for which values are out of the supported ranges."
623///
624/// This implementation follows this guidance by silently skipping keys with
625/// unknown `kty` values, missing required members, or invalid key parameter
626/// values during deserialization rather than failing.
627///
628/// # Examples
629///
630/// Parse a JWKS from JSON:
631///
632/// ```
633/// use jwk_simple::KeySet;
634///
635/// let json = r#"{
636///     "keys": [
637///         {
638///             "kty": "RSA",
639///             "kid": "key-1",
640///             "use": "sig",
641///             "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
642///             "e": "AQAB"
643///         }
644///     ]
645/// }"#;
646///
647/// let jwks: KeySet = serde_json::from_str(json).unwrap();
648/// assert_eq!(jwks.len(), 1);
649/// ```
650///
651/// Keys that cannot be parsed are silently skipped:
652///
653/// ```
654/// use jwk_simple::KeySet;
655///
656/// let json = r#"{
657///     "keys": [
658///         {"kty": "UNKNOWN", "data": "ignored"},
659///         {"kty": "oct", "k": "AQAB"}
660///     ]
661/// }"#;
662///
663/// let jwks: KeySet = serde_json::from_str(json).unwrap();
664/// assert_eq!(jwks.len(), 1); // Only the "oct" key is included
665/// ```
666#[derive(Debug, Clone, Serialize, Default)]
667pub struct KeySet {
668    /// The collection of keys.
669    keys: Vec<Key>,
670}
671
672impl<'de> Deserialize<'de> for KeySet {
673    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
674    where
675        D: Deserializer<'de>,
676    {
677        // Helper struct for raw deserialization
678        #[derive(Deserialize)]
679        struct RawJwkSet {
680            keys: Vec<serde_json::Value>,
681        }
682
683        let raw = RawJwkSet::deserialize(deserializer)?;
684
685        // Parse each key, silently skipping those that cannot be understood
686        // per RFC 7517 Section 5: "Implementations SHOULD ignore JWKs within
687        // a JWK Set that use 'kty' (key type) values that are not understood
688        // by them, that are missing required members, or for which values are
689        // out of the supported ranges."
690        let mut keys = Vec::with_capacity(raw.keys.len());
691        for value in raw.keys {
692            // Attempt to parse each key, then validate.
693            // Skip any key that fails either phase.
694            if let Ok(key) = serde_json::from_value::<Key>(value)
695                && key.validate().is_ok()
696            {
697                keys.push(key);
698            }
699        }
700
701        Ok(KeySet { keys })
702    }
703}
704
705impl KeySet {
706    /// Creates a new empty KeySet.
707    ///
708    /// # Examples
709    ///
710    /// ```
711    /// use jwk_simple::KeySet;
712    ///
713    /// let jwks = KeySet::new();
714    /// assert!(jwks.is_empty());
715    /// ```
716    pub fn new() -> Self {
717        Self { keys: Vec::new() }
718    }
719
720    /// Creates a key set from a list of keys, silently dropping invalid ones.
721    ///
722    /// This matches the deserialization behavior: keys that fail
723    /// [`Key::validate`] are silently skipped.
724    /// Use [`KeySet::add_key`] for validated insertion that reports errors.
725    ///
726    /// # Examples
727    ///
728    /// ```
729    /// use jwk_simple::{Key, KeyParams, KeySet, SymmetricParams};
730    /// use jwk_simple::encoding::Base64UrlBytes;
731    ///
732    /// let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
733    ///     Base64UrlBytes::new(vec![0u8; 32]),
734    /// )));
735    /// let jwks = KeySet::from_keys_lossy(vec![key]);
736    /// assert_eq!(jwks.len(), 1);
737    /// ```
738    #[must_use]
739    pub fn from_keys_lossy(keys: Vec<Key>) -> Self {
740        Self {
741            keys: keys.into_iter().filter(|k| k.validate().is_ok()).collect(),
742        }
743    }
744
745    /// Returns a slice of all keys in the set.
746    pub fn keys(&self) -> &[Key] {
747        &self.keys
748    }
749
750    /// Returns the number of keys in the set.
751    pub fn len(&self) -> usize {
752        self.keys.len()
753    }
754
755    /// Returns `true` if the set contains no keys.
756    pub fn is_empty(&self) -> bool {
757        self.keys.is_empty()
758    }
759
760    /// Adds a key to the set after validating it.
761    ///
762    /// Runs the same validation as [`Key::validate`]: structural parameter
763    /// checks, `use`/`key_ops` consistency, and certificate metadata.
764    ///
765    /// # Errors
766    ///
767    /// Returns an error if the key fails validation.
768    ///
769    /// # Examples
770    ///
771    /// ```
772    /// use jwk_simple::{Key, KeyParams, KeySet, SymmetricParams};
773    /// use jwk_simple::encoding::Base64UrlBytes;
774    ///
775    /// let mut jwks = KeySet::new();
776    /// let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
777    ///     Base64UrlBytes::new(vec![0u8; 32]),
778    /// )));
779    /// jwks.add_key(key).unwrap();
780    /// assert_eq!(jwks.len(), 1);
781    /// ```
782    pub fn add_key(&mut self, key: Key) -> Result<()> {
783        key.validate()?;
784        self.keys.push(key);
785        Ok(())
786    }
787
788    /// Removes and returns a key by its ID.
789    pub fn remove_by_kid(&mut self, kid: &str) -> Option<Key> {
790        if let Some(pos) = self.keys.iter().position(|k| k.kid() == Some(kid)) {
791            Some(self.keys.remove(pos))
792        } else {
793            None
794        }
795    }
796
797    /// Finds a key by its ID (`kid`).
798    ///
799    /// # Examples
800    ///
801    /// ```
802    /// use jwk_simple::KeySet;
803    ///
804    /// let json = r#"{"keys": [{"kty": "oct", "kid": "my-key", "k": "AQAB"}]}"#;
805    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
806    ///
807    /// let key = jwks.get_by_kid("my-key");
808    /// assert!(key.is_some());
809    ///
810    /// let missing = jwks.get_by_kid("unknown");
811    /// assert!(missing.is_none());
812    /// ```
813    pub fn get_by_kid(&self, kid: &str) -> Option<&Key> {
814        self.keys.iter().find(|k| k.kid() == Some(kid))
815    }
816
817    /// Finds all signing keys.
818    ///
819    /// A key is considered a signing key if:
820    /// - It has `key_ops` containing `sign` or `verify`, OR (when `key_ops` is absent)
821    /// - It has `use: "sig"`, OR
822    /// - It has neither `use` nor `key_ops` specified
823    ///
824    /// # Security
825    ///
826    /// This is a discovery helper. Do not use it as a cryptographic trust gate.
827    /// For security-sensitive selection, use [`KeySet::selector`] and
828    /// [`KeySelector::select`].
829    pub fn signing_keys(&self) -> impl Iterator<Item = &Key> {
830        self.keys.iter().filter(|k| is_signing_key(k))
831    }
832
833    /// Finds all encryption keys.
834    ///
835    /// A key is considered an encryption key if:
836    /// - It has `key_ops` containing `encrypt`, `decrypt`, `wrapKey`, or `unwrapKey`,
837    ///   OR (when `key_ops` is absent)
838    /// - It has `use: "enc"`
839    ///
840    /// # Security
841    ///
842    /// This is a discovery helper. Do not use it as a cryptographic trust gate.
843    /// For security-sensitive selection, use [`KeySet::selector`] and
844    /// [`KeySelector::select`].
845    pub fn encryption_keys(&self) -> impl Iterator<Item = &Key> {
846        self.keys.iter().filter(|k| is_encryption_key(k))
847    }
848
849    /// Returns the first signing key, if any.
850    ///
851    /// This is a convenience method for cases where only one signing key is expected.
852    ///
853    /// # Security
854    ///
855    /// This is a discovery helper. Do not use it as a cryptographic trust gate.
856    /// For security-sensitive selection, use [`KeySet::selector`] and
857    /// [`KeySelector::select`].
858    ///
859    /// # Examples
860    ///
861    /// ```
862    /// use jwk_simple::KeySet;
863    ///
864    /// let json = r#"{"keys": [{"kty": "RSA", "use": "sig", "n": "AQAB", "e": "AQAB"}]}"#;
865    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
866    ///
867    /// let key = jwks.first_signing_key().expect("expected a signing key");
868    /// ```
869    pub fn first_signing_key(&self) -> Option<&Key> {
870        self.signing_keys().next()
871    }
872
873    /// Returns the first key, if any.
874    ///
875    /// # Examples
876    ///
877    /// ```
878    /// use jwk_simple::KeySet;
879    ///
880    /// let jwks = KeySet::new();
881    /// assert!(jwks.first().is_none());
882    /// ```
883    pub fn first(&self) -> Option<&Key> {
884        self.keys.first()
885    }
886
887    /// Returns an iterator over the keys.
888    pub fn iter(&self) -> impl Iterator<Item = &Key> {
889        self.keys.iter()
890    }
891
892    /// Validates the structural integrity and metadata consistency of all keys
893    /// in the set (see [`Key::validate`]).
894    ///
895    /// This is a context-free structural check: it does not validate algorithm
896    /// suitability, key strength for a specific algorithm, or operation intent,
897    /// even when the `alg` field is set on a key. Use [`Key::validate_for_use`]
898    /// for those checks.
899    ///
900    /// # Errors
901    ///
902    /// Returns the first validation error encountered, if any.
903    pub fn validate(&self) -> Result<()> {
904        for key in &self.keys {
905            key.validate()?;
906        }
907
908        Ok(())
909    }
910
911    /// Finds a key by its JWK thumbprint (RFC 7638).
912    ///
913    /// # Performance
914    ///
915    /// This method computes the SHA-256 thumbprint of each key in the set on
916    /// every call, making it O(n) hash computations per lookup. For hot paths
917    /// (e.g., verifying JWTs in a web server), consider caching thumbprints
918    /// externally or using [`get_by_kid`](KeySet::get_by_kid) instead.
919    ///
920    /// Thumbprints are derived from public key parameters (RFC 7638), so this
921    /// uses standard equality. The iterator scan short-circuits on first match.
922    ///
923    /// # Security
924    ///
925    /// This method is intended for discovery and cache lookups, not as a
926    /// standalone security gate.
927    pub fn get_by_thumbprint(&self, thumbprint: &str) -> Option<&Key> {
928        self.keys.iter().find(|k| k.thumbprint() == thumbprint)
929    }
930
931    /// Finds keys by optional discovery criteria.
932    ///
933    /// This method is for discovery/filtering only and does not provide
934    /// cryptographic suitability guarantees.
935    ///
936    /// When `filter.op` is set:
937    /// - keys with explicit `key_ops` are included only if they contain that operation,
938    /// - otherwise, keys with `use` are included only if `use` is compatible with the operation,
939    /// - keys with neither `key_ops` nor `use` are treated as discovery
940    ///   candidates and pass through.
941    ///
942    /// Unknown operations in discovery mode are passthrough for `use`-only keys:
943    /// they only filter keys that declare explicit `key_ops` and include the
944    /// unknown operation.
945    /// Keys that declare neither `key_ops` nor `use` also pass through.
946    ///
947    /// # Examples
948    ///
949    /// ```
950    /// use jwk_simple::{Algorithm, KeyFilter, KeySet, KeyType};
951    ///
952    /// let json = r#"{"keys": [
953    ///     {"kty": "RSA", "kid": "r1", "alg": "RS256", "n": "AQAB", "e": "AQAB"},
954    ///     {"kty": "EC", "kid": "e1", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
955    /// ]}"#;
956    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
957    ///
958    /// let rsa_rs256 = KeyFilter::new()
959    ///     .with_kty(KeyType::Rsa)
960    ///     .with_alg(Algorithm::Rs256);
961    ///
962    /// assert_eq!(jwks.find(rsa_rs256).count(), 1);
963    /// ```
964    pub fn find<'a, 'f>(&'a self, filter: KeyFilter<'f>) -> impl Iterator<Item = &'a Key> + 'a {
965        let KeyFilter {
966            op,
967            alg,
968            kid,
969            kty,
970            key_use,
971        } = filter;
972
973        // Capture filter fields up-front so the returned iterator lifetime only
974        // depends on `self`.
975        let kid = kid.map(ToOwned::to_owned);
976
977        self.keys.iter().filter(move |k| {
978            if let Some(kid) = kid.as_deref()
979                && k.kid() != Some(kid)
980            {
981                return false;
982            }
983
984            if let Some(kty) = kty
985                && k.kty() != kty
986            {
987                return false;
988            }
989
990            if let Some(alg) = &alg
991                && k.alg() != Some(alg)
992            {
993                return false;
994            }
995
996            if let Some(key_use) = &key_use
997                && k.key_use() != Some(key_use)
998            {
999                return false;
1000            }
1001
1002            if let Some(op) = &op {
1003                if let Some(key_ops) = k.key_ops() {
1004                    if !key_ops.contains(op) {
1005                        return false;
1006                    }
1007                } else if let Some(key_use) = k.key_use() {
1008                    let allowed_by_use = match op {
1009                        KeyOperation::Sign | KeyOperation::Verify => key_use == &KeyUse::Signature,
1010                        KeyOperation::Encrypt
1011                        | KeyOperation::Decrypt
1012                        | KeyOperation::WrapKey
1013                        | KeyOperation::UnwrapKey
1014                        | KeyOperation::DeriveKey
1015                        | KeyOperation::DeriveBits => key_use == &KeyUse::Encryption,
1016                        KeyOperation::Unknown(_) => true,
1017                    };
1018
1019                    if !allowed_by_use {
1020                        return false;
1021                    }
1022                }
1023            }
1024
1025            true
1026        })
1027    }
1028
1029    /// Creates a strict selector bound to this key set.
1030    ///
1031    /// `allowed_verify_algs` applies only to [`KeyOperation::Verify`].
1032    /// For non-verify operations (for example [`KeyOperation::Sign`]),
1033    /// this allowlist is not consulted.
1034    /// Strict selection failures are returned by [`KeySelector::select`].
1035    pub fn selector(&self, allowed_verify_algs: &[Algorithm]) -> KeySelector<'_> {
1036        KeySelector {
1037            keyset: self,
1038            allowed_verify_algs: allowed_verify_algs.to_vec(),
1039        }
1040    }
1041}
1042
1043impl IntoIterator for KeySet {
1044    type Item = Key;
1045    type IntoIter = std::vec::IntoIter<Key>;
1046
1047    fn into_iter(self) -> Self::IntoIter {
1048        self.keys.into_iter()
1049    }
1050}
1051
1052impl<'a> IntoIterator for &'a KeySet {
1053    type Item = &'a Key;
1054    type IntoIter = std::slice::Iter<'a, Key>;
1055
1056    fn into_iter(self) -> Self::IntoIter {
1057        self.keys.iter()
1058    }
1059}
1060
1061impl std::ops::Index<usize> for KeySet {
1062    type Output = Key;
1063
1064    fn index(&self, index: usize) -> &Self::Output {
1065        &self.keys[index]
1066    }
1067}
1068
1069/// Checks whether a key is suitable for signing/verification operations.
1070///
1071/// When `key_ops` is present it is treated as authoritative: the key must
1072/// include [`KeyOperation::Sign`] or [`KeyOperation::Verify`].
1073/// When `key_ops` is absent, `key_use` is consulted: the key is a signing
1074/// key if `use` is `"sig"` or unset.
1075fn is_signing_key(key: &Key) -> bool {
1076    if let Some(ops) = key.key_ops() {
1077        ops.contains(&KeyOperation::Sign) || ops.contains(&KeyOperation::Verify)
1078    } else {
1079        key.key_use().is_none() || key.key_use() == Some(&KeyUse::Signature)
1080    }
1081}
1082
1083/// Checks whether a key is suitable for encryption operations.
1084///
1085/// When `key_ops` is present it is treated as authoritative: the key must
1086/// include [`KeyOperation::Encrypt`], [`KeyOperation::Decrypt`],
1087/// [`KeyOperation::WrapKey`], [`KeyOperation::UnwrapKey`],
1088/// [`KeyOperation::DeriveKey`], or [`KeyOperation::DeriveBits`].
1089/// When `key_ops` is absent, `key_use` is consulted: the key is an
1090/// encryption key if `use` is `"enc"`.
1091fn is_encryption_key(key: &Key) -> bool {
1092    if let Some(ops) = key.key_ops() {
1093        ops.contains(&KeyOperation::Encrypt)
1094            || ops.contains(&KeyOperation::Decrypt)
1095            || ops.contains(&KeyOperation::WrapKey)
1096            || ops.contains(&KeyOperation::UnwrapKey)
1097            || ops.contains(&KeyOperation::DeriveKey)
1098            || ops.contains(&KeyOperation::DeriveBits)
1099    } else {
1100        key.key_use() == Some(&KeyUse::Encryption)
1101    }
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106    use super::*;
1107
1108    const SAMPLE_JWKS: &str = r#"{
1109        "keys": [
1110            {
1111                "kty": "RSA",
1112                "kid": "rsa-key-1",
1113                "use": "sig",
1114                "alg": "RS256",
1115                "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
1116                "e": "AQAB"
1117            },
1118            {
1119                "kty": "EC",
1120                "kid": "ec-key-1",
1121                "use": "sig",
1122                "alg": "ES256",
1123                "crv": "P-256",
1124                "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
1125                "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
1126            },
1127            {
1128                "kty": "RSA",
1129                "kid": "rsa-enc-1",
1130                "use": "enc",
1131                "n": "sXchDaQebSXKcvL0vwlG",
1132                "e": "AQAB"
1133            }
1134        ]
1135    }"#;
1136
1137    #[test]
1138    fn test_parse_jwks() {
1139        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1140        assert_eq!(jwks.len(), 3);
1141    }
1142
1143    #[test]
1144    fn test_parse_skips_semantically_invalid_key() {
1145        let json = r#"{
1146            "keys": [
1147                {"kty": "EC", "crv": "P-256", "x": "AQ", "y": "AQ", "kid": "bad"},
1148                {"kty": "oct", "k": "AQAB", "kid": "good"}
1149            ]
1150        }"#;
1151
1152        let jwks: KeySet = serde_json::from_str(json).unwrap();
1153        assert_eq!(jwks.len(), 1);
1154        assert!(jwks.get_by_kid("bad").is_none());
1155        assert!(jwks.get_by_kid("good").is_some());
1156    }
1157
1158    #[test]
1159    fn test_parse_skips_unknown_kty() {
1160        let json = r#"{
1161            "keys": [
1162                {"kty": "UNKNOWN", "kid": "unknown"},
1163                {"kty": "oct", "k": "AQAB", "kid": "good"}
1164            ]
1165        }"#;
1166
1167        let jwks: KeySet = serde_json::from_str(json).unwrap();
1168        assert_eq!(jwks.len(), 1);
1169        assert!(jwks.get_by_kid("unknown").is_none());
1170        assert!(jwks.get_by_kid("good").is_some());
1171    }
1172
1173    #[test]
1174    fn test_get_by_kid() {
1175        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1176
1177        assert!(jwks.get_by_kid("rsa-key-1").is_some());
1178        assert!(jwks.get_by_kid("rsa-enc-1").is_some());
1179        assert!(jwks.get_by_kid("ec-key-1").is_some());
1180        assert!(jwks.get_by_kid("unknown").is_none());
1181    }
1182
1183    #[test]
1184    fn test_find_with_filter() {
1185        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1186
1187        let by_alg = KeyFilter::new().with_alg(Algorithm::Rs256);
1188        assert_eq!(jwks.find(by_alg).count(), 1);
1189
1190        let by_kty = KeyFilter::new().with_kty(KeyType::Rsa);
1191        assert_eq!(jwks.find(by_kty).count(), 2);
1192
1193        let by_use = KeyFilter::new().with_key_use(KeyUse::Encryption);
1194        assert_eq!(jwks.find(by_use).count(), 1);
1195
1196        let by_op_use = KeyFilter::new().with_op(KeyOperation::Sign);
1197        assert_eq!(jwks.find(by_op_use).count(), 2);
1198
1199        let by_unknown_op = KeyFilter::new().with_op(KeyOperation::Unknown("custom".to_string()));
1200        assert_eq!(jwks.find(by_unknown_op).count(), 3);
1201
1202        let json = r#"{"keys": [
1203            {"kty": "RSA", "kid": "sign", "n": "AQAB", "e": "AQAB", "key_ops": ["sign"]},
1204            {"kty": "RSA", "kid": "enc", "n": "AQAB", "e": "AQAB", "key_ops": ["encrypt"]}
1205        ]}"#;
1206        let with_key_ops: KeySet = serde_json::from_str(json).unwrap();
1207        let by_op_key_ops = KeyFilter::new().with_op(KeyOperation::Sign);
1208        assert_eq!(with_key_ops.find(by_op_key_ops).count(), 1);
1209    }
1210
1211    #[test]
1212    fn test_find_with_shorthand_constructors() {
1213        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1214
1215        assert_eq!(jwks.find(KeyFilter::for_alg(Algorithm::Rs256)).count(), 1);
1216        assert_eq!(jwks.find(KeyFilter::for_kty(KeyType::Rsa)).count(), 2);
1217        assert_eq!(jwks.find(KeyFilter::for_use(KeyUse::Signature)).count(), 2);
1218        assert_eq!(jwks.find(KeyFilter::for_op(KeyOperation::Sign)).count(), 2);
1219        assert_eq!(
1220            jwks.find(KeyFilter::for_use_alg(KeyUse::Signature, Algorithm::Rs256))
1221                .count(),
1222            1
1223        );
1224        assert_eq!(
1225            jwks.find(KeyFilter::for_op_alg(KeyOperation::Sign, Algorithm::Rs256))
1226                .count(),
1227            1
1228        );
1229    }
1230
1231    #[test]
1232    fn test_selector_verify_empty_allowlist() {
1233        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1234        let selector = jwks.selector(&[]);
1235
1236        let err = selector
1237            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256))
1238            .unwrap_err();
1239
1240        assert!(matches!(err, SelectionError::EmptyVerifyAllowlist));
1241    }
1242
1243    #[test]
1244    fn test_selector_verify_algorithm_not_allowed() {
1245        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1246        let selector = jwks.selector(&[Algorithm::Es256]);
1247
1248        let err = selector
1249            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256))
1250            .unwrap_err();
1251
1252        assert!(matches!(err, SelectionError::AlgorithmNotAllowed));
1253    }
1254
1255    #[test]
1256    fn test_selector_verify_selects_single_key() {
1257        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1258        let selector = jwks.selector(&[Algorithm::Rs256]);
1259
1260        let key = selector
1261            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("rsa-key-1"))
1262            .unwrap();
1263
1264        assert_eq!(key.kid(), Some("rsa-key-1"));
1265    }
1266
1267    #[test]
1268    fn test_selector_ambiguous_selection() {
1269        let json = r#"{"keys": [
1270            {"kty": "EC", "kid": "ec-1", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"},
1271            {"kty": "EC", "kid": "ec-2", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
1272        ]}"#;
1273        let jwks: KeySet = serde_json::from_str(json).unwrap();
1274        let selector = jwks.selector(&[Algorithm::Es256]);
1275
1276        let err = selector
1277            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Es256))
1278            .unwrap_err();
1279
1280        assert!(matches!(
1281            err,
1282            SelectionError::AmbiguousSelection { count: 2 }
1283        ));
1284    }
1285
1286    #[test]
1287    fn test_selector_algorithm_mismatch_for_known_kid() {
1288        let json = r#"{"keys": [
1289            {"kty": "RSA", "kid": "rsa", "alg": "RS256", "use": "sig", "n": "AQAB", "e": "AQAB"}
1290        ]}"#;
1291        let jwks: KeySet = serde_json::from_str(json).unwrap();
1292        let selector = jwks.selector(&[Algorithm::Es256]);
1293
1294        let err = selector
1295            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("rsa"))
1296            .unwrap_err();
1297
1298        assert!(matches!(
1299            err,
1300            SelectionError::AlgorithmMismatch {
1301                requested: Algorithm::Es256,
1302                declared: Algorithm::Rs256
1303            }
1304        ));
1305    }
1306
1307    #[test]
1308    fn test_selector_unknown_algorithm_rejected() {
1309        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1310        let selector = jwks.selector(&[]);
1311
1312        let err = selector
1313            .select(KeyMatcher::new(
1314                KeyOperation::Sign,
1315                Algorithm::Unknown("CUSTOM".to_string()),
1316            ))
1317            .unwrap_err();
1318
1319        assert!(matches!(err, SelectionError::UnknownAlgorithm));
1320    }
1321
1322    #[test]
1323    fn test_selector_unknown_operation_rejected() {
1324        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1325        let selector = jwks.selector(&[]);
1326
1327        let err = selector
1328            .select(KeyMatcher::new(
1329                KeyOperation::Unknown("custom-op".to_string()),
1330                Algorithm::Rs256,
1331            ))
1332            .unwrap_err();
1333
1334        assert!(matches!(err, SelectionError::UnknownOperation));
1335    }
1336
1337    #[test]
1338    fn test_selector_operation_algorithm_mismatch_rejected() {
1339        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1340        let selector = jwks.selector(&[]);
1341
1342        let err = selector
1343            .select(KeyMatcher::new(KeyOperation::Encrypt, Algorithm::Rs256))
1344            .unwrap_err();
1345
1346        assert!(matches!(
1347            err,
1348            SelectionError::OperationAlgorithmMismatch {
1349                operation: KeyOperation::Encrypt,
1350                algorithm: Algorithm::Rs256
1351            }
1352        ));
1353    }
1354
1355    #[test]
1356    fn test_selector_incompatible_key_type_for_known_kid() {
1357        let json = r#"{"keys": [
1358            {"kty": "oct", "kid": "oct-1", "k": "AQAB"}
1359        ]}"#;
1360        let jwks: KeySet = serde_json::from_str(json).unwrap();
1361        let selector = jwks.selector(&[]);
1362
1363        let err = selector
1364            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("oct-1"))
1365            .unwrap_err();
1366
1367        assert!(matches!(err, SelectionError::IncompatibleKeyType));
1368    }
1369
1370    #[test]
1371    fn test_selector_key_validation_failed_for_known_kid() {
1372        let json = r#"{"keys": [
1373            {"kty": "RSA", "kid": "weak-rsa", "use": "sig", "n": "AQAB", "e": "AQAB"}
1374        ]}"#;
1375        let jwks: KeySet = serde_json::from_str(json).unwrap();
1376        let selector = jwks.selector(&[]);
1377
1378        let err = selector
1379            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("weak-rsa"))
1380            .unwrap_err();
1381
1382        assert!(matches!(err, SelectionError::KeySuitabilityFailed(_)));
1383    }
1384
1385    #[test]
1386    fn test_selector_rejects_structurally_invalid_key_added_programmatically() {
1387        // EC P-256 key with x coordinate of wrong length (4 bytes instead of 32).
1388        // Constructed programmatically to bypass JWKS parse-time filtering.
1389        use crate::encoding::Base64UrlBytes;
1390        use crate::{EcCurve, EcParams, KeyParams};
1391
1392        let bad_ec = Key::new(KeyParams::Ec(EcParams::new_public(
1393            EcCurve::P256,
1394            Base64UrlBytes::new(vec![1, 2, 3, 4]), // x: 4 bytes, should be 32
1395            Base64UrlBytes::new(vec![0; 32]),      // y: 32 bytes, correct
1396        )))
1397        .with_kid("bad-ec");
1398
1399        let mut jwks = KeySet::new();
1400        jwks.keys.push(bad_ec); // bypass validation to test selector behavior
1401        assert_eq!(jwks.len(), 1); // Key is present (no parse-time filtering)
1402
1403        let selector = jwks.selector(&[Algorithm::Es256]);
1404        let err = selector
1405            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Es256).with_kid("bad-ec"))
1406            .unwrap_err();
1407
1408        assert!(matches!(err, SelectionError::InvalidKey(_)));
1409    }
1410
1411    #[test]
1412    fn test_selector_key_suitability_failed_hs512_for_known_kid() {
1413        let json = r#"{"keys": [
1414            {"kty": "oct", "kid": "weak-hs", "use": "sig", "alg": "HS512", "k": "AQAB"}
1415        ]}"#;
1416        let jwks: KeySet = serde_json::from_str(json).unwrap();
1417        let selector = jwks.selector(&[Algorithm::Hs512]);
1418
1419        let err = selector
1420            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Hs512).with_kid("weak-hs"))
1421            .unwrap_err();
1422
1423        assert!(matches!(err, SelectionError::KeySuitabilityFailed(_)));
1424    }
1425
1426    #[test]
1427    fn test_selector_intent_mismatch_for_known_kid() {
1428        let json = r#"{"keys": [
1429            {"kty": "RSA", "kid": "enc-rsa", "use": "enc", "n": "AQAB", "e": "AQAB"}
1430        ]}"#;
1431        let jwks: KeySet = serde_json::from_str(json).unwrap();
1432        let selector = jwks.selector(&[]);
1433
1434        let err = selector
1435            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("enc-rsa"))
1436            .unwrap_err();
1437
1438        assert!(matches!(err, SelectionError::IntentMismatch));
1439    }
1440
1441    #[test]
1442    fn test_selector_invalid_key_for_known_kid() {
1443        let bad_key = Key::new(crate::KeyParams::Rsa(crate::RsaParams::new_public(
1444            crate::encoding::Base64UrlBytes::new(vec![1, 2, 3]),
1445            crate::encoding::Base64UrlBytes::new(vec![1, 0, 1]),
1446        )))
1447        .with_kid("dup-ops")
1448        .with_alg(Algorithm::Rs256)
1449        .with_key_ops([KeyOperation::Verify, KeyOperation::Verify]);
1450
1451        let mut jwks = KeySet::new();
1452        jwks.keys.push(bad_key); // bypass validation to test selector behavior
1453        let selector = jwks.selector(&[Algorithm::Rs256]);
1454
1455        let err = selector
1456            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("dup-ops"))
1457            .unwrap_err();
1458
1459        assert!(matches!(err, SelectionError::InvalidKey(_)));
1460    }
1461
1462    #[test]
1463    fn test_selector_invalid_certificate_metadata_for_known_kid() {
1464        let bad_key = Key::new(crate::KeyParams::Rsa(crate::RsaParams::new_public(
1465            crate::encoding::Base64UrlBytes::new(vec![1; 256]),
1466            crate::encoding::Base64UrlBytes::new(vec![1, 0, 1]),
1467        )))
1468        .with_kid("bad-x5u")
1469        .with_alg(Algorithm::Rs256)
1470        .with_x5u("http://example.com/cert.pem");
1471
1472        let mut jwks = KeySet::new();
1473        jwks.keys.push(bad_key); // bypass validation to test selector behavior
1474        let selector = jwks.selector(&[]);
1475
1476        let err = selector
1477            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("bad-x5u"))
1478            .unwrap_err();
1479
1480        assert!(matches!(err, SelectionError::InvalidKey(_)));
1481    }
1482
1483    #[test]
1484    fn test_selector_intent_mismatch_sign_only_key_for_verify() {
1485        let json = r#"{"keys": [
1486            {"kty": "EC", "kid": "sign-only", "key_ops": ["sign"], "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
1487        ]}"#;
1488        let jwks: KeySet = serde_json::from_str(json).unwrap();
1489        let selector = jwks.selector(&[Algorithm::Es256]);
1490
1491        let err = selector
1492            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Es256).with_kid("sign-only"))
1493            .unwrap_err();
1494
1495        assert!(matches!(err, SelectionError::IntentMismatch));
1496    }
1497
1498    #[test]
1499    fn test_selector_intent_mismatch_verify_only_key_for_sign() {
1500        let json = r#"{"keys": [
1501            {"kty": "EC", "kid": "verify-only", "key_ops": ["verify"], "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
1502        ]}"#;
1503        let jwks: KeySet = serde_json::from_str(json).unwrap();
1504        let selector = jwks.selector(&[]);
1505
1506        let err = selector
1507            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("verify-only"))
1508            .unwrap_err();
1509
1510        assert!(matches!(err, SelectionError::IntentMismatch));
1511    }
1512
1513    #[test]
1514    fn test_selector_no_kid_all_candidates_invalid_returns_no_match() {
1515        let json = r#"{"keys": [
1516            {"kty": "RSA", "kid": "weak-1", "use": "sig", "n": "AQAB", "e": "AQAB"},
1517            {"kty": "RSA", "kid": "weak-2", "use": "sig", "n": "AQAB", "e": "AQAB"}
1518        ]}"#;
1519        let jwks: KeySet = serde_json::from_str(json).unwrap();
1520        let selector = jwks.selector(&[]);
1521
1522        let err = selector
1523            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256))
1524            .unwrap_err();
1525
1526        assert!(matches!(err, SelectionError::NoMatchingKey));
1527    }
1528
1529    #[test]
1530    fn test_selector_error_precedence_alg_mismatch_over_intent() {
1531        let json = r#"{"keys": [
1532            {"kty": "RSA", "kid": "dup", "alg": "ES256", "use": "enc", "n": "AQAB", "e": "AQAB"}
1533        ]}"#;
1534        let jwks: KeySet = serde_json::from_str(json).unwrap();
1535        let selector = jwks.selector(&[]);
1536
1537        let err = selector
1538            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("dup"))
1539            .unwrap_err();
1540
1541        assert!(matches!(
1542            err,
1543            SelectionError::AlgorithmMismatch {
1544                requested: Algorithm::Rs256,
1545                declared: Algorithm::Es256
1546            }
1547        ));
1548    }
1549
1550    #[test]
1551    fn test_selector_error_precedence_intent_over_validation() {
1552        let json = r#"{"keys": [
1553            {"kty": "RSA", "kid": "dup", "use": "enc", "n": "AQAB", "e": "AQAB"},
1554            {"kty": "RSA", "kid": "dup", "use": "sig", "n": "AQAB", "e": "AQAB"}
1555        ]}"#;
1556        let jwks: KeySet = serde_json::from_str(json).unwrap();
1557        let selector = jwks.selector(&[]);
1558
1559        let err = selector
1560            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("dup"))
1561            .unwrap_err();
1562
1563        assert!(matches!(err, SelectionError::IntentMismatch));
1564    }
1565
1566    #[test]
1567    fn test_selector_error_precedence_intent_over_incompatible() {
1568        let json = r#"{"keys": [
1569            {"kty": "oct", "kid": "dup", "k": "AQAB"},
1570            {"kty": "RSA", "kid": "dup", "use": "enc", "n": "AQAB", "e": "AQAB"}
1571        ]}"#;
1572        let jwks: KeySet = serde_json::from_str(json).unwrap();
1573        let selector = jwks.selector(&[]);
1574
1575        let err = selector
1576            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("dup"))
1577            .unwrap_err();
1578
1579        assert!(matches!(err, SelectionError::IntentMismatch));
1580    }
1581
1582    #[test]
1583    fn test_selector_error_precedence_validation_over_incompatible() {
1584        let json = r#"{"keys": [
1585            {"kty": "oct", "kid": "dup", "k": "AQAB"},
1586            {"kty": "RSA", "kid": "dup", "use": "sig", "n": "AQAB", "e": "AQAB"}
1587        ]}"#;
1588        let jwks: KeySet = serde_json::from_str(json).unwrap();
1589        let selector = jwks.selector(&[]);
1590
1591        let err = selector
1592            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("dup"))
1593            .unwrap_err();
1594
1595        assert!(matches!(err, SelectionError::KeySuitabilityFailed(_)));
1596    }
1597
1598    #[test]
1599    fn test_selector_verify_selects_single_key_without_kid() {
1600        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1601        let selector = jwks.selector(&[Algorithm::Es256]);
1602
1603        let key = selector
1604            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Es256))
1605            .unwrap();
1606
1607        assert_eq!(key.kid(), Some("ec-key-1"));
1608    }
1609
1610    #[test]
1611    fn test_selector_no_kid_all_declared_algs_mismatch_returns_no_match() {
1612        let json = r#"{"keys": [
1613            {"kty": "RSA", "kid": "r1", "alg": "RS256", "use": "sig", "n": "AQAB", "e": "AQAB"},
1614            {"kty": "EC", "kid": "e1", "alg": "ES256", "use": "sig", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
1615        ]}"#;
1616        let jwks: KeySet = serde_json::from_str(json).unwrap();
1617        let selector = jwks.selector(&[]);
1618
1619        let err = selector
1620            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Ps256))
1621            .unwrap_err();
1622
1623        assert!(matches!(err, SelectionError::NoMatchingKey));
1624    }
1625
1626    #[test]
1627    fn test_selector_okp_verify_success() {
1628        let json = r#"{"keys": [
1629            {"kty": "OKP", "kid": "ed-key", "use": "sig", "alg": "Ed25519", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}
1630        ]}"#;
1631        let jwks: KeySet = serde_json::from_str(json).unwrap();
1632        let selector = jwks.selector(&[Algorithm::Ed25519]);
1633
1634        let key = selector
1635            .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Ed25519).with_kid("ed-key"))
1636            .unwrap();
1637
1638        assert_eq!(key.kid(), Some("ed-key"));
1639    }
1640
1641    #[test]
1642    fn test_selector_okp_sign_success_with_private_key() {
1643        let json = r#"{"keys": [
1644            {"kty": "OKP", "kid": "ed-sign", "use": "sig", "alg": "Ed25519", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "d": "nWGxne_9Wm8tRcf0UjvXw9vQ3j8n0i4Q4fQx5t6k7mA"}
1645        ]}"#;
1646        let jwks: KeySet = serde_json::from_str(json).unwrap();
1647        let selector = jwks.selector(&[]);
1648
1649        let key = selector
1650            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Ed25519).with_kid("ed-sign"))
1651            .unwrap();
1652
1653        assert_eq!(key.kid(), Some("ed-sign"));
1654    }
1655
1656    #[test]
1657    fn test_selector_okp_incompatible_with_ec_algorithm() {
1658        let json = r#"{"keys": [
1659            {"kty": "OKP", "kid": "ed-key", "use": "sig", "alg": "Ed25519", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}
1660        ]}"#;
1661        let jwks: KeySet = serde_json::from_str(json).unwrap();
1662        let selector = jwks.selector(&[]);
1663
1664        let err = selector
1665            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("ed-key"))
1666            .unwrap_err();
1667
1668        assert!(matches!(
1669            err,
1670            SelectionError::AlgorithmMismatch {
1671                requested: Algorithm::Es256,
1672                declared: Algorithm::Ed25519
1673            }
1674        ));
1675    }
1676
1677    #[test]
1678    fn test_selector_sign_selects_single_key() {
1679        let json = r#"{"keys": [
1680            {"kty": "EC", "kid": "ec-sign", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
1681        ]}"#;
1682        let jwks: KeySet = serde_json::from_str(json).unwrap();
1683        let selector = jwks.selector(&[]);
1684
1685        let key = selector
1686            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("ec-sign"))
1687            .unwrap();
1688
1689        assert_eq!(key.kid(), Some("ec-sign"));
1690    }
1691
1692    #[test]
1693    fn test_selector_rejects_public_key_for_sign_with_known_kid() {
1694        let json = r#"{"keys": [
1695            {"kty": "RSA", "kid": "rsa-pub", "use": "sig", "alg": "RS256", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB"}
1696        ]}"#;
1697        let jwks: KeySet = serde_json::from_str(json).unwrap();
1698        let selector = jwks.selector(&[]);
1699
1700        let err = selector
1701            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("rsa-pub"))
1702            .unwrap_err();
1703
1704        assert!(matches!(err, SelectionError::KeySuitabilityFailed(_)));
1705    }
1706
1707    #[test]
1708    fn test_selector_rejects_public_ec_key_for_sign_with_known_kid() {
1709        let json = r#"{"keys": [
1710            {"kty": "EC", "kid": "ec-pub", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
1711        ]}"#;
1712        let jwks: KeySet = serde_json::from_str(json).unwrap();
1713        let selector = jwks.selector(&[]);
1714
1715        let err = selector
1716            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("ec-pub"))
1717            .unwrap_err();
1718
1719        assert!(matches!(err, SelectionError::KeySuitabilityFailed(_)));
1720    }
1721
1722    #[test]
1723    fn test_selector_no_kid_public_signing_candidates_return_no_match() {
1724        let json = r#"{"keys": [
1725            {"kty": "RSA", "kid": "rsa-pub-1", "use": "sig", "alg": "RS256", "n": "AQAB", "e": "AQAB"},
1726            {"kty": "RSA", "kid": "rsa-pub-2", "use": "sig", "alg": "RS256", "n": "AQAB", "e": "AQAB"}
1727        ]}"#;
1728        let jwks: KeySet = serde_json::from_str(json).unwrap();
1729        let selector = jwks.selector(&[]);
1730
1731        let err = selector
1732            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256))
1733            .unwrap_err();
1734
1735        assert!(matches!(err, SelectionError::NoMatchingKey));
1736    }
1737
1738    #[test]
1739    fn test_selector_empty_verify_allowlist_does_not_block_signing() {
1740        let json = r#"{"keys": [
1741            {"kty": "EC", "kid": "ec-key-1", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
1742        ]}"#;
1743        let jwks: KeySet = serde_json::from_str(json).unwrap();
1744        let selector = jwks.selector(&[]);
1745
1746        let key = selector
1747            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("ec-key-1"))
1748            .unwrap();
1749
1750        assert_eq!(key.kid(), Some("ec-key-1"));
1751    }
1752
1753    #[test]
1754    fn test_find_with_filter_op_and_use_combination() {
1755        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1756
1757        let compatible = KeyFilter::new()
1758            .with_op(KeyOperation::Sign)
1759            .with_key_use(KeyUse::Signature);
1760        assert_eq!(jwks.find(compatible).count(), 2);
1761
1762        let conflicting = KeyFilter::new()
1763            .with_op(KeyOperation::Sign)
1764            .with_key_use(KeyUse::Encryption);
1765        assert_eq!(jwks.find(conflicting).count(), 0);
1766    }
1767
1768    #[test]
1769    fn test_find_with_filter_op_passthrough_without_metadata() {
1770        let json = r#"{"keys": [
1771            {"kty": "RSA", "kid": "meta-less", "n": "AQAB", "e": "AQAB"},
1772            {"kty": "RSA", "kid": "sig-use", "use": "sig", "n": "AQAB", "e": "AQAB"}
1773        ]}"#;
1774        let jwks: KeySet = serde_json::from_str(json).unwrap();
1775
1776        let by_sign = KeyFilter::new().with_op(KeyOperation::Sign);
1777
1778        // Keys without key_ops/use pass through in discovery mode by design.
1779        assert_eq!(jwks.find(by_sign).count(), 2);
1780    }
1781
1782    #[test]
1783    fn test_find_with_filter_builder_api() {
1784        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1785
1786        let filter = KeyFilter::new()
1787            .with_kty(KeyType::Rsa)
1788            .with_alg(Algorithm::Rs256)
1789            .with_kid("rsa-key-1");
1790
1791        let keys: Vec<_> = jwks.find(filter).collect();
1792        assert_eq!(keys.len(), 1);
1793        assert_eq!(keys[0].kid(), Some("rsa-key-1"));
1794    }
1795
1796    #[test]
1797    fn test_selector_no_matching_key_for_empty_keyset() {
1798        let jwks = KeySet::new();
1799        let selector = jwks.selector(&[]);
1800
1801        let err = selector
1802            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256))
1803            .unwrap_err();
1804
1805        assert!(matches!(err, SelectionError::NoMatchingKey));
1806    }
1807
1808    #[test]
1809    fn test_selector_no_matching_key_for_unknown_kid() {
1810        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1811        let selector = jwks.selector(&[]);
1812
1813        let err = selector
1814            .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Rs256).with_kid("ghost"))
1815            .unwrap_err();
1816
1817        assert!(matches!(err, SelectionError::NoMatchingKey));
1818    }
1819
1820    #[test]
1821    fn test_selection_error_display_messages() {
1822        assert_eq!(
1823            SelectionError::EmptyVerifyAllowlist.to_string(),
1824            "verification allowlist is empty"
1825        );
1826        assert_eq!(
1827            SelectionError::UnknownAlgorithm.to_string(),
1828            "unknown or unsupported algorithm"
1829        );
1830        assert_eq!(
1831            SelectionError::UnknownOperation.to_string(),
1832            "unknown or unsupported operation"
1833        );
1834        assert_eq!(
1835            SelectionError::OperationAlgorithmMismatch {
1836                operation: KeyOperation::Encrypt,
1837                algorithm: Algorithm::Rs256,
1838            }
1839            .to_string(),
1840            "operation/algorithm mismatch: operation encrypt is not valid for algorithm RS256"
1841        );
1842        assert_eq!(
1843            SelectionError::AlgorithmNotAllowed.to_string(),
1844            "algorithm is not allowed for verification"
1845        );
1846        assert_eq!(
1847            SelectionError::IntentMismatch.to_string(),
1848            "key metadata does not permit requested operation"
1849        );
1850        assert_eq!(
1851            SelectionError::InvalidKey(InvalidKeyError::InconsistentParameters(
1852                "duplicate key_ops".to_string()
1853            ))
1854            .to_string(),
1855            "key is invalid: inconsistent key parameters: duplicate key_ops"
1856        );
1857        assert_eq!(
1858            SelectionError::IncompatibleKeyType.to_string(),
1859            "key type/curve is incompatible with requested algorithm"
1860        );
1861
1862        let mismatch = SelectionError::AlgorithmMismatch {
1863            requested: Algorithm::Rs256,
1864            declared: Algorithm::Es256,
1865        };
1866        assert_eq!(
1867            mismatch.to_string(),
1868            "algorithm mismatch: requested RS256, key declares ES256"
1869        );
1870
1871        let ambiguous = SelectionError::AmbiguousSelection { count: 2 };
1872        assert_eq!(
1873            ambiguous.to_string(),
1874            "selection is ambiguous: 2 matching keys"
1875        );
1876
1877        let suitability =
1878            SelectionError::KeySuitabilityFailed(IncompatibleKeyError::InsufficientKeyStrength {
1879                minimum_bits: 256,
1880                actual_bits: 128,
1881                context: "HS256",
1882            });
1883        assert_eq!(
1884            suitability.to_string(),
1885            "key suitability check failed: insufficient key strength for HS256: need 256 bits, got 128"
1886        );
1887
1888        assert_eq!(
1889            SelectionError::NoMatchingKey.to_string(),
1890            "no matching key found"
1891        );
1892    }
1893
1894    #[test]
1895    fn test_find_by_alg() {
1896        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1897
1898        assert_eq!(
1899            jwks.find(KeyFilter::new().with_alg(Algorithm::Rs256))
1900                .count(),
1901            1
1902        );
1903        assert_eq!(
1904            jwks.find(KeyFilter::new().with_alg(Algorithm::Es256))
1905                .count(),
1906            1
1907        );
1908    }
1909
1910    #[test]
1911    fn test_find_by_use() {
1912        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1913
1914        assert_eq!(
1915            jwks.find(KeyFilter::new().with_key_use(KeyUse::Signature))
1916                .count(),
1917            2
1918        );
1919        assert_eq!(
1920            jwks.find(KeyFilter::new().with_key_use(KeyUse::Encryption))
1921                .count(),
1922            1
1923        );
1924    }
1925
1926    #[test]
1927    fn test_signing_keys() {
1928        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1929
1930        assert_eq!(jwks.signing_keys().count(), 2);
1931    }
1932
1933    #[test]
1934    fn test_encryption_keys() {
1935        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1936
1937        assert_eq!(jwks.encryption_keys().count(), 1);
1938    }
1939
1940    #[test]
1941    fn test_encryption_keys_include_derive_ops() {
1942        let json = r#"{"keys": [
1943            {"kty": "RSA", "kid": "derive-key", "key_ops": ["deriveKey"], "n": "AQAB", "e": "AQAB"},
1944            {"kty": "RSA", "kid": "derive-bits", "key_ops": ["deriveBits"], "n": "AQAB", "e": "AQAB"},
1945            {"kty": "RSA", "kid": "verify-only", "key_ops": ["verify"], "n": "AQAB", "e": "AQAB"}
1946        ]}"#;
1947        let jwks: KeySet = serde_json::from_str(json).unwrap();
1948
1949        let kids: Vec<_> = jwks.encryption_keys().filter_map(Key::kid).collect();
1950
1951        assert_eq!(kids.len(), 2);
1952        assert!(kids.contains(&"derive-key"));
1953        assert!(kids.contains(&"derive-bits"));
1954    }
1955
1956    #[test]
1957    fn test_first_signing_key() {
1958        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1959
1960        let first = jwks.first_signing_key().unwrap();
1961        assert_eq!(first.kid(), Some("rsa-key-1"));
1962    }
1963
1964    #[test]
1965    fn test_find_first_by_alg() {
1966        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
1967
1968        let key = jwks
1969            .find(KeyFilter::new().with_alg(Algorithm::Rs256))
1970            .next();
1971        assert!(key.is_some());
1972        assert_eq!(key.unwrap().kid(), Some("rsa-key-1"));
1973
1974        let key = jwks
1975            .find(KeyFilter::new().with_alg(Algorithm::Es256))
1976            .next();
1977        assert!(key.is_some());
1978        assert_eq!(key.unwrap().kid(), Some("ec-key-1"));
1979
1980        let missing = jwks
1981            .find(KeyFilter::new().with_alg(Algorithm::Ps512))
1982            .next();
1983        assert!(missing.is_none());
1984    }
1985
1986    #[test]
1987    fn test_signing_keys_includes_verify_key_ops() {
1988        // A key with key_ops=["verify"] is a signature-operation key and is
1989        // included in signing_keys(), which covers sign/verify roles.
1990        let json = r#"{"keys": [
1991            {"kty": "RSA", "kid": "verify-key", "key_ops": ["verify"], "n": "AQAB", "e": "AQAB"}
1992        ]}"#;
1993        let jwks: KeySet = serde_json::from_str(json).unwrap();
1994
1995        assert_eq!(jwks.signing_keys().count(), 1);
1996    }
1997
1998    #[test]
1999    fn test_signing_keys_respects_key_ops_sign() {
2000        // A key with key_ops=["sign"] should be considered a signing key
2001        let json = r#"{"keys": [
2002            {"kty": "RSA", "kid": "sign-key", "key_ops": ["sign"], "n": "AQAB", "e": "AQAB"}
2003        ]}"#;
2004        let jwks: KeySet = serde_json::from_str(json).unwrap();
2005
2006        assert_eq!(jwks.signing_keys().count(), 1);
2007    }
2008
2009    #[test]
2010    fn test_signing_keys_excludes_encrypt_key_ops() {
2011        // A key with key_ops=["encrypt"] should NOT be considered a signing key
2012        let json = r#"{"keys": [
2013            {"kty": "RSA", "kid": "enc-key", "key_ops": ["encrypt"], "n": "AQAB", "e": "AQAB"}
2014        ]}"#;
2015        let jwks: KeySet = serde_json::from_str(json).unwrap();
2016
2017        assert_eq!(jwks.signing_keys().count(), 0);
2018    }
2019
2020    #[test]
2021    fn test_rfc9864_alg_lookup_behavior() {
2022        let json = r#"{"keys": [
2023            {"kty": "OKP", "kid": "ed25519-key", "use": "sig", "alg": "Ed25519", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "d": "nWGxne_9Wm8tRcf0UjvXw9vQ3j8n0i4Q4fQx5t6k7mA"},
2024            {"kty": "OKP", "kid": "legacy-eddsa", "use": "sig", "alg": "EdDSA", "crv": "Ed25519", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "d": "nWGxne_9Wm8tRcf0UjvXw9vQ3j8n0i4Q4fQx5t6k7mA"}
2025        ]}"#;
2026        let jwks: KeySet = serde_json::from_str(json).unwrap();
2027
2028        // Strict alg matching only finds exact matches.
2029        assert_eq!(
2030            jwks.find(KeyFilter::new().with_alg(Algorithm::Ed25519))
2031                .count(),
2032            1
2033        );
2034        assert_eq!(
2035            jwks.find(KeyFilter::new().with_alg(Algorithm::EdDsa))
2036                .count(),
2037            1
2038        );
2039
2040        assert_eq!(
2041            jwks.selector(&[])
2042                .select(
2043                    KeyMatcher::new(KeyOperation::Sign, Algorithm::Ed25519).with_kid("ed25519-key")
2044                )
2045                .unwrap()
2046                .kid(),
2047            Some("ed25519-key")
2048        );
2049        assert_eq!(
2050            jwks.selector(&[])
2051                .select(
2052                    KeyMatcher::new(KeyOperation::Sign, Algorithm::EdDsa).with_kid("legacy-eddsa")
2053                )
2054                .unwrap()
2055                .kid(),
2056            Some("legacy-eddsa")
2057        );
2058    }
2059
2060    #[test]
2061    fn test_empty_jwks() {
2062        let jwks = KeySet::new();
2063        assert!(jwks.is_empty());
2064        assert_eq!(jwks.len(), 0);
2065        assert!(jwks.first().is_none());
2066        assert!(jwks.first_signing_key().is_none());
2067    }
2068
2069    #[test]
2070    fn test_serde_roundtrip() {
2071        let original: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2072        let json = serde_json::to_string(&original).unwrap();
2073        let parsed: KeySet = serde_json::from_str(&json).unwrap();
2074        assert_eq!(original.len(), parsed.len());
2075        assert_eq!(original.keys(), parsed.keys());
2076    }
2077
2078    #[test]
2079    fn test_iterator() {
2080        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2081
2082        let count = jwks.iter().count();
2083        assert_eq!(count, 3);
2084
2085        let kids: Vec<_> = jwks.iter().filter_map(Key::kid).collect();
2086        assert!(kids.contains(&"rsa-key-1"));
2087    }
2088
2089    #[test]
2090    fn test_index() {
2091        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2092        let first = &jwks[0];
2093        assert_eq!(first.kid(), Some("rsa-key-1"));
2094    }
2095
2096    #[test]
2097    fn test_add_key() {
2098        let mut jwks = KeySet::new();
2099        assert!(jwks.is_empty());
2100
2101        let key: Key = serde_json::from_str(r#"{"kty":"oct","kid":"k1","k":"AQAB"}"#).unwrap();
2102        jwks.add_key(key).unwrap();
2103        assert_eq!(jwks.len(), 1);
2104        assert!(jwks.get_by_kid("k1").is_some());
2105    }
2106
2107    #[test]
2108    fn test_from_keys_lossy_empty_vec_returns_empty_keyset() {
2109        let jwks = KeySet::from_keys_lossy(Vec::new());
2110        assert!(jwks.is_empty());
2111    }
2112
2113    #[test]
2114    fn test_from_keys_lossy_all_valid_preserves_all_and_order() {
2115        let key1: Key = serde_json::from_str(r#"{"kty":"oct","kid":"k1","k":"AQAB"}"#).unwrap();
2116        let key2: Key = serde_json::from_str(r#"{"kty":"oct","kid":"k2","k":"AQID"}"#).unwrap();
2117
2118        let jwks = KeySet::from_keys_lossy(vec![key1, key2]);
2119
2120        assert_eq!(jwks.len(), 2);
2121        let kids: Vec<_> = jwks.iter().filter_map(Key::kid).collect();
2122        assert_eq!(kids, vec!["k1", "k2"]);
2123    }
2124
2125    #[test]
2126    fn test_from_keys_lossy_mixed_drops_invalid_preserves_valid_order() {
2127        let valid1: Key =
2128            serde_json::from_str(r#"{"kty":"oct","kid":"valid-1","k":"AQAB"}"#).unwrap();
2129        let valid2: Key =
2130            serde_json::from_str(r#"{"kty":"oct","kid":"valid-2","k":"AQID"}"#).unwrap();
2131        let invalid: Key =
2132            serde_json::from_str(r#"{"kty":"oct","kid":"invalid","k":"AQAE"}"#).unwrap();
2133        let invalid = invalid
2134            .with_use(KeyUse::Signature)
2135            .with_key_ops([KeyOperation::Encrypt]);
2136
2137        let jwks = KeySet::from_keys_lossy(vec![valid1, invalid, valid2]);
2138
2139        assert_eq!(jwks.len(), 2);
2140        assert!(jwks.get_by_kid("invalid").is_none());
2141        let kids: Vec<_> = jwks.iter().filter_map(Key::kid).collect();
2142        assert_eq!(kids, vec!["valid-1", "valid-2"]);
2143    }
2144
2145    #[test]
2146    fn test_from_keys_lossy_all_invalid_returns_empty_keyset() {
2147        let invalid1: Key =
2148            serde_json::from_str(r#"{"kty":"oct","kid":"bad-1","k":"AQAE"}"#).unwrap();
2149        let invalid1 = invalid1
2150            .with_use(KeyUse::Signature)
2151            .with_key_ops([KeyOperation::Encrypt]);
2152        let invalid2: Key =
2153            serde_json::from_str(r#"{"kty":"oct","kid":"bad-2","k":"AQAF"}"#).unwrap();
2154        let invalid2 = invalid2
2155            .with_use(KeyUse::Encryption)
2156            .with_key_ops([KeyOperation::Sign]);
2157
2158        let jwks = KeySet::from_keys_lossy(vec![invalid1, invalid2]);
2159        assert!(jwks.is_empty());
2160    }
2161
2162    #[test]
2163    fn test_remove_by_kid() {
2164        let mut jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2165        assert_eq!(jwks.len(), 3);
2166
2167        let removed = jwks.remove_by_kid("ec-key-1");
2168        assert!(removed.is_some());
2169        assert_eq!(removed.unwrap().kid(), Some("ec-key-1"));
2170        assert_eq!(jwks.len(), 2);
2171        assert!(jwks.get_by_kid("ec-key-1").is_none());
2172
2173        // Removing non-existent kid returns None
2174        assert!(jwks.remove_by_kid("nonexistent").is_none());
2175        assert_eq!(jwks.len(), 2);
2176    }
2177
2178    #[test]
2179    fn test_find_by_kty() {
2180        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2181
2182        assert_eq!(
2183            jwks.find(KeyFilter::new().with_kty(KeyType::Rsa)).count(),
2184            2
2185        );
2186        assert_eq!(jwks.find(KeyFilter::new().with_kty(KeyType::Ec)).count(), 1);
2187        assert_eq!(
2188            jwks.find(KeyFilter::new().with_kty(KeyType::Okp)).count(),
2189            0
2190        );
2191        assert_eq!(
2192            jwks.find(KeyFilter::new().with_kty(KeyType::Symmetric))
2193                .count(),
2194            0
2195        );
2196    }
2197
2198    #[test]
2199    fn test_get_by_thumbprint_finds_matching_key() {
2200        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2201        let rsa_key = jwks.get_by_kid("rsa-key-1").unwrap();
2202        let thumbprint = rsa_key.thumbprint();
2203
2204        let found = jwks.get_by_thumbprint(&thumbprint);
2205        assert!(found.is_some());
2206        assert_eq!(found.unwrap().kid(), Some("rsa-key-1"));
2207    }
2208
2209    #[test]
2210    fn test_get_by_thumbprint_returns_none_for_unknown_value() {
2211        let jwks: KeySet = serde_json::from_str(SAMPLE_JWKS).unwrap();
2212        assert!(
2213            jwks.get_by_thumbprint("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
2214                .is_none()
2215        );
2216    }
2217
2218    #[test]
2219    fn test_get_by_thumbprint_matches_first_key_when_duplicates_present() {
2220        let key_json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
2221        let key: Key = serde_json::from_str(key_json).unwrap();
2222        let duplicate: Key = serde_json::from_str(key_json).unwrap();
2223        let thumbprint = key.thumbprint();
2224
2225        let mut jwks = KeySet::new();
2226        jwks.add_key(key).unwrap();
2227        jwks.add_key(duplicate).unwrap();
2228
2229        let found = jwks.get_by_thumbprint(&thumbprint).unwrap();
2230        assert_eq!(found.thumbprint(), thumbprint);
2231    }
2232
2233    #[cfg(not(target_arch = "wasm32"))]
2234    #[tokio::test]
2235    async fn test_jwkset_implements_store() {
2236        let json = r#"{"keys": [{"kty": "oct", "kid": "test-key", "k": "AQAB"}]}"#;
2237        let store: KeySet = serde_json::from_str(json).unwrap();
2238
2239        // Test get_key
2240        let key = store.get_key("test-key").await.unwrap();
2241        assert!(key.is_some());
2242        assert_eq!(key.unwrap().kid(), Some("test-key"));
2243
2244        // Test missing key
2245        let missing = store.get_key("nonexistent").await.unwrap();
2246        assert!(missing.is_none());
2247
2248        // Test get_keyset
2249        let keyset = store.get_keyset().await.unwrap();
2250        assert_eq!(keyset.len(), 1);
2251    }
2252}