aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Rantzsch <[email protected]>2019-04-25 11:31:12 +0200
committerHannes Rantzsch <[email protected]>2019-04-26 11:59:41 +0200
commit2c958f40b3ed1ee9c1f6870f90ddd853f4bb98b5 (patch)
treeed490b3251975cce43453d443ffdb645ebaf1fce
parentbaa0725609df52014b43bb28297594c7a9c3c320 (diff)
tidy up StreamCore as an implementation detail of Stream
-rw-r--r--src/lib/tls/asio/asio_async_handshake_op.h27
-rw-r--r--src/lib/tls/asio/asio_async_read_op.h24
-rw-r--r--src/lib/tls/asio/asio_async_write_op.h13
-rw-r--r--src/lib/tls/asio/asio_stream.h199
4 files changed, 131 insertions, 132 deletions
diff --git a/src/lib/tls/asio/asio_async_handshake_op.h b/src/lib/tls/asio/asio_async_handshake_op.h
index f3eea7d16..e209c91da 100644
--- a/src/lib/tls/asio/asio_async_handshake_op.h
+++ b/src/lib/tls/asio/asio_async_handshake_op.h
@@ -33,20 +33,17 @@ class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::execu
*
* @param handler Handler function to be called upon completion.
* @param stream The stream from which the data will be read
- * @param core The stream's core; used to extract decrypted data.
* @param ec Optional error code; used to report an error to the handler function.
*/
template<class HandlerT>
AsyncHandshakeOperation(
HandlerT&& handler,
Stream& stream,
- typename Stream::StreamCore& core,
const boost::system::error_code& ec = {})
: AsyncBase<Handler, typename Stream::executor_type, Allocator>(
std::forward<HandlerT>(handler),
stream.get_executor())
, m_stream(stream)
- , m_core(core)
{
this->operator()(ec, std::size_t(0), false);
}
@@ -60,10 +57,12 @@ class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::execu
if(bytesTransferred > 0 && !ec)
{
// Provide encrypted TLS data received from the network to TLS::Channel for decryption
- boost::asio::const_buffer read_buffer {m_core.input_buffer.data(), bytesTransferred};
+ boost::asio::const_buffer read_buffer {m_stream.input_buffer().data(), bytesTransferred};
try
{
- m_stream.native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
+ m_stream.native_handle()->received_data(
+ static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size()
+ );
}
catch(const TLS_Exception& e)
{
@@ -79,26 +78,26 @@ class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::execu
}
}
- if(m_core.hasDataToSend() && !ec)
+ if(m_stream.hasDataToSend() && !ec)
{
// Write encrypted TLS data provided by the TLS::Channel on the wire
- // Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`).
- // This operation will eventually call `*this` as its own handler, passing the 0 back to this call
- // operator. This is necessary because the check of `bytesTransferred > 0` assumes that
- // `bytesTransferred` bytes were just read and are in the core's input_buffer for further processing.
+ // Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`). This
+ // operation will eventually call `*this` as its own handler, passing the 0 back to this call operator.
+ // This is necessary because the check of `bytesTransferred > 0` assumes that `bytesTransferred` bytes
+ // were just read and are available in input_buffer for further processing.
AsyncWriteOperation<
AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
Stream,
Allocator>
- op{std::move(*this), m_stream, m_core, 0};
+ op{std::move(*this), m_stream, 0};
return;
}
if(!m_stream.native_handle()->is_active() && !ec)
{
// Read more encrypted TLS data from the network
- m_stream.next_layer().async_read_some(m_core.input_buffer, std::move(*this));
+ m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
return;
}
@@ -116,9 +115,7 @@ class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::execu
}
private:
- Stream& m_stream;
- typename Stream::StreamCore& m_core;
-
+ Stream& m_stream;
boost::system::error_code m_ec;
};
diff --git a/src/lib/tls/asio/asio_async_read_op.h b/src/lib/tls/asio/asio_async_read_op.h
index 12f8da8e8..778977011 100644
--- a/src/lib/tls/asio/asio_async_read_op.h
+++ b/src/lib/tls/asio/asio_async_read_op.h
@@ -33,21 +33,18 @@ class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_t
*
* @param handler Handler function to be called upon completion.
* @param stream The stream from which the data will be read
- * @param core The stream's core; used to extract decrypted data.
* @param buffers The buffers into which the data will be read.
* @param ec Optional error code; used to report an error to the handler function.
*/
template <class HandlerT>
AsyncReadOperation(HandlerT&& handler,
Stream& stream,
- typename Stream::StreamCore& core,
const MutableBufferSequence& buffers,
const boost::system::error_code& ec = {})
: AsyncBase<Handler, typename Stream::executor_type, Allocator>(
std::forward<HandlerT>(handler),
stream.get_executor())
, m_stream(stream)
- , m_core(core)
, m_buffers(buffers)
, m_decodedBytes(0)
{
@@ -63,11 +60,12 @@ class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_t
if(bytes_transferred > 0 && !ec)
{
// We have received encrypted data from the network, now hand it to TLS::Channel for decryption.
- boost::asio::const_buffer read_buffer{m_core.input_buffer.data(), bytes_transferred};
+ boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
try
{
- m_stream.native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()),
- read_buffer.size());
+ m_stream.native_handle()->received_data(
+ static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size()
+ );
}
catch(const TLS_Exception& e)
{
@@ -83,17 +81,17 @@ class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_t
}
}
- if(!m_core.hasReceivedData() && !ec && boost::asio::buffer_size(m_buffers) > 0)
+ if(!m_stream.hasReceivedData() && !ec && boost::asio::buffer_size(m_buffers) > 0)
{
// The channel did not decrypt a complete record yet, we need more data from the socket.
- m_stream.next_layer().async_read_some(m_core.input_buffer, std::move(*this));
+ m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
return;
}
- if(m_core.hasReceivedData() && !ec)
+ if(m_stream.hasReceivedData() && !ec)
{
// The channel has decrypted a TLS record, now copy it to the output buffers.
- m_decodedBytes = m_core.copyReceivedData(m_buffers);
+ m_decodedBytes = m_stream.copyReceivedData(m_buffers);
}
if(!isContinuation)
@@ -110,10 +108,8 @@ class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_t
}
private:
- Stream& m_stream;
- typename Stream::StreamCore& m_core;
- MutableBufferSequence m_buffers;
-
+ Stream& m_stream;
+ MutableBufferSequence m_buffers;
std::size_t m_decodedBytes;
boost::system::error_code m_ec;
};
diff --git a/src/lib/tls/asio/asio_async_write_op.h b/src/lib/tls/asio/asio_async_write_op.h
index 44bd211e3..b14d4ab44 100644
--- a/src/lib/tls/asio/asio_async_write_op.h
+++ b/src/lib/tls/asio/asio_async_write_op.h
@@ -32,7 +32,6 @@ class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_
*
* @param handler Handler function to be called upon completion.
* @param stream The stream from which the data will be read
- * @param core The stream's core; used to extract decrypted data.
* @param plainBytesTransferred Number of bytes to be reported to the user-provided handler function as
* bytes_transferred. This needs to be provided since the amount of plaintext data
* consumed from the input buffer can differ from the amount of encrypted data written
@@ -42,14 +41,12 @@ class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_
template <class HandlerT>
AsyncWriteOperation(HandlerT&& handler,
Stream& stream,
- typename Stream::StreamCore& core,
std::size_t plainBytesTransferred,
const boost::system::error_code& ec = {})
: AsyncBase<Handler, typename Stream::executor_type, Allocator>(
std::forward<HandlerT>(handler),
stream.get_executor())
, m_stream(stream)
- , m_core(core)
, m_plainBytesTransferred(plainBytesTransferred)
{
this->operator()(ec, std::size_t(0), false);
@@ -63,11 +60,11 @@ class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_
{
// mark the number of encrypted bytes sent to the network as "consumed"
// Note: bytes_transferred will be zero on first call
- m_core.consumeSendBuffer(bytes_transferred);
+ m_stream.consumeSendBuffer(bytes_transferred);
- if(m_core.hasDataToSend() && !ec)
+ if(m_stream.hasDataToSend() && !ec)
{
- m_stream.next_layer().async_write_some(m_core.sendBuffer(), std::move(*this));
+ m_stream.next_layer().async_write_some(m_stream.sendBuffer(), std::move(*this));
return;
}
@@ -87,9 +84,7 @@ class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_
}
private:
- Stream& m_stream;
- typename Stream::StreamCore& m_core;
-
+ Stream& m_stream;
std::size_t m_plainBytesTransferred;
boost::system::error_code m_ec;
};
diff --git a/src/lib/tls/asio/asio_stream.h b/src/lib/tls/asio/asio_stream.h
index af257dbd2..9b501a01a 100644
--- a/src/lib/tls/asio/asio_stream.h
+++ b/src/lib/tls/asio/asio_stream.h
@@ -44,27 +44,35 @@ namespace TLS {
*
* Currently only the TLS::Client specialization is implemented.
*
- * \tparam StreamLayer type of the next layer, usually a network socket
- * \tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
+ * @tparam StreamLayer type of the next layer, usually a network socket
+ * @tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
*/
template <class StreamLayer, class ChannelT = Channel>
class Stream
{
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<ChannelT>::type;
+ //
+ // -- -- construction
+ //
- public:
template <typename... Args>
explicit Stream(Context& context, Args&& ... args)
- : m_context(context), m_nextLayer(std::forward<Args>(args)...) {}
+ : m_context(context)
+ , m_nextLayer(std::forward<Args>(args)...)
+ , m_core(m_receive_buffer, m_send_buffer)
+ , 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
template <typename Arg>
explicit Stream(Arg&& arg, Context& context)
- : m_context(context), m_nextLayer(std::forward<Arg>(arg)) {}
+ : m_context(context)
+ , m_nextLayer(std::forward<Arg>(arg))
+ , m_core(m_receive_buffer, m_send_buffer)
+ , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
+ , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
+ {}
virtual ~Stream() = default;
@@ -75,9 +83,14 @@ class Stream
Stream& operator=(const Stream& other) = delete;
//
- // -- -- accessor methods
+ // -- -- boost::asio compatible accessor methods
//
+ 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<ChannelT>::type;
+
executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
const next_layer_type& next_layer() const { return m_nextLayer; }
@@ -157,6 +170,43 @@ class Stream
}
//
+ // -- -- accessor methods for send and receive buffers
+ //
+
+ const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
+ boost::asio::const_buffer sendBuffer() const { return m_send_buffer.data(); } // TODO: really .data() ?
+
+ /**
+ * Check if decrypted data is available in the receive buffer
+ */
+ bool hasReceivedData() const { return m_receive_buffer.size() > 0; }
+
+ /**
+ * Copy decrypted data into the user-provided buffer
+ */
+ template <typename MutableBufferSequence>
+ std::size_t copyReceivedData(MutableBufferSequence buffers)
+ {
+ // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
+ // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
+ // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
+ // than the decrypted record, so this optimization might not be worth the additional complexity.
+ const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
+ m_receive_buffer.consume(copiedBytes);
+ return copiedBytes;
+ }
+
+ /**
+ * Check if encrypted data is available in the send buffer
+ */
+ bool hasDataToSend() const { return m_send_buffer.size() > 0; }
+
+ /**
+ * Mark bytes in the send buffer as consumed, removing them from the buffer
+ */
+ void consumeSendBuffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
+
+ //
// -- -- handshake methods
//
@@ -190,11 +240,7 @@ class Stream
if(ec)
{ return; }
- boost::asio::const_buffer read_buffer
- {
- this->m_core.input_buffer.data(),
- m_nextLayer.read_some(this->m_core.input_buffer, ec)
- };
+ boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
if(ec)
{ return; }
@@ -244,7 +290,7 @@ class Stream
boost::asio::async_completion<HandshakeHandler, void(boost::system::error_code)> init(handler);
AsyncHandshakeOperation<typename std::decay<HandshakeHandler>::type, Stream>
- op{std::move(init.completion_handler), *this, this->m_core};
+ op{std::move(init.completion_handler), *this};
return init.result.get();
}
@@ -341,14 +387,14 @@ class Stream
std::size_t read_some(const MutableBufferSequence& buffers,
boost::system::error_code& ec)
{
- if(this->m_core.hasReceivedData())
- { return this->m_core.copyReceivedData(buffers); }
+ if(hasReceivedData())
+ { return copyReceivedData(buffers); }
tls_receive_some(ec);
if(ec)
{ return 0; }
- return this->m_core.copyReceivedData(buffers);
+ return copyReceivedData(buffers);
}
/**
@@ -429,16 +475,16 @@ class Stream
std::size_t sent = tls_encrypt_some(buffers, ec);
if(ec)
{
- // we cannot be sure how many bytes were committed here
- // so clear the send_buffer and let the AsyncWriteOperation call the handler with the error_code set
- this->m_core.clearSendBuffer();
+ // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
+ // AsyncWriteOperation call the handler with the error_code set
+ consumeSendBuffer(m_send_buffer.size());
Botan::TLS::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
- op{std::move(init.completion_handler), *this, this->m_core, std::size_t(0), ec};
+ op{std::move(init.completion_handler), *this, std::size_t(0), ec};
return init.result.get();
}
Botan::TLS::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
- op{std::move(init.completion_handler), *this, this->m_core, sent};
+ op{std::move(init.completion_handler), *this, sent};
return init.result.get();
}
@@ -463,39 +509,40 @@ class Stream
boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
AsyncReadOperation<typename std::decay<ReadHandler>::type, Stream, MutableBufferSequence>
- op{std::move(init.completion_handler), *this, this->m_core, buffers};
+ op{std::move(init.completion_handler), *this, buffers};
return init.result.get();
}
- // TODO: remove StreamCore from public interface
+ protected:
/**
- * Contains the buffers for reading/sending, and the needed botan callbacks
+ * Helper class that implements Botan::TLS::Callbacks
+ *
+ * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
+ * functions triggered by the native_handle.
+ *
+ * @param receive_buffer reference to the buffer where decrypted data should be placed
+ * @param send_buffer reference to the buffer where encrypted data should be placed
*/
class StreamCore : public Botan::TLS::Callbacks
{
public:
- StreamCore()
- : m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
- input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
+ StreamCore(boost::beast::flat_buffer& receive_buffer, boost::beast::flat_buffer& send_buffer)
+ : m_receive_buffer(receive_buffer), m_send_buffer(send_buffer) {}
virtual ~StreamCore() = default;
void tls_emit_data(const uint8_t data[], std::size_t size) override
{
- // Provide the encrypted TLS data in the sendBuffer. Actually sending the data is done
- // using (async_)write_some either in the stream or in an async operation.
m_send_buffer.commit(
- boost::asio::buffer_copy(m_send_buffer.prepare(size), boost::asio::buffer(data, size)));
+ boost::asio::buffer_copy(m_send_buffer.prepare(size), boost::asio::buffer(data, size))
+ );
}
void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
{
- // TODO: It would be nice to avoid this buffer copy. However, we need to deal with the case
- // that the receive buffer provided by the caller is smaller than the decrypted record.
- auto buffer = m_receive_buffer.prepare(size);
- auto copySize =
- boost::asio::buffer_copy(buffer, boost::asio::const_buffer(data, size));
- m_receive_buffer.commit(copySize);
+ m_receive_buffer.commit(
+ boost::asio::buffer_copy(m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
+ );
}
void tls_alert(Botan::TLS::Alert alert) override
@@ -517,49 +564,10 @@ class Stream
return true;
}
- bool hasReceivedData() const
- {
- return m_receive_buffer.size() > 0;
- }
-
- template <typename MutableBufferSequence>
- std::size_t copyReceivedData(MutableBufferSequence buffers)
- {
- const auto copiedBytes =
- boost::asio::buffer_copy(buffers, m_receive_buffer.data());
- m_receive_buffer.consume(copiedBytes);
- return copiedBytes;
- }
-
- bool hasDataToSend() const { return m_send_buffer.size() > 0; }
-
- boost::asio::const_buffer sendBuffer() const
- {
- return m_send_buffer.data();
- }
-
- void consumeSendBuffer(std::size_t bytesConsumed)
- {
- m_send_buffer.consume(bytesConsumed);
- }
-
- void clearSendBuffer()
- {
- consumeSendBuffer(m_send_buffer.size());
- }
-
- private:
- // Buffer space used to read input intended for the engine.
- std::vector<uint8_t> m_input_buffer_space;
- boost::beast::flat_buffer m_receive_buffer;
- boost::beast::flat_buffer m_send_buffer;
-
- public:
- // A buffer that may be used to read input intended for the engine.
- const boost::asio::mutable_buffer input_buffer;
+ boost::beast::flat_buffer& m_receive_buffer;
+ boost::beast::flat_buffer& m_send_buffer;
};
- protected:
// TODO: explain, note: c++17 makes this much better with constexpr if
template<class T = ChannelT>
typename std::enable_if<!std::is_same<Channel, T>::value>::type
@@ -571,11 +579,11 @@ class Stream
{
assert(side == CLIENT);
m_channel = std::unique_ptr<Client>(new Client(m_core,
- *m_context.sessionManager,
- *m_context.credentialsManager,
- *m_context.policy,
- *m_context.randomNumberGenerator,
- m_context.serverInfo));
+ *m_context.sessionManager,
+ *m_context.credentialsManager,
+ *m_context.policy,
+ *m_context.randomNumberGenerator,
+ m_context.serverInfo));
}
//! \brief validate the connection side (OpenSSL compatibility)
@@ -601,27 +609,22 @@ class Stream
size_t sendPendingEncryptedData(boost::system::error_code& ec)
{
- auto writtenBytes = boost::asio::write(m_nextLayer, this->m_core.sendBuffer(), ec);
+ auto writtenBytes = boost::asio::write(m_nextLayer, sendBuffer(), ec);
- this->m_core.consumeSendBuffer(writtenBytes);
+ consumeSendBuffer(writtenBytes);
return writtenBytes;
}
void tls_receive_some(boost::system::error_code& ec)
{
- boost::asio::const_buffer read_buffer =
- {
- this->m_core.input_buffer.data(),
- m_nextLayer.read_some(this->m_core.input_buffer, ec)
- };
+ boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
if(ec)
{ return; }
try
{
- native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()),
- read_buffer.size());
+ native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
}
catch(const TLS_Exception& e)
{
@@ -685,8 +688,16 @@ class Stream
Context m_context;
StreamLayer m_nextLayer;
+
+ boost::beast::flat_buffer m_receive_buffer;
+ boost::beast::flat_buffer m_send_buffer;
+
StreamCore m_core;
std::unique_ptr<ChannelT> m_channel;
+
+ // Buffer space used to read input intended for the core
+ std::vector<uint8_t> m_input_buffer_space;
+ const boost::asio::mutable_buffer m_input_buffer;
};
} // namespace TLS