Botan 2.19.5 Crypto and TLS for C&

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< Certificate_Store * > &certstores, 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< std::shared_ptr< const X509_CRL > > &crls, 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, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero())
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 664 of file x509path.cpp.

668 {
669 if(!cert_paths_out.empty())
670 {
671 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
672 }
673
674 if(end_entity->is_self_signed())
675 {
676 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
677 }
678
679 /*
680 * Pile up error messages
681 */
682 std::vector<Certificate_Status_Code> stats;
683
684 Certificate_Store_In_Memory ee_extras;
685 for(size_t i = 0; i != end_entity_extra.size(); ++i)
686 {
687 ee_extras.add_certificate(end_entity_extra[i]);
688 }
689
690 /*
691 * This is an inelegant but functional way of preventing path loops
692 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
693 * fingerprints in the path. If there is a duplicate, we error out.
694 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
695 */
696 std::set<std::string> certs_seen;
697
698 // new certs are added and removed from the path during the DFS
699 // it is copied into cert_paths_out when we encounter a trusted root
700 std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
701
702 // todo can we assume that the end certificate is not trusted?
703 std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
704
705 while(!stack.empty())
706 {
707 // found a deletion marker that guides the DFS, backtracing
708 if(stack.back().first == nullptr)
709 {
710 stack.pop_back();
711 std::string fprint = path_so_far.back()->fingerprint("SHA-256");
712 certs_seen.erase(fprint);
713 path_so_far.pop_back();
714 }
715 // process next cert on the path
716 else
717 {
718 std::shared_ptr<const X509_Certificate> last = stack.back().first;
719 bool trusted = stack.back().second;
720 stack.pop_back();
721
722 // certificate already seen?
723 const std::string fprint = last->fingerprint("SHA-256");
724 if(certs_seen.count(fprint) == 1)
725 {
726 stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
727 // the current path ended in a loop
728 continue;
729 }
730
731 // the current path ends here
732 if(last->is_self_signed())
733 {
734 // found a trust anchor
735 if(trusted)
736 {
737 cert_paths_out.push_back(path_so_far);
738 cert_paths_out.back().push_back(last);
739
740 continue;
741 }
742 // found an untrustworthy root
743 else
744 {
745 stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
746 continue;
747 }
748 }
749
750 const X509_DN issuer_dn = last->issuer_dn();
751 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
752
753 // search for trusted issuers
754 std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
755 for(Certificate_Store* store : trusted_certstores)
756 {
757 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
758 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
759 }
760
761 // search the supplemental certs
762 std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
763 ee_extras.find_all_certs(issuer_dn, auth_key_id);
764
765 // if we could not find any issuers, the current path ends here
766 if(trusted_issuers.size() + misc_issuers.size() == 0)
767 {
768 stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
769 continue;
770 }
771
772 // push the latest certificate onto the path_so_far
773 path_so_far.push_back(last);
774 certs_seen.insert(fprint);
775
776 // push a deletion marker on the stack for backtracing later
777 stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
778
779 for(const auto& trusted_cert : trusted_issuers)
780 {
781 stack.push_back({trusted_cert,true});
782 }
783
784 for(const auto& misc : misc_issuers)
785 {
786 stack.push_back({misc,false});
787 }
788 }
789 }
790
791 // could not construct any potentially valid path
792 if(cert_paths_out.empty())
793 {
794 if(stats.empty())
795 throw Internal_Error("X509 path building failed for unknown reasons");
796 else
797 // arbitrarily return the first error
798 return stats[0];
799 }
800 else
801 {
802 return Certificate_Status_Code::OK;
803 }
804 }
Botan::Invalid_Argument
Definition: exceptn.h:137
Botan::OCSP::Response_Status_Code::Internal_Error
@ Internal_Error

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().

## ◆ 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_out | output parameter, cert_path will be appended to this vector
  trusted_certstores | list of certificate stores that contain trusted certificates
  end_entity | the cert to be validated
  end_entity_extra | optional list of additional untrusted certs for path building

Returns
  result of the path building operation (OK or error)

Definition at line 559 of file x509path.cpp.

563 {
564 if(end_entity->is_self_signed())
565 {
566 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
567 }
568
569 /*
570 * This is an inelegant but functional way of preventing path loops
571 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
572 * fingerprints in the path. If there is a duplicate, we error out.
573 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
574 */
575 std::set<std::string> certs_seen;
576
577 cert_path.push_back(end_entity);
578 certs_seen.insert(end_entity->fingerprint("SHA-256"));
579
580 Certificate_Store_In_Memory ee_extras;
581 for(size_t i = 0; i != end_entity_extra.size(); ++i)
582 ee_extras.add_certificate(end_entity_extra[i]);
583
584 // iterate until we reach a root or cannot find the issuer
585 for(;;)
586 {
587 const X509_Certificate& last = *cert_path.back();
588 const X509_DN issuer_dn = last.issuer_dn();
589 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
590
591 std::shared_ptr<const X509_Certificate> issuer;
592 bool trusted_issuer = false;
593
594 for(Certificate_Store* store : trusted_certstores)
595 {
596 issuer = store->find_cert(issuer_dn, auth_key_id);
597 if(issuer)
598 {
599 trusted_issuer = true;
600 break;
601 }
602 }
603
604 if(!issuer)
605 {
606 // fall back to searching supplemental certs
607 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
608 }
609
610 if(!issuer)
611 return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
612
613 const std::string fprint = issuer->fingerprint("SHA-256");
614
615 if(certs_seen.count(fprint) > 0) // already seen?
616 {
617 return Certificate_Status_Code::CERT_CHAIN_LOOP;
618 }
619
620 certs_seen.insert(fprint);
621 cert_path.push_back(issuer);
622
623 if(issuer->is_self_signed())
624 {
625 if(trusted_issuer)
626 {
627 return Certificate_Status_Code::OK;
628 }
629 else
630 {
631 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
632 }
633 }
634 }
635 }
Botan::Certificate_Store_In_Memory
Definition: certstor.h:85
Botan::Certificate_Store_In_Memory::find_cert
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:65
Botan::Certificate_Store_In_Memory::add_certificate
void add_certificate(const X509_Certificate &cert)
Definition: certstor.cpp:38
Botan::Certificate_Store
Definition: certstor.h:20
Botan::X509_Certificate
Definition: x509cert.h:38
Botan::X509_Certificate::authority_key_id
const std::vector< uint8_t > & authority_key_id() const
Definition: x509cert.cpp:451
Botan::X509_Certificate::issuer_dn
const X509_DN & issuer_dn() const
Definition: x509cert.cpp:472
Botan::X509_DN
Definition: pkix_types.h:43

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.

## ◆ 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_path | path built by build_certificate_path with OK result
  ref_time | whatever time you want to perform the validation against (normally current system clock)
  hostname | the hostname
  usage | end entity usage checks
  min_signature_algo_strength | 80 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_hashes | set 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.

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 for(size_t i = 0; i != cert_path.size(); ++i)
55 {
56 std::set<Certificate_Status_Code>& status = cert_status.at(i);
57
58 const bool at_self_signed_root = (i == cert_path.size() - 1);
59
60 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
61
62 const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
63
64 std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
65
66 // Check the signature algorithm is known
67 if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
68 {
69 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
70 }
71 else
72 {
73 // only perform the following checks if the signature algorithm is known
74 if(!issuer_key)
75 {
76 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
77 }
78 else
79 {
80 const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
81
82 if(sig_status != Certificate_Status_Code::VERIFIED)
83 status.insert(sig_status);
84
85 if(issuer_key->estimated_strength() < min_signature_algo_strength)
86 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
87 }
88
89 // Ignore untrusted hashes on self-signed roots
90 if(trusted_hashes.size() > 0 && !at_self_signed_root)
91 {
92 if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
93 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
94 }
95 }
96 }
97
98
99 // If any of the signatures were invalid, return immediately; we know the
100 // chain is invalid and signature failure is always considered the most
101 // critical result. This does mean other problems in the certificate (eg
102 // expired) will not be reported, but we'd have to assume any such data is
103 // anyway arbitrary considering we couldn't verify the signature chain
104
105 for(size_t i = 0; i != cert_path.size(); ++i)
106 {
107 for(auto status : cert_status.at(i))
108 {
109 // This ignores errors relating to the key or hash being weak since
110 // these are somewhat advisory
111 if(static_cast<uint32_t>(status) >= 5000)
112 {
113 return cert_status;
114 }
115 }
116 }
117
118 if(cert_path[0]->is_CA_cert() == false &&
119 cert_path[0]->has_constraints(KEY_CERT_SIGN))
120 {
121 /*
122 "If the keyCertSign bit is asserted, then the cA bit in the
123 basic constraints extension (Section 4.2.1.9) MUST also be
124 asserted." - RFC 5280
125
126 We don't bother doing this check on the rest of the path since they
127 must have the cA bit asserted or the validation will fail anyway.
128 */
129 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
130 }
131
132 for(size_t i = 0; i != cert_path.size(); ++i)
133 {
134 std::set<Certificate_Status_Code>& status = cert_status.at(i);
135
136 const bool at_self_signed_root = (i == cert_path.size() - 1);
137
138 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
139
140 const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
141
142 if(at_self_signed_root && (issuer->is_self_signed() == false))
143 {
144 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
145 }
146
147 if(subject->issuer_dn() != issuer->subject_dn())
148 {
149 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
150 }
151
152 // Check the serial number
153 if(subject->is_serial_negative())
154 {
155 status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
156 }
157
158 // Check the subject's DN components' length
159
160 for(const auto& dn_pair : subject->subject_dn().dn_info())
161 {
162 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
163 // dn_pair = <OID,str>
164 if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
165 {
166 status.insert(Certificate_Status_Code::DN_TOO_LONG);
167 }
168 }
169
170 // Check all certs for valid time range
171 if(validation_time < subject->not_before())
172 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
173
174 if(validation_time > subject->not_after())
175 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
176
177 // Check issuer constraints
178 if(!issuer->is_CA_cert() && !self_signed_ee_cert)
179 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
180
181 // Check cert extensions
182
183 if(subject->x509_version() == 1)
184 {
185 if(subject->v2_issuer_key_id().empty() == false ||
186 subject->v2_subject_key_id().empty() == false)
187 {
188 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
189 }
190 }
191
192 Extensions extensions = subject->v3_extensions();
193 const auto& extensions_vec = extensions.extensions();
194 if(subject->x509_version() < 3 && !extensions_vec.empty())
195 {
196 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
197 }
198 for(auto& extension : extensions_vec)
199 {
200 extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
201 }
202 if(extensions.extensions().size() != extensions.get_extension_oids().size())
203 {
204 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
205 }
206 }
207
208 // path len check
209 size_t max_path_length = cert_path.size();
210 for(size_t i = cert_path.size() - 1; i > 0 ; --i)
211 {
212 std::set<Certificate_Status_Code>& status = cert_status.at(i);
213 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
214
215 /*
216 * If the certificate was not self-issued, verify that max_path_length is
217 * greater than zero and decrement max_path_length by 1.
218 */
219 if(subject->subject_dn() != subject->issuer_dn())
220 {
221 if(max_path_length > 0)
222 {
223 --max_path_length;
224 }
225 else
226 {
227 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
228 }
229 }
230
231 /*
232 * If pathLenConstraint is present in the certificate and is less than max_path_length,
233 * set max_path_length to the value of pathLenConstraint.
234 */
235 if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
236 {
237 max_path_length = subject->path_limit();
238 }
239 }
240
241 return cert_status;
242 }
Botan::ASN1_Time
Definition: asn1_obj.h:328
KEY_CERT_SIGN
@ KEY_CERT_SIGN
Definition: ffi.h:1571
Botan::OIDS::oid2str_or_empty
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
Botan::CertificatePathStatusCodes
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Botan::Certificate_Status_Code
Certificate_Status_Code
Definition: pkix_enums.h:17

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::X509_DN::lookup_ub(), Botan::OIDS::oid2str_or_empty(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, Botan::V2_IDENTIFIERS_IN_V1_CERT, and Botan::VERIFIED.

Referenced by Botan::x509_path_validate().

## ◆ check_crl() [1/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_path | path already validated by check_chain
  certstores | a list of certificate stores to query for the CRL
  ref_time | whatever time you want to perform the validation against (normally current system clock)

Returns
  revocation status

Definition at line 370 of file x509path.cpp.

373 {
374 if(cert_path.empty())
375 throw Invalid_Argument("PKIX::check_crl cert_path empty");
376
377 if(certstores.empty())
378 throw Invalid_Argument("PKIX::check_crl certstores empty");
379
380 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
381
382 for(size_t i = 0; i != cert_path.size(); ++i)
383 {
384 BOTAN_ASSERT_NONNULL(cert_path[i]);
385 for(size_t c = 0; c != certstores.size(); ++c)
386 {
387 crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
388 if(crls[i])
389 break;
390 }
391 }
392
393 return PKIX::check_crl(cert_path, crls, ref_time);
394 }
BOTAN_ASSERT_NONNULL
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:107
Botan::PKIX::check_crl
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:300

References BOTAN_ASSERT_NONNULL, and check_crl().

## ◆ check_crl() [2/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_path | path already validated by check_chain
  crls | the 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_time | whatever time you want to perform the validation against (normally current system clock)

Returns
  revocation status

Definition at line 300 of file x509path.cpp.

303 {
304 if(cert_path.empty())
305 throw Invalid_Argument("PKIX::check_crl cert_path empty");
306
307 CertificatePathStatusCodes cert_status(cert_path.size());
308 const X509_Time validation_time(ref_time);
309
310 for(size_t i = 0; i != cert_path.size() - 1; ++i)
311 {
312 std::set<Certificate_Status_Code>& status = cert_status.at(i);
313
314 if(i < crls.size() && crls.at(i))
315 {
316 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
317 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
318
319 if(!ca->allowed_usage(CRL_SIGN))
320 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
321
322 if(validation_time < crls[i]->this_update())
323 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
324
325 if(validation_time > crls[i]->next_update())
326 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
327
328 if(crls[i]->check_signature(ca->subject_public_key()) == false)
329 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
330
331 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
332
333 if(crls[i]->is_revoked(*subject))
334 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
335
336 std::string dp = subject->crl_distribution_point();
337 if(!dp.empty())
338 {
339 if(dp != crls[i]->crl_issuing_distribution_point())
340 {
341 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
342 }
343 }
344
345 for(const auto& extension : crls[i]->extensions().extensions())
346 {
347 // XXX this is wrong - the OID might be defined but the extention not full parsed
348 // for example see #1652
349
350 // is the extension critical and unknown?
351 if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
352 {
353 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
354 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
355 */
356 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
357 }
358 }
359
360 }
361 }
362
363 while(cert_status.size() > 0 && cert_status.back().empty())
364 cert_status.pop_back();
365
366 return cert_status;
367 }
CRL_SIGN
@ CRL_SIGN
Definition: ffi.h:1572

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::NO_MATCHING_CRLDP, Botan::OIDS::oid2str_or_empty(), and Botan::VALID_CRL_CHECKED.

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

## ◆ 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,
|  | std::chrono::seconds | max_ocsp_age = `std::chrono::seconds::zero()`
| ) |  |

Check OCSP responses for revocation information

Parameters

  cert_path | path already validated by check_chain
  ocsp_responses | the OCSP responses to consider
  certstores | trusted roots
  ref_time | whatever time you want to perform the validation against (normally current system clock)
  max_ocsp_age | maximum age of OCSP responses w/o next_update. If zero, there is no maximum age

Returns
  revocation status

Definition at line 245 of file x509path.cpp.

250 {
251 if(cert_path.empty())
252 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
253
254 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
255
256 for(size_t i = 0; i != cert_path.size() - 1; ++i)
257 {
258 std::set<Certificate_Status_Code>& status = cert_status.at(i);
259
260 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
261 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
262
263 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
264 && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
265 {
266 try
267 {
268 // When verifying intermediate certificates we need to truncate the
269 // cert_path so that the intermediate under investigation becomes the
270 // last certificate in the chain.
271 std::vector<std::shared_ptr<const X509_Certificate>> ocsp_cert_path(cert_path.begin() + i, cert_path.end());
272 Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, ocsp_cert_path);
273
274 if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
275 {
276 // Signature ok, so check the claimed status
277 Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
278 status.insert(ocsp_status);
279 }
280 else
281 {
282 // Some signature problem
283 status.insert(ocsp_signature_status);
284 }
285 }
286 catch(Exception&)
287 {
288 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
289 }
290 }
291 }
292
293 while(cert_status.size() > 0 && cert_status.back().empty())
294 cert_status.pop_back();
295
296 return cert_status;
297 }

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

Referenced by Botan::x509_path_validate().

## ◆ 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_status | the certificate status
  crl_status | results from check_crl
  ocsp_status | results from check_ocsp
  require_rev_on_end_entity | require valid CRL or OCSP on end-entity cert
  require_rev_on_intermediates | require valid CRL or OCSP on all intermediate certificates

Definition at line 807 of file x509path.cpp.

812 {
813 if(chain_status.empty())
814 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
815
816 for(size_t i = 0; i != chain_status.size() - 1; ++i)
817 {
818 bool had_crl = false, had_ocsp = false;
819
820 if(i < crl.size() && crl[i].size() > 0)
821 {
822 for(auto&& code : crl[i])
823 {
824 if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
825 {
826 had_crl = true;
827 }
828 chain_status[i].insert(code);
829 }
830 }
831
832 if(i < ocsp.size() && ocsp[i].size() > 0)
833 {
834 for(auto&& code : ocsp[i])
835 {
836 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
837 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
838 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
839 {
840 had_ocsp = true;
841 }
842
843 chain_status[i].insert(code);
844 }
845 }
846
847 if(had_crl == false && had_ocsp == false)
848 {
849 if((require_rev_on_end_entity && i == 0) ||
850 (require_rev_on_intermediates && i > 0))
851 {
852 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
853 }
854 }
855 }
856 }

References Botan::NO_REVOCATION_DATA, Botan::OCSP_NO_REVOCATION_URL, Botan::OCSP_RESPONSE_GOOD, Botan::OCSP_SERVER_NOT_AVAILABLE, and Botan::VALID_CRL_CHECKED.

Referenced by Botan::x509_path_validate().

## ◆ overall_status()

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

Find overall status (OK, error) of a validation

Parameters

  cert_status | result of merge_revocation_status or check_chain

Definition at line 858 of file x509path.cpp.

859 {
860 if(cert_status.empty())
861 throw Invalid_Argument("PKIX::overall_status empty cert status");
862
863 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
864
865 // take the "worst" error as overall
866 for(const std::set<Certificate_Status_Code>& s : cert_status)
867 {
868 if(!s.empty())
869 {
870 auto worst = *s.rbegin();
871 // Leave informative OCSP/CRL confirmations on cert-level status only
872 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
873 {
874 overall_status = worst;
875 }
876 }
877 }
878 return overall_status;
879 }
Botan::PKIX::overall_status
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:858

References Botan::FIRST_ERROR_STATUS, Botan::OK, and overall_status().

Referenced by overall_status().