aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/api_ref/tls.rst40
-rw-r--r--src/lib/tls/asio/asio_context.h93
-rw-r--r--src/lib/tls/asio/asio_stream.h96
-rw-r--r--src/tests/unit_asio_stream.cpp62
4 files changed, 210 insertions, 81 deletions
diff --git a/doc/api_ref/tls.rst b/doc/api_ref/tls.rst
index 74f8bf79a..f78d59e90 100644
--- a/doc/api_ref/tls.rst
+++ b/doc/api_ref/tls.rst
@@ -85,7 +85,7 @@ information about the connection.
exception which will send a close message to the counterparty and
reset the connection state.
- .. cpp::function:: void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain, \
+ .. cpp:function:: 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, \
@@ -120,7 +120,7 @@ information about the connection.
being authenticated using this certificate chain. It can be consulted
for values such as allowable signature methods and key sizes.
- .. cpp::function:: std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
+ .. cpp:function:: std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
Called by default `tls_verify_cert_chain` to set timeout for online OCSP requests
on the certificate chain. Return 0 to disable OCSP. Current default is 0.
@@ -1618,6 +1618,7 @@ It offers the following interface:
Construct a new TLS stream.
The *context* parameter will be used to set up the underlying *native handle*, i.e. the :ref:`TLS::Client <tls_client>`, when :cpp:func:`handshake` is called.
+ Using code must ensure the context is kept alive for the lifetime of the stream.
The further *args* will be forwarded to the *next layer*'s constructor.
.. cpp:function:: template <typename... Args> \
@@ -1694,22 +1695,23 @@ It offers the following interface:
The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *WriteHandler* type.
*WriteHandler* should suffice the `requirements to a Boost.Asio write handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/WriteHandler.html>`_.
-.. cpp:struct:: TLS::Context
+.. cpp:class:: TLS::Context
- A helper struct to collect the initialization parameters for the Stream's underlying *native handle* (see :cpp:class:`TLS::Client`).
- `TLS::Context` is defined as
+ A helper class to initialize and configure the Stream's underlying *native handle* (see :cpp:class:`TLS::Client`).
- .. code-block:: cpp
+ .. cpp:function:: Context(Credentials_Manager& credentialsManager, \
+ RandomNumberGenerator& randomNumberGenerator, \
+ Session_Manager& sessionManager, \
+ Policy& policy, \
+ Server_Information serverInfo = Server_Information())
- struct Context
- {
- Credentials_Manager* credentialsManager;
- RandomNumberGenerator* randomNumberGenerator;
- Session_Manager* sessionManager;
- Policy* policy;
- Server_Information serverInfo;
- };
+ Constructor for TLS::Context.
+ .. cpp:function:: void set_verify_callback(Verify_Callback_T callback)
+
+ Set a user-defined callback function for certificate chain verification. This
+ will cause the stream to override the default implementation of the
+ :cpp:func:`tls_verify_cert_chain` callback.
Stream Code Example
^^^^^^^^^^^^^^^^^^^^
@@ -1753,11 +1755,11 @@ Stream Code Example
boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
http::request<http::string_body> req)
: request_(req)
- , ctx_{&credentials_mgr_,
- &rng_,
- &session_mgr_,
- &policy_,
- Botan::TLS::Server_Information()}
+ , ctx_(credentials_mgr_,
+ rng_,
+ session_mgr_,
+ policy_,
+ Botan::TLS::Server_Information())
, stream_(io_context, ctx_)
{
boost::asio::async_connect(stream_.lowest_layer(), endpoint_iterator,
diff --git a/src/lib/tls/asio/asio_context.h b/src/lib/tls/asio/asio_context.h
index 7de88ebce..e5e99e83a 100644
--- a/src/lib/tls/asio/asio_context.h
+++ b/src/lib/tls/asio/asio_context.h
@@ -14,8 +14,12 @@
#include <boost/version.hpp>
#if BOOST_VERSION >= 106600
+#include <functional>
+
#include <botan/credentials_manager.h>
+#include <botan/ocsp.h>
#include <botan/rng.h>
+#include <botan/tls_callbacks.h>
#include <botan/tls_policy.h>
#include <botan/tls_server_info.h>
#include <botan/tls_session_manager.h>
@@ -23,13 +27,90 @@
namespace Botan {
namespace TLS {
-struct Context
+namespace detail {
+template <typename FunT>
+struct fn_signature_helper : public std::false_type {};
+
+template <typename R, typename D, typename... Args>
+struct fn_signature_helper<R(D::*)(Args...)>
+ {
+ using type = std::function<R(Args...)>;
+ };
+} // namespace detail
+
+/**
+ * A helper class to initialize and configure Botan::TLS::Stream
+ */
+class Context
{
- Credentials_Manager* credentialsManager;
- RandomNumberGenerator* randomNumberGenerator;
- Session_Manager* sessionManager;
- Policy* policy;
- Server_Information serverInfo;
+ public:
+ // statically extract the function signature type from Callbacks::tls_verify_cert_chain
+ // and reuse it as an std::function<> for the verify callback signature
+ /**
+ * The signature of the callback function should correspond to the signature of
+ * Callbacks::tls_verify_cert_chain
+ */
+ using Verify_Callback =
+ detail::fn_signature_helper<decltype(&Callbacks::tls_verify_cert_chain)>::type;
+
+ Context(Credentials_Manager& credentials_manager,
+ RandomNumberGenerator& rng,
+ Session_Manager& session_manager,
+ Policy& policy,
+ Server_Information server_info = Server_Information()) :
+ m_credentials_manager(credentials_manager),
+ m_rng(rng),
+ m_session_manager(session_manager),
+ m_policy(policy),
+ m_server_info(server_info)
+ {}
+
+ virtual ~Context() = default;
+
+ Context(Context&&) = default;
+ Context(const Context&) = delete;
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ /**
+ * @brief Override the tls_verify_cert_chain callback
+ *
+ * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+ * used in the handshake.
+ * Using this function is equivalent to setting the callback via @see Botan::TLS::Stream::set_verify_callback
+ *
+ * @note This function should only be called before initiating the TLS handshake
+ */
+ void set_verify_callback(Verify_Callback callback)
+ {
+ m_verify_callback = std::move(callback);
+ }
+
+ bool has_verify_callback() const
+ {
+ return static_cast<bool>(m_verify_callback);
+ }
+
+ const Verify_Callback& get_verify_callback() const
+ {
+ return m_verify_callback;
+ }
+
+ void set_server_info(const Server_Information& server_info)
+ {
+ m_server_info = server_info;
+ }
+
+ protected:
+ template <class S, class C> friend class Stream;
+
+ Credentials_Manager& m_credentials_manager;
+ RandomNumberGenerator& m_rng;
+ Session_Manager& m_session_manager;
+ Policy& m_policy;
+
+ Server_Information m_server_info;
+ Verify_Callback m_verify_callback;
};
} // namespace TLS
diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h
index 7cdd8d3dd..e8d9c2930 100644
--- a/src/lib/tls/asio/asio_stream.h
+++ b/src/lib/tls/asio/asio_stream.h
@@ -52,21 +52,38 @@ class Stream
//! \name construction
//! @{
+ /**
+ * @brief Construct a new Stream
+ *
+ * @param context The context parameter is used to set up the underlying native handle. Using code is
+ * responsible for lifetime management of the context and must ensure that it is available for the
+ * lifetime of the stream.
+ * @param args Arguments to be forwarded to the construction of the next layer.
+ */
template <typename... Args>
explicit Stream(Context& context, Args&& ... args)
: m_context(context)
, m_nextLayer(std::forward<Args>(args)...)
- , m_core(m_receive_buffer, m_send_buffer)
+ , m_core(m_receive_buffer, m_send_buffer, m_context)
, m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
, m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
{}
- // overload for boost::asio::ssl::stream compatibility
+ /**
+ * @brief Construct a new Stream
+ *
+ * Convenience overload for boost::asio::ssl::stream compatibility.
+ *
+ * @param arg This argument is forwarded to the construction of the next layer.
+ * @param context The context parameter is used to set up the underlying native handle. Using code is
+ * responsible for lifetime management of the context and must ensure that is available for the
+ * lifetime of the stream.
+ */
template <typename Arg>
explicit Stream(Arg&& arg, Context& context)
: m_context(context)
, m_nextLayer(std::forward<Arg>(arg))
- , m_core(m_receive_buffer, m_send_buffer)
+ , m_core(m_receive_buffer, m_send_buffer, m_context)
, m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
, m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
{}
@@ -102,24 +119,29 @@ class Stream
//! \name configuration and callback setters
//! @{
- //! @throws Not_Implemented
- template<typename VerifyCallback>
- void set_verify_callback(VerifyCallback callback)
+ /**
+ * @brief Override the tls_verify_cert_chain callback
+ *
+ * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+ * used in the handshake.
+ * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
+ *
+ * @note This function should only be called before initiating the TLS handshake
+ */
+ void set_verify_callback(Context::Verify_Callback callback)
{
- BOTAN_UNUSED(callback);
- throw Not_Implemented("set_verify_callback is not implemented");
+ m_context.set_verify_callback(std::move(callback));
}
/**
- * Not Implemented.
- * @param ec Will be set to `Botan::ErrorType::NotImplemented`
+ * @brief Compatibility overload of @ref set_verify_callback
+ *
+ * @param ec This parameter is unused.
*/
- template<typename VerifyCallback>
- void set_verify_callback(VerifyCallback callback,
- boost::system::error_code& ec)
+ void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec)
{
- BOTAN_UNUSED(callback);
- ec = Botan::ErrorType::NotImplemented;
+ BOTAN_UNUSED(ec);
+ m_context.set_verify_callback(std::move(callback));
}
//! @throws Not_Implemented
@@ -133,8 +155,7 @@ class Stream
* Not Implemented.
* @param ec Will be set to `Botan::ErrorType::NotImplemented`
*/
- void set_verify_depth(int depth,
- boost::system::error_code& ec)
+ void set_verify_depth(int depth, boost::system::error_code& ec)
{
BOTAN_UNUSED(depth);
ec = Botan::ErrorType::NotImplemented;
@@ -153,8 +174,7 @@ class Stream
* @param ec Will be set to `Botan::ErrorType::NotImplemented`
*/
template <typename verify_mode>
- void set_verify_mode(verify_mode v,
- boost::system::error_code& ec)
+ void set_verify_mode(verify_mode v, boost::system::error_code& ec)
{
BOTAN_UNUSED(v);
ec = Botan::ErrorType::NotImplemented;
@@ -511,8 +531,8 @@ class Stream
class StreamCore : public Botan::TLS::Callbacks
{
public:
- StreamCore(boost::beast::flat_buffer& receive_buffer, boost::beast::flat_buffer& send_buffer)
- : m_receive_buffer(receive_buffer), m_send_buffer(send_buffer) {}
+ StreamCore(boost::beast::flat_buffer& receive_buffer, boost::beast::flat_buffer& send_buffer, Context& context)
+ : m_receive_buffer(receive_buffer), m_send_buffer(send_buffer), m_tls_context(context) {}
virtual ~StreamCore() = default;
@@ -546,8 +566,27 @@ class Stream
return true;
}
+ 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,
+ const TLS::Policy& policy) override
+ {
+ if(m_tls_context.has_verify_callback())
+ {
+ m_tls_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+ }
+ else
+ {
+ Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+ }
+ }
+
boost::beast::flat_buffer& m_receive_buffer;
boost::beast::flat_buffer& m_send_buffer;
+ Context& m_tls_context;
};
const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
@@ -598,12 +637,13 @@ class Stream
{
if(side == CLIENT)
{
- m_native_handle = std::unique_ptr<Client>(new Client(m_core,
- *m_context.sessionManager,
- *m_context.credentialsManager,
- *m_context.policy,
- *m_context.randomNumberGenerator,
- m_context.serverInfo));
+ m_native_handle = std::unique_ptr<Client>(
+ new Client(m_core,
+ m_context.m_session_manager,
+ m_context.m_credentials_manager,
+ m_context.m_policy,
+ m_context.m_rng,
+ m_context.m_server_info));
}
else
{
@@ -653,7 +693,7 @@ class Stream
}
}
- Context m_context;
+ Context& m_context;
StreamLayer m_nextLayer;
boost::beast::flat_buffer m_receive_buffer;
diff --git a/src/tests/unit_asio_stream.cpp b/src/tests/unit_asio_stream.cpp
index ee80cdba4..5fd67cbd4 100644
--- a/src/tests/unit_asio_stream.cpp
+++ b/src/tests/unit_asio_stream.cpp
@@ -62,10 +62,6 @@ class MockChannel
Botan::TLS::Callbacks& m_callbacks;
std::size_t m_bytes_till_complete_record; // number of bytes still to read before tls record is completed
bool m_active;
-
- Botan::TLS::Session_Manager_Noop m_session_manager;
- Botan::Null_RNG m_rng;
- Botan::TLS::Default_Policy m_policy;
};
class ThrowingMockChannel : public MockChannel
@@ -130,6 +126,16 @@ class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockCha
*/
class Asio_Stream_Tests final : public Test
{
+ Botan::Credentials_Manager m_credentials_manager;
+ Botan::Null_RNG m_rng;
+ Botan::TLS::Session_Manager_Noop m_session_manager;
+ Botan::TLS::Default_Policy m_policy;
+
+ Botan::TLS::Context get_context()
+ {
+ return Botan::TLS::Context(m_credentials_manager, m_rng, m_session_manager, m_policy);
+ }
+
// use memcmp to check if the data in a is a prefix of the data in b
bool contains(const void* a, const void* b, const std::size_t size) { return memcmp(a, b, size) == 0; }
@@ -141,7 +147,7 @@ class Asio_Stream_Tests final : public Test
void test_sync_handshake(std::vector<Test::Result>& results)
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
ssl.handshake(Botan::TLS::CLIENT);
@@ -158,7 +164,7 @@ class Asio_Stream_Tests final : public Test
FailCount fc{0, net::error::eof};
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
ssl.next_layer().connect(remote);
@@ -179,7 +185,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc, test_data());
ssl.next_layer().connect(remote);
@@ -197,7 +203,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
ssl.next_layer().connect(remote);
@@ -227,7 +233,7 @@ class Asio_Stream_Tests final : public Test
FailCount fc{0, net::error::eof};
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
ssl.next_layer().connect(remote);
@@ -253,7 +259,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc, test_data());
ssl.next_layer().connect(remote);
@@ -275,7 +281,7 @@ class Asio_Stream_Tests final : public Test
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
const std::size_t buf_size = 128;
@@ -296,7 +302,7 @@ class Asio_Stream_Tests final : public Test
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
error_code ec;
@@ -326,7 +332,7 @@ class Asio_Stream_Tests final : public Test
FailCount fc{0, net::error::eof};
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
ssl.next_layer().connect(remote);
@@ -347,7 +353,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc, test_data());
ssl.next_layer().connect(remote);
@@ -367,7 +373,7 @@ class Asio_Stream_Tests final : public Test
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
const std::size_t buf_size = 128;
@@ -390,7 +396,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
uint8_t data[TEST_DATA_SIZE];
@@ -414,7 +420,7 @@ class Asio_Stream_Tests final : public Test
void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results)
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, test_data());
std::vector<net::mutable_buffer> data;
@@ -446,7 +452,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
// fail right away
FailCount fc{0, net::error::eof};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
uint8_t data[TEST_DATA_SIZE];
@@ -469,7 +475,7 @@ class Asio_Stream_Tests final : public Test
void test_async_read_some_throw(std::vector<Test::Result>& results)
{
net::io_context ioc;
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc, test_data());
uint8_t data[TEST_DATA_SIZE];
@@ -494,7 +500,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
uint8_t data[TEST_DATA_SIZE];
@@ -521,7 +527,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);
error_code ec;
@@ -541,7 +547,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);
error_code ec;
@@ -581,7 +587,7 @@ class Asio_Stream_Tests final : public Test
FailCount fc{0, net::error::eof};
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
ssl.next_layer().connect(remote);
@@ -601,7 +607,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);
error_code ec;
@@ -620,7 +626,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);
@@ -644,7 +650,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);
@@ -687,7 +693,7 @@ class Asio_Stream_Tests final : public Test
FailCount fc{0, net::error::eof};
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
AsioStream ssl(ctx, ioc, fc);
ssl.next_layer().connect(remote);
@@ -710,7 +716,7 @@ class Asio_Stream_Tests final : public Test
net::io_context ioc;
TestStream remote{ioc};
- Botan::TLS::Context ctx;
+ auto ctx = get_context();
ThrowingAsioStream ssl(ctx, ioc);
ssl.next_layer().connect(remote);