use std::path::PathBuf; use rpki::blob_store::ExternalRepoBytesDb; use rpki::cir::{ CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, compute_reject_list_sha256, encode_cir, }; use sha2::Digest; const USAGE: &str = "Usage: cir_ta_only_fixture --tal-path --ta-path --tal-uri --validation-time --cir-out --repo-bytes-db "; fn parse_args( argv: &[String], ) -> Result< ( PathBuf, PathBuf, String, time::OffsetDateTime, PathBuf, PathBuf, ), String, > { let mut tal_path = None; let mut ta_path = None; let mut tal_uri = None; let mut validation_time = None; let mut cir_out = None; let mut repo_bytes_db = None; let mut i = 1usize; while i < argv.len() { match argv[i].as_str() { "--tal-path" => { i += 1; tal_path = Some(PathBuf::from( argv.get(i).ok_or("--tal-path requires a value")?, )); } "--ta-path" => { i += 1; ta_path = Some(PathBuf::from( argv.get(i).ok_or("--ta-path requires a value")?, )); } "--tal-uri" => { i += 1; tal_uri = Some(argv.get(i).ok_or("--tal-uri requires a value")?.clone()); } "--validation-time" => { i += 1; let raw = argv.get(i).ok_or("--validation-time requires a value")?; validation_time = Some( time::OffsetDateTime::parse( raw, &time::format_description::well_known::Rfc3339, ) .map_err(|e| format!("invalid validation time: {e}"))?, ); } "--cir-out" => { i += 1; cir_out = Some(PathBuf::from( argv.get(i).ok_or("--cir-out requires a value")?, )); } "--repo-bytes-db" => { i += 1; repo_bytes_db = Some(PathBuf::from( argv.get(i).ok_or("--repo-bytes-db requires a value")?, )); } "-h" | "--help" => return Err(USAGE.to_string()), other => return Err(format!("unknown argument: {other}\n\n{USAGE}")), } i += 1; } Ok(( tal_path.ok_or_else(|| format!("--tal-path is required\n\n{USAGE}"))?, ta_path.ok_or_else(|| format!("--ta-path is required\n\n{USAGE}"))?, tal_uri.ok_or_else(|| format!("--tal-uri is required\n\n{USAGE}"))?, validation_time.ok_or_else(|| format!("--validation-time is required\n\n{USAGE}"))?, cir_out.ok_or_else(|| format!("--cir-out is required\n\n{USAGE}"))?, repo_bytes_db.ok_or_else(|| format!("--repo-bytes-db is required\n\n{USAGE}"))?, )) } fn main() -> Result<(), String> { let argv: Vec = std::env::args().collect(); let (tal_path, ta_path, tal_uri, validation_time, cir_out, repo_bytes_db) = parse_args(&argv)?; let tal_bytes = std::fs::read(&tal_path).map_err(|e| format!("read tal failed: {e}"))?; let ta_bytes = std::fs::read(&ta_path).map_err(|e| format!("read ta failed: {e}"))?; let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes) .map_err(|e| format!("decode tal failed: {e}"))?; let ta_rsync_uri = tal .ta_uris .iter() .find(|uri| uri.scheme() == "rsync") .ok_or("tal must contain an rsync URI")? .as_str() .to_string(); let sha = sha2::Sha256::digest(&ta_bytes); let hash_hex = hex::encode(sha); ExternalRepoBytesDb::open(&repo_bytes_db) .map_err(|e| format!("open repo bytes db failed: {e}"))? .put_blob_bytes_batch(&[(hash_hex, ta_bytes.clone())]) .map_err(|e| format!("write repo bytes db failed: {e}"))?; let cir = CanonicalInputRepresentation { version: CIR_VERSION_V2, hash_alg: CirHashAlgorithm::Sha256, validation_time, objects: vec![CirObject { rsync_uri: ta_rsync_uri, sha256: sha.to_vec(), }], tals: vec![CirTal { tal_uri, tal_bytes }], reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()), rejected_objects: Vec::new(), }; let der = encode_cir(&cir).map_err(|e| format!("encode cir failed: {e}"))?; if let Some(parent) = cir_out.parent() { std::fs::create_dir_all(parent).map_err(|e| format!("create cir parent failed: {e}"))?; } std::fs::write(&cir_out, der).map_err(|e| format!("write cir failed: {e}"))?; Ok(()) }