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

randombit / botan / 11844561993

14 Nov 2024 07:58PM UTC coverage: 91.178% (+0.1%) from 91.072%
11844561993

Pull #4435

github

web-flow
Merge 81dcb29da into e430f157a
Pull Request #4435: Test duration values ​​are now presented in seconds with six digits of precision. Tests without time measurements have been edited.

91856 of 100744 relevant lines covered (91.18%)

9311006.71 hits per line

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

99.72
/src/tests/test_tls_record_layer_13.cpp
1
/*
2
* (C) 2021 Jack Lloyd
3
* (C) 2021 Hannes Rantzsch, René Meusel - neXenio
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_TLS_13)
11

12
   #include <botan/tls_ciphersuite.h>
13
   #include <botan/tls_exceptn.h>
14
   #include <botan/tls_magic.h>
15
   #include <botan/internal/stl_util.h>
16
   #include <botan/internal/tls_cipher_state.h>
17
   #include <botan/internal/tls_reader.h>
18

19
   #include <botan/internal/tls_channel_impl_13.h>
20
   #include <botan/internal/tls_record_layer_13.h>
21

22
   #include <array>
23

24
namespace Botan_Tests {
25

26
namespace {
27

28
namespace TLS = Botan::TLS;
29

30
using Records = std::vector<TLS::Record>;
31

32
TLS::Record_Layer record_layer_client(const bool skip_client_hello = false) {
44✔
33
   auto rl = TLS::Record_Layer(TLS::Connection_Side::Client);
14✔
34

35
   // this is relevant for tests that rely on the legacy version in the record
36
   if(skip_client_hello) {
44✔
37
      rl.disable_sending_compat_mode();
36✔
38
   }
39

40
   return rl;
44✔
41
}
42

43
TLS::Record_Layer record_layer_server(const bool skip_client_hello = false) {
23✔
44
   auto rl = TLS::Record_Layer(TLS::Connection_Side::Server);
13✔
45

46
   // this is relevant for tests that rely on the legacy version in the record
47
   if(skip_client_hello) {
12✔
48
      rl.disable_receiving_compat_mode();
4✔
49
   }
50

51
   return rl;
12✔
52
}
53

54
class Mocked_Secret_Logger : public Botan::TLS::Secret_Logger {
23✔
55
   public:
56
      void maybe_log_secret(std::string_view, std::span<const uint8_t>) const override {}
49✔
57
};
58

59
std::unique_ptr<TLS::Cipher_State> rfc8448_rtt1_handshake_traffic(
23✔
60
   Botan::TLS::Connection_Side side = Botan::TLS::Connection_Side::Client) {
61
   const auto transcript_hash = Botan::hex_decode(
23✔
62
      "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed"
63
      "d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8");
23✔
64
   auto shared_secret = Botan::hex_decode_locked(
23✔
65
      "8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d"
66
      "35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d");
23✔
67
   auto cipher = TLS::Ciphersuite::from_name("AES_128_GCM_SHA256").value();
23✔
68
   Mocked_Secret_Logger logger;
23✔
69
   return TLS::Cipher_State::init_with_server_hello(side, std::move(shared_secret), cipher, transcript_hash, logger);
23✔
70
}
69✔
71

72
std::vector<Test::Result> read_full_records() {
1✔
73
   const auto client_hello_record = Botan::hex_decode(  // from RFC 8448
1✔
74
      "16 03 01 00 c4 01 00 00 c0 03 03 cb"
75
      "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12"
76
      "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00"
77
      "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01"
78
      "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02"
79
      "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d"
80
      "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d"
81
      "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e"
82
      "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02"
83
      "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01");
1✔
84
   const auto ccs_record = Botan::hex_decode("14 03 03 00 01 01");
1✔
85

86
   return {CHECK("change cipher spec",
1✔
87
                 [&](auto& result) {
1✔
88
                    result.start_timer();
1✔
89
                    auto rl = record_layer_server();
1✔
90

91
                    rl.copy_data(ccs_record);
1✔
92
                    auto read = rl.next_record();
1✔
93
                    result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
94

95
                    auto record = std::get<TLS::Record>(read);
1✔
96
                    result.confirm("received CCS", record.type == TLS::Record_Type::ChangeCipherSpec);
2✔
97
                    result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01"));
2✔
98

99
                    result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
100
                    result.end_timer();
1✔
101
                 }),
3✔
102

103
           CHECK("two CCS messages",
104
                 [&](auto& result) {
1✔
105
                    result.start_timer();
1✔
106
                    const auto two_ccs_records = Botan::concat(ccs_record, ccs_record);
1✔
107

108
                    auto rl = record_layer_server();
1✔
109

110
                    rl.copy_data(two_ccs_records);
1✔
111

112
                    auto read = rl.next_record();
1✔
113
                    result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
114
                    auto record = std::get<TLS::Record>(read);
1✔
115

116
                    result.confirm("received CCS 1", record.type == TLS::Record_Type::ChangeCipherSpec);
2✔
117
                    result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01"));
2✔
118

119
                    read = rl.next_record();
3✔
120
                    result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
121
                    record = std::get<TLS::Record>(read);
1✔
122

123
                    result.confirm("received CCS 2", record.type == TLS::Record_Type::ChangeCipherSpec);
2✔
124
                    result.test_eq("CCS byte is 0x01", record.fragment, Botan::hex_decode("01"));
2✔
125

126
                    result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
127
                    result.end_timer();
1✔
128
                 }),
4✔
129

130
           CHECK("read full handshake message",
131
                 [&](auto& result) {
1✔
132
                    result.start_timer();
1✔
133
                    auto rl = record_layer_server();
1✔
134
                    rl.copy_data(client_hello_record);
1✔
135

136
                    auto read = rl.next_record();
1✔
137
                    result.confirm("received something", std::holds_alternative<TLS::Record>(read));
2✔
138

139
                    auto rec = std::get<TLS::Record>(read);
1✔
140
                    result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake);
2✔
141
                    result.test_eq("contains the full handshake message",
2✔
142
                                   Botan::secure_vector<uint8_t>(client_hello_record.begin() + TLS::TLS_HEADER_SIZE,
1✔
143
                                                                 client_hello_record.end()),
144
                                   rec.fragment);
145

146
                    result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
147
                    result.end_timer();
1✔
148
                 }),
3✔
149

150
           CHECK("read full handshake message followed by CCS", [&](auto& result) {
1✔
151
              result.start_timer();
1✔
152
              const auto payload = Botan::concat(client_hello_record, ccs_record);
1✔
153

154
              auto rl = record_layer_server();
1✔
155
              rl.copy_data(payload);
1✔
156

157
              auto read = rl.next_record();
1✔
158
              result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
159

160
              auto rec = std::get<TLS::Record>(read);
1✔
161
              result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake);
2✔
162
              result.test_eq("contains the full handshake message",
2✔
163
                             Botan::secure_vector<uint8_t>(client_hello_record.begin() + TLS::TLS_HEADER_SIZE,
1✔
164
                                                           client_hello_record.end()),
165
                             rec.fragment);
166

167
              read = rl.next_record();
3✔
168
              result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
169

170
              rec = std::get<TLS::Record>(read);
1✔
171
              result.confirm("received CCS record", rec.type == TLS::Record_Type::ChangeCipherSpec);
2✔
172
              result.test_eq("CCS byte is 0x01", rec.fragment, Botan::hex_decode("01"));
2✔
173

174
              result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
175
              result.end_timer();
1✔
176
           })};
9✔
177
}
3✔
178

179
std::vector<Test::Result> basic_sanitization_parse_records(TLS::Connection_Side side) {
2✔
180
   auto parse_records = [side](const std::vector<uint8_t>& data, TLS::Cipher_State* cs = nullptr) {
24✔
181
      auto rl = ((side == TLS::Connection_Side::Client) ? record_layer_client(true) : record_layer_server());
33✔
182
      rl.copy_data(data);
36✔
183
      return rl.next_record(cs);
22✔
184
   };
8✔
185

186
   return {CHECK("'receive' empty data",
2✔
187
                 [&](auto& result) {
2✔
188
                    result.start_timer();
2✔
189
                    auto read = parse_records({});
2✔
190
                    result.require("needs bytes", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
191
                    result.test_eq(
2✔
192
                       "need all the header bytes", std::get<TLS::BytesNeeded>(read), Botan::TLS::TLS_HEADER_SIZE);
2✔
193
                    result.end_timer();
2✔
194
                 }),
2✔
195

196
           CHECK("incomplete header asks for more data",
197
                 [&](auto& result) {
2✔
198
                    result.start_timer();
2✔
199
                    std::vector<uint8_t> partial_header{'\x23', '\x03', '\x03'};
2✔
200
                    auto read = parse_records(partial_header);
2✔
201
                    result.require("returned 'bytes needed'", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
202

203
                    result.test_eq("asks for some more bytes",
2✔
204
                                   std::get<TLS::BytesNeeded>(read),
2✔
205
                                   Botan::TLS::TLS_HEADER_SIZE - partial_header.size());
2✔
206
                     result.end_timer();
2✔
207
                 }),
4✔
208

209
           CHECK("complete header asks for enough data to finish processing the record",
210
                 [&](auto& result) {
2✔
211
                    result.start_timer();
2✔
212
                    std::vector<uint8_t> full_header{'\x17', '\x03', '\x03', '\x00', '\x42'};
2✔
213
                    auto read = parse_records(full_header);
2✔
214
                    result.require("returned 'bytes needed'", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
215

216
                    result.test_eq("asks for many more bytes", std::get<TLS::BytesNeeded>(read), 0x42);
2✔
217
                    result.end_timer();
2✔
218
                 }),
4✔
219

220
           CHECK("received an empty record (that is not application data)",
221
                 [&](auto& result) {
2✔
222
                    result.start_timer();
2✔
223
                    std::vector<uint8_t> empty_record{'\x16', '\x03', '\x03', '\x00', '\x00'};
2✔
224
                    result.test_throws("record empty", "empty record received", [&] { parse_records(empty_record); });
6✔
225
                    result.end_timer();
2✔
226
                 }),
2✔
227

228
           CHECK("received the maximum size of an unprotected record",
229
                 [&](auto& result) {
2✔
230
                    result.start_timer();
2✔
231
                    std::vector<uint8_t> full_record{'\x16', '\x03', '\x03', '\x40', '\x00'};
2✔
232
                    full_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE);
2✔
233
                    auto read = parse_records(full_record);
2✔
234
                    result.confirm("returned 'record'", !std::holds_alternative<TLS::BytesNeeded>(read));
4✔
235
                    result.end_timer();
2✔
236
                 }),
4✔
237

238
           CHECK("received too many bytes in one protected record",
239
                 [&](auto& result) {
2✔
240
                    result.start_timer();
2✔
241
                    std::vector<uint8_t> huge_record{'\x17', '\x03', '\x03', '\x41', '\x01'};
2✔
242
                    huge_record.resize(TLS::MAX_CIPHERTEXT_SIZE_TLS13 + TLS::TLS_HEADER_SIZE + 1);
2✔
243
                    result.test_throws("record too big", "Received an encrypted record that exceeds maximum size", [&] {
4✔
244
                       parse_records(huge_record);
2✔
245
                    });
246
                    result.end_timer();
2✔
247
                 }),
2✔
248

249
           CHECK("decryption would result in too large plaintext",
250
                 [&](auto& result) {
2✔
251
                     result.start_timer();
2✔
252
                    // In this case the ciphertext is within the allowed bounds, but the
253
                    // decrypted plaintext would be too large.
254
                    std::vector<uint8_t> huge_record{'\x17', '\x03', '\x03', '\x40', '\x12'};
2✔
255
                    huge_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE + 16 /* AES-GCM tag */
2✔
256
                                       + 1                                                 /* encrypted type */
257
                                       + 1 /* illegal */);
258

259
                    auto cs = rfc8448_rtt1_handshake_traffic();
2✔
260
                    result.test_throws("record too big",
4✔
261
                                       "Received an encrypted record that exceeds maximum plaintext size",
262
                                       [&] { parse_records(huge_record, cs.get()); });
4✔
263
                    result.end_timer();
2✔
264
                 }),
4✔
265

266
           CHECK("received too many bytes in one unprotected record",
267
                 [&](auto& result) {
2✔
268
                    result.start_timer();
2✔
269
                    std::vector<uint8_t> huge_record{'\x16', '\x03', '\x03', '\x40', '\x01'};
2✔
270
                    huge_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE + 1);
2✔
271
                    result.test_throws("record too big", "Received a record that exceeds maximum size", [&] {
4✔
272
                       parse_records(huge_record);
2✔
273
                    });
274
                    result.end_timer();
2✔
275
                 }),
2✔
276

277
           CHECK("invalid record type",
278
                 [&](auto& result) {
2✔
279
                    result.start_timer();
2✔
280
                    std::vector<uint8_t> invalid_record_type{'\x42', '\x03', '\x03', '\x41', '\x01'};
2✔
281
                    result.test_throws("invalid record type", "TLS record type had unexpected value", [&] {
4✔
282
                       parse_records(invalid_record_type);
2✔
283
                    });
284
                    result.end_timer();
2✔
285
                 }),
2✔
286

287
           CHECK("invalid record version",
288
                 [&](auto& result) {
2✔
289
                    result.start_timer();
2✔
290
                    std::vector<uint8_t> invalid_record_version{'\x17', '\x13', '\x37', '\x00', '\x01', '\x42'};
2✔
291
                    result.test_throws("invalid record version", "Received unexpected record version", [&] {
4✔
292
                       parse_records(invalid_record_version);
2✔
293
                    });
294
                    result.end_timer();
2✔
295
                 }),
2✔
296

297
           CHECK("initial received record versions might be 0x03XX ",
298
                 [&](auto& result) {
2✔
299
                    result.start_timer();
2✔
300
                    auto rl = record_layer_client();
2✔
301
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x00, 0x00, 0x01, 0x42});
2✔
302
                    result.test_no_throw("0x03 0x00 should be fine for first records", [&] { rl.next_record(); });
6✔
303

304
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x01, 0x00, 0x01, 0x42});
2✔
305
                    result.test_no_throw("0x03 0x01 should be fine for first records", [&] { rl.next_record(); });
6✔
306

307
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x02, 0x00, 0x01, 0x42});
2✔
308
                    result.test_no_throw("0x03 0x02 should be fine for first records", [&] { rl.next_record(); });
6✔
309

310
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x03, 0x00, 0x01, 0x42});
2✔
311
                    result.test_no_throw("0x03 0x03 should be fine for first records", [&] { rl.next_record(); });
8✔
312

313
                    rl.disable_receiving_compat_mode();
2✔
314

315
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x03, 0x00, 0x01, 0x42});
2✔
316
                    result.test_no_throw("0x03 0x03 is okay regardless", [&] { rl.next_record(); });
6✔
317

318
                    rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x01, 0x00, 0x01, 0x42});
2✔
319
                    result.test_throws("0x03 0x01 not okay once client hello was received", [&] { rl.next_record(); });
6✔
320
                    result.end_timer();
2✔
321
                 }),
2✔
322

323
           CHECK("malformed change cipher spec",
324
                 [&](auto& result) {
2✔
325
                    result.start_timer();
2✔
326
                    std::vector<uint8_t> invalid_ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x02'};
2✔
327
                    result.test_throws("invalid CCS record", "malformed change cipher spec record received", [&] {
4✔
328
                       parse_records(invalid_ccs_record);
2✔
329
                    });
330
                    result.end_timer();
2✔
331
                 })
2✔
332

333
   };
26✔
334
}
2✔
335

336
std::vector<Test::Result> basic_sanitization_parse_records_client() {
1✔
337
   return basic_sanitization_parse_records(TLS::Connection_Side::Client);
1✔
338
}
339

340
std::vector<Test::Result> basic_sanitization_parse_records_server() {
1✔
341
   return basic_sanitization_parse_records(TLS::Connection_Side::Server);
1✔
342
}
343

344
std::vector<Test::Result> read_fragmented_records() {
1✔
345
   TLS::Record_Layer rl = record_layer_client(true);
1✔
346

347
   auto wait_for_more_bytes =
1✔
348
      [](Botan::TLS::BytesNeeded bytes_needed, auto& record_layer, std::vector<uint8_t> bytes, auto& result) {
7✔
349
         record_layer.copy_data(bytes);
7✔
350
         const auto rlr = record_layer.next_record();
7✔
351
         if(result.confirm("waiting for bytes", std::holds_alternative<TLS::BytesNeeded>(rlr))) {
14✔
352
            result.test_eq("right amount", std::get<TLS::BytesNeeded>(rlr), bytes_needed);
14✔
353
         }
354
      };
7✔
355

356
   return {CHECK("change cipher spec in many small pieces",
1✔
357
                 [&](auto& result) {
1✔
358
                    result.start_timer();
1✔
359
                    std::vector<uint8_t> ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'};
1✔
360

361
                    wait_for_more_bytes(4, rl, {'\x14'}, result);
1✔
362
                    wait_for_more_bytes(3, rl, {'\x03'}, result);
1✔
363
                    wait_for_more_bytes(2, rl, {'\x03'}, result);
1✔
364
                    wait_for_more_bytes(1, rl, {'\x00'}, result);
1✔
365
                    wait_for_more_bytes(1, rl, {'\x01'}, result);
1✔
366

367
                    rl.copy_data(std::vector<uint8_t>{'\x01'});
1✔
368
                    auto res1 = rl.next_record();
1✔
369
                    result.require("received something 1", std::holds_alternative<TLS::Record>(res1));
2✔
370

371
                    auto rec1 = std::get<TLS::Record>(res1);
1✔
372
                    result.confirm("received CCS", rec1.type == TLS::Record_Type::ChangeCipherSpec);
2✔
373
                    result.test_eq("CCS byte is 0x01", rec1.fragment, Botan::hex_decode("01"));
2✔
374

375
                    result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
376
                    result.end_timer();
1✔
377
                 }),
3✔
378

379
           CHECK("two change cipher specs in several pieces", [&](auto& result) {
1✔
380
              result.start_timer();
1✔
381
              wait_for_more_bytes(1, rl, {'\x14', '\x03', '\x03', '\x00'}, result);
1✔
382

383
              rl.copy_data(std::vector<uint8_t>{'\x01', '\x01', /* second CCS starts here */ '\x14', '\x03'});
1✔
384

385
              auto res2 = rl.next_record();
1✔
386
              result.require("received something 2", std::holds_alternative<TLS::Record>(res2));
2✔
387

388
              auto rec2 = std::get<TLS::Record>(res2);
1✔
389
              result.confirm("received CCS", rec2.type == TLS::Record_Type::ChangeCipherSpec);
2✔
390
              result.confirm("demands more bytes", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
391

392
              wait_for_more_bytes(2, rl, {'\x03'}, result);
1✔
393

394
              rl.copy_data(std::vector<uint8_t>{'\x00', '\x01', '\x01'});
1✔
395
              auto res3 = rl.next_record();
1✔
396
              result.require("received something 3", std::holds_alternative<TLS::Record>(res3));
2✔
397

398
              auto rec3 = std::get<TLS::Record>(res3);
1✔
399
              result.confirm("received CCS", rec3.type == TLS::Record_Type::ChangeCipherSpec);
2✔
400

401
              result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
402
              result.end_timer();
1✔
403
           })};
7✔
404
}
2✔
405

406
std::vector<Test::Result> write_records() {
1✔
407
   auto cs = rfc8448_rtt1_handshake_traffic();
1✔
408
   return {
1✔
409
      CHECK("prepare an zero-length application data fragment",
410
            [&](auto& result) {
1✔
411
               result.start_timer();
1✔
412
               auto record =
1✔
413
                  record_layer_client().prepare_records(Botan::TLS::Record_Type::ApplicationData, {}, cs.get());
1✔
414

415
               result.require("record header was added",
1✔
416
                              record.size() > Botan::TLS::TLS_HEADER_SIZE + 1 /* encrypted content type */);
1✔
417
               result.end_timer();
1✔
418
            }),
1✔
419
      CHECK("prepare a client hello",
420
            [&](auto& result) {
1✔
421
               result.start_timer();
1✔
422
               const auto client_hello_msg = Botan::hex_decode(  // from RFC 8448
1✔
423
                  "01 00 00 c0 03 03 cb"
424
                  "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12"
425
                  "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00"
426
                  "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01"
427
                  "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02"
428
                  "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d"
429
                  "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d"
430
                  "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e"
431
                  "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02"
432
                  "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01");
433
               auto record =
1✔
434
                  record_layer_client().prepare_records(Botan::TLS::Record_Type::Handshake, client_hello_msg);
1✔
435

436
               result.require("record header was added",
2✔
437
                              record.size() == client_hello_msg.size() + Botan::TLS::TLS_HEADER_SIZE);
1✔
438

439
               const auto header = std::vector<uint8_t>(record.cbegin(), record.cbegin() + Botan::TLS::TLS_HEADER_SIZE);
1✔
440
               result.test_eq("record header is well-formed", header, Botan::hex_decode("16030100c4"));
2✔
441
               result.end_timer();
1✔
442
            }),
3✔
443
      CHECK("prepare a dummy CCS",
444
            [&](auto& result) {
1✔
445
               result.start_timer();
1✔
446
               std::array<uint8_t, 1> ccs_content = {0x01};
1✔
447
               auto record =
1✔
448
                  record_layer_client(true).prepare_records(Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content);
449
               result.require("record was created", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1);
2✔
450

451
               result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101"));
2✔
452
               result.end_timer();
1✔
453
            }),
1✔
454
      CHECK("cannot prepare non-dummy CCS",
455
            [&](auto& result) {
1✔
456
               result.start_timer();
1✔
457
               result.test_throws("cannot create non-dummy CCS", "TLS 1.3 deprecated CHANGE_CIPHER_SPEC", [] {
3✔
458
                  const auto ccs_content = Botan::hex_decode("de ad be ef");
1✔
459
                  record_layer_client().prepare_records(Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content);
1✔
460
               });
×
461
               result.end_timer();
1✔
462
            }),
1✔
463
      CHECK("large messages are sharded", [&](auto& result) {
1✔
464
         result.start_timer();
1✔
465
         const std::vector<uint8_t> large_client_hello(Botan::TLS::MAX_PLAINTEXT_SIZE + 4096);
1✔
466
         auto record = record_layer_client().prepare_records(Botan::TLS::Record_Type::Handshake, large_client_hello);
1✔
467

468
         result.test_gte("produces at least two record headers",
1✔
469
                         record.size(),
470
                         large_client_hello.size() + 2 * Botan::TLS::TLS_HEADER_SIZE);
1✔
471
         result.end_timer();
1✔
472
      })};
9✔
473
}
2✔
474

475
std::vector<Test::Result> read_encrypted_records() {
1✔
476
   // this is the "complete record" server hello portion
477
   // from RFC 8448 page 7
478
   const auto server_hello = Botan::hex_decode(
1✔
479
      "16 03 03 00 5a 02 00 00 56 03 03 a6"
480
      "af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14"
481
      "34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00"
482
      "1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6"
483
      "cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04");
1✔
484

485
   // this is the "complete record" encrypted server hello portion
486
   // from RFC 8448 page 9
487
   const auto encrypted_record = Botan::hex_decode(
1✔
488
      "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf"
489
      "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df"
490
      "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45"
491
      "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3"
492
      "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b"
493
      "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9"
494
      "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf"
495
      "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d"
496
      "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55"
497
      "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f"
498
      "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6"
499
      "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac"
500
      "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea"
501
      "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e"
502
      "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6"
503
      "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb"
504
      "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59"
505
      "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e"
506
      "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af"
507
      "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37"
508
      "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c"
509
      "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88"
510
      "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80"
511
      "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69"
512
      "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99"
513
      "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11"
514
      "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51"
515
      "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42"
516
      "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f"
517
      "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd"
518
      "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af"
519
      "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da"
520
      "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b");
1✔
521

522
   // the record above padded with 42 zeros
523
   const auto encrypted_record_with_padding = Botan::hex_decode(
1✔
524
      "17 03 03 02 cc d1 ff 33 4a 56 f5 bf f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45"
525
      "e4 89 e7 f3 3a f3 5e df 78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61"
526
      "2e f9 f9 45 cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3"
527
      "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b d9 ae fb 0e"
528
      "57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9 b1 18 3e f3 ab 20 e3 7d"
529
      "57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf 51 42 73 25 25 0c 7d 0e 50 92 89 44"
530
      "4c 9b 3a 64 8f 1d 71 03 5d 2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb"
531
      "b3 60 98 72 55 cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a"
532
      "8f d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6 86 94 5b"
533
      "a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac 66 27 2f d8 fb 33 0e"
534
      "f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea 52 0a 56 a8 d6 50 f5 63 aa d2 74"
535
      "09 96 0d ca 63 d3 e6 88 61 1e a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42"
536
      "72 96 8a 26 4e d6 54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a"
537
      "cb bb 31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59 62 22"
538
      "45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e 92 ea 33 0f ae ea"
539
      "6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af 36 87 90 18 e3 f2 52 10 7f 24"
540
      "3d 24 3d c7 33 9d 56 84 c8 b0 37 8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5"
541
      "e8 28 0a 2b 48 05 2c f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6"
542
      "6f 99 88 2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80 f8"
543
      "5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69 18 a3 96 fa 48"
544
      "a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99 2f 67 f8 af e6 7f 76 91 3f"
545
      "a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11 c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b"
546
      "bf 10 dc 35 ae 69 f5 51 56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30"
547
      "38 eb ba 42 f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f"
548
      "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd d5 02 78 40"
549
      "16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af 93 98 28 fd 4a e3 79 4e"
550
      "44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da 04 d8 68 77 bb e0 dc ce f9 01 ed 32"
551
      "59 50 7a 0c d0 62 3f 90 1b 5c 89 d4 b4 f2 d1 56 f6 da 4f 3e c5 fd 2d e5 e2"
552
      "fa 44 23 0a e0 c9 dd dd bb a8 be db d9 d7 f6 b8 3d 56 4c a5 47");
1✔
553

554
   auto parse_records = [](const std::vector<uint8_t>& data) {
12✔
555
      auto rl = record_layer_client(true);
11✔
556
      rl.copy_data(data);
11✔
557
      return rl;
11✔
558
   };
×
559

560
   return {
1✔
561
      CHECK("read encrypted server hello extensions",
562
            [&](Test::Result& result) {
1✔
563
               result.start_timer();
1✔
564
               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
565
               auto rl = parse_records(encrypted_record);
1✔
566

567
               auto res = rl.next_record(cs.get());
1✔
568
               result.require("some records decrypted", !std::holds_alternative<Botan::TLS::BytesNeeded>(res));
1✔
569
               auto record = std::get<TLS::Record>(res);
1✔
570

571
               result.test_is_eq("inner type was 'HANDSHAKE'", record.type, Botan::TLS::Record_Type::Handshake);
1✔
572
               result.test_eq("decrypted payload length", record.fragment.size(), 657 /* taken from RFC 8448 */);
1✔
573

574
               result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
575
               result.end_timer();
1✔
576
            }),
3✔
577

578
      CHECK("premature application data",
579
            [&](Test::Result& result) {
1✔
580
               result.start_timer();
1✔
581
               auto rl = record_layer_client(true);
1✔
582
               rl.copy_data(encrypted_record);
1✔
583

584
               result.test_throws<Botan::TLS::TLS_Exception>(
2✔
585
                  "cannot process encrypted data with uninitialized cipher state",
586
                  "premature Application Data received",
587
                  [&] { auto res = rl.next_record(nullptr); });
2✔
588
               result.end_timer();
1✔
589
            }),
1✔
590

591
      CHECK("decryption fails due to bad MAC",
592
            [&](Test::Result& result) {
1✔
593
               result.start_timer();
1✔
594
               auto tampered_encrypted_record = encrypted_record;
1✔
595
               tampered_encrypted_record.back() = '\x42';  // changing one payload byte causes the MAC check to fails
1✔
596

597
               result.test_throws<Botan::Invalid_Authentication_Tag>("broken record detected", [&] {
2✔
598
                  auto cs = rfc8448_rtt1_handshake_traffic();
1✔
599
                  auto rl = parse_records(tampered_encrypted_record);
1✔
600
                  rl.next_record(cs.get());
1✔
601
               });
1✔
602
               result.end_timer();
1✔
603
            }),
1✔
604

605
      CHECK("decryption fails due to too short record",
606
            [&](Test::Result& result) {
1✔
607
               result.start_timer();
1✔
608
               const auto short_record = Botan::hex_decode("17 03 03 00 08 de ad be ef ba ad f0 0d");
1✔
609

610
               result.test_throws<Botan::TLS::TLS_Exception>("too short to decrypt", [&] {
2✔
611
                  auto cs = rfc8448_rtt1_handshake_traffic();
1✔
612
                  auto rl = parse_records(short_record);
1✔
613
                  rl.next_record(cs.get());
1✔
614
               });
1✔
615
               result.end_timer();
1✔
616
            }),
1✔
617

618
      CHECK("protected Change Cipher Spec message is illegal",
619
            [&](Test::Result& result) {
1✔
620
               result.start_timer();
1✔
621
               // factored message, encrypted under the same key as `encrypted_record`
622
               const auto protected_ccs = Botan::hex_decode("1703030012D8EBBBE055C8167D5690EC67DEA9A525B036");
1✔
623

624
               result.test_throws<Botan::TLS::TLS_Exception>(
2✔
625
                  "illegal state causes TLS alert", "protected change cipher spec received", [&] {
1✔
626
                     auto cs = rfc8448_rtt1_handshake_traffic();
1✔
627
                     auto rl = parse_records(protected_ccs);
1✔
628
                     rl.next_record(cs.get());
1✔
629
                  });
1✔
630
               result.end_timer();
1✔
631
            }),
1✔
632

633
      CHECK("unprotected CCS is legal when encrypted traffic is expected",
634
            [&](Test::Result& result) {
1✔
635
               result.start_timer();
1✔
636
               const auto ccs_record = Botan::hex_decode("14 03 03 00 01 01");
1✔
637

638
               result.test_no_throw("CCS is acceptable", [&] {
2✔
639
                  auto cs = rfc8448_rtt1_handshake_traffic();  // expect encrypted traffic
1✔
640
                  auto rl = parse_records(ccs_record);
1✔
641
                  rl.next_record(cs.get());
2✔
642
               });
1✔
643
               result.end_timer();
1✔
644
            }),
1✔
645

646
      CHECK("unprotected Alert message might be legal",
647
            [&](Test::Result& result) {
1✔
648
               result.start_timer();
1✔
649
               const auto alert = Botan::hex_decode("15030300020232");  // decode error
1✔
650
               const auto hsmsg = Botan::hex_decode(                    // factored 'certificate_request' message
1✔
651
                  "160303002a0d000027000024000d0020001e040305030603"
652
                  "020308040805080604010501060102010402050206020202");
1✔
653

654
               result.test_no_throw("Server allows unprotected alerts after its first flight", [&] {
2✔
655
                  auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Server);
1✔
656
                  auto rl = parse_records(alert);
1✔
657
                  rl.next_record(cs.get());
2✔
658
               });
1✔
659

660
               result.test_throws<Botan::TLS::TLS_Exception>(
2✔
661
                  "Unprotected handshake messages are not allowed for servers",
662
                  "unprotected record received where protected traffic was expected",
663
                  [&] {
1✔
664
                     auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Server);
1✔
665
                     auto rl = parse_records(hsmsg);
1✔
666
                     rl.next_record(cs.get());
1✔
667
                  });
1✔
668

669
               result.test_throws<Botan::TLS::TLS_Exception>(
2✔
670
                  "Clients don't allow unprotected alerts after Server Hello",
671
                  "unprotected record received where protected traffic was expected",
672
                  [&] {
1✔
673
                     auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Client);
1✔
674
                     auto rl = parse_records(alert);
1✔
675
                     rl.next_record(cs.get());
1✔
676
                  });
1✔
677

678
               result.test_throws<Botan::TLS::TLS_Exception>(
2✔
679
                  "Unprotected handshake messages are not allowed for clients",
680
                  "unprotected record received where protected traffic was expected",
681
                  [&] {
1✔
682
                     auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Client);
1✔
683
                     auto rl = parse_records(hsmsg);
1✔
684
                     rl.next_record(cs.get());
1✔
685
                  });
1✔
686
                  result.end_timer();
1✔
687
            }),
2✔
688

689
      CHECK("unprotected traffic is illegal when encrypted traffic is expected",
690
            [&](Test::Result& result) {
1✔
691
               result.start_timer();
1✔
692
               result.test_throws("unprotected record is unacceptable", [&] {
2✔
693
                  auto cs = rfc8448_rtt1_handshake_traffic();  // expect encrypted traffic
1✔
694
                  auto rl = parse_records(server_hello);
1✔
695
                  rl.next_record(cs.get());
1✔
696
               });
1✔
697
               result.end_timer();
1✔
698
            }),
1✔
699

700
      CHECK("read fragmented application data",
701
            [&](Test::Result& result) {
1✔
702
               result.start_timer();
1✔
703
               const auto encrypted = Botan::hex_decode(
1✔
704
                  "17 03 03 00 1A 90 78 6D 7E 6F A8 F7 67 1F 6D 05 F7 24 18 F5 DB 43 F7 0B 9E 48 A6 96 B6 5B EC"
705
                  "17 03 03 00 28 6C 21 B5 B8 D8 1B 85 5C 17 0E C7 9B 2C 28 85 85 51 29 2F 71 14 F3 D7 BD D5 D1"
706
                  "80 C2 E9 3D EC 84 3B 8D 41 30 D8 C8 C5 D8"
707
                  "17 03 03 00 21 29 9A B0 5A EA 3F 8A DE 05 12 E0 6B 4A 28 C3 E2 69 2F 58 82 F1 A3 45 04 EA 16"
708
                  "14 72 39 6F A1 F3 D3 ");
1✔
709
               const std::vector<std::vector<uint8_t>> plaintext_records = {
1✔
710
                  Botan::hex_decode("00 01 02 03 04 05 06 07 08"),
711
                  Botan::hex_decode("09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f"),
712
                  Botan::hex_decode("20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f")};
4✔
713

714
               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
715
               // advance with arbitrary hashes that were used to produce the input data
716
               Mocked_Secret_Logger logger;
1✔
717
               cs->advance_with_server_finished(
1✔
718
                  Botan::hex_decode("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c"), logger);
1✔
719
               cs->advance_with_client_finished(
1✔
720
                  Botan::hex_decode("305e4a0a7cee581b282c571b251b20138a1a6a21918937a6bb95b1e9ba1b5cac"));
1✔
721

722
               auto rl = parse_records(encrypted);
1✔
723
               auto res = rl.next_record(cs.get());
1✔
724
               result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
725
               auto records = std::get<TLS::Record>(res);
1✔
726
               result.test_eq("first record", records.fragment, plaintext_records.at(0));
1✔
727

728
               res = rl.next_record(cs.get());
2✔
729
               result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
730
               records = std::get<TLS::Record>(res);
1✔
731
               result.test_eq("second record", records.fragment, plaintext_records.at(1));
1✔
732

733
               res = rl.next_record(cs.get());
2✔
734
               result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
735
               records = std::get<TLS::Record>(res);
1✔
736
               result.test_eq("third record", records.fragment, plaintext_records.at(2));
1✔
737

738
               result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
739
               result.end_timer();
1✔
740
            }),
6✔
741

742
      CHECK(
743
         "read coalesced server hello and encrypted extensions",
744
         [&](Test::Result& result) {
1✔
745
            result.start_timer();
1✔
746
            // contains the plaintext server hello and the encrypted extensions in one go
747
            auto coalesced = server_hello;
1✔
748
            coalesced.insert(coalesced.end(), encrypted_record.cbegin(), encrypted_record.cend());
1✔
749

750
            auto client = record_layer_client(true);
1✔
751
            client.copy_data(coalesced);
1✔
752

753
            const auto srv_hello = client.next_record(nullptr);
1✔
754
            result.confirm("read a record", std::holds_alternative<TLS::Record>(srv_hello));
2✔
755
            result.confirm("is handshake record", std::get<TLS::Record>(srv_hello).type == TLS::Record_Type::Handshake);
2✔
756

757
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
758
            const auto enc_exts = client.next_record(cs.get());
1✔
759
            result.confirm("read a record", std::holds_alternative<TLS::Record>(enc_exts));
2✔
760
            result.confirm("is handshake record", std::get<TLS::Record>(enc_exts).type == TLS::Record_Type::Handshake);
2✔
761
            result.end_timer();
1✔
762
         }),
4✔
763

764
      CHECK("read a padded record",
765
            [&](Test::Result& result) {
1✔
766
               result.start_timer();
1✔
767
               auto client = record_layer_client(true);
1✔
768
               client.copy_data(encrypted_record_with_padding);
1✔
769

770
               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
771
               const auto record = client.next_record(cs.get());
1✔
772
               result.confirm("read a record with padding", std::holds_alternative<TLS::Record>(record));
2✔
773
               result.end_timer();
1✔
774
            }),
2✔
775

776
      CHECK("read an empty encrypted record", [&](Test::Result& result) {
1✔
777
         result.start_timer();
1✔
778
         auto client = record_layer_client(true);
1✔
779
         client.copy_data(Botan::hex_decode("1703030011CE43CA0D2F28336715E770071B2D5EE0FE"));
1✔
780

781
         auto cs = rfc8448_rtt1_handshake_traffic();
1✔
782
         const auto record = client.next_record(cs.get());
1✔
783
         result.confirm("read an empty record", std::holds_alternative<TLS::Record>(record));
2✔
784
         result.end_timer();
1✔
785
      })};
15✔
786
}
4✔
787

788
std::vector<Test::Result> write_encrypted_records() {
1✔
789
   auto plaintext_msg = Botan::hex_decode(
1✔
790
      "14 00 00 20 a8 ec 43 6d 67 76 34 ae"
791
      "52 5a c1 fc eb e1 1a 03 9e c1 76 94 fa c6 e9 85 27 b6 42 f2 ed d5 ce 61");
1✔
792

793
   auto cs = rfc8448_rtt1_handshake_traffic();
1✔
794
   return {
1✔
795
      CHECK("write encrypted client handshake finished",
796
            [&](Test::Result& result) {
1✔
797
               result.start_timer();
1✔
798
               auto ct =
1✔
799
                  record_layer_client(true).prepare_records(TLS::Record_Type::Handshake, plaintext_msg, cs.get());
1✔
800
               auto expected_ct = Botan::hex_decode(
1✔
801
                  "17 03 03 00 35 75 ec 4d c2 38 cc e6"
802
                  "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 d8 7f"
803
                  "38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 26 c4 05 46");
1✔
804
               result.test_eq("produced the expected ciphertext", ct, expected_ct);
1✔
805
               result.end_timer();
1✔
806
            }),
2✔
807

808
      CHECK("write a dummy CCS (that must not be encrypted)",
809
            [&](auto& result) {
1✔
810
               result.start_timer();
1✔
811
               std::array<uint8_t, 1> ccs_content = {0x01};
1✔
812
               auto record = record_layer_client(true).prepare_records(
1✔
813
                  Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content, cs.get());
814
               result.require("record was created and not encrypted", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1);
2✔
815

816
               result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101"));
2✔
817
               result.end_timer();
1✔
818
            }),
1✔
819

820
      CHECK("write a lot of data producing two protected records", [&](Test::Result& result) {
1✔
821
         result.start_timer();
1✔
822
         std::vector<uint8_t> big_data(TLS::MAX_PLAINTEXT_SIZE + TLS::MAX_PLAINTEXT_SIZE / 2);
1✔
823
         auto ct = record_layer_client(true).prepare_records(TLS::Record_Type::ApplicationData, big_data, cs.get());
1✔
824
         result.require("encryption added some MAC and record headers",
1✔
825
                        ct.size() > big_data.size() + Botan::TLS::TLS_HEADER_SIZE * 2);
1✔
826

827
         auto read_record_header = [&](auto& reader) {
3✔
828
            result.test_is_eq(
2✔
829
               "APPLICATION_DATA", reader.get_byte(), static_cast<uint8_t>(TLS::Record_Type::ApplicationData));
2✔
830
            result.test_is_eq("TLS legacy version", reader.get_uint16_t(), uint16_t(0x0303));
2✔
831

832
            const auto fragment_length = reader.get_uint16_t();
2✔
833
            result.test_lte("TLS limts", fragment_length, TLS::MAX_CIPHERTEXT_SIZE_TLS13);
2✔
834
            result.require("enough data", fragment_length + Botan::TLS::TLS_HEADER_SIZE < ct.size());
2✔
835
            return fragment_length;
2✔
836
         };
1✔
837

838
         TLS::TLS_Data_Reader reader("test reader", ct);
1✔
839
         const auto fragment_length1 = read_record_header(reader);
1✔
840
         reader.discard_next(fragment_length1);
1✔
841

842
         const auto fragment_length2 = read_record_header(reader);
1✔
843
         reader.discard_next(fragment_length2);
1✔
844

845
         result.confirm("consumed all bytes", !reader.has_remaining());
2✔
846
         result.end_timer();
1✔
847
      })};
6✔
848
}
3✔
849

850
std::vector<Test::Result> legacy_version_handling() {
1✔
851
   // RFC 8446 5.1:
852
   // legacy_record_version:  MUST be set to 0x0303 for all records
853
   //    generated by a TLS 1.3 implementation other than an initial
854
   //    ClientHello (i.e., one not generated after a HelloRetryRequest),
855
   //    where it MAY also be 0x0301 for compatibility purposes.
856

857
   auto has_version = [](const auto& record, const uint16_t version) -> bool {
5✔
858
      TLS::TLS_Data_Reader dr("header reader", record);
5✔
859

860
      while(dr.has_remaining()) {
18✔
861
         dr.discard_next(1);  // record type
13✔
862
         if(dr.get_uint16_t() != version) {
13✔
863
            return false;
864
         }
865
         const auto record_size = dr.get_uint16_t();
13✔
866
         dr.discard_next(record_size);
13✔
867
      }
868

869
      dr.assert_done();
870
      return true;
871
   };
872

873
   auto parse_record = [](auto& record_layer, const std::vector<uint8_t>& data) {
11✔
874
      record_layer.copy_data(data);
11✔
875
      return record_layer.next_record();
11✔
876
   };
877

878
   return {CHECK("client side starts with version 0x0301",
1✔
879
                 [&](Test::Result& result) {
1✔
880
                    result.start_timer();
1✔
881
                    auto rl = record_layer_client();
1✔
882
                    auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
1✔
883
                    result.confirm("first record has version 0x0301", has_version(rec, 0x0301));
2✔
884

885
                    rl.disable_sending_compat_mode();
1✔
886

887
                    rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
2✔
888
                    result.confirm("next record has version 0x0303", has_version(rec, 0x0303));
2✔
889
                    result.end_timer();
1✔
890
                 }),
1✔
891

892
           CHECK("client side starts with version 0x0301 (even if multiple reconds are required)",
893
                 [&](Test::Result& result) {
1✔
894
                    result.start_timer();
1✔
895
                    auto rl = record_layer_client();
1✔
896
                    auto rec = rl.prepare_records(TLS::Record_Type::Handshake,
1✔
897
                                                  std::vector<uint8_t>(5 * Botan::TLS::MAX_PLAINTEXT_SIZE));
1✔
898
                    result.confirm("first record has version 0x0301", has_version(rec, 0x0301));
2✔
899

900
                    rl.disable_sending_compat_mode();
1✔
901

902
                    rec = rl.prepare_records(TLS::Record_Type::Handshake,
2✔
903
                                             std::vector<uint8_t>(5 * Botan::TLS::MAX_PLAINTEXT_SIZE));
2✔
904
                    result.confirm("next record has version 0x0303", has_version(rec, 0x0303));
2✔
905
                    result.end_timer();
1✔
906
                 }),
1✔
907

908
           CHECK("server side starts with version 0x0303",
909
                 [&](Test::Result& result) {
1✔
910
                    result.start_timer();
1✔
911
                    auto rl = record_layer_server(true);
1✔
912
                    auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
1✔
913
                    result.confirm("first record has version 0x0303", has_version(rec, 0x0303));
2✔
914
                    result.end_timer();
1✔
915
                 }),
1✔
916

917
           CHECK("server side accepts version 0x0301 for the first record",
918
                 [&](Test::Result& result) {
1✔
919
                    result.start_timer();
1✔
920
                    const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00");
1✔
921
                    const auto second_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00");
1✔
922
                    auto rl = record_layer_server();
1✔
923
                    result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record); });
3✔
924
                    result.test_no_throw("parsing second record", [&] { parse_record(rl, second_record); });
3✔
925
                    result.end_timer();
1✔
926
                 }),
3✔
927

928
           CHECK("server side accepts version 0x0301 for the first record for partial records",
929
                 [&](Test::Result& result) {
1✔
930
                    result.start_timer();
1✔
931
                    const auto first_part = Botan::hex_decode("16 03 01");
1✔
932
                    const auto second_part = Botan::hex_decode("00 05 00 00 00 00 00");
1✔
933
                    auto rl = record_layer_server();
1✔
934
                    result.test_no_throw("parsing initial part", [&] { parse_record(rl, first_part); });
3✔
935
                    result.test_no_throw("parsing second part", [&] { parse_record(rl, second_part); });
3✔
936
                    result.end_timer();
1✔
937
                 }),
3✔
938

939
           CHECK("server side accepts version 0x0303 for the first record",
940
                 [&](Test::Result& result) {
1✔
941
                    result.start_timer();
1✔
942
                    const auto first_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00");
1✔
943
                    auto rl = record_layer_server();
1✔
944
                    result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record); });
3✔
945
                    result.end_timer();
1✔
946
                 }),
2✔
947

948
           CHECK("server side does not accept version 0x0301 after receiving client hello",
949
                 [&](Test::Result& result) {
1✔
950
                    result.start_timer();
1✔
951
                    const auto record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00");
1✔
952
                    auto rl = record_layer_server();
1✔
953
                    result.test_no_throw("parsing initial record", [&] { parse_record(rl, record); });
3✔
954
                    rl.disable_receiving_compat_mode();
1✔
955
                    result.test_throws("parsing second record", [&] { parse_record(rl, record); });
3✔
956
                    result.end_timer();
1✔
957
                 }),
2✔
958

959
           CHECK("server side does not accept other versions (after receiving client hello)",
960
                 [&](Test::Result& result) {
1✔
961
                    result.start_timer();
1✔
962
                    auto rl = record_layer_server(true);
1✔
963
                    result.test_throws("does not accept 0x0300",
2✔
964
                                       [&] { parse_record(rl, Botan::hex_decode("16 03 00 00 05 00 00 00 00 00")); });
2✔
965
                    result.test_throws("does not accept 0x0302",
2✔
966
                                       [&] { parse_record(rl, Botan::hex_decode("16 03 02 00 05 00 00 00 00 00")); });
2✔
967
                    result.test_throws("does not accept 0x0304",
2✔
968
                                       [&] { parse_record(rl, Botan::hex_decode("16 03 04 00 05 00 00 00 00 00")); });
2✔
969
                    result.test_throws("does not accept 0x0305",
2✔
970
                                       [&] { parse_record(rl, Botan::hex_decode("16 03 05 00 05 00 00 00 00 00")); });
2✔
971
                    result.end_timer();
1✔
972
                 })
1✔
973

974
   };
9✔
975
}
1✔
976

977
std::vector<Test::Result> record_size_limits() {
1✔
978
   const auto count_records = [](auto& records) {
10✔
979
      Botan::TLS::TLS_Data_Reader reader("record counter", records);
10✔
980
      size_t record_count = 0;
10✔
981

982
      for(; reader.has_remaining(); ++record_count) {
25✔
983
         reader.discard_next(1);                               // record type
15✔
984
         BOTAN_ASSERT_NOMSG(reader.get_uint16_t() == 0x0303);  // record version
15✔
985
         reader.get_tls_length_value(2);                       // record length/content
30✔
986
      }
987

988
      return record_count;
10✔
989
   };
990

991
   const auto record_length = [](auto& result, auto record) {
3✔
992
      result.require("has record", std::holds_alternative<Botan::TLS::Record>(record));
6✔
993
      const auto& r = std::get<Botan::TLS::Record>(record);
3✔
994
      return r.fragment.size();
3✔
995
   };
996

997
   return {
1✔
998
      CHECK("no specified limits means protocol defaults",
999
            [&](Test::Result& result) {
1✔
1000
               result.start_timer();
1✔
1001
               auto csc = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Client);
1✔
1002
               auto rlc = record_layer_client(true);
1✔
1003

1004
               const auto r1 = rlc.prepare_records(
1✔
1005
                  TLS::Record_Type::ApplicationData, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE), csc.get());
1✔
1006
               result.test_eq("one record generated", count_records(r1), 1);
1✔
1007

1008
               const auto r2 = rlc.prepare_records(TLS::Record_Type::ApplicationData,
1✔
1009
                                                   std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE + 1),
1✔
1010
                                                   csc.get());
1✔
1011
               result.test_eq("two records generated", count_records(r2), 2);
1✔
1012

1013
               auto css = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Server);
1✔
1014
               auto rls = record_layer_server(true);
1✔
1015
               rls.copy_data(r1);
1✔
1016

1017
               result.test_eq("correct length record",
2✔
1018
                              record_length(result, rls.next_record(css.get())),
1✔
1019
                              Botan::TLS::MAX_PLAINTEXT_SIZE);
1020
               result.end_timer();
1✔
1021
            }),
3✔
1022

1023
      CHECK("outgoing record size limit",
1024
            [&](Test::Result& result) {
1✔
1025
               result.start_timer();
1✔
1026
               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
1027
               auto rl = record_layer_client(true);
1✔
1028

1029
               rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1);
1✔
1030

1031
               const auto r1 =
1✔
1032
                  rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(127), cs.get());
1✔
1033
               result.test_eq("one record generated", count_records(r1), 1);
1✔
1034

1035
               const auto r2 =
1✔
1036
                  rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(128), cs.get());
1✔
1037
               result.test_eq("two records generated", count_records(r2), 2);
1✔
1038
               result.end_timer();
1✔
1039
            }),
2✔
1040

1041
      CHECK(
1042
         "outgoing record size limit can be changed",
1043
         [&](Test::Result& result) {
1✔
1044
            result.start_timer();
1✔
1045
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
1046
            auto rl = record_layer_client(true);
1✔
1047

1048
            const auto r1 = rl.prepare_records(
1✔
1049
               TLS::Record_Type::ApplicationData, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE), cs.get());
1✔
1050
            result.test_eq("one record generated", count_records(r1), 1);
1✔
1051

1052
            const auto r2 = rl.prepare_records(
1✔
1053
               TLS::Record_Type::ApplicationData, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE + 1), cs.get());
1✔
1054
            result.test_eq("two records generated", count_records(r2), 2);
1✔
1055

1056
            rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1);
1✔
1057

1058
            const auto r3 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(127), cs.get());
1✔
1059
            result.test_eq("one record generated", count_records(r3), 1);
1✔
1060

1061
            const auto r4 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(128), cs.get());
1✔
1062
            result.test_eq("two records generated", count_records(r4), 2);
1✔
1063
            result.end_timer();
1✔
1064
         }),
4✔
1065

1066
      CHECK("outgoing record limit does not affect unencrypted records",
1067
            [&](Test::Result& result) {
1✔
1068
               result.start_timer();
1✔
1069
               auto rl = record_layer_client(true);
1✔
1070

1071
               rl.set_record_size_limits(127 + 1 /* content type byte */, Botan::TLS::MAX_PLAINTEXT_SIZE + 1);
1✔
1072

1073
               const auto r1 =
1✔
1074
                  rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE));
1✔
1075
               result.test_eq("one record generated", count_records(r1), 1);
1✔
1076

1077
               const auto r2 = rl.prepare_records(TLS::Record_Type::Handshake,
1✔
1078
                                                  std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE + 1));
1✔
1079
               result.test_eq("two records generated", count_records(r2), 2);
1✔
1080
               result.end_timer();
1✔
1081
            }),
2✔
1082

1083
      CHECK("incoming limit is not checked on unprotected records",
1084
            [&](Test::Result& result) {
1✔
1085
               result.start_timer();
1✔
1086
               auto rlc = record_layer_client(true);
1✔
1087

1088
               rlc.set_record_size_limits(Botan::TLS::MAX_PLAINTEXT_SIZE + 1, 95 + 1);
1✔
1089

1090
               rlc.copy_data(Botan::concat(Botan::hex_decode("16 03 03 00 80"), std::vector<uint8_t>(128)));
4✔
1091
               result.test_eq("correct length record", record_length(result, rlc.next_record()), 128);
2✔
1092
               result.end_timer();
1✔
1093
            }),
1✔
1094

1095
      CHECK("incoming limit is checked on protected records",
1096
            [&](Test::Result& result) {
1✔
1097
               result.start_timer();
1✔
1098
               auto css = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Server);
1✔
1099
               auto rls = record_layer_server(true);
1✔
1100

1101
               rls.set_record_size_limits(Botan::TLS::MAX_PLAINTEXT_SIZE + 1, 127 + 1);
1✔
1102
               rls.copy_data(
1✔
1103
                  Botan::hex_decode("170303009061ec4de29020a5664ef670094c7b5daa2796aa52e128cfa8808d15c1"
1✔
1104
                                    "ffc97a0aeeed62f9ea690bb753a03d000c5efac53c619face25ad234dffb63e611"
1105
                                    "4619fb045e3a3a0dde4f22e2399b4891029eccb79ea4a29c45a999e72fc74157f0"
1106
                                    "21db0afa05601af25b61df82fb728c772ad860081d96c86008c08d0c21f991cf0d"
1107
                                    "4a0eadc840d1ea8fb1f5dd852980d78fcc"));
1108

1109
               result.test_eq("correct length record", record_length(result, rls.next_record(css.get())), 127);
2✔
1110

1111
               rls.copy_data(
1✔
1112
                  Botan::hex_decode("1703030091234d4a480092fa6a55f1443345ee8d2250cd9c676370be68f86234db"
1✔
1113
                                    "f5514c6dea8b3fa99c6146fefc780e36230858a53f4c0295b23a77dc5b495e0541"
1114
                                    "093aa05ee6cf6f4a4996d9ffc829b638c822e4c36e4da50f1cf2845c12e4388d58"
1115
                                    "e907e181f2dd38e61e78c13ebcbd562a23025fd327eb4db083330314e4641f3b4b"
1116
                                    "43bf11dbb09f7a82443193dc9ece34dabd15"));
1117

1118
               result.test_throws("overflow detected",
2✔
1119
                                  "Received an encrypted record that exceeds maximum plaintext size",
1120
                                  [&] { rls.next_record(css.get()); });
2✔
1121
               result.end_timer();
1✔
1122
            }),
1✔
1123
   };
7✔
1124
}
1✔
1125

1126
}  // namespace
1127

1128
BOTAN_REGISTER_TEST_FN("tls",
1129
                       "tls_record_layer_13",
1130
                       basic_sanitization_parse_records_client,
1131
                       basic_sanitization_parse_records_server,
1132
                       read_full_records,
1133
                       read_fragmented_records,
1134
                       write_records,
1135
                       read_encrypted_records,
1136
                       write_encrypted_records,
1137
                       legacy_version_handling,
1138
                       record_size_limits);
1139

1140
}  // namespace Botan_Tests
1141

1142
#endif
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