use crate::sm3::{BLOCK_SIZE, DIGEST_SIZE, Sm3, hash};
use zeroize::Zeroize;
#[must_use]
pub fn hmac_sm3(key: &[u8], message: &[u8]) -> [u8; DIGEST_SIZE] {
let mut k_prime = [0u8; BLOCK_SIZE];
if key.len() > BLOCK_SIZE {
let mut hashed = hash(key);
k_prime[..DIGEST_SIZE].copy_from_slice(&hashed);
hashed.zeroize();
} else {
k_prime[..key.len()].copy_from_slice(key);
}
let mut ipad_key = [0x36u8; BLOCK_SIZE];
let mut opad_key = [0x5cu8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
ipad_key[i] ^= k_prime[i];
opad_key[i] ^= k_prime[i];
}
let mut inner = Sm3::new();
inner.update(&ipad_key);
inner.update(message);
let inner_digest = inner.finalize();
let mut outer = Sm3::new();
outer.update(&opad_key);
outer.update(&inner_digest);
let result = outer.finalize();
k_prime.zeroize();
ipad_key.zeroize();
opad_key.zeroize();
result
}
#[cfg(test)]
mod tests {
use super::*;
fn to_hex(bytes: &[u8]) -> alloc::string::String {
use alloc::string::String;
use core::fmt::Write;
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
let _ = write!(s, "{b:02x}");
}
s
}
#[test]
fn test1_hi_there() {
let key = [0x0bu8; 20];
let message = b"Hi There";
let mac = hmac_sm3(&key, message);
assert_eq!(
to_hex(&mac),
"51b00d1fb49832bfb01c3ce27848e59f871d9ba938dc563b338ca964755cce70"
);
}
#[test]
fn test2_jefe_what_do_ya_want() {
let key = b"Jefe";
let message = b"what do ya want for nothing?";
let mac = hmac_sm3(key, message);
assert_eq!(
to_hex(&mac),
"2e87f1d16862e6d964b50a5200bf2b10b764faa9680a296a2405f24bec39f882"
);
}
#[test]
fn test6_long_key_hash_first() {
let key = [0xaau8; 131];
let message = b"Test Using Larger Than Block-Size Key - Hash Key First";
let mac = hmac_sm3(&key, message);
assert_eq!(
to_hex(&mac),
"b4fd844e13342002f0b2e0690ea7741f1497d993a70494cea601e657bedf67a0"
);
}
#[test]
fn empty_key_empty_message() {
let mac = hmac_sm3(&[], &[]);
assert_eq!(
to_hex(&mac),
"0d23f72ba15e9c189a879aefc70996b06091de6e64d31b7a84004356dd915261"
);
}
#[test]
fn long_key_takes_hash_first_path() {
let long_key = [0xaau8; 131]; let message = b"test message";
let mac_long = hmac_sm3(&long_key, message);
let prehashed = hash(&long_key);
let mac_short = hmac_sm3(&prehashed, message);
assert_eq!(
mac_long, mac_short,
"hash-first path on long key must match HMAC over pre-hashed key"
);
}
#[test]
fn key_exactly_block_size() {
let key = [0xccu8; BLOCK_SIZE];
let mac = hmac_sm3(&key, b"x");
assert_eq!(mac.len(), DIGEST_SIZE);
}
#[test]
fn different_messages_different_macs() {
let key = b"key123";
let mac_a = hmac_sm3(key, b"message a");
let mac_b = hmac_sm3(key, b"message b");
assert_ne!(mac_a, mac_b);
}
#[test]
fn different_keys_different_macs() {
let mac_a = hmac_sm3(b"key1", b"the message");
let mac_b = hmac_sm3(b"key2", b"the message");
assert_ne!(mac_a, mac_b);
}
}