9#ifndef BOTAN_ASIO_STREAM_H_
10#define BOTAN_ASIO_STREAM_H_
12#include <botan/types.h>
15#include <boost/version.hpp>
16#if BOOST_VERSION >= 106600
18 #include <botan/asio_async_ops.h>
19 #include <botan/asio_context.h>
20 #include <botan/asio_error.h>
22 #include <botan/tls_callbacks.h>
23 #include <botan/tls_channel.h>
24 #include <botan/tls_client.h>
25 #include <botan/tls_magic.h>
26 #include <botan/tls_server.h>
30 #define BOOST_ASIO_DISABLE_SERIAL_PORT
31 #include <boost/asio.hpp>
32 #include <boost/beast/core.hpp>
36 #include <type_traits>
47template <
class StreamLayer,
class ChannelT = Channel>
61 template <
typename... Args>
62 explicit Stream(std::shared_ptr<Context> context, Args&&... args) :
79 template <
typename Arg>
80 explicit Stream(Arg&& arg, std::shared_ptr<Context> context) :
105 #if BOOST_VERSION >= 107000
151 m_context->set_verify_callback(std::move(callback));
162 m_context->set_verify_callback(std::move(callback));
182 template <
typename verify_mode>
193 template <
typename verify_mode>
212 boost::system::error_code ec;
214 boost::asio::detail::throw_error(ec,
"handshake");
254 template <
typename CompletionToken>
256 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
258 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
260 BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
262 boost::system::error_code ec;
266 std::forward<completion_handler_t>(completion_handler), *
this, ec};
273 template <
typename ConstBufferSequence,
typename BufferedHandshakeHandler>
274 BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
void(boost::system::error_code, std::size_t))
277 BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
312 boost::system::error_code ec;
314 boost::asio::detail::throw_error(ec,
"shutdown");
325 template <
typename Handler,
typename Executor>
327 void operator()(boost::system::error_code ec, std::size_t) { handler(ec); }
329 using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
331 executor_type get_executor() const noexcept {
332 return boost::asio::get_associated_executor(handler, io_executor);
335 using allocator_type = boost::asio::associated_allocator_t<Handler>;
337 allocator_type get_allocator() const noexcept {
return boost::asio::get_associated_allocator(handler); }
340 Executor io_executor;
354 template <
typename CompletionToken>
356 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
357 [
this](
auto&& completion_handler) {
358 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
360 BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
362 boost::system::error_code ec;
365 using write_handler_t = Wrapper<completion_handler_t, typename Stream::executor_type>;
368 write_handler_t{std::forward<completion_handler_t>(completion_handler),
get_executor()},
391 template <
typename MutableBufferSequence>
392 std::size_t
read_some(
const MutableBufferSequence& buffers, boost::system::error_code& ec) {
411 ec = boost::asio::error::eof;
412 }
else if(ec == boost::asio::error::eof) {
430 template <
typename MutableBufferSequence>
431 std::size_t
read_some(
const MutableBufferSequence& buffers) {
432 boost::system::error_code ec;
434 boost::asio::detail::throw_error(ec,
"read_some");
448 template <
typename ConstBufferSequence>
449 std::size_t
write_some(
const ConstBufferSequence& buffers, boost::system::error_code& ec) {
452 return !ec ? boost::asio::buffer_size(buffers) : 0;
465 template <
typename ConstBufferSequence>
467 boost::system::error_code ec;
469 boost::asio::detail::throw_error(ec,
"write_some");
481 template <
typename ConstBufferSequence,
typename CompletionToken>
482 auto async_write_some(
const ConstBufferSequence& buffers, CompletionToken&& completion_token) {
483 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
484 [
this](
auto&& completion_handler,
const auto& bufs) {
485 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
487 BOOST_ASIO_WRITE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
489 boost::system::error_code ec;
499 std::forward<completion_handler_t>(completion_handler),
501 ec ? 0 : boost::asio::buffer_size(bufs),
517 template <
typename MutableBufferSequence,
typename CompletionToken>
518 auto async_read_some(
const MutableBufferSequence& buffers, CompletionToken&& completion_token) {
519 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
520 [
this](
auto&& completion_handler,
const auto& bufs) {
521 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
523 BOOST_ASIO_READ_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
526 std::forward<completion_handler_t>(completion_handler), *
this, bufs};
541 template <
class H,
class S,
class M,
class A>
543 template <
class H,
class S,
class A>
545 template <
class H,
class S,
class A>
562 boost::asio::buffer(data.data(), data.size())));
567 boost::asio::const_buffer(data.data(), data.size())));
585 return std::chrono::milliseconds(1000);
589 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
590 const std::vector<Certificate_Store*>& trusted_roots,
592 std::string_view hostname,
594 auto ctx = m_context.lock();
596 if(ctx && ctx->has_verify_callback()) {
597 ctx->get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
608 std::weak_ptr<TLS::Context> m_context;
619 template <
typename MutableBufferSequence>
625 const auto copiedBytes = boost::asio::buffer_copy(buffers,
m_core->receive_buffer.data());
626 m_core->receive_buffer.consume(copiedBytes);
650 if constexpr(std::is_same<ChannelT, Channel>::value) {
661 m_context->m_policy->latest_supported_version(
false )));
704 template <
typename ConstBufferSequence>
705 void tls_encrypt(
const ConstBufferSequence& buffers, boost::system::error_code& ec) {
709 for(
auto it = boost::asio::buffer_sequence_begin(buffers);
710 !ec && it != boost::asio::buffer_sequence_end(buffers);
712 const boost::asio::const_buffer buffer = *it;
715 native_handle()->send({
static_cast<const uint8_t*
>(buffer.data()), buffer.size()});
732 native_handle()->received_data({
static_cast<const uint8_t*
>(read_buffer.data()), read_buffer.size()});
738 template <
typename Fun>
746 }
catch(
const std::exception&) {
virtual void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::optional< OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &trusted_roots, Usage_Type usage, std::string_view hostname, const TLS::Policy &policy)
detail::fn_signature_helper< decltype(&Callbacks::tls_verify_cert_chain)>::type Verify_Callback
Helper class that implements TLS::Callbacks.
void tls_emit_data(std::span< const uint8_t > data) override
virtual ~StreamCore()=default
boost::beast::flat_buffer send_buffer
void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::optional< OCSP::Response > > &ocsp_responses, const std::vector< Certificate_Store * > &trusted_roots, Usage_Type usage, std::string_view hostname, const TLS::Policy &policy) override
void tls_alert(TLS::Alert alert) override
bool tls_peer_closed_connection() override
void tls_record_received(uint64_t, std::span< const uint8_t > data) override
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
boost::beast::flat_buffer receive_buffer
StreamCore(std::weak_ptr< Botan::TLS::Context > context)
boost::asio compatible SSL/TLS stream
Stream(Stream &&other)=default
native_handle_type native_handle()
std::size_t copy_received_data(MutableBufferSequence buffers)
Copy decrypted data into the user-provided buffer.
const boost::asio::mutable_buffer m_input_buffer
void set_verify_mode(verify_mode v, boost::system::error_code &ec)
void try_with_error_code(Fun f, boost::system::error_code &ec)
Catch exceptions and set an error_code.
void set_verify_mode(verify_mode v)
Stream & operator=(const Stream &other)=delete
auto async_write_some(const ConstBufferSequence &buffers, CompletionToken &&completion_token)
Start an asynchronous write. The function call always returns immediately.
lowest_layer_type & lowest_layer()
void setup_native_handle(Connection_Side side, boost::system::error_code &ec)
Create the native handle.
void set_verify_depth(int depth)
const next_layer_type & next_layer() const
std::unique_ptr< ChannelT > m_native_handle
void consume_send_buffer(std::size_t bytesConsumed)
Mark bytes in the send buffer as consumed, removing them from the buffer.
std::shared_ptr< StreamCore > m_core
typename next_layer_type::executor_type executor_type
boost::asio::const_buffer send_buffer() const
void handshake(Connection_Side side)
Performs SSL handshaking.
bool has_received_data() const
Check if decrypted data is available in the receive buffer.
auto async_shutdown(CompletionToken &&completion_token)
Asynchronously shut down SSL on the stream.
void set_verify_callback(Context::Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Stream(Arg &&arg, std::shared_ptr< Context > context)
Construct a new Stream.
typename std::add_pointer< ChannelT >::type native_handle_type
next_layer_type & next_layer()
std::size_t write_some(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Write some data to the stream.
const boost::asio::mutable_buffer & input_buffer()
void process_encrypted_data(const boost::asio::const_buffer &read_buffer, boost::system::error_code &ec)
Pass encrypted data to the native handle for processing.
executor_type get_executor() noexcept
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code &ec)
Compatibility overload of set_verify_callback.
typename std::remove_reference< StreamLayer >::type next_layer_type
std::size_t read_some(const MutableBufferSequence &buffers)
Read some data from the stream.
std::size_t write_some(const ConstBufferSequence &buffers)
Write some data to the stream.
Stream(const Stream &other)=delete
Stream & operator=(Stream &&other)=default
auto async_read_some(const MutableBufferSequence &buffers, CompletionToken &&completion_token)
Start an asynchronous read. The function call always returns immediately.
size_t send_pending_encrypted_data(boost::system::error_code &ec)
Synchronously write encrypted data from the send buffer to the next layer.
typename next_layer_type::lowest_layer_type lowest_layer_type
void tls_encrypt(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Pass plaintext data to the native handle for processing.
std::size_t read_some(const MutableBufferSequence &buffers, boost::system::error_code &ec)
Read some data from the stream.
void set_verify_depth(int depth, boost::system::error_code &ec)
Stream(std::shared_ptr< Context > context, Args &&... args)
Construct a new Stream.
virtual ~Stream()=default
std::vector< uint8_t > m_input_buffer_space
bool shutdown_received() const
Indicates whether a close_notify alert has been received from the peer.
void shutdown()
Shut down SSL on the stream.
std::shared_ptr< Context > m_context
void handshake(Connection_Side side, boost::system::error_code &ec)
Performs SSL handshaking.
void shutdown(boost::system::error_code &ec)
Shut down SSL on the stream.
const lowest_layer_type & lowest_layer() const
bool has_data_to_send() const
Check if encrypted data is available in the send buffer.
auto async_handshake(Botan::TLS::Connection_Side side, CompletionToken &&completion_token)
Starts an asynchronous SSL handshake.
ErrorType error_type() const noexcept override