use ::signature::SignatureEncoding;
use digest::Digest;
#[cfg(feature = "rand")]
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
#[cfg(any(feature = "p256", feature = "hmac", feature = "rsa"))]
pub use sha2::Sha256;
#[cfg(any(feature = "p384", feature = "hmac", feature = "rsa"))]
pub use sha2::Sha384;
#[cfg(any(feature = "hmac", feature = "rsa"))]
pub use sha2::Sha512;
use crate::key::SerializePublicJWK;
mod sig;
pub use sig::SignatureBytes;
#[cfg(feature = "ecdsa")]
pub mod ecdsa;
#[cfg(feature = "hmac")]
pub mod hmac;
#[cfg(feature = "rsa")]
pub mod rsa;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[non_exhaustive]
pub enum AlgorithmIdentifier {
HS256,
HS384,
HS512,
RS256,
RS384,
RS512,
ES256,
ES384,
ES512,
PS256,
PS384,
PS512,
EdDSA,
#[serde(rename = "none")]
None,
}
impl AlgorithmIdentifier {
pub fn available(&self) -> bool {
match self {
Self::None => false,
Self::HS256 | Self::HS384 | Self::HS512 => cfg!(feature = "hmac"),
Self::RS256 | Self::RS384 | Self::RS512 => cfg!(feature = "rsa"),
Self::ES256 | Self::ES384 | Self::ES512 => cfg!(feature = "ecdsa"),
Self::EdDSA => false, Self::PS256 | Self::PS384 | Self::PS512 => cfg!(feature = "rsa"),
}
}
}
pub trait JsonWebAlgorithm {
const IDENTIFIER: AlgorithmIdentifier;
}
pub trait DynJsonWebAlgorithm {
fn identifier(&self) -> AlgorithmIdentifier;
}
impl<T> DynJsonWebAlgorithm for T
where
T: JsonWebAlgorithm,
{
fn identifier(&self) -> AlgorithmIdentifier {
T::IDENTIFIER
}
}
pub trait JsonWebAlgorithmDigest: JsonWebAlgorithm {
type Digest: Digest;
}
pub trait TokenSigner<S = SignatureBytes>: DynJsonWebAlgorithm + SerializePublicJWK
where
S: SignatureEncoding,
{
fn try_sign_token(&self, header: &str, payload: &str) -> Result<S, sig::Error>;
fn sign_token(&self, header: &str, payload: &str) -> S {
self.try_sign_token(header, payload).unwrap()
}
}
impl<K, S> TokenSigner<S> for K
where
K: JsonWebAlgorithmDigest + SerializePublicJWK,
K: sig::DigestSigner<K::Digest, S>,
S: SignatureEncoding,
{
fn try_sign_token(&self, header: &str, payload: &str) -> Result<S, sig::Error> {
let mut digest = <Self as JsonWebAlgorithmDigest>::Digest::new();
digest.update(header.as_bytes());
digest.update(b".");
digest.update(payload.as_bytes());
self.try_sign_digest(digest)
}
}
#[cfg(feature = "rand")]
pub trait RandomizedTokenSigner<S = SignatureBytes>:
DynJsonWebAlgorithm + SerializePublicJWK
where
S: SignatureEncoding,
{
fn try_sign_token(
&self,
header: &str,
payload: &str,
rng: &mut impl CryptoRngCore,
) -> Result<S, sig::Error>;
fn sign_token(&self, header: &str, payload: &str, rng: &mut impl CryptoRngCore) -> S {
self.try_sign_token(header, payload, rng).unwrap()
}
}
pub trait TokenVerifier<S = SignatureBytes>: DynJsonWebAlgorithm
where
S: SignatureEncoding,
{
fn verify_token(
&self,
header: &[u8],
payload: &[u8],
signature: &[u8],
) -> Result<S, sig::Error>;
}
impl<K, S> TokenVerifier<S> for K
where
K: JsonWebAlgorithmDigest + std::fmt::Debug,
K: sig::DigestVerifier<K::Digest, S>,
K::Digest: Clone + std::fmt::Debug,
S: SignatureEncoding + std::fmt::Debug,
for<'a> <S as TryFrom<&'a [u8]>>::Error: std::error::Error + Send + Sync + 'static,
{
fn verify_token(
&self,
header: &[u8],
payload: &[u8],
signature: &[u8],
) -> Result<S, sig::Error> {
let mut digest = <Self as JsonWebAlgorithmDigest>::Digest::new();
digest.update(header);
digest.update(b".");
digest.update(payload);
let signature = signature.try_into().map_err(sig::Error::from_source)?;
self.verify_digest(digest, &signature)?;
Ok(signature)
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! jose_algorithm {
($alg:ident, $signer:ty, $verifier:ty, $digest:ty, $signature:ty) => {
impl $crate::algorithms::JsonWebAlgorithm for $signer {
const IDENTIFIER: $crate::algorithms::AlgorithmIdentifier =
$crate::algorithms::AlgorithmIdentifier::$alg;
}
impl $crate::algorithms::JsonWebAlgorithmDigest for $signer {
type Digest = $digest;
}
impl signature::DigestSigner<$digest, $crate::algorithms::SignatureBytes> for $signer {
fn try_sign_digest(
&self,
digest: $digest,
) -> Result<$crate::algorithms::SignatureBytes, signature::Error> {
#[allow(unused_imports)]
use signature::SignatureEncoding as _;
let sig = <Self as signature::DigestSigner<$digest, $signature>>::sign_digest(
self, digest,
);
Ok($crate::algorithms::SignatureBytes::from(
sig.to_bytes().as_ref(),
))
}
}
impl $crate::algorithms::JsonWebAlgorithm for $verifier {
const IDENTIFIER: $crate::algorithms::AlgorithmIdentifier =
$crate::algorithms::AlgorithmIdentifier::$alg;
}
impl $crate::algorithms::JsonWebAlgorithmDigest for $verifier {
type Digest = $digest;
}
impl signature::DigestVerifier<$digest, $crate::algorithms::SignatureBytes> for $verifier {
fn verify_digest(
&self,
digest: $digest,
signature: &$crate::algorithms::SignatureBytes,
) -> Result<(), signature::Error> {
#[allow(unused_imports)]
use signature::SignatureEncoding as _;
let sig: $signature = signature
.to_bytes()
.as_ref()
.try_into()
.map_err(|error| signature::Error::from_source(error))?;
<Self as signature::DigestVerifier<$digest, $signature>>::verify_digest(
self, digest, &sig,
)
}
}
};
}
#[cfg(any(feature = "p256", feature = "p384", feature = "p521", feature = "rsa"))]
pub(crate) use jose_algorithm;
#[cfg(test)]
mod test {
use super::*;
use static_assertions as sa;
sa::assert_obj_safe!(TokenSigner<SignatureBytes>);
sa::assert_obj_safe!(TokenVerifier<SignatureBytes>);
sa::assert_obj_safe!(DynJsonWebAlgorithm);
}