205 lines
6.9 KiB
Rust
205 lines
6.9 KiB
Rust
use rpki::data_model::rc::{AsResourceSet, IpResourceSet};
|
|
|
|
fn len_bytes(len: usize) -> Vec<u8> {
|
|
if len < 128 {
|
|
vec![len as u8]
|
|
} else {
|
|
let mut tmp = Vec::new();
|
|
let mut n = len;
|
|
while n > 0 {
|
|
tmp.push((n & 0xFF) as u8);
|
|
n >>= 8;
|
|
}
|
|
tmp.reverse();
|
|
let mut out = vec![0x80 | (tmp.len() as u8)];
|
|
out.extend(tmp);
|
|
out
|
|
}
|
|
}
|
|
|
|
fn tlv(tag: u8, content: &[u8]) -> Vec<u8> {
|
|
let mut out = vec![tag];
|
|
out.extend(len_bytes(content.len()));
|
|
out.extend_from_slice(content);
|
|
out
|
|
}
|
|
|
|
fn der_sequence(children: Vec<Vec<u8>>) -> Vec<u8> {
|
|
let mut content = Vec::new();
|
|
for c in children {
|
|
content.extend(c);
|
|
}
|
|
tlv(0x30, &content)
|
|
}
|
|
|
|
fn der_octet_string(bytes: &[u8]) -> Vec<u8> {
|
|
tlv(0x04, bytes)
|
|
}
|
|
|
|
fn der_null() -> Vec<u8> {
|
|
vec![0x05, 0x00]
|
|
}
|
|
|
|
fn der_integer_bytes(bytes: &[u8]) -> Vec<u8> {
|
|
tlv(0x02, bytes)
|
|
}
|
|
|
|
fn der_integer_u64(v: u64) -> Vec<u8> {
|
|
let mut bytes = Vec::new();
|
|
let mut n = v;
|
|
if n == 0 {
|
|
bytes.push(0);
|
|
} else {
|
|
while n > 0 {
|
|
bytes.push((n & 0xFF) as u8);
|
|
n >>= 8;
|
|
}
|
|
bytes.reverse();
|
|
if bytes[0] & 0x80 != 0 {
|
|
bytes.insert(0, 0);
|
|
}
|
|
}
|
|
der_integer_bytes(&bytes)
|
|
}
|
|
|
|
fn der_bit_string(unused: u8, bytes: &[u8]) -> Vec<u8> {
|
|
let mut content = vec![unused];
|
|
content.extend_from_slice(bytes);
|
|
tlv(0x03, &content)
|
|
}
|
|
|
|
fn cs_cons(tag_no: u8, inner_der: Vec<u8>) -> Vec<u8> {
|
|
tlv(0xA0 | (tag_no & 0x1F), &inner_der)
|
|
}
|
|
|
|
#[test]
|
|
fn ip_addr_blocks_decode_rejects_invalid_encodings() {
|
|
// Not a SEQUENCE.
|
|
assert!(IpResourceSet::decode_extn_value(&der_null()).is_err());
|
|
|
|
// IPAddressFamily wrong shape.
|
|
let fam_wrong = der_sequence(vec![der_octet_string(&[0x00, 0x01])]); // missing choice
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam_wrong])).is_err());
|
|
|
|
// addressFamily wrong length.
|
|
let fam_wrong = der_sequence(vec![
|
|
der_octet_string(&[0x00]), // 1 byte
|
|
der_null(),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam_wrong])).is_err());
|
|
|
|
// unsupported AFI.
|
|
let fam_wrong = der_sequence(vec![der_octet_string(&[0x00, 0x03]), der_null()]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam_wrong])).is_err());
|
|
|
|
// ipAddressChoice wrong type.
|
|
let fam_wrong = der_sequence(vec![der_octet_string(&[0x00, 0x01]), der_integer_u64(1)]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam_wrong])).is_err());
|
|
|
|
// BitString with invalid unused-bits value (>7).
|
|
let min = der_bit_string(0, &[0x0A, 0x00, 0x00, 0x00]);
|
|
let max = der_bit_string(8, &[0x0A, 0xFF, 0xFF, 0xFF]); // invalid unused bits
|
|
let range = der_sequence(vec![min, max]);
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![range]),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam])).is_err());
|
|
|
|
// BitString with non-zero bits in the unused tail.
|
|
let min = der_bit_string(0, &[0x0A, 0x00, 0x00, 0x00]);
|
|
let max = der_bit_string(1, &[0x0A, 0x00, 0x00, 0x01]); // LSB set, but unused=1
|
|
let range = der_sequence(vec![min, max]);
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![range]),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam])).is_err());
|
|
|
|
// Prefix length out of range for IPv4 (40 bits).
|
|
let prefix = der_bit_string(0, &[0x0A, 0x00, 0x00, 0x00, 0x00]); // 40 bits
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![prefix]),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam])).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn autonomous_sys_ids_decode_rejects_invalid_encodings() {
|
|
// Not a SEQUENCE.
|
|
assert!(AsResourceSet::decode_extn_value(&der_null()).is_err());
|
|
|
|
// Wrong tag class (expects [0]/[1] context-specific).
|
|
let as_ids = der_sequence(vec![der_integer_u64(64496)]);
|
|
assert!(AsResourceSet::decode_extn_value(&as_ids).is_err());
|
|
|
|
// Duplicate [0] tags.
|
|
let a0 = cs_cons(0, der_null());
|
|
let a0_dup = cs_cons(0, der_null());
|
|
assert!(AsResourceSet::decode_extn_value(&der_sequence(vec![a0, a0_dup])).is_err());
|
|
|
|
// Out-of-range ASID (> u32::MAX).
|
|
let too_big = der_integer_bytes(&[0x01, 0x00, 0x00, 0x00, 0x00]); // 2^32
|
|
let asnum = cs_cons(0, der_sequence(vec![too_big]));
|
|
assert!(AsResourceSet::decode_extn_value(&der_sequence(vec![asnum])).is_err());
|
|
|
|
// Range min > max.
|
|
let bad_range = der_sequence(vec![der_integer_u64(64510), der_integer_u64(64500)]);
|
|
let asnum = cs_cons(0, der_sequence(vec![bad_range]));
|
|
assert!(AsResourceSet::decode_extn_value(&der_sequence(vec![asnum])).is_err());
|
|
|
|
// Unsupported element inside asIdsOrRanges.
|
|
let asnum = cs_cons(0, der_sequence(vec![der_null()]));
|
|
assert!(AsResourceSet::decode_extn_value(&der_sequence(vec![asnum])).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn ip_addr_blocks_prefix_bitstring_unused_bits_checks_are_enforced() {
|
|
// Prefix BitString with non-zero bits in the unused tail (parse_ip_prefix path).
|
|
let prefix = der_bit_string(1, &[0x0A, 0x00, 0x00, 0x01]); // LSB set, but unused=1
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![prefix]),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam])).is_err());
|
|
|
|
// Prefix BitString with empty bytes but unused_bits != 0 is invalid.
|
|
let prefix = der_bit_string(1, &[]);
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![prefix]),
|
|
]);
|
|
assert!(IpResourceSet::decode_extn_value(&der_sequence(vec![fam])).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn ip_addr_blocks_range_upper_bound_can_fill_all_ones_when_bit_len_zero() {
|
|
// A BIT STRING with 0 bits (content is only the "unused bits" count octet) is allowed.
|
|
// For an IPAddressRange upper bound, RFC 3779 interprets missing bits as 1s.
|
|
let min = der_bit_string(0, &[]);
|
|
let max = der_bit_string(0, &[]);
|
|
let range = der_sequence(vec![min, max]);
|
|
let fam = der_sequence(vec![
|
|
der_octet_string(&[0x00, 0x01]),
|
|
der_sequence(vec![range]),
|
|
]);
|
|
|
|
let set = IpResourceSet::decode_extn_value(&der_sequence(vec![fam]))
|
|
.expect("decode range with 0-bit endpoints");
|
|
let fam = set
|
|
.families
|
|
.iter()
|
|
.find(|f| f.afi == rpki::data_model::rc::Afi::Ipv4)
|
|
.unwrap();
|
|
let rpki::data_model::rc::IpAddressChoice::AddressesOrRanges(items) = &fam.choice else {
|
|
panic!("expected explicit addressesOrRanges");
|
|
};
|
|
assert_eq!(items.len(), 1);
|
|
let rpki::data_model::rc::IpAddressOrRange::Range(r) = &items[0] else {
|
|
panic!("expected a range");
|
|
};
|
|
assert_eq!(r.min, vec![0, 0, 0, 0]);
|
|
assert_eq!(r.max, vec![0xFF, 0xFF, 0xFF, 0xFF]);
|
|
}
|