Botan  2.4.0
Crypto and TLS for C++11
x509path.cpp
Go to the documentation of this file.
1 /*
2 * X.509 Certificate Path Validation
3 * (C) 2010,2011,2012,2014,2016 Jack Lloyd
4 * (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8 
9 #include <botan/x509path.h>
10 #include <botan/x509_ext.h>
11 #include <botan/pk_keys.h>
12 #include <botan/ocsp.h>
13 #include <botan/oids.h>
14 #include <algorithm>
15 #include <chrono>
16 #include <vector>
17 #include <set>
18 #include <string>
19 #include <sstream>
20 
21 #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
22  #include <future>
23  #include <botan/http_util.h>
24 #endif
25 
26 namespace Botan {
27 
28 /*
29 * PKIX path validation
30 */
32 PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
33  std::chrono::system_clock::time_point ref_time,
34  const std::string& hostname,
35  Usage_Type usage,
36  size_t min_signature_algo_strength,
37  const std::set<std::string>& trusted_hashes)
38  {
39  if(cert_path.empty())
40  throw Invalid_Argument("PKIX::check_chain cert_path empty");
41 
42  const bool self_signed_ee_cert = (cert_path.size() == 1);
43 
44  X509_Time validation_time(ref_time);
45 
46  CertificatePathStatusCodes cert_status(cert_path.size());
47 
48  if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
49  cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
50 
51  if(!cert_path[0]->allowed_usage(usage))
52  cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
53 
54  if(cert_path[0]->is_CA_cert() == false &&
55  cert_path[0]->has_constraints(KEY_CERT_SIGN))
56  {
57  /*
58  "If the keyCertSign bit is asserted, then the cA bit in the
59  basic constraints extension (Section 4.2.1.9) MUST also be
60  asserted." - RFC 5280
61 
62  We don't bother doing this check on the rest of the path since they
63  must have the cA bit asserted or the validation will fail anyway.
64  */
65  cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
66  }
67 
68  for(size_t i = 0; i != cert_path.size(); ++i)
69  {
70  std::set<Certificate_Status_Code>& status = cert_status.at(i);
71 
72  const bool at_self_signed_root = (i == cert_path.size() - 1);
73 
74  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
75 
76  const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
77 
78  if(at_self_signed_root && (issuer->is_self_signed() == false))
79  {
81  }
82 
83  if(subject->issuer_dn() != issuer->subject_dn())
84  {
86  }
87 
88  // Check the serial number
89  if(subject->is_serial_negative())
90  {
92  }
93 
94  // Check the subject's DN components' length
95  for(const auto& dn_pair : subject->subject_dn().get_attributes())
96  {
97  const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
98  // dn_pair = <OID,str>
99  if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
100  {
102  }
103  }
104 
105  // Check all certs for valid time range
106  if(validation_time < subject->not_before())
108 
109  if(validation_time > subject->not_after())
111 
112  // Check issuer constraints
113  if(!issuer->is_CA_cert() && !self_signed_ee_cert)
115 
116  std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
117 
118  // Check the signature algorithm
119  if(OIDS::lookup(subject->signature_algorithm().oid).empty())
120  {
122  }
123  // only perform the following checks if the signature algorithm is known
124  else
125  {
126  if(!issuer_key)
127  {
129  }
130  else
131  {
132  const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
133 
134  if(sig_status != Certificate_Status_Code::VERIFIED)
135  status.insert(sig_status);
136 
137  if(issuer_key->estimated_strength() < min_signature_algo_strength)
139  }
140 
141  // Ignore untrusted hashes on self-signed roots
142  if(trusted_hashes.size() > 0 && !at_self_signed_root)
143  {
144  if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
146  }
147  }
148 
149  // Check cert extensions
150  Extensions extensions = subject->v3_extensions();
151  const auto& extensions_vec = extensions.extensions();
152  if(subject->x509_version() < 3 && !extensions_vec.empty())
153  {
155  }
156  for(auto& extension : extensions_vec)
157  {
158  extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
159  }
160  if(extensions.extensions().size() != extensions.get_extension_oids().size())
161  {
163  }
164  }
165 
166  // path len check
167  size_t max_path_length = cert_path.size();
168  for(size_t i = cert_path.size() - 1; i > 0 ; --i)
169  {
170  std::set<Certificate_Status_Code>& status = cert_status.at(i);
171  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
172 
173  /*
174  * If the certificate was not self-issued, verify that max_path_length is
175  * greater than zero and decrement max_path_length by 1.
176  */
177  if(subject->subject_dn() != subject->issuer_dn())
178  {
179  if(max_path_length > 0)
180  {
181  --max_path_length;
182  }
183  else
184  {
186  }
187  }
188 
189  /*
190  * If pathLenConstraint is present in the certificate and is less than max_path_length,
191  * set max_path_length to the value of pathLenConstraint.
192  */
193  if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
194  {
195  max_path_length = subject->path_limit();
196  }
197  }
198 
199  return cert_status;
200  }
201 
203 PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
204  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
205  const std::vector<Certificate_Store*>& trusted_certstores,
206  std::chrono::system_clock::time_point ref_time)
207  {
208  if(cert_path.empty())
209  throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
210 
211  CertificatePathStatusCodes cert_status(cert_path.size() - 1);
212 
213  for(size_t i = 0; i != cert_path.size() - 1; ++i)
214  {
215  std::set<Certificate_Status_Code>& status = cert_status.at(i);
216 
217  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
218  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
219 
220  if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr))
221  {
222  try
223  {
224  Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
225 
226  if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
227  {
228  // Signature ok, so check the claimed status
229  Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time);
230  status.insert(ocsp_status);
231  }
232  else
233  {
234  // Some signature problem
235  status.insert(ocsp_signature_status);
236  }
237  }
238  catch(Exception&)
239  {
241  }
242  }
243  }
244 
245  while(cert_status.size() > 0 && cert_status.back().empty())
246  cert_status.pop_back();
247 
248  return cert_status;
249  }
250 
252 PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
253  const std::vector<std::shared_ptr<const X509_CRL>>& crls,
254  std::chrono::system_clock::time_point ref_time)
255  {
256  if(cert_path.empty())
257  throw Invalid_Argument("PKIX::check_crl cert_path empty");
258 
259  CertificatePathStatusCodes cert_status(cert_path.size());
260  const X509_Time validation_time(ref_time);
261 
262  for(size_t i = 0; i != cert_path.size() - 1; ++i)
263  {
264  std::set<Certificate_Status_Code>& status = cert_status.at(i);
265 
266  if(i < crls.size() && crls.at(i))
267  {
268  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
269  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
270 
271  if(!ca->allowed_usage(CRL_SIGN))
273 
274  if(validation_time < crls[i]->this_update())
276 
277  if(validation_time > crls[i]->next_update())
279 
280  if(crls[i]->check_signature(ca->subject_public_key()) == false)
282 
284 
285  if(crls[i]->is_revoked(*subject))
287 
288  std::string dp = subject->crl_distribution_point();
289  if(!dp.empty())
290  {
291  if(dp != crls[i]->crl_issuing_distribution_point())
292  {
294  }
295  }
296 
297  for(const auto& extension : crls[i]->extensions().extensions())
298  {
299  // is the extension critical and unknown?
300  if(extension.second && OIDS::lookup(extension.first->oid_of()) == "")
301  {
302  /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
303  * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
304  */
306  }
307  }
308 
309  }
310  }
311 
312  while(cert_status.size() > 0 && cert_status.back().empty())
313  cert_status.pop_back();
314 
315  return cert_status;
316  }
317 
319 PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
320  const std::vector<Certificate_Store*>& certstores,
321  std::chrono::system_clock::time_point ref_time)
322  {
323  if(cert_path.empty())
324  throw Invalid_Argument("PKIX::check_crl cert_path empty");
325 
326  if(certstores.empty())
327  throw Invalid_Argument("PKIX::check_crl certstores empty");
328 
329  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
330 
331  for(size_t i = 0; i != cert_path.size(); ++i)
332  {
333  BOTAN_ASSERT_NONNULL(cert_path[i]);
334  for(size_t c = 0; c != certstores.size(); ++c)
335  {
336  crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
337  if(crls[i])
338  break;
339  }
340  }
341 
342  return PKIX::check_crl(cert_path, crls, ref_time);
343  }
344 
345 #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
346 
348 PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
349  const std::vector<Certificate_Store*>& trusted_certstores,
350  std::chrono::system_clock::time_point ref_time,
351  std::chrono::milliseconds timeout,
352  bool ocsp_check_intermediate_CAs)
353  {
354  if(cert_path.empty())
355  throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
356 
357  std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
358 
359  size_t to_ocsp = 1;
360 
361  if(ocsp_check_intermediate_CAs)
362  to_ocsp = cert_path.size() - 1;
363  if(cert_path.size() == 1)
364  to_ocsp = 0;
365 
366  for(size_t i = 0; i < to_ocsp; ++i)
367  {
368  const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
369  const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
370 
371  if(subject->ocsp_responder() == "")
372  {
373  ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
374  throw Exception("No OCSP responder URL set for this certificate");
375  }));
376  }
377  else
378  {
379  ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
380  OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
381 
382  auto http = HTTP::POST_sync(subject->ocsp_responder(),
383  "application/ocsp-request",
384  req.BER_encode(),
385  /*redirects*/1,
386  timeout);
387 
388  http.throw_unless_ok();
389  // Check the MIME type?
390 
391  return std::make_shared<const OCSP::Response>(http.body());
392  }));
393  }
394  }
395 
396  std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
397 
398  for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
399  {
400  ocsp_responses.push_back(ocsp_response_futures[i].get());
401  }
402 
403  return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time);
404  }
405 
407 PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
408  const std::vector<Certificate_Store*>& certstores,
409  Certificate_Store_In_Memory* crl_store,
410  std::chrono::system_clock::time_point ref_time,
411  std::chrono::milliseconds timeout)
412  {
413  if(cert_path.empty())
414  throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
415  if(certstores.empty())
416  throw Invalid_Argument("PKIX::check_crl_online certstores empty");
417 
418  std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
419  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
420 
421  for(size_t i = 0; i != cert_path.size(); ++i)
422  {
423  for(size_t c = 0; c != certstores.size(); ++i)
424  {
425  crls[i] = certstores[i]->find_crl_for(*cert_path[i]);
426  if(crls[i])
427  break;
428  }
429 
430  // TODO: check if CRL is expired and re-request?
431 
432  // Only request if we don't already have a CRL
433  if(crls[i])
434  {
435  /*
436  We already have a CRL, so just insert this empty one to hold a place in the vector
437  so that indexes match up
438  */
439  future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
440  }
441  else if(cert_path[i]->crl_distribution_point() == "")
442  {
443  // Avoid creating a thread for this case
444  future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
445  throw Exception("No CRL distribution point for this certificate");
446  }));
447  }
448  else
449  {
450  future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
451  auto http = HTTP::GET_sync(cert_path[i]->crl_distribution_point());
452  http.throw_unless_ok();
453  // check the mime type?
454  return std::make_shared<const X509_CRL>(http.body());
455  }));
456  }
457  }
458 
459  for(size_t i = 0; i != future_crls.size(); ++i)
460  {
461  if(future_crls[i].valid())
462  {
463  try
464  {
465  std::future_status status = future_crls[i].wait_for(timeout);
466 
467  if(status == std::future_status::ready)
468  {
469  crls[i] = future_crls[i].get();
470  }
471  }
472  catch(std::exception&)
473  {
474  // crls[i] left null
475  }
476  }
477  }
478 
479  const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
480 
481  if(crl_store)
482  {
483  for(size_t i = 0; i != crl_status.size(); ++i)
484  {
485  if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
486  {
487  // better be non-null, we supposedly validated it
488  BOTAN_ASSERT_NONNULL(crls[i]);
489  crl_store->add_crl(crls[i]);
490  }
491  }
492  }
493 
494  return crl_status;
495  }
496 
497 #endif
498 
500 PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
501  const std::vector<Certificate_Store*>& trusted_certstores,
502  const std::shared_ptr<const X509_Certificate>& end_entity,
503  const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
504  {
505  if(end_entity->is_self_signed())
506  {
508  }
509 
510  /*
511  * This is an inelegant but functional way of preventing path loops
512  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
513  * fingerprints in the path. If there is a duplicate, we error out.
514  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
515  */
516  std::set<std::string> certs_seen;
517 
518  cert_path.push_back(end_entity);
519  certs_seen.insert(end_entity->fingerprint("SHA-256"));
520 
521  Certificate_Store_In_Memory ee_extras;
522  for(size_t i = 0; i != end_entity_extra.size(); ++i)
523  ee_extras.add_certificate(end_entity_extra[i]);
524 
525  // iterate until we reach a root or cannot find the issuer
526  for(;;)
527  {
528  const X509_Certificate& last = *cert_path.back();
529  const X509_DN issuer_dn = last.issuer_dn();
530  const std::vector<uint8_t> auth_key_id = last.authority_key_id();
531 
532  std::shared_ptr<const X509_Certificate> issuer;
533  bool trusted_issuer = false;
534 
535  for(Certificate_Store* store : trusted_certstores)
536  {
537  issuer = store->find_cert(issuer_dn, auth_key_id);
538  if(issuer)
539  {
540  trusted_issuer = true;
541  break;
542  }
543  }
544 
545  if(!issuer)
546  {
547  // fall back to searching supplemental certs
548  issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
549  }
550 
551  if(!issuer)
553 
554  const std::string fprint = issuer->fingerprint("SHA-256");
555 
556  if(certs_seen.count(fprint) > 0) // already seen?
557  {
559  }
560 
561  certs_seen.insert(fprint);
562  cert_path.push_back(issuer);
563 
564  if(issuer->is_self_signed())
565  {
566  if(trusted_issuer)
567  {
569  }
570  else
571  {
573  }
574  }
575  }
576  }
577 
578 /**
579  * utilities for PKIX::build_all_certificate_paths
580  */
581 namespace
582 {
583 // <certificate, trusted?>
584 using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
585 }
586 
587 /**
588  * Build all possible certificate paths from the end certificate to self-signed trusted roots.
589  *
590  * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
591  * one of the encountered errors is returned arbitrarily.
592  *
593  * todo add a path building function that returns detailed information on errors encountered while building
594  * the potentially numerous path candidates.
595  *
596  * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
597  * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
598  * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
599  * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
600  * authority key id need not be unique among the certificates used for building the path. In such a case,
601  * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
602  *
603  */
605 PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
606  const std::vector<Certificate_Store*>& trusted_certstores,
607  const std::shared_ptr<const X509_Certificate>& end_entity,
608  const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
609  {
610  if(!cert_paths_out.empty())
611  {
612  throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
613  }
614 
615  if(end_entity->is_self_signed())
616  {
618  }
619 
620  /*
621  * Pile up error messages
622  */
623  std::vector<Certificate_Status_Code> stats;
624 
625  Certificate_Store_In_Memory ee_extras;
626  for(size_t i = 0; i != end_entity_extra.size(); ++i)
627  {
628  ee_extras.add_certificate(end_entity_extra[i]);
629  }
630 
631  /*
632  * This is an inelegant but functional way of preventing path loops
633  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
634  * fingerprints in the path. If there is a duplicate, we error out.
635  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
636  */
637  std::set<std::string> certs_seen;
638 
639  // new certs are added and removed from the path during the DFS
640  // it is copied into cert_paths_out when we encounter a trusted root
641  std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
642 
643  // todo can we assume that the end certificate is not trusted?
644  std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
645 
646  while(!stack.empty())
647  {
648  // found a deletion marker that guides the DFS, backtracing
649  if(stack.back().first == nullptr)
650  {
651  stack.pop_back();
652  std::string fprint = path_so_far.back()->fingerprint("SHA-256");
653  certs_seen.erase(fprint);
654  path_so_far.pop_back();
655  }
656  // process next cert on the path
657  else
658  {
659  std::shared_ptr<const X509_Certificate> last = stack.back().first;
660  bool trusted = stack.back().second;
661  stack.pop_back();
662 
663  // certificate already seen?
664  const std::string fprint = last->fingerprint("SHA-256");
665  if(certs_seen.count(fprint) == 1)
666  {
668  // the current path ended in a loop
669  continue;
670  }
671 
672  // the current path ends here
673  if(last->is_self_signed())
674  {
675  // found a trust anchor
676  if(trusted)
677  {
678  cert_paths_out.push_back(path_so_far);
679  cert_paths_out.back().push_back(last);
680 
681  continue;
682  }
683  // found an untrustworthy root
684  else
685  {
687  continue;
688  }
689  }
690 
691  const X509_DN issuer_dn = last->issuer_dn();
692  const std::vector<uint8_t> auth_key_id = last->authority_key_id();
693 
694  // search for trusted issuers
695  std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
696  for(Certificate_Store* store : trusted_certstores)
697  {
698  auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
699  trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
700  }
701 
702  // search the supplemental certs
703  std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
704  ee_extras.find_all_certs(issuer_dn, auth_key_id);
705 
706  // if we could not find any issuers, the current path ends here
707  if(trusted_issuers.size() + misc_issuers.size() == 0)
708  {
710  continue;
711  }
712 
713  // push the latest certificate onto the path_so_far
714  path_so_far.push_back(last);
715  certs_seen.emplace(fprint);
716 
717  // push a deletion marker on the stack for backtracing later
718  stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
719 
720  for(const auto trusted_cert : trusted_issuers)
721  {
722  stack.push_back({trusted_cert,true});
723  }
724 
725  for(const auto misc : misc_issuers)
726  {
727  stack.push_back({misc,false});
728  }
729  }
730  }
731 
732  // could not construct any potentially valid path
733  if(cert_paths_out.empty())
734  {
735  if(stats.empty())
736  throw Exception("X509 path building failed for unknown reasons");
737  else
738  // arbitrarily return the first error
739  return stats[0];
740  }
741  else
742  {
744  }
745  }
746 
747 
749  const CertificatePathStatusCodes& crl,
750  const CertificatePathStatusCodes& ocsp,
751  bool require_rev_on_end_entity,
752  bool require_rev_on_intermediates)
753  {
754  if(chain_status.empty())
755  throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
756 
757  for(size_t i = 0; i != chain_status.size() - 1; ++i)
758  {
759  bool had_crl = false, had_ocsp = false;
760 
761  if(i < crl.size() && crl[i].size() > 0)
762  {
763  for(auto&& code : crl[i])
764  {
766  {
767  had_crl = true;
768  }
769  chain_status[i].insert(code);
770  }
771  }
772 
773  if(i < ocsp.size() && ocsp[i].size() > 0)
774  {
775  for(auto&& code : ocsp[i])
776  {
778  {
779  had_ocsp = true;
780  }
781 
782  chain_status[i].insert(code);
783  }
784  }
785 
786  if(had_crl == false && had_ocsp == false)
787  {
788  if((require_rev_on_end_entity && i == 0) ||
789  (require_rev_on_intermediates && i > 0))
790  {
791  chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
792  }
793  }
794  }
795  }
796 
798  {
799  if(cert_status.empty())
800  throw Invalid_Argument("PKIX::overall_status empty cert status");
801 
803 
804  // take the "worst" error as overall
805  for(const std::set<Certificate_Status_Code>& s : cert_status)
806  {
807  if(!s.empty())
808  {
809  auto worst = *s.rbegin();
810  // Leave informative OCSP/CRL confirmations on cert-level status only
811  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
812  {
813  overall_status = worst;
814  }
815  }
816  }
817  return overall_status;
818  }
819 
821  const std::vector<X509_Certificate>& end_certs,
822  const Path_Validation_Restrictions& restrictions,
823  const std::vector<Certificate_Store*>& trusted_roots,
824  const std::string& hostname,
825  Usage_Type usage,
826  std::chrono::system_clock::time_point ref_time,
827  std::chrono::milliseconds ocsp_timeout,
828  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
829  {
830  if(end_certs.empty())
831  {
832  throw Invalid_Argument("x509_path_validate called with no subjects");
833  }
834 
835  std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
836  std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
837  for(size_t i = 1; i < end_certs.size(); ++i)
838  {
839  end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
840  }
841 
842  std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
843  Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
844 
845  // If we cannot successfully build a chain to a trusted self-signed root, stop now
846  if(path_building_result != Certificate_Status_Code::OK)
847  {
848  return Path_Validation_Result(path_building_result);
849  }
850 
851  std::vector<Path_Validation_Result> error_results;
852  // Try validating all the potentially valid paths and return the first one to validate properly
853  for(auto cert_path : cert_paths)
854  {
856  PKIX::check_chain(cert_path, ref_time,
857  hostname, usage,
858  restrictions.minimum_key_strength(),
859  restrictions.trusted_hashes());
860 
861  CertificatePathStatusCodes crl_status =
862  PKIX::check_crl(cert_path, trusted_roots, ref_time);
863 
864  CertificatePathStatusCodes ocsp_status;
865 
866  if(ocsp_resp.size() > 0)
867  {
868  ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time);
869  }
870 
871  if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
872  {
873 #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
874  ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
875  ocsp_timeout, restrictions.ocsp_all_intermediates());
876 #else
877  ocsp_status.resize(1);
878  ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
879 #endif
880  }
881 
882  PKIX::merge_revocation_status(status, crl_status, ocsp_status,
883  restrictions.require_revocation_information(),
884  restrictions.ocsp_all_intermediates());
885 
886  Path_Validation_Result pvd(status, std::move(cert_path));
887  if(pvd.successful_validation())
888  {
889  return pvd;
890  }
891  else
892  {
893  error_results.push_back(std::move(pvd));
894  }
895  }
896  return error_results[0];
897  }
898 
900  const X509_Certificate& end_cert,
901  const Path_Validation_Restrictions& restrictions,
902  const std::vector<Certificate_Store*>& trusted_roots,
903  const std::string& hostname,
904  Usage_Type usage,
905  std::chrono::system_clock::time_point when,
906  std::chrono::milliseconds ocsp_timeout,
907  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
908  {
909  std::vector<X509_Certificate> certs;
910  certs.push_back(end_cert);
911  return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
912  }
913 
915  const std::vector<X509_Certificate>& end_certs,
916  const Path_Validation_Restrictions& restrictions,
917  const Certificate_Store& store,
918  const std::string& hostname,
919  Usage_Type usage,
920  std::chrono::system_clock::time_point when,
921  std::chrono::milliseconds ocsp_timeout,
922  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
923  {
924  std::vector<Certificate_Store*> trusted_roots;
925  trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
926 
927  return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
928  }
929 
931  const X509_Certificate& end_cert,
932  const Path_Validation_Restrictions& restrictions,
933  const Certificate_Store& store,
934  const std::string& hostname,
935  Usage_Type usage,
936  std::chrono::system_clock::time_point when,
937  std::chrono::milliseconds ocsp_timeout,
938  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
939  {
940  std::vector<X509_Certificate> certs;
941  certs.push_back(end_cert);
942 
943  std::vector<Certificate_Store*> trusted_roots;
944  trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
945 
946  return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
947  }
948 
950  size_t key_strength,
951  bool ocsp_intermediates) :
952  m_require_revocation_information(require_rev),
953  m_ocsp_all_intermediates(ocsp_intermediates),
954  m_minimum_key_strength(key_strength)
955  {
956  if(key_strength <= 80)
957  m_trusted_hashes.insert("SHA-160");
958 
959  m_trusted_hashes.insert("SHA-224");
960  m_trusted_hashes.insert("SHA-256");
961  m_trusted_hashes.insert("SHA-384");
962  m_trusted_hashes.insert("SHA-512");
963  }
964 
965 namespace {
966 CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
967  {
969  for(const auto& status_set_i : all_statuses)
970  {
971  std::set<Certificate_Status_Code> warning_set_i;
972  for(const auto& code : status_set_i)
973  {
976  {
977  warning_set_i.insert(code);
978  }
979  }
980  warnings.push_back(warning_set_i);
981  }
982  return warnings;
983  }
984 }
985 
987  std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
988  m_all_status(status),
989  m_warnings(find_warnings(m_all_status)),
990  m_cert_path(cert_chain),
991  m_overall(PKIX::overall_status(m_all_status))
992  {
993  }
994 
996  {
997  if(m_cert_path.empty())
998  throw Exception("Path_Validation_Result::trust_root no path set");
1000  throw Exception("Path_Validation_Result::trust_root meaningless with invalid status");
1001 
1002  return *m_cert_path[m_cert_path.size()-1];
1003  }
1004 
1005 std::set<std::string> Path_Validation_Result::trusted_hashes() const
1006  {
1007  std::set<std::string> hashes;
1008  for(size_t i = 0; i != m_cert_path.size(); ++i)
1009  hashes.insert(m_cert_path[i]->hash_used_for_signature());
1010  return hashes;
1011  }
1012 
1014  {
1018  }
1019 
1021  {
1022  return m_warnings.empty();
1023  }
1024 
1026  {
1027  return m_warnings;
1028  }
1029 
1031  {
1032  return status_string(result());
1033  }
1034 
1036  {
1037  if(const char* s = to_string(code))
1038  return s;
1039 
1040  return "Unknown error";
1041  }
1042 
1043 }
CertificatePathStatusCodes check_chain(const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, std::chrono::system_clock::time_point ref_time, const std::string &hostname, Usage_Type usage, size_t min_signature_algo_strength, const std::set< std::string > &trusted_hashes)
Definition: x509path.cpp:32
bool successful_validation() const
Definition: x509path.cpp:1013
CertificatePathStatusCodes warnings() const
Definition: x509path.cpp:1025
const X509_Certificate & trust_root() const
Definition: x509path.cpp:995
size_t minimum_key_strength() const
Definition: x509path.h:97
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< std::shared_ptr< const X509_Certificate >> &&cert_chain)
Definition: x509path.cpp:986
std::vector< std::shared_ptr< const X509_Certificate > > find_all_certs(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition: certstor.cpp:68
Response GET_sync(const std::string &url, size_t allowable_redirects, std::chrono::milliseconds timeout)
Definition: http_util.cpp:235
Response POST_sync(const std::string &url, const std::string &content_type, const std::vector< uint8_t > &body, size_t allowable_redirects, std::chrono::milliseconds timeout)
Definition: http_util.cpp:242
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:797
std::shared_ptr< const X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition: certstor.cpp:47
const std::vector< uint8_t > & authority_key_id() const
Definition: x509cert.cpp:389
void merge_revocation_status(CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, bool require_rev_on_end_entity, bool require_rev_on_intermediates)
Definition: x509path.cpp:748
bool require_revocation_information() const
Definition: x509path.h:78
const std::vector< OID > & get_extension_oids() const
Definition: x509_ext.h:124
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:81
Certificate_Status_Code build_certificate_path(std::vector< std::shared_ptr< const X509_Certificate >> &cert_path_out, const std::vector< Certificate_Store *> &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate >> &end_entity_extra)
Definition: x509path.cpp:500
const X509_DN & issuer_dn() const
Definition: x509cert.cpp:410
const std::set< std::string > & trusted_hashes() const
Definition: x509path.h:91
CertificatePathStatusCodes check_crl(const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< std::shared_ptr< const X509_CRL >> &crls, std::chrono::system_clock::time_point ref_time)
Definition: x509path.cpp:252
Definition: alg_id.cpp:13
Path_Validation_Result x509_path_validate(const std::vector< X509_Certificate > &end_certs, const Path_Validation_Restrictions &restrictions, const std::vector< Certificate_Store *> &trusted_roots, const std::string &hostname, Usage_Type usage, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds ocsp_timeout, const std::vector< std::shared_ptr< const OCSP::Response >> &ocsp_resp)
Definition: x509path.cpp:820
std::string result_string() const
Definition: x509path.cpp:1030
Certificate_Status_Code build_all_certificate_paths(std::vector< std::vector< std::shared_ptr< const X509_Certificate >>> &cert_paths, const std::vector< Certificate_Store *> &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate >> &end_entity_extra)
Definition: x509path.cpp:605
void add_certificate(const X509_Certificate &cert)
Definition: certstor.cpp:20
std::string to_string(const secure_vector< uint8_t > &bytes)
Definition: stl_util.h:25
static const char * status_string(Certificate_Status_Code code)
Definition: x509path.cpp:1035
void add_crl(const X509_CRL &crl)
Definition: certstor.cpp:125
Path_Validation_Restrictions(bool require_rev=false, size_t minimum_key_strength=110, bool ocsp_all_intermediates=false)
Definition: x509path.cpp:949
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
std::vector< uint8_t > BER_encode() const
Definition: ocsp.cpp:69
CertificatePathStatusCodes check_ocsp(const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< std::shared_ptr< const OCSP::Response >> &ocsp_responses, const std::vector< Certificate_Store *> &certstores, std::chrono::system_clock::time_point ref_time)
Definition: x509path.cpp:203
Certificate_Status_Code
Definition: cert_status.h:18
std::set< std::string > trusted_hashes() const
Definition: x509path.cpp:1005
static size_t lookup_ub(const OID &oid)
Definition: x509_dn_ub.cpp:45
std::string lookup(const OID &oid)
Definition: oids.cpp:18
static BigInt decode(const uint8_t buf[], size_t length, Base base=Binary)
Definition: big_code.cpp:114
Usage_Type
Definition: x509cert.h:25
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
Definition: x509_ext.cpp:176
Certificate_Status_Code result() const
Definition: x509path.h:146