rpki/tests/test_ccr_manifest_location_interop.rs

140 lines
4.8 KiB
Rust

use rpki::ccr::{CcrAccumulator, build_roa_payload_state};
use rpki::data_model::roa::{IpPrefix, RoaAfi};
use rpki::storage::{PackTime, VcirCcrManifestProjection};
use rpki::validation::objects::Vrp;
const MANIFEST_URI: &str = "rsync://example.test/repo/current.mft";
const OID_AD_SIGNED_OBJECT: &[u8] = &[0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x0b];
const OID_AD_RPKI_NOTIFY: &[u8] = &[0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x0d];
fn der_length(len: usize) -> Vec<u8> {
if len < 0x80 {
return vec![len as u8];
}
let mut bytes = len.to_be_bytes().to_vec();
let first = bytes
.iter()
.position(|byte| *byte != 0)
.expect("non-zero length");
bytes.drain(..first);
let mut out = vec![0x80 | bytes.len() as u8];
out.extend_from_slice(&bytes);
out
}
fn der_tlv(tag: u8, value: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
out.extend(der_length(value.len()));
out.extend_from_slice(value);
out
}
fn access_description_der(access_method_oid: &[u8], uri: &str) -> Vec<u8> {
let oid = der_tlv(0x06, access_method_oid);
let location = der_tlv(0x86, uri.as_bytes());
let mut body = oid;
body.extend(location);
der_tlv(0x30, &body)
}
fn projection(locations: Vec<Vec<u8>>) -> VcirCcrManifestProjection {
VcirCcrManifestProjection {
manifest_rsync_uri: MANIFEST_URI.to_string(),
manifest_sha256: vec![0x11; 32],
manifest_size: 1000,
manifest_ee_aki: vec![0x22; 20],
manifest_number_be: vec![1],
manifest_this_update: PackTime::from_utc_offset_datetime(time::OffsetDateTime::now_utc()),
manifest_sia_locations_der: locations,
subordinate_skis: vec![vec![0x33; 20]],
}
}
#[test]
fn historical_projection_normalizes_locations_and_accounts_memory() {
let signed_object = access_description_der(OID_AD_SIGNED_OBJECT, MANIFEST_URI);
let notify = access_description_der(
OID_AD_RPKI_NOTIFY,
"https://rrdp.example.test/notification.xml",
);
let projection = projection(vec![notify, signed_object]);
let mut accumulator = CcrAccumulator::new(Vec::new());
accumulator
.append_manifest_projection(&projection)
.expect("append normalized historical projection");
accumulator
.append_manifest_projection(&projection)
.expect("duplicate matching projection is idempotent");
assert_eq!(accumulator.manifest_count(), 1);
let stats = accumulator.memory_stats();
assert_eq!(stats.manifest_count, 1);
assert_eq!(stats.locations_der_count, 1);
assert_eq!(stats.subordinate_ski_count, 1);
assert!(stats.estimated_heap_bytes > 0);
assert!(stats.btree_entry_shallow_bytes > 0);
let mut conflicting = projection;
conflicting.subordinate_skis.push(vec![0x44; 20]);
let error = accumulator
.append_manifest_projection(&conflicting)
.expect_err("same hash with different projection must fail");
assert!(error.contains("duplicate manifest hash"), "{error}");
}
#[test]
fn historical_projection_rejects_invalid_location_sets() {
let mut accumulator = CcrAccumulator::new(Vec::new());
let empty_error = accumulator
.append_manifest_projection(&projection(Vec::new()))
.expect_err("empty location set must fail");
assert!(empty_error.contains("contains 0"), "{empty_error}");
let signed_object = access_description_der(OID_AD_SIGNED_OBJECT, MANIFEST_URI);
let duplicate_error = accumulator
.append_manifest_projection(&projection(vec![signed_object.clone(), signed_object]))
.expect_err("duplicate matching locations must fail");
assert!(duplicate_error.contains("contains 2"), "{duplicate_error}");
let malformed_error = accumulator
.append_manifest_projection(&projection(vec![vec![0x30, 0x01, 0x06]]))
.expect_err("malformed location DER must fail");
assert!(
malformed_error.contains("truncated DER"),
"{malformed_error}"
);
}
#[test]
fn roa_payload_builder_encodes_large_non_byte_aligned_family() {
let mut vrps = Vec::new();
for octet in 0..=255 {
vrps.push(Vrp {
asn: 64_496,
prefix: IpPrefix {
afi: RoaAfi::Ipv4,
prefix_len: 24,
addr: [10, octet, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
},
max_length: 24,
});
}
vrps.push(Vrp {
asn: 64_496,
prefix: IpPrefix {
afi: RoaAfi::Ipv4,
prefix_len: 13,
addr: [10, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
},
max_length: 24,
});
let state = build_roa_payload_state(&vrps).expect("build ROA payload state");
assert_eq!(state.rps.len(), 1);
assert_eq!(state.rps[0].as_id, 64_496);
assert_eq!(state.rps[0].ip_addr_blocks.len(), 1);
assert!(state.rps[0].ip_addr_blocks[0].len() > 128);
}