crates/monetize-product-wasm/tests/crypto_kat.rs
//! **Known answers for the host's crypto**: the HMAC door, pinned on the pre-bump stack
//! (2026-10-03, the release-3 merge: hmac 0.12 / sha2 0.10, before hmac 0.13 and sha2 0.11).
//! What a running host keeps across an upgrade is the vendors' HMAC secrets, so the door must
//! give the same verdicts: RFC 4231 test cases 2 and 6 (6 is the key longer than the block),
//! driven through `Doors::hmac_verify` with the secret in a file. (The identity key's known
//! answers went with the identity in monetize v2.0 T13, A28 M3.)
use std::time::Duration;
use monetize_product_guest::wire::HmacHead;
use monetize_product_wasm::doors::{DoorSpec, Doors};
/// RFC 4231: (key, data, HMAC-SHA256).
const RFC4231: [(&[u8], &[u8], &str); 2] = [
(b"Jefe", b"what do ya want for nothing?", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
(
&[0xaa; 131],
b"Test Using Larger Than Block-Size Key - Hash Key First",
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
),
];
#[test]
fn the_hmac_door_gives_the_rfc_4231_verdicts() {
let dir = tempfile::tempdir().unwrap();
let secrets = dir.path().join("secrets");
std::fs::create_dir(&secrets).unwrap();
let names: Vec<String> = (0..RFC4231.len()).map(|i| format!("kat{i}")).collect();
for (name, (key, _, _)) in names.iter().zip(RFC4231) {
let p = monetize_product_wasm::secrets::path(&secrets, name);
std::fs::write(&p, key).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o600)).unwrap();
}
}
let doors = Doors::open_with(DoorSpec {
product: "kat",
endpoint: "",
bearer: None,
twin: None,
tls_ca: None,
tls_client: None,
grants: &["hmac".to_string()],
http_hosts: &[],
grpc_hosts: &[],
call_timeout: Duration::from_secs(5),
http_auth: &[],
hmac_secrets: &names,
secrets_dir: Some(&secrets),
http_resolve: &[],
})
.expect("the doors open");
for (name, (_, data, mac)) in names.iter().zip(RFC4231) {
let verdict = |candidate: &str| {
let head = HmacHead { secret: name.clone(), algo: "sha256".into(), candidates_hex: vec![candidate.to_string()] };
doors.hmac_verify(&head, data).expect("the door answers").0.value
};
assert_eq!(verdict(mac).as_deref(), Some("match"), "{name}: RFC 4231 says {mac}");
let mut wrong = mac.to_string();
wrong.replace_range(0..2, if &mac[0..2] == "00" { "01" } else { "00" });
assert_eq!(verdict(&wrong).as_deref(), Some("mismatch"), "{name}: a one-byte change still matched");
}
}