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