use libcrux_traits::digest::{arrayref, InitializeDigestState};
use crate::{Error, HmacState};
pub struct HmacStream<const OUTLEN: usize, const BLOCK: usize, D>
where
D: arrayref::DigestIncremental<OUTLEN>,
D::IncrementalState: InitializeDigestState,
{
pub(crate) inner: D::IncrementalState,
pub(crate) opad: [u8; BLOCK],
}
impl<const OUTLEN: usize, const BLOCK: usize, Digest> HmacState<OUTLEN>
for HmacStream<OUTLEN, BLOCK, Digest>
where
Digest: arrayref::DigestIncremental<OUTLEN>,
Digest::IncrementalState: InitializeDigestState,
{
fn new(key: &[u8]) -> Result<Self, Error> {
debug_assert!(OUTLEN <= BLOCK);
debug_assert!(BLOCK <= u32::MAX as usize);
if key.len() > u32::MAX as usize {
return Err(Error::InvalidInputLength);
}
let mut key_block = [0u8; BLOCK];
if key.len() <= BLOCK {
key_block[..key.len()].copy_from_slice(key);
} else {
let mut tmp = Digest::IncrementalState::new();
Digest::update(&mut tmp, key)
.expect("This can't fail because we checked that key.len() <= u32::MAX");
let mut hashed = [0u8; OUTLEN];
Digest::finish(tmp, &mut hashed);
key_block[..OUTLEN].copy_from_slice(&hashed);
}
let mut k_xor_ipad = [0u8; BLOCK];
let mut k_xor_opad = [0u8; BLOCK];
for i in 0..BLOCK {
k_xor_ipad[i] = key_block[i] ^ 0x36;
k_xor_opad[i] = key_block[i] ^ 0x5c;
}
let mut inner = Digest::IncrementalState::new();
Digest::update(&mut inner, &k_xor_ipad).expect("HMAC: ipad update failed");
Ok(Self {
inner,
opad: k_xor_opad,
})
}
fn update(&mut self, data: &[u8]) -> Result<(), Error> {
Digest::update(&mut self.inner, data).map_err(|_| Error::InvalidInputLength)
}
fn finalize(self, dst: &mut [u8; OUTLEN]) {
let mut inner_hash = [0u8; OUTLEN];
Digest::finish(self.inner, &mut inner_hash);
let mut outer = Digest::IncrementalState::new();
Digest::update(&mut outer, &self.opad).expect("self.opad.len() is always <= u32::MAX");
Digest::update(&mut outer, &inner_hash).expect("inner_hash.len() is always <= u32::MAX");
Digest::finish(outer, dst);
}
}
pub type HmacSha256 = HmacStream<32, 64, libcrux_sha2::Sha256Hash>;
pub type HmacSha384 = HmacStream<48, 128, libcrux_sha2::Sha384Hash>;
pub type HmacSha512 = HmacStream<64, 128, libcrux_sha2::Sha512Hash>;
pub type HmacSha3_224 = HmacStream<28, 144, libcrux_sha3::Sha3_224>;
pub type HmacSha3_256 = HmacStream<32, 136, libcrux_sha3::Sha3_256>;
pub type HmacSha3_384 = HmacStream<48, 104, libcrux_sha3::Sha3_384>;
pub type HmacSha3_512 = HmacStream<64, 72, libcrux_sha3::Sha3_512>;
pub fn hmac_slices<const OUTLEN: usize, Hmac: HmacState<OUTLEN>>(
dst: &mut [u8; OUTLEN],
key: &[u8],
slices: &[&[u8]],
) -> Result<(), Error> {
let mut state = Hmac::new(key)?;
for &s in slices {
state.update(s)?;
}
state.finalize(dst);
Ok(())
}
#[inline]
pub fn hmac_sha2_256_slices(dst: &mut [u8; 32], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<32, HmacSha256>(dst, key, slices)
}
#[inline]
pub fn hmac_sha2_384_slices(dst: &mut [u8; 48], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<48, HmacSha384>(dst, key, slices)
}
#[inline]
pub fn hmac_sha2_512_slices(dst: &mut [u8; 64], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<64, HmacSha512>(dst, key, slices)
}
#[inline]
pub fn hmac_sha3_224_slices(dst: &mut [u8; 28], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<28, HmacSha3_224>(dst, key, slices)
}
#[inline]
pub fn hmac_sha3_256_slices(dst: &mut [u8; 32], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<32, HmacSha3_256>(dst, key, slices)
}
#[inline]
pub fn hmac_sha3_384_slices(dst: &mut [u8; 48], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<48, HmacSha3_384>(dst, key, slices)
}
#[inline]
pub fn hmac_sha3_512_slices(dst: &mut [u8; 64], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
hmac_slices::<64, HmacSha3_512>(dst, key, slices)
}
#[inline]
pub fn hmac_sha3_224(dst: &mut [u8; 28], key: &[u8], data: &[u8]) -> Result<(), Error> {
hmac_sha3_224_slices(dst, key, &[data])
}
#[inline]
pub fn hmac_sha3_256(dst: &mut [u8; 32], key: &[u8], data: &[u8]) -> Result<(), Error> {
hmac_sha3_256_slices(dst, key, &[data])
}
#[inline]
pub fn hmac_sha3_384(dst: &mut [u8; 48], key: &[u8], data: &[u8]) -> Result<(), Error> {
hmac_sha3_384_slices(dst, key, &[data])
}
#[inline]
pub fn hmac_sha3_512(dst: &mut [u8; 64], key: &[u8], data: &[u8]) -> Result<(), Error> {
hmac_sha3_512_slices(dst, key, &[data])
}