pub fn hmac<F>(key: &[u8], msg: &[u8], block_len: usize, digest_len: usize, hash: F) -> Vec<u8>
where
F: Fn(&[u8]) -> Vec<u8>,
{
let mut key_block = vec![0u8; block_len];
if key.len() > block_len {
let key_hash = hash(key);
key_block[..digest_len].copy_from_slice(&key_hash[..digest_len]);
} else {
key_block[..key.len()].copy_from_slice(key);
}
let mut inner = Vec::with_capacity(block_len + msg.len());
inner.extend(key_block.iter().map(|byte| byte ^ 0x36));
inner.extend_from_slice(msg);
let inner_hash = hash(&inner);
let mut outer = Vec::with_capacity(block_len + digest_len);
outer.extend(key_block.iter().map(|byte| byte ^ 0x5c));
outer.extend_from_slice(&inner_hash[..digest_len]);
hash(&outer)
}
#[must_use]
pub(crate) fn hmac_md5_words(key: &[u8], msg: &[u8]) -> [u32; 4] {
let mac = hmac(key, msg, 64, 16, |bytes| {
let mut out = Vec::with_capacity(16);
for word in super::md5::md5_words(bytes) {
out.extend_from_slice(&word.to_be_bytes());
}
out
});
words4(&mac)
}
#[must_use]
pub(crate) fn hmac_sha1_words(key: &[u8], msg: &[u8]) -> [u32; 5] {
let mac = hmac(key, msg, 64, 20, |bytes| {
let mut out = Vec::with_capacity(20);
for word in super::sha1::sha1_words(bytes) {
out.extend_from_slice(&word.to_be_bytes());
}
out
});
words5(&mac)
}
#[must_use]
pub(crate) fn hmac_sha256_bytes(key: &[u8], msg: &[u8]) -> [u8; 32] {
let mac = hmac(key, msg, 64, 32, |bytes| {
let mut out = Vec::with_capacity(32);
for word in super::sha256::sha256_words(bytes) {
out.extend_from_slice(&word.to_be_bytes());
}
out
});
let mut out = [0u8; 32];
out.copy_from_slice(&mac[..32]);
out
}
#[must_use]
pub(crate) fn hmac_sha256_words(key: &[u8], msg: &[u8]) -> [u32; 8] {
let bytes = hmac_sha256_bytes(key, msg);
let mut out = [0u32; 8];
for (slot, chunk) in out.iter_mut().zip(bytes.chunks_exact(4)) {
*slot = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
}
out
}
pub fn words4(bytes: &[u8]) -> [u32; 4] {
let mut out = [0u32; 4];
for (slot, chunk) in out.iter_mut().zip(bytes.chunks_exact(4)) {
*slot = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
}
out
}
pub fn words5(bytes: &[u8]) -> [u32; 5] {
let mut out = [0u32; 5];
for (slot, chunk) in out.iter_mut().zip(bytes.chunks_exact(4)) {
*slot = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
}
out
}