Botan  2.19.1
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` to the completion handler
338  * signature of `AsyncWriteOperation`.
339  *
340  * This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
341  * `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
342  */
343  template <typename Handler, typename Executor>
344  struct Wrapper
345  {
346  void operator()(boost::system::error_code ec, std::size_t)
347  {
348  handler(ec);
349  }
350 
351  using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
352 
353  executor_type get_executor() const noexcept
354  {
355  return boost::asio::get_associated_executor(handler, io_executor);
356  }
357 
358  using allocator_type = boost::asio::associated_allocator_t<Handler>;
359 
360  allocator_type get_allocator() const noexcept
361  {
362  return boost::asio::get_associated_allocator(handler);
363  }
364 
365  Handler handler;
366  Executor io_executor;
367  };
368 
369  public:
370  /**
371  * @brief Asynchronously shut down SSL on the stream.
372  *
373  * This function call always returns immediately.
374  *
375  * Note that this can be used in reaction of a received shutdown alert from the peer.
376  *
377  * @param handler The handler to be called when the shutdown operation completes.
378  * The equivalent function signature of the handler must be: void(boost::system::error_code)
379  */
380  template <typename ShutdownHandler>
381  void async_shutdown(ShutdownHandler&& handler)
382  {
383  boost::system::error_code ec;
385  {
386  native_handle()->close();
387  }, ec);
388  // If ec is set by native_handle->close(), the AsyncWriteOperation created below will do nothing but call the
389  // handler with the error_code set appropriately - no need to early return here.
390 
391  using ShutdownHandlerWrapper = Wrapper<ShutdownHandler, typename Stream::executor_type>;
392 
393  ShutdownHandlerWrapper w{std::forward<ShutdownHandler>(handler), get_executor()};
394  BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, w) type_check;
395 
396  boost::asio::async_completion<ShutdownHandlerWrapper, void(boost::system::error_code, std::size_t)>
397  init(w);
398 
400  op{std::move(init.completion_handler), *this, boost::asio::buffer_size(send_buffer())};
401 
402  return init.result.get();
403  }
404 
405  //! @}
406  //! \name I/O methods
407  //! @{
408 
409  /**
410  * @brief Read some data from the stream.
411  *
412  * The function call will block until one or more bytes of data has been read successfully, or until an error
413  * occurs.
414  *
415  * @param buffers The buffers into which the data will be read.
416  * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
417  * has closed the connection but did not properly shut down the SSL connection.
418  * @return The number of bytes read. Returns 0 if an error occurred.
419  */
420  template <typename MutableBufferSequence>
421  std::size_t read_some(const MutableBufferSequence& buffers,
422  boost::system::error_code& ec)
423  {
424  if(has_received_data())
425  { return copy_received_data(buffers); }
426 
427  boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
428  if(ec)
429  { return 0; }
430 
431  process_encrypted_data(read_buffer, ec);
432 
433  if(ec) // something went wrong in process_encrypted_data()
434  { return 0; }
435 
436  if(shutdown_received())
437  {
438  // we just received a 'close_notify' from the peer and don't expect any more data
439  ec = boost::asio::error::eof;
440  }
441  else if(ec == boost::asio::error::eof)
442  {
443  // we did not expect this disconnection from the peer
445  }
446 
447  return !ec ? copy_received_data(buffers) : 0;
448  }
449 
450  /**
451  * @brief Read some data from the stream.
452  *
453  * The function call will block until one or more bytes of data has been read successfully, or until an error
454  * occurs.
455  *
456  * @param buffers The buffers into which the data will be read.
457  * @return The number of bytes read. Returns 0 if an error occurred.
458  * @throws boost::system::system_error if error occured
459  */
460  template <typename MutableBufferSequence>
461  std::size_t read_some(const MutableBufferSequence& buffers)
462  {
463  boost::system::error_code ec;
464  auto const n = read_some(buffers, ec);
465  boost::asio::detail::throw_error(ec, "read_some");
466  return n;
467  }
468 
469  /**
470  * @brief Write some data to the stream.
471  *
472  * The function call will block until one or more bytes of data has been written successfully, or until an error
473  * occurs.
474  *
475  * @param buffers The data to be written.
476  * @param ec Set to indicate what error occurred, if any.
477  * @return The number of bytes processed from the input buffers.
478  */
479  template <typename ConstBufferSequence>
480  std::size_t write_some(const ConstBufferSequence& buffers,
481  boost::system::error_code& ec)
482  {
483  tls_encrypt(buffers, ec);
485  return !ec ? boost::asio::buffer_size(buffers) : 0;
486  }
487 
488  /**
489  * @brief Write some data to the stream.
490  *
491  * The function call will block until one or more bytes of data has been written successfully, or until an error
492  * occurs.
493  *
494  * @param buffers The data to be written.
495  * @return The number of bytes written.
496  * @throws boost::system::system_error if error occured
497  */
498  template <typename ConstBufferSequence>
499  std::size_t write_some(const ConstBufferSequence& buffers)
500  {
501  boost::system::error_code ec;
502  auto const n = write_some(buffers, ec);
503  boost::asio::detail::throw_error(ec, "write_some");
504  return n;
505  }
506 
507  /**
508  * @brief Start an asynchronous write. The function call always returns immediately.
509  *
510  * @param buffers The data to be written.
511  * @param handler The handler to be called when the write operation completes. Copies will be made of the handler
512  * as required. The equivalent function signature of the handler must be:
513  * void(boost::system::error_code, std::size_t)
514  */
515  template <typename ConstBufferSequence, typename WriteHandler>
516  auto async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) ->
517  BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
518  void(boost::system::error_code, std::size_t))
519  {
520  BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
521 
522  boost::asio::async_completion<WriteHandler, void(boost::system::error_code, std::size_t)> init(handler);
523 
524  boost::system::error_code ec;
525  tls_encrypt(buffers, ec);
526  if(ec)
527  {
528  // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
529  // AsyncWriteOperation call the handler with the error_code set
532  op{std::move(init.completion_handler), *this, std::size_t(0), ec};
533  return init.result.get();
534  }
535 
537  op{std::move(init.completion_handler), *this, boost::asio::buffer_size(buffers)};
538 
539  return init.result.get();
540  }
541 
542  /**
543  * @brief Start an asynchronous read. The function call always returns immediately.
544  *
545  * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
546  * necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
547  * that they remain valid until the handler is called.
548  * @param handler The handler to be called when the read operation completes. The equivalent function signature of
549  * the handler must be:
550  * void(boost::system::error_code, std::size_t)
551  */
552  template <typename MutableBufferSequence, typename ReadHandler>
553  auto async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) ->
554  BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
555  void(boost::system::error_code, std::size_t))
556  {
557  BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
558 
559  boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
560 
562  op{std::move(init.completion_handler), *this, buffers};
563  return init.result.get();
564  }
565 
566  //! @}
567 
568  //! @brief Indicates whether a close_notify alert has been received from the peer.
569  bool shutdown_received() const
570  {
571  return m_shutdown_received;
572  }
573 
574  protected:
575  template <class H, class S, class M, class A> friend class detail::AsyncReadOperation;
576  template <class H, class S, class A> friend class detail::AsyncWriteOperation;
577  template <class H, class S, class A> friend class detail::AsyncHandshakeOperation;
578 
579  /**
580  * @brief Helper class that implements Botan::TLS::Callbacks
581  *
582  * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
583  * functions triggered by the native_handle.
584  *
585  * @param receive_buffer reference to the buffer where decrypted data should be placed
586  * @param send_buffer reference to the buffer where encrypted data should be placed
587  */
589  {
590  public:
591  StreamCore(Stream& stream) : m_stream(stream) {}
592 
593  virtual ~StreamCore() = default;
594 
595  void tls_emit_data(const uint8_t data[], std::size_t size) override
596  {
597  m_stream.m_send_buffer.commit(
598  boost::asio::buffer_copy(m_stream.m_send_buffer.prepare(size), boost::asio::buffer(data, size))
599  );
600  }
601 
602  void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
603  {
604  m_stream.m_receive_buffer.commit(
605  boost::asio::buffer_copy(m_stream.m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
606  );
607  }
608 
609  void tls_alert(Botan::TLS::Alert alert) override
610  {
612  {
613  m_stream.set_shutdown_received();
614  // Channel::process_alert will automatically write the corresponding close_notify response to the
615  // send_buffer and close the native_handle after this function returns.
616  }
617  }
618 
619  std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
620  {
621  return std::chrono::milliseconds(1000);
622  }
623 
625  {
626  // TODO: it should be possible to configure this in the using application (via callback?)
627  return true;
628  }
629 
631  const std::vector<X509_Certificate>& cert_chain,
632  const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
633  const std::vector<Certificate_Store*>& trusted_roots,
634  Usage_Type usage,
635  const std::string& hostname,
636  const TLS::Policy& policy) override
637  {
638  if(m_stream.m_context.has_verify_callback())
639  {
640  m_stream.m_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
641  }
642  else
643  {
644  Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
645  }
646  }
647 
648  private:
649  Stream& m_stream;
650  };
651 
652  const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
653  boost::asio::const_buffer send_buffer() const { return m_send_buffer.data(); }
654 
655  //! @brief Check if decrypted data is available in the receive buffer
656  bool has_received_data() const { return m_receive_buffer.size() > 0; }
657 
658  //! @brief Copy decrypted data into the user-provided buffer
659  template <typename MutableBufferSequence>
660  std::size_t copy_received_data(MutableBufferSequence buffers)
661  {
662  // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
663  // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
664  // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
665  // than the decrypted record, so this optimization might not be worth the additional complexity.
666  const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
667  m_receive_buffer.consume(copiedBytes);
668  return copiedBytes;
669  }
670 
671  //! @brief Check if encrypted data is available in the send buffer
672  bool has_data_to_send() const { return m_send_buffer.size() > 0; }
673 
674  //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
675  void consume_send_buffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
676 
677  // This is a helper construct to allow mocking the native_handle in test code. It is activated by explicitly
678  // specifying a (mocked) channel type template parameter when constructing the stream and does not attempt to
679  // instantiate the native_handle.
680  // Note: once we have C++17 we can achieve this much more elegantly using constexpr if.
681  template<class T = ChannelT>
682  typename std::enable_if<!std::is_same<Channel, T>::value>::type
683  setup_native_handle(Connection_Side, boost::system::error_code&) {}
684 
685  /**
686  * @brief Create the native handle.
687  *
688  * Depending on the desired connection side, this function will create a Botan::TLS::Client or a
689  * Botan::TLS::Server.
690  *
691  * @param side The desired connection side (client or server)
692  * @param ec Set to indicate what error occurred, if any.
693  */
694  template<class T = ChannelT>
695  typename std::enable_if<std::is_same<Channel, T>::value>::type
696  setup_native_handle(Connection_Side side, boost::system::error_code& ec)
697  {
699  {
700  if(side == CLIENT)
701  {
702  m_native_handle = std::unique_ptr<Client>(
703  new Client(m_core,
710  }
711  else
712  {
713  m_native_handle = std::unique_ptr<Server>(
714  new Server(m_core,
719  false /* no DTLS */));
720  }
721  }, ec);
722  }
723 
724  /** @brief Synchronously write encrypted data from the send buffer to the next layer.
725  *
726  * If this function is called with an error code other than 'Success', it will do nothing and return 0.
727  *
728  * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
729  * has closed the connection but did not properly shut down the SSL connection.
730  * @return The number of bytes written.
731  */
732  size_t send_pending_encrypted_data(boost::system::error_code& ec)
733  {
734  if(ec)
735  { return 0; }
736 
737  auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
738  consume_send_buffer(writtenBytes);
739 
740  if(ec == boost::asio::error::eof && !shutdown_received())
741  {
742  // transport layer was closed by peer without receiving 'close_notify'
744  }
745 
746  return writtenBytes;
747  }
748 
749  /**
750  * @brief Pass plaintext data to the native handle for processing.
751  *
752  * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
753  */
754  template <typename ConstBufferSequence>
755  void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec)
756  {
757  // NOTE: This is not asynchronous: it encrypts the data synchronously.
758  // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
759  // but is not actually written to the wire, yet.
760  for(auto it = boost::asio::buffer_sequence_begin(buffers);
761  !ec && it != boost::asio::buffer_sequence_end(buffers);
762  it++)
763  {
764  const boost::asio::const_buffer buffer = *it;
766  {
767  native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size());
768  }, ec);
769  }
770  }
771 
772  /**
773  * @brief Pass encrypted data to the native handle for processing.
774  *
775  * If an exception occurs while processing the data, an error code will be set.
776  *
777  * @param read_buffer Input buffer containing the encrypted data.
778  * @param ec Set to indicate what error occurred, if any.
779  */
780  void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
781  {
783  {
784  native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
785  }, ec);
786  }
787 
788  //! @brief Catch exceptions and set an error_code
789  template <typename Fun>
790  void try_with_error_code(Fun f, boost::system::error_code& ec)
791  {
792  try
793  {
794  f();
795  }
796  catch(const TLS_Exception& e)
797  {
798  ec = e.type();
799  }
800  catch(const Botan::Exception& e)
801  {
802  ec = e.error_type();
803  }
804  catch(const std::exception&)
805  {
807  }
808  }
809 
811  {
812  m_shutdown_received = true;
813  }
814 
816  StreamLayer m_nextLayer;
817 
818  boost::beast::flat_buffer m_receive_buffer;
819  boost::beast::flat_buffer m_send_buffer;
820 
822  std::unique_ptr<ChannelT> m_native_handle;
823 
825 
826  // Buffer space used to read input intended for the core
827  std::vector<uint8_t> m_input_buffer_space;
828  const boost::asio::mutable_buffer m_input_buffer;
829  };
830 
831 } // namespace TLS
832 } // namespace Botan
833 
834 #endif // BOOST_VERSION
835 #endif // BOTAN_ASIO_STREAM_H_
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:732
void set_shutdown_received()
Definition: asio_stream.h:810
std::enable_if<!std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side, boost::system::error_code &)
Definition: asio_stream.h:683
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
const next_layer_type & next_layer() const
Definition: asio_stream.h:106
BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check
const ConstBufferSequence & buffers
Definition: asio_stream.h:286
const boost::asio::mutable_buffer m_input_buffer
Definition: asio_stream.h:828
Helper class that implements Botan::TLS::Callbacks.
Definition: asio_stream.h:588
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:696
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:1143
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:816
std::size_t write_some(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Write some data to the stream.
Definition: asio_stream.h:480
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:553
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)
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
Definition: asio_stream.h:619
void async_shutdown(ShutdownHandler &&handler)
Asynchronously shut down SSL on the stream.
Definition: asio_stream.h:381
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:602
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:780
void handshake(Connection_Side side, boost::system::error_code &ec)
Performs SSL handshaking.
Definition: asio_stream.h:232
Stream & operator=(Stream &&other)=default
void tls_emit_data(const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:595
StreamCore m_core
Definition: asio_stream.h:821
Context & m_context
Definition: asio_stream.h:815
Server_Information m_server_info
Definition: asio_context.h:112
virtual ~Stream()=default
const boost::asio::mutable_buffer & input_buffer()
Definition: asio_stream.h:652
std::size_t write_some(const ConstBufferSequence &buffers)
Write some data to the stream.
Definition: asio_stream.h:499
std::size_t read_some(const MutableBufferSequence &buffers, boost::system::error_code &ec)
Read some data from the stream.
Definition: asio_stream.h:421
next_layer_type & next_layer()
Definition: asio_stream.h:107
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:660
native_handle_type native_handle()
Definition: asio_stream.h:130
bool tls_session_established(const Botan::TLS::Session &) override
Definition: asio_stream.h:624
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:516
boost::asio compatible SSL/TLS stream
Definition: asio_stream.h:48
boost::beast::flat_buffer m_send_buffer
Definition: asio_stream.h:819
Definition: alg_id.cpp:13
std::size_t read_some(const MutableBufferSequence &buffers)
Read some data from the stream.
Definition: asio_stream.h:461
void set_verify_depth(int depth, boost::system::error_code &ec)
Definition: asio_stream.h:179
boost::asio::const_buffer send_buffer() const
Definition: asio_stream.h:653
#define BOTAN_UNUSED(...)
Definition: assert.h:142
void tls_encrypt(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Pass plaintext data to the native handle for processing.
Definition: asio_stream.h:755
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
const lowest_layer_type & lowest_layer() const
Definition: asio_stream.h:123
const Verify_Callback & get_verify_callback() const
Definition: asio_context.h:94
bool shutdown_received() const
Indicates whether a close_notify alert has been received from the peer.
Definition: asio_stream.h:569
bool has_data_to_send() const
Check if encrypted data is available in the send buffer.
Definition: asio_stream.h:672
int(* init)(CTX *)
bool has_verify_callback() const
Definition: asio_context.h:89
virtual ~StreamCore()=default
Type type() const
Definition: tls_alert.h:80
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:609
void set_verify_mode(verify_mode v)
Definition: asio_stream.h:187
std::vector< uint8_t > m_input_buffer_space
Definition: asio_stream.h:827
Session_Manager & m_session_manager
Definition: asio_context.h:109
bool has_received_data() const
Check if decrypted data is available in the receive buffer.
Definition: asio_stream.h:656
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
Stream(Context &context, Args &&...args)
Construct a new Stream.
Definition: asio_stream.h:63
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:675
Alert::Type type() const
Definition: tls_exceptn.h:24
void try_with_error_code(Fun f, boost::system::error_code &ec)
Catch exceptions and set an error_code.
Definition: asio_stream.h:790
std::unique_ptr< ChannelT > m_native_handle
Definition: asio_stream.h:822
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:630
detail::fn_signature_helper< decltype(&Callbacks::tls_verify_cert_chain)>::type Verify_Callback
Definition: asio_context.h:54
Usage_Type
Definition: x509cert.h:22
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code &ec)
Compatibility overload of set_verify_callback.
Definition: asio_stream.h:161
boost::beast::flat_buffer m_receive_buffer
Definition: asio_stream.h:818
Stream(Arg &&arg, Context &context)
Construct a new Stream.
Definition: asio_stream.h:83