use der_parser::num_bigint::BigUint; use time::OffsetDateTime; use rpki::data_model::rc::{ Afi, AsResourceSet, IpAddressChoice, IpAddressFamily, IpAddressOrRange, IpPrefix, IpResourceSet, RcExtensions, ResourceCertKind, ResourceCertificate, RpkixTbsCertificate, SubjectInfoAccess, }; use rpki::data_model::roa::{RoaAfi, RoaEContent, RoaIpAddress, RoaIpAddressFamily, RoaValidateError}; fn dummy_ee(ip_resources: Option, as_resources: Option) -> ResourceCertificate { ResourceCertificate { raw_der: vec![], tbs: RpkixTbsCertificate { version: 2, serial_number: BigUint::from(1u8), signature_algorithm: "1.2.840.113549.1.1.11".to_string(), issuer_dn: "CN=issuer".to_string(), subject_dn: "CN=subject".to_string(), validity_not_before: OffsetDateTime::UNIX_EPOCH, validity_not_after: OffsetDateTime::UNIX_EPOCH, subject_public_key_info: vec![], extensions: RcExtensions { basic_constraints_ca: false, subject_key_identifier: Some(vec![0x01]), subject_info_access: Some(SubjectInfoAccess::Ca( rpki::data_model::rc::SubjectInfoAccessCa { access_descriptions: vec![], }, )), certificate_policies_oid: None, ip_resources, as_resources, }, }, kind: ResourceCertKind::Ee, } } fn test_roa_single_v4_prefix() -> RoaEContent { RoaEContent { version: 0, as_id: 64496, ip_addr_blocks: vec![RoaIpAddressFamily { afi: RoaAfi::Ipv4, addresses: vec![RoaIpAddress { prefix: rpki::data_model::roa::IpPrefix { afi: RoaAfi::Ipv4, prefix_len: 8, addr: vec![10, 0, 0, 0], }, max_length: Some(24), }], }], } } #[test] fn validate_accepts_when_prefix_is_covered() { let roa = test_roa_single_v4_prefix(); let ee = dummy_ee( Some(IpResourceSet { families: vec![IpAddressFamily { afi: Afi::Ipv4, choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix( IpPrefix { afi: Afi::Ipv4, prefix_len: 0, addr: vec![0, 0, 0, 0], }, )]), }], }), None, ); roa.validate_against_ee_cert(&ee) .expect("prefix should be covered by 0/0"); } #[test] fn validate_rejects_when_as_resources_present() { let roa = test_roa_single_v4_prefix(); let ee = dummy_ee( Some(IpResourceSet { families: vec![] }), Some(AsResourceSet { asnum: None, rdi: None, }), ); let err = roa.validate_against_ee_cert(&ee).unwrap_err(); assert!(matches!(err, RoaValidateError::EeAsResourcesPresent)); } #[test] fn validate_rejects_when_ip_resources_missing() { let roa = test_roa_single_v4_prefix(); let ee = dummy_ee(None, None); let err = roa.validate_against_ee_cert(&ee).unwrap_err(); assert!(matches!(err, RoaValidateError::EeIpResourcesMissing)); } #[test] fn validate_rejects_when_ip_resources_inherit() { let roa = test_roa_single_v4_prefix(); let ee = dummy_ee( Some(IpResourceSet { families: vec![IpAddressFamily { afi: Afi::Ipv4, choice: IpAddressChoice::Inherit, }], }), None, ); let err = roa.validate_against_ee_cert(&ee).unwrap_err(); assert!(matches!(err, RoaValidateError::EeIpResourcesInherit)); } #[test] fn validate_rejects_when_prefix_not_covered() { let roa = test_roa_single_v4_prefix(); let ee = dummy_ee( Some(IpResourceSet { families: vec![IpAddressFamily { afi: Afi::Ipv4, choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix( IpPrefix { afi: Afi::Ipv4, prefix_len: 24, addr: vec![192, 0, 2, 0], }, )]), }], }), None, ); let err = roa.validate_against_ee_cert(&ee).unwrap_err(); assert!(matches!(err, RoaValidateError::PrefixNotInEeResources { .. })); } #[test] fn contains_prefix_handles_non_octet_boundary_prefix_len() { let roa = RoaEContent { version: 0, as_id: 64496, ip_addr_blocks: vec![RoaIpAddressFamily { afi: RoaAfi::Ipv4, addresses: vec![RoaIpAddress { prefix: rpki::data_model::roa::IpPrefix { afi: RoaAfi::Ipv4, prefix_len: 16, addr: vec![0b1010_0000, 0x12, 0, 0], // 160.18.0.0/16 }, max_length: None, }], }], }; let ee = dummy_ee( Some(IpResourceSet { families: vec![IpAddressFamily { afi: Afi::Ipv4, choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix( IpPrefix { afi: Afi::Ipv4, prefix_len: 9, addr: vec![0b1010_0000, 0, 0, 0], // 160.0.0.0/9 }, )]), }], }), None, ); roa.validate_against_ee_cert(&ee) .expect("160.18.0.0/16 should be covered by 160.0.0.0/9"); }