use std::sync::Arc;
use bon::Builder;
use crate::{
EndpointUrl,
crypto::{
KeyMatchStrength,
verifier::error::{CreateVerifierError, VerifyError},
},
error::Error,
jwk::PublicJwk,
platform::{MaybeSendBoxFuture, MaybeSendSync},
};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Builder)]
pub struct KeyMatch<'a> {
pub alg: &'a str,
pub kid: Option<&'a str>,
}
impl KeyMatch<'_> {
#[must_use]
pub fn strength_for(
&self,
supported_algorithms: &[&str],
registered_kid: Option<&str>,
) -> Option<KeyMatchStrength> {
if !supported_algorithms.contains(&self.alg) {
return None;
}
crate::crypto::kid_match_strength(self.kid, registered_kid)
}
}
pub trait JwsVerifier: std::fmt::Debug + MaybeSendSync {
fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength>;
fn verify<'a>(
&'a self,
input: &'a [u8],
signature: &'a [u8],
key_match: &'a KeyMatch<'a>,
) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>>;
fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
Box::pin(async { false })
}
}
impl<T: JwsVerifier + ?Sized> JwsVerifier for &T {
fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength> {
(**self).key_match(key_match)
}
fn verify<'a>(
&'a self,
input: &'a [u8],
signature: &'a [u8],
key_match: &'a KeyMatch<'a>,
) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>> {
(**self).verify(input, signature, key_match)
}
fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
(**self).try_refresh()
}
}
impl<T: JwsVerifier + ?Sized> JwsVerifier for Box<T> {
fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength> {
(**self).key_match(key_match)
}
fn verify<'a>(
&'a self,
input: &'a [u8],
signature: &'a [u8],
key_match: &'a KeyMatch<'a>,
) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>> {
(**self).verify(input, signature, key_match)
}
fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
(**self).try_refresh()
}
}
impl<T: JwsVerifier + ?Sized> JwsVerifier for Arc<T> {
fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength> {
(**self).key_match(key_match)
}
fn verify<'a>(
&'a self,
input: &'a [u8],
signature: &'a [u8],
key_match: &'a KeyMatch<'a>,
) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>> {
(**self).verify(input, signature, key_match)
}
fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
(**self).try_refresh()
}
}
pub trait JwsVerifierPlatform: std::fmt::Debug + MaybeSendSync {
fn create_verifier_from_jwk(
&self,
jwk: PublicJwk,
) -> MaybeSendBoxFuture<'static, Result<Arc<dyn JwsVerifier>, CreateVerifierError>>;
fn supported_signature_algorithms(&self) -> &[&str];
}
pub trait JwsVerifierFactory: MaybeSendSync {
fn build(
&self,
jwks_uri: Option<&EndpointUrl>,
platform: Arc<dyn JwsVerifierPlatform>,
) -> MaybeSendBoxFuture<'static, Result<Arc<dyn JwsVerifier>, Error>>;
}
impl<F> JwsVerifierFactory for F
where
F: Fn(
Option<&EndpointUrl>,
Arc<dyn JwsVerifierPlatform>,
) -> MaybeSendBoxFuture<'static, Result<Arc<dyn JwsVerifier>, Error>>
+ MaybeSendSync,
{
fn build(
&self,
jwks_uri: Option<&EndpointUrl>,
platform: Arc<dyn JwsVerifierPlatform>,
) -> MaybeSendBoxFuture<'static, Result<Arc<dyn JwsVerifier>, Error>> {
self(jwks_uri, platform)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn key_match<'a>(alg: &'a str, kid: Option<&'a str>) -> KeyMatch<'a> {
KeyMatch { alg, kid }
}
#[test]
fn strength_for_unsupported_algorithm() {
let m = key_match("RS256", Some("kid-1"));
assert_eq!(m.strength_for(&["ES256"], Some("kid-1")), None);
}
#[test]
fn strength_for_matching_kid() {
let m = key_match("ES256", Some("kid-1"));
assert_eq!(
m.strength_for(&["ES256"], Some("kid-1")),
Some(KeyMatchStrength::ByKeyId)
);
}
#[test]
fn strength_for_kid_mismatch_is_none_not_by_algorithm() {
let m = key_match("ES256", Some("kid-1"));
assert_eq!(m.strength_for(&["ES256"], Some("kid-2")), None);
}
#[test]
fn strength_for_no_requested_kid() {
let m = key_match("ES256", None);
assert_eq!(
m.strength_for(&["ES256"], Some("kid-1")),
Some(KeyMatchStrength::ByAlgorithm)
);
}
#[test]
fn strength_for_no_registered_kid() {
let m = key_match("ES256", Some("kid-1"));
assert_eq!(
m.strength_for(&["ES256"], None),
Some(KeyMatchStrength::ByAlgorithm)
);
}
#[test]
fn strength_for_multiple_supported_algorithms() {
let m = key_match("PS384", None);
assert_eq!(
m.strength_for(
&["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"],
None
),
Some(KeyMatchStrength::ByAlgorithm)
);
}
}