diff options
author | Jack Lloyd <[email protected]> | 2016-11-16 01:34:19 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-11-26 12:41:03 -0500 |
commit | 10244d3fa88365a3740eb66ccfd9c90f3a866fcd (patch) | |
tree | 54a746fdcdebd327bbd71d023ce6d02ef7e66b44 | |
parent | 5372d0b499ad317ab3776c9ac92df866cc6a1e84 (diff) |
Add OCSP stapling support to TLS client
-rw-r--r-- | src/cli/tls_client.cpp | 7 | ||||
-rw-r--r-- | src/lib/tls/msg_cert_status.cpp | 65 | ||||
-rw-r--r-- | src/lib/tls/msg_client_hello.cpp | 8 | ||||
-rw-r--r-- | src/lib/tls/msg_server_hello.cpp | 18 | ||||
-rw-r--r-- | src/lib/tls/tls_callbacks.cpp | 4 | ||||
-rw-r--r-- | src/lib/tls/tls_callbacks.h | 3 | ||||
-rw-r--r-- | src/lib/tls/tls_client.cpp | 86 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.cpp | 71 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.h | 36 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.cpp | 6 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.h | 6 | ||||
-rw-r--r-- | src/lib/tls/tls_messages.h | 31 | ||||
-rw-r--r-- | src/lib/tls/tls_server.cpp | 1 | ||||
-rw-r--r-- | src/lib/x509/ocsp.cpp | 10 | ||||
-rw-r--r-- | src/lib/x509/ocsp.h | 15 | ||||
-rw-r--r-- | src/lib/x509/x509path.cpp | 25 | ||||
-rw-r--r-- | src/lib/x509/x509path.h | 18 |
17 files changed, 347 insertions, 63 deletions
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 8e21e21e5..b6cb43ff9 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -11,6 +11,7 @@ #include <botan/tls_client.h> #include <botan/x509path.h> +#include <botan/ocsp.h> #include <botan/hex.h> #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) @@ -253,6 +254,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks void tls_verify_cert_chain( const std::vector<Botan::X509_Certificate>& cert_chain, + const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp, const std::vector<Botan::Certificate_Store*>& trusted_roots, Botan::Usage_Type usage, const std::string& hostname, @@ -263,7 +265,7 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks Botan::Path_Validation_Restrictions restrictions(true, policy.minimum_signature_strength()); - auto ocsp_timeout = std::chrono::milliseconds(300); + auto ocsp_timeout = std::chrono::milliseconds(1000); Botan::Path_Validation_Result result = Botan::x509_path_validate(cert_chain, @@ -272,7 +274,8 @@ class TLS_Client final : public Command, public Botan::TLS::Callbacks hostname, usage, std::chrono::system_clock::now(), - ocsp_timeout); + ocsp_timeout, + ocsp); std::cout << "Certificate validation status: " << result.result_string() << "\n"; if(result.successful_validation()) diff --git a/src/lib/tls/msg_cert_status.cpp b/src/lib/tls/msg_cert_status.cpp new file mode 100644 index 000000000..f28fe10d2 --- /dev/null +++ b/src/lib/tls/msg_cert_status.cpp @@ -0,0 +1,65 @@ +/* +* Certificate Status +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_handshake_io.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> + +namespace Botan { + +namespace TLS { + +Certificate_Status::Certificate_Status(const std::vector<byte>& buf) + { + if(buf.size() < 5) + throw Decoding_Error("Invalid Certificate_Status message: too small"); + + if(buf[0] != 1) + throw Decoding_Error("Unexpected Certificate_Status message: unexpected message type"); + + size_t len = make_u32bit(0, buf[1], buf[2], buf[3]); + + // Verify the redundant length field... + if(buf.size() != len + 4) + throw Decoding_Error("Invalid Certificate_Status: invalid length field"); + + m_response = std::make_shared<OCSP::Response>(buf.data() + 4, buf.size() - 4); + } + +Certificate_Status::Certificate_Status(Handshake_IO& io, + Handshake_Hash& hash, + std::shared_ptr<const OCSP::Response> ocsp) : + m_response(ocsp) + { + hash.update(io.send(*this)); + } + +std::vector<byte> Certificate_Status::serialize() const + { + BOTAN_ASSERT_NONNULL(m_response); + const std::vector<byte>& m_resp_bits = m_response->raw_bits(); + + if(m_resp_bits.size() > 0xFFFFFF) // unlikely + throw Encoding_Error("OCSP response too long to encode in TLS"); + + const uint32_t m_resp_bits_len = static_cast<u32bit>(m_resp_bits.size()); + + std::vector<byte> buf; + buf.push_back(1); // type OCSP + for(size_t i = 1; i < 4; ++i) + buf[i] = get_byte(i, m_resp_bits_len); + + buf += m_resp_bits; + return buf; + } + +} + +} diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 2a42e1144..870307217 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -76,8 +76,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, const std::vector<std::string>& next_protocols) : m_version(client_settings.protocol_version()), m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_version, - client_settings.srp_identifier() != "")), + m_suites(policy.ciphersuite_list(m_version, !client_settings.srp_identifier().empty())), m_comp_methods(policy.compression()) { BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()), @@ -89,12 +88,16 @@ Client_Hello::Client_Hello(Handshake_IO& io, */ m_extensions.add(new Extended_Master_Secret); m_extensions.add(new Session_Ticket()); + m_extensions.add(new Certificate_Status_Request); + if(policy.negotiate_encrypt_then_mac()) m_extensions.add(new Encrypt_then_MAC); m_extensions.add(new Renegotiation_Extension(reneg_info)); m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); + m_extensions.add(new Certificate_Status_Request({}, {})); + if(reneg_info.empty() && !next_protocols.empty()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); @@ -159,6 +162,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, attempt and upgrade us to a new session with the EMS protection. */ m_extensions.add(new Extended_Master_Secret); + m_extensions.add(new Certificate_Status_Request); m_extensions.add(new Renegotiation_Extension(reneg_info)); m_extensions.add(new Server_Name_Indicator(session.server_info().hostname())); diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index 3e8a8dda9..37e521403 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -35,12 +35,15 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); + // Sending the extension back does not commit us to sending a stapled response + if(client_hello.supports_cert_status_message()) + m_extensions.add(new Certificate_Status_Request); + Ciphersuite c = Ciphersuite::by_id(m_ciphersuite); - if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) + if(c.cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { - if(c.cbc_ciphersuite()) - m_extensions.add(new Encrypt_then_MAC); + m_extensions.add(new Encrypt_then_MAC); } if(c.ecc_ciphersuite()) @@ -100,6 +103,10 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); + // Sending the extension back does not commit us to sending a stapled response + if(client_hello.supports_cert_status_message()) + m_extensions.add(new Certificate_Status_Request); + if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) { Ciphersuite c = resumed_session.ciphersuite(); @@ -107,6 +114,11 @@ Server_Hello::Server_Hello(Handshake_IO& io, m_extensions.add(new Encrypt_then_MAC); } + if(client_hello.supports_cert_status_message()) + { + m_extensions.add(new Certificate_Status_Request); + } + if(resumed_session.ciphersuite().ecc_ciphersuite()) { m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression())); diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp index e95b1c0f7..7afb3f17f 100644 --- a/src/lib/tls/tls_callbacks.cpp +++ b/src/lib/tls/tls_callbacks.cpp @@ -27,6 +27,7 @@ std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std void TLS::Callbacks::tls_verify_cert_chain( const std::vector<X509_Certificate>& cert_chain, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses, const std::vector<Certificate_Store*>& trusted_roots, Usage_Type usage, const std::string& hostname, @@ -44,7 +45,8 @@ void TLS::Callbacks::tls_verify_cert_chain( (usage == Usage_Type::TLS_SERVER_AUTH ? hostname : ""), usage, std::chrono::system_clock::now(), - tls_verify_cert_chain_ocsp_timeout()); + tls_verify_cert_chain_ocsp_timeout(), + ocsp_responses); if(!result.successful_validation()) throw Exception("Certificate validation failure: " + result.result_string()); diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h index c5fe32f29..89e4aaa5d 100644 --- a/src/lib/tls/tls_callbacks.h +++ b/src/lib/tls/tls_callbacks.h @@ -108,8 +108,8 @@ class BOTAN_DLL Callbacks * * @param cert_chain specifies a certificate chain leading to a * trusted root CA certificate. + * @param ocsp_responses the server may have provided some * @param trusted_roots the list of trusted certificates - * @param usage what this cert chain is being used for * Usage_Type::TLS_SERVER_AUTH for server chains, * Usage_Type::TLS_CLIENT_AUTH for client chains, @@ -123,6 +123,7 @@ class BOTAN_DLL Callbacks */ virtual void tls_verify_cert_chain( const std::vector<X509_Certificate>& cert_chain, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses, const std::vector<Certificate_Store*>& trusted_roots, Usage_Type usage, const std::string& hostname, diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 185084734..37dd37812 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -13,6 +13,8 @@ #include <iterator> #include <sstream> +#include <botan/hex.h> + namespace Botan { namespace TLS { @@ -26,7 +28,7 @@ class Client_Handshake_State : public Handshake_State Client_Handshake_State(Handshake_IO* io, Callbacks& cb) : Handshake_State(io, cb) {} - const Public_Key& get_server_public_Key() const + const Public_Key& get_server_public_key() const { BOTAN_ASSERT(server_public_key, "Server sent us a certificate"); return *server_public_key.get(); @@ -370,16 +372,6 @@ void Client::process_handshake_msg(const Handshake_State* active_state, } else if(type == CERTIFICATE) { - if(state.ciphersuite().kex_algo() != "RSA") - { - state.set_expected_next(SERVER_KEX); - } - else - { - state.set_expected_next(CERTIFICATE_REQUEST); // optional - state.set_expected_next(SERVER_HELLO_DONE); - } - state.server_certs(new Certificate(contents, policy())); const std::vector<X509_Certificate>& server_certs = @@ -389,20 +381,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state, throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Client: No certificates sent by server"); - try - { - auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname()); - - callbacks().tls_verify_cert_chain(server_certs, - trusted_CAs, - Usage_Type::TLS_SERVER_AUTH, - m_info.hostname(), - policy()); - } - catch(std::exception& e) - { - throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); - } + /* + Certificate verification happens after we receive the server hello done, + in case an OCSP response was also available + */ std::unique_ptr<Public_Key> peer_key(server_certs[0].subject_public_key()); @@ -411,6 +393,35 @@ void Client::process_handshake_msg(const Handshake_State* active_state, "Certificate key type did not match ciphersuite"); state.server_public_key.reset(peer_key.release()); + + if(state.ciphersuite().kex_algo() != "RSA") + { + state.set_expected_next(SERVER_KEX); + } + else + { + state.set_expected_next(CERTIFICATE_REQUEST); // optional + state.set_expected_next(SERVER_HELLO_DONE); + } + + if(state.server_hello()->supports_certificate_status_message()) + { + state.set_expected_next(CERTIFICATE_STATUS); // optional + } + } + else if(type == CERTIFICATE_STATUS) + { + state.server_cert_status(new Certificate_Status(contents)); + + if(state.ciphersuite().kex_algo() != "RSA") + { + state.set_expected_next(SERVER_KEX); + } + else + { + state.set_expected_next(CERTIFICATE_REQUEST); // optional + state.set_expected_next(SERVER_HELLO_DONE); + } } else if(type == SERVER_KEX) { @@ -426,7 +437,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state, if(state.ciphersuite().sig_algo() != "") { - const Public_Key& server_key = state.get_server_public_Key(); + const Public_Key& server_key = state.get_server_public_key(); if(!state.server_kex()->verify(server_key, state, policy())) { @@ -444,6 +455,29 @@ void Client::process_handshake_msg(const Handshake_State* active_state, { state.server_hello_done(new Server_Hello_Done(contents)); + if(state.server_certs() != nullptr) + { + try + { + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname()); + + std::vector<std::shared_ptr<const OCSP::Response>> ocsp; + if(state.server_cert_status() != nullptr) + ocsp.push_back(state.server_cert_status()->response()); + + callbacks().tls_verify_cert_chain(state.server_certs()->cert_chain(), + ocsp, + trusted_CAs, + Usage_Type::TLS_SERVER_AUTH, + m_info.hostname(), + policy()); + } + catch(std::exception& e) + { + throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); + } + } + if(state.received_handshake_msg(CERTIFICATE_REQUEST)) { const auto& types = state.cert_req()->acceptable_cert_types(); diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 712527fc4..731e149b2 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -16,9 +16,7 @@ namespace TLS { namespace { -Extension* make_extension(TLS_Data_Reader& reader, - u16bit code, - u16bit size) +Extension* make_extension(TLS_Data_Reader& reader, u16bit code, u16bit size) { switch(code) { @@ -33,6 +31,9 @@ Extension* make_extension(TLS_Data_Reader& reader, case TLSEXT_USABLE_ELLIPTIC_CURVES: return new Supported_Elliptic_Curves(reader, size); + case TLSEXT_CERT_STATUS_REQUEST: + return new Certificate_Status_Request(reader, size); + case TLSEXT_EC_POINT_FORMATS: return new Supported_Point_Formats(reader, size); @@ -56,10 +57,9 @@ Extension* make_extension(TLS_Data_Reader& reader, case TLSEXT_SESSION_TICKET: return new Session_Ticket(reader, size); - - default: - return nullptr; // not known } + + return nullptr; // not known } } @@ -606,7 +606,7 @@ std::vector<byte> Extended_Master_Secret::serialize() const } Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&, - u16bit extension_size) + u16bit extension_size) { if(extension_size != 0) throw Decoding_Error("Invalid encrypt_then_mac extension"); @@ -617,6 +617,63 @@ std::vector<byte> Encrypt_then_MAC::serialize() const return std::vector<byte>(); } +std::vector<byte> Certificate_Status_Request::serialize() const + { + std::vector<byte> buf; + + if(m_server_side) + return buf; // server reply is empty + + /* + opaque ResponderID<1..2^16-1>; + opaque Extensions<0..2^16-1>; + + CertificateStatusType status_type = ocsp(1) + ResponderID responder_id_list<0..2^16-1> + Extensions request_extensions; + */ + + buf.push_back(1); // CertificateStatusType ocsp + + buf.push_back(0); + buf.push_back(0); + buf.push_back(0); + buf.push_back(0); + + return buf; + } + +Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader, + u16bit extension_size) + { + if(extension_size > 0) + { + const byte type = reader.get_byte(); + if(type == 1) + { + reader.discard_next(extension_size - 1); // fixme + } + else + { + reader.discard_next(extension_size - 1); + } + } + } + +Certificate_Status_Request::Certificate_Status_Request(const std::vector<X509_DN>& ocsp_responder_ids, + const std::vector<std::vector<byte>>& ocsp_key_ids) : + m_ocsp_names(ocsp_responder_ids), + m_ocsp_keys(ocsp_key_ids), + m_server_side(false) + { + + } + +Certificate_Status_Request::Certificate_Status_Request() : m_server_side(true) + { + + } + } } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index 119170797..f766a3b1b 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -12,6 +12,7 @@ #include <botan/secmem.h> #include <botan/tls_magic.h> +#include <botan/ocsp.h> #include <vector> #include <string> #include <map> @@ -25,10 +26,7 @@ class TLS_Data_Reader; enum Handshake_Extension_Type { TLSEXT_SERVER_NAME_INDICATION = 0, - // 1 is maximum fragment length - TLSEXT_CLIENT_CERT_URL = 2, - TLSEXT_TRUSTED_CA_KEYS = 3, - TLSEXT_TRUNCATED_HMAC = 4, + TLSEXT_CERT_STATUS_REQUEST = 5, TLSEXT_CERTIFICATE_TYPES = 9, TLSEXT_USABLE_ELLIPTIC_CURVES = 10, @@ -397,6 +395,36 @@ class Encrypt_then_MAC final : public Extension }; /** +* Certificate Status Request (RFC 6066) +*/ +class Certificate_Status_Request final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_CERT_STATUS_REQUEST; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector<byte> serialize() const override; + + bool empty() const override { return false; } + + // Server generated version: empty + Certificate_Status_Request(); + + // Client version, both lists can be empty + Certificate_Status_Request(const std::vector<X509_DN>& ocsp_responder_ids, + const std::vector<std::vector<byte>>& ocsp_key_ids); + + Certificate_Status_Request(TLS_Data_Reader& reader, u16bit extension_size); + private: + std::vector<X509_DN> m_ocsp_names; + std::vector<std::vector<byte>> m_ocsp_keys; + std::vector<byte> m_extension_bytes; + bool m_server_side; + }; + +/** * Represents a block of extensions in a hello message */ class BOTAN_DLL Extensions diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index 71cacdabd..039d5b326 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -218,6 +218,12 @@ void Handshake_State::server_certs(Certificate* server_certs) note_message(*m_server_certs); } +void Handshake_State::server_cert_status(Certificate_Status* server_cert_status) + { + m_server_cert_status.reset(server_cert_status); + note_message(*m_server_cert_status); + } + void Handshake_State::server_kex(Server_Key_Exchange* server_kex) { m_server_kex.reset(server_kex); diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h index bdec10d14..bdfc0d5d5 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls_handshake_state.h @@ -31,6 +31,7 @@ class Hello_Verify_Request; class Client_Hello; class Server_Hello; class Certificate; +class Certificate_Status; class Server_Key_Exchange; class Certificate_Req; class Server_Hello_Done; @@ -105,6 +106,7 @@ class Handshake_State void client_hello(Client_Hello* client_hello); void server_hello(Server_Hello* server_hello); void server_certs(Certificate* server_certs); + void server_cert_status(Certificate_Status* server_cert_status); void server_kex(Server_Key_Exchange* server_kex); void cert_req(Certificate_Req* cert_req); void server_hello_done(Server_Hello_Done* server_hello_done); @@ -142,6 +144,9 @@ class Handshake_State const Certificate_Verify* client_verify() const { return m_client_verify.get(); } + const Certificate_Status* server_cert_status() const + { return m_server_cert_status.get(); } + const New_Session_Ticket* new_session_ticket() const { return m_new_session_ticket.get(); } @@ -180,6 +185,7 @@ class Handshake_State std::unique_ptr<Client_Hello> m_client_hello; std::unique_ptr<Server_Hello> m_server_hello; std::unique_ptr<Certificate> m_server_certs; + std::unique_ptr<Certificate_Status> m_server_cert_status; std::unique_ptr<Server_Key_Exchange> m_server_kex; std::unique_ptr<Certificate_Req> m_cert_req; std::unique_ptr<Server_Hello_Done> m_server_hello_done; diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 5e6962329..82fa22320 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -184,6 +184,11 @@ class BOTAN_DLL Client_Hello final : public Handshake_Message return m_extensions.has<Extended_Master_Secret>(); } + bool supports_cert_status_message() const + { + return m_extensions.has<Certificate_Status_Request>(); + } + bool supports_encrypt_then_mac() const { return m_extensions.has<Encrypt_then_MAC>(); @@ -313,6 +318,11 @@ class BOTAN_DLL Server_Hello final : public Handshake_Message return m_extensions.has<Encrypt_then_MAC>(); } + bool supports_certificate_status_message() const + { + return m_extensions.has<Certificate_Status_Request>(); + } + bool supports_session_ticket() const { return m_extensions.has<Session_Ticket>(); @@ -439,6 +449,27 @@ class Certificate final : public Handshake_Message }; /** +* Certificate Status (RFC 6066) +*/ +class Certificate_Status final : public Handshake_Message + { + public: + Handshake_Type type() const override { return CERTIFICATE_STATUS; } + + std::shared_ptr<const OCSP::Response> response() const { return m_response; } + + Certificate_Status(const std::vector<byte>& buf); + + Certificate_Status(Handshake_IO& io, + Handshake_Hash& hash, + std::shared_ptr<const OCSP::Response> response); + + private: + std::vector<byte> serialize() const override; + std::shared_ptr<const OCSP::Response> m_response; + }; + +/** * Certificate Request Message */ class Certificate_Req final : public Handshake_Message diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 4e07b5f7c..78c7704cc 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -527,6 +527,7 @@ void Server::process_certificate_verify_msg(Server_Handshake_State& pending_stat auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); callbacks().tls_verify_cert_chain(client_certs, + {}, // ocsp trusted_CAs, Usage_Type::TLS_CLIENT_AUTH, sni_hostname, diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp index fb7b718b6..bd3f1855e 100644 --- a/src/lib/x509/ocsp.cpp +++ b/src/lib/x509/ocsp.cpp @@ -81,9 +81,10 @@ std::string Request::base64_encode() const return Botan::base64_encode(BER_encode()); } -Response::Response(const std::vector<byte>& response_bits) +Response::Response(const uint8_t response_bits[], size_t response_bits_len) : + m_response_bits(response_bits, response_bits + response_bits_len) { - BER_Decoder response_outer = BER_Decoder(response_bits).start_cons(SEQUENCE); + BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE); size_t resp_status = 0; @@ -233,8 +234,11 @@ Certificate_Status_Code Response::check_signature(const std::vector<Certificate_ if(!signing_cert) return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND; - if(!signing_cert->allowed_extended_usage("PKIX.OCSPSigning")) + if(!signing_cert->allowed_usage(CRL_SIGN) && + !signing_cert->allowed_extended_usage("PKIX.OCSPSigning")) + { return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE; + } return this->verify_signature(*signing_cert); } diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h index 64e86b82f..05f194392 100644 --- a/src/lib/x509/ocsp.h +++ b/src/lib/x509/ocsp.h @@ -75,7 +75,17 @@ class BOTAN_DLL Response * Parses an OCSP response. * @param response_bits response bits received */ - Response(const std::vector<byte>& response_bits); + Response(const std::vector<byte>& response_bits) : + Response(response_bits.data(), response_bits.size()) + {} + + /** + * Parses an OCSP response. + * @param response_bits response bits received + * @param response_bits_len length of response in bytes + */ + Response(const uint8_t response_bits[], + size_t response_bits_len); /** * Check signature and return status @@ -111,6 +121,8 @@ class BOTAN_DLL Response */ const std::vector<byte>& signer_key_hash() const { return m_key_hash; } + const std::vector<byte>& raw_bits() const { return m_response_bits; } + /** * Searches the OCSP response for issuer and subject certificate. * @param issuer issuer certificate @@ -129,6 +141,7 @@ class BOTAN_DLL Response std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now()) const; private: + std::vector<byte> m_response_bits; X509_Time m_produced_at; X509_DN m_signer_name; std::vector<byte> m_key_hash; diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp index beda83eed..5c1e94ff8 100644 --- a/src/lib/x509/x509path.cpp +++ b/src/lib/x509/x509path.cpp @@ -568,7 +568,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( const std::string& hostname, Usage_Type usage, std::chrono::system_clock::time_point ref_time, - std::chrono::milliseconds ocsp_timeout) + std::chrono::milliseconds ocsp_timeout, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp) { if(end_certs.empty()) throw Invalid_Argument("x509_path_validate called with no subjects"); @@ -601,7 +602,12 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( std::vector<std::set<Certificate_Status_Code>> ocsp_status; - if(ocsp_timeout != std::chrono::milliseconds(0)) + if(ocsp_resp.size() > 0) + { + ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time); + } + + if(ocsp_status.empty() && 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, @@ -626,11 +632,12 @@ Path_Validation_Result x509_path_validate( const std::string& hostname, Usage_Type usage, std::chrono::system_clock::time_point when, - std::chrono::milliseconds ocsp_timeout) + std::chrono::milliseconds ocsp_timeout, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp) { std::vector<X509_Certificate> certs; certs.push_back(end_cert); - return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp); } Path_Validation_Result x509_path_validate( @@ -640,12 +647,13 @@ Path_Validation_Result x509_path_validate( const std::string& hostname, Usage_Type usage, std::chrono::system_clock::time_point when, - std::chrono::milliseconds ocsp_timeout) + std::chrono::milliseconds ocsp_timeout, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp) { std::vector<Certificate_Store*> trusted_roots; trusted_roots.push_back(const_cast<Certificate_Store*>(&store)); - return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); + return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp); } Path_Validation_Result x509_path_validate( @@ -655,7 +663,8 @@ Path_Validation_Result x509_path_validate( const std::string& hostname, Usage_Type usage, std::chrono::system_clock::time_point when, - std::chrono::milliseconds ocsp_timeout) + std::chrono::milliseconds ocsp_timeout, + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp) { std::vector<X509_Certificate> certs; certs.push_back(end_cert); @@ -663,7 +672,7 @@ Path_Validation_Result x509_path_validate( std::vector<Certificate_Store*> trusted_roots; trusted_roots.push_back(const_cast<Certificate_Store*>(&store)); - return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout); + return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp); } Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev, diff --git a/src/lib/x509/x509path.h b/src/lib/x509/x509path.h index e8d90b4a9..79daca672 100644 --- a/src/lib/x509/x509path.h +++ b/src/lib/x509/x509path.h @@ -177,7 +177,8 @@ class BOTAN_DLL Path_Validation_Result * @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 +* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check +* @param ocsp_resp additional OCSP responses to consider (eg from peer) * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -187,7 +188,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( 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::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0), + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {}); /** * PKIX Path Validation @@ -198,6 +200,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( * @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 +* @param ocsp_resp additional OCSP responses to consider (eg from peer) * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -207,7 +210,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( 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::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0), + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {}); /** * PKIX Path Validation @@ -218,6 +222,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( * @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 +* @param ocsp_resp additional OCSP responses to consider (eg from peer) * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -227,7 +232,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( 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::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0), + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {}); /** * PKIX Path Validation @@ -238,6 +244,7 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( * @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 +* @param ocsp_resp additional OCSP responses to consider (eg from peer) * @return result of the path validation */ Path_Validation_Result BOTAN_DLL x509_path_validate( @@ -247,7 +254,8 @@ Path_Validation_Result BOTAN_DLL x509_path_validate( 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::milliseconds ocsp_timeout = std::chrono::milliseconds(0)); + std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0), + const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {}); /** |