Skip to main content

jwt/
jwk.rs

1use serde::{Deserialize, Serialize};
2use small_collections::SmallString;
3use smallvec::SmallVec;
4
5use crate::{
6    Algorithm, Blake3Key, Ed25519PublicKey, Ed25519SecretKey, Error, HmacSha256Key, HmacSha512Key, P256PublicKey,
7    P256SecretKey, RsaPublicKey, Signature, Signer, Verifier,
8};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Jwks {
12    pub keys: SmallVec<Jwk, 5>,
13}
14
15/// a JSON Web Key
16/// https://www.rfc-editor.org/rfc/rfc7517
17/// https://www.rfc-editor.org/rfc/rfc8037
18/// Note: Jwk are not validated during deserialization
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Jwk {
21    pub kid: SmallString<36>, // 36 = UUID length
22    pub r#use: KeyUse,
23    #[serde(rename = "alg")]
24    pub algorithm: Algorithm,
25
26    #[serde(flatten)]
27    pub crypto: JwkCrypto,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(rename_all = "UPPERCASE", tag = "kty")]
32pub enum JwkCrypto {
33    /// EdDSA
34    Okp {
35        #[serde(rename = "crv")]
36        curve: OkpCurve,
37        #[serde(with = "base64_url_no_padding")]
38        x: SmallVec<u8, 32>,
39        #[serde(with = "base64_url_no_padding::option", skip_serializing_if = "Option::is_none")]
40        d: Option<SmallVec<u8, 32>>,
41    },
42    /// ECDSA
43    Ec {
44        #[serde(rename = "crv")]
45        curve: EcCurve,
46        #[serde(with = "base64_url_no_padding")]
47        x: SmallVec<u8, 32>,
48        #[serde(with = "base64_url_no_padding")]
49        y: SmallVec<u8, 32>,
50        #[serde(with = "base64_url_no_padding::option", skip_serializing_if = "Option::is_none")]
51        d: Option<SmallVec<u8, 32>>,
52    },
53    /// Static keys
54    #[serde(rename = "oct")]
55    Oct {
56        #[serde(with = "base64_url_no_padding")]
57        key: SmallVec<u8, 32>,
58    },
59    /// RSA public key
60    #[serde(rename = "RSA")]
61    Rsa {
62        // Always heap-allocated. We use a `SmallVec` to avoid needing a separate implmentation for
63        // serde's `base64_url_no_padding`.
64        #[serde(with = "base64_url_no_padding")]
65        n: SmallVec<u8, 0>,
66        #[serde(with = "base64_url_no_padding")]
67        e: SmallVec<u8, 4>,
68    },
69}
70
71#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
72pub enum KeyUse {
73    #[serde(rename = "sig")]
74    Sign,
75    #[serde(rename = "enc")]
76    Encrypt,
77}
78
79// https://csrc.nist.gov/pubs/fips/186-5/final
80// https://csrc.nist.gov/pubs/sp/800/186/final
81// https://www.rfc-editor.org/rfc/rfc8032
82#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
83pub enum OkpCurve {
84    Ed25519,
85}
86
87impl core::str::FromStr for OkpCurve {
88    type Err = Error;
89
90    fn from_str(s: &str) -> Result<Self, Self::Err> {
91        match s {
92            "Ed25519" => Ok(OkpCurve::Ed25519),
93            _ => Err(Error::InvalidCurve),
94        }
95    }
96}
97
98impl core::fmt::Display for OkpCurve {
99    fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
100        write!(f, "{self:?}")
101    }
102}
103
104#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
105pub enum EcCurve {
106    /// P-256 and SHA-256
107    P256,
108
109    /// P-384 and SHA-384
110    P384,
111
112    /// P-521 and SHA-512
113    P521,
114}
115
116impl From<&Blake3Key> for Jwk {
117    #[inline]
118    fn from(key: &Blake3Key) -> Self {
119        return Jwk {
120            kid: SmallString::new(),
121            r#use: KeyUse::Sign,
122            algorithm: Algorithm::BLAKE3,
123            crypto: JwkCrypto::Oct {
124                key: key.as_bytes().into(),
125            },
126        };
127    }
128}
129
130impl From<&HmacSha256Key> for Jwk {
131    #[inline]
132    fn from(key: &HmacSha256Key) -> Self {
133        return Jwk {
134            kid: SmallString::new(),
135            r#use: KeyUse::Sign,
136            algorithm: Algorithm::HS256,
137            crypto: JwkCrypto::Oct {
138                key: key.as_bytes().into(),
139            },
140        };
141    }
142}
143
144impl From<&HmacSha512Key> for Jwk {
145    #[inline]
146    fn from(key: &HmacSha512Key) -> Self {
147        return Jwk {
148            kid: SmallString::new(),
149            r#use: KeyUse::Sign,
150            algorithm: Algorithm::HS512,
151            crypto: JwkCrypto::Oct {
152                key: key.as_bytes().into(),
153            },
154        };
155    }
156}
157
158impl From<&Ed25519SecretKey> for Jwk {
159    #[inline]
160    fn from(key: &Ed25519SecretKey) -> Self {
161        return Jwk {
162            kid: SmallString::new(),
163            r#use: KeyUse::Sign,
164            algorithm: Algorithm::EdDSA,
165            crypto: JwkCrypto::Okp {
166                curve: OkpCurve::Ed25519,
167                x: key.public_key().to_bytes().into(),
168                d: Some(key.to_bytes().into()),
169            },
170        };
171    }
172}
173
174impl From<&Ed25519PublicKey> for Jwk {
175    #[inline]
176    fn from(key: &Ed25519PublicKey) -> Self {
177        return Jwk {
178            kid: SmallString::new(),
179            r#use: KeyUse::Sign,
180            algorithm: Algorithm::EdDSA,
181            crypto: JwkCrypto::Okp {
182                curve: OkpCurve::Ed25519,
183                x: key.to_bytes().into(),
184                d: None,
185            },
186        };
187    }
188}
189
190impl From<&P256SecretKey> for Jwk {
191    #[inline]
192    fn from(key: &P256SecretKey) -> Self {
193        let public_key = key.public_key();
194        let (x, y) = public_key.key.x_y();
195        return Jwk {
196            kid: SmallString::new(),
197            r#use: KeyUse::Sign,
198            algorithm: Algorithm::ES256,
199            crypto: JwkCrypto::Ec {
200                curve: EcCurve::P256,
201                x: x.into(),
202                y: y.into(),
203                d: Some(key.to_bytes().into()),
204            },
205        };
206    }
207}
208
209impl From<&P256PublicKey> for Jwk {
210    #[inline]
211    fn from(key: &P256PublicKey) -> Self {
212        let (x, y) = key.key.x_y();
213        return Jwk {
214            kid: SmallString::new(),
215            r#use: KeyUse::Sign,
216            algorithm: Algorithm::ES256,
217            crypto: JwkCrypto::Ec {
218                curve: EcCurve::P256,
219                x: x.into(),
220                y: y.into(),
221                d: None,
222            },
223        };
224    }
225}
226
227impl From<&RsaPublicKey> for Jwk {
228    fn from(key: &RsaPublicKey) -> Self {
229        Jwk {
230            kid: SmallString::new(),
231            r#use: KeyUse::Sign,
232            algorithm: key.algorithm(),
233            crypto: JwkCrypto::Rsa {
234                n: key.key.n_bytes().into(),
235                e: key.key.e_bytes().into(),
236            },
237        }
238    }
239}
240
241impl From<&Key> for Jwk {
242    fn from(key: &Key) -> Self {
243        match key {
244            Key::Blake3(k) => Jwk::from(k),
245            Key::HmacSha256(k) => Jwk::from(k),
246            Key::HmacSha512(k) => Jwk::from(k),
247            Key::Ed25519Secret(k) => Jwk::from(k),
248            Key::Ed25519Public(k) => Jwk::from(k),
249            Key::P256Secret(k) => Jwk::from(k),
250            Key::P256Public(k) => Jwk::from(k),
251            Key::RsaPublic(k) => Jwk::from(k.as_ref()),
252        }
253    }
254}
255
256////////////////////////////////////////////////////////////////////////////////////////////////////
257// Key
258////////////////////////////////////////////////////////////////////////////////////////////////////
259
260/// A concrete cryptographic key extracted from and that can be converted to a [`Jwk`].
261///
262/// Use [`Key::try_from`]`(&Jwk)` to convert a [`Jwk`] (e.g. from a JWKS endpoint)
263/// into a key without knowing the exact key type at compile time. The result
264/// can be passed directly to [`crate::parse_and_verify`] or [`crate::sign`].
265///
266/// Use [`Key::into`]`(Jwk)` to convert a [`Key`] into a [`Jwk`].
267///
268/// `RsaPublicKey`s require heap-allocation to avoid bloating the enum, as RSA should now be deprecated.
269pub enum Key {
270    Blake3(Blake3Key),
271    HmacSha256(HmacSha256Key),
272    HmacSha512(HmacSha512Key),
273    Ed25519Secret(Ed25519SecretKey),
274    Ed25519Public(Ed25519PublicKey),
275    P256Secret(P256SecretKey),
276    P256Public(P256PublicKey),
277    RsaPublic(alloc::boxed::Box<RsaPublicKey>),
278}
279
280impl TryFrom<&Jwk> for Key {
281    type Error = Error;
282
283    fn try_from(jwk: &Jwk) -> Result<Self, Error> {
284        match &jwk.crypto {
285            JwkCrypto::Okp {
286                curve,
287                x,
288                d,
289            } => match curve {
290                OkpCurve::Ed25519 => match d {
291                    Some(d_bytes) => {
292                        let seed: [u8; 32] = d_bytes.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
293                        Ok(Key::Ed25519Secret(Ed25519SecretKey::from_bytes(&seed)?))
294                    }
295                    None => {
296                        let pk: [u8; 32] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
297                        Ok(Key::Ed25519Public(Ed25519PublicKey::from_bytes(&pk)?))
298                    }
299                },
300            },
301            JwkCrypto::Ec {
302                curve,
303                x,
304                y,
305                d,
306            } => match curve {
307                EcCurve::P256 => {
308                    let x_arr: [u8; 32] = x.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
309                    let y_arr: [u8; 32] = y.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
310                    match d {
311                        Some(d_bytes) => {
312                            let key: [u8; 32] = d_bytes.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
313                            Ok(Key::P256Secret(P256SecretKey::from_bytes(&key)?))
314                        }
315                        None => Ok(Key::P256Public(P256PublicKey::from_x_y(&x_arr, &y_arr)?)),
316                    }
317                }
318                EcCurve::P384 | EcCurve::P521 => Err(Error::InvalidEllipticCurve(alloc::format!("{curve:?}"))),
319            },
320            JwkCrypto::Oct {
321                key,
322            } => match jwk.algorithm {
323                Algorithm::BLAKE3 => {
324                    let arr: [u8; 32] = key.as_slice().try_into().map_err(|_| Error::InvalidKey)?;
325                    Ok(Key::Blake3(Blake3Key::from_bytes(&arr)))
326                }
327                Algorithm::HS256 => Ok(Key::HmacSha256(HmacSha256Key::from_bytes(key)?)),
328                Algorithm::HS512 => Ok(Key::HmacSha512(HmacSha512Key::from_bytes(key)?)),
329                _ => Err(Error::InvalidJwk {
330                    kid: alloc::format!("{}", jwk.kid),
331                    err: alloc::format!("unsupported algorithm for oct key: {:?}", jwk.algorithm),
332                }),
333            },
334            JwkCrypto::Rsa {
335                n,
336                e,
337            } => {
338                let key = RsaPublicKey::from_n_e(jwk.algorithm, n, e)?;
339                Ok(Key::RsaPublic(alloc::boxed::Box::new(key)))
340            }
341        }
342    }
343}
344
345impl Signer for Key {
346    fn sign(&self, message: &[u8]) -> Result<Signature, Error> {
347        match self {
348            Key::Blake3(k) => k.sign(message),
349            Key::HmacSha256(k) => k.sign(message),
350            Key::HmacSha512(k) => k.sign(message),
351            Key::Ed25519Secret(k) => k.sign(message),
352            Key::P256Secret(k) => k.sign(message),
353            Key::Ed25519Public(_) | Key::P256Public(_) | Key::RsaPublic(_) => Err(Error::InvalidKey),
354        }
355    }
356
357    fn algorithm(&self) -> Algorithm {
358        match self {
359            Key::Blake3(k) => Signer::algorithm(k),
360            Key::HmacSha256(k) => Signer::algorithm(k),
361            Key::HmacSha512(k) => Signer::algorithm(k),
362            Key::Ed25519Secret(k) => k.algorithm(),
363            Key::P256Secret(k) => k.algorithm(),
364            Key::Ed25519Public(k) => k.algorithm(),
365            Key::P256Public(k) => k.algorithm(),
366            Key::RsaPublic(k) => k.algorithm(),
367        }
368    }
369}
370
371impl Verifier for Key {
372    fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Error> {
373        match self {
374            Key::Blake3(k) => k.verify(message, signature),
375            Key::HmacSha256(k) => k.verify(message, signature),
376            Key::HmacSha512(k) => k.verify(message, signature),
377            Key::Ed25519Secret(k) => k.public_key().verify(message, signature),
378            Key::Ed25519Public(k) => k.verify(message, signature),
379            Key::P256Secret(k) => k.public_key().verify(message, signature),
380            Key::P256Public(k) => k.verify(message, signature),
381            Key::RsaPublic(k) => k.verify(message, signature),
382        }
383    }
384
385    fn algorithm(&self) -> Algorithm {
386        match self {
387            Key::Blake3(k) => Verifier::algorithm(k),
388            Key::HmacSha256(k) => Verifier::algorithm(k),
389            Key::HmacSha512(k) => Verifier::algorithm(k),
390            Key::Ed25519Secret(k) => k.algorithm(),
391            Key::P256Secret(k) => k.algorithm(),
392            Key::Ed25519Public(k) => k.algorithm(),
393            Key::P256Public(k) => k.algorithm(),
394            Key::RsaPublic(k) => k.algorithm(),
395        }
396    }
397}
398
399mod base64_url_no_padding {
400    use base64::{Alphabet, decode, encode};
401    use serde::{Deserializer, Serializer};
402
403    use super::*;
404
405    pub fn serialize<S: Serializer, const N: usize>(data: &SmallVec<u8, N>, serializer: S) -> Result<S::Ok, S::Error> {
406        serializer.serialize_str(&encode(data, Alphabet::UrlNoPadding))
407    }
408
409    pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(
410        deserializer: D,
411    ) -> Result<SmallVec<u8, N>, D::Error> {
412        let s = <&str>::deserialize(deserializer)?;
413        let bytes = decode(s.as_bytes(), Alphabet::UrlNoPadding).map_err(serde::de::Error::custom)?;
414        Ok(SmallVec::from(bytes))
415    }
416
417    pub(crate) mod option {
418        use alloc::string::String;
419
420        use super::*;
421
422        pub fn serialize<S: Serializer, const N: usize>(
423            data: &Option<SmallVec<u8, N>>,
424            serializer: S,
425        ) -> Result<S::Ok, S::Error> {
426            match data {
427                Some(val) => serializer.serialize_str(&encode(val, Alphabet::UrlNoPadding)),
428                None => serializer.serialize_none(),
429            }
430        }
431
432        pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(
433            deserializer: D,
434        ) -> Result<Option<SmallVec<u8, N>>, D::Error> {
435            let opt: Option<String> = Option::deserialize(deserializer)?;
436            match opt {
437                Some(s) => {
438                    let bytes = decode(s.as_bytes(), Alphabet::UrlNoPadding).map_err(serde::de::Error::custom)?;
439                    Ok(Some(SmallVec::from(bytes)))
440                }
441                None => Ok(None),
442            }
443        }
444    }
445}