20260711 补充P1缓存与证书profile回归
This commit is contained in:
parent
1ce897dbff
commit
4a64fa7900
@ -4346,6 +4346,58 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roa_validation_cache_view_blocks_before_safe_reuse_time() {
|
||||
let validation_time = fixed_time("2026-06-05T00:00:00Z");
|
||||
let issuer_der = b"issuer-ca";
|
||||
let crl_bytes = b"current-crl".to_vec();
|
||||
let crl_hash = sha256_32(&crl_bytes);
|
||||
let roa_hash = [0x11; 32];
|
||||
let vcir = sample_roa_cache_vcir(
|
||||
issuer_der,
|
||||
crl_hash,
|
||||
roa_hash,
|
||||
fixed_time("2026-06-07T00:00:00Z"),
|
||||
fixed_time("2026-06-08T00:00:00Z"),
|
||||
);
|
||||
let mut projection = sample_roa_cache_projection(&vcir, roa_hash);
|
||||
projection.entries[0].earliest_safe_reuse_time_unix =
|
||||
Some(fixed_time("2026-06-06T00:00:00Z").unix_timestamp());
|
||||
let view = RoaValidationCacheView::from_projection(&projection, validation_time);
|
||||
let file = PackFile::from_bytes_with_sha256(TEST_ROA_URI, vec![0x01], roa_hash);
|
||||
let mut crl_cache = sample_crl_cache(crl_bytes);
|
||||
|
||||
assert!(matches!(
|
||||
view.lookup(&file, &mut crl_cache, issuer_der, validation_time),
|
||||
RoaCacheLookupResult::ExpiredBlocked
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roa_validation_cache_view_treats_legacy_entry_without_lower_bound_as_miss() {
|
||||
let validation_time = fixed_time("2026-06-05T00:00:00Z");
|
||||
let issuer_der = b"issuer-ca";
|
||||
let crl_hash = sha256_32(b"current-crl");
|
||||
let roa_hash = [0x11; 32];
|
||||
let vcir = sample_roa_cache_vcir(
|
||||
issuer_der,
|
||||
crl_hash,
|
||||
roa_hash,
|
||||
fixed_time("2026-06-07T00:00:00Z"),
|
||||
fixed_time("2026-06-08T00:00:00Z"),
|
||||
);
|
||||
let mut projection = sample_roa_cache_projection(&vcir, roa_hash);
|
||||
projection.entries[0].earliest_safe_reuse_time_unix = None;
|
||||
let view = RoaValidationCacheView::from_projection(&projection, validation_time);
|
||||
let file = PackFile::from_bytes_with_sha256(TEST_ROA_URI, vec![0x01], roa_hash);
|
||||
let mut crl_cache = sample_crl_cache(b"current-crl".to_vec());
|
||||
|
||||
assert!(matches!(
|
||||
view.lookup(&file, &mut crl_cache, issuer_der, validation_time),
|
||||
RoaCacheLookupResult::Miss
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roa_validation_cache_stats_records_vcir_miss_to_timing() {
|
||||
let stats = RoaValidationCacheStats::for_input(RoaValidationCacheInput::enabled(None), 3);
|
||||
|
||||
@ -775,6 +775,22 @@ fn put_vcir_for_failed_fetch_reuse(
|
||||
.expect("put reusable VCIR");
|
||||
}
|
||||
|
||||
fn sample_ca_for_failed_fetch_reuse(vcir: &ValidatedCaInstanceResult) -> CaInstanceHandle {
|
||||
CaInstanceHandle {
|
||||
depth: 0,
|
||||
tal_id: "test-tal".to_string(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
ca_certificate: CaCertificateRef::inline_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: vcir.manifest_rsync_uri.clone(),
|
||||
publication_point_rsync_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||
rrdp_notification_uri: Some("https://example.test/notify.xml".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn never_http_fetcher_returns_error() {
|
||||
let f = NeverHttpFetcher;
|
||||
@ -3937,6 +3953,79 @@ fn project_current_instance_vcir_reuses_local_outputs_and_restores_children() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_current_instance_vcir_does_not_reuse_without_identity() {
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
let child_cert_hash = sha256_hex(b"child-cert");
|
||||
let vcir = sample_vcir_for_projection(now, &child_cert_hash);
|
||||
let store_dir = tempfile::tempdir().expect("store dir");
|
||||
let store = RocksStore::open(store_dir.path()).expect("open rocksdb");
|
||||
store.put_vcir(&vcir).expect("put legacy-style VCIR");
|
||||
let ca = sample_ca_for_failed_fetch_reuse(&vcir);
|
||||
|
||||
let projection = project_current_instance_vcir_on_failed_fetch(
|
||||
&store,
|
||||
&ca,
|
||||
&ManifestFreshError::RepoSyncFailed {
|
||||
detail: "synthetic".to_string(),
|
||||
},
|
||||
&Policy::default(),
|
||||
now,
|
||||
)
|
||||
.expect("project VCIR");
|
||||
|
||||
assert_eq!(
|
||||
projection.source,
|
||||
PublicationPointSource::FailedFetchNoCache
|
||||
);
|
||||
assert!(projection.objects.vrps.is_empty());
|
||||
assert!(projection.discovered_children.is_empty());
|
||||
assert!(
|
||||
projection
|
||||
.warnings
|
||||
.iter()
|
||||
.any(|warning| warning.message.contains("reuse identity is missing"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_current_instance_vcir_does_not_reuse_when_ta_context_changes() {
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
let child_cert_hash = sha256_hex(b"child-cert");
|
||||
let vcir = sample_vcir_for_projection(now, &child_cert_hash);
|
||||
let store_dir = tempfile::tempdir().expect("store dir");
|
||||
let store = RocksStore::open(store_dir.path()).expect("open rocksdb");
|
||||
let policy = Policy::default();
|
||||
let original_ca = sample_ca_for_failed_fetch_reuse(&vcir);
|
||||
put_vcir_for_failed_fetch_reuse(&store, &original_ca, &policy, &vcir);
|
||||
let mut changed_ca = original_ca;
|
||||
changed_ca.tal_id = "different-tal".to_string();
|
||||
|
||||
let projection = project_current_instance_vcir_on_failed_fetch(
|
||||
&store,
|
||||
&changed_ca,
|
||||
&ManifestFreshError::RepoSyncFailed {
|
||||
detail: "synthetic".to_string(),
|
||||
},
|
||||
&policy,
|
||||
now,
|
||||
)
|
||||
.expect("project VCIR");
|
||||
|
||||
assert_eq!(
|
||||
projection.source,
|
||||
PublicationPointSource::FailedFetchNoCache
|
||||
);
|
||||
assert!(projection.objects.vrps.is_empty());
|
||||
assert!(projection.discovered_children.is_empty());
|
||||
assert!(
|
||||
projection
|
||||
.warnings
|
||||
.iter()
|
||||
.any(|warning| warning.message.contains("identity does not match"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_current_instance_vcir_returns_no_output_when_instance_gate_expired() {
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
|
||||
131
tests/test_resource_certificate_profile.rs
Normal file
131
tests/test_resource_certificate_profile.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use der_parser::num_bigint::BigUint;
|
||||
use rpki::data_model::common::X509NameDer;
|
||||
use rpki::data_model::oid::{
|
||||
OID_AUTONOMOUS_SYS_IDS, OID_BASIC_CONSTRAINTS, OID_CERTIFICATE_POLICIES, OID_CP_IPADDR_ASNUMBER,
|
||||
};
|
||||
use rpki::data_model::rc::{
|
||||
AsIdOrRange, AsIdentifierChoice, AsResourceSet, BasicConstraintsProfile,
|
||||
CertificatePoliciesProfile, RcExtensions, ResourceCertKind, ResourceCertificate,
|
||||
ResourceCertificateProfileError, ResourceCertificateRole, RpkixTbsCertificate,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
fn valid_ca_certificate() -> ResourceCertificate {
|
||||
let validation_time = OffsetDateTime::from_unix_timestamp(0).expect("valid epoch");
|
||||
ResourceCertificate {
|
||||
raw_der: Vec::new(),
|
||||
kind: ResourceCertKind::Ca,
|
||||
tbs: RpkixTbsCertificate {
|
||||
version: 2,
|
||||
serial_number: BigUint::from(1u32),
|
||||
signature_algorithm: "1.2.840.113549.1.1.11".to_string(),
|
||||
issuer_name: X509NameDer(b"CN=issuer".to_vec()),
|
||||
subject_name: X509NameDer(b"CN=subject".to_vec()),
|
||||
validity_not_before: validation_time,
|
||||
validity_not_after: validation_time,
|
||||
subject_public_key_info: Vec::new(),
|
||||
extensions: RcExtensions {
|
||||
basic_constraints_ca: true,
|
||||
basic_constraints: Some(BasicConstraintsProfile {
|
||||
ca: true,
|
||||
critical: true,
|
||||
path_len_constraint: None,
|
||||
}),
|
||||
subject_key_identifier: None,
|
||||
authority_key_identifier: None,
|
||||
crl_distribution_points_uris: None,
|
||||
ca_issuers_uris: None,
|
||||
subject_info_access: None,
|
||||
certificate_policies_oid: Some(OID_CP_IPADDR_ASNUMBER.to_string()),
|
||||
certificate_policies: Some(CertificatePoliciesProfile {
|
||||
policy_oid: OID_CP_IPADDR_ASNUMBER.to_string(),
|
||||
qualifier_oids: Vec::new(),
|
||||
}),
|
||||
extension_oids: vec![
|
||||
OID_BASIC_CONSTRAINTS.to_string(),
|
||||
OID_CERTIFICATE_POLICIES.to_string(),
|
||||
],
|
||||
ip_resources: None,
|
||||
as_resources: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_profile_accepts_required_basic_constraints_and_policy() {
|
||||
valid_ca_certificate()
|
||||
.validate_rfc6487_profile(ResourceCertificateRole::Ca)
|
||||
.expect("valid CA profile");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_profile_rejects_noncritical_basic_constraints() {
|
||||
let mut certificate = valid_ca_certificate();
|
||||
certificate
|
||||
.tbs
|
||||
.extensions
|
||||
.basic_constraints
|
||||
.as_mut()
|
||||
.expect("constraints")
|
||||
.critical = false;
|
||||
|
||||
assert!(matches!(
|
||||
certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
|
||||
Err(ResourceCertificateProfileError::BasicConstraintsCriticality)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_profile_rejects_unknown_extension() {
|
||||
let mut certificate = valid_ca_certificate();
|
||||
certificate
|
||||
.tbs
|
||||
.extensions
|
||||
.extension_oids
|
||||
.push("1.2.3.4".to_string());
|
||||
|
||||
assert!(matches!(
|
||||
certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
|
||||
Err(ResourceCertificateProfileError::DisallowedExtension { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_profile_rejects_non_cps_policy_qualifier() {
|
||||
let mut certificate = valid_ca_certificate();
|
||||
certificate
|
||||
.tbs
|
||||
.extensions
|
||||
.certificate_policies
|
||||
.as_mut()
|
||||
.expect("certificate policies")
|
||||
.qualifier_oids
|
||||
.push("1.2.3.4".to_string());
|
||||
|
||||
assert!(matches!(
|
||||
certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
|
||||
Err(ResourceCertificateProfileError::CertificatePoliciesInvalidQualifier(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ca_profile_rejects_autonomous_system_rdi() {
|
||||
let mut certificate = valid_ca_certificate();
|
||||
certificate.tbs.extensions.as_resources = Some(AsResourceSet {
|
||||
asnum: None,
|
||||
rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
|
||||
64512,
|
||||
)])),
|
||||
});
|
||||
certificate
|
||||
.tbs
|
||||
.extensions
|
||||
.extension_oids
|
||||
.push(OID_AUTONOMOUS_SYS_IDS.to_string());
|
||||
|
||||
assert!(matches!(
|
||||
certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
|
||||
Err(ResourceCertificateProfileError::AsResourcesRdiPresent)
|
||||
));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user