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>
21#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
23 #include <botan/internal/http_util.h>
33 std::chrono::system_clock::time_point ref_time,
34 std::string_view hostname,
41 const bool self_signed_ee_cert = (cert_path.size() == 1);
47 if(!hostname.empty() && !cert_path[0].matches_dns_name(hostname))
50 if(!cert_path[0].allowed_usage(usage))
58 cert_path[0].is_CA_cert() ==
false)
71 for(
size_t i = 0; i != cert_path.size(); ++i)
73 std::set<Certificate_Status_Code>& status = cert_status.at(i);
75 const bool at_self_signed_root = (i == cert_path.size() - 1);
78 const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
102 if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
112 if(validation_time > subject.
not_after())
116 if(!issuer.
is_CA_cert() && !self_signed_ee_cert)
139 const std::string hash_used_for_signature = sig_status.second;
144 if(!trusted_hashes.empty() && !at_self_signed_root)
146 if(!trusted_hashes.contains(hash_used_for_signature))
152 status.insert(sig_status.first);
172 const auto& extensions_vec = extensions.
extensions();
173 if(subject.
x509_version() < 3 && !extensions_vec.empty())
177 for(
auto& extension : extensions_vec)
179 extension.first->validate(subject, issuer, cert_path, cert_status, i);
188 size_t max_path_length = cert_path.size();
189 for(
size_t i = cert_path.size() - 1; i > 0 ; --i)
191 std::set<Certificate_Status_Code>& status = cert_status.at(i);
200 if(max_path_length > 0)
214 if(subject.
path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject.
path_limit() < max_path_length)
228 const std::vector<X509_Certificate>& extra_certs,
229 const std::vector<Certificate_Store*>& certstores,
230 std::chrono::system_clock::time_point ref_time,
247 if(signing_cert == ca)
266 const auto ocsp_timeout = std::chrono::milliseconds::zero();
267 const auto relaxed_restrictions =
274 concat(std::vector{signing_cert}, extra_certs),
275 relaxed_restrictions,
282 return validation_result.
result();
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,
294 if(cert_path.empty())
299 for(
size_t i = 0; i != cert_path.size() - 1; ++i)
301 std::set<Certificate_Status_Code>& status = cert_status.at(i);
306 if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt)
311 const auto& ocsp_response = ocsp_responses.at(i);
313 if(
auto dummy_status = ocsp_response->dummy_status())
316 status.insert(dummy_status.value());
318 else if(
auto signing_cert = ocsp_response->find_signing_certificate(ca, restrictions.
trusted_ocsp_responders());
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);
326 status.insert(ocsp_signing_cert_status);
331 status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.
max_ocsp_age()));
341 while(!cert_status.empty() && cert_status.back().empty())
342 cert_status.pop_back();
349 const std::vector<std::optional<X509_CRL>>& crls,
350 std::chrono::system_clock::time_point ref_time)
352 if(cert_path.empty())
356 const X509_Time validation_time(ref_time);
358 for(
size_t i = 0; i != cert_path.size() - 1; ++i)
360 std::set<Certificate_Status_Code>& status = cert_status.at(i);
362 if(i < crls.size() && crls[i].has_value())
370 if(validation_time < crls[i]->this_update())
373 if(validation_time > crls[i]->next_update())
377 if(crls[i]->check_signature(*ca_key) ==
false)
382 if(crls[i]->is_revoked(subject))
388 if(dp != crls[i]->crl_issuing_distribution_point())
394 for(
const auto& extension : crls[i]->extensions().extensions())
400 if(extension.second && !extension.first->oid_of().registered_oid())
412 while(!cert_status.empty() && cert_status.back().empty())
413 cert_status.pop_back();
420 const std::vector<Certificate_Store*>& certstores,
421 std::chrono::system_clock::time_point ref_time)
423 if(cert_path.empty())
426 if(certstores.empty())
429 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
431 for(
size_t i = 0; i != cert_path.size(); ++i)
433 for(
auto certstore : certstores)
435 crls[i] = certstore->find_crl_for(cert_path[i]);
444#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
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,
453 if(cert_path.empty())
456 std::vector<std::future<std::optional<OCSP::Response>>> ocsp_response_futures;
461 to_ocsp = cert_path.size() - 1;
462 if(cert_path.size() == 1)
465 for(
size_t i = 0; i < to_ocsp; ++i)
472 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::optional<OCSP::Response> {
478 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::optional<OCSP::Response> {
485 "application/ocsp-request",
490 catch(std::exception&)
494 if (http.status_code() != 200)
498 return OCSP::Response(http.body());
503 std::vector<std::optional<OCSP::Response>> ocsp_responses;
504 ocsp_responses.reserve(ocsp_response_futures.size());
506 for(
auto& ocsp_response_future : ocsp_response_futures)
508 ocsp_responses.push_back(ocsp_response_future.get());
511 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, restrictions);
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)
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");
526 std::vector<std::future<std::optional<X509_CRL>>> future_crls;
527 std::vector<std::optional<X509_CRL>> crls(cert_path.size());
529 for(
size_t i = 0; i != cert_path.size(); ++i)
531 const std::optional<X509_Certificate>& cert = cert_path.at(i);
532 for(
auto certstore : certstores)
534 crls[i] = certstore->find_crl_for(*cert);
535 if(crls[i].has_value())
548 future_crls.emplace_back(std::future<std::optional<X509_CRL>>());
550 else if(cert->crl_distribution_point().empty())
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");
559 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::optional<X509_CRL> {
563 http.throw_unless_ok();
565 return X509_CRL(http.body());
570 for(
size_t i = 0; i != future_crls.size(); ++i)
572 if(future_crls[i].valid())
576 crls[i] = future_crls[i].get();
578 catch(std::exception&)
590 for(
size_t i = 0; i != crl_status.size(); ++i)
596 crl_store->add_crl(*crls[i]);
608 const std::vector<Certificate_Store*>& trusted_certstores,
610 const std::vector<X509_Certificate>& end_entity_extra)
623 std::set<std::string> certs_seen;
625 cert_path.push_back(end_entity);
626 certs_seen.insert(end_entity.
fingerprint(
"SHA-256"));
629 for(
const auto& cert : end_entity_extra)
639 std::optional<X509_Certificate> issuer;
640 bool trusted_issuer =
false;
644 issuer = store->find_cert(issuer_dn, auth_key_id);
647 trusted_issuer =
true;
655 issuer = ee_extras.
find_cert(issuer_dn, auth_key_id);
661 const std::string fprint = issuer->fingerprint(
"SHA-256");
663 if(certs_seen.contains(fprint))
668 certs_seen.insert(fprint);
669 cert_path.push_back(*issuer);
671 if(issuer->is_self_signed())
691using cert_maybe_trusted = std::pair<std::optional<X509_Certificate>,
bool>;
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)
717 if(!cert_paths_out.empty())
719 throw Invalid_Argument(
"PKIX::build_all_certificate_paths: cert_paths_out must be empty");
722 if(end_entity->is_self_signed())
730 std::vector<Certificate_Status_Code> stats;
733 for(
const auto& cert : end_entity_extra)
744 std::set<std::string> certs_seen;
748 std::vector<X509_Certificate> path_so_far;
751 std::vector<cert_maybe_trusted> stack = { {end_entity,
false} };
753 while(!stack.empty())
755 std::optional<X509_Certificate> last = stack.back().first;
757 if(last == std::nullopt)
760 std::string fprint = path_so_far.back().fingerprint(
"SHA-256");
761 certs_seen.erase(fprint);
762 path_so_far.pop_back();
767 const bool trusted = stack.back().second;
771 const std::string fprint = last->fingerprint(
"SHA-256");
772 if(certs_seen.count(fprint) == 1)
780 if(last->is_self_signed())
785 cert_paths_out.push_back(path_so_far);
786 cert_paths_out.back().push_back(*last);
798 const X509_DN issuer_dn = last->issuer_dn();
799 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
802 std::vector<X509_Certificate> trusted_issuers;
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());
810 std::vector<X509_Certificate> misc_issuers =
814 if(trusted_issuers.size() + misc_issuers.size() == 0)
821 path_so_far.push_back(*last);
822 certs_seen.emplace(fprint);
825 stack.push_back({std::optional<X509_Certificate>(),
false});
827 for(
const auto& trusted_cert : trusted_issuers)
829 stack.push_back({trusted_cert,
true});
832 for(
const auto& misc : misc_issuers)
834 stack.push_back({misc,
false});
840 if(cert_paths_out.empty())
843 throw Internal_Error(
"X509 path building failed for unknown reasons");
860 if(chain_status.empty())
861 throw Invalid_Argument(
"PKIX::merge_revocation_status chain_status was empty");
863 for(
size_t i = 0; i != chain_status.size() - 1; ++i)
865 bool had_crl =
false, had_ocsp =
false;
867 if(i < crl.size() && !crl[i].empty())
869 for(
auto&& code : crl[i])
875 chain_status[i].insert(code);
879 if(i < ocsp.size() && !ocsp[i].empty())
881 for(
auto&& code : ocsp[i])
890 chain_status[i].insert(code);
894 if(had_crl ==
false && had_ocsp ==
false)
907 if(cert_status.empty())
913 for(
const std::set<Certificate_Status_Code>& s : cert_status)
917 auto worst = *s.rbegin();
929 const std::vector<X509_Certificate>& end_certs,
931 const std::vector<Certificate_Store*>& trusted_roots,
932 std::string_view hostname,
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)
938 if(end_certs.empty())
944 std::vector<X509_Certificate> end_entity_extra;
945 for(
size_t i = 1; i < end_certs.size(); ++i)
947 end_entity_extra.push_back(end_certs[i]);
950 std::vector<std::vector<X509_Certificate>> cert_paths;
959 std::vector<Path_Validation_Result> error_results;
961 for(
auto cert_path : cert_paths)
965 hostname, usage, restrictions);
972 if(!ocsp_resp.empty())
974 ocsp_status =
PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions);
977 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
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);
983 ocsp_status.resize(1);
997 error_results.push_back(std::move(pvd));
1000 return error_results[0];
1006 const std::vector<Certificate_Store*>& trusted_roots,
1007 std::string_view hostname,
1009 std::chrono::system_clock::time_point when,
1010 std::chrono::milliseconds ocsp_timeout,
1011 const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
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);
1019 const std::vector<X509_Certificate>& end_certs,
1022 std::string_view hostname,
1024 std::chrono::system_clock::time_point when,
1025 std::chrono::milliseconds ocsp_timeout,
1026 const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
1028 std::vector<Certificate_Store*> trusted_roots;
1031 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1038 std::string_view hostname,
1040 std::chrono::system_clock::time_point when,
1041 std::chrono::milliseconds ocsp_timeout,
1042 const std::vector<std::optional<OCSP::Response>>& ocsp_resp)
1044 std::vector<X509_Certificate> certs;
1045 certs.push_back(end_cert);
1047 std::vector<Certificate_Store*> trusted_roots;
1050 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
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))
1064 if(key_strength <= 80)
1065 { m_trusted_hashes.insert(
"SHA-1"); }
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)");
1078 for(
const auto& status_set_i : all_statuses)
1080 std::set<Certificate_Status_Code> warning_set_i;
1081 for(
const auto& code : status_set_i)
1086 warning_set_i.insert(code);
1089 warnings.push_back(warning_set_i);
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))
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");
1111 return m_cert_path[m_cert_path.size()-1];
1123 for(
const auto& status_set_i : m_warnings)
1124 if(!status_set_i.empty())
1144 return "Unknown error";
1149 const std::string sep(
", ");
1150 std::ostringstream oss;
1151 for(
size_t i = 0; i < m_warnings.size(); i++)
1153 for(
auto code : m_warnings[i])
1155 oss <<
"[" << std::to_string(i) <<
"] " <<
status_string(code) << sep;
1159 std::string res = oss.str();
1161 if(res.size() >= sep.size())
1162 res = res.substr(0, res.size() - sep.size());
#define BOTAN_ASSERT_NOMSG(expr)
static BigInt decode(const uint8_t buf[], size_t length)
std::vector< X509_Certificate > find_all_certs(const X509_DN &subject_dn, const std::vector< uint8_t > &key_id) const override
std::optional< X509_Certificate > find_cert(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
size_t minimum_key_strength() const
const Certificate_Store * trusted_ocsp_responders() const
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 >())
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 std::vector< uint8_t > & serial_number() const
std::string fingerprint(std::string_view hash_name="SHA-1") const
const X509_DN & subject_dn() const
uint32_t path_limit() const
const X509_Time & not_after() const
const std::vector< uint8_t > & authority_key_id() const
const Extensions & v3_extensions() const
bool allowed_usage(Key_Constraints usage) const
const X509_DN & issuer_dn() const
const std::vector< uint8_t > & v2_issuer_key_id() const
std::string ocsp_responder() const
uint32_t x509_version() const
std::string crl_distribution_point() 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)
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)
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)
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
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)
decltype(auto) concat(Ts &&...buffers)
std::string to_string(ErrorType type)
Convert an ErrorType to string.
@ DUPLICATE_CERT_EXTENSION
@ OCSP_RESPONSE_MISSING_KEYUSAGE
@ 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