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, SignedObjectParseError, SignedObjectValidateError,
};
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 as _, engine::general_purpose};
    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 der_parser::asn1_rs::ToDer;
    use std::str::FromStr;

    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::Parse(SignedObjectParseError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::SignedAttrsParse(_))
    ));

    // 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::Validate(SignedObjectValidateError::SignedAttrsContentTypeMissing)
    ));

    // 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::Validate(
            SignedObjectValidateError::SignedAttrsMessageDigestMissing
        )
    ));

    // 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::Validate(SignedObjectValidateError::SignedAttrsSigningTimeMissing)
    ));
}

#[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();
    let rejected_for_sia = matches!(
        &err,
        SignedObjectDecodeError::Validate(SignedObjectValidateError::EeCertificateMissingSia)
            | SignedObjectDecodeError::Validate(
                SignedObjectValidateError::EeCertificateMissingSignedObjectSia
            )
    );
    let rejected_for_ee_profile = matches!(
        &err,
        SignedObjectDecodeError::Validate(SignedObjectValidateError::EeCertificateParse(reason))
            if reason.contains("basicConstraints must be absent")
    );
    assert!(
        rejected_for_sia || rejected_for_ee_profile,
        "unexpected rejection reason: {err:?}"
    );
}

#[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();
    let rejected_for_sia = matches!(
        &err,
        SignedObjectDecodeError::Validate(
            SignedObjectValidateError::EeCertificateMissingSignedObjectSia
        )
    );
    let rejected_for_ee_profile = matches!(
        &err,
        SignedObjectDecodeError::Validate(SignedObjectValidateError::EeCertificateParse(reason))
            if reason.contains("basicConstraints must be absent")
    );
    assert!(
        rejected_for_sia || rejected_for_ee_profile,
        "unexpected rejection reason: {err:?}"
    );
}

#[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::Validate(
            SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(SignedObjectValidateError::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::Validate(
            SignedObjectValidateError::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::Validate(SignedObjectValidateError::UnsignedAttrsPresent)
    ));
}
