Botan 3.12.0
Crypto and TLS for C&
x509path.cpp
Go to the documentation of this file.
1/*
2* X.509 Certificate Path Validation
3* (C) 2010,2011,2012,2014,2016,2026 Jack Lloyd
4* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/x509path.h>
10
11#include <botan/assert.h>
12#include <botan/ocsp.h>
13#include <botan/pk_keys.h>
14#include <botan/x509_ext.h>
15#include <botan/internal/concat_util.h>
16#include <algorithm>
17#include <chrono>
18#include <set>
19#include <sstream>
20#include <string>
21#include <unordered_set>
22#include <vector>
23
24#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
25 #include <botan/internal/http_util.h>
26 #include <future>
27#endif
28
29namespace Botan {
30
31namespace {
32
33/**
34 * Lazy DFS iterator that yields certificate paths one at a time.
35 *
36 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
37 *
38 * Basically, a DFS is performed starting from the end certificate. A stack (vector)
39 * serves to control the DFS. At the beginning of each iteration, a pair is popped from
40 * the stack that contains (1) the next certificate to add to the path (2) a bool that
41 * indicates if the certificate is part of a trusted certstore. Ideally, we follow the
42 * unique issuer of the current certificate until a trusted root is reached. However, the
43 * issuer DN + authority key id need not be unique among the certificates used for
44 * building the path. In such a case, we consider all the matching issuers by pushing
45 * <IssuerCert, trusted?> on the stack for each of them.
46 *
47 * Each call to next() resumes the search and returns the next discovered path, or nullopt
48 * when the search space is exhausted.
49*/
50class CertificatePathBuilder final {
51 public:
52 CertificatePathBuilder(const std::vector<Certificate_Store*>& trusted_certstores,
53 const X509_Certificate& end_entity,
54 const std::vector<X509_Certificate>& end_entity_extra,
55 bool require_self_signed = false) :
56 m_trusted_certstores(trusted_certstores), m_require_self_signed(require_self_signed) {
57 if(std::ranges::any_of(trusted_certstores, [](auto* ptr) { return ptr == nullptr; })) {
58 throw Invalid_Argument("Certificate store list must not contain nullptr");
59 }
60
61 for(const auto& cert : end_entity_extra) {
62 if(!cert_in_any_trusted_store(cert)) {
63 m_ee_extras.add_certificate(cert);
64 }
65 }
66
67 m_stack.push_back({end_entity, cert_in_any_trusted_store(end_entity)});
68 }
69
70 std::optional<std::vector<X509_Certificate>> next() {
71 size_t steps = 0;
72
73 while(!m_stack.empty()) {
74 constexpr size_t MAX_DFS_STEPS = 1000;
75
76 steps++;
77
78 if(steps > MAX_DFS_STEPS) {
79 // Intentionally overwrite any previous builder error
81 return std::nullopt;
82 }
83
84 auto [last, trusted] = std::move(m_stack.back()); // move before pop_back
85 m_stack.pop_back();
86
87 // Found a deletion marker that guides the DFS, backtracking
88 if(!last.has_value()) {
89 m_certs_seen.erase(m_path_so_far.back().tag());
90 m_path_so_far.pop_back();
91 continue;
92 }
93
94 // Certificate already seen in this path?
95 const auto tag = last->tag();
96 if(m_certs_seen.contains(tag)) {
97 if(!m_error.has_value()) {
99 }
100 continue;
101 }
102
103 // A valid path has been discovered. It includes endpoints that may end
104 // with either a self-signed or a non-self-signed certificate. For
105 // certificates that are not self-signed, additional paths could
106 // potentially extend from the current one.
107 if(trusted) {
108 auto path = m_path_so_far;
109 path.push_back(*last);
110 push_issuers(*last);
111
112 if(!m_require_self_signed || last->is_self_signed()) {
113 return path;
114 }
115
116 /*
117 This unconditionally overwrites the error because it's likely the most
118 informative error in this context - we found a path that seemed entirely
119 suitable, except that self-signed roots are required so it was skipped.
120 */
122 continue;
123 }
124
125 if(last->is_self_signed()) {
126 if(!m_error.has_value()) {
128 }
129 continue;
130 }
131
132 push_issuers(*last);
133 }
134
135 return std::nullopt;
136 }
137
138 /**
139 * Return the first error encountered during path building
140 *
141 * Only used as a last resort if there were no successful paths
142 */
143 Certificate_Status_Code error() const {
144 if(m_error.has_value()) {
145 // Confirm it is an actual error code and not accidentally OK...
146 BOTAN_ASSERT_NOMSG(static_cast<uint32_t>(m_error.value()) >= 3000);
147 return m_error.value();
148 } else {
150 }
151 }
152
153 private:
154 bool cert_in_any_trusted_store(const X509_Certificate& cert) const {
155 return std::ranges::any_of(m_trusted_certstores,
156 [&](const Certificate_Store* store) { return store->contains(cert); });
157 }
158
159 void push_issuers(const X509_Certificate& cert) {
160 const X509_DN& issuer_dn = cert.issuer_dn();
161 const std::vector<uint8_t>& auth_key_id = cert.authority_key_id();
162
163 // Search for trusted issuers
164 std::vector<X509_Certificate> trusted_issuers;
165 for(const Certificate_Store* store : m_trusted_certstores) {
166 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
167 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
168 }
169
170 // Search the supplemental certs
171 const std::vector<X509_Certificate> misc_issuers = m_ee_extras.find_all_certs(issuer_dn, auth_key_id);
172
173 // If we could not find any issuers, the current path ends here
174 if(trusted_issuers.empty() && misc_issuers.empty()) {
175 if(!m_error.has_value()) {
177 }
178 return;
179 }
180
181 m_path_so_far.push_back(cert);
182 m_certs_seen.emplace(cert.tag());
183
184 // Push a deletion marker on the stack for backtracking later
185 m_stack.push_back({std::nullopt, false});
186
187 for(const auto& trusted_cert : trusted_issuers) {
188 m_stack.push_back({trusted_cert, true});
189 }
190 for(const auto& misc : misc_issuers) {
191 m_stack.push_back({misc, false});
192 }
193 }
194
195 const std::vector<Certificate_Store*> m_trusted_certstores;
196 const bool m_require_self_signed;
197 Certificate_Store_In_Memory m_ee_extras;
198 std::vector<std::pair<std::optional<X509_Certificate>, bool>> m_stack;
199 std::vector<X509_Certificate> m_path_so_far;
200 std::unordered_set<X509_Certificate::Tag, X509_Certificate::TagHash> m_certs_seen;
201 std::optional<Certificate_Status_Code> m_error;
202};
203
204} // namespace
205
206/*
207* PKIX path validation
208*/
209CertificatePathStatusCodes PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
210 std::chrono::system_clock::time_point ref_time,
211 std::string_view hostname,
212 Usage_Type usage,
213 const Path_Validation_Restrictions& restrictions) {
214 if(cert_path.empty()) {
215 throw Invalid_Argument("PKIX::check_chain cert_path empty");
216 }
217
218 const bool is_end_entity_trust_anchor = (cert_path.size() == 1);
219
220 const X509_Time validation_time(ref_time);
221
222 CertificatePathStatusCodes cert_status(cert_path.size());
223
224 // Before anything else verify the entire chain of signatures
225 for(size_t i = 0; i != cert_path.size(); ++i) {
226 std::set<Certificate_Status_Code>& status = cert_status.at(i);
227
228 const bool at_trust_anchor = (i == cert_path.size() - 1);
229
230 const X509_Certificate& subject = cert_path[i];
231
232 // If using intermediate CAs as trust anchors, the signature of the trust
233 // anchor cannot be verified since the issuer is not part of the
234 // certificate chain
235 if(!restrictions.require_self_signed_trust_anchors() && at_trust_anchor && !subject.is_self_signed()) {
236 continue;
237 }
238
239 const X509_Certificate& issuer = cert_path[at_trust_anchor ? (i) : (i + 1)];
240
241 // Check the signature algorithm is known
242 if(!subject.signature_algorithm().oid().registered_oid()) {
244 } else {
245 std::unique_ptr<Public_Key> issuer_key;
246 try {
247 issuer_key = issuer.subject_public_key();
248 } catch(...) {
250 }
251
252 if(issuer_key) {
253 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
255 }
256
257 const auto sig_status = subject.verify_signature(*issuer_key);
258
259 if(sig_status.first != Certificate_Status_Code::VERIFIED) {
260 status.insert(sig_status.first);
261 } else {
262 // Signature is valid, check if hash used was acceptable
263 const std::string hash_used_for_signature = sig_status.second;
264 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
265 const auto& trusted_hashes = restrictions.trusted_hashes();
266
267 // Ignore untrusted hashes on self-signed roots
268 if(!trusted_hashes.empty() && !at_trust_anchor) {
269 if(!trusted_hashes.contains(hash_used_for_signature)) {
271 }
272 }
273 }
274 }
275 }
276 }
277
278 // If any of the signatures were invalid, return immediately; we know the
279 // chain is invalid and signature failure is always considered the most
280 // critical result. This does mean other problems in the certificate (eg
281 // expired) will not be reported, but we'd have to assume any such data is
282 // anyway arbitrary considering we couldn't verify the signature chain
283
284 for(size_t i = 0; i != cert_path.size(); ++i) {
285 for(auto status : cert_status.at(i)) {
286 // This ignores errors relating to the key or hash being weak since
287 // these are somewhat advisory
288 if(static_cast<uint32_t>(status) >= 5000) {
289 return cert_status;
290 }
291 }
292 }
293
294 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
295 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
296 }
297
298 if(!cert_path[0].allowed_usage(usage)) {
299 if(usage == Usage_Type::OCSP_RESPONDER) {
301 }
302 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
303 }
304
305 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
306 /*
307 "If the keyCertSign bit is asserted, then the cA bit in the
308 basic constraints extension (Section 4.2.1.9) MUST also be
309 asserted." - RFC 5280
310
311 We don't bother doing this check on the rest of the path since they
312 must have the cA bit asserted or the validation will fail anyway.
313 */
314 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
315 }
316
317 for(size_t i = 0; i != cert_path.size(); ++i) {
318 std::set<Certificate_Status_Code>& status = cert_status.at(i);
319
320 const bool at_trust_anchor = (i == cert_path.size() - 1);
321
322 const X509_Certificate& subject = cert_path[i];
323 const auto issuer = [&]() -> std::optional<X509_Certificate> {
324 if(!at_trust_anchor) {
325 return cert_path[i + 1];
326 } else if(subject.is_self_signed()) {
327 return cert_path[i];
328 } else {
329 return {}; // Non self-signed trust anchors have no checkable issuers.
330 }
331 }();
332
333 if(restrictions.require_self_signed_trust_anchors() && !issuer.has_value()) {
335 }
336
337 // This should never happen; it indicates a bug in path building
338 if(issuer.has_value() && subject.issuer_dn() != issuer->subject_dn()) {
340 }
341
342 // Check the serial number
343 if(subject.is_serial_negative()) {
345 }
346
347 // Check the subject's DN components' length
348
349 for(const auto& dn_pair : subject.subject_dn().dn_info()) {
350 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
351 // dn_pair = <OID,str>
352 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
354 }
355 }
356
357 // If so configured, allow trust anchors outside the validity period with
358 // a warning rather than a hard error
359 const bool enforce_validity_period = !at_trust_anchor || !restrictions.ignore_trusted_root_time_range();
360 // Check all certs for valid time range
361 if(validation_time < subject.not_before()) {
362 if(enforce_validity_period) {
364 } else {
365 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
366 }
367 }
368
369 if(validation_time > subject.not_after()) {
370 if(enforce_validity_period) {
372 } else {
373 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
374 }
375 }
376
377 // Check issuer constraints
378 if(issuer.has_value() && !issuer->is_CA_cert() && !is_end_entity_trust_anchor) {
380 }
381
382 // Check cert extensions
383
384 if(subject.x509_version() == 1) {
385 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
387 }
388 }
389
390 const Extensions& extensions = subject.v3_extensions();
391 const auto& extensions_vec = extensions.extensions();
392 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
394 }
395
396 for(const auto& extension : extensions_vec) {
397 extension.first->validate(subject, issuer, cert_path, cert_status, i);
398 }
399
400 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
402 }
403 }
404
405 // path len check
406 size_t max_path_length = cert_path.size();
407 for(size_t i = cert_path.size() - 1; i > 0; --i) {
408 std::set<Certificate_Status_Code>& status = cert_status.at(i);
409 const X509_Certificate& subject = cert_path[i];
410
411 /*
412 * If the certificate was not self-issued, verify that max_path_length is
413 * greater than zero and decrement max_path_length by 1.
414 */
415 if(subject.subject_dn() != subject.issuer_dn()) {
416 if(max_path_length > 0) {
417 max_path_length -= 1;
418 } else {
420 }
421 }
422
423 /*
424 * If pathLenConstraint is present in the certificate and is less than max_path_length,
425 * set max_path_length to the value of pathLenConstraint.
426 */
427 if(auto path_len_constraint = subject.path_length_constraint()) {
428 max_path_length = std::min(max_path_length, *path_len_constraint);
429 }
430 }
431
432 return cert_status;
433}
434
435namespace {
436
437Certificate_Status_Code verify_ocsp_signing_cert(const X509_Certificate& signing_cert,
438 const X509_Certificate& ca,
439 const std::vector<X509_Certificate>& extra_certs,
440 const std::vector<Certificate_Store*>& certstores,
441 std::chrono::system_clock::time_point ref_time,
442 const Path_Validation_Restrictions& restrictions) {
443 // RFC 6960 4.2.2.2
444 // [Applications] MUST reject the response if the certificate
445 // required to validate the signature on the response does not
446 // meet at least one of the following criteria:
447 //
448 // 1. Matches a local configuration of OCSP signing authority
449 // for the certificate in question, or
450 if(restrictions.trusted_ocsp_responders() != nullptr &&
451 restrictions.trusted_ocsp_responders()->contains(signing_cert)) {
453 }
454
455 // RFC 6960 4.2.2.2
456 //
457 // 2. Is the certificate of the CA that issued the certificate
458 // in question, or
459 if(signing_cert == ca) {
461 }
462
463 // RFC 6960 4.2.2.2
464 //
465 // 3. Includes a value of id-kp-OCSPSigning in an extended key
466 // usage extension and is issued by the CA that issued the
467 // certificate in question as stated above.
468
469 // Verify the delegated responder was issued by the CA that issued
470 // the certificate in question (the EKU and signature chain are
471 // verified by the path validation below).
472 if(signing_cert.issuer_dn() != ca.subject_dn()) {
474 } else {
475 // If both key identifiers are available, verify they match to
476 // handle CAs that share a subject DN but have different keys
477 // (eg re-keyed or cross-certified CAs).
478 const auto& aki = signing_cert.authority_key_id();
479 const auto& ski = ca.subject_key_id();
480 if(!aki.empty() && !ski.empty() && aki != ski) {
482 }
483 }
484
485 // TODO: Implement OCSP revocation check of OCSP signer certificate
486 // Note: This needs special care to prevent endless loops on specifically
487 // forged chains of OCSP responses referring to each other.
488 //
489 // Currently, we're disabling OCSP-based revocation checks by setting the
490 // timeout to 0. Additionally, the library's API would not allow an
491 // application to pass in the required "second order" OCSP responses. I.e.
492 // "second order" OCSP checks would need to rely on `check_ocsp_online()`
493 // which is not an option for some applications (e.g. that require a proxy
494 // for external HTTP requests).
495 const auto ocsp_timeout = std::chrono::milliseconds::zero();
496 const auto relaxed_restrictions =
497 Path_Validation_Restrictions(false /* do not enforce revocation data */,
498 restrictions.minimum_key_strength(),
499 false /* OCSP is not available, so don't try for intermediates */,
500 restrictions.trusted_hashes());
501
502 const auto validation_result = x509_path_validate(concat(std::vector{signing_cert}, extra_certs),
503 relaxed_restrictions,
504 certstores,
505 {} /* hostname */,
507 ref_time,
508 ocsp_timeout);
509
510 return validation_result.result();
511}
512
513std::set<Certificate_Status_Code> evaluate_ocsp_response(const OCSP::Response& ocsp_response,
514 const X509_Certificate& subject,
515 const X509_Certificate& ca,
516 const std::vector<X509_Certificate>& cert_path,
517 const std::vector<Certificate_Store*>& certstores,
518 std::chrono::system_clock::time_point ref_time,
519 const Path_Validation_Restrictions& restrictions) {
520 // Handle softfail conditions (eg. OCSP unavailable)
521 if(auto dummy_status = ocsp_response.dummy_status()) {
522 return {dummy_status.value()};
523 }
524
525 // Find the certificate that signed this OCSP response
526 auto signing_cert = ocsp_response.find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
527 if(!signing_cert) {
529 }
530
531 // Verify the signing certificate is trusted
532 auto cert_status = verify_ocsp_signing_cert(
533 signing_cert.value(), ca, concat(ocsp_response.certificates(), cert_path), certstores, ref_time, restrictions);
536 }
537
538 // Verify the cryptographic signature on the OCSP response
539 auto sig_status = ocsp_response.verify_signature(signing_cert.value(), restrictions);
541 return {sig_status};
542 }
543
544 // All checks passed, return the certificate's revocation status
545 return {ocsp_response.status_for(ca, subject, ref_time, restrictions.max_ocsp_age())};
546}
547
548} // namespace
549
550CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
551 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
552 const std::vector<Certificate_Store*>& certstores,
553 std::chrono::system_clock::time_point ref_time,
554 const Path_Validation_Restrictions& restrictions) {
555 if(cert_path.empty()) {
556 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
557 }
558
559 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
560
561 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
562 const X509_Certificate& subject = cert_path.at(i);
563 const X509_Certificate& ca = cert_path.at(i + 1);
564
565 if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
566 ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) {
567 try {
568 cert_status.at(i) = evaluate_ocsp_response(
569 ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
570 } catch(Exception&) {
571 cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
572 }
573 }
574 }
575
576 return cert_status;
577}
578
579CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
580 const std::vector<std::optional<X509_CRL>>& crls,
581 std::chrono::system_clock::time_point ref_time) {
582 if(cert_path.empty()) {
583 throw Invalid_Argument("PKIX::check_crl cert_path empty");
584 }
585
586 CertificatePathStatusCodes cert_status(cert_path.size());
587 const X509_Time validation_time(ref_time);
588
589 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
590 std::set<Certificate_Status_Code>& status = cert_status.at(i);
591
592 if(i < crls.size() && crls[i].has_value()) {
593 const X509_Certificate& subject = cert_path.at(i);
594 const X509_Certificate& ca = cert_path.at(i + 1);
595
598 }
599
600 if(validation_time < crls[i]->this_update()) {
602 }
603
604 if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
606 }
607
608 auto ca_key = ca.subject_public_key();
609 if(crls[i]->check_signature(*ca_key) == false) {
611 } else {
613
614 if(crls[i]->is_revoked(subject)) {
616 }
617
618 if(!crls[i]->has_matching_distribution_point(subject)) {
620 }
621
622 for(const auto& [extension, critical] : crls[i]->extensions().extensions()) {
623 if(critical) {
624 /* NIST Certificate Path Validation Testing document: "When an implementation does
625 * not recognize a critical extension in the crlExtensions field, it shall assume
626 * that identified certificates have been revoked and are no longer valid"
627 */
628 if(dynamic_cast<const Cert_Extension::Unknown_Extension*>(extension.get()) != nullptr) {
630 }
631 }
632 }
633 }
634 }
635 }
636
637 while(!cert_status.empty() && cert_status.back().empty()) {
638 cert_status.pop_back();
639 }
640
641 return cert_status;
642}
643
644CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
645 const std::vector<Certificate_Store*>& certstores,
646 std::chrono::system_clock::time_point ref_time) {
647 if(cert_path.empty()) {
648 throw Invalid_Argument("PKIX::check_crl cert_path empty");
649 }
650
651 if(certstores.empty()) {
652 throw Invalid_Argument("PKIX::check_crl certstores empty");
653 }
654
655 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
656
657 for(size_t i = 0; i != cert_path.size(); ++i) {
658 for(auto* certstore : certstores) {
659 crls[i] = certstore->find_crl_for(cert_path[i]);
660 if(crls[i]) {
661 break;
662 }
663 }
664 }
665
666 return PKIX::check_crl(cert_path, crls, ref_time);
667}
668
669#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
670
671CertificatePathStatusCodes PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
672 const std::vector<Certificate_Store*>& trusted_certstores,
673 std::chrono::system_clock::time_point ref_time,
674 std::chrono::milliseconds timeout,
675 const Path_Validation_Restrictions& restrictions) {
676 if(cert_path.empty()) {
677 throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
678 }
679
680 std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
681
682 size_t to_ocsp = 1;
683
684 if(restrictions.ocsp_all_intermediates()) {
685 to_ocsp = cert_path.size() - 1;
686 }
687 if(cert_path.size() == 1) {
688 to_ocsp = 0;
689 }
690
691 for(size_t i = 0; i < to_ocsp; ++i) {
692 const auto& subject = cert_path.at(i);
693 const auto& issuer = cert_path.at(i + 1);
694
695 if(subject.ocsp_responder().empty()) {
696 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, []() -> std::optional<OCSP::Response> {
698 }));
699 } else {
700 auto ocsp_url = subject.ocsp_responder();
701 auto ocsp_req = OCSP::Request(issuer, BigInt::from_bytes(subject.serial_number()));
702 ocsp_response_futures.emplace_back(
703 std::async(std::launch::async, [ocsp_url, ocsp_req, timeout]() -> std::optional<OCSP::Response> {
704 HTTP::Response http;
705 try {
706 http = HTTP::POST_sync(ocsp_url,
707 "application/ocsp-request",
708 ocsp_req.BER_encode(),
709 /*redirects*/ 1,
710 timeout);
711
712 if(http.status_code() != 200) {
714 }
715
716 return OCSP::Response(http.body());
717 } catch(std::exception&) {
719 }
720 }));
721 }
722 }
723
724 std::vector<std::optional<OCSP::Response>> ocsp_responses;
725 ocsp_responses.reserve(ocsp_response_futures.size());
726
727 for(auto& ocsp_response_future : ocsp_response_futures) {
728 ocsp_responses.push_back(ocsp_response_future.get());
729 }
730
731 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
732}
733
734CertificatePathStatusCodes PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
735 const std::vector<Certificate_Store*>& certstores,
737 std::chrono::system_clock::time_point ref_time,
738 std::chrono::milliseconds timeout) {
739 if(cert_path.empty()) {
740 throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
741 }
742 if(certstores.empty()) {
743 throw Invalid_Argument("PKIX::check_crl_online certstores empty");
744 }
745
746 std::vector<std::future<std::optional<X509_CRL>>> future_crls;
747 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
748
749 for(size_t i = 0; i != cert_path.size(); ++i) {
750 const auto& cert = cert_path.at(i);
751 for(auto* certstore : certstores) {
752 crls[i] = certstore->find_crl_for(cert);
753 if(crls[i].has_value()) {
754 break;
755 }
756 }
757
758 // TODO: check if CRL is expired and re-request?
759
760 // Only request if we don't already have a CRL
761 if(crls[i]) {
762 /*
763 We already have a CRL, so just insert this empty one to hold a place in the vector
764 so that indexes match up
765 */
766 future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
767 } else if(cert.crl_distribution_point().empty()) {
768 // Avoid creating a thread for this case
769 future_crls.emplace_back(std::async(std::launch::deferred, []() -> std::optional<X509_CRL> {
770 throw Not_Implemented("No CRL distribution point for this certificate");
771 }));
772 } else {
773 auto cdp = cert.crl_distribution_point();
774 future_crls.emplace_back(std::async(std::launch::async, [cdp, timeout]() -> std::optional<X509_CRL> {
775 auto http = HTTP::GET_sync(cdp,
776 /*redirects*/ 1,
777 timeout);
778
779 http.throw_unless_ok();
780 // check the mime type?
781 return X509_CRL(http.body());
782 }));
783 }
784 }
785
786 for(size_t i = 0; i != future_crls.size(); ++i) {
787 if(future_crls[i].valid()) {
788 try {
789 crls[i] = future_crls[i].get();
790 } catch(std::exception&) {
791 // crls[i] left null
792 // todo: log exception e.what() ?
793 }
794 }
795 }
796
797 auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
798
799 if(crl_store != nullptr) {
800 for(size_t i = 0; i != crl_status.size(); ++i) {
801 if(crl_status[i].contains(Certificate_Status_Code::VALID_CRL_CHECKED)) {
802 // better be non-null, we supposedly validated it
803 BOTAN_ASSERT_NOMSG(crls[i].has_value());
804 crl_store->add_crl(*crls[i]);
805 }
806 }
807 }
808
809 return crl_status;
810}
811
812#endif
813
814Certificate_Status_Code PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
815 const std::vector<Certificate_Store*>& trusted_certstores,
816 const X509_Certificate& end_entity,
817 const std::vector<X509_Certificate>& end_entity_extra) {
818 CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra);
819
820 std::vector<X509_Certificate> first_path;
821
822 while(auto path = builder.next()) {
823 BOTAN_ASSERT_NOMSG(path->empty() == false);
824
825 // Prefer paths ending in self-signed certificates.
826 if(path->back().is_self_signed()) {
827 cert_path.insert(cert_path.end(), path->begin(), path->end());
829 }
830
831 // Save the first path for later just in case we find nothing better
832 if(first_path.empty()) {
833 first_path = std::move(*path);
834 }
835 }
836
837 if(!first_path.empty()) {
838 // We found a path, it's not self-signed but it's as good as can be formed...
839 cert_path.insert(cert_path.end(), first_path.begin(), first_path.end());
841 }
842
843 // Failed to build any path at all
844 return builder.error();
845}
846
847Certificate_Status_Code PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
848 const std::vector<Certificate_Store*>& trusted_certstores,
849 const X509_Certificate& end_entity,
850 const std::vector<X509_Certificate>& end_entity_extra) {
851 if(!cert_paths_out.empty()) {
852 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
853 }
854 CertificatePathBuilder builder(trusted_certstores, end_entity, end_entity_extra);
855
856 while(auto path = builder.next()) {
857 BOTAN_ASSERT_NOMSG(path->empty() == false);
858 cert_paths_out.push_back(std::move(*path));
859 }
860
861 if(!cert_paths_out.empty()) {
862 // Was able to generate at least one potential path
864 } else {
865 // Could not construct any potentially valid path...
866 return builder.error();
867 }
868}
869
871 const CertificatePathStatusCodes& crl_status,
872 const CertificatePathStatusCodes& ocsp_status,
873 const Path_Validation_Restrictions& restrictions) {
874 if(chain_status.empty()) {
875 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
876 }
877
878 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
879 bool had_crl = false;
880 bool had_ocsp = false;
881
882 if(i < crl_status.size() && !crl_status[i].empty()) {
883 for(auto&& code : crl_status[i]) {
885 had_crl = true;
886 }
887 chain_status[i].insert(code);
888 }
889 }
890
891 if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
892 for(auto&& code : ocsp_status[i]) {
893 // NO_REVOCATION_URL and OCSP_SERVER_NOT_AVAILABLE are softfail
897 had_ocsp = true;
898 }
899
900 chain_status[i].insert(code);
901 }
902 }
903
904 if(had_crl == false && had_ocsp == false) {
905 if((restrictions.require_revocation_information() && i == 0) ||
906 (restrictions.ocsp_all_intermediates() && i > 0)) {
907 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
908 }
909 }
910 }
911}
912
914 if(cert_status.empty()) {
915 throw Invalid_Argument("PKIX::overall_status empty cert status");
916 }
917
919
920 // take the "worst" error as overall
921 for(const std::set<Certificate_Status_Code>& s : cert_status) {
922 if(!s.empty()) {
923 auto worst = *s.rbegin();
924 // Leave informative OCSP/CRL confirmations on cert-level status only
926 overall_status = worst;
927 }
928 }
929 }
930 return overall_status;
931}
932
933Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
934 const Path_Validation_Restrictions& restrictions,
935 const std::vector<Certificate_Store*>& trusted_roots,
936 std::string_view hostname,
937 Usage_Type usage,
938 std::chrono::system_clock::time_point ref_time,
939 std::chrono::milliseconds ocsp_timeout,
940 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
941 if(end_certs.empty()) {
942 throw Invalid_Argument("x509_path_validate called with no subjects");
943 }
944
945 const X509_Certificate& end_entity = end_certs[0];
946 std::vector<X509_Certificate> end_entity_extra;
947 for(size_t i = 1; i < end_certs.size(); ++i) {
948 end_entity_extra.push_back(end_certs[i]);
949 }
950
951 const bool require_self_signed = restrictions.require_self_signed_trust_anchors();
952
953 CertificatePathBuilder builder(trusted_roots, end_entity, end_entity_extra, require_self_signed);
954
955 constexpr size_t max_paths = 50;
956 constexpr size_t max_verifications = 200;
957
958 std::optional<Path_Validation_Result> first_path_error;
959 size_t paths_checked = 0;
960 size_t certs_checked = 0;
961
962 while(auto cert_path = builder.next()) {
963 BOTAN_ASSERT_NOMSG(cert_path->empty() == false);
964
965 paths_checked += 1;
966 certs_checked += cert_path->size();
967 if(paths_checked > max_paths || certs_checked > max_verifications) {
969 break;
970 }
971
972 CertificatePathStatusCodes status = PKIX::check_chain(*cert_path, ref_time, hostname, usage, restrictions);
973
974 // Skip revocation checks if the chain already has fatal errors.
976 const CertificatePathStatusCodes crl_status = PKIX::check_crl(*cert_path, trusted_roots, ref_time);
977
978 CertificatePathStatusCodes ocsp_status;
979
980 if(!ocsp_resp.empty()) {
981 ocsp_status = PKIX::check_ocsp(*cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
982 }
983
984 if(ocsp_timeout != std::chrono::milliseconds(0)) {
985 const size_t to_online = restrictions.ocsp_all_intermediates() ? (cert_path->size() - 1) : 1;
986 bool need_online = false;
987 for(size_t i = 0; i < to_online; ++i) {
988 if(i >= ocsp_status.size() || ocsp_status[i].empty()) {
989 need_online = true;
990 break;
991 }
992 }
993
994 if(need_online) {
995#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
996 auto online_status =
997 PKIX::check_ocsp_online(*cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions);
998 if(ocsp_status.size() < online_status.size()) {
999 ocsp_status.resize(online_status.size());
1000 }
1001 for(size_t i = 0; i < online_status.size(); ++i) {
1002 if(ocsp_status[i].empty()) {
1003 ocsp_status[i] = std::move(online_status[i]);
1004 }
1005 }
1006#else
1007 if(ocsp_status.size() < to_online) {
1008 ocsp_status.resize(to_online);
1009 }
1010 for(size_t i = 0; i < to_online; ++i) {
1011 if(ocsp_status[i].empty()) {
1012 ocsp_status[i].insert(Certificate_Status_Code::OCSP_NO_HTTP);
1013 }
1014 }
1015#endif
1016 }
1017 }
1018
1019 PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions);
1020 }
1021
1022 Path_Validation_Result pvd(status, std::move(*cert_path));
1023 if(pvd.successful_validation()) {
1024 return pvd;
1025 } else if(!first_path_error.has_value()) {
1026 // Save the errors from the first path we attempted
1027 first_path_error = std::move(pvd);
1028 }
1029 }
1030
1031 if(first_path_error.has_value()) {
1032 // We found at least one path, but none of them verified
1033 // Return arbitrarily the error from the first path attempted
1034 return first_path_error.value();
1035 } else {
1036 // Failed to build any path at all
1037 return Path_Validation_Result(builder.error());
1038 }
1039}
1040
1042 const Path_Validation_Restrictions& restrictions,
1043 const std::vector<Certificate_Store*>& trusted_roots,
1044 std::string_view hostname,
1045 Usage_Type usage,
1046 std::chrono::system_clock::time_point when,
1047 std::chrono::milliseconds ocsp_timeout,
1048 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
1049 std::vector<X509_Certificate> certs;
1050 certs.push_back(end_cert);
1051 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1052}
1053
1054Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
1055 const Path_Validation_Restrictions& restrictions,
1056 const Certificate_Store& store,
1057 std::string_view hostname,
1058 Usage_Type usage,
1059 std::chrono::system_clock::time_point when,
1060 std::chrono::milliseconds ocsp_timeout,
1061 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
1062 std::vector<Certificate_Store*> trusted_roots;
1063 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
1064
1065 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1066}
1067
1069 const Path_Validation_Restrictions& restrictions,
1070 const Certificate_Store& store,
1071 std::string_view hostname,
1072 Usage_Type usage,
1073 std::chrono::system_clock::time_point when,
1074 std::chrono::milliseconds ocsp_timeout,
1075 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
1076 std::vector<X509_Certificate> certs;
1077 certs.push_back(end_cert);
1078
1079 std::vector<Certificate_Store*> trusted_roots;
1080 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
1081
1082 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1083}
1084
1086 size_t key_strength,
1087 bool ocsp_intermediates,
1088 std::chrono::seconds max_ocsp_age,
1089 std::unique_ptr<Certificate_Store> trusted_ocsp_responders,
1092 m_require_revocation_information(require_rev),
1093 m_ocsp_all_intermediates(ocsp_intermediates),
1094 m_minimum_key_strength(key_strength),
1095 m_max_ocsp_age(max_ocsp_age),
1096 m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)),
1097 m_ignore_trusted_root_time_range(ignore_trusted_root_time_range),
1098 m_require_self_signed_trust_anchors(require_self_signed_trust_anchors) {
1099 if(key_strength <= 80) {
1100 m_trusted_hashes.insert("SHA-1");
1101 }
1102
1103 m_trusted_hashes.insert("SHA-224");
1104 m_trusted_hashes.insert("SHA-256");
1105 m_trusted_hashes.insert("SHA-384");
1106 m_trusted_hashes.insert("SHA-512");
1107 m_trusted_hashes.insert("SHAKE-256(512)"); // Dilithium/ML-DSA
1108 m_trusted_hashes.insert("SHAKE-256(912)"); // Ed448
1109}
1110
1111namespace {
1112CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses) {
1114 for(const auto& status_set_i : all_statuses) {
1115 std::set<Certificate_Status_Code> warning_set_i;
1116 for(const auto& code : status_set_i) {
1119 warning_set_i.insert(code);
1120 }
1121 }
1122 warnings.push_back(warning_set_i);
1123 }
1124 return warnings;
1125}
1126} // namespace
1127
1129 std::vector<X509_Certificate>&& cert_chain) :
1130 m_all_status(std::move(status)),
1131 m_warnings(find_warnings(m_all_status)),
1132 m_cert_path(std::move(cert_chain)),
1133 m_overall(PKIX::overall_status(m_all_status)) {}
1134
1136 if(m_cert_path.empty()) {
1137 throw Invalid_State("Path_Validation_Result::trust_root no path set");
1138 }
1140 throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1141 }
1142
1143 return m_cert_path[m_cert_path.size() - 1];
1144}
1145
1150
1152 for(const auto& status_set_i : m_warnings) {
1153 if(!status_set_i.empty()) {
1154 return false;
1155 }
1156 }
1157 return true;
1158}
1159
1161 return m_warnings;
1162}
1163
1165 return status_string(result());
1166}
1167
1169 if(const char* s = to_string(code)) {
1170 return s;
1171 }
1172
1173 return "Unknown error";
1174}
1175
1177 const std::string sep(", ");
1178 std::ostringstream oss;
1179 for(size_t i = 0; i < m_warnings.size(); i++) {
1180 for(auto code : m_warnings[i]) {
1181 oss << "[" << std::to_string(i) << "] " << status_string(code) << sep;
1182 }
1183 }
1184
1185 std::string res = oss.str();
1186 // remove last sep
1187 if(res.size() >= sep.size()) {
1188 res = res.substr(0, res.size() - sep.size());
1189 }
1190 return res;
1191}
1192} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
const OID & oid() const
Definition asn1_obj.h:407
static BigInt from_bytes(std::span< const uint8_t > bytes)
Definition bigint.cpp:83
virtual bool contains(const X509_Certificate &cert) const
Definition certstor.cpp:28
const std::vector< OID > & get_extension_oids() const
Definition pkix_types.h:570
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
Definition x509_ext.cpp:248
bool registered_oid() const
Definition asn1_oid.cpp:151
bool require_revocation_information() const
Definition x509path.h:108
const std::set< std::string > & trusted_hashes() const
Definition x509path.h:119
std::chrono::seconds max_ocsp_age() const
Definition x509path.h:130
bool ignore_trusted_root_time_range() const
Definition x509path.h:150
bool require_self_signed_trust_anchors() const
Definition x509path.h:159
const Certificate_Store * trusted_ocsp_responders() const
Definition x509path.h:137
BOTAN_FUTURE_EXPLICIT Path_Validation_Restrictions(bool require_rev=false, size_t minimum_key_strength=110, bool ocsp_all_intermediates=false, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero(), std::unique_ptr< Certificate_Store > trusted_ocsp_responders=std::make_unique< Certificate_Store_In_Memory >(), bool ignore_trusted_root_time_range=false, bool require_self_signed_trust_anchors=true)
Certificate_Status_Code result() const
Definition x509path.h:204
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< X509_Certificate > &&cert_chain)
static const char * status_string(Certificate_Status_Code code)
const X509_Certificate & trust_root() const
std::string result_string() const
std::string warnings_string() const
CertificatePathStatusCodes warnings() const
const X509_DN & subject_dn() const
Definition x509cert.cpp:418
const X509_Time & not_after() const
Definition x509cert.cpp:362
const std::vector< uint8_t > & authority_key_id() const
Definition x509cert.cpp:398
const std::vector< uint8_t > & subject_key_id() const
Definition x509cert.cpp:402
std::optional< size_t > path_length_constraint() const
Definition x509cert.cpp:457
const Extensions & v3_extensions() const
Definition x509cert.cpp:477
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:486
const X509_DN & issuer_dn() const
Definition x509cert.cpp:414
const std::vector< uint8_t > & v2_issuer_key_id() const
Definition x509cert.cpp:370
uint32_t x509_version() const
Definition x509cert.cpp:350
bool is_self_signed() const
Definition x509cert.cpp:354
bool is_serial_negative() const
Definition x509cert.cpp:410
std::unique_ptr< Public_Key > subject_public_key() const
Definition x509cert.cpp:659
const std::vector< uint8_t > & v2_subject_key_id() const
Definition x509cert.cpp:374
const X509_Time & not_before() const
Definition x509cert.cpp:358
static size_t lookup_ub(const OID &oid)
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition pkix_types.h:98
const AlgorithmIdentifier & signature_algorithm() const
Definition x509_obj.cpp:73
std::pair< Certificate_Status_Code, std::string > verify_signature(const Public_Key &key) const
Definition x509_obj.cpp:130
Response POST_sync(std::string_view url, std::string_view content_type, const std::vector< uint8_t > &body, size_t allowable_redirects, std::chrono::milliseconds timeout)
Response GET_sync(std::string_view url, size_t allowable_redirects, std::chrono::milliseconds timeout)
Certificate_Status_Code build_all_certificate_paths(std::vector< std::vector< X509_Certificate > > &cert_paths, const std::vector< Certificate_Store * > &trusted_certstores, const X509_Certificate &end_entity, const std::vector< X509_Certificate > &end_entity_extra)
Definition x509path.cpp:847
void merge_revocation_status(CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, const Path_Validation_Restrictions &restrictions)
Definition x509path.cpp:870
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)
Definition x509path.cpp:814
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition x509path.cpp:913
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)
Definition x509path.cpp:550
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)
Definition x509path.cpp:209
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:579
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:28
ASN1_Time X509_Time
Definition asn1_obj.h:23
Certificate_Status_Code
Definition pkix_enums.h:20
Path_Validation_Result x509_path_validate(const std::vector< X509_Certificate > &end_certs, const Path_Validation_Restrictions &restrictions, const std::vector< Certificate_Store * > &trusted_roots, std::string_view hostname, Usage_Type usage, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds ocsp_timeout, const std::vector< std::optional< OCSP::Response > > &ocsp_resp)
Definition x509path.cpp:933
constexpr auto concat(Rs &&... ranges)
Definition concat_util.h:90
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition exceptn.cpp:13