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>
Stream(Context & context,Args &&...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)
68          , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
69          , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
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>
Stream(Arg && arg,Context & context)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)
88          , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
89          , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
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 
104       using next_layer_type = typename std::remove_reference<StreamLayer>::type;
105 
next_layer()106       const next_layer_type& next_layer() const { return m_nextLayer; }
next_layer()107       next_layer_type& next_layer() { 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 
lowest_layer()117       lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
lowest_layer()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 
lowest_layer()122       lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
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;
get_executor()127       executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
128 
129       using native_handle_type = typename std::add_pointer<ChannelT>::type;
native_handle()130       native_handle_type native_handle()
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        */
set_verify_callback(Context::Verify_Callback callback)150       void set_verify_callback(Context::Verify_Callback callback)
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        */
set_verify_callback(Context::Verify_Callback callback,boost::system::error_code & ec)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
set_verify_depth(int depth)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        */
set_verify_depth(int depth,boost::system::error_code & ec)179       void set_verify_depth(int depth, boost::system::error_code& ec)
180          {
181          BOTAN_UNUSED(depth);
182          ec = Botan::ErrorType::NotImplemented;
183          }
184 
185       //! @throws Not_Implemented
186       template <typename verify_mode>
set_verify_mode(verify_mode v)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>
set_verify_mode(verify_mode v,boost::system::error_code & ec)199       void set_verify_mode(verify_mode v, boost::system::error_code& ec)
200          {
201          BOTAN_UNUSED(v);
202          ec = Botan::ErrorType::NotImplemented;
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        */
handshake(Connection_Side side)217       void handshake(Connection_Side side)
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        */
handshake(Connection_Side side,boost::system::error_code & ec)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
239             send_pending_encrypted_data(ec);
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 
250             send_pending_encrypted_data(ec);
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 
276          detail::AsyncHandshakeOperation<typename std::decay<HandshakeHandler>::type, Stream>
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>
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,void (boost::system::error_code,std::size_t))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        */
shutdown(boost::system::error_code & ec)308       void shutdown(boost::system::error_code& ec)
309          {
310          try_with_error_code([&]
311             {
312             native_handle()->close();
313             }, ec);
314 
315          send_pending_encrypted_data(ec);
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        */
shutdown()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          {
operatorWrapper346          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 
get_executorWrapper353          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 
get_allocatorWrapper360          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>
async_shutdown(ShutdownHandler && handler)381       void async_shutdown(ShutdownHandler&& handler)
382          {
383          boost::system::error_code ec;
384          try_with_error_code([&]
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 
399          detail::AsyncWriteOperation<typename std::decay<ShutdownHandlerWrapper>::type, Stream>
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>
read_some(const MutableBufferSequence & buffers,boost::system::error_code & ec)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
444             ec = StreamError::StreamTruncated;
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>
read_some(const MutableBufferSequence & buffers)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>
write_some(const ConstBufferSequence & buffers,boost::system::error_code & ec)480       std::size_t write_some(const ConstBufferSequence& buffers,
481                              boost::system::error_code& ec)
482          {
483          tls_encrypt(buffers, ec);
484          send_pending_encrypted_data(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>
write_some(const ConstBufferSequence & buffers)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
530             consume_send_buffer(m_send_buffer.size());
531             detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
532             op{std::move(init.completion_handler), *this, std::size_t(0), ec};
533             return init.result.get();
534             }
535 
536          detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
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 
561          detail::AsyncReadOperation<typename std::decay<ReadHandler>::type, Stream, MutableBufferSequence>
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.
shutdown_received()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        */
588       class StreamCore : public Botan::TLS::Callbacks
589          {
590          public:
StreamCore(Stream & stream)591             StreamCore(Stream& stream) : m_stream(stream) {}
592 
593             virtual ~StreamCore() = default;
594 
tls_emit_data(const uint8_t data[],std::size_t size)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 
tls_record_received(uint64_t,const uint8_t data[],std::size_t size)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 
tls_alert(Botan::TLS::Alert alert)609             void tls_alert(Botan::TLS::Alert alert) override
610                {
611                if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
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 
tls_verify_cert_chain_ocsp_timeout()619             std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
620                {
621                return std::chrono::milliseconds(1000);
622                }
623 
tls_session_established(const Botan::TLS::Session &)624             bool tls_session_established(const Botan::TLS::Session&) override
625                {
626                // TODO: it should be possible to configure this in the using application (via callback?)
627                return true;
628                }
629 
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)630             void tls_verify_cert_chain(
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 
input_buffer()652       const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
send_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
has_received_data()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>
copy_received_data(MutableBufferSequence buffers)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
has_data_to_send()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
consume_send_buffer(std::size_t bytesConsumed)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
setup_native_handle(Connection_Side,boost::system::error_code &)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
setup_native_handle(Connection_Side side,boost::system::error_code & ec)696       setup_native_handle(Connection_Side side, boost::system::error_code& ec)
697          {
698          try_with_error_code([&]
699             {
700             if(side == CLIENT)
701                {
702                m_native_handle = std::unique_ptr<Client>(
703                   new Client(m_core,
704                              m_context.m_session_manager,
705                              m_context.m_credentials_manager,
706                              m_context.m_policy,
707                              m_context.m_rng,
708                              m_context.m_server_info,
709                              Protocol_Version::latest_tls_version()));
710                }
711             else
712                {
713                m_native_handle = std::unique_ptr<Server>(
714                   new Server(m_core,
715                              m_context.m_session_manager,
716                              m_context.m_credentials_manager,
717                              m_context.m_policy,
718                              m_context.m_rng,
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        */
send_pending_encrypted_data(boost::system::error_code & ec)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'
743             ec = StreamError::StreamTruncated;
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>
tls_encrypt(const ConstBufferSequence & buffers,boost::system::error_code & ec)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;
765             try_with_error_code([&]
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        */
process_encrypted_data(const boost::asio::const_buffer & read_buffer,boost::system::error_code & ec)780       void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
781          {
782          try_with_error_code([&]
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>
try_with_error_code(Fun f,boost::system::error_code & ec)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             {
806             ec = Botan::ErrorType::Unknown;
807             }
808          }
809 
set_shutdown_received()810       void set_shutdown_received()
811          {
812          m_shutdown_received = true;
813          }
814 
815       Context&                  m_context;
816       StreamLayer               m_nextLayer;
817 
818       boost::beast::flat_buffer m_receive_buffer;
819       boost::beast::flat_buffer m_send_buffer;
820 
821       StreamCore                m_core;
822       std::unique_ptr<ChannelT> m_native_handle;
823 
824       bool m_shutdown_received;
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_
836