use rpki::data_model::aspa::{AspaEContent, AspaObject}; use rpki::data_model::crl::{CrlExtensions, RevokedCert, RpkixCrl}; use rpki::data_model::manifest::{FileAndHash, ManifestEContent, ManifestObject}; use rpki::data_model::rc::{ AsResourceSet, IpResourceSet, RcExtensions, ResourceCertKind, ResourceCertificate, RpkixTbsCertificate, SubjectInfoAccess, }; use rpki::data_model::roa::{RoaEContent, RoaIpAddressFamily, RoaObject}; use rpki::data_model::signed_object::{ EncapsulatedContentInfo, ResourceEeCertificate, RpkiSignedObject, SignedAttrsProfiled, SignedDataProfiled, SignerInfoProfiled, }; use rpki::data_model::ta::{TaCertificate, TrustAnchor}; use rpki::data_model::tal::Tal; use sha2::{Digest, Sha256}; #[derive(Clone, Debug, PartialEq, Eq)] struct BytesFmt { len: usize, sha256_hex: String, head_hex: String, tail_hex: String, } fn bytes_fmt(bytes: &[u8]) -> BytesFmt { let len = bytes.len(); let sha = Sha256::digest(bytes); let head_len = len.min(16); let tail_len = len.min(16); let head = &bytes[..head_len]; let tail = &bytes[len.saturating_sub(tail_len)..]; BytesFmt { len, sha256_hex: hex::encode(sha), head_hex: hex::encode(head), tail_hex: hex::encode(tail), } } #[derive(Clone, Debug, PartialEq, Eq)] struct TalPretty { raw: BytesFmt, comments: Vec, ta_uris: Vec, subject_public_key_info_der: BytesFmt, } impl From<&Tal> for TalPretty { fn from(v: &Tal) -> Self { Self { raw: bytes_fmt(&v.raw), comments: v.comments.clone(), ta_uris: v.ta_uris.iter().map(|u| u.to_string()).collect(), subject_public_key_info_der: bytes_fmt(&v.subject_public_key_info_der), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct TaCertificatePretty { raw_der: BytesFmt, rc_ca: ResourceCertificatePretty, } impl From<&TaCertificate> for TaCertificatePretty { fn from(v: &TaCertificate) -> Self { Self { raw_der: bytes_fmt(&v.raw_der), rc_ca: ResourceCertificatePretty::from(&v.rc_ca), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct TrustAnchorPretty { tal: TalPretty, ta_certificate: TaCertificatePretty, resolved_ta_uri: Option, } impl From<&TrustAnchor> for TrustAnchorPretty { fn from(v: &TrustAnchor) -> Self { Self { tal: TalPretty::from(&v.tal), ta_certificate: TaCertificatePretty::from(&v.ta_certificate), resolved_ta_uri: v.resolved_ta_uri.as_ref().map(|u| u.to_string()), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct ResourceCertificatePretty { raw_der: BytesFmt, tbs: RpkixTbsCertificatePretty, kind: ResourceCertKind, } impl From<&ResourceCertificate> for ResourceCertificatePretty { fn from(v: &ResourceCertificate) -> Self { Self { raw_der: bytes_fmt(&v.raw_der), tbs: RpkixTbsCertificatePretty::from(&v.tbs), kind: v.kind, } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RpkixTbsCertificatePretty { version: u32, serial_number: String, signature_algorithm: String, issuer_dn: String, subject_dn: String, validity_not_before: time::OffsetDateTime, validity_not_after: time::OffsetDateTime, subject_public_key_info: BytesFmt, extensions: RcExtensionsPretty, } impl From<&RpkixTbsCertificate> for RpkixTbsCertificatePretty { fn from(v: &RpkixTbsCertificate) -> Self { Self { version: v.version, serial_number: hex::encode(v.serial_number.to_bytes_be()), signature_algorithm: v.signature_algorithm.clone(), issuer_dn: v.issuer_dn.clone(), subject_dn: v.subject_dn.clone(), validity_not_before: v.validity_not_before, validity_not_after: v.validity_not_after, subject_public_key_info: bytes_fmt(&v.subject_public_key_info), extensions: RcExtensionsPretty::from(&v.extensions), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RcExtensionsPretty { basic_constraints_ca: bool, subject_key_identifier: Option, subject_info_access: Option, certificate_policies_oid: Option, ip_resources: Option, as_resources: Option, } impl From<&RcExtensions> for RcExtensionsPretty { fn from(v: &RcExtensions) -> Self { Self { basic_constraints_ca: v.basic_constraints_ca, subject_key_identifier: v.subject_key_identifier.as_ref().map(|b| bytes_fmt(b)), subject_info_access: v.subject_info_access.clone(), certificate_policies_oid: v.certificate_policies_oid.clone(), ip_resources: v.ip_resources.clone(), as_resources: v.as_resources.clone(), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RpkiSignedObjectPretty { raw_der: BytesFmt, content_info_content_type: String, signed_data: SignedDataProfiledPretty, } impl From<&RpkiSignedObject> for RpkiSignedObjectPretty { fn from(v: &RpkiSignedObject) -> Self { Self { raw_der: bytes_fmt(&v.raw_der), content_info_content_type: v.content_info_content_type.clone(), signed_data: SignedDataProfiledPretty::from(&v.signed_data), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct SignedDataProfiledPretty { version: u32, digest_algorithms: Vec, encap_content_info: EncapsulatedContentInfoPretty, certificates: Vec, crls_present: bool, signer_infos: Vec, } impl From<&SignedDataProfiled> for SignedDataProfiledPretty { fn from(v: &SignedDataProfiled) -> Self { Self { version: v.version, digest_algorithms: v.digest_algorithms.clone(), encap_content_info: EncapsulatedContentInfoPretty::from(&v.encap_content_info), certificates: v .certificates .iter() .map(ResourceEeCertificatePretty::from) .collect(), crls_present: v.crls_present, signer_infos: v.signer_infos.iter().map(SignerInfoProfiledPretty::from).collect(), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct EncapsulatedContentInfoPretty { econtent_type: String, econtent: BytesFmt, } impl From<&EncapsulatedContentInfo> for EncapsulatedContentInfoPretty { fn from(v: &EncapsulatedContentInfo) -> Self { Self { econtent_type: v.econtent_type.clone(), econtent: bytes_fmt(&v.econtent), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct ResourceEeCertificatePretty { raw_der: BytesFmt, subject_key_identifier: BytesFmt, spki_der: BytesFmt, sia_signed_object_uris: Vec, resource_cert: ResourceCertificatePretty, } impl From<&ResourceEeCertificate> for ResourceEeCertificatePretty { fn from(v: &ResourceEeCertificate) -> Self { Self { raw_der: bytes_fmt(&v.raw_der), subject_key_identifier: bytes_fmt(&v.subject_key_identifier), spki_der: bytes_fmt(&v.spki_der), sia_signed_object_uris: v.sia_signed_object_uris.clone(), resource_cert: ResourceCertificatePretty::from(&v.resource_cert), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct SignerInfoProfiledPretty { version: u32, sid_ski: BytesFmt, digest_algorithm: String, signature_algorithm: String, signed_attrs: SignedAttrsProfiledPretty, unsigned_attrs_present: bool, signature: BytesFmt, signed_attrs_der_for_signature: BytesFmt, } impl From<&SignerInfoProfiled> for SignerInfoProfiledPretty { fn from(v: &SignerInfoProfiled) -> Self { Self { version: v.version, sid_ski: bytes_fmt(&v.sid_ski), digest_algorithm: v.digest_algorithm.clone(), signature_algorithm: v.signature_algorithm.clone(), signed_attrs: SignedAttrsProfiledPretty::from(&v.signed_attrs), unsigned_attrs_present: v.unsigned_attrs_present, signature: bytes_fmt(&v.signature), signed_attrs_der_for_signature: bytes_fmt(&v.signed_attrs_der_for_signature), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct SignedAttrsProfiledPretty { content_type: String, message_digest: BytesFmt, signing_time: rpki::data_model::common::Asn1TimeUtc, other_attrs_present: bool, } impl From<&SignedAttrsProfiled> for SignedAttrsProfiledPretty { fn from(v: &SignedAttrsProfiled) -> Self { Self { content_type: v.content_type.clone(), message_digest: bytes_fmt(&v.message_digest), signing_time: v.signing_time, other_attrs_present: v.other_attrs_present, } } } #[derive(Clone, Debug, PartialEq, Eq)] struct ManifestObjectPretty { signed_object: RpkiSignedObjectPretty, econtent_type: String, manifest: ManifestEContentPretty, } impl From<&ManifestObject> for ManifestObjectPretty { fn from(v: &ManifestObject) -> Self { Self { signed_object: RpkiSignedObjectPretty::from(&v.signed_object), econtent_type: v.econtent_type.clone(), manifest: ManifestEContentPretty::from(&v.manifest), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct ManifestEContentPretty { version: u32, manifest_number: String, this_update: time::OffsetDateTime, next_update: time::OffsetDateTime, file_hash_alg: String, files: Vec, } impl From<&ManifestEContent> for ManifestEContentPretty { fn from(v: &ManifestEContent) -> Self { Self { version: v.version, manifest_number: v.manifest_number.to_hex_upper(), this_update: v.this_update, next_update: v.next_update, file_hash_alg: v.file_hash_alg.clone(), files: v.files.iter().map(FileAndHashPretty::from).collect(), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct FileAndHashPretty { file_name: String, hash_hex: String, } impl From<&FileAndHash> for FileAndHashPretty { fn from(v: &FileAndHash) -> Self { Self { file_name: v.file_name.clone(), hash_hex: hex::encode(&v.hash_bytes), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RoaObjectPretty { signed_object: RpkiSignedObjectPretty, econtent_type: String, roa: RoaEContentPretty, } impl From<&RoaObject> for RoaObjectPretty { fn from(v: &RoaObject) -> Self { Self { signed_object: RpkiSignedObjectPretty::from(&v.signed_object), econtent_type: v.econtent_type.clone(), roa: RoaEContentPretty::from(&v.roa), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RoaEContentPretty { version: u32, as_id: u32, ip_addr_blocks: Vec, } impl From<&RoaEContent> for RoaEContentPretty { fn from(v: &RoaEContent) -> Self { Self { version: v.version, as_id: v.as_id, ip_addr_blocks: v.ip_addr_blocks.clone(), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct AspaObjectPretty { signed_object: RpkiSignedObjectPretty, econtent_type: String, aspa: AspaEContent, } impl From<&AspaObject> for AspaObjectPretty { fn from(v: &AspaObject) -> Self { Self { signed_object: RpkiSignedObjectPretty::from(&v.signed_object), econtent_type: v.econtent_type.clone(), aspa: v.aspa.clone(), } } } #[derive(Clone, Debug, PartialEq, Eq)] struct RpkixCrlPretty { raw_der: BytesFmt, version: u32, issuer_dn: String, signature_algorithm_oid: String, this_update: rpki::data_model::common::Asn1TimeUtc, next_update: rpki::data_model::common::Asn1TimeUtc, revoked_certs: Vec, extensions: CrlExtensions, } impl From<&RpkixCrl> for RpkixCrlPretty { fn from(v: &RpkixCrl) -> Self { Self { raw_der: bytes_fmt(&v.raw_der), version: v.version, issuer_dn: v.issuer_dn.clone(), signature_algorithm_oid: v.signature_algorithm_oid.clone(), this_update: v.this_update, next_update: v.next_update, revoked_certs: v.revoked_certs.clone(), extensions: v.extensions.clone(), } } } #[test] fn print_all_models_from_real_fixtures() { // Note: run this test with `cargo test --test test_model_print_real_fixtures -- --nocapture` // to see the output. let tal_path = "tests/fixtures/tal/ripe-ncc.tal"; println!("== TAL / TA / TrustAnchor =="); println!("Fixture (TAL): {tal_path}"); let tal_raw = std::fs::read(tal_path).expect("read TAL fixture"); let tal = Tal::decode_bytes(&tal_raw).expect("decode TAL"); let ta_path = "tests/fixtures/ta/ripe-ncc-ta.cer"; println!("Fixture (TA): {ta_path}"); let ta_der = std::fs::read(ta_path).expect("read TA fixture"); let ta = TaCertificate::from_der(&ta_der).expect("parse TA cert"); let resolved = tal .ta_uris .iter() .find(|u| u.scheme() == "https") .or_else(|| tal.ta_uris.first()) .cloned() .expect("tal has at least one uri"); let trust_anchor = TrustAnchor::bind(tal, &ta_der, Some(&resolved)).expect("bind trust anchor"); println!("{:#?}", TalPretty::from(&trust_anchor.tal)); println!("{:#?}", TaCertificatePretty::from(&ta)); println!("{:#?}", TrustAnchorPretty::from(&trust_anchor)); println!(); println!("== ResourceCertificate (example non-TA CA cert) =="); let ca_path = "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"; println!("Fixture (CA cert): {ca_path}"); let ca_der = std::fs::read(ca_path).expect("read CA cert fixture"); let ca_rc = ResourceCertificate::from_der(&ca_der).expect("parse CA resource certificate"); println!("{:#?}", ResourceCertificatePretty::from(&ca_rc)); println!(); println!("== Signed Object / Manifest =="); let mft_path = "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft"; println!("Fixture (MFT): {mft_path}"); let mft_der = std::fs::read(mft_path).expect("read MFT fixture"); let mft_obj = ManifestObject::decode_der(&mft_der).expect("decode manifest object"); println!("{:#?}", ManifestObjectPretty::from(&mft_obj)); println!("Manifest.validate_embedded_ee_cert={:?}", mft_obj.validate_embedded_ee_cert()); println!("Manifest.verify_signature={:?}", mft_obj.signed_object.verify_signature()); println!(); println!("== Signed Object / ROA =="); let roa_path = "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa"; println!("Fixture (ROA): {roa_path}"); let roa_der = std::fs::read(roa_path).expect("read ROA fixture"); let roa_obj = RoaObject::decode_der(&roa_der).expect("decode ROA object"); println!("{:#?}", RoaObjectPretty::from(&roa_obj)); println!("ROA.validate_embedded_ee_cert={:?}", roa_obj.validate_embedded_ee_cert()); println!("ROA.verify_signature={:?}", roa_obj.signed_object.verify_signature()); println!(); println!("== Signed Object / ASPA =="); let aspa_path = "tests/fixtures/repository/chloe.sobornost.net/rpki/RIPE-nljobsnijders/5m80fwYws_3FiFD7JiQjAqZ1RYQ.asa"; println!("Fixture (ASPA): {aspa_path}"); let aspa_der = std::fs::read(aspa_path).expect("read ASPA fixture"); let aspa_obj = AspaObject::decode_der(&aspa_der).expect("decode ASPA object"); println!("{:#?}", AspaObjectPretty::from(&aspa_obj)); println!("ASPA.validate_embedded_ee_cert={:?}", aspa_obj.validate_embedded_ee_cert()); println!("ASPA.verify_signature={:?}", aspa_obj.signed_object.verify_signature()); println!(); println!("== CRL =="); let crl_path = "tests/fixtures/0099DEAB073EFD74C250C0A382B25012B5082AEE.crl"; println!("Fixture (CRL): {crl_path}"); let crl_der = std::fs::read(crl_path).expect("read CRL fixture"); let crl = RpkixCrl::decode_der(&crl_der).expect("decode CRL"); println!("{:#?}", RpkixCrlPretty::from(&crl)); }