use rpki::data_model::rc::{ResourceCertificate, ResourceCertificateError}; 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="; fn decode_b64(b64: &str) -> Vec { use base64::engine::general_purpose::STANDARD; use base64::Engine as _; STANDARD.decode(b64).unwrap() } fn replace_first(haystack: &mut [u8], needle: &[u8], replacement: &[u8]) -> bool { if needle.len() != replacement.len() { return false; } for i in 0..=haystack.len().saturating_sub(needle.len()) { if &haystack[i..i + needle.len()] == needle { haystack[i..i + needle.len()].copy_from_slice(replacement); return true; } } false } fn replace_all(haystack: &mut [u8], needle: &[u8], replacement: &[u8]) -> usize { if needle.len() != replacement.len() { return 0; } let mut n = 0; let mut i = 0; while i + needle.len() <= haystack.len() { if &haystack[i..i + needle.len()] == needle { haystack[i..i + needle.len()].copy_from_slice(replacement); n += 1; i += needle.len(); } else { i += 1; } } n } #[test] fn trailing_bytes_after_cert_are_rejected() { let mut der = decode_b64(TEST_NO_SIA_CERT_DER_B64); der.push(0); let err = ResourceCertificate::from_der(&der).unwrap_err(); assert!(matches!(err, ResourceCertificateError::TrailingBytes(1))); } #[test] fn signature_algorithm_mismatch_is_detected() { let mut der = decode_b64(TEST_NO_SIA_CERT_DER_B64); // DER encoding of sha256WithRSAEncryption OID: // 06 09 2A 86 48 86 F7 0D 01 01 0B let oid = [0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B]; let mut patched = oid; patched[10] = 0x01; // rsaEncryption, same length encoding assert!(replace_first(&mut der, &oid, &patched)); let err = ResourceCertificate::from_der(&der).unwrap_err(); assert!(matches!(err, ResourceCertificateError::SignatureAlgorithmMismatch)); } #[test] fn unsupported_signature_algorithm_is_detected() { let mut der = decode_b64(TEST_NO_SIA_CERT_DER_B64); let oid = [0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B]; let mut patched = oid; patched[10] = 0x01; let n = replace_all(&mut der, &oid, &patched); assert!(n >= 2, "expected to patch at least inner+outer AlgorithmIdentifier"); let err = ResourceCertificate::from_der(&der).unwrap_err(); assert!(matches!(err, ResourceCertificateError::UnsupportedSignatureAlgorithm)); } #[test] fn invalid_signature_algorithm_parameters_are_detected() { let mut der = decode_b64(TEST_NO_SIA_CERT_DER_B64); // Replace NULL parameters (05 00) right after the sha256WithRSAEncryption OID with // an empty OCTET STRING (04 00) to keep the encoding length unchanged. let alg = [ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, ]; let mut patched = alg; patched[11] = 0x04; let n = replace_all(&mut der, &alg, &patched); assert!(n >= 2, "expected to patch at least inner+outer AlgorithmIdentifier parameters"); let err = ResourceCertificate::from_der(&der).unwrap_err(); assert!(matches!( err, ResourceCertificateError::InvalidSignatureAlgorithmParameters )); }