1use crate::error::{Error, Result};
34use crate::jwt::{check_claims, now, Validation};
35use base64::engine::general_purpose::URL_SAFE_NO_PAD;
36use base64::Engine;
37use ring::rand::SystemRandom;
38use ring::signature::{
39 EcdsaKeyPair, KeyPair, RsaKeyPair, UnparsedPublicKey, ECDSA_P256_SHA256_FIXED,
40 ECDSA_P256_SHA256_FIXED_SIGNING, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256,
41};
42use serde::de::DeserializeOwned;
43use serde::Serialize;
44use serde_json::Value;
45use std::sync::Arc;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum Algorithm {
50 RS256,
52 ES256,
54}
55
56impl Algorithm {
57 fn name(self) -> &'static str {
58 match self {
59 Algorithm::RS256 => "RS256",
60 Algorithm::ES256 => "ES256",
61 }
62 }
63}
64
65fn invalid() -> Error {
66 Error::unauthorized("Invalid token")
67}
68
69fn bad_key(what: &str) -> Error {
70 Error::internal(format!("Invalid JWT key: {what}"))
71}
72
73#[derive(Debug, Clone)]
75pub struct VerifyingKey {
76 algorithm: Algorithm,
77 kid: Option<String>,
78 public: Vec<u8>,
80}
81
82impl VerifyingKey {
83 pub fn rs256_der(der: &[u8]) -> Self {
85 Self {
86 algorithm: Algorithm::RS256,
87 kid: None,
88 public: der.to_vec(),
89 }
90 }
91
92 pub fn es256_point(point: &[u8]) -> Result<Self> {
94 if point.len() != 65 || point[0] != 4 {
95 return Err(bad_key("expected an uncompressed P-256 point"));
96 }
97 Ok(Self {
98 algorithm: Algorithm::ES256,
99 kid: None,
100 public: point.to_vec(),
101 })
102 }
103
104 pub fn from_jwk(jwk: &Value) -> Result<Self> {
106 let field = |name: &str| -> Result<Vec<u8>> {
107 let v = jwk
108 .get(name)
109 .and_then(Value::as_str)
110 .ok_or_else(|| bad_key(&format!("JWK is missing `{name}`")))?;
111 URL_SAFE_NO_PAD
112 .decode(v)
113 .map_err(|_| bad_key(&format!("JWK `{name}` is not base64url")))
114 };
115 let mut key = match jwk.get("kty").and_then(Value::as_str) {
116 Some("RSA") => {
117 let (n, e) = (field("n")?, field("e")?);
118 let body = [der_integer(&n), der_integer(&e)].concat();
119 Self::rs256_der(&der_wrap(0x30, &body))
120 }
121 Some("EC") => {
122 if jwk.get("crv").and_then(Value::as_str) != Some("P-256") {
123 return Err(bad_key("only the P-256 curve is supported"));
124 }
125 let (x, y) = (field("x")?, field("y")?);
126 if x.len() != 32 || y.len() != 32 {
127 return Err(bad_key("JWK coordinates must be 32 bytes"));
128 }
129 Self::es256_point(&[&[4][..], &x, &y].concat())?
130 }
131 _ => return Err(bad_key("unsupported JWK `kty`")),
132 };
133 key.kid = jwk.get("kid").and_then(Value::as_str).map(String::from);
134 Ok(key)
135 }
136
137 pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
139 self.kid = Some(kid.into());
140 self
141 }
142
143 pub fn algorithm(&self) -> Algorithm {
144 self.algorithm
145 }
146
147 pub fn kid(&self) -> Option<&str> {
148 self.kid.as_deref()
149 }
150
151 fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
152 let alg: &dyn ring::signature::VerificationAlgorithm = match self.algorithm {
153 Algorithm::RS256 => &RSA_PKCS1_2048_8192_SHA256,
154 Algorithm::ES256 => &ECDSA_P256_SHA256_FIXED,
155 };
156 UnparsedPublicKey::new(alg, &self.public)
157 .verify(message, signature)
158 .is_ok()
159 }
160}
161
162fn der_len(len: usize) -> Vec<u8> {
164 match len {
165 0..=127 => vec![len as u8],
166 128..=255 => vec![0x81, len as u8],
167 _ => vec![0x82, (len >> 8) as u8, len as u8],
168 }
169}
170
171fn der_wrap(tag: u8, body: &[u8]) -> Vec<u8> {
172 [&[tag][..], &der_len(body.len()), body].concat()
173}
174
175fn der_integer(bytes: &[u8]) -> Vec<u8> {
177 let start = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len());
178 let mut body = bytes[start..].to_vec();
179 if body.first().is_none_or(|b| b & 0x80 != 0) {
180 body.insert(0, 0);
181 }
182 der_wrap(0x02, &body)
183}
184
185pub struct SigningKey {
187 kind: Kind,
188 kid: Option<String>,
189 rng: SystemRandom,
190}
191
192enum Kind {
193 Rsa(RsaKeyPair),
194 Ec(EcdsaKeyPair),
195}
196
197impl std::fmt::Debug for SigningKey {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 f.debug_struct("SigningKey")
200 .field("algorithm", &self.algorithm())
201 .field("kid", &self.kid)
202 .finish_non_exhaustive()
203 }
204}
205
206impl SigningKey {
207 pub fn rs256_pkcs8(der: &[u8]) -> Result<Self> {
209 let pair = RsaKeyPair::from_pkcs8(der).map_err(|_| bad_key("not an RSA PKCS#8 key"))?;
210 Ok(Self::new(Kind::Rsa(pair)))
211 }
212
213 pub fn es256_pkcs8(der: &[u8]) -> Result<Self> {
215 let pair =
216 EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, der, &SystemRandom::new())
217 .map_err(|_| bad_key("not a P-256 PKCS#8 key"))?;
218 Ok(Self::new(Kind::Ec(pair)))
219 }
220
221 fn new(kind: Kind) -> Self {
222 Self {
223 kind,
224 kid: None,
225 rng: SystemRandom::new(),
226 }
227 }
228
229 pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
231 self.kid = Some(kid.into());
232 self
233 }
234
235 pub fn algorithm(&self) -> Algorithm {
236 match self.kind {
237 Kind::Rsa(_) => Algorithm::RS256,
238 Kind::Ec(_) => Algorithm::ES256,
239 }
240 }
241
242 pub fn verifying_key(&self) -> VerifyingKey {
244 let (algorithm, public) = match &self.kind {
245 Kind::Rsa(p) => (Algorithm::RS256, p.public_key().as_ref().to_vec()),
246 Kind::Ec(p) => (Algorithm::ES256, p.public_key().as_ref().to_vec()),
247 };
248 VerifyingKey {
249 algorithm,
250 kid: self.kid.clone(),
251 public,
252 }
253 }
254
255 pub fn encode<T: Serialize>(&self, claims: &T) -> Result<String> {
257 let mut header = serde_json::json!({ "alg": self.algorithm().name(), "typ": "JWT" });
258 if let Some(kid) = &self.kid {
259 header["kid"] = Value::String(kid.clone());
260 }
261 let payload = serde_json::to_vec(claims)
262 .map_err(|e| Error::internal(format!("JWT claims: {}", e)))?;
263 let input = format!(
264 "{}.{}",
265 URL_SAFE_NO_PAD.encode(header.to_string()),
266 URL_SAFE_NO_PAD.encode(payload)
267 );
268 let signature = match &self.kind {
269 Kind::Rsa(pair) => {
270 let mut sig = vec![0u8; pair.public().modulus_len()];
271 pair.sign(&RSA_PKCS1_SHA256, &self.rng, input.as_bytes(), &mut sig)
272 .map_err(|_| Error::internal("JWT signing failed"))?;
273 sig
274 }
275 Kind::Ec(pair) => pair
276 .sign(&self.rng, input.as_bytes())
277 .map_err(|_| Error::internal("JWT signing failed"))?
278 .as_ref()
279 .to_vec(),
280 };
281 Ok(format!("{}.{}", input, URL_SAFE_NO_PAD.encode(signature)))
282 }
283}
284
285#[derive(Debug, Clone, Default)]
287pub struct KeySet {
288 keys: Arc<Vec<VerifyingKey>>,
289}
290
291impl KeySet {
292 pub fn new() -> Self {
293 Self::default()
294 }
295
296 pub fn with(mut self, key: VerifyingKey) -> Self {
298 Arc::make_mut(&mut self.keys).push(key);
299 self
300 }
301
302 pub fn from_jwks(json: &str) -> Result<Self> {
305 let doc: Value = serde_json::from_str(json).map_err(|_| bad_key("JWKS is not JSON"))?;
306 let list = doc
307 .get("keys")
308 .and_then(Value::as_array)
309 .ok_or_else(|| bad_key("JWKS has no `keys` array"))?;
310 let mut set = Self::new();
311 for jwk in list {
312 if let Ok(key) = VerifyingKey::from_jwk(jwk) {
313 set = set.with(key);
314 }
315 }
316 if set.keys.is_empty() {
317 return Err(bad_key("JWKS contains no supported keys"));
318 }
319 Ok(set)
320 }
321
322 pub fn len(&self) -> usize {
323 self.keys.len()
324 }
325
326 pub fn is_empty(&self) -> bool {
327 self.keys.is_empty()
328 }
329
330 pub fn decode<T: DeserializeOwned>(&self, token: &str, validation: &Validation) -> Result<T> {
332 let mut parts = token.split('.');
333 let (Some(h), Some(p), Some(s), None) =
334 (parts.next(), parts.next(), parts.next(), parts.next())
335 else {
336 return Err(invalid());
337 };
338 let header: Value =
339 serde_json::from_slice(&URL_SAFE_NO_PAD.decode(h).map_err(|_| invalid())?)
340 .map_err(|_| invalid())?;
341 let algorithm = match header.get("alg").and_then(Value::as_str) {
342 Some("RS256") => Algorithm::RS256,
343 Some("ES256") => Algorithm::ES256,
344 _ => return Err(invalid()),
345 };
346 let kid = header.get("kid").and_then(Value::as_str);
347 let signature = URL_SAFE_NO_PAD.decode(s).map_err(|_| invalid())?;
348 let message = format!("{h}.{p}");
349
350 let verified = self
351 .keys
352 .iter()
353 .filter(|k| k.algorithm == algorithm)
354 .filter(|k| match (kid, k.kid.as_deref()) {
356 (Some(t), Some(k)) => t == k,
357 _ => true,
358 })
359 .any(|k| k.verify(message.as_bytes(), &signature));
360 if !verified {
361 return Err(invalid());
362 }
363
364 let claims: Value =
365 serde_json::from_slice(&URL_SAFE_NO_PAD.decode(p).map_err(|_| invalid())?)
366 .map_err(|_| invalid())?;
367 check_claims(&claims, validation, now())?;
368 serde_json::from_value(claims).map_err(|_| invalid())
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use serde_json::json;
376
377 const RSA_PKCS8: &[u8] = include_bytes!("../tests/fixtures/rsa_pkcs8.der");
378 const RSA_PUBLIC: &[u8] = include_bytes!("../tests/fixtures/rsa_public.der");
379
380 fn ec() -> SigningKey {
381 let pkcs8 =
382 EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &SystemRandom::new())
383 .unwrap();
384 SigningKey::es256_pkcs8(pkcs8.as_ref()).unwrap()
385 }
386
387 fn rsa() -> SigningKey {
388 SigningKey::rs256_pkcs8(RSA_PKCS8).unwrap()
389 }
390
391 fn claims() -> Value {
392 json!({ "sub": "ann", "exp": now() + 60 })
393 }
394
395 fn v() -> Validation {
396 Validation::default()
397 }
398
399 #[test]
400 fn es256_and_rs256_roundtrip() {
401 for key in [ec(), rsa()] {
402 let token = key.encode(&claims()).unwrap();
403 let set = KeySet::new().with(key.verifying_key());
404 let got: Value = set.decode(&token, &v()).unwrap();
405 assert_eq!(got["sub"], "ann", "{:?}", key.algorithm());
406 }
407 }
408
409 #[test]
410 fn rsa_public_der_fixture_matches_the_private_key() {
411 assert_eq!(rsa().verifying_key().public, RSA_PUBLIC);
412 }
413
414 #[test]
415 fn rejects_tampering_wrong_key_and_malformed() {
416 let key = ec();
417 let token = key.encode(&claims()).unwrap();
418 let set = KeySet::new().with(key.verifying_key());
419
420 let other = KeySet::new().with(ec().verifying_key());
421 assert!(other.decode::<Value>(&token, &v()).is_err());
422
423 let mut parts: Vec<String> = token.split('.').map(String::from).collect();
424 parts[1] = URL_SAFE_NO_PAD.encode(r#"{"sub":"root"}"#);
425 assert!(set.decode::<Value>(&parts.join("."), &v()).is_err());
426
427 for bad in ["", "a", "a.b", "a.b.c", "a.b.c.d", "!!!.!!!.!!!"] {
428 assert_eq!(
429 set.decode::<Value>(bad, &v()).unwrap_err().status_code(),
430 401,
431 "{bad}"
432 );
433 }
434 }
435
436 #[test]
437 fn algorithm_confusion_is_rejected() {
438 let (e, r) = (ec(), rsa());
440 let rs_token = r.encode(&claims()).unwrap();
441 let es_token = e.encode(&claims()).unwrap();
442 let only_ec = KeySet::new().with(e.verifying_key());
443 let only_rsa = KeySet::new().with(r.verifying_key());
444 assert!(only_ec.decode::<Value>(&rs_token, &v()).is_err());
445 assert!(only_rsa.decode::<Value>(&es_token, &v()).is_err());
446
447 let payload = URL_SAFE_NO_PAD.encode(r#"{"sub":"root"}"#);
449 for alg in ["none", "HS256"] {
450 let header = URL_SAFE_NO_PAD.encode(format!(r#"{{"alg":"{alg}"}}"#));
451 let token = format!("{header}.{payload}.");
452 assert!(only_ec.decode::<Value>(&token, &v()).is_err(), "{alg}");
453 }
454 }
455
456 #[test]
457 fn expired_tokens_and_claim_checks_apply() {
458 let key = ec();
459 let set = KeySet::new().with(key.verifying_key());
460 let old = key.encode(&json!({ "exp": now() - 10 })).unwrap();
461 assert_eq!(
462 set.decode::<Value>(&old, &v()).unwrap_err().message(),
463 "Token expired"
464 );
465 let strict = Validation {
466 issuer: Some("me".into()),
467 ..Default::default()
468 };
469 let token = key.encode(&json!({ "iss": "you" })).unwrap();
470 assert!(set.decode::<Value>(&token, &strict).is_err());
471 let token = key.encode(&json!({ "iss": "me" })).unwrap();
472 assert!(set.decode::<Value>(&token, &strict).is_ok());
473 }
474
475 #[test]
476 fn kid_selects_the_key() {
477 let (a, b) = (ec().with_kid("a"), ec().with_kid("b"));
478 let set = KeySet::new()
479 .with(a.verifying_key())
480 .with(b.verifying_key());
481 assert!(set
482 .decode::<Value>(&a.encode(&claims()).unwrap(), &v())
483 .is_ok());
484 assert!(set
485 .decode::<Value>(&b.encode(&claims()).unwrap(), &v())
486 .is_ok());
487
488 let forged = ec().with_kid("a").encode(&claims()).unwrap();
490 assert!(set.decode::<Value>(&forged, &v()).is_err());
491 assert_eq!(a.verifying_key().kid(), Some("a"));
492 }
493
494 #[test]
495 fn jwks_with_rsa_and_ec_keys() {
496 let rsa_key = rsa();
498 let ec_key = ec().with_kid("ec1");
499 let pub_ec = ec_key.verifying_key().public;
500 let (n, e) = rsa_components(RSA_PUBLIC);
501 let jwks = json!({ "keys": [
502 { "kty": "oct", "k": "AAAA" },
503 { "kty": "EC", "crv": "P-384", "x": "AA", "y": "AA" },
504 { "kty": "RSA", "kid": "rsa1", "n": URL_SAFE_NO_PAD.encode(&n), "e": URL_SAFE_NO_PAD.encode(&e) },
505 { "kty": "EC", "crv": "P-256", "kid": "ec1",
506 "x": URL_SAFE_NO_PAD.encode(&pub_ec[1..33]),
507 "y": URL_SAFE_NO_PAD.encode(&pub_ec[33..]) },
508 ]});
509 let set = KeySet::from_jwks(&jwks.to_string()).unwrap();
510 assert_eq!(set.len(), 2);
511 assert!(!set.is_empty());
512
513 let rs_token = rsa_key.with_kid("rsa1").encode(&claims()).unwrap();
514 assert!(set.decode::<Value>(&rs_token, &v()).is_ok());
515 assert!(set
516 .decode::<Value>(&ec_key.encode(&claims()).unwrap(), &v())
517 .is_ok());
518 }
519
520 #[test]
521 fn bad_jwks_and_keys_are_refused() {
522 assert!(KeySet::from_jwks("not json").is_err());
523 assert!(KeySet::from_jwks("{}").is_err());
524 assert!(KeySet::from_jwks(r#"{"keys":[]}"#).is_err());
525 assert!(KeySet::from_jwks(r#"{"keys":[{"kty":"oct","k":"AA"}]}"#).is_err());
526 assert!(VerifyingKey::from_jwk(&json!({ "kty": "RSA", "n": "AA" })).is_err());
527 assert!(VerifyingKey::from_jwk(
528 &json!({ "kty": "EC", "crv": "P-256", "x": "AA", "y": "AA" })
529 )
530 .is_err());
531 assert!(VerifyingKey::es256_point(&[4; 10]).is_err());
532 assert!(SigningKey::rs256_pkcs8(b"junk").is_err());
533 assert!(SigningKey::es256_pkcs8(b"junk").is_err());
534 assert!(KeySet::new().is_empty());
535 }
536
537 #[test]
538 fn der_integers_are_minimal_and_positive() {
539 assert_eq!(der_integer(&[0, 0, 1]), [2, 1, 1]);
540 assert_eq!(der_integer(&[0x80]), [2, 2, 0, 0x80]);
541 assert_eq!(der_integer(&[]), [2, 1, 0]);
542 assert_eq!(der_len(300), [0x82, 1, 44]);
543 assert_eq!(der_len(200), [0x81, 200]);
544 }
545
546 fn rsa_components(der: &[u8]) -> (Vec<u8>, Vec<u8>) {
548 fn read(buf: &[u8]) -> (u8, &[u8], &[u8]) {
549 let (tag, rest) = (buf[0], &buf[1..]);
550 let (len, rest) = match rest[0] {
551 n if n < 0x80 => (n as usize, &rest[1..]),
552 0x81 => (rest[1] as usize, &rest[2..]),
553 _ => (((rest[1] as usize) << 8) | rest[2] as usize, &rest[3..]),
554 };
555 (tag, &rest[..len], &rest[len..])
556 }
557 let (_, body, _) = read(der);
558 let (_, n, rest) = read(body);
559 let (_, e, _) = read(rest);
560 let trim = |b: &[u8]| {
561 b.iter()
562 .skip_while(|&&x| x == 0)
563 .copied()
564 .collect::<Vec<_>>()
565 };
566 (trim(n), trim(e))
567 }
568
569 #[test]
570 fn der_encoding_roundtrips_the_rsa_fixture() {
571 let (n, e) = rsa_components(RSA_PUBLIC);
572 let jwk =
573 json!({ "kty": "RSA", "n": URL_SAFE_NO_PAD.encode(n), "e": URL_SAFE_NO_PAD.encode(e) });
574 assert_eq!(VerifyingKey::from_jwk(&jwk).unwrap().public, RSA_PUBLIC);
575 }
576}