1use 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#[derive(Debug, Clone, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum SelectionError {
35 EmptyVerifyAllowlist,
37 UnknownAlgorithm,
39 UnknownOperation,
41 OperationAlgorithmMismatch {
43 operation: KeyOperation,
45 algorithm: Algorithm,
47 },
48 AlgorithmNotAllowed,
50 AlgorithmMismatch {
52 requested: Algorithm,
54 declared: Algorithm,
56 },
57 IntentMismatch,
59 InvalidKey(InvalidKeyError),
61 IncompatibleKeyType,
63 KeySuitabilityFailed(IncompatibleKeyError),
65 AmbiguousSelection {
67 count: usize,
69 },
70 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 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#[derive(Debug, Clone)]
164pub struct KeyMatcher<'a> {
165 op: KeyOperation,
167 alg: Algorithm,
169 kid: Option<&'a str>,
171}
172
173impl<'a> KeyMatcher<'a> {
174 #[must_use]
176 pub fn new(op: KeyOperation, alg: Algorithm) -> Self {
177 Self { op, alg, kid: None }
178 }
179
180 #[must_use]
182 pub fn with_kid(mut self, kid: &'a str) -> Self {
183 self.kid = Some(kid);
184 self
185 }
186}
187
188#[derive(Debug, Clone, Default)]
198#[non_exhaustive]
199pub struct KeyFilter<'a> {
200 pub op: Option<KeyOperation>,
202 pub alg: Option<Algorithm>,
204 pub kid: Option<&'a str>,
206 pub kty: Option<KeyType>,
208 pub key_use: Option<KeyUse>,
210}
211
212impl<'a> KeyFilter<'a> {
213 #[must_use]
215 pub fn new() -> Self {
216 Self::default()
217 }
218
219 #[must_use]
221 pub fn for_alg(alg: Algorithm) -> Self {
222 Self::new().with_alg(alg)
223 }
224
225 #[must_use]
227 pub fn for_use(key_use: KeyUse) -> Self {
228 Self::new().with_key_use(key_use)
229 }
230
231 #[must_use]
233 pub fn for_kty(kty: KeyType) -> Self {
234 Self::new().with_kty(kty)
235 }
236
237 #[must_use]
239 pub fn for_op(op: KeyOperation) -> Self {
240 Self::new().with_op(op)
241 }
242
243 #[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 #[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 #[must_use]
257 pub fn with_op(mut self, op: KeyOperation) -> Self {
258 self.op = Some(op);
259 self
260 }
261
262 #[must_use]
264 pub fn with_alg(mut self, alg: Algorithm) -> Self {
265 self.alg = Some(alg);
266 self
267 }
268
269 #[must_use]
271 pub fn with_kid(mut self, kid: &'a str) -> Self {
272 self.kid = Some(kid);
273 self
274 }
275
276 #[must_use]
278 pub fn with_kty(mut self, kty: KeyType) -> Self {
279 self.kty = Some(kty);
280 self
281 }
282
283 #[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#[derive(Debug, Clone)]
293pub struct KeySelector<'a> {
294 keyset: &'a KeySet,
296 allowed_verify_algs: Vec<Algorithm>,
298}
299
300impl<'a> KeySelector<'a> {
301 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 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 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 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 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#[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 async fn get_keyset(&self) -> Result<KeySet>;
586
587 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#[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#[derive(Debug, Clone, Serialize, Default)]
667pub struct KeySet {
668 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 #[derive(Deserialize)]
679 struct RawJwkSet {
680 keys: Vec<serde_json::Value>,
681 }
682
683 let raw = RawJwkSet::deserialize(deserializer)?;
684
685 let mut keys = Vec::with_capacity(raw.keys.len());
691 for value in raw.keys {
692 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 pub fn new() -> Self {
717 Self { keys: Vec::new() }
718 }
719
720 #[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 pub fn keys(&self) -> &[Key] {
747 &self.keys
748 }
749
750 pub fn len(&self) -> usize {
752 self.keys.len()
753 }
754
755 pub fn is_empty(&self) -> bool {
757 self.keys.is_empty()
758 }
759
760 pub fn add_key(&mut self, key: Key) -> Result<()> {
783 key.validate()?;
784 self.keys.push(key);
785 Ok(())
786 }
787
788 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 pub fn get_by_kid(&self, kid: &str) -> Option<&Key> {
814 self.keys.iter().find(|k| k.kid() == Some(kid))
815 }
816
817 pub fn signing_keys(&self) -> impl Iterator<Item = &Key> {
830 self.keys.iter().filter(|k| is_signing_key(k))
831 }
832
833 pub fn encryption_keys(&self) -> impl Iterator<Item = &Key> {
846 self.keys.iter().filter(|k| is_encryption_key(k))
847 }
848
849 pub fn first_signing_key(&self) -> Option<&Key> {
870 self.signing_keys().next()
871 }
872
873 pub fn first(&self) -> Option<&Key> {
884 self.keys.first()
885 }
886
887 pub fn iter(&self) -> impl Iterator<Item = &Key> {
889 self.keys.iter()
890 }
891
892 pub fn validate(&self) -> Result<()> {
904 for key in &self.keys {
905 key.validate()?;
906 }
907
908 Ok(())
909 }
910
911 pub fn get_by_thumbprint(&self, thumbprint: &str) -> Option<&Key> {
928 self.keys.iter().find(|k| k.thumbprint() == thumbprint)
929 }
930
931 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 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 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
1069fn 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
1083fn 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 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]), Base64UrlBytes::new(vec![0; 32]), )))
1397 .with_kid("bad-ec");
1398
1399 let mut jwks = KeySet::new();
1400 jwks.keys.push(bad_ec); assert_eq!(jwks.len(), 1); 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); 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); 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 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 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 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 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 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 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 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 let missing = store.get_key("nonexistent").await.unwrap();
2246 assert!(missing.is_none());
2247
2248 let keyset = store.get_keyset().await.unwrap();
2250 assert_eq!(keyset.len(), 1);
2251 }
2252}