diff options
author | René Meusel <[email protected]> | 2019-02-19 15:09:17 +0100 |
---|---|---|
committer | Hannes Rantzsch <[email protected]> | 2019-04-16 10:47:44 +0200 |
commit | ae8fb39545a8d574fba15b3690093edce93e7b18 (patch) | |
tree | 8259a94fc673c12f172e15cb6ef4a1eeec3c3cf6 /src/lib/tls/asio | |
parent | 19317c96bde9f2bfe01c692f7e53051ebd0005e8 (diff) |
move stream implementation to tls subdir
Diffstat (limited to 'src/lib/tls/asio')
-rw-r--r-- | src/lib/tls/asio/asio_async_handshake_op.h | 86 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_async_read_op.h | 74 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_async_write_op.h | 40 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_convert_exceptions.h | 117 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_error.h | 155 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_includes.h | 11 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_stream.h | 465 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_stream_base.h | 64 | ||||
-rw-r--r-- | src/lib/tls/asio/asio_stream_core.h | 104 | ||||
-rw-r--r-- | src/lib/tls/asio/info.txt | 23 |
10 files changed, 1139 insertions, 0 deletions
diff --git a/src/lib/tls/asio/asio_async_handshake_op.h b/src/lib/tls/asio/asio_async_handshake_op.h new file mode 100644 index 000000000..6ef7fdf94 --- /dev/null +++ b/src/lib/tls/asio/asio_async_handshake_op.h @@ -0,0 +1,86 @@ +#ifndef BOTAN_ASIO_ASYNC_HANDSHAKE_OP_H_ +#define BOTAN_ASIO_ASYNC_HANDSHAKE_OP_H_ + +#include <botan/internal/asio_async_write_op.h> +#include <botan/internal/asio_convert_exceptions.h> +#include <botan/internal/asio_stream_core.h> +#include <botan/internal/asio_includes.h> + +namespace Botan { +template <class Channel, class StreamLayer, class Handler> +struct AsyncHandshakeOperation + { + AsyncHandshakeOperation(Channel* channel, StreamCore& core, + StreamLayer& nextLayer, Handler&& handler) + : channel_(channel), + core_(core), + nextLayer_(nextLayer), + handler_(std::forward<Handler>(handler)) {} + + AsyncHandshakeOperation(AsyncHandshakeOperation&& right) + : channel_(right.channel_), + core_(right.core_), + nextLayer_(right.nextLayer_), + handler_(std::move(right.handler_)) {} + + ~AsyncHandshakeOperation() = default; + AsyncHandshakeOperation(AsyncHandshakeOperation const&) = delete; + + void operator()(boost::system::error_code ec, + std::size_t bytesTransferred = 0, int start = 0) + { + // process tls packets from socket first + if(bytesTransferred > 0) + { + auto read_buffer = + boost::asio::buffer(core_.input_buffer_, bytesTransferred); + try + { + channel_->received_data( + static_cast<const uint8_t*>(read_buffer.data()), + read_buffer.size()); + } + catch(...) + { + ec = convertException(); + handler_(ec); + return; + } + } + + // send tls packets + if(core_.hasDataToSend()) + { + AsyncWriteOperation<AsyncHandshakeOperation<Channel, StreamLayer, Handler>> + op{core_, std::move(*this), 0}; + boost::asio::async_write(nextLayer_, core_.sendBuffer(), + std::move(op)); + return; + } + + if(!channel_->is_active() && !ec) + { + // we need more tls data from the socket + nextLayer_.async_read_some(core_.input_buffer_, std::move(*this)); + return; + } + + if(start) + { + // don't call the handler directly, similar to io_context.post + nextLayer_.async_read_some( + boost::asio::buffer(core_.input_buffer_, 0), std::move(*this)); + return; + } + handler_(ec); + } + + private: + Channel* channel_; + StreamCore& core_; + StreamLayer& nextLayer_; + Handler handler_; + }; +} // namespace Botan + +#endif diff --git a/src/lib/tls/asio/asio_async_read_op.h b/src/lib/tls/asio/asio_async_read_op.h new file mode 100644 index 000000000..56e5da7b6 --- /dev/null +++ b/src/lib/tls/asio/asio_async_read_op.h @@ -0,0 +1,74 @@ +#ifndef BOTAN_ASIO_ASYNC_READ_OP_H_ +#define BOTAN_ASIO_ASYNC_READ_OP_H_ + +#include <botan/internal/asio_convert_exceptions.h> +#include <botan/internal/asio_includes.h> +#include <botan/internal/asio_stream_core.h> + +namespace Botan { + +template <class Channel, class StreamLayer, class Handler, + class MutableBufferSequence> +struct AsyncReadOperation + { + AsyncReadOperation(Channel* channel, StreamCore& core, StreamLayer& nextLayer, + Handler&& handler, const MutableBufferSequence& buffers) + : channel_(channel), core_(core), nextLayer_(nextLayer), + handler_(std::forward<Handler>(handler)), buffers_(buffers) {} + + AsyncReadOperation(AsyncReadOperation&& right) + : channel_(right.channel_), core_(right.core_), + nextLayer_(right.nextLayer_), handler_(std::move(right.handler_)), + buffers_(right.buffers_) {} + + ~AsyncReadOperation() = default; + AsyncReadOperation(AsyncReadOperation const&) = delete; + + void operator()(boost::system::error_code ec, + std::size_t bytes_transferred = ~std::size_t(0)) + { + std::size_t decodedBytes = 0; + + if(bytes_transferred > 0) + { + auto read_buffer = + boost::asio::buffer(core_.input_buffer_, bytes_transferred); + try + { + channel_->received_data(static_cast<const uint8_t*>(read_buffer.data()), + read_buffer.size()); + } + catch(...) + { + // TODO: don't call handler directly + handler_(convertException(), 0); + return; + } + } + + if(!core_.hasReceivedData() && !ec) + { + // we need more tls packets from the socket + nextLayer_.async_read_some(core_.input_buffer_, std::move(*this)); + return; + } + + if(core_.hasReceivedData()) + { + decodedBytes = core_.copyReceivedData(buffers_); + ec = boost::system::error_code{}; + } + + handler_(ec, decodedBytes); + } + + private: + Channel* channel_; + StreamCore& core_; + StreamLayer& nextLayer_; + Handler handler_; + MutableBufferSequence buffers_; + }; +} // namespace Botan + +#endif diff --git a/src/lib/tls/asio/asio_async_write_op.h b/src/lib/tls/asio/asio_async_write_op.h new file mode 100644 index 000000000..28611fe1f --- /dev/null +++ b/src/lib/tls/asio/asio_async_write_op.h @@ -0,0 +1,40 @@ +#ifndef BOTAN_ASIO_ASYNC_WRITE_OP_H_ +#define BOTAN_ASIO_ASYNC_WRITE_OP_H_ + +#include <botan/internal/asio_stream_core.h> +#include <botan/internal/asio_includes.h> + +namespace Botan { + +template <typename Handler> +struct AsyncWriteOperation + { + AsyncWriteOperation(StreamCore& core, Handler&& handler, + std::size_t plainBytesTransferred) + : core_(core), + handler_(std::forward<Handler>(handler)), + plainBytesTransferred_(plainBytesTransferred) {} + + AsyncWriteOperation(AsyncWriteOperation&& right) + : core_(right.core_), + handler_(std::move(right.handler_)), + plainBytesTransferred_(right.plainBytesTransferred_) {} + + ~AsyncWriteOperation() = default; + AsyncWriteOperation(AsyncWriteOperation const&) = delete; + + void operator()(boost::system::error_code ec, + std::size_t bytes_transferred = ~std::size_t(0)) + { + core_.consumeSendBuffer(bytes_transferred); + // TODO: make sure returning 0 in error case is correct here--core has already eaten the data + handler_(ec, ec ? 0 : plainBytesTransferred_); + } + + StreamCore& core_; + Handler handler_; + std::size_t plainBytesTransferred_; + }; +} // namespace Botan + +#endif diff --git a/src/lib/tls/asio/asio_convert_exceptions.h b/src/lib/tls/asio/asio_convert_exceptions.h new file mode 100644 index 000000000..3a054be10 --- /dev/null +++ b/src/lib/tls/asio/asio_convert_exceptions.h @@ -0,0 +1,117 @@ +#ifndef BOTAN_ASIO_CONVERT_EXCEPTIONS_H_ +#define BOTAN_ASIO_CONVERT_EXCEPTIONS_H_ + +#include <botan/asio_error.h> +#include <botan/tls_exceptn.h> + +namespace Botan { +inline boost::system::error_code convertException() + { + try + { + throw; + } + catch(Botan::TLS::Unexpected_Message& e) + { + return make_error_code(Botan::error::unexpected_message); + } + catch(Botan::TLS::TLS_Exception& e) + { + return make_error_code(e.type()); + } + catch(Botan::Unsupported_Argument& e) + { + return make_error_code(Botan::error::unsupported_argument); + } + catch(Botan::Invalid_Key_Length& e) + { + return make_error_code(Botan::error::invalid_key_length); + } + catch(Botan::Invalid_IV_Length& e) + { + return make_error_code(Botan::error::invalid_iv_length); + } + catch(Botan::Invalid_Algorithm_Name& e) + { + return make_error_code(Botan::error::invalid_algorithm_name); + } + catch(Botan::Encoding_Error& e) + { + return make_error_code(Botan::error::encoding_error); + } + catch(Botan::Invalid_OID& e) + { + return make_error_code(Botan::error::invalid_oid); + } + catch(Botan::Decoding_Error& e) + { + return make_error_code(Botan::error::decoding_error); + } + catch(Botan::Invalid_Argument& e) + { + return make_error_code(Botan::error::invalid_argument); + } + catch(Botan::Key_Not_Set& e) + { + return make_error_code(Botan::error::key_not_set); + } + catch(Botan::PRNG_Unseeded& e) + { + return make_error_code(Botan::error::prng_unseeded); + } + catch(Botan::Policy_Violation& e) + { + return make_error_code(Botan::error::policy_violation); + } + catch(Botan::Invalid_State& e) + { + return make_error_code(Botan::error::invalid_state); + } + catch(Botan::Algorithm_Not_Found& e) + { + return make_error_code(Botan::error::algorithm_not_found); + } + catch(Botan::Provider_Not_Found& e) + { + return make_error_code(Botan::error::provider_not_found); + } + catch(Botan::Lookup_Error& e) + { + return make_error_code(Botan::error::lookup_error); + } + catch(Botan::Self_Test_Failure& e) + { + return make_error_code(Botan::error::self_test_failure); + } + catch(Botan::Internal_Error& e) + { + return make_error_code(Botan::error::internal_error); + } + catch(Botan::No_Provider_Found& e) + { + return make_error_code(Botan::error::no_provider_found); + } + catch(Botan::Integrity_Failure& e) + { + return make_error_code(Botan::error::integrity_failure); + } + catch(Botan::Stream_IO_Error& e) + { + return make_error_code(Botan::error::stream_io_error); + } + catch(Botan::Not_Implemented& e) + { + return make_error_code(Botan::error::not_implemented); + } + catch(Botan::Exception& e) + { + return make_error_code(Botan::error::unknown); + } + catch(std::exception& e) + { + return make_error_code(Botan::error::unknown); + } + } +} + +#endif diff --git a/src/lib/tls/asio/asio_error.h b/src/lib/tls/asio/asio_error.h new file mode 100644 index 000000000..a285dff43 --- /dev/null +++ b/src/lib/tls/asio/asio_error.h @@ -0,0 +1,155 @@ +#ifndef BOTAN_ASIO_ERROR_H_ +#define BOTAN_ASIO_ERROR_H_ + +#include <boost/system/system_error.hpp> + +#include <botan/tls_alert.h> + +namespace Botan { + +using error_code = boost::system::error_code; +using error_category = boost::system::error_category; + +// TLS Alerts +struct BotanAlertCategory : error_category + { + const char* name() const noexcept override + { + return "asio.botan.tls.alert"; + } + + std::string message(int ev) const override + { + Botan::TLS::Alert alert(static_cast<Botan::TLS::Alert::Type>(ev)); + return alert.type_string(); + } + }; + +inline const BotanAlertCategory& botan_alert_category() noexcept + { + static BotanAlertCategory category; + return category; + } + +inline error_code make_error_code(Botan::TLS::Alert::Type c) + { + return error_code(static_cast<int>(c), botan_alert_category()); + } + +enum class error + { + unexpected_message = 1, + invalid_argument, + unsupported_argument, + invalid_state, + key_not_set, + lookup_error, + internal_error, + invalid_key_length, + invalid_iv_length, + prng_unseeded, + policy_violation, + algorithm_not_found, + no_provider_found, + provider_not_found, + invalid_algorithm_name, + encoding_error, + decoding_error, + integrity_failure, + invalid_oid, + stream_io_error, + self_test_failure, + not_implemented, + unknown + }; + +struct BotanErrorCategory : error_category + { + const char* name() const noexcept override + { + return "asio.botan.tls"; + } + + std::string message(int ev) const override + { + switch(static_cast<error>(ev)) + { + case error::unexpected_message: + return "unexpected_message"; + case error::invalid_argument: + return "invalid_argument"; + case error::unsupported_argument: + return "unsupported_argument"; + case error::invalid_state: + return "invalid_state"; + case error::key_not_set: + return "key_not_set"; + case error::lookup_error: + return "lookup_error"; + case error::internal_error: + return "internal_error"; + case error::invalid_key_length: + return "invalid_key_length"; + case error::invalid_iv_length: + return "invalid_iv_length"; + case error::prng_unseeded: + return "prng_unseeded"; + case error::policy_violation: + return "policy_violation"; + case error::algorithm_not_found: + return "algorithm_not_found"; + case error::no_provider_found: + return "no_provider_found"; + case error::provider_not_found: + return "provider_not_found"; + case error::invalid_algorithm_name: + return "invalid_algorithm_name"; + case error::encoding_error: + return "encoding_error"; + case error::decoding_error: + return "decoding_error"; + case error::integrity_failure: + return "integrity_failure"; + case error::invalid_oid: + return "invalid_oid"; + case error::stream_io_error: + return "stream_io_error"; + case error::self_test_failure: + return "self_test_failure"; + case error::not_implemented: + return "not_implemented"; + + default: + return "(unrecognized botan tls error)"; + } + } + }; + +inline const BotanErrorCategory& botan_category() noexcept + { + static BotanErrorCategory category; + return category; + } + +inline error_code make_error_code(error c) + { + return error_code(static_cast<int>(c), botan_category()); + } +} + +namespace boost { +namespace system { + +template<> struct is_error_code_enum<Botan::TLS::Alert::Type> + { + static const bool value = true; + }; + +template<> struct is_error_code_enum<Botan::error> + { + static const bool value = true; + }; +} +} + +#endif diff --git a/src/lib/tls/asio/asio_includes.h b/src/lib/tls/asio/asio_includes.h new file mode 100644 index 000000000..796aca10e --- /dev/null +++ b/src/lib/tls/asio/asio_includes.h @@ -0,0 +1,11 @@ +#ifndef BOTAN_ASIO_INCLUDES_H_ +#define BOTAN_ASIO_INCLUDES_H_ + +// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>, +// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'. +#define BOOST_ASIO_DISABLE_SERIAL_PORT +#include <boost/asio.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/asio/ip/tcp.hpp> + +#endif diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h new file mode 100644 index 000000000..f6bac7b27 --- /dev/null +++ b/src/lib/tls/asio/asio_stream.h @@ -0,0 +1,465 @@ +#ifndef BOTAN_ASIO_STREAM_H_ +#define BOTAN_ASIO_STREAM_H_ + +#include <botan/internal/asio_async_handshake_op.h> +#include <botan/internal/asio_async_read_op.h> +#include <botan/internal/asio_async_write_op.h> +#include <botan/internal/asio_convert_exceptions.h> +#include <botan/internal/asio_includes.h> +#include <botan/internal/asio_stream_base.h> +#include <botan/internal/asio_stream_core.h> + +#include <memory> +#include <thread> +#include <type_traits> + +namespace boost { +namespace asio { +namespace ssl { +class context; +} +} +} + +namespace Botan { + +/** + * boost::asio compatible SSL/TLS stream based on TLS::Client or TLS::Server. + */ +template <class StreamLayer, class Channel> +class Stream : public StreamBase<Channel> + { + public: + using next_layer_type = typename std::remove_reference<StreamLayer>::type; + using lowest_layer_type = typename next_layer_type::lowest_layer_type; + using executor_type = typename next_layer_type::executor_type; + using native_handle_type = typename std::add_pointer<Channel>::type; + + enum handshake_type + { + client, + server + }; + + private: + void validate_handshake_type(handshake_type type) + { + if(type != handshake_type::client) + { + throw Not_Implemented("server-side TLS stream is not implemented"); + } + } + + bool validate_handshake_type(handshake_type type, boost::system::error_code& ec) + { + if(type != handshake_type::client) + { + ec = make_error_code(Botan::error::not_implemented); + return false; + } + + return true; + } + + public: + template <typename... Args> + Stream(StreamLayer&& nextLayer, Args&& ... args) + : StreamBase<Channel>(std::forward<Args>(args)...), + nextLayer_(std::forward<StreamLayer>(nextLayer)) {} + + Stream(StreamLayer&& nextLayer, boost::asio::ssl::context&) + : StreamBase<Channel>(Botan::TLS::Session_Manager_Noop(), Botan::Credentials_Manager()), + nextLayer_(std::forward<StreamLayer>(nextLayer)) + { + // Configuring a TLS stream via asio::ssl::context is not supported. + // The corresponding configuration objects for Botan are: + // * TLS::Session_Manager + // * Credentials_Manager + // * TLS::Policy + // * TLS::Server_Information + // It would be nice to have a masquarading wrapper that exposes an API + // compatible with asio::ssl::context for convenient drop-in replacement. + // For now, base your TLS configurations on the above mentioned classes. + throw Not_Implemented("cannot handle an asio::ssl::context"); + } + + + Stream(Stream&& other) = default; + Stream& operator=(Stream&& other) = default; + + Stream(const Stream& other) = delete; + Stream& operator=(const Stream& other) = delete; + + // + // -- -- accessor methods + // + + executor_type get_executor() noexcept { return nextLayer_.get_executor(); } + + const next_layer_type& next_layer() const { return nextLayer_; } + next_layer_type& next_layer() { return nextLayer_; } + + lowest_layer_type& lowest_layer() { return nextLayer_.lowest_layer(); } + const lowest_layer_type& lowest_layer() const { return nextLayer_.lowest_layer(); } + + native_handle_type native_handle() { return &this->channel_; } + + // + // -- -- configuration and callback setters + // + + template< + typename VerifyCallback> + void set_verify_callback(VerifyCallback callback) + { + BOTAN_UNUSED(callback); + throw Not_Implemented("set_verify_callback is not implemented"); + } + + template< + typename VerifyCallback> + void set_verify_callback(VerifyCallback callback, + boost::system::error_code& ec) + { + BOTAN_UNUSED(callback); + ec = make_error_code(Botan::error::not_implemented); + } + + void set_verify_depth(int depth) + { + BOTAN_UNUSED(depth); + throw Not_Implemented("set_verify_depth is not implemented"); + } + + void set_verify_depth(int depth, + boost::system::error_code& ec) + { + BOTAN_UNUSED(depth); + ec = make_error_code(Botan::error::not_implemented); + } + + template <typename verify_mode> + void set_verify_mode(verify_mode v) + { + BOTAN_UNUSED(v); + throw Not_Implemented("set_verify_mode is not implemented"); + } + + template <typename verify_mode> + void set_verify_mode(verify_mode v, + boost::system::error_code& ec) + { + BOTAN_UNUSED(v); + ec = make_error_code(Botan::error::not_implemented); + } + + // + // -- -- handshake methods + // + + void handshake(handshake_type type) + { + validate_handshake_type(type); + + boost::system::error_code ec; + handshake(type, ec); + boost::asio::detail::throw_error(ec, "handshake"); + } + + void handshake(handshake_type type, boost::system::error_code& ec) + { + if(!validate_handshake_type(type, ec)) + { + return; + } + + while(!native_handle()->is_active()) + { + writePendingTlsData(ec); + if(ec) + { + return; + } + + auto read_buffer = boost::asio::buffer( + this->core_.input_buffer_, + nextLayer_.read_some(this->core_.input_buffer_, ec)); + if(ec) + { + return; + } + + try + { + native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), + read_buffer.size()); + } + catch(...) + { + ec = Botan::convertException(); + return; + } + + writePendingTlsData(ec); + } + } + + template<typename ConstBufferSequence> + void handshake(handshake_type type, const ConstBufferSequence& buffers) + { + BOTAN_UNUSED(type, buffers); + throw Not_Implemented("server-side TLS stream is not implemented"); + } + + template<typename ConstBufferSequence> + void handshake(handshake_type type, + const ConstBufferSequence& buffers, + boost::system::error_code& ec) + { + BOTAN_UNUSED(type, buffers); + ec = make_error_code(Botan::error::not_implemented); + } + + template <typename HandshakeHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, + void(boost::system::error_code)) + async_handshake(handshake_type type, HandshakeHandler&& handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a HandshakeHandler. + BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check; + + validate_handshake_type(type); + + boost::asio::async_completion<HandshakeHandler, + void(boost::system::error_code)> + init(handler); + + auto op = create_async_handshake_op(std::move(init.completion_handler)); + op(boost::system::error_code{}, 0, 1); + + return init.result.get(); + } + + template <typename ConstBufferSequence, typename BufferedHandshakeHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, + void(boost::system::error_code, std::size_t)) + async_handshake(handshake_type type, + const ConstBufferSequence& buffers, + BufferedHandshakeHandler&& handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a BufferedHandshakeHandler. + BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check; + BOTAN_UNUSED(type, buffers, handler); + throw Not_Implemented("buffered async handshake is not implemented"); + } + + // + // -- -- shutdown methods + // + + void shutdown(boost::system::error_code& ec) + { + try + { + native_handle()->close(); + } + catch(...) + { + ec = Botan::convertException(); + return; + } + writePendingTlsData(ec); + } + + void shutdown() + { + boost::system::error_code ec; + shutdown(ec); + boost::asio::detail::throw_error(ec, "shutdown"); + } + + template <typename ShutdownHandler> + void async_shutdown(ShutdownHandler&& handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ShutdownHandler. + BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(ShutdownHandler, handler) type_check; + BOTAN_UNUSED(handler); + throw Not_Implemented("async shutdown is not implemented"); + } + + // + // -- -- I/O methods + // + + template <typename MutableBufferSequence> + std::size_t read_some(const MutableBufferSequence& buffers, + boost::system::error_code& ec) + { + if(this->core_.hasReceivedData()) + { + return this->core_.copyReceivedData(buffers); + } + + auto read_buffer = boost::asio::buffer( + this->core_.input_buffer_, + nextLayer_.read_some(this->core_.input_buffer_, ec)); + if(ec) + { + return 0; + } + + try + { + native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), + read_buffer.size()); + } + catch(...) + { + ec = Botan::convertException(); + return 0; + } + + return this->core_.copyReceivedData(buffers); + } + + template <typename MutableBufferSequence> + std::size_t read_some(const MutableBufferSequence& buffers) + { + boost::system::error_code ec; + auto const n = read_some(buffers, ec); + boost::asio::detail::throw_error(ec, "read_some"); + return n; + } + + template <typename ConstBufferSequence> + std::size_t write_some(const ConstBufferSequence& buffers, + boost::system::error_code& ec) + { + boost::asio::const_buffer buffer = + boost::asio::detail::buffer_sequence_adapter< + boost::asio::const_buffer, ConstBufferSequence>::first(buffers); + + try + { + native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size()); + } + catch(...) + { + ec = Botan::convertException(); + return 0; + } + + writePendingTlsData(ec); + if(ec) + { + return 0; + } + return buffer.size(); + } + + template <typename ConstBufferSequence> + std::size_t write_some(const ConstBufferSequence& buffers) + { + boost::system::error_code ec; + auto const n = write_some(buffers, ec); + boost::asio::detail::throw_error(ec, "write_some"); + return n; + } + + template <typename ConstBufferSequence, typename WriteHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) + { + BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + boost::asio::const_buffer buffer = + boost::asio::detail::buffer_sequence_adapter< + boost::asio::const_buffer, ConstBufferSequence>::first(buffers); + + try + { + native_handle()->send(static_cast<const uint8_t*>(buffer.data()), + buffer.size()); + } + catch(...) + { + // TODO: don't call directly + handler(Botan::convertException(), 0); + return; + } + + boost::asio::async_completion<WriteHandler, + void(boost::system::error_code, std::size_t)> + init(handler); + auto op = create_async_write_op(std::move(init.completion_handler), + buffer.size()); + + boost::asio::async_write(nextLayer_, this->core_.sendBuffer(), + std::move(op)); + return init.result.get(); + } + + template <typename MutableBufferSequence, typename ReadHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) + { + BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + boost::asio::async_completion<ReadHandler, + void(boost::system::error_code, std::size_t)> + init(handler); + + auto op = create_async_read_op(std::move(init.completion_handler), buffers); + op(boost::system::error_code{}, 0); + return init.result.get(); + } + + protected: + size_t writePendingTlsData(boost::system::error_code& ec) + { + auto writtenBytes = + boost::asio::write(nextLayer_, this->core_.sendBuffer(), ec); + + this->core_.consumeSendBuffer(writtenBytes); + return writtenBytes; + } + + template <typename Handler> + Botan::AsyncHandshakeOperation<Channel, StreamLayer, Handler> + create_async_handshake_op(Handler&& handler) + { + return Botan::AsyncHandshakeOperation<Channel, StreamLayer, Handler>( + native_handle(), this->core_, nextLayer_, std::forward<Handler>(handler)); + } + + template <typename Handler, typename MutableBufferSequence> + Botan::AsyncReadOperation<Channel, StreamLayer, Handler, + MutableBufferSequence> + create_async_read_op(Handler&& handler, + const MutableBufferSequence& buffers) + { + return Botan::AsyncReadOperation<Channel, StreamLayer, Handler, + MutableBufferSequence>( + native_handle(), this->core_, nextLayer_, std::forward<Handler>(handler), + buffers); + } + + template <typename Handler> + Botan::AsyncWriteOperation<Handler> + create_async_write_op(Handler&& handler, std::size_t plainBytesTransferred) + { + return Botan::AsyncWriteOperation<Handler>( + this->core_, std::forward<Handler>(handler), plainBytesTransferred); + } + + StreamLayer nextLayer_; + }; + +} // namespace Botan + +#endif diff --git a/src/lib/tls/asio/asio_stream_base.h b/src/lib/tls/asio/asio_stream_base.h new file mode 100644 index 000000000..93a14bd5c --- /dev/null +++ b/src/lib/tls/asio/asio_stream_base.h @@ -0,0 +1,64 @@ +#ifndef BOTAN_ASIO_STREAM_BASE_H_ +#define BOTAN_ASIO_STREAM_BASE_H_ + +#include <botan/auto_rng.h> +#include <botan/tls_client.h> +#include <botan/tls_server.h> + +namespace Botan { + +template <class Channel> +class StreamBase + { + }; + +template <> +class StreamBase<Botan::TLS::Client> + { + public: + StreamBase(Botan::TLS::Session_Manager& sessionManager, + Botan::Credentials_Manager& credentialsManager, + const Botan::TLS::Policy& policy = Botan::TLS::Strict_Policy{}, + const Botan::TLS::Server_Information& serverInfo = + Botan::TLS::Server_Information{}) + : channel_(core_, + sessionManager, + credentialsManager, + policy, + rng_, + serverInfo) + { + } + + StreamBase(const StreamBase&) = delete; + StreamBase& operator=(const StreamBase&) = delete; + + protected: + Botan::StreamCore core_; + Botan::AutoSeeded_RNG rng_; + Botan::TLS::Client channel_; + }; + +template <> +class StreamBase<Botan::TLS::Server> + { + public: + StreamBase(Botan::TLS::Session_Manager& sessionManager, + Botan::Credentials_Manager& credentialsManager, + const Botan::TLS::Policy& policy = Botan::TLS::Strict_Policy{}) + : channel_(core_, sessionManager, credentialsManager, policy, rng_) + { + } + + StreamBase(const StreamBase&) = delete; + StreamBase& operator=(const StreamBase&) = delete; + + protected: + Botan::StreamCore core_; + Botan::AutoSeeded_RNG rng_; + Botan::TLS::Server channel_; + }; + +} // namespace botan + +#endif diff --git a/src/lib/tls/asio/asio_stream_core.h b/src/lib/tls/asio/asio_stream_core.h new file mode 100644 index 000000000..caac6d06a --- /dev/null +++ b/src/lib/tls/asio/asio_stream_core.h @@ -0,0 +1,104 @@ +#ifndef BOTAN_ASIO_STREAM_CORE_H_ +#define BOTAN_ASIO_STREAM_CORE_H_ + +#include <botan/internal/asio_includes.h> +#include <botan/tls_callbacks.h> +#include <mutex> +#include <vector> + +namespace Botan { +/** + * Contains the buffers for reading/sending, and the needed botan callbacks + */ +struct StreamCore : public Botan::TLS::Callbacks + { + struct Buffer + { + Buffer() : dynamicBuffer(data_buffer) {} + std::vector<uint8_t> data_buffer; + boost::asio::dynamic_vector_buffer< + uint8_t, typename decltype(data_buffer)::allocator_type> + dynamicBuffer; + }; + + StreamCore() + : input_buffer_space_(17 * 1024, '\0'), // enough for a TLS Datagram + input_buffer_(boost::asio::buffer(input_buffer_space_)) {} + + virtual ~StreamCore() = default; + + void tls_emit_data(const uint8_t data[], size_t size) override + { + auto buffer = send_buffer_.dynamicBuffer.prepare(size); + auto copySize = + boost::asio::buffer_copy(buffer, boost::asio::buffer(data, size)); + send_buffer_.dynamicBuffer.commit(copySize); + } + + void tls_record_received(uint64_t, const uint8_t data[], + size_t size) override + { + auto buffer = receive_buffer_.dynamicBuffer.prepare(size); + auto copySize = + boost::asio::buffer_copy(buffer, boost::asio::buffer(data, size)); + receive_buffer_.dynamicBuffer.commit(copySize); + } + + void tls_alert(Botan::TLS::Alert alert) override + { + if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY) + { + // TODO + } + } + + std::chrono::milliseconds + tls_verify_cert_chain_ocsp_timeout() const override + { + return std::chrono::milliseconds(1000); + } + + bool tls_session_established(const Botan::TLS::Session&) override + { + return true; + } + + bool hasReceivedData() const + { + return receive_buffer_.dynamicBuffer.size() > 0; + } + + template <typename MutableBufferSequence> + std::size_t copyReceivedData(MutableBufferSequence buffers) + { + const auto copiedBytes = + boost::asio::buffer_copy(buffers, receive_buffer_.dynamicBuffer.data()); + receive_buffer_.dynamicBuffer.consume(copiedBytes); + return copiedBytes; + } + + bool hasDataToSend() const { return send_buffer_.dynamicBuffer.size() > 0; } + + boost::asio::const_buffer sendBuffer() const + { + return send_buffer_.dynamicBuffer.data(); + } + + void consumeSendBuffer(std::size_t bytesConsumed) + { + send_buffer_.dynamicBuffer.consume(bytesConsumed); + } + + // Buffer space used to read input intended for the engine. + std::vector<uint8_t> input_buffer_space_; + + // A buffer that may be used to read input intended for the engine. + const boost::asio::mutable_buffer input_buffer_; + + private: + Buffer receive_buffer_; + Buffer send_buffer_; + }; +} // namespace Botan + +#endif diff --git a/src/lib/tls/asio/info.txt b/src/lib/tls/asio/info.txt new file mode 100644 index 000000000..a78f5c85c --- /dev/null +++ b/src/lib/tls/asio/info.txt @@ -0,0 +1,23 @@ +<defines> +ASIO -> 20181218 +</defines> + +<header:public> +asio_stream.h +asio_error.h +</header:public> + +<header:internal> +asio_async_handshake_op.h +asio_async_read_op.h +asio_async_write_op.h +asio_convert_exceptions.h +asio_stream_base.h +asio_stream_core.h +asio_includes.h +</header:internal> + +<requires> +boost +tls +</requires> |