wire_e2e_identity/acme/x509_check/
revocation.rs

1#![allow(dead_code)]
2
3use certval::{
4    CertSource, CertVector, CertificationPath, CertificationPathResults, CertificationPathSettings, DeferDecodeSigned,
5    EXTS_OF_INTEREST, ExtensionProcessing, PDVTrustAnchorChoice, TaSource, check_revocation, get_validation_status,
6    populate_5280_pki_environment, set_check_crls, set_forbid_self_signed_ee, set_require_ta_store,
7    set_time_of_interest, validate_path_rfc5280,
8    validator::{PDVCertificate, path_validator::check_validity},
9    verify_signatures,
10};
11use const_oid::AssociatedOid;
12use crl_store::CrlStore;
13use x509_cert::{
14    der::{Decode, DecodePem, Encode},
15    ext::pkix::AuthorityKeyIdentifier,
16};
17
18use crate::acme::x509_check::{RustyX509CheckError, RustyX509CheckResult, revocation::cache::RevocationCache};
19
20mod cache;
21mod crl_info;
22mod crl_store;
23mod misc;
24
25#[derive(Default)]
26pub struct PkiEnvironmentParams<'a> {
27    /// Intermediate CAs and cross-signed CAs
28    pub intermediates: &'a [x509_cert::Certificate],
29    /// Trust Anchor roots
30    pub trust_roots: &'a [x509_cert::anchor::TrustAnchorChoice],
31    /// CRLs to add to the revocation check
32    pub crls: &'a [x509_cert::crl::CertificateList],
33    /// Time of interest for CRL verfication. If not provided, will default to current UNIX epoch
34    pub time_of_interest: Option<u64>,
35}
36
37pub struct PkiEnvironment {
38    pe: certval::environment::PkiEnvironment,
39    toi: u64,
40}
41
42impl std::ops::Deref for PkiEnvironment {
43    type Target = certval::environment::PkiEnvironment;
44
45    fn deref(&self) -> &Self::Target {
46        &self.pe
47    }
48}
49
50impl std::ops::DerefMut for PkiEnvironment {
51    fn deref_mut(&mut self) -> &mut Self::Target {
52        &mut self.pe
53    }
54}
55
56impl std::fmt::Debug for PkiEnvironment {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        f.debug_struct("PkiEnvironment")
59            .field("pe", &"[OPAQUE]")
60            .field("toi", &self.toi)
61            .finish()
62    }
63}
64
65fn check_cpr(cpr: CertificationPathResults) -> RustyX509CheckResult<()> {
66    if let Some(validation_status) = get_validation_status(&cpr) {
67        match validation_status {
68            certval::PathValidationStatus::Valid => Ok(()),
69            // No CRL is available, this is fine
70            certval::PathValidationStatus::RevocationStatusNotDetermined
71            | certval::PathValidationStatus::RevocationStatusNotAvailable => Ok(()),
72            validation_status => Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
73                validation_status,
74            ))),
75        }
76    } else {
77        Err(RustyX509CheckError::CannotDetermineVerificationStatus)
78    }
79}
80
81impl PkiEnvironment {
82    pub fn decode_pem_cert(pem: String) -> RustyX509CheckResult<x509_cert::Certificate> {
83        Ok(x509_cert::Certificate::from_pem(pem)?)
84    }
85
86    pub fn decode_der_crl(crl_der: Vec<u8>) -> RustyX509CheckResult<x509_cert::crl::CertificateList> {
87        Ok(x509_cert::crl::CertificateList::from_der(&crl_der)?)
88    }
89
90    pub fn extract_ski_aki_from_cert(cert: &x509_cert::Certificate) -> RustyX509CheckResult<(String, Option<String>)> {
91        let cert = PDVCertificate::try_from(cert.clone())?;
92
93        let ski = cert
94            .get_extension(&const_oid::db::rfc5912::ID_CE_SUBJECT_KEY_IDENTIFIER)?
95            .ok_or(RustyX509CheckError::MissingSki)?;
96        let ski = match ski {
97            certval::PDVExtension::SubjectKeyIdentifier(ski) => hex::encode(ski.0.as_bytes()),
98            _ => return Err(RustyX509CheckError::ImplementationError),
99        };
100
101        let aki = cert
102            .get_extension(&const_oid::db::rfc5912::ID_CE_AUTHORITY_KEY_IDENTIFIER)?
103            .and_then(|ext| match ext {
104                certval::PDVExtension::AuthorityKeyIdentifier(AuthorityKeyIdentifier { key_identifier, .. }) => {
105                    key_identifier.as_ref()
106                }
107                _ => None,
108            })
109            .map(|ki| hex::encode(ki.as_bytes()));
110
111        Ok((ski, aki))
112    }
113
114    pub fn encode_cert_to_der(cert: &x509_cert::Certificate) -> RustyX509CheckResult<Vec<u8>> {
115        Ok(cert.to_der()?)
116    }
117
118    pub fn encode_crl_to_der(crl: &x509_cert::crl::CertificateList) -> RustyX509CheckResult<Vec<u8>> {
119        Ok(crl.to_der()?)
120    }
121
122    /// Initializes a certval PkiEnvironment using the provided params
123    pub fn init(params: PkiEnvironmentParams) -> RustyX509CheckResult<PkiEnvironment> {
124        let toi = if let Some(toi) = params.time_of_interest {
125            toi
126        } else {
127            web_time::SystemTime::now()
128                .duration_since(web_time::SystemTime::UNIX_EPOCH)
129                .map_err(|_| RustyX509CheckError::CannotDetermineCurrentTime)?
130                .as_secs()
131        };
132
133        let mut cps = CertificationPathSettings::new();
134        set_time_of_interest(&mut cps, toi);
135
136        // Make a Certificate source for intermediate CA certs
137        let mut cert_source = CertSource::new();
138        for (i, cert) in params.intermediates.iter().enumerate() {
139            cert_source.push(certval::CertFile {
140                filename: format!("Intermediate CA #{i} [{}]", cert.tbs_certificate.subject),
141                bytes: cert.to_der()?,
142            });
143        }
144
145        cert_source.initialize(&cps)?;
146
147        // Make a TrustAnchor source
148        let mut trust_anchors = TaSource::new();
149        for (i, root) in params.trust_roots.iter().enumerate() {
150            trust_anchors.push(certval::CertFile {
151                filename: format!("TrustAnchor #{i}"),
152                bytes: root.to_der()?,
153            });
154        }
155
156        trust_anchors.initialize()?;
157
158        let revocation_cache = RevocationCache::default();
159
160        // Make a CRL source
161        let crl_source = CrlStore::from(params.crls);
162        crl_source.index_crls(toi)?;
163
164        let mut pe = certval::environment::PkiEnvironment::default();
165        populate_5280_pki_environment(&mut pe);
166        pe.add_trust_anchor_source(Box::new(trust_anchors));
167        pe.add_crl_source(Box::new(crl_source));
168        pe.add_revocation_cache(Box::new(revocation_cache));
169
170        cert_source.find_all_partial_paths(&pe, &cps);
171
172        pe.add_certificate_source(Box::new(cert_source));
173
174        Ok(Self { pe, toi })
175    }
176
177    /// Overrides TIME_OF_INTEREST for certificate verifications based on a moment in the past or future
178    pub fn set_time_of_interest(&mut self, toi: u64) {
179        self.toi = toi;
180    }
181
182    /// Updates the TIME_OF_INTEREST for certificate checks to be `now`
183    pub fn refresh_time_of_interest(&mut self) -> RustyX509CheckResult<()> {
184        self.set_time_of_interest(
185            web_time::SystemTime::now()
186                .duration_since(web_time::SystemTime::UNIX_EPOCH)
187                .map_err(|_| RustyX509CheckError::CannotDetermineCurrentTime)?
188                .as_secs(),
189        );
190
191        Ok(())
192    }
193
194    pub fn validate_trust_anchor_cert(&self, cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
195        let mut cps = CertificationPathSettings::default();
196        set_time_of_interest(&mut cps, self.toi);
197
198        let mut cert = PDVCertificate::try_from(cert.clone())?;
199        cert.parse_extensions(EXTS_OF_INTEREST);
200
201        let ta = PDVTrustAnchorChoice::try_from(x509_cert::anchor::TrustAnchorChoice::Certificate(
202            cert.decoded_cert.clone(),
203        ))?;
204        let mut certification_path = CertificationPath::new(ta, vec![], cert);
205
206        check_validity(
207            &self.pe,
208            &cps,
209            &mut certification_path,
210            &mut CertificationPathResults::new(),
211        )?;
212
213        verify_signatures(
214            &self.pe,
215            &cps,
216            &mut certification_path,
217            &mut CertificationPathResults::new(),
218        )?;
219
220        Ok(())
221    }
222
223    #[inline]
224    #[deprecated = "This method is not to be used as it causes spurious verification failures because of re-encoding the DER repr of the CRL. Use `validate_crl_with_raw`"]
225    pub fn validate_crl(&self, crl: &x509_cert::crl::CertificateList) -> RustyX509CheckResult<()> {
226        let _ = self.validate_crl_with_raw(&crl.to_der()?)?;
227        Ok(())
228    }
229
230    pub fn validate_crl_with_raw(&self, crl_raw: &[u8]) -> RustyX509CheckResult<x509_cert::crl::CertificateList> {
231        let crl = x509_cert::crl::CertificateList::from_der(crl_raw)?;
232
233        let mut spki_list = vec![];
234        if let Some(aki) = crl.tbs_cert_list.crl_extensions.as_ref().and_then(|extensions| {
235            extensions
236                .iter()
237                .find(|ext| ext.extn_id == x509_cert::ext::pkix::AuthorityKeyIdentifier::OID)
238        }) {
239            let akid = aki.extn_value.as_bytes();
240            if let Ok(ta) = self.pe.get_trust_anchor(akid) {
241                spki_list
242                    .push(certval::source::ta_source::get_subject_public_key_info_from_trust_anchor(&ta.decoded_ta));
243            } else if let Ok(intermediates) = self.pe.get_intermediates_by_skid(akid) {
244                spki_list.extend(
245                    intermediates
246                        .into_iter()
247                        .map(|c| &c.decoded_cert.tbs_certificate.subject_public_key_info),
248                );
249            }
250        }
251
252        if let Ok(ta) = self.pe.get_trust_anchor_by_name(&crl.tbs_cert_list.issuer) {
253            let spki = certval::source::ta_source::get_subject_public_key_info_from_trust_anchor(&ta.decoded_ta);
254            if !spki_list.contains(&spki) {
255                spki_list.push(spki);
256            }
257        }
258
259        spki_list.extend(
260            self.pe
261                .get_cert_by_name(&crl.tbs_cert_list.issuer)
262                .into_iter()
263                .map(|c| &c.decoded_cert.tbs_certificate.subject_public_key_info),
264        );
265
266        spki_list.dedup();
267
268        let crl_defer = DeferDecodeSigned::from_der(crl_raw)?;
269
270        let any_spki_verifies = spki_list.into_iter().any(|spki| {
271            self.pe
272                .verify_signature_message(
273                    &self.pe,
274                    &crl_defer.tbs_field,
275                    crl.signature.raw_bytes(),
276                    &crl.signature_algorithm,
277                    spki,
278                )
279                .is_ok()
280        });
281
282        if any_spki_verifies {
283            Ok(crl)
284        } else {
285            Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
286                certval::PathValidationStatus::SignatureVerificationFailure,
287            )))
288        }
289    }
290
291    fn validate_cert_internal(
292        &self,
293        end_identity_cert: &x509_cert::Certificate,
294        perform_revocation_check: bool,
295    ) -> RustyX509CheckResult<()> {
296        let mut cps = CertificationPathSettings::default();
297        set_time_of_interest(&mut cps, self.toi);
298        set_require_ta_store(&mut cps, true);
299        set_forbid_self_signed_ee(&mut cps, true);
300
301        let mut end_identity_cert = PDVCertificate::try_from(end_identity_cert.clone())?;
302        end_identity_cert.parse_extensions(EXTS_OF_INTEREST);
303
304        let mut paths = vec![];
305        self.pe
306            .get_paths_for_target(&self.pe, &end_identity_cert, &mut paths, 0, self.toi)?;
307
308        if paths.is_empty() {
309            return Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
310                certval::PathValidationStatus::NoPathsFound,
311            )));
312        }
313
314        let mut result = Ok(());
315
316        let any_path_validates = paths.into_iter().any(|mut path| {
317            let mut cpr = CertificationPathResults::new();
318            let _ = validate_path_rfc5280(&self.pe, &cps, &mut path, &mut cpr);
319            let r = check_cpr(cpr);
320            if r.is_err() {
321                result = r;
322                return false;
323            }
324
325            if perform_revocation_check {
326                set_check_crls(&mut cps, true);
327                let mut cpr = CertificationPathResults::new();
328                let _ = check_revocation(&self.pe, &cps, &mut path, &mut cpr);
329                let r = check_cpr(cpr);
330                if r.is_err() {
331                    result = r;
332                    return false;
333                }
334            }
335
336            true
337        });
338
339        if any_path_validates { Ok(()) } else { result }
340    }
341
342    #[inline]
343    pub fn validate_cert(&self, end_identity_cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
344        self.validate_cert_internal(end_identity_cert, false)
345    }
346
347    #[inline]
348    pub fn validate_cert_and_revocation(&self, end_identity_cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
349        self.validate_cert_internal(end_identity_cert, true)
350    }
351}