aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/authors.txt1
-rw-r--r--doc/manual/tls.rst242
-rw-r--r--readme.rst1
-rw-r--r--src/lib/tls/asio/asio_stream.h2
4 files changed, 245 insertions, 1 deletions
diff --git a/doc/authors.txt b/doc/authors.txt
index 7f5f90d0b..abaaab024 100644
--- a/doc/authors.txt
+++ b/doc/authors.txt
@@ -35,6 +35,7 @@ Frank Schoenmann
Google Inc
Gustavo Serra Scalet
guywithcrookedface
+Hannes Rantzsch
Harry Reimann
Hegedüs Márton Csaba
Hubert Bugaj
diff --git a/doc/manual/tls.rst b/doc/manual/tls.rst
index bb363ca43..164b6b8d6 100644
--- a/doc/manual/tls.rst
+++ b/doc/manual/tls.rst
@@ -1600,3 +1600,245 @@ Server Code Example
// send data to the tls client using server.send_data()
// ...
}
+
+TLS Stream
+----------------------------------------
+
+:cpp:class:`TLS::Stream` offers a Boost.Asio compatible wrapper around :cpp:class:`TLS::Client`.
+It can be used as an alternative to Boost.Asio's `ssl::stream <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ssl__stream.html>`_ with minor adjustments to the using code.
+It offers the following interface:
+
+.. cpp:class:: template <class StreamLayer, class ChannelT> TLS::Stream
+
+ *StreamLayer* specifies the type of the stream's *next layer*, for example a `Boost.Asio TCP socket <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ip__tcp/socket.html>`_.
+ *ChannelT* is the type of the stream's *native handle*; it defaults to :cpp:class:`TLS::Channel` and should not be specified manually.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Context& context, Args&& ... args)
+
+ 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.
+ The further *args* will be forwarded to the *next layer*'s constructor.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Arg&& arg, Context& context)
+
+ Convenience constructor for :cpp:class:`boost::asio::ssl::stream` compatibility.
+ The parameters have the same meaning as for the first constructor, but their order is changed and only one argument can be passed to the *next layer* constructor.
+
+
+ .. cpp:function:: void handshake(Connection_Side side, boost::system::error_code& ec)
+
+ Set up the *native handle* and perform the TLS handshake.
+ As only the client side of the stream is currently implemented, *side* should be ``Connection_Side::CLIENT``.
+
+ .. cpp:function:: void handshake(Connection_Side side)
+
+ Overload of :cpp:func:`handshake` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename HandshakeHandler> \
+ DEDUCED async_handshake(Connection_Side side, HandshakeHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`handshake`.
+ The function returns immediately and calls the *handler* callback function after performing asynchronous I/O to complete the TLS handshake.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *HandshakeHandler* type.
+
+
+ .. cpp:function:: void shutdown(boost::system::error_code& ec)
+
+ Calls :ref:`close <TLS::Channel::close>` on the native handle and writes the TLS alert to the *next layer*.
+
+ .. cpp:function:: void shutdown()
+
+ Overload of :cpp:func:`shutdown` that throws an exception if an error occurs.
+
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec)
+
+ Reads encrypted data from the *next layer*, decrypts it, and writes it into the provided *buffers*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes read.
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers)
+
+ Overload of :cpp:func:`read_some` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename MutableBufferSequence, typename ReadHandler> \
+ DEDUCED async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`read_some`.
+ The function returns immediately and calls the *handler* callback function after writing the decrypted data into the provided *buffers*.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *ReadHandler* type.
+ *ReadHandler* should suffice the `requirements to a Boost.Asio read handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ReadHandler.html>`_.
+
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec)
+
+ Encrypts data from the provided *buffers* and writes it to the *next layer*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes written.
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers)
+
+ Overload of :cpp:func:`write_some` that throws an exception rather than setting an error code.
+
+ .. cpp:function:: template <typename ConstBufferSequence, typename WriteHandler> \
+ DEDUCED async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`write_some`.
+ The function returns immediately and calls the *handler* callback function after writing the encrypted data to the *next layer*.
+ 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
+
+ A helper struct to collect the initialization parameters for the Stream's underlying *native handle* (see :cpp:func:`TLS::Client`).
+ `TLS::Context` is defined as
+
+ .. code-block:: cpp
+
+ struct Context
+ {
+ Credentials_Manager* credentialsManager;
+ RandomNumberGenerator* randomNumberGenerator;
+ Session_Manager* sessionManager;
+ Policy* policy;
+ Server_Information serverInfo;
+ };
+
+
+Stream Code Example
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <iostream>
+
+ #include <botan/asio_stream.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor_system.h>
+
+ #include <boost/asio.hpp>
+ #include <boost/beast.hpp>
+ #include <boost/bind.hpp>
+
+ namespace http = boost::beast::http;
+ namespace _ = boost::asio::placeholders;
+
+ // very basic credentials manager
+ class Credentials_Manager : public Botan::Credentials_Manager
+ {
+ public:
+ Credentials_Manager() {}
+
+ std::vector<Botan::Certificate_Store*>
+ trusted_certificate_authorities(const std::string&, const std::string&) override
+ {
+ return {&cert_store_};
+ }
+
+ private:
+ Botan::System_Certificate_Store cert_store_;
+ };
+
+ // a simple https client based on TLS::Stream
+ class client
+ {
+ public:
+ client(boost::asio::io_context& io_context,
+ 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()}
+ , stream_(io_context, ctx_)
+ {
+ boost::asio::async_connect(stream_.lowest_layer(), endpoint_iterator,
+ boost::bind(&client::handle_connect, this, _::error));
+ }
+
+ void handle_connect(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Connect failed: " << error.message() << "\n";
+ return;
+ }
+ stream_.async_handshake(Botan::TLS::Connection_Side::CLIENT,
+ boost::bind(&client::handle_handshake, this, _::error));
+ }
+
+ void handle_handshake(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Handshake failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_write(stream_, request_,
+ boost::bind(&client::handle_write, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_write(const boost::system::error_code& error, size_t)
+ {
+ if(error)
+ {
+ std::cout << "Write failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_read(stream_, reply_, response_,
+ boost::bind(&client::handle_read, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_read(const boost::system::error_code& error, size_t)
+ {
+ if(!error)
+ {
+ std::cout << "Reply: ";
+ std::cout << response_.body() << "\n";
+ }
+ else
+ {
+ std::cout << "Read failed: " << error.message() << "\n";
+ }
+ }
+
+ private:
+ http::request<http::dynamic_body> request_;
+ http::response<http::string_body> response_;
+ boost::beast::flat_buffer reply_;
+
+ Botan::TLS::Session_Manager_Noop session_mgr_;
+ Botan::AutoSeeded_RNG rng_;
+ Credentials_Manager credentials_mgr_;
+ Botan::TLS::Policy policy_;
+
+ Botan::TLS::Context ctx_;
+ Botan::TLS::Stream<boost::asio::ip::tcp::socket> stream_;
+ };
+
+ int main()
+ {
+ boost::asio::io_context io_context;
+
+ boost::asio::ip::tcp::resolver resolver(io_context);
+ boost::asio::ip::tcp::resolver::query query("botan.randombit.net", "443");
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ http::request<http::string_body> req;
+ req.version(11);
+ req.method(http::verb::get);
+ req.target("/news.html");
+ req.set(http::field::host, "botan.randombit.net");
+
+ client c(io_context, iterator, req);
+
+ io_context.run();
+ }
diff --git a/readme.rst b/readme.rst
index 1c5855c2c..dd7876368 100644
--- a/readme.rst
+++ b/readme.rst
@@ -119,3 +119,4 @@ Other Useful Things
* Format preserving encryption scheme FE1
* Threshold secret sharing
* NIST key wrapping
+* Boost.Asio compatible TLS client stream
diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h
index c0f8c73f4..7cdd8d3dd 100644
--- a/src/lib/tls/asio/asio_stream.h
+++ b/src/lib/tls/asio/asio_stream.h
@@ -229,7 +229,7 @@ class Stream
* @param type The type of handshaking to be performed, i.e. as a client or as a server.
* @param handler The handler to be called when the handshake operation completes.
* The equivalent function signature of the handler must be: void(boost::system::error_code)
- * @throws Invalid_Argument if Connection_Side could not be validated
+ * @throws NotImplemented if Connection_Side is not CLIENT
*/
template <typename HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler,