use constant_time_eq::constant_time_eq_32;
use hmac::{Hmac, KeyInit, Mac};
use sha3::Sha3_256;
use crate::CryptoError;
pub(crate) const HMAC_KEY_SIZE: usize = 32;
pub(crate) const HMAC_TAG_SIZE: usize = 32;
type HmacSha3_256 = Hmac<Sha3_256>;
pub(crate) fn hmac_sha3_256_parts(key: &[u8], parts: &[&[u8]]) -> Result<[u8; 32], CryptoError> {
Ok(hmac_state_for_parts(key, parts)?
.finalize()
.into_bytes()
.into())
}
pub(crate) fn hmac_sha3_256_parts_verify(
key: &[u8],
parts: &[&[u8]],
tag: &[u8],
) -> Result<(), CryptoError> {
hmac_state_for_parts(key, parts)?
.verify_slice(tag)
.map_err(|_| CryptoError::HeaderTampered)
}
fn hmac_state_for_parts(key: &[u8], parts: &[&[u8]]) -> Result<HmacSha3_256, CryptoError> {
let mut mac = HmacSha3_256::new_from_slice(key)
.map_err(|_| CryptoError::InternalInvariant("Internal error: invalid HMAC key length"))?;
for part in parts {
mac.update(part);
}
Ok(mac)
}
pub(crate) fn ct_eq_32(a: &[u8; 32], b: &[u8; 32]) -> bool {
constant_time_eq_32(a, b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constant_time_compare_equal() {
let data = [42u8; 32];
assert!(ct_eq_32(&data, &data));
}
#[test]
fn test_constant_time_compare_not_equal() {
let data1 = [42u8; 32];
let mut data2 = [42u8; 32];
data2[0] = 43;
assert!(!ct_eq_32(&data1, &data2));
}
#[test]
fn test_constant_time_compare_all_zeros() {
let data1 = [0u8; 32];
let data2 = [0u8; 32];
assert!(ct_eq_32(&data1, &data2));
}
}