use crate::sha1::sha1;
const BLOCK_SIZE: usize = 64;
pub fn hmac_sha1(key: &[u8], message: &[u8]) -> [u8; 20] {
let mut k = [0u8; BLOCK_SIZE];
if key.len() > BLOCK_SIZE {
k[..20].copy_from_slice(&sha1(key));
} else {
k[..key.len()].copy_from_slice(key);
}
let mut ipad = [0u8; BLOCK_SIZE];
let mut opad = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
ipad[i] = k[i] ^ 0x36;
opad[i] = k[i] ^ 0x5C;
}
let mut inner_input = Vec::with_capacity(BLOCK_SIZE + message.len());
inner_input.extend_from_slice(&ipad);
inner_input.extend_from_slice(message);
let inner_hash = sha1(&inner_input);
let mut outer_input = Vec::with_capacity(BLOCK_SIZE + 20);
outer_input.extend_from_slice(&opad);
outer_input.extend_from_slice(&inner_hash);
sha1(&outer_input)
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
#[test]
fn rfc2202_case1() {
let key = [0x0bu8; 20];
let msg = b"Hi There";
assert_eq!(
hex(&hmac_sha1(&key, msg)),
"b617318655057264e28bc0b6fb378c8ef146be00"
);
}
#[test]
fn rfc2202_case2() {
let key = b"Jefe";
let msg = b"what do ya want for nothing?";
assert_eq!(
hex(&hmac_sha1(key, msg)),
"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"
);
}
#[test]
fn rfc2202_case3_long_data() {
let key = [0xaau8; 20];
let msg = [0xddu8; 50];
assert_eq!(
hex(&hmac_sha1(&key, &msg)),
"125d7342b9ac11cd91a39af48aa17b4f63f175d3"
);
}
#[test]
fn rfc2202_case5_long_key() {
let key = [0xaau8; 80];
let msg = b"Test Using Larger Than Block-Size Key - Hash Key First";
assert_eq!(
hex(&hmac_sha1(&key, msg)),
"aa4ae5e15272d00e95705637ce8a3b55ed402112"
);
}
}