|
|
|
|
@@ -9,6 +9,10 @@ use crate::data_model::crl::RpkixCrl;
|
|
|
|
|
use crate::data_model::manifest::ManifestObject;
|
|
|
|
|
use crate::data_model::rc::ResourceCertificate;
|
|
|
|
|
use crate::data_model::roa::{RoaAfi, RoaObject};
|
|
|
|
|
use crate::data_model::router_cert::{
|
|
|
|
|
BgpsecRouterCertificate, BgpsecRouterCertificateDecodeError,
|
|
|
|
|
BgpsecRouterCertificatePathError, BgpsecRouterCertificateProfileError,
|
|
|
|
|
};
|
|
|
|
|
use crate::fetch::rsync::RsyncFetcher;
|
|
|
|
|
use crate::policy::Policy;
|
|
|
|
|
use crate::replay::archive::ReplayArchiveIndex;
|
|
|
|
|
@@ -33,7 +37,7 @@ use crate::validation::manifest::{
|
|
|
|
|
ManifestFreshError, PublicationPointData, PublicationPointSource,
|
|
|
|
|
process_manifest_publication_point_fresh_after_repo_sync,
|
|
|
|
|
};
|
|
|
|
|
use crate::validation::objects::{AspaAttestation, Vrp, process_publication_point_for_issuer};
|
|
|
|
|
use crate::validation::objects::{AspaAttestation, RouterKeyPayload, Vrp, process_publication_point_for_issuer};
|
|
|
|
|
use crate::validation::publication_point::PublicationPointSnapshot;
|
|
|
|
|
use crate::validation::tree::{
|
|
|
|
|
CaInstanceHandle, DiscoveredChildCaInstance, PublicationPointRunResult, PublicationPointRunner,
|
|
|
|
|
@@ -42,6 +46,7 @@ use std::collections::{HashMap, HashSet};
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
use base64::Engine as _;
|
|
|
|
|
use serde_json::json;
|
|
|
|
|
use x509_parser::prelude::FromDer;
|
|
|
|
|
use x509_parser::x509::SubjectPublicKeyInfo;
|
|
|
|
|
@@ -266,7 +271,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
|
|
|
|
|
|
|
|
|
match fresh_publication_point {
|
|
|
|
|
Ok(fresh_point) => {
|
|
|
|
|
let objects = {
|
|
|
|
|
let mut objects = {
|
|
|
|
|
let _objects_total = self
|
|
|
|
|
.timing
|
|
|
|
|
.as_ref()
|
|
|
|
|
@@ -295,18 +300,23 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
|
|
|
|
self.timing.as_ref(),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let (discovered_children, child_audits) = match out {
|
|
|
|
|
Ok(out) => (out.children, out.audits),
|
|
|
|
|
let (discovered_children, child_audits, discovered_router_keys) = match out {
|
|
|
|
|
Ok(out) => (out.children, out.audits, out.router_keys),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warnings.push(
|
|
|
|
|
Warning::new(format!("child CA discovery failed: {e}"))
|
|
|
|
|
.with_rfc_refs(&[RfcRef("RFC 6487 §7.2")])
|
|
|
|
|
.with_context(&ca.manifest_rsync_uri),
|
|
|
|
|
);
|
|
|
|
|
(Vec::new(), Vec::new())
|
|
|
|
|
(Vec::new(), Vec::new(), Vec::new())
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
objects.router_keys.extend(discovered_router_keys);
|
|
|
|
|
objects
|
|
|
|
|
.local_outputs_cache
|
|
|
|
|
.extend(build_router_key_local_outputs(ca, &objects.router_keys));
|
|
|
|
|
|
|
|
|
|
let pack = fresh_point.to_publication_point_snapshot();
|
|
|
|
|
|
|
|
|
|
persist_vcir_for_fresh_result(
|
|
|
|
|
@@ -384,6 +394,7 @@ fn normalize_rsync_base_uri(s: &str) -> String {
|
|
|
|
|
struct ChildDiscoveryOutput {
|
|
|
|
|
children: Vec<DiscoveredChildCaInstance>,
|
|
|
|
|
audits: Vec<ObjectAuditEntry>,
|
|
|
|
|
router_keys: Vec<RouterKeyPayload>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
@@ -421,6 +432,13 @@ struct VcirAspaPayload {
|
|
|
|
|
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,
|
|
|
|
|
@@ -484,6 +502,7 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
|
|
|
|
|
let mut out: Vec<DiscoveredChildCaInstance> = Vec::new();
|
|
|
|
|
let mut audits: Vec<ObjectAuditEntry> = Vec::new();
|
|
|
|
|
let mut router_keys: Vec<RouterKeyPayload> = Vec::new();
|
|
|
|
|
let issuer_resources_index = IssuerEffectiveResourcesIndex::from_effective_resources(
|
|
|
|
|
issuer.effective_ip_resources.as_ref(),
|
|
|
|
|
issuer.effective_as_resources.as_ref(),
|
|
|
|
|
@@ -494,12 +513,16 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
let mut ca_skipped_not_ca: u64 = 0;
|
|
|
|
|
let mut ca_ok: u64 = 0;
|
|
|
|
|
let mut ca_error: u64 = 0;
|
|
|
|
|
let mut router_ok: u64 = 0;
|
|
|
|
|
let mut router_error: u64 = 0;
|
|
|
|
|
let mut router_skipped_non_router: u64 = 0;
|
|
|
|
|
let mut crl_select_error: u64 = 0;
|
|
|
|
|
let mut uri_discovery_error: u64 = 0;
|
|
|
|
|
|
|
|
|
|
let mut select_crl_nanos: u64 = 0;
|
|
|
|
|
let mut child_decode_nanos: u64 = 0;
|
|
|
|
|
let mut validate_sub_ca_nanos: u64 = 0;
|
|
|
|
|
let mut validate_router_nanos: u64 = 0;
|
|
|
|
|
let mut uri_discovery_nanos: u64 = 0;
|
|
|
|
|
let mut enqueue_nanos: u64 = 0;
|
|
|
|
|
|
|
|
|
|
@@ -639,14 +662,100 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
) {
|
|
|
|
|
Ok(v) => v,
|
|
|
|
|
Err(CaPathError::ChildNotCa) => {
|
|
|
|
|
ca_skipped_not_ca = ca_skipped_not_ca.saturating_add(1);
|
|
|
|
|
audits.push(ObjectAuditEntry {
|
|
|
|
|
rsync_uri: f.rsync_uri.clone(),
|
|
|
|
|
sha256_hex: sha256_hex_from_32(&f.sha256),
|
|
|
|
|
kind: AuditObjectKind::Certificate,
|
|
|
|
|
result: AuditObjectResult::Skipped,
|
|
|
|
|
detail: Some("skipped: not a CA resource certificate".to_string()),
|
|
|
|
|
});
|
|
|
|
|
let tr = std::time::Instant::now();
|
|
|
|
|
let router_result = match ensure_issuer_crl_verified(
|
|
|
|
|
issuer_crl_uri.as_str(),
|
|
|
|
|
&mut crl_cache,
|
|
|
|
|
issuer_ca_der,
|
|
|
|
|
) {
|
|
|
|
|
Ok(verified_crl) => {
|
|
|
|
|
BgpsecRouterCertificate::validate_path_with_prevalidated_issuer(
|
|
|
|
|
child_der,
|
|
|
|
|
issuer_ca_ref,
|
|
|
|
|
issuer_spki_ref,
|
|
|
|
|
&verified_crl.crl,
|
|
|
|
|
&verified_crl.revoked_serials,
|
|
|
|
|
issuer.ca_certificate_rsync_uri.as_deref(),
|
|
|
|
|
Some(issuer_crl_uri.as_str()),
|
|
|
|
|
validation_time,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
validate_router_nanos = validate_router_nanos.saturating_add(
|
|
|
|
|
tr.elapsed().as_nanos().min(u128::from(u64::MAX)) as u64,
|
|
|
|
|
);
|
|
|
|
|
router_error = router_error.saturating_add(1);
|
|
|
|
|
audits.push(ObjectAuditEntry {
|
|
|
|
|
rsync_uri: f.rsync_uri.clone(),
|
|
|
|
|
sha256_hex: sha256_hex_from_32(&f.sha256),
|
|
|
|
|
kind: AuditObjectKind::RouterCertificate,
|
|
|
|
|
result: AuditObjectResult::Error,
|
|
|
|
|
detail: Some(format!(
|
|
|
|
|
"router certificate issuer CRL validation failed: {err}"
|
|
|
|
|
)),
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
validate_router_nanos = validate_router_nanos
|
|
|
|
|
.saturating_add(tr.elapsed().as_nanos().min(u128::from(u64::MAX)) as u64);
|
|
|
|
|
|
|
|
|
|
match router_result {
|
|
|
|
|
Ok(router) => {
|
|
|
|
|
router_ok = router_ok.saturating_add(1);
|
|
|
|
|
let source_object_hash = sha256_hex_from_32(&f.sha256);
|
|
|
|
|
let item_effective_until = PackTime::from_utc_offset_datetime(
|
|
|
|
|
router.resource_cert.tbs.validity_not_after,
|
|
|
|
|
);
|
|
|
|
|
for as_id in &router.asns {
|
|
|
|
|
router_keys.push(RouterKeyPayload {
|
|
|
|
|
as_id: *as_id,
|
|
|
|
|
ski: router.subject_key_identifier.clone(),
|
|
|
|
|
spki_der: router.spki_der.clone(),
|
|
|
|
|
source_object_uri: f.rsync_uri.clone(),
|
|
|
|
|
source_object_hash: source_object_hash.clone(),
|
|
|
|
|
source_ee_cert_hash: source_object_hash.clone(),
|
|
|
|
|
item_effective_until: item_effective_until.clone(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
audits.push(ObjectAuditEntry {
|
|
|
|
|
rsync_uri: f.rsync_uri.clone(),
|
|
|
|
|
sha256_hex: sha256_hex_from_32(&f.sha256),
|
|
|
|
|
kind: AuditObjectKind::RouterCertificate,
|
|
|
|
|
result: AuditObjectResult::Ok,
|
|
|
|
|
detail: Some(
|
|
|
|
|
"validated BGPsec router certificate (RFC 8209); no child CA instance enqueued"
|
|
|
|
|
.to_string(),
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
Err(err) if is_non_router_certificate(&err) => {
|
|
|
|
|
ca_skipped_not_ca = ca_skipped_not_ca.saturating_add(1);
|
|
|
|
|
router_skipped_non_router = router_skipped_non_router.saturating_add(1);
|
|
|
|
|
audits.push(ObjectAuditEntry {
|
|
|
|
|
rsync_uri: f.rsync_uri.clone(),
|
|
|
|
|
sha256_hex: sha256_hex_from_32(&f.sha256),
|
|
|
|
|
kind: AuditObjectKind::Certificate,
|
|
|
|
|
result: AuditObjectResult::Skipped,
|
|
|
|
|
detail: Some(
|
|
|
|
|
"skipped: not a CA resource certificate or BGPsec router certificate"
|
|
|
|
|
.to_string(),
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
router_error = router_error.saturating_add(1);
|
|
|
|
|
audits.push(ObjectAuditEntry {
|
|
|
|
|
rsync_uri: f.rsync_uri.clone(),
|
|
|
|
|
sha256_hex: sha256_hex_from_32(&f.sha256),
|
|
|
|
|
kind: AuditObjectKind::RouterCertificate,
|
|
|
|
|
result: AuditObjectResult::Error,
|
|
|
|
|
detail: Some(format!(
|
|
|
|
|
"router certificate validation failed: {err}"
|
|
|
|
|
)),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
@@ -734,6 +843,9 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
t.record_count("child_ca_ok", ca_ok);
|
|
|
|
|
t.record_count("child_ca_error", ca_error);
|
|
|
|
|
t.record_count("child_ca_skipped_not_ca", ca_skipped_not_ca);
|
|
|
|
|
t.record_count("child_router_ok", router_ok);
|
|
|
|
|
t.record_count("child_router_error", router_error);
|
|
|
|
|
t.record_count("child_router_skipped_non_router", router_skipped_non_router);
|
|
|
|
|
t.record_count("child_crl_select_error", crl_select_error);
|
|
|
|
|
t.record_count("child_uri_discovery_error", uri_discovery_error);
|
|
|
|
|
|
|
|
|
|
@@ -759,6 +871,7 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
t.record_phase_nanos("child_select_issuer_crl_total", select_crl_nanos);
|
|
|
|
|
t.record_phase_nanos("child_decode_certificate_total", child_decode_nanos);
|
|
|
|
|
t.record_phase_nanos("child_validate_subordinate_total", validate_sub_ca_nanos);
|
|
|
|
|
t.record_phase_nanos("child_validate_router_certificate_total", validate_router_nanos);
|
|
|
|
|
t.record_phase_nanos("child_ca_instance_uri_discovery_total", uri_discovery_nanos);
|
|
|
|
|
t.record_phase_nanos("child_enqueue_total", enqueue_nanos);
|
|
|
|
|
}
|
|
|
|
|
@@ -766,9 +879,21 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
|
|
|
|
Ok(ChildDiscoveryOutput {
|
|
|
|
|
children: out,
|
|
|
|
|
audits,
|
|
|
|
|
router_keys,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_non_router_certificate(err: &BgpsecRouterCertificatePathError) -> bool {
|
|
|
|
|
matches!(
|
|
|
|
|
err,
|
|
|
|
|
BgpsecRouterCertificatePathError::Decode(BgpsecRouterCertificateDecodeError::Validate(
|
|
|
|
|
BgpsecRouterCertificateProfileError::NotEe
|
|
|
|
|
| BgpsecRouterCertificateProfileError::MissingExtendedKeyUsage
|
|
|
|
|
| BgpsecRouterCertificateProfileError::MissingBgpsecRouterEku
|
|
|
|
|
))
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn select_issuer_crl_uri_for_child<'a>(
|
|
|
|
|
child: &'a crate::data_model::rc::ResourceCertificate,
|
|
|
|
|
crl_cache: &std::collections::HashMap<String, CachedIssuerCrl>,
|
|
|
|
|
@@ -1181,6 +1306,7 @@ fn empty_objects_output() -> crate::validation::objects::ObjectsOutput {
|
|
|
|
|
crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: Vec::new(),
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -1367,6 +1493,14 @@ fn reconstruct_snapshot_from_vcir(
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn audit_kind_for_vcir_output_type(output_type: VcirOutputType) -> AuditObjectKind {
|
|
|
|
|
match output_type {
|
|
|
|
|
VcirOutputType::Vrp => AuditObjectKind::Roa,
|
|
|
|
|
VcirOutputType::Aspa => AuditObjectKind::Aspa,
|
|
|
|
|
VcirOutputType::RouterKey => AuditObjectKind::RouterCertificate,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_objects_output_from_vcir(
|
|
|
|
|
vcir: &ValidatedCaInstanceResult,
|
|
|
|
|
validation_time: time::OffsetDateTime,
|
|
|
|
|
@@ -1411,11 +1545,7 @@ fn build_objects_output_from_vcir(
|
|
|
|
|
ObjectAuditEntry {
|
|
|
|
|
rsync_uri: local.source_object_uri.clone(),
|
|
|
|
|
sha256_hex: local.source_object_hash.clone(),
|
|
|
|
|
kind: if local.output_type == VcirOutputType::Vrp {
|
|
|
|
|
AuditObjectKind::Roa
|
|
|
|
|
} else {
|
|
|
|
|
AuditObjectKind::Aspa
|
|
|
|
|
},
|
|
|
|
|
kind: audit_kind_for_vcir_output_type(local.output_type),
|
|
|
|
|
result: AuditObjectResult::Error,
|
|
|
|
|
detail: Some(
|
|
|
|
|
"cached local output has invalid item_effective_until".to_string(),
|
|
|
|
|
@@ -1431,11 +1561,7 @@ fn build_objects_output_from_vcir(
|
|
|
|
|
.or_insert_with(|| ObjectAuditEntry {
|
|
|
|
|
rsync_uri: local.source_object_uri.clone(),
|
|
|
|
|
sha256_hex: local.source_object_hash.clone(),
|
|
|
|
|
kind: if local.output_type == VcirOutputType::Vrp {
|
|
|
|
|
AuditObjectKind::Roa
|
|
|
|
|
} else {
|
|
|
|
|
AuditObjectKind::Aspa
|
|
|
|
|
},
|
|
|
|
|
kind: audit_kind_for_vcir_output_type(local.output_type),
|
|
|
|
|
result: AuditObjectResult::Skipped,
|
|
|
|
|
detail: Some("skipped: cached local output expired".to_string()),
|
|
|
|
|
});
|
|
|
|
|
@@ -1507,6 +1633,37 @@ fn build_objects_output_from_vcir(
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
VcirOutputType::RouterKey => match parse_vcir_router_key_output(local) {
|
|
|
|
|
Ok(router_key) => {
|
|
|
|
|
output.router_keys.push(router_key);
|
|
|
|
|
audit_by_uri.insert(
|
|
|
|
|
local.source_object_uri.clone(),
|
|
|
|
|
ObjectAuditEntry {
|
|
|
|
|
rsync_uri: local.source_object_uri.clone(),
|
|
|
|
|
sha256_hex: local.source_object_hash.clone(),
|
|
|
|
|
kind: AuditObjectKind::RouterCertificate,
|
|
|
|
|
result: AuditObjectResult::Ok,
|
|
|
|
|
detail: Some("cached Router Key local output restored".to_string()),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warnings.push(
|
|
|
|
|
Warning::new(format!("cached Router Key local output parse failed: {e}"))
|
|
|
|
|
.with_context(&local.source_object_uri),
|
|
|
|
|
);
|
|
|
|
|
audit_by_uri.insert(
|
|
|
|
|
local.source_object_uri.clone(),
|
|
|
|
|
ObjectAuditEntry {
|
|
|
|
|
rsync_uri: local.source_object_uri.clone(),
|
|
|
|
|
sha256_hex: local.source_object_hash.clone(),
|
|
|
|
|
kind: AuditObjectKind::RouterCertificate,
|
|
|
|
|
result: AuditObjectResult::Error,
|
|
|
|
|
detail: Some(format!("cached Router Key local output parse failed: {e}")),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1539,6 +1696,24 @@ fn parse_vcir_aspa_output(local: &VcirLocalOutput) -> Result<AspaAttestation, St
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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('/')
|
|
|
|
|
@@ -1775,6 +1950,10 @@ fn build_vcir_from_fresh_result(
|
|
|
|
|
.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,
|
|
|
|
|
child_count: discovered_children.len() as u32,
|
|
|
|
|
accepted_object_count,
|
|
|
|
|
rejected_object_count,
|
|
|
|
|
@@ -1937,6 +2116,47 @@ fn build_vcir_local_outputs(
|
|
|
|
|
Ok(out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_router_key_local_outputs(
|
|
|
|
|
ca: &CaInstanceHandle,
|
|
|
|
|
router_keys: &[RouterKeyPayload],
|
|
|
|
|
) -> Vec<VcirLocalOutput> {
|
|
|
|
|
router_keys
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|router_key| {
|
|
|
|
|
let ski_hex = hex::encode(&router_key.ski);
|
|
|
|
|
let spki_der_base64 = base64::engine::general_purpose::STANDARD.encode(&router_key.spki_der);
|
|
|
|
|
let rule_hash = sha256_hex(
|
|
|
|
|
format!(
|
|
|
|
|
"router-key-rule:{}:{}:{}:{}",
|
|
|
|
|
router_key.source_object_hash, router_key.as_id, ski_hex, spki_der_base64
|
|
|
|
|
)
|
|
|
|
|
.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(),
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_vcir_child_entries(
|
|
|
|
|
discovered_children: &[DiscoveredChildCaInstance],
|
|
|
|
|
validation_time: time::OffsetDateTime,
|
|
|
|
|
@@ -2494,6 +2714,191 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct GeneratedRouter {
|
|
|
|
|
issuer_ca_der: Vec<u8>,
|
|
|
|
|
router_der: Vec<u8>,
|
|
|
|
|
issuer_crl_der: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_router_cert_with_variant(key_spec: &str, include_eku: bool) -> GeneratedRouter {
|
|
|
|
|
assert!(openssl_available(), "openssl is required for this test");
|
|
|
|
|
|
|
|
|
|
let td = tempfile::tempdir().expect("tempdir");
|
|
|
|
|
let dir = td.path();
|
|
|
|
|
|
|
|
|
|
std::fs::create_dir_all(dir.join("newcerts")).expect("newcerts");
|
|
|
|
|
std::fs::write(dir.join("index.txt"), b"").expect("index");
|
|
|
|
|
std::fs::write(dir.join("serial"), b"1000\n").expect("serial");
|
|
|
|
|
std::fs::write(dir.join("crlnumber"), b"1000\n").expect("crlnumber");
|
|
|
|
|
|
|
|
|
|
let eku_line = if include_eku {
|
|
|
|
|
"extendedKeyUsage = 1.3.6.1.5.5.7.3.30"
|
|
|
|
|
} else {
|
|
|
|
|
""
|
|
|
|
|
};
|
|
|
|
|
let cnf = format!(
|
|
|
|
|
r#"
|
|
|
|
|
[ ca ]
|
|
|
|
|
default_ca = CA_default
|
|
|
|
|
|
|
|
|
|
[ CA_default ]
|
|
|
|
|
dir = {dir}
|
|
|
|
|
database = $dir/index.txt
|
|
|
|
|
new_certs_dir = $dir/newcerts
|
|
|
|
|
certificate = $dir/issuer.pem
|
|
|
|
|
private_key = $dir/issuer.key
|
|
|
|
|
serial = $dir/serial
|
|
|
|
|
crlnumber = $dir/crlnumber
|
|
|
|
|
default_md = sha256
|
|
|
|
|
default_days = 365
|
|
|
|
|
default_crl_days = 1
|
|
|
|
|
policy = policy_any
|
|
|
|
|
x509_extensions = v3_issuer_ca
|
|
|
|
|
crl_extensions = crl_ext
|
|
|
|
|
unique_subject = no
|
|
|
|
|
copy_extensions = none
|
|
|
|
|
|
|
|
|
|
[ policy_any ]
|
|
|
|
|
commonName = supplied
|
|
|
|
|
|
|
|
|
|
[ req ]
|
|
|
|
|
prompt = no
|
|
|
|
|
distinguished_name = dn
|
|
|
|
|
|
|
|
|
|
[ dn ]
|
|
|
|
|
CN = Test Issuer CA
|
|
|
|
|
|
|
|
|
|
[ v3_issuer_ca ]
|
|
|
|
|
basicConstraints = critical,CA:true
|
|
|
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
|
|
|
subjectKeyIdentifier = hash
|
|
|
|
|
authorityKeyIdentifier = keyid:always
|
|
|
|
|
certificatePolicies = critical, 1.3.6.1.5.5.7.14.2
|
|
|
|
|
subjectInfoAccess = caRepository;URI:rsync://example.test/repo/issuer/, rpkiManifest;URI:rsync://example.test/repo/issuer/issuer.mft, rpkiNotify;URI:https://example.test/notification.xml
|
|
|
|
|
sbgp-ipAddrBlock = critical, IPv4:10.0.0.0/8
|
|
|
|
|
sbgp-autonomousSysNum = critical, AS:64496-64511
|
|
|
|
|
|
|
|
|
|
[ v3_router ]
|
|
|
|
|
keyUsage = critical, digitalSignature
|
|
|
|
|
{eku_line}
|
|
|
|
|
authorityKeyIdentifier = keyid:always
|
|
|
|
|
crlDistributionPoints = URI:rsync://example.test/repo/issuer/issuer.crl
|
|
|
|
|
authorityInfoAccess = caIssuers;URI:rsync://example.test/repo/issuer/issuer.cer
|
|
|
|
|
certificatePolicies = critical, 1.3.6.1.5.5.7.14.2
|
|
|
|
|
sbgp-autonomousSysNum = critical, AS:64496
|
|
|
|
|
|
|
|
|
|
[ crl_ext ]
|
|
|
|
|
authorityKeyIdentifier = keyid:always
|
|
|
|
|
"#,
|
|
|
|
|
dir = dir.display(),
|
|
|
|
|
eku_line = eku_line,
|
|
|
|
|
);
|
|
|
|
|
std::fs::write(dir.join("openssl.cnf"), cnf.as_bytes()).expect("write cnf");
|
|
|
|
|
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("genrsa")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("issuer.key"))
|
|
|
|
|
.arg("2048"));
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("req")
|
|
|
|
|
.arg("-new")
|
|
|
|
|
.arg("-x509")
|
|
|
|
|
.arg("-sha256")
|
|
|
|
|
.arg("-days")
|
|
|
|
|
.arg("365")
|
|
|
|
|
.arg("-key")
|
|
|
|
|
.arg(dir.join("issuer.key"))
|
|
|
|
|
.arg("-config")
|
|
|
|
|
.arg(dir.join("openssl.cnf"))
|
|
|
|
|
.arg("-extensions")
|
|
|
|
|
.arg("v3_issuer_ca")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("issuer.pem")));
|
|
|
|
|
|
|
|
|
|
match key_spec {
|
|
|
|
|
"ec-p256" => run(Command::new("openssl")
|
|
|
|
|
.arg("ecparam")
|
|
|
|
|
.arg("-name")
|
|
|
|
|
.arg("prime256v1")
|
|
|
|
|
.arg("-genkey")
|
|
|
|
|
.arg("-noout")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("router.key"))),
|
|
|
|
|
"ec-p384" => run(Command::new("openssl")
|
|
|
|
|
.arg("ecparam")
|
|
|
|
|
.arg("-name")
|
|
|
|
|
.arg("secp384r1")
|
|
|
|
|
.arg("-genkey")
|
|
|
|
|
.arg("-noout")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("router.key"))),
|
|
|
|
|
other => panic!("unsupported key_spec {other}"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("req")
|
|
|
|
|
.arg("-new")
|
|
|
|
|
.arg("-key")
|
|
|
|
|
.arg(dir.join("router.key"))
|
|
|
|
|
.arg("-subj")
|
|
|
|
|
.arg("/CN=ROUTER-0000FC10/serialNumber=01020304")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("router.csr")));
|
|
|
|
|
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("ca")
|
|
|
|
|
.arg("-batch")
|
|
|
|
|
.arg("-config")
|
|
|
|
|
.arg(dir.join("openssl.cnf"))
|
|
|
|
|
.arg("-in")
|
|
|
|
|
.arg(dir.join("router.csr"))
|
|
|
|
|
.arg("-extensions")
|
|
|
|
|
.arg("v3_router")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("router.pem")));
|
|
|
|
|
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("x509")
|
|
|
|
|
.arg("-in")
|
|
|
|
|
.arg(dir.join("issuer.pem"))
|
|
|
|
|
.arg("-outform")
|
|
|
|
|
.arg("DER")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("issuer.cer")));
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("x509")
|
|
|
|
|
.arg("-in")
|
|
|
|
|
.arg(dir.join("router.pem"))
|
|
|
|
|
.arg("-outform")
|
|
|
|
|
.arg("DER")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("router.cer")));
|
|
|
|
|
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("ca")
|
|
|
|
|
.arg("-gencrl")
|
|
|
|
|
.arg("-config")
|
|
|
|
|
.arg(dir.join("openssl.cnf"))
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("issuer.crl.pem")));
|
|
|
|
|
run(Command::new("openssl")
|
|
|
|
|
.arg("crl")
|
|
|
|
|
.arg("-in")
|
|
|
|
|
.arg(dir.join("issuer.crl.pem"))
|
|
|
|
|
.arg("-outform")
|
|
|
|
|
.arg("DER")
|
|
|
|
|
.arg("-out")
|
|
|
|
|
.arg(dir.join("issuer.crl")));
|
|
|
|
|
|
|
|
|
|
GeneratedRouter {
|
|
|
|
|
issuer_ca_der: std::fs::read(dir.join("issuer.cer")).expect("read issuer der"),
|
|
|
|
|
router_der: std::fs::read(dir.join("router.cer")).expect("read router der"),
|
|
|
|
|
issuer_crl_der: std::fs::read(dir.join("issuer.crl")).expect("read crl der"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn dummy_pack_with_files(files: Vec<PackFile>) -> PublicationPointSnapshot {
|
|
|
|
|
let now = time::OffsetDateTime::now_utc();
|
|
|
|
|
PublicationPointSnapshot {
|
|
|
|
|
@@ -2577,10 +2982,12 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let child_manifest_uri = "rsync://example.test/repo/child/child.mft".to_string();
|
|
|
|
|
let roa_uri = "rsync://example.test/repo/issuer/a.roa".to_string();
|
|
|
|
|
let aspa_uri = "rsync://example.test/repo/issuer/a.asa".to_string();
|
|
|
|
|
let router_uri = "rsync://example.test/repo/issuer/router.cer".to_string();
|
|
|
|
|
let manifest_hash = sha256_hex(b"manifest-bytes");
|
|
|
|
|
let current_crl_hash = sha256_hex(b"current-crl-bytes");
|
|
|
|
|
let roa_hash = sha256_hex(b"roa-bytes");
|
|
|
|
|
let aspa_hash = sha256_hex(b"aspa-bytes");
|
|
|
|
|
let router_hash = sha256_hex(b"router-bytes");
|
|
|
|
|
let ee_hash = sha256_hex(b"ee-cert-bytes");
|
|
|
|
|
let gate_until = PackTime::from_utc_offset_datetime(now + time::Duration::hours(1));
|
|
|
|
|
ValidatedCaInstanceResult {
|
|
|
|
|
@@ -2641,6 +3048,22 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
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 {
|
|
|
|
|
@@ -2687,6 +3110,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
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,
|
|
|
|
|
@@ -2765,6 +3189,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
&crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: cached.clone(),
|
|
|
|
|
warnings: Vec::new(),
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -2831,6 +3256,39 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_router_key_local_outputs_encodes_router_key_payloads() {
|
|
|
|
|
let ca = CaInstanceHandle {
|
|
|
|
|
depth: 0,
|
|
|
|
|
tal_id: "test-tal".to_string(),
|
|
|
|
|
parent_manifest_rsync_uri: None,
|
|
|
|
|
ca_certificate_der: Vec::new(),
|
|
|
|
|
ca_certificate_rsync_uri: None,
|
|
|
|
|
effective_ip_resources: None,
|
|
|
|
|
effective_as_resources: None,
|
|
|
|
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
manifest_rsync_uri: "rsync://example.test/repo/issuer/issuer.mft".to_string(),
|
|
|
|
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
rrdp_notification_uri: None,
|
|
|
|
|
};
|
|
|
|
|
let outputs = build_router_key_local_outputs(
|
|
|
|
|
&ca,
|
|
|
|
|
&[RouterKeyPayload {
|
|
|
|
|
as_id: 64496,
|
|
|
|
|
ski: vec![0x11; 20],
|
|
|
|
|
spki_der: vec![0x30, 0x00],
|
|
|
|
|
source_object_uri: "rsync://example.test/repo/issuer/router.cer".to_string(),
|
|
|
|
|
source_object_hash: "11".repeat(32),
|
|
|
|
|
source_ee_cert_hash: "11".repeat(32),
|
|
|
|
|
item_effective_until: PackTime { rfc3339_utc: "2026-12-31T00:00:00Z".to_string() },
|
|
|
|
|
}],
|
|
|
|
|
);
|
|
|
|
|
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"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn build_vcir_local_outputs_falls_back_to_decoding_accepted_objects_when_cache_is_empty() {
|
|
|
|
|
let (pack, issuer_ca_der, validation_time) = cernet_publication_point_snapshot_for_vcir_tests();
|
|
|
|
|
@@ -2996,6 +3454,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let objects = crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: Vec::new(),
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -3566,6 +4025,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let objects = crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: Vec::new(),
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -3627,6 +4087,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let objects = crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: Vec::new(),
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -3670,6 +4131,145 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let _ = now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn discover_children_with_router_certificate_records_ok_audit_and_no_child() {
|
|
|
|
|
let g = generate_router_cert_with_variant("ec-p256", true);
|
|
|
|
|
let pack = dummy_pack_with_files(vec![
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/issuer.crl",
|
|
|
|
|
g.issuer_crl_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/router.cer",
|
|
|
|
|
g.router_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let issuer_ca = ResourceCertificate::decode_der(&g.issuer_ca_der).expect("decode issuer");
|
|
|
|
|
let issuer = CaInstanceHandle {
|
|
|
|
|
depth: 0,
|
|
|
|
|
tal_id: "test-tal".to_string(),
|
|
|
|
|
parent_manifest_rsync_uri: None,
|
|
|
|
|
ca_certificate_der: g.issuer_ca_der.clone(),
|
|
|
|
|
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
|
|
|
|
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
|
|
|
|
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
|
|
|
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
manifest_rsync_uri: "rsync://example.test/repo/issuer/issuer.mft".to_string(),
|
|
|
|
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
rrdp_notification_uri: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let out = discover_children_from_fresh_snapshot_with_audit(
|
|
|
|
|
&issuer,
|
|
|
|
|
&pack,
|
|
|
|
|
time::OffsetDateTime::now_utc(),
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.expect("discover router cert");
|
|
|
|
|
assert!(out.children.is_empty());
|
|
|
|
|
assert_eq!(out.audits.len(), 1);
|
|
|
|
|
assert!(matches!(out.audits[0].result, AuditObjectResult::Ok));
|
|
|
|
|
assert!(out.audits[0]
|
|
|
|
|
.detail
|
|
|
|
|
.as_deref()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.contains("validated BGPsec router certificate"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn discover_children_with_non_router_ee_certificate_records_skipped_audit() {
|
|
|
|
|
let g = generate_router_cert_with_variant("ec-p256", false);
|
|
|
|
|
let pack = dummy_pack_with_files(vec![
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/issuer.crl",
|
|
|
|
|
g.issuer_crl_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/router-no-eku.cer",
|
|
|
|
|
g.router_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let issuer_ca = ResourceCertificate::decode_der(&g.issuer_ca_der).expect("decode issuer");
|
|
|
|
|
let issuer = CaInstanceHandle {
|
|
|
|
|
depth: 0,
|
|
|
|
|
tal_id: "test-tal".to_string(),
|
|
|
|
|
parent_manifest_rsync_uri: None,
|
|
|
|
|
ca_certificate_der: g.issuer_ca_der.clone(),
|
|
|
|
|
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
|
|
|
|
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
|
|
|
|
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
|
|
|
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
manifest_rsync_uri: "rsync://example.test/repo/issuer/issuer.mft".to_string(),
|
|
|
|
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
rrdp_notification_uri: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let out = discover_children_from_fresh_snapshot_with_audit(
|
|
|
|
|
&issuer,
|
|
|
|
|
&pack,
|
|
|
|
|
time::OffsetDateTime::now_utc(),
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.expect("discover non-router cert");
|
|
|
|
|
assert!(out.children.is_empty());
|
|
|
|
|
assert_eq!(out.audits.len(), 1);
|
|
|
|
|
assert!(matches!(out.audits[0].result, AuditObjectResult::Skipped));
|
|
|
|
|
assert!(out.audits[0]
|
|
|
|
|
.detail
|
|
|
|
|
.as_deref()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.contains("not a CA resource certificate or BGPsec router certificate"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn discover_children_with_invalid_router_certificate_records_error_audit() {
|
|
|
|
|
let g = generate_router_cert_with_variant("ec-p384", true);
|
|
|
|
|
let pack = dummy_pack_with_files(vec![
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/issuer.crl",
|
|
|
|
|
g.issuer_crl_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
PackFile::from_bytes_compute_sha256(
|
|
|
|
|
"rsync://example.test/repo/issuer/router-invalid.cer",
|
|
|
|
|
g.router_der.clone(),
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let issuer_ca = ResourceCertificate::decode_der(&g.issuer_ca_der).expect("decode issuer");
|
|
|
|
|
let issuer = CaInstanceHandle {
|
|
|
|
|
depth: 0,
|
|
|
|
|
tal_id: "test-tal".to_string(),
|
|
|
|
|
parent_manifest_rsync_uri: None,
|
|
|
|
|
ca_certificate_der: g.issuer_ca_der.clone(),
|
|
|
|
|
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
|
|
|
|
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
|
|
|
|
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
|
|
|
|
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
manifest_rsync_uri: "rsync://example.test/repo/issuer/issuer.mft".to_string(),
|
|
|
|
|
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
|
|
|
|
rrdp_notification_uri: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let out = discover_children_from_fresh_snapshot_with_audit(
|
|
|
|
|
&issuer,
|
|
|
|
|
&pack,
|
|
|
|
|
time::OffsetDateTime::now_utc(),
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
.expect("discover invalid router cert");
|
|
|
|
|
assert!(out.children.is_empty());
|
|
|
|
|
assert_eq!(out.audits.len(), 1);
|
|
|
|
|
assert!(matches!(out.audits[0].result, AuditObjectResult::Error));
|
|
|
|
|
assert!(out.audits[0]
|
|
|
|
|
.detail
|
|
|
|
|
.as_deref()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.contains("router certificate validation failed"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn discover_children_with_audit_records_decode_error_for_corrupt_cer() {
|
|
|
|
|
let g = generate_chain_and_crl();
|
|
|
|
|
@@ -3889,6 +4489,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(projection.objects.vrps.len(), 1);
|
|
|
|
|
assert_eq!(projection.objects.aspas.len(), 1);
|
|
|
|
|
assert_eq!(projection.objects.router_keys.len(), 1);
|
|
|
|
|
assert_eq!(projection.discovered_children.len(), 1);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
projection.discovered_children[0].handle.manifest_rsync_uri,
|
|
|
|
|
@@ -4233,6 +4834,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
let objects = crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: vec![Warning::new("objects warning")],
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
@@ -4318,6 +4920,7 @@ authorityKeyIdentifier = keyid:always
|
|
|
|
|
&crate::validation::objects::ObjectsOutput {
|
|
|
|
|
vrps: Vec::new(),
|
|
|
|
|
aspas: Vec::new(),
|
|
|
|
|
router_keys: Vec::new(),
|
|
|
|
|
local_outputs_cache: Vec::new(),
|
|
|
|
|
warnings: vec![Warning::new("object warning")],
|
|
|
|
|
stats: crate::validation::objects::ObjectsStats::default(),
|
|
|
|
|
|