20260407 & 20260408 基于cir 三方replay对齐,并且materialize 使用hard link优化

This commit is contained in:
2026-04-08 16:27:46 +08:00
parent 34fb9657f1
commit c9ef5aaf4c
36 changed files with 8743 additions and 31 deletions

View File

@@ -2,6 +2,7 @@ use url::Url;
use crate::data_model::ta::{TrustAnchor, TrustAnchorError};
use crate::data_model::tal::{Tal, TalDecodeError};
use crate::fetch::rsync::RsyncFetcher;
use crate::sync::rrdp::Fetcher;
use crate::validation::ca_instance::{
CaInstanceUris, CaInstanceUrisError, ca_instance_uris_from_ca_certificate,
@@ -104,6 +105,108 @@ pub fn discover_root_ca_instance_from_tal(
})))
}
pub fn discover_root_ca_instance_from_tal_with_fetchers(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal: Tal,
tal_url: Option<String>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if tal.ta_uris.is_empty() {
return Err(FromTalError::NoTaUris);
}
let mut last_err: Option<String> = None;
let mut ta_uris = tal.ta_uris.clone();
ta_uris.sort_by_key(|uri| if uri.scheme() == "rsync" { 0 } else { 1 });
for ta_uri in ta_uris.iter() {
let ta_der = match fetch_ta_der(http_fetcher, rsync_fetcher, ta_uri) {
Ok(b) => b,
Err(e) => {
last_err = Some(format!("fetch {ta_uri} failed: {e}"));
continue;
}
};
let trust_anchor = match TrustAnchor::bind_der(tal.clone(), &ta_der, Some(ta_uri)) {
Ok(ta) => ta,
Err(e) => {
last_err = Some(format!("bind {ta_uri} failed: {e}"));
continue;
}
};
let ca_instance =
match ca_instance_uris_from_ca_certificate(&trust_anchor.ta_certificate.rc_ca) {
Ok(v) => v,
Err(e) => {
last_err = Some(format!("CA instance discovery failed: {e}"));
continue;
}
};
return Ok(DiscoveredRootCaInstance {
tal_url,
trust_anchor,
ca_instance,
});
}
Err(FromTalError::TaFetch(last_err.unwrap_or_else(|| {
"unknown TA candidate error".to_string()
})))
}
fn fetch_ta_der(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
ta_uri: &Url,
) -> Result<Vec<u8>, String> {
match ta_uri.scheme() {
"https" | "http" => http_fetcher.fetch(ta_uri.as_str()),
"rsync" => fetch_ta_der_via_rsync(rsync_fetcher, ta_uri.as_str()),
scheme => Err(format!("unsupported TA URI scheme: {scheme}")),
}
}
fn fetch_ta_der_via_rsync(
rsync_fetcher: &dyn RsyncFetcher,
ta_rsync_uri: &str,
) -> Result<Vec<u8>, String> {
let base = rsync_parent_uri(ta_rsync_uri)?;
let objects = rsync_fetcher
.fetch_objects(&base)
.map_err(|e| e.to_string())?;
objects
.into_iter()
.find(|(uri, _)| uri == ta_rsync_uri)
.map(|(_, bytes)| bytes)
.ok_or_else(|| format!("TA rsync object not found in fetched subtree: {ta_rsync_uri}"))
}
fn rsync_parent_uri(ta_rsync_uri: &str) -> Result<String, String> {
let url = Url::parse(ta_rsync_uri).map_err(|e| e.to_string())?;
if url.scheme() != "rsync" {
return Err(format!("not an rsync URI: {ta_rsync_uri}"));
}
let host = url
.host_str()
.ok_or_else(|| format!("missing host in rsync URI: {ta_rsync_uri}"))?;
let segments = url
.path_segments()
.ok_or_else(|| format!("missing path in rsync URI: {ta_rsync_uri}"))?
.collect::<Vec<_>>();
if segments.is_empty() || segments.last().copied().unwrap_or_default().is_empty() {
return Err(format!("rsync URI must reference a file object: {ta_rsync_uri}"));
}
let parent_segments = &segments[..segments.len() - 1];
let mut parent = format!("rsync://{host}/");
if !parent_segments.is_empty() {
parent.push_str(&parent_segments.join("/"));
parent.push('/');
}
Ok(parent)
}
pub fn discover_root_ca_instance_from_tal_and_ta_der(
tal_bytes: &[u8],
ta_der: &[u8],
@@ -119,6 +222,58 @@ pub fn discover_root_ca_instance_from_tal_and_ta_der(
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fetch::rsync::LocalDirRsyncFetcher;
#[test]
fn discover_root_ca_instance_from_tal_with_fetchers_supports_rsync_ta_uri() {
let tal_bytes = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/tal/apnic-rfc7730-https.tal"),
)
.unwrap();
let ta_der = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/ta/apnic-ta.cer"),
)
.unwrap();
let tal = Tal::decode_bytes(&tal_bytes).unwrap();
let rsync_uri = tal
.ta_uris
.iter()
.find(|uri| uri.scheme() == "rsync")
.unwrap()
.clone();
let td = tempfile::tempdir().unwrap();
let mirror_root = td.path().join(rsync_uri.host_str().unwrap()).join("repository");
std::fs::create_dir_all(&mirror_root).unwrap();
std::fs::write(
mirror_root.join("apnic-rpki-root-iana-origin.cer"),
ta_der,
)
.unwrap();
let http = crate::fetch::http::BlockingHttpFetcher::new(
crate::fetch::http::HttpFetcherConfig::default(),
)
.unwrap();
let rsync = LocalDirRsyncFetcher::new(
td.path().join(rsync_uri.host_str().unwrap()).join("repository"),
);
let discovery = discover_root_ca_instance_from_tal_with_fetchers(&http, &rsync, tal, None)
.expect("discover via rsync TA");
assert!(discovery
.trust_anchor
.resolved_ta_uri
.unwrap()
.as_str()
.starts_with("rsync://"));
}
}
pub fn run_root_from_tal_url_once(
store: &crate::storage::RocksStore,
policy: &crate::policy::Policy,