9#include <botan/x509path.h>
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>
23#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
24 #include <botan/internal/http_util.h>
34 std::chrono::system_clock::time_point ref_time,
35 std::string_view hostname,
38 if(cert_path.empty()) {
42 const bool is_end_entity_trust_anchor = (cert_path.size() == 1);
44 const X509_Time validation_time(ref_time);
49 for(
size_t i = 0; i != cert_path.size(); ++i) {
50 std::set<Certificate_Status_Code>& status = cert_status.at(i);
52 const bool at_trust_anchor = (i == cert_path.size() - 1);
69 std::unique_ptr<Public_Key> issuer_key;
84 status.insert(sig_status.first);
87 const std::string hash_used_for_signature = sig_status.second;
92 if(!trusted_hashes.empty() && !at_trust_anchor) {
93 if(!trusted_hashes.contains(hash_used_for_signature)) {
108 for(
size_t i = 0; i != cert_path.size(); ++i) {
109 for(
auto status : cert_status.at(i)) {
112 if(
static_cast<uint32_t
>(status) >= 5000) {
118 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname)) {
122 if(!cert_path[0].allowed_usage(usage)) {
141 for(
size_t i = 0; i != cert_path.size(); ++i) {
142 std::set<Certificate_Status_Code>& status = cert_status.at(i);
144 const bool at_trust_anchor = (i == cert_path.size() - 1);
147 const auto issuer = [&]() -> std::optional<X509_Certificate> {
148 if(!at_trust_anchor) {
149 return cert_path[i + 1];
162 if(issuer.has_value() && subject.
issuer_dn() != issuer->subject_dn()) {
176 if(dn_ub > 0 && dn_pair.second.size() > dn_ub) {
186 if(enforce_validity_period) {
193 if(validation_time > subject.
not_after()) {
194 if(enforce_validity_period) {
202 if(issuer.has_value() && !issuer->is_CA_cert() && !is_end_entity_trust_anchor) {
215 const auto& extensions_vec = extensions.
extensions();
216 if(subject.
x509_version() < 3 && !extensions_vec.empty()) {
220 for(
const auto& extension : extensions_vec) {
221 extension.first->validate(subject, issuer, cert_path, cert_status, i);
230 size_t max_path_length = cert_path.size();
231 for(
size_t i = cert_path.size() - 1; i > 0; --i) {
232 std::set<Certificate_Status_Code>& status = cert_status.at(i);
240 if(max_path_length > 0) {
241 max_path_length -= 1;
252 max_path_length = std::min(max_path_length, *path_len_constraint);
263 const std::vector<X509_Certificate>& extra_certs,
264 const std::vector<Certificate_Store*>& certstores,
265 std::chrono::system_clock::time_point ref_time,
282 if(signing_cert == ca) {
302 const auto ocsp_timeout = std::chrono::milliseconds::zero();
303 const auto relaxed_restrictions =
310 relaxed_restrictions,
317 return validation_result.
result();
320std::set<Certificate_Status_Code> evaluate_ocsp_response(
const OCSP::Response& ocsp_response,
323 const std::vector<X509_Certificate>& cert_path,
324 const std::vector<Certificate_Store*>& certstores,
325 std::chrono::system_clock::time_point ref_time,
328 if(
auto dummy_status = ocsp_response.dummy_status()) {
329 return {dummy_status.value()};
333 auto signing_cert = ocsp_response.find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
339 auto cert_status = verify_ocsp_signing_cert(
340 signing_cert.value(), ca,
concat(ocsp_response.certificates(), cert_path), certstores, ref_time, restrictions);
346 auto sig_status = ocsp_response.verify_signature(signing_cert.value());
352 return {ocsp_response.status_for(ca, subject, ref_time, restrictions.max_ocsp_age())};
358 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
359 const std::vector<Certificate_Store*>& certstores,
360 std::chrono::system_clock::time_point ref_time,
362 if(cert_path.empty()) {
368 for(
size_t i = 0; i != cert_path.size() - 1; ++i) {
372 if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
375 cert_status.at(i) = evaluate_ocsp_response(
376 ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
383 while(!cert_status.empty() && cert_status.back().empty()) {
384 cert_status.pop_back();
391 const std::vector<std::optional<X509_CRL>>& crls,
392 std::chrono::system_clock::time_point ref_time) {
393 if(cert_path.empty()) {
398 const X509_Time validation_time(ref_time);
400 for(
size_t i = 0; i != cert_path.size() - 1; ++i) {
401 std::set<Certificate_Status_Code>& status = cert_status.at(i);
403 if(i < crls.size() && crls[i].has_value()) {
411 if(validation_time < crls[i]->this_update()) {
415 if(crls[i]->next_update().time_is_set() && validation_time > crls[i]->next_update()) {
420 if(crls[i]->check_signature(*ca_key) ==
false) {
426 if(crls[i]->is_revoked(subject)) {
432 const auto crl_idp = crls[i]->crl_issuing_distribution_point();
434 if(std::find(dp.begin(), dp.end(), crl_idp) == dp.end()) {
439 for(
const auto& extension : crls[i]->extensions().extensions()) {
444 if(extension.second && !extension.first->oid_of().registered_oid()) {
454 while(!cert_status.empty() && cert_status.back().empty()) {
455 cert_status.pop_back();
462 const std::vector<Certificate_Store*>& certstores,
463 std::chrono::system_clock::time_point ref_time) {
464 if(cert_path.empty()) {
468 if(certstores.empty()) {
472 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
474 for(
size_t i = 0; i != cert_path.size(); ++i) {
475 for(
auto* certstore : certstores) {
476 crls[i] = certstore->find_crl_for(cert_path[i]);
486#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
489 const std::vector<Certificate_Store*>& trusted_certstores,
490 std::chrono::system_clock::time_point ref_time,
491 std::chrono::milliseconds timeout,
493 if(cert_path.empty()) {
497 std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
502 to_ocsp = cert_path.size() - 1;
504 if(cert_path.size() == 1) {
508 for(
size_t i = 0; i < to_ocsp; ++i) {
512 if(subject.ocsp_responder().empty()) {
513 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
517 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
523 "application/ocsp-request",
527 }
catch(std::exception&) {
530 if(http.status_code() != 200) {
540 std::vector<std::optional<OCSP::Response>> ocsp_responses;
541 ocsp_responses.reserve(ocsp_response_futures.size());
543 for(
auto& ocsp_response_future : ocsp_response_futures) {
544 ocsp_responses.push_back(ocsp_response_future.get());
547 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
551 const std::vector<Certificate_Store*>& certstores,
553 std::chrono::system_clock::time_point ref_time,
554 std::chrono::milliseconds timeout) {
555 if(cert_path.empty()) {
558 if(certstores.empty()) {
562 std::vector<std::future<std::optional<X509_CRL>>> future_crls;
563 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
565 for(
size_t i = 0; i != cert_path.size(); ++i) {
566 const std::optional<X509_Certificate>& cert = cert_path.at(i);
567 for(
auto* certstore : certstores) {
568 crls[i] = certstore->find_crl_for(*cert);
569 if(crls[i].has_value()) {
582 future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
583 }
else if(cert->crl_distribution_point().empty()) {
585 future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<X509_CRL> {
586 throw Not_Implemented(
"No CRL distribution point for this certificate");
589 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
594 http.throw_unless_ok();
601 for(
size_t i = 0; i != future_crls.size(); ++i) {
602 if(future_crls[i].valid()) {
604 crls[i] = future_crls[i].get();
605 }
catch(std::exception&) {
614 if(crl_store !=
nullptr) {
615 for(
size_t i = 0; i != crl_status.size(); ++i) {
619 crl_store->add_crl(*crls[i]);
630 const std::vector<Certificate_Store*>& trusted_certstores,
632 const std::vector<X509_Certificate>& end_entity_extra) {
633 std::vector<std::vector<X509_Certificate>> all_cert_paths;
634 const auto build_all_paths_res =
638 return build_all_paths_res;
643 const auto first_with_self_signed_anchor = std::ranges::find_if(all_cert_paths, [&](
const auto& left_path) {
645 return left_path.back().is_self_signed();
647 if(first_with_self_signed_anchor != all_cert_paths.end()) {
648 cert_path.insert(cert_path.end(), first_with_self_signed_anchor->begin(), first_with_self_signed_anchor->end());
650 cert_path.insert(cert_path.end(), all_cert_paths.front().begin(), all_cert_paths.front().end());
660using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>,
bool>;
681 const std::vector<Certificate_Store*>& trusted_certstores,
683 const std::vector<X509_Certificate>& end_entity_extra) {
684 if(!cert_paths_out.empty()) {
685 throw Invalid_Argument(
"PKIX::build_all_certificate_paths: cert_paths_out must be empty");
687 if(std::ranges::any_of(trusted_certstores, [](
auto* ptr) {
return ptr ==
nullptr; })) {
692 return std::ranges::any_of(trusted_certstores,
699 std::vector<Certificate_Status_Code> stats;
702 for(
const auto& cert : end_entity_extra) {
703 if(!cert_in_any_trusted_store(cert)) {
714 std::set<std::string> certs_seen;
718 std::vector<X509_Certificate> path_so_far;
720 std::vector<cert_maybe_trusted> stack = {{end_entity, cert_in_any_trusted_store(end_entity)}};
722 while(!stack.empty()) {
723 std::optional<X509_Certificate> last = stack.back().first;
725 if(last == std::nullopt) {
727 const std::string fprint = path_so_far.back().fingerprint(
"SHA-256");
728 certs_seen.erase(fprint);
729 path_so_far.pop_back();
733 const bool trusted = stack.back().second;
737 const std::string fprint = last->fingerprint(
"SHA-256");
738 if(certs_seen.count(fprint) == 1) {
749 cert_paths_out.push_back(path_so_far);
750 cert_paths_out.back().push_back(*last);
751 }
else if(last->is_self_signed()) {
756 const X509_DN issuer_dn = last->issuer_dn();
757 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
760 std::vector<X509_Certificate> trusted_issuers;
762 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
763 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
767 const std::vector<X509_Certificate> misc_issuers = ee_extras.
find_all_certs(issuer_dn, auth_key_id);
770 if(trusted_issuers.empty() && misc_issuers.empty()) {
776 path_so_far.push_back(*last);
777 certs_seen.emplace(fprint);
780 stack.push_back({std::optional<X509_Certificate>(),
false});
782 for(
const auto& trusted_cert : trusted_issuers) {
783 stack.push_back({trusted_cert,
true});
786 for(
const auto& misc : misc_issuers) {
787 stack.push_back({misc,
false});
793 if(cert_paths_out.empty()) {
795 throw Internal_Error(
"X509 path building failed for unknown reasons");
809 if(chain_status.empty()) {
810 throw Invalid_Argument(
"PKIX::merge_revocation_status chain_status was empty");
813 for(
size_t i = 0; i != chain_status.size() - 1; ++i) {
814 bool had_crl =
false;
815 bool had_ocsp =
false;
817 if(i < crl_status.size() && !crl_status[i].empty()) {
818 for(
auto&& code : crl_status[i]) {
822 chain_status[i].insert(code);
826 if(i < ocsp_status.size() && !ocsp_status[i].empty()) {
827 for(
auto&& code : ocsp_status[i]) {
835 chain_status[i].insert(code);
839 if(had_crl ==
false && had_ocsp ==
false) {
849 if(cert_status.empty()) {
856 for(
const std::set<Certificate_Status_Code>& s : cert_status) {
858 auto worst = *s.rbegin();
870 const std::vector<Certificate_Store*>& trusted_roots,
871 std::string_view hostname,
873 std::chrono::system_clock::time_point ref_time,
874 std::chrono::milliseconds ocsp_timeout,
875 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
876 if(end_certs.empty()) {
881 std::vector<X509_Certificate> end_entity_extra;
882 for(
size_t i = 1; i < end_certs.size(); ++i) {
883 end_entity_extra.push_back(end_certs[i]);
886 std::vector<std::vector<X509_Certificate>> cert_paths;
898 auto has_non_self_signed_trust_anchor = [](
const auto& cert_path) {
899 return cert_path.empty() || !cert_path.back().is_self_signed();
901 std::erase_if(cert_paths, has_non_self_signed_trust_anchor);
903 if(cert_paths.empty()) {
907 std::vector<Path_Validation_Result> error_results;
909 for(
auto cert_path : cert_paths) {
916 if(!ocsp_resp.empty()) {
917 ocsp_status =
PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
920 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0)) {
921#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
922 ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, ocsp_timeout, restrictions);
924 ocsp_status.resize(1);
935 error_results.push_back(std::move(pvd));
938 return error_results[0];
943 const std::vector<Certificate_Store*>& trusted_roots,
944 std::string_view hostname,
946 std::chrono::system_clock::time_point when,
947 std::chrono::milliseconds ocsp_timeout,
948 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
949 std::vector<X509_Certificate> certs;
950 certs.push_back(end_cert);
951 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
957 std::string_view hostname,
959 std::chrono::system_clock::time_point when,
960 std::chrono::milliseconds ocsp_timeout,
961 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
962 std::vector<Certificate_Store*> trusted_roots;
965 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
971 std::string_view hostname,
973 std::chrono::system_clock::time_point when,
974 std::chrono::milliseconds ocsp_timeout,
975 const std::vector<std::optional<OCSP::Response>>& ocsp_resp) {
976 std::vector<X509_Certificate> certs;
977 certs.push_back(end_cert);
979 std::vector<Certificate_Store*> trusted_roots;
982 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
987 bool ocsp_intermediates,
992 m_require_revocation_information(require_rev),
993 m_ocsp_all_intermediates(ocsp_intermediates),
994 m_minimum_key_strength(key_strength),
999 if(key_strength <= 80) {
1000 m_trusted_hashes.insert(
"SHA-1");
1003 m_trusted_hashes.insert(
"SHA-224");
1004 m_trusted_hashes.insert(
"SHA-256");
1005 m_trusted_hashes.insert(
"SHA-384");
1006 m_trusted_hashes.insert(
"SHA-512");
1007 m_trusted_hashes.insert(
"SHAKE-256(512)");
1008 m_trusted_hashes.insert(
"SHAKE-256(912)");
1014 for(
const auto& status_set_i : all_statuses) {
1015 std::set<Certificate_Status_Code> warning_set_i;
1016 for(
const auto& code : status_set_i) {
1019 warning_set_i.insert(code);
1022 warnings.push_back(warning_set_i);
1029 std::vector<X509_Certificate>&& cert_chain) :
1030 m_all_status(std::move(status)),
1031 m_warnings(find_warnings(m_all_status)),
1032 m_cert_path(std::move(cert_chain)),
1033 m_overall(
PKIX::overall_status(m_all_status)) {}
1036 if(m_cert_path.empty()) {
1037 throw Invalid_State(
"Path_Validation_Result::trust_root no path set");
1040 throw Invalid_State(
"Path_Validation_Result::trust_root meaningless with invalid status");
1043 return m_cert_path[m_cert_path.size() - 1];
1052 for(
const auto& status_set_i : m_warnings) {
1053 if(!status_set_i.empty()) {
1073 return "Unknown error";
1077 const std::string sep(
", ");
1078 std::ostringstream oss;
1079 for(
size_t i = 0; i < m_warnings.size(); i++) {
1080 for(
auto code : m_warnings[i]) {
1081 oss <<
"[" << std::to_string(i) <<
"] " <<
status_string(code) << sep;
1085 std::string res = oss.str();
1087 if(res.size() >= sep.size()) {
1088 res = res.substr(0, res.size() - sep.size());
#define BOTAN_ASSERT_NOMSG(expr)
static BigInt from_bytes(std::span< const uint8_t > bytes)
std::vector< X509_Certificate > find_all_certs(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
void add_certificate(const X509_Certificate &cert)
bool certificate_known(const X509_Certificate &cert) const
const std::vector< OID > & get_extension_oids() const
std::vector< std::pair< std::unique_ptr< Certificate_Extension >, bool > > extensions() const
bool registered_oid() const
bool require_revocation_information() const
bool ocsp_all_intermediates() const
const std::set< std::string > & trusted_hashes() const
std::chrono::seconds max_ocsp_age() const
bool ignore_trusted_root_time_range() const
bool require_self_signed_trust_anchors() const
size_t minimum_key_strength() const
const Certificate_Store * trusted_ocsp_responders() const
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
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< X509_Certificate > &&cert_chain)
bool successful_validation() const
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
const X509_Time & not_after() const
std::optional< size_t > path_length_constraint() const
const Extensions & v3_extensions() const
std::vector< std::string > crl_distribution_points() const
bool allowed_usage(Key_Constraints usage) const
const X509_DN & issuer_dn() const
const std::vector< uint8_t > & v2_issuer_key_id() const
uint32_t x509_version() const
bool is_self_signed() const
bool is_serial_negative() const
std::unique_ptr< Public_Key > subject_public_key() const
const std::vector< uint8_t > & v2_subject_key_id() const
const X509_Time & not_before() const
static size_t lookup_ub(const OID &oid)
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
const AlgorithmIdentifier & signature_algorithm() const
std::pair< Certificate_Status_Code, std::string > verify_signature(const Public_Key &key) const
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)
void merge_revocation_status(CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, const Path_Validation_Restrictions &restrictions)
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)
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
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)
CertificatePathStatusCodes check_chain(const std::vector< X509_Certificate > &cert_path, std::chrono::system_clock::time_point ref_time, std::string_view hostname, Usage_Type usage, const Path_Validation_Restrictions &restrictions)
CertificatePathStatusCodes check_crl(const std::vector< X509_Certificate > &cert_path, const std::vector< std::optional< X509_CRL > > &crls, std::chrono::system_clock::time_point ref_time)
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
@ TRUSTED_CERT_NOT_YET_VALID
@ DUPLICATE_CERT_EXTENSION
@ OCSP_RESPONSE_MISSING_KEYUSAGE
@ TRUSTED_CERT_HAS_EXPIRED
@ OCSP_ISSUER_NOT_TRUSTED
@ CA_CERT_NOT_FOR_CRL_ISSUER
@ CA_CERT_NOT_FOR_CERT_ISSUER
@ OCSP_SERVER_NOT_AVAILABLE
@ V2_IDENTIFIERS_IN_V1_CERT
@ SIGNATURE_METHOD_TOO_WEAK
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)
constexpr auto concat(Rs &&... ranges)
std::string to_string(ErrorType type)
Convert an ErrorType to string.