use crate::header::{Header, Algorithm, ValidateHeaderParams, RecommendHeaderParams};
use crate::sign::{JwsSigner, JwsVerifier, SigningAlgorithm};
use hmac::{Hmac, Mac};
use sha2::{Sha256, Sha384, Sha512};
pub type ConstructError = hmac::digest::InvalidLength;
pub struct HS256 {
inner: Hmac<Sha256>,
}
impl HS256 {
pub fn new(key: &[u8]) -> Result<Self, ConstructError> {
Ok(Self {
inner: Hmac::new_from_slice(key)?,
})
}
}
pub struct HS384 {
inner: Hmac<Sha384>,
}
impl HS384 {
pub fn new(key: &[u8]) -> Result<Self, ConstructError> {
Ok(Self {
inner: Hmac::new_from_slice(key)?,
})
}
}
pub struct HS512 {
inner: Hmac<Sha512>,
}
impl HS512 {
pub fn new(key: &[u8]) -> Result<Self, ConstructError> {
Ok(Self {
inner: Hmac::new_from_slice(key)?,
})
}
}
macro_rules! impl_hs {
($struct_ident:ty: alg = $algorithm:expr, hash = $hash_ty:ty) => {
impl $struct_ident {
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
pub fn generate_key<R>(rng: &mut R) -> Vec<u8>
where
R: rand_core::CryptoRngCore,
{
use hmac::digest::crypto_common::KeySizeUser;
let mut vec = vec![0u8; Hmac::<$hash_ty>::key_size()];
rng.fill_bytes(&mut vec);
vec
}
}
impl RecommendHeaderParams for $struct_ident {
fn alg(&self) -> Algorithm {
Algorithm::Signing($algorithm)
}
}
impl JwsSigner for $struct_ident {
fn sign(&self, data: &[u8]) -> Vec<u8> {
let mut inner = self.inner.clone();
inner.update(data);
let result = inner.finalize();
crate::util::to_byte_vec(result.into_bytes().as_ref())
}
}
impl ValidateHeaderParams for $struct_ident {
fn validate_header(&self, header: &Header) -> bool {
header.algorithm == $algorithm
}
}
impl JwsVerifier for $struct_ident {
fn verify_signature(&self, data: &[u8], signature: &[u8]) -> bool {
let mut inner = self.inner.clone();
inner.update(data);
return inner.verify_slice(signature).is_ok();
}
}
};
}
impl_hs!(
HS256:
alg = SigningAlgorithm::HS256,
hash = Sha256
);
impl_hs!(
HS384:
alg = SigningAlgorithm::HS384,
hash = Sha384
);
impl_hs!(
HS512:
alg = SigningAlgorithm::HS512,
hash = Sha512
);
#[cfg(test)]
mod tests {
use super::*;
use crate::repr;
macro_rules! do_test {
($target_ty:ty => data: $data:expr, sig: $signature:expr, key: $key:expr) => {{
let data: &[u8] = { $data }.as_ref();
let signature =
repr::decode_bytes_from_base64url($signature).expect("invalid signature");
let key: &[u8] = { $key }.as_ref();
let instance = <$target_ty>::new(key).expect("could not create verifier");
assert!(
instance.verify_signature(data, &signature),
"{}: the signature couldn't be verified",
stringify!($ty)
)
}};
}
#[test]
fn hs256() {
do_test!(
HS256 =>
data: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ",
sig: "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
key: b"your-256-bit-secret"
);
do_test!(
HS256 =>
data: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkIn0",
sig: "eH9qoMvdv12LsZ3Og_K20no8uiBQFuJg6k6A7O8l06U",
key: "your-256-bit-secret"
);
}
#[test]
fn hs384() {
do_test!(
HS384 =>
data: "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
sig: "bQTnz6AuMJvmXXQsVPrxeQNvzDkimo7VNXxHeSBfClLufmCVZRUuyTwJF311JHuh",
key: "your-384-bit-secret"
);
do_test!(
HS384 =>
data: "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkIn0",
sig: "a-ezeeJcey3XsFBva3BnF_cZbJ9p461eGI9XWkw-Bs4w_c3E3qM5GlxEpUmNgxYJ",
key: "your-384-bit-secret"
);
}
#[test]
fn hs512() {
do_test!(
HS512 =>
data: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0",
sig: "VFb0qJ1LRg_4ujbZoRMXnVkUgiuKq5KxWqNdbKq_G9Vvz-S1zZa9LPxtHWKa64zDl2ofkT8F6jBt_K4riU-fPg",
key: "your-512-bit-secret"
);
do_test!(
HS512 =>
data: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkIn0",
sig: "GsvmpqH8-zqpoMshseGSahCipAkxF8lDDK7G21jUXpFgjMZiFjn-4sgO62xGdI8T_gfay2Q3uZJUeJT0TKVC-g",
key: "your-512-bit-secret"
);
}
}