#![cfg(feature = "hmac")]
use bolero::check;
use evidence::{
codec::Identity,
mac::{Mac, MacUnchecked, hmac::HmacSha256},
};
use hmac::Hmac;
fn key_from_seed(seed: [u8; 64]) -> hmac::digest::Key<Hmac<sha2::Sha256>> {
*hmac::digest::Key::<Hmac<sha2::Sha256>>::from_slice(&seed)
}
#[test]
#[allow(clippy::expect_used)] fn mac_verify_roundtrip() {
check!()
.with_type::<([u8; 64], Vec<u8>)>()
.for_each(|(seed, payload)| {
let key = key_from_seed(*seed);
let mac: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, payload);
let verified = mac
.try_verify(&key)
.expect("verification should succeed for just-tagged payload");
assert_eq!(&verified, payload, "verified payload must match original");
});
}
#[test]
fn mac_deterministic() {
check!()
.with_type::<([u8; 64], Vec<u8>)>()
.for_each(|(seed, payload)| {
let key = key_from_seed(*seed);
let mac1: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, payload);
let mac2: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, payload);
assert_eq!(mac1, mac2, "same input must produce same MAC");
});
}
#[test]
fn different_payloads_different_macs() {
check!()
.with_type::<([u8; 64], Vec<u8>, Vec<u8>)>()
.filter(|(_, a, b)| a != b)
.for_each(|(seed, payload_a, payload_b)| {
let key = key_from_seed(*seed);
let mac_a: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, payload_a);
let mac_b: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, payload_b);
assert_ne!(
mac_a.mac_tag(),
mac_b.mac_tag(),
"different payloads should produce different MACs"
);
});
}
#[test]
fn wrong_key_fails_verification() {
check!()
.with_type::<([u8; 64], [u8; 64], Vec<u8>)>()
.filter(|(a, b, _)| a != b)
.for_each(|(seed_correct, seed_wrong, payload)| {
let correct_key = key_from_seed(*seed_correct);
let wrong_key = key_from_seed(*seed_wrong);
let mac: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&correct_key, payload);
assert!(
mac.try_verify(&wrong_key).is_err(),
"verification must fail with wrong key"
);
});
}
#[test]
#[allow(clippy::indexing_slicing)] fn tampered_payload_fails_verification() {
check!()
.with_type::<([u8; 64], Vec<u8>)>()
.cloned()
.filter(|(_, payload)| !payload.is_empty())
.for_each(|(seed, payload)| {
let key = key_from_seed(seed);
let mac: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, &payload);
let mut tampered_bytes = mac.encoded_payload().to_vec();
tampered_bytes[0] = tampered_bytes[0].wrapping_add(1);
let tampered: Mac<Vec<u8>, HmacSha256, Identity> =
Mac::from_unchecked_parts(*mac.mac_tag(), tampered_bytes);
assert!(
tampered.try_verify(&key).is_err(),
"verification must fail with tampered payload"
);
});
}
#[test]
fn tampered_tag_fails_verification() {
check!()
.with_type::<([u8; 64], Vec<u8>)>()
.cloned()
.for_each(|(seed, payload)| {
let key = key_from_seed(seed);
let mac: Mac<Vec<u8>, HmacSha256, Identity> = Mac::tag(&key, &payload);
let mut tampered_tag = *mac.mac_tag();
tampered_tag[0] = tampered_tag[0].wrapping_add(1);
let tampered: Mac<Vec<u8>, HmacSha256, Identity> =
Mac::from_unchecked_parts(tampered_tag, mac.encoded_payload().to_vec());
assert!(
tampered.try_verify(&key).is_err(),
"verification must fail with tampered tag"
);
});
}