app_store_server_library/
chain_verifier.rs

1use crate::chain_verifier::ChainVerificationFailureReason::{
2    CertificateExpired, InvalidCertificate, 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
50/// A structure for verifying certificate chains.
51///
52/// This struct holds the root certificates and provides methods to verify certificate chains.
53pub struct ChainVerifier {
54    root_certificates: Vec<Vec<u8>>,
55}
56
57impl ChainVerifier {
58    /// Creates a new `ChainVerifier` with the provided root certificates.
59    ///
60    /// # Arguments
61    ///
62    /// * `root_certificates`: A vector of byte slices containing the root certificates.
63    ///
64    /// # Returns
65    ///
66    /// A new instance of `ChainVerifier`.
67    pub fn new(root_certificates: Vec<Vec<u8>>) -> Self {
68        ChainVerifier { root_certificates }
69    }
70
71    /// Verifies a certificate pair (leaf and intermediate).
72    ///
73    /// This method verifies a leaf certificate against an intermediate certificate. It performs various
74    /// checks to ensure the validity and integrity of the certificates.
75    ///
76    /// # Arguments
77    ///
78    /// * `leaf_certificate`: The leaf certificate as a byte slice.
79    /// * `intermediate_certificate`: The intermediate certificate as a byte slice.
80    /// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
81    ///
82    /// # Returns
83    ///
84    /// * `Ok(Vec<u8>)`: If the certificates are valid, it returns the public key data from the leaf certificate.
85    /// * `Err(ChainVerifierError)`: If the verification fails for any reason, it returns a `ChainVerifierError` enum.
86    /// TODO: Implement issuer checking
87    pub fn verify(
88        &self,
89        leaf_certificate: &Vec<u8>,
90        intermediate_certificate: &Vec<u8>,
91        effective_date: Option<u64>,
92    ) -> Result<Vec<u8>, ChainVerifierError> {
93        if self.root_certificates.is_empty() {
94            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
95        }
96        let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
97            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
98        };
99        let leaf_certificate = leaf_certificate.1;
100
101        let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))? else {
102            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
103        };
104
105        let Ok(intermediate_certificate) = X509Certificate::from_der(intermediate_certificate.as_slice()) else {
106            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
107        };
108        let intermediate_certificate = intermediate_certificate.1;
109
110        let Some(_) = intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))? else {
111            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
112        };
113
114        let mut root_certificate: Option<X509Certificate> = None;
115
116        for cert in &self.root_certificates {
117            let Ok(cert) = X509Certificate::from_der(&cert) else {
118                return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
119            };
120
121            match intermediate_certificate.verify_signature(Some(cert.1.public_key())) {
122                Ok(_) => (),
123                Err(_) => continue,
124            }
125
126            root_certificate = Some(cert.1)
127        }
128
129        let Some(root_certificate) = root_certificate else {
130            return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
131        };
132
133        self.verify_chain(
134            &leaf_certificate,
135            &intermediate_certificate,
136            &root_certificate,
137            effective_date,
138        )
139    }
140
141    fn verify_chain(
142        &self,
143        leaf: &X509Certificate,
144        intermediate: &X509Certificate,
145        root_certificate: &X509Certificate,
146        effective_date: Option<u64>,
147    ) -> Result<Vec<u8>, ChainVerifierError> {
148        leaf.verify_signature(Some(intermediate.public_key()))?;
149
150        if let Some(date) = effective_date {
151            let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
152                return Err(ChainVerifierError::VerificationFailure(
153                    InvalidEffectiveDate,
154                ));
155            };
156
157            if !(root_certificate.validity.is_valid_at(time) &&
158                leaf.validity.is_valid_at(time) &&
159                intermediate.validity.is_valid_at(time))
160            {
161                return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
162            }
163        }
164
165        let k = leaf.public_key().raw.to_vec();
166
167        // Make online verification as additional step if ocsp flag enabled
168        #[cfg(all(feature = "ocsp"))]
169        {
170            // Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
171            match self.check_ocsp_status(leaf, intermediate) {
172                Ok(()) => {
173                    // Certificate is valid according to OCSP
174                }
175                Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
176                    // Certificate is revoked - this should fail
177                    return Err(ChainVerifierError::VerificationFailure(
178                        ChainVerificationFailureReason::CertificateRevoked,
179                    ));
180                }
181                Err(e) => {
182                    // Other OCSP errors (network, parsing, etc.) - log but don't fail
183                    eprintln!("OCSP check failed (non-fatal): {:?}", e);
184                }
185            }
186        };
187
188        Ok(k)
189    }
190}