20260701 add validation-update-03 VRS mode
This commit is contained in:
@@ -13,7 +13,7 @@ use crate::parallel::config::ParallelPhase2Config;
|
||||
use crate::parallel::object_worker::{
|
||||
ObjectTaskExecutor, ObjectWorkerPool, ObjectWorkerSubmitError,
|
||||
};
|
||||
use crate::policy::{Policy, SignedObjectFailurePolicy};
|
||||
use crate::policy::{Policy, ResourceValidationMode, SignedObjectFailurePolicy};
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, RoaCacheObjectMeta, RoaCacheProjection, VcirLocalOutput,
|
||||
@@ -1226,6 +1226,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
collect_vcir_local_outputs,
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
)
|
||||
}
|
||||
blocked @ (RoaCacheLookupResult::HashBlocked
|
||||
@@ -1251,6 +1252,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
collect_vcir_local_outputs,
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1273,6 +1275,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
collect_vcir_local_outputs,
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
)
|
||||
};
|
||||
match result.outcome {
|
||||
@@ -1386,6 +1389,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
collect_vcir_local_outputs,
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
) {
|
||||
Ok((att, local_output)) => {
|
||||
stats.aspa_ok += 1;
|
||||
@@ -1735,6 +1739,7 @@ pub(crate) struct RoaTaskShared {
|
||||
issuer_resources_index: Arc<IssuerResourcesIndex>,
|
||||
issuer_effective_ip: Option<Arc<crate::data_model::rc::IpResourceSet>>,
|
||||
issuer_effective_as: Option<Arc<crate::data_model::rc::AsResourceSet>>,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1746,6 +1751,7 @@ pub(crate) struct OwnedRoaTask {
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
pub(crate) submitted_at: Option<Instant>,
|
||||
}
|
||||
|
||||
@@ -1854,6 +1860,7 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
task.collect_vcir_local_outputs,
|
||||
task.strict_cms_der,
|
||||
task.strict_name,
|
||||
task.resource_validation_mode,
|
||||
)
|
||||
.map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk {
|
||||
vrps,
|
||||
@@ -1884,6 +1891,7 @@ pub(crate) struct ParallelObjectsStage {
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
roa_task_indices: Vec<usize>,
|
||||
cached_roa_results: Vec<RoaTaskResult>,
|
||||
roa_cache_stats: RoaValidationCacheStats,
|
||||
@@ -1921,6 +1929,7 @@ impl ParallelObjectsStage {
|
||||
collect_vcir_local_outputs: self.collect_vcir_local_outputs,
|
||||
strict_cms_der: self.strict_cms_der,
|
||||
strict_name: self.strict_name,
|
||||
resource_validation_mode: self.resource_validation_mode,
|
||||
submitted_at: None,
|
||||
});
|
||||
});
|
||||
@@ -2257,11 +2266,13 @@ pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache<P: Publicati
|
||||
)),
|
||||
issuer_effective_ip: issuer_effective_ip.cloned().map(Arc::new),
|
||||
issuer_effective_as: issuer_effective_as.cloned().map(Arc::new),
|
||||
resource_validation_mode: policy.resource_validation_mode,
|
||||
}),
|
||||
validation_time,
|
||||
collect_vcir_local_outputs,
|
||||
strict_cms_der: policy.strict.cms_der,
|
||||
strict_name: policy.strict.name,
|
||||
resource_validation_mode: policy.resource_validation_mode,
|
||||
roa_task_indices,
|
||||
cached_roa_results,
|
||||
roa_cache_stats,
|
||||
@@ -2370,6 +2381,7 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
collect_vcir_local_outputs,
|
||||
strict_cms_der,
|
||||
strict_name,
|
||||
shared.resource_validation_mode,
|
||||
) {
|
||||
Ok((att, local_output)) => {
|
||||
stats.aspa_ok += 1;
|
||||
@@ -2604,6 +2616,7 @@ pub(crate) fn validate_roa_task_serial(
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
) -> RoaTaskResult {
|
||||
let outcome = process_roa_with_issuer(
|
||||
task.file,
|
||||
@@ -2621,6 +2634,7 @@ pub(crate) fn validate_roa_task_serial(
|
||||
collect_vcir_local_outputs,
|
||||
strict_cms_der,
|
||||
strict_name,
|
||||
resource_validation_mode,
|
||||
)
|
||||
.map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk {
|
||||
vrps,
|
||||
@@ -2655,6 +2669,7 @@ fn process_roa_with_issuer(
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>, Option<RoaCacheObjectMeta>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@@ -2706,15 +2721,16 @@ fn process_roa_with_issuer(
|
||||
let _subset = timing
|
||||
.as_ref()
|
||||
.map(|t| t.span_phase("objects_roa_validate_ee_resources_subset_total"));
|
||||
validate_ee_resources_subset(
|
||||
let ee_vrs = validate_ee_resources_for_mode(
|
||||
&ee.resource_cert,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
issuer_resources_index,
|
||||
resource_validation_mode,
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
let vrps = roa_to_vrps(&roa);
|
||||
let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?;
|
||||
let cache_object_meta = RoaCacheObjectMeta {
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_hash: file.sha256,
|
||||
@@ -2777,6 +2793,7 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>, Option<RoaCacheObjectMeta>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@@ -2834,15 +2851,16 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
let _subset = timing
|
||||
.as_ref()
|
||||
.map(|t| t.span_phase("objects_roa_validate_ee_resources_subset_total"));
|
||||
validate_ee_resources_subset(
|
||||
let ee_vrs = validate_ee_resources_for_mode(
|
||||
&ee.resource_cert,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
issuer_resources_index,
|
||||
resource_validation_mode,
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
let vrps = roa_to_vrps(&roa);
|
||||
let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?;
|
||||
let cache_object_meta = RoaCacheObjectMeta {
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
source_object_hash: file.sha256,
|
||||
@@ -2905,6 +2923,7 @@ fn process_aspa_with_issuer(
|
||||
collect_vcir_local_outputs: bool,
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
) -> Result<(AspaAttestation, Option<VcirLocalOutput>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@@ -2956,14 +2975,17 @@ fn process_aspa_with_issuer(
|
||||
let _subset = timing
|
||||
.as_ref()
|
||||
.map(|t| t.span_phase("objects_aspa_validate_ee_resources_subset_total"));
|
||||
validate_ee_resources_subset(
|
||||
let ee_vrs = validate_ee_resources_for_mode(
|
||||
&ee.resource_cert,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
issuer_resources_index,
|
||||
resource_validation_mode,
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
validate_aspa_customer_in_vrs(&aspa, ee_vrs.asn.as_ref())?;
|
||||
|
||||
let attestation = AspaAttestation {
|
||||
customer_as_id: aspa.aspa.customer_as_id,
|
||||
provider_as_ids: aspa.aspa.provider_as_ids.clone(),
|
||||
@@ -3119,6 +3141,176 @@ fn validate_ee_resources_subset(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EeVerifiedResources {
|
||||
ip: Option<crate::data_model::rc::IpResourceSet>,
|
||||
asn: Option<crate::data_model::rc::AsResourceSet>,
|
||||
}
|
||||
|
||||
fn validate_ee_resources_for_mode(
|
||||
ee: &ResourceCertificate,
|
||||
issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>,
|
||||
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
|
||||
issuer_resources_index: &IssuerResourcesIndex,
|
||||
mode: ResourceValidationMode,
|
||||
) -> Result<EeVerifiedResources, ObjectValidateError> {
|
||||
match mode {
|
||||
ResourceValidationMode::Rfc6487 => {
|
||||
validate_ee_resources_subset(
|
||||
ee,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
issuer_resources_index,
|
||||
)?;
|
||||
Ok(EeVerifiedResources {
|
||||
ip: ee.tbs.extensions.ip_resources.clone(),
|
||||
asn: ee.tbs.extensions.as_resources.clone(),
|
||||
})
|
||||
}
|
||||
ResourceValidationMode::ValidationUpdate03 => {
|
||||
let ip = match ee.tbs.extensions.ip_resources.as_ref() {
|
||||
Some(child_ip) => Some(intersect_ee_ip_resources_vrs(
|
||||
child_ip,
|
||||
issuer_effective_ip,
|
||||
issuer_resources_index,
|
||||
)?),
|
||||
None => None,
|
||||
};
|
||||
let asn = match ee.tbs.extensions.as_resources.as_ref() {
|
||||
Some(child_as) => Some(intersect_ee_as_resources_vrs(
|
||||
child_as,
|
||||
issuer_effective_as,
|
||||
issuer_resources_index,
|
||||
)?),
|
||||
None => None,
|
||||
};
|
||||
Ok(EeVerifiedResources { ip, asn })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn intersect_ee_ip_resources_vrs(
|
||||
child_ip: &crate::data_model::rc::IpResourceSet,
|
||||
issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>,
|
||||
issuer_resources_index: &IssuerResourcesIndex,
|
||||
) -> Result<crate::data_model::rc::IpResourceSet, ObjectValidateError> {
|
||||
if child_ip.has_any_inherit() {
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
}
|
||||
let _ = issuer_effective_ip;
|
||||
let mut families = Vec::new();
|
||||
for fam in &child_ip.families {
|
||||
let parent_intervals = match fam.afi {
|
||||
crate::data_model::rc::Afi::Ipv4 => issuer_resources_index.ip_v4.as_deref(),
|
||||
crate::data_model::rc::Afi::Ipv6 => issuer_resources_index.ip_v6.as_deref(),
|
||||
}
|
||||
.unwrap_or(&[]);
|
||||
let items = match &fam.choice {
|
||||
IpAddressChoice::Inherit => return Err(ObjectValidateError::EeResourcesNotSubset),
|
||||
IpAddressChoice::AddressesOrRanges(items) => items,
|
||||
};
|
||||
let intersections = intersect_ip_items_with_parent_intervals(items, parent_intervals);
|
||||
if !intersections.is_empty() {
|
||||
families.push(crate::data_model::rc::IpAddressFamily {
|
||||
afi: fam.afi,
|
||||
choice: IpAddressChoice::AddressesOrRanges(ip_intervals_to_ranges(
|
||||
fam.afi,
|
||||
&intersections,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(crate::data_model::rc::IpResourceSet { families })
|
||||
}
|
||||
|
||||
fn intersect_ee_as_resources_vrs(
|
||||
child_as: &crate::data_model::rc::AsResourceSet,
|
||||
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
|
||||
issuer_resources_index: &IssuerResourcesIndex,
|
||||
) -> Result<crate::data_model::rc::AsResourceSet, ObjectValidateError> {
|
||||
let _ = issuer_effective_as;
|
||||
if matches!(child_as.asnum, Some(AsIdentifierChoice::Inherit))
|
||||
|| matches!(child_as.rdi, Some(AsIdentifierChoice::Inherit))
|
||||
{
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
}
|
||||
let asnum = child_as.asnum.as_ref().map(|choice| {
|
||||
let child_intervals = as_choice_to_merged_intervals(choice);
|
||||
AsIdentifierChoice::AsIdsOrRanges(as_intervals_to_items(&intersect_as_intervals(
|
||||
&child_intervals,
|
||||
issuer_resources_index.asnum.as_deref().unwrap_or(&[]),
|
||||
)))
|
||||
});
|
||||
let rdi = child_as.rdi.as_ref().map(|choice| {
|
||||
let child_intervals = as_choice_to_merged_intervals(choice);
|
||||
AsIdentifierChoice::AsIdsOrRanges(as_intervals_to_items(&intersect_as_intervals(
|
||||
&child_intervals,
|
||||
issuer_resources_index.rdi.as_deref().unwrap_or(&[]),
|
||||
)))
|
||||
});
|
||||
Ok(crate::data_model::rc::AsResourceSet { asnum, rdi })
|
||||
}
|
||||
|
||||
fn roa_to_vrps_with_vrs(
|
||||
roa: &RoaObject,
|
||||
ee_vrs_ip: Option<&crate::data_model::rc::IpResourceSet>,
|
||||
) -> Result<Vec<Vrp>, ObjectValidateError> {
|
||||
let vrps = roa_to_vrps(roa);
|
||||
let Some(ee_vrs_ip) = ee_vrs_ip else {
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
};
|
||||
for vrp in &vrps {
|
||||
let rc_prefix = roa_prefix_to_rc_prefix(&vrp.prefix);
|
||||
if !ee_vrs_ip.contains_prefix(&rc_prefix) {
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
}
|
||||
}
|
||||
Ok(vrps)
|
||||
}
|
||||
|
||||
fn validate_aspa_customer_in_vrs(
|
||||
aspa: &AspaObject,
|
||||
ee_vrs_as: Option<&crate::data_model::rc::AsResourceSet>,
|
||||
) -> Result<(), ObjectValidateError> {
|
||||
let Some(ee_vrs_as) = ee_vrs_as else {
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
};
|
||||
if !as_resource_set_contains_asn(ee_vrs_as, aspa.aspa.customer_as_id) {
|
||||
return Err(ObjectValidateError::EeResourcesNotSubset);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn as_resource_set_contains_asn(
|
||||
resources: &crate::data_model::rc::AsResourceSet,
|
||||
asn: u32,
|
||||
) -> bool {
|
||||
let Some(choice) = resources.asnum.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
match choice {
|
||||
AsIdentifierChoice::Inherit => false,
|
||||
AsIdentifierChoice::AsIdsOrRanges(items) => items.iter().any(|item| match item {
|
||||
crate::data_model::rc::AsIdOrRange::Id(id) => *id == asn,
|
||||
crate::data_model::rc::AsIdOrRange::Range { min, max } => *min <= asn && asn <= *max,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn roa_prefix_to_rc_prefix(prefix: &IpPrefix) -> RcIpPrefix {
|
||||
let afi = match prefix.afi {
|
||||
RoaAfi::Ipv4 => crate::data_model::rc::Afi::Ipv4,
|
||||
RoaAfi::Ipv6 => crate::data_model::rc::Afi::Ipv6,
|
||||
};
|
||||
let mut addr = prefix.addr.to_vec();
|
||||
addr.truncate(afi.octets_len());
|
||||
RcIpPrefix {
|
||||
afi,
|
||||
prefix_len: prefix.prefix_len,
|
||||
addr,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_resources_is_subset(child: &AsResourceSet, parent: &AsResourceSet) -> bool {
|
||||
as_choice_subset(child.asnum.as_ref(), parent.asnum.as_ref())
|
||||
&& as_choice_subset(child.rdi.as_ref(), parent.rdi.as_ref())
|
||||
@@ -3292,6 +3484,121 @@ fn ip_resources_is_subset_indexed(
|
||||
true
|
||||
}
|
||||
|
||||
fn ip_items_to_merged_intervals(
|
||||
items: &[crate::data_model::rc::IpAddressOrRange],
|
||||
) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
let mut intervals = Vec::new();
|
||||
for item in items {
|
||||
match item {
|
||||
IpAddressOrRange::Prefix(p) => intervals.push(prefix_to_range(p)),
|
||||
IpAddressOrRange::Range(r) => intervals.push((r.min.clone(), r.max.clone())),
|
||||
}
|
||||
}
|
||||
intervals.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
merge_ip_intervals_in_place(&mut intervals);
|
||||
intervals
|
||||
}
|
||||
|
||||
fn intersect_ip_items_with_parent_intervals(
|
||||
items: &[crate::data_model::rc::IpAddressOrRange],
|
||||
parent_intervals: &[(Vec<u8>, Vec<u8>)],
|
||||
) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
let child_intervals = ip_items_to_merged_intervals(items);
|
||||
let mut out = Vec::new();
|
||||
let mut parent_index = 0usize;
|
||||
for (child_min, child_max) in &child_intervals {
|
||||
while parent_index < parent_intervals.len()
|
||||
&& parent_intervals[parent_index].1.as_slice() < child_min.as_slice()
|
||||
{
|
||||
parent_index += 1;
|
||||
}
|
||||
let mut scan = parent_index;
|
||||
while scan < parent_intervals.len()
|
||||
&& parent_intervals[scan].0.as_slice() <= child_max.as_slice()
|
||||
{
|
||||
let (parent_min, parent_max) = &parent_intervals[scan];
|
||||
let min = if bytes_leq(child_min, parent_min) {
|
||||
parent_min.clone()
|
||||
} else {
|
||||
child_min.clone()
|
||||
};
|
||||
let max = if bytes_leq(child_max, parent_max) {
|
||||
child_max.clone()
|
||||
} else {
|
||||
parent_max.clone()
|
||||
};
|
||||
if bytes_leq(&min, &max) {
|
||||
out.push((min, max));
|
||||
}
|
||||
scan += 1;
|
||||
}
|
||||
}
|
||||
merge_ip_intervals_in_place(&mut out);
|
||||
out
|
||||
}
|
||||
|
||||
fn ip_intervals_to_ranges(
|
||||
afi: crate::data_model::rc::Afi,
|
||||
intervals: &[(Vec<u8>, Vec<u8>)],
|
||||
) -> Vec<crate::data_model::rc::IpAddressOrRange> {
|
||||
intervals
|
||||
.iter()
|
||||
.map(|(min, max)| {
|
||||
IpAddressOrRange::Range(crate::data_model::rc::IpAddressRange {
|
||||
min: normalize_ip_bytes(afi, min),
|
||||
max: normalize_ip_bytes(afi, max),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn normalize_ip_bytes(afi: crate::data_model::rc::Afi, bytes: &[u8]) -> Vec<u8> {
|
||||
let target_len = afi.octets_len();
|
||||
if bytes.len() == target_len {
|
||||
return bytes.to_vec();
|
||||
}
|
||||
let mut out = vec![0u8; target_len];
|
||||
let copy_len = bytes.len().min(target_len);
|
||||
out[..copy_len].copy_from_slice(&bytes[..copy_len]);
|
||||
out
|
||||
}
|
||||
|
||||
fn intersect_as_intervals(child: &[(u32, u32)], parent: &[(u32, u32)]) -> Vec<(u32, u32)> {
|
||||
let mut out = Vec::new();
|
||||
let mut parent_index = 0usize;
|
||||
for (child_min, child_max) in child {
|
||||
while parent_index < parent.len() && parent[parent_index].1 < *child_min {
|
||||
parent_index += 1;
|
||||
}
|
||||
let mut scan = parent_index;
|
||||
while scan < parent.len() && parent[scan].0 <= *child_max {
|
||||
let min = (*child_min).max(parent[scan].0);
|
||||
let max = (*child_max).min(parent[scan].1);
|
||||
if min <= max {
|
||||
out.push((min, max));
|
||||
}
|
||||
scan += 1;
|
||||
}
|
||||
}
|
||||
merge_as_intervals(&out)
|
||||
}
|
||||
|
||||
fn as_intervals_to_items(intervals: &[(u32, u32)]) -> Vec<crate::data_model::rc::AsIdOrRange> {
|
||||
intervals
|
||||
.iter()
|
||||
.map(|(min, max)| {
|
||||
if min == max {
|
||||
crate::data_model::rc::AsIdOrRange::Id(*min)
|
||||
} else {
|
||||
crate::data_model::rc::AsIdOrRange::Range {
|
||||
min: *min,
|
||||
max: *max,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum AfiKey {
|
||||
V4,
|
||||
@@ -4724,6 +5031,120 @@ mod tests {
|
||||
assert!(matches!(err, ObjectValidateError::EeResourcesNotSubset));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_update_03_ee_resources_reduce_to_vrs_for_roa_checks() {
|
||||
let roa_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa");
|
||||
let roa_der = std::fs::read(roa_path).expect("read roa");
|
||||
let roa = RoaObject::decode_der(&roa_der).expect("decode roa");
|
||||
let ee = &roa.signed_object.signed_data.certificates[0].resource_cert;
|
||||
let ee_ip = ee
|
||||
.tbs
|
||||
.extensions
|
||||
.ip_resources
|
||||
.as_ref()
|
||||
.expect("fixture EE has IP resources");
|
||||
let first_family = ee_ip.families.first().expect("family");
|
||||
let first_item = match &first_family.choice {
|
||||
IpAddressChoice::AddressesOrRanges(items) => items.first().expect("item").clone(),
|
||||
IpAddressChoice::Inherit => panic!("fixture should not inherit"),
|
||||
};
|
||||
let issuer_ip = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
afi: first_family.afi,
|
||||
choice: IpAddressChoice::AddressesOrRanges(vec![first_item]),
|
||||
}],
|
||||
};
|
||||
let idx = build_issuer_resources_index(Some(&issuer_ip), None);
|
||||
|
||||
let strict_err = validate_ee_resources_for_mode(
|
||||
ee,
|
||||
Some(&issuer_ip),
|
||||
None,
|
||||
&idx,
|
||||
ResourceValidationMode::Rfc6487,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(matches!(
|
||||
strict_err,
|
||||
ObjectValidateError::EeResourcesNotSubset
|
||||
));
|
||||
|
||||
let vrs = validate_ee_resources_for_mode(
|
||||
ee,
|
||||
Some(&issuer_ip),
|
||||
None,
|
||||
&idx,
|
||||
ResourceValidationMode::ValidationUpdate03,
|
||||
)
|
||||
.expect("vrs resource resolution");
|
||||
assert_eq!(vrs.ip.expect("vrs ip").families.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_update_03_ee_as_vrs_filters_aspa_customer_resources() {
|
||||
let issuer_as = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![
|
||||
AsIdOrRange::Range {
|
||||
min: 64500,
|
||||
max: 64510,
|
||||
},
|
||||
])),
|
||||
rdi: None,
|
||||
};
|
||||
let child_as = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![
|
||||
AsIdOrRange::Range {
|
||||
min: 64505,
|
||||
max: 64520,
|
||||
},
|
||||
])),
|
||||
rdi: None,
|
||||
};
|
||||
let idx = build_issuer_resources_index(None, Some(&issuer_as));
|
||||
let vrs = intersect_ee_as_resources_vrs(&child_as, Some(&issuer_as), &idx)
|
||||
.expect("intersect AS resources");
|
||||
assert!(as_resource_set_contains_asn(&vrs, 64505));
|
||||
assert!(as_resource_set_contains_asn(&vrs, 64510));
|
||||
assert!(!as_resource_set_contains_asn(&vrs, 64511));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_update_03_empty_ee_vrs_rejects_roa_and_aspa_outputs() {
|
||||
let roa_der =
|
||||
fixture_bytes("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa");
|
||||
let roa = RoaObject::decode_der(&roa_der).expect("decode roa");
|
||||
let empty_ip_vrs = IpResourceSet {
|
||||
families: Vec::new(),
|
||||
};
|
||||
let roa_err = roa_to_vrps_with_vrs(&roa, Some(&empty_ip_vrs)).unwrap_err();
|
||||
assert!(matches!(roa_err, ObjectValidateError::EeResourcesNotSubset));
|
||||
let missing_ip_err = roa_to_vrps_with_vrs(&roa, None).unwrap_err();
|
||||
assert!(matches!(
|
||||
missing_ip_err,
|
||||
ObjectValidateError::EeResourcesNotSubset
|
||||
));
|
||||
|
||||
let aspa_der = fixture_bytes(
|
||||
"tests/fixtures/repository/chloe.sobornost.net/rpki/RIPE-nljobsnijders/5m80fwYws_3FiFD7JiQjAqZ1RYQ.asa",
|
||||
);
|
||||
let aspa = AspaObject::decode_der(&aspa_der).expect("decode aspa");
|
||||
let empty_as_vrs = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(Vec::new())),
|
||||
rdi: None,
|
||||
};
|
||||
let aspa_err = validate_aspa_customer_in_vrs(&aspa, Some(&empty_as_vrs)).unwrap_err();
|
||||
assert!(matches!(
|
||||
aspa_err,
|
||||
ObjectValidateError::EeResourcesNotSubset
|
||||
));
|
||||
let missing_as_err = validate_aspa_customer_in_vrs(&aspa, None).unwrap_err();
|
||||
assert!(matches!(
|
||||
missing_as_err,
|
||||
ObjectValidateError::EeResourcesNotSubset
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_rfc_refs_for_crl_selection_distinguishes_crl_errors() {
|
||||
assert_eq!(
|
||||
@@ -4976,11 +5397,13 @@ mod tests {
|
||||
issuer_resources_index: Arc::new(IssuerResourcesIndex::default()),
|
||||
issuer_effective_ip: None,
|
||||
issuer_effective_as: None,
|
||||
resource_validation_mode: ResourceValidationMode::default(),
|
||||
}),
|
||||
validation_time: OffsetDateTime::now_utc(),
|
||||
collect_vcir_local_outputs: false,
|
||||
strict_cms_der: false,
|
||||
strict_name: false,
|
||||
resource_validation_mode: ResourceValidationMode::default(),
|
||||
roa_task_indices: vec![0, 1],
|
||||
cached_roa_results: Vec::new(),
|
||||
roa_cache_stats: RoaValidationCacheStats::default(),
|
||||
|
||||
Reference in New Issue
Block a user