use assert_cmd::Command;
use enprot::capability::KeyFp;
use enprot::ledger::{Anchor, AnchorHash, PayloadHash, SignerId, sign_anchor};
use enprot::pki::{self, SigAlgKind};
use std::collections::BTreeMap;
use std::fs;
use tempfile::tempdir;
fn setup_signed_chain_file() -> (tempfile::TempDir, std::path::PathBuf, std::path::PathBuf) {
let dir = tempdir().unwrap();
let mut rng = botan::RandomNumberGenerator::new_system().unwrap();
let (priv_pem, pub_pem) = pki::keygen(SigAlgKind::Ed25519, &mut rng).unwrap();
let fp = KeyFp::from_pem(&pub_pem).unwrap();
let signer = SignerId::new(SigAlgKind::Ed25519, fp);
let empty_hash = {
let policy = enprot::crypto::CryptoPolicyDefault {};
let hex = enprot::crypto::hexdigest("sha3-256", b"", &policy).unwrap();
let mut arr = [0u8; 32];
arr.copy_from_slice(&hex::decode(&hex).unwrap());
PayloadHash(arr)
};
let anchor = Anchor::builder(signer, empty_hash)
.with_mutations("genesis")
.build();
let signed = sign_anchor(&anchor, &priv_pem, &pub_pem, SigAlgKind::Ed25519).unwrap();
let ext = signed.to_extfields();
let _ = AnchorHash([0u8; 32]);
let mut line = String::from("// <( CHAIN");
for (k, v) in &ext {
line.push_str(&format!(" {}:{}", k, v));
}
line.push_str(" )>\n");
let file_path = dir.path().join("anchor.ept");
fs::write(&file_path, line).unwrap();
let pub_path = dir.path().join("pub.pem");
fs::write(&pub_path, pub_pem).unwrap();
(dir, file_path, pub_path)
}
#[test]
fn verify_chain_accepts_real_signature() {
let (_td, file, pub_pem) = setup_signed_chain_file();
Command::cargo_bin("enprot")
.unwrap()
.arg("verify-chain")
.arg("--trust-root")
.arg(&pub_pem)
.arg(&file)
.assert()
.success();
}
#[test]
fn verify_chain_rejects_when_no_trust_root_supplied() {
let (_td, file, _pub_pem) = setup_signed_chain_file();
Command::cargo_bin("enprot")
.unwrap()
.arg("verify-chain")
.arg(&file)
.assert()
.failure();
}
#[test]
fn verify_chain_rejects_wrong_trust_root() {
let (_td, file, _real_pub) = setup_signed_chain_file();
let other_dir = tempdir().unwrap();
let other_pub = other_dir.path().join("other.pub");
Command::cargo_bin("enprot")
.unwrap()
.args(["keygen", "ed25519", "--out-pub"])
.arg(&other_pub)
.assert()
.success();
Command::cargo_bin("enprot")
.unwrap()
.arg("verify-chain")
.arg("--trust-root")
.arg(&other_pub)
.arg(&file)
.assert()
.failure();
}
#[test]
fn verify_chain_rejects_synthetic_fixture_with_phantom_parent() {
let dir = tempdir().unwrap();
let pub_pem = dir.path().join("pub.pem");
Command::cargo_bin("enprot")
.unwrap()
.args(["keygen", "ed25519", "--out-pub"])
.arg(&pub_pem)
.assert()
.success();
Command::cargo_bin("enprot")
.unwrap()
.arg("verify-chain")
.arg("--trust-root")
.arg(&pub_pem)
.arg("test-data/chain-anchor.ept")
.assert()
.failure();
}
#[test]
fn verify_chain_handles_empty_file_gracefully() {
let dir = tempdir().unwrap();
let empty = dir.path().join("empty.ept");
fs::write(&empty, "").unwrap();
let pub_pem = dir.path().join("pub.pem");
Command::cargo_bin("enprot")
.unwrap()
.args(["keygen", "ed25519", "--out-pub"])
.arg(&pub_pem)
.assert()
.success();
Command::cargo_bin("enprot")
.unwrap()
.arg("verify-chain")
.arg("--trust-root")
.arg(&pub_pem)
.arg(&empty)
.assert()
.success();
}
#[allow(dead_code)]
fn _typecheck_btreemap() -> BTreeMap<String, String> {
BTreeMap::new()
}