Botan 3.4.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 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/ocsp.h>
12#include <botan/pk_keys.h>
13#include <botan/x509_ext.h>
14#include <botan/internal/stl_util.h>
15#include <algorithm>
16#include <chrono>
17#include <set>
18#include <sstream>
19#include <string>
20#include <vector>
21
22#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
23 #include <botan/internal/http_util.h>
24 #include <future>
25#endif
26
27#if defined(BOTAN_HAS_ECC_KEY)
28 #include <botan/ecc_key.h>
29#endif
30
31namespace Botan {
32
33/*
34* PKIX path validation
35*/
36CertificatePathStatusCodes PKIX::check_chain(const std::vector<X509_Certificate>& cert_path,
37 std::chrono::system_clock::time_point ref_time,
38 std::string_view hostname,
39 Usage_Type usage,
40 const Path_Validation_Restrictions& restrictions) {
41 if(cert_path.empty()) {
42 throw Invalid_Argument("PKIX::check_chain cert_path empty");
43 }
44
45 const bool self_signed_ee_cert = (cert_path.size() == 1);
46
47 X509_Time validation_time(ref_time);
48
49 CertificatePathStatusCodes cert_status(cert_path.size());
50
51 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
52 cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
53 }
54
55 if(!cert_path[0].allowed_usage(usage)) {
56 if(usage == Usage_Type::OCSP_RESPONDER) {
58 }
59 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
60 }
61
62 if(cert_path[0].has_constraints(Key_Constraints::KeyCertSign) && cert_path[0].is_CA_cert() == false) {
63 /*
64 "If the keyCertSign bit is asserted, then the cA bit in the
65 basic constraints extension (Section 4.2.1.9) MUST also be
66 asserted." - RFC 5280
67
68 We don't bother doing this check on the rest of the path since they
69 must have the cA bit asserted or the validation will fail anyway.
70 */
71 cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
72 }
73
74 for(size_t i = 0; i != cert_path.size(); ++i) {
75 std::set<Certificate_Status_Code>& status = cert_status.at(i);
76
77 const bool at_self_signed_root = (i == cert_path.size() - 1);
78
79 const X509_Certificate& subject = cert_path[i];
80 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
81
82 if(at_self_signed_root && (issuer.is_self_signed() == false)) {
84 }
85
86 if(subject.issuer_dn() != issuer.subject_dn()) {
88 }
89
90 // Check the serial number
91 if(subject.is_serial_negative()) {
93 }
94
95 // Check the subject's DN components' length
96
97 for(const auto& dn_pair : subject.subject_dn().dn_info()) {
98 const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
99 // dn_pair = <OID,str>
100 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
102 }
103 }
104
105 // Only warn, if trusted root is not in time range if configured this way
106 const bool is_trusted_root_and_time_ignored =
107 restrictions.ignore_trusted_root_time_range() && at_self_signed_root;
108 // Check all certs for valid time range
109 if(validation_time < subject.not_before()) {
110 if(is_trusted_root_and_time_ignored) {
111 status.insert(Certificate_Status_Code::TRUSTED_CERT_NOT_YET_VALID); // only warn
112 } else {
114 }
115 }
116
117 if(validation_time > subject.not_after()) {
118 if(is_trusted_root_and_time_ignored) {
119 status.insert(Certificate_Status_Code::TRUSTED_CERT_HAS_EXPIRED); // only warn
120 } else {
122 }
123 }
124
125 // Check issuer constraints
126 if(!issuer.is_CA_cert() && !self_signed_ee_cert) {
128 }
129
130 auto issuer_key = issuer.subject_public_key();
131
132 // Check the signature algorithm is known
133 if(!subject.signature_algorithm().oid().registered_oid()) {
135 } else {
136 // only perform the following checks if the signature algorithm is known
137 if(!issuer_key) {
139 } else {
140 const auto sig_status = subject.verify_signature(*issuer_key);
141
142 if(sig_status.first == Certificate_Status_Code::VERIFIED) {
143 const std::string hash_used_for_signature = sig_status.second;
144 BOTAN_ASSERT_NOMSG(!hash_used_for_signature.empty());
145 const auto& trusted_hashes = restrictions.trusted_hashes();
146
147 // Ignore untrusted hashes on self-signed roots
148 if(!trusted_hashes.empty() && !at_self_signed_root) {
149 if(!trusted_hashes.contains(hash_used_for_signature)) {
151 }
152 }
153 } else {
154 status.insert(sig_status.first);
155 }
156
157 if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) {
159 }
160 }
161 }
162
163 // Check cert extensions
164
165 if(subject.x509_version() == 1) {
166 if(subject.v2_issuer_key_id().empty() == false || subject.v2_subject_key_id().empty() == false) {
168 }
169 }
170
171 const Extensions& extensions = subject.v3_extensions();
172 const auto& extensions_vec = extensions.extensions();
173 if(subject.x509_version() < 3 && !extensions_vec.empty()) {
175 }
176 for(auto& extension : extensions_vec) {
177 extension.first->validate(subject, issuer, cert_path, cert_status, i);
178 }
179 if(extensions_vec.size() != extensions.get_extension_oids().size()) {
181 }
182 }
183
184 // path len check
185 size_t max_path_length = cert_path.size();
186 for(size_t i = cert_path.size() - 1; i > 0; --i) {
187 std::set<Certificate_Status_Code>& status = cert_status.at(i);
188 const X509_Certificate& subject = cert_path[i];
189
190 /*
191 * If the certificate was not self-issued, verify that max_path_length is
192 * greater than zero and decrement max_path_length by 1.
193 */
194 if(subject.subject_dn() != subject.issuer_dn()) {
195 if(max_path_length > 0) {
196 --max_path_length;
197 } else {
199 }
200 }
201
202 /*
203 * If pathLenConstraint is present in the certificate and is less than max_path_length,
204 * set max_path_length to the value of pathLenConstraint.
205 */
206 if(subject.path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.path_limit() < max_path_length) {
207 max_path_length = subject.path_limit();
208 }
209 }
210
211 return cert_status;
212}
213
214namespace {
215
216Certificate_Status_Code verify_ocsp_signing_cert(const X509_Certificate& signing_cert,
217 const X509_Certificate& ca,
218 const std::vector<X509_Certificate>& extra_certs,
219 const std::vector<Certificate_Store*>& certstores,
220 std::chrono::system_clock::time_point ref_time,
221 const Path_Validation_Restrictions& restrictions) {
222 // RFC 6960 4.2.2.2
223 // [Applications] MUST reject the response if the certificate
224 // required to validate the signature on the response does not
225 // meet at least one of the following criteria:
226 //
227 // 1. Matches a local configuration of OCSP signing authority
228 // for the certificate in question, or
229 if(restrictions.trusted_ocsp_responders()->certificate_known(signing_cert)) {
231 }
232
233 // RFC 6960 4.2.2.2
234 //
235 // 2. Is the certificate of the CA that issued the certificate
236 // in question, or
237 if(signing_cert == ca) {
239 }
240
241 // RFC 6960 4.2.2.2
242 //
243 // 3. Includes a value of id-kp-OCSPSigning in an extended key
244 // usage extension and is issued by the CA that issued the
245 // certificate in question as stated above.
246
247 // TODO: Implement OCSP revocation check of OCSP signer certificate
248 // Note: This needs special care to prevent endless loops on specifically
249 // forged chains of OCSP responses referring to each other.
250 //
251 // Currently, we're disabling OCSP-based revocation checks by setting the
252 // timeout to 0. Additionally, the library's API would not allow an
253 // application to pass in the required "second order" OCSP responses. I.e.
254 // "second order" OCSP checks would need to rely on `check_ocsp_online()`
255 // which is not an option for some applications (e.g. that require a proxy
256 // for external HTTP requests).
257 const auto ocsp_timeout = std::chrono::milliseconds::zero();
258 const auto relaxed_restrictions =
259 Path_Validation_Restrictions(false /* do not enforce revocation data */,
260 restrictions.minimum_key_strength(),
261 false /* OCSP is not available, so don't try for intermediates */,
262 restrictions.trusted_hashes());
263
264 const auto validation_result = x509_path_validate(concat(std::vector{signing_cert}, extra_certs),
265 relaxed_restrictions,
266 certstores,
267 {} /* hostname */,
269 ref_time,
270 ocsp_timeout);
271
272 return validation_result.result();
273}
274
275} // namespace
276
277CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
278 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
279 const std::vector<Certificate_Store*>& certstores,
280 std::chrono::system_clock::time_point ref_time,
281 const Path_Validation_Restrictions& restrictions) {
282 if(cert_path.empty()) {
283 throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
284 }
285
286 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
287
288 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
289 std::set<Certificate_Status_Code>& status = cert_status.at(i);
290
291 const X509_Certificate& subject = cert_path.at(i);
292 const X509_Certificate& ca = cert_path.at(i + 1);
293
294 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
295 (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
296 try {
297 const auto& ocsp_response = ocsp_responses.at(i);
298
299 if(auto dummy_status = ocsp_response->dummy_status()) {
300 // handle softfail conditions
301 status.insert(dummy_status.value());
302 } else if(auto signing_cert =
303 ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
304 !signing_cert) {
306 } else if(auto ocsp_signing_cert_status =
307 verify_ocsp_signing_cert(signing_cert.value(),
308 ca,
309 concat(ocsp_response->certificates(), cert_path),
310 certstores,
311 ref_time,
312 restrictions);
313 ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
314 status.insert(ocsp_signing_cert_status);
316 } else {
317 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
318 }
319 } catch(Exception&) {
321 }
322 }
323 }
324
325 while(!cert_status.empty() && cert_status.back().empty()) {
326 cert_status.pop_back();
327 }
328
329 return cert_status;
330}
331
332CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
333 const std::vector<std::optional<X509_CRL>>& crls,
334 std::chrono::system_clock::time_point ref_time) {
335 if(cert_path.empty()) {
336 throw Invalid_Argument("PKIX::check_crl cert_path empty");
337 }
338
339 CertificatePathStatusCodes cert_status(cert_path.size());
340 const X509_Time validation_time(ref_time);
341
342 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
343 std::set<Certificate_Status_Code>& status = cert_status.at(i);
344
345 if(i < crls.size() && crls[i].has_value()) {
346 const X509_Certificate& subject = cert_path.at(i);
347 const X509_Certificate& ca = cert_path.at(i + 1);
348
351 }
352
353 if(validation_time < crls[i]->this_update()) {
355 }
356
357 if(validation_time > crls[i]->next_update()) {
359 }
360
361 auto ca_key = ca.subject_public_key();
362 if(crls[i]->check_signature(*ca_key) == false) {
364 }
365
367
368 if(crls[i]->is_revoked(subject)) {
370 }
371
372 std::string dp = subject.crl_distribution_point();
373 if(!dp.empty()) {
374 if(dp != crls[i]->crl_issuing_distribution_point()) {
376 }
377 }
378
379 for(const auto& extension : crls[i]->extensions().extensions()) {
380 // XXX this is wrong - the OID might be defined but the extention not full parsed
381 // for example see #1652
382
383 // is the extension critical and unknown?
384 if(extension.second && !extension.first->oid_of().registered_oid()) {
385 /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
386 * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
387 */
389 }
390 }
391 }
392 }
393
394 while(!cert_status.empty() && cert_status.back().empty()) {
395 cert_status.pop_back();
396 }
397
398 return cert_status;
399}
400
401CertificatePathStatusCodes PKIX::check_crl(const std::vector<X509_Certificate>& cert_path,
402 const std::vector<Certificate_Store*>& certstores,
403 std::chrono::system_clock::time_point ref_time) {
404 if(cert_path.empty()) {
405 throw Invalid_Argument("PKIX::check_crl cert_path empty");
406 }
407
408 if(certstores.empty()) {
409 throw Invalid_Argument("PKIX::check_crl certstores empty");
410 }
411
412 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
413
414 for(size_t i = 0; i != cert_path.size(); ++i) {
415 for(auto certstore : certstores) {
416 crls[i] = certstore->find_crl_for(cert_path[i]);
417 if(crls[i]) {
418 break;
419 }
420 }
421 }
422
423 return PKIX::check_crl(cert_path, crls, ref_time);
424}
425
426#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
427
428CertificatePathStatusCodes PKIX::check_ocsp_online(const std::vector<X509_Certificate>& cert_path,
429 const std::vector<Certificate_Store*>& trusted_certstores,
430 std::chrono::system_clock::time_point ref_time,
431 std::chrono::milliseconds timeout,
432 const Path_Validation_Restrictions& restrictions) {
433 if(cert_path.empty()) {
434 throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
435 }
436
437 std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
438
439 size_t to_ocsp = 1;
440
441 if(restrictions.ocsp_all_intermediates()) {
442 to_ocsp = cert_path.size() - 1;
443 }
444 if(cert_path.size() == 1) {
445 to_ocsp = 0;
446 }
447
448 for(size_t i = 0; i < to_ocsp; ++i) {
449 const X509_Certificate& subject = cert_path.at(i);
450 const X509_Certificate& issuer = cert_path.at(i + 1);
451
452 if(subject.ocsp_responder().empty()) {
453 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
455 }));
456 } else {
457 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
458 OCSP::Request req(issuer, BigInt::decode(subject.serial_number()));
459
460 HTTP::Response http;
461 try {
462 http = HTTP::POST_sync(subject.ocsp_responder(),
463 "application/ocsp-request",
464 req.BER_encode(),
465 /*redirects*/ 1,
466 timeout);
467 } catch(std::exception&) {
468 // log e.what() ?
469 }
470 if(http.status_code() != 200) {
472 }
473 // Check the MIME type?
474
475 return OCSP::Response(http.body());
476 }));
477 }
478 }
479
480 std::vector<std::optional<OCSP::Response>> ocsp_responses;
481 ocsp_responses.reserve(ocsp_response_futures.size());
482
483 for(auto& ocsp_response_future : ocsp_response_futures) {
484 ocsp_responses.push_back(ocsp_response_future.get());
485 }
486
487 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
488}
489
490CertificatePathStatusCodes PKIX::check_crl_online(const std::vector<X509_Certificate>& cert_path,
491 const std::vector<Certificate_Store*>& certstores,
492 Certificate_Store_In_Memory* crl_store,
493 std::chrono::system_clock::time_point ref_time,
494 std::chrono::milliseconds timeout) {
495 if(cert_path.empty()) {
496 throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
497 }
498 if(certstores.empty()) {
499 throw Invalid_Argument("PKIX::check_crl_online certstores empty");
500 }
501
502 std::vector<std::future<std::optional<X509_CRL>>> future_crls;
503 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
504
505 for(size_t i = 0; i != cert_path.size(); ++i) {
506 const std::optional<X509_Certificate>& cert = cert_path.at(i);
507 for(auto certstore : certstores) {
508 crls[i] = certstore->find_crl_for(*cert);
509 if(crls[i].has_value()) {
510 break;
511 }
512 }
513
514 // TODO: check if CRL is expired and re-request?
515
516 // Only request if we don't already have a CRL
517 if(crls[i]) {
518 /*
519 We already have a CRL, so just insert this empty one to hold a place in the vector
520 so that indexes match up
521 */
522 future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
523 } else if(cert->crl_distribution_point().empty()) {
524 // Avoid creating a thread for this case
525 future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
526 throw Not_Implemented("No CRL distribution point for this certificate");
527 }));
528 } else {
529 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
530 auto http = HTTP::GET_sync(cert->crl_distribution_point(),
531 /*redirects*/ 1,
532 timeout);
533
534 http.throw_unless_ok();
535 // check the mime type?
536 return X509_CRL(http.body());
537 }));
538 }
539 }
540
541 for(size_t i = 0; i != future_crls.size(); ++i) {
542 if(future_crls[i].valid()) {
543 try {
544 crls[i] = future_crls[i].get();
545 } catch(std::exception&) {
546 // crls[i] left null
547 // todo: log exception e.what() ?
548 }
549 }
550 }
551
552 auto crl_status = PKIX::check_crl(cert_path, crls, ref_time);
553
554 if(crl_store) {
555 for(size_t i = 0; i != crl_status.size(); ++i) {
556 if(crl_status[i].contains(Certificate_Status_Code::VALID_CRL_CHECKED)) {
557 // better be non-null, we supposedly validated it
558 BOTAN_ASSERT_NOMSG(crls[i].has_value());
559 crl_store->add_crl(*crls[i]);
560 }
561 }
562 }
563
564 return crl_status;
565}
566
567#endif
568
569Certificate_Status_Code PKIX::build_certificate_path(std::vector<X509_Certificate>& cert_path,
570 const std::vector<Certificate_Store*>& trusted_certstores,
571 const X509_Certificate& end_entity,
572 const std::vector<X509_Certificate>& end_entity_extra) {
573 if(end_entity.is_self_signed()) {
575 }
576
577 /*
578 * This is an inelegant but functional way of preventing path loops
579 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
580 * fingerprints in the path. If there is a duplicate, we error out.
581 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
582 */
583 std::set<std::string> certs_seen;
584
585 cert_path.push_back(end_entity);
586 certs_seen.insert(end_entity.fingerprint("SHA-256"));
587
589 for(const auto& cert : end_entity_extra) {
590 ee_extras.add_certificate(cert);
591 }
592
593 // iterate until we reach a root or cannot find the issuer
594 for(;;) {
595 const X509_Certificate& last = cert_path.back();
596 const X509_DN issuer_dn = last.issuer_dn();
597 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
598
599 std::optional<X509_Certificate> issuer;
600 bool trusted_issuer = false;
601
602 for(Certificate_Store* store : trusted_certstores) {
603 issuer = store->find_cert(issuer_dn, auth_key_id);
604 if(issuer) {
605 trusted_issuer = true;
606 break;
607 }
608 }
609
610 if(!issuer) {
611 // fall back to searching supplemental certs
612 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
613 }
614
615 if(!issuer) {
617 }
618
619 const std::string fprint = issuer->fingerprint("SHA-256");
620
621 if(certs_seen.contains(fprint)) // already seen?
622 {
624 }
625
626 certs_seen.insert(fprint);
627 cert_path.push_back(*issuer);
628
629 if(issuer->is_self_signed()) {
630 if(trusted_issuer) {
632 } else {
634 }
635 }
636 }
637}
638
639/**
640 * utilities for PKIX::build_all_certificate_paths
641 */
642namespace {
643// <certificate, trusted?>
644using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>, bool>;
645} // namespace
646
647/**
648 * Build all possible certificate paths from the end certificate to self-signed trusted roots.
649 *
650 * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
651 * one of the encountered errors is returned arbitrarily.
652 *
653 * todo add a path building function that returns detailed information on errors encountered while building
654 * the potentially numerous path candidates.
655 *
656 * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
657 * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
658 * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
659 * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
660 * authority key id need not be unique among the certificates used for building the path. In such a case,
661 * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
662 *
663 */
664Certificate_Status_Code PKIX::build_all_certificate_paths(std::vector<std::vector<X509_Certificate>>& cert_paths_out,
665 const std::vector<Certificate_Store*>& trusted_certstores,
666 const std::optional<X509_Certificate>& end_entity,
667 const std::vector<X509_Certificate>& end_entity_extra) {
668 if(!cert_paths_out.empty()) {
669 throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
670 }
671
672 if(end_entity->is_self_signed()) {
674 }
675
676 /*
677 * Pile up error messages
678 */
679 std::vector<Certificate_Status_Code> stats;
680
682 for(const auto& cert : end_entity_extra) {
683 ee_extras.add_certificate(cert);
684 }
685
686 /*
687 * This is an inelegant but functional way of preventing path loops
688 * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
689 * fingerprints in the path. If there is a duplicate, we error out.
690 * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
691 */
692 std::set<std::string> certs_seen;
693
694 // new certs are added and removed from the path during the DFS
695 // it is copied into cert_paths_out when we encounter a trusted root
696 std::vector<X509_Certificate> path_so_far;
697
698 // todo can we assume that the end certificate is not trusted?
699 std::vector<cert_maybe_trusted> stack = {{end_entity, false}};
700
701 while(!stack.empty()) {
702 std::optional<X509_Certificate> last = stack.back().first;
703 // found a deletion marker that guides the DFS, backtracing
704 if(last == std::nullopt) {
705 stack.pop_back();
706 std::string fprint = path_so_far.back().fingerprint("SHA-256");
707 certs_seen.erase(fprint);
708 path_so_far.pop_back();
709 }
710 // process next cert on the path
711 else {
712 const bool trusted = stack.back().second;
713 stack.pop_back();
714
715 // certificate already seen?
716 const std::string fprint = last->fingerprint("SHA-256");
717 if(certs_seen.count(fprint) == 1) {
719 // the current path ended in a loop
720 continue;
721 }
722
723 // the current path ends here
724 if(last->is_self_signed()) {
725 // found a trust anchor
726 if(trusted) {
727 cert_paths_out.push_back(path_so_far);
728 cert_paths_out.back().push_back(*last);
729
730 continue;
731 }
732 // found an untrustworthy root
733 else {
735 continue;
736 }
737 }
738
739 const X509_DN issuer_dn = last->issuer_dn();
740 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
741
742 // search for trusted issuers
743 std::vector<X509_Certificate> trusted_issuers;
744 for(Certificate_Store* store : trusted_certstores) {
745 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
746 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
747 }
748
749 // search the supplemental certs
750 std::vector<X509_Certificate> misc_issuers = ee_extras.find_all_certs(issuer_dn, auth_key_id);
751
752 // if we could not find any issuers, the current path ends here
753 if(trusted_issuers.empty() && misc_issuers.empty()) {
755 continue;
756 }
757
758 // push the latest certificate onto the path_so_far
759 path_so_far.push_back(*last);
760 certs_seen.emplace(fprint);
761
762 // push a deletion marker on the stack for backtracing later
763 stack.push_back({std::optional<X509_Certificate>(), false});
764
765 for(const auto& trusted_cert : trusted_issuers) {
766 stack.push_back({trusted_cert, true});
767 }
768
769 for(const auto& misc : misc_issuers) {
770 stack.push_back({misc, false});
771 }
772 }
773 }
774
775 // could not construct any potentially valid path
776 if(cert_paths_out.empty()) {
777 if(stats.empty()) {
778 throw Internal_Error("X509 path building failed for unknown reasons");
779 } else {
780 // arbitrarily return the first error
781 return stats[0];
782 }
783 } else {
785 }
786}
787
790 const CertificatePathStatusCodes& ocsp,
791 const Path_Validation_Restrictions& restrictions) {
792 if(chain_status.empty()) {
793 throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
794 }
795
796 for(size_t i = 0; i != chain_status.size() - 1; ++i) {
797 bool had_crl = false, had_ocsp = false;
798
799 if(i < crl.size() && !crl[i].empty()) {
800 for(auto&& code : crl[i]) {
802 had_crl = true;
803 }
804 chain_status[i].insert(code);
805 }
806 }
807
808 if(i < ocsp.size() && !ocsp[i].empty()) {
809 for(auto&& code : ocsp[i]) {
813 {
814 had_ocsp = true;
815 }
816
817 chain_status[i].insert(code);
818 }
819 }
820
821 if(had_crl == false && had_ocsp == false) {
822 if((restrictions.require_revocation_information() && i == 0) ||
823 (restrictions.ocsp_all_intermediates() && i > 0)) {
824 chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
825 }
826 }
827 }
828}
829
831 if(cert_status.empty()) {
832 throw Invalid_Argument("PKIX::overall_status empty cert status");
833 }
834
836
837 // take the "worst" error as overall
838 for(const std::set<Certificate_Status_Code>& s : cert_status) {
839 if(!s.empty()) {
840 auto worst = *s.rbegin();
841 // Leave informative OCSP/CRL confirmations on cert-level status only
843 overall_status = worst;
844 }
845 }
846 }
847 return overall_status;
848}
849
850Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
851 const Path_Validation_Restrictions& restrictions,
852 const std::vector<Certificate_Store*>& trusted_roots,
853 std::string_view hostname,
854 Usage_Type usage,
855 std::chrono::system_clock::time_point ref_time,
856 std::chrono::milliseconds ocsp_timeout,
857 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
858 if(end_certs.empty()) {
859 throw Invalid_Argument("x509_path_validate called with no subjects");
860 }
861
862 X509_Certificate end_entity = end_certs[0];
863 std::vector<X509_Certificate> end_entity_extra;
864 for(size_t i = 1; i < end_certs.size(); ++i) {
865 end_entity_extra.push_back(end_certs[i]);
866 }
867
868 std::vector<std::vector<X509_Certificate>> cert_paths;
869 Certificate_Status_Code path_building_result =
870 PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
871
872 // If we cannot successfully build a chain to a trusted self-signed root, stop now
873 if(path_building_result != Certificate_Status_Code::OK) {
874 return Path_Validation_Result(path_building_result);
875 }
876
877 std::vector<Path_Validation_Result> error_results;
878 // Try validating all the potentially valid paths and return the first one to validate properly
879 for(auto cert_path : cert_paths) {
880 CertificatePathStatusCodes status = PKIX::check_chain(cert_path, ref_time, hostname, usage, restrictions);
881
882 CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, trusted_roots, ref_time);
883
884 CertificatePathStatusCodes ocsp_status;
885
886 if(!ocsp_resp.empty()) {
887 ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
888 }
889
890 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) {
891#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
892 ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions);
893#else
894 ocsp_status.resize(1);
895 ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
896#endif
897 }
898
899 PKIX::merge_revocation_status(status, crl_status, ocsp_status, restrictions);
900
901 Path_Validation_Result pvd(status, std::move(cert_path));
902 if(pvd.successful_validation()) {
903 return pvd;
904 } else {
905 error_results.push_back(std::move(pvd));
906 }
907 }
908 return error_results[0];
909}
910
912 const Path_Validation_Restrictions& restrictions,
913 const std::vector<Certificate_Store*>& trusted_roots,
914 std::string_view hostname,
915 Usage_Type usage,
916 std::chrono::system_clock::time_point when,
917 std::chrono::milliseconds ocsp_timeout,
918 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
919 std::vector<X509_Certificate> certs;
920 certs.push_back(end_cert);
921 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
922}
923
924Path_Validation_Result x509_path_validate(const std::vector<X509_Certificate>& end_certs,
925 const Path_Validation_Restrictions& restrictions,
926 const Certificate_Store& store,
927 std::string_view hostname,
928 Usage_Type usage,
929 std::chrono::system_clock::time_point when,
930 std::chrono::milliseconds ocsp_timeout,
931 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
932 std::vector<Certificate_Store*> trusted_roots;
933 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
934
935 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
936}
937
939 const Path_Validation_Restrictions& restrictions,
940 const Certificate_Store& store,
941 std::string_view hostname,
942 Usage_Type usage,
943 std::chrono::system_clock::time_point when,
944 std::chrono::milliseconds ocsp_timeout,
945 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
946 std::vector<X509_Certificate> certs;
947 certs.push_back(end_cert);
948
949 std::vector<Certificate_Store*> trusted_roots;
950 trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
951
952 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
953}
954
956 size_t key_strength,
957 bool ocsp_intermediates,
958 std::chrono::seconds max_ocsp_age,
959 std::unique_ptr<Certificate_Store> trusted_ocsp_responders,
960 bool ignore_trusted_root_time_range) :
961 m_require_revocation_information(require_rev),
962 m_ocsp_all_intermediates(ocsp_intermediates),
963 m_minimum_key_strength(key_strength),
964 m_max_ocsp_age(max_ocsp_age),
965 m_trusted_ocsp_responders(std::move(trusted_ocsp_responders)),
966 m_ignore_trusted_root_time_range(ignore_trusted_root_time_range) {
967 if(key_strength <= 80) {
968 m_trusted_hashes.insert("SHA-1");
969 }
970
971 m_trusted_hashes.insert("SHA-224");
972 m_trusted_hashes.insert("SHA-256");
973 m_trusted_hashes.insert("SHA-384");
974 m_trusted_hashes.insert("SHA-512");
975 m_trusted_hashes.insert("SHAKE-256(512)"); // Dilithium/ML-DSA
976 m_trusted_hashes.insert("SHAKE-256(912)"); // Ed448
977}
978
979namespace {
980CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses) {
982 for(const auto& status_set_i : all_statuses) {
983 std::set<Certificate_Status_Code> warning_set_i;
984 for(const auto& code : status_set_i) {
987 warning_set_i.insert(code);
988 }
989 }
990 warnings.push_back(warning_set_i);
991 }
992 return warnings;
993}
994} // namespace
995
997 std::vector<X509_Certificate>&& cert_chain) :
998 m_all_status(std::move(status)),
999 m_warnings(find_warnings(m_all_status)),
1000 m_cert_path(cert_chain),
1001 m_overall(PKIX::overall_status(m_all_status)) {}
1002
1004 if(m_cert_path.empty()) {
1005 throw Invalid_State("Path_Validation_Result::trust_root no path set");
1006 }
1008 throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
1009 }
1010
1011 return m_cert_path[m_cert_path.size() - 1];
1012}
1013
1018
1020 for(const auto& status_set_i : m_warnings) {
1021 if(!status_set_i.empty()) {
1022 return false;
1023 }
1024 }
1025 return true;
1026}
1027
1029 return m_warnings;
1030}
1031
1033 return status_string(result());
1034}
1035
1037 if(const char* s = to_string(code)) {
1038 return s;
1039 }
1040
1041 return "Unknown error";
1042}
1043
1045 const std::string sep(", ");
1046 std::ostringstream oss;
1047 for(size_t i = 0; i < m_warnings.size(); i++) {
1048 for(auto code : m_warnings[i]) {
1049 oss << "[" << std::to_string(i) << "] " << status_string(code) << sep;
1050 }
1051 }
1052
1053 std::string res = oss.str();
1054 // remove last sep
1055 if(res.size() >= sep.size()) {
1056 res = res.substr(0, res.size() - sep.size());
1057 }
1058 return res;
1059}
1060} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
const OID & oid() const
Definition asn1_obj.h:455
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:773
std::vector< X509_Certificate > find_all_certs(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
Definition certstor.cpp:75
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
bool certificate_known(const X509_Certificate &cert) const
Definition certstor.h:70
const std::vector< OID > & get_extension_oids() const
Definition pkix_types.h:416
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
Definition x509_ext.cpp:206
bool registered_oid() const
Definition asn1_oid.cpp:126
bool require_revocation_information() const
Definition x509path.h:100
const std::set< std::string > & trusted_hashes() const
Definition x509path.h:111
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)
Definition x509path.cpp:955
std::chrono::seconds max_ocsp_age() const
Definition x509path.h:122
bool ignore_trusted_root_time_range() const
Definition x509path.h:142
const Certificate_Store * trusted_ocsp_responders() const
Definition x509path.h:129
Certificate_Status_Code result() const
Definition x509path.h:186
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< X509_Certificate > &&cert_chain)
Definition x509path.cpp:996
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
bool is_CA_cert() const
Definition x509cert.cpp:374
std::string fingerprint(std::string_view hash_name="SHA-1") const
Definition x509cert.cpp:572
const X509_DN & subject_dn() const
Definition x509cert.cpp:362
uint32_t path_limit() const
Definition x509cert.cpp:382
const X509_Time & not_after() const
Definition x509cert.cpp:306
const std::vector< uint8_t > & authority_key_id() const
Definition x509cert.cpp:342
const Extensions & v3_extensions() const
Definition x509cert.cpp:406
bool allowed_usage(Key_Constraints usage) const
Definition x509cert.cpp:415
const X509_DN & issuer_dn() const
Definition x509cert.cpp:358
const std::vector< uint8_t > & v2_issuer_key_id() const
Definition x509cert.cpp:314
uint32_t x509_version() const
Definition x509cert.cpp:294
std::string crl_distribution_point() const
Definition x509cert.cpp:493
bool is_self_signed() const
Definition x509cert.cpp:298
bool is_serial_negative() const
Definition x509cert.cpp:354
std::unique_ptr< Public_Key > subject_public_key() const
Definition x509cert.cpp:546
const std::vector< uint8_t > & v2_subject_key_id() const
Definition x509cert.cpp:318
const X509_Time & not_before() const
Definition x509cert.cpp:302
static size_t lookup_ub(const OID &oid)
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition pkix_types.h:70
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
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)
void merge_revocation_status(CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, const Path_Validation_Restrictions &restrictions)
Definition x509path.cpp:788
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:569
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition x509path.cpp:830
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:277
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)
Definition x509path.cpp:664
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:36
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:332
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition x509path.h:29
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:850
Usage_Type
Definition x509cert.h:22
std::string to_string(ErrorType type)
Convert an ErrorType to string.
Definition exceptn.cpp:13
decltype(auto) concat(Ts &&... buffers)
Definition stl_util.h:258
Certificate_Status_Code
Definition pkix_enums.h:20