1use std::convert::{TryFrom, TryInto};
2use 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 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 pub fn keys_len(&self) -> usize {
121 self.keys.len()
122 }
123
124 pub fn add_key(&mut self, key: JWKDecodingKey) {
126 self.keys.push(key);
127 }
128
129 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 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); assert_eq!(format!("{:?}", result), r#"Err(Error(ImmatureSignature))"#);
363
364 let result: Result<crate::TokenData<TestClaims>, _> =
365 key_set.verify(&expired_token(), &validation); 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}