aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-11-16 01:34:19 -0500
committerJack Lloyd <[email protected]>2016-11-26 12:41:03 -0500
commit10244d3fa88365a3740eb66ccfd9c90f3a866fcd (patch)
tree54a746fdcdebd327bbd71d023ce6d02ef7e66b44 /src
parent5372d0b499ad317ab3776c9ac92df866cc6a1e84 (diff)
Add OCSP stapling support to TLS client
Diffstat (limited to 'src')
-rw-r--r--src/cli/tls_client.cpp7
-rw-r--r--src/lib/tls/msg_cert_status.cpp65
-rw-r--r--src/lib/tls/msg_client_hello.cpp8
-rw-r--r--src/lib/tls/msg_server_hello.cpp18
-rw-r--r--src/lib/tls/tls_callbacks.cpp4
-rw-r--r--src/lib/tls/tls_callbacks.h3
-rw-r--r--src/lib/tls/tls_client.cpp86
-rw-r--r--src/lib/tls/tls_extensions.cpp71
-rw-r--r--src/lib/tls/tls_extensions.h36
-rw-r--r--src/lib/tls/tls_handshake_state.cpp6
-rw-r--r--src/lib/tls/tls_handshake_state.h6
-rw-r--r--src/lib/tls/tls_messages.h31
-rw-r--r--src/lib/tls/tls_server.cpp1
-rw-r--r--src/lib/x509/ocsp.cpp10
-rw-r--r--src/lib/x509/ocsp.h15
-rw-r--r--src/lib/x509/x509path.cpp25
-rw-r--r--src/lib/x509/x509path.h18
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 = {});
/**