ts_token/
jwk.rs

1//! A JSON Web Key.
2
3use alloc::string::String;
4
5use base64ct::{Base64UrlUnpadded, Encoding};
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8use ts_crypto::{
9    any,
10    ecdsa::{
11        P256VerifyingKey, P384VerifyingKey, P521VerifyingKey, SECP_256_R_1, SECP_384_R_1,
12        SECP_521_R_1, VerifyingKey as _,
13    },
14    eddsa::{Ed448VerifyingKey, Ed25519VerifyingKey, ID_ED_448, ID_ED_25519, VerifyingKey as _},
15    rsa::{RsaVerifyingKey, VerifyingKey as _},
16};
17use ts_io::Base64Url;
18
19use crate::{Algorithm, Curve, JsonWebToken};
20
21/// A JSON web key used to verify signatures.
22///
23/// <https://www.iana.org/assignments/jose/jose.xhtml>
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
26pub struct JsonWebKey {
27    /// The ID of this key.
28    pub kid: String,
29
30    /// The use for this key.
31    /// <http://www.iana.org/assignments/jose/jose.xhtml#web-key-use>
32    #[serde(rename = "use")]
33    pub usage: String,
34
35    /// The algorithm for this key
36    pub alg: Algorithm,
37
38    /// This key's curve
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub crv: Option<Curve>,
41
42    /// The parameters that make up the public key.
43    #[serde(flatten)]
44    pub kty: KeyType,
45}
46
47/// <https://www.iana.org/assignments/jose/jose.xhtml#web-key-types>
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
50#[serde(tag = "kty")]
51#[non_exhaustive]
52pub enum KeyType {
53    /// <https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1>
54    #[serde(rename = "EC")]
55    #[allow(missing_docs)]
56    Ec { x: Base64Url, y: Base64Url },
57
58    /// <https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3>
59    #[serde(rename = "RSA")]
60    #[allow(missing_docs)]
61    Rsa { n: Base64Url, e: Base64Url },
62    /// <https://www.rfc-editor.org/rfc/rfc8037.html#section-2>
63    #[serde(rename = "OKP")]
64    #[allow(missing_docs)]
65    Okp { x: Base64Url },
66}
67
68impl KeyType {
69    /// Get the key ID from the key parameters
70    pub fn kid(&self) -> String {
71        let mut digest = Sha256::default();
72        match &self {
73            Self::Ec { x, y } => {
74                digest.update(x);
75                digest.update(y);
76            }
77            Self::Rsa { n, e } => {
78                digest.update(n);
79                digest.update(e);
80            }
81            Self::Okp { x } => {
82                digest.update(x);
83            }
84        };
85        let hash = digest.finalize();
86        Base64UrlUnpadded::encode_string(&hash)
87    }
88}
89
90impl JsonWebKey {
91    /// Returns if the signature on a JSON web token is valid according to this key.
92    pub fn verifies_signature(&self, jwt: &JsonWebToken) -> bool {
93        match &self.kty {
94            KeyType::Ec { x, y } => {
95                let key: Box<dyn ts_crypto::ecdsa::VerifyingKey> = match self.crv {
96                    Some(Curve::P256) => {
97                        let Some(key) = P256VerifyingKey::from_coordinates(x, y) else {
98                            return false;
99                        };
100                        Box::new(key)
101                    }
102                    Some(Curve::P384) => {
103                        let Some(key) = P384VerifyingKey::from_coordinates(x, y) else {
104                            return false;
105                        };
106                        Box::new(key)
107                    }
108                    Some(Curve::P521) => {
109                        let Some(key) = P521VerifyingKey::from_coordinates(x, y) else {
110                            return false;
111                        };
112                        Box::new(key)
113                    }
114                    _ => return false,
115                };
116                match self.alg {
117                    Algorithm::ES256 => key.verifies_sha256(&jwt.signature, &jwt.message()),
118                    Algorithm::ES384 => key.verifies_sha384(&jwt.signature, &jwt.message()),
119                    Algorithm::ES512 => key.verifies_sha512(&jwt.signature, &jwt.message()),
120                    _ => false,
121                }
122            }
123            KeyType::Rsa { n, e } => {
124                let Some(key) = RsaVerifyingKey::from_parameters(n, e) else {
125                    return false;
126                };
127
128                match self.alg {
129                    Algorithm::RS256 => key.verifies_rs256(&jwt.signature, &jwt.message()),
130                    Algorithm::RS384 => key.verifies_rs384(&jwt.signature, &jwt.message()),
131                    Algorithm::RS512 => key.verifies_rs512(&jwt.signature, &jwt.message()),
132                    Algorithm::PS256 => key.verifies_ps256(&jwt.signature, &jwt.message()),
133                    Algorithm::PS384 => key.verifies_ps384(&jwt.signature, &jwt.message()),
134                    Algorithm::PS512 => key.verifies_ps512(&jwt.signature, &jwt.message()),
135                    _ => false,
136                }
137            }
138            KeyType::Okp { x } => match (self.alg, self.crv) {
139                (Algorithm::Ed25519, _) | (Algorithm::EdDSA, Some(Curve::Ed25519)) => {
140                    let Some(key) = Ed25519VerifyingKey::from_raw_public_key(x) else {
141                        return false;
142                    };
143                    key.verifies(&jwt.signature, &jwt.message())
144                }
145                (Algorithm::Ed448, _) | (Algorithm::EdDSA, Some(Curve::Ed448)) => {
146                    let Some(key) = Ed448VerifyingKey::from_raw_public_key(x) else {
147                        return false;
148                    };
149                    key.verifies(&jwt.signature, &jwt.message())
150                }
151                _ => false,
152            },
153        }
154    }
155
156    /// Create a JSON web key from a key.
157    pub fn from_key(key: &any::VerifyingKey) -> Option<Self> {
158        let (kty, alg, crv) = match &key {
159            any::VerifyingKey::Ecdsa(key) => {
160                let (alg, crv) = match key.curve_oid() {
161                    SECP_256_R_1 => (Algorithm::ES256, Curve::P256),
162                    SECP_384_R_1 => (Algorithm::ES384, Curve::P384),
163                    SECP_521_R_1 => (Algorithm::ES512, Curve::P521),
164                    _ => return None,
165                };
166
167                let kty = KeyType::Ec {
168                    x: Base64Url(key.x()),
169                    y: Base64Url(key.y()),
170                };
171                (kty, alg, Some(crv))
172            }
173            any::VerifyingKey::EdDsa(key) => {
174                let crv = match key.curve_oid() {
175                    ID_ED_25519 => Curve::Ed25519,
176                    ID_ED_448 => Curve::Ed448,
177                    _ => return None,
178                };
179
180                let kty = KeyType::Okp {
181                    x: Base64Url(key.raw_public_key()),
182                };
183                (kty, Algorithm::EdDSA, Some(crv))
184            }
185            any::VerifyingKey::Rsa(key) => {
186                let alg = Algorithm::PS256;
187                let kty = KeyType::Rsa {
188                    n: Base64Url(key.modulus()),
189                    e: Base64Url(key.exponent()),
190                };
191                (kty, alg, None)
192            }
193        };
194
195        Some(Self {
196            kid: kty.kid(),
197            usage: "sig".to_string(),
198            alg,
199            crv,
200            kty,
201        })
202    }
203}