use hmac::{Hmac, Mac};
use sha2::{Sha256, Sha512};
use super::{HMAC_SHA256_OUTPUT_LEN, HMAC_SHA512_OUTPUT_LEN};
use crate::error::{Error, Result};
type HmacSha256Inner = Hmac<Sha256>;
type HmacSha512Inner = Hmac<Sha512>;
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; HMAC_SHA256_OUTPUT_LEN]> {
let mut mac =
HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
mac.update(data);
Ok(mac.finalize().into_bytes().into())
}
pub fn hmac_sha256_verify(key: &[u8], data: &[u8], expected_tag: &[u8]) -> Result<bool> {
let mut mac =
HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
mac.update(data);
Ok(mac.verify_slice(expected_tag).is_ok())
}
pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; HMAC_SHA512_OUTPUT_LEN]> {
let mut mac =
HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
mac.update(data);
Ok(mac.finalize().into_bytes().into())
}
pub fn hmac_sha512_verify(key: &[u8], data: &[u8], expected_tag: &[u8]) -> Result<bool> {
let mut mac =
HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
mac.update(data);
Ok(mac.verify_slice(expected_tag).is_ok())
}
#[derive(Debug, Clone)]
pub struct HmacSha256 {
inner: HmacSha256Inner,
}
impl HmacSha256 {
pub fn new(key: &[u8]) -> Result<Self> {
let inner =
HmacSha256Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha256 init"))?;
Ok(Self { inner })
}
pub fn update(&mut self, data: &[u8]) -> &mut Self {
self.inner.update(data);
self
}
#[must_use]
pub fn finalize(self) -> [u8; HMAC_SHA256_OUTPUT_LEN] {
self.inner.finalize().into_bytes().into()
}
#[must_use]
pub fn verify(self, expected_tag: &[u8]) -> bool {
self.inner.verify_slice(expected_tag).is_ok()
}
}
#[derive(Debug, Clone)]
pub struct HmacSha512 {
inner: HmacSha512Inner,
}
impl HmacSha512 {
pub fn new(key: &[u8]) -> Result<Self> {
let inner =
HmacSha512Inner::new_from_slice(key).map_err(|_| Error::Mac("hmac-sha512 init"))?;
Ok(Self { inner })
}
pub fn update(&mut self, data: &[u8]) -> &mut Self {
self.inner.update(data);
self
}
#[must_use]
pub fn finalize(self) -> [u8; HMAC_SHA512_OUTPUT_LEN] {
self.inner.finalize().into_bytes().into()
}
#[must_use]
pub fn verify(self, expected_tag: &[u8]) -> bool {
self.inner.verify_slice(expected_tag).is_ok()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, unused_results)]
mod tests {
use super::*;
fn hex_to_bytes(s: &str) -> alloc::vec::Vec<u8> {
hex::decode(s).expect("valid hex")
}
#[test]
fn hmac_sha256_rfc4231_case1() {
let key = hex_to_bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
let data = b"Hi There";
let expected =
hex_to_bytes("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7");
assert_eq!(&hmac_sha256(&key, data).unwrap()[..], &expected[..]);
assert!(hmac_sha256_verify(&key, data, &expected).unwrap());
}
#[test]
fn hmac_sha256_rfc4231_case2() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let expected =
hex_to_bytes("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843");
assert_eq!(&hmac_sha256(key, data).unwrap()[..], &expected[..]);
assert!(hmac_sha256_verify(key, data, &expected).unwrap());
}
#[test]
fn hmac_sha512_rfc4231_case1() {
let key = hex_to_bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
let data = b"Hi There";
let expected = hex_to_bytes(
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
);
assert_eq!(&hmac_sha512(&key, data).unwrap()[..], &expected[..]);
assert!(hmac_sha512_verify(&key, data, &expected).unwrap());
}
#[test]
fn hmac_sha512_rfc4231_case2() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let expected = hex_to_bytes(
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
);
assert_eq!(&hmac_sha512(key, data).unwrap()[..], &expected[..]);
assert!(hmac_sha512_verify(key, data, &expected).unwrap());
}
#[test]
fn hmac_sha256_verify_rejects_wrong_tag() {
let tag = hmac_sha256(b"key", b"data").unwrap();
let mut tampered = tag;
tampered[0] ^= 0x01;
assert!(!hmac_sha256_verify(b"key", b"data", &tampered).unwrap());
}
#[test]
fn hmac_sha256_verify_rejects_wrong_key() {
let tag = hmac_sha256(b"correct", b"data").unwrap();
assert!(!hmac_sha256_verify(b"wrong", b"data", &tag).unwrap());
}
#[test]
fn hmac_sha256_verify_rejects_wrong_data() {
let tag = hmac_sha256(b"key", b"original").unwrap();
assert!(!hmac_sha256_verify(b"key", b"tampered", &tag).unwrap());
}
#[test]
fn hmac_sha256_verify_rejects_truncated_tag() {
let tag = hmac_sha256(b"key", b"data").unwrap();
assert!(!hmac_sha256_verify(b"key", b"data", &tag[..16]).unwrap());
}
#[test]
fn hmac_sha512_verify_rejects_wrong_tag() {
let tag = hmac_sha512(b"key", b"data").unwrap();
let mut tampered = tag;
tampered[0] ^= 0x01;
assert!(!hmac_sha512_verify(b"key", b"data", &tampered).unwrap());
}
#[test]
fn hmac_sha256_streaming_equals_one_shot() {
let key = b"shared secret";
let data = b"the quick brown fox jumps over the lazy dog";
let one_shot = hmac_sha256(key, data).unwrap();
let mut m = HmacSha256::new(key).unwrap();
m.update(&data[..10]);
m.update(&data[10..25]);
m.update(&data[25..]);
assert_eq!(m.finalize(), one_shot);
}
#[test]
fn hmac_sha512_streaming_equals_one_shot() {
let key = b"shared secret";
let data = b"the quick brown fox jumps over the lazy dog";
let one_shot = hmac_sha512(key, data).unwrap();
let mut m = HmacSha512::new(key).unwrap();
m.update(&data[..10]);
m.update(&data[10..25]);
m.update(&data[25..]);
assert_eq!(m.finalize(), one_shot);
}
#[test]
fn hmac_sha256_streaming_chain_returns_self() {
let mut m = HmacSha256::new(b"k").unwrap();
m.update(b"chain").update(b"-friendly");
assert_eq!(m.finalize(), hmac_sha256(b"k", b"chain-friendly").unwrap());
}
#[test]
fn hmac_sha512_streaming_chain_returns_self() {
let mut m = HmacSha512::new(b"k").unwrap();
m.update(b"chain").update(b"-friendly");
assert_eq!(m.finalize(), hmac_sha512(b"k", b"chain-friendly").unwrap());
}
#[test]
fn hmac_sha256_streaming_verify_accepts_correct_tag() {
let key = b"k";
let tag = hmac_sha256(key, b"message").unwrap();
let mut m = HmacSha256::new(key).unwrap();
m.update(b"message");
assert!(m.verify(&tag));
}
#[test]
fn hmac_sha256_streaming_verify_rejects_wrong_tag() {
let key = b"k";
let tag = hmac_sha256(key, b"message").unwrap();
let mut tampered = tag;
tampered[0] ^= 0xff;
let mut m = HmacSha256::new(key).unwrap();
m.update(b"message");
assert!(!m.verify(&tampered));
}
#[test]
fn hmac_sha512_streaming_verify_accepts_correct_tag() {
let key = b"k";
let tag = hmac_sha512(key, b"message").unwrap();
let mut m = HmacSha512::new(key).unwrap();
m.update(b"message");
assert!(m.verify(&tag));
}
#[test]
fn hmac_sha256_accepts_empty_key() {
let tag = hmac_sha256(&[], b"data").unwrap();
assert!(hmac_sha256_verify(&[], b"data", &tag).unwrap());
}
#[test]
fn hmac_sha256_accepts_long_key() {
let key = [0xaau8; 256];
let tag = hmac_sha256(&key, b"data").unwrap();
assert!(hmac_sha256_verify(&key, b"data", &tag).unwrap());
}
}