app_store_server_library/
chain_verifier.rs

1use crate::chain_verifier::ChainVerificationFailureReason::{
2    CertificateExpired, InvalidCertificate, InvalidChainLength, InvalidEffectiveDate,
3};
4use thiserror::Error;
5
6use x509_parser::certificate::X509Certificate;
7use x509_parser::der_parser::asn1_rs::oid;
8use x509_parser::error::X509Error;
9use x509_parser::prelude::{ASN1Time, FromDer};
10
11#[derive(Error, Debug, PartialEq)]
12pub enum ChainVerifierError {
13    #[error("VerificationFailure: [{0}]")]
14    VerificationFailure(ChainVerificationFailureReason),
15
16    #[error("InternalX509Error: [{0}]")]
17    InternalX509Error(#[from] X509Error),
18
19    #[error("InternalDecodeError: [{0}]")]
20    InternalDecodeError(#[from] base64::DecodeError),
21}
22
23#[derive(Error, Debug, PartialEq)]
24pub enum ChainVerificationFailureReason {
25    #[error("InvalidAppIdentifier")]
26    InvalidAppIdentifier,
27
28    #[error("InvalidIssuer")]
29    InvalidIssuer,
30
31    #[error("InvalidCertificate")]
32    InvalidCertificate,
33
34    #[error("InvalidChainLength")]
35    InvalidChainLength,
36
37    #[error("InvalidChain")]
38    InvalidChain,
39
40    #[error("InvalidEnvironment")]
41    InvalidEffectiveDate,
42
43    #[error("CertificateExpired")]
44    CertificateExpired,
45
46    #[error("CertificateRevoked")]
47    CertificateRevoked,
48}
49
50const EXPECTED_CHAIN_LENGTH: usize = 3;
51
52/// Verifies a certificate chain.
53///
54/// This function verifies a certificate chain consisting of multiple certificates. It performs various
55/// checks to ensure the validity and integrity of the chain.
56///
57/// # Arguments
58///
59/// * `certificates`: A vector of byte slices containing the certificates in the chain.
60/// * `root_certificates`: A vector of byte slices containing the root certificates.
61/// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
62///
63/// # Returns
64///
65/// * `Ok(Vec<u8>)`: If the certificate chain is valid, it returns the public key data from the leaf certificate.
66/// * `Err(ChainVerifierError)`: If the chain verification fails for any reason, it returns a `ChainVerifierError` enum.
67///
68/// # Example
69///
70/// ```rust
71/// use app_store_server_library::chain_verifier::{verify_chain, ChainVerifierError};
72///
73/// fn main() {
74///     let certificates: Vec<Vec<u8>> = vec![]; // Load your certificates here
75///     let root_certificates: Vec<Vec<u8>> = vec![]; // Load your root certificates here
76///     let effective_date: Option<u64> = None; // Provide an effective date if needed
77///
78///     match verify_chain(&certificates, &root_certificates, effective_date) {
79///         Ok(public_key) => println!("Certificate chain is valid. Public key: {:?}", public_key),
80///         Err(err) => eprintln!("Certificate chain verification failed: {}", err),
81///     }
82/// }
83/// ```
84///
85/// TODO: Implement issuer checking
86pub fn verify_chain(
87    certificates: &Vec<Vec<u8>>,
88    root_certificates: &Vec<Vec<u8>>,
89    effective_date: Option<u64>,
90) -> Result<Vec<u8>, ChainVerifierError> {
91    if root_certificates.is_empty() {
92        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
93    }
94
95    if certificates.len() != EXPECTED_CHAIN_LENGTH {
96        return Err(ChainVerifierError::VerificationFailure(InvalidChainLength));
97    }
98
99    let leaf_certificate = &certificates[0];
100    let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
101        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
102    };
103    let leaf_certificate = leaf_certificate.1;
104
105    let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))? else {
106        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
107    };
108
109    let intermediate_certificate = &certificates[1];
110    let Ok(intermediate_certificate) = X509Certificate::from_der(intermediate_certificate.as_slice()) else {
111        return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
112    };
113    let intermediate_certificate = intermediate_certificate.1;
114
115    let Some(_) = intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))? 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    verify_chain_impl(
139        &leaf_certificate,
140        &intermediate_certificate,
141        &root_certificate,
142        effective_date,
143    )
144}
145
146fn verify_chain_impl(
147    leaf: &X509Certificate,
148    intermediate: &X509Certificate,
149    root_certificate: &X509Certificate,
150    effective_date: Option<u64>,
151) -> Result<Vec<u8>, ChainVerifierError> {
152    leaf.verify_signature(Some(intermediate.public_key()))?;
153
154    if let Some(date) = effective_date {
155        let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
156            return Err(ChainVerifierError::VerificationFailure(
157                InvalidEffectiveDate,
158            ));
159        };
160
161        if !(root_certificate.validity.is_valid_at(time) &&
162            leaf.validity.is_valid_at(time) &&
163            intermediate.validity.is_valid_at(time))
164        {
165            return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
166        }
167    }
168
169    let k = leaf.public_key().raw.to_vec();
170
171    // Make online verification as additional step if ocsp flag enabled
172    #[cfg(all(feature = "ocsp"))]
173    {
174        use crate::chain_verifier_ocsp::check_ocsp_status;
175        // Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
176        match check_ocsp_status(leaf, intermediate) {
177            Ok(()) => {
178                // Certificate is valid according to OCSP
179            }
180            Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
181                // Certificate is revoked - this should fail
182                return Err(ChainVerifierError::VerificationFailure(
183                    ChainVerificationFailureReason::CertificateRevoked,
184                ));
185            }
186            Err(e) => {
187                // Other OCSP errors (network, parsing, etc.) - log but don't fail
188                eprintln!("OCSP check failed (non-fatal): {:?}", e);
189            }
190        }
191    };
192
193    Ok(k)
194}