165 lines
5.2 KiB
Rust
165 lines
5.2 KiB
Rust
use rpki::data_model::signed_object::{RpkiSignedObject, SignedObjectVerifyError};
|
|
use x509_parser::prelude::FromDer;
|
|
use x509_parser::prelude::X509Certificate;
|
|
|
|
fn len_bytes(len: usize) -> Vec<u8> {
|
|
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<u8> {
|
|
let mut out = vec![tag];
|
|
out.extend(len_bytes(content.len()));
|
|
out.extend_from_slice(content);
|
|
out
|
|
}
|
|
|
|
fn der_integer_bytes(bytes: &[u8]) -> Vec<u8> {
|
|
tlv(0x02, bytes)
|
|
}
|
|
|
|
fn der_integer_u64(v: u64) -> Vec<u8> {
|
|
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<u8> {
|
|
vec![0x05, 0x00]
|
|
}
|
|
|
|
fn der_oid(oid: &str) -> Vec<u8> {
|
|
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<u8>>) -> Vec<u8> {
|
|
let mut content = Vec::new();
|
|
for c in children {
|
|
content.extend(c);
|
|
}
|
|
tlv(0x30, &content)
|
|
}
|
|
|
|
fn der_bit_string(unused: u8, bytes: &[u8]) -> Vec<u8> {
|
|
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<u8> {
|
|
// 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));
|
|
}
|