20260605 移除audit rule index和DB trace
This commit is contained in:
@@ -14,7 +14,10 @@ use crate::parallel::object_worker::{
|
||||
};
|
||||
use crate::policy::{Policy, SignedObjectFailurePolicy};
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{PackFile, PackTime, VcirLocalOutput, VcirOutputType};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, VcirLocalOutput, VcirLocalOutputPayload, VcirOutputType,
|
||||
VcirSourceObjectType,
|
||||
};
|
||||
use crate::validation::cert_path::{CertPathError, validate_signed_object_ee_cert_path_fast};
|
||||
use crate::validation::manifest::PublicationPointData;
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
@@ -28,6 +31,13 @@ const RFC_CRLDP: &[RfcRef] = &[RfcRef("RFC 6487 §4.8.6")];
|
||||
const RFC_CRLDP_AND_LOCKED_PACK: &[RfcRef] =
|
||||
&[RfcRef("RFC 6487 §4.8.6"), RfcRef("RFC 9286 §4.2.1")];
|
||||
|
||||
fn sha256_hex_to_32(hex_value: &str) -> [u8; 32] {
|
||||
let bytes = hex::decode(hex_value).expect("internal sha256 hex should decode");
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&bytes);
|
||||
out
|
||||
}
|
||||
|
||||
fn decode_resource_certificate_with_policy(
|
||||
der: &[u8],
|
||||
policy: &Policy,
|
||||
@@ -132,8 +142,6 @@ pub(crate) struct RoaTaskResult {
|
||||
pub(crate) worker_index: usize,
|
||||
pub(crate) queue_wait_ms: u64,
|
||||
pub(crate) worker_ms: u64,
|
||||
pub(crate) rsync_uri: String,
|
||||
pub(crate) sha256_hex: String,
|
||||
pub(crate) outcome: Result<RoaTaskOk, ObjectValidateError>,
|
||||
}
|
||||
|
||||
@@ -418,8 +426,8 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
|
||||
local_outputs_cache.extend(ok.local_outputs);
|
||||
}
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: result.rsync_uri,
|
||||
sha256_hex: result.sha256_hex,
|
||||
rsync_uri: file.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&file.sha256),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
@@ -428,8 +436,8 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
|
||||
Err(e) => match policy.signed_object_failure_policy {
|
||||
SignedObjectFailurePolicy::DropObject => {
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: result.rsync_uri.clone(),
|
||||
sha256_hex: result.sha256_hex,
|
||||
rsync_uri: file.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&file.sha256),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(e.to_string()),
|
||||
@@ -437,24 +445,21 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
|
||||
let mut refs = vec![RfcRef("RFC 6488 §3"), RfcRef("RFC 9582 §4-§5")];
|
||||
refs.extend_from_slice(extra_rfc_refs_for_crl_selection(&e));
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"dropping invalid ROA: {}: {e}",
|
||||
result.rsync_uri
|
||||
))
|
||||
.with_rfc_refs(&refs)
|
||||
.with_context(&result.rsync_uri),
|
||||
Warning::new(format!("dropping invalid ROA: {}: {e}", file.rsync_uri))
|
||||
.with_rfc_refs(&refs)
|
||||
.with_context(&file.rsync_uri),
|
||||
)
|
||||
}
|
||||
SignedObjectFailurePolicy::DropPublicationPoint => {
|
||||
stats.publication_point_dropped = true;
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: result.rsync_uri.clone(),
|
||||
sha256_hex: result.sha256_hex,
|
||||
rsync_uri: file.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&file.sha256),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(e.to_string()),
|
||||
});
|
||||
for f in locked_files.iter().skip(result.index + 1) {
|
||||
for f in locked_files.iter().skip(idx + 1) {
|
||||
if f.rsync_uri.ends_with(".roa") {
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: f.rsync_uri.clone(),
|
||||
@@ -484,7 +489,7 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"dropping publication point due to invalid ROA: {}: {e}",
|
||||
result.rsync_uri
|
||||
file.rsync_uri
|
||||
))
|
||||
.with_rfc_refs(&refs)
|
||||
.with_context(manifest_rsync_uri),
|
||||
@@ -786,19 +791,24 @@ pub fn process_publication_point_for_issuer_parallel_roa_with_pool_options<
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedRoaTask {
|
||||
pub(crate) publication_point_id: u64,
|
||||
index: usize,
|
||||
file: PackFile,
|
||||
manifest_rsync_uri: String,
|
||||
pub(crate) struct RoaTaskShared {
|
||||
locked_files: Arc<[PackFile]>,
|
||||
manifest_rsync_uri: Arc<str>,
|
||||
issuer_ca_der: Arc<[u8]>,
|
||||
issuer_ca: Arc<ResourceCertificate>,
|
||||
issuer_spki_der: Arc<[u8]>,
|
||||
issuer_ca_rsync_uri: Option<String>,
|
||||
issuer_ca_rsync_uri: Option<Arc<str>>,
|
||||
crl_cache: Arc<Mutex<std::collections::HashMap<String, CachedIssuerCrl>>>,
|
||||
issuer_resources_index: Arc<IssuerResourcesIndex>,
|
||||
issuer_effective_ip: Option<crate::data_model::rc::IpResourceSet>,
|
||||
issuer_effective_as: Option<crate::data_model::rc::AsResourceSet>,
|
||||
issuer_effective_ip: Option<Arc<crate::data_model::rc::IpResourceSet>>,
|
||||
issuer_effective_as: Option<Arc<crate::data_model::rc::AsResourceSet>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedRoaTask {
|
||||
pub(crate) publication_point_id: u64,
|
||||
index: usize,
|
||||
shared: Arc<RoaTaskShared>,
|
||||
validation_time: time::OffsetDateTime,
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
@@ -862,8 +872,13 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
.map(|submitted_at| worker_started.saturating_duration_since(submitted_at))
|
||||
.map(|duration| duration.as_millis() as u64)
|
||||
.unwrap_or(0);
|
||||
let sha256_hex = sha256_hex_from_32(&task.file.sha256);
|
||||
let issuer_spki = match SubjectPublicKeyInfo::from_der(task.issuer_spki_der.as_ref()) {
|
||||
let shared = task.shared.as_ref();
|
||||
let file = task
|
||||
.shared
|
||||
.locked_files
|
||||
.get(task.index)
|
||||
.expect("ROA task index must reference locked file");
|
||||
let issuer_spki = match SubjectPublicKeyInfo::from_der(shared.issuer_spki_der.as_ref()) {
|
||||
Ok((rem, spki)) if rem.is_empty() => spki,
|
||||
Ok((rem, _)) => {
|
||||
return RoaTaskResult {
|
||||
@@ -872,8 +887,6 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
worker_index,
|
||||
queue_wait_ms,
|
||||
worker_ms: worker_started.elapsed().as_millis() as u64,
|
||||
rsync_uri: task.file.rsync_uri,
|
||||
sha256_hex,
|
||||
outcome: Err(ObjectValidateError::CertPath(
|
||||
CertPathError::IssuerSpkiTrailingBytes(rem.len()),
|
||||
)),
|
||||
@@ -886,8 +899,6 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
worker_index,
|
||||
queue_wait_ms,
|
||||
worker_ms: worker_started.elapsed().as_millis() as u64,
|
||||
rsync_uri: task.file.rsync_uri,
|
||||
sha256_hex,
|
||||
outcome: Err(ObjectValidateError::CertPath(
|
||||
CertPathError::IssuerSpkiParse(e.to_string()),
|
||||
)),
|
||||
@@ -895,16 +906,16 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
}
|
||||
};
|
||||
let outcome = process_roa_with_issuer_parallel_cached(
|
||||
&task.file,
|
||||
task.manifest_rsync_uri.as_str(),
|
||||
task.issuer_ca_der.as_ref(),
|
||||
task.issuer_ca.as_ref(),
|
||||
file,
|
||||
shared.manifest_rsync_uri.as_ref(),
|
||||
shared.issuer_ca_der.as_ref(),
|
||||
shared.issuer_ca.as_ref(),
|
||||
&issuer_spki,
|
||||
task.issuer_ca_rsync_uri.as_deref(),
|
||||
task.crl_cache.as_ref(),
|
||||
task.issuer_resources_index.as_ref(),
|
||||
task.issuer_effective_ip.as_ref(),
|
||||
task.issuer_effective_as.as_ref(),
|
||||
shared.issuer_ca_rsync_uri.as_deref(),
|
||||
shared.crl_cache.as_ref(),
|
||||
shared.issuer_resources_index.as_ref(),
|
||||
shared.issuer_effective_ip.as_deref(),
|
||||
shared.issuer_effective_as.as_deref(),
|
||||
task.validation_time,
|
||||
None,
|
||||
task.collect_vcir_local_outputs,
|
||||
@@ -922,8 +933,6 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
worker_index,
|
||||
queue_wait_ms,
|
||||
worker_ms: worker_started.elapsed().as_millis() as u64,
|
||||
rsync_uri: task.file.rsync_uri,
|
||||
sha256_hex,
|
||||
outcome,
|
||||
}
|
||||
}
|
||||
@@ -935,16 +944,7 @@ pub(crate) enum ParallelObjectsPrepare {
|
||||
|
||||
pub(crate) struct ParallelObjectsStage {
|
||||
pub(crate) publication_point_id: u64,
|
||||
pub(crate) locked_files: Vec<PackFile>,
|
||||
pub(crate) manifest_rsync_uri: String,
|
||||
issuer_ca_der: Arc<[u8]>,
|
||||
issuer_ca: Arc<ResourceCertificate>,
|
||||
issuer_spki_der: Arc<[u8]>,
|
||||
issuer_ca_rsync_uri: Option<String>,
|
||||
crl_cache: Arc<Mutex<std::collections::HashMap<String, CachedIssuerCrl>>>,
|
||||
issuer_resources_index: Arc<IssuerResourcesIndex>,
|
||||
issuer_effective_ip: Option<crate::data_model::rc::IpResourceSet>,
|
||||
issuer_effective_as: Option<crate::data_model::rc::AsResourceSet>,
|
||||
shared: Arc<RoaTaskShared>,
|
||||
validation_time: time::OffsetDateTime,
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
@@ -955,31 +955,41 @@ pub(crate) struct ParallelObjectsStage {
|
||||
}
|
||||
|
||||
impl ParallelObjectsStage {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn build_roa_tasks(&self) -> Vec<OwnedRoaTask> {
|
||||
self.locked_files
|
||||
let mut tasks = Vec::with_capacity(self.roa_task_count());
|
||||
self.extend_roa_tasks(|task| tasks.push(task));
|
||||
tasks
|
||||
}
|
||||
|
||||
pub(crate) fn append_roa_tasks_to(
|
||||
&self,
|
||||
pending: &mut std::collections::VecDeque<OwnedRoaTask>,
|
||||
) {
|
||||
self.extend_roa_tasks(|task| pending.push_back(task));
|
||||
}
|
||||
|
||||
fn extend_roa_tasks<F>(&self, mut push: F)
|
||||
where
|
||||
F: FnMut(OwnedRoaTask),
|
||||
{
|
||||
let shared = self.shared.clone();
|
||||
self.locked_files()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, file)| file.rsync_uri.ends_with(".roa"))
|
||||
.map(|(index, file)| OwnedRoaTask {
|
||||
publication_point_id: self.publication_point_id,
|
||||
index,
|
||||
file: file.clone(),
|
||||
manifest_rsync_uri: self.manifest_rsync_uri.clone(),
|
||||
issuer_ca_der: self.issuer_ca_der.clone(),
|
||||
issuer_ca: self.issuer_ca.clone(),
|
||||
issuer_spki_der: self.issuer_spki_der.clone(),
|
||||
issuer_ca_rsync_uri: self.issuer_ca_rsync_uri.clone(),
|
||||
crl_cache: self.crl_cache.clone(),
|
||||
issuer_resources_index: self.issuer_resources_index.clone(),
|
||||
issuer_effective_ip: self.issuer_effective_ip.clone(),
|
||||
issuer_effective_as: self.issuer_effective_as.clone(),
|
||||
validation_time: self.validation_time,
|
||||
collect_vcir_local_outputs: self.collect_vcir_local_outputs,
|
||||
strict_cms_der: self.strict_cms_der,
|
||||
strict_name: self.strict_name,
|
||||
submitted_at: None,
|
||||
})
|
||||
.collect()
|
||||
.for_each(|(index, _)| {
|
||||
push(OwnedRoaTask {
|
||||
publication_point_id: self.publication_point_id,
|
||||
index,
|
||||
shared: shared.clone(),
|
||||
validation_time: self.validation_time,
|
||||
collect_vcir_local_outputs: self.collect_vcir_local_outputs,
|
||||
strict_cms_der: self.strict_cms_der,
|
||||
strict_name: self.strict_name,
|
||||
submitted_at: None,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn roa_task_count(&self) -> usize {
|
||||
@@ -991,7 +1001,11 @@ impl ParallelObjectsStage {
|
||||
}
|
||||
|
||||
pub(crate) fn locked_file_count(&self) -> usize {
|
||||
self.locked_files.len()
|
||||
self.shared.locked_files.len()
|
||||
}
|
||||
|
||||
fn locked_files(&self) -> &[PackFile] {
|
||||
self.shared.locked_files.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,19 +1226,21 @@ pub(crate) fn prepare_publication_point_for_parallel_roa<P: PublicationPointData
|
||||
|
||||
ParallelObjectsPrepare::Staged(ParallelObjectsStage {
|
||||
publication_point_id,
|
||||
locked_files: locked_files.to_vec(),
|
||||
manifest_rsync_uri: manifest_rsync_uri.to_string(),
|
||||
issuer_ca_der: Arc::<[u8]>::from(issuer_ca_der.to_vec()),
|
||||
issuer_spki_der: Arc::<[u8]>::from(issuer_ca.tbs.subject_public_key_info.clone()),
|
||||
issuer_ca: Arc::new(issuer_ca),
|
||||
issuer_ca_rsync_uri: issuer_ca_rsync_uri.map(ToString::to_string),
|
||||
crl_cache: Arc::new(Mutex::new(crl_cache)),
|
||||
issuer_resources_index: Arc::new(build_issuer_resources_index(
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
)),
|
||||
issuer_effective_ip: issuer_effective_ip.cloned(),
|
||||
issuer_effective_as: issuer_effective_as.cloned(),
|
||||
shared: Arc::new(RoaTaskShared {
|
||||
locked_files: Arc::<[PackFile]>::from(locked_files.to_vec()),
|
||||
manifest_rsync_uri: Arc::<str>::from(manifest_rsync_uri),
|
||||
issuer_ca_der: Arc::<[u8]>::from(issuer_ca_der.to_vec()),
|
||||
issuer_spki_der: Arc::<[u8]>::from(issuer_ca.tbs.subject_public_key_info.clone()),
|
||||
issuer_ca: Arc::new(issuer_ca),
|
||||
issuer_ca_rsync_uri: issuer_ca_rsync_uri.map(Arc::<str>::from),
|
||||
crl_cache: Arc::new(Mutex::new(crl_cache)),
|
||||
issuer_resources_index: Arc::new(build_issuer_resources_index(
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
)),
|
||||
issuer_effective_ip: issuer_effective_ip.cloned().map(Arc::new),
|
||||
issuer_effective_as: issuer_effective_as.cloned().map(Arc::new),
|
||||
}),
|
||||
validation_time,
|
||||
collect_vcir_local_outputs,
|
||||
strict_cms_der: policy.strict.cms_der,
|
||||
@@ -1241,18 +1257,20 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
timing: Option<&TimingHandle>,
|
||||
) -> Result<ObjectsOutput, String> {
|
||||
roa_results.sort_by_key(|result| result.index);
|
||||
let mut roa_by_index = roa_results
|
||||
.into_iter()
|
||||
.map(|result| (result.index, result))
|
||||
.collect::<std::collections::HashMap<_, _>>();
|
||||
let mut aspa_crl_cache = stage
|
||||
let mut roa_results = roa_results.into_iter().peekable();
|
||||
let shared = stage.shared.clone();
|
||||
let mut aspa_crl_cache = shared
|
||||
.crl_cache
|
||||
.lock()
|
||||
.expect("parallel ROA CRL cache lock")
|
||||
.clone();
|
||||
let issuer_spki = SubjectPublicKeyInfo::from_der(stage.issuer_spki_der.as_ref())
|
||||
let issuer_spki = SubjectPublicKeyInfo::from_der(shared.issuer_spki_der.as_ref())
|
||||
.map_err(|e| e.to_string())?
|
||||
.1;
|
||||
let collect_vcir_local_outputs = stage.collect_vcir_local_outputs;
|
||||
let validation_time = stage.validation_time;
|
||||
let strict_cms_der = stage.strict_cms_der;
|
||||
let strict_name = stage.strict_name;
|
||||
let mut stats = stage.stats;
|
||||
let mut warnings = stage.warnings;
|
||||
let mut audit = stage.audit;
|
||||
@@ -1260,21 +1278,32 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
let mut aspas: Vec<AspaAttestation> = Vec::new();
|
||||
let mut local_outputs_cache: Vec<VcirLocalOutput> = Vec::new();
|
||||
|
||||
for (idx, file) in stage.locked_files.iter().enumerate() {
|
||||
for (idx, file) in shared.locked_files.iter().enumerate() {
|
||||
if file.rsync_uri.ends_with(".roa") {
|
||||
let result = roa_by_index
|
||||
.remove(&idx)
|
||||
.ok_or_else(|| format!("missing ROA task result for {}", file.rsync_uri))?;
|
||||
let result = match roa_results.peek() {
|
||||
Some(result) if result.index == idx => roa_results
|
||||
.next()
|
||||
.expect("peeked ROA task result must be present"),
|
||||
Some(result) => {
|
||||
return Err(format!(
|
||||
"unexpected ROA task result index {} while reducing {} at index {}",
|
||||
result.index, file.rsync_uri, idx
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(format!("missing ROA task result for {}", file.rsync_uri));
|
||||
}
|
||||
};
|
||||
match result.outcome {
|
||||
Ok(mut ok) => {
|
||||
stats.roa_ok += 1;
|
||||
vrps.append(&mut ok.vrps);
|
||||
if stage.collect_vcir_local_outputs {
|
||||
if collect_vcir_local_outputs {
|
||||
local_outputs_cache.extend(ok.local_outputs);
|
||||
}
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: result.rsync_uri,
|
||||
sha256_hex: result.sha256_hex,
|
||||
rsync_uri: file.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&file.sha256),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
@@ -1282,8 +1311,8 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
}
|
||||
Err(e) => {
|
||||
audit.push(ObjectAuditEntry {
|
||||
rsync_uri: result.rsync_uri.clone(),
|
||||
sha256_hex: result.sha256_hex,
|
||||
rsync_uri: file.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&file.sha256),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(e.to_string()),
|
||||
@@ -1291,9 +1320,9 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
let mut refs = vec![RfcRef("RFC 6488 §3"), RfcRef("RFC 9582 §4-§5")];
|
||||
refs.extend_from_slice(extra_rfc_refs_for_crl_selection(&e));
|
||||
warnings.push(
|
||||
Warning::new(format!("dropping invalid ROA: {}: {e}", result.rsync_uri))
|
||||
Warning::new(format!("dropping invalid ROA: {}: {e}", file.rsync_uri))
|
||||
.with_rfc_refs(&refs)
|
||||
.with_context(&result.rsync_uri),
|
||||
.with_context(&file.rsync_uri),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1301,20 +1330,20 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
let _t = timing.as_ref().map(|t| t.span_phase("objects_aspa_total"));
|
||||
match process_aspa_with_issuer(
|
||||
file,
|
||||
&stage.manifest_rsync_uri,
|
||||
stage.issuer_ca_der.as_ref(),
|
||||
stage.issuer_ca.as_ref(),
|
||||
shared.manifest_rsync_uri.as_ref(),
|
||||
shared.issuer_ca_der.as_ref(),
|
||||
shared.issuer_ca.as_ref(),
|
||||
&issuer_spki,
|
||||
stage.issuer_ca_rsync_uri.as_deref(),
|
||||
shared.issuer_ca_rsync_uri.as_deref(),
|
||||
&mut aspa_crl_cache,
|
||||
stage.issuer_resources_index.as_ref(),
|
||||
stage.issuer_effective_ip.as_ref(),
|
||||
stage.issuer_effective_as.as_ref(),
|
||||
stage.validation_time,
|
||||
shared.issuer_resources_index.as_ref(),
|
||||
shared.issuer_effective_ip.as_deref(),
|
||||
shared.issuer_effective_as.as_deref(),
|
||||
validation_time,
|
||||
timing,
|
||||
stage.collect_vcir_local_outputs,
|
||||
stage.strict_cms_der,
|
||||
stage.strict_name,
|
||||
collect_vcir_local_outputs,
|
||||
strict_cms_der,
|
||||
strict_name,
|
||||
) {
|
||||
Ok((att, local_output)) => {
|
||||
stats.aspa_ok += 1;
|
||||
@@ -1349,6 +1378,12 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(result) = roa_results.next() {
|
||||
return Err(format!(
|
||||
"unexpected trailing ROA task result at index {}",
|
||||
result.index
|
||||
));
|
||||
}
|
||||
|
||||
Ok(ObjectsOutput {
|
||||
vrps,
|
||||
@@ -1388,8 +1423,9 @@ fn process_publication_point_for_issuer_parallel_roa_inner<P: PublicationPointDa
|
||||
ParallelObjectsPrepare::Staged(stage) => stage,
|
||||
};
|
||||
|
||||
let mut pending = std::collections::VecDeque::from(stage.build_roa_tasks());
|
||||
let roa_task_count = stage.roa_task_count();
|
||||
let mut pending = std::collections::VecDeque::with_capacity(roa_task_count);
|
||||
stage.append_roa_tasks_to(&mut pending);
|
||||
let mut worker_pool = pool
|
||||
.pool
|
||||
.lock()
|
||||
@@ -1537,7 +1573,6 @@ pub(crate) fn validate_roa_task_serial(
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
) -> RoaTaskResult {
|
||||
let sha256_hex = sha256_hex_from_32(&task.file.sha256);
|
||||
let outcome = process_roa_with_issuer(
|
||||
task.file,
|
||||
manifest_rsync_uri,
|
||||
@@ -1566,15 +1601,13 @@ pub(crate) fn validate_roa_task_serial(
|
||||
worker_index: 0,
|
||||
queue_wait_ms: 0,
|
||||
worker_ms: 0,
|
||||
rsync_uri: task.file.rsync_uri.clone(),
|
||||
sha256_hex,
|
||||
outcome,
|
||||
}
|
||||
}
|
||||
|
||||
fn process_roa_with_issuer(
|
||||
file: &PackFile,
|
||||
manifest_rsync_uri: &str,
|
||||
_manifest_rsync_uri: &str,
|
||||
issuer_ca_der: &[u8],
|
||||
issuer_ca: &ResourceCertificate,
|
||||
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
||||
@@ -1659,12 +1692,6 @@ fn process_roa_with_issuer(
|
||||
.iter()
|
||||
.map(|vrp| {
|
||||
let prefix = vrp_prefix_to_string(vrp);
|
||||
let payload_json = serde_json::json!({
|
||||
"asn": vrp.asn,
|
||||
"prefix": prefix,
|
||||
"max_length": vrp.max_length,
|
||||
})
|
||||
.to_string();
|
||||
let rule_hash = crate::audit::sha256_hex(
|
||||
format!(
|
||||
"roa-rule:{}:{}:{}:{}",
|
||||
@@ -1673,20 +1700,20 @@ fn process_roa_with_issuer(
|
||||
.as_bytes(),
|
||||
);
|
||||
VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: item_effective_until.clone(),
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: source_object_hash.clone(),
|
||||
source_ee_cert_hash: source_ee_cert_hash.clone(),
|
||||
payload_json,
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
manifest_rsync_uri.to_string(),
|
||||
file.rsync_uri.clone(),
|
||||
source_object_hash.clone(),
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: file.sha256,
|
||||
source_ee_cert_hash: sha256_hex_to_32(&source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: vrp.asn,
|
||||
afi: vrp.prefix.afi,
|
||||
prefix_len: vrp.prefix.prefix_len,
|
||||
addr: vrp.prefix.addr,
|
||||
max_length: vrp.max_length,
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -1696,7 +1723,7 @@ fn process_roa_with_issuer(
|
||||
|
||||
fn process_roa_with_issuer_parallel_cached(
|
||||
file: &PackFile,
|
||||
manifest_rsync_uri: &str,
|
||||
_manifest_rsync_uri: &str,
|
||||
issuer_ca_der: &[u8],
|
||||
issuer_ca: &ResourceCertificate,
|
||||
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
||||
@@ -1787,12 +1814,6 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
.iter()
|
||||
.map(|vrp| {
|
||||
let prefix = vrp_prefix_to_string(vrp);
|
||||
let payload_json = serde_json::json!({
|
||||
"asn": vrp.asn,
|
||||
"prefix": prefix,
|
||||
"max_length": vrp.max_length,
|
||||
})
|
||||
.to_string();
|
||||
let rule_hash = crate::audit::sha256_hex(
|
||||
format!(
|
||||
"roa-rule:{}:{}:{}:{}",
|
||||
@@ -1801,20 +1822,20 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
.as_bytes(),
|
||||
);
|
||||
VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: item_effective_until.clone(),
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: source_object_hash.clone(),
|
||||
source_ee_cert_hash: source_ee_cert_hash.clone(),
|
||||
payload_json,
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
manifest_rsync_uri.to_string(),
|
||||
file.rsync_uri.clone(),
|
||||
source_object_hash.clone(),
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: file.sha256,
|
||||
source_ee_cert_hash: sha256_hex_to_32(&source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: vrp.asn,
|
||||
afi: vrp.prefix.afi,
|
||||
prefix_len: vrp.prefix.prefix_len,
|
||||
addr: vrp.prefix.addr,
|
||||
max_length: vrp.max_length,
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -1824,7 +1845,7 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
|
||||
fn process_aspa_with_issuer(
|
||||
file: &PackFile,
|
||||
manifest_rsync_uri: &str,
|
||||
_manifest_rsync_uri: &str,
|
||||
issuer_ca_der: &[u8],
|
||||
issuer_ca: &ResourceCertificate,
|
||||
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
||||
@@ -1922,24 +1943,17 @@ fn process_aspa_with_issuer(
|
||||
.as_bytes(),
|
||||
);
|
||||
let local_output = VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until,
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_type: "aspa".to_string(),
|
||||
source_object_hash: source_object_hash.clone(),
|
||||
source_ee_cert_hash,
|
||||
payload_json: serde_json::json!({
|
||||
"customer_as_id": attestation.customer_as_id,
|
||||
"provider_as_ids": attestation.provider_as_ids,
|
||||
})
|
||||
.to_string(),
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
manifest_rsync_uri.to_string(),
|
||||
file.rsync_uri.clone(),
|
||||
source_object_hash,
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::Aspa,
|
||||
source_object_hash: file.sha256,
|
||||
source_ee_cert_hash: sha256_hex_to_32(&source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: attestation.customer_as_id,
|
||||
provider_as_ids: attestation.provider_as_ids.clone(),
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
};
|
||||
|
||||
Ok((attestation, Some(local_output)))
|
||||
@@ -2965,6 +2979,54 @@ mod tests {
|
||||
assert_eq!(roa_afi_to_string(RoaAfi::Ipv6), "ipv6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parallel_stage_roa_tasks_share_stage_owned_payloads() {
|
||||
let stage = ParallelObjectsStage {
|
||||
publication_point_id: 7,
|
||||
shared: Arc::new(RoaTaskShared {
|
||||
locked_files: Arc::<[PackFile]>::from(vec![
|
||||
PackFile::from_bytes_with_sha256(
|
||||
"rsync://example.test/repo/a.roa",
|
||||
vec![1, 2, 3],
|
||||
[1u8; 32],
|
||||
),
|
||||
PackFile::from_bytes_with_sha256(
|
||||
"rsync://example.test/repo/b.roa",
|
||||
vec![4, 5, 6],
|
||||
[2u8; 32],
|
||||
),
|
||||
]),
|
||||
manifest_rsync_uri: Arc::<str>::from("rsync://example.test/repo/manifest.mft"),
|
||||
issuer_ca_der: Arc::from([0x01u8].as_slice()),
|
||||
issuer_ca: Arc::new(
|
||||
ResourceCertificate::decode_der(&fixture_bytes(
|
||||
"tests/fixtures/ta/apnic-ta.cer",
|
||||
))
|
||||
.expect("decode fixture CA certificate"),
|
||||
),
|
||||
issuer_spki_der: Arc::from([0x02u8].as_slice()),
|
||||
issuer_ca_rsync_uri: Some(Arc::<str>::from("rsync://example.test/repo/ca.cer")),
|
||||
crl_cache: Arc::new(Mutex::new(HashMap::new())),
|
||||
issuer_resources_index: Arc::new(IssuerResourcesIndex::default()),
|
||||
issuer_effective_ip: None,
|
||||
issuer_effective_as: None,
|
||||
}),
|
||||
validation_time: OffsetDateTime::now_utc(),
|
||||
collect_vcir_local_outputs: false,
|
||||
strict_cms_der: false,
|
||||
strict_name: false,
|
||||
warnings: Vec::new(),
|
||||
stats: ObjectsStats {
|
||||
roa_total: 2,
|
||||
..ObjectsStats::default()
|
||||
},
|
||||
audit: Vec::new(),
|
||||
};
|
||||
let tasks = stage.build_roa_tasks();
|
||||
assert_eq!(tasks.len(), 2);
|
||||
assert!(Arc::ptr_eq(&tasks[0].shared, &tasks[1].shared));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_name_manifest_decode_failure_drops_publication_point() {
|
||||
let publication_point = PublicationPointSnapshot {
|
||||
|
||||
@@ -55,8 +55,37 @@ struct InflightPublicationPoint {
|
||||
}
|
||||
|
||||
struct FinishedPublicationPoint {
|
||||
node: QueuedCaInstance,
|
||||
result: Result<PublicationPointRunResult, String>,
|
||||
node: FinishedPublicationPointNode,
|
||||
result: FinishedPublicationPointResult,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct FinishedPublicationPointNode {
|
||||
id: u64,
|
||||
parent_id: Option<u64>,
|
||||
discovered_from: Option<DiscoveredFrom>,
|
||||
manifest_rsync_uri: String,
|
||||
}
|
||||
|
||||
impl FinishedPublicationPointNode {
|
||||
fn from_queued(node: QueuedCaInstance) -> Self {
|
||||
Self {
|
||||
id: node.id,
|
||||
parent_id: node.parent_id,
|
||||
discovered_from: node.discovered_from,
|
||||
manifest_rsync_uri: node.handle.manifest_rsync_uri,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FinishedPublicationPointResult {
|
||||
Ok {
|
||||
warnings: Vec<Warning>,
|
||||
objects: ObjectsOutput,
|
||||
audit: PublicationPointAudit,
|
||||
},
|
||||
Err(String),
|
||||
}
|
||||
|
||||
struct FinalizeTask {
|
||||
@@ -310,24 +339,28 @@ fn elapsed_ms(started: Instant) -> u64 {
|
||||
fn compact_phase2_finished_result(
|
||||
mut result: PublicationPointRunResult,
|
||||
compact_audit: bool,
|
||||
) -> PublicationPointRunResult {
|
||||
// Phase2 only needs warnings, objects, audit, and traversal metadata after finalize.
|
||||
// Dropping the snapshot here avoids retaining manifest/files/raw-byte caches until run end.
|
||||
result.snapshot = None;
|
||||
) -> FinishedPublicationPointResult {
|
||||
result.objects.audit.clear();
|
||||
result.objects.local_outputs_cache.clear();
|
||||
if compact_audit {
|
||||
result.audit.objects.clear();
|
||||
result.audit.warnings.clear();
|
||||
result.objects.audit.clear();
|
||||
result.discovered_children.clear();
|
||||
}
|
||||
result
|
||||
FinishedPublicationPointResult::Ok {
|
||||
warnings: result.warnings,
|
||||
objects: result.objects,
|
||||
audit: result.audit,
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_phase2_finished_result_result(
|
||||
result: Result<PublicationPointRunResult, String>,
|
||||
compact_audit: bool,
|
||||
) -> Result<PublicationPointRunResult, String> {
|
||||
result.map(|result| compact_phase2_finished_result(result, compact_audit))
|
||||
) -> FinishedPublicationPointResult {
|
||||
match result {
|
||||
Ok(result) => compact_phase2_finished_result(result, compact_audit),
|
||||
Err(err) => FinishedPublicationPointResult::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_tree_parallel_phase2_audit_multi_root(
|
||||
@@ -712,8 +745,8 @@ fn start_queued_ca_instances(
|
||||
}
|
||||
Err(err) => {
|
||||
finished.push(FinishedPublicationPoint {
|
||||
node,
|
||||
result: Err(err),
|
||||
node: FinishedPublicationPointNode::from_queued(node),
|
||||
result: FinishedPublicationPointResult::Err(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -781,7 +814,7 @@ fn stage_ready_publication_point(
|
||||
metrics.child_enqueue_ms = elapsed_ms(child_enqueue_started);
|
||||
}
|
||||
finished.push(FinishedPublicationPoint {
|
||||
node: ready.node,
|
||||
node: FinishedPublicationPointNode::from_queued(ready.node),
|
||||
result: compact_phase2_finished_result_result(fallback, compact_audit),
|
||||
});
|
||||
metrics.total_ms = elapsed_ms(publication_point_started);
|
||||
@@ -897,13 +930,10 @@ fn stage_ready_publication_point(
|
||||
metrics.locked_files = objects_stage.locked_file_count();
|
||||
metrics.aspa_objects = objects_stage.aspa_task_count();
|
||||
let build_tasks_started = Instant::now();
|
||||
let tasks = objects_stage.build_roa_tasks();
|
||||
objects_stage.append_roa_tasks_to(pending_roa_dispatch);
|
||||
metrics.build_roa_tasks_ms = elapsed_ms(build_tasks_started);
|
||||
let task_count = objects_stage.roa_task_count();
|
||||
metrics.roa_tasks = task_count;
|
||||
for task in tasks {
|
||||
pending_roa_dispatch.push_back(task);
|
||||
}
|
||||
if task_count == 0 {
|
||||
metrics.zero_task_count = 1;
|
||||
match reduce_parallel_roa_stage(objects_stage, Vec::new(), runner.timing.as_ref()) {
|
||||
@@ -929,8 +959,8 @@ fn stage_ready_publication_point(
|
||||
);
|
||||
}
|
||||
Err(err) => finished.push(FinishedPublicationPoint {
|
||||
node: ready.node,
|
||||
result: Err(err),
|
||||
node: FinishedPublicationPointNode::from_queued(ready.node),
|
||||
result: FinishedPublicationPointResult::Err(err),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
@@ -1022,7 +1052,7 @@ fn finalize_ready_objects(
|
||||
)
|
||||
.map(|out| out.result);
|
||||
finished.push(FinishedPublicationPoint {
|
||||
node,
|
||||
node: FinishedPublicationPointNode::from_queued(node),
|
||||
result: compact_phase2_finished_result_result(result, compact_audit),
|
||||
});
|
||||
}
|
||||
@@ -1400,7 +1430,7 @@ fn finalize_publication_point_state(
|
||||
elapsed_ms(finalize_started),
|
||||
)
|
||||
}
|
||||
Err(err) => (Err(err), 0),
|
||||
Err(err) => (FinishedPublicationPointResult::Err(err), 0),
|
||||
};
|
||||
let finalize_worker_ms = elapsed_ms(finalize_worker_started);
|
||||
crate::progress_log::emit(
|
||||
@@ -1438,7 +1468,10 @@ fn finalize_publication_point_state(
|
||||
}),
|
||||
);
|
||||
FinalizeWorkerResult {
|
||||
finished: FinishedPublicationPoint { node, result },
|
||||
finished: FinishedPublicationPoint {
|
||||
node: FinishedPublicationPointNode::from_queued(node),
|
||||
result,
|
||||
},
|
||||
metrics: FinalizePublicationPointMetrics {
|
||||
reduce_ms,
|
||||
finalize_ms,
|
||||
@@ -1541,25 +1574,29 @@ fn build_tree_output(mut finished: Vec<FinishedPublicationPoint>) -> TreeRunAudi
|
||||
|
||||
for item in finished {
|
||||
match item.result {
|
||||
Ok(result) => {
|
||||
FinishedPublicationPointResult::Ok {
|
||||
warnings: result_warnings,
|
||||
objects,
|
||||
audit,
|
||||
} => {
|
||||
instances_processed += 1;
|
||||
warnings.extend(result.warnings);
|
||||
warnings.extend(result.objects.warnings);
|
||||
vrps.extend(result.objects.vrps);
|
||||
aspas.extend(result.objects.aspas);
|
||||
router_keys.extend(result.objects.router_keys);
|
||||
warnings.extend(result_warnings);
|
||||
warnings.extend(objects.warnings);
|
||||
vrps.extend(objects.vrps);
|
||||
aspas.extend(objects.aspas);
|
||||
router_keys.extend(objects.router_keys);
|
||||
|
||||
let mut audit: PublicationPointAudit = result.audit;
|
||||
let mut audit: PublicationPointAudit = audit;
|
||||
audit.node_id = Some(item.node.id);
|
||||
audit.parent_node_id = item.node.parent_id;
|
||||
audit.discovered_from = item.node.discovered_from;
|
||||
publication_points.push(audit);
|
||||
}
|
||||
Err(err) => {
|
||||
FinishedPublicationPointResult::Err(err) => {
|
||||
instances_failed += 1;
|
||||
warnings.push(
|
||||
Warning::new(format!("publication point failed: {err}"))
|
||||
.with_context(&item.node.handle.manifest_rsync_uri),
|
||||
.with_context(&item.node.manifest_rsync_uri),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1588,7 +1625,10 @@ pub fn run_tree_parallel_phase2_audit(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{compact_phase2_finished_result, compact_phase2_finished_result_result};
|
||||
use super::{
|
||||
FinishedPublicationPointResult, compact_phase2_finished_result,
|
||||
compact_phase2_finished_result_result,
|
||||
};
|
||||
use crate::audit::PublicationPointAudit;
|
||||
use crate::storage::PackTime;
|
||||
use crate::validation::manifest::PublicationPointSource;
|
||||
@@ -1638,9 +1678,12 @@ mod tests {
|
||||
#[test]
|
||||
fn compact_phase2_finished_result_drops_snapshot() {
|
||||
let result = compact_phase2_finished_result(sample_result(), false);
|
||||
assert!(result.snapshot.is_none());
|
||||
assert_eq!(result.source, PublicationPointSource::Fresh);
|
||||
assert!(result.discovered_children.is_empty());
|
||||
match result {
|
||||
FinishedPublicationPointResult::Ok { warnings, .. } => {
|
||||
assert!(warnings.is_empty());
|
||||
}
|
||||
FinishedPublicationPointResult::Err(err) => panic!("unexpected error: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1666,15 +1709,21 @@ mod tests {
|
||||
detail: None,
|
||||
});
|
||||
let result = compact_phase2_finished_result(sample, true);
|
||||
assert!(result.audit.objects.is_empty());
|
||||
assert!(result.audit.warnings.is_empty());
|
||||
assert!(result.objects.audit.is_empty());
|
||||
match result {
|
||||
FinishedPublicationPointResult::Ok { objects, audit, .. } => {
|
||||
assert!(audit.objects.is_empty());
|
||||
assert!(audit.warnings.is_empty());
|
||||
assert!(objects.audit.is_empty());
|
||||
}
|
||||
FinishedPublicationPointResult::Err(err) => panic!("unexpected error: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_phase2_finished_result_result_preserves_err() {
|
||||
let err = compact_phase2_finished_result_result(Err("boom".to_string()), false)
|
||||
.expect_err("error should be preserved");
|
||||
assert_eq!(err, "boom");
|
||||
match compact_phase2_finished_result_result(Err("boom".to_string()), false) {
|
||||
FinishedPublicationPointResult::Err(err) => assert_eq!(err, "boom"),
|
||||
FinishedPublicationPointResult::Ok { .. } => panic!("error should be preserved"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult, VcirArtifactKind,
|
||||
VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary, VcirCcrManifestProjection,
|
||||
VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirOutputType, VcirRelatedArtifact,
|
||||
VcirSummary,
|
||||
VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirLocalOutputPayload, VcirOutputType,
|
||||
VcirRelatedArtifact, VcirReplaceTimingBreakdown, VcirSourceObjectType, VcirSummary,
|
||||
};
|
||||
use crate::sync::repo::{
|
||||
sync_publication_point, sync_publication_point_replay, sync_publication_point_replay_delta,
|
||||
@@ -58,13 +58,17 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use base64::Engine as _;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use x509_parser::prelude::FromDer;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
|
||||
use vcir_der::encode_access_description_der_for_vcir_ccr_projection;
|
||||
|
||||
fn sha256_hex_to_32(hex_value: &str) -> [u8; 32] {
|
||||
let mut out = [0u8; 32];
|
||||
hex::decode_to_slice(hex_value, &mut out).expect("internal sha256 hex should decode");
|
||||
out
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct BuildVcirTimingBreakdown {
|
||||
pub(crate) select_crl_ms: u64,
|
||||
@@ -80,9 +84,9 @@ pub(crate) struct PersistVcirTimingBreakdown {
|
||||
pub(crate) embedded_collect_ms: u64,
|
||||
pub(crate) embedded_store_ms: u64,
|
||||
pub(crate) build_vcir_ms: u64,
|
||||
pub(crate) previous_load_ms: u64,
|
||||
pub(crate) replace_vcir_ms: u64,
|
||||
pub(crate) build_vcir: BuildVcirTimingBreakdown,
|
||||
pub(crate) replace_vcir: VcirReplaceTimingBreakdown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -311,7 +315,7 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
self.store,
|
||||
ca,
|
||||
&pack,
|
||||
&objects,
|
||||
&mut objects,
|
||||
&warnings,
|
||||
&child_audits,
|
||||
&discovered_children,
|
||||
@@ -666,7 +670,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
self.validation_time,
|
||||
self.timing.as_ref(),
|
||||
phase2_pool,
|
||||
self.persist_vcir,
|
||||
false,
|
||||
)
|
||||
} else if let Some(phase2_config) = self.parallel_phase2_config.as_ref() {
|
||||
process_publication_point_for_issuer_parallel_roa_with_options(
|
||||
@@ -679,7 +683,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
self.validation_time,
|
||||
self.timing.as_ref(),
|
||||
phase2_config,
|
||||
self.persist_vcir,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
crate::validation::objects::process_publication_point_for_issuer_with_options(
|
||||
@@ -691,7 +695,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
ca.effective_as_resources.as_ref(),
|
||||
self.validation_time,
|
||||
self.timing.as_ref(),
|
||||
self.persist_vcir,
|
||||
false,
|
||||
)
|
||||
}
|
||||
};
|
||||
@@ -747,7 +751,6 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"persist_embedded_collect_ms": persist_vcir_timing.embedded_collect_ms,
|
||||
"persist_embedded_store_ms": persist_vcir_timing.embedded_store_ms,
|
||||
"persist_build_vcir_ms": persist_vcir_timing.build_vcir_ms,
|
||||
"persist_previous_load_ms": persist_vcir_timing.previous_load_ms,
|
||||
"persist_replace_vcir_ms": persist_vcir_timing.replace_vcir_ms,
|
||||
"persist_select_crl_ms": persist_vcir_timing.build_vcir.select_crl_ms,
|
||||
"persist_current_ca_decode_ms": persist_vcir_timing.build_vcir.current_ca_decode_ms,
|
||||
@@ -755,6 +758,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"persist_child_entries_ms": persist_vcir_timing.build_vcir.child_entries_ms,
|
||||
"persist_related_artifacts_ms": persist_vcir_timing.build_vcir.related_artifacts_ms,
|
||||
"persist_vcir_struct_ms": persist_vcir_timing.build_vcir.struct_build_ms,
|
||||
"persist_replace_breakdown": &persist_vcir_timing.replace_vcir,
|
||||
"audit_build_ms": audit_build_ms,
|
||||
"warning_count": result.warnings.len(),
|
||||
"vrp_count": result.objects.vrps.len(),
|
||||
@@ -790,7 +794,6 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"persist_embedded_collect_ms": persist_vcir_timing.embedded_collect_ms,
|
||||
"persist_embedded_store_ms": persist_vcir_timing.embedded_store_ms,
|
||||
"persist_build_vcir_ms": persist_vcir_timing.build_vcir_ms,
|
||||
"persist_previous_load_ms": persist_vcir_timing.previous_load_ms,
|
||||
"persist_replace_vcir_ms": persist_vcir_timing.replace_vcir_ms,
|
||||
"persist_select_crl_ms": persist_vcir_timing.build_vcir.select_crl_ms,
|
||||
"persist_current_ca_decode_ms": persist_vcir_timing.build_vcir.current_ca_decode_ms,
|
||||
@@ -798,6 +801,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"persist_child_entries_ms": persist_vcir_timing.build_vcir.child_entries_ms,
|
||||
"persist_related_artifacts_ms": persist_vcir_timing.build_vcir.related_artifacts_ms,
|
||||
"persist_vcir_struct_ms": persist_vcir_timing.build_vcir.struct_build_ms,
|
||||
"persist_replace_breakdown": &persist_vcir_timing.replace_vcir,
|
||||
"audit_build_ms": audit_build_ms,
|
||||
}),
|
||||
);
|
||||
@@ -1012,26 +1016,6 @@ struct VcirReuseProjection {
|
||||
warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct VcirVrpPayload {
|
||||
asn: u32,
|
||||
prefix: String,
|
||||
max_length: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct VcirAspaPayload {
|
||||
customer_as_id: u32,
|
||||
provider_as_ids: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct VcirRouterKeyPayload {
|
||||
as_id: u32,
|
||||
ski_hex: String,
|
||||
spki_der_base64: String,
|
||||
}
|
||||
|
||||
fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
||||
issuer: &CaInstanceHandle,
|
||||
publication_point: &P,
|
||||
@@ -2299,7 +2283,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: audit_kind_for_vcir_output_type(local.output_type),
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(
|
||||
@@ -2315,7 +2299,7 @@ fn build_objects_output_from_vcir(
|
||||
.entry(local.source_object_uri.clone())
|
||||
.or_insert_with(|| ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: audit_kind_for_vcir_output_type(local.output_type),
|
||||
result: AuditObjectResult::Skipped,
|
||||
detail: Some("skipped: cached local output expired".to_string()),
|
||||
@@ -2332,7 +2316,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
@@ -2348,7 +2332,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!("cached ROA local output parse failed: {e}")),
|
||||
@@ -2364,7 +2348,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::Aspa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
@@ -2380,7 +2364,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::Aspa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!("cached ASPA local output parse failed: {e}")),
|
||||
@@ -2395,7 +2379,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: Some("cached Router Key local output restored".to_string()),
|
||||
@@ -2411,7 +2395,7 @@ fn build_objects_output_from_vcir(
|
||||
local.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: local.source_object_uri.clone(),
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
sha256_hex: local.source_object_hash_hex(),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
@@ -2435,68 +2419,55 @@ fn build_objects_output_from_vcir(
|
||||
}
|
||||
|
||||
fn parse_vcir_vrp_output(local: &VcirLocalOutput) -> Result<Vrp, String> {
|
||||
let payload: VcirVrpPayload = serde_json::from_str(&local.payload_json)
|
||||
.map_err(|e| format!("invalid VRP payload JSON: {e}"))?;
|
||||
Ok(Vrp {
|
||||
asn: payload.asn,
|
||||
prefix: parse_vcir_prefix(&payload.prefix)?,
|
||||
max_length: payload.max_length,
|
||||
})
|
||||
match &local.payload {
|
||||
VcirLocalOutputPayload::Vrp {
|
||||
asn,
|
||||
afi,
|
||||
prefix_len,
|
||||
addr,
|
||||
max_length,
|
||||
} => Ok(Vrp {
|
||||
asn: *asn,
|
||||
prefix: crate::data_model::roa::IpPrefix {
|
||||
afi: *afi,
|
||||
prefix_len: *prefix_len,
|
||||
addr: *addr,
|
||||
},
|
||||
max_length: *max_length,
|
||||
}),
|
||||
_ => Err("VCIR local output payload is not VRP".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_vcir_aspa_output(local: &VcirLocalOutput) -> Result<AspaAttestation, String> {
|
||||
let payload: VcirAspaPayload = serde_json::from_str(&local.payload_json)
|
||||
.map_err(|e| format!("invalid ASPA payload JSON: {e}"))?;
|
||||
Ok(AspaAttestation {
|
||||
customer_as_id: payload.customer_as_id,
|
||||
provider_as_ids: payload.provider_as_ids,
|
||||
})
|
||||
match &local.payload {
|
||||
VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id,
|
||||
provider_as_ids,
|
||||
} => Ok(AspaAttestation {
|
||||
customer_as_id: *customer_as_id,
|
||||
provider_as_ids: provider_as_ids.clone(),
|
||||
}),
|
||||
_ => Err("VCIR local output payload is not ASPA".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_vcir_router_key_output(local: &VcirLocalOutput) -> Result<RouterKeyPayload, String> {
|
||||
let payload: VcirRouterKeyPayload = serde_json::from_str(&local.payload_json)
|
||||
.map_err(|e| format!("invalid Router Key payload JSON: {e}"))?;
|
||||
let ski =
|
||||
hex::decode(&payload.ski_hex).map_err(|e| format!("invalid Router Key SKI hex: {e}"))?;
|
||||
let spki_der = base64::engine::general_purpose::STANDARD
|
||||
.decode(&payload.spki_der_base64)
|
||||
.map_err(|e| format!("invalid Router Key SPKI base64: {e}"))?;
|
||||
Ok(RouterKeyPayload {
|
||||
as_id: payload.as_id,
|
||||
ski,
|
||||
spki_der,
|
||||
source_object_uri: local.source_object_uri.clone(),
|
||||
source_object_hash: local.source_object_hash.clone(),
|
||||
source_ee_cert_hash: local.source_ee_cert_hash.clone(),
|
||||
item_effective_until: local.item_effective_until.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_vcir_prefix(prefix: &str) -> Result<crate::data_model::roa::IpPrefix, String> {
|
||||
let (addr, len) = prefix
|
||||
.split_once('/')
|
||||
.ok_or_else(|| format!("prefix missing '/': {prefix}"))?;
|
||||
let prefix_len = len
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("invalid prefix length '{len}': {e}"))?;
|
||||
let ip = addr
|
||||
.parse::<std::net::IpAddr>()
|
||||
.map_err(|e| format!("invalid IP address '{addr}': {e}"))?;
|
||||
match ip {
|
||||
std::net::IpAddr::V4(v4) => Ok(crate::data_model::roa::IpPrefix {
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len,
|
||||
addr: {
|
||||
let mut addr = [0u8; 16];
|
||||
addr[..4].copy_from_slice(&v4.octets());
|
||||
addr
|
||||
},
|
||||
}),
|
||||
std::net::IpAddr::V6(v6) => Ok(crate::data_model::roa::IpPrefix {
|
||||
afi: RoaAfi::Ipv6,
|
||||
prefix_len,
|
||||
addr: v6.octets(),
|
||||
match &local.payload {
|
||||
VcirLocalOutputPayload::RouterKey {
|
||||
as_id,
|
||||
ski,
|
||||
spki_der,
|
||||
} => Ok(RouterKeyPayload {
|
||||
as_id: *as_id,
|
||||
ski: ski.clone(),
|
||||
spki_der: spki_der.clone(),
|
||||
source_object_uri: local.source_object_uri.clone(),
|
||||
source_object_hash: local.source_object_hash_hex(),
|
||||
source_ee_cert_hash: local.source_ee_cert_hash_hex(),
|
||||
item_effective_until: local.item_effective_until.clone(),
|
||||
}),
|
||||
_ => Err("VCIR local output payload is not Router Key".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2582,7 +2553,7 @@ fn persist_vcir_for_fresh_result(
|
||||
store: &RocksStore,
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
objects: &mut crate::validation::objects::ObjectsOutput,
|
||||
warnings: &[Warning],
|
||||
child_audits: &[ObjectAuditEntry],
|
||||
discovered_children: &[DiscoveredChildCaInstance],
|
||||
@@ -2605,7 +2576,7 @@ fn persist_vcir_for_fresh_result_with_timing(
|
||||
store: &RocksStore,
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
objects: &mut crate::validation::objects::ObjectsOutput,
|
||||
warnings: &[Warning],
|
||||
child_audits: &[ObjectAuditEntry],
|
||||
discovered_children: &[DiscoveredChildCaInstance],
|
||||
@@ -2635,17 +2606,12 @@ fn persist_vcir_for_fresh_result_with_timing(
|
||||
timing.build_vcir_ms = build_vcir_started.elapsed().as_millis() as u64;
|
||||
timing.build_vcir = build_vcir_timing;
|
||||
|
||||
let previous_load_started = std::time::Instant::now();
|
||||
let previous = store
|
||||
.get_vcir(&pack.manifest_rsync_uri)
|
||||
.map_err(|e| format!("load existing VCIR failed: {e}"))?;
|
||||
timing.previous_load_ms = previous_load_started.elapsed().as_millis() as u64;
|
||||
|
||||
let replace_vcir_started = std::time::Instant::now();
|
||||
store
|
||||
.replace_vcir_and_audit_rule_indexes(previous.as_ref(), &vcir)
|
||||
.map_err(|e| format!("store VCIR and audit rule index failed: {e}"))?;
|
||||
let replace_timing = store
|
||||
.replace_vcir_and_manifest_replay_meta(&vcir)
|
||||
.map_err(|e| format!("store VCIR and manifest replay meta failed: {e}"))?;
|
||||
timing.replace_vcir_ms = replace_vcir_started.elapsed().as_millis() as u64;
|
||||
timing.replace_vcir = replace_timing;
|
||||
|
||||
Ok(timing)
|
||||
}
|
||||
@@ -2653,7 +2619,7 @@ fn persist_vcir_for_fresh_result_with_timing(
|
||||
fn build_vcir_from_fresh_result(
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
objects: &mut crate::validation::objects::ObjectsOutput,
|
||||
warnings: &[Warning],
|
||||
child_audits: &[ObjectAuditEntry],
|
||||
discovered_children: &[DiscoveredChildCaInstance],
|
||||
@@ -2674,7 +2640,7 @@ fn build_vcir_from_fresh_result(
|
||||
fn build_vcir_from_fresh_result_with_timing(
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
objects: &mut crate::validation::objects::ObjectsOutput,
|
||||
warnings: &[Warning],
|
||||
child_audits: &[ObjectAuditEntry],
|
||||
discovered_children: &[DiscoveredChildCaInstance],
|
||||
@@ -2692,7 +2658,7 @@ fn build_vcir_from_fresh_result_with_timing(
|
||||
timing.current_ca_decode_ms = current_ca_decode_started.elapsed().as_millis() as u64;
|
||||
|
||||
let local_outputs_started = std::time::Instant::now();
|
||||
let local_outputs = build_vcir_local_outputs(ca, pack, objects)?;
|
||||
let local_outputs = take_or_build_vcir_local_outputs(ca, pack, objects)?;
|
||||
timing.local_outputs_ms = local_outputs_started.elapsed().as_millis() as u64;
|
||||
|
||||
let child_entries_started = std::time::Instant::now();
|
||||
@@ -2710,6 +2676,18 @@ fn build_vcir_from_fresh_result_with_timing(
|
||||
timing.related_artifacts_ms = related_artifacts_started.elapsed().as_millis() as u64;
|
||||
let ccr_manifest_projection =
|
||||
build_vcir_ccr_manifest_projection_from_fresh(ca, pack, &child_entries)?;
|
||||
let local_vrp_count = local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::Vrp)
|
||||
.count() as u32;
|
||||
let local_aspa_count = local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::Aspa)
|
||||
.count() as u32;
|
||||
let local_router_key_count = local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::RouterKey)
|
||||
.count() as u32;
|
||||
let accepted_object_count = related_artifacts
|
||||
.iter()
|
||||
.filter(|artifact| artifact.validation_status == VcirArtifactValidationStatus::Accepted)
|
||||
@@ -2768,21 +2746,12 @@ fn build_vcir_from_fresh_result_with_timing(
|
||||
),
|
||||
},
|
||||
child_entries,
|
||||
local_outputs: local_outputs.clone(),
|
||||
related_artifacts: related_artifacts.clone(),
|
||||
local_outputs,
|
||||
related_artifacts,
|
||||
summary: VcirSummary {
|
||||
local_vrp_count: local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::Vrp)
|
||||
.count() as u32,
|
||||
local_aspa_count: local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::Aspa)
|
||||
.count() as u32,
|
||||
local_router_key_count: local_outputs
|
||||
.iter()
|
||||
.filter(|output| output.output_type == VcirOutputType::RouterKey)
|
||||
.count() as u32,
|
||||
local_vrp_count,
|
||||
local_aspa_count,
|
||||
local_router_key_count,
|
||||
child_count: discovered_children.len() as u32,
|
||||
accepted_object_count,
|
||||
rejected_object_count,
|
||||
@@ -2799,6 +2768,17 @@ fn build_vcir_from_fresh_result_with_timing(
|
||||
Ok((vcir, timing))
|
||||
}
|
||||
|
||||
fn take_or_build_vcir_local_outputs(
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &mut crate::validation::objects::ObjectsOutput,
|
||||
) -> Result<Vec<VcirLocalOutput>, String> {
|
||||
if !objects.local_outputs_cache.is_empty() {
|
||||
return Ok(std::mem::take(&mut objects.local_outputs_cache));
|
||||
}
|
||||
build_vcir_local_outputs(ca, pack, objects)
|
||||
}
|
||||
|
||||
fn build_vcir_ccr_manifest_projection_from_fresh(
|
||||
ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
@@ -2892,7 +2872,7 @@ fn select_manifest_current_crl_from_snapshot(
|
||||
}
|
||||
|
||||
fn build_vcir_local_outputs(
|
||||
ca: &CaInstanceHandle,
|
||||
_ca: &CaInstanceHandle,
|
||||
pack: &PublicationPointSnapshot,
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
) -> Result<Vec<VcirLocalOutput>, String> {
|
||||
@@ -2930,12 +2910,6 @@ fn build_vcir_local_outputs(
|
||||
PackTime::from_utc_offset_datetime(ee.resource_cert.tbs.validity_not_after);
|
||||
for vrp in roa_to_vrps_for_vcir(&roa) {
|
||||
let prefix = vrp_prefix_to_string(&vrp);
|
||||
let payload_json = json!({
|
||||
"asn": vrp.asn,
|
||||
"prefix": prefix,
|
||||
"max_length": vrp.max_length,
|
||||
})
|
||||
.to_string();
|
||||
let rule_hash = sha256_hex(
|
||||
format!(
|
||||
"roa-rule:{}:{}:{}:{}",
|
||||
@@ -2944,20 +2918,20 @@ fn build_vcir_local_outputs(
|
||||
.as_bytes(),
|
||||
);
|
||||
out.push(VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: item_effective_until.clone(),
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: source_object_hash.clone(),
|
||||
source_ee_cert_hash: source_ee_cert_hash.clone(),
|
||||
payload_json,
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
ca.manifest_rsync_uri.clone(),
|
||||
file.rsync_uri.clone(),
|
||||
source_object_hash.clone(),
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: file.sha256,
|
||||
source_ee_cert_hash: sha256_hex_to_32(&source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: vrp.asn,
|
||||
afi: vrp.prefix.afi,
|
||||
prefix_len: vrp.prefix.prefix_len,
|
||||
addr: vrp.prefix.addr,
|
||||
max_length: vrp.max_length,
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
});
|
||||
}
|
||||
} else if accepted_aspa_uris.contains(file.rsync_uri.as_str()) {
|
||||
@@ -2970,11 +2944,6 @@ fn build_vcir_local_outputs(
|
||||
let source_ee_cert_hash = sha256_hex(ee.raw_der.as_slice());
|
||||
let item_effective_until =
|
||||
PackTime::from_utc_offset_datetime(ee.resource_cert.tbs.validity_not_after);
|
||||
let payload_json = json!({
|
||||
"customer_as_id": aspa.aspa.customer_as_id,
|
||||
"provider_as_ids": aspa.aspa.provider_as_ids,
|
||||
})
|
||||
.to_string();
|
||||
let providers = aspa
|
||||
.aspa
|
||||
.provider_as_ids
|
||||
@@ -2990,20 +2959,17 @@ fn build_vcir_local_outputs(
|
||||
.as_bytes(),
|
||||
);
|
||||
out.push(VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until,
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_type: "aspa".to_string(),
|
||||
source_object_hash: source_object_hash.clone(),
|
||||
source_ee_cert_hash,
|
||||
payload_json,
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
ca.manifest_rsync_uri.clone(),
|
||||
file.rsync_uri.clone(),
|
||||
source_object_hash,
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::Aspa,
|
||||
source_object_hash: file.sha256,
|
||||
source_ee_cert_hash: sha256_hex_to_32(&source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: aspa.aspa.customer_as_id,
|
||||
provider_as_ids: aspa.aspa.provider_as_ids.clone(),
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3011,7 +2977,7 @@ fn build_vcir_local_outputs(
|
||||
}
|
||||
|
||||
pub(crate) fn build_router_key_local_outputs(
|
||||
ca: &CaInstanceHandle,
|
||||
_ca: &CaInstanceHandle,
|
||||
router_keys: &[RouterKeyPayload],
|
||||
) -> Vec<VcirLocalOutput> {
|
||||
router_keys
|
||||
@@ -3028,25 +2994,18 @@ pub(crate) fn build_router_key_local_outputs(
|
||||
.as_bytes(),
|
||||
);
|
||||
VcirLocalOutput {
|
||||
output_id: rule_hash.clone(),
|
||||
output_type: VcirOutputType::RouterKey,
|
||||
item_effective_until: router_key.item_effective_until.clone(),
|
||||
source_object_uri: router_key.source_object_uri.clone(),
|
||||
source_object_type: "router_key".to_string(),
|
||||
source_object_hash: router_key.source_object_hash.clone(),
|
||||
source_ee_cert_hash: router_key.source_ee_cert_hash.clone(),
|
||||
payload_json: json!({
|
||||
"as_id": router_key.as_id,
|
||||
"ski_hex": ski_hex,
|
||||
"spki_der_base64": spki_der_base64,
|
||||
})
|
||||
.to_string(),
|
||||
rule_hash,
|
||||
validation_path_hint: vec![
|
||||
ca.manifest_rsync_uri.clone(),
|
||||
router_key.source_object_uri.clone(),
|
||||
router_key.source_object_hash.clone(),
|
||||
],
|
||||
source_object_type: VcirSourceObjectType::RouterKey,
|
||||
source_object_hash: sha256_hex_to_32(&router_key.source_object_hash),
|
||||
source_ee_cert_hash: sha256_hex_to_32(&router_key.source_ee_cert_hash),
|
||||
payload: VcirLocalOutputPayload::RouterKey {
|
||||
as_id: router_key.as_id,
|
||||
ski: router_key.ski.clone(),
|
||||
spki_der: router_key.spki_der.clone(),
|
||||
},
|
||||
rule_hash: sha256_hex_to_32(&rule_hash),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use super::*;
|
||||
use crate::data_model::rc::ResourceCertificate;
|
||||
use crate::data_model::roa::RoaAfi;
|
||||
use crate::fetch::rsync::LocalDirRsyncFetcher;
|
||||
use crate::fetch::rsync::{RsyncFetchError, RsyncFetcher};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult,
|
||||
ValidatedManifestMeta, VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus,
|
||||
VcirAuditSummary, VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirOutputType,
|
||||
VcirRelatedArtifact, VcirSummary,
|
||||
VcirAuditSummary, VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirLocalOutputPayload,
|
||||
VcirOutputType, VcirRelatedArtifact, VcirSourceObjectType, VcirSummary,
|
||||
};
|
||||
use crate::sync::rrdp::Fetcher;
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
@@ -16,6 +17,16 @@ use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
fn sha256_32(input: &[u8]) -> [u8; 32] {
|
||||
sha256_hex_to_32(&sha256_hex(input))
|
||||
}
|
||||
|
||||
fn ipv4_addr(octets: [u8; 4]) -> [u8; 16] {
|
||||
let mut addr = [0u8; 16];
|
||||
addr[..4].copy_from_slice(&octets);
|
||||
addr
|
||||
}
|
||||
|
||||
struct NeverHttpFetcher;
|
||||
impl Fetcher for NeverHttpFetcher {
|
||||
fn fetch(&self, _uri: &str) -> Result<Vec<u8>, String> {
|
||||
@@ -534,138 +545,147 @@ fn sample_vcir_for_projection(
|
||||
subordinate_skis: vec![vec![0x33; 20]],
|
||||
};
|
||||
ValidatedCaInstanceResult {
|
||||
manifest_rsync_uri: manifest_uri.clone(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
tal_id: "test-tal".to_string(),
|
||||
ca_subject_name: "CN=Issuer".to_string(),
|
||||
ca_ski: "11".repeat(20),
|
||||
issuer_ski: "22".repeat(20),
|
||||
last_successful_validation_time: PackTime::from_utc_offset_datetime(now),
|
||||
current_manifest_rsync_uri: manifest_uri.clone(),
|
||||
current_crl_rsync_uri: current_crl_uri.clone(),
|
||||
validated_manifest_meta: ValidatedManifestMeta {
|
||||
validated_manifest_number: vec![1],
|
||||
validated_manifest_this_update: PackTime::from_utc_offset_datetime(now),
|
||||
validated_manifest_next_update: gate_until.clone(),
|
||||
manifest_rsync_uri: manifest_uri.clone(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
tal_id: "test-tal".to_string(),
|
||||
ca_subject_name: "CN=Issuer".to_string(),
|
||||
ca_ski: "11".repeat(20),
|
||||
issuer_ski: "22".repeat(20),
|
||||
last_successful_validation_time: PackTime::from_utc_offset_datetime(now),
|
||||
current_manifest_rsync_uri: manifest_uri.clone(),
|
||||
current_crl_rsync_uri: current_crl_uri.clone(),
|
||||
validated_manifest_meta: ValidatedManifestMeta {
|
||||
validated_manifest_number: vec![1],
|
||||
validated_manifest_this_update: PackTime::from_utc_offset_datetime(now),
|
||||
validated_manifest_next_update: gate_until.clone(),
|
||||
},
|
||||
ccr_manifest_projection,
|
||||
instance_gate: VcirInstanceGate {
|
||||
manifest_next_update: gate_until.clone(),
|
||||
current_crl_next_update: gate_until.clone(),
|
||||
self_ca_not_after: PackTime::from_utc_offset_datetime(now + time::Duration::hours(2)),
|
||||
instance_effective_until: gate_until.clone(),
|
||||
},
|
||||
child_entries: vec![VcirChildEntry {
|
||||
child_manifest_rsync_uri: child_manifest_uri,
|
||||
child_cert_rsync_uri: child_cert_uri.clone(),
|
||||
child_cert_hash: child_cert_hash.to_string(),
|
||||
child_ski: "33".repeat(20),
|
||||
child_rsync_base_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_publication_point_rsync_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_rrdp_notification_uri: Some("https://example.test/child-notify.xml".to_string()),
|
||||
child_effective_ip_resources: None,
|
||||
child_effective_as_resources: None,
|
||||
accepted_at_validation_time: PackTime::from_utc_offset_datetime(now),
|
||||
}],
|
||||
local_outputs: vec![
|
||||
VcirLocalOutput {
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(
|
||||
now + time::Duration::minutes(30),
|
||||
),
|
||||
source_object_uri: roa_uri.clone(),
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: sha256_hex_to_32(&roa_hash),
|
||||
source_ee_cert_hash: sha256_hex_to_32(&ee_hash),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: 64496,
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 24,
|
||||
addr: ipv4_addr([203, 0, 113, 0]),
|
||||
max_length: 24,
|
||||
},
|
||||
rule_hash: sha256_32(b"roa-rule"),
|
||||
},
|
||||
ccr_manifest_projection,
|
||||
instance_gate: VcirInstanceGate {
|
||||
manifest_next_update: gate_until.clone(),
|
||||
current_crl_next_update: gate_until.clone(),
|
||||
self_ca_not_after: PackTime::from_utc_offset_datetime(now + time::Duration::hours(2)),
|
||||
instance_effective_until: gate_until.clone(),
|
||||
VcirLocalOutput {
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(
|
||||
now + time::Duration::minutes(30),
|
||||
),
|
||||
source_object_uri: aspa_uri.clone(),
|
||||
source_object_type: VcirSourceObjectType::Aspa,
|
||||
source_object_hash: sha256_hex_to_32(&aspa_hash),
|
||||
source_ee_cert_hash: sha256_hex_to_32(&ee_hash),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: 64496,
|
||||
provider_as_ids: vec![64497, 64498],
|
||||
},
|
||||
rule_hash: sha256_32(b"aspa-rule"),
|
||||
},
|
||||
child_entries: vec![VcirChildEntry {
|
||||
child_manifest_rsync_uri: child_manifest_uri,
|
||||
child_cert_rsync_uri: child_cert_uri.clone(),
|
||||
child_cert_hash: child_cert_hash.to_string(),
|
||||
child_ski: "33".repeat(20),
|
||||
child_rsync_base_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_publication_point_rsync_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_rrdp_notification_uri: Some("https://example.test/child-notify.xml".to_string()),
|
||||
child_effective_ip_resources: None,
|
||||
child_effective_as_resources: None,
|
||||
accepted_at_validation_time: PackTime::from_utc_offset_datetime(now),
|
||||
}],
|
||||
local_outputs: vec![
|
||||
VcirLocalOutput {
|
||||
output_id: sha256_hex(b"vrp-out"),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(30)),
|
||||
source_object_uri: roa_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: roa_hash.clone(),
|
||||
source_ee_cert_hash: ee_hash.clone(),
|
||||
payload_json: serde_json::json!({"asn": 64496, "prefix": "203.0.113.0/24", "max_length": 24}).to_string(),
|
||||
rule_hash: sha256_hex(b"roa-rule"),
|
||||
validation_path_hint: vec![manifest_uri.clone(), roa_uri.clone()],
|
||||
VcirLocalOutput {
|
||||
output_type: VcirOutputType::RouterKey,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(
|
||||
now + time::Duration::minutes(30),
|
||||
),
|
||||
source_object_uri: router_uri.clone(),
|
||||
source_object_type: VcirSourceObjectType::RouterKey,
|
||||
source_object_hash: sha256_hex_to_32(&router_hash),
|
||||
source_ee_cert_hash: sha256_hex_to_32(&router_hash),
|
||||
payload: VcirLocalOutputPayload::RouterKey {
|
||||
as_id: 64496,
|
||||
ski: vec![0x11; 20],
|
||||
spki_der: vec![0x30, 0x00],
|
||||
},
|
||||
VcirLocalOutput {
|
||||
output_id: sha256_hex(b"aspa-out"),
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(30)),
|
||||
source_object_uri: aspa_uri.clone(),
|
||||
source_object_type: "aspa".to_string(),
|
||||
source_object_hash: aspa_hash.clone(),
|
||||
source_ee_cert_hash: ee_hash,
|
||||
payload_json: serde_json::json!({"customer_as_id": 64496, "provider_as_ids": [64497, 64498]}).to_string(),
|
||||
rule_hash: sha256_hex(b"aspa-rule"),
|
||||
validation_path_hint: vec![manifest_uri.clone(), aspa_uri.clone()],
|
||||
},
|
||||
VcirLocalOutput {
|
||||
output_id: sha256_hex(b"router-key-out"),
|
||||
output_type: VcirOutputType::RouterKey,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(30)),
|
||||
source_object_uri: router_uri.clone(),
|
||||
source_object_type: "router_key".to_string(),
|
||||
source_object_hash: router_hash.clone(),
|
||||
source_ee_cert_hash: router_hash.clone(),
|
||||
payload_json: serde_json::json!({
|
||||
"as_id": 64496,
|
||||
"ski_hex": "11".repeat(20),
|
||||
"spki_der_base64": base64::engine::general_purpose::STANDARD.encode([0x30u8, 0x00]),
|
||||
}).to_string(),
|
||||
rule_hash: sha256_hex(b"router-key-rule"),
|
||||
validation_path_hint: vec![manifest_uri.clone(), router_uri.clone()],
|
||||
},
|
||||
],
|
||||
related_artifacts: vec![
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::Manifest,
|
||||
artifact_kind: VcirArtifactKind::Mft,
|
||||
uri: Some(manifest_uri.clone()),
|
||||
sha256: manifest_hash,
|
||||
object_type: Some("mft".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::CurrentCrl,
|
||||
artifact_kind: VcirArtifactKind::Crl,
|
||||
uri: Some(current_crl_uri),
|
||||
sha256: current_crl_hash,
|
||||
object_type: Some("crl".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::ChildCaCert,
|
||||
artifact_kind: VcirArtifactKind::Cer,
|
||||
uri: Some(child_cert_uri),
|
||||
sha256: child_cert_hash.to_string(),
|
||||
object_type: Some("cer".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::SignedObject,
|
||||
artifact_kind: VcirArtifactKind::Roa,
|
||||
uri: Some(roa_uri),
|
||||
sha256: roa_hash,
|
||||
object_type: Some("roa".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::SignedObject,
|
||||
artifact_kind: VcirArtifactKind::Aspa,
|
||||
uri: Some(aspa_uri),
|
||||
sha256: aspa_hash,
|
||||
object_type: Some("aspa".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
],
|
||||
summary: VcirSummary {
|
||||
local_vrp_count: 1,
|
||||
local_aspa_count: 1,
|
||||
local_router_key_count: 1,
|
||||
child_count: 1,
|
||||
accepted_object_count: 4,
|
||||
rejected_object_count: 0,
|
||||
rule_hash: sha256_32(b"router-key-rule"),
|
||||
},
|
||||
audit_summary: VcirAuditSummary {
|
||||
failed_fetch_eligible: true,
|
||||
last_failed_fetch_reason: None,
|
||||
warning_count: 0,
|
||||
audit_flags: Vec::new(),
|
||||
],
|
||||
related_artifacts: vec![
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::Manifest,
|
||||
artifact_kind: VcirArtifactKind::Mft,
|
||||
uri: Some(manifest_uri.clone()),
|
||||
sha256: manifest_hash,
|
||||
object_type: Some("mft".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
}
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::CurrentCrl,
|
||||
artifact_kind: VcirArtifactKind::Crl,
|
||||
uri: Some(current_crl_uri),
|
||||
sha256: current_crl_hash,
|
||||
object_type: Some("crl".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::ChildCaCert,
|
||||
artifact_kind: VcirArtifactKind::Cer,
|
||||
uri: Some(child_cert_uri),
|
||||
sha256: child_cert_hash.to_string(),
|
||||
object_type: Some("cer".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::SignedObject,
|
||||
artifact_kind: VcirArtifactKind::Roa,
|
||||
uri: Some(roa_uri),
|
||||
sha256: roa_hash,
|
||||
object_type: Some("roa".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
VcirRelatedArtifact {
|
||||
artifact_role: VcirArtifactRole::SignedObject,
|
||||
artifact_kind: VcirArtifactKind::Aspa,
|
||||
uri: Some(aspa_uri),
|
||||
sha256: aspa_hash,
|
||||
object_type: Some("aspa".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
},
|
||||
],
|
||||
summary: VcirSummary {
|
||||
local_vrp_count: 1,
|
||||
local_aspa_count: 1,
|
||||
local_router_key_count: 1,
|
||||
child_count: 1,
|
||||
accepted_object_count: 4,
|
||||
rejected_object_count: 0,
|
||||
},
|
||||
audit_summary: VcirAuditSummary {
|
||||
failed_fetch_eligible: true,
|
||||
last_failed_fetch_reason: None,
|
||||
warning_count: 0,
|
||||
audit_flags: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -716,16 +736,20 @@ fn build_vcir_local_outputs_prefers_cached_outputs() {
|
||||
rrdp_notification_uri: None,
|
||||
};
|
||||
let cached = vec![VcirLocalOutput {
|
||||
output_id: "cached-output".to_string(),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: pack.next_update.clone(),
|
||||
source_object_uri: "rsync://example.test/repo/issuer/a.roa".to_string(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: sha256_hex(b"cached-roa"),
|
||||
source_ee_cert_hash: sha256_hex(b"cached-ee"),
|
||||
payload_json: "{\"asn\":64500}".to_string(),
|
||||
rule_hash: sha256_hex(b"cached-rule"),
|
||||
validation_path_hint: vec![pack.manifest_rsync_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: sha256_32(b"cached-roa"),
|
||||
source_ee_cert_hash: sha256_32(b"cached-ee"),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: 64500,
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 24,
|
||||
addr: ipv4_addr([203, 0, 113, 0]),
|
||||
max_length: 24,
|
||||
},
|
||||
rule_hash: sha256_32(b"cached-rule"),
|
||||
}];
|
||||
let outputs = build_vcir_local_outputs(
|
||||
&ca,
|
||||
@@ -801,7 +825,7 @@ fn persist_vcir_non_repository_evidence_stores_current_ca_cert_only() {
|
||||
.expect("first local output");
|
||||
assert!(
|
||||
store
|
||||
.get_raw_by_hash_entry(&first_output.source_ee_cert_hash)
|
||||
.get_raw_by_hash_entry(&first_output.source_ee_cert_hash_hex())
|
||||
.expect("load source ee raw")
|
||||
.is_none()
|
||||
);
|
||||
@@ -838,8 +862,11 @@ fn build_router_key_local_outputs_encodes_router_key_payloads() {
|
||||
);
|
||||
assert_eq!(outputs.len(), 1);
|
||||
assert_eq!(outputs[0].output_type, VcirOutputType::RouterKey);
|
||||
assert_eq!(outputs[0].source_object_type, "router_key");
|
||||
assert!(outputs[0].payload_json.contains("spki_der_base64"));
|
||||
assert_eq!(
|
||||
outputs[0].source_object_type,
|
||||
VcirSourceObjectType::RouterKey
|
||||
);
|
||||
assert!(outputs[0].payload_json().contains("spki_der_base64"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -996,7 +1023,7 @@ fn finalize_fresh_publication_point_releases_local_outputs_cache_after_persist()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persist_vcir_for_fresh_result_stores_vcir_and_audit_indexes_for_real_snapshot() {
|
||||
fn persist_vcir_for_fresh_result_stores_vcir_and_replay_meta_for_real_snapshot() {
|
||||
let (pack, issuer_ca_der, validation_time) = cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca");
|
||||
let objects = crate::validation::objects::process_publication_point_snapshot_for_issuer(
|
||||
@@ -1029,8 +1056,18 @@ fn persist_vcir_for_fresh_result_stores_vcir_and_audit_indexes_for_real_snapshot
|
||||
rrdp_notification_uri: None,
|
||||
};
|
||||
|
||||
persist_vcir_for_fresh_result(&store, &ca, &pack, &objects, &[], &[], &[], validation_time)
|
||||
.expect("persist vcir for fresh result");
|
||||
let mut objects = objects;
|
||||
persist_vcir_for_fresh_result(
|
||||
&store,
|
||||
&ca,
|
||||
&pack,
|
||||
&mut objects,
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
validation_time,
|
||||
)
|
||||
.expect("persist vcir for fresh result");
|
||||
|
||||
let vcir = store
|
||||
.get_vcir(&pack.manifest_rsync_uri)
|
||||
@@ -1054,12 +1091,15 @@ fn persist_vcir_for_fresh_result_stores_vcir_and_audit_indexes_for_real_snapshot
|
||||
vcir.ccr_manifest_projection.manifest_size,
|
||||
pack.manifest_bytes.len() as u64
|
||||
);
|
||||
let first_output = vcir.local_outputs.first().expect("local outputs stored");
|
||||
assert!(
|
||||
store
|
||||
.get_audit_rule_index_entry(crate::storage::AuditRuleKind::Roa, &first_output.rule_hash)
|
||||
.expect("get audit rule index entry")
|
||||
.is_some()
|
||||
assert!(vcir.local_outputs.first().is_some(), "local outputs stored");
|
||||
let replay_meta = store
|
||||
.get_manifest_replay_meta(&pack.manifest_rsync_uri)
|
||||
.expect("get replay meta")
|
||||
.expect("replay meta exists");
|
||||
assert_eq!(replay_meta.manifest_rsync_uri, pack.manifest_rsync_uri);
|
||||
assert_eq!(
|
||||
replay_meta.manifest_sha256,
|
||||
sha2::Sha256::digest(&pack.manifest_bytes).to_vec()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1531,26 +1571,16 @@ fn runner_offline_rsync_fixture_produces_pack_and_warnings() {
|
||||
.iter()
|
||||
.find(|output| output.output_type == crate::storage::VcirOutputType::Vrp)
|
||||
.expect("first VCIR VRP output");
|
||||
let audit_rule = store
|
||||
.get_audit_rule_index_entry(crate::storage::AuditRuleKind::Roa, &first_vrp.rule_hash)
|
||||
.expect("get audit rule index")
|
||||
.expect("audit rule index exists");
|
||||
assert_eq!(audit_rule.manifest_rsync_uri, manifest_rsync_uri);
|
||||
assert_eq!(audit_rule.output_id, first_vrp.output_id);
|
||||
let trace = crate::audit_trace::trace_rule_to_root(
|
||||
&store,
|
||||
crate::storage::AuditRuleKind::Roa,
|
||||
&first_vrp.rule_hash,
|
||||
)
|
||||
.expect("trace rule")
|
||||
.expect("trace exists");
|
||||
assert_eq!(trace.chain_leaf_to_root.len(), 1);
|
||||
assert!(!first_vrp.rule_hash_hex().is_empty());
|
||||
assert!(!first_vrp.output_id().is_empty());
|
||||
let replay_meta = store
|
||||
.get_manifest_replay_meta(&manifest_rsync_uri)
|
||||
.expect("get replay meta")
|
||||
.expect("replay meta exists");
|
||||
assert_eq!(
|
||||
trace.chain_leaf_to_root[0].manifest_rsync_uri,
|
||||
replay_meta.manifest_rsync_uri,
|
||||
manifest_rsync_uri
|
||||
);
|
||||
assert!(trace.source_object_raw.raw_present);
|
||||
assert!(trace.source_ee_cert_raw.raw_present);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2791,10 +2821,11 @@ fn fresh_and_reuse_paths_produce_equivalent_ccr_manifest_projection() {
|
||||
let child_discovery =
|
||||
discover_children_from_fresh_snapshot_with_audit(&ca, &pack, validation_time, None)
|
||||
.expect("discover children");
|
||||
let mut objects = empty_objects_output();
|
||||
let fresh_vcir = build_vcir_from_fresh_result(
|
||||
&ca,
|
||||
&pack,
|
||||
&empty_objects_output(),
|
||||
&mut objects,
|
||||
&[],
|
||||
&child_discovery.audits,
|
||||
&child_discovery.children,
|
||||
@@ -2913,70 +2944,77 @@ fn build_objects_output_from_vcir_tracks_expired_and_invalid_cached_outputs() {
|
||||
}
|
||||
|
||||
vcir.local_outputs.push(VcirLocalOutput {
|
||||
output_id: sha256_hex(b"bad-time"),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: PackTime {
|
||||
rfc3339_utc: "bad-time-value".to_string(),
|
||||
},
|
||||
source_object_uri: bad_time_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: sha256_hex(b"bad-time-src"),
|
||||
source_ee_cert_hash: sha256_hex(b"bad-time-ee"),
|
||||
payload_json:
|
||||
serde_json::json!({"asn": 64496, "prefix": "203.0.113.0/24", "max_length": 24})
|
||||
.to_string(),
|
||||
rule_hash: sha256_hex(b"bad-time-rule"),
|
||||
validation_path_hint: vec![vcir.manifest_rsync_uri.clone(), bad_time_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: sha256_32(b"bad-time-src"),
|
||||
source_ee_cert_hash: sha256_32(b"bad-time-ee"),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: 64496,
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 24,
|
||||
addr: ipv4_addr([203, 0, 113, 0]),
|
||||
max_length: 24,
|
||||
},
|
||||
rule_hash: sha256_32(b"bad-time-rule"),
|
||||
});
|
||||
vcir.local_outputs.push(VcirLocalOutput {
|
||||
output_id: sha256_hex(b"expired"),
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now - time::Duration::minutes(1)),
|
||||
source_object_uri: expired_uri.clone(),
|
||||
source_object_type: "aspa".to_string(),
|
||||
source_object_hash: sha256_hex(b"expired-src"),
|
||||
source_ee_cert_hash: sha256_hex(b"expired-ee"),
|
||||
payload_json: serde_json::json!({"customer_as_id": 64500, "provider_as_ids": [64501]})
|
||||
.to_string(),
|
||||
rule_hash: sha256_hex(b"expired-rule"),
|
||||
validation_path_hint: vec![vcir.manifest_rsync_uri.clone(), expired_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Aspa,
|
||||
source_object_hash: sha256_32(b"expired-src"),
|
||||
source_ee_cert_hash: sha256_32(b"expired-ee"),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: 64500,
|
||||
provider_as_ids: vec![64501],
|
||||
},
|
||||
rule_hash: sha256_32(b"expired-rule"),
|
||||
});
|
||||
vcir.local_outputs.push(VcirLocalOutput {
|
||||
output_id: sha256_hex(b"bad-json"),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(5)),
|
||||
source_object_uri: bad_json_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: sha256_hex(b"bad-json-src"),
|
||||
source_ee_cert_hash: sha256_hex(b"bad-json-ee"),
|
||||
payload_json: "{not-json".to_string(),
|
||||
rule_hash: sha256_hex(b"bad-json-rule"),
|
||||
validation_path_hint: vec![vcir.manifest_rsync_uri.clone(), bad_json_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: sha256_32(b"bad-json-src"),
|
||||
source_ee_cert_hash: sha256_32(b"bad-json-ee"),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: 64510,
|
||||
provider_as_ids: vec![64511],
|
||||
},
|
||||
rule_hash: sha256_32(b"bad-json-rule"),
|
||||
});
|
||||
vcir.local_outputs.push(VcirLocalOutput {
|
||||
output_id: sha256_hex(b"bad-prefix"),
|
||||
output_type: VcirOutputType::Vrp,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(5)),
|
||||
source_object_uri: bad_prefix_uri.clone(),
|
||||
source_object_type: "roa".to_string(),
|
||||
source_object_hash: sha256_hex(b"bad-prefix-src"),
|
||||
source_ee_cert_hash: sha256_hex(b"bad-prefix-ee"),
|
||||
payload_json: serde_json::json!({"asn": 64510, "prefix": "203.0.113.0", "max_length": 24})
|
||||
.to_string(),
|
||||
rule_hash: sha256_hex(b"bad-prefix-rule"),
|
||||
validation_path_hint: vec![vcir.manifest_rsync_uri.clone(), bad_prefix_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Roa,
|
||||
source_object_hash: sha256_32(b"bad-prefix-src"),
|
||||
source_ee_cert_hash: sha256_32(b"bad-prefix-ee"),
|
||||
payload: VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id: 64512,
|
||||
provider_as_ids: vec![64513],
|
||||
},
|
||||
rule_hash: sha256_32(b"bad-prefix-rule"),
|
||||
});
|
||||
vcir.local_outputs.push(VcirLocalOutput {
|
||||
output_id: sha256_hex(b"bad-aspa"),
|
||||
output_type: VcirOutputType::Aspa,
|
||||
item_effective_until: PackTime::from_utc_offset_datetime(now + time::Duration::minutes(5)),
|
||||
source_object_uri: bad_aspa_uri.clone(),
|
||||
source_object_type: "aspa".to_string(),
|
||||
source_object_hash: sha256_hex(b"bad-aspa-src"),
|
||||
source_ee_cert_hash: sha256_hex(b"bad-aspa-ee"),
|
||||
payload_json: serde_json::json!({"customer_as_id": 64520}).to_string(),
|
||||
rule_hash: sha256_hex(b"bad-aspa-rule"),
|
||||
validation_path_hint: vec![vcir.manifest_rsync_uri.clone(), bad_aspa_uri.clone()],
|
||||
source_object_type: VcirSourceObjectType::Aspa,
|
||||
source_object_hash: sha256_32(b"bad-aspa-src"),
|
||||
source_ee_cert_hash: sha256_32(b"bad-aspa-ee"),
|
||||
payload: VcirLocalOutputPayload::Vrp {
|
||||
asn: 64520,
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 24,
|
||||
addr: ipv4_addr([198, 51, 100, 0]),
|
||||
max_length: 24,
|
||||
},
|
||||
rule_hash: sha256_32(b"bad-aspa-rule"),
|
||||
});
|
||||
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user