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::jwk::Key;
19//!
20//! let json = r#"{
21//!     "kty": "RSA",
22//!     "kid": "my-key-id",
23//!     "use": "sig",
24//!     "alg": "RS256",
25//!     "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
26//!     "e": "AQAB"
27//! }"#;
28//!
29//! let jwk: Key = serde_json::from_str(json).unwrap();
30//! assert_eq!(jwk.kid.as_deref(), Some("my-key-id"));
31//! ```
32
33mod ec;
34mod okp;
35mod rsa;
36mod symmetric;
37pub mod thumbprint;
38
39pub use ec::{EcCurve, EcParams};
40pub use okp::{OkpCurve, OkpParams};
41pub use rsa::{RsaOtherPrime, RsaParams, RsaParamsBuilder};
42pub use symmetric::SymmetricParams;
43
44use std::collections::HashSet;
45
46use serde::{Deserialize, Deserializer, Serialize, Serializer};
47use zeroize::{Zeroize, ZeroizeOnDrop};
48
49use crate::encoding::Base64UrlBytes;
50use crate::error::{Error, ParseError, Result, ValidationError};
51
52/// Key type identifier (RFC 7517 Section 4.1).
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub enum KeyType {
55    /// RSA key type.
56    Rsa,
57    /// Elliptic Curve key type.
58    Ec,
59    /// Symmetric key type (octet sequence).
60    Symmetric,
61    /// Octet Key Pair (Edwards/Montgomery curves).
62    Okp,
63}
64
65impl KeyType {
66    /// Returns the key type as the JWK `kty` string.
67    pub fn as_str(&self) -> &'static str {
68        match self {
69            KeyType::Rsa => "RSA",
70            KeyType::Ec => "EC",
71            KeyType::Symmetric => "oct",
72            KeyType::Okp => "OKP",
73        }
74    }
75}
76
77impl std::str::FromStr for KeyType {
78    type Err = Error;
79
80    fn from_str(s: &str) -> Result<Self> {
81        match s {
82            "RSA" => Ok(KeyType::Rsa),
83            "EC" => Ok(KeyType::Ec),
84            "oct" => Ok(KeyType::Symmetric),
85            "OKP" => Ok(KeyType::Okp),
86            _ => Err(Error::Parse(ParseError::UnknownKeyType(s.to_string()))),
87        }
88    }
89}
90
91impl Serialize for KeyType {
92    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
93    where
94        S: Serializer,
95    {
96        serializer.serialize_str(self.as_str())
97    }
98}
99
100impl<'de> Deserialize<'de> for KeyType {
101    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
102    where
103        D: Deserializer<'de>,
104    {
105        let s = String::deserialize(deserializer)?;
106        s.parse().map_err(serde::de::Error::custom)
107    }
108}
109
110/// Key use (RFC 7517 Section 4.2).
111///
112/// Per RFC 7517, the "use" parameter is intended to identify the intended use
113/// of the public key. While "sig" and "enc" are the defined values, the
114/// specification allows for other values via registration or collision-resistant
115/// names for private use.
116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
117pub enum KeyUse {
118    /// Key is used for signatures.
119    Signature,
120    /// Key is used for encryption.
121    Encryption,
122    /// Unknown or private-use key use.
123    ///
124    /// Per RFC 7517, key use values should either be registered in IANA
125    /// or be a collision-resistant name. Unknown values are preserved.
126    Unknown(String),
127}
128
129impl KeyUse {
130    /// Returns the key use as a string.
131    pub fn as_str(&self) -> &str {
132        match self {
133            KeyUse::Signature => "sig",
134            KeyUse::Encryption => "enc",
135            KeyUse::Unknown(s) => s.as_str(),
136        }
137    }
138
139    /// Returns `true` if this is an unknown/unrecognized key use.
140    pub fn is_unknown(&self) -> bool {
141        matches!(self, KeyUse::Unknown(_))
142    }
143}
144
145impl std::fmt::Display for KeyUse {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        write!(f, "{}", self.as_str())
148    }
149}
150
151impl Serialize for KeyUse {
152    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
153    where
154        S: Serializer,
155    {
156        serializer.serialize_str(self.as_str())
157    }
158}
159
160impl<'de> Deserialize<'de> for KeyUse {
161    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
162    where
163        D: Deserializer<'de>,
164    {
165        let s = String::deserialize(deserializer)?;
166        Ok(match s.as_str() {
167            "sig" => KeyUse::Signature,
168            "enc" => KeyUse::Encryption,
169            _ => KeyUse::Unknown(s),
170        })
171    }
172}
173
174/// Key operation (RFC 7517 Section 4.3).
175///
176/// Per RFC 7517, unknown key operation values should be accepted to support
177/// collision-resistant names and future extensions.
178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
179pub enum KeyOperation {
180    /// Compute digital signature or MAC.
181    Sign,
182    /// Verify digital signature or MAC.
183    Verify,
184    /// Encrypt content.
185    Encrypt,
186    /// Decrypt content.
187    Decrypt,
188    /// Encrypt key.
189    WrapKey,
190    /// Decrypt key.
191    UnwrapKey,
192    /// Derive key.
193    DeriveKey,
194    /// Derive bits not to be used as a key.
195    DeriveBits,
196    /// Unknown or private-use key operation.
197    ///
198    /// Per RFC 7517, key operation values should either be registered in IANA
199    /// or be a collision-resistant name. Unknown values are preserved.
200    Unknown(String),
201}
202
203impl KeyOperation {
204    /// Returns the key operation as a string.
205    pub fn as_str(&self) -> &str {
206        match self {
207            KeyOperation::Sign => "sign",
208            KeyOperation::Verify => "verify",
209            KeyOperation::Encrypt => "encrypt",
210            KeyOperation::Decrypt => "decrypt",
211            KeyOperation::WrapKey => "wrapKey",
212            KeyOperation::UnwrapKey => "unwrapKey",
213            KeyOperation::DeriveKey => "deriveKey",
214            KeyOperation::DeriveBits => "deriveBits",
215            KeyOperation::Unknown(s) => s.as_str(),
216        }
217    }
218
219    /// Returns `true` if this is an unknown/unrecognized key operation.
220    pub fn is_unknown(&self) -> bool {
221        matches!(self, KeyOperation::Unknown(_))
222    }
223}
224
225impl std::fmt::Display for KeyOperation {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        write!(f, "{}", self.as_str())
228    }
229}
230
231impl Serialize for KeyOperation {
232    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
233    where
234        S: Serializer,
235    {
236        serializer.serialize_str(self.as_str())
237    }
238}
239
240impl<'de> Deserialize<'de> for KeyOperation {
241    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
242    where
243        D: Deserializer<'de>,
244    {
245        let s = String::deserialize(deserializer)?;
246        Ok(match s.as_str() {
247            "sign" => KeyOperation::Sign,
248            "verify" => KeyOperation::Verify,
249            "encrypt" => KeyOperation::Encrypt,
250            "decrypt" => KeyOperation::Decrypt,
251            "wrapKey" => KeyOperation::WrapKey,
252            "unwrapKey" => KeyOperation::UnwrapKey,
253            "deriveKey" => KeyOperation::DeriveKey,
254            "deriveBits" => KeyOperation::DeriveBits,
255            _ => KeyOperation::Unknown(s),
256        })
257    }
258}
259
260/// JWK Algorithm (RFC 7518).
261///
262/// Per RFC 7517, unknown algorithm values should be accepted and preserved
263/// to allow for future extensions and private-use algorithms.
264#[derive(Debug, Clone, PartialEq, Eq, Hash)]
265pub enum Algorithm {
266    // HMAC
267    /// HMAC using SHA-256.
268    Hs256,
269    /// HMAC using SHA-384.
270    Hs384,
271    /// HMAC using SHA-512.
272    Hs512,
273
274    // RSA PKCS#1
275    /// RSASSA-PKCS1-v1_5 using SHA-256.
276    Rs256,
277    /// RSASSA-PKCS1-v1_5 using SHA-384.
278    Rs384,
279    /// RSASSA-PKCS1-v1_5 using SHA-512.
280    Rs512,
281
282    // RSA-PSS
283    /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256.
284    Ps256,
285    /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384.
286    Ps384,
287    /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512.
288    Ps512,
289
290    // ECDSA
291    /// ECDSA using P-256 and SHA-256.
292    Es256,
293    /// ECDSA using P-384 and SHA-384.
294    Es384,
295    /// ECDSA using P-521 and SHA-512.
296    Es512,
297    /// ECDSA using secp256k1 and SHA-256.
298    Es256k,
299
300    // EdDSA
301    /// Edwards-curve Digital Signature Algorithm (Ed25519/Ed448).
302    EdDsa,
303
304    // RSA Encryption
305    /// RSAES-OAEP using default parameters.
306    RsaOaep,
307    /// RSAES-OAEP using SHA-256 and MGF1 with SHA-256.
308    RsaOaep256,
309    /// RSAES-PKCS1-v1_5.
310    Rsa1_5,
311
312    // AES Key Wrap
313    /// AES Key Wrap with 128-bit key.
314    A128kw,
315    /// AES Key Wrap with 192-bit key.
316    A192kw,
317    /// AES Key Wrap with 256-bit key.
318    A256kw,
319
320    // Direct
321    /// Direct use of a shared symmetric key.
322    Dir,
323
324    // ECDH-ES
325    /// ECDH-ES using Concat KDF.
326    EcdhEs,
327    /// ECDH-ES using Concat KDF and A128KW wrapping.
328    EcdhEsA128kw,
329    /// ECDH-ES using Concat KDF and A192KW wrapping.
330    EcdhEsA192kw,
331    /// ECDH-ES using Concat KDF and A256KW wrapping.
332    EcdhEsA256kw,
333
334    // AES-GCM Key Wrap
335    /// Key wrapping with AES-GCM using 128-bit key.
336    A128gcmkw,
337    /// Key wrapping with AES-GCM using 192-bit key.
338    A192gcmkw,
339    /// Key wrapping with AES-GCM using 256-bit key.
340    A256gcmkw,
341
342    // PBES2
343    /// PBES2 with HMAC SHA-256 and A128KW wrapping.
344    Pbes2Hs256A128kw,
345    /// PBES2 with HMAC SHA-384 and A192KW wrapping.
346    Pbes2Hs384A192kw,
347    /// PBES2 with HMAC SHA-512 and A256KW wrapping.
348    Pbes2Hs512A256kw,
349
350    // Content Encryption
351    /// AES-CBC with HMAC SHA-256 using 128-bit keys.
352    A128cbcHs256,
353    /// AES-CBC with HMAC SHA-384 using 192-bit keys.
354    A192cbcHs384,
355    /// AES-CBC with HMAC SHA-512 using 256-bit keys.
356    A256cbcHs512,
357    /// AES-GCM using 128-bit key.
358    A128gcm,
359    /// AES-GCM using 192-bit key.
360    A192gcm,
361    /// AES-GCM using 256-bit key.
362    A256gcm,
363
364    /// Unknown or private-use algorithm.
365    ///
366    /// Per RFC 7517, implementations should accept unknown algorithm values
367    /// to support future extensions and collision-resistant names.
368    Unknown(String),
369}
370
371impl Algorithm {
372    /// Returns the algorithm as a string.
373    ///
374    /// For unknown algorithms, this returns a static "unknown" string.
375    /// Use [`Algorithm::to_string()`] to get the actual algorithm name for unknown variants.
376    pub fn as_str(&self) -> &str {
377        match self {
378            Algorithm::Hs256 => "HS256",
379            Algorithm::Hs384 => "HS384",
380            Algorithm::Hs512 => "HS512",
381            Algorithm::Rs256 => "RS256",
382            Algorithm::Rs384 => "RS384",
383            Algorithm::Rs512 => "RS512",
384            Algorithm::Ps256 => "PS256",
385            Algorithm::Ps384 => "PS384",
386            Algorithm::Ps512 => "PS512",
387            Algorithm::Es256 => "ES256",
388            Algorithm::Es384 => "ES384",
389            Algorithm::Es512 => "ES512",
390            Algorithm::Es256k => "ES256K",
391            Algorithm::EdDsa => "EdDSA",
392            Algorithm::RsaOaep => "RSA-OAEP",
393            Algorithm::RsaOaep256 => "RSA-OAEP-256",
394            Algorithm::Rsa1_5 => "RSA1_5",
395            Algorithm::A128kw => "A128KW",
396            Algorithm::A192kw => "A192KW",
397            Algorithm::A256kw => "A256KW",
398            Algorithm::Dir => "dir",
399            Algorithm::EcdhEs => "ECDH-ES",
400            Algorithm::EcdhEsA128kw => "ECDH-ES+A128KW",
401            Algorithm::EcdhEsA192kw => "ECDH-ES+A192KW",
402            Algorithm::EcdhEsA256kw => "ECDH-ES+A256KW",
403            Algorithm::A128gcmkw => "A128GCMKW",
404            Algorithm::A192gcmkw => "A192GCMKW",
405            Algorithm::A256gcmkw => "A256GCMKW",
406            Algorithm::Pbes2Hs256A128kw => "PBES2-HS256+A128KW",
407            Algorithm::Pbes2Hs384A192kw => "PBES2-HS384+A192KW",
408            Algorithm::Pbes2Hs512A256kw => "PBES2-HS512+A256KW",
409            Algorithm::A128cbcHs256 => "A128CBC-HS256",
410            Algorithm::A192cbcHs384 => "A192CBC-HS384",
411            Algorithm::A256cbcHs512 => "A256CBC-HS512",
412            Algorithm::A128gcm => "A128GCM",
413            Algorithm::A192gcm => "A192GCM",
414            Algorithm::A256gcm => "A256GCM",
415            Algorithm::Unknown(s) => s.as_str(),
416        }
417    }
418
419    /// Returns `true` if this is an unknown/unrecognized algorithm.
420    pub fn is_unknown(&self) -> bool {
421        matches!(self, Algorithm::Unknown(_))
422    }
423
424    /// Returns `true` if this is a signing algorithm.
425    ///
426    /// Unknown algorithms return `false` since their purpose cannot be determined.
427    pub fn is_signing(&self) -> bool {
428        matches!(
429            self,
430            Algorithm::Hs256
431                | Algorithm::Hs384
432                | Algorithm::Hs512
433                | Algorithm::Rs256
434                | Algorithm::Rs384
435                | Algorithm::Rs512
436                | Algorithm::Ps256
437                | Algorithm::Ps384
438                | Algorithm::Ps512
439                | Algorithm::Es256
440                | Algorithm::Es384
441                | Algorithm::Es512
442                | Algorithm::Es256k
443                | Algorithm::EdDsa
444        )
445    }
446
447    /// Returns `true` if this is a key encryption algorithm.
448    ///
449    /// Unknown algorithms return `false` since their purpose cannot be determined.
450    pub fn is_key_encryption(&self) -> bool {
451        matches!(
452            self,
453            Algorithm::RsaOaep
454                | Algorithm::RsaOaep256
455                | Algorithm::Rsa1_5
456                | Algorithm::A128kw
457                | Algorithm::A192kw
458                | Algorithm::A256kw
459                | Algorithm::Dir
460                | Algorithm::EcdhEs
461                | Algorithm::EcdhEsA128kw
462                | Algorithm::EcdhEsA192kw
463                | Algorithm::EcdhEsA256kw
464                | Algorithm::A128gcmkw
465                | Algorithm::A192gcmkw
466                | Algorithm::A256gcmkw
467                | Algorithm::Pbes2Hs256A128kw
468                | Algorithm::Pbes2Hs384A192kw
469                | Algorithm::Pbes2Hs512A256kw
470        )
471    }
472}
473
474impl std::fmt::Display for Algorithm {
475    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476        write!(f, "{}", self.as_str())
477    }
478}
479
480impl std::str::FromStr for Algorithm {
481    type Err = std::convert::Infallible;
482
483    /// Parses an algorithm string.
484    ///
485    /// Per RFC 7517, unknown algorithm values are accepted and stored as `Unknown`.
486    /// This function never fails - unrecognized values become `Algorithm::Unknown(s)`.
487    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
488        Ok(match s {
489            "HS256" => Algorithm::Hs256,
490            "HS384" => Algorithm::Hs384,
491            "HS512" => Algorithm::Hs512,
492            "RS256" => Algorithm::Rs256,
493            "RS384" => Algorithm::Rs384,
494            "RS512" => Algorithm::Rs512,
495            "PS256" => Algorithm::Ps256,
496            "PS384" => Algorithm::Ps384,
497            "PS512" => Algorithm::Ps512,
498            "ES256" => Algorithm::Es256,
499            "ES384" => Algorithm::Es384,
500            "ES512" => Algorithm::Es512,
501            "ES256K" => Algorithm::Es256k,
502            "EdDSA" => Algorithm::EdDsa,
503            "RSA-OAEP" => Algorithm::RsaOaep,
504            "RSA-OAEP-256" => Algorithm::RsaOaep256,
505            "RSA1_5" => Algorithm::Rsa1_5,
506            "A128KW" => Algorithm::A128kw,
507            "A192KW" => Algorithm::A192kw,
508            "A256KW" => Algorithm::A256kw,
509            "dir" => Algorithm::Dir,
510            "ECDH-ES" => Algorithm::EcdhEs,
511            "ECDH-ES+A128KW" => Algorithm::EcdhEsA128kw,
512            "ECDH-ES+A192KW" => Algorithm::EcdhEsA192kw,
513            "ECDH-ES+A256KW" => Algorithm::EcdhEsA256kw,
514            "A128GCMKW" => Algorithm::A128gcmkw,
515            "A192GCMKW" => Algorithm::A192gcmkw,
516            "A256GCMKW" => Algorithm::A256gcmkw,
517            "PBES2-HS256+A128KW" => Algorithm::Pbes2Hs256A128kw,
518            "PBES2-HS384+A192KW" => Algorithm::Pbes2Hs384A192kw,
519            "PBES2-HS512+A256KW" => Algorithm::Pbes2Hs512A256kw,
520            "A128CBC-HS256" => Algorithm::A128cbcHs256,
521            "A192CBC-HS384" => Algorithm::A192cbcHs384,
522            "A256CBC-HS512" => Algorithm::A256cbcHs512,
523            "A128GCM" => Algorithm::A128gcm,
524            "A192GCM" => Algorithm::A192gcm,
525            "A256GCM" => Algorithm::A256gcm,
526            _ => Algorithm::Unknown(s.to_string()),
527        })
528    }
529}
530
531impl Serialize for Algorithm {
532    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
533    where
534        S: Serializer,
535    {
536        serializer.serialize_str(self.as_str())
537    }
538}
539
540impl<'de> Deserialize<'de> for Algorithm {
541    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
542    where
543        D: Deserializer<'de>,
544    {
545        let s = String::deserialize(deserializer)?;
546        s.parse().map_err(serde::de::Error::custom)
547    }
548}
549
550/// Key-type-specific parameters.
551#[derive(Clone, Zeroize, ZeroizeOnDrop)]
552pub enum KeyParams {
553    /// RSA key parameters.
554    Rsa(RsaParams),
555    /// Elliptic Curve key parameters.
556    Ec(EcParams),
557    /// Symmetric key parameters.
558    Symmetric(SymmetricParams),
559    /// Octet Key Pair parameters.
560    Okp(OkpParams),
561}
562
563impl KeyParams {
564    /// Returns `true` if this contains only public key parameters.
565    pub fn is_public_key_only(&self) -> bool {
566        match self {
567            KeyParams::Rsa(p) => p.is_public_key_only(),
568            KeyParams::Ec(p) => p.is_public_key_only(),
569            KeyParams::Symmetric(p) => p.is_public_key_only(),
570            KeyParams::Okp(p) => p.is_public_key_only(),
571        }
572    }
573
574    /// Returns `true` if this contains private key parameters.
575    pub fn has_private_key(&self) -> bool {
576        !self.is_public_key_only()
577    }
578
579    /// Validates the key parameters.
580    pub fn validate(&self) -> Result<()> {
581        match self {
582            KeyParams::Rsa(p) => p.validate(),
583            KeyParams::Ec(p) => p.validate(),
584            KeyParams::Symmetric(p) => p.validate(),
585            KeyParams::Okp(p) => p.validate(),
586        }
587    }
588}
589
590impl std::fmt::Debug for KeyParams {
591    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592        match self {
593            KeyParams::Rsa(p) => write!(f, "KeyParams::Rsa({:?})", p),
594            KeyParams::Ec(p) => write!(f, "KeyParams::Ec({:?})", p),
595            KeyParams::Symmetric(p) => write!(f, "KeyParams::Symmetric({:?})", p),
596            KeyParams::Okp(p) => write!(f, "KeyParams::Okp({:?})", p),
597        }
598    }
599}
600
601impl PartialEq for KeyParams {
602    fn eq(&self, other: &Self) -> bool {
603        match (self, other) {
604            (KeyParams::Rsa(a), KeyParams::Rsa(b)) => a == b,
605            (KeyParams::Ec(a), KeyParams::Ec(b)) => a == b,
606            (KeyParams::Symmetric(a), KeyParams::Symmetric(b)) => a == b,
607            (KeyParams::Okp(a), KeyParams::Okp(b)) => a == b,
608            _ => false,
609        }
610    }
611}
612
613impl Eq for KeyParams {}
614
615/// A JSON Web Key (RFC 7517).
616///
617/// Represents a single cryptographic key with its parameters and metadata.
618///
619/// # Examples
620///
621/// ```
622/// use jwk_simple::jwk::{Key, KeyType};
623///
624/// // Parse from JSON
625/// let json = r#"{"kty":"RSA","n":"AQAB","e":"AQAB"}"#;
626/// let jwk: Key = serde_json::from_str(json).unwrap();
627///
628/// // Check key properties
629/// assert_eq!(jwk.kty, KeyType::Rsa);
630/// assert!(jwk.is_public_key_only());
631/// ```
632#[derive(Clone, Zeroize, ZeroizeOnDrop)]
633pub struct Key {
634    /// The key type.
635    #[zeroize(skip)]
636    pub kty: KeyType,
637
638    /// The key ID.
639    #[zeroize(skip)]
640    pub kid: Option<String>,
641
642    /// The intended use of the key.
643    #[zeroize(skip)]
644    pub key_use: Option<KeyUse>,
645
646    /// The permitted operations for the key.
647    #[zeroize(skip)]
648    pub key_ops: Option<Vec<KeyOperation>>,
649
650    /// The algorithm intended for use with the key.
651    #[zeroize(skip)]
652    pub alg: Option<Algorithm>,
653
654    /// The key-type-specific parameters.
655    pub params: KeyParams,
656
657    /// X.509 certificate chain (base64-encoded DER).
658    #[zeroize(skip)]
659    pub x5c: Option<Vec<String>>,
660
661    /// X.509 certificate SHA-1 thumbprint (base64url-encoded).
662    #[zeroize(skip)]
663    pub x5t: Option<String>,
664
665    /// X.509 certificate SHA-256 thumbprint (base64url-encoded).
666    #[zeroize(skip)]
667    #[allow(non_snake_case)]
668    pub x5t_s256: Option<String>,
669
670    /// X.509 URL.
671    #[zeroize(skip)]
672    pub x5u: Option<String>,
673}
674
675impl Key {
676    /// Returns `true` if this contains only public key parameters.
677    pub fn is_public_key_only(&self) -> bool {
678        self.params.is_public_key_only()
679    }
680
681    /// Returns `true` if this contains private key parameters.
682    pub fn has_private_key(&self) -> bool {
683        self.params.has_private_key()
684    }
685
686    /// Validates the JWK.
687    ///
688    /// # Errors
689    ///
690    /// Returns an error if:
691    /// - The key parameters are invalid
692    /// - The key type doesn't match the parameters
693    /// - The algorithm doesn't match the key type
694    /// - Both `use` and `key_ops` are specified (RFC 7517 Section 4.3 SHOULD NOT)
695    pub fn validate(&self) -> Result<()> {
696        // Verify kty matches params
697        let expected_kty = match &self.params {
698            KeyParams::Rsa(_) => KeyType::Rsa,
699            KeyParams::Ec(_) => KeyType::Ec,
700            KeyParams::Symmetric(_) => KeyType::Symmetric,
701            KeyParams::Okp(_) => KeyType::Okp,
702        };
703
704        if self.kty != expected_kty {
705            return Err(Error::Validation(ValidationError::InconsistentParameters(
706                format!(
707                    "kty '{}' doesn't match key parameters (expected '{}')",
708                    self.kty.as_str(),
709                    expected_kty.as_str()
710                ),
711            )));
712        }
713
714        // RFC 7517 Section 4.3: "use" and "key_ops" SHOULD NOT be used together
715        if self.key_use.is_some() && self.key_ops.is_some() {
716            return Err(Error::Validation(ValidationError::InconsistentParameters(
717                "RFC 7517: 'use' and 'key_ops' SHOULD NOT be used together".to_string(),
718            )));
719        }
720
721        // RFC 7517 Section 4.3: key_ops values MUST be unique (no duplicates)
722        if let Some(ref ops) = self.key_ops {
723            let mut seen = HashSet::new();
724            for op in ops {
725                if !seen.insert(op) {
726                    return Err(Error::Validation(ValidationError::InconsistentParameters(
727                        format!(
728                            "RFC 7517: key_ops array contains duplicate value '{:?}'",
729                            op
730                        ),
731                    )));
732                }
733            }
734        }
735
736        // RFC 7517 Section 4.6: x5u MUST use TLS (HTTPS)
737        if let Some(ref url) = self.x5u
738            && !url.starts_with("https://") {
739                return Err(Error::Validation(ValidationError::InvalidParameter {
740                    name: "x5u",
741                    reason: "RFC 7517: x5u URL must use HTTPS for integrity protection".to_string(),
742                }));
743            }
744
745        // RFC 7517 Section 4.7: x5c values are base64 encoded (NOT base64url)
746        if let Some(ref certs) = self.x5c {
747            for (i, cert) in certs.iter().enumerate() {
748                // Standard base64 uses '+' and '/' which are NOT valid in base64url
749                // base64url uses '-' and '_' instead
750                // We validate it's proper base64 by checking for base64url-only chars
751                // and attempting to decode
752                if cert.contains('-') || cert.contains('_') {
753                    return Err(Error::Validation(ValidationError::InvalidParameter {
754                        name: "x5c",
755                        reason: format!(
756                            "RFC 7517: x5c[{}] appears to be base64url encoded; must be standard base64",
757                            i
758                        ),
759                    }));
760                }
761
762                // Validate it's valid base64 by checking character set and padding
763                if !is_valid_base64(cert) {
764                    return Err(Error::Validation(ValidationError::InvalidParameter {
765                        name: "x5c",
766                        reason: format!("RFC 7517: x5c[{}] is not valid base64 encoding", i),
767                    }));
768                }
769
770                // RFC 7517 Section 4.7: Each certificate value MUST be a DER-encoded X.509 certificate
771                // Decode and validate basic DER certificate structure
772                use base64ct::{Base64, Encoding};
773                if let Ok(der_bytes) = Base64::decode_vec(cert) {
774                    if !is_valid_der_certificate(&der_bytes) {
775                        return Err(Error::Validation(ValidationError::InvalidParameter {
776                            name: "x5c",
777                            reason: format!(
778                                "RFC 7517: x5c[{}] is not a valid DER-encoded X.509 certificate",
779                                i
780                            ),
781                        }));
782                    }
783                } else {
784                    return Err(Error::Validation(ValidationError::InvalidParameter {
785                        name: "x5c",
786                        reason: format!("RFC 7517: x5c[{}] failed base64 decoding", i),
787                    }));
788                }
789            }
790        }
791
792        // RFC 7517 Section 4.8: x5t is base64url-encoded SHA-1 thumbprint (20 bytes)
793        if let Some(ref x5t) = self.x5t {
794            validate_x509_thumbprint(x5t, "x5t", 20)?;
795        }
796
797        // RFC 7517 Section 4.9: x5t#S256 is base64url-encoded SHA-256 thumbprint (32 bytes)
798        if let Some(ref x5t_s256) = self.x5t_s256 {
799            validate_x509_thumbprint(x5t_s256, "x5t#S256", 32)?;
800        }
801
802        // Validate algorithm matches key type if specified
803        if let Some(ref alg) = self.alg {
804            self.validate_algorithm_key_type_match(alg)?;
805        }
806
807        // Validate key parameters
808        self.params.validate()
809    }
810
811    /// Validates that the algorithm matches the key type.
812    fn validate_algorithm_key_type_match(&self, alg: &Algorithm) -> Result<()> {
813        // Unknown algorithms cannot be validated against key types
814        // Per RFC 7517, we accept them but cannot verify compatibility
815        if alg.is_unknown() {
816            return Ok(());
817        }
818
819        let valid = match (&self.params, alg) {
820            // RSA algorithms require RSA keys
821            (KeyParams::Rsa(_), Algorithm::Rs256)
822            | (KeyParams::Rsa(_), Algorithm::Rs384)
823            | (KeyParams::Rsa(_), Algorithm::Rs512)
824            | (KeyParams::Rsa(_), Algorithm::Ps256)
825            | (KeyParams::Rsa(_), Algorithm::Ps384)
826            | (KeyParams::Rsa(_), Algorithm::Ps512)
827            | (KeyParams::Rsa(_), Algorithm::RsaOaep)
828            | (KeyParams::Rsa(_), Algorithm::RsaOaep256)
829            | (KeyParams::Rsa(_), Algorithm::Rsa1_5) => true,
830
831            // HMAC algorithms require symmetric keys
832            (KeyParams::Symmetric(_), Algorithm::Hs256)
833            | (KeyParams::Symmetric(_), Algorithm::Hs384)
834            | (KeyParams::Symmetric(_), Algorithm::Hs512) => true,
835
836            // AES key wrap algorithms require symmetric keys
837            (KeyParams::Symmetric(_), Algorithm::A128kw)
838            | (KeyParams::Symmetric(_), Algorithm::A192kw)
839            | (KeyParams::Symmetric(_), Algorithm::A256kw)
840            | (KeyParams::Symmetric(_), Algorithm::A128gcmkw)
841            | (KeyParams::Symmetric(_), Algorithm::A192gcmkw)
842            | (KeyParams::Symmetric(_), Algorithm::A256gcmkw)
843            | (KeyParams::Symmetric(_), Algorithm::Dir) => true,
844
845            // AES content encryption algorithms require symmetric keys
846            (KeyParams::Symmetric(_), Algorithm::A128cbcHs256)
847            | (KeyParams::Symmetric(_), Algorithm::A192cbcHs384)
848            | (KeyParams::Symmetric(_), Algorithm::A256cbcHs512)
849            | (KeyParams::Symmetric(_), Algorithm::A128gcm)
850            | (KeyParams::Symmetric(_), Algorithm::A192gcm)
851            | (KeyParams::Symmetric(_), Algorithm::A256gcm) => true,
852
853            // PBES2 algorithms require symmetric keys
854            (KeyParams::Symmetric(_), Algorithm::Pbes2Hs256A128kw)
855            | (KeyParams::Symmetric(_), Algorithm::Pbes2Hs384A192kw)
856            | (KeyParams::Symmetric(_), Algorithm::Pbes2Hs512A256kw) => true,
857
858            // EC algorithms require EC keys
859            (KeyParams::Ec(ec), Algorithm::Es256) => ec.crv == EcCurve::P256,
860            (KeyParams::Ec(ec), Algorithm::Es384) => ec.crv == EcCurve::P384,
861            (KeyParams::Ec(ec), Algorithm::Es512) => ec.crv == EcCurve::P521,
862            (KeyParams::Ec(ec), Algorithm::Es256k) => ec.crv == EcCurve::Secp256k1,
863
864            // ECDH algorithms require EC keys
865            (KeyParams::Ec(_), Algorithm::EcdhEs)
866            | (KeyParams::Ec(_), Algorithm::EcdhEsA128kw)
867            | (KeyParams::Ec(_), Algorithm::EcdhEsA192kw)
868            | (KeyParams::Ec(_), Algorithm::EcdhEsA256kw) => true,
869
870            // EdDSA requires OKP keys with Ed25519 or Ed448 curves
871            (KeyParams::Okp(okp), Algorithm::EdDsa) => {
872                okp.crv == OkpCurve::Ed25519 || okp.crv == OkpCurve::Ed448
873            }
874
875            // ECDH with OKP keys (X25519, X448)
876            (KeyParams::Okp(okp), Algorithm::EcdhEs)
877            | (KeyParams::Okp(okp), Algorithm::EcdhEsA128kw)
878            | (KeyParams::Okp(okp), Algorithm::EcdhEsA192kw)
879            | (KeyParams::Okp(okp), Algorithm::EcdhEsA256kw) => {
880                okp.crv == OkpCurve::X25519 || okp.crv == OkpCurve::X448
881            }
882
883            // All other combinations are invalid (including Unknown which is handled above)
884            _ => false,
885        };
886
887        if !valid {
888            return Err(Error::Validation(ValidationError::InconsistentParameters(
889                format!(
890                    "algorithm '{}' is not compatible with key type '{}'",
891                    alg.as_str(),
892                    self.kty.as_str()
893                ),
894            )));
895        }
896
897        Ok(())
898    }
899
900    /// Calculates the JWK thumbprint (RFC 7638).
901    ///
902    /// The thumbprint is a base64url-encoded SHA-256 hash of the key's
903    /// required members.
904    #[must_use]
905    pub fn thumbprint(&self) -> String {
906        thumbprint::calculate_thumbprint(self)
907    }
908
909    /// Returns the key as RSA parameters, if applicable.
910    pub fn as_rsa(&self) -> Option<&RsaParams> {
911        match &self.params {
912            KeyParams::Rsa(p) => Some(p),
913            _ => None,
914        }
915    }
916
917    /// Returns the key as EC parameters, if applicable.
918    pub fn as_ec(&self) -> Option<&EcParams> {
919        match &self.params {
920            KeyParams::Ec(p) => Some(p),
921            _ => None,
922        }
923    }
924
925    /// Returns the key as symmetric parameters, if applicable.
926    pub fn as_symmetric(&self) -> Option<&SymmetricParams> {
927        match &self.params {
928            KeyParams::Symmetric(p) => Some(p),
929            _ => None,
930        }
931    }
932
933    /// Returns the key as OKP parameters, if applicable.
934    pub fn as_okp(&self) -> Option<&OkpParams> {
935        match &self.params {
936            KeyParams::Okp(p) => Some(p),
937            _ => None,
938        }
939    }
940
941    /// Extracts only the public key components, removing any private key material.
942    ///
943    /// For symmetric keys, this returns `None` since symmetric keys don't have
944    /// a separate public component.
945    ///
946    /// # Returns
947    ///
948    /// A new `Key` containing only the public key parameters, or `None` for symmetric keys.
949    ///
950    /// # Examples
951    ///
952    /// ```
953    /// use jwk_simple::KeySet;
954    ///
955    /// let json = r#"{"keys": [{
956    ///     "kty": "RSA",
957    ///     "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
958    ///     "e": "AQAB",
959    ///     "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"
960    /// }]}"#;
961    /// let jwks: KeySet = serde_json::from_str(json).unwrap();
962    /// let private_key = jwks.first().unwrap();
963    ///
964    /// // Extract public key
965    /// let public_key = private_key.to_public().expect("RSA keys have public components");
966    /// assert!(public_key.is_public_key_only());
967    /// ```
968    #[must_use]
969    pub fn to_public(&self) -> Option<Key> {
970        let public_params = match &self.params {
971            KeyParams::Rsa(p) => KeyParams::Rsa(p.to_public()),
972            KeyParams::Ec(p) => KeyParams::Ec(p.to_public()),
973            KeyParams::Okp(p) => KeyParams::Okp(p.to_public()),
974            KeyParams::Symmetric(_) => return None, // No public component for symmetric keys
975        };
976
977        Some(Key {
978            kty: self.kty,
979            kid: self.kid.clone(),
980            key_use: self.key_use.clone(),
981            key_ops: self.key_ops.clone(),
982            alg: self.alg.clone(),
983            params: public_params,
984            x5c: self.x5c.clone(),
985            x5t: self.x5t.clone(),
986            x5t_s256: self.x5t_s256.clone(),
987            x5u: self.x5u.clone(),
988        })
989    }
990}
991
992impl std::fmt::Debug for Key {
993    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
994        f.debug_struct("Key")
995            .field("kty", &self.kty)
996            .field("kid", &self.kid)
997            .field("key_use", &self.key_use)
998            .field("alg", &self.alg)
999            .field("params", &self.params)
1000            .finish()
1001    }
1002}
1003
1004impl PartialEq for Key {
1005    fn eq(&self, other: &Self) -> bool {
1006        self.kty == other.kty
1007            && self.kid == other.kid
1008            && self.key_use == other.key_use
1009            && self.key_ops == other.key_ops
1010            && self.alg == other.alg
1011            && self.params == other.params
1012    }
1013}
1014
1015impl Eq for Key {}
1016
1017// Custom serialization for Key that flattens the params
1018impl Serialize for Key {
1019    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1020    where
1021        S: Serializer,
1022    {
1023        use serde::ser::SerializeMap;
1024
1025        // Count the number of fields to serialize
1026        let mut field_count = 1; // kty is always present
1027        if self.kid.is_some() {
1028            field_count += 1;
1029        }
1030        if self.key_use.is_some() {
1031            field_count += 1;
1032        }
1033        if self.key_ops.is_some() {
1034            field_count += 1;
1035        }
1036        if self.alg.is_some() {
1037            field_count += 1;
1038        }
1039        if self.x5c.is_some() {
1040            field_count += 1;
1041        }
1042        if self.x5t.is_some() {
1043            field_count += 1;
1044        }
1045        if self.x5t_s256.is_some() {
1046            field_count += 1;
1047        }
1048        if self.x5u.is_some() {
1049            field_count += 1;
1050        }
1051
1052        // Add params fields
1053        field_count += match &self.params {
1054            KeyParams::Rsa(p) => {
1055                2 + if p.d.is_some() { 1 } else { 0 }
1056                    + if p.p.is_some() { 1 } else { 0 }
1057                    + if p.q.is_some() { 1 } else { 0 }
1058                    + if p.dp.is_some() { 1 } else { 0 }
1059                    + if p.dq.is_some() { 1 } else { 0 }
1060                    + if p.qi.is_some() { 1 } else { 0 }
1061                    + if p.oth.is_some() { 1 } else { 0 }
1062            }
1063            KeyParams::Ec(p) => 3 + if p.d.is_some() { 1 } else { 0 },
1064            KeyParams::Symmetric(_) => 1,
1065            KeyParams::Okp(p) => 2 + if p.d.is_some() { 1 } else { 0 },
1066        };
1067
1068        let mut map = serializer.serialize_map(Some(field_count))?;
1069
1070        // Serialize kty first
1071        map.serialize_entry("kty", self.kty.as_str())?;
1072
1073        // Serialize optional common fields
1074        if let Some(ref kid) = self.kid {
1075            map.serialize_entry("kid", kid)?;
1076        }
1077        if let Some(ref use_) = self.key_use {
1078            map.serialize_entry("use", use_)?;
1079        }
1080        if let Some(ref key_ops) = self.key_ops {
1081            map.serialize_entry("key_ops", key_ops)?;
1082        }
1083        if let Some(ref alg) = self.alg {
1084            map.serialize_entry("alg", alg)?;
1085        }
1086
1087        // Serialize key-specific parameters
1088        match &self.params {
1089            KeyParams::Rsa(p) => {
1090                map.serialize_entry("n", &p.n)?;
1091                map.serialize_entry("e", &p.e)?;
1092                if let Some(ref d) = p.d {
1093                    map.serialize_entry("d", d)?;
1094                }
1095                if let Some(ref p_val) = p.p {
1096                    map.serialize_entry("p", p_val)?;
1097                }
1098                if let Some(ref q) = p.q {
1099                    map.serialize_entry("q", q)?;
1100                }
1101                if let Some(ref dp) = p.dp {
1102                    map.serialize_entry("dp", dp)?;
1103                }
1104                if let Some(ref dq) = p.dq {
1105                    map.serialize_entry("dq", dq)?;
1106                }
1107                if let Some(ref qi) = p.qi {
1108                    map.serialize_entry("qi", qi)?;
1109                }
1110                if let Some(ref oth) = p.oth {
1111                    map.serialize_entry("oth", oth)?;
1112                }
1113            }
1114            KeyParams::Ec(p) => {
1115                map.serialize_entry("crv", &p.crv)?;
1116                map.serialize_entry("x", &p.x)?;
1117                map.serialize_entry("y", &p.y)?;
1118                if let Some(ref d) = p.d {
1119                    map.serialize_entry("d", d)?;
1120                }
1121            }
1122            KeyParams::Symmetric(p) => {
1123                map.serialize_entry("k", &p.k)?;
1124            }
1125            KeyParams::Okp(p) => {
1126                map.serialize_entry("crv", &p.crv)?;
1127                map.serialize_entry("x", &p.x)?;
1128                if let Some(ref d) = p.d {
1129                    map.serialize_entry("d", d)?;
1130                }
1131            }
1132        }
1133
1134        // Serialize X.509 fields
1135        if let Some(ref x5c) = self.x5c {
1136            map.serialize_entry("x5c", x5c)?;
1137        }
1138        if let Some(ref x5t) = self.x5t {
1139            map.serialize_entry("x5t", x5t)?;
1140        }
1141        if let Some(ref x5t_s256) = self.x5t_s256 {
1142            map.serialize_entry("x5t#S256", x5t_s256)?;
1143        }
1144        if let Some(ref x5u) = self.x5u {
1145            map.serialize_entry("x5u", x5u)?;
1146        }
1147
1148        map.end()
1149    }
1150}
1151
1152// Custom deserialization for Key that handles flattened params
1153impl<'de> Deserialize<'de> for Key {
1154    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
1155    where
1156        D: Deserializer<'de>,
1157    {
1158        #[derive(Deserialize)]
1159        struct RawJwk {
1160            kty: String,
1161            kid: Option<String>,
1162            #[serde(rename = "use")]
1163            key_use: Option<KeyUse>,
1164            key_ops: Option<Vec<KeyOperation>>,
1165            alg: Option<Algorithm>,
1166
1167            // RSA parameters
1168            n: Option<Base64UrlBytes>,
1169            e: Option<Base64UrlBytes>,
1170
1171            // EC and OKP parameters
1172            crv: Option<String>,
1173            x: Option<Base64UrlBytes>,
1174            y: Option<Base64UrlBytes>,
1175
1176            // Private key parameter (RSA, EC, OKP)
1177            d: Option<Base64UrlBytes>,
1178
1179            // RSA CRT parameters
1180            p: Option<Base64UrlBytes>,
1181            q: Option<Base64UrlBytes>,
1182            dp: Option<Base64UrlBytes>,
1183            dq: Option<Base64UrlBytes>,
1184            qi: Option<Base64UrlBytes>,
1185
1186            // RSA multi-prime parameter
1187            oth: Option<Vec<rsa::RsaOtherPrime>>,
1188
1189            // Symmetric key
1190            k: Option<Base64UrlBytes>,
1191
1192            // X.509 fields
1193            x5c: Option<Vec<String>>,
1194            x5t: Option<String>,
1195            #[serde(rename = "x5t#S256")]
1196            x5t_s256: Option<String>,
1197            x5u: Option<String>,
1198        }
1199
1200        let raw = RawJwk::deserialize(deserializer)?;
1201
1202        let kty: KeyType = raw.kty.parse().map_err(serde::de::Error::custom)?;
1203
1204        let params = match kty {
1205            KeyType::Rsa => {
1206                let n = raw
1207                    .n
1208                    .ok_or_else(|| serde::de::Error::missing_field("n"))?;
1209                let e = raw
1210                    .e
1211                    .ok_or_else(|| serde::de::Error::missing_field("e"))?;
1212
1213                KeyParams::Rsa(RsaParams {
1214                    n,
1215                    e,
1216                    d: raw.d,
1217                    p: raw.p,
1218                    q: raw.q,
1219                    dp: raw.dp,
1220                    dq: raw.dq,
1221                    qi: raw.qi,
1222                    oth: raw.oth,
1223                })
1224            }
1225            KeyType::Ec => {
1226                let crv_str = raw
1227                    .crv
1228                    .ok_or_else(|| serde::de::Error::missing_field("crv"))?;
1229                let crv: EcCurve = crv_str.parse().map_err(serde::de::Error::custom)?;
1230                let x = raw
1231                    .x
1232                    .ok_or_else(|| serde::de::Error::missing_field("x"))?;
1233                let y = raw
1234                    .y
1235                    .ok_or_else(|| serde::de::Error::missing_field("y"))?;
1236
1237                KeyParams::Ec(EcParams {
1238                    crv,
1239                    x,
1240                    y,
1241                    d: raw.d,
1242                })
1243            }
1244            KeyType::Symmetric => {
1245                let k = raw
1246                    .k
1247                    .ok_or_else(|| serde::de::Error::missing_field("k"))?;
1248
1249                KeyParams::Symmetric(SymmetricParams { k })
1250            }
1251            KeyType::Okp => {
1252                let crv_str = raw
1253                    .crv
1254                    .ok_or_else(|| serde::de::Error::missing_field("crv"))?;
1255                let crv: OkpCurve = crv_str.parse().map_err(serde::de::Error::custom)?;
1256                let x = raw
1257                    .x
1258                    .ok_or_else(|| serde::de::Error::missing_field("x"))?;
1259
1260                KeyParams::Okp(OkpParams { crv, x, d: raw.d })
1261            }
1262        };
1263
1264        Ok(Key {
1265            kty,
1266            kid: raw.kid,
1267            key_use: raw.key_use,
1268            key_ops: raw.key_ops,
1269            alg: raw.alg,
1270            params,
1271            x5c: raw.x5c,
1272            x5t: raw.x5t,
1273            x5t_s256: raw.x5t_s256,
1274            x5u: raw.x5u,
1275        })
1276    }
1277}
1278
1279/// Validates that a byte slice contains a valid DER-encoded X.509 certificate structure.
1280///
1281/// This performs basic structural validation without full ASN.1 parsing:
1282/// - Checks for SEQUENCE tag at the start
1283/// - Validates the length encoding
1284/// - Ensures the certificate has the expected minimum structure
1285///
1286/// This is a lightweight check that catches obviously malformed certificates
1287/// without requiring a full X.509 parsing library.
1288fn is_valid_der_certificate(der: &[u8]) -> bool {
1289    // Minimum X.509 certificate is at least ~100 bytes (self-signed with minimal data)
1290    if der.len() < 100 {
1291        return false;
1292    }
1293
1294    // X.509 certificates are SEQUENCE at the top level (tag 0x30)
1295    if der[0] != 0x30 {
1296        return false;
1297    }
1298
1299    // Parse the length to validate structure
1300    let (content_len, header_len) = match parse_der_length(&der[1..]) {
1301        Some((len, hlen)) => (len, hlen + 1), // +1 for the tag byte
1302        None => return false,
1303    };
1304
1305    // Total length should match: header + content
1306    let expected_len = header_len + content_len;
1307    if der.len() < expected_len {
1308        return false;
1309    }
1310
1311    // The certificate content should start with another SEQUENCE (TBSCertificate)
1312    let content_start = header_len;
1313    if der.get(content_start) != Some(&0x30) {
1314        return false;
1315    }
1316
1317    true
1318}
1319
1320/// Parses a DER length encoding and returns (length, bytes_consumed).
1321fn parse_der_length(data: &[u8]) -> Option<(usize, usize)> {
1322    if data.is_empty() {
1323        return None;
1324    }
1325
1326    let first = data[0];
1327    if first < 0x80 {
1328        // Short form: length is directly in first byte
1329        Some((first as usize, 1))
1330    } else if first == 0x80 {
1331        // Indefinite length - not valid for DER
1332        None
1333    } else {
1334        // Long form: first byte indicates number of length bytes
1335        let num_bytes = (first & 0x7f) as usize;
1336        if num_bytes > 4 || data.len() < 1 + num_bytes {
1337            return None;
1338        }
1339
1340        let mut length: usize = 0;
1341        for &b in &data[1..1 + num_bytes] {
1342            length = length.checked_mul(256)?.checked_add(b as usize)?;
1343        }
1344
1345        Some((length, 1 + num_bytes))
1346    }
1347}
1348
1349/// Validates that a string is valid standard base64 encoding.
1350/// This checks the character set (A-Z, a-z, 0-9, +, /) and padding (=).
1351fn is_valid_base64(s: &str) -> bool {
1352    if s.is_empty() {
1353        return false;
1354    }
1355
1356    let bytes = s.as_bytes();
1357    let mut padding_started = false;
1358
1359    for (i, &b) in bytes.iter().enumerate() {
1360        match b {
1361            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'+' | b'/' => {
1362                if padding_started {
1363                    return false; // No data after padding
1364                }
1365            }
1366            b'=' => {
1367                padding_started = true;
1368                // Padding can only be at the end, max 2 characters
1369                let remaining = bytes.len() - i;
1370                if remaining > 2 {
1371                    return false;
1372                }
1373            }
1374            _ => return false, // Invalid character
1375        }
1376    }
1377
1378    // Valid base64 length is multiple of 4 when padded
1379    // Or when unpadded, (len % 4) should be 0, 2, or 3
1380    let len = s.len();
1381    if padding_started {
1382        len.is_multiple_of(4)
1383    } else {
1384        // Unpadded base64 can have len % 4 of 0, 2, or 3 (not 1)
1385        len % 4 != 1
1386    }
1387}
1388
1389/// Validates an X.509 certificate thumbprint (x5t or x5t#S256).
1390///
1391/// RFC 7517 Section 4.8 and 4.9: These are base64url-encoded certificate digests.
1392/// - x5t: SHA-1 digest (20 bytes)
1393/// - x5t#S256: SHA-256 digest (32 bytes)
1394fn validate_x509_thumbprint(thumbprint: &str, param_name: &'static str, expected_bytes: usize) -> Result<()> {
1395    use base64ct::{Base64UrlUnpadded, Encoding};
1396
1397    // Validate it's valid base64url
1398    if !is_valid_base64url(thumbprint) {
1399        return Err(Error::Validation(ValidationError::InvalidParameter {
1400            name: param_name,
1401            reason: format!(
1402                "RFC 7517: {} must be base64url-encoded (invalid characters found)",
1403                param_name
1404            ),
1405        }));
1406    }
1407
1408    // Try to decode and check the length
1409    match Base64UrlUnpadded::decode_vec(thumbprint) {
1410        Ok(decoded) => {
1411            if decoded.len() != expected_bytes {
1412                return Err(Error::Validation(ValidationError::InvalidParameter {
1413                    name: param_name,
1414                    reason: format!(
1415                        "RFC 7517: {} must be {} bytes when decoded (got {} bytes)",
1416                        param_name,
1417                        expected_bytes,
1418                        decoded.len()
1419                    ),
1420                }));
1421            }
1422            Ok(())
1423        }
1424        Err(_) => Err(Error::Validation(ValidationError::InvalidParameter {
1425            name: param_name,
1426            reason: format!("RFC 7517: {} failed base64url decoding", param_name),
1427        })),
1428    }
1429}
1430
1431/// Validates that a string is valid base64url encoding.
1432/// This checks the character set (A-Z, a-z, 0-9, -, _) without padding.
1433fn is_valid_base64url(s: &str) -> bool {
1434    if s.is_empty() {
1435        return false;
1436    }
1437
1438    for b in s.bytes() {
1439        match b {
1440            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' => {}
1441            _ => return false,
1442        }
1443    }
1444
1445    // Valid base64url without padding has len % 4 of 0, 2, or 3 (not 1)
1446    s.len() % 4 != 1
1447}
1448
1449#[cfg(test)]
1450mod tests {
1451    use super::*;
1452
1453    #[test]
1454    fn test_parse_rsa_public_key() {
1455        let json = r#"{
1456            "kty": "RSA",
1457            "kid": "test-key",
1458            "use": "sig",
1459            "alg": "RS256",
1460            "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
1461            "e": "AQAB"
1462        }"#;
1463
1464        let jwk: Key = serde_json::from_str(json).unwrap();
1465        assert_eq!(jwk.kty, KeyType::Rsa);
1466        assert_eq!(jwk.kid, Some("test-key".to_string()));
1467        assert_eq!(jwk.key_use, Some(KeyUse::Signature));
1468        assert_eq!(jwk.alg, Some(Algorithm::Rs256));
1469        assert!(jwk.is_public_key_only());
1470    }
1471
1472    #[test]
1473    fn test_parse_ec_public_key() {
1474        let json = r#"{
1475            "kty": "EC",
1476            "crv": "P-256",
1477            "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
1478            "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
1479        }"#;
1480
1481        let jwk: Key = serde_json::from_str(json).unwrap();
1482        assert_eq!(jwk.kty, KeyType::Ec);
1483
1484        let ec = jwk.as_ec().unwrap();
1485        assert_eq!(ec.crv, EcCurve::P256);
1486        assert!(jwk.is_public_key_only());
1487    }
1488
1489    #[test]
1490    fn test_parse_symmetric_key() {
1491        let json = r#"{
1492            "kty": "oct",
1493            "k": "GawgguFyGrWKav7AX4VKUg"
1494        }"#;
1495
1496        let jwk: Key = serde_json::from_str(json).unwrap();
1497        assert_eq!(jwk.kty, KeyType::Symmetric);
1498        assert!(jwk.as_symmetric().is_some());
1499    }
1500
1501    #[test]
1502    fn test_parse_okp_key() {
1503        let json = r#"{
1504            "kty": "OKP",
1505            "crv": "Ed25519",
1506            "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
1507        }"#;
1508
1509        let jwk: Key = serde_json::from_str(json).unwrap();
1510        assert_eq!(jwk.kty, KeyType::Okp);
1511
1512        let okp = jwk.as_okp().unwrap();
1513        assert_eq!(okp.crv, OkpCurve::Ed25519);
1514    }
1515
1516    #[test]
1517    fn test_roundtrip_serialization() {
1518        let json = r#"{"kty":"RSA","kid":"test","use":"sig","n":"AQAB","e":"AQAB"}"#;
1519        let jwk: Key = serde_json::from_str(json).unwrap();
1520        let serialized = serde_json::to_string(&jwk).unwrap();
1521        let deserialized: Key = serde_json::from_str(&serialized).unwrap();
1522        assert_eq!(jwk, deserialized);
1523    }
1524}