//! Consume the published AR-1 conformance pack. //! //! This is the test that makes the pack mean something. It reads //! `jcp/conformance/v1/action_receipt.json` from disk — the same bytes a third //! party would download — resolves the public key from the meta file, and //! checks every vector against the stated expectation. //! //! Crucially it consumes the *published artifact*, not in-memory values. A pack //! that only its own generator can read is not a conformance pack. use ed25519_dalek::VerifyingKey; use jcp_action_receipt::verify; use serde_json::Value; fn pack_dir() -> std::path::PathBuf { std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../../conformance/v1") .canonicalize() .expect("conformance dir must exist next to the spec") } fn load(name: &str) -> Value { let p = pack_dir().join(name); let bytes = std::fs::read(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display())); serde_json::from_slice(&bytes).unwrap_or_else(|e| panic!("parse {}: {e}", p.display())) } fn key_and_steward() -> (VerifyingKey, String) { let meta = load("action_receipt_meta.json"); let hex_key = meta["pubkey_hex"].as_str().expect("meta.pubkey_hex"); let raw = hex::decode(hex_key).expect("pubkey hex"); let arr: [u8; 32] = raw.as_slice().try_into().expect("32-byte pubkey"); let steward = meta["steward"].as_str().expect("meta.steward").to_string(); (VerifyingKey::from_bytes(&arr).expect("valid pubkey"), steward) } #[test] fn every_positive_vector_verifies() { let (vk, steward) = key_and_steward(); let pack = load("action_receipt.json"); let vectors = pack["vectors"].as_array().expect("vectors array"); let mut checked = 0; for v in vectors { if v["expect"] != "verify" { continue; } let name = v["name"].as_str().unwrap_or(""); let env = hex::decode(v["envelope_hex"].as_str().expect("envelope_hex")) .unwrap_or_else(|e| panic!("{name}: envelope hex: {e}")); let ok = verify(&env, &vk, &steward) .unwrap_or_else(|e| panic!("positive vector `{name}` failed to verify: {e}")); // The receipt id in the pack must match what a verifier re-derives from // the bytes — otherwise the published id is decorative. let want = v["id_hex"].as_str().expect("id_hex"); assert_eq!(ok.id.to_hex(), want, "{name}: id mismatch"); // And the canonical core must match byte-for-byte, which is what makes // the content address reproducible on another implementation. let want_core = v["core_hex"].as_str().expect("core_hex"); assert_eq!(hex::encode(&ok.core), want_core, "{name}: core mismatch"); checked += 1; } assert!(checked >= 7, "expected at least 7 positive vectors, saw {checked}"); } #[test] fn every_negative_vector_is_rejected_despite_a_valid_signature() { let (vk, steward) = key_and_steward(); let pack = load("action_receipt.json"); let vectors = pack["vectors"].as_array().expect("vectors array"); let mut checked = 0; for v in vectors { let expect = v["expect"].as_str().unwrap_or("verify"); if expect == "verify" { continue; } let name = v["name"].as_str().unwrap_or(""); let env = hex::decode(v["envelope_hex"].as_str().expect("envelope_hex")) .unwrap_or_else(|e| panic!("{name}: envelope hex: {e}")); // The signature is genuine — these vectors exist precisely because a // signature-only verifier would accept them. let sig_ok = jcp_receipt::jcr1::verify_cose_sign1(&env, &steward, vk.as_bytes()); assert!( sig_ok.is_ok(), "{name}: negative vector should carry a genuine signature" ); // AR-1 must refuse it anyway. assert!( verify(&env, &vk, &steward).is_err(), "negative vector `{name}` was accepted — the rule it pins is not enforced" ); checked += 1; } assert!(checked >= 3, "expected at least 3 negative vectors, saw {checked}"); } #[test] fn pack_declares_the_profile_this_build_implements() { let meta = load("action_receipt_meta.json"); assert_eq!( meta["profile"].as_str().expect("meta.profile"), jcp_action_receipt::AR_PROFILE, "vector pack was generated for a different profile than this build implements" ); assert_eq!(meta["alg"].as_str().expect("meta.alg"), "EdDSA (COSE -8)"); }