Botan 3.7.1
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 696 of file x509path.cpp.

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

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

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

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(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;
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(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length) {
237 max_path_length = subject.path_limit();
238 }
239 }
240
241 return cert_status;
242}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
const OID & oid() const
Definition asn1_obj.h:472
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:603
static size_t lookup_ub(const OID &oid)
const AlgorithmIdentifier & signature_algorithm() const
Definition x509_obj.h:47
std::pair< Certificate_Status_Code, std::string > verify_signature(const Public_Key &key) const
Definition x509_obj.cpp:101
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:29
ASN1_Time X509_Time
Definition asn1_obj.h:417

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_limit(), 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 433 of file x509path.cpp.

435 {
436 if(cert_path.empty()) {
437 throw Invalid_Argument("PKIX::check_crl cert_path empty");
438 }
439
440 if(certstores.empty()) {
441 throw Invalid_Argument("PKIX::check_crl certstores empty");
442 }
443
444 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
445
446 for(size_t i = 0; i != cert_path.size(); ++i) {
447 for(auto certstore : certstores) {
448 crls[i] = certstore->find_crl_for(cert_path[i]);
449 if(crls[i]) {
450 break;
451 }
452 }
453 }
454
455 return PKIX::check_crl(cert_path, crls, ref_time);
456}
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:362

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

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

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

311 {
312 if(cert_path.empty()) {
313 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
314 }
315
316 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
317
318 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
319 std::set<Certificate_Status_Code>& status = cert_status.at(i);
320
321 const X509_Certificate& subject = cert_path.at(i);
322 const X509_Certificate& ca = cert_path.at(i + 1);
323
324 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
325 (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
326 try {
327 const auto& ocsp_response = ocsp_responses.at(i);
328
329 if(auto dummy_status = ocsp_response->dummy_status()) {
330 // handle softfail conditions
331 status.insert(dummy_status.value());
332 } else if(auto signing_cert =
333 ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
334 !signing_cert) {
336 } else if(auto ocsp_signing_cert_status =
337 verify_ocsp_signing_cert(signing_cert.value(),
338 ca,
339 concat(ocsp_response->certificates(), cert_path),
340 certstores,
341 ref_time,
342 restrictions);
343 ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
344 status.insert(ocsp_signing_cert_status);
346 } else {
347 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
348 }
349 } catch(Exception&) {
351 }
352 }
353 }
354
355 while(!cert_status.empty() && cert_status.back().empty()) {
356 cert_status.pop_back();
357 }
358
359 return cert_status;
360}
std::chrono::seconds max_ocsp_age() const
Definition x509path.h:122
const Certificate_Store * trusted_ocsp_responders() const
Definition x509path.h:129
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263

References Botan::concat(), Botan::FIRST_ERROR_STATUS, Botan::Path_Validation_Restrictions::max_ocsp_age(), Botan::OCSP_ISSUER_NOT_FOUND, Botan::OCSP_ISSUER_NOT_TRUSTED, Botan::OCSP_RESPONSE_INVALID, Botan::OCSP::Successful, and Botan::Path_Validation_Restrictions::trusted_ocsp_responders().

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

823 {
824 if(chain_status.empty()) {
825 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
826 }
827
828 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
829 bool had_crl = false, had_ocsp = false;
830
831 if(i < crl.size() && !crl[i].empty()) {
832 for(auto&& code : crl[i]) {
834 had_crl = true;
835 }
836 chain_status[i].insert(code);
837 }
838 }
839
840 if(i < ocsp.size() && !ocsp[i].empty()) {
841 for(auto&& code : ocsp[i]) {
842 // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
846 had_ocsp = true;
847 }
848
849 chain_status[i].insert(code);
850 }
851 }
852
853 if(had_crl == false && had_ocsp == false) {
854 if((restrictions.require_revocation_information() && i == 0) ||
855 (restrictions.ocsp_all_intermediates() && i > 0)) {
856 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
857 }
858 }
859 }
860}
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 862 of file x509path.cpp.

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

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

Referenced by overall_status().