app_store_server_library/
chain_verifier.rs

1use crate::chain_verifier::ChainVerificationFailureReason::{CertificateExpired, InvalidCertificate, InvalidChainLength, InvalidEffectiveDate};
2use thiserror::Error;
3
4use x509_parser::certificate::X509Certificate;
5use x509_parser::der_parser::asn1_rs::oid;
6use x509_parser::error::X509Error;
7use x509_parser::prelude::{ASN1Time, FromDer};
8
9#[derive(Error, Debug, PartialEq)]
10pub enum ChainVerifierError {
11    #[error("VerificationFailure: [{0}]")]
12    VerificationFailure(ChainVerificationFailureReason),
13
14    #[error("InternalX509Error: [{0}]")]
15    InternalX509Error(#[from] X509Error),
16
17    #[error("InternalDecodeError: [{0}]")]
18    InternalDecodeError(#[from] base64::DecodeError),
19}
20
21#[derive(Error, Debug, PartialEq)]
22pub enum ChainVerificationFailureReason {
23    #[error("InvalidAppIdentifier")]
24    InvalidAppIdentifier,
25
26    #[error("InvalidIssuer")]
27    InvalidIssuer,
28
29    #[error("InvalidCertificate")]
30    InvalidCertificate,
31
32    #[error("InvalidChainLength")]
33    InvalidChainLength,
34
35    #[error("InvalidChain")]
36    InvalidChain,
37
38    #[error("InvalidEnvironment")]
39    InvalidEffectiveDate,
40
41    #[error("CertificateExpired")]
42    CertificateExpired,
43}
44
45const EXPECTED_CHAIN_LENGTH: usize = 3;
46
47/// Verifies a certificate chain.
48///
49/// This function verifies a certificate chain consisting of multiple certificates. It performs various
50/// checks to ensure the validity and integrity of the chain.
51///
52/// # Arguments
53///
54/// * `certificates`: A vector of byte slices containing the certificates in the chain.
55/// * `root_certificates`: A vector of byte slices containing the root certificates.
56/// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
57///
58/// # Returns
59///
60/// * `Ok(Vec<u8>)`: If the certificate chain is valid, it returns the public key data from the leaf certificate.
61/// * `Err(ChainVerifierError)`: If the chain verification fails for any reason, it returns a `ChainVerifierError` enum.
62///
63/// # Example
64///
65/// ```rust
66/// use app_store_server_library::chain_verifier::{verify_chain, ChainVerifierError};
67///
68/// fn main() {
69///     let certificates: Vec<Vec<u8>> = vec![]; // Load your certificates here
70///     let root_certificates: Vec<Vec<u8>> = vec![]; // Load your root certificates here
71///     let effective_date: Option<u64> = None; // Provide an effective date if needed
72///
73///     match verify_chain(&certificates, &root_certificates, effective_date) {
74///         Ok(public_key) => println!("Certificate chain is valid. Public key: {:?}", public_key),
75///         Err(err) => eprintln!("Certificate chain verification failed: {}", err),
76///     }
77/// }
78/// ```
79///
80/// TODO: Implement issuer checking
81pub fn verify_chain(
82    certificates: &Vec<Vec<u8>>,
83    root_certificates: &Vec<Vec<u8>>,
84    effective_date: Option<u64>,
85) -> Result<Vec<u8>, ChainVerifierError> {
86    if root_certificates.is_empty() {
87        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
88    }
89
90    if certificates.len() != EXPECTED_CHAIN_LENGTH {
91        return Err(ChainVerifierError::VerificationFailure(InvalidChainLength));
92    }
93
94    let leaf_certificate = &certificates[0];
95    let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
96        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
97    };
98    let leaf_certificate = leaf_certificate.1;
99
100    let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))?
101    else {
102        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
103    };
104
105    let intermediate_certificate = &certificates[1];
106    let Ok(intermediate_certificate) =
107        X509Certificate::from_der(intermediate_certificate.as_slice())
108    else {
109        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
110    };
111    let intermediate_certificate = intermediate_certificate.1;
112
113    let Some(_) =
114        intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))?
115    else {
116        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
117    };
118
119    let mut root_certificate: Option<X509Certificate> = None;
120
121    for cert in root_certificates {
122        let Ok(cert) = X509Certificate::from_der(&cert) else {
123            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
124        };
125
126        match intermediate_certificate.verify_signature(Some(cert.1.public_key())) {
127            Ok(_) => (),
128            Err(_) => continue,
129        }
130
131        root_certificate = Some(cert.1)
132    }
133
134    let Some(root_certificate) = root_certificate else {
135        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
136    };
137
138    leaf_certificate.verify_signature(Some(intermediate_certificate.public_key()))?;
139
140    if let Some(date) = effective_date {
141        let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
142            return Err(ChainVerifierError::VerificationFailure(
143                InvalidEffectiveDate,
144            ));
145        };
146
147        if !(root_certificate.validity.is_valid_at(time)
148            && leaf_certificate.validity.is_valid_at(time)
149            && intermediate_certificate.validity.is_valid_at(time))
150        {
151            return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
152        }
153    }
154
155    let k = leaf_certificate.public_key().raw.to_vec();
156    Ok(k)
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use crate::utils::StringExt;
163    use base64::engine::general_purpose::STANDARD;
164    use base64::{DecodeError, Engine};
165    extern crate base64;
166
167    const ROOT_CA_BASE64_ENCODED: &str = "MIIBgjCCASmgAwIBAgIJALUc5ALiH5pbMAoGCCqGSM49BAMDMDYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8wHhcNMjMwMTA1MjEzMDIyWhcNMzMwMTAyMjEzMDIyWjA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc+/Bl+gospo6tf9Z7io5tdKdrlN1YdVnqEhEDXDShzdAJPQijamXIMHf8xWWTa1zgoYTxOKpbuJtDplz1XriTaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDRwAwRAIgemWQXnMAdTad2JDJWng9U4uBBL5mA7WI05H7oH7c6iQCIHiRqMjNfzUAyiu9h6rOU/K+iTR0I/3Y/NSWsXHX+acc";
168    const INTERMEDIATE_CA_BASE64_ENCODED: &str = "MIIBnzCCAUWgAwIBAgIBCzAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEwNTIxMzEwNVoXDTMzMDEwMTIxMzEwNVowRTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xFTATBgNVBAoMDEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBUN5V9rKjfRiMAIojEA0Av5Mp0oF+O0cL4gzrTF178inUHugj7Et46NrkQ7hKgMVnjogq45Q1rMs+cMHVNILWqjNTAzMA8GA1UdEwQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMBAGCiqGSIb3Y2QGAgEEAgUAMAoGCCqGSM49BAMDA0gAMEUCIQCmsIKYs41ullssHX4rVveUT0Z7Is5/hLK1lFPTtun3hAIgc2+2RG5+gNcFVcs+XJeEl4GZ+ojl3ROOmll+ye7dynQ=";
169    const LEAF_CERT_BASE64_ENCODED: &str = "MIIBoDCCAUagAwIBAgIBDDAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzEzNFoXDTMzMDEwMTIxMzEzNFowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNIADBFAiBX4c+T0Fp5nJ5QRClRfu5PSByRvNPtuaTsk0vPB3WAIAIhANgaauAj/YP9s0AkEhyJhxQO/6Q2zouZ+H1CIOehnMzQ";
170
171    const INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED: &str = "MIIBnjCCAUWgAwIBAgIBDTAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEwNTIxMzYxNFoXDTMzMDEwMTIxMzYxNFowRTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xFTATBgNVBAoMDEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBUN5V9rKjfRiMAIojEA0Av5Mp0oF+O0cL4gzrTF178inUHugj7Et46NrkQ7hKgMVnjogq45Q1rMs+cMHVNILWqjNTAzMA8GA1UdEwQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMBAGCiqGSIb3Y2QGAgIEAgUAMAoGCCqGSM49BAMDA0cAMEQCIFROtTE+RQpKxNXETFsf7Mc0h+5IAsxxo/X6oCC/c33qAiAmC5rn5yCOOEjTY4R1H1QcQVh+eUwCl13NbQxWCuwxxA==";
172    const LEAF_CERT_FOR_INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED: &str = "MIIBnzCCAUagAwIBAgIBDjAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzY1OFoXDTMzMDEwMTIxMzY1OFowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNHADBEAiAUAs+gzYOsEXDwQquvHYbcVymyNqDtGw9BnUFp2YLuuAIgXxQ3Ie9YU0cMqkeaFd+lyo0asv9eyzk6stwjeIeOtTU=";
173    const LEAF_CERT_INVALID_OID_BASE64_ENCODED: &str = "MIIBoDCCAUagAwIBAgIBDzAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzczMVoXDTMzMDEwMTIxMzczMVowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsCBAIFADAKBggqhkjOPQQDAwNIADBFAiAb+7S3i//bSGy7skJY9+D4VgcQLKFeYfIMSrUCmdrFqwIhAIMVwzD1RrxPRtJyiOCXLyibIvwcY+VS73HYfk0O9lgz";
174
175    const LEAF_CERT_PUBLIC_KEY_BASE64_ENCODED: &str = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4rWBxGmFbnPIPQI0zsBKzLxsj8pD2vqbr0yPISUx2WQyxmrNql9fhK8YEEyYFV7++p5i4YUSR/o9uQIgCPIhrA==";
176
177    const REAL_APPLE_ROOT_BASE64_ENCODED: &str = "MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtfTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySrMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM6BgD56KyKA==";
178    const REAL_APPLE_MULTI_ROOT_BASE64_ENCODED: [&'static str; 4] = [
179        "MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUh",
180        "MIIFujCCBKKgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFEFwcGxlIENvbXB1dGVyLCBJbmMuMS0wKwYDVQQLEyRBcHBsZSBDb21wdXRlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIEFwcGxlIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA1MDIxMDAwMTgxNFoXDTI1MDIxMDAwMTgxNFowgYYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBcHBsZSBDb21wdXRlciwgSW5jLjEtMCsGA1UECxMkQXBwbGUgQ29tcHV0ZXIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSkwJwYDVQQDEyBBcHBsZSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1eeYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsqwx+VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV/X5vyJQO6VY9NXQ3xZDUjFUsVWR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw+dPfMrSSgayP7OtbkO2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4++NwzeajTEV+H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz/2deZbxJ2HafMxRloXeUyS0CAwEAAaOCAi8wggIrMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjCCASkGA1UdIASCASAwggEcMIIBGAYJKoZIhvdjZAUBMIIBCTBBBggrBgEFBQcCARY1aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Rlcm1zLmh0bWwwgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Jvb3QuY3JsMFUGCCsGAQUFBwEBBEkwRzBFBggrBgEFBQcwAoY5aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L2Nhc2lnbmVycy5odG1sMA0GCSqGSIb3DQEBBQUAA4IBAQCd2i0oWC99dgS5BNM+zrdmY06PL9T+S61yvaM5xlJNBZhS9YlRASR5vhoy9+VEi0tEBzmC1lrKtCBe2a4VXR2MHTK/ODFiSF3H4ZCx+CRA+F9Ym1FdV53B5f88zHIhbsTp6aF31ywXJsM/65roCwO66bNKcuszCVut5mIxauivL9WvHld2j383LS4CXN1jyfJxuCZA3xWNdUQ/eb3mHZnhQyw+rW++uaT+DjUZUWOxw961kj5ReAFziqQjyqSI8R5cH0EWLX6VCqrpiUGYGxrdyyC/R14MJsVVNU3GMIuZZxTHCR+6R8faAQmHJEKVvRNgGQrv6n8Obs3BREM6StXj",
181        "MIIFkjCCA3qgAwIBAgIIAeDltYNno+AwDQYJKoZIhvcNAQEMBQAwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEcyMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxMDA5WhcNMzkwNDMwMTgxMDA5WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzIxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgREkhI2imKScUcx+xuM23+TfvgHN6sXuI2pyT5f1BrTM65MFQn5bPW7SXmMLYFN14UIhHF6Kob0vuy0gmVOKTvKkmMXT5xZgM4+xb1hYjkWpIMBDLyyED7Ul+f9sDx47pFoFDVEovy3d6RhiPw9bZyLgHaC/YuOQhfGaFjQQscp5TBhsRTL3b2CtcM0YM/GlMZ81fVJ3/8E7j4ko380yhDPLVoACVdJ2LT3VXdRCCQgzWTxb+4Gftr49wIQuavbfqeQMpOhYV4SbHXw8EwOTKrfl+q04tvny0aIWhwZ7Oj8ZhBbZF8+NfbqOdfIRqMM78xdLe40fTgIvS/cjTf94FNcX1RoeKz8NMoFnNvzcytN31O661A4T+B/fc9Cj6i8b0xlilZ3MIZgIxbdMYs0xBTJh0UT8TUgWY8h2czJxQI6bR3hDRSj4n4aJgXv8O7qhOTH11UL6jHfPsNFL4VPSQ08prcdUFmIrQB1guvkJ4M6mL4m1k8COKWNORj3rw31OsMiANDC1CvoDTdUE0V+1ok2Az6DGOeHwOx4e7hqkP0ZmUoNwIx7wHHHtHMn23KVDpA287PT0aLSmWaasZobNfMmRtHsHLDd4/E92GcdB/O/WuhwpyUgquUoue9G7q5cDmVF8Up8zlYNPXEpMZ7YLlmQ1A/bmH8DvmGqmAMQ0uVAgMBAAGjQjBAMB0GA1UdDgQWBBTEmRNsGAPCe8CjoA1/coB6HHcmjTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAUabz4vS4PZO/Lc4Pu1vhVRROTtHlznldgX/+tvCHM/jvlOV+3Gp5pxy+8JS3ptEwnMgNCnWefZKVfhidfsJxaXwU6s+DDuQUQp50DhDNqxq6EWGBeNjxtUVAeKuowM77fWM3aPbn+6/Gw0vsHzYmE1SGlHKy6gLti23kDKaQwFd1z4xCfVzmMX3zybKSaUYOiPjjLUKyOKimGY3xn83uamW8GrAlvacp/fQ+onVJv57byfenHmOZ4VxG/5IFjPoeIPmGlFYl5bRXOJ3riGQUIUkhOb9iZqmxospvPyFgxYnURTbImHy99v6ZSYA7LNKmp4gDBDEZt7Y6YUX6yfIjyGNzv1aJMbDZfGKnexWoiIqrOEDCzBL/FePwN983csvMmOa/orz6JopxVtfnJBtIRD6e/J/JzBrsQzwBvDR4yGn1xuZW7AYJNpDrFEobXsmII9oDMJELuDY++ee1KG++P+w8j2Ud5cAeh6Squpj9kuNsJnfdBrRkBof0Tta6SqoWqPQFZ2aWuuJVecMsXUmPgEkrihLHdoBR37q9ZV0+N0djMenl9MU/S60EinpxLK8JQzcPqOMyT/RFtm2XNuyE9QoB6he7hY1Ck3DDUOUUi78/w0EP3SIEIwiKum1xRKtzCTrJ+VKACd+66eYWyi4uTLLT3OUEVLLUNIAytbwPF+E=",
182        "MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtfTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySrMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM6BgD56KyKA==",
183    ];
184    const REAL_APPLE_INTERMEDIATE_BASE64_ENCODED: &str = "MIIDFjCCApygAwIBAgIUIsGhRwp0c2nvU4YSycafPTjzbNcwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMjEwMzE3MjAzNzEwWhcNMzYwMzE5MDAwMDAwWjB1MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEbsQKC94PrlWmZXnXgtxzdVJL8T0SGYngDRGpngn3N6PT8JMEb7FDi4bBmPhCnZ3/sq6PF/cGcKXWsL5vOteRhyJ45x3ASP7cOB+aao90fcpxSv/EZFbniAbNgZGhIhpIo4H6MIH3MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUu7DeoVgziJqkipnevr3rr9rLJKswRgYIKwYBBQUHAQEEOjA4MDYGCCsGAQUFBzABhipodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNhZzMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2NybC5hcHBsZS5jb20vYXBwbGVyb290Y2FnMy5jcmwwHQYDVR0OBBYEFD8vlCNR01DJmig97bB85c+lkGKZMA4GA1UdDwEB/wQEAwIBBjAQBgoqhkiG92NkBgIBBAIFADAKBggqhkjOPQQDAwNoADBlAjBAXhSq5IyKogMCPtw490BaB677CaEGJXufQB/EqZGd6CSjiCtOnuMTbXVXmxxcxfkCMQDTSPxarZXvNrkxU3TkUMI33yzvFVVRT4wxWJC994OsdcZ4+RGNsYDyR5gmdr0nDGg=";
185    const REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED: &str = "MIIEMDCCA7agAwIBAgIQaPoPldvpSoEH0lBrjDPv9jAKBggqhkjOPQQDAzB1MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTIxMDgyNTAyNTAzNFoXDTIzMDkyNDAyNTAzM1owgZIxQDA+BgNVBAMMN1Byb2QgRUNDIE1hYyBBcHAgU3RvcmUgYW5kIGlUdW5lcyBTdG9yZSBSZWNlaXB0IFNpZ25pbmcxLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOoTcaPcpeipNL9eQ06tCu7pUcwdCXdN8vGqaUjd58Z8tLxiUC0dBeA+euMYggh1/5iAk+FMxUFmA2a1r4aCZ8SjggIIMIICBDAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFD8vlCNR01DJmig97bB85c+lkGKZMHAGCCsGAQUFBwEBBGQwYjAtBggrBgEFBQcwAoYhaHR0cDovL2NlcnRzLmFwcGxlLmNvbS93d2RyZzYuZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLXd3ZHJnNjAyMIIBHgYDVR0gBIIBFTCCAREwggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wHQYDVR0OBBYEFCOCmMBq//1L5imvVmqX1oCYeqrMMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNoADBlAjEAl4JB9GJHixP2nuibyU1k3wri5psGIxPME05sFKq7hQuzvbeyBu82FozzxmbzpogoAjBLSFl0dZWIYl2ejPV+Di5fBnKPu8mymBQtoE/H2bES0qAs8bNueU3CBjjh1lwnDsI=";
186    const EFFECTIVE_DATE: u64 = 1681312846;
187
188    #[test]
189    fn test_valid_chain_without_ocsp() -> Result<(), ChainVerifierError> {
190        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
191        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
192        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
193        let chain = vec![leaf.clone(), intermediate, root.clone()];
194
195        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE))?;
196        assert_eq!(
197            LEAF_CERT_PUBLIC_KEY_BASE64_ENCODED.as_der_bytes().unwrap(),
198            public_key
199        );
200        Ok(())
201    }
202
203    #[test]
204    fn test_valid_chain_invalid_intermediate_oid_without_ocsp() -> Result<(), ChainVerifierError> {
205        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
206        let leaf = LEAF_CERT_FOR_INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
207            .as_der_bytes()
208            .unwrap();
209        let intermediate = INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
210            .as_der_bytes()
211            .unwrap();
212        let chain = vec![leaf.clone(), intermediate, root.clone()];
213
214        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
215        assert_eq!(
216            public_key.expect_err("Expect error"),
217            ChainVerifierError::VerificationFailure(InvalidCertificate)
218        );
219        Ok(())
220    }
221
222    #[test]
223    fn test_valid_chain_invalid_leaf_oid_without_ocsp() -> Result<(), ChainVerifierError> {
224        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
225        let leaf = LEAF_CERT_INVALID_OID_BASE64_ENCODED.as_der_bytes().unwrap();
226        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
227        let chain = vec![leaf.clone(), intermediate, root.clone()];
228
229        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
230        assert_eq!(
231            public_key.expect_err("Expect error"),
232            ChainVerifierError::VerificationFailure(InvalidCertificate)
233        );
234        Ok(())
235    }
236
237    #[test]
238    fn test_invalid_chain_length() -> Result<(), ChainVerifierError> {
239        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
240        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
241        let intermediate = INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
242            .as_der_bytes()
243            .unwrap();
244        let chain = vec![leaf.clone(), intermediate];
245
246        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
247        assert_eq!(
248            public_key.expect_err("Expect error"),
249            ChainVerifierError::VerificationFailure(InvalidChainLength)
250        );
251        Ok(())
252    }
253
254    #[test]
255    fn test_invalid_base64_in_certificate_list() -> Result<(), ChainVerifierError> {
256        assert_eq!(
257            "abc".as_der_bytes().expect_err("Expect Error"),
258            DecodeError::InvalidPadding
259        );
260        Ok(())
261    }
262
263    #[test]
264    fn test_invalid_data_in_certificate_list() -> Result<(), ChainVerifierError> {
265        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
266        let leaf = STANDARD.encode("abc").as_der_bytes().unwrap();
267        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
268        let chain = vec![leaf.clone(), intermediate, root.clone()];
269
270        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
271        assert_eq!(
272            public_key.expect_err("Expect error"),
273            ChainVerifierError::VerificationFailure(InvalidCertificate)
274        );
275        Ok(())
276    }
277
278    #[test]
279    fn test_malformed_root_cert() -> Result<(), ChainVerifierError> {
280        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
281        let malformed_root = STANDARD.encode("abc").as_der_bytes().unwrap();
282        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
283        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
284        let chain = vec![leaf.clone(), intermediate, root.clone()];
285
286        let public_key = verify_chain(&chain, &vec![malformed_root], Some(EFFECTIVE_DATE));
287        assert_eq!(
288            public_key.expect_err("Expect error"),
289            ChainVerifierError::VerificationFailure(InvalidCertificate)
290        );
291        Ok(())
292    }
293
294    #[test]
295    fn test_chain_different_than_root_certificate() -> Result<(), ChainVerifierError> {
296        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
297        let real_root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes().unwrap();
298        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
299        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
300        let chain = vec![leaf.clone(), intermediate, root.clone()];
301
302        let public_key = verify_chain(&chain, &vec![real_root], Some(EFFECTIVE_DATE));
303        assert_eq!(
304            public_key.expect_err("Expect error"),
305            ChainVerifierError::VerificationFailure(InvalidCertificate)
306        );
307        Ok(())
308    }
309
310    #[test]
311    fn test_valid_expired_chain() -> Result<(), ChainVerifierError> {
312        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
313        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
314        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
315        let chain = vec![leaf.clone(), intermediate, root.clone()];
316
317        let public_key = verify_chain(&chain, &vec![root], Some(2280946846));
318        assert_eq!(
319            public_key.expect_err("Expect error"),
320            ChainVerifierError::VerificationFailure(CertificateExpired)
321        );
322        Ok(())
323    }
324
325    #[test]
326    fn test_apple_chain_is_valid() -> Result<(), ChainVerifierError> {
327        let root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes().unwrap();
328        let leaf = REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED
329            .as_der_bytes()
330            .unwrap();
331        let intermediate = REAL_APPLE_INTERMEDIATE_BASE64_ENCODED
332            .as_der_bytes()
333            .unwrap();
334        let chain = vec![leaf.clone(), intermediate, root.clone()];
335
336        let _public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE)).unwrap();
337        Ok(())
338    }
339
340    #[test]
341    fn test_apple_chain_is_valid_multi_root() -> Result<(), ChainVerifierError> {
342        let root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes()?;
343        let leaf = REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED
344            .as_der_bytes()?;
345        let intermediate = REAL_APPLE_INTERMEDIATE_BASE64_ENCODED
346            .as_der_bytes()?;
347        let chain = vec![leaf.clone(), intermediate, root.clone()];
348
349        let multi_root: Vec<_> = REAL_APPLE_MULTI_ROOT_BASE64_ENCODED
350            .into_iter()
351            .map(|str| str.as_der_bytes().unwrap())
352            .collect();
353
354        let _public_key = verify_chain(&chain, &multi_root, Some(EFFECTIVE_DATE))?;
355        Ok(())
356    }
357}