串行验证通过
This commit is contained in:
163
src/validation/tree.rs
Normal file
163
src/validation/tree.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::VerifiedPublicationPointPack;
|
||||
use crate::validation::manifest::PublicationPointSource;
|
||||
use crate::audit::PublicationPointAudit;
|
||||
use crate::data_model::rc::{AsResourceSet, IpResourceSet};
|
||||
use crate::validation::objects::{AspaAttestation, ObjectsOutput, Vrp};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TreeRunConfig {
|
||||
/// Max CA instance depth to process (0 = root only).
|
||||
pub max_depth: Option<usize>,
|
||||
/// Max number of CA instances to process.
|
||||
pub max_instances: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TreeRunConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_depth: None,
|
||||
max_instances: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CaInstanceHandle {
|
||||
pub depth: usize,
|
||||
/// DER bytes of the CA certificate for this CA instance.
|
||||
pub ca_certificate_der: Vec<u8>,
|
||||
/// rsync URI of this CA certificate object (where it is published).
|
||||
///
|
||||
/// This is used for strict AIA binding checks (RFC 6487 §4.8.7) when validating
|
||||
/// subordinate CA and EE certificates. Trust anchor certificates downloaded from
|
||||
/// TAL URIs may not have an rsync publication URI, so this can be `None`.
|
||||
pub ca_certificate_rsync_uri: Option<String>,
|
||||
/// Effective (fully resolved) resources for this CA instance, used for resource-path
|
||||
/// validation of subordinate CA certificates.
|
||||
pub effective_ip_resources: Option<IpResourceSet>,
|
||||
pub effective_as_resources: Option<AsResourceSet>,
|
||||
pub rsync_base_uri: String,
|
||||
pub manifest_rsync_uri: String,
|
||||
pub publication_point_rsync_uri: String,
|
||||
pub rrdp_notification_uri: Option<String>,
|
||||
}
|
||||
|
||||
impl CaInstanceHandle {
|
||||
pub fn with_depth(mut self, depth: usize) -> Self {
|
||||
self.depth = depth;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PublicationPointRunResult {
|
||||
pub source: PublicationPointSource,
|
||||
pub pack: VerifiedPublicationPointPack,
|
||||
pub warnings: Vec<Warning>,
|
||||
pub objects: ObjectsOutput,
|
||||
pub audit: PublicationPointAudit,
|
||||
/// Candidate child CA instances discovered from this publication point.
|
||||
///
|
||||
/// RFC 9286 §6.6 restriction is enforced by the tree engine: if this
|
||||
/// publication point used verified cache due to failed fetch, children MUST NOT
|
||||
/// be enqueued/processed in this run.
|
||||
pub discovered_children: Vec<CaInstanceHandle>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TreeRunOutput {
|
||||
pub instances_processed: usize,
|
||||
pub instances_failed: usize,
|
||||
pub warnings: Vec<Warning>,
|
||||
pub vrps: Vec<Vrp>,
|
||||
pub aspas: Vec<AspaAttestation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TreeRunError {
|
||||
#[error("publication point runner failed: {0}")]
|
||||
Runner(String),
|
||||
}
|
||||
|
||||
pub trait PublicationPointRunner {
|
||||
fn run_publication_point(
|
||||
&self,
|
||||
ca: &CaInstanceHandle,
|
||||
) -> Result<PublicationPointRunResult, String>;
|
||||
}
|
||||
|
||||
pub fn run_tree_serial(
|
||||
root: CaInstanceHandle,
|
||||
runner: &dyn PublicationPointRunner,
|
||||
config: &TreeRunConfig,
|
||||
) -> Result<TreeRunOutput, TreeRunError> {
|
||||
let mut queue: std::collections::VecDeque<CaInstanceHandle> = std::collections::VecDeque::new();
|
||||
queue.push_back(root);
|
||||
|
||||
let mut visited_manifest_uris: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
let mut instances_processed = 0usize;
|
||||
let mut instances_failed = 0usize;
|
||||
let mut warnings: Vec<Warning> = Vec::new();
|
||||
let mut vrps: Vec<Vrp> = Vec::new();
|
||||
let mut aspas: Vec<AspaAttestation> = Vec::new();
|
||||
|
||||
while let Some(ca) = queue.pop_front() {
|
||||
if !visited_manifest_uris.insert(ca.manifest_rsync_uri.clone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(max_instances) = config.max_instances {
|
||||
if instances_processed >= max_instances {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max_depth) = config.max_depth {
|
||||
if ca.depth > max_depth {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let res = match runner.run_publication_point(&ca) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
instances_failed += 1;
|
||||
warnings.push(
|
||||
Warning::new(format!("publication point failed: {e}"))
|
||||
.with_context(&ca.manifest_rsync_uri),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
instances_processed += 1;
|
||||
warnings.extend(res.warnings.clone());
|
||||
warnings.extend(res.objects.warnings.clone());
|
||||
vrps.extend(res.objects.vrps.clone());
|
||||
aspas.extend(res.objects.aspas.clone());
|
||||
|
||||
let enqueue_children = res.source == PublicationPointSource::Fresh;
|
||||
if !enqueue_children && !res.discovered_children.is_empty() {
|
||||
warnings.push(
|
||||
Warning::new("skipping child CA discovery due to failed fetch cache use")
|
||||
.with_rfc_refs(&[RfcRef("RFC 9286 §6.6")])
|
||||
.with_context(&ca.manifest_rsync_uri),
|
||||
);
|
||||
}
|
||||
|
||||
if enqueue_children {
|
||||
for child in res.discovered_children {
|
||||
queue.push_back(child.with_depth(ca.depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TreeRunOutput {
|
||||
instances_processed,
|
||||
instances_failed,
|
||||
warnings,
|
||||
vrps,
|
||||
aspas,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user