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