From 7ef2285a09c8476708f45e3bb69dbb8c8b6fb704 Mon Sep 17 00:00:00 2001 From: lloyd Date: Fri, 29 Nov 2013 19:42:46 +0000 Subject: Have OCSP responses return an enum allowing a range of conditions to be expressed (good status, cert revoked, some other error, etc). Add a certificate store backed by files (requiring boost filesystem). Change Certificate_Store interface somewhat to support retrieval without copying. --- doc/examples/credentials.h | 23 ++-- src/cert/ocsp/ocsp.cpp | 43 ++++-- src/cert/ocsp/ocsp.h | 5 +- src/cert/ocsp/ocsp_types.cpp | 23 +--- src/cert/ocsp/ocsp_types.h | 15 +- src/cert/x509/cert_status.h | 54 ++++++++ src/cert/x509/certstor.cpp | 91 ++++++++---- src/cert/x509/certstor.h | 67 ++++----- src/cert/x509/info.txt | 2 + src/cert/x509/x509path.cpp | 320 +++++++++++++++++++++---------------------- src/cert/x509/x509path.h | 40 +----- 11 files changed, 368 insertions(+), 315 deletions(-) create mode 100644 src/cert/x509/cert_status.h diff --git a/doc/examples/credentials.h b/doc/examples/credentials.h index c7dae364a..d87fab7ba 100644 --- a/doc/examples/credentials.h +++ b/doc/examples/credentials.h @@ -25,7 +25,11 @@ bool value_exists(const std::vector& vec, class Credentials_Manager_Simple : public Botan::Credentials_Manager { public: - Credentials_Manager_Simple(Botan::RandomNumberGenerator& rng) : rng(rng) {} + Credentials_Manager_Simple(Botan::RandomNumberGenerator& rng) : + rng(rng) + { + m_certstores.push_back(new Botan::Certificate_Store_In_Memory("/usr/share/ca-certificates")); + } std::string srp_identifier(const std::string& type, const std::string& hostname) @@ -47,19 +51,8 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager trusted_certificate_authorities(const std::string& type, const std::string& hostname) { - - std::vector certs; - - if(type == "tls-client" && hostname == "twitter.com") - { - Botan::X509_Certificate verisign("/usr/share/ca-certificates/mozilla/VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.crt"); - - auto store = new Botan::Certificate_Store_In_Memory; - store->add_certificate(verisign); - certs.push_back(store); - } - - return certs; + // can very based on hostname eg for pinning + return m_certstores; } void verify_certificate_chain( @@ -291,6 +284,8 @@ class Credentials_Manager_Simple : public Botan::Credentials_Manager Botan::SymmetricKey session_ticket_key; std::map certs_and_keys; + + std::vector m_certstores; }; #endif diff --git a/src/cert/ocsp/ocsp.cpp b/src/cert/ocsp/ocsp.cpp index d894abc97..2ea413e76 100644 --- a/src/cert/ocsp/ocsp.cpp +++ b/src/cert/ocsp/ocsp.cpp @@ -1,6 +1,6 @@ /* * OCSP -* (C) 2012 Jack Lloyd +* (C) 2012,2013 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -77,7 +77,7 @@ void check_signature(const std::vector& tbs_response, if(certs.size() < 1) throw std::invalid_argument("Short cert chain for check_signature"); - if(trusted_roots.certificate_known(certs[0])) // directly signed by + 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 @@ -85,10 +85,7 @@ void check_signature(const std::vector& tbs_response, if(!certs[0].allowed_usage("PKIX.OCSPSigning")) throw std::runtime_error("OCSP response cert does not allow OCSP signing"); - Path_Validation_Result result = - x509_path_validate(certs, - Path_Validation_Restrictions(), - trusted_roots); + auto result = x509_path_validate(certs, Path_Validation_Restrictions(), trusted_roots); if(!result.successful_validation()) throw std::runtime_error("Certificate validation failure: " + result.result_string()); @@ -186,8 +183,9 @@ Response::Response(const Certificate_Store& trusted_roots, if(certs.empty()) { - certs = trusted_roots.find_cert_by_subject_and_key_id(name, std::vector()); - if(certs.empty()) + if(auto cert = trusted_roots.find_cert(name, std::vector())) + certs.push_back(*cert); + else throw std::runtime_error("Could not find certificate that signed OCSP response"); } @@ -197,14 +195,31 @@ Response::Response(const Certificate_Store& trusted_roots, response_outer.end_cons(); } -bool Response::affirmative_response_for(const X509_Certificate& issuer, - const X509_Certificate& subject) const +Certificate_Status_Code Response::status_for(const X509_Certificate& issuer, + const X509_Certificate& subject) const { - for(auto response : m_responses) - if(response.affirmative_response_for(issuer, subject)) - return true; + for(const auto& response : m_responses) + { + if(response.certid().is_id_for(issuer, subject)) + { + X509_Time current_time(std::chrono::system_clock::now()); + + if(response.this_update() > current_time) + return Certificate_Status_Code::OCSP_NOT_YET_VALID; + + if(response.next_update().time_is_set() && current_time > response.next_update()) + return Certificate_Status_Code::OCSP_EXPIRED; + + if(response.cert_status() == 1) + return Certificate_Status_Code::CERT_IS_REVOKED; + else if(response.cert_status() == 0) + return Certificate_Status_Code::OCSP_RESPONSE_GOOD; + else + return Certificate_Status_Code::OCSP_BAD_STATUS; + } + } - return false; + return Certificate_Status_Code::OCSP_CERT_NOT_LISTED; } Response online_check(const X509_Certificate& issuer, diff --git a/src/cert/ocsp/ocsp.h b/src/cert/ocsp/ocsp.h index ec86b434e..0c40bc282 100644 --- a/src/cert/ocsp/ocsp.h +++ b/src/cert/ocsp/ocsp.h @@ -8,6 +8,7 @@ #ifndef BOTAN_OCSP_H__ #define BOTAN_OCSP_H__ +#include #include namespace Botan { @@ -42,8 +43,8 @@ class BOTAN_DLL Response Response(const Certificate_Store& trusted_roots, const std::vector& response); - bool affirmative_response_for(const X509_Certificate& issuer, - const X509_Certificate& subject) const; + Certificate_Status_Code status_for(const X509_Certificate& issuer, + const X509_Certificate& subject) const; private: std::vector m_responses; diff --git a/src/cert/ocsp/ocsp_types.cpp b/src/cert/ocsp/ocsp_types.cpp index ed78f839b..be41499ee 100644 --- a/src/cert/ocsp/ocsp_types.cpp +++ b/src/cert/ocsp/ocsp_types.cpp @@ -92,27 +92,6 @@ void CertID::decode_from(class BER_Decoder& from) } -bool SingleResponse::affirmative_response_for( - const X509_Certificate& issuer, - const X509_Certificate& subject) const - { - if(!m_good_status) - return false; - - if(!m_certid.is_id_for(issuer, subject)) - return false; - - X509_Time current_time(std::chrono::system_clock::now()); - - if(m_thisupdate > current_time) - return false; // not yet valid? - - if(m_nextupdate.time_is_set() && current_time > m_nextupdate) - return false; // expired, probably replayed - - return true; - } - void SingleResponse::encode_into(class DER_Encoder&) const { throw std::runtime_error("Not implemented (SingleResponse::encode_into)"); @@ -134,7 +113,7 @@ void SingleResponse::decode_from(class BER_Decoder& from) ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) .end_cons(); - m_good_status = (cert_status.type_tag == 0); + m_cert_status = cert_status.type_tag; } } diff --git a/src/cert/ocsp/ocsp_types.h b/src/cert/ocsp/ocsp_types.h index e51089aca..f693600ea 100644 --- a/src/cert/ocsp/ocsp_types.h +++ b/src/cert/ocsp/ocsp_types.h @@ -42,21 +42,20 @@ class BOTAN_DLL CertID : public ASN1_Object class BOTAN_DLL SingleResponse : public ASN1_Object { public: - SingleResponse() : m_good_status(false) {} + const CertID& certid() const { return m_certid; } - /** - * Return true if and only if this response is one matching - * the current issuer and subject AND is a postive affirmation - */ - bool affirmative_response_for(const X509_Certificate& issuer, - const X509_Certificate& subject) const; + size_t cert_status() const { return m_cert_status; } + + X509_Time this_update() const { return m_thisupdate; } + + X509_Time next_update() const { return m_thisupdate; } void encode_into(class DER_Encoder& to) const override; void decode_from(class BER_Decoder& from) override; private: CertID m_certid; - bool m_good_status; + size_t m_cert_status = 2; // unknown X509_Time m_thisupdate; X509_Time m_nextupdate; }; diff --git a/src/cert/x509/cert_status.h b/src/cert/x509/cert_status.h new file mode 100644 index 000000000..d78f14473 --- /dev/null +++ b/src/cert/x509/cert_status.h @@ -0,0 +1,54 @@ +/* +* Result enums +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_X509_PATH_RESULT_H__ +#define BOTAN_X509_PATH_RESULT_H__ + +#include + +namespace Botan { + +enum Certificate_Status_Code { + VERIFIED, + UNKNOWN_X509_ERROR, + CANNOT_ESTABLISH_TRUST, + CERT_CHAIN_TOO_LONG, + SIGNATURE_ERROR, + POLICY_ERROR, + INVALID_USAGE, + + SIGNATURE_METHOD_TOO_WEAK, + UNTRUSTED_HASH, + + CERT_MULTIPLE_ISSUERS_FOUND, + + CERT_FORMAT_ERROR, + CERT_ISSUER_NOT_FOUND, + CERT_NOT_YET_VALID, + CERT_HAS_EXPIRED, + CERT_IS_REVOKED, + + CRL_NOT_FOUND, + CRL_FORMAT_ERROR, + CRL_NOT_YET_VALID, + CRL_HAS_EXPIRED, + + OCSP_CERT_NOT_LISTED, + OCSP_NOT_YET_VALID, + OCSP_EXPIRED, + OCSP_BAD_STATUS, + OCSP_RESPONSE_GOOD, + + CA_CERT_CANNOT_SIGN, + CA_CERT_NOT_FOR_CERT_ISSUER, + CA_CERT_NOT_FOR_CRL_ISSUER +}; + + +} + +#endif diff --git a/src/cert/x509/certstor.cpp b/src/cert/x509/certstor.cpp index afb0ddd0c..e8b3a0718 100644 --- a/src/cert/x509/certstor.cpp +++ b/src/cert/x509/certstor.cpp @@ -1,21 +1,18 @@ /* * Certificate Store -* (C) 1999-2010 Jack Lloyd +* (C) 1999-2010,2013 Jack Lloyd * * Distributed under the terms of the Botan license */ #include +#include namespace Botan { -bool Certificate_Store::certificate_known(const X509_Certificate& cert) const +const X509_CRL* Certificate_Store::find_crl(const X509_Certificate&) const { - std::vector found = - find_cert_by_subject_and_key_id(cert.subject_dn(), - cert.subject_key_id()); - - return !found.empty(); + return nullptr; } void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert) @@ -37,29 +34,37 @@ std::vector Certificate_Store_In_Memory::all_subjects() const return subjects; } -std::vector -Certificate_Store_In_Memory::find_cert_by_subject_and_key_id( - const X509_DN& subject_dn, - const std::vector& key_id) const - { - std::vector result; +namespace { - for(size_t i = 0; i != m_certs.size(); ++i) +const X509_Certificate* +cert_search(const X509_DN& subject_dn, const std::vector& key_id, + const std::vector& certs) + { + for(size_t i = 0; i != certs.size(); ++i) { // Only compare key ids if set in both call and in the cert if(key_id.size()) { - std::vector skid = m_certs[i].subject_key_id(); + std::vector skid = certs[i].subject_key_id(); if(skid.size() && skid != key_id) // no match continue; } - if(m_certs[i].subject_dn() == subject_dn) - result.push_back(m_certs[i]); + if(certs[i].subject_dn() == subject_dn) + return &certs[i]; } - return result; + return nullptr; + } + +} + +const X509_Certificate* +Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn, + const std::vector& key_id) const + { + return cert_search(subject_dn, key_id, m_certs); } void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) @@ -81,12 +86,9 @@ void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl) m_crls.push_back(crl); } -std::vector -Certificate_Store_In_Memory::find_crl_by_issuer_and_key_id( - const X509_DN& issuer_dn, - const std::vector& key_id) const +const X509_CRL* Certificate_Store_In_Memory::find_crl(const X509_Certificate& subject) const { - std::vector result; + const std::vector& key_id = subject.authority_key_id(); for(size_t i = 0; i != m_crls.size(); ++i) { @@ -99,11 +101,48 @@ Certificate_Store_In_Memory::find_crl_by_issuer_and_key_id( continue; } - if(m_crls[i].issuer_dn() == issuer_dn) - result.push_back(m_crls[i]); + if(m_crls[i].issuer_dn() == subject.issuer_dn()) + return &m_crls[i]; } - return result; + return nullptr; + } + +Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir) + { + if(dir == "") + return; + + boost::filesystem::recursive_directory_iterator i(dir); + boost::filesystem::recursive_directory_iterator end; + + while(i != end) + { + auto path = i->path(); + ++i; + + try + { + if(boost::filesystem::is_regular_file(path)) + m_certs.push_back(X509_Certificate(path.native())); + } + catch(...) {} + } + } + +const X509_Certificate* +Certificate_Store_Overlay::find_cert(const X509_DN& subject_dn, + const std::vector& key_id) const + { + return cert_search(subject_dn, key_id, m_certs); + } + +std::vector Certificate_Store_Overlay::all_subjects() const + { + std::vector subjects; + for(size_t i = 0; i != m_certs.size(); ++i) + subjects.push_back(m_certs[i].subject_dn()); + return subjects; } } diff --git a/src/cert/x509/certstor.h b/src/cert/x509/certstor.h index 6faa0bfae..fc37d8327 100644 --- a/src/cert/x509/certstor.h +++ b/src/cert/x509/certstor.h @@ -1,6 +1,6 @@ /* * Certificate Store -* (C) 1999-2010 Jack Lloyd +* (C) 1999-2010,2013 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -22,34 +22,20 @@ class BOTAN_DLL Certificate_Store virtual ~Certificate_Store() {} /** - * Add a certificate; this may fail if the store is write-only + * Subject DN and (optionally) key identifier */ - virtual void add_certificate(const X509_Certificate& cert) = 0; + virtual const X509_Certificate* + find_cert(const X509_DN& subject_dn, const std::vector& key_id) const = 0; - /** - * Add a CRL; this may fail if the store is write-only - */ - virtual void add_crl(const X509_CRL& crl) = 0; + virtual const X509_CRL* find_crl(const X509_Certificate& subject) const; - bool certificate_known(const X509_Certificate& cert) const; + bool certificate_known(const X509_Certificate& cert) const + { + return find_cert(cert.subject_dn(), cert.subject_key_id()); + } + // remove this (used by TLS::Server) virtual std::vector all_subjects() const = 0; - - /** - * Subject DN and (optionally) key identifier - */ - virtual std::vector - find_cert_by_subject_and_key_id( - const X509_DN& subject_dn, - const std::vector& key_id) const = 0; - - /** - * Find CRLs by the DN and key id of the issuer - */ - virtual std::vector - find_crl_by_issuer_and_key_id( - const X509_DN& issuer_dn, - const std::vector& key_id) const = 0; }; /** @@ -58,30 +44,45 @@ class BOTAN_DLL Certificate_Store class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store { public: + /** + * Attempt to parse all files in dir (including subdirectories) + * as certificates. Ignores errors. + */ + Certificate_Store_In_Memory(const std::string& dir); + Certificate_Store_In_Memory() {} - void add_certificate(const X509_Certificate& cert) override; + void add_certificate(const X509_Certificate& cert); - void add_crl(const X509_CRL& crl) override; + void add_crl(const X509_CRL& crl); std::vector all_subjects() const override; - std::vector find_cert_by_subject_and_key_id( + const X509_Certificate* find_cert( const X509_DN& subject_dn, const std::vector& key_id) const override; - std::vector find_crl_by_issuer_and_key_id( - const X509_DN& issuer_dn, - const std::vector& key_id) const override; + const X509_CRL* find_crl(const X509_Certificate& subject) const override; private: // TODO: Add indexing on the DN and key id to avoid linear search std::vector m_certs; std::vector m_crls; }; -// TODO: file backed store -// TODO: directory backed store (eg /usr/share/ca-certificates) -// TODO: sqlite3 backed store +class BOTAN_DLL Certificate_Store_Overlay : public Certificate_Store + { + public: + Certificate_Store_Overlay(const std::vector& certs) : + m_certs(certs) {} + + std::vector all_subjects() const override; + + const X509_Certificate* find_cert( + const X509_DN& subject_dn, + const std::vector& key_id) const override; + private: + const std::vector& m_certs; + }; } diff --git a/src/cert/x509/info.txt b/src/cert/x509/info.txt index 061c52a13..1aebd0c81 100644 --- a/src/cert/x509/info.txt +++ b/src/cert/x509/info.txt @@ -2,4 +2,6 @@ define X509_CERTIFICATES 20131128 datastor +http_util +ocsp diff --git a/src/cert/x509/x509path.cpp b/src/cert/x509/x509path.cpp index e3cd0b424..c8266c129 100644 --- a/src/cert/x509/x509path.cpp +++ b/src/cert/x509/x509path.cpp @@ -20,13 +20,13 @@ namespace { class PKIX_Validation_Failure : public std::exception { public: - PKIX_Validation_Failure(Path_Validation_Result::Code code) : m_code(code) {} + PKIX_Validation_Failure(Certificate_Status_Code code) : m_code(code) {} - Path_Validation_Result::Code code() const { return m_code; } + Certificate_Status_Code code() const { return m_code; } const char* what() const noexcept { return "PKIX validation failed"; } private: - Path_Validation_Result::Code m_code; + Certificate_Status_Code m_code; }; X509_Certificate find_issuing_cert(const X509_Certificate& cert, @@ -37,157 +37,30 @@ X509_Certificate find_issuing_cert(const X509_Certificate& cert, for(size_t i = 0; i != certstores.size(); ++i) { - std::vector certs = - certstores[i]->find_cert_by_subject_and_key_id(issuer_dn, auth_key_id); - - if(certs.empty()) - continue; - - if(certs.size() > 1) - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_MULTIPLE_ISSUERS_FOUND); - - return certs[0]; + if(const X509_Certificate* cert = certstores[i]->find_cert(issuer_dn, auth_key_id)) + return *cert; } - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_ISSUER_NOT_FOUND); + throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND); } -std::vector find_crls_from(const X509_Certificate& cert, - const std::vector& certstores) +const X509_CRL* find_crls_from(const X509_Certificate& cert, + const std::vector& certstores) { const X509_DN issuer_dn = cert.subject_dn(); const std::vector auth_key_id = cert.subject_key_id(); for(size_t i = 0; i != certstores.size(); ++i) { - std::vector crl = - certstores[i]->find_crl_by_issuer_and_key_id(issuer_dn, auth_key_id); - - if(!crl.empty()) + if(const X509_CRL* crl = certstores[i]->find_crl(cert)) return crl; } - return std::vector(); + return nullptr; } } -Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, - size_t key_strength) : - m_require_revocation_information(require_rev), - m_minimum_key_strength(key_strength) - { - m_trusted_hashes.insert("SHA-160"); - m_trusted_hashes.insert("SHA-224"); - m_trusted_hashes.insert("SHA-256"); - m_trusted_hashes.insert("SHA-384"); - m_trusted_hashes.insert("SHA-512"); - } - -const X509_Certificate& Path_Validation_Result::trust_root() const - { - return m_cert_path[m_cert_path.size()-1]; - } - -std::set Path_Validation_Result::trusted_hashes() const - { - std::set hashes; - for(size_t i = 0; i != m_cert_path.size(); ++i) - hashes.insert(m_cert_path[i].hash_used_for_signature()); - return hashes; - } - -std::string Path_Validation_Result::result_string() const - { - switch(m_result) - { - case VERIFIED: - return "verified"; - case UNKNOWN_X509_ERROR: - return "unknown error"; - case CANNOT_ESTABLISH_TRUST: - return "cannot establish trust"; - case CERT_CHAIN_TOO_LONG: - return "certificate chain too long"; - case SIGNATURE_ERROR: - return "signature error"; - case SIGNATURE_METHOD_TOO_WEAK: - return "signature method too weak"; - - case POLICY_ERROR: - return "policy error"; - case INVALID_USAGE: - return "invalid usage"; - case UNTRUSTED_HASH: - return "untrusted hash function"; - - case CERT_MULTIPLE_ISSUERS_FOUND: - return "Multiple certificate issuers found"; - case CERT_FORMAT_ERROR: - return "Certificate format error"; - case CERT_ISSUER_NOT_FOUND: - return "Certificate issuer not found"; - case CERT_NOT_YET_VALID: - return "Certificate is not yet valid"; - case CERT_HAS_EXPIRED: - return "Certificate has expired"; - case CERT_IS_REVOKED: - return "Certificate is revoked"; - case CRL_NOT_FOUND: - return "CRL not found"; - case CRL_FORMAT_ERROR: - return "CRL format error"; - case CRL_NOT_YET_VALID: - return "CRL is not yet valid"; - case CRL_HAS_EXPIRED: - return "CRL has expired"; - case CA_CERT_CANNOT_SIGN: - return "CA certificate cannot sign"; - case CA_CERT_NOT_FOR_CERT_ISSUER: - return "CA certificate not allowed to issue certs"; - case CA_CERT_NOT_FOR_CRL_ISSUER: - return "CA certificate not allowed to issue CRLs"; - } - - // default case - return "Unknown code " + std::to_string(m_result); - } - -Path_Validation_Result x509_path_validate( - const X509_Certificate& end_cert, - const Path_Validation_Restrictions& restrictions, - const std::vector& certstores) - { - std::vector certs; - certs.push_back(end_cert); - return x509_path_validate(certs, restrictions, certstores); - } - -Path_Validation_Result x509_path_validate( - const std::vector& end_certs, - const Path_Validation_Restrictions& restrictions, - const Certificate_Store& store) - { - std::vector certstores; - certstores.push_back(const_cast(&store)); - - return x509_path_validate(end_certs, restrictions, certstores); - } - -Path_Validation_Result x509_path_validate( - const X509_Certificate& end_cert, - const Path_Validation_Restrictions& restrictions, - const Certificate_Store& store) - { - std::vector certs; - certs.push_back(end_cert); - - std::vector certstores; - certstores.push_back(const_cast(&store)); - - return x509_path_validate(certs, restrictions, certstores); - } - Path_Validation_Result x509_path_validate( const std::vector& end_certs, const Path_Validation_Restrictions& restrictions, @@ -222,18 +95,12 @@ Path_Validation_Result x509_path_validate( { const X509_Certificate& subject = cert_path[i]; - if(!trusted_hashes.empty() && i != cert_path.size() - 1) - { - if(trusted_hashes.count(subject.hash_used_for_signature()) == 0) - throw PKIX_Validation_Failure(Path_Validation_Result::UNTRUSTED_HASH); - } - // Check all certs for valid time range if(current_time < X509_Time(subject.start_time())) - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_NOT_YET_VALID); + throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_NOT_YET_VALID); if(current_time > X509_Time(subject.end_time())) - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_HAS_EXPIRED); + throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_HAS_EXPIRED); const bool at_self_signed_root = (i == cert_path.size() - 1); @@ -244,18 +111,22 @@ Path_Validation_Result x509_path_validate( // Don't require CA bit set on self-signed end entity cert if(!issuer.is_CA_cert() && !self_signed_ee_cert) - throw PKIX_Validation_Failure(Path_Validation_Result::CA_CERT_NOT_FOR_CERT_ISSUER); + throw PKIX_Validation_Failure(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER); if(issuer.path_limit() < i) - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_CHAIN_TOO_LONG); + throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_CHAIN_TOO_LONG); std::unique_ptr issuer_key(issuer.subject_public_key()); if(subject.check_signature(*issuer_key) == false) - throw PKIX_Validation_Failure(Path_Validation_Result::SIGNATURE_ERROR); + throw PKIX_Validation_Failure(Certificate_Status_Code::SIGNATURE_ERROR); if(issuer_key->estimated_strength() < restrictions.minimum_key_strength()) - throw PKIX_Validation_Failure(Path_Validation_Result::SIGNATURE_METHOD_TOO_WEAK); + throw PKIX_Validation_Failure(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK); + + if(!trusted_hashes.empty() && !at_self_signed_root) + if(!trusted_hashes.count(subject.hash_used_for_signature())) + throw PKIX_Validation_Failure(Certificate_Status_Code::UNTRUSTED_HASH); } for(size_t i = 1; i != cert_path.size(); ++i) @@ -263,36 +134,36 @@ Path_Validation_Result x509_path_validate( const X509_Certificate& subject = cert_path[i-1]; const X509_Certificate& ca = cert_path[i]; - std::vector crls = find_crls_from(ca, certstores); + const X509_CRL* crl_p = find_crls_from(ca, certstores); - if(crls.empty()) + if(!crl_p) { if(restrictions.require_revocation_information()) - throw PKIX_Validation_Failure(Path_Validation_Result::CRL_NOT_FOUND); + throw PKIX_Validation_Failure(Certificate_Status_Code::CRL_NOT_FOUND); continue; } - const X509_CRL& crl = crls[0]; + const X509_CRL& crl = *crl_p; if(!ca.allowed_usage(CRL_SIGN)) - throw PKIX_Validation_Failure(Path_Validation_Result::CA_CERT_NOT_FOR_CRL_ISSUER); + throw PKIX_Validation_Failure(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER); if(current_time < X509_Time(crl.this_update())) - throw PKIX_Validation_Failure(Path_Validation_Result::CRL_NOT_YET_VALID); + throw PKIX_Validation_Failure(Certificate_Status_Code::CRL_NOT_YET_VALID); if(current_time > X509_Time(crl.next_update())) - throw PKIX_Validation_Failure(Path_Validation_Result::CRL_HAS_EXPIRED); + throw PKIX_Validation_Failure(Certificate_Status_Code::CRL_HAS_EXPIRED); if(crl.check_signature(ca.subject_public_key()) == false) - throw PKIX_Validation_Failure(Path_Validation_Result::SIGNATURE_ERROR); + throw PKIX_Validation_Failure(Certificate_Status_Code::SIGNATURE_ERROR); if(crl.is_revoked(subject)) - throw PKIX_Validation_Failure(Path_Validation_Result::CERT_IS_REVOKED); + throw PKIX_Validation_Failure(Certificate_Status_Code::CERT_IS_REVOKED); } r.set_result(self_signed_ee_cert ? - Path_Validation_Result::CANNOT_ESTABLISH_TRUST : - Path_Validation_Result::VERIFIED); + Certificate_Status_Code::CANNOT_ESTABLISH_TRUST : + Certificate_Status_Code::VERIFIED); } catch(PKIX_Validation_Failure& e) { @@ -302,4 +173,133 @@ Path_Validation_Result x509_path_validate( return r; } +Path_Validation_Result x509_path_validate( + const X509_Certificate& end_cert, + const Path_Validation_Restrictions& restrictions, + const std::vector& certstores) + { + std::vector certs; + certs.push_back(end_cert); + return x509_path_validate(certs, restrictions, certstores); + } + +Path_Validation_Result x509_path_validate( + const std::vector& end_certs, + const Path_Validation_Restrictions& restrictions, + const Certificate_Store& store) + { + std::vector certstores; + certstores.push_back(const_cast(&store)); + + return x509_path_validate(end_certs, restrictions, certstores); + } + +Path_Validation_Result x509_path_validate( + const X509_Certificate& end_cert, + const Path_Validation_Restrictions& restrictions, + const Certificate_Store& store) + { + std::vector certs; + certs.push_back(end_cert); + + std::vector certstores; + certstores.push_back(const_cast(&store)); + + return x509_path_validate(certs, restrictions, certstores); + } + +Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, + size_t key_strength) : + m_require_revocation_information(require_rev), + m_minimum_key_strength(key_strength) + { + if(key_strength <= 80) + m_trusted_hashes.insert("SHA-160"); + + m_trusted_hashes.insert("SHA-224"); + m_trusted_hashes.insert("SHA-256"); + m_trusted_hashes.insert("SHA-384"); + m_trusted_hashes.insert("SHA-512"); + } + +const X509_Certificate& Path_Validation_Result::trust_root() const + { + return m_cert_path[m_cert_path.size()-1]; + } + +std::set Path_Validation_Result::trusted_hashes() const + { + std::set hashes; + for(size_t i = 0; i != m_cert_path.size(); ++i) + hashes.insert(m_cert_path[i].hash_used_for_signature()); + return hashes; + } + +std::string Path_Validation_Result::result_string() const + { + switch(m_result) + { + case VERIFIED: + return "verified"; + case UNKNOWN_X509_ERROR: + return "unknown error"; + case CANNOT_ESTABLISH_TRUST: + return "cannot establish trust"; + case CERT_CHAIN_TOO_LONG: + return "certificate chain too long"; + case SIGNATURE_ERROR: + return "signature error"; + case SIGNATURE_METHOD_TOO_WEAK: + return "signature method too weak"; + + case POLICY_ERROR: + return "policy error"; + case INVALID_USAGE: + return "invalid usage"; + case UNTRUSTED_HASH: + return "untrusted hash function"; + + case CERT_MULTIPLE_ISSUERS_FOUND: + return "Multiple certificate issuers found"; + case CERT_FORMAT_ERROR: + return "Certificate format error"; + case CERT_ISSUER_NOT_FOUND: + return "Certificate issuer not found"; + case CERT_NOT_YET_VALID: + return "Certificate is not yet valid"; + case CERT_HAS_EXPIRED: + return "Certificate has expired"; + case CERT_IS_REVOKED: + return "Certificate is revoked"; + case CRL_NOT_FOUND: + return "CRL not found"; + case CRL_FORMAT_ERROR: + return "CRL format error"; + case CRL_NOT_YET_VALID: + return "CRL is not yet valid"; + case CRL_HAS_EXPIRED: + return "CRL has expired"; + case CA_CERT_CANNOT_SIGN: + return "CA certificate cannot sign"; + case CA_CERT_NOT_FOR_CERT_ISSUER: + return "CA certificate not allowed to issue certs"; + case CA_CERT_NOT_FOR_CRL_ISSUER: + return "CA certificate not allowed to issue CRLs"; + + case OCSP_CERT_NOT_LISTED: + return "OCSP response does not included requested cert"; + case OCSP_NOT_YET_VALID: + return "OCSP response is from the future"; + case OCSP_EXPIRED: + return "OCSP response is expired"; + case OCSP_BAD_STATUS: + return "OCSP response had unknown/bad status"; + case OCSP_RESPONSE_GOOD: + return "OCSP response had good status"; + } + + // default case + return "Unknown code " + std::to_string(m_result); + } + } diff --git a/src/cert/x509/x509path.h b/src/cert/x509/x509path.h index 829aa9d91..a3854ebf3 100644 --- a/src/cert/x509/x509path.h +++ b/src/cert/x509/x509path.h @@ -8,6 +8,7 @@ #ifndef BOTAN_X509_CERT_PATH_VALIDATION_H__ #define BOTAN_X509_CERT_PATH_VALIDATION_H__ +#include #include #include #include @@ -66,39 +67,6 @@ class BOTAN_DLL Path_Validation_Restrictions class BOTAN_DLL Path_Validation_Result { public: - /** - * X.509 Certificate Validation Result - */ - enum Code { - VERIFIED, - UNKNOWN_X509_ERROR, - CANNOT_ESTABLISH_TRUST, - CERT_CHAIN_TOO_LONG, - SIGNATURE_ERROR, - POLICY_ERROR, - INVALID_USAGE, - - SIGNATURE_METHOD_TOO_WEAK, - UNTRUSTED_HASH, - - CERT_MULTIPLE_ISSUERS_FOUND, - - CERT_FORMAT_ERROR, - CERT_ISSUER_NOT_FOUND, - CERT_NOT_YET_VALID, - CERT_HAS_EXPIRED, - CERT_IS_REVOKED, - - CRL_NOT_FOUND, - CRL_FORMAT_ERROR, - CRL_NOT_YET_VALID, - CRL_HAS_EXPIRED, - - CA_CERT_CANNOT_SIGN, - CA_CERT_NOT_FOR_CERT_ISSUER, - CA_CERT_NOT_FOR_CRL_ISSUER - }; - /** * @return the set of hash functions you are implicitly * trusting by trusting this result. @@ -123,7 +91,7 @@ class BOTAN_DLL Path_Validation_Result /** * @return validation result code */ - Code result() const { return m_result; } + Certificate_Status_Code result() const { return m_result; } /** * @return string representation of the validation result @@ -138,9 +106,9 @@ class BOTAN_DLL Path_Validation_Result const Path_Validation_Restrictions& restrictions, const std::vector& certstores); - void set_result(Code result) { m_result = result; } + void set_result(Certificate_Status_Code result) { m_result = result; } - Code m_result; + Certificate_Status_Code m_result; std::vector m_cert_path; }; -- cgit v1.2.3