use crate::{
crypto::{
KeyMatchStrength,
verifier::{JwsVerifier, KeyMatch, VerifyError},
},
platform::MaybeSendBoxFuture,
};
const KNOWN_JWS_ALGORITHMS: &[&str] = &[
"HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "ES256K",
"PS256", "PS384", "PS512", "EdDSA", "none",
];
fn alg_label(alg: &str) -> &'static str {
KNOWN_JWS_ALGORITHMS
.iter()
.copied()
.find(|known| *known == alg)
.unwrap_or("other")
}
#[derive(Debug)]
pub struct MetricsJwsVerifier<V> {
inner: V,
name: String,
}
#[bon::bon]
impl<V> MetricsJwsVerifier<V> {
#[builder]
pub fn new(inner: V, #[builder(into)] name: String) -> Self {
Self { inner, name }
}
}
impl<V> MetricsJwsVerifier<V> {
pub fn inner(&self) -> &V {
&self.inner
}
pub fn into_inner(self) -> V {
self.inner
}
}
impl<V: JwsVerifier> JwsVerifier for MetricsJwsVerifier<V> {
fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength> {
self.inner.key_match(key_match)
}
fn verify<'a>(
&'a self,
input: &'a [u8],
signature: &'a [u8],
key_match: &'a KeyMatch<'a>,
) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>> {
Box::pin(async move {
let alg = alg_label(key_match.alg);
let result = self.inner.verify(input, signature, key_match).await;
let outcome = match &result {
Ok(()) => "success",
Err(VerifyError::NoMatchingKey) => "no_matching_key",
Err(VerifyError::AmbiguousKeyMatch) => "ambiguous_key",
Err(VerifyError::SignatureMismatch) => "signature_mismatch",
Err(VerifyError::Other { .. }) => "error",
};
::metrics::counter!(
"huskarl.jws.verify",
"name" => self.name.clone(),
"alg" => alg,
"outcome" => outcome,
)
.increment(1);
result
})
}
fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
Box::pin(async move {
let refreshed = self.inner.try_refresh().await;
let outcome = if refreshed {
"refreshed"
} else {
"not_refreshed"
};
::metrics::counter!(
"huskarl.jws.refresh",
"name" => self.name.clone(),
"outcome" => outcome,
)
.increment(1);
refreshed
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_algorithms_pass_through() {
for alg in [
"HS256", "RS256", "ES256", "ES256K", "PS512", "EdDSA", "none",
] {
assert_eq!(alg_label(alg), alg);
}
}
#[test]
fn unknown_algorithms_bucket_to_other() {
assert_eq!(alg_label("RS999"), "other");
assert_eq!(alg_label(""), "other");
assert_eq!(alg_label("rs256"), "other");
assert_eq!(alg_label("' OR 1=1--"), "other");
}
}