Skip to main content

jwk_simple/integrations/
jwt_simple.rs

1//! jwt-simple key conversion implementations.
2//!
3//! # Security Note — RSA Timing Side-Channel (RUSTSEC-2023-0071)
4//!
5//! The underlying [`rsa`] crate (used by `jwt-simple`) does not perform
6//! RSA private-key operations in constant time. This means RSA **signing**
7//! conversions produced by this module may be vulnerable to timing
8//! side-channel attacks that could leak private key material.
9//!
10//! This library is primarily designed for key parsing, selection, and
11//! **verification**, where the timing issue is not relevant (verification
12//! uses the public exponent). If you use these conversions for RSA signing
13//! in a threat model where timing attacks are a concern, evaluate whether
14//! the risk is acceptable for your deployment.
15//!
16//! See [RUSTSEC-2023-0071](https://rustsec.org/advisories/RUSTSEC-2023-0071)
17//! for details. No upstream fix is currently available.
18
19use 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
47// ============================================================================
48// RSA Key Conversions
49// ============================================================================
50
51/// Builds a DER-encoded RSA public key from JWK parameters.
52///
53/// The DER format used is PKCS#1 RSAPublicKey:
54/// ```text
55/// RSAPublicKey ::= SEQUENCE {
56///     modulus           INTEGER,  -- n
57///     publicExponent    INTEGER   -- e
58/// }
59/// ```
60fn 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
76/// Builds a DER-encoded RSA private key from JWK parameters.
77///
78/// The DER format used is PKCS#1 RSAPrivateKey:
79/// ```text
80/// RSAPrivateKey ::= SEQUENCE {
81///     version           Version,
82///     modulus           INTEGER,  -- n
83///     publicExponent    INTEGER,  -- e
84///     privateExponent   INTEGER,  -- d
85///     prime1            INTEGER,  -- p
86///     prime2            INTEGER,  -- q
87///     exponent1         INTEGER,  -- dp (d mod p-1)
88///     exponent2         INTEGER,  -- dq (d mod q-1)
89///     coefficient       INTEGER,  -- qi (q^-1 mod p)
90/// }
91/// ```
92fn build_rsa_private_key_der(params: &RsaParams) -> Result<Zeroizing<Vec<u8>>> {
93    // Multi-prime RSA keys (with `oth` parameter) require PKCS#1 version 1
94    // encoding with otherPrimeInfos, which is not implemented. Reject them
95    // explicitly rather than producing a structurally incorrect two-prime DER.
96    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        // The 64-byte "extended" form (seed || public-key) is not defined by
186        // RFC 8037 (which specifies `d` as the 32-byte seed only) but is used
187        // by some implementations (e.g. libsodium). We accept it for
188        // interoperability but validate that the embedded public key matches
189        // the JWK's `x` parameter to prevent silent key-mismatch confusion.
190        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
205// Macro to implement RSA public key conversions
206macro_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
240// Macro to implement RSA key pair conversions
241macro_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
279// Implement conversions for all RSA types
280impl_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
294// ============================================================================
295// EC Key Conversions
296// ============================================================================
297
298// Macro to implement EC public key conversions
299macro_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
340// Macro to implement EC key pair conversions
341macro_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
386// Implement EC conversions
387impl_ec_public_key_conversion!(ES256PublicKey, EcCurve::P256, "P-256", Algorithm::Es256);
388impl_ec_public_key_conversion!(ES384PublicKey, EcCurve::P384, "P-384", Algorithm::Es384);
389// Note: ES512 uses P-521, but jwt-simple may not support it
390impl_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
406// ============================================================================
407// EdDSA Key Conversions
408// ============================================================================
409
410impl 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
466// ============================================================================
467// Symmetric Key Conversions
468// ============================================================================
469
470impl TryFrom<&Key> for HS256Key {
471    type Error = JwtSimpleKeyConversionError;
472
473    /// # Errors
474    ///
475    /// Returns [`JwtSimpleKeyConversionError::IncompatibleKey`] if `key_ops` is
476    /// present but does not include both [`KeyOperation::Sign`] and
477    /// [`KeyOperation::Verify`]. `HS256Key` in `jwt-simple` is bidirectional;
478    /// callers that need one-directional HMAC usage should remove `key_ops`
479    /// before converting.
480    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    /// # Errors
513    ///
514    /// Returns [`JwtSimpleKeyConversionError::IncompatibleKey`] if `key_ops` is
515    /// present but does not include both [`KeyOperation::Sign`] and
516    /// [`KeyOperation::Verify`]. `HS384Key` in `jwt-simple` is bidirectional;
517    /// callers that need one-directional HMAC usage should remove `key_ops`
518    /// before converting.
519    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    /// # Errors
552    ///
553    /// Returns [`JwtSimpleKeyConversionError::IncompatibleKey`] if `key_ops` is
554    /// present but does not include both [`KeyOperation::Sign`] and
555    /// [`KeyOperation::Verify`]. `HS512Key` in `jwt-simple` is bidirectional;
556    /// callers that need one-directional HMAC usage should remove `key_ops`
557    /// before converting.
558    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    // Test RSA public key from RFC 7517 Appendix A.1
595    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    // Test EC P-256 public key from RFC 7517 Appendix A.1
602    const RFC_EC_PUBLIC_KEY: &str = r#"{
603        "kty": "EC",
604        "crv": "P-256",
605        "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
606        "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
607    }"#;
608
609    // Test EC P-256 private key from RFC 7517 Appendix A.2
610    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    // Test symmetric key
619    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        // Just verify it doesn't panic - the key was successfully converted
629        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        // A JWK with empty base64url strings should produce an error, not a panic
772        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}