use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
pub const HMAC_SHA256_LEN: usize = 32;
#[inline]
#[must_use]
pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> [u8; HMAC_SHA256_LEN] {
let mut mac =
<Hmac<Sha256>>::new_from_slice(key).expect("HMAC accepts a key of any length (RFC 2104)");
mac.update(msg);
mac.finalize().into_bytes().into()
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(s: &str) -> Vec<u8> {
assert!(s.len() % 2 == 0, "odd-length hex");
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn rfc4231_case_1() {
let key = hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
let data = b"Hi There";
let expected = hex("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
assert_eq!(hmac_sha256(&key, data).to_vec(), expected);
}
#[test]
fn rfc4231_case_2() {
let expected = hex("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");
assert_eq!(
hmac_sha256(b"Jefe", b"what do ya want for nothing?").to_vec(),
expected
);
}
#[test]
fn rfc4231_case_3() {
let key = vec![0xaau8; 20];
let data = vec![0xddu8; 50];
let expected = hex("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe");
assert_eq!(hmac_sha256(&key, &data).to_vec(), expected);
}
#[test]
fn rfc4231_case_6_over_long_key() {
let key = vec![0xaau8; 131];
let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
let expected = hex("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54");
assert_eq!(hmac_sha256(&key, data).to_vec(), expected);
}
#[test]
fn distinct_keys_distinct_tags() {
assert_ne!(hmac_sha256(b"k1", b"msg"), hmac_sha256(b"k2", b"msg"));
}
#[test]
fn distinct_messages_distinct_tags() {
assert_ne!(hmac_sha256(b"k", b"a"), hmac_sha256(b"k", b"b"));
}
#[test]
fn empty_key_and_message_is_defined() {
assert_eq!(hmac_sha256(b"", b"").len(), HMAC_SHA256_LEN);
}
use proptest::prelude::*;
proptest! {
#[test]
fn hmac_sha256_is_deterministic(key: Vec<u8>, msg: Vec<u8>) {
prop_assert_eq!(hmac_sha256(&key, &msg), hmac_sha256(&key, &msg));
}
#[test]
fn hmac_sha256_output_is_always_32_bytes(key: Vec<u8>, msg: Vec<u8>) {
prop_assert_eq!(hmac_sha256(&key, &msg).len(), HMAC_SHA256_LEN);
}
}
}