use std::path::PathBuf; use rpki::cir::{ CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir, write_bytes_to_static_pool, }; use sha2::Digest; const USAGE: &str = "Usage: cir_ta_only_fixture --tal-path --ta-path --tal-uri --validation-time --cir-out --static-root "; 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 static_root = 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")?, )); } "--static-root" => { i += 1; static_root = Some(PathBuf::from( argv.get(i).ok_or("--static-root 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}"))?, static_root.ok_or_else(|| format!("--static-root 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, static_root) = 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); write_bytes_to_static_pool(&static_root, validation_time.date(), &hash_hex, &ta_bytes) .map_err(|e| format!("write static pool failed: {e}"))?; let cir = CanonicalInputRepresentation { version: CIR_VERSION_V1, 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 }], }; 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(()) }