use crate::compat;
use crate::crypto::subtle;
use crate::hash;
use std::cell::RefCell;
pub struct HMAC<H: hash::Hash> {
opad: Vec<u8>,
ipad: Vec<u8>,
outer: RefCell<H>, inner: H,
}
impl<H: hash::Hash> hash::Hash for HMAC<H> {
fn sum(&self, b: &[u8]) -> Vec<u8> {
let orig_len = b.len();
let b = self.inner.sum(b);
let mut outer = self.outer.borrow_mut();
outer.reset();
_ = outer.write(&self.opad);
_ = outer.write(&b[orig_len..]);
outer.sum(&b[..orig_len])
}
fn size(&self) -> usize {
self.inner.size()
}
fn block_size(&self) -> usize {
self.inner.block_size()
}
fn reset(&mut self) {
self.inner.reset();
_ = self.inner.write_all(&self.ipad);
}
}
impl<H: hash::Hash> HMAC<H> {
pub fn new(h: fn() -> H, key: &[u8]) -> Self {
let mut outer = h();
let mut inner = h();
let blocksize = inner.block_size();
let mut ipad = vec![0; blocksize];
let mut opad = vec![0; blocksize];
if key.len() > blocksize {
_ = outer.write(key);
let key = outer.sum(&[]);
compat::copy(&mut ipad, &key);
compat::copy(&mut opad, &key);
} else {
compat::copy(&mut ipad, key);
compat::copy(&mut opad, key);
}
for i in 0..blocksize {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
_ = inner.write(&ipad);
Self {
opad,
ipad,
outer: RefCell::new(outer),
inner,
}
}
}
impl<H: hash::Hash> std::io::Write for HMAC<H> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub fn equal(mac1: &[u8], mac2: &[u8]) -> bool {
subtle::constant_time_compare(mac1, mac2) == 1
}