Botan  2.7.0
Crypto and TLS for C++11
Functions
Botan::PKIX Namespace Reference

Functions

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)
 
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)
 
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)
 
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)
 
CertificatePathStatusCodes check_crl (const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< Certificate_Store *> &certstores, std::chrono::system_clock::time_point ref_time)
 
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)
 
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)
 
Certificate_Status_Code overall_status (const CertificatePathStatusCodes &cert_status)
 

Detailed Description

namespace PKIX holds the building blocks that are called by x509_path_validate. This allows custom validation logic to be written by applications and makes for easier testing, but unless you're positive you know what you're doing you probably want to just call x509_path_validate instead.

Function Documentation

◆ build_all_certificate_paths()

Certificate_Status_Code Botan::PKIX::build_all_certificate_paths ( std::vector< std::vector< std::shared_ptr< const X509_Certificate >>> &  cert_paths_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 
)

Build all possible certificate paths from the end certificate to self-signed trusted roots.

All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found, one of the encountered errors is returned arbitrarily.

todo add a path building function that returns detailed information on errors encountered while building the potentially numerous path candidates.

Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS. At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN + authority key id need not be unique among the certificates used for building the path. In such a case, we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.

Definition at line 613 of file x509path.cpp.

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_all_certs(), and Botan::OK.

Referenced by Botan::x509_path_validate().

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  {
625  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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  {
675  stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
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  {
694  stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
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  {
717  stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
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  {
751  return Certificate_Status_Code::OK;
752  }
753  }

◆ build_certificate_path()

Certificate_Status_Code Botan::PKIX::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 
)

Build certificate path

Parameters
cert_path_outoutput parameter, cert_path will be appended to this vector
trusted_certstoreslist of certificate stores that contain trusted certificates
end_entitythe cert to be validated
end_entity_extraoptional list of additional untrusted certs for path building
Returns
result of the path building operation (OK or error)

Definition at line 508 of file x509path.cpp.

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::X509_Certificate::authority_key_id(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_cert(), Botan::X509_Certificate::issuer_dn(), and Botan::OK.

512  {
513  if(end_entity->is_self_signed())
514  {
515  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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)
560  return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
561 
562  const std::string fprint = issuer->fingerprint("SHA-256");
563 
564  if(certs_seen.count(fprint) > 0) // already seen?
565  {
566  return Certificate_Status_Code::CERT_CHAIN_LOOP;
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  {
576  return Certificate_Status_Code::OK;
577  }
578  else
579  {
580  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
581  }
582  }
583  }
584  }

◆ check_chain()

CertificatePathStatusCodes Botan::PKIX::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 
)

Check the certificate chain, but not any revocation data

Parameters
cert_pathpath built by build_certificate_path with OK result
ref_timewhatever time you want to perform the validation against (normally current system clock)
hostnamethe hostname
usageend entity usage checks
min_signature_algo_strength80 or 110 typically Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2. Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
trusted_hashesset of trusted hash functions, empty means accept any hash we have an OID for
Returns
vector of results on per certificate in the path, each containing a set of results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS, then the result for that certificate is successful. If all results are

Definition at line 32 of file x509path.cpp.

References Botan::CA_CERT_NOT_FOR_CERT_ISSUER, Botan::CERT_CHAIN_TOO_LONG, Botan::CERT_HAS_EXPIRED, Botan::CERT_NAME_NOMATCH, Botan::CERT_NOT_YET_VALID, Botan::CERT_PUBKEY_INVALID, Botan::CERT_SERIAL_NEGATIVE, Botan::CHAIN_LACKS_TRUST_ROOT, Botan::CHAIN_NAME_MISMATCH, Botan::DN_TOO_LONG, Botan::DUPLICATE_CERT_EXTENSION, Botan::EXT_IN_V1_V2_CERT, Botan::Extensions::extensions(), Botan::Extensions::get_extension_oids(), Botan::INVALID_USAGE, Botan::KEY_CERT_SIGN, Botan::OIDS::lookup(), Botan::X509_DN::lookup_ub(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, and Botan::VERIFIED.

Referenced by Botan::x509_path_validate().

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  {
80  status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
81  }
82 
83  if(subject->issuer_dn() != issuer->subject_dn())
84  {
85  status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
86  }
87 
88  // Check the serial number
89  if(subject->is_serial_negative())
90  {
91  status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
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  {
102  status.insert(Certificate_Status_Code::DN_TOO_LONG);
103  }
104  }
105 
106  // Check all certs for valid time range
107  if(validation_time < subject->not_before())
108  status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
109 
110  if(validation_time > subject->not_after())
111  status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
112 
113  // Check issuer constraints
114  if(!issuer->is_CA_cert() && !self_signed_ee_cert)
115  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
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  {
122  status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
123  }
124  // only perform the following checks if the signature algorithm is known
125  else
126  {
127  if(!issuer_key)
128  {
129  status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
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)
139  status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
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)
146  status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
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  {
155  status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
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  {
163  status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
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  {
186  status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
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  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18
std::string lookup(const OID &oid)
Definition: oids.cpp:113

◆ check_crl() [1/2]

CertificatePathStatusCodes Botan::PKIX::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 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
crlsthe list of CRLs to check, it is assumed that crls[i] (if not null) is the associated CRL for the subject in cert_path[i].
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 253 of file x509path.cpp.

References Botan::CA_CERT_NOT_FOR_CRL_ISSUER, Botan::CERT_IS_REVOKED, Botan::CRL_BAD_SIGNATURE, Botan::CRL_HAS_EXPIRED, Botan::CRL_NOT_YET_VALID, Botan::CRL_SIGN, Botan::OIDS::lookup(), Botan::NO_MATCHING_CRLDP, and Botan::VALID_CRL_CHECKED.

Referenced by check_crl(), and Botan::x509_path_validate().

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))
273  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
274 
275  if(validation_time < crls[i]->this_update())
276  status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
277 
278  if(validation_time > crls[i]->next_update())
279  status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
280 
281  if(crls[i]->check_signature(ca->subject_public_key()) == false)
282  status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
283 
284  status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
285 
286  if(crls[i]->is_revoked(*subject))
287  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
288 
289  std::string dp = subject->crl_distribution_point();
290  if(!dp.empty())
291  {
292  if(dp != crls[i]->crl_issuing_distribution_point())
293  {
294  status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
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  */
306  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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  }
Definition: ffi.h:1171
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
std::string lookup(const OID &oid)
Definition: oids.cpp:113

◆ check_crl() [2/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path,
const std::vector< Certificate_Store *> &  certstores,
std::chrono::system_clock::time_point  ref_time 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
certstoresa list of certificate stores to query for the CRL
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 320 of file x509path.cpp.

References BOTAN_ASSERT_NONNULL, and check_crl().

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  }
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:95
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

◆ check_ocsp()

CertificatePathStatusCodes Botan::PKIX::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 
)

Check OCSP responses for revocation information

Parameters
cert_pathpath already validated by check_chain
ocsp_responsesthe OCSP responses to consider
certstorestrusted roots
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 204 of file x509path.cpp.

References Botan::OCSP_RESPONSE_INVALID, and Botan::OCSP_SIGNATURE_OK.

Referenced by Botan::x509_path_validate().

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  {
241  status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
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  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18

◆ merge_revocation_status()

void Botan::PKIX::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 
)

Merge the results from CRL and/or OCSP checks into chain_status

Parameters
chain_statusthe certificate status
crl_statusresults from check_crl
ocsp_statusresults from check_ocsp
require_rev_on_end_entityrequire valid CRL or OCSP on end-entity cert
require_rev_on_intermediatesrequire valid CRL or OCSP on all intermediate certificates

Definition at line 756 of file x509path.cpp.

References Botan::NO_REVOCATION_DATA, Botan::OCSP_RESPONSE_GOOD, Botan::OSCP_NO_REVOCATION_URL, Botan::OSCP_SERVER_NOT_AVAILABLE, and Botan::VALID_CRL_CHECKED.

Referenced by Botan::x509_path_validate().

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  {
773  if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
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  {
785  if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
786  code == Certificate_Status_Code::OSCP_NO_REVOCATION_URL || // softfail
787  code == Certificate_Status_Code::OSCP_SERVER_NOT_AVAILABLE) // softfail
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  }

◆ overall_status()

Certificate_Status_Code Botan::PKIX::overall_status ( const CertificatePathStatusCodes cert_status)

Find overall status (OK, error) of a validation

Parameters
cert_statusresult of merge_revocation_status or check_chain

Definition at line 807 of file x509path.cpp.

References Botan::FIRST_ERROR_STATUS, and Botan::OK.

808  {
809  if(cert_status.empty())
810  throw Invalid_Argument("PKIX::overall_status empty cert status");
811 
812  Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
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
821  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
822  {
823  overall_status = worst;
824  }
825  }
826  }
827  return overall_status;
828  }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:807
Certificate_Status_Code
Definition: cert_status.h:18