Botan  2.15.0
Crypto and TLS for C++11
asio_stream.h
Go to the documentation of this file.
1 /*
2 * TLS ASIO Stream
3 * (C) 2018-2020 Jack Lloyd
4 * 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8 
9 #ifndef BOTAN_ASIO_STREAM_H_
10 #define BOTAN_ASIO_STREAM_H_
11 
12 #include <botan/build.h>
13 
14 // first version to be compatible with Networking TS (N4656) and boost::beast
15 #include <boost/version.hpp>
16 #if BOOST_VERSION >= 106600
17 
18 #include <botan/asio_async_ops.h>
19 #include <botan/asio_context.h>
20 #include <botan/asio_error.h>
21 
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>
27 
28 // We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
29 // which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
30 #define BOOST_ASIO_DISABLE_SERIAL_PORT
31 #include <boost/asio.hpp>
32 #include <boost/beast/core.hpp>
33 
34 #include <algorithm>
35 #include <memory>
36 #include <type_traits>
37 
38 namespace Botan {
39 namespace TLS {
40 
41 /**
42  * @brief boost::asio compatible SSL/TLS stream
43  *
44  * @tparam StreamLayer type of the next layer, usually a network socket
45  * @tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
46  */
47 template <class StreamLayer, class ChannelT = Channel>
48 class Stream
49  {
50  public:
51  //! \name construction
52  //! @{
53 
54  /**
55  * @brief Construct a new Stream
56  *
57  * @param context The context parameter is used to set up the underlying native handle. Using code is
58  * responsible for lifetime management of the context and must ensure that it is available for the
59  * lifetime of the stream.
60  * @param args Arguments to be forwarded to the construction of the next layer.
61  */
62  template <typename... Args>
63  explicit Stream(Context& context, Args&& ... args)
64  : m_context(context)
65  , m_nextLayer(std::forward<Args>(args)...)
66  , m_core(*this)
67  , m_shutdown_received(false)
70  {}
71 
72  /**
73  * @brief Construct a new Stream
74  *
75  * Convenience overload for boost::asio::ssl::stream compatibility.
76  *
77  * @param arg This argument is forwarded to the construction of the next layer.
78  * @param context The context parameter is used to set up the underlying native handle. Using code is
79  * responsible for lifetime management of the context and must ensure that is available for the
80  * lifetime of the stream.
81  */
82  template <typename Arg>
83  explicit Stream(Arg&& arg, Context& context)
84  : m_context(context)
85  , m_nextLayer(std::forward<Arg>(arg))
86  , m_core(*this)
87  , m_shutdown_received(false)
90  {}
91 
92  virtual ~Stream() = default;
93 
94  Stream(Stream&& other) = default;
95  Stream& operator=(Stream&& other) = default;
96 
97  Stream(const Stream& other) = delete;
98  Stream& operator=(const Stream& other) = delete;
99 
100  //! @}
101  //! \name boost::asio accessor methods
102  //! @{
103 
105 
106  const next_layer_type& next_layer() const { return m_nextLayer; }
108 
109 #if BOOST_VERSION >= 107000
110  /*
111  * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
112  * Instead, the new free-standing functions in Beast need to be used.
113  * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
114  */
115  using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
116 
117  lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
118  const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
119 #else
120  using lowest_layer_type = typename next_layer_type::lowest_layer_type;
121 
122  lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
123  const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
124 #endif
125 
126  using executor_type = typename next_layer_type::executor_type;
127  executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
128 
131  {
132  if(m_native_handle == nullptr)
133  { throw Invalid_State("Invalid handshake state"); }
134  return m_native_handle.get();
135  }
136 
137  //! @}
138  //! \name configuration and callback setters
139  //! @{
140 
141  /**
142  * @brief Override the tls_verify_cert_chain callback
143  *
144  * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
145  * used in the handshake.
146  * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
147  *
148  * @note This function should only be called before initiating the TLS handshake
149  */
151  {
152  m_context.set_verify_callback(std::move(callback));
153  }
154 
155  /**
156  * @brief Compatibility overload of @ref set_verify_callback
157  *
158  * @param callback the callback implementation
159  * @param ec This parameter is unused.
160  */
161  void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec)
162  {
163  BOTAN_UNUSED(ec);
164  m_context.set_verify_callback(std::move(callback));
165  }
166 
167  //! @throws Not_Implemented
168  void set_verify_depth(int depth)
169  {
170  BOTAN_UNUSED(depth);
171  throw Not_Implemented("set_verify_depth is not implemented");
172  }
173 
174  /**
175  * Not Implemented.
176  * @param depth the desired verification depth
177  * @param ec Will be set to `Botan::ErrorType::NotImplemented`
178  */
179  void set_verify_depth(int depth, boost::system::error_code& ec)
180  {
181  BOTAN_UNUSED(depth);
183  }
184 
185  //! @throws Not_Implemented
186  template <typename verify_mode>
187  void set_verify_mode(verify_mode v)
188  {
189  BOTAN_UNUSED(v);
190  throw Not_Implemented("set_verify_mode is not implemented");
191  }
192 
193  /**
194  * Not Implemented.
195  * @param v the desired verify mode
196  * @param ec Will be set to `Botan::ErrorType::NotImplemented`
197  */
198  template <typename verify_mode>
199  void set_verify_mode(verify_mode v, boost::system::error_code& ec)
200  {
201  BOTAN_UNUSED(v);
203  }
204 
205  //! @}
206  //! \name handshake methods
207  //! @{
208 
209  /**
210  * @brief Performs SSL handshaking.
211  *
212  * The function call will block until handshaking is complete or an error occurs.
213  *
214  * @param side The type of handshaking to be performed, i.e. as a client or as a server.
215  * @throws boost::system::system_error if error occured
216  */
218  {
219  boost::system::error_code ec;
220  handshake(side, ec);
221  boost::asio::detail::throw_error(ec, "handshake");
222  }
223 
224  /**
225  * @brief Performs SSL handshaking.
226  *
227  * The function call will block until handshaking is complete or an error occurs.
228  *
229  * @param side The type of handshaking to be performed, i.e. as a client or as a server.
230  * @param ec Set to indicate what error occurred, if any.
231  */
232  void handshake(Connection_Side side, boost::system::error_code& ec)
233  {
234  setup_native_handle(side, ec);
235 
236  if(side == CLIENT)
237  {
238  // send client hello, which was written to the send buffer on client instantiation
240  }
241 
242  while(!native_handle()->is_active() && !ec)
243  {
244  boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
245  if(ec)
246  { return; }
247 
248  process_encrypted_data(read_buffer, ec);
249 
251  }
252  }
253 
254  /**
255  * @brief Starts an asynchronous SSL handshake.
256  *
257  * This function call always returns immediately.
258  *
259  * @param side The type of handshaking to be performed, i.e. as a client or as a server.
260  * @param handler The handler to be called when the handshake operation completes.
261  * The equivalent function signature of the handler must be: void(boost::system::error_code)
262  */
263  template <typename HandshakeHandler>
264  auto async_handshake(Connection_Side side, HandshakeHandler&& handler) ->
265  BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, void(boost::system::error_code))
266  {
267  BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check;
268 
269  boost::system::error_code ec;
270  setup_native_handle(side, ec);
271  // If ec is set by setup_native_handle, the AsyncHandshakeOperation created below will do nothing but call the
272  // handler with the error_code set appropriately - no need to early return here.
273 
274  boost::asio::async_completion<HandshakeHandler, void(boost::system::error_code)> init(handler);
275 
277  op{std::move(init.completion_handler), *this, ec};
278 
279  return init.result.get();
280  }
281 
282  //! @throws Not_Implemented
283  template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
284  BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
285  void(boost::system::error_code, std::size_t))
286  async_handshake(Connection_Side side, const ConstBufferSequence& buffers,
287  BufferedHandshakeHandler&& handler)
288  {
289  BOTAN_UNUSED(side, buffers, handler);
290  BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
291  throw Not_Implemented("buffered async handshake is not implemented");
292  }
293 
294  //! @}
295  //! \name shutdown methods
296  //! @{
297 
298  /**
299  * @brief Shut down SSL on the stream.
300  *
301  * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
302  * or an error occurs. Note that this will not close the lowest layer.
303  *
304  * Note that this can be used in reaction of a received shutdown alert from the peer.
305  *
306  * @param ec Set to indicate what error occured, if any.
307  */
308  void shutdown(boost::system::error_code& ec)
309  {
311  {
312  native_handle()->close();
313  }, ec);
314 
316  }
317 
318  /**
319  * @brief Shut down SSL on the stream.
320  *
321  * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
322  * or an error occurs. Note that this will not close the lowest layer.
323  *
324  * Note that this can be used in reaction of a received shutdown alert from the peer.
325  *
326  * @throws boost::system::system_error if error occured
327  */
328  void shutdown()
329  {
330  boost::system::error_code ec;
331  shutdown(ec);
332  boost::asio::detail::throw_error(ec, "shutdown");
333  }
334 
335  private:
336  /**
337  * @brief Internal wrapper type to adapt the expected signature of `async_shutdown`
338  * to the completion handler signature of `AsyncWriteOperation`.
339  *
340  * This is boilerplate to ignore the `size_t` parameter that is passed to the
341  * completion handler of `AsyncWriteOperation`.
342  *
343  * @todo in C++14 and above this could be implemented as a mutable lambda expression
344  * that captures `handler` by perfect forwarding, like so:
345  *
346  * [h = std::forward<Handler>(handler)] (...) mutable { return h(ec); }
347  */
348  template <typename Handler>
349  class Wrapper
350  {
351  public:
352  Wrapper(Handler&& handler) : _handler(std::forward<Handler>(handler)) {}
353  void operator()(boost::system::error_code ec, size_t)
354  {
355  _handler(ec);
356  }
357  private:
358  Handler _handler;
359  };
360 
361  public:
362  /**
363  * @brief Asynchronously shut down SSL on the stream.
364  *
365  * This function call always returns immediately.
366  *
367  * Note that this can be used in reaction of a received shutdown alert from the peer.
368  *
369  * @param handler The handler to be called when the shutdown operation completes.
370  * The equivalent function signature of the handler must be: void(boost::system::error_code)
371  */
372  template <typename ShutdownHandler>
373  void async_shutdown(ShutdownHandler&& handler)
374  {
375  boost::system::error_code ec;
377  {
378  native_handle()->close();
379  }, ec);
380  // If ec is set by native_handle->close(), the AsyncWriteOperation created below will do nothing but call the
381  // handler with the error_code set appropriately - no need to early return here.
382 
383  using ShutdownHandlerWrapper = Wrapper<ShutdownHandler>;
384 
385  ShutdownHandlerWrapper w(std::forward<ShutdownHandler>(handler));
386  BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, w) type_check;
387 
388  boost::asio::async_completion<ShutdownHandlerWrapper, void(boost::system::error_code, std::size_t)>
389  init(w);
390 
392  op{std::move(init.completion_handler), *this, boost::asio::buffer_size(send_buffer())};
393 
394  return init.result.get();
395  }
396 
397  //! @}
398  //! \name I/O methods
399  //! @{
400 
401  /**
402  * @brief Read some data from the stream.
403  *
404  * The function call will block until one or more bytes of data has been read successfully, or until an error
405  * occurs.
406  *
407  * @param buffers The buffers into which the data will be read.
408  * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
409  * has closed the connection but did not properly shut down the SSL connection.
410  * @return The number of bytes read. Returns 0 if an error occurred.
411  */
412  template <typename MutableBufferSequence>
413  std::size_t read_some(const MutableBufferSequence& buffers,
414  boost::system::error_code& ec)
415  {
416  if(has_received_data())
417  { return copy_received_data(buffers); }
418 
419  boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
420  if(ec)
421  { return 0; }
422 
423  process_encrypted_data(read_buffer, ec);
424 
425  if(ec) // something went wrong in process_encrypted_data()
426  { return 0; }
427 
428  if(shutdown_received())
429  {
430  // we just received a 'close_notify' from the peer and don't expect any more data
431  ec = boost::asio::error::eof;
432  }
433  else if(ec == boost::asio::error::eof)
434  {
435  // we did not expect this disconnection from the peer
437  }
438 
439  return !ec ? copy_received_data(buffers) : 0;
440  }
441 
442  /**
443  * @brief Read some data from the stream.
444  *
445  * The function call will block until one or more bytes of data has been read successfully, or until an error
446  * occurs.
447  *
448  * @param buffers The buffers into which the data will be read.
449  * @return The number of bytes read. Returns 0 if an error occurred.
450  * @throws boost::system::system_error if error occured
451  */
452  template <typename MutableBufferSequence>
453  std::size_t read_some(const MutableBufferSequence& buffers)
454  {
455  boost::system::error_code ec;
456  auto const n = read_some(buffers, ec);
457  boost::asio::detail::throw_error(ec, "read_some");
458  return n;
459  }
460 
461  /**
462  * @brief Write some data to the stream.
463  *
464  * The function call will block until one or more bytes of data has been written successfully, or until an error
465  * occurs.
466  *
467  * @param buffers The data to be written.
468  * @param ec Set to indicate what error occurred, if any.
469  * @return The number of bytes processed from the input buffers.
470  */
471  template <typename ConstBufferSequence>
472  std::size_t write_some(const ConstBufferSequence& buffers,
473  boost::system::error_code& ec)
474  {
475  tls_encrypt(buffers, ec);
477  return !ec ? boost::asio::buffer_size(buffers) : 0;
478  }
479 
480  /**
481  * @brief Write some data to the stream.
482  *
483  * The function call will block until one or more bytes of data has been written successfully, or until an error
484  * occurs.
485  *
486  * @param buffers The data to be written.
487  * @return The number of bytes written.
488  * @throws boost::system::system_error if error occured
489  */
490  template <typename ConstBufferSequence>
491  std::size_t write_some(const ConstBufferSequence& buffers)
492  {
493  boost::system::error_code ec;
494  auto const n = write_some(buffers, ec);
495  boost::asio::detail::throw_error(ec, "write_some");
496  return n;
497  }
498 
499  /**
500  * @brief Start an asynchronous write. The function call always returns immediately.
501  *
502  * @param buffers The data to be written.
503  * @param handler The handler to be called when the write operation completes. Copies will be made of the handler
504  * as required. The equivalent function signature of the handler must be:
505  * void(boost::system::error_code, std::size_t)
506  */
507  template <typename ConstBufferSequence, typename WriteHandler>
508  auto async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) ->
509  BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
510  void(boost::system::error_code, std::size_t))
511  {
512  BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
513 
514  boost::asio::async_completion<WriteHandler, void(boost::system::error_code, std::size_t)> init(handler);
515 
516  boost::system::error_code ec;
517  tls_encrypt(buffers, ec);
518  if(ec)
519  {
520  // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
521  // AsyncWriteOperation call the handler with the error_code set
524  op{std::move(init.completion_handler), *this, std::size_t(0), ec};
525  return init.result.get();
526  }
527 
529  op{std::move(init.completion_handler), *this, boost::asio::buffer_size(buffers)};
530 
531  return init.result.get();
532  }
533 
534  /**
535  * @brief Start an asynchronous read. The function call always returns immediately.
536  *
537  * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
538  * necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
539  * that they remain valid until the handler is called.
540  * @param handler The handler to be called when the read operation completes. The equivalent function signature of
541  * the handler must be:
542  * void(boost::system::error_code, std::size_t)
543  */
544  template <typename MutableBufferSequence, typename ReadHandler>
545  auto async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) ->
546  BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
547  void(boost::system::error_code, std::size_t))
548  {
549  BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
550 
551  boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
552 
554  op{std::move(init.completion_handler), *this, buffers};
555  return init.result.get();
556  }
557 
558  //! @}
559 
560  //! @brief Indicates whether a close_notify alert has been received from the peer.
561  bool shutdown_received() const
562  {
563  return m_shutdown_received;
564  }
565 
566  protected:
567  template <class H, class S, class M, class A> friend class detail::AsyncReadOperation;
568  template <class H, class S, class A> friend class detail::AsyncWriteOperation;
569  template <class H, class S, class A> friend class detail::AsyncHandshakeOperation;
570 
571  /**
572  * @brief Helper class that implements Botan::TLS::Callbacks
573  *
574  * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
575  * functions triggered by the native_handle.
576  *
577  * @param receive_buffer reference to the buffer where decrypted data should be placed
578  * @param send_buffer reference to the buffer where encrypted data should be placed
579  */
581  {
582  public:
583  StreamCore(Stream& stream) : m_stream(stream) {}
584 
585  virtual ~StreamCore() = default;
586 
587  void tls_emit_data(const uint8_t data[], std::size_t size) override
588  {
589  m_stream.m_send_buffer.commit(
590  boost::asio::buffer_copy(m_stream.m_send_buffer.prepare(size), boost::asio::buffer(data, size))
591  );
592  }
593 
594  void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
595  {
596  m_stream.m_receive_buffer.commit(
597  boost::asio::buffer_copy(m_stream.m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
598  );
599  }
600 
601  void tls_alert(Botan::TLS::Alert alert) override
602  {
604  {
605  m_stream.set_shutdown_received();
606  // Channel::process_alert will automatically write the corresponding close_notify response to the
607  // send_buffer and close the native_handle after this function returns.
608  }
609  }
610 
611  std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
612  {
613  return std::chrono::milliseconds(1000);
614  }
615 
617  {
618  // TODO: it should be possible to configure this in the using application (via callback?)
619  return true;
620  }
621 
623  const std::vector<X509_Certificate>& cert_chain,
624  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
625  const std::vector<Certificate_Store*>& trusted_roots,
626  Usage_Type usage,
627  const std::string& hostname,
628  const TLS::Policy& policy) override
629  {
630  if(m_stream.m_context.has_verify_callback())
631  {
632  m_stream.m_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
633  }
634  else
635  {
636  Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
637  }
638  }
639 
640  private:
641  Stream& m_stream;
642  };
643 
644  const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
645  boost::asio::const_buffer send_buffer() const { return m_send_buffer.data(); }
646 
647  //! @brief Check if decrypted data is available in the receive buffer
648  bool has_received_data() const { return m_receive_buffer.size() > 0; }
649 
650  //! @brief Copy decrypted data into the user-provided buffer
651  template <typename MutableBufferSequence>
652  std::size_t copy_received_data(MutableBufferSequence buffers)
653  {
654  // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
655  // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
656  // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
657  // than the decrypted record, so this optimization might not be worth the additional complexity.
658  const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
659  m_receive_buffer.consume(copiedBytes);
660  return copiedBytes;
661  }
662 
663  //! @brief Check if encrypted data is available in the send buffer
664  bool has_data_to_send() const { return m_send_buffer.size() > 0; }
665 
666  //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
667  void consume_send_buffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
668 
669  // This is a helper construct to allow mocking the native_handle in test code. It is activated by explicitly
670  // specifying a (mocked) channel type template parameter when constructing the stream and does not attempt to
671  // instantiate the native_handle.
672  // Note: once we have C++17 we can achieve this much more elegantly using constexpr if.
673  template<class T = ChannelT>
674  typename std::enable_if<!std::is_same<Channel, T>::value>::type
675  setup_native_handle(Connection_Side, boost::system::error_code&) {}
676 
677  /**
678  * @brief Create the native handle.
679  *
680  * Depending on the desired connection side, this function will create a Botan::TLS::Client or a
681  * Botan::TLS::Server.
682  *
683  * @param side The desired connection side (client or server)
684  * @param ec Set to indicate what error occurred, if any.
685  */
686  template<class T = ChannelT>
687  typename std::enable_if<std::is_same<Channel, T>::value>::type
688  setup_native_handle(Connection_Side side, boost::system::error_code& ec)
689  {
691  {
692  if(side == CLIENT)
693  {
694  m_native_handle = std::unique_ptr<Client>(
695  new Client(m_core,
702  }
703  else
704  {
705  m_native_handle = std::unique_ptr<Server>(
706  new Server(m_core,
711  false /* no DTLS */));
712  }
713  }, ec);
714  }
715 
716  /** @brief Synchronously write encrypted data from the send buffer to the next layer.
717  *
718  * If this function is called with an error code other than 'Success', it will do nothing and return 0.
719  *
720  * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
721  * has closed the connection but did not properly shut down the SSL connection.
722  * @return The number of bytes written.
723  */
724  size_t send_pending_encrypted_data(boost::system::error_code& ec)
725  {
726  if(ec)
727  { return 0; }
728 
729  auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
730  consume_send_buffer(writtenBytes);
731 
732  if(ec == boost::asio::error::eof && !shutdown_received())
733  {
734  // transport layer was closed by peer without receiving 'close_notify'
736  }
737 
738  return writtenBytes;
739  }
740 
741  /**
742  * @brief Pass plaintext data to the native handle for processing.
743  *
744  * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
745  */
746  template <typename ConstBufferSequence>
747  void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec)
748  {
749  // NOTE: This is not asynchronous: it encrypts the data synchronously.
750  // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
751  // but is not actually written to the wire, yet.
752  for(auto it = boost::asio::buffer_sequence_begin(buffers);
753  !ec && it != boost::asio::buffer_sequence_end(buffers);
754  it++)
755  {
756  const boost::asio::const_buffer buffer = *it;
758  {
759  native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size());
760  }, ec);
761  }
762  }
763 
764  /**
765  * @brief Pass encrypted data to the native handle for processing.
766  *
767  * If an exception occurs while processing the data, an error code will be set.
768  *
769  * @param read_buffer Input buffer containing the encrypted data.
770  * @param ec Set to indicate what error occurred, if any.
771  */
772  void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
773  {
775  {
776  native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
777  }, ec);
778  }
779 
780  //! @brief Catch exceptions and set an error_code
781  template <typename Fun>
782  void try_with_error_code(Fun f, boost::system::error_code& ec)
783  {
784  try
785  {
786  f();
787  }
788  catch(const TLS_Exception& e)
789  {
790  ec = e.type();
791  }
792  catch(const Botan::Exception& e)
793  {
794  ec = e.error_type();
795  }
796  catch(const std::exception&)
797  {
799  }
800  }
801 
803  {
804  m_shutdown_received = true;
805  }
806 
808  StreamLayer m_nextLayer;
809 
810  boost::beast::flat_buffer m_receive_buffer;
811  boost::beast::flat_buffer m_send_buffer;
812 
814  std::unique_ptr<ChannelT> m_native_handle;
815 
817 
818  // Buffer space used to read input intended for the core
819  std::vector<uint8_t> m_input_buffer_space;
820  const boost::asio::mutable_buffer m_input_buffer;
821  };
822 
823 } // namespace TLS
824 } // namespace Botan
825 
826 #endif // BOOST_VERSION
827 #endif // BOTAN_ASIO_STREAM_H_
void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::shared_ptr< const OCSP::Response >> &ocsp_responses, const std::vector< Certificate_Store *> &trusted_roots, Usage_Type usage, const std::string &hostname, const TLS::Policy &policy) override
Definition: asio_stream.h:622
RandomNumberGenerator & m_rng
Definition: asio_context.h:108
void shutdown(boost::system::error_code &ec)
Shut down SSL on the stream.
Definition: asio_stream.h:308
size_t send_pending_encrypted_data(boost::system::error_code &ec)
Synchronously write encrypted data from the send buffer to the next layer.
Definition: asio_stream.h:724
void set_shutdown_received()
Definition: asio_stream.h:802
std::enable_if<!std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side, boost::system::error_code &)
Definition: asio_stream.h:675
void set_verify_mode(verify_mode v, boost::system::error_code &ec)
Definition: asio_stream.h:199
void set_verify_callback(Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_context.h:84
BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check
const ConstBufferSequence & buffers
Definition: asio_stream.h:286
Type type() const
Definition: tls_alert.h:80
bool shutdown_received() const
Indicates whether a close_notify alert has been received from the peer.
Definition: asio_stream.h:561
const boost::asio::mutable_buffer m_input_buffer
Definition: asio_stream.h:820
Helper class that implements Botan::TLS::Callbacks.
Definition: asio_stream.h:580
virtual ErrorType error_type() const noexcept
Definition: exceptn.h:101
void set_verify_depth(int depth)
Definition: asio_stream.h:168
auto async_handshake(Connection_Side side, HandshakeHandler &&handler) ->
Starts an asynchronous SSL handshake.
Definition: asio_stream.h:264
std::enable_if< std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side side, boost::system::error_code &ec)
Create the native handle.
Definition: asio_stream.h:688
throw Not_Implemented("buffered async handshake is not implemented")
typename std::remove_reference< StreamLayer >::type next_layer_type
Definition: asio_stream.h:104
Definition: bigint.h:1142
void set_verify_callback(Context::Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_stream.h:150
static Protocol_Version latest_tls_version()
Definition: tls_version.h:36
StreamLayer m_nextLayer
Definition: asio_stream.h:808
std::size_t write_some(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Write some data to the stream.
Definition: asio_stream.h:472
MechanismType type
auto async_read_some(const MutableBufferSequence &buffers, ReadHandler &&handler) -> BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void(boost::system::error_code, std::size_t))
Start an asynchronous read. The function call always returns immediately.
Definition: asio_stream.h:545
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
Definition: asio_stream.h:611
void async_shutdown(ShutdownHandler &&handler)
Asynchronously shut down SSL on the stream.
Definition: asio_stream.h:373
Credentials_Manager & m_credentials_manager
Definition: asio_context.h:107
void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:594
typename next_layer_type::executor_type executor_type
Definition: asio_stream.h:126
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.
Definition: asio_stream.h:772
bool has_data_to_send() const
Check if encrypted data is available in the send buffer.
Definition: asio_stream.h:664
void handshake(Connection_Side side, boost::system::error_code &ec)
Performs SSL handshaking.
Definition: asio_stream.h:232
Stream & operator=(Stream &&other)=default
boost::asio::const_buffer send_buffer() const
Definition: asio_stream.h:645
void tls_emit_data(const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:587
StreamCore m_core
Definition: asio_stream.h:813
Context & m_context
Definition: asio_stream.h:807
Server_Information m_server_info
Definition: asio_context.h:112
virtual ~Stream()=default
const boost::asio::mutable_buffer & input_buffer()
Definition: asio_stream.h:644
std::size_t write_some(const ConstBufferSequence &buffers)
Write some data to the stream.
Definition: asio_stream.h:491
std::size_t read_some(const MutableBufferSequence &buffers, boost::system::error_code &ec)
Read some data from the stream.
Definition: asio_stream.h:413
next_layer_type & next_layer()
Definition: asio_stream.h:107
const lowest_layer_type & lowest_layer() const
Definition: asio_stream.h:123
const ConstBufferSequence BufferedHandshakeHandler && handler
Definition: asio_stream.h:288
lowest_layer_type & lowest_layer()
Definition: asio_stream.h:122
std::size_t copy_received_data(MutableBufferSequence buffers)
Copy decrypted data into the user-provided buffer.
Definition: asio_stream.h:652
bool has_verify_callback() const
Definition: asio_context.h:89
native_handle_type native_handle()
Definition: asio_stream.h:130
bool tls_session_established(const Botan::TLS::Session &) override
Definition: asio_stream.h:616
executor_type get_executor() noexcept
Definition: asio_stream.h:127
auto async_write_some(const ConstBufferSequence &buffers, WriteHandler &&handler) -> BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, void(boost::system::error_code, std::size_t))
Start an asynchronous write. The function call always returns immediately.
Definition: asio_stream.h:508
virtual void tls_verify_cert_chain(const std::vector< X509_Certificate > &cert_chain, const std::vector< std::shared_ptr< const OCSP::Response >> &ocsp_responses, const std::vector< Certificate_Store *> &trusted_roots, Usage_Type usage, const std::string &hostname, const TLS::Policy &policy)
boost::asio compatible SSL/TLS stream
Definition: asio_stream.h:48
bool has_received_data() const
Check if decrypted data is available in the receive buffer.
Definition: asio_stream.h:648
boost::beast::flat_buffer m_send_buffer
Definition: asio_stream.h:811
Definition: alg_id.cpp:13
std::size_t read_some(const MutableBufferSequence &buffers)
Read some data from the stream.
Definition: asio_stream.h:453
void set_verify_depth(int depth, boost::system::error_code &ec)
Definition: asio_stream.h:179
#define BOTAN_UNUSED(...)
Definition: assert.h:142
Alert::Type type() const
Definition: tls_exceptn.h:24
void tls_encrypt(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Pass plaintext data to the native handle for processing.
Definition: asio_stream.h:747
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t)) async_handshake(Connection_Side side
void handshake(Connection_Side side)
Performs SSL handshaking.
Definition: asio_stream.h:217
int(* init)(CTX *)
virtual ~StreamCore()=default
void shutdown()
Shut down SSL on the stream.
Definition: asio_stream.h:328
void tls_alert(Botan::TLS::Alert alert) override
Definition: asio_stream.h:601
void set_verify_mode(verify_mode v)
Definition: asio_stream.h:187
std::vector< uint8_t > m_input_buffer_space
Definition: asio_stream.h:819
Stream(Context &context, Args &&... args)
Construct a new Stream.
Definition: asio_stream.h:63
Session_Manager & m_session_manager
Definition: asio_context.h:109
typename std::add_pointer< ChannelT >::type native_handle_type
Definition: asio_stream.h:129
typename next_layer_type::lowest_layer_type lowest_layer_type
Definition: asio_stream.h:120
const Verify_Callback & get_verify_callback() const
Definition: asio_context.h:94
void consume_send_buffer(std::size_t bytesConsumed)
Mark bytes in the send buffer as consumed, removing them from the buffer.
Definition: asio_stream.h:667
void try_with_error_code(Fun f, boost::system::error_code &ec)
Catch exceptions and set an error_code.
Definition: asio_stream.h:782
std::unique_ptr< ChannelT > m_native_handle
Definition: asio_stream.h:814
detail::fn_signature_helper< decltype(&Callbacks::tls_verify_cert_chain)>::type Verify_Callback
Definition: asio_context.h:54
Usage_Type
Definition: x509cert.h:25
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code &ec)
Compatibility overload of set_verify_callback.
Definition: asio_stream.h:161
const next_layer_type & next_layer() const
Definition: asio_stream.h:106
boost::beast::flat_buffer m_receive_buffer
Definition: asio_stream.h:810
Stream(Arg &&arg, Context &context)
Construct a new Stream.
Definition: asio_stream.h:83