jwt_lab/
jwk.rs

1use crate::{types::Algorithm, Error, Result};
2use serde::{Deserialize, Serialize};
3use base64ct::Encoding;
4
5/// Cryptographic keys for JWT operations
6#[derive(Debug, Clone)]
7pub enum Key {
8    /// HMAC shared secret key
9    Hs(Vec<u8>),
10    /// RSA public key in PEM format
11    RsaPublicPem(Vec<u8>),
12    /// RSA private key in PEM format
13    RsaPrivatePem(Vec<u8>),
14    /// ECDSA public key in PEM format
15    EcPublicPem(Vec<u8>),
16    /// ECDSA private key in PEM format
17    EcPrivatePem(Vec<u8>),
18    /// EdDSA public key in PEM format
19    EdPublicPem(Vec<u8>),
20    /// EdDSA private key in PEM format
21    EdPrivatePem(Vec<u8>),
22}
23impl Key {
24    /// Create an HMAC key from a shared secret
25    pub fn hs(secret: impl AsRef<[u8]>) -> Self { Key::Hs(secret.as_ref().to_vec()) }
26    /// Create an RSA public key from PEM data
27    pub fn rsa_public_pem(pem: impl AsRef<[u8]>) -> Self { Key::RsaPublicPem(pem.as_ref().to_vec()) }
28    /// Create an RSA private key from PEM data
29    pub fn rsa_private_pem(pem: impl AsRef<[u8]>) -> Self { Key::RsaPrivatePem(pem.as_ref().to_vec()) }
30    /// Create an ECDSA public key from PEM data
31    pub fn ec_public_pem(pem: impl AsRef<[u8]>) -> Self { Key::EcPublicPem(pem.as_ref().to_vec()) }
32    /// Create an ECDSA private key from PEM data
33    pub fn ec_private_pem(pem: impl AsRef<[u8]>) -> Self { Key::EcPrivatePem(pem.as_ref().to_vec()) }
34    /// Create an EdDSA public key from PEM data
35    pub fn ed_public_pem(pem: impl AsRef<[u8]>) -> Self { Key::EdPublicPem(pem.as_ref().to_vec()) }
36    /// Create an EdDSA private key from PEM data
37    pub fn ed_private_pem(pem: impl AsRef<[u8]>) -> Self { Key::EdPrivatePem(pem.as_ref().to_vec()) }
38}
39
40/// JSON Web Key (JWK) representation
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Jwk {
43    /// Key type (e.g., "RSA", "EC", "OKP", "oct")
44    pub kty: String,
45    /// Key ID
46    #[serde(default)]
47    pub kid: Option<String>,
48    /// Algorithm
49    #[serde(default)]
50    pub alg: Option<String>,
51    /// Key use
52    #[serde(default, rename = "use")]
53    pub use_: Option<String>,
54    /// RSA modulus
55    #[serde(default)] pub n: Option<String>,
56    /// RSA exponent
57    #[serde(default)] pub e: Option<String>,
58    /// Private exponent (RSA, EC, OKP)
59    #[serde(default)] pub d: Option<String>,
60    /// RSA first prime factor
61    #[serde(default)] pub p: Option<String>,
62    /// RSA second prime factor
63    #[serde(default)] pub q: Option<String>,
64    /// RSA first factor CRT exponent
65    #[serde(default)] pub dp: Option<String>,
66    /// RSA second factor CRT exponent
67    #[serde(default)] pub dq: Option<String>,
68    /// RSA first CRT coefficient
69    #[serde(default)] pub qi: Option<String>,
70    /// Curve name (EC, OKP)
71    #[serde(default)] pub crv: Option<String>,
72    /// X coordinate (EC, OKP)
73    #[serde(default)] pub x: Option<String>,
74    /// Y coordinate (EC)
75    #[serde(default)] pub y: Option<String>,
76    /// Octet key value
77    #[serde(default)] pub k: Option<String>,
78}
79
80/// JSON Web Key Set (JWKS) containing multiple JWKs
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct Jwks {
83    /// List of JSON Web Keys
84    pub keys: Vec<Jwk>
85}
86
87impl Jwks {
88    /// Parse a JWKS from a JSON string
89    ///
90    /// # Arguments
91    ///
92    /// * `s` - JSON string containing the JWKS
93    ///
94    /// # Returns
95    ///
96    /// Returns a `Result` containing the parsed JWKS or an error
97    pub fn from_str(s: &str) -> Result<Self> {
98        serde_json::from_str(s).map_err(|e| Error::Json(e.to_string()))
99    }
100    /// Select a key from the JWKS by kid and algorithm
101    ///
102    /// # Arguments
103    ///
104    /// * `kid` - Key ID to search for
105    /// * `alg` - Algorithm to use for key conversion
106    ///
107    /// # Returns
108    ///
109    /// Returns a `Result` containing the selected key or an error
110    ///
111    /// # Errors
112    ///
113    /// - `Error::KidNotFound` if no key with the given kid is found
114    /// - `Error::Key` if key conversion fails
115    pub fn select_for(&self, kid: &str, alg: crate::types::Algorithm) -> Result<Key> {
116        let jwk = self.keys.iter().find(|k| k.kid.as_deref() == Some(kid)).ok_or(Error::KidNotFound)?;
117        jwk.to_key_for(alg)
118    }
119}
120
121impl Jwk {
122    /// Convert this JWK to a Key for the given algorithm
123    ///
124    /// # Arguments
125    ///
126    /// * `alg` - The algorithm to use for key conversion
127    ///
128    /// # Returns
129    ///
130    /// Returns a `Result` containing the converted key or an error
131    ///
132    /// # Errors
133    ///
134    /// - `Error::Key` if the JWK is invalid or conversion fails
135    ///
136    /// # Note
137    ///
138    /// Currently only supports octet keys (HMAC). RSA, EC, and EdDSA JWK to PEM
139    /// conversion is not implemented and will return an error.
140    pub fn to_key_for(&self, alg: Algorithm) -> Result<Key> {
141        match self.kty.as_str() {
142            "oct" => {
143                let k = self.k.as_ref().ok_or_else(|| Error::Key("oct missing k".into()))?;
144                let bytes = base64ct::Base64UrlUnpadded::decode_vec(k).map_err(|e| Error::Key(e.to_string()))?;
145                Ok(Key::Hs(bytes))
146            }
147            "RSA" => match alg {
148                Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => {
149                    Err(Error::Key("RSA JWK to PEM conversion not implemented".into()))
150                }
151                _ => Err(Error::Key("RSA JWK used with non-RS alg".into())),
152            }
153            "EC" => match (self.crv.as_deref(), alg) {
154                (Some("P-256"), Algorithm::ES256) |
155                (Some("P-384"), Algorithm::ES384) |
156                (Some("P-521"), Algorithm::ES512) => {
157                    Err(Error::Key("EC JWK to PEM conversion not implemented".into()))
158                }
159                _ => Err(Error::Key("EC JWK and alg mismatch or unsupported curve".into())),
160            }
161            "OKP" => match (self.crv.as_deref(), alg) {
162                (Some("Ed25519"), Algorithm::EdDSA) => {
163                    Err(Error::Key("EdDSA JWK to PEM conversion not implemented".into()))
164                }
165                _ => Err(Error::Key("OKP JWK not Ed25519".into())),
166            }
167            _ => Err(Error::Key("unsupported kty".into())),
168        }
169    }
170}
171
172/// Key source selection strategy
173#[derive(Debug, Clone, Copy)]
174pub enum KeySource {
175    /// Automatically select the appropriate key source
176    Auto
177}