206 lines
6.0 KiB
Rust
206 lines
6.0 KiB
Rust
use rpki::data_model::roa::{RoaDecodeError, RoaEContent};
|
|
|
|
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_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);
|
|
}
|
|
}
|
|
tlv(0x02, &bytes)
|
|
}
|
|
|
|
fn der_octet_string(bytes: &[u8]) -> Vec<u8> {
|
|
tlv(0x04, 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 der_sequence(children: Vec<Vec<u8>>) -> Vec<u8> {
|
|
let mut content = Vec::new();
|
|
for c in children {
|
|
content.extend(c);
|
|
}
|
|
tlv(0x30, &content)
|
|
}
|
|
|
|
fn cs_explicit(tag_no: u8, inner_der: Vec<u8>) -> Vec<u8> {
|
|
tlv(0xA0 | (tag_no & 0x1F), &inner_der)
|
|
}
|
|
|
|
fn roa_ip_address(prefix_bs: Vec<u8>, max_len: Option<u64>) -> Vec<u8> {
|
|
let mut fields = vec![prefix_bs];
|
|
if let Some(m) = max_len {
|
|
fields.push(der_integer_u64(m));
|
|
}
|
|
der_sequence(fields)
|
|
}
|
|
|
|
fn roa_ip_family(afi: [u8; 2], addresses: Vec<Vec<u8>>) -> Vec<u8> {
|
|
der_sequence(vec![der_octet_string(&afi), der_sequence(addresses)])
|
|
}
|
|
|
|
fn roa_attestation(version: Option<u64>, as_id: u64, families: Vec<Vec<u8>>) -> Vec<u8> {
|
|
let mut fields = Vec::new();
|
|
if let Some(v) = version {
|
|
fields.push(cs_explicit(0, der_integer_u64(v)));
|
|
}
|
|
fields.push(der_integer_u64(as_id));
|
|
fields.push(der_sequence(families));
|
|
der_sequence(fields)
|
|
}
|
|
|
|
#[test]
|
|
fn version_must_be_zero_when_present() {
|
|
let der = roa_attestation(
|
|
Some(1),
|
|
64496,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidVersion(1)));
|
|
}
|
|
|
|
#[test]
|
|
fn as_id_out_of_range_is_rejected() {
|
|
let der = roa_attestation(
|
|
None,
|
|
(u32::MAX as u64) + 1,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::AsIdOutOfRange(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn ip_addr_blocks_len_is_validated() {
|
|
let der = roa_attestation(None, 64496, vec![]);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidIpAddrBlocksLen(0)));
|
|
|
|
let families = vec![
|
|
roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)]),
|
|
roa_ip_family([0, 2], vec![roa_ip_address(der_bit_string(0, &[]), None)]),
|
|
roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)]),
|
|
];
|
|
let der = roa_attestation(None, 64496, families);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidIpAddrBlocksLen(3)));
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_afi_is_rejected() {
|
|
let families = vec![
|
|
roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)]),
|
|
roa_ip_family([0, 1], vec![roa_ip_address(der_bit_string(0, &[]), None)]),
|
|
];
|
|
let der = roa_attestation(None, 64496, families);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::DuplicateAfi(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn unsupported_afi_is_rejected() {
|
|
let der = roa_attestation(
|
|
None,
|
|
64496,
|
|
vec![roa_ip_family([0x12, 0x34], vec![roa_ip_address(der_bit_string(0, &[]), None)])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::UnsupportedAfi(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn empty_address_list_is_rejected() {
|
|
let der = roa_attestation(None, 64496, vec![roa_ip_family([0, 1], vec![])]);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::EmptyAddressList));
|
|
}
|
|
|
|
#[test]
|
|
fn prefix_unused_bits_must_be_zeroed() {
|
|
// prefix_len=1 => unused_bits=7, last byte must have lower 7 bits zero.
|
|
// Use 0b1000_0001 which has a non-zero unused bit.
|
|
let bs = der_bit_string(7, &[0b1000_0001]);
|
|
let der = roa_attestation(
|
|
None,
|
|
64496,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(bs, None)])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidPrefixUnusedBits));
|
|
}
|
|
|
|
#[test]
|
|
fn prefix_len_out_of_range_is_rejected() {
|
|
// IPv4 ub=32, encode 33 bits: 5 bytes with unused_bits=7 => 40-7=33.
|
|
let bs = der_bit_string(7, &[0u8; 5]);
|
|
let der = roa_attestation(
|
|
None,
|
|
64496,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(bs, None)])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::PrefixLenOutOfRange { .. }));
|
|
}
|
|
|
|
#[test]
|
|
fn max_length_range_and_relation_are_validated() {
|
|
// IPv4, prefix_len=8
|
|
let bs = der_bit_string(0, &[0x0A]);
|
|
// maxLength < prefix_len
|
|
let der = roa_attestation(
|
|
None,
|
|
64496,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(bs.clone(), Some(7))])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidMaxLength { .. }));
|
|
|
|
// maxLength > ub
|
|
let der = roa_attestation(
|
|
None,
|
|
64496,
|
|
vec![roa_ip_family([0, 1], vec![roa_ip_address(bs, Some(33))])],
|
|
);
|
|
let err = RoaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(err, RoaDecodeError::InvalidMaxLength { .. }));
|
|
}
|
|
|