use core::time::Duration;
use base64ct::{Base64UrlUnpadded, Encoding};
use ts_crypto::{
EdwardsVerifyingKey, EllipticVerifyingKey, RsaVerifyingKey, Sha256, Sha384, Sha512,
VerifyingKey,
rsa::{Pkcs1v15, Pss},
};
use crate::{
JsonWebKey, JsonWebToken,
jwt::{Claims, Header},
};
pub struct TokenVerifier {
pub jwk: JsonWebKey,
pub key: VerifyingKey,
}
impl TokenVerifier {
pub fn new(jwk: JsonWebKey) -> Option<Self> {
let key = match &jwk.kty {
crate::KeyType::Ec { x, y } => {
let x = Base64UrlUnpadded::decode_vec(x).ok()?;
let y = Base64UrlUnpadded::decode_vec(y).ok()?;
let key = EllipticVerifyingKey::from_coordinates(&x, &y)?;
VerifyingKey::Elliptic(key)
}
crate::KeyType::Rsa { n, e } => {
let n = Base64UrlUnpadded::decode_vec(n).ok()?;
let e = Base64UrlUnpadded::decode_vec(e).ok()?;
let key = RsaVerifyingKey::from_parameters(&n, &e)?;
VerifyingKey::Rsa(key)
}
crate::KeyType::Okp { x } => {
let raw_key = Base64UrlUnpadded::decode_vec(x).ok()?;
let key = EdwardsVerifyingKey::from_raw_key(&raw_key)?;
VerifyingKey::Edwards(key)
}
};
Some(Self { jwk, key })
}
pub fn verify(&self, jws: &str) -> Option<JsonWebToken> {
let mut parts = jws.split('.');
let header_str = parts.next()?;
let claims_str = parts.next()?;
let signature = parts.next()?;
let header = Base64UrlUnpadded::decode_vec(header_str).ok()?;
let header: Header = serde_json::from_slice(&header).ok()?;
let claims = Base64UrlUnpadded::decode_vec(claims_str).ok()?;
let claims: Claims = serde_json::from_slice(&claims).ok()?;
let signature = Base64UrlUnpadded::decode_vec(signature).ok()?;
if header.kid != self.jwk.kid {
return None;
}
let message = [header_str, claims_str].join(".");
match self.jwk.alg.as_str() {
"RS256" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pkcs1v15, Sha256>(&signature, message.as_bytes()) {
return None;
}
}
"RS384" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pkcs1v15, Sha384>(&signature, message.as_bytes()) {
return None;
}
}
"RS512" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pkcs1v15, Sha512>(&signature, message.as_bytes()) {
return None;
}
}
"PS256" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pss, Sha256>(&signature, message.as_bytes()) {
return None;
}
}
"PS384" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pss, Sha384>(&signature, message.as_bytes()) {
return None;
}
}
"PS512" => {
let VerifyingKey::Rsa(key) = &self.key else {
return None;
};
if !key.verifies::<Pss, Sha512>(&signature, message.as_bytes()) {
return None;
}
}
"ES256" => {
let VerifyingKey::Elliptic(key) = &self.key else {
return None;
};
if !key.verifies::<Sha256>(&signature, message.as_bytes()) {
return None;
}
}
"ES384" => {
let VerifyingKey::Elliptic(key) = &self.key else {
return None;
};
if !key.verifies::<Sha384>(&signature, message.as_bytes()) {
return None;
}
}
"ES512" => {
let VerifyingKey::Elliptic(key) = &self.key else {
return None;
};
if !key.verifies::<Sha512>(&signature, message.as_bytes()) {
return None;
}
}
"Ed25519" | "Ed448" | "EdDSA" => {
let VerifyingKey::Edwards(key) = &self.key else {
return None;
};
if !key.verifies(&signature, message.as_bytes()) {
return None;
}
}
_ => return None,
}
let exp = Duration::from_secs(claims.exp);
let iat = Duration::from_secs(claims.iat);
let Ok(now) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) else {
return None;
};
if exp < now || iat > now {
return None;
}
Some(JsonWebToken { header, claims })
}
}