wire_e2e_identity/x509_check/
revocation.rs1#![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 pub intermediates: &'a [x509_cert::Certificate],
29 pub trust_roots: &'a [x509_cert::anchor::TrustAnchorChoice],
31 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 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 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 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 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 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}