188 lines
5.7 KiB
Rust
188 lines
5.7 KiB
Rust
use rpki::data_model::aspa::{AspaDecodeError, AspaEContent, AspaParseError, AspaProfileError};
|
|
|
|
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_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 aspa_attestation_explicit_version(version: u64, customer: u64, providers: Vec<u64>) -> Vec<u8> {
|
|
let providers_der = der_sequence(providers.into_iter().map(der_integer_u64).collect());
|
|
der_sequence(vec![
|
|
cs_explicit(0, der_integer_u64(version)),
|
|
der_integer_u64(customer),
|
|
providers_der,
|
|
])
|
|
}
|
|
|
|
fn aspa_attestation_missing_version(customer: u64, providers: Vec<u64>) -> Vec<u8> {
|
|
let providers_der = der_sequence(providers.into_iter().map(der_integer_u64).collect());
|
|
der_sequence(vec![der_integer_u64(customer), providers_der])
|
|
}
|
|
|
|
#[test]
|
|
fn trailing_bytes_are_rejected_in_parse_step() {
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![64497]);
|
|
let mut bad = der.clone();
|
|
bad.push(0);
|
|
let err = AspaEContent::decode_der(&bad).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Parse(AspaParseError::TrailingBytes(1))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn version_tag_must_be_context_specific_0() {
|
|
// Build a 3-element SEQUENCE but make the first element an INTEGER, not [0] EXPLICIT.
|
|
let providers_der = der_sequence(vec![der_integer_u64(64497)]);
|
|
let der = der_sequence(vec![
|
|
der_integer_u64(1), // wrong tag/class
|
|
der_integer_u64(64496), // customerASID
|
|
providers_der, // providers
|
|
]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::VersionMustBeExplicitOne)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn version_explicit_tag_rejects_trailing_bytes_inside_inner_der() {
|
|
let mut version_inner = der_integer_u64(1);
|
|
version_inner.extend(tlv(0x05, &[])); // NULL after INTEGER
|
|
|
|
let providers_der = der_sequence(vec![der_integer_u64(64497)]);
|
|
let der = der_sequence(vec![
|
|
cs_explicit(0, version_inner),
|
|
der_integer_u64(64496),
|
|
providers_der,
|
|
]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::ProfileDecode(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn provider_asid_out_of_range_is_rejected() {
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![(u32::MAX as u64) + 1]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::ProviderAsIdOutOfRange(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn version_must_be_explicit_and_equal_to_one() {
|
|
let der = aspa_attestation_missing_version(64496, vec![64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::InvalidAttestationSequence)
|
|
));
|
|
|
|
let der = aspa_attestation_explicit_version(0, 64496, vec![64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::VersionMustBeExplicitOne)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn customer_asid_out_of_range_is_rejected() {
|
|
let der = aspa_attestation_explicit_version(1, (u32::MAX as u64) + 1, vec![64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::CustomerAsIdOutOfRange(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn providers_constraints_are_enforced() {
|
|
// empty providers
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::EmptyProviders)
|
|
));
|
|
|
|
// not strictly increasing (duplicate)
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![64497, 64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::ProvidersNotStrictlyIncreasing)
|
|
));
|
|
|
|
// not strictly increasing (descending)
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![64500, 64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::ProvidersNotStrictlyIncreasing)
|
|
));
|
|
|
|
// contains customer
|
|
let der = aspa_attestation_explicit_version(1, 64496, vec![64496, 64497]);
|
|
let err = AspaEContent::decode_der(&der).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
AspaDecodeError::Validate(AspaProfileError::ProvidersContainCustomer(64496))
|
|
));
|
|
}
|