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