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

randombit / botan / 6548132566

17 Oct 2023 01:46PM UTC coverage: 91.704% (-0.006%) from 91.71%
6548132566

push

github

web-flow
Merge pull request #3762 from Rohde-Schwarz/fix/handshake_complete

Introduce TLS::Channel::is_handshake complete()

80122 of 87370 relevant lines covered (91.7%)

8608923.3 hits per line

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

98.93
/src/tests/unit_asio_stream.cpp
1
/*
2
* TLS ASIO Stream Unit Tests
3
* (C) 2018-2020 Jack Lloyd
4
*     2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include "tests.h"
10

11
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_TLS_ASIO_STREAM)
12

13
   #include <botan/asio_stream.h>
14
   #include <botan/tls_callbacks.h>
15
   #include <botan/tls_session_manager_noop.h>
16

17
   // The boost::beast::test::stream we use is available starting from boost
18
   // version 1.68, so we cannot run these tests with a smaller version.
19
   #include <boost/version.hpp>
20
   #if BOOST_VERSION >= 106800
21

22
      // boost::beast::test::stream's include path has been changed in boost version
23
      // 1.70.
24
      #if BOOST_VERSION < 107000
25
         #include <boost/beast/experimental/test/stream.hpp>
26
      #else
27
         #include <boost/beast/_experimental/test/stream.hpp>
28
      #endif
29

30
      #include <boost/bind.hpp>
31
      #include <utility>
32

33
namespace Botan_Tests {
34

35
namespace net = boost::asio;
36
using error_code = boost::system::error_code;
37

38
constexpr uint8_t TEST_DATA[] =
39
   "The story so far: In the beginning the Universe was created. "
40
   "This has made a lot of people very angry and been widely regarded as a bad move.";
41
constexpr std::size_t TEST_DATA_SIZE = 142;
42
static_assert(sizeof(TEST_DATA) == TEST_DATA_SIZE, "size of TEST_DATA must match TEST_DATA_SIZE");
43

44
/**
45
 * Mocked Botan::TLS::Channel. Pretends to perform TLS operations and triggers appropriate callbacks in StreamCore.
46
 */
47
class MockChannel {
36✔
48
   public:
49
      MockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) :
24✔
50
            m_callbacks(std::move(core)), m_bytes_till_complete_record(TEST_DATA_SIZE), m_active(false) {}
24✔
51

52
   public:
53
      std::size_t received_data(std::span<const uint8_t> data) {
6✔
54
         if(m_bytes_till_complete_record <= data.size()) {
6✔
55
            m_callbacks->tls_record_received(0, TEST_DATA);
6✔
56
            m_active = true;  // claim to be active once a full record has been received (for handshake test)
6✔
57
            return 0;
6✔
58
         }
59
         m_bytes_till_complete_record -= data.size();
×
60
         return m_bytes_till_complete_record;
×
61
      }
62

63
      void send(std::span<const uint8_t> buf) { m_callbacks->tls_emit_data(buf); }
43✔
64

65
      bool is_active() const { return m_active; }
6✔
66

67
      bool is_handshake_complete() const { return m_active; }
10✔
68

69
   private:
70
      std::shared_ptr<Botan::TLS::Callbacks> m_callbacks;
71
      std::size_t m_bytes_till_complete_record;  // number of bytes still to read before tls record is completed
72
      bool m_active;
73
};
74

75
class ThrowingMockChannel : public MockChannel {
12✔
76
   public:
77
      static boost::system::error_code expected_ec() { return Botan::TLS::Alert::UnexpectedMessage; }
12✔
78

79
      ThrowingMockChannel(std::shared_ptr<Botan::TLS::Callbacks> core) : MockChannel(std::move(core)) {}
6✔
80

81
      std::size_t received_data(std::span<const uint8_t>) { throw Botan::TLS::Unexpected_Message("test_error"); }
4✔
82

83
      void send(std::span<const uint8_t>) { throw Botan::TLS::Unexpected_Message("test_error"); }
2✔
84
};
85

86
// Unfortunately, boost::beast::test::stream keeps lowest_layer_type private and
87
// only friends boost::asio::ssl::stream. We need to make our own.
88
class TestStream : public boost::beast::test::stream {
25✔
89
   public:
90
      using boost::beast::test::stream::stream;
25✔
91
      using lowest_layer_type = boost::beast::test::stream;
92
};
93

94
using FailCount = boost::beast::test::fail_count;
95

96
class AsioStream : public Botan::TLS::Stream<TestStream, MockChannel> {
×
97
   public:
98
      template <typename... Args>
99
      AsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
18✔
100
         m_native_handle = std::make_unique<MockChannel>(m_core);
18✔
101
      }
18✔
102
};
103

104
class ThrowingAsioStream : public Botan::TLS::Stream<TestStream, ThrowingMockChannel> {
12✔
105
   public:
106
      template <typename... Args>
107
      ThrowingAsioStream(std::shared_ptr<Botan::TLS::Context> context, Args&&... args) : Stream(context, args...) {
6✔
108
         m_native_handle = std::make_unique<ThrowingMockChannel>(m_core);
6✔
109
      }
6✔
110
};
111

112
/**
113
 * Synchronous tests for Botan::Stream.
114
 *
115
 * This test validates the asynchronous behavior Botan::Stream, including its utility classes StreamCore and Async_*_Op.
116
 * The stream's channel, i.e. TLS_Client or TLS_Server, is mocked and pretends to perform TLS operations (noop) and
117
 * provides the test data to the stream.
118
 * The underlying network socket, claiming it read / wrote a number of bytes.
119
 */
120
class Asio_Stream_Tests final : public Test {
×
121
      std::shared_ptr<Botan::TLS::Context> get_context() {
25✔
122
         return std::make_shared<Botan::TLS::Context>(std::make_shared<Botan::Credentials_Manager>(),
25✔
123
                                                      std::make_shared<Botan::Null_RNG>(),
25✔
124
                                                      std::make_shared<Botan::TLS::Session_Manager_Noop>(),
25✔
125
                                                      std::make_shared<Botan::TLS::Default_Policy>());
50✔
126
      }
127

128
      // use memcmp to check if the data in a is a prefix of the data in b
129
      bool contains(const void* a, const void* b, const std::size_t size) { return memcmp(a, b, size) == 0; }
8✔
130

131
      boost::string_view test_data() const {
12✔
132
         return boost::string_view(reinterpret_cast<const char*>(TEST_DATA), TEST_DATA_SIZE);
12✔
133
      }
134

135
      void test_sync_handshake(std::vector<Test::Result>& results) {
1✔
136
         net::io_context ioc;
1✔
137
         auto ctx = get_context();
1✔
138
         AsioStream ssl(ctx, ioc, test_data());
1✔
139

140
         ssl.handshake(Botan::TLS::Connection_Side::Client);
1✔
141

142
         Test::Result result("sync TLS handshake");
1✔
143
         result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
144
         results.push_back(result);
1✔
145
      }
2✔
146

147
      void test_sync_handshake_error(std::vector<Test::Result>& results) {
1✔
148
         net::io_context ioc;
1✔
149
         // fail right away
150
         FailCount fc{0, net::error::no_recovery};
1✔
151
         TestStream remote{ioc};
1✔
152

153
         auto ctx = get_context();
1✔
154
         AsioStream ssl(ctx, ioc, fc);
1✔
155
         ssl.next_layer().connect(remote);
1✔
156

157
         // mimic handshake initialization
158
         ssl.native_handle()->send(TEST_DATA);
1✔
159

160
         error_code ec;
1✔
161
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
162

163
         Test::Result result("sync TLS handshake error");
1✔
164
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
165
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
166
         results.push_back(result);
1✔
167
      }
3✔
168

169
      void test_sync_handshake_throw(std::vector<Test::Result>& results) {
1✔
170
         net::io_context ioc;
1✔
171
         TestStream remote{ioc};
1✔
172

173
         auto ctx = get_context();
1✔
174
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
175
         ssl.next_layer().connect(remote);
1✔
176

177
         error_code ec;
1✔
178
         ssl.handshake(Botan::TLS::Connection_Side::Client, ec);
1✔
179

180
         Test::Result result("sync TLS handshake error");
1✔
181
         result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
182
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
183
         results.push_back(result);
1✔
184
      }
2✔
185

186
      void test_async_handshake(std::vector<Test::Result>& results) {
1✔
187
         net::io_context ioc;
1✔
188
         TestStream remote{ioc};
1✔
189

190
         auto ctx = get_context();
1✔
191
         AsioStream ssl(ctx, ioc, test_data());
1✔
192
         ssl.next_layer().connect(remote);
1✔
193

194
         // mimic handshake initialization
195
         ssl.native_handle()->send(TEST_DATA);
1✔
196

197
         Test::Result result("async TLS handshake");
1✔
198

199
         auto handler = [&](const error_code&) {
2✔
200
            result.confirm("reads from socket", ssl.next_layer().nread() > 0);
2✔
201
            result.confirm("writes from socket", ssl.next_layer().nwrite() > 0);
2✔
202
            result.test_eq("feeds data into channel until active", ssl.native_handle()->is_active(), true);
1✔
203
         };
1✔
204

205
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
206

207
         ssl.next_layer().close_remote();
1✔
208
         ioc.run();
1✔
209
         results.push_back(result);
1✔
210
      }
2✔
211

212
      void test_async_handshake_error(std::vector<Test::Result>& results) {
1✔
213
         net::io_context ioc;
1✔
214
         // fail right away
215
         FailCount fc{0, net::error::no_recovery};
1✔
216
         TestStream remote{ioc};
1✔
217

218
         auto ctx = get_context();
1✔
219
         AsioStream ssl(ctx, ioc, fc);
1✔
220
         ssl.next_layer().connect(remote);
1✔
221

222
         // mimic handshake initialization
223
         ssl.native_handle()->send(TEST_DATA);
1✔
224

225
         Test::Result result("async TLS handshake error");
1✔
226

227
         auto handler = [&](const error_code& ec) {
2✔
228
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
229
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
230
         };
1✔
231

232
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
233

234
         ioc.run();
1✔
235
         results.push_back(result);
1✔
236
      }
2✔
237

238
      void test_async_handshake_throw(std::vector<Test::Result>& results) {
1✔
239
         net::io_context ioc;
1✔
240
         TestStream remote{ioc};
1✔
241

242
         auto ctx = get_context();
1✔
243
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
244
         ssl.next_layer().connect(remote);
1✔
245

246
         Test::Result result("async TLS handshake throw");
1✔
247

248
         auto handler = [&](const error_code& ec) {
2✔
249
            result.test_eq("does not activate channel", ssl.native_handle()->is_active(), false);
1✔
250
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
251
         };
1✔
252

253
         ssl.async_handshake(Botan::TLS::Connection_Side::Client, handler);
1✔
254

255
         ioc.run();
1✔
256
         results.push_back(result);
1✔
257
      }
2✔
258

259
      void test_sync_read_some_success(std::vector<Test::Result>& results) {
1✔
260
         net::io_context ioc;
1✔
261

262
         auto ctx = get_context();
1✔
263
         AsioStream ssl(ctx, ioc, test_data());
1✔
264

265
         const std::size_t buf_size = 128;
1✔
266
         uint8_t buf[buf_size];
1✔
267
         error_code ec;
1✔
268

269
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
270

271
         Test::Result result("sync read_some success");
1✔
272
         result.confirm("reads the correct data", contains(buf, TEST_DATA, buf_size));
2✔
273
         result.test_eq("reads the correct amount of data", bytes_transferred, buf_size);
1✔
274
         result.confirm("does not report an error", !ec);
2✔
275

276
         results.push_back(result);
1✔
277
      }
2✔
278

279
      void test_sync_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
280
         net::io_context ioc;
1✔
281

282
         auto ctx = get_context();
1✔
283
         AsioStream ssl(ctx, ioc, test_data());
1✔
284
         error_code ec;
1✔
285

286
         std::vector<net::mutable_buffer> data;
1✔
287
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
288
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
289
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
290
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
291

292
         auto bytes_transferred = net::read(ssl, data, ec);
1✔
293

294
         Test::Result result("sync read_some buffer sequence");
1✔
295

296
         result.confirm("reads the correct data",
2✔
297
                        contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
298
                           contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
299
         result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
300
         result.confirm("does not report an error", !ec);
2✔
301

302
         results.push_back(result);
1✔
303
      }
3✔
304

305
      void test_sync_read_some_error(std::vector<Test::Result>& results) {
1✔
306
         net::io_context ioc;
1✔
307
         // fail right away
308
         FailCount fc{0, net::error::no_recovery};
1✔
309
         TestStream remote{ioc};
1✔
310

311
         auto ctx = get_context();
1✔
312
         AsioStream ssl(ctx, ioc, fc);
1✔
313
         ssl.next_layer().connect(remote);
1✔
314

315
         uint8_t buf[128];
1✔
316
         error_code ec;
1✔
317

318
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
319

320
         Test::Result result("sync read_some error");
1✔
321
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
322
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
323

324
         results.push_back(result);
1✔
325
      }
2✔
326

327
      void test_sync_read_some_throw(std::vector<Test::Result>& results) {
1✔
328
         net::io_context ioc;
1✔
329
         TestStream remote{ioc};
1✔
330

331
         auto ctx = get_context();
1✔
332
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
333
         ssl.next_layer().connect(remote);
1✔
334

335
         uint8_t buf[128];
1✔
336
         error_code ec;
1✔
337

338
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, sizeof(buf)), ec);
1✔
339

340
         Test::Result result("sync read_some throw");
1✔
341
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
342
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
343

344
         results.push_back(result);
1✔
345
      }
2✔
346

347
      void test_sync_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
348
         net::io_context ioc;
1✔
349

350
         auto ctx = get_context();
1✔
351
         AsioStream ssl(ctx, ioc);
1✔
352

353
         const std::size_t buf_size = 128;
1✔
354
         uint8_t buf[buf_size];
1✔
355
         error_code ec;
1✔
356

357
         auto bytes_transferred = net::read(ssl, net::mutable_buffer(buf, std::size_t(0)), ec);
1✔
358

359
         Test::Result result("sync read_some into zero-size buffer");
1✔
360
         result.test_eq("reads the correct amount of data", bytes_transferred, 0);
1✔
361
         // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
362
         // would block here. TestStream sets error_code::eof.
363
         result.confirm("does not report an error", !ec);
2✔
364

365
         results.push_back(result);
1✔
366
      }
2✔
367

368
      void test_async_read_some_success(std::vector<Test::Result>& results) {
1✔
369
         net::io_context ioc;
1✔
370
         TestStream remote{ioc};
1✔
371

372
         auto ctx = get_context();
1✔
373
         AsioStream ssl(ctx, ioc, test_data());
1✔
374
         uint8_t data[TEST_DATA_SIZE];
1✔
375

376
         Test::Result result("async read_some success");
1✔
377

378
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
379
            result.confirm("reads the correct data", contains(data, TEST_DATA, TEST_DATA_SIZE));
2✔
380
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
381
            result.confirm("does not report an error", !ec);
2✔
382
         };
1✔
383

384
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
385
         net::async_read(ssl, buf, read_handler);
1✔
386

387
         ssl.next_layer().close_remote();
1✔
388
         ioc.run();
1✔
389
         results.push_back(result);
1✔
390
      }
2✔
391

392
      void test_async_read_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
393
         net::io_context ioc;
1✔
394
         auto ctx = get_context();
1✔
395
         AsioStream ssl(ctx, ioc, test_data());
1✔
396

397
         std::vector<net::mutable_buffer> data;
1✔
398
         uint8_t buf1[TEST_DATA_SIZE / 2];
1✔
399
         uint8_t buf2[TEST_DATA_SIZE / 2];
1✔
400
         data.emplace_back(net::mutable_buffer(buf1, TEST_DATA_SIZE / 2));
1✔
401
         data.emplace_back(net::mutable_buffer(buf2, TEST_DATA_SIZE / 2));
1✔
402

403
         Test::Result result("async read_some buffer sequence");
1✔
404

405
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
406
            result.confirm("reads the correct data",
2✔
407
                           contains(buf1, TEST_DATA, TEST_DATA_SIZE / 2) &&
1✔
408
                              contains(buf2, TEST_DATA + TEST_DATA_SIZE / 2, TEST_DATA_SIZE / 2));
1✔
409
            result.test_eq("reads the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
410
            result.confirm("does not report an error", !ec);
2✔
411
         };
1✔
412

413
         net::async_read(ssl, data, read_handler);
1✔
414

415
         ssl.next_layer().close_remote();
1✔
416
         ioc.run();
1✔
417
         results.push_back(result);
1✔
418
      }
3✔
419

420
      void test_async_read_some_error(std::vector<Test::Result>& results) {
1✔
421
         net::io_context ioc;
1✔
422
         // fail right away
423
         FailCount fc{0, net::error::no_recovery};
1✔
424
         auto ctx = get_context();
1✔
425
         AsioStream ssl(ctx, ioc, fc);
1✔
426
         uint8_t data[TEST_DATA_SIZE];
1✔
427

428
         Test::Result result("async read_some error");
1✔
429

430
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
431
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
432
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
433
         };
1✔
434

435
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
436
         net::async_read(ssl, buf, read_handler);
1✔
437

438
         ssl.next_layer().close_remote();
1✔
439
         ioc.run();
1✔
440
         results.push_back(result);
1✔
441
      }
2✔
442

443
      void test_async_read_some_throw(std::vector<Test::Result>& results) {
1✔
444
         net::io_context ioc;
1✔
445
         auto ctx = get_context();
1✔
446
         ThrowingAsioStream ssl(ctx, ioc, test_data());
1✔
447
         uint8_t data[TEST_DATA_SIZE];
1✔
448

449
         Test::Result result("async read_some throw");
1✔
450

451
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
452
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
453
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
454
         };
1✔
455

456
         net::mutable_buffer buf{data, TEST_DATA_SIZE};
1✔
457
         net::async_read(ssl, buf, read_handler);
1✔
458

459
         ssl.next_layer().close_remote();
1✔
460
         ioc.run();
1✔
461
         results.push_back(result);
1✔
462
      }
2✔
463

464
      void test_async_read_zero_buffer(std::vector<Test::Result>& results) {
1✔
465
         net::io_context ioc;
1✔
466
         TestStream remote{ioc};
1✔
467

468
         auto ctx = get_context();
1✔
469
         AsioStream ssl(ctx, ioc);
1✔
470
         uint8_t data[TEST_DATA_SIZE];
1✔
471

472
         Test::Result result("async read_some into zero-size buffer");
1✔
473

474
         auto read_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
475
            result.test_eq("reads the correct amount of data", bytes_transferred, 0);
1✔
476
            // This relies on an implementation detail of TestStream: A "real" asio::tcp::stream
477
            // would block here. TestStream sets error_code::eof.
478
            result.confirm("does not report an error", !ec);
2✔
479
         };
1✔
480

481
         net::mutable_buffer buf{data, std::size_t(0)};
1✔
482
         net::async_read(ssl, buf, read_handler);
1✔
483

484
         ssl.next_layer().close_remote();
1✔
485
         ioc.run();
1✔
486
         results.push_back(result);
1✔
487
      }
2✔
488

489
      void test_sync_write_some_success(std::vector<Test::Result>& results) {
1✔
490
         net::io_context ioc;
1✔
491
         TestStream remote{ioc};
1✔
492

493
         auto ctx = get_context();
1✔
494
         AsioStream ssl(ctx, ioc);
1✔
495
         ssl.next_layer().connect(remote);
1✔
496
         error_code ec;
1✔
497

498
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
499

500
         Test::Result result("sync write_some success");
1✔
501
         result.confirm("writes the correct data", remote.str() == test_data());
3✔
502
         result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
503
         result.confirm("does not report an error", !ec);
2✔
504

505
         results.push_back(result);
1✔
506
      }
2✔
507

508
      void test_sync_no_handshake(std::vector<Test::Result>& results) {
1✔
509
         net::io_context ioc;
1✔
510
         TestStream remote{ioc};
1✔
511

512
         auto ctx = get_context();
1✔
513
         Botan::TLS::Stream<TestStream> ssl(ctx, ioc);  // Note that we're not using MockChannel here
1✔
514
         ssl.next_layer().connect(remote);
1✔
515
         error_code ec;
1✔
516

517
         net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
518

519
         Test::Result result("sync write_some without handshake fails gracefully");
1✔
520
         result.confirm("reports an error", ec.failed());
2✔
521

522
         results.push_back(result);
1✔
523
      }
2✔
524

525
      void test_sync_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
526
         net::io_context ioc;
1✔
527
         TestStream remote{ioc};
1✔
528

529
         auto ctx = get_context();
1✔
530
         AsioStream ssl(ctx, ioc);
1✔
531
         ssl.next_layer().connect(remote);
1✔
532
         error_code ec;
1✔
533

534
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
535
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
536
         random_data.fill('4');  // chosen by fair dice roll
1✔
537
         random_data.back() = '5';
1✔
538

539
         std::vector<net::const_buffer> data;
1✔
540
         data.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
541
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
542
            data.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
543
         }
544

545
         auto bytes_transferred = net::write(ssl, data, ec);
1✔
546

547
         Test::Result result("sync write_some buffer sequence");
1✔
548

549
         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
2✔
550
                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
551

552
         result.confirm("writes the correct data",
2✔
553
                        contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
554
         result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
555
         result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
556
         result.confirm("does not report an error", !ec);
2✔
557

558
         results.push_back(result);
1✔
559
      }
3✔
560

561
      void test_sync_write_some_error(std::vector<Test::Result>& results) {
1✔
562
         net::io_context ioc;
1✔
563
         // fail right away
564
         FailCount fc{0, net::error::no_recovery};
1✔
565
         TestStream remote{ioc};
1✔
566

567
         auto ctx = get_context();
1✔
568
         AsioStream ssl(ctx, ioc, fc);
1✔
569
         ssl.next_layer().connect(remote);
1✔
570

571
         error_code ec;
1✔
572

573
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
574

575
         Test::Result result("sync write_some error");
1✔
576
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
577
         result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
578

579
         results.push_back(result);
1✔
580
      }
2✔
581

582
      void test_sync_write_some_throw(std::vector<Test::Result>& results) {
1✔
583
         net::io_context ioc;
1✔
584
         TestStream remote{ioc};
1✔
585

586
         auto ctx = get_context();
1✔
587
         ThrowingAsioStream ssl(ctx, ioc);
1✔
588
         ssl.next_layer().connect(remote);
1✔
589
         error_code ec;
1✔
590

591
         auto bytes_transferred = net::write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), ec);
1✔
592

593
         Test::Result result("sync write_some throw");
1✔
594
         result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
595
         result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
596

597
         results.push_back(result);
1✔
598
      }
2✔
599

600
      void test_async_write_some_success(std::vector<Test::Result>& results) {
1✔
601
         net::io_context ioc;
1✔
602
         TestStream remote{ioc};
1✔
603

604
         auto ctx = get_context();
1✔
605
         AsioStream ssl(ctx, ioc);
1✔
606
         ssl.next_layer().connect(remote);
1✔
607

608
         Test::Result result("async write_some success");
1✔
609

610
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
611
            result.confirm("writes the correct data", remote.str() == test_data());
3✔
612
            result.test_eq("writes the correct amount of data", bytes_transferred, TEST_DATA_SIZE);
1✔
613
            result.confirm("does not report an error", !ec);
2✔
614
         };
1✔
615

616
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
617

618
         ioc.run();
1✔
619
         results.push_back(result);
1✔
620
      }
2✔
621

622
      void test_async_write_some_buffer_sequence(std::vector<Test::Result>& results) {
1✔
623
         net::io_context ioc;
1✔
624
         TestStream remote{ioc};
1✔
625

626
         auto ctx = get_context();
1✔
627
         AsioStream ssl(ctx, ioc);
1✔
628
         ssl.next_layer().connect(remote);
1✔
629

630
         // this should be Botan::TLS::MAX_PLAINTEXT_SIZE + 1024 + 1
631
         std::array<uint8_t, 17 * 1024 + 1> random_data;
1✔
632
         random_data.fill('4');  // chosen by fair dice roll
1✔
633
         random_data.back() = '5';
1✔
634

635
         std::vector<net::const_buffer> src;
1✔
636
         src.emplace_back(net::const_buffer(random_data.data(), 1));
1✔
637
         for(std::size_t i = 1; i < random_data.size(); i += 1024) {
18✔
638
            src.emplace_back(net::const_buffer(random_data.data() + i, 1024));
17✔
639
         }
640

641
         Test::Result result("async write_some buffer sequence");
1✔
642

643
         result.confirm("[precondition] MAX_PLAINTEXT_SIZE is still smaller than random_data.size()",
2✔
644
                        Botan::TLS::MAX_PLAINTEXT_SIZE < random_data.size());
645

646
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
647
            result.confirm("writes the correct data",
2✔
648
                           contains(remote.buffer().data().data(), random_data.data(), random_data.size()));
1✔
649
            result.test_eq("writes the correct amount of data", bytes_transferred, random_data.size());
1✔
650
            result.test_eq("correct number of writes", ssl.next_layer().nwrite(), 2);
1✔
651
            result.confirm("does not report an error", !ec);
2✔
652
         };
1✔
653

654
         net::async_write(ssl, src, write_handler);
1✔
655

656
         ioc.run();
1✔
657
         results.push_back(result);
1✔
658
      }
3✔
659

660
      void test_async_write_some_error(std::vector<Test::Result>& results) {
1✔
661
         net::io_context ioc;
1✔
662
         // fail right away
663
         FailCount fc{0, net::error::no_recovery};
1✔
664
         TestStream remote{ioc};
1✔
665

666
         auto ctx = get_context();
1✔
667
         AsioStream ssl(ctx, ioc, fc);
1✔
668
         ssl.next_layer().connect(remote);
1✔
669

670
         Test::Result result("async write_some error");
1✔
671

672
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
673
            result.test_eq("committed some bytes to the core", bytes_transferred, TEST_DATA_SIZE);
1✔
674
            result.confirm("propagates error code", ec == net::error::no_recovery);
3✔
675
         };
1✔
676

677
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
678

679
         ioc.run();
1✔
680
         results.push_back(result);
1✔
681
      }
2✔
682

683
      void test_async_write_throw(std::vector<Test::Result>& results) {
1✔
684
         net::io_context ioc;
1✔
685
         TestStream remote{ioc};
1✔
686

687
         auto ctx = get_context();
1✔
688
         ThrowingAsioStream ssl(ctx, ioc);
1✔
689
         ssl.next_layer().connect(remote);
1✔
690

691
         Test::Result result("async write_some throw");
1✔
692

693
         auto write_handler = [&](const error_code& ec, std::size_t bytes_transferred) {
2✔
694
            result.test_eq("didn't transfer anything", bytes_transferred, 0);
1✔
695
            result.confirm("propagates error code", ec == ThrowingMockChannel::expected_ec());
3✔
696
         };
1✔
697

698
         net::async_write(ssl, net::const_buffer(TEST_DATA, TEST_DATA_SIZE), write_handler);
1✔
699

700
         ioc.run();
1✔
701
         results.push_back(result);
1✔
702
      }
2✔
703

704
   public:
705
      std::vector<Test::Result> run() override {
1✔
706
         std::vector<Test::Result> results;
1✔
707

708
         test_sync_no_handshake(results);
1✔
709

710
         test_sync_handshake(results);
1✔
711
         test_sync_handshake_error(results);
1✔
712
         test_sync_handshake_throw(results);
1✔
713

714
         test_async_handshake(results);
1✔
715
         test_async_handshake_error(results);
1✔
716
         test_async_handshake_throw(results);
1✔
717

718
         test_sync_read_some_success(results);
1✔
719
         test_sync_read_some_buffer_sequence(results);
1✔
720
         test_sync_read_some_error(results);
1✔
721
         test_sync_read_some_throw(results);
1✔
722
         test_sync_read_zero_buffer(results);
1✔
723

724
         test_async_read_some_success(results);
1✔
725
         test_async_read_some_buffer_sequence(results);
1✔
726
         test_async_read_some_error(results);
1✔
727
         test_async_read_some_throw(results);
1✔
728
         test_async_read_zero_buffer(results);
1✔
729

730
         test_sync_write_some_success(results);
1✔
731
         test_sync_write_some_buffer_sequence(results);
1✔
732
         test_sync_write_some_error(results);
1✔
733
         test_sync_write_some_throw(results);
1✔
734

735
         test_async_write_some_success(results);
1✔
736
         test_async_write_some_buffer_sequence(results);
1✔
737
         test_async_write_some_error(results);
1✔
738
         test_async_write_throw(results);
1✔
739

740
         return results;
1✔
741
      }
×
742
};
743

744
BOTAN_REGISTER_TEST("tls", "tls_asio_stream", Asio_Stream_Tests);
745

746
}  // namespace Botan_Tests
747

748
   #endif  // BOOST_VERSION
749
#endif     // BOTAN_HAS_TLS && BOTAN_HAS_BOOST_ASIO
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