1use jwt_simple::prelude::*;
20use pkcs1::der::Encode;
21use pkcs1::{RsaPrivateKey as Pkcs1RsaPrivateKey, RsaPublicKey as Pkcs1RsaPublicKey, UintRef};
22use zeroize::Zeroizing;
23
24#[cfg(test)]
25use crate::IncompatibleKeyError;
26use crate::error::{Error, JwtSimpleKeyConversionError};
27use crate::jwk::{Algorithm, EcCurve, Key, KeyOperation, KeyParams, OkpCurve, RsaParams};
28
29type Result<T> = std::result::Result<T, JwtSimpleKeyConversionError>;
30
31fn map_validation_error(err: Error) -> JwtSimpleKeyConversionError {
32 match err {
33 Error::InvalidKey(err) => JwtSimpleKeyConversionError::InvalidKey(err),
34 Error::IncompatibleKey(err) => JwtSimpleKeyConversionError::IncompatibleKey(err),
35 other => JwtSimpleKeyConversionError::Core(other),
36 }
37}
38
39fn validate_for_jwt_simple(
40 jwk: &Key,
41 alg: &Algorithm,
42 ops: impl IntoIterator<Item = KeyOperation>,
43) -> Result<()> {
44 jwk.validate_for_use(alg, ops).map_err(map_validation_error)
45}
46
47fn build_rsa_public_key_der(params: &RsaParams) -> Result<Vec<u8>> {
61 let modulus = uint_ref(params.n.as_bytes(), "n")?;
62 let public_exponent = uint_ref(params.e.as_bytes(), "e")?;
63
64 Pkcs1RsaPublicKey {
65 modulus,
66 public_exponent,
67 }
68 .to_der()
69 .map_err(|e| {
70 JwtSimpleKeyConversionError::Encoding(format!(
71 "failed to encode PKCS#1 RSA public key: {e}"
72 ))
73 })
74}
75
76fn build_rsa_private_key_der(params: &RsaParams) -> Result<Zeroizing<Vec<u8>>> {
93 if params.oth.is_some() {
97 return Err(JwtSimpleKeyConversionError::Encoding(
98 "multi-prime RSA keys are not supported for jwt-simple conversion".into(),
99 ));
100 }
101
102 let d = params
103 .d
104 .as_ref()
105 .ok_or(JwtSimpleKeyConversionError::MissingPrivateKey)?;
106 let p = params
107 .p
108 .as_ref()
109 .ok_or(JwtSimpleKeyConversionError::MissingComponent { field: "p" })?;
110 let q = params
111 .q
112 .as_ref()
113 .ok_or(JwtSimpleKeyConversionError::MissingComponent { field: "q" })?;
114 let dp = params
115 .dp
116 .as_ref()
117 .ok_or(JwtSimpleKeyConversionError::MissingComponent { field: "dp" })?;
118 let dq = params
119 .dq
120 .as_ref()
121 .ok_or(JwtSimpleKeyConversionError::MissingComponent { field: "dq" })?;
122 let qi = params
123 .qi
124 .as_ref()
125 .ok_or(JwtSimpleKeyConversionError::MissingComponent { field: "qi" })?;
126
127 let private_key = Pkcs1RsaPrivateKey {
128 modulus: uint_ref(params.n.as_bytes(), "n")?,
129 public_exponent: uint_ref(params.e.as_bytes(), "e")?,
130 private_exponent: uint_ref(d.as_bytes(), "d")?,
131 prime1: uint_ref(p.as_bytes(), "p")?,
132 prime2: uint_ref(q.as_bytes(), "q")?,
133 exponent1: uint_ref(dp.as_bytes(), "dp")?,
134 exponent2: uint_ref(dq.as_bytes(), "dq")?,
135 coefficient: uint_ref(qi.as_bytes(), "qi")?,
136 other_prime_infos: None,
137 };
138
139 private_key.to_der().map(Zeroizing::new).map_err(|e| {
140 JwtSimpleKeyConversionError::Encoding(format!(
141 "failed to encode PKCS#1 RSA private key: {e}"
142 ))
143 })
144}
145
146fn uint_ref<'a>(bytes: &'a [u8], field: &'static str) -> Result<UintRef<'a>> {
147 UintRef::new(bytes).map_err(|e| {
148 JwtSimpleKeyConversionError::Encoding(format!(
149 "invalid RSA integer '{field}' for PKCS#1 encoding: {e}"
150 ))
151 })
152}
153
154fn build_ed25519_jwt_simple_keypair_bytes(jwk: &Key) -> Result<Zeroizing<Vec<u8>>> {
155 let params = match jwk.params() {
156 KeyParams::Okp(p) => p,
157 _ => {
158 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
159 expected: "OKP",
160 actual: jwk.kty().as_str().to_string(),
161 });
162 }
163 };
164
165 if params.crv != OkpCurve::Ed25519 {
166 return Err(JwtSimpleKeyConversionError::CurveMismatch {
167 expected: "Ed25519",
168 actual: params.crv.as_str().to_string(),
169 });
170 }
171
172 let d = params
173 .d
174 .as_ref()
175 .ok_or(JwtSimpleKeyConversionError::MissingPrivateKey)?;
176 let d_bytes = d.as_bytes();
177
178 match d_bytes.len() {
179 32 => {
180 let mut bytes = Zeroizing::new(Vec::with_capacity(64));
181 bytes.extend_from_slice(d_bytes);
182 bytes.extend_from_slice(params.x.as_bytes());
183 Ok(bytes)
184 }
185 64 => {
191 let embedded_pub = &d_bytes[32..];
192 if embedded_pub != params.x.as_bytes() {
193 return Err(JwtSimpleKeyConversionError::Import(
194 "Ed25519 extended private key: embedded public key does not match JWK x parameter".into(),
195 ));
196 }
197 Ok(Zeroizing::new(d_bytes.to_vec()))
198 }
199 len => Err(JwtSimpleKeyConversionError::Import(format!(
200 "invalid Ed25519 private key length for jwt-simple conversion: expected 32 or 64 bytes, got {len}"
201 ))),
202 }
203}
204
205macro_rules! impl_rsa_public_key_conversion {
207 ($key_type:ty, $alg:expr) => {
208 impl TryFrom<&Key> for $key_type {
209 type Error = JwtSimpleKeyConversionError;
210
211 fn try_from(jwk: &Key) -> Result<Self> {
212 let params = match jwk.params() {
213 KeyParams::Rsa(p) => p,
214 _ => {
215 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
216 expected: "RSA",
217 actual: jwk.kty().as_str().to_string(),
218 });
219 }
220 };
221
222 validate_for_jwt_simple(jwk, &$alg, [KeyOperation::Verify])?;
223
224 let der = build_rsa_public_key_der(params)?;
225 <$key_type>::from_der(&der)
226 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
227 }
228 }
229
230 impl TryFrom<Key> for $key_type {
231 type Error = JwtSimpleKeyConversionError;
232
233 fn try_from(jwk: Key) -> Result<Self> {
234 <$key_type>::try_from(&jwk)
235 }
236 }
237 };
238}
239
240macro_rules! impl_rsa_key_pair_conversion {
242 ($key_type:ty, $alg:expr) => {
243 impl TryFrom<&Key> for $key_type {
244 type Error = JwtSimpleKeyConversionError;
245
246 fn try_from(jwk: &Key) -> Result<Self> {
247 let params = match jwk.params() {
248 KeyParams::Rsa(p) => p,
249 _ => {
250 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
251 expected: "RSA",
252 actual: jwk.kty().as_str().to_string(),
253 });
254 }
255 };
256
257 if !params.has_private_key() {
258 return Err(JwtSimpleKeyConversionError::MissingPrivateKey);
259 }
260
261 validate_for_jwt_simple(jwk, &$alg, [KeyOperation::Sign])?;
262
263 let der = build_rsa_private_key_der(params)?;
264 <$key_type>::from_der(&der)
265 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
266 }
267 }
268
269 impl TryFrom<Key> for $key_type {
270 type Error = JwtSimpleKeyConversionError;
271
272 fn try_from(jwk: Key) -> Result<Self> {
273 <$key_type>::try_from(&jwk)
274 }
275 }
276 };
277}
278
279impl_rsa_public_key_conversion!(RS256PublicKey, Algorithm::Rs256);
281impl_rsa_public_key_conversion!(RS384PublicKey, Algorithm::Rs384);
282impl_rsa_public_key_conversion!(RS512PublicKey, Algorithm::Rs512);
283impl_rsa_public_key_conversion!(PS256PublicKey, Algorithm::Ps256);
284impl_rsa_public_key_conversion!(PS384PublicKey, Algorithm::Ps384);
285impl_rsa_public_key_conversion!(PS512PublicKey, Algorithm::Ps512);
286
287impl_rsa_key_pair_conversion!(RS256KeyPair, Algorithm::Rs256);
288impl_rsa_key_pair_conversion!(RS384KeyPair, Algorithm::Rs384);
289impl_rsa_key_pair_conversion!(RS512KeyPair, Algorithm::Rs512);
290impl_rsa_key_pair_conversion!(PS256KeyPair, Algorithm::Ps256);
291impl_rsa_key_pair_conversion!(PS384KeyPair, Algorithm::Ps384);
292impl_rsa_key_pair_conversion!(PS512KeyPair, Algorithm::Ps512);
293
294macro_rules! impl_ec_public_key_conversion {
300 ($key_type:ty, $curve:expr, $curve_name:expr, $alg:expr) => {
301 impl TryFrom<&Key> for $key_type {
302 type Error = JwtSimpleKeyConversionError;
303
304 fn try_from(jwk: &Key) -> Result<Self> {
305 let params = match jwk.params() {
306 KeyParams::Ec(p) => p,
307 _ => {
308 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
309 expected: "EC",
310 actual: jwk.kty().as_str().to_string(),
311 });
312 }
313 };
314
315 if params.crv != $curve {
316 return Err(JwtSimpleKeyConversionError::CurveMismatch {
317 expected: $curve_name,
318 actual: params.crv.as_str().to_string(),
319 });
320 }
321
322 validate_for_jwt_simple(jwk, &$alg, [KeyOperation::Verify])?;
323
324 let bytes = params.to_uncompressed_point();
325 <$key_type>::from_bytes(&bytes)
326 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
327 }
328 }
329
330 impl TryFrom<Key> for $key_type {
331 type Error = JwtSimpleKeyConversionError;
332
333 fn try_from(jwk: Key) -> Result<Self> {
334 <$key_type>::try_from(&jwk)
335 }
336 }
337 };
338}
339
340macro_rules! impl_ec_key_pair_conversion {
342 ($key_type:ty, $curve:expr, $curve_name:expr, $alg:expr) => {
343 impl TryFrom<&Key> for $key_type {
344 type Error = JwtSimpleKeyConversionError;
345
346 fn try_from(jwk: &Key) -> Result<Self> {
347 let params = match jwk.params() {
348 KeyParams::Ec(p) => p,
349 _ => {
350 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
351 expected: "EC",
352 actual: jwk.kty().as_str().to_string(),
353 });
354 }
355 };
356
357 if params.crv != $curve {
358 return Err(JwtSimpleKeyConversionError::CurveMismatch {
359 expected: $curve_name,
360 actual: params.crv.as_str().to_string(),
361 });
362 }
363
364 validate_for_jwt_simple(jwk, &$alg, [KeyOperation::Sign])?;
365
366 let d = params
367 .d
368 .as_ref()
369 .ok_or(JwtSimpleKeyConversionError::MissingPrivateKey)?;
370
371 <$key_type>::from_bytes(d.as_bytes())
372 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
373 }
374 }
375
376 impl TryFrom<Key> for $key_type {
377 type Error = JwtSimpleKeyConversionError;
378
379 fn try_from(jwk: Key) -> Result<Self> {
380 <$key_type>::try_from(&jwk)
381 }
382 }
383 };
384}
385
386impl_ec_public_key_conversion!(ES256PublicKey, EcCurve::P256, "P-256", Algorithm::Es256);
388impl_ec_public_key_conversion!(ES384PublicKey, EcCurve::P384, "P-384", Algorithm::Es384);
389impl_ec_public_key_conversion!(
391 ES256kPublicKey,
392 EcCurve::Secp256k1,
393 "secp256k1",
394 Algorithm::Es256k
395);
396
397impl_ec_key_pair_conversion!(ES256KeyPair, EcCurve::P256, "P-256", Algorithm::Es256);
398impl_ec_key_pair_conversion!(ES384KeyPair, EcCurve::P384, "P-384", Algorithm::Es384);
399impl_ec_key_pair_conversion!(
400 ES256kKeyPair,
401 EcCurve::Secp256k1,
402 "secp256k1",
403 Algorithm::Es256k
404);
405
406impl TryFrom<&Key> for Ed25519PublicKey {
411 type Error = JwtSimpleKeyConversionError;
412
413 fn try_from(jwk: &Key) -> Result<Self> {
414 let params = match jwk.params() {
415 KeyParams::Okp(p) => p,
416 _ => {
417 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
418 expected: "OKP",
419 actual: jwk.kty().as_str().to_string(),
420 });
421 }
422 };
423
424 if params.crv != OkpCurve::Ed25519 {
425 return Err(JwtSimpleKeyConversionError::CurveMismatch {
426 expected: "Ed25519",
427 actual: params.crv.as_str().to_string(),
428 });
429 }
430
431 validate_for_jwt_simple(jwk, &Algorithm::Ed25519, [KeyOperation::Verify])?;
432
433 Ed25519PublicKey::from_bytes(params.x.as_bytes())
434 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
435 }
436}
437
438impl TryFrom<Key> for Ed25519PublicKey {
439 type Error = JwtSimpleKeyConversionError;
440
441 fn try_from(jwk: Key) -> Result<Self> {
442 Ed25519PublicKey::try_from(&jwk)
443 }
444}
445
446impl TryFrom<&Key> for Ed25519KeyPair {
447 type Error = JwtSimpleKeyConversionError;
448
449 fn try_from(jwk: &Key) -> Result<Self> {
450 validate_for_jwt_simple(jwk, &Algorithm::Ed25519, [KeyOperation::Sign])?;
451
452 let keypair_bytes = build_ed25519_jwt_simple_keypair_bytes(jwk)?;
453 Ed25519KeyPair::from_bytes(&keypair_bytes)
454 .map_err(|e| JwtSimpleKeyConversionError::Import(e.to_string()))
455 }
456}
457
458impl TryFrom<Key> for Ed25519KeyPair {
459 type Error = JwtSimpleKeyConversionError;
460
461 fn try_from(jwk: Key) -> Result<Self> {
462 Ed25519KeyPair::try_from(&jwk)
463 }
464}
465
466impl TryFrom<&Key> for HS256Key {
471 type Error = JwtSimpleKeyConversionError;
472
473 fn try_from(jwk: &Key) -> Result<Self> {
481 let params = match jwk.params() {
482 KeyParams::Symmetric(p) => p,
483 _ => {
484 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
485 expected: "oct",
486 actual: jwk.kty().as_str().to_string(),
487 });
488 }
489 };
490
491 validate_for_jwt_simple(
492 jwk,
493 &Algorithm::Hs256,
494 [KeyOperation::Sign, KeyOperation::Verify],
495 )?;
496
497 Ok(HS256Key::from_bytes(params.k.as_bytes()))
498 }
499}
500
501impl TryFrom<Key> for HS256Key {
502 type Error = JwtSimpleKeyConversionError;
503
504 fn try_from(jwk: Key) -> Result<Self> {
505 HS256Key::try_from(&jwk)
506 }
507}
508
509impl TryFrom<&Key> for HS384Key {
510 type Error = JwtSimpleKeyConversionError;
511
512 fn try_from(jwk: &Key) -> Result<Self> {
520 let params = match jwk.params() {
521 KeyParams::Symmetric(p) => p,
522 _ => {
523 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
524 expected: "oct",
525 actual: jwk.kty().as_str().to_string(),
526 });
527 }
528 };
529
530 validate_for_jwt_simple(
531 jwk,
532 &Algorithm::Hs384,
533 [KeyOperation::Sign, KeyOperation::Verify],
534 )?;
535
536 Ok(HS384Key::from_bytes(params.k.as_bytes()))
537 }
538}
539
540impl TryFrom<Key> for HS384Key {
541 type Error = JwtSimpleKeyConversionError;
542
543 fn try_from(jwk: Key) -> Result<Self> {
544 HS384Key::try_from(&jwk)
545 }
546}
547
548impl TryFrom<&Key> for HS512Key {
549 type Error = JwtSimpleKeyConversionError;
550
551 fn try_from(jwk: &Key) -> Result<Self> {
559 let params = match jwk.params() {
560 KeyParams::Symmetric(p) => p,
561 _ => {
562 return Err(JwtSimpleKeyConversionError::KeyTypeMismatch {
563 expected: "oct",
564 actual: jwk.kty().as_str().to_string(),
565 });
566 }
567 };
568
569 validate_for_jwt_simple(
570 jwk,
571 &Algorithm::Hs512,
572 [KeyOperation::Sign, KeyOperation::Verify],
573 )?;
574
575 Ok(HS512Key::from_bytes(params.k.as_bytes()))
576 }
577}
578
579impl TryFrom<Key> for HS512Key {
580 type Error = JwtSimpleKeyConversionError;
581
582 fn try_from(jwk: Key) -> Result<Self> {
583 HS512Key::try_from(&jwk)
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use super::*;
590 use crate::KeyMatcher;
591 use crate::SelectionError;
592 use crate::jwks::KeySet;
593
594 const RFC_RSA_PUBLIC_KEY: &str = r#"{
596 "kty": "RSA",
597 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
598 "e": "AQAB"
599 }"#;
600
601 const RFC_EC_PUBLIC_KEY: &str = r#"{
603 "kty": "EC",
604 "crv": "P-256",
605 "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
606 "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
607 }"#;
608
609 const RFC_EC_P256_PRIVATE_KEY: &str = r#"{
611 "kty": "EC",
612 "crv": "P-256",
613 "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
614 "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
615 "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"
616 }"#;
617
618 const SYMMETRIC_KEY: &str = r#"{
620 "kty": "oct",
621 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
622 }"#;
623
624 #[test]
625 fn test_rsa_public_key_conversion() {
626 let jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
627 let key: RS256PublicKey = (&jwk).try_into().unwrap();
628 assert!(!key.to_der().expect("to_der failed").is_empty());
630 }
631
632 #[test]
633 fn test_ec_public_key_conversion() {
634 let jwk: Key = serde_json::from_str(RFC_EC_PUBLIC_KEY).unwrap();
635 let key: ES256PublicKey = (&jwk).try_into().unwrap();
636 assert!(!key.to_bytes().is_empty());
637 }
638
639 #[test]
640 fn test_rsa_conversion_rejects_mismatched_token() {
641 let public_jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
642 let ec_private_jwk: Key = serde_json::from_str(RFC_EC_P256_PRIVATE_KEY).unwrap();
643
644 let public_key: RS256PublicKey = (&public_jwk).try_into().unwrap();
645 let ec_key_pair: ES256KeyPair = (&ec_private_jwk).try_into().unwrap();
646
647 let claims = Claims::create(Duration::from_hours(1)).with_subject("rsa-conversion-test");
648 let token = ec_key_pair.sign(claims).unwrap();
649
650 assert!(
651 public_key
652 .verify_token::<NoCustomClaims>(&token, None)
653 .is_err()
654 );
655
656 let mut tampered = token.clone();
657 tampered.push('x');
658 assert!(
659 public_key
660 .verify_token::<NoCustomClaims>(&tampered, None)
661 .is_err()
662 );
663 }
664
665 #[test]
666 fn test_ec_conversion_verifies_real_token() {
667 let private_jwk: Key = serde_json::from_str(RFC_EC_P256_PRIVATE_KEY).unwrap();
668 let public_jwk: Key = serde_json::from_str(RFC_EC_PUBLIC_KEY).unwrap();
669
670 let key_pair: ES256KeyPair = (&private_jwk).try_into().unwrap();
671 let public_key: ES256PublicKey = (&public_jwk).try_into().unwrap();
672
673 let claims = Claims::create(Duration::from_hours(1)).with_subject("ec-conversion-test");
674 let token = key_pair.sign(claims).unwrap();
675
676 assert!(
677 public_key
678 .verify_token::<NoCustomClaims>(&token, None)
679 .is_ok()
680 );
681
682 let mut tampered = token.clone();
683 tampered.push('x');
684 assert!(
685 public_key
686 .verify_token::<NoCustomClaims>(&tampered, None)
687 .is_err()
688 );
689 }
690
691 #[test]
692 fn test_symmetric_key_conversion() {
693 let jwk: Key = serde_json::from_str(SYMMETRIC_KEY).unwrap();
694
695 let hs256_key: HS256Key = (&jwk).try_into().unwrap();
696 let hs384_key: HS384Key = (&jwk).try_into().unwrap();
697 let hs512_key: HS512Key = (&jwk).try_into().unwrap();
698
699 let claims = Claims::create(Duration::from_hours(1)).with_subject("conversion-test");
700
701 let token_256 = hs256_key.authenticate(claims.clone()).unwrap();
702 assert!(
703 hs256_key
704 .verify_token::<NoCustomClaims>(&token_256, None)
705 .is_ok()
706 );
707
708 let token_384 = hs384_key.authenticate(claims.clone()).unwrap();
709 assert!(
710 hs384_key
711 .verify_token::<NoCustomClaims>(&token_384, None)
712 .is_ok()
713 );
714
715 let token_512 = hs512_key.authenticate(claims).unwrap();
716 assert!(
717 hs512_key
718 .verify_token::<NoCustomClaims>(&token_512, None)
719 .is_ok()
720 );
721
722 assert!(
723 hs256_key
724 .verify_token::<NoCustomClaims>(&token_384, None)
725 .is_err(),
726 "HS384 token should not verify with HS256 key"
727 );
728
729 let mut tampered = token_256.clone();
730 tampered.push('x');
731 assert!(
732 hs256_key
733 .verify_token::<NoCustomClaims>(&tampered, None)
734 .is_err(),
735 "Tampered token must fail verification"
736 );
737 }
738
739 #[test]
740 fn test_key_type_mismatch() {
741 let jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
742 let result: Result<ES256PublicKey> = (&jwk).try_into();
743 assert!(matches!(
744 result,
745 Err(JwtSimpleKeyConversionError::KeyTypeMismatch { .. })
746 ));
747 }
748
749 #[test]
750 fn test_curve_mismatch() {
751 let jwk: Key = serde_json::from_str(RFC_EC_PUBLIC_KEY).unwrap();
752 let result: Result<ES384PublicKey> = (&jwk).try_into();
753 assert!(matches!(
754 result,
755 Err(JwtSimpleKeyConversionError::CurveMismatch { .. })
756 ));
757 }
758
759 #[test]
760 fn test_missing_private_key() {
761 let jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
762 let result: Result<RS256KeyPair> = (&jwk).try_into();
763 assert!(matches!(
764 result,
765 Err(JwtSimpleKeyConversionError::MissingPrivateKey)
766 ));
767 }
768
769 #[test]
770 fn test_rsa_public_key_empty_params_no_panic() {
771 let json = r#"{"kty":"RSA","n":"","e":"AQAB"}"#;
773 let key: Key = serde_json::from_str(json).unwrap();
774 let result: Result<RS256PublicKey> = (&key).try_into();
775 assert!(matches!(
776 result,
777 Err(JwtSimpleKeyConversionError::InvalidKey(_))
778 ));
779 }
780
781 #[test]
782 fn test_tryfrom_conversions() {
783 let jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
784 assert!(RS256PublicKey::try_from(&jwk).is_ok());
785 assert!(RS384PublicKey::try_from(&jwk).is_ok());
786 assert!(RS512PublicKey::try_from(&jwk).is_ok());
787 assert!(PS256PublicKey::try_from(&jwk).is_ok());
788
789 let jwk: Key = serde_json::from_str(RFC_EC_PUBLIC_KEY).unwrap();
790 assert!(ES256PublicKey::try_from(&jwk).is_ok());
791
792 let jwk: Key = serde_json::from_str(SYMMETRIC_KEY).unwrap();
793 assert!(HS256Key::try_from(&jwk).is_ok());
794 }
795
796 #[test]
797 fn test_hs256_conversion_rejects_weak_key_without_alg_field() {
798 let weak_hs_key_json = r#"{
799 "kty": "oct",
800 "k": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
801 }"#;
802
803 let jwk: Key = serde_json::from_str(weak_hs_key_json).unwrap();
804 let result: Result<HS256Key> = (&jwk).try_into();
805 assert!(matches!(
806 result,
807 Err(JwtSimpleKeyConversionError::IncompatibleKey(
808 IncompatibleKeyError::InsufficientKeyStrength { .. }
809 ))
810 ));
811 }
812
813 #[test]
814 fn test_rs256_conversion_rejects_weak_rsa_without_alg_field() {
815 let weak_rsa_json = r#"{
816 "kty": "RSA",
817 "n": "sXchhHu5Mdu8J-4n8x66I8f32xNkoTfEhQ",
818 "e": "AQAB"
819 }"#;
820
821 let jwk: Key = serde_json::from_str(weak_rsa_json).unwrap();
822 let result: Result<RS256PublicKey> = (&jwk).try_into();
823 assert!(matches!(
824 result,
825 Err(JwtSimpleKeyConversionError::IncompatibleKey(
826 IncompatibleKeyError::InsufficientKeyStrength { .. }
827 ))
828 ));
829 }
830
831 #[test]
832 fn test_rs256_conversion_rejects_declared_algorithm_mismatch() {
833 let jwk: Key = serde_json::from_str(RFC_RSA_PUBLIC_KEY).unwrap();
834 let jwk = jwk.with_alg(Algorithm::Ps256);
835
836 let result: Result<RS256PublicKey> = (&jwk).try_into();
837 assert!(matches!(
838 result,
839 Err(JwtSimpleKeyConversionError::IncompatibleKey(
840 IncompatibleKeyError::AlgorithmMismatch { .. }
841 ))
842 ));
843 }
844
845 #[test]
846 fn test_rs256_public_conversion_rejects_encryption_use() {
847 let json = r#"{
848 "kty": "RSA",
849 "use": "enc",
850 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
851 "e": "AQAB"
852 }"#;
853
854 let jwk: Key = serde_json::from_str(json).unwrap();
855 let result: Result<RS256PublicKey> = (&jwk).try_into();
856 assert!(matches!(
857 result,
858 Err(JwtSimpleKeyConversionError::IncompatibleKey(
859 IncompatibleKeyError::OperationNotPermitted { .. }
860 ))
861 ));
862 }
863
864 #[test]
865 fn test_select_verify_key_strict_for_jwt_simple_flow() {
866 let json = r#"{"keys": [
867 {"kty": "RSA", "kid": "rsa-verify", "use": "sig", "alg": "RS256", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB"},
868 {"kty": "EC", "kid": "ec-verify", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}
869 ]}"#;
870
871 let jwks: KeySet = serde_json::from_str(json).unwrap();
872 let key = jwks
873 .selector(&[Algorithm::Rs256])
874 .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Rs256).with_kid("rsa-verify"))
875 .unwrap();
876
877 assert_eq!(key.kid(), Some("rsa-verify"));
878
879 let err = jwks
880 .selector(&[Algorithm::Rs256])
881 .select(KeyMatcher::new(KeyOperation::Verify, Algorithm::Es256).with_kid("rsa-verify"))
882 .unwrap_err();
883 assert!(matches!(err, SelectionError::AlgorithmNotAllowed));
884 }
885
886 #[test]
887 fn test_select_signing_key_strict_for_jwt_simple_flow() {
888 let json = r#"{"keys": [
889 {"kty": "EC", "kid": "ec-sign", "use": "sig", "alg": "ES256", "crv": "P-256", "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
890 ]}"#;
891
892 let jwks: KeySet = serde_json::from_str(json).unwrap();
893 let key = jwks
894 .selector(&[])
895 .select(KeyMatcher::new(KeyOperation::Sign, Algorithm::Es256).with_kid("ec-sign"))
896 .unwrap();
897
898 assert_eq!(key.kid(), Some("ec-sign"));
899 }
900
901 #[test]
902 fn test_rs256_public_conversion_rejects_verify_missing_in_key_ops() {
903 let json = r#"{
904 "kty": "RSA",
905 "key_ops": ["encrypt"],
906 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
907 "e": "AQAB"
908 }"#;
909
910 let jwk: Key = serde_json::from_str(json).unwrap();
911 let result: Result<RS256PublicKey> = (&jwk).try_into();
912 assert!(matches!(
913 result,
914 Err(JwtSimpleKeyConversionError::IncompatibleKey(
915 IncompatibleKeyError::OperationNotPermitted { .. }
916 ))
917 ));
918 }
919
920 #[test]
921 fn test_rs256_key_pair_conversion_rejects_encryption_use() {
922 let json = r#"{
923 "kty": "RSA",
924 "use": "enc",
925 "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
926 "e": "AQAB",
927 "d": "X4cTteJY_gn4FYPsXB8rd5Qw9Y8Q8fN4EuM4fM9x2s8"
928 }"#;
929
930 let jwk: Key = serde_json::from_str(json).unwrap();
931 let result: Result<RS256KeyPair> = (&jwk).try_into();
932 assert!(matches!(
933 result,
934 Err(JwtSimpleKeyConversionError::IncompatibleKey(
935 crate::error::IncompatibleKeyError::OperationNotPermitted { .. }
936 ))
937 ));
938 }
939
940 #[test]
941 fn test_ed25519_key_pair_conversion_rejects_verify_only_key_ops() {
942 let json = r#"{
943 "kty": "OKP",
944 "crv": "Ed25519",
945 "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
946 "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
947 "key_ops": ["verify"]
948 }"#;
949
950 let jwk: Key = serde_json::from_str(json).unwrap();
951 let result: Result<Ed25519KeyPair> = (&jwk).try_into();
952 assert!(matches!(
953 result,
954 Err(JwtSimpleKeyConversionError::IncompatibleKey(
955 IncompatibleKeyError::OperationNotPermitted { .. }
956 ))
957 ));
958 }
959
960 #[test]
961 fn test_ed25519_key_pair_conversion_accepts_seed_form_private_key() {
962 let json = r#"{
963 "kty": "OKP",
964 "crv": "Ed25519",
965 "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
966 "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A"
967 }"#;
968
969 let jwk: Key = serde_json::from_str(json).unwrap();
970 let key_pair: Ed25519KeyPair = (&jwk).try_into().unwrap();
971 let public_key = key_pair.public_key();
972 let expected_x = match jwk.params() {
973 KeyParams::Okp(params) => params.x.as_bytes(),
974 _ => unreachable!("test fixture must be an OKP key"),
975 };
976
977 assert_eq!(public_key.to_bytes(), expected_x);
978 }
979
980 #[test]
981 fn test_hs256_conversion_rejects_sign_only_key_ops() {
982 let json = r#"{
983 "kty": "oct",
984 "key_ops": ["sign"],
985 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
986 }"#;
987
988 let jwk: Key = serde_json::from_str(json).unwrap();
989 let result: Result<HS256Key> = (&jwk).try_into();
990 assert!(matches!(
991 result,
992 Err(JwtSimpleKeyConversionError::IncompatibleKey(
993 IncompatibleKeyError::OperationNotPermitted { .. }
994 ))
995 ));
996 }
997
998 #[test]
999 fn test_hs256_conversion_rejects_verify_only_key_ops() {
1000 let json = r#"{
1001 "kty": "oct",
1002 "key_ops": ["verify"],
1003 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1004 }"#;
1005
1006 let jwk: Key = serde_json::from_str(json).unwrap();
1007 let result: Result<HS256Key> = (&jwk).try_into();
1008 assert!(matches!(
1009 result,
1010 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1011 IncompatibleKeyError::OperationNotPermitted { .. }
1012 ))
1013 ));
1014 }
1015
1016 #[test]
1017 fn test_hs256_conversion_accepts_sign_and_verify_key_ops() {
1018 let json = r#"{
1019 "kty": "oct",
1020 "key_ops": ["sign", "verify"],
1021 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1022 }"#;
1023
1024 let jwk: Key = serde_json::from_str(json).unwrap();
1025 let result: Result<HS256Key> = (&jwk).try_into();
1026 assert!(matches!(result, Ok(_)));
1027 }
1028
1029 #[test]
1030 fn test_hs256_conversion_rejects_encryption_use() {
1031 let json = r#"{
1032 "kty": "oct",
1033 "use": "enc",
1034 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1035 }"#;
1036
1037 let jwk: Key = serde_json::from_str(json).unwrap();
1038 let result: Result<HS256Key> = (&jwk).try_into();
1039 assert!(matches!(
1040 result,
1041 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1042 IncompatibleKeyError::OperationNotPermitted { .. }
1043 ))
1044 ));
1045 }
1046
1047 #[test]
1048 fn test_hs384_conversion_rejects_sign_only_key_ops() {
1049 let json = r#"{
1050 "kty": "oct",
1051 "key_ops": ["sign"],
1052 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1053 }"#;
1054
1055 let jwk: Key = serde_json::from_str(json).unwrap();
1056 let result: Result<HS384Key> = (&jwk).try_into();
1057 assert!(matches!(
1058 result,
1059 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1060 IncompatibleKeyError::OperationNotPermitted { .. }
1061 ))
1062 ));
1063 }
1064
1065 #[test]
1066 fn test_hs512_conversion_rejects_verify_only_key_ops() {
1067 let json = r#"{
1068 "kty": "oct",
1069 "key_ops": ["verify"],
1070 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1071 }"#;
1072
1073 let jwk: Key = serde_json::from_str(json).unwrap();
1074 let result: Result<HS512Key> = (&jwk).try_into();
1075 assert!(matches!(
1076 result,
1077 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1078 IncompatibleKeyError::OperationNotPermitted { .. }
1079 ))
1080 ));
1081 }
1082
1083 #[test]
1084 fn test_hs384_conversion_rejects_verify_only_key_ops() {
1085 let json = r#"{
1086 "kty": "oct",
1087 "key_ops": ["verify"],
1088 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1089 }"#;
1090
1091 let jwk: Key = serde_json::from_str(json).unwrap();
1092 let result: Result<HS384Key> = (&jwk).try_into();
1093 assert!(matches!(
1094 result,
1095 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1096 IncompatibleKeyError::OperationNotPermitted { .. }
1097 ))
1098 ));
1099 }
1100
1101 #[test]
1102 fn test_hs512_conversion_rejects_sign_only_key_ops() {
1103 let json = r#"{
1104 "kty": "oct",
1105 "key_ops": ["sign"],
1106 "k": "AyM32w-8O0TGsGDYX0MlWy-9XQP-xrryrP7gkXKfY5WhoLxmT3fzfVr7LXqgDDFSfowWBY-u6bSH5f9kBZ_n7Q"
1107 }"#;
1108
1109 let jwk: Key = serde_json::from_str(json).unwrap();
1110 let result: Result<HS512Key> = (&jwk).try_into();
1111 assert!(matches!(
1112 result,
1113 Err(JwtSimpleKeyConversionError::IncompatibleKey(
1114 IncompatibleKeyError::OperationNotPermitted { .. }
1115 ))
1116 ));
1117 }
1118
1119 #[test]
1120 fn test_validation_error_preserves_source_chain() {
1121 let weak_hs_key_json = r#"{
1122 "kty": "oct",
1123 "k": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
1124 }"#;
1125
1126 let jwk: Key = serde_json::from_str(weak_hs_key_json).unwrap();
1127 let err = HS256Key::try_from(&jwk).unwrap_err();
1128
1129 let JwtSimpleKeyConversionError::IncompatibleKey(ref inner) = err else {
1130 panic!("expected IncompatibleKey, got {err}");
1131 };
1132 assert!(
1133 matches!(inner, IncompatibleKeyError::InsufficientKeyStrength { .. }),
1134 "expected InsufficientKeyStrength, got {inner}"
1135 );
1136 }
1137}