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