use std::path::Path; use rpki::data_model::crl::RpkixCrl; use rpki::data_model::manifest::ManifestObject; use rpki::data_model::rc::ResourceCertificate; use rpki::policy::{Policy, SignedObjectFailurePolicy}; use rpki::storage::{PackFile, RocksStore}; use rpki::validation::manifest::process_manifest_publication_point; use rpki::validation::objects::process_fetch_cache_pp_pack_for_issuer; fn fixture_to_rsync_uri(path: &Path) -> String { let rel = path .strip_prefix("tests/fixtures/repository") .expect("path under tests/fixtures/repository"); let mut it = rel.components(); let host = it .next() .expect("host component") .as_os_str() .to_string_lossy(); let rest = it.as_path().to_string_lossy(); format!("rsync://{host}/{rest}") } fn fixture_dir_to_rsync_uri(dir: &Path) -> String { let mut s = fixture_to_rsync_uri(dir); if !s.ends_with('/') { s.push('/'); } s } fn build_cernet_pack_and_validation_time() -> ( rpki::storage::FetchCachePpPack, time::OffsetDateTime, Vec, ResourceCertificate, ) { let manifest_path = Path::new( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ); let manifest_bytes = std::fs::read(manifest_path).expect("read manifest fixture"); let manifest = ManifestObject::decode_der(&manifest_bytes).expect("decode manifest fixture"); let manifest_rsync_uri = fixture_to_rsync_uri(manifest_path); let publication_point_rsync_uri = fixture_dir_to_rsync_uri(manifest_path.parent().unwrap()); let temp = tempfile::tempdir().expect("tempdir"); let store = RocksStore::open(temp.path()).expect("open rocksdb"); store .put_raw(&manifest_rsync_uri, &manifest_bytes) .expect("store manifest"); for entry in &manifest.manifest.files { let file_path = manifest_path.parent().unwrap().join(&entry.file_name); let bytes = std::fs::read(&file_path) .unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}")); let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name); store.put_raw(&rsync_uri, &bytes).expect("store file"); } let issuer_ca_der = std::fs::read( "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer", ) .expect("read issuer CA cert fixture"); let policy = Policy::default(); let out = process_manifest_publication_point( &store, &policy, &manifest_rsync_uri, &publication_point_rsync_uri, &issuer_ca_der, Some("rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"), manifest.manifest.this_update + time::Duration::seconds(1), ) .expect("process manifest publication point"); let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer CA cert"); let crl_file = out .pack .files .iter() .find(|f| f.rsync_uri.ends_with(".crl")) .expect("crl present in pack"); let crl = RpkixCrl::decode_der(&crl_file.bytes).expect("decode crl"); let mut t = manifest.manifest.this_update; if issuer_ca.tbs.validity_not_before > t { t = issuer_ca.tbs.validity_not_before; } if crl.this_update.utc > t { t = crl.this_update.utc; } t += time::Duration::seconds(1); (out.pack, t, issuer_ca_der, issuer_ca) } #[test] fn missing_crl_causes_roas_to_be_dropped_under_drop_object_policy() { let (mut pack, validation_time, issuer_ca_der, issuer_ca) = build_cernet_pack_and_validation_time(); pack.files.retain(|f| !f.rsync_uri.ends_with(".crl")); let mut policy = Policy::default(); policy.signed_object_failure_policy = SignedObjectFailurePolicy::DropObject; let out = process_fetch_cache_pp_pack_for_issuer( &pack, &policy, &issuer_ca_der, None, issuer_ca.tbs.extensions.ip_resources.as_ref(), issuer_ca.tbs.extensions.as_resources.as_ref(), validation_time, ); assert!(out.vrps.is_empty()); assert!(!out.warnings.is_empty()); assert!(out.stats.publication_point_dropped); } #[test] fn wrong_issuer_ca_cert_causes_roas_to_be_dropped_under_drop_object_policy() { let (pack, validation_time, _issuer_ca_der, _issuer_ca) = build_cernet_pack_and_validation_time(); let mut policy = Policy::default(); policy.signed_object_failure_policy = SignedObjectFailurePolicy::DropObject; // Use an unrelated trust anchor certificate as the issuer to force EE cert path validation to fail. let wrong_issuer_ca_der = std::fs::read("tests/fixtures/ta/arin-ta.cer").expect("read wrong issuer ca"); let out = process_fetch_cache_pp_pack_for_issuer( &pack, &policy, &wrong_issuer_ca_der, None, None, None, validation_time, ); assert!(out.vrps.is_empty()); assert!(!out.warnings.is_empty()); } #[test] fn invalid_aspa_object_is_reported_as_warning_under_drop_object_policy() { let (mut pack, validation_time, issuer_ca_der, issuer_ca) = build_cernet_pack_and_validation_time(); let uri = "rsync://rpki.cernet.net/repo/cernet/0/INVALID.asa".to_string(); pack.files.push(PackFile::from_bytes_compute_sha256( uri.clone(), b"\0\0".to_vec(), )); let mut policy = Policy::default(); policy.signed_object_failure_policy = SignedObjectFailurePolicy::DropObject; let out = process_fetch_cache_pp_pack_for_issuer( &pack, &policy, &issuer_ca_der, None, issuer_ca.tbs.extensions.ip_resources.as_ref(), issuer_ca.tbs.extensions.as_resources.as_ref(), validation_time, ); assert!( out.warnings .iter() .any(|w| w.context.as_deref() == Some(&uri)), "expected warning for invalid ASPA" ); }