• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5696981105

28 Jul 2023 12:55PM UTC coverage: 91.69% (+0.005%) from 91.685%
5696981105

push

github

randombit
Merge GH #3649 Remove some macros from loadstor.h

78267 of 85360 relevant lines covered (91.69%)

12322548.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.24
/src/lib/tls/asio/asio_stream.h
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

38
namespace Botan::TLS {
39

40
/**
41
 * @brief boost::asio compatible SSL/TLS stream
42
 *
43
 * @tparam StreamLayer type of the next layer, usually a network socket
44
 * @tparam ChannelT type of the native_handle, defaults to TLS::Channel, only needed for testing purposes
45
 */
46
template <class StreamLayer, class ChannelT = Channel>
47
class Stream {
48
   public:
49
      //! \name construction
50
      //! @{
51

52
      /**
53
       * @brief Construct a new Stream
54
       *
55
       * @param context The context parameter is used to set up the underlying native handle. Using code is
56
       *                responsible for lifetime management of the context and must ensure that it is available for the
57
       *                lifetime of the stream.
58
       * @param args Arguments to be forwarded to the construction of the next layer.
59
       */
60
      template <typename... Args>
61
      explicit Stream(std::shared_ptr<Context> context, Args&&... args) :
25✔
62
            m_context(context),
50✔
63
            m_nextLayer(std::forward<Args>(args)...),
25✔
64
            m_core(std::make_shared<StreamCore>(context)),
25✔
65
            m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
25✔
66
            m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
50✔
67

68
      /**
69
       * @brief Construct a new Stream
70
       *
71
       * Convenience overload for boost::asio::ssl::stream compatibility.
72
       *
73
       * @param arg This argument is forwarded to the construction of the next layer.
74
       * @param context The context parameter is used to set up the underlying native handle. Using code is
75
       *                responsible for lifetime management of the context and must ensure that is available for the
76
       *                lifetime of the stream.
77
       */
78
      template <typename Arg>
79
      explicit Stream(Arg&& arg, std::shared_ptr<Context> context) :
72✔
80
            m_context(context),
144✔
81
            m_nextLayer(std::forward<Arg>(arg)),
108✔
82
            m_core(std::make_shared<StreamCore>(context)),
72✔
83
            m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
72✔
84
            m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
144✔
85

86
      virtual ~Stream() = default;
564✔
87

88
      Stream(Stream&& other) = default;
4✔
89
      Stream& operator=(Stream&& other) = default;
90

91
      Stream(const Stream& other) = delete;
92
      Stream& operator=(const Stream& other) = delete;
93

94
      //! @}
95
      //! \name boost::asio accessor methods
96
      //! @{
97

98
      using next_layer_type = typename std::remove_reference<StreamLayer>::type;
99

100
      const next_layer_type& next_layer() const { return m_nextLayer; }
101

102
      next_layer_type& next_layer() { return m_nextLayer; }
×
103

104
   #if BOOST_VERSION >= 107000
105
      /*
106
       * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
107
       * Instead, the new free-standing functions in Beast need to be used.
108
       * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
109
       */
110
      using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
111

112
      lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
113

114
      const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
115
   #else
116
      using lowest_layer_type = typename next_layer_type::lowest_layer_type;
117

118
      lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
119

120
      const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
121
   #endif
122

123
      using executor_type = typename next_layer_type::executor_type;
124

125
      executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
322✔
126

127
      using native_handle_type = typename std::add_pointer<ChannelT>::type;
128

129
      native_handle_type native_handle() {
573✔
130
         if(m_native_handle == nullptr) {
573✔
131
            throw Invalid_State("Invalid handshake state");
1✔
132
         }
133
         return m_native_handle.get();
572✔
134
      }
135

136
      //! @}
137
      //! \name configuration and callback setters
138
      //! @{
139

140
      /**
141
       * @brief Override the tls_verify_cert_chain callback
142
       *
143
       * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
144
       * used in the handshake.
145
       * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
146
       *
147
       * @note This function should only be called before initiating the TLS handshake
148
       */
149
      void set_verify_callback(Context::Verify_Callback callback) {
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
         BOTAN_UNUSED(ec);
161
         m_context->set_verify_callback(std::move(callback));
162
      }
163

164
      //! @throws Not_Implemented
165
      void set_verify_depth(int depth) {
166
         BOTAN_UNUSED(depth);
167
         throw Not_Implemented("set_verify_depth is not implemented");
168
      }
169

170
      /**
171
       * Not Implemented.
172
       * @param depth the desired verification depth
173
       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
174
       */
175
      void set_verify_depth(int depth, boost::system::error_code& ec) {
176
         BOTAN_UNUSED(depth);
177
         ec = ErrorType::NotImplemented;
178
      }
179

180
      //! @throws Not_Implemented
181
      template <typename verify_mode>
182
      void set_verify_mode(verify_mode v) {
183
         BOTAN_UNUSED(v);
184
         throw Not_Implemented("set_verify_mode is not implemented");
185
      }
186

187
      /**
188
       * Not Implemented.
189
       * @param v the desired verify mode
190
       * @param ec Will be set to `Botan::ErrorType::NotImplemented`
191
       */
192
      template <typename verify_mode>
193
      void set_verify_mode(verify_mode v, boost::system::error_code& ec) {
194
         BOTAN_UNUSED(v);
195
         ec = ErrorType::NotImplemented;
196
      }
197

198
      //! @}
199
      //! \name handshake methods
200
      //! @{
201

202
      /**
203
       * @brief Performs SSL handshaking.
204
       *
205
       * The function call will block until handshaking is complete or an error occurs.
206
       *
207
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
208
       * @throws boost::system::system_error if error occured
209
       */
210
      void handshake(Connection_Side side) {
1✔
211
         boost::system::error_code ec;
1✔
212
         handshake(side, ec);
1✔
213
         boost::asio::detail::throw_error(ec, "handshake");
1✔
214
      }
1✔
215

216
      /**
217
       * @brief Performs SSL handshaking.
218
       *
219
       * The function call will block until handshaking is complete or an error occurs.
220
       *
221
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
222
       * @param ec Set to indicate what error occurred, if any.
223
       */
224
      void handshake(Connection_Side side, boost::system::error_code& ec) {
19✔
225
         setup_native_handle(side, ec);
16✔
226

227
         if(side == Connection_Side::Client) {
19✔
228
            // send client hello, which was written to the send buffer on client instantiation
229
            send_pending_encrypted_data(ec);
19✔
230
         }
231

232
         while(!native_handle()->is_active() && !ec) {
49✔
233
            boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
30✔
234
            if(ec) {
30✔
235
               return;
×
236
            }
237

238
            process_encrypted_data(read_buffer, ec);
30✔
239

240
            send_pending_encrypted_data(ec);
30✔
241
         }
242
      }
243

244
      /**
245
       * @brief Starts an asynchronous SSL handshake.
246
       *
247
       * This function call always returns immediately.
248
       *
249
       * @param side The type of handshaking to be performed, i.e. as a client or as a server.
250
       * @param completion_token The completion handler to be called when the handshake operation completes.
251
       *                         The completion signature of the handler must be: void(boost::system::error_code).
252
       */
253
      template <typename CompletionToken>
254
      auto async_handshake(Botan::TLS::Connection_Side side, CompletionToken&& completion_token) {
59✔
255
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
59✔
256
            [this](auto&& completion_handler, TLS::Connection_Side connection_side) {
174✔
257
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
258

259
               BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
260

261
               boost::system::error_code ec;
59✔
262
               setup_native_handle(connection_side, ec);
56✔
263

264
               detail::AsyncHandshakeOperation<completion_handler_t, Stream> op{
59✔
265
                  std::forward<completion_handler_t>(completion_handler), *this, ec};
266
            },
59✔
267
            completion_token,
268
            side);
269
      }
270

271
      //! @throws Not_Implemented
272
      template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
273
      BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t))
274
      async_handshake(Connection_Side side, const ConstBufferSequence& buffers, BufferedHandshakeHandler&& handler) {
275
         BOTAN_UNUSED(side, buffers, handler);
276
         BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
277
         throw Not_Implemented("buffered async handshake is not implemented");
278
      }
279

280
      //! @}
281
      //! \name shutdown methods
282
      //! @{
283

284
      /**
285
       * @brief Shut down SSL on the stream.
286
       *
287
       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
288
       * or an error occurs. Note that this will not close the lowest layer.
289
       *
290
       * Note that this can be used in reaction of a received shutdown alert from the peer.
291
       *
292
       * @param ec Set to indicate what error occured, if any.
293
       */
294
      void shutdown(boost::system::error_code& ec) {
16✔
295
         try_with_error_code([&] { native_handle()->close(); }, ec);
32✔
296

297
         send_pending_encrypted_data(ec);
16✔
298
      }
16✔
299

300
      /**
301
       * @brief Shut down SSL on the stream.
302
       *
303
       * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
304
       * or an error occurs. Note that this will not close the lowest layer.
305
       *
306
       * Note that this can be used in reaction of a received shutdown alert from the peer.
307
       *
308
       * @throws boost::system::system_error if error occured
309
       */
310
      void shutdown() {
311
         boost::system::error_code ec;
312
         shutdown(ec);
313
         boost::asio::detail::throw_error(ec, "shutdown");
314
      }
315

316
   private:
317
      /**
318
       * @brief Internal wrapper type to adapt the expected signature of `async_shutdown` to the completion handler
319
       *        signature of `AsyncWriteOperation`.
320
       *
321
       * This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
322
       * `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
323
       */
324
      template <typename Handler, typename Executor>
325
      struct Wrapper {
32✔
326
            void operator()(boost::system::error_code ec, std::size_t) { handler(ec); }
32✔
327

328
            using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
329

330
            executor_type get_executor() const noexcept {
32✔
331
               return boost::asio::get_associated_executor(handler, io_executor);
32✔
332
            }
333

334
            using allocator_type = boost::asio::associated_allocator_t<Handler>;
335

336
            allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(handler); }
337

338
            Handler handler;
339
            Executor io_executor;
340
      };
341

342
   public:
343
      /**
344
       * @brief Asynchronously shut down SSL on the stream.
345
       *
346
       * This function call always returns immediately.
347
       *
348
       * Note that this can be used in reaction of a received shutdown alert from the peer.
349
       *
350
       * @param completion_token The completion handler to be called when the shutdown operation completes.
351
       *                         The completion signature of the handler must be: void(boost::system::error_code).
352
       */
353
      template <typename CompletionToken>
354
      auto async_shutdown(CompletionToken&& completion_token) {
32✔
355
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
32✔
356
            [this](auto&& completion_handler) {
32✔
357
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
358

359
               BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
360

361
               boost::system::error_code ec;
32✔
362
               try_with_error_code([&] { native_handle()->close(); }, ec);
64✔
363

364
               using write_handler_t = Wrapper<completion_handler_t, typename Stream::executor_type>;
365

366
               TLS::detail::AsyncWriteOperation<write_handler_t, Stream> op{
64✔
367
                  write_handler_t{std::forward<completion_handler_t>(completion_handler), get_executor()},
368
                  *this,
369
                  boost::asio::buffer_size(send_buffer()),
32✔
370
                  ec};
371
            },
32✔
372
            completion_token);
373
      }
374

375
      //! @}
376
      //! \name I/O methods
377
      //! @{
378

379
      /**
380
       * @brief Read some data from the stream.
381
       *
382
       * The function call will block until one or more bytes of data has been read successfully, or until an error
383
       * occurs.
384
       *
385
       * @param buffers The buffers into which the data will be read.
386
       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
387
       *           has closed the connection but did not properly shut down the SSL connection.
388
       * @return The number of bytes read. Returns 0 if an error occurred.
389
       */
390
      template <typename MutableBufferSequence>
391
      std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec) {
16✔
392
         if(has_received_data()) {
16✔
393
            return copy_received_data(buffers);
×
394
         }
395

396
         boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
16✔
397
         if(ec) {
16✔
398
            return 0;
399
         }
400

401
         process_encrypted_data(read_buffer, ec);
15✔
402

403
         if(ec)  // something went wrong in process_encrypted_data()
15✔
404
         {
405
            return 0;
406
         }
407

408
         if(shutdown_received()) {
14✔
409
            // we just received a 'close_notify' from the peer and don't expect any more data
410
            ec = boost::asio::error::eof;
14✔
411
         } else if(ec == boost::asio::error::eof) {
6✔
412
            // we did not expect this disconnection from the peer
413
            ec = StreamError::StreamTruncated;
14✔
414
         }
415

416
         return !ec ? copy_received_data(buffers) : 0;
14✔
417
      }
418

419
      /**
420
       * @brief Read some data from the stream.
421
       *
422
       * The function call will block until one or more bytes of data has been read successfully, or until an error
423
       * occurs.
424
       *
425
       * @param buffers The buffers into which the data will be read.
426
       * @return The number of bytes read. Returns 0 if an error occurred.
427
       * @throws boost::system::system_error if error occured
428
       */
429
      template <typename MutableBufferSequence>
430
      std::size_t read_some(const MutableBufferSequence& buffers) {
431
         boost::system::error_code ec;
432
         const auto n = read_some(buffers, ec);
433
         boost::asio::detail::throw_error(ec, "read_some");
434
         return n;
435
      }
436

437
      /**
438
       * @brief Write some data to the stream.
439
       *
440
       * The function call will block until one or more bytes of data has been written successfully, or until an error
441
       * occurs.
442
       *
443
       * @param buffers The data to be written.
444
       * @param ec Set to indicate what error occurred, if any.
445
       * @return The number of bytes processed from the input buffers.
446
       */
447
      template <typename ConstBufferSequence>
448
      std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
14✔
449
         tls_encrypt(buffers, ec);
14✔
450
         send_pending_encrypted_data(ec);
14✔
451
         return !ec ? boost::asio::buffer_size(buffers) : 0;
14✔
452
      }
453

454
      /**
455
       * @brief Write some data to the stream.
456
       *
457
       * The function call will block until one or more bytes of data has been written successfully, or until an error
458
       * occurs.
459
       *
460
       * @param buffers The data to be written.
461
       * @return The number of bytes written.
462
       * @throws boost::system::system_error if error occured
463
       */
464
      template <typename ConstBufferSequence>
465
      std::size_t write_some(const ConstBufferSequence& buffers) {
466
         boost::system::error_code ec;
467
         const auto n = write_some(buffers, ec);
468
         boost::asio::detail::throw_error(ec, "write_some");
469
         return n;
470
      }
471

472
      /**
473
       * @brief Start an asynchronous write. The function call always returns immediately.
474
       *
475
       * @param buffers The data to be written.
476
       * @param completion_token The completion handler to be called when the write operation completes. Copies of the
477
       *                         handler will be made as required. The completion signature of the handler must be:
478
       *                         void(boost::system::error_code, std::size_t).
479
       */
480
      template <typename ConstBufferSequence, typename CompletionToken>
481
      auto async_write_some(const ConstBufferSequence& buffers, CompletionToken&& completion_token) {
37✔
482
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
37✔
483
            [this](auto&& completion_handler, const auto& bufs) {
112✔
484
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
485

486
               BOOST_ASIO_WRITE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
487

488
               boost::system::error_code ec;
37✔
489
               tls_encrypt(bufs, ec);
37✔
490

491
               if(ec) {
37✔
492
                  // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
493
                  // AsyncWriteOperation call the handler with the error_code set
494
                  consume_send_buffer(m_core->send_buffer.size());
1✔
495
               }
496

497
               detail::AsyncWriteOperation<completion_handler_t, Stream> op{
35✔
498
                  std::forward<completion_handler_t>(completion_handler),
499
                  *this,
500
                  ec ? 0 : boost::asio::buffer_size(bufs),
39✔
501
                  ec};
502
            },
37✔
503
            completion_token,
504
            buffers);
505
      }
506

507
      /**
508
       * @brief Start an asynchronous read. The function call always returns immediately.
509
       *
510
       * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
511
       *                necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
512
       *                that they remain valid until the handler is called.
513
       * @param completion_token The completion handler to be called when the read operation completes. The completion
514
       *                         signature of the handler must be: void(boost::system::error_code, std::size_t).
515
       */
516
      template <typename MutableBufferSequence, typename CompletionToken>
517
      auto async_read_some(const MutableBufferSequence& buffers, CompletionToken&& completion_token) {
89✔
518
         return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
89✔
519
            [this](auto&& completion_handler, const auto& bufs) {
89✔
520
               using completion_handler_t = std::decay_t<decltype(completion_handler)>;
521

522
               BOOST_ASIO_READ_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
523

524
               detail::AsyncReadOperation<completion_handler_t, Stream, MutableBufferSequence> op{
89✔
525
                  std::forward<completion_handler_t>(completion_handler), *this, bufs};
526
            },
89✔
527
            completion_token,
528
            buffers);
529
      }
530

531
      //! @}
532

533
      //! @brief Indicates whether a close_notify alert has been received from the peer.
534
      //!
535
      //! Note that we cannot m_core.is_closed_for_reading() because this wants to
536
      //! explicitly check that the peer sent close_notify.
537
      bool shutdown_received() const { return m_core->shutdown_received; }
256✔
538

539
   protected:
540
      template <class H, class S, class M, class A>
541
      friend class detail::AsyncReadOperation;
542
      template <class H, class S, class A>
543
      friend class detail::AsyncWriteOperation;
544
      template <class H, class S, class A>
545
      friend class detail::AsyncHandshakeOperation;
546

547
      /**
548
       * @brief Helper class that implements TLS::Callbacks
549
       *
550
       * This class is provided to the stream's native_handle (TLS::Channel) and implements the callback
551
       * functions triggered by the native_handle.
552
       */
553
      class StreamCore : public TLS::Callbacks {
554
         public:
555
            StreamCore(std::weak_ptr<Botan::TLS::Context> context) : shutdown_received(false), m_context(context) {}
97✔
556

557
            ~StreamCore() override = default;
291✔
558

559
            void tls_emit_data(std::span<const uint8_t> data) override {
454✔
560
               send_buffer.commit(boost::asio::buffer_copy(send_buffer.prepare(data.size()),
908✔
561
                                                           boost::asio::buffer(data.data(), data.size())));
454✔
562
            }
454✔
563

564
            void tls_record_received(uint64_t, std::span<const uint8_t> data) override {
46✔
565
               receive_buffer.commit(boost::asio::buffer_copy(receive_buffer.prepare(data.size()),
92✔
566
                                                              boost::asio::const_buffer(data.data(), data.size())));
46✔
567
            }
46✔
568

569
            bool tls_peer_closed_connection() override {
40✔
570
               // Instruct the TLS implementation to reply with our close_notify to obtain
571
               // the same behaviour for TLS 1.2 and TLS 1.3.
572
               return true;
40✔
573
            }
574

575
            void tls_alert(TLS::Alert alert) override {
40✔
576
               if(alert.type() == TLS::AlertType::CloseNotify) {
40✔
577
                  shutdown_received = true;
40✔
578
                  // Channel::process_alert will automatically write the corresponding close_notify response to the
579
                  // send_buffer and close the native_handle after this function returns.
580
               }
581
            }
40✔
582

583
            std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override {
×
584
               return std::chrono::milliseconds(1000);
×
585
            }
586

587
            void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
36✔
588
                                       const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
589
                                       const std::vector<Certificate_Store*>& trusted_roots,
590
                                       Usage_Type usage,
591
                                       std::string_view hostname,
592
                                       const TLS::Policy& policy) override {
593
               auto ctx = m_context.lock();
36✔
594

595
               if(ctx && ctx->has_verify_callback()) {
36✔
596
                  ctx->get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
36✔
597
               } else {
598
                  Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
×
599
               }
600
            }
36✔
601

602
            bool shutdown_received;
603
            boost::beast::flat_buffer receive_buffer;
604
            boost::beast::flat_buffer send_buffer;
605

606
         private:
607
            std::weak_ptr<TLS::Context> m_context;
608
      };
609

610
      const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
423✔
611

612
      boost::asio::const_buffer send_buffer() const { return m_core->send_buffer.data(); }
281✔
613

614
      //! @brief Check if decrypted data is available in the receive buffer
615
      bool has_received_data() const { return m_core->receive_buffer.size() > 0; }
253✔
616

617
      //! @brief Copy decrypted data into the user-provided buffer
618
      template <typename MutableBufferSequence>
619
      std::size_t copy_received_data(MutableBufferSequence buffers) {
44✔
620
         // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
621
         // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
622
         // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
623
         // than the decrypted record, so this optimization might not be worth the additional complexity.
624
         const auto copiedBytes = boost::asio::buffer_copy(buffers, m_core->receive_buffer.data());
44✔
625
         m_core->receive_buffer.consume(copiedBytes);
44✔
626
         return copiedBytes;
44✔
627
      }
628

629
      //! @brief Check if encrypted data is available in the send buffer
630
      bool has_data_to_send() const { return m_core->send_buffer.size() > 0; }
611✔
631

632
      //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
633
      void consume_send_buffer(std::size_t bytesConsumed) { m_core->send_buffer.consume(bytesConsumed); }
695✔
634

635
      /**
636
       * @brief Create the native handle.
637
       *
638
       * Depending on the desired connection side, this function will create a TLS::Client or a
639
       * TLS::Server.
640
       *
641
       * @param side The desired connection side (client or server)
642
       * @param ec Set to indicate what error occurred, if any.
643
       */
644
      void setup_native_handle(Connection_Side side, boost::system::error_code& ec) {
72✔
645
         BOTAN_UNUSED(side);  // workaround: GCC 9 produces a warning claiming side is unused
646

647
         // Do not attempt to instantiate the native_handle when a custom (mocked) channel type template parameter has
648
         // been specified. This allows mocking the native_handle in test code.
649
         if constexpr(std::is_same<ChannelT, Channel>::value) {
650
            try_with_error_code(
72✔
651
               [&] {
72✔
652
                  if(side == Connection_Side::Client) {
72✔
653
                     m_native_handle = std::unique_ptr<Client>(
144✔
654
                        new Client(m_core,
72✔
655
                                   m_context->m_session_manager,
36✔
656
                                   m_context->m_credentials_manager,
36✔
657
                                   m_context->m_policy,
36✔
658
                                   m_context->m_rng,
36✔
659
                                   m_context->m_server_info,
36✔
660
                                   m_context->m_policy->latest_supported_version(false /* no DTLS */)));
72✔
661
                  } else {
662
                     m_native_handle = std::unique_ptr<Server>(new Server(m_core,
108✔
663
                                                                          m_context->m_session_manager,
36✔
664
                                                                          m_context->m_credentials_manager,
36✔
665
                                                                          m_context->m_policy,
36✔
666
                                                                          m_context->m_rng,
36✔
667
                                                                          false /* no DTLS */));
668
                  }
669
               },
670
               ec);
671
         }
672
      }
673

674
      /** @brief Synchronously write encrypted data from the send buffer to the next layer.
675
       *
676
       * If this function is called with an error code other than 'Success', it will do nothing and return 0.
677
       *
678
       * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
679
       *           has closed the connection but did not properly shut down the SSL connection.
680
       * @return The number of bytes written.
681
       */
682
      size_t send_pending_encrypted_data(boost::system::error_code& ec) {
79✔
683
         if(ec) {
79✔
684
            return 0;
685
         }
686

687
         auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
76✔
688
         consume_send_buffer(writtenBytes);
76✔
689

690
         if(ec == boost::asio::error::eof && !shutdown_received()) {
76✔
691
            // transport layer was closed by peer without receiving 'close_notify'
692
            ec = StreamError::StreamTruncated;
79✔
693
         }
694

695
         return writtenBytes;
696
      }
697

698
      /**
699
       * @brief Pass plaintext data to the native handle for processing.
700
       *
701
       * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
702
       */
703
      template <typename ConstBufferSequence>
704
      void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
51✔
705
         // NOTE: This is not asynchronous: it encrypts the data synchronously.
706
         // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
707
         // but is not actually written to the wire, yet.
708
         for(auto it = boost::asio::buffer_sequence_begin(buffers);
134✔
709
             !ec && it != boost::asio::buffer_sequence_end(buffers);
134✔
710
             it++) {
711
            const boost::asio::const_buffer buffer = *it;
83✔
712
            try_with_error_code(
83✔
713
               [&] {
84✔
714
                  native_handle()->send({static_cast<const uint8_t*>(buffer.data()), buffer.size()});
83✔
715
               },
716
               ec);
717
         }
718
      }
51✔
719

720
      /**
721
       * @brief Pass encrypted data to the native handle for processing.
722
       *
723
       * If an exception occurs while processing the data, an error code will be set.
724
       *
725
       * @param read_buffer Input buffer containing the encrypted data.
726
       * @param ec Set to indicate what error occurred, if any.
727
       */
728
      void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec) {
225✔
729
         try_with_error_code(
225✔
730
            [&] {
229✔
731
               native_handle()->received_data({static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size()});
225✔
732
            },
733
            ec);
734
      }
180✔
735

736
      //! @brief Catch exceptions and set an error_code
737
      template <typename Fun>
738
      void try_with_error_code(Fun f, boost::system::error_code& ec) {
428✔
739
         try {
740
            f();
428✔
741
         } catch(const TLS_Exception& e) {
7✔
742
            ec = e.type();
6✔
743
         } catch(const Exception& e) {
1✔
744
            ec = e.error_type();
1✔
745
         } catch(const std::exception&) {
×
746
            ec = ErrorType::Unknown;
×
747
         }
748
      }
428✔
749

750
      std::shared_ptr<Context> m_context;
751
      StreamLayer m_nextLayer;
752

753
      std::shared_ptr<StreamCore> m_core;
754
      std::unique_ptr<ChannelT> m_native_handle;
755

756
      // Buffer space used to read input intended for the core
757
      std::vector<uint8_t> m_input_buffer_space;
758
      const boost::asio::mutable_buffer m_input_buffer;
759
};
760

761
}  // namespace Botan::TLS
762

763
#endif  // BOOST_VERSION
764
#endif  // BOTAN_ASIO_STREAM_H_
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc