diff options
author | lloyd <[email protected]> | 2013-11-29 22:50:03 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2013-11-29 22:50:03 +0000 |
commit | 9ee17c62aba097b3b682d1ec217c31fdf8f2dedb (patch) | |
tree | 85cb25107dec4272b98e3c86651201afb4e02f40 | |
parent | 41cc764815c5cdf1902e923b7bea3f3eec288c31 (diff) |
First pass at automatic OCSP checks
-rw-r--r-- | checks/x509.cpp | 4 | ||||
-rw-r--r-- | src/asn1/asn1_time.h | 2 | ||||
-rw-r--r-- | src/cert/x509/cert_status.h | 3 | ||||
-rw-r--r-- | src/cert/x509/ocsp.cpp | 7 | ||||
-rw-r--r-- | src/cert/x509/ocsp.h | 2 | ||||
-rw-r--r-- | src/cert/x509/ocsp_types.h | 2 | ||||
-rw-r--r-- | src/cert/x509/x509path.cpp | 138 | ||||
-rw-r--r-- | src/cert/x509/x509path.h | 14 |
8 files changed, 112 insertions, 60 deletions
diff --git a/checks/x509.cpp b/checks/x509.cpp index 46b748da6..77d4e803f 100644 --- a/checks/x509.cpp +++ b/checks/x509.cpp @@ -219,12 +219,12 @@ void do_x509_tests(RandomNumberGenerator& rng) store.add_crl(crl2); result_u1 = x509_path_validate(user1_cert, restrictions, store); - if(result_u1.result() != Path_Validation_Result::CERT_IS_REVOKED) + if(result_u1.result() != Certificate_Status_Code::CERT_IS_REVOKED) std::cout << "FAILED: User cert #1 was not revoked - " << result_u1.result_string() << std::endl; result_u2 = x509_path_validate(user2_cert, restrictions, store); - if(result_u2.result() != Path_Validation_Result::CERT_IS_REVOKED) + if(result_u2.result() != Certificate_Status_Code::CERT_IS_REVOKED) std::cout << "FAILED: User cert #2 was not revoked - " << result_u2.result_string() << std::endl; diff --git a/src/asn1/asn1_time.h b/src/asn1/asn1_time.h index 3e7e0ef1c..95baacd86 100644 --- a/src/asn1/asn1_time.h +++ b/src/asn1/asn1_time.h @@ -26,6 +26,8 @@ class BOTAN_DLL X509_Time : public ASN1_Object std::string readable_string() const; bool time_is_set() const; + std::string to_string() const { return readable_string(); } + s32bit cmp(const X509_Time&) const; void set_to(const std::string&); diff --git a/src/cert/x509/cert_status.h b/src/cert/x509/cert_status.h index d78f14473..b688cbcd7 100644 --- a/src/cert/x509/cert_status.h +++ b/src/cert/x509/cert_status.h @@ -32,7 +32,8 @@ enum Certificate_Status_Code { CERT_HAS_EXPIRED, CERT_IS_REVOKED, - CRL_NOT_FOUND, + NO_REVOCATION_DATA, + CRL_FORMAT_ERROR, CRL_NOT_YET_VALID, CRL_HAS_EXPIRED, diff --git a/src/cert/x509/ocsp.cpp b/src/cert/x509/ocsp.cpp index 2ea413e76..a118a9c4e 100644 --- a/src/cert/x509/ocsp.cpp +++ b/src/cert/x509/ocsp.cpp @@ -224,7 +224,7 @@ Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - const Certificate_Store& trusted_roots) + const Certificate_Store* trusted_roots) { const std::string responder_url = subject.ocsp_responder(); @@ -237,12 +237,11 @@ Response online_check(const X509_Certificate& issuer, "application/ocsp-request", req.BER_encode()); - if(http.status_code() != 200) - throw std::runtime_error("HTTP error: " + http.status_message()); + http.throw_unless_ok(); // Check the MIME type? - OCSP::Response response(trusted_roots, http.body()); + OCSP::Response response(*trusted_roots, http.body()); return response; } diff --git a/src/cert/x509/ocsp.h b/src/cert/x509/ocsp.h index 0c40bc282..b2a06b9a4 100644 --- a/src/cert/x509/ocsp.h +++ b/src/cert/x509/ocsp.h @@ -52,7 +52,7 @@ class BOTAN_DLL Response BOTAN_DLL Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, - const Certificate_Store& trusted_roots); + const Certificate_Store* trusted_roots); } diff --git a/src/cert/x509/ocsp_types.h b/src/cert/x509/ocsp_types.h index f693600ea..a2983f10b 100644 --- a/src/cert/x509/ocsp_types.h +++ b/src/cert/x509/ocsp_types.h @@ -48,7 +48,7 @@ class BOTAN_DLL SingleResponse : public ASN1_Object X509_Time this_update() const { return m_thisupdate; } - X509_Time next_update() const { return m_thisupdate; } + X509_Time next_update() const { return m_nextupdate; } void encode_into(class DER_Encoder& to) const override; diff --git a/src/cert/x509/x509path.cpp b/src/cert/x509/x509path.cpp index 3ed57206d..6aa7251e7 100644 --- a/src/cert/x509/x509path.cpp +++ b/src/cert/x509/x509path.cpp @@ -6,31 +6,22 @@ */ #include <botan/x509path.h> +#include <botan/ocsp.h> +#include <botan/http_util.h> #include <botan/parsing.h> #include <botan/pubkey.h> #include <botan/oids.h> #include <algorithm> #include <chrono> #include <memory> +#include <iostream> namespace Botan { namespace { -class PKIX_Validation_Failure : public std::exception - { - public: - PKIX_Validation_Failure(Certificate_Status_Code code) : m_code(code) {} - - Certificate_Status_Code code() const { return m_code; } - - const char* what() const noexcept { return "PKIX validation failed"; } - private: - Certificate_Status_Code m_code; - }; - -X509_Certificate find_issuing_cert(const X509_Certificate& cert, - const std::vector<Certificate_Store*>& certstores) +const X509_Certificate* find_issuing_cert(const X509_Certificate& cert, + const std::vector<Certificate_Store*>& certstores) { const X509_DN issuer_dn = cert.issuer_dn(); const std::vector<byte> auth_key_id = cert.authority_key_id(); @@ -38,14 +29,14 @@ X509_Certificate find_issuing_cert(const X509_Certificate& cert, for(size_t i = 0; i != certstores.size(); ++i) { if(const X509_Certificate* cert = certstores[i]->find_cert(issuer_dn, auth_key_id)) - return *cert; + return cert; } - throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + return nullptr; } const X509_CRL* find_crls_from(const X509_Certificate& cert, - const std::vector<Certificate_Store*>& certstores) + const std::vector<Certificate_Store*>& certstores) { const X509_DN issuer_dn = cert.subject_dn(); const std::vector<byte> auth_key_id = cert.subject_key_id(); @@ -56,6 +47,24 @@ const X509_CRL* find_crls_from(const X509_Certificate& 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<X509_CRL> crl(new X509_CRL(http.body())); + + return crl.release(); + } +#endif + return nullptr; } @@ -69,10 +78,23 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa X509_Time current_time(std::chrono::system_clock::now()); + std::vector<std::future<OCSP::Response>> ocsp_responses; + for(size_t i = 0; i != cert_path.size(); ++i) { + const bool at_self_signed_root = (i == cert_path.size() - 1); + const X509_Certificate& subject = cert_path[i]; + const X509_Certificate& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)]; + + const Certificate_Store* trusted = certstores[0]; // fixme + + if(i == 0 || restrictions.ocsp_all_intermediates()) + ocsp_responses.push_back( + std::async(std::launch::async, + OCSP::online_check, issuer, subject, trusted)); + // Check all certs for valid time range if(current_time < X509_Time(subject.start_time())) return Certificate_Status_Code::CERT_NOT_YET_VALID; @@ -80,11 +102,6 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa if(current_time > X509_Time(subject.end_time())) return Certificate_Status_Code::CERT_HAS_EXPIRED; - const bool at_self_signed_root = (i == cert_path.size() - 1); - - const X509_Certificate& issuer = - cert_path[at_self_signed_root ? (i) : (i + 1)]; - // Check issuer constraints // Don't require CA bit set on self-signed end entity cert @@ -107,17 +124,42 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa return Certificate_Status_Code::UNTRUSTED_HASH; } - for(size_t i = 1; i != cert_path.size(); ++i) + for(size_t i = 0; i != cert_path.size() - 1; ++i) { - const X509_Certificate& subject = cert_path[i-1]; - const X509_Certificate& ca = cert_path[i]; + const X509_Certificate& subject = cert_path[i]; + const X509_Certificate& ca = cert_path[i+1]; + + if(i < ocsp_responses.size()) + { + try + { + OCSP::Response ocsp = ocsp_responses[i].get(); + + auto status = ocsp.status_for(ca, subject); + + if(status == CERT_IS_REVOKED) + return status; + + if(status == OCSP_RESPONSE_GOOD) + { + if(i == 0 && !restrictions.ocsp_all_intermediates()) + return status; // return immediately to just OCSP end cert + else + continue; + } + } + catch(std::exception& e) + { + } + } const X509_CRL* crl_p = find_crls_from(ca, certstores); if(!crl_p) { if(restrictions.require_revocation_information()) - return Certificate_Status_Code::CRL_NOT_FOUND; + return Certificate_Status_Code::NO_REVOCATION_DATA; + std::cout << "No revocation information for " << subject.subject_dn() << "\n"; continue; } @@ -147,7 +189,6 @@ Certificate_Status_Code check_chain(const std::vector<X509_Certificate>& cert_pa } - Path_Validation_Result x509_path_validate( const std::vector<X509_Certificate>& end_certs, const Path_Validation_Restrictions& restrictions, @@ -158,26 +199,18 @@ Path_Validation_Result x509_path_validate( std::vector<X509_Certificate> cert_path = end_certs; - try - { - // iterate until we reach a root or cannot find the issuer - while(!cert_path.back().is_self_signed()) - { - cert_path.push_back( - find_issuing_cert(cert_path.back(), certstores) - ); - } - - Certificate_Status_Code res = check_chain(cert_path, restrictions, certstores); - - return Path_Validation_Result(res, std::move(cert_path)); - } - catch(PKIX_Validation_Failure& e) + // iterate until we reach a root or cannot find the issuer + while(!cert_path.back().is_self_signed()) { - return Path_Validation_Result(e.code()); + const X509_Certificate* cert = find_issuing_cert(cert_path.back(), certstores); + if(!cert) + return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); + cert_path.push_back(*cert); } - return Path_Validation_Result(Certificate_Status_Code::UNKNOWN_X509_ERROR); + Certificate_Status_Code res = check_chain(cert_path, restrictions, certstores); + + return Path_Validation_Result(res, std::move(cert_path)); } Path_Validation_Result x509_path_validate( @@ -216,8 +249,10 @@ Path_Validation_Result x509_path_validate( } Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, - size_t key_strength) : + size_t key_strength, + bool ocsp_all) : m_require_revocation_information(require_rev), + m_ocsp_all_intermediates(ocsp_all), m_minimum_key_strength(key_strength) { if(key_strength <= 80) @@ -242,7 +277,14 @@ std::set<std::string> Path_Validation_Result::trusted_hashes() const return hashes; } -std::string Path_Validation_Result::result_string() constmtn +bool Path_Validation_Result::successful_validation() const + { + if(status() == VERIFIED || status() == OCSP_RESPONSE_GOOD) + return true; + return false; + } + +std::string Path_Validation_Result::result_string() const { return status_string(m_status); } @@ -283,8 +325,8 @@ std::string Path_Validation_Result::status_string(Certificate_Status_Code code) return "Certificate has expired"; case CERT_IS_REVOKED: return "Certificate is revoked"; - case CRL_NOT_FOUND: - return "CRL not found"; + case NO_REVOCATION_DATA: + return "No revocation data available"; case CRL_FORMAT_ERROR: return "CRL format error"; case CRL_NOT_YET_VALID: diff --git a/src/cert/x509/x509path.h b/src/cert/x509/x509path.h index c935daa77..2c3268529 100644 --- a/src/cert/x509/x509path.h +++ b/src/cert/x509/x509path.h @@ -25,10 +25,12 @@ class BOTAN_DLL Path_Validation_Restrictions * @param require_rev if true, revocation information is required * @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. + * weaker than this are rejected. If more than 80, SHA-1 + * signatures are also rejected. */ Path_Validation_Restrictions(bool require_rev = false, - size_t minimum_key_strength = 80); + size_t minimum_key_strength = 80, + bool ocsp_all_intermediates = false); /** * @param require_rev if true, revocation information is required @@ -41,14 +43,19 @@ class BOTAN_DLL Path_Validation_Restrictions */ Path_Validation_Restrictions(bool require_rev, size_t minimum_key_strength, + bool ocsp_all_intermediates, const std::set<std::string>& trusted_hashes) : m_require_revocation_information(require_rev), + m_ocsp_all_intermediates(ocsp_all_intermediates), m_trusted_hashes(trusted_hashes), m_minimum_key_strength(minimum_key_strength) {} bool require_revocation_information() const { return m_require_revocation_information; } + bool ocsp_all_intermediates() const + { return m_ocsp_all_intermediates; } + const std::set<std::string>& trusted_hashes() const { return m_trusted_hashes; } @@ -57,6 +64,7 @@ class BOTAN_DLL Path_Validation_Restrictions private: bool m_require_revocation_information; + bool m_ocsp_all_intermediates; std::set<std::string> m_trusted_hashes; size_t m_minimum_key_strength; }; @@ -86,7 +94,7 @@ class BOTAN_DLL Path_Validation_Result /** * @return true iff the validation was succesful */ - bool successful_validation() const { return status() == VERIFIED; } + bool successful_validation() const; /** * @return validation result code |