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