Skip to main content

jwk_simple/
jwk.rs

1//! JSON Web Key (JWK) types as defined in RFC 7517.
2//!
3//! This module provides the core [`Key`] type and related enums for
4//! representing cryptographic keys in JSON format.
5//!
6//! # Supported Key Types
7//!
8//! - **RSA** (`kty: "RSA"`) - RSA public and private keys
9//! - **EC** (`kty: "EC"`) - Elliptic Curve keys (P-256, P-384, P-521, secp256k1)
10//! - **oct** (`kty: "oct"`) - Symmetric keys (HMAC, AES)
11//! - **OKP** (`kty: "OKP"`) - Octet Key Pairs (Ed25519, Ed448, X25519, X448)
12//!
13//! # Examples
14//!
15//! Parse a JWK from JSON:
16//!
17//! ```
18//! use jwk_simple::Key;
19//!
20//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
21//! let json = r#"{
22//!     "kty": "RSA",
23//!     "kid": "my-key-id",
24//!     "use": "sig",
25//!     "alg": "RS256",
26//!     "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
27//!     "e": "AQAB"
28//! }"#;
29//!
30//! let jwk: Key = serde_json::from_str(json)?;
31//! assert_eq!(jwk.kid(), Some("my-key-id"));
32//! # Ok(())
33//! # }
34//! ```
35
36mod ec;
37mod okp;
38mod rsa;
39mod symmetric;
40pub(crate) mod thumbprint;
41
42pub use ec::{EcCurve, EcParams};
43pub use okp::{OkpCurve, OkpParams};
44pub use rsa::{RsaOtherPrime, RsaParams, RsaParamsBuilder};
45pub use symmetric::SymmetricParams;
46
47use std::collections::HashSet;
48use std::convert::Infallible;
49use std::fmt::{self, Debug, Display};
50use std::hash::{Hash, Hasher};
51use std::str::FromStr;
52
53use base64ct::{Base64UrlUnpadded, Encoding};
54use serde::{Deserialize, Deserializer, Serialize, Serializer};
55use sha1::{Digest as Sha1Digest, Sha1};
56use sha2::Sha256;
57use url::Url;
58use x509_parser::prelude::parse_x509_certificate;
59use zeroize::{Zeroize, ZeroizeOnDrop};
60
61use crate::encoding::Base64UrlBytes;
62use crate::error::{Error, IncompatibleKeyError, InvalidKeyError, ParseError, Result};
63
64/// Key type identifier (RFC 7517 Section 4.1).
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66#[non_exhaustive]
67pub enum KeyType {
68    /// RSA key type.
69    Rsa,
70    /// Elliptic Curve key type.
71    Ec,
72    /// Symmetric key type (octet sequence).
73    Symmetric,
74    /// Octet Key Pair (Edwards/Montgomery curves).
75    Okp,
76}
77
78impl KeyType {
79    /// Returns the key type as the JWK `kty` string.
80    pub fn as_str(&self) -> &'static str {
81        match self {
82            KeyType::Rsa => "RSA",
83            KeyType::Ec => "EC",
84            KeyType::Symmetric => "oct",
85            KeyType::Okp => "OKP",
86        }
87    }
88}
89
90impl Display for KeyType {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(f, "{}", self.as_str())
93    }
94}
95
96impl FromStr for KeyType {
97    type Err = Error;
98
99    fn from_str(s: &str) -> Result<Self> {
100        match s {
101            "RSA" => Ok(KeyType::Rsa),
102            "EC" => Ok(KeyType::Ec),
103            "oct" => Ok(KeyType::Symmetric),
104            "OKP" => Ok(KeyType::Okp),
105            _ => Err(Error::Parse(ParseError::UnknownKeyType(s.to_string()))),
106        }
107    }
108}
109
110impl Serialize for KeyType {
111    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
112    where
113        S: Serializer,
114    {
115        serializer.serialize_str(self.as_str())
116    }
117}
118
119impl<'de> Deserialize<'de> for KeyType {
120    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
121    where
122        D: Deserializer<'de>,
123    {
124        String::deserialize(deserializer)?
125            .parse()
126            .map_err(serde::de::Error::custom)
127    }
128}
129
130/// Key use (RFC 7517 Section 4.2).
131///
132/// Per RFC 7517, the "use" parameter is intended to identify the intended use
133/// of the public key. While "sig" and "enc" are the defined values, the
134/// specification allows for other values via registration or collision-resistant
135/// names for private use.
136#[derive(Debug, Clone, PartialEq, Eq, Hash)]
137#[non_exhaustive]
138pub enum KeyUse {
139    /// Key is used for signatures.
140    Signature,
141    /// Key is used for encryption.
142    Encryption,
143    /// Unknown or private-use key use.
144    ///
145    /// Per RFC 7517, key use values should either be registered in IANA
146    /// or be a collision-resistant name. Unknown values are preserved.
147    Unknown(String),
148}
149
150impl KeyUse {
151    /// Returns the key use as a string.
152    pub fn as_str(&self) -> &str {
153        match self {
154            KeyUse::Signature => "sig",
155            KeyUse::Encryption => "enc",
156            KeyUse::Unknown(s) => s.as_str(),
157        }
158    }
159}
160
161impl Display for KeyUse {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        write!(f, "{}", self.as_str())
164    }
165}
166
167impl From<&str> for KeyUse {
168    /// Parses a key use string.
169    ///
170    /// Per RFC 7517, unknown key use values are accepted and stored as `Unknown`.
171    fn from(s: &str) -> Self {
172        match s {
173            "sig" => KeyUse::Signature,
174            "enc" => KeyUse::Encryption,
175            _ => KeyUse::Unknown(s.to_string()),
176        }
177    }
178}
179
180impl FromStr for KeyUse {
181    type Err = Infallible;
182
183    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
184        Ok(Self::from(s))
185    }
186}
187
188impl Serialize for KeyUse {
189    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
190    where
191        S: Serializer,
192    {
193        serializer.serialize_str(self.as_str())
194    }
195}
196
197impl<'de> Deserialize<'de> for KeyUse {
198    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
199    where
200        D: Deserializer<'de>,
201    {
202        Ok(KeyUse::from(String::deserialize(deserializer)?.as_str()))
203    }
204}
205
206/// Key operation (RFC 7517 Section 4.3).
207///
208/// Per RFC 7517, unknown key operation values should be accepted to support
209/// collision-resistant names and future extensions.
210#[derive(Debug, Clone, PartialEq, Eq, Hash)]
211#[non_exhaustive]
212pub enum KeyOperation {
213    /// Compute digital signature or MAC.
214    Sign,
215    /// Verify digital signature or MAC.
216    Verify,
217    /// Encrypt content.
218    Encrypt,
219    /// Decrypt content.
220    Decrypt,
221    /// Encrypt key.
222    WrapKey,
223    /// Decrypt key.
224    UnwrapKey,
225    /// Derive key.
226    DeriveKey,
227    /// Derive bits not to be used as a key.
228    DeriveBits,
229    /// Unknown or private-use key operation.
230    ///
231    /// Per RFC 7517, key operation values should either be registered in IANA
232    /// or be a collision-resistant name. Unknown values are preserved.
233    Unknown(String),
234}
235
236impl KeyOperation {
237    /// Returns the key operation as a string.
238    pub fn as_str(&self) -> &str {
239        match self {
240            KeyOperation::Sign => "sign",
241            KeyOperation::Verify => "verify",
242            KeyOperation::Encrypt => "encrypt",
243            KeyOperation::Decrypt => "decrypt",
244            KeyOperation::WrapKey => "wrapKey",
245            KeyOperation::UnwrapKey => "unwrapKey",
246            KeyOperation::DeriveKey => "deriveKey",
247            KeyOperation::DeriveBits => "deriveBits",
248            KeyOperation::Unknown(s) => s.as_str(),
249        }
250    }
251
252    /// Returns `true` if this is an unknown/unrecognized key operation.
253    pub fn is_unknown(&self) -> bool {
254        matches!(self, KeyOperation::Unknown(_))
255    }
256}
257
258impl Display for KeyOperation {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        write!(f, "{}", self.as_str())
261    }
262}
263
264impl From<&str> for KeyOperation {
265    /// Parses a key operation string.
266    ///
267    /// Per RFC 7517, unknown key operation values are accepted and stored as `Unknown`.
268    fn from(s: &str) -> Self {
269        match s {
270            "sign" => KeyOperation::Sign,
271            "verify" => KeyOperation::Verify,
272            "encrypt" => KeyOperation::Encrypt,
273            "decrypt" => KeyOperation::Decrypt,
274            "wrapKey" => KeyOperation::WrapKey,
275            "unwrapKey" => KeyOperation::UnwrapKey,
276            "deriveKey" => KeyOperation::DeriveKey,
277            "deriveBits" => KeyOperation::DeriveBits,
278            _ => KeyOperation::Unknown(s.to_string()),
279        }
280    }
281}
282
283impl FromStr for KeyOperation {
284    type Err = Infallible;
285
286    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
287        Ok(Self::from(s))
288    }
289}
290
291impl Serialize for KeyOperation {
292    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
293    where
294        S: Serializer,
295    {
296        serializer.serialize_str(self.as_str())
297    }
298}
299
300impl<'de> Deserialize<'de> for KeyOperation {
301    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
302    where
303        D: Deserializer<'de>,
304    {
305        Ok(KeyOperation::from(
306            String::deserialize(deserializer)?.as_str(),
307        ))
308    }
309}
310
311/// JWK Algorithm (RFC 7518).
312///
313/// Per RFC 7517, unknown algorithm values should be accepted and preserved
314/// to allow for future extensions and private-use algorithms.
315#[derive(Debug, Clone, PartialEq, Eq, Hash)]
316#[non_exhaustive]
317pub enum Algorithm {
318    // HMAC
319    /// HMAC using SHA-256.
320    Hs256,
321    /// HMAC using SHA-384.
322    Hs384,
323    /// HMAC using SHA-512.
324    Hs512,
325
326    // RSA PKCS#1
327    /// RSASSA-PKCS1-v1_5 using SHA-256.
328    Rs256,
329    /// RSASSA-PKCS1-v1_5 using SHA-384.
330    Rs384,
331    /// RSASSA-PKCS1-v1_5 using SHA-512.
332    Rs512,
333
334    // RSA-PSS
335    /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256.
336    Ps256,
337    /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384.
338    Ps384,
339    /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512.
340    Ps512,
341
342    // ECDSA
343    /// ECDSA using P-256 and SHA-256.
344    Es256,
345    /// ECDSA using P-384 and SHA-384.
346    Es384,
347    /// ECDSA using P-521 and SHA-512.
348    Es512,
349    /// ECDSA using secp256k1 and SHA-256.
350    Es256k,
351
352    // EdDSA
353    /// Edwards-curve Digital Signature Algorithm (legacy JOSE identifier).
354    ///
355    /// RFC 9864 deprecates this generic identifier in favor of the fully
356    /// specified [`Algorithm::Ed25519`] and [`Algorithm::Ed448`] values.
357    EdDsa,
358    /// Ed25519 signature algorithm (RFC 9864 JOSE algorithm identifier).
359    Ed25519,
360    /// Ed448 signature algorithm (RFC 9864 JOSE algorithm identifier).
361    Ed448,
362
363    // RSA Encryption
364    /// RSAES-OAEP using default parameters.
365    RsaOaep,
366    /// RSAES-OAEP using SHA-256 and MGF1 with SHA-256.
367    RsaOaep256,
368    /// RSAES-OAEP using SHA-384 and MGF1 with SHA-384.
369    RsaOaep384,
370    /// RSAES-OAEP using SHA-512 and MGF1 with SHA-512.
371    RsaOaep512,
372    /// RSAES-PKCS1-v1_5.
373    Rsa1_5,
374
375    // AES Key Wrap
376    /// AES Key Wrap with 128-bit key.
377    A128kw,
378    /// AES Key Wrap with 192-bit key.
379    A192kw,
380    /// AES Key Wrap with 256-bit key.
381    A256kw,
382
383    // Direct
384    /// Direct use of a shared symmetric key.
385    Dir,
386
387    // ECDH-ES
388    /// ECDH-ES using Concat KDF.
389    EcdhEs,
390    /// ECDH-ES using Concat KDF and A128KW wrapping.
391    EcdhEsA128kw,
392    /// ECDH-ES using Concat KDF and A192KW wrapping.
393    EcdhEsA192kw,
394    /// ECDH-ES using Concat KDF and A256KW wrapping.
395    EcdhEsA256kw,
396
397    // AES-GCM Key Wrap
398    /// Key wrapping with AES-GCM using 128-bit key.
399    A128gcmkw,
400    /// Key wrapping with AES-GCM using 192-bit key.
401    A192gcmkw,
402    /// Key wrapping with AES-GCM using 256-bit key.
403    A256gcmkw,
404
405    // PBES2
406    /// PBES2 with HMAC SHA-256 and A128KW wrapping.
407    Pbes2Hs256A128kw,
408    /// PBES2 with HMAC SHA-384 and A192KW wrapping.
409    Pbes2Hs384A192kw,
410    /// PBES2 with HMAC SHA-512 and A256KW wrapping.
411    Pbes2Hs512A256kw,
412
413    // Content Encryption
414    /// AES-CBC with HMAC SHA-256 using 128-bit keys.
415    A128cbcHs256,
416    /// AES-CBC with HMAC SHA-384 using 192-bit keys.
417    A192cbcHs384,
418    /// AES-CBC with HMAC SHA-512 using 256-bit keys.
419    A256cbcHs512,
420    /// AES-GCM using 128-bit key.
421    A128gcm,
422    /// AES-GCM using 192-bit key.
423    A192gcm,
424    /// AES-GCM using 256-bit key.
425    A256gcm,
426
427    /// Unknown or private-use algorithm.
428    ///
429    /// Per RFC 7517, implementations should accept unknown algorithm values
430    /// to support future extensions and collision-resistant names.
431    Unknown(String),
432}
433
434impl Algorithm {
435    /// Returns the algorithm as a string.
436    ///
437    /// For unknown algorithms, this returns the original algorithm name.
438    pub fn as_str(&self) -> &str {
439        match self {
440            Algorithm::Hs256 => "HS256",
441            Algorithm::Hs384 => "HS384",
442            Algorithm::Hs512 => "HS512",
443            Algorithm::Rs256 => "RS256",
444            Algorithm::Rs384 => "RS384",
445            Algorithm::Rs512 => "RS512",
446            Algorithm::Ps256 => "PS256",
447            Algorithm::Ps384 => "PS384",
448            Algorithm::Ps512 => "PS512",
449            Algorithm::Es256 => "ES256",
450            Algorithm::Es384 => "ES384",
451            Algorithm::Es512 => "ES512",
452            Algorithm::Es256k => "ES256K",
453            Algorithm::EdDsa => "EdDSA",
454            Algorithm::Ed25519 => "Ed25519",
455            Algorithm::Ed448 => "Ed448",
456            Algorithm::RsaOaep => "RSA-OAEP",
457            Algorithm::RsaOaep256 => "RSA-OAEP-256",
458            Algorithm::RsaOaep384 => "RSA-OAEP-384",
459            Algorithm::RsaOaep512 => "RSA-OAEP-512",
460            Algorithm::Rsa1_5 => "RSA1_5",
461            Algorithm::A128kw => "A128KW",
462            Algorithm::A192kw => "A192KW",
463            Algorithm::A256kw => "A256KW",
464            Algorithm::Dir => "dir",
465            Algorithm::EcdhEs => "ECDH-ES",
466            Algorithm::EcdhEsA128kw => "ECDH-ES+A128KW",
467            Algorithm::EcdhEsA192kw => "ECDH-ES+A192KW",
468            Algorithm::EcdhEsA256kw => "ECDH-ES+A256KW",
469            Algorithm::A128gcmkw => "A128GCMKW",
470            Algorithm::A192gcmkw => "A192GCMKW",
471            Algorithm::A256gcmkw => "A256GCMKW",
472            Algorithm::Pbes2Hs256A128kw => "PBES2-HS256+A128KW",
473            Algorithm::Pbes2Hs384A192kw => "PBES2-HS384+A192KW",
474            Algorithm::Pbes2Hs512A256kw => "PBES2-HS512+A256KW",
475            Algorithm::A128cbcHs256 => "A128CBC-HS256",
476            Algorithm::A192cbcHs384 => "A192CBC-HS384",
477            Algorithm::A256cbcHs512 => "A256CBC-HS512",
478            Algorithm::A128gcm => "A128GCM",
479            Algorithm::A192gcm => "A192GCM",
480            Algorithm::A256gcm => "A256GCM",
481            Algorithm::Unknown(s) => s.as_str(),
482        }
483    }
484
485    /// Returns `true` if this is an unknown/unrecognized algorithm.
486    pub fn is_unknown(&self) -> bool {
487        matches!(self, Algorithm::Unknown(_))
488    }
489
490    /// Returns `true` if this algorithm identifier is deprecated.
491    ///
492    /// Per RFC 9864, `EdDSA` is deprecated in JOSE in favor of the fully
493    /// specified `Ed25519` and `Ed448` identifiers.
494    pub fn is_deprecated(&self) -> bool {
495        matches!(self, Algorithm::EdDsa)
496    }
497}
498
499impl Display for Algorithm {
500    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501        write!(f, "{}", self.as_str())
502    }
503}
504
505impl From<&str> for Algorithm {
506    /// Parses an algorithm string.
507    ///
508    /// Per RFC 7517, unknown algorithm values are accepted and stored as `Unknown`.
509    fn from(s: &str) -> Self {
510        match s {
511            "HS256" => Algorithm::Hs256,
512            "HS384" => Algorithm::Hs384,
513            "HS512" => Algorithm::Hs512,
514            "RS256" => Algorithm::Rs256,
515            "RS384" => Algorithm::Rs384,
516            "RS512" => Algorithm::Rs512,
517            "PS256" => Algorithm::Ps256,
518            "PS384" => Algorithm::Ps384,
519            "PS512" => Algorithm::Ps512,
520            "ES256" => Algorithm::Es256,
521            "ES384" => Algorithm::Es384,
522            "ES512" => Algorithm::Es512,
523            "ES256K" => Algorithm::Es256k,
524            "EdDSA" => Algorithm::EdDsa,
525            "Ed25519" => Algorithm::Ed25519,
526            "Ed448" => Algorithm::Ed448,
527            "RSA-OAEP" => Algorithm::RsaOaep,
528            "RSA-OAEP-256" => Algorithm::RsaOaep256,
529            "RSA-OAEP-384" => Algorithm::RsaOaep384,
530            "RSA-OAEP-512" => Algorithm::RsaOaep512,
531            "RSA1_5" => Algorithm::Rsa1_5,
532            "A128KW" => Algorithm::A128kw,
533            "A192KW" => Algorithm::A192kw,
534            "A256KW" => Algorithm::A256kw,
535            "dir" => Algorithm::Dir,
536            "ECDH-ES" => Algorithm::EcdhEs,
537            "ECDH-ES+A128KW" => Algorithm::EcdhEsA128kw,
538            "ECDH-ES+A192KW" => Algorithm::EcdhEsA192kw,
539            "ECDH-ES+A256KW" => Algorithm::EcdhEsA256kw,
540            "A128GCMKW" => Algorithm::A128gcmkw,
541            "A192GCMKW" => Algorithm::A192gcmkw,
542            "A256GCMKW" => Algorithm::A256gcmkw,
543            "PBES2-HS256+A128KW" => Algorithm::Pbes2Hs256A128kw,
544            "PBES2-HS384+A192KW" => Algorithm::Pbes2Hs384A192kw,
545            "PBES2-HS512+A256KW" => Algorithm::Pbes2Hs512A256kw,
546            "A128CBC-HS256" => Algorithm::A128cbcHs256,
547            "A192CBC-HS384" => Algorithm::A192cbcHs384,
548            "A256CBC-HS512" => Algorithm::A256cbcHs512,
549            "A128GCM" => Algorithm::A128gcm,
550            "A192GCM" => Algorithm::A192gcm,
551            "A256GCM" => Algorithm::A256gcm,
552            _ => Algorithm::Unknown(s.to_string()),
553        }
554    }
555}
556
557impl FromStr for Algorithm {
558    type Err = Infallible;
559
560    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
561        Ok(Self::from(s))
562    }
563}
564
565impl Serialize for Algorithm {
566    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
567    where
568        S: Serializer,
569    {
570        serializer.serialize_str(self.as_str())
571    }
572}
573
574impl<'de> Deserialize<'de> for Algorithm {
575    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
576    where
577        D: Deserializer<'de>,
578    {
579        Ok(Algorithm::from(String::deserialize(deserializer)?.as_str()))
580    }
581}
582
583/// Key-type-specific parameters.
584#[derive(Debug, Clone, PartialEq, Eq, Hash, Zeroize, ZeroizeOnDrop)]
585#[non_exhaustive]
586pub enum KeyParams {
587    /// RSA key parameters.
588    Rsa(RsaParams),
589    /// Elliptic Curve key parameters.
590    Ec(EcParams),
591    /// Symmetric key parameters.
592    Symmetric(SymmetricParams),
593    /// Octet Key Pair parameters.
594    Okp(OkpParams),
595}
596
597impl KeyParams {
598    /// Returns the [`KeyType`] corresponding to this variant.
599    pub fn key_type(&self) -> KeyType {
600        match self {
601            KeyParams::Rsa(_) => KeyType::Rsa,
602            KeyParams::Ec(_) => KeyType::Ec,
603            KeyParams::Symmetric(_) => KeyType::Symmetric,
604            KeyParams::Okp(_) => KeyType::Okp,
605        }
606    }
607
608    /// Returns `true` if this contains only public key parameters.
609    pub fn is_public_key_only(&self) -> bool {
610        match self {
611            KeyParams::Rsa(p) => p.is_public_key_only(),
612            KeyParams::Ec(p) => p.is_public_key_only(),
613            KeyParams::Symmetric(p) => p.is_public_key_only(),
614            KeyParams::Okp(p) => p.is_public_key_only(),
615        }
616    }
617
618    /// Returns `true` if this contains private key parameters.
619    pub fn has_private_key(&self) -> bool {
620        !self.is_public_key_only()
621    }
622
623    /// Validates the key parameters.
624    pub fn validate(&self) -> Result<()> {
625        match self {
626            KeyParams::Rsa(p) => p.validate(),
627            KeyParams::Ec(p) => p.validate(),
628            KeyParams::Symmetric(p) => p.validate(),
629            KeyParams::Okp(p) => p.validate(),
630        }
631    }
632}
633
634impl From<&KeyParams> for KeyType {
635    fn from(params: &KeyParams) -> Self {
636        params.key_type()
637    }
638}
639
640impl From<KeyParams> for KeyType {
641    fn from(params: KeyParams) -> Self {
642        (&params).into()
643    }
644}
645
646impl From<RsaParams> for KeyParams {
647    fn from(p: RsaParams) -> Self {
648        KeyParams::Rsa(p)
649    }
650}
651
652impl From<EcParams> for KeyParams {
653    fn from(p: EcParams) -> Self {
654        KeyParams::Ec(p)
655    }
656}
657
658impl From<SymmetricParams> for KeyParams {
659    fn from(p: SymmetricParams) -> Self {
660        KeyParams::Symmetric(p)
661    }
662}
663
664impl From<OkpParams> for KeyParams {
665    fn from(p: OkpParams) -> Self {
666        KeyParams::Okp(p)
667    }
668}
669
670/// A JSON Web Key (RFC 7517).
671///
672/// Represents a single cryptographic key with its parameters and metadata.
673///
674/// # Examples
675///
676/// ```
677/// use jwk_simple::{Key, KeyType};
678///
679/// // Parse from JSON
680/// let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
681/// let jwk: Key = serde_json::from_str(json).unwrap();
682///
683/// // Check key properties
684/// assert_eq!(jwk.kty(), KeyType::Rsa);
685/// assert!(jwk.is_public_key_only());
686/// ```
687#[derive(Clone, Zeroize, ZeroizeOnDrop)]
688pub struct Key {
689    /// The key ID.
690    #[zeroize(skip)]
691    kid: Option<String>,
692
693    /// The intended use of the key.
694    #[zeroize(skip)]
695    key_use: Option<KeyUse>,
696
697    /// The permitted operations for the key.
698    #[zeroize(skip)]
699    key_ops: Option<Vec<KeyOperation>>,
700
701    /// The algorithm intended for use with the key.
702    #[zeroize(skip)]
703    alg: Option<Algorithm>,
704
705    /// The key-type-specific parameters.
706    params: KeyParams,
707
708    /// X.509 certificate chain (base64-encoded DER).
709    #[zeroize(skip)]
710    x5c: Option<Vec<String>>,
711
712    /// X.509 certificate SHA-1 thumbprint (base64url-encoded).
713    #[zeroize(skip)]
714    x5t: Option<String>,
715
716    /// X.509 certificate SHA-256 thumbprint (base64url-encoded).
717    #[zeroize(skip)]
718    #[allow(non_snake_case)]
719    x5t_s256: Option<String>,
720
721    /// X.509 URL.
722    #[zeroize(skip)]
723    x5u: Option<String>,
724}
725
726impl Key {
727    /// Creates a new `Key` from key-type-specific parameters.
728    ///
729    /// The key type is automatically derived from the [`KeyParams`] variant via
730    /// the [`kty()`](Key::kty) accessor, which makes it impossible to construct
731    /// a `Key` with a mismatched key type.
732    ///
733    /// Use the `with_*` methods to set optional metadata fields:
734    ///
735    /// # Examples
736    ///
737    /// ```
738    /// use jwk_simple::{Key, KeyType, KeyParams, RsaParams, KeyUse, Algorithm};
739    /// use jwk_simple::encoding::Base64UrlBytes;
740    ///
741    /// let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
742    ///     Base64UrlBytes::new(vec![1, 2, 3]),
743    ///     Base64UrlBytes::new(vec![1, 0, 1]),
744    /// )))
745    /// .with_kid("my-key-id")
746    /// .with_alg(Algorithm::Rs256)
747    /// .with_use(KeyUse::Signature);
748    ///
749    /// assert_eq!(key.kty(), KeyType::Rsa);
750    /// assert_eq!(key.kid(), Some("my-key-id"));
751    /// ```
752    #[must_use]
753    pub fn new(params: KeyParams) -> Self {
754        Self {
755            kid: None,
756            key_use: None,
757            key_ops: None,
758            alg: None,
759            params,
760            x5c: None,
761            x5t: None,
762            x5t_s256: None,
763            x5u: None,
764        }
765    }
766
767    /// Returns the key type, derived from the [`KeyParams`] variant.
768    ///
769    /// This is always consistent with the key's parameters:
770    /// - [`KeyParams::Rsa`] → [`KeyType::Rsa`]
771    /// - [`KeyParams::Ec`] → [`KeyType::Ec`]
772    /// - [`KeyParams::Symmetric`] → [`KeyType::Symmetric`]
773    /// - [`KeyParams::Okp`] → [`KeyType::Okp`]
774    #[inline]
775    #[must_use]
776    pub fn kty(&self) -> KeyType {
777        self.params.key_type()
778    }
779
780    /// Returns the key ID (`kid`), if present.
781    #[must_use]
782    pub fn kid(&self) -> Option<&str> {
783        self.kid.as_deref()
784    }
785
786    /// Returns the intended key use (`use`), if present.
787    #[must_use]
788    pub fn key_use(&self) -> Option<&KeyUse> {
789        self.key_use.as_ref()
790    }
791
792    /// Returns the permitted operations (`key_ops`), if present.
793    #[must_use]
794    pub fn key_ops(&self) -> Option<&[KeyOperation]> {
795        self.key_ops.as_deref()
796    }
797
798    /// Returns the declared algorithm (`alg`), if present.
799    #[must_use]
800    pub fn alg(&self) -> Option<&Algorithm> {
801        self.alg.as_ref()
802    }
803
804    /// Returns the key-type-specific parameters.
805    #[must_use]
806    pub fn params(&self) -> &KeyParams {
807        &self.params
808    }
809
810    /// Returns the X.509 certificate chain (`x5c`), if present.
811    #[must_use]
812    pub fn x5c(&self) -> Option<&[String]> {
813        self.x5c.as_deref()
814    }
815
816    /// Returns the X.509 SHA-1 certificate thumbprint (`x5t`), if present.
817    #[must_use]
818    pub fn x5t(&self) -> Option<&str> {
819        self.x5t.as_deref()
820    }
821
822    /// Returns the X.509 SHA-256 certificate thumbprint (`x5t#S256`), if present.
823    #[must_use]
824    #[allow(non_snake_case)]
825    pub fn x5t_s256(&self) -> Option<&str> {
826        self.x5t_s256.as_deref()
827    }
828
829    /// Returns the X.509 URL (`x5u`), if present.
830    #[must_use]
831    pub fn x5u(&self) -> Option<&str> {
832        self.x5u.as_deref()
833    }
834
835    /// Sets the key ID (`kid`).
836    #[must_use]
837    pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
838        self.kid = Some(kid.into());
839        self
840    }
841
842    /// Sets the intended use of the key (`use`).
843    #[must_use]
844    pub fn with_use(mut self, key_use: KeyUse) -> Self {
845        self.key_use = Some(key_use);
846        self
847    }
848
849    /// Sets the permitted key operations (`key_ops`).
850    #[must_use]
851    pub fn with_key_ops(mut self, key_ops: impl IntoIterator<Item = KeyOperation>) -> Self {
852        self.key_ops = Some(key_ops.into_iter().collect());
853        self
854    }
855
856    /// Sets the algorithm intended for use with the key (`alg`).
857    #[must_use]
858    pub fn with_alg(mut self, alg: Algorithm) -> Self {
859        self.alg = Some(alg);
860        self
861    }
862
863    /// Sets the X.509 certificate chain (`x5c`).
864    #[must_use]
865    pub fn with_x5c(mut self, x5c: Vec<String>) -> Self {
866        self.x5c = Some(x5c);
867        self
868    }
869
870    /// Sets the X.509 certificate SHA-1 thumbprint (`x5t`).
871    #[must_use]
872    pub fn with_x5t(mut self, x5t: impl Into<String>) -> Self {
873        self.x5t = Some(x5t.into());
874        self
875    }
876
877    /// Sets the X.509 certificate SHA-256 thumbprint (`x5t#S256`).
878    #[must_use]
879    pub fn with_x5t_s256(mut self, x5t_s256: impl Into<String>) -> Self {
880        self.x5t_s256 = Some(x5t_s256.into());
881        self
882    }
883
884    /// Sets the X.509 URL (`x5u`).
885    #[must_use]
886    pub fn with_x5u(mut self, x5u: impl Into<String>) -> Self {
887        self.x5u = Some(x5u.into());
888        self
889    }
890
891    /// Returns `true` if this contains only public key parameters.
892    pub fn is_public_key_only(&self) -> bool {
893        self.params.is_public_key_only()
894    }
895
896    /// Returns `true` if this contains private key parameters.
897    pub fn has_private_key(&self) -> bool {
898        self.params.has_private_key()
899    }
900
901    /// Validates the JWK structure and metadata consistency.
902    ///
903    /// This is a context-free structural check: it verifies the key material
904    /// is well-formed, metadata fields are internally consistent, and X.509
905    /// fields are properly encoded and match the key material.
906    ///
907    /// This method does **not** check algorithm suitability, key strength for
908    /// a specific algorithm, or operation intent, even if the `alg` field is
909    /// set on the key. A key with `"alg": "RS256"` on a symmetric key type
910    /// passes `validate()` because the key material itself is structurally
911    /// valid; the algorithm mismatch is a suitability concern.
912    /// Use [`Key::validate_for_use`] for those checks.
913    ///
914    /// In other words, `validate()` is the context-free gate: it validates the
915    /// key's own parameters and metadata (`use`, `key_ops`, `x5u`, `x5c`,
916    /// `x5t`, `x5t#S256`) without deciding whether the key is acceptable for a
917    /// particular algorithm or operation.
918    ///
919    /// This method does **not** perform PKIX trust/path validation for `x5c`
920    /// chains (trust anchors, validity period, key usage/EKU, revocation, etc.).
921    /// PKIX trust validation is application-defined and out of scope for this crate.
922    #[must_use = "validation result must be checked"]
923    pub fn validate(&self) -> Result<()> {
924        // Structural key-material validation (key-type-specific).
925        self.params.validate()?;
926
927        // RFC 7517 Section 4.3 metadata constraints.
928        self.validate_use_key_ops_consistency()?;
929        self.validate_key_ops_unique()?;
930
931        self.validate_certificate_metadata()?;
932
933        Ok(())
934    }
935
936    pub(crate) fn validate_certificate_metadata(&self) -> Result<()> {
937        // RFC 7517 Section 4.6: x5u MUST use TLS (HTTPS)
938        if let Some(ref x5u) = self.x5u {
939            let parsed = Url::parse(x5u).map_err(|_| {
940                Error::from(InvalidKeyError::InvalidParameter {
941                    name: "x5u",
942                    reason: "RFC 7517: x5u must be a valid absolute URL".to_string(),
943                })
944            })?;
945
946            if parsed.scheme() != "https" || parsed.host_str().is_none() {
947                return Err(InvalidKeyError::InvalidParameter {
948                    name: "x5u",
949                    reason: "RFC 7517: x5u URL must use HTTPS and include a host".to_string(),
950                }
951                .into());
952            }
953        }
954
955        let first_x5c_der = self.decode_and_validate_x5c_first_der()?;
956
957        // RFC 7517 Section 4.7: the key in the first certificate MUST match
958        // the public key represented by other JWK members.
959        if let Some(first_der) = first_x5c_der.as_ref() {
960            self.validate_x5c_public_key_match(first_der)?;
961        }
962
963        // RFC 7517 Section 4.8: x5t is base64url-encoded SHA-1 thumbprint (20 bytes)
964        if let Some(ref x5t) = self.x5t {
965            validate_x509_thumbprint(x5t, "x5t", 20)?;
966        }
967
968        // RFC 7517 Section 4.9: x5t#S256 is base64url-encoded SHA-256 thumbprint (32 bytes)
969        if let Some(ref x5t_s256) = self.x5t_s256 {
970            validate_x509_thumbprint(x5t_s256, "x5t#S256", 32)?;
971        }
972
973        // RFC 7517 Section 4.8/4.9 with 4.7 consistency: if x5c and thumbprints
974        // are both present, thumbprints must match the first certificate.
975        if let Some(first_der) = first_x5c_der.as_ref() {
976            if let Some(ref x5t) = self.x5t {
977                let mut hasher = Sha1::new();
978                hasher.update(first_der);
979                let expected = Base64UrlUnpadded::encode_string(&hasher.finalize());
980
981                if x5t != &expected {
982                    return Err(InvalidKeyError::InconsistentParameters(
983                        "RFC 7517: x5t does not match SHA-1 thumbprint of x5c[0]".to_string(),
984                    )
985                    .into());
986                }
987            }
988
989            if let Some(ref x5t_s256) = self.x5t_s256 {
990                let mut hasher = Sha256::new();
991                hasher.update(first_der);
992                let expected = Base64UrlUnpadded::encode_string(&hasher.finalize());
993
994                if x5t_s256 != &expected {
995                    return Err(InvalidKeyError::InconsistentParameters(
996                        "RFC 7517: x5t#S256 does not match SHA-256 thumbprint of x5c[0]"
997                            .to_string(),
998                    )
999                    .into());
1000                }
1001            }
1002        }
1003
1004        Ok(())
1005    }
1006
1007    fn validate_use_key_ops_consistency(&self) -> Result<()> {
1008        if let (Some(key_use), Some(key_ops)) = (&self.key_use, &self.key_ops)
1009            && !is_use_consistent_with_ops(key_use, key_ops)
1010        {
1011            return Err(InvalidKeyError::InconsistentParameters(
1012                "RFC 7517: 'use' and 'key_ops' are both present but inconsistent".to_string(),
1013            )
1014            .into());
1015        }
1016
1017        Ok(())
1018    }
1019
1020    fn validate_key_ops_unique(&self) -> Result<()> {
1021        if let Some(ref ops) = self.key_ops {
1022            let mut seen = HashSet::new();
1023            for op in ops {
1024                if !seen.insert(op) {
1025                    return Err(InvalidKeyError::InconsistentParameters(format!(
1026                        "RFC 7517: key_ops array contains duplicate value '{}'",
1027                        op.as_str()
1028                    ))
1029                    .into());
1030                }
1031            }
1032        }
1033
1034        Ok(())
1035    }
1036
1037    fn decode_and_validate_x5c_first_der(&self) -> Result<Option<Vec<u8>>> {
1038        // RFC 7517 Section 4.7: x5c contains "a chain of one or more PKIX certificates"
1039        let Some(ref certs) = self.x5c else {
1040            return Ok(None);
1041        };
1042
1043        if certs.is_empty() {
1044            return Err(InvalidKeyError::InvalidParameter {
1045                name: "x5c",
1046                reason: "RFC 7517: x5c must contain one or more certificates".to_string(),
1047            }
1048            .into());
1049        }
1050
1051        let mut first_der = None;
1052
1053        // RFC 7517 Section 4.7: x5c values are base64 encoded (NOT base64url)
1054        for (i, cert) in certs.iter().enumerate() {
1055            // Standard base64 uses '+' and '/' which are NOT valid in base64url
1056            // base64url uses '-' and '_' instead
1057            // We validate it's proper base64 by checking for base64url-only chars
1058            // and attempting to decode
1059            if cert.contains('-') || cert.contains('_') {
1060                return Err(InvalidKeyError::InvalidParameter {
1061                    name: "x5c",
1062                    reason: format!(
1063                        "RFC 7517: x5c[{}] appears to be base64url encoded; must be standard base64",
1064                        i
1065                    ),
1066                }
1067                .into());
1068            }
1069
1070            // Validate it's valid base64 by checking character set and padding
1071            if !is_valid_base64(cert) {
1072                return Err(InvalidKeyError::InvalidParameter {
1073                    name: "x5c",
1074                    reason: format!("RFC 7517: x5c[{}] is not valid base64 encoding", i),
1075                }
1076                .into());
1077            }
1078
1079            // RFC 7517 Section 4.7: Each certificate value MUST be a DER-encoded X.509 certificate
1080            // Decode and validate basic DER certificate structure
1081            use base64ct::{Base64, Encoding};
1082            let der_bytes = Base64::decode_vec(cert).map_err(|_| {
1083                Error::from(InvalidKeyError::InvalidParameter {
1084                    name: "x5c",
1085                    reason: format!("RFC 7517: x5c[{}] failed base64 decoding", i),
1086                })
1087            })?;
1088
1089            let (remaining, _) = parse_x509_certificate(&der_bytes).map_err(|_| {
1090                InvalidKeyError::InvalidParameter {
1091                    name: "x5c",
1092                    reason: format!(
1093                        "RFC 7517: x5c[{}] is not a valid DER-encoded X.509 certificate",
1094                        i
1095                    ),
1096                }
1097            })?;
1098
1099            if !remaining.is_empty() {
1100                return Err(InvalidKeyError::InvalidParameter {
1101                    name: "x5c",
1102                    reason: format!(
1103                        "RFC 7517: x5c[{}] contains trailing data after DER certificate",
1104                        i
1105                    ),
1106                }
1107                .into());
1108            }
1109
1110            if i == 0 {
1111                first_der = Some(der_bytes);
1112            }
1113        }
1114
1115        Ok(first_der)
1116    }
1117
1118    /// Validates this key for a specific algorithm and operation(s).
1119    ///
1120    /// This is the full pre-use gate: it performs structural validation
1121    /// ([`Key::validate`]) followed by algorithm suitability checks
1122    /// (type compatibility, key strength), algorithm-operation compatibility
1123    /// checks (requested operation is valid for the algorithm), operation
1124    /// capability checks (key material can actually perform the operation),
1125    /// and operation-intent enforcement (metadata permits the requested
1126    /// operations).
1127    ///
1128    /// The `alg` parameter controls which algorithm constraints are applied
1129    /// (key type, minimum strength). If the key declares its own `alg`, it must
1130    /// match the requested algorithm. Unknown algorithms are rejected because
1131    /// suitability (type compatibility and strength) cannot be validated for
1132    /// them.
1133    ///
1134    /// At least one operation must be provided. Passing an empty iterator
1135    /// returns an error (this is a caller precondition, not a key problem).
1136    ///
1137    /// This method calls [`Key::validate`] internally, so callers do not
1138    /// need to call it separately.
1139    ///
1140    /// This is the full pre-use gate for direct key use. It layers algorithm
1141    /// suitability, operation/algorithm compatibility, key-material capability,
1142    /// and operation intent on top of [`Key::validate`].
1143    ///
1144    /// # Examples
1145    ///
1146    /// ```
1147    /// use jwk_simple::{Key, Algorithm, KeyOperation};
1148    ///
1149    /// let json = r#"{"kty":"oct","k":"c2VjcmV0LWtleS1tYXRlcmlhbC10aGF0LWlzLWxvbmctZW5vdWdo"}"#;
1150    /// let key: Key = serde_json::from_str(json).unwrap();
1151    ///
1152    /// // Validate for HMAC signing
1153    /// assert!(key.validate_for_use(&Algorithm::Hs256, [KeyOperation::Sign]).is_ok());
1154    /// ```
1155    #[must_use = "validation result must be checked"]
1156    pub fn validate_for_use(
1157        &self,
1158        alg: &Algorithm,
1159        ops: impl IntoIterator<Item = KeyOperation>,
1160    ) -> Result<()> {
1161        let ops: Vec<KeyOperation> = ops.into_iter().collect();
1162        if ops.is_empty() {
1163            return Err(Error::InvalidInput(
1164                "at least one requested operation is required",
1165            ));
1166        }
1167
1168        // Structural validation first (belt-and-suspenders).
1169        self.validate()?;
1170
1171        self.validate_declared_algorithm_match(alg)?;
1172
1173        self.validate_for_use_inner(alg, &ops)
1174    }
1175
1176    /// Like [`validate_for_use`](Key::validate_for_use) but skips the declared
1177    /// algorithm match check.
1178    ///
1179    /// This is intended for callers that explicitly override the algorithm
1180    /// (e.g. WebCrypto import with an explicit `alg` parameter).
1181    #[cfg(feature = "web-crypto")]
1182    pub(crate) fn validate_for_use_with_alg_override(
1183        &self,
1184        alg: &Algorithm,
1185        ops: impl IntoIterator<Item = KeyOperation>,
1186    ) -> Result<()> {
1187        let ops: Vec<KeyOperation> = ops.into_iter().collect();
1188        if ops.is_empty() {
1189            return Err(Error::InvalidInput(
1190                "at least one requested operation is required",
1191            ));
1192        }
1193
1194        // Structural validation first (belt-and-suspenders).
1195        self.validate()?;
1196
1197        // Deliberately skip validate_declared_algorithm_match: the caller is
1198        // explicitly overriding the algorithm.
1199
1200        self.validate_for_use_inner(alg, &ops)
1201    }
1202
1203    /// Shared validation logic for [`validate_for_use`](Key::validate_for_use)
1204    /// and [`validate_for_use_with_alg_override`](Key::validate_for_use_with_alg_override).
1205    fn validate_for_use_inner(&self, alg: &Algorithm, ops: &[KeyOperation]) -> Result<()> {
1206        // Algorithm suitability: type match + strength.
1207        // `validate()` above already ran `params.validate()`, so we call the
1208        // algorithm-specific checks directly to avoid redundant structural work.
1209        self.validate_algorithm_key_type_match(alg)?;
1210        self.validate_algorithm_key_strength(alg)?;
1211        self.validate_operation_algorithm_compatibility_for_all(alg, ops)?;
1212
1213        // Operation capability: key material can perform requested operations.
1214        self.validate_operation_capability_for_all(ops)?;
1215
1216        // Operation intent: use/key_ops metadata permits the requested operations.
1217        // `validate()` above already enforced `use`/`key_ops` consistency and
1218        // uniqueness, so we call the intent-only helper directly.
1219        self.validate_operation_intent_for_all(ops)
1220    }
1221
1222    /// Checks whether this key's metadata permits the requested operation(s).
1223    ///
1224    /// This enforces RFC 7517 operation-intent semantics:
1225    /// - If `use` is present, it must be compatible with all requested operations.
1226    /// - If `key_ops` is present, it must include all requested operations.
1227    /// - If both are present, they must be mutually consistent.
1228    /// - `key_ops` values must be unique.
1229    ///
1230    /// Metadata members are optional in RFC 7517. If both `use` and `key_ops`
1231    /// are absent, this check succeeds.
1232    ///
1233    /// At least one operation must be provided. Passing an empty slice
1234    /// returns an error (this is a caller precondition, not a key problem).
1235    ///
1236    /// This does **not** perform key-material or algorithm-suitability checks.
1237    /// It does enforce `use`/`key_ops` metadata consistency (RFC 7517 §4.3),
1238    /// which may return [`Error::InvalidKey`] if the key's own metadata is
1239    /// self-contradictory. Use [`Key::validate_for_use`] for the full pre-use
1240    /// gate.
1241    ///
1242    /// # Examples
1243    ///
1244    /// ```
1245    /// use jwk_simple::{Key, KeyOperation};
1246    ///
1247    /// let json = r#"{"kty":"oct","use":"sig","k":"c2VjcmV0LWtleS1tYXRlcmlhbC10aGF0LWlzLWxvbmctZW5vdWdo"}"#;
1248    /// let key: Key = serde_json::from_str(json).unwrap();
1249    ///
1250    /// // Signing is permitted by use="sig"
1251    /// assert!(key.check_operations_permitted(&[KeyOperation::Sign]).is_ok());
1252    ///
1253    /// // Encryption is not permitted by use="sig"
1254    /// assert!(key.check_operations_permitted(&[KeyOperation::Encrypt]).is_err());
1255    /// ```
1256    pub fn check_operations_permitted(&self, operations: impl AsRef<[KeyOperation]>) -> Result<()> {
1257        let operations = operations.as_ref();
1258        if operations.is_empty() {
1259            return Err(Error::InvalidInput(
1260                "at least one requested operation is required",
1261            ));
1262        }
1263        // Consistency and uniqueness checks must run here since this is a
1264        // standalone public method: callers may not have called `validate()`.
1265        self.validate_use_key_ops_consistency()?;
1266        self.validate_key_ops_unique()?;
1267        self.validate_operation_intent_for_all(operations)
1268    }
1269
1270    /// Checks algorithm suitability: structural validity, key-type
1271    /// compatibility, and key strength.
1272    ///
1273    /// This does **not** perform operation-intent checks (`use`/`key_ops`)
1274    /// and does not enforce selection policy. Certificate metadata checks are
1275    /// handled separately by strict selection. It is intended
1276    /// for internal use by [`KeySelector`](crate::KeySelector).
1277    /// [`Key::validate_for_use`] calls the underlying helpers directly to
1278    /// avoid redundant structural validation.
1279    pub(crate) fn check_algorithm_suitability(&self, alg: &Algorithm) -> Result<()> {
1280        // Structural validation first: reject malformed key material before
1281        // checking algorithm-specific constraints.
1282        self.params.validate()?;
1283        self.validate_algorithm_key_type_match(alg)?;
1284        self.validate_algorithm_key_strength(alg)
1285    }
1286
1287    /// Checks operation-intent metadata for requested operations.
1288    ///
1289    /// This enforces RFC 7517 operation intent semantics:
1290    /// - If `use` is present, it must be compatible with all requested operations.
1291    /// - If `key_ops` is present, it must include all requested operations.
1292    /// - If both are present, they must be mutually consistent.
1293    ///
1294    /// Metadata members are optional in RFC 7517. If both are absent, this
1295    /// check succeeds.
1296    ///
1297    /// This does **not** perform key-material or algorithm-suitability checks.
1298    /// It does enforce `use`/`key_ops` metadata consistency (RFC 7517 §4.3),
1299    /// which may return [`Error::InvalidKey`] if the key's own metadata is
1300    /// self-contradictory. It is intended for internal use by
1301    /// [`KeySelector`](crate::KeySelector).
1302    pub(crate) fn check_operation_intent(&self, operations: &[KeyOperation]) -> Result<()> {
1303        debug_assert!(!operations.is_empty());
1304        self.validate_use_key_ops_consistency()?;
1305        self.validate_key_ops_unique()?;
1306        self.validate_operation_intent_for_all(operations)
1307    }
1308
1309    /// Checks whether this key material can actually perform requested operations.
1310    ///
1311    /// This is separate from metadata intent checks (`use`/`key_ops`):
1312    /// a key may declare an operation but still lack required private material.
1313    pub(crate) fn check_operation_capability(&self, operations: &[KeyOperation]) -> Result<()> {
1314        debug_assert!(!operations.is_empty());
1315        self.validate_operation_capability_for_all(operations)
1316    }
1317
1318    /// Validates only operation-intent compatibility for all requested operations.
1319    ///
1320    /// Unlike [`Key::check_operation_intent`], this does not run
1321    /// `use`/`key_ops` structural consistency or uniqueness pre-checks.
1322    pub(crate) fn validate_operation_intent_for_all(
1323        &self,
1324        operations: &[KeyOperation],
1325    ) -> Result<()> {
1326        debug_assert!(!operations.is_empty());
1327
1328        if let Some(key_use) = &self.key_use {
1329            let disallowed: Vec<KeyOperation> = operations
1330                .iter()
1331                .filter(|op| !is_operation_allowed_by_use(key_use, op))
1332                .cloned()
1333                .collect();
1334
1335            if !disallowed.is_empty() {
1336                return Err(IncompatibleKeyError::OperationNotPermitted {
1337                    operations: disallowed,
1338                    reason: format!(
1339                        "RFC 7517: key 'use' '{}' does not permit requested operation(s)",
1340                        key_use
1341                    ),
1342                }
1343                .into());
1344            }
1345        }
1346
1347        if let Some(key_ops) = &self.key_ops {
1348            // `key_ops` is an explicit allow-list. Unknown operations are
1349            // accepted only when explicitly listed in the key metadata.
1350            let disallowed: Vec<KeyOperation> = operations
1351                .iter()
1352                .filter(|op| !key_ops.contains(op))
1353                .cloned()
1354                .collect();
1355
1356            if !disallowed.is_empty() {
1357                return Err(IncompatibleKeyError::OperationNotPermitted {
1358                    operations: disallowed,
1359                    reason: "RFC 7517: key_ops does not permit requested operation(s)".to_string(),
1360                }
1361                .into());
1362            }
1363        }
1364
1365        Ok(())
1366    }
1367
1368    fn validate_operation_capability_for_all(&self, operations: &[KeyOperation]) -> Result<()> {
1369        debug_assert!(!operations.is_empty());
1370
1371        // Symmetric keys always carry secret material and do not have a
1372        // public/private split. Private-material capability checks apply only
1373        // to asymmetric key types.
1374        if matches!(self.params, KeyParams::Symmetric(_)) || self.has_private_key() {
1375            return Ok(());
1376        }
1377
1378        let requires_private: Vec<KeyOperation> = operations
1379            .iter()
1380            .filter(|op| {
1381                matches!(
1382                    op,
1383                    KeyOperation::Sign
1384                        | KeyOperation::Decrypt
1385                        | KeyOperation::UnwrapKey
1386                        | KeyOperation::DeriveKey
1387                        | KeyOperation::DeriveBits
1388                )
1389            })
1390            .cloned()
1391            .collect();
1392
1393        if requires_private.is_empty() {
1394            return Ok(());
1395        }
1396
1397        Err(IncompatibleKeyError::OperationNotPermitted {
1398            operations: requires_private,
1399            reason: "requested operation(s) require private key material, but key contains only public parameters".to_string(),
1400        }
1401        .into())
1402    }
1403
1404    fn validate_operation_algorithm_compatibility_for_all(
1405        &self,
1406        alg: &Algorithm,
1407        operations: &[KeyOperation],
1408    ) -> Result<()> {
1409        debug_assert!(!operations.is_empty());
1410
1411        let incompatible: Vec<KeyOperation> = operations
1412            .iter()
1413            .filter(|op| !is_operation_compatible_with_algorithm(op, alg))
1414            .cloned()
1415            .collect();
1416
1417        if incompatible.is_empty() {
1418            return Ok(());
1419        }
1420
1421        Err(IncompatibleKeyError::OperationNotPermitted {
1422            operations: incompatible,
1423            reason: format!(
1424                "requested operation(s) are not compatible with algorithm '{}'",
1425                alg.as_str()
1426            ),
1427        }
1428        .into())
1429    }
1430
1431    /// Returns `true` if this key's type (and curve, where applicable) is
1432    /// compatible with the given algorithm per RFC 7518.
1433    ///
1434    /// This checks that the key type matches what the algorithm requires.
1435    /// For example, an RSA key is compatible with RS256 but not with ES256.
1436    /// For EC keys, the curve is also checked (e.g., P-256 for ES256).
1437    ///
1438    /// Unknown algorithms always return `false` since their requirements
1439    /// cannot be determined.
1440    ///
1441    /// # Examples
1442    ///
1443    /// ```
1444    /// use jwk_simple::{Key, Algorithm};
1445    ///
1446    /// let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
1447    /// let key: Key = serde_json::from_str(json).unwrap();
1448    ///
1449    /// assert!(key.is_algorithm_compatible(&Algorithm::Rs256));
1450    /// assert!(!key.is_algorithm_compatible(&Algorithm::Es256));
1451    /// ```
1452    pub fn is_algorithm_compatible(&self, alg: &Algorithm) -> bool {
1453        // Unknown algorithms cannot be validated against key types
1454        if alg.is_unknown() {
1455            return false;
1456        }
1457
1458        match (&self.params, alg) {
1459            // RSA algorithms require RSA keys
1460            (KeyParams::Rsa(_), Algorithm::Rs256)
1461            | (KeyParams::Rsa(_), Algorithm::Rs384)
1462            | (KeyParams::Rsa(_), Algorithm::Rs512)
1463            | (KeyParams::Rsa(_), Algorithm::Ps256)
1464            | (KeyParams::Rsa(_), Algorithm::Ps384)
1465            | (KeyParams::Rsa(_), Algorithm::Ps512)
1466            | (KeyParams::Rsa(_), Algorithm::RsaOaep)
1467            | (KeyParams::Rsa(_), Algorithm::RsaOaep256)
1468            | (KeyParams::Rsa(_), Algorithm::RsaOaep384)
1469            | (KeyParams::Rsa(_), Algorithm::RsaOaep512)
1470            | (KeyParams::Rsa(_), Algorithm::Rsa1_5) => true,
1471
1472            // HMAC algorithms require symmetric keys
1473            (KeyParams::Symmetric(_), Algorithm::Hs256)
1474            | (KeyParams::Symmetric(_), Algorithm::Hs384)
1475            | (KeyParams::Symmetric(_), Algorithm::Hs512) => true,
1476
1477            // AES key wrap algorithms require symmetric keys
1478            (KeyParams::Symmetric(_), Algorithm::A128kw)
1479            | (KeyParams::Symmetric(_), Algorithm::A192kw)
1480            | (KeyParams::Symmetric(_), Algorithm::A256kw)
1481            | (KeyParams::Symmetric(_), Algorithm::A128gcmkw)
1482            | (KeyParams::Symmetric(_), Algorithm::A192gcmkw)
1483            | (KeyParams::Symmetric(_), Algorithm::A256gcmkw)
1484            | (KeyParams::Symmetric(_), Algorithm::Dir) => true,
1485
1486            // AES content encryption algorithms require symmetric keys
1487            (KeyParams::Symmetric(_), Algorithm::A128cbcHs256)
1488            | (KeyParams::Symmetric(_), Algorithm::A192cbcHs384)
1489            | (KeyParams::Symmetric(_), Algorithm::A256cbcHs512)
1490            | (KeyParams::Symmetric(_), Algorithm::A128gcm)
1491            | (KeyParams::Symmetric(_), Algorithm::A192gcm)
1492            | (KeyParams::Symmetric(_), Algorithm::A256gcm) => true,
1493
1494            // PBES2 algorithms require symmetric keys
1495            (KeyParams::Symmetric(_), Algorithm::Pbes2Hs256A128kw)
1496            | (KeyParams::Symmetric(_), Algorithm::Pbes2Hs384A192kw)
1497            | (KeyParams::Symmetric(_), Algorithm::Pbes2Hs512A256kw) => true,
1498
1499            // EC algorithms require EC keys
1500            (KeyParams::Ec(ec), Algorithm::Es256) => ec.crv == EcCurve::P256,
1501            (KeyParams::Ec(ec), Algorithm::Es384) => ec.crv == EcCurve::P384,
1502            (KeyParams::Ec(ec), Algorithm::Es512) => ec.crv == EcCurve::P521,
1503            (KeyParams::Ec(ec), Algorithm::Es256k) => ec.crv == EcCurve::Secp256k1,
1504
1505            // ECDH algorithms require EC keys
1506            (KeyParams::Ec(_), Algorithm::EcdhEs)
1507            | (KeyParams::Ec(_), Algorithm::EcdhEsA128kw)
1508            | (KeyParams::Ec(_), Algorithm::EcdhEsA192kw)
1509            | (KeyParams::Ec(_), Algorithm::EcdhEsA256kw) => true,
1510
1511            // EdDSA (legacy identifier) requires OKP keys with Ed25519 or Ed448 curves
1512            (KeyParams::Okp(okp), Algorithm::EdDsa) => {
1513                okp.crv == OkpCurve::Ed25519 || okp.crv == OkpCurve::Ed448
1514            }
1515
1516            // RFC 9864 fully specified JOSE identifiers
1517            (KeyParams::Okp(okp), Algorithm::Ed25519) => okp.crv == OkpCurve::Ed25519,
1518            (KeyParams::Okp(okp), Algorithm::Ed448) => okp.crv == OkpCurve::Ed448,
1519
1520            // ECDH with OKP keys (X25519, X448)
1521            (KeyParams::Okp(okp), Algorithm::EcdhEs)
1522            | (KeyParams::Okp(okp), Algorithm::EcdhEsA128kw)
1523            | (KeyParams::Okp(okp), Algorithm::EcdhEsA192kw)
1524            | (KeyParams::Okp(okp), Algorithm::EcdhEsA256kw) => {
1525                okp.crv == OkpCurve::X25519 || okp.crv == OkpCurve::X448
1526            }
1527
1528            // All other combinations are invalid
1529            _ => false,
1530        }
1531    }
1532
1533    /// Validates that the algorithm matches the key type.
1534    fn validate_algorithm_key_type_match(&self, alg: &Algorithm) -> Result<()> {
1535        if !self.is_algorithm_compatible(alg) {
1536            return Err(IncompatibleKeyError::IncompatibleAlgorithm {
1537                algorithm: alg.clone(),
1538                key_type: self.kty(),
1539            }
1540            .into());
1541        }
1542
1543        Ok(())
1544    }
1545
1546    /// Validates algorithm-specific key strength requirements.
1547    fn validate_algorithm_key_strength(&self, alg: &Algorithm) -> Result<()> {
1548        match (&self.params, alg) {
1549            (
1550                KeyParams::Rsa(rsa),
1551                Algorithm::Rs256
1552                | Algorithm::Rs384
1553                | Algorithm::Rs512
1554                | Algorithm::Ps256
1555                | Algorithm::Ps384
1556                | Algorithm::Ps512
1557                | Algorithm::Rsa1_5
1558                | Algorithm::RsaOaep
1559                | Algorithm::RsaOaep256
1560                | Algorithm::RsaOaep384
1561                | Algorithm::RsaOaep512,
1562            ) => rsa.validate_key_size(2048),
1563            (KeyParams::Symmetric(sym), Algorithm::Hs256) => sym.validate_min_size(256, "HS256"),
1564            (KeyParams::Symmetric(sym), Algorithm::Hs384) => sym.validate_min_size(384, "HS384"),
1565            (KeyParams::Symmetric(sym), Algorithm::Hs512) => sym.validate_min_size(512, "HS512"),
1566            (KeyParams::Symmetric(sym), Algorithm::A128kw)
1567            | (KeyParams::Symmetric(sym), Algorithm::A128gcmkw)
1568            | (KeyParams::Symmetric(sym), Algorithm::A128gcm) => {
1569                sym.validate_exact_size(128, "AES-128")
1570            }
1571            (KeyParams::Symmetric(sym), Algorithm::A192kw)
1572            | (KeyParams::Symmetric(sym), Algorithm::A192gcmkw)
1573            | (KeyParams::Symmetric(sym), Algorithm::A192gcm) => {
1574                sym.validate_exact_size(192, "AES-192")
1575            }
1576            (KeyParams::Symmetric(sym), Algorithm::A256kw)
1577            | (KeyParams::Symmetric(sym), Algorithm::A256gcmkw)
1578            | (KeyParams::Symmetric(sym), Algorithm::A256gcm) => {
1579                sym.validate_exact_size(256, "AES-256")
1580            }
1581            (KeyParams::Symmetric(sym), Algorithm::A128cbcHs256) => {
1582                // Per RFC 7518, this composite algorithm uses a 256-bit key
1583                // (128-bit MAC key + 128-bit ENC key).
1584                sym.validate_exact_size(256, "A128CBC-HS256")
1585            }
1586            (KeyParams::Symmetric(sym), Algorithm::A192cbcHs384) => {
1587                // Per RFC 7518, this composite algorithm uses a 384-bit key
1588                // (192-bit MAC key + 192-bit ENC key).
1589                sym.validate_exact_size(384, "A192CBC-HS384")
1590            }
1591            (KeyParams::Symmetric(sym), Algorithm::A256cbcHs512) => {
1592                // Per RFC 7518, this composite algorithm uses a 512-bit key
1593                // (256-bit MAC key + 256-bit ENC key).
1594                sym.validate_exact_size(512, "A256CBC-HS512")
1595            }
1596            _ => Ok(()),
1597        }
1598    }
1599
1600    /// Validates that the first `x5c` certificate public key matches JWK key material.
1601    fn validate_x5c_public_key_match(&self, cert_der: &[u8]) -> Result<()> {
1602        let (remaining, cert) = parse_x509_certificate(cert_der).map_err(|_| {
1603            Error::from(InvalidKeyError::InvalidParameter {
1604                name: "x5c",
1605                reason: "RFC 7517: x5c[0] is not a parseable X.509 certificate".to_string(),
1606            })
1607        })?;
1608
1609        if !remaining.is_empty() {
1610            return Err(InvalidKeyError::InvalidParameter {
1611                name: "x5c",
1612                reason: "RFC 7517: x5c[0] contains trailing data after DER certificate".to_string(),
1613            }
1614            .into());
1615        }
1616
1617        let spki = &cert.tbs_certificate.subject_pki;
1618        let cert_alg_oid = spki.algorithm.algorithm.to_id_string();
1619        let cert_key = spki.subject_public_key.data.as_ref();
1620
1621        match &self.params {
1622            KeyParams::Rsa(rsa) => {
1623                if cert_alg_oid != "1.2.840.113549.1.1.1" {
1624                    return Err(InvalidKeyError::InconsistentParameters(
1625                        "RFC 7517: x5c[0] public key algorithm does not match JWK RSA key"
1626                            .to_string(),
1627                    )
1628                    .into());
1629                }
1630
1631                let expected_der = encode_rsa_public_key_der(rsa.n.as_bytes(), rsa.e.as_bytes());
1632                if cert_key != expected_der.as_slice() {
1633                    return Err(InvalidKeyError::InconsistentParameters(
1634                        "RFC 7517: x5c[0] public key does not match JWK RSA key parameters"
1635                            .to_string(),
1636                    )
1637                    .into());
1638                }
1639            }
1640            KeyParams::Ec(ec) => {
1641                if cert_alg_oid != "1.2.840.10045.2.1" {
1642                    return Err(InvalidKeyError::InconsistentParameters(
1643                        "RFC 7517: x5c[0] public key algorithm does not match JWK EC key"
1644                            .to_string(),
1645                    )
1646                    .into());
1647                }
1648
1649                let expected_curve_oid = match ec.crv {
1650                    EcCurve::P256 => "1.2.840.10045.3.1.7",
1651                    EcCurve::P384 => "1.3.132.0.34",
1652                    EcCurve::P521 => "1.3.132.0.35",
1653                    EcCurve::Secp256k1 => "1.3.132.0.10",
1654                };
1655
1656                let cert_curve_oid = spki
1657                    .algorithm
1658                    .parameters
1659                    .as_ref()
1660                    .and_then(|p| p.as_oid().ok())
1661                    .map(|oid| oid.to_id_string());
1662
1663                if cert_curve_oid.as_deref() != Some(expected_curve_oid) {
1664                    return Err(InvalidKeyError::InconsistentParameters(
1665                        "RFC 7517: x5c[0] EC curve does not match JWK crv".to_string(),
1666                    )
1667                    .into());
1668                }
1669
1670                let expected_point = ec.to_uncompressed_point();
1671                if cert_key != expected_point.as_slice() {
1672                    return Err(InvalidKeyError::InconsistentParameters(
1673                        "RFC 7517: x5c[0] public key does not match JWK EC key parameters"
1674                            .to_string(),
1675                    )
1676                    .into());
1677                }
1678            }
1679            KeyParams::Okp(okp) => {
1680                let expected_oid = match okp.crv {
1681                    OkpCurve::Ed25519 => "1.3.101.112",
1682                    OkpCurve::Ed448 => "1.3.101.113",
1683                    OkpCurve::X25519 => "1.3.101.110",
1684                    OkpCurve::X448 => "1.3.101.111",
1685                };
1686
1687                if cert_alg_oid != expected_oid {
1688                    return Err(InvalidKeyError::InconsistentParameters(
1689                        "RFC 7517: x5c[0] public key algorithm does not match JWK OKP key"
1690                            .to_string(),
1691                    )
1692                    .into());
1693                }
1694
1695                if cert_key != okp.x.as_bytes() {
1696                    return Err(InvalidKeyError::InconsistentParameters(
1697                        "RFC 7517: x5c[0] public key does not match JWK OKP key parameters"
1698                            .to_string(),
1699                    )
1700                    .into());
1701                }
1702            }
1703            KeyParams::Symmetric(_) => {
1704                return Err(InvalidKeyError::InconsistentParameters(
1705                    "RFC 7517: x5c is not valid for symmetric (oct) keys".to_string(),
1706                )
1707                .into());
1708            }
1709        }
1710
1711        Ok(())
1712    }
1713
1714    #[cfg(test)]
1715    fn validate_algorithm_strength_for_test(&self, alg: &Algorithm) -> Result<()> {
1716        self.validate_algorithm_key_strength(alg)
1717    }
1718
1719    /// Calculates the JWK thumbprint (RFC 7638).
1720    ///
1721    /// The thumbprint is a base64url-encoded SHA-256 hash of the key's
1722    /// required members.
1723    #[must_use]
1724    pub fn thumbprint(&self) -> String {
1725        thumbprint::calculate_thumbprint(self)
1726    }
1727
1728    /// Returns the key as RSA parameters, if applicable.
1729    pub fn as_rsa(&self) -> Option<&RsaParams> {
1730        match &self.params {
1731            KeyParams::Rsa(p) => Some(p),
1732            _ => None,
1733        }
1734    }
1735
1736    /// Returns the key as EC parameters, if applicable.
1737    pub fn as_ec(&self) -> Option<&EcParams> {
1738        match &self.params {
1739            KeyParams::Ec(p) => Some(p),
1740            _ => None,
1741        }
1742    }
1743
1744    /// Returns the key as symmetric parameters, if applicable.
1745    pub fn as_symmetric(&self) -> Option<&SymmetricParams> {
1746        match &self.params {
1747            KeyParams::Symmetric(p) => Some(p),
1748            _ => None,
1749        }
1750    }
1751
1752    /// Returns the key as OKP parameters, if applicable.
1753    pub fn as_okp(&self) -> Option<&OkpParams> {
1754        match &self.params {
1755            KeyParams::Okp(p) => Some(p),
1756            _ => None,
1757        }
1758    }
1759
1760    /// Extracts only the public key components, removing any private key material.
1761    ///
1762    /// Public projection also normalizes `key_ops` to the subset that remains
1763    /// meaningful for a public key (`verify`, `encrypt`, `wrapKey`). Operations
1764    /// that require private or secret key material are removed.
1765    ///
1766    /// For symmetric keys, this returns `None` since symmetric keys don't have
1767    /// a separate public component.
1768    ///
1769    /// # Examples
1770    ///
1771    /// ```
1772    /// use jwk_simple::KeySet;
1773    ///
1774    /// let json = r#"{"keys": [{
1775    ///     "kty": "RSA",
1776    ///     "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
1777    ///     "e": "AQAB",
1778    ///     "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"
1779    /// }]}"#;
1780    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
1781    /// let private_key = jwks.first().unwrap();
1782    ///
1783    /// // Extract public key
1784    /// let public_key = private_key.to_public().expect("RSA keys have public components");
1785    /// assert!(public_key.is_public_key_only());
1786    /// ```
1787    #[must_use]
1788    pub fn to_public(&self) -> Option<Key> {
1789        let public_params = match &self.params {
1790            KeyParams::Rsa(p) => KeyParams::Rsa(p.to_public()),
1791            KeyParams::Ec(p) => KeyParams::Ec(p.to_public()),
1792            KeyParams::Okp(p) => KeyParams::Okp(p.to_public()),
1793            KeyParams::Symmetric(_) => return None, // No public component for symmetric keys
1794        };
1795
1796        Some(Key {
1797            kid: self.kid.clone(),
1798            key_use: self.key_use.clone(),
1799            key_ops: self
1800                .key_ops
1801                .as_ref()
1802                .map(|ops| {
1803                    ops.iter()
1804                        .filter(|op| operation_survives_public_projection(op))
1805                        .cloned()
1806                        .collect::<Vec<_>>()
1807                })
1808                .filter(|ops| !ops.is_empty()),
1809            alg: self.alg.clone(),
1810            params: public_params,
1811            x5c: self.x5c.clone(),
1812            x5t: self.x5t.clone(),
1813            x5t_s256: self.x5t_s256.clone(),
1814            x5u: self.x5u.clone(),
1815        })
1816    }
1817}
1818
1819impl Debug for Key {
1820    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1821        f.debug_struct("Key")
1822            .field("kty", &self.kty())
1823            .field("kid", &self.kid)
1824            .field("key_use", &self.key_use)
1825            .field("alg", &self.alg)
1826            .field("params", &self.params)
1827            .finish()
1828    }
1829}
1830
1831/// Equality is based on the key type, key ID, use, operations, algorithm,
1832/// and key material parameters. X.509 certificate fields (`x5c`, `x5t`,
1833/// `x5t#S256`, `x5u`) are **not** compared, because two representations
1834/// of the same cryptographic key may carry different certificate metadata.
1835///
1836/// # Security Note
1837///
1838/// This comparison is **not** constant-time. It uses short-circuit
1839/// byte-by-byte comparison of key material, including private key
1840/// components. Do not use `==` on [`Key`] values in security-sensitive
1841/// decisions where one side is attacker-controlled, as timing differences
1842/// may leak information about secret key material (CWE-208).
1843///
1844/// For constant-time comparison of symmetric key material, use
1845/// [`SymmetricParams::ct_eq`]. For the underlying byte buffers, use
1846/// [`Base64UrlBytes::ct_eq`](crate::encoding::Base64UrlBytes::ct_eq).
1847impl PartialEq for Key {
1848    fn eq(&self, other: &Self) -> bool {
1849        self.kty() == other.kty()
1850            && self.kid == other.kid
1851            && self.key_use == other.key_use
1852            && self.key_ops == other.key_ops
1853            && self.alg == other.alg
1854            && self.params == other.params
1855    }
1856}
1857
1858impl Eq for Key {}
1859
1860impl Hash for Key {
1861    fn hash<H: Hasher>(&self, state: &mut H) {
1862        // Same fields as PartialEq: kty, kid, key_use, key_ops, alg, params
1863        // Excludes X.509 fields, consistent with the PartialEq contract.
1864        self.kty().hash(state);
1865        self.kid.hash(state);
1866        self.key_use.hash(state);
1867        self.key_ops.hash(state);
1868        self.alg.hash(state);
1869        self.params.hash(state);
1870    }
1871}
1872
1873// Custom serialization for Key that flattens the params
1874impl Serialize for Key {
1875    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1876    where
1877        S: Serializer,
1878    {
1879        use serde::ser::SerializeMap;
1880
1881        // Count the number of fields to serialize
1882        let mut field_count = 1; // kty is always present
1883        if self.kid.is_some() {
1884            field_count += 1;
1885        }
1886        if self.key_use.is_some() {
1887            field_count += 1;
1888        }
1889        if self.key_ops.is_some() {
1890            field_count += 1;
1891        }
1892        if self.alg.is_some() {
1893            field_count += 1;
1894        }
1895        if self.x5c.is_some() {
1896            field_count += 1;
1897        }
1898        if self.x5t.is_some() {
1899            field_count += 1;
1900        }
1901        if self.x5t_s256.is_some() {
1902            field_count += 1;
1903        }
1904        if self.x5u.is_some() {
1905            field_count += 1;
1906        }
1907
1908        // Add params fields
1909        field_count += match &self.params {
1910            KeyParams::Rsa(p) => {
1911                2 + if p.d.is_some() { 1 } else { 0 }
1912                    + if p.p.is_some() { 1 } else { 0 }
1913                    + if p.q.is_some() { 1 } else { 0 }
1914                    + if p.dp.is_some() { 1 } else { 0 }
1915                    + if p.dq.is_some() { 1 } else { 0 }
1916                    + if p.qi.is_some() { 1 } else { 0 }
1917                    + if p.oth.is_some() { 1 } else { 0 }
1918            }
1919            KeyParams::Ec(p) => 3 + if p.d.is_some() { 1 } else { 0 },
1920            KeyParams::Symmetric(_) => 1,
1921            KeyParams::Okp(p) => 2 + if p.d.is_some() { 1 } else { 0 },
1922        };
1923
1924        let mut map = serializer.serialize_map(Some(field_count))?;
1925
1926        // Serialize kty first
1927        map.serialize_entry("kty", self.kty().as_str())?;
1928
1929        // Serialize optional common fields
1930        if let Some(ref kid) = self.kid {
1931            map.serialize_entry("kid", kid)?;
1932        }
1933        if let Some(ref use_) = self.key_use {
1934            map.serialize_entry("use", use_)?;
1935        }
1936        if let Some(ref key_ops) = self.key_ops {
1937            map.serialize_entry("key_ops", key_ops)?;
1938        }
1939        if let Some(ref alg) = self.alg {
1940            map.serialize_entry("alg", alg)?;
1941        }
1942
1943        // Serialize key-specific parameters
1944        match &self.params {
1945            KeyParams::Rsa(p) => {
1946                map.serialize_entry("n", &p.n)?;
1947                map.serialize_entry("e", &p.e)?;
1948                if let Some(ref d) = p.d {
1949                    map.serialize_entry("d", d)?;
1950                }
1951                if let Some(ref p_val) = p.p {
1952                    map.serialize_entry("p", p_val)?;
1953                }
1954                if let Some(ref q) = p.q {
1955                    map.serialize_entry("q", q)?;
1956                }
1957                if let Some(ref dp) = p.dp {
1958                    map.serialize_entry("dp", dp)?;
1959                }
1960                if let Some(ref dq) = p.dq {
1961                    map.serialize_entry("dq", dq)?;
1962                }
1963                if let Some(ref qi) = p.qi {
1964                    map.serialize_entry("qi", qi)?;
1965                }
1966                if let Some(ref oth) = p.oth {
1967                    map.serialize_entry("oth", oth)?;
1968                }
1969            }
1970            KeyParams::Ec(p) => {
1971                map.serialize_entry("crv", &p.crv)?;
1972                map.serialize_entry("x", &p.x)?;
1973                map.serialize_entry("y", &p.y)?;
1974                if let Some(ref d) = p.d {
1975                    map.serialize_entry("d", d)?;
1976                }
1977            }
1978            KeyParams::Symmetric(p) => {
1979                map.serialize_entry("k", &p.k)?;
1980            }
1981            KeyParams::Okp(p) => {
1982                map.serialize_entry("crv", &p.crv)?;
1983                map.serialize_entry("x", &p.x)?;
1984                if let Some(ref d) = p.d {
1985                    map.serialize_entry("d", d)?;
1986                }
1987            }
1988        }
1989
1990        // Serialize X.509 fields
1991        if let Some(ref x5c) = self.x5c {
1992            map.serialize_entry("x5c", x5c)?;
1993        }
1994        if let Some(ref x5t) = self.x5t {
1995            map.serialize_entry("x5t", x5t)?;
1996        }
1997        if let Some(ref x5t_s256) = self.x5t_s256 {
1998            map.serialize_entry("x5t#S256", x5t_s256)?;
1999        }
2000        if let Some(ref x5u) = self.x5u {
2001            map.serialize_entry("x5u", x5u)?;
2002        }
2003
2004        map.end()
2005    }
2006}
2007
2008// Custom deserialization for Key that handles flattened params
2009impl<'de> Deserialize<'de> for Key {
2010    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
2011    where
2012        D: Deserializer<'de>,
2013    {
2014        #[derive(Deserialize)]
2015        struct RawJwk {
2016            kty: String,
2017            kid: Option<String>,
2018            #[serde(rename = "use")]
2019            key_use: Option<KeyUse>,
2020            key_ops: Option<Vec<KeyOperation>>,
2021            alg: Option<Algorithm>,
2022
2023            // RSA parameters
2024            n: Option<Base64UrlBytes>,
2025            e: Option<Base64UrlBytes>,
2026
2027            // EC and OKP parameters
2028            crv: Option<String>,
2029            x: Option<Base64UrlBytes>,
2030            y: Option<Base64UrlBytes>,
2031
2032            // Private key parameter (RSA, EC, OKP)
2033            d: Option<Base64UrlBytes>,
2034
2035            // RSA CRT parameters
2036            p: Option<Base64UrlBytes>,
2037            q: Option<Base64UrlBytes>,
2038            dp: Option<Base64UrlBytes>,
2039            dq: Option<Base64UrlBytes>,
2040            qi: Option<Base64UrlBytes>,
2041
2042            // RSA multi-prime parameter
2043            oth: Option<Vec<rsa::RsaOtherPrime>>,
2044
2045            // Symmetric key
2046            k: Option<Base64UrlBytes>,
2047
2048            // X.509 fields
2049            x5c: Option<Vec<String>>,
2050            x5t: Option<String>,
2051            #[serde(rename = "x5t#S256")]
2052            x5t_s256: Option<String>,
2053            x5u: Option<String>,
2054        }
2055
2056        let raw = RawJwk::deserialize(deserializer)?;
2057
2058        let kty: KeyType = raw.kty.parse().map_err(serde::de::Error::custom)?;
2059
2060        let params = match kty {
2061            KeyType::Rsa => {
2062                let n = raw.n.ok_or_else(|| serde::de::Error::missing_field("n"))?;
2063                let e = raw.e.ok_or_else(|| serde::de::Error::missing_field("e"))?;
2064
2065                KeyParams::Rsa(RsaParams {
2066                    n,
2067                    e,
2068                    d: raw.d,
2069                    p: raw.p,
2070                    q: raw.q,
2071                    dp: raw.dp,
2072                    dq: raw.dq,
2073                    qi: raw.qi,
2074                    oth: raw.oth,
2075                })
2076            }
2077            KeyType::Ec => {
2078                let crv_str = raw
2079                    .crv
2080                    .ok_or_else(|| serde::de::Error::missing_field("crv"))?;
2081                let crv: EcCurve = crv_str.parse().map_err(serde::de::Error::custom)?;
2082                let x = raw.x.ok_or_else(|| serde::de::Error::missing_field("x"))?;
2083                let y = raw.y.ok_or_else(|| serde::de::Error::missing_field("y"))?;
2084
2085                KeyParams::Ec(EcParams {
2086                    crv,
2087                    x,
2088                    y,
2089                    d: raw.d,
2090                })
2091            }
2092            KeyType::Symmetric => {
2093                let k = raw.k.ok_or_else(|| serde::de::Error::missing_field("k"))?;
2094
2095                KeyParams::Symmetric(SymmetricParams { k })
2096            }
2097            KeyType::Okp => {
2098                let crv_str = raw
2099                    .crv
2100                    .ok_or_else(|| serde::de::Error::missing_field("crv"))?;
2101                let crv: OkpCurve = crv_str.parse().map_err(serde::de::Error::custom)?;
2102                let x = raw.x.ok_or_else(|| serde::de::Error::missing_field("x"))?;
2103
2104                KeyParams::Okp(OkpParams { crv, x, d: raw.d })
2105            }
2106        };
2107
2108        Ok(Key {
2109            kid: raw.kid,
2110            key_use: raw.key_use,
2111            key_ops: raw.key_ops,
2112            alg: raw.alg,
2113            params,
2114            x5c: raw.x5c,
2115            x5t: raw.x5t,
2116            x5t_s256: raw.x5t_s256,
2117            x5u: raw.x5u,
2118        })
2119    }
2120}
2121
2122/// Checks whether a `use` value is consistent with a set of `key_ops` values.
2123///
2124/// Per RFC 7517 Section 4.3, if both `use` and `key_ops` are present, the information
2125/// they convey MUST be consistent. The natural mapping is:
2126/// - `sig` is consistent with `sign` and `verify`
2127/// - `enc` is consistent with `encrypt`, `decrypt`, `wrapKey`, `unwrapKey`,
2128///   `deriveKey`, and `deriveBits`
2129///
2130/// For unknown `use` values, consistency cannot be determined, so we accept them.
2131fn is_use_consistent_with_ops(key_use: &KeyUse, key_ops: &[KeyOperation]) -> bool {
2132    // Empty key_ops is trivially consistent (no operations claimed).
2133    if key_ops.is_empty() {
2134        return true;
2135    }
2136
2137    match key_use {
2138        KeyUse::Signature => key_ops.iter().all(|op| {
2139            matches!(
2140                op,
2141                KeyOperation::Sign | KeyOperation::Verify | KeyOperation::Unknown(_)
2142            )
2143        }),
2144        KeyUse::Encryption => key_ops.iter().all(|op| {
2145            matches!(
2146                op,
2147                KeyOperation::Encrypt
2148                    | KeyOperation::Decrypt
2149                    | KeyOperation::WrapKey
2150                    | KeyOperation::UnwrapKey
2151                    | KeyOperation::DeriveKey
2152                    | KeyOperation::DeriveBits
2153                    | KeyOperation::Unknown(_)
2154            )
2155        }),
2156        // Unknown use values: we can't determine consistency, so accept.
2157        KeyUse::Unknown(_) => true,
2158    }
2159}
2160
2161fn is_operation_allowed_by_use(key_use: &KeyUse, operation: &KeyOperation) -> bool {
2162    if matches!(operation, KeyOperation::Unknown(_)) {
2163        return true;
2164    }
2165
2166    match key_use {
2167        KeyUse::Signature => is_signature_operation(operation),
2168        KeyUse::Encryption => is_encryption_operation(operation),
2169        KeyUse::Unknown(_) => true,
2170    }
2171}
2172
2173fn is_signature_operation(operation: &KeyOperation) -> bool {
2174    matches!(operation, KeyOperation::Sign | KeyOperation::Verify)
2175}
2176
2177fn is_encryption_operation(operation: &KeyOperation) -> bool {
2178    matches!(
2179        operation,
2180        KeyOperation::Encrypt
2181            | KeyOperation::Decrypt
2182            | KeyOperation::WrapKey
2183            | KeyOperation::UnwrapKey
2184            | KeyOperation::DeriveKey
2185            | KeyOperation::DeriveBits
2186    )
2187}
2188
2189fn operation_survives_public_projection(operation: &KeyOperation) -> bool {
2190    matches!(
2191        operation,
2192        KeyOperation::Verify | KeyOperation::Encrypt | KeyOperation::WrapKey
2193    )
2194}
2195
2196impl Key {
2197    fn validate_declared_algorithm_match(&self, requested_alg: &Algorithm) -> Result<()> {
2198        if let Some(declared_alg) = self.alg()
2199            && declared_alg != requested_alg
2200        {
2201            return Err(Error::IncompatibleKey(
2202                IncompatibleKeyError::AlgorithmMismatch {
2203                    requested: requested_alg.clone(),
2204                    declared: declared_alg.clone(),
2205                },
2206            ));
2207        }
2208
2209        Ok(())
2210    }
2211}
2212
2213pub(crate) fn is_operation_compatible_with_algorithm(
2214    operation: &KeyOperation,
2215    alg: &Algorithm,
2216) -> bool {
2217    if matches!(operation, KeyOperation::Unknown(_)) {
2218        return true;
2219    }
2220
2221    debug_assert!(
2222        !alg.is_unknown(),
2223        "unknown algorithms should be rejected before operation/algorithm compatibility checks"
2224    );
2225
2226    match alg {
2227        Algorithm::Rs256
2228        | Algorithm::Rs384
2229        | Algorithm::Rs512
2230        | Algorithm::Ps256
2231        | Algorithm::Ps384
2232        | Algorithm::Ps512
2233        | Algorithm::Es256
2234        | Algorithm::Es384
2235        | Algorithm::Es512
2236        | Algorithm::Es256k
2237        | Algorithm::EdDsa
2238        | Algorithm::Ed25519
2239        | Algorithm::Ed448
2240        | Algorithm::Hs256
2241        | Algorithm::Hs384
2242        | Algorithm::Hs512 => matches!(operation, KeyOperation::Sign | KeyOperation::Verify),
2243        Algorithm::RsaOaep
2244        | Algorithm::RsaOaep256
2245        | Algorithm::RsaOaep384
2246        | Algorithm::RsaOaep512
2247        | Algorithm::Rsa1_5 => matches!(
2248            operation,
2249            KeyOperation::Encrypt
2250                | KeyOperation::Decrypt
2251                | KeyOperation::WrapKey
2252                | KeyOperation::UnwrapKey
2253        ),
2254        Algorithm::A128kw
2255        | Algorithm::A192kw
2256        | Algorithm::A256kw
2257        | Algorithm::A128gcmkw
2258        | Algorithm::A192gcmkw
2259        | Algorithm::A256gcmkw
2260        | Algorithm::Pbes2Hs256A128kw
2261        | Algorithm::Pbes2Hs384A192kw
2262        | Algorithm::Pbes2Hs512A256kw => {
2263            matches!(operation, KeyOperation::WrapKey | KeyOperation::UnwrapKey)
2264        }
2265        Algorithm::Dir
2266        | Algorithm::A128cbcHs256
2267        | Algorithm::A192cbcHs384
2268        | Algorithm::A256cbcHs512
2269        | Algorithm::A128gcm
2270        | Algorithm::A192gcm
2271        | Algorithm::A256gcm => matches!(operation, KeyOperation::Encrypt | KeyOperation::Decrypt),
2272        Algorithm::EcdhEs
2273        | Algorithm::EcdhEsA128kw
2274        | Algorithm::EcdhEsA192kw
2275        | Algorithm::EcdhEsA256kw => {
2276            matches!(
2277                operation,
2278                KeyOperation::DeriveKey | KeyOperation::DeriveBits
2279            )
2280        }
2281        // Defensive fallback for future callers; current strict validation paths
2282        // reject unknown algorithms before reaching this helper.
2283        Algorithm::Unknown(_) => false,
2284    }
2285}
2286
2287/// Encodes a byte slice as a DER INTEGER.
2288fn encode_der_integer(bytes: &[u8]) -> Vec<u8> {
2289    if bytes.is_empty() {
2290        return vec![0x02, 0x01, 0x00];
2291    }
2292
2293    let bytes = {
2294        let mut start = 0;
2295        while start < bytes.len() - 1 && bytes[start] == 0 {
2296            start += 1;
2297        }
2298        &bytes[start..]
2299    };
2300
2301    let needs_padding = (bytes[0] & 0x80) != 0;
2302    let len = bytes.len() + if needs_padding { 1 } else { 0 };
2303
2304    let mut der = Vec::with_capacity(2 + len + 2);
2305    der.push(0x02);
2306    encode_der_length(&mut der, len);
2307    if needs_padding {
2308        der.push(0);
2309    }
2310    der.extend_from_slice(bytes);
2311    der
2312}
2313
2314/// Encodes a DER length value.
2315fn encode_der_length(der: &mut Vec<u8>, len: usize) {
2316    if len < 128 {
2317        der.push(len as u8);
2318    } else if len < 256 {
2319        der.push(0x81);
2320        der.push(len as u8);
2321    } else if len < 65536 {
2322        der.push(0x82);
2323        der.push((len >> 8) as u8);
2324        der.push(len as u8);
2325    } else {
2326        der.push(0x83);
2327        der.push((len >> 16) as u8);
2328        der.push((len >> 8) as u8);
2329        der.push(len as u8);
2330    }
2331}
2332
2333/// Encodes PKCS#1 RSAPublicKey DER (`SEQUENCE { n INTEGER, e INTEGER }`).
2334fn encode_rsa_public_key_der(n: &[u8], e: &[u8]) -> Vec<u8> {
2335    let n_der = encode_der_integer(n);
2336    let e_der = encode_der_integer(e);
2337
2338    let content_len = n_der.len() + e_der.len();
2339    let mut der = Vec::with_capacity(4 + content_len);
2340    der.push(0x30);
2341    encode_der_length(&mut der, content_len);
2342    der.extend_from_slice(&n_der);
2343    der.extend_from_slice(&e_der);
2344    der
2345}
2346
2347/// Validates that a string is valid standard base64 encoding.
2348/// This checks the character set (A-Z, a-z, 0-9, +, /) and padding (=).
2349fn is_valid_base64(s: &str) -> bool {
2350    if s.is_empty() {
2351        return false;
2352    }
2353
2354    let bytes = s.as_bytes();
2355    let mut padding_started = false;
2356
2357    for (i, &b) in bytes.iter().enumerate() {
2358        match b {
2359            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'+' | b'/' => {
2360                if padding_started {
2361                    return false; // No data after padding
2362                }
2363            }
2364            b'=' => {
2365                padding_started = true;
2366                // Padding can only be at the end, max 2 characters
2367                let remaining = bytes.len() - i;
2368                if remaining > 2 {
2369                    return false;
2370                }
2371            }
2372            _ => return false, // Invalid character
2373        }
2374    }
2375
2376    // Valid base64 length is multiple of 4 when padded
2377    // Or when unpadded, (len % 4) should be 0, 2, or 3
2378    let len = s.len();
2379    if padding_started {
2380        len.is_multiple_of(4)
2381    } else {
2382        // Unpadded base64 can have len % 4 of 0, 2, or 3 (not 1)
2383        len % 4 != 1
2384    }
2385}
2386
2387/// Validates an X.509 certificate thumbprint (x5t or x5t#S256).
2388///
2389/// RFC 7517 Section 4.8 and 4.9: These are base64url-encoded certificate digests.
2390/// - x5t: SHA-1 digest (20 bytes)
2391/// - x5t#S256: SHA-256 digest (32 bytes)
2392fn validate_x509_thumbprint(
2393    thumbprint: &str,
2394    param_name: &'static str,
2395    expected_bytes: usize,
2396) -> Result<()> {
2397    use base64ct::{Base64UrlUnpadded, Encoding};
2398
2399    // Validate it's valid base64url
2400    if !is_valid_base64url(thumbprint) {
2401        return Err(InvalidKeyError::InvalidParameter {
2402            name: param_name,
2403            reason: format!(
2404                "RFC 7517: {} must be base64url-encoded (invalid characters found)",
2405                param_name
2406            ),
2407        }
2408        .into());
2409    }
2410
2411    // Try to decode and check the length
2412    match Base64UrlUnpadded::decode_vec(thumbprint) {
2413        Ok(decoded) => {
2414            if decoded.len() != expected_bytes {
2415                return Err(InvalidKeyError::InvalidParameter {
2416                    name: param_name,
2417                    reason: format!(
2418                        "RFC 7517: {} must be {} bytes when decoded (got {} bytes)",
2419                        param_name,
2420                        expected_bytes,
2421                        decoded.len()
2422                    ),
2423                }
2424                .into());
2425            }
2426            Ok(())
2427        }
2428        Err(_) => Err(InvalidKeyError::InvalidParameter {
2429            name: param_name,
2430            reason: format!("RFC 7517: {} failed base64url decoding", param_name),
2431        }
2432        .into()),
2433    }
2434}
2435
2436/// Validates that a string is valid base64url encoding.
2437/// This checks the character set (A-Z, a-z, 0-9, -, _) without padding.
2438fn is_valid_base64url(s: &str) -> bool {
2439    if s.is_empty() {
2440        return false;
2441    }
2442
2443    for b in s.bytes() {
2444        match b {
2445            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' => {}
2446            _ => return false,
2447        }
2448    }
2449
2450    // Valid base64url without padding has len % 4 of 0, 2, or 3 (not 1)
2451    s.len() % 4 != 1
2452}
2453
2454#[cfg(test)]
2455mod tests {
2456    use super::*;
2457
2458    #[test]
2459    fn test_parse_rsa_public_key() {
2460        let json = r#"{
2461            "kty": "RSA",
2462            "kid": "test-key",
2463            "use": "sig",
2464            "alg": "RS256",
2465            "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
2466            "e": "AQAB"
2467        }"#;
2468
2469        let jwk: Key = serde_json::from_str(json).unwrap();
2470        assert_eq!(jwk.kty(), KeyType::Rsa);
2471        assert_eq!(jwk.kid, Some("test-key".to_string()));
2472        assert_eq!(jwk.key_use, Some(KeyUse::Signature));
2473        assert_eq!(jwk.alg, Some(Algorithm::Rs256));
2474        assert!(jwk.is_public_key_only());
2475    }
2476
2477    #[test]
2478    fn test_parse_ec_public_key() {
2479        let json = r#"{
2480            "kty": "EC",
2481            "crv": "P-256",
2482            "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
2483            "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
2484        }"#;
2485
2486        let jwk: Key = serde_json::from_str(json).unwrap();
2487        assert_eq!(jwk.kty(), KeyType::Ec);
2488
2489        let ec = jwk.as_ec().unwrap();
2490        assert_eq!(ec.crv, EcCurve::P256);
2491        assert!(jwk.is_public_key_only());
2492    }
2493
2494    #[test]
2495    fn test_parse_symmetric_key() {
2496        let json = r#"{
2497            "kty": "oct",
2498            "k": "GawgguFyGrWKav7AX4VKUg"
2499        }"#;
2500
2501        let jwk: Key = serde_json::from_str(json).unwrap();
2502        assert_eq!(jwk.kty(), KeyType::Symmetric);
2503        assert!(jwk.as_symmetric().is_some());
2504    }
2505
2506    #[test]
2507    fn test_parse_okp_key() {
2508        let json = r#"{
2509            "kty": "OKP",
2510            "crv": "Ed25519",
2511            "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
2512        }"#;
2513
2514        let jwk: Key = serde_json::from_str(json).unwrap();
2515        assert_eq!(jwk.kty(), KeyType::Okp);
2516
2517        let okp = jwk.as_okp().unwrap();
2518        assert_eq!(okp.crv, OkpCurve::Ed25519);
2519    }
2520
2521    #[test]
2522    fn test_roundtrip_serialization() {
2523        let json = r#"{"kty":"RSA","kid":"test","use":"sig","n":"AQAB","e":"AQAB"}"#;
2524        let jwk: Key = serde_json::from_str(json).unwrap();
2525        let serialized = serde_json::to_string(&jwk).unwrap();
2526        let deserialized: Key = serde_json::from_str(&serialized).unwrap();
2527        assert_eq!(jwk, deserialized);
2528    }
2529
2530    #[test]
2531    fn test_is_algorithm_compatible_rsa() {
2532        let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
2533        let key: Key = serde_json::from_str(json).unwrap();
2534
2535        // RSA key should be compatible with RSA algorithms
2536        assert!(key.is_algorithm_compatible(&Algorithm::Rs256));
2537        assert!(key.is_algorithm_compatible(&Algorithm::Rs384));
2538        assert!(key.is_algorithm_compatible(&Algorithm::Rs512));
2539        assert!(key.is_algorithm_compatible(&Algorithm::Ps256));
2540        assert!(key.is_algorithm_compatible(&Algorithm::Ps384));
2541        assert!(key.is_algorithm_compatible(&Algorithm::Ps512));
2542        assert!(key.is_algorithm_compatible(&Algorithm::RsaOaep));
2543        assert!(key.is_algorithm_compatible(&Algorithm::RsaOaep256));
2544
2545        // RSA key should NOT be compatible with other algorithms
2546        assert!(!key.is_algorithm_compatible(&Algorithm::Es256));
2547        assert!(!key.is_algorithm_compatible(&Algorithm::Hs256));
2548        assert!(!key.is_algorithm_compatible(&Algorithm::EdDsa));
2549        assert!(!key.is_algorithm_compatible(&Algorithm::Ed25519));
2550        assert!(!key.is_algorithm_compatible(&Algorithm::Ed448));
2551    }
2552
2553    #[test]
2554    fn test_is_algorithm_compatible_ec() {
2555        let p256_json = r#"{"kty":"EC","crv":"P-256","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}"#;
2556        let p256: Key = serde_json::from_str(p256_json).unwrap();
2557
2558        assert!(p256.is_algorithm_compatible(&Algorithm::Es256));
2559        assert!(!p256.is_algorithm_compatible(&Algorithm::Es384));
2560        assert!(!p256.is_algorithm_compatible(&Algorithm::Rs256));
2561        assert!(!p256.is_algorithm_compatible(&Algorithm::Hs256));
2562    }
2563
2564    #[test]
2565    fn test_is_algorithm_compatible_symmetric() {
2566        let json = r#"{"kty":"oct","k":"GawgguFyGrWKav7AX4VKUg"}"#;
2567        let key: Key = serde_json::from_str(json).unwrap();
2568
2569        assert!(key.is_algorithm_compatible(&Algorithm::Hs256));
2570        assert!(key.is_algorithm_compatible(&Algorithm::Hs384));
2571        assert!(key.is_algorithm_compatible(&Algorithm::Hs512));
2572        assert!(key.is_algorithm_compatible(&Algorithm::A128kw));
2573
2574        assert!(!key.is_algorithm_compatible(&Algorithm::Rs256));
2575        assert!(!key.is_algorithm_compatible(&Algorithm::Es256));
2576    }
2577
2578    #[test]
2579    fn test_is_algorithm_compatible_okp() {
2580        let json =
2581            r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#;
2582        let key: Key = serde_json::from_str(json).unwrap();
2583
2584        assert!(key.is_algorithm_compatible(&Algorithm::EdDsa));
2585        assert!(key.is_algorithm_compatible(&Algorithm::Ed25519));
2586        assert!(!key.is_algorithm_compatible(&Algorithm::Ed448));
2587        assert!(!key.is_algorithm_compatible(&Algorithm::Rs256));
2588        assert!(!key.is_algorithm_compatible(&Algorithm::Es256));
2589    }
2590
2591    #[test]
2592    fn test_is_algorithm_compatible_okp_ed448() {
2593        let json = r#"{
2594            "kty": "OKP",
2595            "crv": "Ed448",
2596            "x": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
2597        }"#;
2598        let key: Key = serde_json::from_str(json).unwrap();
2599
2600        assert!(key.is_algorithm_compatible(&Algorithm::EdDsa));
2601        assert!(!key.is_algorithm_compatible(&Algorithm::Ed25519));
2602        assert!(key.is_algorithm_compatible(&Algorithm::Ed448));
2603    }
2604
2605    #[test]
2606    fn test_parse_rfc9864_ed_algorithms() {
2607        assert_eq!(Algorithm::from("Ed25519"), Algorithm::Ed25519);
2608        assert_eq!(Algorithm::from("Ed448"), Algorithm::Ed448);
2609        assert_eq!("Ed25519".parse::<Algorithm>().unwrap(), Algorithm::Ed25519);
2610        assert_eq!("Ed448".parse::<Algorithm>().unwrap(), Algorithm::Ed448);
2611        assert_eq!(Algorithm::Ed25519.as_str(), "Ed25519");
2612        assert_eq!(Algorithm::Ed448.as_str(), "Ed448");
2613    }
2614
2615    #[test]
2616    fn test_parse_key_use_and_operation_with_from_str() {
2617        assert_eq!("sig".parse::<KeyUse>().unwrap(), KeyUse::Signature);
2618        assert_eq!("enc".parse::<KeyUse>().unwrap(), KeyUse::Encryption);
2619        assert_eq!(
2620            "private-use".parse::<KeyUse>().unwrap(),
2621            KeyUse::Unknown("private-use".to_string())
2622        );
2623
2624        assert_eq!("sign".parse::<KeyOperation>().unwrap(), KeyOperation::Sign);
2625        assert_eq!(
2626            "verify".parse::<KeyOperation>().unwrap(),
2627            KeyOperation::Verify
2628        );
2629        assert_eq!(
2630            "custom-op".parse::<KeyOperation>().unwrap(),
2631            KeyOperation::Unknown("custom-op".to_string())
2632        );
2633    }
2634
2635    #[test]
2636    fn test_algorithm_deprecation_status() {
2637        assert!(Algorithm::EdDsa.is_deprecated());
2638        assert!(!Algorithm::Ed25519.is_deprecated());
2639        assert!(!Algorithm::Ed448.is_deprecated());
2640        assert!(!Algorithm::Rs256.is_deprecated());
2641    }
2642
2643    #[test]
2644    fn test_is_algorithm_compatible_unknown_algorithm() {
2645        let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
2646        let key: Key = serde_json::from_str(json).unwrap();
2647
2648        assert!(!key.is_algorithm_compatible(&Algorithm::Unknown("CUSTOM".to_string())));
2649    }
2650
2651    #[test]
2652    fn test_to_public_rsa() {
2653        let json = r#"{
2654            "kty": "RSA",
2655            "kid": "rsa-key",
2656            "use": "sig",
2657            "alg": "RS256",
2658            "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
2659            "e": "AQAB",
2660            "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"
2661        }"#;
2662        let private_key: Key = serde_json::from_str(json).unwrap();
2663        assert!(private_key.has_private_key());
2664
2665        let public_key = private_key.to_public().unwrap();
2666        assert!(public_key.is_public_key_only());
2667        assert_eq!(public_key.kty(), KeyType::Rsa);
2668        assert_eq!(public_key.kid, Some("rsa-key".to_string()));
2669        assert_eq!(public_key.key_use, Some(KeyUse::Signature));
2670        assert_eq!(public_key.alg, Some(Algorithm::Rs256));
2671
2672        let rsa = public_key.as_rsa().unwrap();
2673        assert!(rsa.d.is_none());
2674        // Modulus should be preserved
2675        assert_eq!(rsa.n, private_key.as_rsa().unwrap().n);
2676    }
2677
2678    #[test]
2679    fn test_to_public_ec() {
2680        let json = r#"{
2681            "kty": "EC",
2682            "crv": "P-256",
2683            "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
2684            "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
2685            "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"
2686        }"#;
2687        let private_key: Key = serde_json::from_str(json).unwrap();
2688        assert!(private_key.has_private_key());
2689
2690        let public_key = private_key.to_public().unwrap();
2691        assert!(public_key.is_public_key_only());
2692        assert_eq!(public_key.kty(), KeyType::Ec);
2693
2694        let ec = public_key.as_ec().unwrap();
2695        assert!(ec.d.is_none());
2696        assert_eq!(ec.crv, EcCurve::P256);
2697    }
2698
2699    #[test]
2700    fn test_to_public_okp() {
2701        let json = r#"{
2702            "kty": "OKP",
2703            "crv": "Ed25519",
2704            "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
2705            "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A"
2706        }"#;
2707        let private_key: Key = serde_json::from_str(json).unwrap();
2708        assert!(private_key.has_private_key());
2709
2710        let public_key = private_key.to_public().unwrap();
2711        assert!(public_key.is_public_key_only());
2712        assert_eq!(public_key.kty(), KeyType::Okp);
2713
2714        let okp = public_key.as_okp().unwrap();
2715        assert!(okp.d.is_none());
2716        assert_eq!(okp.crv, OkpCurve::Ed25519);
2717    }
2718
2719    #[test]
2720    fn test_to_public_symmetric_returns_none() {
2721        let json = r#"{"kty":"oct","k":"GawgguFyGrWKav7AX4VKUg"}"#;
2722        let key: Key = serde_json::from_str(json).unwrap();
2723        assert!(key.to_public().is_none());
2724    }
2725
2726    #[test]
2727    fn test_to_public_already_public() {
2728        let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
2729        let key: Key = serde_json::from_str(json).unwrap();
2730        assert!(key.is_public_key_only());
2731
2732        let public = key.to_public().unwrap();
2733        assert!(public.is_public_key_only());
2734        assert_eq!(key, public);
2735    }
2736
2737    #[test]
2738    fn test_to_public_filters_private_only_key_ops() {
2739        let key = Key::new(KeyParams::Rsa(RsaParams::new_private(
2740            Base64UrlBytes::new(vec![0x01; 256]),
2741            Base64UrlBytes::new(vec![0x01, 0x00, 0x01]),
2742            Base64UrlBytes::new(vec![0x02; 256]),
2743            None,
2744            None,
2745            None,
2746            None,
2747            None,
2748        )))
2749        .with_key_ops([
2750            KeyOperation::Sign,
2751            KeyOperation::Verify,
2752            KeyOperation::Decrypt,
2753            KeyOperation::Encrypt,
2754            KeyOperation::WrapKey,
2755            KeyOperation::UnwrapKey,
2756            KeyOperation::DeriveKey,
2757            KeyOperation::Unknown("custom".into()),
2758        ]);
2759
2760        let public = key.to_public().unwrap();
2761
2762        assert_eq!(
2763            public.key_ops(),
2764            Some(
2765                &[
2766                    KeyOperation::Verify,
2767                    KeyOperation::Encrypt,
2768                    KeyOperation::WrapKey,
2769                ][..]
2770            )
2771        );
2772    }
2773
2774    #[test]
2775    fn test_to_public_clears_empty_key_ops_after_projection() {
2776        let key = Key::new(KeyParams::Ec(EcParams::new_private(
2777            EcCurve::P256,
2778            Base64UrlBytes::new(vec![0x01; 32]),
2779            Base64UrlBytes::new(vec![0x02; 32]),
2780            Base64UrlBytes::new(vec![0x03; 32]),
2781        )))
2782        .with_key_ops([KeyOperation::Sign]);
2783
2784        let public = key.to_public().unwrap();
2785
2786        assert_eq!(public.key_ops(), None);
2787    }
2788
2789    #[test]
2790    fn test_validate_algorithm_strength_enforces_cbc_hs_sizes() {
2791        let k256 = Base64UrlBytes::new(vec![0u8; 32]);
2792        let k384 = Base64UrlBytes::new(vec![0u8; 48]);
2793        let k512 = Base64UrlBytes::new(vec![0u8; 64]);
2794
2795        let key_256 = Key::new(KeyParams::Symmetric(SymmetricParams::new(k256)));
2796        assert!(
2797            key_256
2798                .validate_algorithm_strength_for_test(&Algorithm::A128cbcHs256)
2799                .is_ok()
2800        );
2801        assert!(
2802            key_256
2803                .validate_algorithm_strength_for_test(&Algorithm::A192cbcHs384)
2804                .is_err()
2805        );
2806
2807        let key_384 = Key::new(KeyParams::Symmetric(SymmetricParams::new(k384)));
2808        assert!(
2809            key_384
2810                .validate_algorithm_strength_for_test(&Algorithm::A192cbcHs384)
2811                .is_ok()
2812        );
2813        assert!(
2814            key_384
2815                .validate_algorithm_strength_for_test(&Algorithm::A256cbcHs512)
2816                .is_err()
2817        );
2818
2819        let key_512 = Key::new(KeyParams::Symmetric(SymmetricParams::new(k512)));
2820        assert!(
2821            key_512
2822                .validate_algorithm_strength_for_test(&Algorithm::A256cbcHs512)
2823                .is_ok()
2824        );
2825    }
2826    #[test]
2827    fn test_check_algorithm_suitability_enforces_strength_without_key_alg() {
2828        let weak_hmac_key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
2829            Base64UrlBytes::new(vec![0u8; 31]),
2830        )));
2831
2832        // Baseline JWK validation (structure) still passes: key is structurally valid.
2833        assert!(weak_hmac_key.validate().is_ok());
2834
2835        // Algorithm suitability check enforces HS256 minimum key strength.
2836        assert!(
2837            weak_hmac_key
2838                .check_algorithm_suitability(&Algorithm::Hs256)
2839                .is_err()
2840        );
2841    }
2842
2843    #[test]
2844    fn test_check_operation_intent_enforces_use_when_present() {
2845        let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
2846            Base64UrlBytes::new(vec![0u8; 32]),
2847        )))
2848        .with_use(KeyUse::Encryption);
2849
2850        assert!(key.check_operation_intent(&[KeyOperation::Encrypt]).is_ok());
2851        assert!(key.check_operation_intent(&[KeyOperation::Sign]).is_err());
2852    }
2853
2854    #[test]
2855    fn test_check_operation_intent_enforces_key_ops_when_present() {
2856        let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
2857            Base64UrlBytes::new(vec![1, 2, 3]),
2858            Base64UrlBytes::new(vec![1, 0, 1]),
2859        )))
2860        .with_key_ops(vec![KeyOperation::Verify]);
2861
2862        assert!(key.check_operation_intent(&[KeyOperation::Verify]).is_ok());
2863        assert!(key.check_operation_intent(&[KeyOperation::Sign]).is_err());
2864    }
2865
2866    #[test]
2867    fn test_check_operation_intent_allows_missing_optional_fields() {
2868        let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
2869            Base64UrlBytes::new(vec![1, 2, 3]),
2870            Base64UrlBytes::new(vec![1, 0, 1]),
2871        )));
2872
2873        assert!(key.check_operation_intent(&[KeyOperation::Verify]).is_ok());
2874        assert!(key.check_operation_intent(&[KeyOperation::Sign]).is_ok());
2875    }
2876
2877    #[test]
2878    fn test_validate_for_use_rejects_empty_operation_set() {
2879        let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
2880            Base64UrlBytes::new(vec![1, 2, 3]),
2881            Base64UrlBytes::new(vec![1, 0, 1]),
2882        )));
2883
2884        let result = key.validate_for_use(&Algorithm::Rs256, vec![]);
2885        assert!(matches!(result, Err(Error::InvalidInput(_))));
2886    }
2887
2888    #[test]
2889    fn test_validate_for_use_rejects_sign_with_public_rsa_key() {
2890        let mut n = vec![0xff; 256];
2891        n[255] = 0x01;
2892        let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
2893            Base64UrlBytes::new(n),
2894            Base64UrlBytes::new(vec![1, 0, 1]),
2895        )));
2896
2897        let result = key.validate_for_use(&Algorithm::Rs256, [KeyOperation::Sign]);
2898        assert!(matches!(
2899            result,
2900            Err(Error::IncompatibleKey(
2901                IncompatibleKeyError::OperationNotPermitted { .. }
2902            ))
2903        ));
2904    }
2905
2906    #[test]
2907    fn test_validate_for_use_rejects_sign_with_public_okp_key() {
2908        let key = Key::new(KeyParams::Okp(OkpParams::new_public(
2909            OkpCurve::Ed25519,
2910            Base64UrlBytes::new(vec![0u8; 32]),
2911        )));
2912
2913        let result = key.validate_for_use(&Algorithm::Ed25519, [KeyOperation::Sign]);
2914        assert!(matches!(
2915            result,
2916            Err(Error::IncompatibleKey(
2917                IncompatibleKeyError::OperationNotPermitted { .. }
2918            ))
2919        ));
2920    }
2921
2922    #[test]
2923    fn test_validate_for_use_rejects_sign_with_public_ec_key() {
2924        let key = Key::new(KeyParams::Ec(EcParams::new_public(
2925            EcCurve::P256,
2926            Base64UrlBytes::new(vec![0u8; 32]),
2927            Base64UrlBytes::new(vec![0u8; 32]),
2928        )));
2929
2930        let result = key.validate_for_use(&Algorithm::Es256, [KeyOperation::Sign]);
2931        assert!(matches!(
2932            result,
2933            Err(Error::IncompatibleKey(
2934                IncompatibleKeyError::OperationNotPermitted { .. }
2935            ))
2936        ));
2937    }
2938
2939    #[test]
2940    fn test_validate_for_use_rejects_unknown_algorithm() {
2941        let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
2942            Base64UrlBytes::new(vec![0u8; 32]),
2943        )));
2944
2945        let result = key.validate_for_use(
2946            &Algorithm::Unknown("CUSTOM-ALG".to_string()),
2947            [KeyOperation::Sign],
2948        );
2949        assert!(matches!(
2950            result,
2951            Err(Error::IncompatibleKey(
2952                IncompatibleKeyError::IncompatibleAlgorithm { .. }
2953            ))
2954        ));
2955    }
2956
2957    #[test]
2958    fn test_validate_for_use_rejects_operation_algorithm_mismatch() {
2959        let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
2960            Base64UrlBytes::new(vec![0u8; 32]),
2961        )));
2962
2963        let result = key.validate_for_use(&Algorithm::Hs256, [KeyOperation::Encrypt]);
2964        assert!(matches!(
2965            result,
2966            Err(Error::IncompatibleKey(
2967                IncompatibleKeyError::OperationNotPermitted { .. }
2968            ))
2969        ));
2970    }
2971
2972    #[test]
2973    fn test_validate_for_use_rejects_declared_algorithm_mismatch() {
2974        let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
2975            Base64UrlBytes::new(vec![0u8; 32]),
2976        )))
2977        .with_alg(Algorithm::Hs256);
2978
2979        let result = key.validate_for_use(&Algorithm::Hs384, [KeyOperation::Sign]);
2980        assert!(matches!(
2981            result,
2982            Err(Error::IncompatibleKey(
2983                IncompatibleKeyError::AlgorithmMismatch { .. }
2984            ))
2985        ));
2986    }
2987
2988    #[test]
2989    fn test_check_operation_intent_rejects_inconsistent_use_and_key_ops() {
2990        let key = Key::new(KeyParams::Rsa(RsaParams::new_public(
2991            Base64UrlBytes::new(vec![1, 2, 3]),
2992            Base64UrlBytes::new(vec![1, 0, 1]),
2993        )))
2994        .with_use(KeyUse::Signature)
2995        .with_key_ops(vec![KeyOperation::Encrypt]);
2996
2997        assert!(key.check_operation_intent(&[KeyOperation::Verify]).is_err());
2998    }
2999
3000    #[test]
3001    fn test_check_operation_intent_allows_unknown_operation_with_use() {
3002        let key = Key::new(KeyParams::Symmetric(SymmetricParams::new(
3003            Base64UrlBytes::new(vec![0u8; 32]),
3004        )))
3005        .with_use(KeyUse::Signature);
3006
3007        let result = key.check_operation_intent(&[KeyOperation::Unknown("custom-op".into())]);
3008        assert!(result.is_ok());
3009    }
3010}