app_store_server_library/
chain_verifier.rs

1use crate::x509::x509::X509Error;
2use crate::chain_verifier::ChainVerificationFailureReason::{
3    CertificateExpired, InvalidCertificate, InvalidEffectiveDate,
4};
5use thiserror::Error;
6
7use x509_cert::Certificate;
8use const_oid::ObjectIdentifier;
9use crate::x509::x509;
10
11#[derive(Error, Debug, PartialEq)]
12pub enum ChainVerifierError {
13    #[error("VerificationFailure: [{0}]")]
14    VerificationFailure(ChainVerificationFailureReason),
15
16    #[error("InternalX509Error: [{0}]")]
17    InternalX509Error(String),
18
19    #[error("InternalDecodeError: [{0}]")]
20    InternalDecodeError(#[from] base64::DecodeError),
21}
22
23impl From<X509Error> for ChainVerifierError {
24    fn from(err: X509Error) -> Self {
25        ChainVerifierError::InternalX509Error(err.to_string())
26    }
27}
28
29#[derive(Error, Debug, PartialEq)]
30pub enum ChainVerificationFailureReason {
31    #[error("InvalidAppIdentifier")]
32    InvalidAppIdentifier,
33
34    #[error("InvalidIssuer")]
35    InvalidIssuer,
36
37    #[error("InvalidCertificate")]
38    InvalidCertificate,
39
40    #[error("InvalidChainLength")]
41    InvalidChainLength,
42
43    #[error("InvalidChain")]
44    InvalidChain,
45
46    #[error("InvalidEnvironment")]
47    InvalidEffectiveDate,
48
49    #[error("CertificateExpired")]
50    CertificateExpired,
51
52    #[error("CertificateRevoked")]
53    CertificateRevoked,
54
55    #[error("RetryableVerificationFailure")]
56    RetryableVerificationFailure,
57}
58
59/// A structure for verifying certificate chains.
60///
61/// This struct holds the root certificates and provides methods to verify certificate chains.
62pub struct ChainVerifier {
63    root_certificates: Vec<Vec<u8>>,
64}
65
66impl ChainVerifier {
67    /// Creates a new `ChainVerifier` with the provided root certificates.
68    ///
69    /// # Arguments
70    ///
71    /// * `root_certificates`: A vector of byte slices containing the root certificates.
72    ///
73    /// # Returns
74    ///
75    /// A new instance of `ChainVerifier`.
76    pub fn new(root_certificates: Vec<Vec<u8>>) -> Self {
77        ChainVerifier { root_certificates }
78    }
79
80    /// Verifies a certificate pair (leaf and intermediate).
81    ///
82    /// This method verifies a leaf certificate against an intermediate certificate. It performs various
83    /// checks to ensure the validity and integrity of the certificates.
84    ///
85    /// # Arguments
86    ///
87    /// * `leaf_certificate`: The leaf certificate as a byte slice.
88    /// * `intermediate_certificate`: The intermediate certificate as a byte slice.
89    /// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
90    ///
91    /// # Returns
92    ///
93    /// * `Ok(Vec<u8>)`: If the certificates are valid, it returns the public key data from the leaf certificate.
94    /// * `Err(ChainVerifierError)`: If the verification fails for any reason, it returns a `ChainVerifierError` enum.
95    /// TODO: Implement issuer checking
96    pub fn verify(
97        &self,
98        leaf_certificate: &Vec<u8>,
99        intermediate_certificate: &Vec<u8>,
100        effective_date: Option<u64>,
101    ) -> Result<Vec<u8>, ChainVerifierError> {
102        if self.root_certificates.is_empty() {
103            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
104        }
105
106        let leaf_certificate = x509::parse_certificate(leaf_certificate.as_slice())
107            .map_err(|_| ChainVerifierError::VerificationFailure(InvalidCertificate))?;
108
109        // Check for Apple-specific leaf certificate extension (1.2.840.113635.100.6.11.1)
110        let leaf_oid = ObjectIdentifier::new("1.2.840.113635.100.6.11.1")
111            .map_err(|_| ChainVerifierError::VerificationFailure(InvalidCertificate))?;
112
113        if !x509::has_extension(&leaf_certificate, &leaf_oid) {
114            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
115        }
116
117        let intermediate_certificate = x509::parse_certificate(intermediate_certificate.as_slice())
118            .map_err(|_| ChainVerifierError::VerificationFailure(InvalidCertificate))?;
119
120        // Check for Apple-specific intermediate certificate extension (1.2.840.113635.100.6.2.1)
121        let intermediate_oid = ObjectIdentifier::new("1.2.840.113635.100.6.2.1")
122            .map_err(|_| ChainVerifierError::VerificationFailure(InvalidCertificate))?;
123
124        if !x509::has_extension(&intermediate_certificate, &intermediate_oid) {
125            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
126        }
127
128        let mut root_certificate: Option<Certificate> = None;
129
130        for cert in &self.root_certificates {
131            let cert = x509::parse_certificate(&cert)
132                .map_err(|_| ChainVerifierError::VerificationFailure(InvalidCertificate))?;
133
134            if x509::verify_signature(&intermediate_certificate, &cert).is_ok() {
135                root_certificate = Some(cert);
136                break;
137            }
138        }
139
140        let Some(root_certificate) = root_certificate else {
141            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
142        };
143
144        self.verify_chain(
145            &leaf_certificate,
146            &intermediate_certificate,
147            &root_certificate,
148            effective_date,
149        )
150    }
151
152    fn verify_chain(
153        &self,
154        leaf: &Certificate,
155        intermediate: &Certificate,
156        root_certificate: &Certificate,
157        effective_date: Option<u64>,
158    ) -> Result<Vec<u8>, ChainVerifierError> {
159        x509::verify_signature(leaf, intermediate)?;
160
161        if let Some(date) = effective_date {
162            let timestamp = i64::try_from(date)
163                .map_err(|_| ChainVerifierError::VerificationFailure(InvalidEffectiveDate))?;
164
165            if !x509::is_valid_at(leaf, timestamp) ||
166               !x509::is_valid_at(intermediate, timestamp) ||
167               !x509::is_valid_at(root_certificate, timestamp)
168            {
169                return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
170            }
171        }
172
173        let public_key_bytes = x509::public_key_bytes(leaf);
174
175        #[cfg(all(feature = "ocsp"))]
176        {
177            // Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
178            match self.check_ocsp_status(leaf, intermediate) {
179                Ok(()) => {
180                    // Certificate is valid according to OCSP
181                }
182                Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
183                    // Certificate is revoked - this should fail
184                    return Err(ChainVerifierError::VerificationFailure(
185                        ChainVerificationFailureReason::CertificateRevoked,
186                    ));
187                }
188                Err(e) => {
189                    // Other OCSP errors (network, parsing, etc.) - log but don't fail
190                    eprintln!("OCSP check failed (non-fatal): {:?}", e);
191                }
192            }
193        };
194
195        Ok(public_key_bytes)
196    }
197}