Botan 3.9.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 std::optional< 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 std::optional< X509_Certificate > & end_entity,
const std::vector< 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 709 of file x509path.cpp.

712 {
713 if(!cert_paths_out.empty()) {
714 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
715 }
716
717 if(end_entity->is_self_signed()) {
719 }
720
721 /*
722 * Pile up error messages
723 */
724 std::vector<Certificate_Status_Code> stats;
725
726 Certificate_Store_In_Memory ee_extras;
727 for(const auto& cert : end_entity_extra) {
728 ee_extras.add_certificate(cert);
729 }
730
731 /*
732 * This is an inelegant but functional way of preventing path loops
733 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
734 * fingerprints in the path. If there is a duplicate, we error out.
735 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
736 */
737 std::set<std::string> certs_seen;
738
739 // new certs are added and removed from the path during the DFS
740 // it is copied into cert_paths_out when we encounter a trusted root
741 std::vector<X509_Certificate> path_so_far;
742
743 // todo can we assume that the end certificate is not trusted?
744 std::vector<cert_maybe_trusted> stack = {{end_entity, false}};
745
746 while(!stack.empty()) {
747 std::optional<X509_Certificate> last = stack.back().first;
748 // found a deletion marker that guides the DFS, backtracing
749 if(last == std::nullopt) {
750 stack.pop_back();
751 std::string fprint = path_so_far.back().fingerprint("SHA-256");
752 certs_seen.erase(fprint);
753 path_so_far.pop_back();
754 }
755 // process next cert on the path
756 else {
757 const bool trusted = stack.back().second;
758 stack.pop_back();
759
760 // certificate already seen?
761 const std::string fprint = last->fingerprint("SHA-256");
762 if(certs_seen.count(fprint) == 1) {
764 // the current path ended in a loop
765 continue;
766 }
767
768 // the current path ends here
769 if(last->is_self_signed()) {
770 // found a trust anchor
771 if(trusted) {
772 cert_paths_out.push_back(path_so_far);
773 cert_paths_out.back().push_back(*last);
774
775 continue;
776 }
777 // found an untrustworthy root
778 else {
780 continue;
781 }
782 }
783
784 const X509_DN issuer_dn = last->issuer_dn();
785 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
786
787 // search for trusted issuers
788 std::vector<X509_Certificate> trusted_issuers;
789 for(Certificate_Store* store : trusted_certstores) {
790 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
791 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
792 }
793
794 // search the supplemental certs
795 std::vector<X509_Certificate> misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id);
796
797 // if we could not find any issuers, the current path ends here
798 if(trusted_issuers.empty() && misc_issuers.empty()) {
800 continue;
801 }
802
803 // push the latest certificate onto the path_so_far
804 path_so_far.push_back(*last);
805 certs_seen.emplace(fprint);
806
807 // push a deletion marker on the stack for backtracing later
808 stack.push_back({std::optional<X509_Certificate>(), false});
809
810 for(const auto& trusted_cert : trusted_issuers) {
811 stack.push_back({trusted_cert, true});
812 }
813
814 for(const auto& misc : misc_issuers) {
815 stack.push_back({misc, false});
816 }
817 }
818 }
819
820 // could not construct any potentially valid path
821 if(cert_paths_out.empty()) {
822 if(stats.empty()) {
823 throw Internal_Error("X509 path building failed for unknown reasons");
824 } else {
825 // arbitrarily return the first error
826 return stats[0];
827 }
828 } else {
830 }
831}

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

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

617 {
618 if(end_entity.is_self_signed()) {
620 }
621
622 /*
623 * This is an inelegant but functional way of preventing path loops
624 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
625 * fingerprints in the path. If there is a duplicate, we error out.
626 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
627 */
628 std::set<std::string> certs_seen;
629
630 cert_path.push_back(end_entity);
631 certs_seen.insert(end_entity.fingerprint("SHA-256"));
632
634 for(const auto& cert : end_entity_extra) {
635 ee_extras.add_certificate(cert);
636 }
637
638 // iterate until we reach a root or cannot find the issuer
639 for(;;) {
640 const X509_Certificate& last = cert_path.back();
641 const X509_DN issuer_dn = last.issuer_dn();
642 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
643
644 std::optional<X509_Certificate> issuer;
645 bool trusted_issuer = false;
646
647 for(Certificate_Store* store : trusted_certstores) {
648 issuer = store->find_cert(issuer_dn, auth_key_id);
649 if(issuer) {
650 trusted_issuer = true;
651 break;
652 }
653 }
654
655 if(!issuer) {
656 // fall back to searching supplemental certs
657 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
658 }
659
660 if(!issuer) {
662 }
663
664 const std::string fprint = issuer->fingerprint("SHA-256");
665
666 if(certs_seen.contains(fprint)) {
667 // we already saw this certificate -> loop
669 }
670
671 certs_seen.insert(fprint);
672 cert_path.push_back(*issuer);
673
674 if(issuer->is_self_signed()) {
675 if(trusted_issuer) {
677 } else {
679 }
680 }
681 }
682}
std::optional< X509_Certificate > find_cert(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition certstor.cpp:65
void add_certificate(const X509_Certificate &cert)
Definition certstor.cpp:46
std::string fingerprint(std::string_view hash_name="SHA-1") const
Definition x509cert.cpp:635
bool is_self_signed() const
Definition x509cert.cpp:347

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::fingerprint(), Botan::X509_Certificate::is_self_signed(), Botan::X509_Certificate::issuer_dn(), 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 36 of file x509path.cpp.

40 {
41 if(cert_path.empty()) {
42 throw Invalid_Argument("PKIX::check_chain cert_path empty");
43 }
44
45 const bool self_signed_ee_cert = (cert_path.size() == 1);
46
47 X509_Time validation_time(ref_time);
48
49 CertificatePathStatusCodes cert_status(cert_path.size());
50
51 // Before anything else verify the entire chain of signatures
52 for(size_t i = 0; i != cert_path.size(); ++i) {
53 std::set<Certificate_Status_Code>& status = cert_status.at(i);
54
55 const bool at_self_signed_root = (i == cert_path.size() - 1);
56
57 const X509_Certificate& subject = cert_path[i];
58 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
59
60 // Check the signature algorithm is known
61 if(!subject.signature_algorithm().oid().registered_oid()) {
63 } else {
64 std::unique_ptr<Public_Key> issuer_key;
65 try {
66 issuer_key = issuer.subject_public_key();
67 } catch(...) {
69 }
70
71 if(issuer_key) {
72 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
74 }
75
76 const auto sig_status = subject.verify_signature(*issuer_key);
77
78 if(sig_status.first != Certificate_Status_Code::VERIFIED) {
79 status.insert(sig_status.first);
80 } else {
81 // Signature is valid, check if hash used was acceptable
82 const std::string hash_used_for_signature = sig_status.second;
83 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
84 const auto& trusted_hashes = restrictions.trusted_hashes();
85
86 // Ignore untrusted hashes on self-signed roots
87 if(!trusted_hashes.empty() && !at_self_signed_root) {
88 if(!trusted_hashes.contains(hash_used_for_signature)) {
90 }
91 }
92 }
93 }
94 }
95 }
96
97 // If any of the signatures were invalid, return immediately; we know the
98 // chain is invalid and signature failure is always considered the most
99 // critical result. This does mean other problems in the certificate (eg
100 // expired) will not be reported, but we'd have to assume any such data is
101 // anyway arbitrary considering we couldn't verify the signature chain
102
103 for(size_t i = 0; i != cert_path.size(); ++i) {
104 for(auto status : cert_status.at(i)) {
105 // This ignores errors relating to the key or hash being weak since
106 // these are somewhat advisory
107 if(static_cast<uint32_t>(status) >= 5000) {
108 return cert_status;
109 }
110 }
111 }
112
113 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
114 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
115 }
116
117 if(!cert_path[0].allowed_usage(usage)) {
118 if(usage == Usage_Type::OCSP_RESPONDER) {
120 }
121 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
122 }
123
124 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
125 /*
126 "If the keyCertSign bit is asserted, then the cA bit in the
127 basic constraints extension (Section 4.2.1.9) MUST also be
128 asserted." - RFC 5280
129
130 We don't bother doing this check on the rest of the path since they
131 must have the cA bit asserted or the validation will fail anyway.
132 */
133 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
134 }
135
136 for(size_t i = 0; i != cert_path.size(); ++i) {
137 std::set<Certificate_Status_Code>& status = cert_status.at(i);
138
139 const bool at_self_signed_root = (i == cert_path.size() - 1);
140
141 const X509_Certificate& subject = cert_path[i];
142 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
143
144 if(at_self_signed_root && (issuer.is_self_signed() == false)) {
146 }
147
148 // This should never happen; it indicates a bug in path building
149 if(subject.issuer_dn() != issuer.subject_dn()) {
151 }
152
153 // Check the serial number
154 if(subject.is_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 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
162 // dn_pair = <OID,str>
163 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
165 }
166 }
167
168 // Only warn, if trusted root is not in time range if configured this way
169 const bool is_trusted_root_and_time_ignored =
170 restrictions.ignore_trusted_root_time_range() && at_self_signed_root;
171 // Check all certs for valid time range
172 if(validation_time < subject.not_before()) {
173 if(is_trusted_root_and_time_ignored) {
174 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
175 } else {
177 }
178 }
179
180 if(validation_time > subject.not_after()) {
181 if(is_trusted_root_and_time_ignored) {
182 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
183 } else {
185 }
186 }
187
188 // Check issuer constraints
189 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
191 }
192
193 // Check cert extensions
194
195 if(subject.x509_version() == 1) {
196 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
198 }
199 }
200
201 const Extensions& extensions = subject.v3_extensions();
202 const auto& extensions_vec = extensions.extensions();
203 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
205 }
206 for(const auto& extension : extensions_vec) {
207 extension.first->validate(subject, issuer, cert_path, cert_status, i);
208 }
209 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
211 }
212 }
213
214 // path len check
215 size_t max_path_length = cert_path.size();
216 for(size_t i = cert_path.size() - 1; i > 0; --i) {
217 std::set<Certificate_Status_Code>& status = cert_status.at(i);
218 const X509_Certificate& subject = cert_path[i];
219
220 /*
221 * If the certificate was not self-issued, verify that max_path_length is
222 * greater than zero and decrement max_path_length by 1.
223 */
224 if(subject.subject_dn() != subject.issuer_dn()) {
225 if(max_path_length > 0) {
226 max_path_length -= 1;
227 } else {
229 }
230 }
231
232 /*
233 * If pathLenConstraint is present in the certificate and is less than max_path_length,
234 * set max_path_length to the value of pathLenConstraint.
235 */
236 if(auto path_len_constraint = subject.path_length_constraint()) {
237 max_path_length = std::min(max_path_length, *path_len_constraint);
238 }
239 }
240
241 return cert_status;
242}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
const OID & oid() const
Definition asn1_obj.h:479
bool registered_oid() const
Definition asn1_oid.cpp:151
const std::set< std::string > & trusted_hashes() const
Definition x509path.h:111
bool ignore_trusted_root_time_range() const
Definition x509path.h:142
std::unique_ptr< Public_Key > subject_public_key() const
Definition x509cert.cpp:609
static size_t lookup_ub(const OID &oid)
const AlgorithmIdentifier & signature_algorithm() const
Definition x509_obj.h:50
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:29
ASN1_Time X509_Time
Definition asn1_obj.h:424

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_CA_cert(), 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::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 446 of file x509path.cpp.

448 {
449 if(cert_path.empty()) {
450 throw Invalid_Argument("PKIX::check_crl cert_path empty");
451 }
452
453 if(certstores.empty()) {
454 throw Invalid_Argument("PKIX::check_crl certstores empty");
455 }
456
457 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
458
459 for(size_t i = 0; i != cert_path.size(); ++i) {
460 for(auto* certstore : certstores) {
461 crls[i] = certstore->find_crl_for(cert_path[i]);
462 if(crls[i]) {
463 break;
464 }
465 }
466 }
467
468 return PKIX::check_crl(cert_path, crls, ref_time);
469}
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:375

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

377 {
378 if(cert_path.empty()) {
379 throw Invalid_Argument("PKIX::check_crl cert_path empty");
380 }
381
382 CertificatePathStatusCodes cert_status(cert_path.size());
383 const X509_Time validation_time(ref_time);
384
385 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
386 std::set<Certificate_Status_Code>& status = cert_status.at(i);
387
388 if(i < crls.size() && crls[i].has_value()) {
389 const X509_Certificate& subject = cert_path.at(i);
390 const X509_Certificate& ca = cert_path.at(i + 1);
391
394 }
395
396 if(validation_time < crls[i]->this_update()) {
398 }
399
400 if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
402 }
403
404 auto ca_key = ca.subject_public_key();
405 if(crls[i]->check_signature(*ca_key) == false) {
407 }
408
410
411 if(crls[i]->is_revoked(subject)) {
413 }
414
415 const auto dp = subject.crl_distribution_points();
416 if(!dp.empty()) {
417 const auto crl_idp = crls[i]->crl_issuing_distribution_point();
418
419 if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
421 }
422 }
423
424 for(const auto& extension : crls[i]->extensions().extensions()) {
425 // XXX this is wrong - the OID might be defined but the extention not full parsed
426 // for example see #1652
427
428 // is the extension critical and unknown?
429 if(extension.second && !extension.first->oid_of().registered_oid()) {
430 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
431 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
432 */
434 }
435 }
436 }
437 }
438
439 while(!cert_status.empty() && cert_status.back().empty()) {
440 cert_status.pop_back();
441 }
442
443 return cert_status;
444}
std::vector< std::string > crl_distribution_points() const
Definition x509cert.cpp:546
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:468

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

346 {
347 if(cert_path.empty()) {
348 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
349 }
350
351 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
352
353 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
354 const X509_Certificate& subject = cert_path.at(i);
355 const X509_Certificate& ca = cert_path.at(i + 1);
356
357 if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
358 ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) {
359 try {
360 cert_status.at(i) = evaluate_ocsp_response(
361 ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
362 } catch(Exception&) {
363 cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
364 }
365 }
366 }
367
368 while(!cert_status.empty() && cert_status.back().empty()) {
369 cert_status.pop_back();
370 }
371
372 return cert_status;
373}

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

836 {
837 if(chain_status.empty()) {
838 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
839 }
840
841 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
842 bool had_crl = false;
843 bool had_ocsp = false;
844
845 if(i < crl_status.size() && !crl_status[i].empty()) {
846 for(auto&& code : crl_status[i]) {
848 had_crl = true;
849 }
850 chain_status[i].insert(code);
851 }
852 }
853
854 if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
855 for(auto&& code : ocsp_status[i]) {
856 // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
860 had_ocsp = true;
861 }
862
863 chain_status[i].insert(code);
864 }
865 }
866
867 if(had_crl == false && had_ocsp == false) {
868 if((restrictions.require_revocation_information() && i == 0) ||
869 (restrictions.ocsp_all_intermediates() && i > 0)) {
870 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
871 }
872 }
873 }
874}
bool require_revocation_information() const
Definition x509path.h:100

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

876 {
877 if(cert_status.empty()) {
878 throw Invalid_Argument("PKIX::overall_status empty cert status");
879 }
880
882
883 // take the "worst" error as overall
884 for(const std::set<Certificate_Status_Code>& s : cert_status) {
885 if(!s.empty()) {
886 auto worst = *s.rbegin();
887 // Leave informative OCSP/CRL confirmations on cert-level status only
889 overall_status = worst;
890 }
891 }
892 }
893 return overall_status;
894}
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition x509path.cpp:876
Certificate_Status_Code
Definition pkix_enums.h:20

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

Referenced by overall_status().