use rpki::data_model::signed_object::{RpkiSignedObject, SignedObjectVerifyError}; use x509_parser::prelude::FromDer; use x509_parser::prelude::X509Certificate; 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_bytes(bytes: &[u8]) -> Vec { tlv(0x02, bytes) } 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); } } der_integer_bytes(&bytes) } fn der_null() -> Vec { vec![0x05, 0x00] } fn der_oid(oid: &str) -> Vec { use std::str::FromStr; use der_parser::asn1_rs::ToDer; let oid = der_parser::Oid::from_str(oid).unwrap(); oid.to_der_vec().unwrap() } fn der_sequence(children: Vec>) -> Vec { let mut content = Vec::new(); for c in children { content.extend(c); } tlv(0x30, &content) } fn der_bit_string(unused: u8, bytes: &[u8]) -> Vec { let mut content = vec![unused]; content.extend_from_slice(bytes); tlv(0x03, &content) } fn rsa_spki_der_with_modulus_bytes(modulus: &[u8], exponent: u64) -> Vec { // SubjectPublicKeyInfo for RSA public key: // SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } // subjectPublicKey contains RSAPublicKey DER. let alg = der_sequence(vec![ der_oid(rpki::data_model::oid::OID_RSA_ENCRYPTION), der_null(), ]); let rsa_pk = der_sequence(vec![der_integer_bytes(modulus), der_integer_u64(exponent)]); let spk = der_bit_string(0, &rsa_pk); der_sequence(vec![alg, spk]) } #[test] fn verify_mft_cms_signature_with_embedded_ee_cert() { let der = std::fs::read( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ) .expect("read MFT fixture"); let so = RpkiSignedObject::decode_der(&der).expect("decode signed object"); so.verify_signature() .expect("CMS signature should verify with embedded EE cert"); } #[test] fn verify_fails_with_wrong_spki() { let mft_der = std::fs::read( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ) .expect("read MFT fixture"); let so = RpkiSignedObject::decode_der(&mft_der).expect("decode signed object"); let issuer_cert_der = std::fs::read( "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer", ) .expect("read issuer certificate fixture"); let (_rem, issuer_cert) = X509Certificate::from_der(&issuer_cert_der).expect("parse cert"); let wrong_spki_der = issuer_cert.public_key().raw.to_vec(); let err = so .verify_signature_with_ee_spki_der(&wrong_spki_der) .unwrap_err(); assert!(matches!(err, SignedObjectVerifyError::InvalidSignature)); } #[test] fn verify_fails_with_tampered_signature() { let der = std::fs::read( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ) .expect("read MFT fixture"); let mut so = RpkiSignedObject::decode_der(&der).expect("decode signed object"); so.signed_data.signer_infos[0].signature[0] ^= 0x01; let err = so.verify_signature().unwrap_err(); assert!(matches!(err, SignedObjectVerifyError::InvalidSignature)); } #[test] fn verify_rejects_spki_der_with_trailing_bytes() { let der = std::fs::read( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ) .expect("read MFT fixture"); let so = RpkiSignedObject::decode_der(&der).expect("decode signed object"); let mut spki_der = so.signed_data.certificates[0].spki_der.clone(); spki_der.push(0); let err = so .verify_signature_with_ee_spki_der(&spki_der) .unwrap_err(); assert!(matches!(err, SignedObjectVerifyError::EeSpkiTrailingBytes(1))); } #[test] fn verify_with_all_zero_modulus_exercises_strip_leading_zeros_fallback() { let der = std::fs::read( "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft", ) .expect("read MFT fixture"); let so = RpkiSignedObject::decode_der(&der).expect("decode signed object"); // modulus INTEGER 0, exponent 65537 (valid exponent encoding); signature verification must fail // but the SPKI parsing path should succeed. let spki_der = rsa_spki_der_with_modulus_bytes(&[0x00], 65537); let err = so .verify_signature_with_ee_spki_der(&spki_der) .unwrap_err(); assert!(matches!(err, SignedObjectVerifyError::InvalidSignature)); }