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

600 {
601 if(end_entity.is_self_signed()) {
602 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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) {
644 return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
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
651 return Certificate_Status_Code::CERT_CHAIN_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) {
659 return Certificate_Status_Code::OK;
660 } else {
661 return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
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()) {
58 status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
59 } else {
60 std::unique_ptr<Public_Key> issuer_key;
61 try {
62 issuer_key = issuer.subject_public_key();
63 } catch(...) {
64 status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
65 }
66
67 if(issuer_key) {
68 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
69 status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
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)) {
85 status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
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) {
115 cert_status[0].insert(Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE);
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)) {
141 status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
142 }
143
144 // This should never happen; it indicates a bug in path building
145 if(subject.issuer_dn() != issuer.subject_dn()) {
146 status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
147 }
148
149 // Check the serial number
150 if(subject.is_serial_negative()) {
151 status.insert(Certificate_Status_Code::CERT_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) {
160 status.insert(Certificate_Status_Code::DN_TOO_LONG);
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 {
172 status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
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 {
180 status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
181 }
182 }
183
184 // Check issuer constraints
185 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
186 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
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) {
193 status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
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()) {
200 status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
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()) {
206 status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
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 {
224 status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
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
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

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}

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
375 if(!ca.allowed_usage(Key_Constraints::CrlSign)) {
376 status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
377 }
378
379 if(validation_time < crls[i]->this_update()) {
380 status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
381 }
382
383 if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
384 status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
385 }
386
387 auto ca_key = ca.subject_public_key();
388 if(crls[i]->check_signature(*ca_key) == false) {
389 status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
390 }
391
392 status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
393
394 if(crls[i]->is_revoked(subject)) {
395 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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()) {
403 status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
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 */
416 status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
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) {
331 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
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);
341 status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
342 } else {
343 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
344 }
345 } catch(Exception&) {
346 status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
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]) {
830 if(code == Certificate_Status_Code::VALID_CRL_CHECKED) {
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
840 if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
841 code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL ||
842 code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) {
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
864 Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
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
871 if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status) {
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().