use rpki::data_model::aspa::{AspaDecodeError, AspaEContent, AspaParseError, AspaProfileError}; fn len_bytes(len: usize) -> Vec { 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 { let mut out = vec![tag]; out.extend(len_bytes(content.len())); out.extend_from_slice(content); out } fn der_integer_u64(v: u64) -> Vec { 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 { let mut content = Vec::new(); for c in children { content.extend(c); } tlv(0x30, &content) } fn cs_explicit(tag_no: u8, inner_der: Vec) -> Vec { tlv(0xA0 | (tag_no & 0x1F), &inner_der) } fn aspa_attestation_explicit_version(version: u64, customer: u64, providers: Vec) -> Vec { 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) -> Vec { 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)) )); }