jsonwebtoken_rustcrypto/
jwk.rs

1use std::convert::{TryFrom, TryInto};
2// use std::time::Duration;
3
4use crate::{dangerous_insecure_decode_with_validation, decode, decode_header};
5use crate::{errors::new_error, Algorithm, DecodingKey, TokenData, Validation};
6use serde::{self, de::DeserializeOwned, Deserialize, Serialize};
7
8use crate::errors::{Error, ErrorKind, Result};
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
11pub struct JWK {
12    pub kty: JsonWebKeyTypes,
13    pub alg: Option<Algorithm>,
14    pub kid: Option<String>,
15    #[serde(rename = "use")]
16    pub key_use: Option<JwkPublicKeyUse>,
17
18    pub e: Option<String>,
19    pub n: Option<String>,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize)]
23pub struct JWKS {
24    keys: Vec<JWK>,
25}
26
27#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
28pub enum JsonWebKeyTypes {
29    #[serde(rename = "RSA")]
30    Rsa,
31    #[serde(rename = "EC")]
32    Ec,
33    #[serde(rename = "oct")]
34    OctetSeq,
35}
36#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
37pub enum JwkPublicKeyUse {
38    #[serde(rename = "sig")]
39    Signature,
40    #[serde(rename = "enc")]
41    Encryption,
42}
43
44#[derive(Clone, Debug)]
45pub struct JWKDecodingKey {
46    pub alg: Option<Algorithm>,
47    pub kid: Option<String>,
48    pub key: DecodingKey,
49}
50
51impl JWKDecodingKey {
52    pub fn new(kid: Option<String>, alg: Option<Algorithm>, key: DecodingKey) -> JWKDecodingKey {
53        JWKDecodingKey { alg, kid, key }
54    }
55
56    pub fn new_rsa(
57        kid: std::option::Option<String>,
58        alg: Option<Algorithm>,
59        n: &str,
60        e: &str,
61    ) -> Result<JWKDecodingKey> {
62        Ok(JWKDecodingKey { alg, kid, key: DecodingKey::from_rsa_components(n, e)? })
63    }
64
65    pub fn decoding_key(&self) -> &DecodingKey {
66        &self.key
67    }
68}
69
70impl TryFrom<JWK> for JWKDecodingKey {
71    type Error = Error;
72
73    fn try_from(JWK { kid, alg, kty, key_use: _, n, e }: JWK) -> Result<JWKDecodingKey> {
74        let key = match (kty, n, e) {
75            (JsonWebKeyTypes::Rsa, Some(n), Some(e)) => {
76                JWKDecodingKey::new(kid, alg, DecodingKey::from_rsa_components(&n, &e)?)
77            }
78            (JsonWebKeyTypes::Rsa, _, _) => return Err(new_error(ErrorKind::InvalidRsaKey)),
79            (_, _, _) => return Err(new_error(ErrorKind::UnsupportedKeyType)),
80        };
81        Ok(key)
82    }
83}
84
85#[derive(Clone)]
86pub struct JWKDecodingKeySet {
87    pub(crate) keys: Vec<JWKDecodingKey>,
88}
89
90impl TryFrom<JWKS> for JWKDecodingKeySet {
91    type Error = Error;
92
93    fn try_from(jwks: JWKS) -> Result<Self> {
94        let mut ks: JWKDecodingKeySet = JWKDecodingKeySet::new();
95        for key in jwks.keys.iter() {
96            if let Ok(k) = key.clone().try_into() {
97                ks.add_key(k);
98            }
99        }
100        Ok(ks)
101    }
102}
103
104#[allow(dead_code)]
105impl JWKDecodingKeySet {
106    pub fn new() -> JWKDecodingKeySet {
107        JWKDecodingKeySet { keys: Vec::new() }
108    }
109
110    pub fn clear_keys(&mut self) {
111        self.keys.clear();
112    }
113
114    /// Fetch a key by key id (KID)
115    pub fn keys_by_id(&self, kid: String) -> Vec<JWKDecodingKey> {
116        self.keys.iter().filter(|k| k.kid == Some(kid.clone())).cloned().collect()
117    }
118
119    /// Number of keys in keystore
120    pub fn keys_len(&self) -> usize {
121        self.keys.len()
122    }
123
124    /// Manually add a key to the keystore
125    pub fn add_key(&mut self, key: JWKDecodingKey) {
126        self.keys.push(key);
127    }
128
129    /// Verify a JWT token.
130    /// If the token is valid, it is returned.
131    ///
132    /// A token is considered valid if:
133    /// * Is well formed
134    /// * Has a `kid` field that matches a public signature `kid
135    /// * Signature matches public key
136    /// * It is not expired
137    /// * The `nbf` is not set to before now
138    pub fn verify<T: DeserializeOwned>(
139        &self,
140        token: &str,
141        validation: &Validation,
142    ) -> Result<TokenData<T>> {
143        let _ = dangerous_insecure_decode_with_validation::<serde_json::Value>(token, validation)?;
144        let header = decode_header(token)?;
145        // println!("{:?}", self.keys_by_id(header.kid.clone().unwrap()));
146        // println!("{:?}", self.keys_by_id(header.kid.clone().unwrap()).iter().filter(|key| {if let Some(alg) = key.alg {
147        //     alg == header.alg
148        // } else {true}}));
149        // println!("{:?}", self.keys_by_id(header.kid.clone().unwrap()).iter().filter(|key| {if let Some(alg) = key.alg {
150        //     alg == header.alg
151        // } else {true}}).find_map(|key| {Some(decode::<serde_json::Value>(token, &key.key, &validation).unwrap())}));
152        let data = if let Some(ref kid) = header.kid {
153            self.keys_by_id(kid.clone())
154        } else {
155            self.keys.clone()
156        }
157        .iter()
158        .filter(|key| if let Some(alg) = key.alg { alg == header.alg } else { true })
159        .find_map(|key| decode(token, &key.key, validation).ok())
160        .ok_or(new_error(ErrorKind::NoWorkingKey))?;
161
162        Ok(data)
163    }
164}
165
166impl Default for JWKDecodingKeySet {
167    fn default() -> Self {
168        Self::new()
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use std::time::{SystemTime, UNIX_EPOCH};
175
176    use rsa::pkcs8::DecodePrivateKey;
177    use serde::{Deserialize, Serialize};
178
179    use crate::{
180        jwk::{JWKDecodingKeySet, JWKS},
181        Algorithm,
182    };
183
184    const PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----
185MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3k3jgfWalvlaX
186H/R7sJ2gpNjzG37fIxvLwGA11qBU2R4KRnilFuZBdFY0Luqf/9lrHH12fMdvMYK6
187XlbREt93egHc8RLCytCH6iJdUQ9NiG9Q9CRfS8F8Jcm2D/NjaZjUPEo4iJednw9o
188r/tLHRoya9H//RjLZrl0gBYKaM+zGf49wZk6RVSb4PlsfX5lK1wD/ryvhlI+LHtL
189OGCXVGgdv1DolJ6TJmozRB/CGBvuytlVcL/3+bk8TH3AxU1ilgkGteEPkhCb1HMT
1902glLxP4CL7j/mY1M1cjx/WutiB99ZLjB/1y+wSKGu8SD3zrsHZ/3wvu7H0uWV7uO
191wcK3CBXXAgMBAAECggEBALCePOTXIYQDPXIGt4StZupgz64N3SG3uI1ul+IUjYs0
192nPdL19UV1BBTaoCoRuoLENZBwd/Wq1Yqr1i5XROn8cIUa2hztBYfZUPumoNncq6o
193vliHm9rnms7j2E7Gx+b0eKpxGwOLPzvSBdQ1xDL/W+342EFO86T2PyV7+jinoHTx
194wN7aO5AygunyVW8CFoOFfZRFi8g9J4CkZ2HvGzAerMNz41UJc/pNa8Sl7E1Z3HWq
195IHmahP8sVqo/zm/cDpivn6MZKBadASkvBf4TNJU5uZPdn37Rmwl4xVyNyNPCq3On
196afSnvl1Z4XyESUQSi0L/EEkdDV7qgj1MhUHsUMy6SskCgYEA8A+X9dk11TjGBki1
197ulThRteKNGTdweRk6ZiR7dV7+Oq4W8yYrX95f2byxVrFKUhxV5BIYyOtcpqa5FGc
198AXcczpJQ9lUCEqQrQp/JXk4yZrwsQd63PHei72pgp6iZVHhtBJXedWsx3B8fLsz4
199YaE3laTF4OQY6iLH0TDizMKsb4sCgYEAw8PIvzFnLsCNDXfNLxfHu8m4xRaNO4Qn
200q8laJpwL9AIKKnFaKOdmEY0fuyh+YRcBmgSc1OMSOspAguHBRYB7RO0+Q6Qswa/L
201+ra8HTol0Gu021NF81t7fT4tWww/PRDNoobpvkt9xc7t6cCljq6ysP9qKiaO6YvD
2026BVHTDgCvGUCgYBQMoVrpmLrlonhM40ycITJ69vJ4SCMU8a6mzO1JqrB33gzgshG
203vd1TCIt2Dvrq7b6fqB3msNaTAL9aI0Fg4/AFuq+9e2yz2ZByM3tMPAdudtK6NrM0
204SitunDUqDZMbuxeAfqjQxTzk8qiyM9uOkhNFtMQ+ezUvkur6IcDC8w40PQKBgBnO
205MiU53u8jxLQ0yaBekx6m/atFSMLCrfQQK8kRIuY8apijro3byvGfV8J1GQu1W6mw
2061/ecIygKxyw5lB+USyuP1nhWKOwzP0FCW1PC+X8a45FRzdPBiGq/Cn2JBMEI+QJm
207pGNIUkEEAQqiMXC4iKeaU6VQxd/1Y5KxU1+xtnKdAoGBAJIWLnvxcMbF1JNft9Li
208vQlvs/dWpK19XvDXENyEmQQQcYkq35GbtxPDXiRvFrQbr44DwHS7EXthATsl03Ow
209G0nSkIxiRIJNXp6mst6C6hMWzgWV4GBcTUWSw2WhKojwGVRfZgU0d2awSI0YtsT4
210Ko5K8hGLY0C471Wy9yWk+hAI
211-----END PRIVATE KEY-----
212";
213
214    fn valid_token() -> String {
215        let mut claims: serde_json::Map<String, serde_json::Value> =
216            serde_json::from_str(TEST_CLAIMS).unwrap();
217        claims["exp"] =
218            (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 10).into();
219
220        encode_token(claims)
221    }
222    fn early_token() -> String {
223        let mut claims: serde_json::Map<String, serde_json::Value> =
224            serde_json::from_str(TEST_CLAIMS).unwrap();
225        claims["nbf"] =
226            (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 100).into();
227        claims["exp"] =
228            (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 200).into();
229
230        encode_token(claims)
231    }
232    fn expired_token() -> String {
233        let claims: serde_json::Map<String, serde_json::Value> =
234            serde_json::from_str(TEST_CLAIMS).unwrap();
235
236        encode_token(claims)
237    }
238    fn encode_token(claims: serde_json::Map<String, serde_json::Value>) -> String {
239        let key =
240            crate::EncodingKey::from_rsa(rsa::RsaPrivateKey::from_pkcs8_pem(PRIVATE_KEY).unwrap())
241                .unwrap();
242        let mut header = crate::Header::new(crate::Algorithm::RS256);
243        header.kid = Some("1".to_owned());
244        crate::encode(&header, &claims, &key).unwrap()
245    }
246
247    pub const TEST_CLAIMS: &str = r#"
248    {
249        "name": "Ada Lovelace",
250        "iss": "https://example.com/test",
251        "aud": "test",
252        "auth_time": 100,
253        "user_id": "uid123",
254        "sub": "sbu123",
255        "iat": 200,
256        "exp": 500,
257        "nbf": 300,
258        "email": "alovelace@example.com"
259    }"#;
260    pub const KEY_URL: &str =
261        "https://raw.githubusercontent.com/jfbilodeau/jwks-client/0.1.8/test/test-jwks.json";
262    pub const E: &str = "AQAB";
263    pub const N: &str = "t5N44H1mpb5Wlx_0e7CdoKTY8xt-3yMby8BgNdagVNkeCkZ4pRbmQXRWNC7qn__Zaxx9dnzHbzGCul5W0RLfd3oB3PESwsrQh-oiXVEPTYhvUPQkX0vBfCXJtg_zY2mY1DxKOIiXnZ8PaK_7Sx0aMmvR__0Yy2a5dIAWCmjPsxn-PcGZOkVUm-D5bH1-ZStcA_68r4ZSPix7Szhgl1RoHb9Q6JSekyZqM0Qfwhgb7srZVXC_9_m5PEx9wMVNYpYJBrXhD5IQm9RzE9oJS8T-Ai-4_5mNTNXI8f1rrYgffWS4wf9cvsEihrvEg9867B2f98L7ux9Llle7jsHCtwgV1w";
264    pub const INV_CERT: &str = ".XXXeTQnwXrri_uY55fS4IygseBzzbosDM1hP153EZXzNlLH5s29kdlGt2mL_KIjYmQa8hmptt9RwKJHBtw6l4KFHvIcuif86Ix-iI2fCpqNnKyGZfgERV51NXk1THkgWj0GQB6X5cvOoFIdHa9XvgPl_rVmzXSUYDgkhd2t01FOjQeeT6OL2d9KdlQHJqAsvvKVc3wnaYYoSqv2z0IluvK93Tk1dUBU2yWXH34nX3GAVGvIoFoNRiiFfZwFlnz78G0b2fQV7B5g5F8XlNRdD1xmVZXU8X2-xh9LqRpnEakdhecciFHg0u6AyC4c00rlo_HBb69wlXajQ3R4y26Kpxn7HA";
265
266    #[derive(Debug, Serialize, Deserialize)]
267    pub struct TestPayload {
268        pub iss: String,
269        pub name: String,
270        pub email: String,
271    }
272
273    #[derive(Deserialize, Debug)]
274    struct TestClaims {
275        iss: String,
276        name: String,
277        email: String,
278    }
279
280    #[test]
281    fn test_from_json() {
282        use std::convert::TryInto;
283        let jwks: JWKS = reqwest::blocking::get(KEY_URL).unwrap().json().unwrap();
284        assert_eq!(jwks.keys.len(), 2);
285        let key_set: JWKDecodingKeySet = jwks.try_into().unwrap();
286        assert_eq!(key_set.keys.len(), 1);
287    }
288
289    #[test]
290    fn test_add_key() {
291        let key =
292            crate::jwk::JWKDecodingKey::new_rsa(Some("1".into()), Some(Algorithm::RS256), N, E);
293
294        let mut key_set = JWKDecodingKeySet::new();
295
296        assert_eq!(0usize, key_set.keys_len());
297
298        key_set.add_key(key.unwrap());
299
300        assert_eq!(1usize, key_set.keys_len());
301
302        let result = key_set.keys_by_id("1".into());
303
304        assert!(!result.is_empty());
305    }
306
307    #[test]
308    fn test_get_key() {
309        let key =
310            crate::jwk::JWKDecodingKey::new_rsa(Some("1".into()), Some(Algorithm::RS256), N, E);
311
312        let mut key_set = JWKDecodingKeySet::new();
313
314        assert_eq!(0usize, key_set.keys.len());
315
316        key_set.add_key(key.unwrap());
317
318        assert_eq!(1usize, key_set.keys_len());
319
320        let result = key_set.keys_by_id("1".into());
321
322        assert!(!result.is_empty());
323
324        let result = key_set.keys_by_id("2".into());
325
326        assert!(result.is_empty());
327    }
328
329    #[test]
330    fn test_verify() {
331        let key =
332            crate::jwk::JWKDecodingKey::new_rsa(Some("1".into()), Some(Algorithm::RS256), N, E);
333
334        let mut key_set = JWKDecodingKeySet::new();
335
336        key_set.add_key(key.unwrap());
337
338        let validation = crate::Validation {
339            validate_nbf: true,
340            validate_exp: true,
341            algorithms: vec![crate::Algorithm::RS256],
342            leeway: 15,
343            sub: None,
344            aud: None,
345            iss: Some("https://example.com/test".to_owned()),
346        };
347
348        let result: Result<crate::TokenData<TestClaims>, _> =
349            key_set.verify(&valid_token(), &validation);
350
351        assert!(result.is_ok(), "{:?}", result);
352
353        let jwt = result.unwrap();
354
355        assert_eq!("https://example.com/test", jwt.claims.iss);
356        assert_eq!("Ada Lovelace", jwt.claims.name);
357        assert_eq!("alovelace@example.com", jwt.claims.email);
358
359        let result: Result<crate::TokenData<TestClaims>, _> =
360            key_set.verify(&early_token(), &validation); // early
361
362        assert_eq!(format!("{:?}", result), r#"Err(Error(ImmatureSignature))"#);
363
364        let result: Result<crate::TokenData<TestClaims>, _> =
365            key_set.verify(&expired_token(), &validation); // late
366
367        assert_eq!(format!("{:?}", result), r#"Err(Error(ExpiredSignature))"#);
368    }
369
370    #[test]
371    #[should_panic(expected = "NoWorkingKey")]
372    fn test_verify_invalid_certificate() {
373        let key =
374            crate::jwk::JWKDecodingKey::new_rsa(Some("1".into()), Some(Algorithm::RS256), N, E);
375
376        let mut key_set = JWKDecodingKeySet::new();
377
378        key_set.add_key(key.unwrap());
379
380        let validation = crate::Validation {
381            validate_nbf: true,
382            validate_exp: true,
383            algorithms: vec![crate::Algorithm::RS256],
384            leeway: 0,
385            sub: None,
386            aud: None,
387            iss: Some("https://example.com/test".to_owned()),
388        };
389
390        let _result: crate::TokenData<()> = key_set.verify(&valid_token(), &validation).unwrap();
391    }
392
393    #[test]
394    #[should_panic(expected = "NoWorkingKey")]
395    fn test_verify_invalid_signature() {
396        let key =
397            crate::jwk::JWKDecodingKey::new_rsa(Some("1".into()), Some(Algorithm::RS256), N, E);
398
399        let mut key_set = JWKDecodingKeySet::new();
400
401        key_set.add_key(key.unwrap());
402
403        let validation = crate::Validation {
404            validate_nbf: true,
405            validate_exp: true,
406            algorithms: vec![crate::Algorithm::RS256],
407            leeway: 0,
408            sub: None,
409            aud: None,
410            iss: Some("https://example.com/test".to_owned()),
411        };
412        let valid_token = valid_token();
413        let mut split = valid_token.rsplitn(2, '.');
414        let _ = split.next();
415        let token = split.next().unwrap().to_string() + INV_CERT;
416        println!("{:?}", token);
417        let _result: crate::TokenData<()> = key_set.verify(&token, &validation).unwrap();
418    }
419}