Botan 2.17.3
Crypto and TLS for C&
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
38namespace Botan {
39namespace 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 */
47template <class StreamLayer, class ChannelT = Channel>
48class 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 {
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 {
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
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) ->
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) ->
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_
#define BOTAN_UNUSED(...)
Definition: assert.h:142
Type type() const
Definition: tls_alert.h:80
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)
Credentials_Manager & m_credentials_manager
Definition: asio_context.h:107
bool has_verify_callback() const
Definition: asio_context.h:89
Server_Information m_server_info
Definition: asio_context.h:112
RandomNumberGenerator & m_rng
Definition: asio_context.h:108
void set_verify_callback(Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_context.h:84
const Verify_Callback & get_verify_callback() const
Definition: asio_context.h:94
Session_Manager & m_session_manager
Definition: asio_context.h:109
detail::fn_signature_helper< decltype(&Callbacks::tls_verify_cert_chain)>::type Verify_Callback
Definition: asio_context.h:54
static Protocol_Version latest_tls_version()
Definition: tls_version.h:36
Helper class that implements Botan::TLS::Callbacks.
Definition: asio_stream.h:581
void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:594
bool tls_session_established(const Botan::TLS::Session &) override
Definition: asio_stream.h:616
virtual ~StreamCore()=default
void tls_emit_data(const uint8_t data[], std::size_t size) override
Definition: asio_stream.h:587
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
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
Definition: asio_stream.h:611
void tls_alert(Botan::TLS::Alert alert) override
Definition: asio_stream.h:601
boost::asio compatible SSL/TLS stream
Definition: asio_stream.h:49
Stream(Stream &&other)=default
native_handle_type native_handle()
Definition: asio_stream.h:130
std::size_t copy_received_data(MutableBufferSequence buffers)
Copy decrypted data into the user-provided buffer.
Definition: asio_stream.h:652
const boost::asio::mutable_buffer m_input_buffer
Definition: asio_stream.h:820
void set_verify_mode(verify_mode v, boost::system::error_code &ec)
Definition: asio_stream.h:199
void try_with_error_code(Fun f, boost::system::error_code &ec)
Catch exceptions and set an error_code.
Definition: asio_stream.h:782
void set_verify_mode(verify_mode v)
Definition: asio_stream.h:187
Stream & operator=(const Stream &other)=delete
boost::beast::flat_buffer m_receive_buffer
Definition: asio_stream.h:810
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
lowest_layer_type & lowest_layer()
Definition: asio_stream.h:122
const ConstBufferSequence & buffers
Definition: asio_stream.h:286
std::enable_if<!std::is_same< Channel, T >::value >::type setup_native_handle(Connection_Side, boost::system::error_code &)
Definition: asio_stream.h:675
Stream(Context &context, Args &&... args)
Construct a new Stream.
Definition: asio_stream.h:63
void set_verify_depth(int depth)
Definition: asio_stream.h:168
const next_layer_type & next_layer() const
Definition: asio_stream.h:106
std::unique_ptr< ChannelT > m_native_handle
Definition: asio_stream.h:814
Stream(Arg &&arg, Context &context)
Construct a new Stream.
Definition: asio_stream.h:83
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
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
typename next_layer_type::executor_type executor_type
Definition: asio_stream.h:126
boost::asio::const_buffer send_buffer() const
Definition: asio_stream.h:645
void handshake(Connection_Side side)
Performs SSL handshaking.
Definition: asio_stream.h:217
bool has_received_data() const
Check if decrypted data is available in the receive buffer.
Definition: asio_stream.h:648
void set_verify_callback(Context::Verify_Callback callback)
Override the tls_verify_cert_chain callback.
Definition: asio_stream.h:150
typename std::add_pointer< ChannelT >::type native_handle_type
Definition: asio_stream.h:129
next_layer_type & next_layer()
Definition: asio_stream.h:107
std::size_t write_some(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Write some data to the stream.
Definition: asio_stream.h:472
const boost::asio::mutable_buffer & input_buffer()
Definition: asio_stream.h:644
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
executor_type get_executor() noexcept
Definition: asio_stream.h:127
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code &ec)
Compatibility overload of set_verify_callback.
Definition: asio_stream.h:161
typename std::remove_reference< StreamLayer >::type next_layer_type
Definition: asio_stream.h:104
void set_shutdown_received()
Definition: asio_stream.h:802
std::size_t read_some(const MutableBufferSequence &buffers)
Read some data from the stream.
Definition: asio_stream.h:453
std::size_t write_some(const ConstBufferSequence &buffers)
Write some data to the stream.
Definition: asio_stream.h:491
Stream(const Stream &other)=delete
Stream & operator=(Stream &&other)=default
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
typename next_layer_type::lowest_layer_type lowest_layer_type
Definition: asio_stream.h:120
auto async_handshake(Connection_Side side, HandshakeHandler &&handler) ->
Starts an asynchronous SSL handshake.
Definition: asio_stream.h:264
const ConstBufferSequence BufferedHandshakeHandler && handler
Definition: asio_stream.h:288
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
std::size_t read_some(const MutableBufferSequence &buffers, boost::system::error_code &ec)
Read some data from the stream.
Definition: asio_stream.h:413
void set_verify_depth(int depth, boost::system::error_code &ec)
Definition: asio_stream.h:179
virtual ~Stream()=default
std::vector< uint8_t > m_input_buffer_space
Definition: asio_stream.h:819
bool shutdown_received() const
Indicates whether a close_notify alert has been received from the peer.
Definition: asio_stream.h:561
void shutdown()
Shut down SSL on the stream.
Definition: asio_stream.h:328
StreamLayer m_nextLayer
Definition: asio_stream.h:808
Context & m_context
Definition: asio_stream.h:807
void handshake(Connection_Side side, boost::system::error_code &ec)
Performs SSL handshaking.
Definition: asio_stream.h:232
void shutdown(boost::system::error_code &ec)
Shut down SSL on the stream.
Definition: asio_stream.h:308
BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check
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
const lowest_layer_type & lowest_layer() const
Definition: asio_stream.h:123
boost::beast::flat_buffer m_send_buffer
Definition: asio_stream.h:811
throw Not_Implemented("buffered async handshake is not implemented")
void async_shutdown(ShutdownHandler &&handler)
Asynchronously shut down SSL on the stream.
Definition: asio_stream.h:373
bool has_data_to_send() const
Check if encrypted data is available in the send buffer.
Definition: asio_stream.h:664
StreamCore m_core
Definition: asio_stream.h:813
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t)) async_handshake(Connection_Side side
Alert::Type type() const
Definition: tls_exceptn.h:24
ErrorType error_type() const noexcept override
Definition: tls_exceptn.h:32
int(* init)(CTX *)
@ StreamTruncated
Definition: asio_error.h:35
@ MAX_CIPHERTEXT_SIZE
Definition: tls_magic.h:28
Definition: alg_id.cpp:13
Usage_Type
Definition: x509cert.h:23
Definition: bigint.h:1143
MechanismType type