rpki/tests/test_cir_sequence_peer_replay_m4.rs

215 lines
7.8 KiB
Rust

use std::path::{Path, PathBuf};
use std::process::Command;
use rpki::cir::{
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
materialize_cir,
};
fn apnic_tal_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/tal/apnic-rfc7730-https.tal")
}
fn apnic_ta_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer")
}
fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
let tal_bytes = std::fs::read(apnic_tal_path()).expect("read tal");
let ta_bytes = std::fs::read(apnic_ta_path()).expect("read ta");
let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes).expect("decode tal");
let ta_rsync_uri = tal
.ta_uris
.iter()
.find(|uri| uri.scheme() == "rsync")
.expect("tal has rsync uri")
.as_str()
.to_string();
let ta_hash = {
use sha2::{Digest, Sha256};
Sha256::digest(&ta_bytes).to_vec()
};
(
CanonicalInputRepresentation {
version: CIR_VERSION_V1,
hash_alg: CirHashAlgorithm::Sha256,
validation_time: time::OffsetDateTime::parse(
"2026-04-07T00:00:00Z",
&time::format_description::well_known::Rfc3339,
)
.unwrap(),
objects: vec![CirObject {
rsync_uri: ta_rsync_uri,
sha256: ta_hash,
}],
tals: vec![CirTal {
tal_uri: "https://example.test/root.tal".to_string(),
tal_bytes,
}],
},
ta_bytes,
)
}
fn write_static(root: &Path, date: &str, bytes: &[u8]) {
use sha2::{Digest, Sha256};
let hash = hex::encode(Sha256::digest(bytes));
let dir = root.join(date).join(&hash[0..2]).join(&hash[2..4]);
std::fs::create_dir_all(&dir).expect("mkdir static");
std::fs::write(dir.join(hash), bytes).expect("write static object");
}
fn prepare_reference_ccr(
work: &Path,
cir: &CanonicalInputRepresentation,
mirror_root: &Path,
) -> PathBuf {
let reference_ccr = work.join("reference.ccr");
let rpki_bin = env!("CARGO_BIN_EXE_rpki");
let wrapper = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper");
let tal_path = apnic_tal_path();
let ta_path = apnic_ta_path();
let out = Command::new(rpki_bin)
.env("REAL_RSYNC_BIN", "/usr/bin/rsync")
.env("CIR_MIRROR_ROOT", mirror_root)
.env("CIR_LOCAL_LINK_MODE", "1")
.args([
"--db",
work.join("reference-db").to_string_lossy().as_ref(),
"--tal-path",
tal_path.to_string_lossy().as_ref(),
"--ta-path",
ta_path.to_string_lossy().as_ref(),
"--disable-rrdp",
"--rsync-command",
wrapper.to_string_lossy().as_ref(),
"--validation-time",
&cir.validation_time
.format(&time::format_description::well_known::Rfc3339)
.unwrap(),
"--max-depth",
"0",
"--max-instances",
"1",
"--ccr-out",
reference_ccr.to_string_lossy().as_ref(),
])
.output()
.expect("run reference rpki");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
reference_ccr
}
fn prepare_sequence_root(td: &Path) -> PathBuf {
let sequence_root = td.join("sequence");
let static_root = sequence_root.join("static");
let mirror_root = td.join("mirror");
std::fs::create_dir_all(sequence_root.join("full")).unwrap();
std::fs::create_dir_all(sequence_root.join("delta-001")).unwrap();
std::fs::create_dir_all(sequence_root.join("delta-002")).unwrap();
let (cir, ta_bytes) = build_ta_only_cir();
let cir_bytes = encode_cir(&cir).expect("encode cir");
std::fs::write(sequence_root.join("full").join("input.cir"), &cir_bytes).unwrap();
std::fs::write(
sequence_root.join("delta-001").join("input.cir"),
&cir_bytes,
)
.unwrap();
std::fs::write(
sequence_root.join("delta-002").join("input.cir"),
&cir_bytes,
)
.unwrap();
write_static(&static_root, "20260407", &ta_bytes);
materialize_cir(&cir, &static_root, &mirror_root, true).unwrap();
let reference = prepare_reference_ccr(td, &cir, &mirror_root);
std::fs::copy(&reference, sequence_root.join("full").join("result.ccr")).unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-001").join("result.ccr"),
)
.unwrap();
std::fs::copy(
&reference,
sequence_root.join("delta-002").join("result.ccr"),
)
.unwrap();
std::fs::write(sequence_root.join("full").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-001").join("report.json"), b"{}").unwrap();
std::fs::write(sequence_root.join("delta-002").join("report.json"), b"{}").unwrap();
let sequence = serde_json::json!({
"version": 1,
"staticRoot": "static",
"steps": [
{"stepId":"full","kind":"full","validationTime":"2026-04-07T00:00:00Z","cirPath":"full/input.cir","ccrPath":"full/result.ccr","reportPath":"full/report.json","previousStepId":null},
{"stepId":"delta-001","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-001/input.cir","ccrPath":"delta-001/result.ccr","reportPath":"delta-001/report.json","previousStepId":"full"},
{"stepId":"delta-002","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-002/input.cir","ccrPath":"delta-002/result.ccr","reportPath":"delta-002/report.json","previousStepId":"delta-001"}
]
});
std::fs::write(
sequence_root.join("sequence.json"),
serde_json::to_vec_pretty(&sequence).unwrap(),
)
.unwrap();
sequence_root
}
#[test]
fn peer_sequence_replay_scripts_replay_all_steps() {
if !Path::new("/usr/bin/rsync").exists()
|| !Path::new("/home/yuyr/dev/rust_playground/routinator/target/debug/routinator").exists()
|| !Path::new("/home/yuyr/dev/rpki-client-9.7/build-m5/src/rpki-client").exists()
{
return;
}
let td = tempfile::tempdir().expect("tempdir");
let sequence_root = prepare_sequence_root(td.path());
let routinator_script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts/cir/run_cir_replay_sequence_routinator.sh");
let out = Command::new(routinator_script)
.args(["--sequence-root", sequence_root.to_string_lossy().as_ref()])
.output()
.expect("run routinator sequence replay");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let r_summary: serde_json::Value = serde_json::from_slice(
&std::fs::read(sequence_root.join("sequence-summary-routinator.json")).unwrap(),
)
.unwrap();
assert_eq!(r_summary["stepCount"], 3);
assert_eq!(r_summary["allMatch"], true);
let rpki_client_script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts/cir/run_cir_replay_sequence_rpki_client.sh");
let out = Command::new(rpki_client_script)
.args([
"--sequence-root",
sequence_root.to_string_lossy().as_ref(),
"--build-dir",
"/home/yuyr/dev/rpki-client-9.7/build-m5",
])
.output()
.expect("run rpki-client sequence replay");
assert!(
out.status.success(),
"stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let c_summary: serde_json::Value = serde_json::from_slice(
&std::fs::read(sequence_root.join("sequence-summary-rpki-client.json")).unwrap(),
)
.unwrap();
assert_eq!(c_summary["stepCount"], 3);
assert_eq!(c_summary["allMatch"], true);
}