520 lines
16 KiB
Rust
520 lines
16 KiB
Rust
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<String>,
|
|
ta_uris: Vec<String>,
|
|
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<String>,
|
|
}
|
|
|
|
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<BytesFmt>,
|
|
subject_info_access: Option<SubjectInfoAccess>,
|
|
certificate_policies_oid: Option<String>,
|
|
ip_resources: Option<IpResourceSet>,
|
|
as_resources: Option<AsResourceSet>,
|
|
}
|
|
|
|
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<String>,
|
|
encap_content_info: EncapsulatedContentInfoPretty,
|
|
certificates: Vec<ResourceEeCertificatePretty>,
|
|
crls_present: bool,
|
|
signer_infos: Vec<SignerInfoProfiledPretty>,
|
|
}
|
|
|
|
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<String>,
|
|
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<FileAndHashPretty>,
|
|
}
|
|
|
|
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<RoaIpAddressFamily>,
|
|
}
|
|
|
|
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<RevokedCert>,
|
|
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");
|
|
println!("TA.verify_self_signature={:?}", ta.verify_self_signature());
|
|
|
|
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.clone(), 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));
|
|
}
|