use super::{Digest, Mac};
use crate::ct::{Choice, ConstantTimeEq};
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5c;
#[derive(Clone)]
pub struct Hmac<D: Digest> {
inner: D,
outer: D,
}
impl<D: Digest> Hmac<D> {
pub fn new(key: &[u8]) -> Self {
let mut block = D::zeroed_block();
let buf = block.as_mut();
if key.len() > buf.len() {
let hashed = D::digest(key);
let h = hashed.as_ref();
buf[..h.len()].copy_from_slice(h);
} else {
buf[..key.len()].copy_from_slice(key);
}
let mut ipad_block = block;
let mut opad_block = block;
for b in ipad_block.as_mut() {
*b ^= IPAD;
}
for b in opad_block.as_mut() {
*b ^= OPAD;
}
let mut inner = D::new();
inner.update(ipad_block.as_ref());
let mut outer = D::new();
outer.update(opad_block.as_ref());
Hmac { inner, outer }
}
#[inline]
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
#[inline]
pub fn chain(mut self, data: &[u8]) -> Self {
self.update(data);
self
}
#[inline]
pub fn finalize(mut self) -> D::Output {
let inner = core::mem::replace(&mut self.inner, D::new()).finalize();
let mut outer = core::mem::replace(&mut self.outer, D::new());
outer.update(inner.as_ref());
outer.finalize()
}
#[inline]
pub fn verify(self, expected: &[u8]) -> Choice {
let tag = self.finalize();
tag.as_ref().ct_eq(expected)
}
#[inline]
pub fn mac(key: &[u8], data: &[u8]) -> D::Output {
let mut h = Self::new(key);
h.update(data);
h.finalize()
}
}
impl<D: Digest> Drop for Hmac<D> {
fn drop(&mut self) {
self.inner.zeroize();
self.outer.zeroize();
}
}
impl<D: Digest> Mac for Hmac<D> {
#[inline]
fn update(&mut self, data: &[u8]) {
Hmac::update(self, data);
}
#[inline]
fn finalize_into(self, out: &mut [u8]) {
let tag = self.finalize();
let t = tag.as_ref();
let n = out.len().min(t.len());
out[..n].copy_from_slice(&t[..n]);
}
#[inline]
fn verify(self, expected: &[u8]) -> Choice {
Hmac::verify(self, expected)
}
}
pub type HmacSha224 = Hmac<super::Sha224>;
pub type HmacSha256 = Hmac<super::Sha256>;
pub type HmacSha384 = Hmac<super::Sha384>;
pub type HmacSha512 = Hmac<super::Sha512>;
pub type HmacSha512_224 = Hmac<super::Sha512_224>;
pub type HmacSha512_256 = Hmac<super::Sha512_256>;
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::from_hex;
#[test]
fn rfc4231_tc1() {
let key = [0x0bu8; 20];
let data = b"Hi There";
assert_eq!(
HmacSha256::mac(&key, data),
from_hex::<32>("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7")
);
assert_eq!(
HmacSha224::mac(&key, data),
from_hex::<28>("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22")
);
assert_eq!(
HmacSha384::mac(&key, data),
from_hex::<48>(
"afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59c\
faea9ea9076ede7f4af152e8b2fa9cb6"
)
);
assert_eq!(
HmacSha512::mac(&key, data),
from_hex::<64>(
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"
)
);
}
#[test]
fn rfc4231_tc2() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
assert_eq!(
HmacSha256::mac(key, data),
from_hex::<32>("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843")
);
assert_eq!(
HmacSha512::mac(key, data),
from_hex::<64>(
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"
)
);
}
#[test]
fn rfc4231_tc6_long_key() {
let key = [0xaau8; 131];
let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
assert_eq!(
HmacSha256::mac(&key, data),
from_hex::<32>("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54")
);
}
#[test]
fn streaming_matches_oneshot() {
let key = b"secret key";
let msg = b"The quick brown fox jumps over the lazy dog";
let oneshot = HmacSha256::mac(key, msg);
let mut h = HmacSha256::new(key);
for &byte in msg {
h.update(&[byte]);
}
assert_eq!(h.finalize(), oneshot);
}
#[test]
fn verify_constant_time() {
let key = b"k";
let msg = b"data";
let tag = HmacSha256::mac(key, msg);
assert!(bool::from(HmacSha256::new(key).chain(msg).verify(&tag)));
let mut bad = tag;
bad[0] ^= 1;
assert!(!bool::from(HmacSha256::new(key).chain(msg).verify(&bad)));
assert!(!bool::from(
HmacSha256::new(key).chain(msg).verify(&tag[..31])
));
}
}