Botan 3.4.0
Crypto and TLS for C&
Functions
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 664 of file x509path.cpp.

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

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

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

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 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
52 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
53 }
54
55 if(!cert_path[0].allowed_usage(usage)) {
56 if(usage == Usage_Type::OCSP_RESPONDER) {
57 cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
58 }
59 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
60 }
61
62 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
63 /*
64 "If the keyCertSign bit is asserted, then the cA bit in the
65 basic constraints extension (Section 4.2.1.9) MUST also be
66 asserted." - RFC 5280
67
68 We don't bother doing this check on the rest of the path since they
69 must have the cA bit asserted or the validation will fail anyway.
70 */
71 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
72 }
73
74 for(size_t i = 0; i != cert_path.size(); ++i) {
75 std::set<Certificate_Status_Code>& status = cert_status.at(i);
76
77 const bool at_self_signed_root = (i == cert_path.size() - 1);
78
79 const X509_Certificate& subject = cert_path[i];
80 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
81
82 if(at_self_signed_root && (issuer.is_self_signed() == false)) {
83 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
84 }
85
86 if(subject.issuer_dn() != issuer.subject_dn()) {
87 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
88 }
89
90 // Check the serial number
91 if(subject.is_serial_negative()) {
92 status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
93 }
94
95 // Check the subject's DN components' length
96
97 for(const auto& dn_pair : subject.subject_dn().dn_info()) {
98 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
99 // dn_pair = <OID,str>
100 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
101 status.insert(Certificate_Status_Code::DN_TOO_LONG);
102 }
103 }
104
105 // Only warn, if trusted root is not in time range if configured this way
106 const bool is_trusted_root_and_time_ignored =
107 restrictions.ignore_trusted_root_time_range() && at_self_signed_root;
108 // Check all certs for valid time range
109 if(validation_time < subject.not_before()) {
110 if(is_trusted_root_and_time_ignored) {
111 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
112 } else {
113 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
114 }
115 }
116
117 if(validation_time > subject.not_after()) {
118 if(is_trusted_root_and_time_ignored) {
119 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
120 } else {
121 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
122 }
123 }
124
125 // Check issuer constraints
126 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
127 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
128 }
129
130 auto issuer_key = issuer.subject_public_key();
131
132 // Check the signature algorithm is known
133 if(!subject.signature_algorithm().oid().registered_oid()) {
134 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
135 } else {
136 // only perform the following checks if the signature algorithm is known
137 if(!issuer_key) {
138 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
139 } else {
140 const auto sig_status = subject.verify_signature(*issuer_key);
141
142 if(sig_status.first == Certificate_Status_Code::VERIFIED) {
143 const std::string hash_used_for_signature = sig_status.second;
144 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
145 const auto& trusted_hashes = restrictions.trusted_hashes();
146
147 // Ignore untrusted hashes on self-signed roots
148 if(!trusted_hashes.empty() && !at_self_signed_root) {
149 if(!trusted_hashes.contains(hash_used_for_signature)) {
150 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
151 }
152 }
153 } else {
154 status.insert(sig_status.first);
155 }
156
157 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
158 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
159 }
160 }
161 }
162
163 // Check cert extensions
164
165 if(subject.x509_version() == 1) {
166 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
167 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
168 }
169 }
170
171 const Extensions& extensions = subject.v3_extensions();
172 const auto& extensions_vec = extensions.extensions();
173 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
174 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
175 }
176 for(auto& extension : extensions_vec) {
177 extension.first->validate(subject, issuer, cert_path, cert_status, i);
178 }
179 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
180 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
181 }
182 }
183
184 // path len check
185 size_t max_path_length = cert_path.size();
186 for(size_t i = cert_path.size() - 1; i > 0; --i) {
187 std::set<Certificate_Status_Code>& status = cert_status.at(i);
188 const X509_Certificate& subject = cert_path[i];
189
190 /*
191 * If the certificate was not self-issued, verify that max_path_length is
192 * greater than zero and decrement max_path_length by 1.
193 */
194 if(subject.subject_dn() != subject.issuer_dn()) {
195 if(max_path_length > 0) {
196 --max_path_length;
197 } else {
198 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
199 }
200 }
201
202 /*
203 * If pathLenConstraint is present in the certificate and is less than max_path_length,
204 * set max_path_length to the value of pathLenConstraint.
205 */
206 if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length) {
207 max_path_length = subject.path_limit();
208 }
209 }
210
211 return cert_status;
212}
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
const std::set< std::string > & trusted_hashes() const
Definition x509path.h:111
bool ignore_trusted_root_time_range() const
Definition x509path.h:142
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:29

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

403 {
404 if(cert_path.empty()) {
405 throw Invalid_Argument("PKIX::check_crl cert_path empty");
406 }
407
408 if(certstores.empty()) {
409 throw Invalid_Argument("PKIX::check_crl certstores empty");
410 }
411
412 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
413
414 for(size_t i = 0; i != cert_path.size(); ++i) {
415 for(auto certstore : certstores) {
416 crls[i] = certstore->find_crl_for(cert_path[i]);
417 if(crls[i]) {
418 break;
419 }
420 }
421 }
422
423 return PKIX::check_crl(cert_path, crls, ref_time);
424}

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

334 {
335 if(cert_path.empty()) {
336 throw Invalid_Argument("PKIX::check_crl cert_path empty");
337 }
338
339 CertificatePathStatusCodes cert_status(cert_path.size());
340 const X509_Time validation_time(ref_time);
341
342 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
343 std::set<Certificate_Status_Code>& status = cert_status.at(i);
344
345 if(i < crls.size() && crls[i].has_value()) {
346 const X509_Certificate& subject = cert_path.at(i);
347 const X509_Certificate& ca = cert_path.at(i + 1);
348
349 if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
350 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
351 }
352
353 if(validation_time < crls[i]->this_update()) {
354 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
355 }
356
357 if(validation_time > crls[i]->next_update()) {
358 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
359 }
360
361 auto ca_key = ca.subject_public_key();
362 if(crls[i]->check_signature(*ca_key) == false) {
363 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
364 }
365
366 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
367
368 if(crls[i]->is_revoked(subject)) {
369 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
370 }
371
372 std::string dp = subject.crl_distribution_point();
373 if(!dp.empty()) {
374 if(dp != crls[i]->crl_issuing_distribution_point()) {
375 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
376 }
377 }
378
379 for(const auto& extension : crls[i]->extensions().extensions()) {
380 // XXX this is wrong - the OID might be defined but the extention not full parsed
381 // for example see #1652
382
383 // is the extension critical and unknown?
384 if(extension.second && !extension.first->oid_of().registered_oid()) {
385 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
386 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
387 */
388 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
389 }
390 }
391 }
392 }
393
394 while(!cert_status.empty() && cert_status.back().empty()) {
395 cert_status.pop_back();
396 }
397
398 return cert_status;
399}
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:415
std::string crl_distribution_point() const
Definition x509cert.cpp:493
std::unique_ptr< Public_Key > subject_public_key() const
Definition x509cert.cpp:546

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

281 {
282 if(cert_path.empty()) {
283 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
284 }
285
286 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
287
288 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
289 std::set<Certificate_Status_Code>& status = cert_status.at(i);
290
291 const X509_Certificate& subject = cert_path.at(i);
292 const X509_Certificate& ca = cert_path.at(i + 1);
293
294 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
295 (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
296 try {
297 const auto& ocsp_response = ocsp_responses.at(i);
298
299 if(auto dummy_status = ocsp_response->dummy_status()) {
300 // handle softfail conditions
301 status.insert(dummy_status.value());
302 } else if(auto signing_cert =
303 ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
304 !signing_cert) {
305 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
306 } else if(auto ocsp_signing_cert_status =
307 verify_ocsp_signing_cert(signing_cert.value(),
308 ca,
309 concat(ocsp_response->certificates(), cert_path),
310 certstores,
311 ref_time,
312 restrictions);
313 ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
314 status.insert(ocsp_signing_cert_status);
315 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
316 } else {
317 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
318 }
319 } catch(Exception&) {
320 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
321 }
322 }
323 }
324
325 while(!cert_status.empty() && cert_status.back().empty()) {
326 cert_status.pop_back();
327 }
328
329 return cert_status;
330}
std::chrono::seconds max_ocsp_age() const
Definition x509path.h:122
const Certificate_Store * trusted_ocsp_responders() const
Definition x509path.h:129
decltype(auto) concat(Ts &&... buffers)
Definition stl_util.h:258

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

791 {
792 if(chain_status.empty()) {
793 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
794 }
795
796 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
797 bool had_crl = false, had_ocsp = false;
798
799 if(i < crl.size() && !crl[i].empty()) {
800 for(auto&& code : crl[i]) {
801 if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
802 had_crl = true;
803 }
804 chain_status[i].insert(code);
805 }
806 }
807
808 if(i < ocsp.size() && !ocsp[i].empty()) {
809 for(auto&& code : ocsp[i]) {
810 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
811 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
812 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
813 {
814 had_ocsp = true;
815 }
816
817 chain_status[i].insert(code);
818 }
819 }
820
821 if(had_crl == false && had_ocsp == false) {
822 if((restrictions.require_revocation_information() && i == 0) ||
823 (restrictions.ocsp_all_intermediates() && i > 0)) {
824 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
825 }
826 }
827 }
828}
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 830 of file x509path.cpp.

830 {
831 if(cert_status.empty()) {
832 throw Invalid_Argument("PKIX::overall_status empty cert status");
833 }
834
835 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
836
837 // take the "worst" error as overall
838 for(const std::set<Certificate_Status_Code>& s : cert_status) {
839 if(!s.empty()) {
840 auto worst = *s.rbegin();
841 // Leave informative OCSP/CRL confirmations on cert-level status only
842 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
843 overall_status = worst;
844 }
845 }
846 }
847 return overall_status;
848}
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition x509path.cpp:830
Certificate_Status_Code
Definition pkix_enums.h:20

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

Referenced by overall_status().