use crate::sha2::{Sha256, Sha384, Sha512};
macro_rules! hmac_impl {
($name:ident, $hash:ident, $out:expr, $block:expr, $doc:expr) => {
#[doc = $doc]
pub struct $name {
ipad: [u8; $block],
opad: [u8; $block],
inner: $hash,
}
impl $name {
pub const OUTPUT_LEN: usize = $out;
pub const BLOCK_LEN: usize = $block;
pub fn new(key: &[u8]) -> Self {
let mut k = [0u8; $block];
if key.len() > $block {
k[..$out].copy_from_slice(&$hash::one_shot(key));
} else {
k[..key.len()].copy_from_slice(key);
}
let mut ipad = [0x36u8; $block];
let mut opad = [0x5cu8; $block];
for i in 0..$block {
ipad[i] ^= k[i];
opad[i] ^= k[i];
}
let mut inner = $hash::new();
inner.update(&ipad);
Self { ipad, opad, inner }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn finalize(mut self) -> [u8; $out] {
let inner = std::mem::take(&mut self.inner);
let it = inner.finalize();
let mut outer = $hash::new();
outer.update(&self.opad);
outer.update(&it);
let tag = outer.finalize();
self.ipad.fill(0);
self.opad.fill(0);
tag
}
pub fn verify(self, tag: &[u8]) -> Result<(), crate::Error> {
let computed = self.finalize();
crate::ct::verify_tag(&computed, tag)
}
pub fn one_shot(key: &[u8], data: &[u8]) -> [u8; $out] {
let mut m = Self::new(key);
m.update(data);
m.finalize()
}
}
impl Drop for $name {
fn drop(&mut self) {
self.ipad.fill(0);
self.opad.fill(0);
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(stringify!($name))
}
}
};
}
hmac_impl!(HmacSha256, Sha256, 32, 64, "HMAC-SHA-256 流式计算器。");
hmac_impl!(HmacSha384, Sha384, 48, 128, "HMAC-SHA-384 流式计算器。");
hmac_impl!(HmacSha512, Sha512, 64, 128, "HMAC-SHA-512 流式计算器。");