use super::sha3::Sha3_256;
const BLOCK_SIZE: usize = 136;
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5c;
pub fn hmac_sha3_256(key: &[u8], data: &[u8]) -> [u8; 32] {
let key_padded = if key.len() > BLOCK_SIZE {
let hashed = Sha3_256::digest(key);
let mut padded = [0u8; BLOCK_SIZE];
padded[..32].copy_from_slice(&hashed);
padded
} else {
let mut padded = [0u8; BLOCK_SIZE];
padded[..key.len()].copy_from_slice(key);
padded
};
let mut inner_key = [0u8; BLOCK_SIZE];
let mut outer_key = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
inner_key[i] = key_padded[i] ^ IPAD;
outer_key[i] = key_padded[i] ^ OPAD;
}
let mut inner_hasher = Sha3_256::new();
inner_hasher.update(&inner_key);
inner_hasher.update(data);
let inner_hash = inner_hasher.finalize();
let mut outer_hasher = Sha3_256::new();
outer_hasher.update(&outer_key);
outer_hasher.update(&inner_hash);
outer_hasher.finalize()
}
pub fn hmac_sha3_256_multi(key: &[u8], chunks: &[&[u8]]) -> [u8; 32] {
let key_padded = if key.len() > BLOCK_SIZE {
let hashed = Sha3_256::digest(key);
let mut padded = [0u8; BLOCK_SIZE];
padded[..32].copy_from_slice(&hashed);
padded
} else {
let mut padded = [0u8; BLOCK_SIZE];
padded[..key.len()].copy_from_slice(key);
padded
};
let mut inner_key = [0u8; BLOCK_SIZE];
let mut outer_key = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
inner_key[i] = key_padded[i] ^ IPAD;
outer_key[i] = key_padded[i] ^ OPAD;
}
let mut inner_hasher = Sha3_256::new();
inner_hasher.update(&inner_key);
for chunk in chunks {
inner_hasher.update(chunk);
}
let inner_hash = inner_hasher.finalize();
let mut outer_hasher = Sha3_256::new();
outer_hasher.update(&outer_key);
outer_hasher.update(&inner_hash);
outer_hasher.finalize()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hmac_empty() {
let key = b"";
let data = b"";
let result = hmac_sha3_256(key, data);
let result2 = hmac_sha3_256(key, data);
assert_eq!(result, result2);
}
#[test]
fn test_hmac_basic() {
let key = b"test_key";
let data = b"test_data";
let result = hmac_sha3_256(key, data);
let result2 = hmac_sha3_256(key, data);
assert_eq!(result, result2);
let key2 = b"other_key";
let result3 = hmac_sha3_256(key2, data);
assert_ne!(result, result3);
}
#[test]
fn test_hmac_long_key() {
let key = vec![0u8; 200];
let data = b"test_data";
let result = hmac_sha3_256(&key, data);
let hashed_key = Sha3_256::digest(&key);
let result2 = hmac_sha3_256(&hashed_key, data);
assert_eq!(result, result2);
}
#[test]
fn test_hmac_multi() {
let key = b"test_key";
let chunks: &[&[u8]] = &[b"chunk1", b"chunk2", b"chunk3"];
let result_multi = hmac_sha3_256_multi(key, chunks);
let concatenated = b"chunk1chunk2chunk3";
let result_concat = hmac_sha3_256(key, concatenated);
assert_eq!(result_multi, result_concat);
}
#[test]
fn test_hmac_different_data_different_result() {
let key = b"secret_key";
let result1 = hmac_sha3_256(key, b"message1");
let result2 = hmac_sha3_256(key, b"message2");
assert_ne!(result1, result2);
}
}