10#ifndef BOTAN_ASIO_STREAM_H_
11#define BOTAN_ASIO_STREAM_H_
13#include <botan/asio_compat.h>
14#if !defined(BOTAN_FOUND_COMPATIBLE_BOOST_ASIO_VERSION)
15 #error Available boost headers are too old for the boost asio stream.
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>
35 #include <type_traits>
39template <
class SL,
class C>
59 StreamCallbacks() =
default;
61 void tls_emit_data(std::span<const uint8_t> data)
final {
62 m_send_buffer.commit(boost::asio::buffer_copy(m_send_buffer.prepare(data.size()),
63 boost::asio::buffer(data.data(), data.size())));
66 void tls_record_received(uint64_t , std::span<const uint8_t> data)
final {
67 m_receive_buffer.commit(boost::asio::buffer_copy(m_receive_buffer.prepare(data.size()),
68 boost::asio::const_buffer(data.data(), data.size())));
71 bool tls_peer_closed_connection() final {
85 void tls_alert(TLS::Alert alert)
final {
86 if(alert.is_fatal() || alert.type() == TLS::AlertType::CloseNotify) {
91 m_alert_from_peer = alert;
95 void tls_verify_cert_chain(
const std::vector<X509_Certificate>& cert_chain,
96 const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
97 const std::vector<Certificate_Store*>& trusted_roots,
99 std::string_view hostname,
100 const TLS::Policy& policy)
override {
101 auto ctx = m_context.lock();
103 if(ctx && ctx->has_verify_callback()) {
104 ctx->get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
106 Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
110 std::string tls_server_choose_app_protocol(
const std::vector<std::string>& client_protos)
override {
111 if(client_protos.empty()) {
114 auto ctx = m_context.lock();
116 if(!ctx || ctx->m_app_protocols.empty()) {
120 for(
const auto& server_proto : ctx->m_app_protocols) {
121 for(
const auto& client_proto : client_protos) {
122 if(server_proto == client_proto) {
129 Alert::NoApplicationProtocol,
130 "Rejecting ALPN request: no overlap between client-offered and server-configured application protocols");
135 template <
class SL,
class C>
138 void set_context(std::weak_ptr<Botan::TLS::Context> context) { m_context = std::move(context); }
140 void consume_send_buffer() { m_send_buffer.consume(m_send_buffer.size()); }
142 boost::beast::flat_buffer& send_buffer() {
return m_send_buffer; }
144 const boost::beast::flat_buffer& send_buffer()
const {
return m_send_buffer; }
146 boost::beast::flat_buffer& receive_buffer() {
return m_receive_buffer; }
148 const boost::beast::flat_buffer& receive_buffer()
const {
return m_receive_buffer; }
150 bool shutdown_received()
const {
151 return m_alert_from_peer && m_alert_from_peer->type() == AlertType::CloseNotify;
154 std::optional<Alert> alert_from_peer()
const {
return m_alert_from_peer; }
157 std::optional<Alert> m_alert_from_peer;
158 boost::beast::flat_buffer m_receive_buffer;
159 boost::beast::flat_buffer m_send_buffer;
161 std::weak_ptr<TLS::Context> m_context;
167concept basic_completion_token = boost::asio::completion_token_for<T, void(boost::system::error_code)>;
170concept byte_size_completion_token = boost::asio::completion_token_for<T, void(boost::system::error_code,
size_t)>;
180template <
class StreamLayer,
class ChannelT = Channel>
183 using default_completion_token =
184 boost::asio::default_completion_token_t<boost::beast::executor_type<StreamLayer>>;
202 template <
typename... Args>
203 explicit Stream(std::shared_ptr<Context> context, std::shared_ptr<StreamCallbacks> callbacks, Args&&... args) :
204 m_context(std::move(context)),
205 m_nextLayer(std::forward<Args>(args)...),
206 m_core(std::move(callbacks)),
208 m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {
209 m_core->set_context(m_context);
218 template <
typename... Args>
219 explicit Stream(std::shared_ptr<Context> context, Args&&... args) :
220 Stream(std::move(context), std::make_shared<StreamCallbacks>(), std::forward<Args>(args)...) {}
233 template <
typename Arg>
234 explicit Stream(Arg&& arg,
235 std::shared_ptr<Context> context,
236 std::shared_ptr<StreamCallbacks> callbacks = std::make_shared<StreamCallbacks>()) :
237 Stream(std::move(context), std::move(callbacks), std::forward<Arg>(arg)) {}
239 #if defined(BOTAN_HAS_DEFAULT_TLS_CONTEXT)
250 template <
typename... Args>
251 explicit Stream(Server_Information server_info, Args&&... args) :
252 Stream(std::make_shared<Context>(std::move(server_info)), std::forward<Args>(args)...) {}
255 virtual ~Stream() =
default;
257 Stream(Stream&& other) =
default;
258 Stream& operator=(Stream&& other) =
default;
260 Stream(
const Stream& other) =
delete;
261 Stream& operator=(
const Stream& other) =
delete;
267 using next_layer_type = std::remove_reference_t<StreamLayer>;
269 const next_layer_type& next_layer()
const {
return m_nextLayer; }
271 next_layer_type& next_layer() {
return m_nextLayer; }
273 using lowest_layer_type =
typename boost::beast::lowest_layer_type<StreamLayer>;
275 lowest_layer_type& lowest_layer() {
return boost::beast::get_lowest_layer(m_nextLayer); }
277 const lowest_layer_type& lowest_layer()
const {
return boost::beast::get_lowest_layer(m_nextLayer); }
279 using executor_type =
typename next_layer_type::executor_type;
281 executor_type get_executor() noexcept {
return m_nextLayer.get_executor(); }
283 using native_handle_type = std::add_pointer_t<ChannelT>;
285 native_handle_type native_handle() {
286 if(m_native_handle ==
nullptr) {
287 throw Botan::Invalid_State(
"ASIO native handle unexpectedly null");
289 return m_native_handle.get();
305 void set_verify_callback(Context::Verify_Callback callback) {
306 m_context->set_verify_callback(std::move(callback));
315 void set_verify_callback(Context::Verify_Callback callback, [[maybe_unused]] boost::system::error_code& ec) {
316 m_context->set_verify_callback(std::move(callback));
324 void set_verify_depth([[maybe_unused]]
int depth) {
325 throw Not_Implemented(
"set_verify_depth is not implemented");
333 void set_verify_depth([[maybe_unused]]
int depth, boost::system::error_code& ec) {
334 ec = ErrorType::NotImplemented;
342 template <
typename verify_mode>
343 void set_verify_mode([[maybe_unused]] verify_mode v) {
344 throw Not_Implemented(
"set_verify_mode is not implemented");
352 template <
typename verify_mode>
353 void set_verify_mode([[maybe_unused]] verify_mode v, boost::system::error_code& ec) {
354 ec = ErrorType::NotImplemented;
369 void handshake(Connection_Side side) {
370 boost::system::error_code ec;
372 boost::asio::detail::throw_error(ec,
"handshake");
383 void handshake(Connection_Side side, boost::system::error_code& ec) {
384 setup_native_handle(side, ec);
393 if(has_data_to_send()) {
394 send_pending_encrypted_data(ec);
399 if(native_handle()->is_handshake_complete()) {
407 handle_tls_protocol_errors(ec);
413 read_and_process_encrypted_data_from_peer(ec);
426 template <detail::basic_completion_token CompletionToken = default_completion_token>
429 CompletionToken&& completion_token = default_completion_token{}) {
430 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
431 [
this](
auto&& completion_handler, TLS::Connection_Side connection_side) {
432 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
434 boost::system::error_code ec;
435 setup_native_handle(connection_side, ec);
437 const detail::AsyncHandshakeOperation<completion_handler_t, Stream> op{
438 std::forward<completion_handler_t>(completion_handler), *
this, ec};
448 template <
typename ConstBufferSequence, detail::basic_completion_token BufferedHandshakeHandler>
449 auto async_handshake([[maybe_unused]] Connection_Side side,
450 [[maybe_unused]]
const ConstBufferSequence& buffers,
451 [[maybe_unused]] BufferedHandshakeHandler&& handler ) {
452 throw Not_Implemented(
"buffered async handshake is not implemented");
469 void shutdown(boost::system::error_code& ec) {
470 try_with_error_code([&] { native_handle()->close(); }, ec);
472 send_pending_encrypted_data(ec);
486 boost::system::error_code ec;
488 boost::asio::detail::throw_error(ec,
"shutdown");
499 template <
typename Handler,
typename Executor>
501 void operator()(boost::system::error_code ec, std::size_t ) { handler(ec); }
503 using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
505 executor_type get_executor() const noexcept {
506 return boost::asio::get_associated_executor(handler, io_executor);
509 using allocator_type = boost::asio::associated_allocator_t<Handler>;
511 allocator_type get_allocator() const noexcept {
return boost::asio::get_associated_allocator(handler); }
514 Executor io_executor;
528 template <detail::basic_completion_token CompletionToken = default_completion_token>
530 auto async_shutdown(CompletionToken&& completion_token = default_completion_token{}) {
531 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
532 [
this](
auto&& completion_handler) {
533 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
535 boost::system::error_code ec;
536 try_with_error_code([&] { native_handle()->close(); }, ec);
538 using write_handler_t = Wrapper<completion_handler_t, typename Stream::executor_type>;
540 const TLS::detail::AsyncWriteOperation<write_handler_t, Stream> op{
541 write_handler_t{std::forward<completion_handler_t>(completion_handler), get_executor()},
543 boost::asio::buffer_size(send_buffer()),
564 template <
typename MutableBufferSequence>
565 std::size_t read_some(
const MutableBufferSequence& buffers, boost::system::error_code& ec) {
571 if(has_received_data()) {
572 return copy_received_data(buffers);
581 handle_tls_protocol_errors(ec);
588 read_and_process_encrypted_data_from_peer(ec);
604 template <
typename MutableBufferSequence>
605 std::size_t read_some(
const MutableBufferSequence& buffers) {
606 boost::system::error_code ec;
607 const auto n = read_some(buffers, ec);
608 boost::asio::detail::throw_error(ec,
"read_some");
622 template <
typename ConstBufferSequence>
623 std::size_t write_some(
const ConstBufferSequence& buffers, boost::system::error_code& ec) {
624 tls_encrypt(buffers, ec);
625 send_pending_encrypted_data(ec);
626 return !ec ? boost::asio::buffer_size(buffers) : 0;
639 template <
typename ConstBufferSequence>
640 std::size_t write_some(
const ConstBufferSequence& buffers) {
641 boost::system::error_code ec;
642 const auto n = write_some(buffers, ec);
643 boost::asio::detail::throw_error(ec,
"write_some");
655 template <
typename ConstBufferSequence,
656 detail::byte_size_completion_token CompletionToken = default_completion_token>
657 auto async_write_some(
const ConstBufferSequence& buffers,
659 CompletionToken&& completion_token = default_completion_token{}) {
660 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
661 [
this](
auto&& completion_handler,
const auto& bufs) {
662 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
664 boost::system::error_code ec;
665 tls_encrypt(bufs, ec);
670 m_core->send_buffer().consume(m_core->send_buffer().size());
673 const detail::AsyncWriteOperation<completion_handler_t, Stream> op{
674 std::forward<completion_handler_t>(completion_handler),
676 ec ? 0 : boost::asio::buffer_size(bufs),
692 template <
typename MutableBufferSequence,
693 detail::byte_size_completion_token CompletionToken = default_completion_token>
694 auto async_read_some(
const MutableBufferSequence& buffers,
696 CompletionToken&& completion_token = default_completion_token{}) {
697 return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
698 [
this](
auto&& completion_handler,
const auto& bufs) {
699 using completion_handler_t = std::decay_t<
decltype(completion_handler)>;
701 const detail::AsyncReadOperation<completion_handler_t, Stream, MutableBufferSequence> op{
702 std::forward<completion_handler_t>(completion_handler), *
this, bufs};
714 bool shutdown_received()
const {
return m_core->shutdown_received(); }
717 template <
class H,
class S,
class M,
class A>
718 friend class detail::AsyncReadOperation;
719 template <
class H,
class S,
class A>
720 friend class detail::AsyncWriteOperation;
721 template <
class H,
class S,
class A>
722 friend class detail::AsyncHandshakeOperation;
724 const boost::asio::mutable_buffer& input_buffer() {
return m_input_buffer; }
726 boost::asio::const_buffer send_buffer()
const {
return m_core->send_buffer().data(); }
729 bool has_received_data()
const {
return m_core->receive_buffer().size() > 0; }
732 template <
typename MutableBufferSequence>
733 std::size_t copy_received_data(MutableBufferSequence buffers) {
738 const auto copiedBytes = boost::asio::buffer_copy(buffers, m_core->receive_buffer().data());
739 m_core->receive_buffer().consume(copiedBytes);
744 bool has_data_to_send()
const {
return m_core->send_buffer().size() > 0; }
747 void consume_send_buffer(std::size_t bytesConsumed) { m_core->send_buffer().consume(bytesConsumed); }
758 void setup_native_handle(Connection_Side side, boost::system::error_code& ec) {
761 if constexpr(std::is_same_v<ChannelT, Channel>) {
762 if(m_native_handle !=
nullptr) {
763 throw Botan::Invalid_State(
"ASIO native handle unexpectedly set");
768 if(side == Connection_Side::Client) {
769 m_native_handle = std::unique_ptr<Client>(
771 m_context->m_session_manager,
772 m_context->m_credentials_manager,
775 m_context->m_server_info,
776 m_context->m_policy->latest_supported_version(
false ),
777 m_context->m_app_protocols));
779 m_native_handle = std::unique_ptr<Server>(
new Server(m_core,
780 m_context->m_session_manager,
781 m_context->m_credentials_manager,
807 void handle_tls_protocol_errors(boost::system::error_code& ec) {
816 else if(
auto error = error_from_us()) {
828 else if(
auto alert = alert_from_peer()) {
829 if(alert->type() == AlertType::CloseNotify) {
830 ec = boost::asio::error::eof;
846 void read_and_process_encrypted_data_from_peer(boost::system::error_code& ec) {
854 if(has_received_data()) {
855 throw Botan::Invalid_State(
"ASIO receive buffer not empty");
858 if(error_from_us() || alert_from_peer()) {
859 throw Botan::Invalid_State(
"ASIO TLS session no longer healthy");
866 const boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
868 process_encrypted_data(read_buffer);
869 }
else if(ec == boost::asio::error::eof) {
870 ec = StreamError::StreamTruncated;
882 size_t send_pending_encrypted_data(boost::system::error_code& ec) {
887 auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
888 consume_send_buffer(writtenBytes);
890 if(ec == boost::asio::error::eof && !shutdown_received()) {
892 ec = StreamError::StreamTruncated;
903 template <
typename ConstBufferSequence>
904 void tls_encrypt(
const ConstBufferSequence& buffers, boost::system::error_code& ec) {
908 std::vector<uint8_t> copy_buffer;
909 auto unpack = [©_buffer](
const auto& bufs) -> std::span<const uint8_t> {
910 const auto buffers_in_sequence =
911 std::distance(boost::asio::buffer_sequence_begin(bufs), boost::asio::buffer_sequence_end(bufs));
913 if(buffers_in_sequence == 0) {
915 }
else if(buffers_in_sequence == 1) {
916 const boost::asio::const_buffer buf = *boost::asio::buffer_sequence_begin(bufs);
917 return {
static_cast<const uint8_t*
>(buf.data()), buf.size()};
919 copy_buffer.resize(boost::asio::buffer_size(bufs));
920 boost::asio::buffer_copy(boost::asio::buffer(copy_buffer), bufs);
928 try_with_error_code([&] { native_handle()->send(
unpack(buffers)); }, ec);
942 void process_encrypted_data(
const boost::asio::const_buffer& read_buffer) {
943 if(alert_from_peer() || error_from_us()) {
944 throw Botan::Invalid_State(
"ASIO TLS session no longer healthy");
953 native_handle()->received_data({
static_cast<const uint8_t*
>(read_buffer.data()), read_buffer.size()});
955 m_ec_from_last_read);
959 template <
typename Fun>
960 void try_with_error_code(Fun f, boost::system::error_code& ec) {
963 }
catch(
const TLS_Exception& e) {
965 }
catch(
const Exception& e) {
967 }
catch(
const std::exception&) {
968 ec = ErrorType::Unknown;
978 std::optional<Alert> alert_from_peer()
const {
return m_core->alert_from_peer(); }
986 boost::system::error_code error_from_us()
const {
return m_ec_from_last_read; }
989 std::shared_ptr<Context> m_context;
990 StreamLayer m_nextLayer;
992 std::shared_ptr<StreamCallbacks> m_core;
993 std::unique_ptr<ChannelT> m_native_handle;
994 boost::system::error_code m_ec_from_last_read;
997 std::vector<uint8_t> m_input_buffer_space;
998 const boost::asio::mutable_buffer m_input_buffer;
1003template <
typename T>
1005template <
typename T>
1006Stream(std::shared_ptr<Context>, std::shared_ptr<StreamCallbacks>, T) -> Stream<T>;
1007template <
typename T>
1008Stream(std::shared_ptr<Context>, T) -> Stream<T>;
1009template <
typename T>
1010Stream(T, std::shared_ptr<Context>) -> Stream<T>;
constexpr void unpack(Polynomial< PolyTrait, D > &p, ByteSourceT &byte_source, UnmapFnT unmap)