20260716 增加verification-only缓存正确性复核
This commit is contained in:
171
src/bin/verification_ccr_compare.rs
Normal file
171
src/bin/verification_ccr_compare.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"Usage: verification_ccr_compare --source-ccr <path> --verification-ccr <path> --out-json <path> [--out-md <path>]"
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Args {
|
||||
source_ccr: PathBuf,
|
||||
verification_ccr: PathBuf,
|
||||
out_json: PathBuf,
|
||||
out_md: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn parse_args(argv: &[String]) -> Result<Args, String> {
|
||||
let mut source_ccr = None;
|
||||
let mut verification_ccr = None;
|
||||
let mut out_json = None;
|
||||
let mut out_md = None;
|
||||
let mut index = 1usize;
|
||||
while index < argv.len() {
|
||||
let target = match argv[index].as_str() {
|
||||
"--source-ccr" => &mut source_ccr,
|
||||
"--verification-ccr" => &mut verification_ccr,
|
||||
"--out-json" => &mut out_json,
|
||||
"--out-md" => &mut out_md,
|
||||
"-h" | "--help" => return Err(usage().to_string()),
|
||||
other => return Err(format!("unknown argument: {other}\n{}", usage())),
|
||||
};
|
||||
index += 1;
|
||||
*target =
|
||||
Some(PathBuf::from(argv.get(index).ok_or_else(|| {
|
||||
format!("{} requires a value", argv[index - 1])
|
||||
})?));
|
||||
index += 1;
|
||||
}
|
||||
let source_ccr = source_ccr.ok_or_else(|| format!("--source-ccr is required\n{}", usage()))?;
|
||||
let verification_ccr =
|
||||
verification_ccr.ok_or_else(|| format!("--verification-ccr is required\n{}", usage()))?;
|
||||
let out_json = out_json.ok_or_else(|| format!("--out-json is required\n{}", usage()))?;
|
||||
Ok(Args {
|
||||
source_ccr,
|
||||
verification_ccr,
|
||||
out_json,
|
||||
out_md,
|
||||
})
|
||||
}
|
||||
|
||||
fn run(args: &Args) -> Result<(), String> {
|
||||
let comparison =
|
||||
rpki::verification_only::compare_ccr_files(&args.source_ccr, &args.verification_ccr)?;
|
||||
rpki::verification_only::write_ccr_comparison(
|
||||
&args.out_json,
|
||||
args.out_md.as_deref(),
|
||||
&comparison,
|
||||
)?;
|
||||
if !comparison.state_digest_match {
|
||||
return Err(format!(
|
||||
"CCR state digest mismatch: {}",
|
||||
comparison.mismatched_states.join(",")
|
||||
));
|
||||
}
|
||||
println!("CCR state digests match: {}", args.out_json.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let argv = std::env::args().collect::<Vec<_>>();
|
||||
run(&parse_args(&argv)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rpki::ccr::{
|
||||
CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation,
|
||||
build_roa_payload_state, encode_content_info,
|
||||
};
|
||||
use rpki::data_model::roa::{IpPrefix, RoaAfi};
|
||||
use rpki::validation::objects::Vrp;
|
||||
use std::fs;
|
||||
|
||||
fn argv(values: &[&str]) -> Vec<String> {
|
||||
std::iter::once("verification_ccr_compare")
|
||||
.chain(values.iter().copied())
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn sample_ccr(asn: u32) -> Vec<u8> {
|
||||
let vrps = build_roa_payload_state(&[Vrp {
|
||||
asn,
|
||||
prefix: IpPrefix {
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 24,
|
||||
addr: [192, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
},
|
||||
max_length: 24,
|
||||
}])
|
||||
.expect("build VRP state");
|
||||
encode_content_info(&CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
hash_alg: CcrDigestAlgorithm::Sha256,
|
||||
produced_at: time::OffsetDateTime::UNIX_EPOCH,
|
||||
mfts: None,
|
||||
vrps: Some(vrps),
|
||||
vaps: None,
|
||||
tas: None,
|
||||
rks: None,
|
||||
}))
|
||||
.expect("encode CCR")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_args_accepts_complete_input_and_rejects_bad_forms() {
|
||||
let parsed = parse_args(&argv(&[
|
||||
"--source-ccr",
|
||||
"source.ccr",
|
||||
"--verification-ccr",
|
||||
"verification.ccr",
|
||||
"--out-json",
|
||||
"result.json",
|
||||
"--out-md",
|
||||
"result.md",
|
||||
]))
|
||||
.expect("parse");
|
||||
assert_eq!(parsed.source_ccr, PathBuf::from("source.ccr"));
|
||||
assert_eq!(parsed.out_md, Some(PathBuf::from("result.md")));
|
||||
assert!(parse_args(&argv(&[])).unwrap_err().contains("source-ccr"));
|
||||
assert!(
|
||||
parse_args(&argv(&["--source-ccr"]))
|
||||
.unwrap_err()
|
||||
.contains("requires a value")
|
||||
);
|
||||
assert!(
|
||||
parse_args(&argv(&["--unknown"]))
|
||||
.unwrap_err()
|
||||
.contains("unknown argument")
|
||||
);
|
||||
assert!(parse_args(&argv(&["--help"])).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_writes_matching_outputs_and_rejects_mismatch() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let source = temp.path().join("source.ccr");
|
||||
let verification = temp.path().join("verification.ccr");
|
||||
let out_json = temp.path().join("out/result.json");
|
||||
let out_md = temp.path().join("out/result.md");
|
||||
fs::write(&source, sample_ccr(64496)).expect("source");
|
||||
fs::write(&verification, sample_ccr(64496)).expect("verification");
|
||||
let args = Args {
|
||||
source_ccr: source.clone(),
|
||||
verification_ccr: verification.clone(),
|
||||
out_json: out_json.clone(),
|
||||
out_md: Some(out_md.clone()),
|
||||
};
|
||||
run(&args).expect("matching run");
|
||||
assert!(out_json.is_file());
|
||||
assert!(out_md.is_file());
|
||||
|
||||
fs::write(&verification, sample_ccr(64497)).expect("mismatch");
|
||||
let error = run(&args).unwrap_err();
|
||||
assert!(error.contains("vrps"));
|
||||
assert!(
|
||||
fs::read_to_string(out_json)
|
||||
.expect("comparison JSON")
|
||||
.contains("mismatchedStates")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user