From cdd2e2babc0506d0c727aff06d1fc430cedbf695 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Mon, 21 Nov 2016 20:07:26 -0500 Subject: Refactor X.509 path validation Splits path building, path validation, CRL checks, and OCSP checks into distinct functions in namespace PKIX. The previous path validation APIs remain. Fixes to OCSP to store more information and to handle modern OCSP setups in at least some situations. --- src/cli/x509.cpp | 7 +- src/lib/x509/info.txt | 3 +- src/lib/x509/ocsp.cpp | 208 +++++++------ src/lib/x509/ocsp.h | 62 +++- src/lib/x509/ocsp_types.cpp | 18 +- src/lib/x509/ocsp_types.h | 5 +- src/lib/x509/x509path.cpp | 696 ++++++++++++++++++++++++++++++-------------- src/lib/x509/x509path.h | 108 ++++++- 8 files changed, 748 insertions(+), 359 deletions(-) diff --git a/src/cli/x509.cpp b/src/cli/x509.cpp index 25261a2d1..7ae980d76 100644 --- a/src/cli/x509.cpp +++ b/src/cli/x509.cpp @@ -104,7 +104,8 @@ class Cert_Info final : public Command BOTAN_REGISTER_COMMAND("cert_info", Cert_Info); -#if defined(BOTAN_HAS_OCSP) +#if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL) + class OCSP_Check final : public Command { public: @@ -119,7 +120,7 @@ class OCSP_Check final : public Command cas.add_certificate(issuer); Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, &cas); - auto status = resp.status_for(issuer, subject); + auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now()); if(status == Botan::Certificate_Status_Code::VERIFIED) { @@ -135,7 +136,7 @@ class OCSP_Check final : public Command BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check); -#endif // OCSP +#endif // OCSP && HTTP class Cert_Verify final : public Command { diff --git a/src/lib/x509/info.txt b/src/lib/x509/info.txt index be1e879c3..7e6afc5ad 100644 --- a/src/lib/x509/info.txt +++ b/src/lib/x509/info.txt @@ -1,10 +1,9 @@ define X509_CERTIFICATES 20151023 -define OCSP 20131128 +define OCSP 20161118 asn1 datastor -http_util pubkey sha1 diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp index 761c5b436..af7126580 100644 --- a/src/lib/x509/ocsp.cpp +++ b/src/lib/x509/ocsp.cpp @@ -14,7 +14,10 @@ #include #include #include -#include + +#if defined(BOTAN_HAS_HTTP_UTIL) + #include +#endif namespace Botan { @@ -22,6 +25,7 @@ namespace OCSP { namespace { +// TODO: should this be in a header somewhere? void decode_optional_list(BER_Decoder& ber, ASN1_Tag tag, std::vector& output) @@ -44,65 +48,18 @@ void decode_optional_list(BER_Decoder& ber, } } -void check_signature(const std::vector& tbs_response, - const AlgorithmIdentifier& sig_algo, - const std::vector& signature, - const X509_Certificate& cert) - { - std::unique_ptr pub_key(cert.subject_public_key()); - - const std::vector sig_info = - split_on(OIDS::lookup(sig_algo.oid), '/'); - - if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name()) - throw Exception("Information in OCSP response does not match cert"); - - std::string padding = sig_info[1]; - Signature_Format format = - (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363; - - PK_Verifier verifier(*pub_key, padding, format); - - if(!verifier.verify_message(ASN1::put_in_sequence(tbs_response), signature)) - throw Exception("Signature on OCSP response does not verify"); - } +} -void check_signature(const std::vector& tbs_response, - const AlgorithmIdentifier& sig_algo, - const std::vector& signature, - const Certificate_Store& trusted_roots, - const std::vector& certs) +Request::Request(const X509_Certificate& issuer_cert, + const X509_Certificate& subject_cert) : + m_issuer(issuer_cert), + m_subject(subject_cert), + m_certid(m_issuer, m_subject) { - if(certs.size() < 1) - throw Invalid_Argument("Short cert chain for check_signature"); - - if(trusted_roots.certificate_known(certs[0])) - return check_signature(tbs_response, sig_algo, signature, certs[0]); - - // Otherwise attempt to chain the signing cert to a trust root - - if(!certs[0].allowed_extended_usage("PKIX.OCSPSigning")) - throw Exception("OCSP response cert does not allow OCSP signing"); - - auto result = x509_path_validate(certs, Path_Validation_Restrictions(), trusted_roots); - - if(!result.successful_validation()) - throw Exception("Certificate validation failure: " + result.result_string()); - - if(!trusted_roots.certificate_known(result.trust_root())) // not needed anymore? - throw Exception("Certificate chain roots in unknown/untrusted CA"); - - const std::vector>& cert_path = result.cert_path(); - - check_signature(tbs_response, sig_algo, signature, *cert_path[0]); } -} - std::vector Request::BER_encode() const { - CertID certid(m_issuer, m_subject); - return DER_Encoder().start_cons(SEQUENCE) .start_cons(SEQUENCE) .start_explicit(0) @@ -110,7 +67,7 @@ std::vector Request::BER_encode() const .end_explicit() .start_cons(SEQUENCE) .start_cons(SEQUENCE) - .encode(certid) + .encode(m_certid) .end_cons() .end_cons() .end_cons() @@ -122,8 +79,7 @@ std::string Request::base64_encode() const return Botan::base64_encode(BER_encode()); } -Response::Response(const Certificate_Store& trusted_roots, - const std::vector& response_bits) +Response::Response(const std::vector& response_bits) { BER_Decoder response_outer = BER_Decoder(response_bits).start_cons(SEQUENCE); @@ -145,71 +101,141 @@ Response::Response(const Certificate_Store& trusted_roots, BER_Decoder basicresponse = BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE); - std::vector tbs_bits; - AlgorithmIdentifier sig_algo; - std::vector signature; - std::vector certs; - basicresponse.start_cons(SEQUENCE) - .raw_bytes(tbs_bits) + .raw_bytes(m_tbs_bits) .end_cons() - .decode(sig_algo) - .decode(signature, BIT_STRING); - decode_optional_list(basicresponse, ASN1_Tag(0), certs); + .decode(m_sig_algo) + .decode(m_signature, BIT_STRING); + decode_optional_list(basicresponse, ASN1_Tag(0), m_certs); size_t responsedata_version = 0; - X509_DN name; - std::vector key_hash; - X509_Time produced_at; Extensions extensions; - BER_Decoder(tbs_bits) + BER_Decoder(m_tbs_bits) .decode_optional(responsedata_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) - .decode_optional(name, ASN1_Tag(1), + .decode_optional(m_signer_name, ASN1_Tag(1), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) - .decode_optional_string(key_hash, OCTET_STRING, 2, + .decode_optional_string(m_key_hash, OCTET_STRING, 2, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)) - .decode(produced_at) + .decode(m_produced_at) .decode_list(m_responses) .decode_optional(extensions, ASN1_Tag(1), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)); + } + + response_outer.end_cons(); + } + +Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const + { + try + { + std::unique_ptr pub_key(issuer.subject_public_key()); + + const std::vector sig_info = + split_on(OIDS::lookup(m_sig_algo.oid), '/'); - if(certs.empty()) + if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name()) + return Certificate_Status_Code::OCSP_RESPONSE_INVALID; + + std::string padding = sig_info[1]; + Signature_Format format = (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363; + + PK_Verifier verifier(*pub_key, padding, format); + + if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) + return Certificate_Status_Code::OCSP_SIGNATURE_OK; + else + return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; + } + catch(Exception&) + { + return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; + } + } + +Certificate_Status_Code Response::check_signature(const std::vector& trusted_roots, + const std::vector>& ee_cert_path) const + { + std::shared_ptr signing_cert; + + for(size_t i = 0; i != trusted_roots.size(); ++i) + { + if(m_signer_name.empty() && m_key_hash.empty()) + return Certificate_Status_Code::OCSP_RESPONSE_INVALID; + + if(!m_signer_name.empty()) { - if(auto cert = trusted_roots.find_cert(name, std::vector())) - certs.push_back(*cert); - else - throw Exception("Could not find certificate that signed OCSP response"); + signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector()); + if(signing_cert) + { + break; + } + } + + if(m_key_hash.size() > 0) + { + signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash); + if(signing_cert) + { + break; + } } + } - check_signature(tbs_bits, sig_algo, signature, trusted_roots, certs); + if(!signing_cert) + { + for(size_t i = 1; i < ee_cert_path.size(); ++i) + { + // Check all CA certificates in the (assumed validated) EE cert path + if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name) + { + signing_cert = ee_cert_path[i]; + break; + } + + if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash) + { + signing_cert = ee_cert_path[i]; + break; + } + } } - response_outer.end_cons(); + // TODO: this ignores m_certs + + if(!signing_cert) + return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND; + + if(!signing_cert->allowed_extended_usage("PKIX.OCSPSigning")) + return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE; + + return this->verify_signature(*signing_cert); } Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, - const X509_Certificate& subject) const + const X509_Certificate& subject, + std::chrono::system_clock::time_point ref_time) const { for(const auto& response : m_responses) { if(response.certid().is_id_for(issuer, subject)) { - X509_Time current_time(std::chrono::system_clock::now()); + X509_Time x509_ref_time(ref_time); if(response.cert_status() == 1) return Certificate_Status_Code::CERT_IS_REVOKED; - if(response.this_update() > current_time) + if(response.this_update() > x509_ref_time) return Certificate_Status_Code::OCSP_NOT_YET_VALID; - if(response.next_update().time_is_set() && current_time > response.next_update()) + if(response.next_update().time_is_set() && x509_ref_time > response.next_update()) return Certificate_Status_Code::OCSP_HAS_EXPIRED; if(response.cert_status() == 0) @@ -222,9 +248,11 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, return Certificate_Status_Code::OCSP_CERT_NOT_LISTED; } +#if defined(BOTAN_HAS_HTTP_UTIL) + Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - const Certificate_Store* trusted_roots) + Certificate_Store* trusted_roots) { const std::string responder_url = subject.ocsp_responder(); @@ -241,11 +269,19 @@ Response online_check(const X509_Certificate& issuer, // Check the MIME type? - OCSP::Response response(*trusted_roots, http.body()); + OCSP::Response response(http.body()); + + std::vector trusted_roots_vec; + trusted_roots_vec.push_back(trusted_roots); + + if(trusted_roots) + response.check_signature(trusted_roots_vec); return response; } +#endif + } } diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h index fe1796984..b86b6a5e4 100644 --- a/src/lib/x509/ocsp.h +++ b/src/lib/x509/ocsp.h @@ -29,10 +29,7 @@ class BOTAN_DLL Request * @param subject_cert subject certificate */ Request(const X509_Certificate& issuer_cert, - const X509_Certificate& subject_cert) : - m_issuer(issuer_cert), - m_subject(subject_cert) - {} + const X509_Certificate& subject_cert); /** * @return BER-encoded OCSP request @@ -53,12 +50,18 @@ class BOTAN_DLL Request * @return subject certificate */ const X509_Certificate& subject() const { return m_subject; } + + const std::vector& issuer_key_hash() const + { return m_certid.issuer_key_hash(); } private: X509_Certificate m_issuer, m_subject; + CertID m_certid; }; /** -* An OCSP response. +* OCSP response. +* +* Note this class is only usable as an OCSP client */ class BOTAN_DLL Response { @@ -69,12 +72,36 @@ class BOTAN_DLL Response Response() {} /** - * Creates an OCSP response. - * @param trusted_roots trusted roots for the OCSP response + * Parses an OCSP response. + * @param request the OCSP request this is a respone to * @param response_bits response bits received */ - Response(const Certificate_Store& trusted_roots, - const std::vector& response_bits); + Response(const std::vector& response_bits); + + /* + * Check signature and return status + * The optional cert_path is the (already validated!) certificate path of + * the end entity which is being inquired about + */ + Certificate_Status_Code check_signature(const std::vector& trust_roots, + const std::vector>& cert_path = {}) const; + + /* + * Verify that issuer's key signed this response + */ + Certificate_Status_Code verify_signature(const X509_Certificate& issuer) const; + + const X509_Time& produced_at() const { return m_produced_at; } + + /** + * @return DN of signer, if provided in response (may be empty) + */ + const X509_DN& signer_name() const { return m_signer_name; } + + /** + * @return key hash, if provided in response (may be empty) + */ + const std::vector& signer_key_hash() const { return m_key_hash; } /** * Searches the OCSP response for issuer and subject certificate. @@ -89,12 +116,23 @@ class BOTAN_DLL Response * OCSP_CERT_NOT_LISTED */ Certificate_Status_Code status_for(const X509_Certificate& issuer, - const X509_Certificate& subject) const; + const X509_Certificate& subject, + std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now()) const; private: + X509_Time m_produced_at; + X509_DN m_signer_name; + std::vector m_key_hash; + std::vector m_tbs_bits; + AlgorithmIdentifier m_sig_algo; + std::vector m_signature; + std::vector m_certs; + std::vector m_responses; }; +#if defined(BOTAN_HAS_HTTP_UTIL) + /** * Makes an online OCSP request via HTTP and returns the OCSP response. * @param issuer issuer certificate @@ -104,7 +142,9 @@ class BOTAN_DLL Response */ BOTAN_DLL Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - const Certificate_Store* trusted_roots); + Certificate_Store* trusted_roots); + +#endif } diff --git a/src/lib/x509/ocsp_types.cpp b/src/lib/x509/ocsp_types.cpp index d470c2fa1..c9d349a4b 100644 --- a/src/lib/x509/ocsp_types.cpp +++ b/src/lib/x509/ocsp_types.cpp @@ -26,25 +26,11 @@ CertID::CertID(const X509_Certificate& issuer, std::unique_ptr hash(HashFunction::create("SHA-160")); m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM); - m_issuer_key_hash = unlock(hash->process(extract_key_bitstr(issuer))); + m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring())); m_issuer_dn_hash = unlock(hash->process(subject.raw_issuer_dn())); m_subject_serial = BigInt::decode(subject.serial_number()); } -std::vector CertID::extract_key_bitstr(const X509_Certificate& cert) const - { - const auto key_bits = cert.subject_public_key_bits(); - - AlgorithmIdentifier public_key_algid; - std::vector public_key_bitstr; - - BER_Decoder(key_bits) - .decode(public_key_algid) - .decode(public_key_bitstr, BIT_STRING); - - return public_key_bitstr; - } - bool CertID::is_id_for(const X509_Certificate& issuer, const X509_Certificate& subject) const { @@ -58,7 +44,7 @@ bool CertID::is_id_for(const X509_Certificate& issuer, if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn()))) return false; - if(m_issuer_key_hash != unlock(hash->process(extract_key_bitstr(issuer)))) + if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring()))) return false; } catch(...) diff --git a/src/lib/x509/ocsp_types.h b/src/lib/x509/ocsp_types.h index 6df8ac17f..40fbb85a8 100644 --- a/src/lib/x509/ocsp_types.h +++ b/src/lib/x509/ocsp_types.h @@ -30,9 +30,10 @@ class BOTAN_DLL CertID final : public ASN1_Object void encode_into(class DER_Encoder& to) const override; void decode_from(class BER_Decoder& from) override; - private: - std::vector extract_key_bitstr(const X509_Certificate& cert) const; + const std::vector& issuer_key_hash() const { return m_issuer_key_hash; } + + private: AlgorithmIdentifier m_hash_id; std::vector m_issuer_dn_hash; std::vector m_issuer_key_hash; diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index f0b07e5fc..946539bab 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -1,13 +1,12 @@ /* * X.509 Certificate Path Validation -* (C) 2010,2011,2012,2014 Jack Lloyd +* (C) 2010,2011,2012,2014,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include -#include #include #include #include @@ -15,98 +14,58 @@ #include #include #include -#include -namespace Botan { - -namespace { - -std::shared_ptr -find_issuing_cert(const X509_Certificate& cert, - Certificate_Store& end_certs, - const std::vector& certstores) - { - const X509_DN issuer_dn = cert.issuer_dn(); - const std::vector auth_key_id = cert.authority_key_id(); - - if(std::shared_ptr c = end_certs.find_cert(issuer_dn, auth_key_id)) - { - if(*c != cert) - return c; - } - - for(size_t i = 0; i != certstores.size(); ++i) - { - if(std::shared_ptr c = certstores[i]->find_cert(issuer_dn, auth_key_id)) - return c; - } - - return nullptr; - } - -std::shared_ptr find_crls_for(const X509_Certificate& cert, - const std::vector& certstores) - { - for(size_t i = 0; i != certstores.size(); ++i) - { - if(std::shared_ptr crl = certstores[i]->find_crl_for(cert)) - return crl; - } - -#if 0 - const std::string crl_url = cert.crl_distribution_point(); - if(crl_url != "") - { - std::cout << "Downloading CRL " << crl_url << "\n"; - auto http = HTTP::GET_sync(crl_url); - - std::cout << http.status_message() << "\n"; - - http.throw_unless_ok(); - // check the mime type - - std::unique_ptr crl(new X509_CRL(http.body())); - - return crl.release(); - } +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + #include + #include #endif - return nullptr; - } +namespace Botan { +/* +* PKIX path validation +*/ std::vector> -check_chain(const std::vector>& cert_path, - const Path_Validation_Restrictions& restrictions, - const std::vector& certstores, - std::chrono::system_clock::time_point ref_time) +PKIX::check_chain(const std::vector>& cert_path, + std::chrono::system_clock::time_point ref_time, + const std::string& hostname, + Usage_Type usage, + size_t min_signature_algo_strength, + const std::set& trusted_hashes) { - const std::set& trusted_hashes = restrictions.trusted_hashes(); + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_chain cert_path empty"); const bool self_signed_ee_cert = (cert_path.size() == 1); X509_Time validation_time(ref_time); - std::vector> ocsp_responses; - std::vector> cert_status(cert_path.size()); + if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname)) + cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); + + if(!cert_path[0]->allowed_usage(usage)) + cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE); + for(size_t i = 0; i != cert_path.size(); ++i) { std::set& status = cert_status.at(i); const bool at_self_signed_root = (i == cert_path.size() - 1); - std::shared_ptr subject = cert_path[i]; + const std::shared_ptr& subject = cert_path[i]; + + const std::shared_ptr& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; - std::shared_ptr issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + if(at_self_signed_root && (issuer->is_self_signed() == false)) + { + status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT); + } - if(i == 0 || restrictions.ocsp_all_intermediates()) + if(subject->issuer_dn() != issuer->subject_dn()) { - // certstore[0] is treated as trusted for OCSP (FIXME) - if(certstores.size() > 1) - ocsp_responses.push_back( - std::async(std::launch::async, - OCSP::online_check, *issuer, *subject, certstores[0])); + status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH); } // Check all certs for valid time range @@ -128,21 +87,23 @@ check_chain(const std::vector>& cert_pat if(!issuer_key) { - status.insert(Certificate_Status_Code::SIGNATURE_ERROR); + status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID); } else { if(subject->check_signature(*issuer_key) == false) + { status.insert(Certificate_Status_Code::SIGNATURE_ERROR); + } - if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) + if(issuer_key->estimated_strength() < min_signature_algo_strength) status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK); } - // Allow untrusted hashes on self-signed roots - if(!trusted_hashes.empty() && !at_self_signed_root) + // Ignore untrusted hashes on self-signed roots + if(trusted_hashes.size() > 0 && !at_self_signed_root) { - if(!trusted_hashes.count(subject->hash_used_for_signature())) + if(trusted_hashes.count(subject->hash_used_for_signature()) == 0) status.insert(Certificate_Status_Code::UNTRUSTED_HASH); } @@ -154,6 +115,20 @@ check_chain(const std::vector>& cert_pat } } + return cert_status; + } + +std::vector> +PKIX::check_ocsp(const std::vector>& cert_path, + const std::vector>& ocsp_responses, + const std::vector& trusted_certstores, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_ocsp cert_path empty"); + + std::vector> cert_status(cert_path.size() - 1); + for(size_t i = 0; i != cert_path.size() - 1; ++i) { std::set& status = cert_status.at(i); @@ -161,129 +136,459 @@ check_chain(const std::vector>& cert_pat std::shared_ptr subject = cert_path.at(i); std::shared_ptr ca = cert_path.at(i+1); - if(i < ocsp_responses.size()) + if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)) { try { - OCSP::Response ocsp = ocsp_responses[i].get(); + Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path); + + if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK) + { + // Signature ok, so check the claimed status + Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time); + status.insert(ocsp_status); + } + else + { + // Some signature problem + status.insert(ocsp_signature_status); + } + } + catch(Exception& e) + { + status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID); + } + } + } - auto ocsp_status = ocsp.status_for(*ca, *subject); + return cert_status; + } - status.insert(ocsp_status); +std::vector> +PKIX::check_crl(const std::vector>& cert_path, + const std::vector>& crls, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl cert_path empty"); - //std::cout << "OCSP status: " << Path_Validation_Result::status_string(ocsp_status) << "\n"; + std::vector> cert_status(cert_path.size()); + const X509_Time validation_time(ref_time); - // Either way we have a definitive answer, no need to check CRLs - if(ocsp_status == Certificate_Status_Code::CERT_IS_REVOKED) - return cert_status; - else if(ocsp_status == Certificate_Status_Code::OCSP_RESPONSE_GOOD) - continue; + for(size_t i = 0; i != cert_path.size() - 1; ++i) + { + std::set& status = cert_status.at(i); + + if(i < crls.size() && crls.at(i)) + { + std::shared_ptr subject = cert_path.at(i); + std::shared_ptr ca = cert_path.at(i+1); + + if(!ca->allowed_usage(CRL_SIGN)) + status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); + + if(validation_time < X509_Time(crls[i]->this_update())) + status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID); + + if(validation_time > X509_Time(crls[i]->next_update())) + status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); + + if(crls[i]->check_signature(ca->subject_public_key()) == false) + status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); + + status.insert(Certificate_Status_Code::VALID_CRL_CHECKED); + + if(crls[i]->is_revoked(*subject)) + status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + } + } + + return cert_status; + } + +std::vector> +PKIX::check_crl(const std::vector>& cert_path, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl cert_path empty"); + + if(certstores.empty()) + throw Invalid_Argument("PKIX::check_crl certstores empty"); + + std::vector> crls(cert_path.size()); + + for(size_t i = 0; i != cert_path.size(); ++i) + { + BOTAN_ASSERT(cert_path[i] != nullptr, "Not null"); + for(size_t c = 0; c != certstores.size(); ++c) + { + crls[i] = certstores[c]->find_crl_for(*cert_path.at(i)); + if(crls[i]) + break; + } + } + + return PKIX::check_crl(cert_path, crls, ref_time); + } + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + +std::vector> +PKIX::check_ocsp_online(const std::vector>& cert_path, + const std::vector& trusted_certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout, + bool ocsp_check_intermediate_CAs) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty"); + + std::vector>> ocsp_response_futures; + + size_t to_ocsp = 1; + + if(ocsp_check_intermediate_CAs) + to_ocsp = cert_path.size() - 1; + if(cert_path.size() == 1) + to_ocsp = 0; + + for(size_t i = 0; i < to_ocsp; ++i) + { + const std::shared_ptr& subject = cert_path.at(i); + const std::shared_ptr& issuer = cert_path.at(i+1); + + if(subject->ocsp_responder() == "") + { + ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]{ + throw Exception("No OCSP responder URL set for this certificate"); + return std::shared_ptr(); + })); } - catch(std::exception&) + else { - //std::cout << "OCSP error: " << e.what() << "\n"; + ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]{ + OCSP::Request req(*issuer, *subject); + + auto http = HTTP::POST_sync(subject->ocsp_responder(), + "application/ocsp-request", + req.BER_encode()); + + http.throw_unless_ok(); + // Check the MIME type? + + return std::make_shared(http.body()); + })); } - } + } - std::shared_ptr crl_p = find_crls_for(*subject, certstores); + std::vector> ocsp_responses(ocsp_response_futures.size()); - if(!crl_p) + for(size_t pass = 1; pass < 3; ++pass) + { + for(size_t i = 0; i < ocsp_response_futures.size(); ++i) { - if(restrictions.require_revocation_information()) - status.insert(Certificate_Status_Code::NO_REVOCATION_DATA); - continue; + try + { + if(ocsp_responses[i] == nullptr && ocsp_response_futures[i].valid()) + { + std::future_status status = ocsp_response_futures[i].wait_for(timeout); + + if(status == std::future_status::ready || + status == std::future_status::deferred) + { + ocsp_responses[i] = ocsp_response_futures[i].get(); + } + } + } + catch(std::exception&) + { + // value is default initialized to null, no need to do anything + } } + } - const X509_CRL& crl = *crl_p; + return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time); + } - if(!ca->allowed_usage(CRL_SIGN)) - status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); +std::vector> +PKIX::check_crl_online(const std::vector>& cert_path, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout) + { + if(cert_path.empty()) + throw Invalid_Argument("PKIX::check_crl_online cert_path empty"); + if(certstores.empty()) + throw Invalid_Argument("PKIX::check_crl_online certstores empty"); - if(validation_time < X509_Time(crl.this_update())) - status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID); + std::vector>> future_crls; + std::vector> crls(cert_path.size()); - if(validation_time > X509_Time(crl.next_update())) - status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED); + for(size_t i = 0; i != cert_path.size(); ++i) + { + for(size_t c = 0; c != certstores.size(); ++i) + { + crls[i] = certstores[i]->find_crl_for(*cert_path[i]); + if(crls[i]) + break; + } - if(crl.check_signature(ca->subject_public_key()) == false) - status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE); + // TODO: check if CRL is expired and re-request? - if(crl.is_revoked(*subject)) - status.insert(Certificate_Status_Code::CERT_IS_REVOKED); + // Only request if we don't already have a CRL + if(crls[i]) + { + /* + We already have a CRL, so just insert this empty one to hold a place in the vector + so that indexes match up + */ + future_crls.emplace_back(std::future>()); + } + else if(cert_path[i]->crl_distribution_point() == "") + { + // Avoid creating a thread for this case + future_crls.emplace_back(std::async(std::launch::deferred, [&]{ + throw Exception("No CRL distribution point for this certificate"); + return std::shared_ptr(); + })); + } + else + { + future_crls.emplace_back(std::async(std::launch::async, [&]() { + auto http = HTTP::GET_sync(cert_path[i]->crl_distribution_point()); + http.throw_unless_ok(); + // check the mime type? + return std::make_shared(http.body()); + })); + } } - if(self_signed_ee_cert) - cert_status.back().insert(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST); + for(size_t i = 0; i != future_crls.size(); ++i) + { + if(future_crls[i].valid()) + { + try + { + std::future_status status = future_crls[i].wait_for(timeout); + + if(status == std::future_status::ready) + { + crls[i] = future_crls[i].get(); + } + } + catch(std::exception& e) + { + // crls[i] left null + } + } + } - return cert_status; + return PKIX::check_crl(cert_path, crls, ref_time); } -} +#endif -Path_Validation_Result x509_path_validate( - const std::vector& end_certs, - const Path_Validation_Restrictions& restrictions, - const std::vector& certstores, - const std::string& hostname, - Usage_Type usage, - std::chrono::system_clock::time_point validation_time) +Certificate_Status_Code +PKIX::build_certificate_path(std::vector>& cert_path, + const std::vector& trusted_certstores, + const std::shared_ptr& end_entity, + const std::vector>& end_entity_extra) { - if(end_certs.empty()) - throw Invalid_Argument("x509_path_validate called with no subjects"); - - std::vector> cert_path; - std::vector> end_certs_sharedptr; - cert_path.push_back(std::make_shared(end_certs[0])); - - for(auto c: end_certs) - end_certs_sharedptr.push_back(std::make_shared(c)); + if(end_entity->is_self_signed()) + { + return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + } /* * This is an inelegant but functional way of preventing path loops * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate * fingerprints in the path. If there is a duplicate, we error out. + * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc. */ std::set certs_seen; - Certificate_Store_Overlay extra(end_certs_sharedptr); + cert_path.push_back(end_entity); + certs_seen.insert(end_entity->fingerprint("SHA-256")); + + Certificate_Store_In_Memory ee_extras; + for(size_t i = 0; i != end_entity_extra.size(); ++i) + ee_extras.add_certificate(end_entity_extra[i]); // iterate until we reach a root or cannot find the issuer - while(!cert_path.back()->is_self_signed()) + for(;;) { - std::shared_ptr cert = find_issuing_cert(*cert_path.back(), extra, certstores); - if(!cert) - return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + const X509_Certificate& last = *cert_path.back(); + const X509_DN issuer_dn = last.issuer_dn(); + const std::vector auth_key_id = last.authority_key_id(); + + std::shared_ptr issuer; + bool trusted_issuer = false; + + for(Certificate_Store* store : trusted_certstores) + { + issuer = store->find_cert(issuer_dn, auth_key_id); + if(issuer) + { + trusted_issuer = true; + break; + } + } + + if(!issuer) + { + // fall back to searching supplemental certs + issuer = ee_extras.find_cert(issuer_dn, auth_key_id); + } + + if(!issuer) + return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND; + + const std::string fprint = issuer->fingerprint("SHA-256"); + + if(certs_seen.count(fprint) > 0) // already seen? + return Certificate_Status_Code::CERT_CHAIN_LOOP; - const std::string fprint = cert->fingerprint("SHA-256"); - if(certs_seen.count(fprint) > 0) - return Path_Validation_Result(Certificate_Status_Code::CERT_CHAIN_LOOP); certs_seen.insert(fprint); - cert_path.push_back(cert); + cert_path.push_back(issuer); + + if(issuer->is_self_signed()) + { + if(trusted_issuer) + { + return Certificate_Status_Code::OK; + } + else + { + return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST; + } + } } + } - std::vector> res = - check_chain(cert_path, restrictions, certstores, validation_time); +namespace { - if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname)) - res[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH); +void merge_results(std::vector>& results, + const std::vector>& crl, + const std::vector>& ocsp, + bool require_rev_on_end_entity, + bool require_rev_on_intermediates) + { + for(size_t i = 0; i != results.size() - 1; ++i) + { + bool had_crl = false, had_ocsp = false; - if(!cert_path[0]->allowed_usage(usage)) - res[0].insert(Certificate_Status_Code::INVALID_USAGE); + if(i < crl.size() && crl[i].size() > 0) + { + for(auto&& code : crl[i]) + { + if(code == Certificate_Status_Code::VALID_CRL_CHECKED) + { + had_crl = true; + } + results[i].insert(code); + } + } + + if(i < ocsp.size() && ocsp[i].size() > 0) + { + for(auto&& code : ocsp[i]) + { + if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD) + { + had_ocsp = true; + } + + results[i].insert(code); + } + } + + if(had_crl == false && had_ocsp == false) + { + if((require_rev_on_end_entity && i == 0) || + (require_rev_on_intermediates && i > 0)) + { + results[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA); + } + } + } + } + +} + +Path_Validation_Result BOTAN_DLL x509_path_validate( + const std::vector& end_certs, + const Path_Validation_Restrictions& restrictions, + const std::vector& trusted_roots, + const std::string& hostname, + Usage_Type usage, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds ocsp_timeout) + { + if(end_certs.empty()) + throw Invalid_Argument("x509_path_validate called with no subjects"); + + std::shared_ptr end_entity(std::make_shared(end_certs[0])); + std::vector> end_entity_extra; + for(size_t i = 1; i < end_certs.size(); ++i) + { + end_entity_extra.push_back(std::make_shared(end_certs[i])); + } + + std::vector> cert_path; + Certificate_Status_Code path_building_result = + PKIX::build_certificate_path(cert_path, trusted_roots, end_entity, end_entity_extra); + + if(path_building_result != Certificate_Status_Code::OK) + { + return Path_Validation_Result(path_building_result); + } - return Path_Validation_Result(res, std::move(cert_path)); + std::vector> status = + PKIX::check_chain(cert_path, ref_time, + hostname, usage, + restrictions.minimum_key_strength(), + restrictions.trusted_hashes()); + + if(path_building_result != Certificate_Status_Code::OK) + status[0].insert(path_building_result); + + std::vector> crl_status = + PKIX::check_crl(cert_path, trusted_roots, ref_time); + + std::vector> ocsp_status; + + if(ocsp_timeout != std::chrono::milliseconds(0)) + { +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time, + ocsp_timeout, restrictions.ocsp_all_intermediates()); +#else + ocsp_status.resize(1); + ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP); +#endif + } + + merge_results(status, crl_status, ocsp_status, + restrictions.require_revocation_information(), + restrictions.ocsp_all_intermediates()); + + return Path_Validation_Result(status, std::move(cert_path)); } Path_Validation_Result x509_path_validate( const X509_Certificate& end_cert, const Path_Validation_Restrictions& restrictions, - const std::vector& certstores, + const std::vector& trusted_roots, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { std::vector certs; certs.push_back(end_cert); - return x509_path_validate(certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Result x509_path_validate( @@ -292,12 +597,13 @@ Path_Validation_Result x509_path_validate( const Certificate_Store& store, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { - std::vector certstores; - certstores.push_back(const_cast(&store)); + std::vector trusted_roots; + trusted_roots.push_back(const_cast(&store)); - return x509_path_validate(end_certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Result x509_path_validate( @@ -306,22 +612,23 @@ Path_Validation_Result x509_path_validate( const Certificate_Store& store, const std::string& hostname, Usage_Type usage, - std::chrono::system_clock::time_point when) + std::chrono::system_clock::time_point when, + std::chrono::milliseconds ocsp_timeout) { std::vector certs; certs.push_back(end_cert); - std::vector certstores; - certstores.push_back(const_cast(&store)); + std::vector trusted_roots; + trusted_roots.push_back(const_cast(&store)); - return x509_path_validate(certs, restrictions, certstores, hostname, usage, when); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); } Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, size_t key_strength, - bool ocsp_all) : + bool ocsp_intermediates) : m_require_revocation_information(require_rev), - m_ocsp_all_intermediates(ocsp_all), + m_ocsp_all_intermediates(ocsp_intermediates), m_minimum_key_strength(key_strength) { if(key_strength <= 80) @@ -335,7 +642,7 @@ Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, Path_Validation_Result::Path_Validation_Result(std::vector> status, std::vector>&& cert_chain) : - m_overall(Certificate_Status_Code::VERIFIED), + m_overall(Certificate_Status_Code::OK), m_all_status(status), m_cert_path(cert_chain) { @@ -345,9 +652,11 @@ Path_Validation_Result::Path_Validation_Result(std::vector= Certificate_Status_Code::FIRST_ERROR_STATUS) + { m_overall = worst; + } } } } @@ -372,10 +681,9 @@ std::set Path_Validation_Result::trusted_hashes() const bool Path_Validation_Result::successful_validation() const { - if(result() == Certificate_Status_Code::VERIFIED || - result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD) - return true; - return false; + return (result() == Certificate_Status_Code::VERIFIED || + result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD || + result() == Certificate_Status_Code::VALID_CRL_CHECKED); } std::string Path_Validation_Result::result_string() const @@ -385,68 +693,8 @@ std::string Path_Validation_Result::result_string() const const char* Path_Validation_Result::status_string(Certificate_Status_Code code) { - switch(code) - { - case Certificate_Status_Code::VERIFIED: - return "Verified"; - case Certificate_Status_Code::OCSP_RESPONSE_GOOD: - return "OCSP response good"; - case Certificate_Status_Code::NO_REVOCATION_DATA: - return "No revocation data"; - case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK: - return "Signature method too weak"; - case Certificate_Status_Code::UNTRUSTED_HASH: - return "Untrusted hash"; - - case Certificate_Status_Code::CERT_NOT_YET_VALID: - return "Certificate is not yet valid"; - case Certificate_Status_Code::CERT_HAS_EXPIRED: - return "Certificate has expired"; - case Certificate_Status_Code::OCSP_NOT_YET_VALID: - return "OCSP is not yet valid"; - case Certificate_Status_Code::OCSP_HAS_EXPIRED: - return "OCSP has expired"; - case Certificate_Status_Code::CRL_NOT_YET_VALID: - return "CRL is not yet valid"; - case Certificate_Status_Code::CRL_HAS_EXPIRED: - return "CRL has expired"; - - case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND: - return "Certificate issuer not found"; - case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST: - return "Cannot establish trust"; - case Certificate_Status_Code::CERT_CHAIN_LOOP: - return "Loop in certificate chain"; - - case Certificate_Status_Code::POLICY_ERROR: - return "Policy error"; - case Certificate_Status_Code::INVALID_USAGE: - return "Invalid usage"; - case Certificate_Status_Code::CERT_CHAIN_TOO_LONG: - return "Certificate chain too long"; - case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER: - return "CA certificate not allowed to issue certs"; - case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER: - return "CA certificate not allowed to issue CRLs"; - case Certificate_Status_Code::OCSP_CERT_NOT_LISTED: - return "OCSP cert not listed"; - case Certificate_Status_Code::OCSP_BAD_STATUS: - return "OCSP bad status"; - case Certificate_Status_Code::CERT_NAME_NOMATCH: - return "Certificate does not match provided name"; - case Certificate_Status_Code::NAME_CONSTRAINT_ERROR: - return "Certificate does not pass name constraint"; - case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION: - return "Unknown critical extension encountered"; - - case Certificate_Status_Code::CERT_IS_REVOKED: - return "Certificate is revoked"; - case Certificate_Status_Code::CRL_BAD_SIGNATURE: - return "CRL bad signature"; - case Certificate_Status_Code::SIGNATURE_ERROR: - return "Signature error"; - // intentionally no default so we are warned - } + if(const char* s = to_string(code)) + return s; return "Unknown error"; } diff --git a/src/lib/x509/x509path.h b/src/lib/x509/x509path.h index f65652e59..414a877da 100644 --- a/src/lib/x509/x509path.h +++ b/src/lib/x509/x509path.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -28,7 +30,8 @@ class BOTAN_DLL Path_Validation_Restrictions * operations, eg 80 means 2^80) of a signature. Signatures * weaker than this are rejected. If more than 80, SHA-1 * signatures are also rejected. - * @param ocsp_all_intermediates + * @param ocsp_all_intermediates Make OCSP requests for all CAs as + * well as end entity (if OCSP enabled in path validation request) */ Path_Validation_Restrictions(bool require_rev = false, size_t minimum_key_strength = 80, @@ -39,7 +42,8 @@ class BOTAN_DLL Path_Validation_Restrictions * @param minimum_key_strength is the minimum strength (in terms of * operations, eg 80 means 2^80) of a signature. Signatures * weaker than this are rejected. - * @param ocsp_all_intermediates + * @param ocsp_all_intermediates Make OCSP requests for all CAs as + * well as end entity (if OCSP enabled in path validation request) * @param trusted_hashes a set of trusted hashes. Any signatures * created using a hash other than one of these will be * rejected. @@ -106,6 +110,7 @@ class BOTAN_DLL Path_Validation_Result /** * @return the full path from subject to trust root + * This path may be empty */ const std::vector>& cert_path() const { return m_cert_path; } @@ -151,52 +156,121 @@ class BOTAN_DLL Path_Validation_Result explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {} private: - friend Path_Validation_Result BOTAN_DLL x509_path_validate( - const std::vector& end_certs, - const Path_Validation_Restrictions& restrictions, - const std::vector& certstores); - Certificate_Status_Code m_overall; std::vector> m_all_status; std::vector> m_cert_path; }; +namespace PKIX { + +/** +* Build certificate path +* @param cert_path_out output parameter, cert_path will be appended to this vector +* @param trusted_certstores list of certificate stores that contain trusted certificates +* @param end_entity the cert to be validated +* @param end_entity_extra optional list of additional untrusted certs for path building +* @return result of the path building operation (OK or error) +*/ +Certificate_Status_Code +BOTAN_DLL build_certificate_path(std::vector>& cert_path_out, + const std::vector& trusted_certstores, + const std::shared_ptr& end_entity, + const std::vector>& end_entity_extra); + +/** +* Perform certificate validation +* @param cert_path path built by build_certificate_path with OK result +* @param ref_time whatever time you want to perform the validation +* against (normally current system clock) +* @param hostname the hostname +* @param usage end entity usage checks +* @param min_signature_algo_strength 80 or 128 typically +* @param trusted_hashes set of trusted hash functions, +* empty means accept any hash we have an OID for +*/ +std::vector> +BOTAN_DLL check_chain(const std::vector>& cert_path, + std::chrono::system_clock::time_point ref_time, + const std::string& hostname, + Usage_Type usage, + size_t min_signature_algo_strength, + const std::set& trusted_hashes); + +std::vector> +BOTAN_DLL check_ocsp(const std::vector>& cert_path, + const std::vector>& ocsp_responses, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time); + +std::vector> +BOTAN_DLL check_crl(const std::vector>& cert_path, + const std::vector>& crls, + std::chrono::system_clock::time_point ref_time); + +std::vector> +BOTAN_DLL check_crl(const std::vector>& cert_path, + const std::vector& certstores, + std::chrono::system_clock::time_point ref_time); + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL) + +std::vector> +BOTAN_DLL check_ocsp_online(const std::vector>& cert_path, + const std::vector& trusted_certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout, + bool ocsp_check_intermediate_CAs); + +std::vector> +BOTAN_DLL check_crl_online(const std::vector>& cert_path, + const std::vector& trusted_certstores, + std::chrono::system_clock::time_point ref_time, + std::chrono::milliseconds timeout); + +#endif + +} + /** * PKIX Path Validation * @param end_certs certificate chain to validate * @param restrictions path validation restrictions -* @param certstores list of certificate stores that contain trusted certificates +* @param trusted_roots list of certificate stores that contain trusted certificates * @param hostname if not empty, compared against the DNS name in end_certs[0] * @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0] * @param validation_time what reference time to use for validation +* @param ocsp_timeout timeoutput for OCSP operations, 0 disables OCSP check * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( const std::vector& end_certs, const Path_Validation_Restrictions& restrictions, - const std::vector& certstores, + const std::vector& trusted_roots, const std::string& hostname = "", Usage_Type usage = Usage_Type::UNSPECIFIED, - std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now()); + std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(), + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); /** * PKIX Path Validation * @param end_cert certificate to validate * @param restrictions path validation restrictions -* @param certstores list of stores that contain trusted certificates +* @param trusted_roots list of stores that contain trusted certificates * @param hostname if not empty, compared against the DNS name in end_cert * @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert * @param validation_time what reference time to use for validation +* @param ocsp_timeout timeoutput for OCSP operations, 0 disables OCSP check * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( const X509_Certificate& end_cert, const Path_Validation_Restrictions& restrictions, - const std::vector& certstores, + const std::vector& trusted_roots, const std::string& hostname = "", Usage_Type usage = Usage_Type::UNSPECIFIED, - std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now()); + std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(), + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); /** * PKIX Path Validation @@ -206,6 +280,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( * @param hostname if not empty, compared against the DNS name in end_cert * @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert * @param validation_time what reference time to use for validation +* @param ocsp_timeout timeoutput for OCSP operations, 0 disables OCSP check * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -214,7 +289,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( const Certificate_Store& store, const std::string& hostname = "", Usage_Type usage = Usage_Type::UNSPECIFIED, - std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now()); + std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(), + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); /** * PKIX Path Validation @@ -224,6 +300,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( * @param hostname if not empty, compared against the DNS name in end_certs[0] * @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0] * @param validation_time what reference time to use for validation +* @param ocsp_timeout timeoutput for OCSP operations, 0 disables OCSP check * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -232,7 +309,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( const Certificate_Store& store, const std::string& hostname = "", Usage_Type usage = Usage_Type::UNSPECIFIED, - std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now()); + std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(), + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); } -- cgit v1.2.3