use std::marker::PhantomData;
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
use crate::classic::crypto_auth_hmacsha256::{
HmacSha256State, crypto_auth_hmacsha256, crypto_auth_hmacsha256_final,
crypto_auth_hmacsha256_init, crypto_auth_hmacsha256_update, crypto_auth_hmacsha256_verify,
};
use crate::classic::crypto_auth_hmacsha512::{
HmacSha512State, crypto_auth_hmacsha512, crypto_auth_hmacsha512_final,
crypto_auth_hmacsha512_init, crypto_auth_hmacsha512_update, crypto_auth_hmacsha512_verify,
};
use crate::classic::crypto_auth_hmacsha512256::{
HmacSha512256State, crypto_auth_hmacsha512256, crypto_auth_hmacsha512256_final,
crypto_auth_hmacsha512256_init, crypto_auth_hmacsha512256_update,
crypto_auth_hmacsha512256_verify,
};
use crate::constants::{
CRYPTO_AUTH_HMACSHA256_BYTES, CRYPTO_AUTH_HMACSHA256_KEYBYTES, CRYPTO_AUTH_HMACSHA512_BYTES,
CRYPTO_AUTH_HMACSHA512_KEYBYTES, CRYPTO_AUTH_HMACSHA512256_BYTES,
CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
};
use crate::error::Error;
use crate::types::*;
pub type HmacSha256Key = StackByteArray<CRYPTO_AUTH_HMACSHA256_KEYBYTES>;
pub type HmacSha256Mac = StackByteArray<CRYPTO_AUTH_HMACSHA256_BYTES>;
pub type HmacSha512Key = StackByteArray<CRYPTO_AUTH_HMACSHA512_KEYBYTES>;
pub type HmacSha512Mac = StackByteArray<CRYPTO_AUTH_HMACSHA512_BYTES>;
pub type HmacSha512256Key = StackByteArray<CRYPTO_AUTH_HMACSHA512256_KEYBYTES>;
pub type HmacSha512256Mac = StackByteArray<CRYPTO_AUTH_HMACSHA512256_BYTES>;
#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
pub mod protected {
use super::*;
pub use crate::protected::*;
pub type HmacSha256Key = HeapByteArray<CRYPTO_AUTH_HMACSHA256_KEYBYTES>;
pub type HmacSha256Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA256_BYTES>;
pub type HmacSha512Key = HeapByteArray<CRYPTO_AUTH_HMACSHA512_KEYBYTES>;
pub type HmacSha512Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA512_BYTES>;
pub type HmacSha512256Key = HeapByteArray<CRYPTO_AUTH_HMACSHA512256_KEYBYTES>;
pub type HmacSha512256Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA512256_BYTES>;
}
pub trait HmacVariant<const KEY_LENGTH: usize, const MAC_LENGTH: usize> {
type State;
type Mac: NewByteArray<MAC_LENGTH> + Zeroize;
fn compute(mac: &mut [u8; MAC_LENGTH], input: &[u8], key: &[u8; KEY_LENGTH]);
fn verify(mac: &[u8; MAC_LENGTH], input: &[u8], key: &[u8; KEY_LENGTH]) -> Result<(), Error>;
fn init(key: &[u8; KEY_LENGTH]) -> Self::State;
fn update(state: &mut Self::State, input: &[u8]);
fn finalize(state: Self::State, mac: &mut [u8; MAC_LENGTH]);
}
pub struct Hmac<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>
where
Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
{
state: Variant::State,
_variant: PhantomData<Variant>,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct HmacSha256Variant;
#[derive(Clone, Copy, Debug, Default)]
pub struct HmacSha512Variant;
#[derive(Clone, Copy, Debug, Default)]
pub struct HmacSha512256Variant;
pub type HmacSha256 =
Hmac<HmacSha256Variant, CRYPTO_AUTH_HMACSHA256_KEYBYTES, CRYPTO_AUTH_HMACSHA256_BYTES>;
pub type HmacSha512 =
Hmac<HmacSha512Variant, CRYPTO_AUTH_HMACSHA512_KEYBYTES, CRYPTO_AUTH_HMACSHA512_BYTES>;
pub type HmacSha512256 =
Hmac<HmacSha512256Variant, CRYPTO_AUTH_HMACSHA512256_KEYBYTES, CRYPTO_AUTH_HMACSHA512256_BYTES>;
macro_rules! impl_hmac_variant {
(
$variant:ty,
$key_len:expr,
$mac_len:expr,
$state:ty,
$mac:ty,
$compute:path,
$verify:path,
$init:path,
$update:path,
$finalize:path
) => {
impl HmacVariant<$key_len, $mac_len> for $variant {
type Mac = $mac;
type State = $state;
fn compute(mac: &mut [u8; $mac_len], input: &[u8], key: &[u8; $key_len]) {
$compute(mac, input, key);
}
fn verify(
mac: &[u8; $mac_len],
input: &[u8],
key: &[u8; $key_len],
) -> Result<(), Error> {
$verify(mac, input, key)
}
fn init(key: &[u8; $key_len]) -> Self::State {
$init(key)
}
fn update(state: &mut Self::State, input: &[u8]) {
$update(state, input);
}
fn finalize(state: Self::State, mac: &mut [u8; $mac_len]) {
$finalize(state, mac);
}
}
};
}
impl_hmac_variant!(
HmacSha256Variant,
CRYPTO_AUTH_HMACSHA256_KEYBYTES,
CRYPTO_AUTH_HMACSHA256_BYTES,
HmacSha256State,
HmacSha256Mac,
crypto_auth_hmacsha256,
crypto_auth_hmacsha256_verify,
crypto_auth_hmacsha256_init,
crypto_auth_hmacsha256_update,
crypto_auth_hmacsha256_final
);
impl_hmac_variant!(
HmacSha512Variant,
CRYPTO_AUTH_HMACSHA512_KEYBYTES,
CRYPTO_AUTH_HMACSHA512_BYTES,
HmacSha512State,
HmacSha512Mac,
crypto_auth_hmacsha512,
crypto_auth_hmacsha512_verify,
crypto_auth_hmacsha512_init,
crypto_auth_hmacsha512_update,
crypto_auth_hmacsha512_final
);
impl_hmac_variant!(
HmacSha512256Variant,
CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
CRYPTO_AUTH_HMACSHA512256_BYTES,
HmacSha512256State,
HmacSha512256Mac,
crypto_auth_hmacsha512256,
crypto_auth_hmacsha512256_verify,
crypto_auth_hmacsha512256_init,
crypto_auth_hmacsha512256_update,
crypto_auth_hmacsha512256_final
);
impl<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>
Hmac<Variant, KEY_LENGTH, MAC_LENGTH>
where
Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
{
pub fn compute<
Key: ByteArray<KEY_LENGTH>,
Input: Bytes + ?Sized,
Output: NewByteArray<MAC_LENGTH>,
>(
key: Key,
input: &Input,
) -> Output {
let mut output = Output::new_byte_array();
Variant::compute(output.as_mut_array(), input.as_slice(), key.as_array());
output
}
pub fn compute_to_vec<Key: ByteArray<KEY_LENGTH>, Input: Bytes + ?Sized>(
key: Key,
input: &Input,
) -> Vec<u8> {
Self::compute(key, input)
}
pub fn compute_and_verify<
OtherMac: ByteArray<MAC_LENGTH>,
Key: ByteArray<KEY_LENGTH>,
Input: Bytes + ?Sized,
>(
other_mac: &OtherMac,
key: Key,
input: &Input,
) -> Result<(), Error> {
Variant::verify(other_mac.as_array(), input.as_slice(), key.as_array())
}
pub fn new<Key: ByteArray<KEY_LENGTH>>(key: Key) -> Self {
Self {
state: Variant::init(key.as_array()),
_variant: PhantomData,
}
}
pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
Variant::update(&mut self.state, input.as_slice())
}
pub fn finalize<Output: NewByteArray<MAC_LENGTH>>(self) -> Output {
let mut output = Output::new_byte_array();
Variant::finalize(self.state, output.as_mut_array());
output
}
pub fn finalize_to_vec(self) -> Vec<u8> {
self.finalize()
}
pub fn verify<OtherMac: ByteArray<MAC_LENGTH>>(
self,
other_mac: &OtherMac,
) -> Result<(), Error> {
let mut computed_mac = Variant::Mac::new_byte_array();
Variant::finalize(self.state, computed_mac.as_mut_array());
let valid = other_mac
.as_array()
.ct_eq(computed_mac.as_array())
.unwrap_u8();
computed_mac.as_mut_slice().zeroize();
if valid == 1 {
Ok(())
} else {
Err(dryoc_error!("authentication codes do not match"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hmac_sha256() {
let key = HmacSha256Key::generate();
let mac = HmacSha256::compute_to_vec(key.clone(), b"Data to authenticate");
HmacSha256::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed");
}
#[test]
fn test_hmac_sha512() {
let key = HmacSha512Key::generate();
let mut auth = HmacSha512::new(key.clone());
auth.update(b"Multi-part");
auth.update(b"data");
let mac = auth.finalize_to_vec();
let mut verifier = HmacSha512::new(key);
verifier.update(b"Multi-part");
verifier.update(b"data");
verifier.verify(&mac).expect("verify failed");
}
#[test]
fn test_hmac_sha512256_rejects_invalid_input() {
let key = HmacSha512256Key::generate();
let mac = HmacSha512256::compute_to_vec(key.clone(), b"Data to authenticate");
HmacSha512256::compute_and_verify(&mac, key, b"Invalid data")
.expect_err("verify should fail");
}
#[test]
fn test_hmac_variant_generic_api() {
fn compute_with_variant<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>(
key: StackByteArray<KEY_LENGTH>,
input: &[u8],
) -> Vec<u8>
where
Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
{
Hmac::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_to_vec(key, input)
}
let key = HmacSha256Key::generate();
let generic_mac = compute_with_variant::<
HmacSha256Variant,
CRYPTO_AUTH_HMACSHA256_KEYBYTES,
CRYPTO_AUTH_HMACSHA256_BYTES,
>(key.clone(), b"Data to authenticate");
let concrete_mac = HmacSha256::compute_to_vec(key, b"Data to authenticate");
assert_eq!(generic_mac, concrete_mac);
}
}