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