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    #[error("CertificateRevoked")]
45    CertificateRevoked,
46}
47
48const EXPECTED_CHAIN_LENGTH: usize = 3;
49
50/// Verifies a certificate chain.
51///
52/// This function verifies a certificate chain consisting of multiple certificates. It performs various
53/// checks to ensure the validity and integrity of the chain.
54///
55/// # Arguments
56///
57/// * `certificates`: A vector of byte slices containing the certificates in the chain.
58/// * `root_certificates`: A vector of byte slices containing the root certificates.
59/// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
60///
61/// # Returns
62///
63/// * `Ok(Vec<u8>)`: If the certificate chain is valid, it returns the public key data from the leaf certificate.
64/// * `Err(ChainVerifierError)`: If the chain verification fails for any reason, it returns a `ChainVerifierError` enum.
65///
66/// # Example
67///
68/// ```rust
69/// use app_store_server_library::chain_verifier::{verify_chain, ChainVerifierError};
70///
71/// fn main() {
72///     let certificates: Vec<Vec<u8>> = vec![]; // Load your certificates here
73///     let root_certificates: Vec<Vec<u8>> = vec![]; // Load your root certificates here
74///     let effective_date: Option<u64> = None; // Provide an effective date if needed
75///
76///     match verify_chain(&certificates, &root_certificates, effective_date) {
77///         Ok(public_key) => println!("Certificate chain is valid. Public key: {:?}", public_key),
78///         Err(err) => eprintln!("Certificate chain verification failed: {}", err),
79///     }
80/// }
81/// ```
82///
83/// TODO: Implement issuer checking
84pub fn verify_chain(
85    certificates: &Vec<Vec<u8>>,
86    root_certificates: &Vec<Vec<u8>>,
87    effective_date: Option<u64>,
88) -> Result<Vec<u8>, ChainVerifierError> {
89    if root_certificates.is_empty() {
90        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
91    }
92
93    if certificates.len() != EXPECTED_CHAIN_LENGTH {
94        return Err(ChainVerifierError::VerificationFailure(InvalidChainLength));
95    }
96
97    let leaf_certificate = &certificates[0];
98    let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
99        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
100    };
101    let leaf_certificate = leaf_certificate.1;
102
103    let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))?
104    else {
105        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
106    };
107
108    let intermediate_certificate = &certificates[1];
109    let Ok(intermediate_certificate) =
110        X509Certificate::from_der(intermediate_certificate.as_slice())
111    else {
112        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
113    };
114    let intermediate_certificate = intermediate_certificate.1;
115
116    let Some(_) =
117        intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))?
118    else {
119        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
120    };
121
122    let mut root_certificate: Option<X509Certificate> = None;
123
124    for cert in root_certificates {
125        let Ok(cert) = X509Certificate::from_der(&cert) else {
126            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
127        };
128
129        match intermediate_certificate.verify_signature(Some(cert.1.public_key())) {
130            Ok(_) => (),
131            Err(_) => continue,
132        }
133
134        root_certificate = Some(cert.1)
135    }
136
137    let Some(root_certificate) = root_certificate else {
138        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
139    };
140
141    verify_chain_impl(&leaf_certificate, &intermediate_certificate, &root_certificate, effective_date)
142}
143
144fn verify_chain_impl(leaf: &X509Certificate, intermediate: &X509Certificate, root_certificate: &X509Certificate, effective_date: Option<u64>) -> Result<Vec<u8>, ChainVerifierError> {
145    leaf.verify_signature(Some(intermediate.public_key()))?;
146
147    if let Some(date) = effective_date {
148        let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
149            return Err(ChainVerifierError::VerificationFailure(
150                InvalidEffectiveDate,
151            ));
152        };
153
154        if !(root_certificate.validity.is_valid_at(time) &&
155            leaf.validity.is_valid_at(time) &&
156            intermediate.validity.is_valid_at(time)) {
157            return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
158        }
159    }
160
161    let k = leaf.public_key().raw.to_vec();
162
163    // Make online verification as additional step if ocsp flag enabled
164    #[cfg(all(feature = "ocsp"))] {
165        use crate::chain_verifier_ocsp::check_ocsp_status;
166        // Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
167        match check_ocsp_status(leaf, intermediate) {
168            Ok(()) => {
169                // Certificate is valid according to OCSP
170            }
171            Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
172                // Certificate is revoked - this should fail
173                return Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked));
174            }
175            Err(e) => {
176                // Other OCSP errors (network, parsing, etc.) - log but don't fail
177                eprintln!("OCSP check failed (non-fatal): {:?}", e);
178            }
179        }
180    };
181
182    Ok(k)
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use crate::utils::StringExt;
189    use base64::engine::general_purpose::STANDARD;
190    use base64::{DecodeError, Engine};
191    extern crate base64;
192
193    const ROOT_CA_BASE64_ENCODED: &str = "MIIBgjCCASmgAwIBAgIJALUc5ALiH5pbMAoGCCqGSM49BAMDMDYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDdXBlcnRpbm8wHhcNMjMwMTA1MjEzMDIyWhcNMzMwMTAyMjEzMDIyWjA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc+/Bl+gospo6tf9Z7io5tdKdrlN1YdVnqEhEDXDShzdAJPQijamXIMHf8xWWTa1zgoYTxOKpbuJtDplz1XriTaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDRwAwRAIgemWQXnMAdTad2JDJWng9U4uBBL5mA7WI05H7oH7c6iQCIHiRqMjNfzUAyiu9h6rOU/K+iTR0I/3Y/NSWsXHX+acc";
194    const INTERMEDIATE_CA_BASE64_ENCODED: &str = "MIIBnzCCAUWgAwIBAgIBCzAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEwNTIxMzEwNVoXDTMzMDEwMTIxMzEwNVowRTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xFTATBgNVBAoMDEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBUN5V9rKjfRiMAIojEA0Av5Mp0oF+O0cL4gzrTF178inUHugj7Et46NrkQ7hKgMVnjogq45Q1rMs+cMHVNILWqjNTAzMA8GA1UdEwQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMBAGCiqGSIb3Y2QGAgEEAgUAMAoGCCqGSM49BAMDA0gAMEUCIQCmsIKYs41ullssHX4rVveUT0Z7Is5/hLK1lFPTtun3hAIgc2+2RG5+gNcFVcs+XJeEl4GZ+ojl3ROOmll+ye7dynQ=";
195    const LEAF_CERT_BASE64_ENCODED: &str = "MIIBoDCCAUagAwIBAgIBDDAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzEzNFoXDTMzMDEwMTIxMzEzNFowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNIADBFAiBX4c+T0Fp5nJ5QRClRfu5PSByRvNPtuaTsk0vPB3WAIAIhANgaauAj/YP9s0AkEhyJhxQO/6Q2zouZ+H1CIOehnMzQ";
196
197    const INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED: &str = "MIIBnjCCAUWgAwIBAgIBDTAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEwNTIxMzYxNFoXDTMzMDEwMTIxMzYxNFowRTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xFTATBgNVBAoMDEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBUN5V9rKjfRiMAIojEA0Av5Mp0oF+O0cL4gzrTF178inUHugj7Et46NrkQ7hKgMVnjogq45Q1rMs+cMHVNILWqjNTAzMA8GA1UdEwQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMBAGCiqGSIb3Y2QGAgIEAgUAMAoGCCqGSM49BAMDA0cAMEQCIFROtTE+RQpKxNXETFsf7Mc0h+5IAsxxo/X6oCC/c33qAiAmC5rn5yCOOEjTY4R1H1QcQVh+eUwCl13NbQxWCuwxxA==";
198    const LEAF_CERT_FOR_INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED: &str = "MIIBnzCCAUagAwIBAgIBDjAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzY1OFoXDTMzMDEwMTIxMzY1OFowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNHADBEAiAUAs+gzYOsEXDwQquvHYbcVymyNqDtGw9BnUFp2YLuuAIgXxQ3Ie9YU0cMqkeaFd+lyo0asv9eyzk6stwjeIeOtTU=";
199    const LEAF_CERT_INVALID_OID_BASE64_ENCODED: &str = "MIIBoDCCAUagAwIBAgIBDzAKBggqhkjOPQQDAzBFMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEVMBMGA1UECgwMSW50ZXJtZWRpYXRlMB4XDTIzMDEwNTIxMzczMVoXDTMzMDEwMTIxMzczMVowPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDTALBgNVBAoMBExlYWYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATitYHEaYVuc8g9AjTOwErMvGyPykPa+puvTI8hJTHZZDLGas2qX1+ErxgQTJgVXv76nmLhhRJH+j25AiAI8iGsoy8wLTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsCBAIFADAKBggqhkjOPQQDAwNIADBFAiAb+7S3i//bSGy7skJY9+D4VgcQLKFeYfIMSrUCmdrFqwIhAIMVwzD1RrxPRtJyiOCXLyibIvwcY+VS73HYfk0O9lgz";
200
201    const LEAF_CERT_PUBLIC_KEY_BASE64_ENCODED: &str = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4rWBxGmFbnPIPQI0zsBKzLxsj8pD2vqbr0yPISUx2WQyxmrNql9fhK8YEEyYFV7++p5i4YUSR/o9uQIgCPIhrA==";
202
203    const REAL_APPLE_ROOT_BASE64_ENCODED: &str = "MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtfTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySrMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM6BgD56KyKA==";
204    const REAL_APPLE_MULTI_ROOT_BASE64_ENCODED: [&'static str; 4] = [
205        "MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUh",
206        "MIIFujCCBKKgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFEFwcGxlIENvbXB1dGVyLCBJbmMuMS0wKwYDVQQLEyRBcHBsZSBDb21wdXRlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIEFwcGxlIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA1MDIxMDAwMTgxNFoXDTI1MDIxMDAwMTgxNFowgYYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBcHBsZSBDb21wdXRlciwgSW5jLjEtMCsGA1UECxMkQXBwbGUgQ29tcHV0ZXIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSkwJwYDVQQDEyBBcHBsZSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1eeYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsqwx+VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV/X5vyJQO6VY9NXQ3xZDUjFUsVWR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw+dPfMrSSgayP7OtbkO2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4++NwzeajTEV+H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz/2deZbxJ2HafMxRloXeUyS0CAwEAAaOCAi8wggIrMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjCCASkGA1UdIASCASAwggEcMIIBGAYJKoZIhvdjZAUBMIIBCTBBBggrBgEFBQcCARY1aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Rlcm1zLmh0bWwwgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Jvb3QuY3JsMFUGCCsGAQUFBwEBBEkwRzBFBggrBgEFBQcwAoY5aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L2Nhc2lnbmVycy5odG1sMA0GCSqGSIb3DQEBBQUAA4IBAQCd2i0oWC99dgS5BNM+zrdmY06PL9T+S61yvaM5xlJNBZhS9YlRASR5vhoy9+VEi0tEBzmC1lrKtCBe2a4VXR2MHTK/ODFiSF3H4ZCx+CRA+F9Ym1FdV53B5f88zHIhbsTp6aF31ywXJsM/65roCwO66bNKcuszCVut5mIxauivL9WvHld2j383LS4CXN1jyfJxuCZA3xWNdUQ/eb3mHZnhQyw+rW++uaT+DjUZUWOxw961kj5ReAFziqQjyqSI8R5cH0EWLX6VCqrpiUGYGxrdyyC/R14MJsVVNU3GMIuZZxTHCR+6R8faAQmHJEKVvRNgGQrv6n8Obs3BREM6StXj",
207        "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=",
208        "MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtfTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySrMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM6BgD56KyKA==",
209    ];
210    const REAL_APPLE_INTERMEDIATE_BASE64_ENCODED: &str = "MIIDFjCCApygAwIBAgIUIsGhRwp0c2nvU4YSycafPTjzbNcwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMjEwMzE3MjAzNzEwWhcNMzYwMzE5MDAwMDAwWjB1MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEbsQKC94PrlWmZXnXgtxzdVJL8T0SGYngDRGpngn3N6PT8JMEb7FDi4bBmPhCnZ3/sq6PF/cGcKXWsL5vOteRhyJ45x3ASP7cOB+aao90fcpxSv/EZFbniAbNgZGhIhpIo4H6MIH3MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUu7DeoVgziJqkipnevr3rr9rLJKswRgYIKwYBBQUHAQEEOjA4MDYGCCsGAQUFBzABhipodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNhZzMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2NybC5hcHBsZS5jb20vYXBwbGVyb290Y2FnMy5jcmwwHQYDVR0OBBYEFD8vlCNR01DJmig97bB85c+lkGKZMA4GA1UdDwEB/wQEAwIBBjAQBgoqhkiG92NkBgIBBAIFADAKBggqhkjOPQQDAwNoADBlAjBAXhSq5IyKogMCPtw490BaB677CaEGJXufQB/EqZGd6CSjiCtOnuMTbXVXmxxcxfkCMQDTSPxarZXvNrkxU3TkUMI33yzvFVVRT4wxWJC994OsdcZ4+RGNsYDyR5gmdr0nDGg=";
211    const REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED: &str = "MIIEMDCCA7agAwIBAgIQaPoPldvpSoEH0lBrjDPv9jAKBggqhkjOPQQDAzB1MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTIxMDgyNTAyNTAzNFoXDTIzMDkyNDAyNTAzM1owgZIxQDA+BgNVBAMMN1Byb2QgRUNDIE1hYyBBcHAgU3RvcmUgYW5kIGlUdW5lcyBTdG9yZSBSZWNlaXB0IFNpZ25pbmcxLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOoTcaPcpeipNL9eQ06tCu7pUcwdCXdN8vGqaUjd58Z8tLxiUC0dBeA+euMYggh1/5iAk+FMxUFmA2a1r4aCZ8SjggIIMIICBDAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFD8vlCNR01DJmig97bB85c+lkGKZMHAGCCsGAQUFBwEBBGQwYjAtBggrBgEFBQcwAoYhaHR0cDovL2NlcnRzLmFwcGxlLmNvbS93d2RyZzYuZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLXd3ZHJnNjAyMIIBHgYDVR0gBIIBFTCCAREwggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wHQYDVR0OBBYEFCOCmMBq//1L5imvVmqX1oCYeqrMMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADAKBggqhkjOPQQDAwNoADBlAjEAl4JB9GJHixP2nuibyU1k3wri5psGIxPME05sFKq7hQuzvbeyBu82FozzxmbzpogoAjBLSFl0dZWIYl2ejPV+Di5fBnKPu8mymBQtoE/H2bES0qAs8bNueU3CBjjh1lwnDsI=";
212    const EFFECTIVE_DATE: u64 = 1681312846;
213
214    #[test]
215    #[cfg(all(feature = "ocsp"))]
216    fn test_apple_chain_is_valid_with_ocsp() -> Result<(), ChainVerifierError> {
217        let root = crate::chain_verifier::tests::REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes().unwrap();
218        let leaf = crate::chain_verifier::tests::REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED
219            .as_der_bytes()
220            .unwrap();
221        let intermediate = crate::chain_verifier::tests::REAL_APPLE_INTERMEDIATE_BASE64_ENCODED
222            .as_der_bytes()
223            .unwrap();
224        let chain = vec![leaf.clone(), intermediate, root.clone()];
225
226        let _public_key = verify_chain(&chain, &vec![root], Some(crate::chain_verifier::tests::EFFECTIVE_DATE)).unwrap();
227        Ok(())
228    }
229
230    #[test]
231    #[cfg(not(feature = "ocsp"))]
232    fn test_valid_chain_without_ocsp() -> Result<(), ChainVerifierError> {
233        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
234        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
235        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
236        let chain = vec![leaf.clone(), intermediate, root.clone()];
237
238        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE))?;
239        assert_eq!(
240            LEAF_CERT_PUBLIC_KEY_BASE64_ENCODED.as_der_bytes().unwrap(),
241            public_key
242        );
243        Ok(())
244    }
245
246    #[test]
247    #[cfg(not(feature = "ocsp"))]
248    fn test_valid_chain_invalid_intermediate_oid_without_ocsp() -> Result<(), ChainVerifierError> {
249        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
250        let leaf = LEAF_CERT_FOR_INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
251            .as_der_bytes()
252            .unwrap();
253        let intermediate = INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
254            .as_der_bytes()
255            .unwrap();
256        let chain = vec![leaf.clone(), intermediate, root.clone()];
257
258        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
259        assert_eq!(
260            public_key.expect_err("Expect error"),
261            ChainVerifierError::VerificationFailure(InvalidCertificate)
262        );
263        Ok(())
264    }
265
266    #[test]
267    #[cfg(not(feature = "ocsp"))]
268    fn test_valid_chain_invalid_leaf_oid_without_ocsp() -> Result<(), ChainVerifierError> {
269        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
270        let leaf = LEAF_CERT_INVALID_OID_BASE64_ENCODED.as_der_bytes().unwrap();
271        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
272        let chain = vec![leaf.clone(), intermediate, root.clone()];
273
274        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
275        assert_eq!(
276            public_key.expect_err("Expect error"),
277            ChainVerifierError::VerificationFailure(InvalidCertificate)
278        );
279        Ok(())
280    }
281
282    #[test]
283    #[cfg(not(feature = "ocsp"))]
284    fn test_invalid_chain_length() -> Result<(), ChainVerifierError> {
285        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
286        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
287        let intermediate = INTERMEDIATE_CA_INVALID_OID_BASE64_ENCODED
288            .as_der_bytes()
289            .unwrap();
290        let chain = vec![leaf.clone(), intermediate];
291
292        let public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE));
293        assert_eq!(
294            public_key.expect_err("Expect error"),
295            ChainVerifierError::VerificationFailure(InvalidChainLength)
296        );
297        Ok(())
298    }
299
300    #[test]
301    fn test_invalid_base64_in_certificate_list() -> Result<(), ChainVerifierError> {
302        assert_eq!(
303            "abc".as_der_bytes().expect_err("Expect Error"),
304            DecodeError::InvalidPadding
305        );
306        Ok(())
307    }
308
309    #[test]
310    #[cfg(not(feature = "ocsp"))]
311    fn test_invalid_data_in_certificate_list() -> Result<(), ChainVerifierError> {
312        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
313        let leaf = STANDARD.encode("abc").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(EFFECTIVE_DATE));
318        assert_eq!(
319            public_key.expect_err("Expect error"),
320            ChainVerifierError::VerificationFailure(InvalidCertificate)
321        );
322        Ok(())
323    }
324
325    #[test]
326    #[cfg(not(feature = "ocsp"))]
327    fn test_malformed_root_cert() -> Result<(), ChainVerifierError> {
328        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
329        let malformed_root = STANDARD.encode("abc").as_der_bytes().unwrap();
330        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
331        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
332        let chain = vec![leaf.clone(), intermediate, root.clone()];
333
334        let public_key = verify_chain(&chain, &vec![malformed_root], Some(EFFECTIVE_DATE));
335        assert_eq!(
336            public_key.expect_err("Expect error"),
337            ChainVerifierError::VerificationFailure(InvalidCertificate)
338        );
339        Ok(())
340    }
341
342    #[test]
343    #[cfg(not(feature = "ocsp"))]
344    fn test_chain_different_than_root_certificate() -> Result<(), ChainVerifierError> {
345        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
346        let real_root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes().unwrap();
347        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
348        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
349        let chain = vec![leaf.clone(), intermediate, root.clone()];
350
351        let public_key = verify_chain(&chain, &vec![real_root], Some(EFFECTIVE_DATE));
352        assert_eq!(
353            public_key.expect_err("Expect error"),
354            ChainVerifierError::VerificationFailure(InvalidCertificate)
355        );
356        Ok(())
357    }
358
359    #[test]
360    #[cfg(not(feature = "ocsp"))]
361    fn test_valid_expired_chain() -> Result<(), ChainVerifierError> {
362        let root = ROOT_CA_BASE64_ENCODED.as_der_bytes().unwrap();
363        let leaf = LEAF_CERT_BASE64_ENCODED.as_der_bytes().unwrap();
364        let intermediate = INTERMEDIATE_CA_BASE64_ENCODED.as_der_bytes().unwrap();
365        let chain = vec![leaf.clone(), intermediate, root.clone()];
366
367        let public_key = verify_chain(&chain, &vec![root], Some(2280946846));
368        assert_eq!(
369            public_key.expect_err("Expect error"),
370            ChainVerifierError::VerificationFailure(CertificateExpired)
371        );
372        Ok(())
373    }
374
375    #[test]
376    #[cfg(not(feature = "ocsp"))]
377    fn test_apple_chain_is_valid() -> Result<(), ChainVerifierError> {
378        let root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes().unwrap();
379        let leaf = REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED
380            .as_der_bytes()
381            .unwrap();
382        let intermediate = REAL_APPLE_INTERMEDIATE_BASE64_ENCODED
383            .as_der_bytes()
384            .unwrap();
385        let chain = vec![leaf.clone(), intermediate, root.clone()];
386
387        let _public_key = verify_chain(&chain, &vec![root], Some(EFFECTIVE_DATE)).unwrap();
388        Ok(())
389    }
390
391    #[test]
392    #[cfg(not(feature = "ocsp"))]
393    fn test_apple_chain_is_valid_multi_root() -> Result<(), ChainVerifierError> {
394        let root = REAL_APPLE_ROOT_BASE64_ENCODED.as_der_bytes()?;
395        let leaf = REAL_APPLE_SIGNING_CERTIFICATE_BASE64_ENCODED
396            .as_der_bytes()?;
397        let intermediate = REAL_APPLE_INTERMEDIATE_BASE64_ENCODED
398            .as_der_bytes()?;
399        let chain = vec![leaf.clone(), intermediate, root.clone()];
400
401        let multi_root: Vec<_> = REAL_APPLE_MULTI_ROOT_BASE64_ENCODED
402            .into_iter()
403            .map(|str| str.as_der_bytes().unwrap())
404            .collect();
405
406        let _public_key = verify_chain(&chain, &multi_root, Some(EFFECTIVE_DATE))?;
407        Ok(())
408    }
409}