use std::fs;
use std::path::PathBuf;
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use serde_json::Value;
use agent_rooms::canonical::{canonical_json, parse};
use agent_rooms::keys::{pubkey_from_hex, sig_from_hex, sign, to_hex, verify};
fn vectors_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("vectors")
}
fn read_array(name: &str) -> Vec<Value> {
let path = vectors_dir().join(name);
let raw = fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {:?}: {e}", path));
let v: Value = serde_json::from_str(&raw).unwrap();
v.as_array().cloned().unwrap_or_default()
}
#[test]
fn canonical_json_vectors() {
let mut count = 0usize;
for v in read_array("canonical_json.json") {
let name = v["name"].as_str().unwrap();
let input = &v["input"];
let want_utf8 = v["expected_bytes_utf8"].as_str().unwrap();
let want_b64 = v["expected_bytes_b64"].as_str().unwrap();
let got = canonical_json(input);
let got_str =
std::str::from_utf8(&got).unwrap_or_else(|e| panic!("[{name}] non-utf8 output: {e}"));
assert_eq!(got_str, want_utf8, "[{name}] canonical bytes mismatch");
let want_bytes = B64.decode(want_b64).unwrap();
assert_eq!(got, want_bytes, "[{name}] b64 mismatch");
count += 1;
}
assert!(count > 0, "no canonical vectors loaded");
eprintln!("canonical_json: {count} vectors passed");
}
#[test]
fn signature_vectors() {
let mut count = 0usize;
for v in read_array("signatures.json") {
let name = v["name"].as_str().unwrap();
let sk_hex = v["sk_hex"].as_str().unwrap();
let pk_hex = v["pk_hex"].as_str().unwrap();
let payload = &v["payload"];
let want_canon = v["canonical_bytes_utf8"].as_str().unwrap();
let want_sig = v["expected_sig_hex"].as_str().unwrap();
let got_canon_bytes = canonical_json(payload);
let got_canon = std::str::from_utf8(&got_canon_bytes).unwrap();
assert_eq!(got_canon, want_canon, "[{name}] canonical drift");
let sk_bytes = hex::decode(sk_hex).unwrap();
let sk_arr: [u8; 32] = sk_bytes.try_into().unwrap();
let derived_pk = {
use ed25519_dalek::SigningKey;
SigningKey::from_bytes(&sk_arr).verifying_key().to_bytes()
};
assert_eq!(to_hex(&derived_pk), pk_hex, "[{name}] pk derivation");
let got_sig = sign(&sk_arr, &got_canon_bytes);
assert_eq!(to_hex(&got_sig), want_sig, "[{name}] sig mismatch");
let pk_arr = pubkey_from_hex(pk_hex).unwrap();
assert!(
verify(&pk_arr, &got_canon_bytes, &got_sig),
"[{name}] self-verify"
);
count += 1;
}
assert!(count > 0, "no signature vectors loaded");
eprintln!("signatures: {count} vectors passed");
}
#[test]
fn mutation_vectors() {
let mut count = 0usize;
for v in read_array("mutation.json") {
let name = v["name"].as_str().unwrap();
let pk_hex = v["pk_hex"].as_str().unwrap();
let canon_str = v["canonical_bytes_utf8"].as_str().unwrap();
let sig_hex = v["sig_hex"].as_str().unwrap();
let must_verify = v["must_verify"].as_bool().unwrap();
let pk = pubkey_from_hex(pk_hex).unwrap();
let canon_bytes = canon_str.as_bytes();
let got = match sig_from_hex(sig_hex) {
Ok(sig) => verify(&pk, canon_bytes, &sig),
Err(_) => false,
};
assert_eq!(
got, must_verify,
"[{name}] verify={got}, expected {must_verify}"
);
if let Some(wire) = v.get("non_canonical_wire_utf8").and_then(|x| x.as_str()) {
let reparsed = parse(wire).expect("non-canonical wire is valid JSON");
let recanon = canonical_json(&reparsed);
let recanon_str = std::str::from_utf8(&recanon).unwrap();
assert_eq!(
recanon_str, canon_str,
"[{name}] recanonicalized wire != expected canonical"
);
}
count += 1;
}
assert!(count > 0, "no mutation vectors loaded");
eprintln!("mutation: {count} vectors passed");
}