20260324 完成和routinator对齐snapshot delta replay correctness一致

This commit is contained in:
2026-03-24 10:10:04 +08:00
parent 557a69cbd2
commit d6d44669b4
23 changed files with 2043 additions and 223 deletions

View File

@@ -10,8 +10,10 @@ use crate::data_model::roa::{IpPrefix, RoaAfi, RoaDecodeError, RoaObject, RoaVal
use crate::data_model::signed_object::SignedObjectVerifyError;
use crate::policy::{Policy, SignedObjectFailurePolicy};
use crate::report::{RfcRef, Warning};
use crate::storage::PackFile;
use crate::validation::cert_path::{CertPathError, validate_ee_cert_path_with_prevalidated_issuer};
use crate::storage::{PackFile, PackTime, VcirLocalOutput, VcirOutputType};
use crate::validation::cert_path::{
CertPathError, validate_ee_cert_path_with_predecoded_ee,
};
use crate::validation::manifest::PublicationPointData;
use crate::validation::publication_point::PublicationPointSnapshot;
use x509_parser::prelude::FromDer;
@@ -67,6 +69,7 @@ pub struct AspaAttestation {
pub struct ObjectsOutput {
pub vrps: Vec<Vrp>,
pub aspas: Vec<AspaAttestation>,
pub local_outputs_cache: Vec<VcirLocalOutput>,
pub warnings: Vec<Warning>,
pub stats: ObjectsStats,
pub audit: Vec<ObjectAuditEntry>,
@@ -148,6 +151,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -171,6 +175,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -188,6 +193,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -246,6 +252,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -254,12 +261,14 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
let mut vrps: Vec<Vrp> = Vec::new();
let mut aspas: Vec<AspaAttestation> = Vec::new();
let mut local_outputs_cache: Vec<VcirLocalOutput> = Vec::new();
for (idx, file) in locked_files.iter().enumerate() {
if file.rsync_uri.ends_with(".roa") {
let _t = timing.as_ref().map(|t| t.span_phase("objects_roa_total"));
match process_roa_with_issuer(
file,
manifest_rsync_uri,
issuer_ca_der,
&issuer_ca,
&issuer_spki,
@@ -271,9 +280,10 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
validation_time,
timing,
) {
Ok(mut out) => {
Ok((mut out, local_outputs)) => {
stats.roa_ok += 1;
vrps.append(&mut out);
local_outputs_cache.extend(local_outputs);
audit.push(ObjectAuditEntry {
rsync_uri: file.rsync_uri.clone(),
sha256_hex: sha256_hex_from_32(&file.sha256),
@@ -346,6 +356,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -357,6 +368,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
let _t = timing.as_ref().map(|t| t.span_phase("objects_aspa_total"));
match process_aspa_with_issuer(
file,
manifest_rsync_uri,
issuer_ca_der,
&issuer_ca,
&issuer_spki,
@@ -368,9 +380,10 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
validation_time,
timing,
) {
Ok(att) => {
Ok((att, local_output)) => {
stats.aspa_ok += 1;
aspas.push(att);
local_outputs_cache.push(local_output);
audit.push(ObjectAuditEntry {
rsync_uri: file.rsync_uri.clone(),
sha256_hex: sha256_hex_from_32(&file.sha256),
@@ -443,6 +456,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
return ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
local_outputs_cache: Vec::new(),
warnings,
stats,
audit,
@@ -456,6 +470,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
ObjectsOutput {
vrps,
aspas,
local_outputs_cache,
warnings,
stats,
audit,
@@ -537,6 +552,7 @@ enum ObjectValidateError {
fn process_roa_with_issuer(
file: &PackFile,
manifest_rsync_uri: &str,
issuer_ca_der: &[u8],
issuer_ca: &ResourceCertificate,
issuer_spki: &SubjectPublicKeyInfo<'_>,
@@ -547,7 +563,7 @@ fn process_roa_with_issuer(
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
) -> Result<Vec<Vrp>, ObjectValidateError> {
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>), ObjectValidateError> {
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_decode_and_validate_total"));
@@ -566,9 +582,9 @@ fn process_roa_with_issuer(
roa.signed_object.verify()?;
drop(_verify);
let ee = &roa.signed_object.signed_data.certificates[0].resource_cert;
let ee_der = &roa.signed_object.signed_data.certificates[0].raw_der;
let ee_crldp_uris = roa.signed_object.signed_data.certificates[0]
.resource_cert
let ee_crldp_uris = ee
.tbs
.extensions
.crl_distribution_points_uris
@@ -579,7 +595,8 @@ fn process_roa_with_issuer(
let _cert_path = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_validate_ee_cert_path_total"));
let ee = validate_ee_cert_path_with_prevalidated_issuer(
validate_ee_cert_path_with_predecoded_ee(
ee,
ee_der,
issuer_ca,
issuer_spki,
@@ -602,11 +619,52 @@ fn process_roa_with_issuer(
)?;
drop(_subset);
Ok(roa_to_vrps(&roa))
let vrps = roa_to_vrps(&roa);
let source_object_hash = sha256_hex_from_32(&file.sha256);
let source_ee_cert_hash = crate::audit::sha256_hex(ee.raw_der.as_slice());
let item_effective_until = PackTime::from_utc_offset_datetime(ee.tbs.validity_not_after);
let local_outputs = vrps
.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:{}:{}:{}:{}",
source_object_hash, vrp.asn, prefix, vrp.max_length
)
.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(),
],
}
})
.collect();
Ok((vrps, local_outputs))
}
fn process_aspa_with_issuer(
file: &PackFile,
manifest_rsync_uri: &str,
issuer_ca_der: &[u8],
issuer_ca: &ResourceCertificate,
issuer_spki: &SubjectPublicKeyInfo<'_>,
@@ -617,7 +675,7 @@ fn process_aspa_with_issuer(
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
) -> Result<AspaAttestation, ObjectValidateError> {
) -> Result<(AspaAttestation, VcirLocalOutput), ObjectValidateError> {
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_aspa_decode_and_validate_total"));
@@ -636,9 +694,9 @@ fn process_aspa_with_issuer(
aspa.signed_object.verify()?;
drop(_verify);
let ee = &aspa.signed_object.signed_data.certificates[0].resource_cert;
let ee_der = &aspa.signed_object.signed_data.certificates[0].raw_der;
let ee_crldp_uris = aspa.signed_object.signed_data.certificates[0]
.resource_cert
let ee_crldp_uris = ee
.tbs
.extensions
.crl_distribution_points_uris
@@ -649,7 +707,8 @@ fn process_aspa_with_issuer(
let _cert_path = timing
.as_ref()
.map(|t| t.span_phase("objects_aspa_validate_ee_cert_path_total"));
let ee = validate_ee_cert_path_with_prevalidated_issuer(
validate_ee_cert_path_with_predecoded_ee(
ee,
ee_der,
issuer_ca,
issuer_spki,
@@ -672,10 +731,70 @@ fn process_aspa_with_issuer(
)?;
drop(_subset);
Ok(AspaAttestation {
let attestation = AspaAttestation {
customer_as_id: aspa.aspa.customer_as_id,
provider_as_ids: aspa.aspa.provider_as_ids.clone(),
})
};
let source_object_hash = sha256_hex_from_32(&file.sha256);
let source_ee_cert_hash = crate::audit::sha256_hex(ee.raw_der.as_slice());
let item_effective_until = PackTime::from_utc_offset_datetime(ee.tbs.validity_not_after);
let providers = attestation
.provider_as_ids
.iter()
.map(u32::to_string)
.collect::<Vec<_>>()
.join(",");
let rule_hash = crate::audit::sha256_hex(
format!(
"aspa-rule:{}:{}:{}",
source_object_hash, attestation.customer_as_id, providers
)
.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,
],
};
Ok((attestation, local_output))
}
fn vrp_prefix_to_string(vrp: &Vrp) -> String {
let prefix = &vrp.prefix;
match prefix.afi {
RoaAfi::Ipv4 => {
let addr = std::net::Ipv4Addr::new(
prefix.addr[0],
prefix.addr[1],
prefix.addr[2],
prefix.addr[3],
);
format!("{addr}/{}", prefix.prefix_len)
}
RoaAfi::Ipv6 => {
let mut octets = [0u8; 16];
octets.copy_from_slice(&prefix.addr[..16]);
let addr = std::net::Ipv6Addr::from(octets);
format!("{addr}/{}", prefix.prefix_len)
}
}
}
fn choose_crl_uri_for_certificate<'a>(