use assert_cmd::Command;
use enprot::capability::KeyFp;
use enprot::ledger::{Anchor, AnchorHash, PayloadHash, SignerId, sign_anchor};
use enprot::pki::{self, SigAlgKind};
use std::fs;
use tempfile::tempdir;
fn setup_keypair(dir: &std::path::Path) -> (std::path::PathBuf, std::path::PathBuf) {
let priv_pem = dir.join("priv.pem");
let pub_pem = dir.join("pub.pem");
Command::cargo_bin("enprot")
.unwrap()
.args(["keygen", "ed25519", "--out-priv"])
.arg(&priv_pem)
.args(["--out-pub"])
.arg(&pub_pem)
.assert()
.success();
(priv_pem, pub_pem)
}
fn write_secret_file(path: &std::path::Path) {
let body = "// <( BEGIN Secret )>\nplaintext\n// <( END Secret )>\n";
fs::write(path, body).unwrap();
}
#[test]
fn encrypt_anchor_produces_chain_block() {
let dir = tempdir().unwrap();
let (priv_pem, _pub_pem) = setup_keypair(dir.path());
let cas = dir.path().join("cas");
fs::create_dir(&cas).unwrap();
let ept = dir.path().join("file.ept");
write_secret_file(&ept);
Command::cargo_bin("enprot")
.unwrap()
.arg("-c")
.arg(&cas)
.args(["encrypt", "-w", "Secret", "-k", "Secret=password"])
.args(["--pbkdf", "legacy"])
.args(["--anchor", "--signer"])
.arg(&priv_pem)
.arg(&ept)
.assert()
.success();
let after = fs::read_to_string(&ept).unwrap();
assert!(
after.contains("// <( CHAIN"),
"expected CHAIN block in output, got:\n{}",
after
);
assert!(after.contains("signer:ed25519:"));
assert!(after.contains("payload:"));
assert!(after.contains("sig:"));
assert!(
after.contains("mut:encrypt+Secret"),
"expected mut:encrypt+Secret, got: {}",
after
);
}
#[test]
fn anchor_then_verify_chain_round_trips() {
let dir = tempdir().unwrap();
let (priv_pem, pub_pem) = setup_keypair(dir.path());
let cas = dir.path().join("cas");
fs::create_dir(&cas).unwrap();
let ept = dir.path().join("file.ept");
write_secret_file(&ept);
Command::cargo_bin("enprot")
.unwrap()
.arg("-c")
.arg(&cas)
.args(["encrypt", "-w", "Secret", "-k", "Secret=password"])
.args(["--pbkdf", "legacy"])
.args(["--anchor", "--signer"])
.arg(&priv_pem)
.arg(&ept)
.assert()
.success();
Command::cargo_bin("enprot")
.unwrap()
.args(["verify-chain", "--trust-root"])
.arg(&pub_pem)
.arg(&ept)
.assert()
.success();
}
#[test]
fn anchor_requires_signer() {
let dir = tempdir().unwrap();
let cas = dir.path().join("cas");
fs::create_dir(&cas).unwrap();
let ept = dir.path().join("file.ept");
write_secret_file(&ept);
Command::cargo_bin("enprot")
.unwrap()
.arg("-c")
.arg(&cas)
.args(["encrypt", "-w", "Secret", "-k", "Secret=password"])
.args(["--pbkdf", "legacy", "--anchor"])
.arg(&ept)
.assert()
.failure();
}
#[test]
fn anchor_rejects_wrong_pubkey_in_verify() {
let dir = tempdir().unwrap();
let (priv_pem, _real_pub) = setup_keypair(dir.path());
let cas = dir.path().join("cas");
fs::create_dir(&cas).unwrap();
let ept = dir.path().join("file.ept");
write_secret_file(&ept);
Command::cargo_bin("enprot")
.unwrap()
.arg("-c")
.arg(&cas)
.args(["encrypt", "-w", "Secret", "-k", "Secret=password"])
.args(["--pbkdf", "legacy"])
.args(["--anchor", "--signer"])
.arg(&priv_pem)
.arg(&ept)
.assert()
.success();
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()
.args(["verify-chain", "--trust-root"])
.arg(&other_pub)
.arg(&ept)
.assert()
.failure();
}
#[test]
fn anchor_tampered_payload_fails_verify() {
let dir = tempdir().unwrap();
let (priv_pem, pub_pem) = setup_keypair(dir.path());
let cas = dir.path().join("cas");
fs::create_dir(&cas).unwrap();
let ept = dir.path().join("file.ept");
write_secret_file(&ept);
Command::cargo_bin("enprot")
.unwrap()
.arg("-c")
.arg(&cas)
.args(["encrypt", "-w", "Secret", "-k", "Secret=password"])
.args(["--pbkdf", "legacy"])
.args(["--anchor", "--signer"])
.arg(&priv_pem)
.arg(&ept)
.assert()
.success();
let mut content = fs::read_to_string(&ept).unwrap();
let old = "payload:";
let idx = content.find(old).unwrap() + old.len();
let original = content.as_bytes()[idx];
let tampered = if original == b'0' { b'1' } else { b'0' };
let mut bytes = content.into_bytes();
bytes[idx] = tampered;
content = String::from_utf8(bytes).unwrap();
fs::write(&ept, content).unwrap();
Command::cargo_bin("enprot")
.unwrap()
.args(["verify-chain", "--trust-root"])
.arg(&pub_pem)
.arg(&ept)
.assert()
.failure();
}
#[allow(dead_code)]
fn _wire_format_check() {
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 anchor = Anchor::builder(signer, PayloadHash([0u8; 32])).build();
let _signed = sign_anchor(&anchor, &priv_pem, &pub_pem, SigAlgKind::Ed25519).unwrap();
let _h = AnchorHash([0u8; 32]);
}