1use std::fmt;
2
3use crate::crypto::SignatureVerifier;
4use crate::diagnostic::VerificationDiagnostic;
5use crate::store::CertificateStore;
6use crate::unverified_chain::UnverifiedCertificateChain;
7use crate::validated_chain::ValidatedCertificateChain;
8use crate::{Certificate, CertificateExt, PolicyFailureReason, ValidationPolicy};
9
10macro_rules! diagnose {
13 ($diagnostics:expr, $diagnostic:expr) => {
14 if let Some(callback) = $diagnostics.as_deref_mut() {
15 callback($diagnostic);
16 }
17 };
18}
19
20pub struct Validator<'a, P> {
28 root_certificates: CertificateStore<'a>,
30 crypto: &'a dyn SignatureVerifier,
31 policy: P,
33}
34
35impl<'a, P> Validator<'a, P>
36where
37 P: ValidationPolicy,
38{
39 pub fn with_policy_and_backend(
45 root_certificates: CertificateStore<'a>,
46 policy: P,
47 crypto: &'a dyn SignatureVerifier,
48 ) -> Self {
49 Self {
50 root_certificates,
51 crypto,
52 policy,
53 }
54 }
55
56 pub fn with_policy(root_certificates: CertificateStore<'a>, policy: P) -> Self {
69 Self::with_policy_and_backend(root_certificates, policy, crate::crypto::default_provider())
70 }
71
72 pub fn validate(
79 &self,
80 leaf: &Certificate<'a>,
81 intermediates: &CertificateStore<'a>,
82 ) -> ChainValidationResult<'a> {
83 self.validate_inner(leaf, intermediates, None)
84 }
85
86 pub fn validate_with_diagnostics(
95 &self,
96 leaf: &Certificate<'a>,
97 intermediates: &CertificateStore<'a>,
98 diagnostic_callback: &mut dyn FnMut(VerificationDiagnostic<'a>),
99 ) -> ChainValidationResult<'a> {
100 self.validate_inner(leaf, intermediates, Some(diagnostic_callback))
101 }
102
103 fn validate_inner(
104 &self,
105 leaf: &Certificate<'a>,
106 intermediates: &CertificateStore<'a>,
107 mut diagnostics: Option<&mut dyn FnMut(VerificationDiagnostic<'a>)>,
108 ) -> ChainValidationResult<'a> {
109 if has_unhandled_critical_extensions(leaf, &self.policy) {
112 diagnose!(
113 diagnostics,
114 VerificationDiagnostic::leaf_certificate_has_unhandled_critical_extension(
115 leaf.clone(),
116 self.policy
117 .verifying_critical_extensions(),
118 )
119 );
120 return Err(vec![PolicyFailure::new(
121 UnverifiedCertificateChain::new(vec![leaf.clone()]),
122 PolicyFailureReason::new("leaf certificate has unhandled critical extension"),
123 )]);
124 }
125
126 let mut policy_failures = Vec::new();
127
128 let leaf_key = leaf.subject_key();
135 if self
136 .root_certificates
137 .find_by_subject(&leaf_key)
138 .iter()
139 .any(|c| c == leaf)
140 {
141 let chain = UnverifiedCertificateChain::new(vec![leaf.clone()]);
142 match self
143 .policy
144 .chain_meets_policy_requirements(&chain)
145 {
146 Ok(()) => {
147 diagnose!(
149 diagnostics,
150 VerificationDiagnostic::found_valid_certificate_chain(vec![leaf.clone()])
151 );
152 return Ok(ValidatedCertificateChain::new_unchecked(vec![leaf.clone()]));
153 }
154 Err(reason) => {
155 diagnose!(
156 diagnostics,
157 VerificationDiagnostic::leaf_certificate_is_in_the_root_store_but_does_not_meet_policy(leaf.clone(), reason.clone())
158 );
159 policy_failures.push(PolicyFailure::new(chain, reason));
160 }
161 }
162 }
163
164 let mut stack: Vec<Vec<Certificate<'a>>> = vec![vec![leaf.clone()]];
165
166 while let Some(partial_chain) = stack.pop() {
168 diagnose!(
169 diagnostics,
170 VerificationDiagnostic::searching_for_issuer_of_partial_chain(
171 partial_chain.clone(),
172 )
173 );
174
175 let tip = partial_chain.last().unwrap();
176 let issuer_key = tip.issuer_key();
177
178 let mut root_candidates = self
181 .root_certificates
182 .find_by_subject(&issuer_key)
183 .to_vec();
184 sort_by_suitability_for_issuing(&mut root_candidates, tip);
186 if !root_candidates.is_empty() {
187 diagnose!(
188 diagnostics,
189 VerificationDiagnostic::found_candidate_issuers_of_partial_chain_in_root_store(
190 partial_chain.clone(),
191 root_candidates.clone(),
192 )
193 );
194 }
195 for candidate in &root_candidates {
197 if should_skip_adding_certificate(
198 candidate,
199 &partial_chain,
200 self.crypto,
201 &self.policy,
202 &mut diagnostics,
203 ) {
204 continue;
205 }
206 let mut chain_certs = partial_chain.clone();
207 chain_certs.push(candidate.clone());
208 let chain = UnverifiedCertificateChain::new(chain_certs.clone());
209 match self
210 .policy
211 .chain_meets_policy_requirements(&chain)
212 {
213 Ok(()) => {
214 diagnose!(
216 diagnostics,
217 VerificationDiagnostic::found_valid_certificate_chain(
218 chain_certs.clone(),
219 )
220 );
221 return Ok(ValidatedCertificateChain::new_unchecked(chain_certs));
222 }
223 Err(reason) => {
224 diagnose!(
225 diagnostics,
226 VerificationDiagnostic::chain_fails_to_meet_policy(
227 chain_certs,
228 reason.clone(),
229 )
230 );
231 policy_failures.push(PolicyFailure::new(chain, reason));
232 }
233 }
234 }
235
236 let mut intermediate_candidates = intermediates
237 .find_by_subject(&issuer_key)
238 .to_vec();
239 sort_by_suitability_for_issuing(&mut intermediate_candidates, tip);
241 if !intermediate_candidates.is_empty() {
242 diagnose!(
243 diagnostics,
244 VerificationDiagnostic::found_candidate_issuers_of_partial_chain_in_intermediate_store(
245 partial_chain.clone(),
246 intermediate_candidates.clone(),
247 )
248 );
249 }
250 for candidate in intermediate_candidates
254 .into_iter()
255 .rev()
256 {
257 if should_skip_adding_certificate(
258 &candidate,
259 &partial_chain,
260 self.crypto,
261 &self.policy,
262 &mut diagnostics,
263 ) {
264 continue;
265 }
266 let mut next = partial_chain.clone();
267 next.push(candidate);
268 stack.push(next);
269 }
270 }
271
272 diagnose!(
273 diagnostics,
274 VerificationDiagnostic::could_not_validate_leaf_certificate(leaf.clone())
275 );
276 Err(policy_failures)
277 }
278}
279
280fn has_unhandled_critical_extensions(
281 cert: &Certificate<'_>,
282 policy: &impl ValidationPolicy,
283) -> bool {
284 let handled = policy.verifying_critical_extensions();
285 cert.tbs_certificate
286 .iter_extensions()
287 .any(|ext| ext.critical && !handled.contains(&ext.oid))
288}
289
290fn sort_by_suitability_for_issuing<'a>(
291 candidates: &mut [Certificate<'a>],
292 subject: &Certificate<'a>,
293) {
294 let subject_aki = subject.authority_key_identifier();
297
298 let rank = |candidate: &Certificate<'a>| -> u8 {
301 match (subject_aki, candidate.subject_key_identifier()) {
302 (Some(aki), Some(ski)) if aki == ski => 0,
303 (_, None) => 1,
304 (None, Some(_)) => 1,
305 (Some(_), Some(_)) => 2,
306 }
307 };
308
309 candidates.sort_by_key(rank);
310}
311
312fn should_skip_adding_certificate<'a>(
313 candidate: &Certificate<'a>,
314 partial_chain: &[Certificate<'a>],
315 crypto: &dyn SignatureVerifier,
316 policy: &impl ValidationPolicy,
317 diagnostics: &mut Option<&mut dyn FnMut(VerificationDiagnostic<'a>)>,
318) -> bool {
319 if has_unhandled_critical_extensions(candidate, policy) {
321 diagnose!(
322 diagnostics,
323 VerificationDiagnostic::issuer_has_unhandled_critical_extension(
324 candidate.clone(),
325 partial_chain.to_vec(),
326 policy.verifying_critical_extensions(),
327 )
328 );
329 return true;
330 }
331
332 if partial_chain
335 .iter()
336 .any(|existing| existing.has_same_identity_as(candidate))
337 {
338 diagnose!(
339 diagnostics,
340 VerificationDiagnostic::issuer_is_already_in_the_chain(
341 partial_chain.to_vec(),
342 candidate.clone(),
343 )
344 );
345 return true;
346 }
347
348 let tip = partial_chain.last().unwrap();
350 let signature_verifies = crypto
351 .verify_signature(
352 &tip.signature_algorithm,
353 candidate.public_key(),
354 tip.tbs_certificate.as_ref(),
355 tip.signature_value.as_ref(),
356 )
357 .is_ok();
358
359 if !signature_verifies {
360 diagnose!(
361 diagnostics,
362 VerificationDiagnostic::issuer_has_not_signed_certificate(
363 candidate.clone(),
364 partial_chain.to_vec(),
365 )
366 );
367 }
368
369 !signature_verifies
370}
371
372pub type ChainValidationResult<'a> = Result<ValidatedCertificateChain<'a>, Vec<PolicyFailure<'a>>>;
377
378#[derive(Clone)]
380pub struct PolicyFailure<'a> {
381 pub chain: UnverifiedCertificateChain<'a>,
382 pub policy_failure_reason: PolicyFailureReason,
383}
384
385impl<'a> PolicyFailure<'a> {
386 pub fn new(
387 chain: UnverifiedCertificateChain<'a>,
388 policy_failure_reason: PolicyFailureReason,
389 ) -> Self {
390 Self {
391 chain,
392 policy_failure_reason,
393 }
394 }
395}
396
397impl fmt::Display for PolicyFailure<'_> {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 write!(f, "{}", self.policy_failure_reason)
400 }
401}
402
403impl fmt::Debug for PolicyFailure<'_> {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 write!(
406 f,
407 "{} (chain of {})",
408 self.policy_failure_reason,
409 self.chain.len()
410 )
411 }
412}