use super::{sha256, sha384, sha512};
use crate::internal_alloc::Vec;
#[must_use]
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
hmac_with_block(key, data, 64, HashVariant::Sha256)
.try_into()
.expect("hmac-sha256 output is always 32 bytes")
}
#[must_use]
pub fn hmac_sha512(key: &[u8], data: &[u8]) -> [u8; 64] {
hmac_with_block(key, data, 128, HashVariant::Sha512)
.try_into()
.expect("hmac-sha512 output is always 64 bytes")
}
#[must_use]
pub fn hmac_sha384(key: &[u8], data: &[u8]) -> [u8; 48] {
hmac_with_block(key, data, 128, HashVariant::Sha384)
.try_into()
.expect("hmac-sha384 output is always 48 bytes")
}
#[derive(Copy, Clone)]
enum HashVariant {
Sha256,
Sha384,
Sha512,
}
fn hmac_with_block(key: &[u8], data: &[u8], block_size: usize, variant: HashVariant) -> Vec<u8> {
let mut k0 = vec![0_u8; block_size];
if key.len() > block_size {
let digest = match variant {
HashVariant::Sha256 => sha256(key).to_vec(),
HashVariant::Sha384 => sha384(key).to_vec(),
HashVariant::Sha512 => sha512(key).to_vec(),
};
k0[..digest.len()].copy_from_slice(&digest);
} else {
k0[..key.len()].copy_from_slice(key);
}
let mut ipad = vec![0x36_u8; block_size];
let mut opad = vec![0x5c_u8; block_size];
for i in 0..block_size {
ipad[i] ^= k0[i];
opad[i] ^= k0[i];
}
let mut inner = ipad;
inner.extend_from_slice(data);
let inner_hash = match variant {
HashVariant::Sha256 => sha256(&inner).to_vec(),
HashVariant::Sha384 => sha384(&inner).to_vec(),
HashVariant::Sha512 => sha512(&inner).to_vec(),
};
let mut outer = opad;
outer.extend_from_slice(&inner_hash);
match variant {
HashVariant::Sha256 => sha256(&outer).to_vec(),
HashVariant::Sha384 => sha384(&outer).to_vec(),
HashVariant::Sha512 => sha512(&outer).to_vec(),
}
}