use crate::errors::{CrabError, CrabResult};
use hmac::{Hmac, Mac};
use sha2::{Sha256, Sha512};
type HmacSha256 = Hmac<Sha256>;
type HmacSha512 = Hmac<Sha512>;
pub type HmacTag256 = [u8; 32];
pub type HmacTag512 = [u8; 64];
pub fn hmac_sha256(key: &[u8], message: &[u8]) -> CrabResult<HmacTag256> {
let mut mac = HmacSha256::new_from_slice(key)
.map_err(|e| CrabError::crypto_error(format!("HMAC key error: {}", e)))?;
mac.update(message);
let result = mac.finalize();
Ok(result.into_bytes().into())
}
pub fn hmac_sha256_verify(key: &[u8], message: &[u8], tag: &[u8]) -> CrabResult<bool> {
let mut mac = HmacSha256::new_from_slice(key)
.map_err(|e| CrabError::crypto_error(format!("HMAC key error: {}", e)))?;
mac.update(message);
Ok(mac.verify_slice(tag).is_ok())
}
pub fn hmac_sha512(key: &[u8], message: &[u8]) -> CrabResult<HmacTag512> {
let mut mac = HmacSha512::new_from_slice(key)
.map_err(|e| CrabError::crypto_error(format!("HMAC key error: {}", e)))?;
mac.update(message);
let result = mac.finalize();
Ok(result.into_bytes().into())
}
pub fn hmac_sha512_verify(key: &[u8], message: &[u8], tag: &[u8]) -> CrabResult<bool> {
let mut mac = HmacSha512::new_from_slice(key)
.map_err(|e| CrabError::crypto_error(format!("HMAC key error: {}", e)))?;
mac.update(message);
Ok(mac.verify_slice(tag).is_ok())
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
#[test]
fn test_hmac_sha256_basic() {
let key = b"secret_key";
let message = b"message";
let tag = hmac_sha256(key, message).unwrap();
assert_eq!(tag.len(), 32);
assert!(hmac_sha256_verify(key, message, &tag).unwrap());
assert!(!hmac_sha256_verify(key, b"wrong", &tag).unwrap());
}
#[test]
fn test_hmac_sha256_rfc_vector() {
let key = b"Jefe";
let message = b"what do ya want for nothing?";
let expected = hex!("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");
let tag = hmac_sha256(key, message).unwrap();
assert_eq!(tag, expected);
}
#[test]
fn test_hmac_sha512_basic() {
let key = b"secret_key_for_sha512";
let message = b"message";
let tag = hmac_sha512(key, message).unwrap();
assert_eq!(tag.len(), 64);
assert!(hmac_sha512_verify(key, message, &tag).unwrap());
assert!(!hmac_sha512_verify(b"wrong_key", message, &tag).unwrap());
}
#[test]
fn test_hmac_deterministic() {
let key = b"consistent_key";
let message = b"consistent_message";
let tag1 = hmac_sha256(key, message).unwrap();
let tag2 = hmac_sha256(key, message).unwrap();
assert_eq!(tag1, tag2);
}
}