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