#![allow(clippy::indexing_slicing)]
use crate::digest::Digest;
use crate::sha256::Sha256;
use crate::sha512::Sha512;
use crate::{ct, Error};
use core::fmt;
pub type HmacSha256 = Hmac<Sha256>;
pub type HmacSha512 = Hmac<Sha512>;
const MAX_BLOCK_SIZE: usize = 128;
const _: () = assert!(
crate::sha256::BLOCK_SIZE <= MAX_BLOCK_SIZE
&& crate::sha512::BLOCK_SIZE <= MAX_BLOCK_SIZE
&& crate::sha256::OUTPUT_SIZE <= crate::sha256::BLOCK_SIZE
&& crate::sha512::OUTPUT_SIZE <= crate::sha512::BLOCK_SIZE
);
#[derive(Clone)]
pub struct Hmac<H: Digest> {
inner: H,
outer: H,
}
impl<H: Digest> fmt::Debug for Hmac<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Hmac { .. }")
}
}
impl<H: Digest> Hmac<H> {
pub fn new(key: &[u8]) -> Self {
let mut block = [0u8; MAX_BLOCK_SIZE];
let block = &mut block[..H::BLOCK_SIZE];
if key.len() > H::BLOCK_SIZE {
let digest = H::digest(key);
block[..H::OUTPUT_SIZE].copy_from_slice(digest.as_ref());
} else {
block[..key.len()].copy_from_slice(key);
}
for b in block.iter_mut() {
*b ^= 0x36;
}
let mut inner = H::new();
inner.update(block);
for b in block.iter_mut() {
*b ^= 0x36 ^ 0x5c;
}
let mut outer = H::new();
outer.update(block);
crate::zeroize::Zeroize::zeroize(block);
Hmac { inner, outer }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn finalize(self) -> H::Output {
let mut inner = self.inner.finalize();
let mut outer = self.outer;
outer.update(inner.as_ref());
crate::zeroize::Zeroize::zeroize(inner.as_mut());
outer.finalize()
}
pub fn verify(self, tag: &[u8]) -> Result<(), Error> {
if ct::eq(self.finalize().as_ref(), tag) {
Ok(())
} else {
Err(Error::AuthenticationFailed)
}
}
pub fn verify_truncated(self, tag: &[u8]) -> Result<(), Error> {
if tag.len() < 10 || tag.len() > H::OUTPUT_SIZE {
return Err(Error::AuthenticationFailed);
}
if ct::eq(&self.finalize().as_ref()[..tag.len()], tag) {
Ok(())
} else {
Err(Error::AuthenticationFailed)
}
}
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
let mut mac = HmacSha256::new(key);
mac.update(data);
mac.finalize()
}
pub fn hmac_sha512(key: &[u8], data: &[u8]) -> [u8; 64] {
let mut mac = HmacSha512::new(key);
mac.update(data);
mac.finalize()
}