Botan 3.11.0
Crypto and TLS for C&
Botan::PKIX Namespace Reference

Functions

Certificate_Status_Code build_all_certificate_paths (std::vector< std::vector< X509_Certificate > > &cert_paths, const std::vector< Certificate_Store * > &trusted_certstores, const X509_Certificate &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
Certificate_Status_Code build_certificate_path (std::vector< X509_Certificate > &cert_path_out, const std::vector< Certificate_Store * > &trusted_certstores, const X509_Certificate &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
CertificatePathStatusCodes check_chain (const std::vector< X509_Certificate > &cert_path, std::chrono::system_clock::time_point ref_time, std::string_view hostname, Usage_Type usage, const Path_Validation_Restrictions &restrictions)
CertificatePathStatusCodes check_crl (const std::vector< X509_Certificate > &cert_path, const std::vector< Certificate_Store * > &certstores, std::chrono::system_clock::time_point ref_time)
CertificatePathStatusCodes check_crl (const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
CertificatePathStatusCodes check_ocsp (const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &certstores, std::chrono::system_clock::time_point ref_time, const Path_Validation_Restrictions &restrictions)
void merge_revocation_status (CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, const Path_Validation_Restrictions &restrictions)
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< X509_Certificate > > & cert_paths_out,
const std::vector< Certificate_Store * > & trusted_certstores,
const X509_Certificate & end_entity,
const std::vector< X509_Certificate > & end_entity_extra )

Create all certificate paths by identifying all possible routes from the end-entity certificate to any certificate in the certificate store list. Paths may also end in intermediate or leaf certificates found in the certificate stores.

WARNING: The validity (e.g. signatures or constraints) of the output path IS NOT checked.

Parameters
cert_pathsoutput parameter to be filled with all discovered certificate paths
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)

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 680 of file x509path.cpp.

683 {
684 if(!cert_paths_out.empty()) {
685 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
686 }
687 if(std::ranges::any_of(trusted_certstores, [](auto* ptr) { return ptr == nullptr; })) {
688 throw Invalid_Argument("certificate store list must not contain nullptr");
689 }
690
691 auto cert_in_any_trusted_store = [&](const X509_Certificate& cert) {
692 return std::ranges::any_of(trusted_certstores,
693 [&](const Certificate_Store* store) { return store->certificate_known(cert); });
694 };
695
696 /*
697 * Pile up error messages
698 */
699 std::vector<Certificate_Status_Code> stats;
700
701 Certificate_Store_In_Memory ee_extras;
702 for(const auto& cert : end_entity_extra) {
703 if(!cert_in_any_trusted_store(cert)) {
704 ee_extras.add_certificate(cert);
705 }
706 }
707
708 /*
709 * This is an inelegant but functional way of preventing path loops
710 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
711 * fingerprints in the path. If there is a duplicate, we error out.
712 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
713 */
714 std::set<std::string> certs_seen;
715
716 // new certs are added and removed from the path during the DFS
717 // it is copied into cert_paths_out when we encounter a trusted root
718 std::vector<X509_Certificate> path_so_far;
719
720 std::vector<cert_maybe_trusted> stack = {{end_entity, cert_in_any_trusted_store(end_entity)}};
721
722 while(!stack.empty()) {
723 std::optional<X509_Certificate> last = stack.back().first;
724 // found a deletion marker that guides the DFS, backtracking
725 if(last == std::nullopt) {
726 stack.pop_back();
727 const std::string fprint = path_so_far.back().fingerprint("SHA-256");
728 certs_seen.erase(fprint);
729 path_so_far.pop_back();
730 }
731 // process next cert on the path
732 else {
733 const bool trusted = stack.back().second;
734 stack.pop_back();
735
736 // certificate already seen?
737 const std::string fprint = last->fingerprint("SHA-256");
738 if(certs_seen.count(fprint) == 1) {
740 // the current path ended in a loop
741 continue;
742 }
743
744 // A valid path has been discovered. It includes endpoints that may end
745 // with either a self-signed or a non-self-signed certificate. For
746 // certificates that are not self-signed, additional paths could
747 // potentially extend from the current one.
748 if(trusted) {
749 cert_paths_out.push_back(path_so_far);
750 cert_paths_out.back().push_back(*last);
751 } else if(last->is_self_signed()) {
753 continue;
754 }
755
756 const X509_DN issuer_dn = last->issuer_dn();
757 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
758
759 // search for trusted issuers
760 std::vector<X509_Certificate> trusted_issuers;
761 for(const Certificate_Store* store : trusted_certstores) {
762 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
763 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
764 }
765
766 // search the supplemental certs
767 const std::vector<X509_Certificate> misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id);
768
769 // if we could not find any issuers, the current path ends here
770 if(trusted_issuers.empty() && misc_issuers.empty()) {
772 continue;
773 }
774
775 // push the latest certificate onto the path_so_far
776 path_so_far.push_back(*last);
777 certs_seen.emplace(fprint);
778
779 // push a deletion marker on the stack for backtracking later
780 stack.push_back({std::optional<X509_Certificate>(), false});
781
782 for(const auto& trusted_cert : trusted_issuers) {
783 stack.push_back({trusted_cert, true});
784 }
785
786 for(const auto& misc : misc_issuers) {
787 stack.push_back({misc, false});
788 }
789 }
790 }
791
792 // could not construct any potentially valid path
793 if(cert_paths_out.empty()) {
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 {
802 }
803}

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

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

◆ build_certificate_path()

Certificate_Status_Code Botan::PKIX::build_certificate_path ( std::vector< X509_Certificate > & cert_path_out,
const std::vector< Certificate_Store * > & trusted_certstores,
const X509_Certificate & end_entity,
const std::vector< X509_Certificate > & end_entity_extra )

Same as build_all_certificate_paths but only outputs a single path. If there are paths ending in self-signed certificates, these are prioritized over paths ending in intermediate or leaf certificates of the certificate store.

WARNING: The validity (e.g. signatures or constraints) of the output path IS NOT checked.

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 629 of file x509path.cpp.

632 {
633 std::vector<std::vector<X509_Certificate>> all_cert_paths;
634 const auto build_all_paths_res =
635 build_all_certificate_paths(all_cert_paths, trusted_certstores, end_entity, end_entity_extra);
636
637 if(build_all_paths_res != Certificate_Status_Code::OK) {
638 return build_all_paths_res;
639 }
640 BOTAN_ASSERT_NOMSG(!all_cert_paths.empty());
641
642 // Paths ending in self-signed certificates are preferred.
643 const auto first_with_self_signed_anchor = std::ranges::find_if(all_cert_paths, [&](const auto& left_path) {
644 BOTAN_ASSERT_NOMSG(!left_path.empty());
645 return left_path.back().is_self_signed();
646 });
647 if(first_with_self_signed_anchor != all_cert_paths.end()) {
648 cert_path.insert(cert_path.end(), first_with_self_signed_anchor->begin(), first_with_self_signed_anchor->end());
649 } else {
650 cert_path.insert(cert_path.end(), all_cert_paths.front().begin(), all_cert_paths.front().end());
651 }
653}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
Certificate_Status_Code build_all_certificate_paths(std::vector< std::vector< X509_Certificate > > &cert_paths, const std::vector< Certificate_Store * > &trusted_certstores, const X509_Certificate &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
Definition x509path.cpp:680

References BOTAN_ASSERT_NOMSG, build_all_certificate_paths(), and Botan::OK.

◆ check_chain()

CertificatePathStatusCodes Botan::PKIX::check_chain ( const std::vector< X509_Certificate > & cert_path,
std::chrono::system_clock::time_point ref_time,
std::string_view hostname,
Usage_Type usage,
const Path_Validation_Restrictions & restrictions )

Check the certificate chain, but not any revocation data

Parameters
cert_pathpath built by build_certificate_path with OK result. The first element is the end entity certificate, the last element is the trusted root certificate.
ref_timewhatever time you want to perform the validation against (normally current system clock)
hostnamethe hostname
usageend entity usage checks
restrictionsthe relevant path validation restrictions object
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 33 of file x509path.cpp.

37 {
38 if(cert_path.empty()) {
39 throw Invalid_Argument("PKIX::check_chain cert_path empty");
40 }
41
42 const bool is_end_entity_trust_anchor = (cert_path.size() == 1);
43
44 const X509_Time validation_time(ref_time);
45
46 CertificatePathStatusCodes cert_status(cert_path.size());
47
48 // Before anything else verify the entire chain of signatures
49 for(size_t i = 0; i != cert_path.size(); ++i) {
50 std::set<Certificate_Status_Code>& status = cert_status.at(i);
51
52 const bool at_trust_anchor = (i == cert_path.size() - 1);
53
54 const X509_Certificate& subject = cert_path[i];
55
56 // If using intermediate CAs as trust anchors, the signature of the trust
57 // anchor cannot be verified since the issuer is not part of the
58 // certificate chain
59 if(!restrictions.require_self_signed_trust_anchors() && at_trust_anchor && !subject.is_self_signed()) {
60 continue;
61 }
62
63 const X509_Certificate& issuer = cert_path[at_trust_anchor ? (i) : (i + 1)];
64
65 // Check the signature algorithm is known
66 if(!subject.signature_algorithm().oid().registered_oid()) {
68 } else {
69 std::unique_ptr<Public_Key> issuer_key;
70 try {
71 issuer_key = issuer.subject_public_key();
72 } catch(...) {
74 }
75
76 if(issuer_key) {
77 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
79 }
80
81 const auto sig_status = subject.verify_signature(*issuer_key);
82
83 if(sig_status.first != Certificate_Status_Code::VERIFIED) {
84 status.insert(sig_status.first);
85 } else {
86 // Signature is valid, check if hash used was acceptable
87 const std::string hash_used_for_signature = sig_status.second;
88 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
89 const auto& trusted_hashes = restrictions.trusted_hashes();
90
91 // Ignore untrusted hashes on self-signed roots
92 if(!trusted_hashes.empty() && !at_trust_anchor) {
93 if(!trusted_hashes.contains(hash_used_for_signature)) {
95 }
96 }
97 }
98 }
99 }
100 }
101
102 // If any of the signatures were invalid, return immediately; we know the
103 // chain is invalid and signature failure is always considered the most
104 // critical result. This does mean other problems in the certificate (eg
105 // expired) will not be reported, but we'd have to assume any such data is
106 // anyway arbitrary considering we couldn't verify the signature chain
107
108 for(size_t i = 0; i != cert_path.size(); ++i) {
109 for(auto status : cert_status.at(i)) {
110 // This ignores errors relating to the key or hash being weak since
111 // these are somewhat advisory
112 if(static_cast<uint32_t>(status) >= 5000) {
113 return cert_status;
114 }
115 }
116 }
117
118 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
119 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
120 }
121
122 if(!cert_path[0].allowed_usage(usage)) {
123 if(usage == Usage_Type::OCSP_RESPONDER) {
125 }
126 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
127 }
128
129 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
130 /*
131 "If the keyCertSign bit is asserted, then the cA bit in the
132 basic constraints extension (Section 4.2.1.9) MUST also be
133 asserted." - RFC 5280
134
135 We don't bother doing this check on the rest of the path since they
136 must have the cA bit asserted or the validation will fail anyway.
137 */
138 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
139 }
140
141 for(size_t i = 0; i != cert_path.size(); ++i) {
142 std::set<Certificate_Status_Code>& status = cert_status.at(i);
143
144 const bool at_trust_anchor = (i == cert_path.size() - 1);
145
146 const X509_Certificate& subject = cert_path[i];
147 const auto issuer = [&]() -> std::optional<X509_Certificate> {
148 if(!at_trust_anchor) {
149 return cert_path[i + 1];
150 } else if(subject.is_self_signed()) {
151 return cert_path[i];
152 } else {
153 return {}; // Non self-signed trust anchors have no checkable issuers.
154 }
155 }();
156
157 if(restrictions.require_self_signed_trust_anchors() && !issuer.has_value()) {
159 }
160
161 // This should never happen; it indicates a bug in path building
162 if(issuer.has_value() && subject.issuer_dn() != issuer->subject_dn()) {
164 }
165
166 // Check the serial number
167 if(subject.is_serial_negative()) {
169 }
170
171 // Check the subject's DN components' length
172
173 for(const auto& dn_pair : subject.subject_dn().dn_info()) {
174 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
175 // dn_pair = <OID,str>
176 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
178 }
179 }
180
181 // If so configured, allow trust anchors outside the validity period with
182 // a warning rather than a hard error
183 const bool enforce_validity_period = !at_trust_anchor || !restrictions.ignore_trusted_root_time_range();
184 // Check all certs for valid time range
185 if(validation_time < subject.not_before()) {
186 if(enforce_validity_period) {
188 } else {
189 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
190 }
191 }
192
193 if(validation_time > subject.not_after()) {
194 if(enforce_validity_period) {
196 } else {
197 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
198 }
199 }
200
201 // Check issuer constraints
202 if(issuer.has_value() && !issuer->is_CA_cert() && !is_end_entity_trust_anchor) {
204 }
205
206 // Check cert extensions
207
208 if(subject.x509_version() == 1) {
209 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
211 }
212 }
213
214 const Extensions& extensions = subject.v3_extensions();
215 const auto& extensions_vec = extensions.extensions();
216 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
218 }
219
220 for(const auto& extension : extensions_vec) {
221 extension.first->validate(subject, issuer, cert_path, cert_status, i);
222 }
223
224 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
226 }
227 }
228
229 // path len check
230 size_t max_path_length = cert_path.size();
231 for(size_t i = cert_path.size() - 1; i > 0; --i) {
232 std::set<Certificate_Status_Code>& status = cert_status.at(i);
233 const X509_Certificate& subject = cert_path[i];
234
235 /*
236 * If the certificate was not self-issued, verify that max_path_length is
237 * greater than zero and decrement max_path_length by 1.
238 */
239 if(subject.subject_dn() != subject.issuer_dn()) {
240 if(max_path_length > 0) {
241 max_path_length -= 1;
242 } else {
244 }
245 }
246
247 /*
248 * If pathLenConstraint is present in the certificate and is less than max_path_length,
249 * set max_path_length to the value of pathLenConstraint.
250 */
251 if(auto path_len_constraint = subject.path_length_constraint()) {
252 max_path_length = std::min(max_path_length, *path_len_constraint);
253 }
254 }
255
256 return cert_status;
257}
const OID & oid() const
Definition asn1_obj.h:407
bool registered_oid() const
Definition asn1_oid.cpp:151
const std::set< std::string > & trusted_hashes() const
Definition x509path.h:119
bool ignore_trusted_root_time_range() const
Definition x509path.h:150
bool require_self_signed_trust_anchors() const
Definition x509path.h:159
bool is_self_signed() const
Definition x509cert.cpp:350
static size_t lookup_ub(const OID &oid)
const AlgorithmIdentifier & signature_algorithm() const
Definition x509_obj.h:51
std::pair< Certificate_Status_Code, std::string > verify_signature(const Public_Key &key) const
Definition x509_obj.cpp:102
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:28
ASN1_Time X509_Time
Definition asn1_obj.h:23

References BOTAN_ASSERT_NOMSG, 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::X509_DN::dn_info(), Botan::DN_TOO_LONG, Botan::DUPLICATE_CERT_EXTENSION, Botan::EXT_IN_V1_V2_CERT, Botan::Extensions::extensions(), Botan::Extensions::get_extension_oids(), Botan::Path_Validation_Restrictions::ignore_trusted_root_time_range(), Botan::INVALID_USAGE, Botan::X509_Certificate::is_self_signed(), Botan::X509_Certificate::is_serial_negative(), Botan::X509_Certificate::issuer_dn(), Botan::Key_Constraints::KeyCertSign, Botan::X509_DN::lookup_ub(), Botan::Path_Validation_Restrictions::minimum_key_strength(), Botan::X509_Certificate::not_after(), Botan::X509_Certificate::not_before(), Botan::OCSP_RESPONDER, Botan::OCSP_RESPONSE_MISSING_KEYUSAGE, Botan::AlgorithmIdentifier::oid(), Botan::X509_Certificate::path_length_constraint(), Botan::OID::registered_oid(), Botan::Path_Validation_Restrictions::require_self_signed_trust_anchors(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::X509_Object::signature_algorithm(), Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::X509_Certificate::subject_dn(), Botan::X509_Certificate::subject_public_key(), Botan::TRUSTED_CERT_HAS_EXPIRED, Botan::TRUSTED_CERT_NOT_YET_VALID, Botan::Path_Validation_Restrictions::trusted_hashes(), Botan::UNTRUSTED_HASH, Botan::V2_IDENTIFIERS_IN_V1_CERT, Botan::X509_Certificate::v2_issuer_key_id(), Botan::X509_Certificate::v2_subject_key_id(), Botan::X509_Certificate::v3_extensions(), Botan::VERIFIED, Botan::X509_Object::verify_signature(), and Botan::X509_Certificate::x509_version().

Referenced by Botan::x509_path_validate().

◆ check_crl() [1/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< 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 461 of file x509path.cpp.

463 {
464 if(cert_path.empty()) {
465 throw Invalid_Argument("PKIX::check_crl cert_path empty");
466 }
467
468 if(certstores.empty()) {
469 throw Invalid_Argument("PKIX::check_crl certstores empty");
470 }
471
472 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
473
474 for(size_t i = 0; i != cert_path.size(); ++i) {
475 for(auto* certstore : certstores) {
476 crls[i] = certstore->find_crl_for(cert_path[i]);
477 if(crls[i]) {
478 break;
479 }
480 }
481 }
482
483 return PKIX::check_crl(cert_path, crls, ref_time);
484}
CertificatePathStatusCodes check_crl(const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
Definition x509path.cpp:390

References check_crl().

◆ check_crl() [2/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< X509_Certificate > & cert_path,
const std::vector< std::optional< 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 390 of file x509path.cpp.

392 {
393 if(cert_path.empty()) {
394 throw Invalid_Argument("PKIX::check_crl cert_path empty");
395 }
396
397 CertificatePathStatusCodes cert_status(cert_path.size());
398 const X509_Time validation_time(ref_time);
399
400 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
401 std::set<Certificate_Status_Code>& status = cert_status.at(i);
402
403 if(i < crls.size() && crls[i].has_value()) {
404 const X509_Certificate& subject = cert_path.at(i);
405 const X509_Certificate& ca = cert_path.at(i + 1);
406
409 }
410
411 if(validation_time < crls[i]->this_update()) {
413 }
414
415 if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
417 }
418
419 auto ca_key = ca.subject_public_key();
420 if(crls[i]->check_signature(*ca_key) == false) {
422 }
423
425
426 if(crls[i]->is_revoked(subject)) {
428 }
429
430 const auto dp = subject.crl_distribution_points();
431 if(!dp.empty()) {
432 const auto crl_idp = crls[i]->crl_issuing_distribution_point();
433
434 if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
436 }
437 }
438
439 for(const auto& extension : crls[i]->extensions().extensions()) {
440 // XXX this is wrong - the OID might be defined but the extension not full parsed
441 // for example see #1652
442
443 // is the extension critical and unknown?
444 if(extension.second && !extension.first->oid_of().registered_oid()) {
445 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
446 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
447 */
449 }
450 }
451 }
452 }
453
454 while(!cert_status.empty() && cert_status.back().empty()) {
455 cert_status.pop_back();
456 }
457
458 return cert_status;
459}
std::vector< std::string > crl_distribution_points() const
Definition x509cert.cpp:559
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:471
std::unique_ptr< Public_Key > subject_public_key() const
Definition x509cert.cpp:622

References Botan::X509_Certificate::allowed_usage(), Botan::CA_CERT_NOT_FOR_CRL_ISSUER, Botan::CERT_IS_REVOKED, Botan::CRL_BAD_SIGNATURE, Botan::X509_Certificate::crl_distribution_points(), Botan::CRL_HAS_EXPIRED, Botan::CRL_NOT_YET_VALID, Botan::Key_Constraints::CrlSign, Botan::NO_MATCHING_CRLDP, Botan::X509_Certificate::subject_public_key(), and Botan::VALID_CRL_CHECKED.

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

◆ check_ocsp()

CertificatePathStatusCodes Botan::PKIX::check_ocsp ( const std::vector< X509_Certificate > & cert_path,
const std::vector< std::optional< OCSP::Response > > & ocsp_responses,
const std::vector< Certificate_Store * > & certstores,
std::chrono::system_clock::time_point ref_time,
const Path_Validation_Restrictions & restrictions )

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)
restrictionsthe relevant path validation restrictions object
Returns
revocation status

Definition at line 357 of file x509path.cpp.

361 {
362 if(cert_path.empty()) {
363 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
364 }
365
366 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
367
368 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
369 const X509_Certificate& subject = cert_path.at(i);
370 const X509_Certificate& ca = cert_path.at(i + 1);
371
372 if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
373 ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) {
374 try {
375 cert_status.at(i) = evaluate_ocsp_response(
376 ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
377 } catch(Exception&) {
378 cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
379 }
380 }
381 }
382
383 while(!cert_status.empty() && cert_status.back().empty()) {
384 cert_status.pop_back();
385 }
386
387 return cert_status;
388}

References Botan::OCSP_RESPONSE_INVALID, 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,
const Path_Validation_Restrictions & restrictions )

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
restrictionsthe relevant path validation restrictions object

Definition at line 805 of file x509path.cpp.

808 {
809 if(chain_status.empty()) {
810 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
811 }
812
813 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
814 bool had_crl = false;
815 bool had_ocsp = false;
816
817 if(i < crl_status.size() && !crl_status[i].empty()) {
818 for(auto&& code : crl_status[i]) {
820 had_crl = true;
821 }
822 chain_status[i].insert(code);
823 }
824 }
825
826 if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
827 for(auto&& code : ocsp_status[i]) {
828 // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
832 had_ocsp = true;
833 }
834
835 chain_status[i].insert(code);
836 }
837 }
838
839 if(had_crl == false && had_ocsp == false) {
840 if((restrictions.require_revocation_information() && i == 0) ||
841 (restrictions.ocsp_all_intermediates() && i > 0)) {
842 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
843 }
844 }
845 }
846}
bool require_revocation_information() const
Definition x509path.h:108

References Botan::NO_REVOCATION_DATA, Botan::Path_Validation_Restrictions::ocsp_all_intermediates(), Botan::OCSP_NO_REVOCATION_URL, Botan::OCSP_RESPONSE_GOOD, Botan::OCSP_SERVER_NOT_AVAILABLE, Botan::Path_Validation_Restrictions::require_revocation_information(), 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_statusresult of merge_revocation_status or check_chain

Definition at line 848 of file x509path.cpp.

848 {
849 if(cert_status.empty()) {
850 throw Invalid_Argument("PKIX::overall_status empty cert status");
851 }
852
854
855 // take the "worst" error as overall
856 for(const std::set<Certificate_Status_Code>& s : cert_status) {
857 if(!s.empty()) {
858 auto worst = *s.rbegin();
859 // Leave informative OCSP/CRL confirmations on cert-level status only
861 overall_status = worst;
862 }
863 }
864 }
865 return overall_status;
866}
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition x509path.cpp:848
Certificate_Status_Code
Definition pkix_enums.h:20

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

Referenced by overall_status().