Botan 3.6.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,
const std::vector< Certificate_Store * > & trusted_certstores,
const std::optional< X509_Certificate > & end_entity,
const std::vector< X509_Certificate > & end_entity_extra )

◆ 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()) {
606 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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) {
648 return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
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
655 return Certificate_Status_Code::CERT_CHAIN_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) {
663 return Certificate_Status_Code::OK;
664 } else {
665 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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:615
bool is_self_signed() const
Definition x509cert.cpp:331

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()) {
62 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
63 } else {
64 std::unique_ptr<Public_Key> issuer_key;
65 try {
66 issuer_key = issuer.subject_public_key();
67 } catch(...) {
68 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
69 }
70
71 if(issuer_key) {
72 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
73 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
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)) {
89 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
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) {
119 cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
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)) {
145 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
146 }
147
148 // This should never happen; it indicates a bug in path building
149 if(subject.issuer_dn() != issuer.subject_dn()) {
150 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
151 }
152
153 // Check the serial number
154 if(subject.is_serial_negative()) {
155 status.insert(Certificate_Status_Code::CERT_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) {
164 status.insert(Certificate_Status_Code::DN_TOO_LONG);
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 {
176 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
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 {
184 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
185 }
186 }
187
188 // Check issuer constraints
189 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
190 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
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) {
197 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
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()) {
204 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
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()) {
210 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
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 {
228 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
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:464
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:589
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:102
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 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}

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
379 if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
380 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
381 }
382
383 if(validation_time < crls[i]->this_update()) {
384 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
385 }
386
387 if(validation_time > crls[i]->next_update()) {
388 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
389 }
390
391 auto ca_key = ca.subject_public_key();
392 if(crls[i]->check_signature(*ca_key) == false) {
393 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
394 }
395
396 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
397
398 if(crls[i]->is_revoked(subject)) {
399 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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()) {
407 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
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 */
420 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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:526
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:448

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) {
335 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
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);
345 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
346 } else {
347 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
348 }
349 } catch(Exception&) {
350 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
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]) {
833 if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
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
843 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
844 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||
845 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) {
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
867 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
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
874 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
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().