Botan 3.10.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 692 of file x509path.cpp.

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

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

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

36 {
37 if(cert_path.empty()) {
38 throw Invalid_Argument("PKIX::check_chain cert_path empty");
39 }
40
41 const bool self_signed_ee_cert = (cert_path.size() == 1);
42
43 X509_Time validation_time(ref_time);
44
45 CertificatePathStatusCodes cert_status(cert_path.size());
46
47 // Before anything else verify the entire chain of signatures
48 for(size_t i = 0; i != cert_path.size(); ++i) {
49 std::set<Certificate_Status_Code>& status = cert_status.at(i);
50
51 const bool at_self_signed_root = (i == cert_path.size() - 1);
52
53 const X509_Certificate& subject = cert_path[i];
54 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
55
56 // Check the signature algorithm is known
57 if(!subject.signature_algorithm().oid().registered_oid()) {
59 } else {
60 std::unique_ptr<Public_Key> issuer_key;
61 try {
62 issuer_key = issuer.subject_public_key();
63 } catch(...) {
65 }
66
67 if(issuer_key) {
68 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
70 }
71
72 const auto sig_status = subject.verify_signature(*issuer_key);
73
74 if(sig_status.first != Certificate_Status_Code::VERIFIED) {
75 status.insert(sig_status.first);
76 } else {
77 // Signature is valid, check if hash used was acceptable
78 const std::string hash_used_for_signature = sig_status.second;
79 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
80 const auto& trusted_hashes = restrictions.trusted_hashes();
81
82 // Ignore untrusted hashes on self-signed roots
83 if(!trusted_hashes.empty() && !at_self_signed_root) {
84 if(!trusted_hashes.contains(hash_used_for_signature)) {
86 }
87 }
88 }
89 }
90 }
91 }
92
93 // If any of the signatures were invalid, return immediately; we know the
94 // chain is invalid and signature failure is always considered the most
95 // critical result. This does mean other problems in the certificate (eg
96 // expired) will not be reported, but we'd have to assume any such data is
97 // anyway arbitrary considering we couldn't verify the signature chain
98
99 for(size_t i = 0; i != cert_path.size(); ++i) {
100 for(auto status : cert_status.at(i)) {
101 // This ignores errors relating to the key or hash being weak since
102 // these are somewhat advisory
103 if(static_cast<uint32_t>(status) >= 5000) {
104 return cert_status;
105 }
106 }
107 }
108
109 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
110 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
111 }
112
113 if(!cert_path[0].allowed_usage(usage)) {
114 if(usage == Usage_Type::OCSP_RESPONDER) {
116 }
117 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
118 }
119
120 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
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 std::set<Certificate_Status_Code>& status = cert_status.at(i);
134
135 const bool at_self_signed_root = (i == cert_path.size() - 1);
136
137 const X509_Certificate& subject = cert_path[i];
138 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
139
140 if(at_self_signed_root && (issuer.is_self_signed() == false)) {
142 }
143
144 // This should never happen; it indicates a bug in path building
145 if(subject.issuer_dn() != issuer.subject_dn()) {
147 }
148
149 // Check the serial number
150 if(subject.is_serial_negative()) {
152 }
153
154 // Check the subject's DN components' length
155
156 for(const auto& dn_pair : subject.subject_dn().dn_info()) {
157 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
158 // dn_pair = <OID,str>
159 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
161 }
162 }
163
164 // Only warn, if trusted root is not in time range if configured this way
165 const bool is_trusted_root_and_time_ignored =
166 restrictions.ignore_trusted_root_time_range() && at_self_signed_root;
167 // Check all certs for valid time range
168 if(validation_time < subject.not_before()) {
169 if(is_trusted_root_and_time_ignored) {
170 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
171 } else {
173 }
174 }
175
176 if(validation_time > subject.not_after()) {
177 if(is_trusted_root_and_time_ignored) {
178 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
179 } else {
181 }
182 }
183
184 // Check issuer constraints
185 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
187 }
188
189 // Check cert extensions
190
191 if(subject.x509_version() == 1) {
192 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
194 }
195 }
196
197 const Extensions& extensions = subject.v3_extensions();
198 const auto& extensions_vec = extensions.extensions();
199 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
201 }
202 for(const auto& extension : extensions_vec) {
203 extension.first->validate(subject, issuer, cert_path, cert_status, i);
204 }
205 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
207 }
208 }
209
210 // path len check
211 size_t max_path_length = cert_path.size();
212 for(size_t i = cert_path.size() - 1; i > 0; --i) {
213 std::set<Certificate_Status_Code>& status = cert_status.at(i);
214 const X509_Certificate& subject = cert_path[i];
215
216 /*
217 * If the certificate was not self-issued, verify that max_path_length is
218 * greater than zero and decrement max_path_length by 1.
219 */
220 if(subject.subject_dn() != subject.issuer_dn()) {
221 if(max_path_length > 0) {
222 max_path_length -= 1;
223 } else {
225 }
226 }
227
228 /*
229 * If pathLenConstraint is present in the certificate and is less than max_path_length,
230 * set max_path_length to the value of pathLenConstraint.
231 */
232 if(auto path_len_constraint = subject.path_length_constraint()) {
233 max_path_length = std::min(max_path_length, *path_len_constraint);
234 }
235 }
236
237 return cert_status;
238}
#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:101
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 429 of file x509path.cpp.

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

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

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

307 {
308 if(cert_path.empty()) {
309 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
310 }
311
312 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
313
314 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
315 std::set<Certificate_Status_Code>& status = cert_status.at(i);
316
317 const X509_Certificate& subject = cert_path.at(i);
318 const X509_Certificate& ca = cert_path.at(i + 1);
319
320 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
321 (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
322 try {
323 const auto& ocsp_response = ocsp_responses.at(i);
324
325 if(auto dummy_status = ocsp_response->dummy_status()) {
326 // handle softfail conditions
327 status.insert(dummy_status.value());
328 } else if(auto signing_cert =
329 ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
330 !signing_cert) {
332 } else if(auto ocsp_signing_cert_status =
333 verify_ocsp_signing_cert(signing_cert.value(),
334 ca,
335 concat(ocsp_response->certificates(), cert_path),
336 certstores,
337 ref_time,
338 restrictions);
339 ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
340 status.insert(ocsp_signing_cert_status);
342 } else {
343 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
344 }
345 } catch(Exception&) {
347 }
348 }
349 }
350
351 while(!cert_status.empty() && cert_status.back().empty()) {
352 cert_status.pop_back();
353 }
354
355 return cert_status;
356}
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:254

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

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

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

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

Referenced by overall_status().