use {
crate::{
Digest,
PeerId,
UniqueId,
discovery::PeerEntry,
id,
tickets::{Expiration, InvalidTicket, Ticket, TicketValidator},
},
chrono::Utc,
core::time::Duration,
itertools::Itertools,
jwt::{
FromBase64,
RegisteredClaims,
SigningAlgorithm,
ToBase64,
VerifyingAlgorithm,
},
serde_json::Value,
std::{collections::BTreeMap, sync::Arc},
};
#[derive(Clone)]
pub struct Jwt {
issuers: Vec<String>,
allow_non_expiring: bool,
subject: Option<String>,
audience: Vec<String>,
custom_claims: BTreeMap<String, Value>,
max_lifetime: Option<Duration>,
max_age: Option<Duration>,
keys: Vec<VerifyingKey>,
}
impl Jwt {
pub const CLASS: UniqueId = id!("mosaik.tickets.jwt.v1");
#[must_use]
pub fn with_key(key: impl Into<VerifyingKey>) -> Self {
Self {
keys: vec![key.into()],
issuers: Vec::new(),
allow_non_expiring: false,
subject: None,
audience: Vec::new(),
custom_claims: BTreeMap::new(),
max_lifetime: None,
max_age: None,
}
}
#[must_use]
pub fn add_key(mut self, key: impl Into<VerifyingKey>) -> Self {
self.keys.push(key.into());
self
}
#[must_use]
pub fn require_subject(mut self, subject: impl Into<String>) -> Self {
self.subject = Some(subject.into());
self
}
#[must_use]
pub fn require_claim(
mut self,
name: impl Into<String>,
value: impl Into<Value>,
) -> Self {
self.custom_claims.insert(name.into(), value.into());
self
}
#[must_use]
pub const fn allow_non_expiring(mut self) -> Self {
self.allow_non_expiring = true;
self
}
#[must_use]
pub fn allow_issuer(mut self, issuer: impl Into<String>) -> Self {
self.issuers.push(issuer.into());
self
}
#[must_use]
pub fn allow_audience(mut self, audience: impl Into<String>) -> Self {
self.audience.push(audience.into());
self
}
#[must_use]
pub const fn max_lifetime(mut self, duration: Duration) -> Self {
self.max_lifetime = Some(duration);
self
}
#[must_use]
pub const fn max_age(mut self, duration: Duration) -> Self {
self.max_age = Some(duration);
self
}
}
impl TicketValidator for Jwt {
fn class(&self) -> UniqueId {
Self::CLASS
}
fn signature(&self) -> UniqueId {
let mut sig = self.class();
for key in &self.keys {
sig = sig
.derive(key.algorithm_name.as_bytes())
.derive(key.fingerprint);
}
sig = sig
.derive(if self.allow_non_expiring { [1] } else { [0] })
.derive(self.issuers.iter().join(","))
.derive(self.audience.iter().join(","))
.derive(self.subject.as_deref().unwrap_or(""))
.derive(self.max_lifetime.map_or(0, |d| d.as_secs()).to_le_bytes())
.derive(self.max_age.map_or(0, |d| d.as_secs()).to_le_bytes());
for (key, value) in &self.custom_claims {
sig = sig.derive(key).derive(value.to_string());
}
sig
}
#[allow(clippy::too_many_lines)]
fn validate(
&self,
ticket: &[u8],
peer: &PeerEntry,
) -> Result<Expiration, InvalidTicket> {
let expected_subject = self
.subject
.clone()
.unwrap_or_else(|| peer.id().to_string().to_lowercase());
let jwt_str = core::str::from_utf8(ticket).map_err(|_| InvalidTicket)?;
let mut parts = jwt_str.splitn(3, '.');
let header_str = parts.next().ok_or(InvalidTicket)?;
let claims_str = parts.next().ok_or(InvalidTicket)?;
let sig_str = parts.next().ok_or(InvalidTicket)?;
let header_bytes =
base64::decode_config(header_str, base64::URL_SAFE_NO_PAD)
.map_err(|_| InvalidTicket)?;
let header_json: Value =
serde_json::from_slice(&header_bytes).map_err(|_| InvalidTicket)?;
let alg = header_json
.get("alg")
.and_then(Value::as_str)
.ok_or(InvalidTicket)?;
if !self.keys.iter().any(|k| k.algorithm_name == alg) {
return Err(InvalidTicket);
}
let signature_verified = self
.keys
.iter()
.filter(|k| k.algorithm_name == alg)
.any(|k| {
k.algo
.verify(header_str, claims_str, sig_str)
.unwrap_or(false)
});
if !signature_verified {
return Err(InvalidTicket);
}
let claims: jwt::Claims =
FromBase64::from_base64(claims_str).map_err(|_| InvalidTicket)?;
let reg = &claims.registered;
let now = Utc::now().timestamp().cast_unsigned();
if !self.issuers.is_empty() {
match reg.issuer.as_deref() {
Some(iss) if self.issuers.iter().any(|i| i == iss) => {}
_ => return Err(InvalidTicket),
}
}
if reg.subject.as_deref() != Some(expected_subject.as_str()) {
return Err(InvalidTicket);
}
if let Some(nbf) = reg.not_before
&& nbf > now
{
return Err(InvalidTicket);
}
let exp = reg.expiration;
match exp {
None if !self.allow_non_expiring => {
return Err(InvalidTicket);
}
Some(e) if e <= now => return Err(InvalidTicket),
_ => {}
}
if let Some(max_lifetime) = self.max_lifetime
&& let Some(e) = exp
{
let remaining = e.saturating_sub(now);
if remaining > max_lifetime.as_secs() {
return Err(InvalidTicket);
}
}
if let Some(max_age) = self.max_age {
match reg.issued_at {
Some(iat) => {
let age = now.saturating_sub(iat);
if age > max_age.as_secs() {
return Err(InvalidTicket);
}
}
None => return Err(InvalidTicket),
}
}
if !self.audience.is_empty() {
match reg.audience.as_deref() {
Some(aud) if self.audience.iter().any(|a| a == aud) => {}
_ => return Err(InvalidTicket),
}
}
for (key, expected_value) in &self.custom_claims {
if claims.private.get(key) != Some(expected_value) {
return Err(InvalidTicket);
}
}
Ok(match exp {
None => Expiration::Never,
Some(ts) => {
let ts_i64 = i64::try_from(ts).map_err(|_| InvalidTicket)?;
let dt =
chrono::DateTime::from_timestamp(ts_i64, 0).ok_or(InvalidTicket)?;
Expiration::At(dt)
}
})
}
}
#[derive(Clone)]
pub struct SigningKey {
key: Arc<dyn SigningAlgorithm + Send + Sync>,
algorithm_name: &'static str,
}
impl SigningKey {
pub fn custom(
key: impl SigningAlgorithm + Send + Sync + 'static,
algorithm_name: &'static str,
) -> Self {
Self {
key: Arc::new(key),
algorithm_name,
}
}
}
pub use chrono::Duration as ChronoDuration;
#[derive(Clone)]
pub struct JwtTicketBuilder {
key: SigningKey,
issuer: Option<String>,
subject: Option<String>,
audience: Option<String>,
expiration: Expiration,
not_before: Option<u64>,
issued_at: Option<u64>,
token_id: Option<String>,
custom_claims: BTreeMap<String, Value>,
}
impl JwtTicketBuilder {
pub fn new(key: impl Into<SigningKey>) -> Self {
Self {
key: key.into(),
issuer: None,
subject: None,
audience: None,
expiration: Expiration::Never,
not_before: None,
issued_at: None,
token_id: None,
custom_claims: BTreeMap::new(),
}
}
#[must_use]
pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
self.issuer = Some(issuer.into());
self
}
#[must_use]
pub fn subject(mut self, subject: impl Into<String>) -> Self {
self.subject = Some(subject.into());
self
}
#[must_use]
pub fn audience(mut self, audience: impl Into<String>) -> Self {
self.audience = Some(audience.into());
self
}
#[must_use]
pub const fn expires_at(mut self, expiration: Expiration) -> Self {
self.expiration = expiration;
self
}
#[must_use]
pub fn expires_in(mut self, duration: impl Into<chrono::Duration>) -> Self {
self.expiration = Expiration::after(duration.into());
self
}
#[must_use]
pub const fn issued_at(mut self, ts: chrono::DateTime<Utc>) -> Self {
self.issued_at = Some(ts.timestamp().cast_unsigned());
self
}
#[must_use]
pub const fn not_before(mut self, ts: chrono::DateTime<Utc>) -> Self {
self.not_before = Some(ts.timestamp().cast_unsigned());
self
}
#[must_use]
pub fn token_id(mut self, jti: impl Into<String>) -> Self {
self.token_id = Some(jti.into());
self
}
#[must_use]
pub fn claim(
mut self,
name: impl Into<String>,
value: impl Into<Value>,
) -> Self {
let name = name.into();
let value = value.into();
match name.as_str() {
"iss" => self.issuer = value.as_str().map(String::from),
"sub" => self.subject = value.as_str().map(String::from),
"aud" => self.audience = value.as_str().map(String::from),
"exp" => match value
.as_u64()
.and_then(|ts| chrono::DateTime::from_timestamp(ts.cast_signed(), 0))
{
Some(dt) => self.expiration = Expiration::At(dt),
None => {
self.custom_claims.insert(name, value);
}
},
"nbf" => self.not_before = value.as_u64(),
"iat" => self.issued_at = value.as_u64(),
"jti" => {
self.token_id = value.as_str().map(String::from);
}
_ => {
self.custom_claims.insert(name, value);
}
}
self
}
pub fn build(&self, peer_id: &PeerId) -> Ticket {
let now = Utc::now().timestamp().cast_unsigned();
let claims = jwt::Claims {
registered: RegisteredClaims {
issuer: self.issuer.clone(),
subject: Some(
self
.subject
.clone()
.unwrap_or_else(|| peer_id.to_string().to_lowercase()),
),
audience: self.audience.clone(),
expiration: match self.expiration {
Expiration::Never => None,
Expiration::At(ts) => Some(ts.timestamp().cast_unsigned()),
},
not_before: self.not_before,
issued_at: Some(self.issued_at.unwrap_or(now)),
json_web_token_id: self.token_id.clone(),
},
private: self.custom_claims.clone(),
};
let header_json =
format!(r#"{{"alg":"{}","typ":"JWT"}}"#, self.key.algorithm_name);
let header_b64 =
base64::encode_config(header_json.as_bytes(), base64::URL_SAFE_NO_PAD);
let claims_b64 = claims.to_base64().expect("claims are serializable");
let sig = self
.key
.key
.sign(&header_b64, &claims_b64)
.expect("signing succeeds");
Ticket::new(
Jwt::CLASS,
format!("{header_b64}.{claims_b64}.{sig}").into(),
)
}
}
pub use alg::{
EdDsa,
EdDsaSigningKey,
Es256,
Es256SigningKey,
Es384,
Es384SigningKey,
Es512,
Es512SigningKey,
Hs256,
Hs384,
Hs512,
};
#[derive(Clone)]
pub struct VerifyingKey {
algo: Arc<dyn VerifyingAlgorithm + Send + Sync + 'static>,
fingerprint: Digest,
algorithm_name: &'static str,
}
impl VerifyingKey {
pub fn custom(
algo: impl VerifyingAlgorithm + Send + Sync + 'static,
key_material: impl AsRef<[u8]>,
algorithm_name: &'static str,
) -> Self {
Self {
fingerprint: Digest::from(key_material.as_ref()),
algo: Arc::new(algo),
algorithm_name,
}
}
}
mod alg {
use {
super::{SigningKey, VerifyingKey},
crate::{id, primitives::const_hex},
hmac::{Hmac, digest::KeyInit},
jwt::{SigningAlgorithm, VerifyingAlgorithm},
std::sync::Arc,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Hs256([u8; 32]);
impl Hs256 {
pub const fn new(secret: [u8; 32]) -> Self {
Self(secret)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<32>(input))
}
}
impl SigningAlgorithm for Hs256 {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Hs256
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
Hmac::<sha2::Sha256>::new_from_slice(&self.0)
.expect("HMAC accepts any key length")
.sign(header, claims)
}
}
impl From<Hs256> for VerifyingKey {
fn from(key: Hs256) -> Self {
Self {
fingerprint: id!("hs256").derive(key.0),
algo: Arc::new(
Hmac::<sha2::Sha256>::new_from_slice(&key.0)
.expect("HMAC accepts any key length"),
),
algorithm_name: "HS256",
}
}
}
impl From<Hs256> for SigningKey {
fn from(key: Hs256) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "HS256",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Hs384([u8; 48]);
impl Hs384 {
pub const fn new(secret: [u8; 48]) -> Self {
Self(secret)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<48>(input))
}
}
impl SigningAlgorithm for Hs384 {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Hs384
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
Hmac::<sha2::Sha384>::new_from_slice(&self.0)
.expect("HMAC accepts any key length")
.sign(header, claims)
}
}
impl From<Hs384> for VerifyingKey {
fn from(key: Hs384) -> Self {
Self {
fingerprint: id!("hs384").derive(key.0),
algo: Arc::new(
Hmac::<sha2::Sha384>::new_from_slice(&key.0)
.expect("HMAC accepts any key length"),
),
algorithm_name: "HS384",
}
}
}
impl From<Hs384> for SigningKey {
fn from(key: Hs384) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "HS384",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Hs512([u8; 64]);
impl Hs512 {
pub const fn new(secret: [u8; 64]) -> Self {
Self(secret)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<64>(input))
}
}
impl SigningAlgorithm for Hs512 {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Hs512
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
Hmac::<sha2::Sha512>::new_from_slice(&self.0)
.expect("HMAC accepts any key length")
.sign(header, claims)
}
}
impl From<Hs512> for VerifyingKey {
fn from(key: Hs512) -> Self {
Self {
fingerprint: id!("hs512").derive(key.0),
algo: Arc::new(
Hmac::<sha2::Sha512>::new_from_slice(&key.0)
.expect("HMAC accepts any key length"),
),
algorithm_name: "HS512",
}
}
}
impl From<Hs512> for SigningKey {
fn from(key: Hs512) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "HS512",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Es256([u8; 33]);
impl core::fmt::Debug for Es256 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Es256").field(&hex::encode(self.0)).finish()
}
}
impl Es256 {
pub const fn new(key: [u8; 33]) -> Self {
Self(key)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<33>(input))
}
}
impl From<Es256> for VerifyingKey {
fn from(key: Es256) -> Self {
Self {
fingerprint: id!("es256").derive(key.0),
algo: Arc::new(Es256Verifier(
p256::ecdsa::VerifyingKey::from_sec1_bytes(&key.0)
.expect("valid compressed P-256 public key"),
)),
algorithm_name: "ES256",
}
}
}
#[derive(Clone)]
pub struct Es256SigningKey(p256::ecdsa::SigningKey);
impl Es256SigningKey {
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(
p256::ecdsa::SigningKey::from_slice(&bytes)
.expect("valid P-256 private key"),
)
}
pub fn random() -> Self {
Self(p256::ecdsa::SigningKey::random(
&mut p256::elliptic_curve::rand_core::OsRng,
))
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn verifying_key(&self) -> Es256 {
let point = self.0.verifying_key().to_encoded_point(true);
Es256(
point
.as_bytes()
.try_into()
.expect("compressed P-256 key is 33 bytes"),
)
}
}
impl SigningAlgorithm for Es256SigningKey {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es256
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
use p256::ecdsa::signature::Signer;
let message = format!("{header}.{claims}");
let sig: p256::ecdsa::Signature = self.0.sign(message.as_bytes());
Ok(base64::encode_config(
sig.to_bytes(),
base64::URL_SAFE_NO_PAD,
))
}
}
impl From<Es256SigningKey> for SigningKey {
fn from(key: Es256SigningKey) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "ES256",
}
}
}
pub struct Es384([u8; 49]);
impl core::fmt::Debug for Es384 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Es384").field(&hex::encode(self.0)).finish()
}
}
impl Es384 {
pub const fn new(key: [u8; 49]) -> Self {
Self(key)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<49>(input))
}
}
impl From<Es384> for VerifyingKey {
fn from(key: Es384) -> Self {
Self {
fingerprint: id!("es384").derive(key.0),
algo: Arc::new(Es384Verifier(
p384::ecdsa::VerifyingKey::from_sec1_bytes(&key.0)
.expect("valid compressed P-384 public key"),
)),
algorithm_name: "ES384",
}
}
}
#[derive(Clone)]
pub struct Es384SigningKey(p384::ecdsa::SigningKey);
impl Es384SigningKey {
pub fn from_bytes(bytes: [u8; 48]) -> Self {
Self(
p384::ecdsa::SigningKey::from_slice(&bytes)
.expect("valid P-384 private key"),
)
}
pub fn random() -> Self {
Self(p384::ecdsa::SigningKey::random(
&mut p384::elliptic_curve::rand_core::OsRng,
))
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn verifying_key(&self) -> Es384 {
let point = self.0.verifying_key().to_encoded_point(true);
Es384(
point
.as_bytes()
.try_into()
.expect("compressed P-384 key is 49 bytes"),
)
}
}
impl SigningAlgorithm for Es384SigningKey {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es384
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
use p384::ecdsa::signature::Signer;
let message = format!("{header}.{claims}");
let sig: p384::ecdsa::Signature = self.0.sign(message.as_bytes());
Ok(base64::encode_config(
sig.to_bytes(),
base64::URL_SAFE_NO_PAD,
))
}
}
impl From<Es384SigningKey> for SigningKey {
fn from(key: Es384SigningKey) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "ES384",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Es512([u8; 67]);
impl core::fmt::Debug for Es512 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Es512").field(&hex::encode(self.0)).finish()
}
}
impl Es512 {
pub const fn new(key: [u8; 67]) -> Self {
Self(key)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<67>(input))
}
}
impl From<Es512> for VerifyingKey {
fn from(key: Es512) -> Self {
Self {
fingerprint: id!("es512").derive(key.0),
algo: Arc::new(Es512Verifier(
p521::ecdsa::VerifyingKey::from_sec1_bytes(&key.0)
.expect("valid compressed P-521 public key"),
)),
algorithm_name: "ES512",
}
}
}
#[derive(Clone)]
pub struct Es512SigningKey(p521::ecdsa::SigningKey);
impl Es512SigningKey {
pub fn from_bytes(bytes: [u8; 66]) -> Self {
Self(
p521::ecdsa::SigningKey::from_slice(&bytes)
.expect("valid P-521 private key"),
)
}
pub fn random() -> Self {
Self(p521::ecdsa::SigningKey::random(
&mut p521::elliptic_curve::rand_core::OsRng,
))
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn verifying_key(&self) -> Es512 {
let vk = p521::ecdsa::VerifyingKey::from(&self.0);
let point = vk.to_encoded_point(true);
Es512(
point
.as_bytes()
.try_into()
.expect("compressed P-521 key is 67 bytes"),
)
}
}
impl SigningAlgorithm for Es512SigningKey {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es512
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
use p521::ecdsa::signature::Signer;
let message = format!("{header}.{claims}");
let sig: p521::ecdsa::Signature = self.0.sign(message.as_bytes());
Ok(base64::encode_config(
sig.to_bytes(),
base64::URL_SAFE_NO_PAD,
))
}
}
impl From<Es512SigningKey> for SigningKey {
fn from(key: Es512SigningKey) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "ES512",
}
}
}
pub struct EdDsa([u8; 32]);
impl EdDsa {
pub const fn new(key: [u8; 32]) -> Self {
Self(key)
}
pub const fn hex(input: &str) -> Self {
Self(const_hex::<32>(input))
}
}
impl From<EdDsa> for VerifyingKey {
fn from(key: EdDsa) -> Self {
Self {
fingerprint: id!("EdDsa").derive(key.0),
algo: Arc::new(EdDsaVerifier(
ed25519_dalek::VerifyingKey::from_bytes(&key.0)
.expect("valid Ed25519 public key"),
)),
algorithm_name: "EdDSA",
}
}
}
pub struct EdDsaSigningKey(ed25519_dalek::SigningKey);
impl EdDsaSigningKey {
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(ed25519_dalek::SigningKey::from_bytes(&bytes))
}
pub fn random() -> Self {
Self(ed25519_dalek::SigningKey::from_bytes(&rand::random()))
}
#[must_use]
pub fn verifying_key(&self) -> EdDsa {
EdDsa(self.0.verifying_key().to_bytes())
}
}
impl SigningAlgorithm for EdDsaSigningKey {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::None
}
fn sign(
&self,
header: &str,
claims: &str,
) -> Result<String, jwt::error::Error> {
use ed25519_dalek::Signer;
let message = format!("{header}.{claims}");
let sig = self.0.sign(message.as_bytes());
Ok(base64::encode_config(
sig.to_bytes(),
base64::URL_SAFE_NO_PAD,
))
}
}
impl From<EdDsaSigningKey> for SigningKey {
fn from(key: EdDsaSigningKey) -> Self {
Self {
key: Arc::new(key),
algorithm_name: "EdDSA",
}
}
}
struct Es256Verifier(p256::ecdsa::VerifyingKey);
impl VerifyingAlgorithm for Es256Verifier {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es256
}
fn verify_bytes(
&self,
header: &str,
claims: &str,
signature: &[u8],
) -> Result<bool, jwt::error::Error> {
use p256::ecdsa::signature::Verifier;
let Ok(sig) = p256::ecdsa::Signature::from_slice(signature) else {
return Ok(false);
};
let message = format!("{header}.{claims}");
Ok(self.0.verify(message.as_bytes(), &sig).is_ok())
}
}
struct Es384Verifier(p384::ecdsa::VerifyingKey);
impl VerifyingAlgorithm for Es384Verifier {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es384
}
fn verify_bytes(
&self,
header: &str,
claims: &str,
signature: &[u8],
) -> Result<bool, jwt::error::Error> {
use p384::ecdsa::signature::Verifier;
let Ok(sig) = p384::ecdsa::Signature::from_slice(signature) else {
return Ok(false);
};
let message = format!("{header}.{claims}");
Ok(self.0.verify(message.as_bytes(), &sig).is_ok())
}
}
struct Es512Verifier(p521::ecdsa::VerifyingKey);
impl VerifyingAlgorithm for Es512Verifier {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::Es512
}
fn verify_bytes(
&self,
header: &str,
claims: &str,
signature: &[u8],
) -> Result<bool, jwt::error::Error> {
use p521::ecdsa::signature::Verifier;
let Ok(sig) = p521::ecdsa::Signature::from_slice(signature) else {
return Ok(false);
};
let message = format!("{header}.{claims}");
Ok(self.0.verify(message.as_bytes(), &sig).is_ok())
}
}
struct EdDsaVerifier(ed25519_dalek::VerifyingKey);
impl VerifyingAlgorithm for EdDsaVerifier {
fn algorithm_type(&self) -> jwt::AlgorithmType {
jwt::AlgorithmType::None
}
fn verify_bytes(
&self,
header: &str,
claims: &str,
signature: &[u8],
) -> Result<bool, jwt::error::Error> {
use ed25519_dalek::Verifier;
let Ok(sig) = ed25519_dalek::Signature::from_slice(signature) else {
return Ok(false);
};
let message = format!("{header}.{claims}");
Ok(self.0.verify(message.as_bytes(), &sig).is_ok())
}
}
}