use core::fmt;
use crate::crypto::constant_time::constant_time_eq;
use crate::crypto::sha1::Sha1;
use crate::crypto::sha2::{Sha256, Sha512};
const SHA1_BLOCK_SIZE: usize = 64;
#[derive(Clone)]
pub struct HmacSha1 {
inner: Sha1,
outer: Sha1,
}
impl HmacSha1 {
#[must_use]
pub fn new(key: &[u8]) -> Self {
let mut key_block: [u8; SHA1_BLOCK_SIZE] = if key.len() > SHA1_BLOCK_SIZE {
let mut hashed = Sha1::digest(key);
let mut block = [0u8; SHA1_BLOCK_SIZE];
block[..20].copy_from_slice(&hashed);
crate::crypto::zeroize::zeroize(&mut hashed);
block
} else {
let mut block = [0u8; SHA1_BLOCK_SIZE];
block[..key.len()].copy_from_slice(key);
block
};
let mut ipad = [0x36u8; SHA1_BLOCK_SIZE];
let mut opad = [0x5cu8; SHA1_BLOCK_SIZE];
for i in 0..SHA1_BLOCK_SIZE {
ipad[i] ^= key_block[i];
opad[i] ^= key_block[i];
}
let mut inner = Sha1::new();
inner.update(&ipad);
let mut outer = Sha1::new();
outer.update(&opad);
crate::crypto::zeroize::zeroize(&mut key_block);
crate::crypto::zeroize::zeroize(&mut ipad);
crate::crypto::zeroize::zeroize(&mut opad);
Self { inner, outer }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
#[must_use]
pub fn finalize(self) -> [u8; 20] {
let mut inner_hash = self.inner.finalize();
let mut outer = self.outer;
outer.update(&inner_hash);
crate::crypto::zeroize::zeroize(&mut inner_hash);
outer.finalize()
}
#[must_use]
pub fn mac(key: &[u8], data: &[u8]) -> [u8; 20] {
let mut hmac = Self::new(key);
hmac.update(data);
hmac.finalize()
}
#[must_use]
pub fn verify(key: &[u8], data: &[u8], expected: &[u8]) -> bool {
let computed = Self::mac(key, data);
constant_time_eq(&computed, expected)
}
}
impl fmt::Debug for HmacSha1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HmacSha1").finish_non_exhaustive()
}
}
const SHA256_BLOCK_SIZE: usize = 64;
#[derive(Clone)]
pub struct HmacSha256 {
inner: Sha256,
outer: Sha256,
}
impl HmacSha256 {
#[must_use]
pub fn new(key: &[u8]) -> Self {
let mut key_block: [u8; SHA256_BLOCK_SIZE] = if key.len() > SHA256_BLOCK_SIZE {
let mut hashed = Sha256::digest(key);
let mut block = [0u8; SHA256_BLOCK_SIZE];
block[..32].copy_from_slice(&hashed);
crate::crypto::zeroize::zeroize(&mut hashed);
block
} else {
let mut block = [0u8; SHA256_BLOCK_SIZE];
block[..key.len()].copy_from_slice(key);
block
};
let mut ipad = [0x36u8; SHA256_BLOCK_SIZE];
let mut opad = [0x5cu8; SHA256_BLOCK_SIZE];
for i in 0..SHA256_BLOCK_SIZE {
ipad[i] ^= key_block[i];
opad[i] ^= key_block[i];
}
let mut inner = Sha256::new();
inner.update(&ipad);
let mut outer = Sha256::new();
outer.update(&opad);
crate::crypto::zeroize::zeroize(&mut key_block);
crate::crypto::zeroize::zeroize(&mut ipad);
crate::crypto::zeroize::zeroize(&mut opad);
Self { inner, outer }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
#[must_use]
pub fn finalize(self) -> [u8; 32] {
let mut inner_hash = self.inner.finalize();
let mut outer = self.outer;
outer.update(&inner_hash);
crate::crypto::zeroize::zeroize(&mut inner_hash);
outer.finalize()
}
#[must_use]
pub fn mac(key: &[u8], data: &[u8]) -> [u8; 32] {
let mut hmac = Self::new(key);
hmac.update(data);
hmac.finalize()
}
#[must_use]
pub fn verify(key: &[u8], data: &[u8], expected: &[u8]) -> bool {
let computed = Self::mac(key, data);
constant_time_eq(&computed, expected)
}
}
impl fmt::Debug for HmacSha256 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HmacSha256").finish_non_exhaustive()
}
}
const SHA512_BLOCK_SIZE: usize = 128;
#[derive(Clone)]
pub struct HmacSha512 {
inner: Sha512,
outer: Sha512,
}
impl HmacSha512 {
#[must_use]
pub fn new(key: &[u8]) -> Self {
let mut key_block: [u8; SHA512_BLOCK_SIZE] = if key.len() > SHA512_BLOCK_SIZE {
let mut hashed = Sha512::digest(key);
let mut block = [0u8; SHA512_BLOCK_SIZE];
block[..64].copy_from_slice(&hashed);
crate::crypto::zeroize::zeroize(&mut hashed);
block
} else {
let mut block = [0u8; SHA512_BLOCK_SIZE];
block[..key.len()].copy_from_slice(key);
block
};
let mut ipad = [0x36u8; SHA512_BLOCK_SIZE];
let mut opad = [0x5cu8; SHA512_BLOCK_SIZE];
for i in 0..SHA512_BLOCK_SIZE {
ipad[i] ^= key_block[i];
opad[i] ^= key_block[i];
}
let mut inner = Sha512::new();
inner.update(&ipad);
let mut outer = Sha512::new();
outer.update(&opad);
crate::crypto::zeroize::zeroize(&mut key_block);
crate::crypto::zeroize::zeroize(&mut ipad);
crate::crypto::zeroize::zeroize(&mut opad);
Self { inner, outer }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
#[must_use]
pub fn finalize(self) -> [u8; 64] {
let mut inner_hash = self.inner.finalize();
let mut outer = self.outer;
outer.update(&inner_hash);
crate::crypto::zeroize::zeroize(&mut inner_hash);
outer.finalize()
}
#[must_use]
pub fn mac(key: &[u8], data: &[u8]) -> [u8; 64] {
let mut hmac = Self::new(key);
hmac.update(data);
hmac.finalize()
}
#[must_use]
pub fn verify(key: &[u8], data: &[u8], expected: &[u8]) -> bool {
let computed = Self::mac(key, data);
constant_time_eq(&computed, expected)
}
}
impl fmt::Debug for HmacSha512 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HmacSha512").finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoding::{hex_decode, hex_encode};
#[test]
fn rfc2202_test_case_1_sha1() {
let key = vec![0x0bu8; 20];
let data = b"Hi There";
assert_eq!(
hex_encode(&HmacSha1::mac(&key, data)),
"b617318655057264e28bc0b6fb378c8ef146be00",
);
}
#[test]
fn rfc2202_test_case_2_sha1() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
assert_eq!(
hex_encode(&HmacSha1::mac(key, data)),
"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
);
}
#[test]
fn rfc2202_test_case_3_sha1() {
let key = vec![0xaau8; 20];
let data = vec![0xddu8; 50];
assert_eq!(
hex_encode(&HmacSha1::mac(&key, &data)),
"125d7342b9ac11cd91a39af48aa17b4f63f175d3",
);
}
#[test]
fn verify_correct_sha1() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha1::mac(key, data);
assert!(HmacSha1::verify(key, data, &tag));
}
#[test]
fn verify_incorrect_sha1() {
let key = b"secret";
let data = b"payload";
let mut tag = HmacSha1::mac(key, data);
tag[0] ^= 0x01;
assert!(!HmacSha1::verify(key, data, &tag));
}
#[test]
fn verify_wrong_length_sha1() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha1::mac(key, data);
assert!(!HmacSha1::verify(key, data, &tag[..19]));
let mut extended = tag.to_vec();
extended.push(0x00);
assert!(!HmacSha1::verify(key, data, &extended));
}
#[test]
fn incremental_update_sha1() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let mut hmac = HmacSha1::new(key);
hmac.update(&data[..10]);
hmac.update(&data[10..]);
let incremental = hmac.finalize();
let one_shot = HmacSha1::mac(key, data);
assert_eq!(incremental, one_shot);
}
#[test]
fn hmac_sha1_debug_does_not_leak_state() {
let hmac = HmacSha1::new(b"super-secret-key");
let debug = format!("{hmac:?}");
assert!(debug.contains("HmacSha1"), "should contain the type name");
assert!(!debug.contains("inner"), "must not expose inner hasher");
assert!(!debug.contains("outer"), "must not expose outer hasher");
assert!(!debug.contains("secret"), "must not expose key material");
}
#[test]
fn rfc4231_test_case_1_sha256() {
let key = vec![0x0bu8; 20];
let data = b"Hi There";
assert_eq!(
hex_encode(&HmacSha256::mac(&key, data)),
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
);
}
#[test]
fn rfc4231_test_case_1_sha512() {
let key = vec![0x0bu8; 20];
let data = b"Hi There";
assert_eq!(
hex_encode(&HmacSha512::mac(&key, data)),
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
);
}
#[test]
fn rfc4231_test_case_2_sha256() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
assert_eq!(
hex_encode(&HmacSha256::mac(key, data)),
"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
);
}
#[test]
fn rfc4231_test_case_2_sha512() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
assert_eq!(
hex_encode(&HmacSha512::mac(key, data)),
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
);
}
#[test]
fn rfc4231_test_case_3_sha256() {
let key = vec![0xaau8; 20];
let data = vec![0xddu8; 50];
assert_eq!(
hex_encode(&HmacSha256::mac(&key, &data)),
"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
);
}
#[test]
fn rfc4231_test_case_3_sha512() {
let key = vec![0xaau8; 20];
let data = vec![0xddu8; 50];
assert_eq!(
hex_encode(&HmacSha512::mac(&key, &data)),
"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\
bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
);
}
#[test]
fn rfc4231_test_case_4_sha256() {
let key = hex_decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap();
let data = vec![0xcdu8; 50];
assert_eq!(
hex_encode(&HmacSha256::mac(&key, &data)),
"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
);
}
#[test]
fn rfc4231_test_case_4_sha512() {
let key = hex_decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap();
let data = vec![0xcdu8; 50];
assert_eq!(
hex_encode(&HmacSha512::mac(&key, &data)),
"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\
a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
);
}
#[test]
fn rfc4231_test_case_6_sha256() {
let key = vec![0xaau8; 131];
let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
assert_eq!(
hex_encode(&HmacSha256::mac(&key, data)),
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
);
}
#[test]
fn rfc4231_test_case_6_sha512() {
let key = vec![0xaau8; 131];
let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
assert_eq!(
hex_encode(&HmacSha512::mac(&key, data)),
"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\
6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598",
);
}
#[test]
fn rfc4231_test_case_7_sha256() {
let key = vec![0xaau8; 131];
let data = b"This is a test using a larger than block-size key and a larger \
than block-size data. The key needs to be hashed before being \
used by the HMAC algorithm.";
assert_eq!(
hex_encode(&HmacSha256::mac(&key, data)),
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
);
}
#[test]
fn rfc4231_test_case_7_sha512() {
let key = vec![0xaau8; 131];
let data = b"This is a test using a larger than block-size key and a larger \
than block-size data. The key needs to be hashed before being \
used by the HMAC algorithm.";
assert_eq!(
hex_encode(&HmacSha512::mac(&key, data)),
"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\
b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58",
);
}
#[test]
fn verify_correct_sha256() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha256::mac(key, data);
assert!(HmacSha256::verify(key, data, &tag));
}
#[test]
fn verify_correct_sha512() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha512::mac(key, data);
assert!(HmacSha512::verify(key, data, &tag));
}
#[test]
fn verify_incorrect_sha256() {
let key = b"secret";
let data = b"payload";
let mut tag = HmacSha256::mac(key, data);
tag[0] ^= 0x01;
assert!(!HmacSha256::verify(key, data, &tag));
}
#[test]
fn verify_incorrect_sha512() {
let key = b"secret";
let data = b"payload";
let mut tag = HmacSha512::mac(key, data);
tag[0] ^= 0x01;
assert!(!HmacSha512::verify(key, data, &tag));
}
#[test]
fn verify_wrong_length_sha256() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha256::mac(key, data);
assert!(!HmacSha256::verify(key, data, &tag[..31]));
let mut extended = tag.to_vec();
extended.push(0x00);
assert!(!HmacSha256::verify(key, data, &extended));
}
#[test]
fn verify_wrong_length_sha512() {
let key = b"secret";
let data = b"payload";
let tag = HmacSha512::mac(key, data);
assert!(!HmacSha512::verify(key, data, &tag[..63]));
let mut extended = tag.to_vec();
extended.push(0x00);
assert!(!HmacSha512::verify(key, data, &extended));
}
#[test]
fn incremental_update_sha256() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let mut hmac = HmacSha256::new(key);
hmac.update(&data[..10]);
hmac.update(&data[10..]);
let incremental = hmac.finalize();
let one_shot = HmacSha256::mac(key, data);
assert_eq!(incremental, one_shot);
}
#[test]
fn incremental_update_sha512() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let mut hmac = HmacSha512::new(key);
hmac.update(&data[..10]);
hmac.update(&data[10..]);
let incremental = hmac.finalize();
let one_shot = HmacSha512::mac(key, data);
assert_eq!(incremental, one_shot);
}
#[test]
fn incremental_single_byte_updates_sha256() {
let key = vec![0x0bu8; 20];
let data = b"Hi There";
let mut hmac = HmacSha256::new(&key);
for &byte in data {
hmac.update(&[byte]);
}
assert_eq!(
hex_encode(&hmac.finalize()),
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
);
}
#[test]
fn incremental_single_byte_updates_sha512() {
let key = vec![0x0bu8; 20];
let data = b"Hi There";
let mut hmac = HmacSha512::new(&key);
for &byte in data {
hmac.update(&[byte]);
}
assert_eq!(
hex_encode(&hmac.finalize()),
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
);
}
#[test]
fn hmac_sha256_debug_does_not_leak_state() {
let hmac = HmacSha256::new(b"super-secret-key");
let debug = format!("{hmac:?}");
assert!(debug.contains("HmacSha256"), "should contain the type name");
assert!(!debug.contains("inner"), "must not expose inner hasher");
assert!(!debug.contains("outer"), "must not expose outer hasher");
assert!(!debug.contains("secret"), "must not expose key material");
}
#[test]
fn hmac_sha512_debug_does_not_leak_state() {
let hmac = HmacSha512::new(b"super-secret-key");
let debug = format!("{hmac:?}");
assert!(debug.contains("HmacSha512"), "should contain the type name");
assert!(!debug.contains("inner"), "must not expose inner hasher");
assert!(!debug.contains("outer"), "must not expose outer hasher");
assert!(!debug.contains("secret"), "must not expose key material");
}
}