Skip to main content

wire_e2e_identity/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 super::{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}
34
35pub struct PkiEnvironment {
36    pe: certval::environment::PkiEnvironment,
37}
38
39impl std::ops::Deref for PkiEnvironment {
40    type Target = certval::environment::PkiEnvironment;
41
42    fn deref(&self) -> &Self::Target {
43        &self.pe
44    }
45}
46
47impl std::ops::DerefMut for PkiEnvironment {
48    fn deref_mut(&mut self) -> &mut Self::Target {
49        &mut self.pe
50    }
51}
52
53impl std::fmt::Debug for PkiEnvironment {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.debug_struct("PkiEnvironment").field("pe", &"[OPAQUE]").finish()
56    }
57}
58
59fn check_cpr(cpr: CertificationPathResults) -> RustyX509CheckResult<()> {
60    if let Some(validation_status) = get_validation_status(&cpr) {
61        match validation_status {
62            certval::PathValidationStatus::Valid => Ok(()),
63            // No CRL is available, this is fine
64            certval::PathValidationStatus::RevocationStatusNotDetermined
65            | certval::PathValidationStatus::RevocationStatusNotAvailable => Ok(()),
66            validation_status => Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
67                validation_status,
68            ))),
69        }
70    } else {
71        Err(RustyX509CheckError::CannotDetermineVerificationStatus)
72    }
73}
74
75fn now() -> RustyX509CheckResult<u64> {
76    Ok(web_time::SystemTime::now()
77        .duration_since(web_time::SystemTime::UNIX_EPOCH)
78        .map_err(|_| RustyX509CheckError::CannotDetermineCurrentTime)?
79        .as_secs())
80}
81
82impl PkiEnvironment {
83    pub fn decode_pem_cert(pem: String) -> RustyX509CheckResult<x509_cert::Certificate> {
84        Ok(x509_cert::Certificate::from_pem(pem)?)
85    }
86
87    pub fn decode_der_crl(crl_der: Vec<u8>) -> RustyX509CheckResult<x509_cert::crl::CertificateList> {
88        Ok(x509_cert::crl::CertificateList::from_der(&crl_der)?)
89    }
90
91    pub fn extract_ski_aki_from_cert(cert: &x509_cert::Certificate) -> RustyX509CheckResult<(String, Option<String>)> {
92        let cert = PDVCertificate::try_from(cert.clone())?;
93
94        let ski = cert
95            .get_extension(&const_oid::db::rfc5912::ID_CE_SUBJECT_KEY_IDENTIFIER)?
96            .ok_or(RustyX509CheckError::MissingSki)?;
97        let ski = match ski {
98            certval::PDVExtension::SubjectKeyIdentifier(ski) => hex::encode(ski.0.as_bytes()),
99            _ => return Err(RustyX509CheckError::ImplementationError),
100        };
101
102        let aki = cert
103            .get_extension(&const_oid::db::rfc5912::ID_CE_AUTHORITY_KEY_IDENTIFIER)?
104            .and_then(|ext| match ext {
105                certval::PDVExtension::AuthorityKeyIdentifier(AuthorityKeyIdentifier { key_identifier, .. }) => {
106                    key_identifier.as_ref()
107                }
108                _ => None,
109            })
110            .map(|ki| hex::encode(ki.as_bytes()));
111
112        Ok((ski, aki))
113    }
114
115    pub fn encode_cert_to_der(cert: &x509_cert::Certificate) -> RustyX509CheckResult<Vec<u8>> {
116        Ok(cert.to_der()?)
117    }
118
119    pub fn encode_crl_to_der(crl: &x509_cert::crl::CertificateList) -> RustyX509CheckResult<Vec<u8>> {
120        Ok(crl.to_der()?)
121    }
122
123    /// Initializes a certval PkiEnvironment using the provided params
124    pub fn init(params: PkiEnvironmentParams) -> RustyX509CheckResult<PkiEnvironment> {
125        let toi = now()?;
126
127        let mut cps = CertificationPathSettings::new();
128        set_time_of_interest(&mut cps, toi);
129
130        // Make a Certificate source for intermediate CA certs
131        let mut cert_source = CertSource::new();
132        for (i, cert) in params.intermediates.iter().enumerate() {
133            cert_source.push(certval::CertFile {
134                filename: format!("Intermediate CA #{i} [{}]", cert.tbs_certificate.subject),
135                bytes: cert.to_der()?,
136            });
137        }
138
139        cert_source.initialize(&cps)?;
140
141        // Make a TrustAnchor source
142        let mut trust_anchors = TaSource::new();
143        for (i, root) in params.trust_roots.iter().enumerate() {
144            trust_anchors.push(certval::CertFile {
145                filename: format!("TrustAnchor #{i}"),
146                bytes: root.to_der()?,
147            });
148        }
149
150        trust_anchors.initialize()?;
151
152        let revocation_cache = RevocationCache::default();
153
154        // Make a CRL source
155        let crl_source = CrlStore::from(params.crls);
156        crl_source.index_crls(toi)?;
157
158        let mut pe = certval::environment::PkiEnvironment::default();
159        populate_5280_pki_environment(&mut pe);
160        pe.add_trust_anchor_source(Box::new(trust_anchors));
161        pe.add_crl_source(Box::new(crl_source));
162        pe.add_revocation_cache(Box::new(revocation_cache));
163
164        cert_source.find_all_partial_paths(&pe, &cps);
165
166        pe.add_certificate_source(Box::new(cert_source));
167
168        Ok(Self { pe })
169    }
170
171    pub fn validate_trust_anchor_cert(&self, cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
172        let mut cps = CertificationPathSettings::default();
173        set_time_of_interest(&mut cps, now()?);
174
175        let mut cert = PDVCertificate::try_from(cert.clone())?;
176        cert.parse_extensions(EXTS_OF_INTEREST);
177
178        let ta = PDVTrustAnchorChoice::try_from(x509_cert::anchor::TrustAnchorChoice::Certificate(
179            cert.decoded_cert.clone(),
180        ))?;
181        let mut certification_path = CertificationPath::new(ta, vec![], cert);
182
183        check_validity(
184            &self.pe,
185            &cps,
186            &mut certification_path,
187            &mut CertificationPathResults::new(),
188        )?;
189
190        verify_signatures(
191            &self.pe,
192            &cps,
193            &mut certification_path,
194            &mut CertificationPathResults::new(),
195        )?;
196
197        Ok(())
198    }
199
200    pub fn validate_crl_with_raw(&self, crl_raw: &[u8]) -> RustyX509CheckResult<x509_cert::crl::CertificateList> {
201        let crl = x509_cert::crl::CertificateList::from_der(crl_raw)?;
202
203        let mut spki_list = vec![];
204        if let Some(aki) = crl.tbs_cert_list.crl_extensions.as_ref().and_then(|extensions| {
205            extensions
206                .iter()
207                .find(|ext| ext.extn_id == x509_cert::ext::pkix::AuthorityKeyIdentifier::OID)
208        }) {
209            let akid = aki.extn_value.as_bytes();
210            if let Ok(ta) = self.pe.get_trust_anchor(akid) {
211                spki_list
212                    .push(certval::source::ta_source::get_subject_public_key_info_from_trust_anchor(&ta.decoded_ta));
213            } else if let Ok(intermediates) = self.pe.get_intermediates_by_skid(akid) {
214                spki_list.extend(
215                    intermediates
216                        .into_iter()
217                        .map(|c| &c.decoded_cert.tbs_certificate.subject_public_key_info),
218                );
219            }
220        }
221
222        if let Ok(ta) = self.pe.get_trust_anchor_by_name(&crl.tbs_cert_list.issuer) {
223            let spki = certval::source::ta_source::get_subject_public_key_info_from_trust_anchor(&ta.decoded_ta);
224            if !spki_list.contains(&spki) {
225                spki_list.push(spki);
226            }
227        }
228
229        spki_list.extend(
230            self.pe
231                .get_cert_by_name(&crl.tbs_cert_list.issuer)
232                .into_iter()
233                .map(|c| &c.decoded_cert.tbs_certificate.subject_public_key_info),
234        );
235
236        spki_list.dedup();
237
238        let crl_defer = DeferDecodeSigned::from_der(crl_raw)?;
239
240        let any_spki_verifies = spki_list.into_iter().any(|spki| {
241            self.pe
242                .verify_signature_message(
243                    &self.pe,
244                    &crl_defer.tbs_field,
245                    crl.signature.raw_bytes(),
246                    &crl.signature_algorithm,
247                    spki,
248                )
249                .is_ok()
250        });
251
252        if any_spki_verifies {
253            Ok(crl)
254        } else {
255            Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
256                certval::PathValidationStatus::SignatureVerificationFailure,
257            )))
258        }
259    }
260
261    fn validate_cert_internal(
262        &self,
263        end_identity_cert: &x509_cert::Certificate,
264        perform_revocation_check: bool,
265    ) -> RustyX509CheckResult<()> {
266        let toi = now()?;
267
268        let mut cps = CertificationPathSettings::default();
269        set_time_of_interest(&mut cps, toi);
270        set_require_ta_store(&mut cps, true);
271        set_forbid_self_signed_ee(&mut cps, true);
272
273        let mut end_identity_cert = PDVCertificate::try_from(end_identity_cert.clone())?;
274        end_identity_cert.parse_extensions(EXTS_OF_INTEREST);
275
276        let mut paths = vec![];
277        self.pe
278            .get_paths_for_target(&self.pe, &end_identity_cert, &mut paths, 0, toi)?;
279
280        if paths.is_empty() {
281            return Err(RustyX509CheckError::CertValError(certval::Error::PathValidation(
282                certval::PathValidationStatus::NoPathsFound,
283            )));
284        }
285
286        let mut result = Ok(());
287
288        let any_path_validates = paths.into_iter().any(|mut path| {
289            let mut cpr = CertificationPathResults::new();
290            let _ = validate_path_rfc5280(&self.pe, &cps, &mut path, &mut cpr);
291            let r = check_cpr(cpr);
292            if r.is_err() {
293                result = r;
294                return false;
295            }
296
297            if perform_revocation_check {
298                set_check_crls(&mut cps, true);
299                let mut cpr = CertificationPathResults::new();
300                let _ = check_revocation(&self.pe, &cps, &mut path, &mut cpr);
301                let r = check_cpr(cpr);
302                if r.is_err() {
303                    result = r;
304                    return false;
305                }
306            }
307
308            true
309        });
310
311        if any_path_validates { Ok(()) } else { result }
312    }
313
314    #[inline]
315    pub fn validate_cert(&self, end_identity_cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
316        self.validate_cert_internal(end_identity_cert, false)
317    }
318
319    #[inline]
320    pub fn validate_cert_and_revocation(&self, end_identity_cert: &x509_cert::Certificate) -> RustyX509CheckResult<()> {
321        self.validate_cert_internal(end_identity_cert, true)
322    }
323}