1524 lines
50 KiB
Rust
1524 lines
50 KiB
Rust
use rpki::data_model::oid::{
|
|
OID_CMS_ATTR_CONTENT_TYPE, OID_CMS_ATTR_MESSAGE_DIGEST, OID_CMS_ATTR_SIGNING_TIME,
|
|
OID_RSA_ENCRYPTION, OID_SHA256, OID_SHA256_WITH_RSA_ENCRYPTION, OID_SIGNED_DATA,
|
|
};
|
|
use rpki::data_model::signed_object::{RpkiSignedObject, SignedObjectDecodeError};
|
|
use sha2::{Digest, Sha256};
|
|
use x509_parser::extensions::ParsedExtension;
|
|
use x509_parser::prelude::FromDer;
|
|
use x509_parser::prelude::X509Certificate;
|
|
|
|
const TEST_NO_SIA_CERT_DER_B64: &str = "MIIDATCCAemgAwIBAgIUCyQLQJn92+gyAzvIz22q1F/97OMwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPVGVzdCBObyBDUkxTaWduMB4XDTI2MDEyNzAzNTk1OVoXDTM2MDEyNTAzNTk1OVowGjEYMBYGA1UEAwwPVGVzdCBObyBDUkxTaWduMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr/aoMU8J6cddkM2r6F2snd1rCdQPepgo2T2lrqWFcxnQJdcxBL1OYg3wFi95TJmZSeIHIOGauDaJ2abmjgyOUHOC4U68x66JRg4hLkmLxo1cf3uYHWl9Obph6g2qPRvN80ORq70JPuL6mAfUkNiO9hnwK6oQiTzc/rjCQGIFH8kTESBMXLfNCyUpGi+MNztYH6Ha6bKAQuXgd29OFwIkOlGQnYgGC2qBMvnp86eITvV1gTiuI8Ho9m9nZHCmaD7TylvkMDq8Hk5nkIpRcG0uO60SkR2BiMOYe/TNn5dTmHd6bsdbU2GOvgnq1SnqGq3FOWhKIe3ycUJde0uNfZOqRwIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUFjyzfJCDNhFfKxVr06kjUkE23dMwDQYJKoZIhvcNAQELBQADggEBAK98n2gVlwKA3Ob1YeAm9f+8hm7pbvrt0tA8GW180CILjf09k7fKgiRlxqGdZ9ySXjU52+zCqu3MpBXVbI87ZC+zA6uK05n4y1F0n85MJ9hGR2UEiPcqou85X73LvioynnSOy/OV1PjKJXReUsqF3GgDtgcMyFssPJ9s/5DWuUCScUJY6pu0kuIGOLQ/oXUw4TvxUeyz73gOTiAJshVTQoLpHUhj0595S7lArjwi7oLI1b8m8guTknvhk0Sc3tJZmUqOcIvYIs0guHpaeC+sMoF4K+6UTrxxOBdX+fUEWNpUyYXWHjdZq25PbJdHwA/VAW2zYVojaVREligf0Qfo6F4=";
|
|
const TEST_SIA_OTHER_CERT_DER_B64: &str = "MIIDMDCCAhigAwIBAgIUEqpG+JXMZKL3bEarJ9NzqmE0mbMwDQYJKoZIhvcNAQELBQAwGTEXMBUGA1UEAwwOVGVzdCBTSUEgb3RoZXIwHhcNMjYwMTI3MDY1MzA5WhcNMzYwMTI1MDY1MzA5WjAZMRcwFQYDVQQDDA5UZXN0IFNJQSBvdGhlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANheqbavt3/tlRUrGJrZQFHzNqqmjOjnugWqkaxXwKa9WSQUs+sWFpcsKzhLW0dTLyL/6SylCNumXSbNHv92oXj0NQMzD4yXzHb78QceEk+O6Rpwtmcts03vI96kUw/xoW94+A7P4imPYOIwVqMzt0qcSxEkeYxwnN/IA+nLvlDO0Uw+0ctxDwz1/EdhsVFr8WHY5pu0w9n9R7xRPTJoke0fn0Q1ptI07aBpMIVxHMfdEuS7Mabu07LLFc2AC1XxdCbuBpqAgLPgBbFe9w2edTUFCYzCTZ3KiezrpQea0sKElC27JnuJO4ySbrMTbXv/JxlnvRcro1fRjIB0Mv14ptMCAwEAAaNwMG4wCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwHQYDVR0OBBYEFLXYTYaN8zXjQ5wqrRUbvEKSD3wcMDUGCCsGAQUFBwELBCkwJzAlBggrBgEFBQcwBYYZcnN5bmM6Ly9leGFtcGxlLm5ldC9yZXBvLzANBgkqhkiG9w0BAQsFAAOCAQEAC0/lIV63dWy/jRfx2sYtCBV6ob+wTiOazSJL8s4lpIdZgmABw3YagjTwqHMQim/xVA8ecG9q8QVZ/AXYSnxtK6zTULXhAXDBdYHEUl/slDHOfk1Nvvd8t3qm7yk8wwP74xdqVk17NY5stcIyt5kf3w75adGy0we4jfGPNAKIpcRJpQSvLfhEsecnLzPPq3F4dLFFvyMNLT3rjFrJmxPviqBdt6Dm8l6MqbexxDeNTtqhq7JutBp1arzFzGicOzvG/CEskPKNPK7mIk3jdR9zfG+lygfoZe7cQEmvH3DtoUWVXoAYNALI5b3gE0CutR8x3M8h/jgRg4IutPafWqHXtw==";
|
|
const TEST_SIA_HTTPS_CERT_DER_B64: &str = "MIIDODCCAiCgAwIBAgIUBp2fsJYhUBJk711xTOahGbDzwN8wDQYJKoZIhvcNAQELBQAwGTEXMBUGA1UEAwwOVGVzdCBTSUEgaHR0cHMwHhcNMjYwMTI3MDY1MzA5WhcNMzYwMTI1MDY1MzA5WjAZMRcwFQYDVQQDDA5UZXN0IFNJQSBodHRwczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOMHIMaPf4Lr5DVDs1J3wPb6XL+b9EvmuVMo6JRhZHse3+Qx9ubOywowU+3NS50tgFe2H6Xm8XpeXMMhmWyKS/Z81J/6Sa9ZRI/vYLGOZmq1ORV44EDILoxxEazstD3LSgg5w28frAmY1wo0HvCX15Mp54nEwgjmwM9Vssg47iNz859grF/V3bRKlsnTwPhiJ79yt8etpNYZHCEc+h1PUilJh81NYPKnOGOUbJPVUkCF6MnLCM4WdYjKrnaGQzTAlDht15gcEyKmMEczsmU5TJe5ToJf+72bxOeGSvRbqS8iXvi/lZS0EcGVdpOc7oh6yHhC3EcZrHmvDHEeKtav18MCAwEAAaN4MHYwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwHQYDVR0OBBYEFHXD07FAu+OLsSH95FGd2ONTX8pGMD0GCCsGAQUFBwELBDEwLzAtBggrBgEFBQcwC4YhaHR0cHM6Ly9leGFtcGxlLm5ldC9yZXBvL3Rlc3QubWZ0MA0GCSqGSIb3DQEBCwUAA4IBAQDG7Haq/PbaDKfnd7kKkvuB9GBkJXhYK8k5qVHoPS1CnWWCWRpVjMfOni6v3Ylxa1TUYmWEdjSeQYso5xzC/vWiWG1nnt8Pn2W34R6vlx/yN9mwBMr6QK+F3IrprTQEbxWY019GFwaFcCSptWff1/YckjeTZFg+eSkuWembXO4788Opqx9d1KPJSviTODK2B0S0U5UQCeRAFD7cuwa1zrZJ/Y5kktI4lChymMAHx3N3CeJC8CvDg6VRncKDNkw0dCzLrcwX/ufQ1BbR4pmiedK9SiBqGKZJRZwUEOSYBLf5uitN/mkmZCTWnQQSySQM+QlNjNRo/+6qiBKWpKpADZlI";
|
|
const TEST_SIA_DNS_CERT_DER_B64: &str = "MIIDHjCCAgagAwIBAgIUJlS9d2BCJaamLyjYVTjvMNzWDxMwDQYJKoZIhvcNAQELBQAwFzEVMBMGA1UEAwwMVGVzdCBTSUEgZG5zMB4XDTI2MDEyNzA2NTMwOVoXDTM2MDEyNTA2NTMwOVowFzEVMBMGA1UEAwwMVGVzdCBTSUEgZG5zMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnKIddAQzQpDLbM+ZX3qR707L7CZvZ3MDGYN+tvuPXOfhATcOLtEaxu1dK4ZhV4Ou3ZqdxwYauyC+N4An0qCJW8mVr3zhbxathVGW7w4/S9pEV/+8dGW8ypOiqNixtmV++Ww54PguD6uxMk1S3IUOVTJY+QaetMy+SV9lCbOykZys17J56tMBmHRtuOxGPnaLtzZLddWqGhGFSDthSbKX4yToUIhTUl+wIRRYjBjnbGgzH5jV6eHUgrHRk+n567jNa9fe3cuRCGNBe6ny/8NPQnJEksWpA9lGfJDlEFsDIM9cXY78izr6i4JHeErwfusJiSchTT0ePhHXRAYMQoIqywIDAQABo2IwYDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUaY5MXriJ8s5VVa+s09HoSUloKBUwJwYIKwYBBQUHAQsEGzAZMBcGCCsGAQUFBzALggtleGFtcGxlLm5ldDANBgkqhkiG9w0BAQsFAAOCAQEAZSnkWxPTFWepHaK0XvAV145idY0ztEqY9BWUql2Ythzb4rjBAU1TfDRRklnnlE9o9/I6363ltaZBvj95e3CyTu4YGflxEpHsW+4aTth8ty1ee7YSqsdJ8gN08sroIpMTfr6tvWf65cVLSTkB4yP8cnNEM3zGr37zb32ChPXgUFwS9JFf3SMsXudZ4rHougE/PM4pQZvaOl3tFEzohV5MjA2VD38n3y6bVmx3i0Xqze7UZnl06aDKozzTXmFy/DoDRGG2pd2EjoC8gNAqIOL53uRz5nJlp8WEIBMe5Hmokrzv+zkAywVZZtYo1FvonOdg5etH94oMnZEtgV/OO9joRg==";
|
|
|
|
fn decode_b64(b64: &str) -> Vec<u8> {
|
|
use base64::{engine::general_purpose, Engine as _};
|
|
general_purpose::STANDARD
|
|
.decode(b64)
|
|
.expect("decode base64 cert")
|
|
}
|
|
|
|
fn test_no_sia_cert_der() -> Vec<u8> {
|
|
decode_b64(TEST_NO_SIA_CERT_DER_B64)
|
|
}
|
|
|
|
fn test_sia_other_cert_der() -> Vec<u8> {
|
|
decode_b64(TEST_SIA_OTHER_CERT_DER_B64)
|
|
}
|
|
|
|
fn test_sia_https_cert_der() -> Vec<u8> {
|
|
decode_b64(TEST_SIA_HTTPS_CERT_DER_B64)
|
|
}
|
|
|
|
fn test_sia_dns_cert_der() -> Vec<u8> {
|
|
decode_b64(TEST_SIA_DNS_CERT_DER_B64)
|
|
}
|
|
|
|
fn extract_ski_from_cert(cert_der: &[u8]) -> Vec<u8> {
|
|
let (_rem, cert) = X509Certificate::from_der(cert_der).expect("parse cert");
|
|
cert.extensions()
|
|
.iter()
|
|
.find(|ext| ext.oid.to_id_string() == rpki::data_model::oid::OID_SUBJECT_KEY_IDENTIFIER)
|
|
.and_then(|ext| match ext.parsed_extension() {
|
|
ParsedExtension::SubjectKeyIdentifier(ki) => Some(ki.0.to_vec()),
|
|
_ => None,
|
|
})
|
|
.expect("cert has SKI")
|
|
}
|
|
|
|
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_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);
|
|
}
|
|
}
|
|
tlv(0x02, &bytes)
|
|
}
|
|
|
|
fn der_null() -> Vec<u8> {
|
|
vec![0x05, 0x00]
|
|
}
|
|
|
|
fn der_octet_string(bytes: &[u8]) -> Vec<u8> {
|
|
tlv(0x04, bytes)
|
|
}
|
|
|
|
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_set(mut children: Vec<Vec<u8>>) -> Vec<u8> {
|
|
// DER requires SET elements to be sorted by their encoded form.
|
|
children.sort();
|
|
let mut content = Vec::new();
|
|
for c in children {
|
|
content.extend(c);
|
|
}
|
|
tlv(0x31, &content)
|
|
}
|
|
|
|
fn cs_prim(tag_no: u8, content: &[u8]) -> Vec<u8> {
|
|
tlv(0x80 | (tag_no & 0x1F), content)
|
|
}
|
|
|
|
fn cs_cons(tag_no: u8, content: &[u8]) -> Vec<u8> {
|
|
tlv(0xA0 | (tag_no & 0x1F), content)
|
|
}
|
|
|
|
fn algorithm_id(oid: &str, params: Option<Vec<u8>>) -> Vec<u8> {
|
|
let mut items = vec![der_oid(oid)];
|
|
if let Some(p) = params {
|
|
items.push(p);
|
|
}
|
|
der_sequence(items)
|
|
}
|
|
|
|
fn cms_attribute(oid: &str, value_der: Vec<u8>) -> Vec<u8> {
|
|
der_sequence(vec![der_oid(oid), der_set(vec![value_der])])
|
|
}
|
|
|
|
fn cms_attribute_values(oid: &str, values_der: Vec<Vec<u8>>) -> Vec<u8> {
|
|
der_sequence(vec![der_oid(oid), der_set(values_der)])
|
|
}
|
|
|
|
fn signed_attrs_implicit(
|
|
content_type_oid: &str,
|
|
message_digest: &[u8],
|
|
signing_time_der: Vec<u8>,
|
|
extra_attrs: Vec<Vec<u8>>,
|
|
) -> Vec<u8> {
|
|
let mut attrs = vec![
|
|
cms_attribute(OID_CMS_ATTR_CONTENT_TYPE, der_oid(content_type_oid)),
|
|
cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(message_digest)),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, signing_time_der),
|
|
];
|
|
attrs.extend(extra_attrs);
|
|
// Content of SET OF Attribute (tag is IMPLICIT in SignerInfo, so we only include the SET content).
|
|
let mut set_children = attrs;
|
|
set_children.sort();
|
|
let mut content = Vec::new();
|
|
for a in set_children {
|
|
content.extend(a);
|
|
}
|
|
cs_cons(0, &content)
|
|
}
|
|
|
|
fn signed_attrs_raw(mut attrs: Vec<Vec<u8>>) -> Vec<u8> {
|
|
attrs.sort();
|
|
let mut content = Vec::new();
|
|
for a in attrs {
|
|
content.extend(a);
|
|
}
|
|
cs_cons(0, &content)
|
|
}
|
|
|
|
fn signer_info(
|
|
sid_ski: Vec<u8>,
|
|
digest_oid: &str,
|
|
signed_attrs: Option<Vec<u8>>,
|
|
sig_alg_oid: &str,
|
|
sig_alg_params: Option<Vec<u8>>,
|
|
include_unsigned_attrs: bool,
|
|
) -> Vec<u8> {
|
|
let mut fields = Vec::new();
|
|
fields.push(der_integer_u64(3));
|
|
fields.push(cs_prim(0, &sid_ski));
|
|
fields.push(algorithm_id(digest_oid, Some(der_null())));
|
|
if let Some(sa) = signed_attrs {
|
|
fields.push(sa);
|
|
}
|
|
fields.push(algorithm_id(sig_alg_oid, sig_alg_params));
|
|
fields.push(der_octet_string(b"sig"));
|
|
if include_unsigned_attrs {
|
|
fields.push(cs_cons(1, &der_set(vec![der_null()])));
|
|
}
|
|
der_sequence(fields)
|
|
}
|
|
|
|
fn signer_info_with_version(
|
|
version: u64,
|
|
sid_ski: Vec<u8>,
|
|
digest_oid: &str,
|
|
signed_attrs: Option<Vec<u8>>,
|
|
sig_alg_oid: &str,
|
|
sig_alg_params: Option<Vec<u8>>,
|
|
include_unsigned_attrs: bool,
|
|
) -> Vec<u8> {
|
|
let mut fields = Vec::new();
|
|
fields.push(der_integer_u64(version));
|
|
fields.push(cs_prim(0, &sid_ski));
|
|
fields.push(algorithm_id(digest_oid, Some(der_null())));
|
|
if let Some(sa) = signed_attrs {
|
|
fields.push(sa);
|
|
}
|
|
fields.push(algorithm_id(sig_alg_oid, sig_alg_params));
|
|
fields.push(der_octet_string(b"sig"));
|
|
if include_unsigned_attrs {
|
|
fields.push(cs_cons(1, &der_set(vec![der_null()])));
|
|
}
|
|
der_sequence(fields)
|
|
}
|
|
|
|
fn signed_data(
|
|
version: u64,
|
|
digest_alg_ids: Vec<Vec<u8>>,
|
|
econtent_type_oid: &str,
|
|
econtent_bytes: Vec<u8>,
|
|
certificates_set_content: Option<Vec<u8>>,
|
|
include_crls: bool,
|
|
signer_infos: Vec<Vec<u8>>,
|
|
) -> Vec<u8> {
|
|
let mut fields = Vec::new();
|
|
fields.push(der_integer_u64(version));
|
|
fields.push(der_set(digest_alg_ids));
|
|
let encap = der_sequence(vec![
|
|
der_oid(econtent_type_oid),
|
|
cs_cons(0, &der_octet_string(&econtent_bytes)),
|
|
]);
|
|
fields.push(encap);
|
|
|
|
if let Some(cert_content) = certificates_set_content {
|
|
fields.push(cs_cons(0, &cert_content));
|
|
}
|
|
if include_crls {
|
|
fields.push(cs_cons(1, &der_null()));
|
|
}
|
|
fields.push(der_set(signer_infos));
|
|
der_sequence(fields)
|
|
}
|
|
|
|
fn content_info_signed_data(signed_data_der: Vec<u8>) -> Vec<u8> {
|
|
der_sequence(vec![der_oid(OID_SIGNED_DATA), cs_cons(0, &signed_data_der)])
|
|
}
|
|
|
|
fn load_fixture_mft_ee_cert_and_ski() -> (Vec<u8>, Vec<u8>) {
|
|
let mft = std::fs::read(
|
|
"tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
|
|
)
|
|
.expect("read MFT fixture");
|
|
let so = rpki::data_model::signed_object::RpkiSignedObject::decode_der(&mft)
|
|
.expect("decode MFT signed object");
|
|
let ee = &so.signed_data.certificates[0];
|
|
(ee.raw_der.clone(), ee.subject_key_identifier.clone())
|
|
}
|
|
|
|
#[test]
|
|
fn trailing_bytes_after_object() {
|
|
let der = std::fs::read(
|
|
"tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
|
|
)
|
|
.expect("read MFT fixture");
|
|
let mut bad = der.clone();
|
|
bad.push(0);
|
|
let err = RpkiSignedObject::decode_der(&bad).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::TrailingBytes(1)));
|
|
}
|
|
|
|
#[test]
|
|
fn content_info_must_have_two_elements() {
|
|
let ci = der_sequence(vec![der_oid(OID_SIGNED_DATA)]);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn content_info_content_must_be_tag0_explicit() {
|
|
let sd = signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
);
|
|
let ci = der_sequence(vec![der_oid(OID_SIGNED_DATA), cs_cons(1, &sd)]);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn content_info_content_must_contain_only_one_inner_object() {
|
|
let sd = signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
);
|
|
let mut inner = sd.clone();
|
|
inner.extend(der_null());
|
|
let ci = der_sequence(vec![der_oid(OID_SIGNED_DATA), cs_cons(0, &inner)]);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn signed_data_must_have_expected_element_count() {
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
der_sequence(vec![der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST)]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn signer_infos_missing_is_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(0, &der_octet_string(&econtent)),
|
|
]),
|
|
cs_cons(0, &cert_der),
|
|
// no signerInfos SET
|
|
]);
|
|
let _ = si; // ensure we don't accidentally depend on `si` here
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn certificates_field_appears_more_than_once_is_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(0, &der_octet_string(&econtent)),
|
|
]),
|
|
cs_cons(0, &cert_der),
|
|
cs_cons(0, &cert_der),
|
|
der_set(vec![si]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn signed_data_unexpected_field_is_rejected() {
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(0, &der_octet_string(b"e")),
|
|
]),
|
|
der_null(),
|
|
der_set(vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn encap_content_info_length_and_tags_are_validated() {
|
|
// EncapsulatedContentInfo with 3 elements.
|
|
let encap = der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(0, &der_octet_string(b"e")),
|
|
der_null(),
|
|
]);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
encap,
|
|
der_set(vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// eContent wrong tag number.
|
|
let encap = der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(1, &der_octet_string(b"e")),
|
|
]);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
encap,
|
|
der_set(vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// eContent contains trailing bytes inside the explicit wrapper.
|
|
let mut inner = der_octet_string(b"e");
|
|
inner.push(0);
|
|
let encap = der_sequence(vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
cs_cons(0, &inner),
|
|
]);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
encap,
|
|
der_set(vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)]),
|
|
]);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn empty_econtent_is_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = Vec::new();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::EContentMissing));
|
|
}
|
|
|
|
#[test]
|
|
fn signer_info_sequence_len_and_version_are_validated() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
|
|
// Too-short SignerInfo SEQUENCE.
|
|
let si_short = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
cs_prim(0, &cert_ski),
|
|
algorithm_id(OID_SHA256, Some(der_null())),
|
|
algorithm_id(OID_RSA_ENCRYPTION, Some(der_null())),
|
|
]);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si_short],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// Invalid SignerInfo.version.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info_with_version(
|
|
1,
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::InvalidSignerInfoVersion(1)));
|
|
}
|
|
|
|
#[test]
|
|
fn signed_attrs_structure_and_presence_are_validated() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
|
|
// Attribute SEQUENCE must have 2 elements.
|
|
let bad_attr = der_sequence(vec![der_oid(OID_CMS_ATTR_CONTENT_TYPE)]);
|
|
let signed_attrs = signed_attrs_raw(vec![bad_attr]);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// Missing content-type.
|
|
let signed_attrs = signed_attrs_raw(vec![
|
|
cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(&digest)),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, tlv(0x17, b"240101000000Z")),
|
|
]);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// Missing message-digest.
|
|
let signed_attrs = signed_attrs_raw(vec![
|
|
cms_attribute(OID_CMS_ATTR_CONTENT_TYPE, der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST)),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, tlv(0x17, b"240101000000Z")),
|
|
]);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
|
|
// Missing signing-time.
|
|
let signed_attrs = signed_attrs_raw(vec![
|
|
cms_attribute(OID_CMS_ATTR_CONTENT_TYPE, der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST)),
|
|
cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(&digest)),
|
|
]);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn algorithm_identifier_sequence_shape_is_validated() {
|
|
let sd = signed_data(
|
|
3,
|
|
vec![der_sequence(vec![])], // invalid AlgorithmIdentifier
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
);
|
|
let ci = content_info_signed_data(sd);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::Parse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn ee_certificate_missing_signed_object_sia_is_rejected() {
|
|
let cert_der = test_no_sia_cert_der();
|
|
let cert_ski = extract_ski_from_cert(&cert_der);
|
|
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::EeCertificateMissingSia
|
|
| SignedObjectDecodeError::EeCertificateMissingSignedObjectSia
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn ee_certificate_sia_without_signed_object_access_method_is_rejected() {
|
|
let cert_der = test_sia_other_cert_der();
|
|
let cert_ski = extract_ski_from_cert(&cert_der);
|
|
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::EeCertificateMissingSignedObjectSia
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn ee_certificate_signed_object_sia_must_be_uri() {
|
|
let cert_der = test_sia_dns_cert_der();
|
|
let cert_ski = extract_ski_from_cert(&cert_der);
|
|
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::EeCertificateSignedObjectSiaNotUri
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn ee_certificate_signed_object_sia_requires_rsync_uri() {
|
|
let cert_der = test_sia_https_cert_der();
|
|
let cert_ski = extract_ski_from_cert(&cert_der);
|
|
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::EeCertificateSignedObjectSiaNoRsync
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn signed_attrs_duplicate_content_type_is_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
|
|
let signed_attrs = signed_attrs_raw(vec![
|
|
cms_attribute(
|
|
OID_CMS_ATTR_CONTENT_TYPE,
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
),
|
|
cms_attribute(
|
|
OID_CMS_ATTR_CONTENT_TYPE,
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
),
|
|
cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(&digest)),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, tlv(0x17, b"240101000000Z")),
|
|
]);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::DuplicateSignedAttribute(ref oid)
|
|
if oid == OID_CMS_ATTR_CONTENT_TYPE
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn signed_attrs_duplicate_signing_time_is_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
|
|
let signed_attrs = signed_attrs_raw(vec![
|
|
cms_attribute(
|
|
OID_CMS_ATTR_CONTENT_TYPE,
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
),
|
|
cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(&digest)),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, tlv(0x17, b"240101000000Z")),
|
|
cms_attribute(OID_CMS_ATTR_SIGNING_TIME, tlv(0x17, b"240101000000Z")),
|
|
]);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::DuplicateSignedAttribute(ref oid)
|
|
if oid == OID_CMS_ATTR_SIGNING_TIME
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_content_info_content_type() {
|
|
let sd = der_sequence(vec![der_integer_u64(3), der_set(vec![]), der_sequence(vec![der_oid("1.2.3")]) , der_set(vec![])]);
|
|
let ci = der_sequence(vec![der_oid("1.2.3.4"), cs_cons(0, &sd)]);
|
|
let err = RpkiSignedObject::decode_der(&ci).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidContentInfoContentType(_)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_signed_data_version() {
|
|
let so = content_info_signed_data(signed_data(
|
|
4,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignedDataVersion(4)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_digest_algorithms_count() {
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![
|
|
algorithm_id(OID_SHA256, Some(der_null())),
|
|
algorithm_id(OID_SHA256, Some(der_null())),
|
|
],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidDigestAlgorithmsCount(2)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_digest_algorithm_oid() {
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id("1.2.3.4", Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidDigestAlgorithm(_)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn econtent_missing() {
|
|
let encap = der_sequence(vec![der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST)]);
|
|
let sd = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_set(vec![algorithm_id(OID_SHA256, Some(der_null()))]),
|
|
encap,
|
|
der_set(vec![]),
|
|
]);
|
|
let so = der_sequence(vec![der_oid(OID_SIGNED_DATA), cs_cons(0, &sd)]);
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::EContentMissing));
|
|
}
|
|
|
|
#[test]
|
|
fn crls_present_is_rejected() {
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
true,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::CrlsPresent));
|
|
}
|
|
|
|
#[test]
|
|
fn certificates_missing_is_rejected() {
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"e".to_vec(),
|
|
None,
|
|
false,
|
|
vec![signer_info(vec![1], OID_SHA256, None, OID_RSA_ENCRYPTION, Some(der_null()), false)],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::CertificatesMissing));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_certificates_count_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let mut certs = cert_der.clone();
|
|
certs.extend_from_slice(&cert_der);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(certs),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidCertificatesCount(2)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn ee_certificate_parse_error_is_reported() {
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
vec![0x01, 0x02],
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
// CertificateSet contains a DER object, but it's not a certificate.
|
|
let certs = der_null();
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(certs),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::EeCertificateParse(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_signer_infos_count_rejected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si1 = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs.clone()),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let si2 = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si1, si2],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignerInfosCount(2)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn signer_info_errors_are_detected() {
|
|
let (cert_der, cert_ski) = load_fixture_mft_ee_cert_and_ski();
|
|
let econtent = b"payload".to_vec();
|
|
let digest = Sha256::digest(&econtent).to_vec();
|
|
|
|
// Missing signedAttrs.
|
|
let si_no_attrs = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
None,
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si_no_attrs],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::SignedAttrsMissing));
|
|
|
|
// Invalid signer identifier.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = der_sequence(vec![
|
|
der_integer_u64(3),
|
|
der_null(), // not [0] SKI
|
|
algorithm_id(OID_SHA256, Some(der_null())),
|
|
signed_attrs,
|
|
algorithm_id(OID_RSA_ENCRYPTION, Some(der_null())),
|
|
der_octet_string(b"sig"),
|
|
]);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::InvalidSignerIdentifier));
|
|
|
|
// Invalid digest algorithm.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
"1.2.3.4",
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignerInfoDigestAlgorithm(_)
|
|
));
|
|
|
|
// Invalid signature algorithm OID.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
"1.2.3.4",
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignatureAlgorithm(_)
|
|
));
|
|
|
|
// Invalid signing-time value.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
der_null(),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSigningTimeValue
|
|
));
|
|
|
|
// signedAttrs has duplicate attribute.
|
|
let dup = cms_attribute(OID_CMS_ATTR_MESSAGE_DIGEST, der_octet_string(&digest));
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![dup],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::DuplicateSignedAttribute(_)
|
|
));
|
|
|
|
// signedAttrs attrValues count != 1.
|
|
let bad_values = cms_attribute_values(
|
|
OID_CMS_ATTR_CONTENT_TYPE,
|
|
vec![
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
der_oid(rpki::data_model::oid::OID_CT_RPKI_MANIFEST),
|
|
],
|
|
);
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![bad_values],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignedAttributeValuesCount { .. }
|
|
));
|
|
|
|
// content-type mismatch.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
"1.2.3.4",
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::ContentTypeAttrMismatch { .. }
|
|
));
|
|
|
|
// message-digest mismatch.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
b"wrong",
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::MessageDigestMismatch));
|
|
|
|
// sid_ski mismatch.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
vec![0u8; 20],
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::SidSkiMismatch));
|
|
|
|
// Unsupported signedAttrs attribute.
|
|
let bad_attr = cms_attribute("1.2.3.4", der_null());
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![bad_attr],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::UnsupportedSignedAttribute(_)
|
|
));
|
|
|
|
// signatureAlgorithm parameters invalid.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x18, b"20500101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski.clone(),
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_RSA_ENCRYPTION,
|
|
Some(der_integer_u64(1)),
|
|
false,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent.clone(),
|
|
Some(cert_der.clone()),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(
|
|
err,
|
|
SignedObjectDecodeError::InvalidSignatureAlgorithmParameters
|
|
));
|
|
|
|
// unsignedAttrs present.
|
|
let signed_attrs = signed_attrs_implicit(
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
&digest,
|
|
tlv(0x17, b"240101000000Z"),
|
|
vec![],
|
|
);
|
|
let si = signer_info(
|
|
cert_ski,
|
|
OID_SHA256,
|
|
Some(signed_attrs),
|
|
OID_SHA256_WITH_RSA_ENCRYPTION,
|
|
Some(der_null()),
|
|
true,
|
|
);
|
|
let so = content_info_signed_data(signed_data(
|
|
3,
|
|
vec![algorithm_id(OID_SHA256, Some(der_null()))],
|
|
rpki::data_model::oid::OID_CT_RPKI_MANIFEST,
|
|
econtent,
|
|
Some(cert_der),
|
|
false,
|
|
vec![si],
|
|
));
|
|
let err = RpkiSignedObject::decode_der(&so).unwrap_err();
|
|
assert!(matches!(err, SignedObjectDecodeError::UnsignedAttrsPresent));
|
|
}
|