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

randombit / botan / 5111374265

29 May 2023 11:19AM UTC coverage: 92.227% (+0.5%) from 91.723%
5111374265

push

github

randombit
Next release will be 3.1.0. Update release notes

75588 of 81959 relevant lines covered (92.23%)

11886470.91 hits per line

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

99.67
/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_record_layer_13.h>
20

21
   #include <array>
22

23
namespace Botan_Tests {
24

25
namespace {
26

27
namespace TLS = Botan::TLS;
28

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

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

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

39
   return rl;
33✔
40
}
41

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

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

50
   return rl;
12✔
51
}
52

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

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

79
   return {Botan_Tests::CHECK("change cipher spec",
1✔
80
                              [&](auto& result) {
1✔
81
                                 auto rl = record_layer_server();
1✔
82

83
                                 rl.copy_data(ccs_record);
1✔
84
                                 auto read = rl.next_record();
1✔
85
                                 result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
86

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

91
                                 result.confirm("no more records",
1✔
92
                                                std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
93
                              }),
3✔
94

95
           Botan_Tests::CHECK("two CCS messages",
96
                              [&](auto& result) {
1✔
97
                                 const auto two_ccs_records = Botan::concat(ccs_record, ccs_record);
1✔
98

99
                                 auto rl = record_layer_server();
1✔
100

101
                                 rl.copy_data(two_ccs_records);
1✔
102

103
                                 auto read = rl.next_record();
1✔
104
                                 result.require("received something", std::holds_alternative<TLS::Record>(read));
2✔
105
                                 auto record = std::get<TLS::Record>(read);
1✔
106

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

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

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

117
                                 result.confirm("no more records",
1✔
118
                                                std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
119
                              }),
4✔
120

121
           Botan_Tests::CHECK(
122
              "read full handshake message",
123
              [&](auto& result) {
1✔
124
                 auto rl = record_layer_server();
1✔
125
                 rl.copy_data(client_hello_record);
1✔
126

127
                 auto read = rl.next_record();
1✔
128
                 result.confirm("received something", std::holds_alternative<TLS::Record>(read));
3✔
129

130
                 auto rec = std::get<TLS::Record>(read);
1✔
131
                 result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake);
3✔
132
                 result.test_eq("contains the full handshake message",
2✔
133
                                Botan::secure_vector<uint8_t>(client_hello_record.begin() + TLS::TLS_HEADER_SIZE,
1✔
134
                                                              client_hello_record.end()),
135
                                rec.fragment);
136

137
                 result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
138
              }),
3✔
139

140
           Botan_Tests::CHECK("read full handshake message followed by CCS", [&](auto& result) {
1✔
141
              const auto payload = Botan::concat(client_hello_record, ccs_record);
1✔
142

143
              auto rl = record_layer_server();
1✔
144
              rl.copy_data(payload);
1✔
145

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

149
              auto rec = std::get<TLS::Record>(read);
1✔
150
              result.confirm("received handshake record", rec.type == TLS::Record_Type::Handshake);
3✔
151
              result.test_eq("contains the full handshake message",
2✔
152
                             Botan::secure_vector<uint8_t>(client_hello_record.begin() + TLS::TLS_HEADER_SIZE,
1✔
153
                                                           client_hello_record.end()),
154
                             rec.fragment);
155

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

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

163
              result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
164
           })};
9✔
165
}
2✔
166

167
std::vector<Test::Result> basic_sanitization_parse_records(TLS::Connection_Side side) {
2✔
168
   auto parse_records = [side](const std::vector<uint8_t>& data, TLS::Cipher_State* cs = nullptr) {
24✔
169
      auto rl = ((side == TLS::Connection_Side::Client) ? record_layer_client(true) : record_layer_server());
22✔
170
      rl.copy_data(data);
36✔
171
      return rl.next_record(cs);
22✔
172
   };
8✔
173

174
   return {
2✔
175
      Botan_Tests::CHECK(
176
         "'receive' empty data",
177
         [&](auto& result) {
2✔
178
            auto read = parse_records({});
4✔
179
            result.require("needs bytes", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
180
            result.test_eq("need all the header bytes", std::get<TLS::BytesNeeded>(read), Botan::TLS::TLS_HEADER_SIZE);
4✔
181
         }),
2✔
182

183
      Botan_Tests::CHECK("incomplete header asks for more data",
184
                         [&](auto& result) {
2✔
185
                            std::vector<uint8_t> partial_header{'\x23', '\x03', '\x03'};
2✔
186
                            auto read = parse_records(partial_header);
2✔
187
                            result.require("returned 'bytes needed'", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
188

189
                            result.test_eq("asks for some more bytes",
4✔
190
                                           std::get<TLS::BytesNeeded>(read),
2✔
191
                                           Botan::TLS::TLS_HEADER_SIZE - partial_header.size());
2✔
192
                         }),
4✔
193

194
      Botan_Tests::CHECK("complete header asks for enough data to finish processing the record",
195
                         [&](auto& result) {
2✔
196
                            std::vector<uint8_t> full_header{'\x17', '\x03', '\x03', '\x00', '\x42'};
2✔
197
                            auto read = parse_records(full_header);
2✔
198
                            result.require("returned 'bytes needed'", std::holds_alternative<TLS::BytesNeeded>(read));
4✔
199

200
                            result.test_eq("asks for many more bytes", std::get<TLS::BytesNeeded>(read), 0x42);
4✔
201
                         }),
4✔
202

203
      Botan_Tests::CHECK("received an empty record (that is not application data)",
204
                         [&](auto& result) {
2✔
205
                            std::vector<uint8_t> empty_record{'\x16', '\x03', '\x03', '\x00', '\x00'};
2✔
206
                            result.test_throws(
8✔
207
                               "record empty", "empty record received", [&] { parse_records(empty_record); });
4✔
208
                         }),
2✔
209

210
      Botan_Tests::CHECK("received the maximum size of an unprotected record",
211
                         [&](auto& result) {
2✔
212
                            std::vector<uint8_t> full_record{'\x16', '\x03', '\x03', '\x40', '\x00'};
2✔
213
                            full_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE);
2✔
214
                            auto read = parse_records(full_record);
2✔
215
                            result.confirm("returned 'record'", !std::holds_alternative<TLS::BytesNeeded>(read));
6✔
216
                         }),
4✔
217

218
      Botan_Tests::CHECK("received too many bytes in one protected record",
219
                         [&](auto& result) {
2✔
220
                            std::vector<uint8_t> huge_record{'\x17', '\x03', '\x03', '\x41', '\x01'};
2✔
221
                            huge_record.resize(TLS::MAX_CIPHERTEXT_SIZE_TLS13 + TLS::TLS_HEADER_SIZE + 1);
2✔
222
                            result.test_throws("record too big",
8✔
223
                                               "Received an encrypted record that exceeds maximum size",
224
                                               [&] { parse_records(huge_record); });
4✔
225
                         }),
2✔
226

227
      Botan_Tests::CHECK("decryption would result in too large plaintext",
228
                         [&](auto& result) {
2✔
229
                            // In this case the ciphertext is within the allowed bounds, but the
230
                            // decrypted plaintext would be too large.
231
                            std::vector<uint8_t> huge_record{'\x17', '\x03', '\x03', '\x40', '\x12'};
2✔
232
                            huge_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE + 16 /* AES-GCM tag */
2✔
233
                                               + 1                                                 /* encrypted type */
234
                                               + 1 /* illegal */);
235

236
                            auto cs = rfc8448_rtt1_handshake_traffic();
2✔
237
                            result.test_throws("record too big",
6✔
238
                                               "Received an encrypted record that exceeds maximum plaintext size",
239
                                               [&] { parse_records(huge_record, cs.get()); });
4✔
240
                         }),
4✔
241

242
      Botan_Tests::CHECK("received too many bytes in one unprotected record",
243
                         [&](auto& result) {
2✔
244
                            std::vector<uint8_t> huge_record{'\x16', '\x03', '\x03', '\x40', '\x01'};
2✔
245
                            huge_record.resize(TLS::MAX_PLAINTEXT_SIZE + TLS::TLS_HEADER_SIZE + 1);
2✔
246
                            result.test_throws("record too big", "Received a record that exceeds maximum size", [&] {
8✔
247
                               parse_records(huge_record);
4✔
248
                            });
249
                         }),
2✔
250

251
      Botan_Tests::CHECK("invalid record type",
252
                         [&](auto& result) {
2✔
253
                            std::vector<uint8_t> invalid_record_type{'\x42', '\x03', '\x03', '\x41', '\x01'};
2✔
254
                            result.test_throws("invalid record type", "TLS record type had unexpected value", [&] {
8✔
255
                               parse_records(invalid_record_type);
4✔
256
                            });
257
                         }),
2✔
258

259
      Botan_Tests::CHECK("invalid record version",
260
                         [&](auto& result) {
2✔
261
                            std::vector<uint8_t> invalid_record_version{'\x17', '\x13', '\x37', '\x00', '\x01', '\x42'};
2✔
262
                            result.test_throws("invalid record version", "Received unexpected record version", [&] {
8✔
263
                               parse_records(invalid_record_version);
4✔
264
                            });
265
                         }),
2✔
266

267
      Botan_Tests::CHECK(
268
         "initial received record versions might be 0x03XX ",
269
         [&](auto& result) {
2✔
270
            auto rl = record_layer_client();
2✔
271
            rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x00, 0x00, 0x01, 0x42});
2✔
272
            result.test_no_throw("0x03 0x00 should be fine for first records", [&] { rl.next_record(); });
6✔
273

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

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

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

283
            rl.disable_receiving_compat_mode();
2✔
284

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

288
            rl.copy_data(std::vector<uint8_t>{0x16, 0x03, 0x01, 0x00, 0x01, 0x42});
2✔
289
            result.test_throws("0x03 0x01 not okay once client hello was received", [&] { rl.next_record(); });
8✔
290
         }),
2✔
291

292
      Botan_Tests::CHECK("malformed change cipher spec",
293
                         [&](auto& result) {
2✔
294
                            std::vector<uint8_t> invalid_ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x02'};
2✔
295
                            result.test_throws("invalid CCS record",
8✔
296
                                               "malformed change cipher spec record received",
297
                                               [&] { parse_records(invalid_ccs_record); });
4✔
298
                         })
2✔
299

300
   };
26✔
301
}
302

303
std::vector<Test::Result> basic_sanitization_parse_records_client() {
1✔
304
   return basic_sanitization_parse_records(TLS::Connection_Side::Client);
1✔
305
}
306

307
std::vector<Test::Result> basic_sanitization_parse_records_server() {
1✔
308
   return basic_sanitization_parse_records(TLS::Connection_Side::Server);
1✔
309
}
310

311
std::vector<Test::Result> read_fragmented_records() {
1✔
312
   TLS::Record_Layer rl = record_layer_client(true);
1✔
313

314
   auto wait_for_more_bytes =
1✔
315
      [](Botan::TLS::BytesNeeded bytes_needed, auto& record_layer, std::vector<uint8_t> bytes, auto& result) {
7✔
316
         record_layer.copy_data(bytes);
7✔
317
         const auto rlr = record_layer.next_record();
7✔
318
         if(result.confirm("waiting for bytes", std::holds_alternative<TLS::BytesNeeded>(rlr))) {
21✔
319
            result.test_eq("right amount", std::get<TLS::BytesNeeded>(rlr), bytes_needed);
14✔
320
         }
321
      };
7✔
322

323
   return {Botan_Tests::CHECK("change cipher spec in many small pieces",
1✔
324
                              [&](auto& result) {
1✔
325
                                 std::vector<uint8_t> ccs_record{'\x14', '\x03', '\x03', '\x00', '\x01', '\x01'};
1✔
326

327
                                 wait_for_more_bytes(4, rl, {'\x14'}, result);
1✔
328
                                 wait_for_more_bytes(3, rl, {'\x03'}, result);
1✔
329
                                 wait_for_more_bytes(2, rl, {'\x03'}, result);
1✔
330
                                 wait_for_more_bytes(1, rl, {'\x00'}, result);
1✔
331
                                 wait_for_more_bytes(1, rl, {'\x01'}, result);
1✔
332

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

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

341
                                 result.confirm("no more records",
1✔
342
                                                std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
2✔
343
                              }),
3✔
344

345
           Botan_Tests::CHECK("two change cipher specs in several pieces", [&](auto& result) {
1✔
346
              wait_for_more_bytes(1, rl, {'\x14', '\x03', '\x03', '\x00'}, result);
1✔
347

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

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

353
              auto rec2 = std::get<TLS::Record>(res2);
1✔
354
              result.confirm("received CCS", rec2.type == TLS::Record_Type::ChangeCipherSpec);
2✔
355
              result.confirm("demands more bytes", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
356

357
              wait_for_more_bytes(2, rl, {'\x03'}, result);
1✔
358

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

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

366
              result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
367
           })};
7✔
368
}
1✔
369

370
std::vector<Test::Result> write_records() {
1✔
371
   auto cs = rfc8448_rtt1_handshake_traffic();
1✔
372
   return {
1✔
373
      Botan_Tests::CHECK(
374
         "prepare an zero-length application data fragment",
375
         [&](auto& result) {
1✔
376
            auto record = record_layer_client().prepare_records(Botan::TLS::Record_Type::ApplicationData, {}, cs.get());
1✔
377

378
            result.require("record header was added",
2✔
379
                           record.size() > Botan::TLS::TLS_HEADER_SIZE + 1 /* encrypted content type */);
1✔
380
         }),
1✔
381
      Botan_Tests::CHECK(
382
         "prepare a client hello",
383
         [&](auto& result) {
1✔
384
            const auto client_hello_msg = Botan::hex_decode(  // from RFC 8448
1✔
385
               "01 00 00 c0 03 03 cb"
386
               "34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12"
387
               "ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00"
388
               "00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01"
389
               "00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02"
390
               "01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d"
391
               "e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d"
392
               "54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e"
393
               "04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02"
394
               "01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01");
395
            auto record = record_layer_client().prepare_records(Botan::TLS::Record_Type::Handshake, client_hello_msg);
1✔
396

397
            result.require("record header was added",
2✔
398
                           record.size() == client_hello_msg.size() + Botan::TLS::TLS_HEADER_SIZE);
1✔
399

400
            const auto header = std::vector<uint8_t>(record.cbegin(), record.cbegin() + Botan::TLS::TLS_HEADER_SIZE);
1✔
401
            result.test_eq("record header is well-formed", header, Botan::hex_decode("16030100c4"));
3✔
402
         }),
3✔
403
      Botan_Tests::CHECK("prepare a dummy CCS",
404
                         [&](auto& result) {
1✔
405
                            std::array<uint8_t, 1> ccs_content = {0x01};
1✔
406
                            auto record = record_layer_client(true).prepare_records(
1✔
407
                               Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content);
408
                            result.require("record was created", record.size() == Botan::TLS::TLS_HEADER_SIZE + 1);
2✔
409

410
                            result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101"));
3✔
411
                         }),
1✔
412
      Botan_Tests::CHECK(
413
         "cannot prepare non-dummy CCS",
414
         [&](auto& result) {
1✔
415
            result.test_throws("cannot create non-dummy CCS", "TLS 1.3 deprecated CHANGE_CIPHER_SPEC", [] {
4✔
416
               const auto ccs_content = Botan::hex_decode("de ad be ef");
1✔
417
               record_layer_client().prepare_records(Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content);
1✔
418
            });
×
419
         }),
1✔
420
      Botan_Tests::CHECK("large messages are sharded", [&](auto& result) {
1✔
421
         const std::vector<uint8_t> large_client_hello(Botan::TLS::MAX_PLAINTEXT_SIZE + 4096);
1✔
422
         auto record = record_layer_client().prepare_records(Botan::TLS::Record_Type::Handshake, large_client_hello);
1✔
423

424
         result.test_gte("produces at least two record headers",
2✔
425
                         record.size(),
426
                         large_client_hello.size() + 2 * Botan::TLS::TLS_HEADER_SIZE);
1✔
427
      })};
9✔
428
}
1✔
429

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

440
   // this is the "complete record" encrypted server hello portion
441
   // from RFC 8448 page 9
442
   const auto encrypted_record = Botan::hex_decode(
1✔
443
      "17 03 03 02 a2 d1 ff 33 4a 56 f5 bf"
444
      "f6 59 4a 07 cc 87 b5 80 23 3f 50 0f 45 e4 89 e7 f3 3a f3 5e df"
445
      "78 69 fc f4 0a a4 0a a2 b8 ea 73 f8 48 a7 ca 07 61 2e f9 f9 45"
446
      "cb 96 0b 40 68 90 51 23 ea 78 b1 11 b4 29 ba 91 91 cd 05 d2 a3"
447
      "89 28 0f 52 61 34 aa dc 7f c7 8c 4b 72 9d f8 28 b5 ec f7 b1 3b"
448
      "d9 ae fb 0e 57 f2 71 58 5b 8e a9 bb 35 5c 7c 79 02 07 16 cf b9"
449
      "b1 18 3e f3 ab 20 e3 7d 57 a6 b9 d7 47 76 09 ae e6 e1 22 a4 cf"
450
      "51 42 73 25 25 0c 7d 0e 50 92 89 44 4c 9b 3a 64 8f 1d 71 03 5d"
451
      "2e d6 5b 0e 3c dd 0c ba e8 bf 2d 0b 22 78 12 cb b3 60 98 72 55"
452
      "cc 74 41 10 c4 53 ba a4 fc d6 10 92 8d 80 98 10 e4 b7 ed 1a 8f"
453
      "d9 91 f0 6a a6 24 82 04 79 7e 36 a6 a7 3b 70 a2 55 9c 09 ea d6"
454
      "86 94 5b a2 46 ab 66 e5 ed d8 04 4b 4c 6d e3 fc f2 a8 94 41 ac"
455
      "66 27 2f d8 fb 33 0e f8 19 05 79 b3 68 45 96 c9 60 bd 59 6e ea"
456
      "52 0a 56 a8 d6 50 f5 63 aa d2 74 09 96 0d ca 63 d3 e6 88 61 1e"
457
      "a5 e2 2f 44 15 cf 95 38 d5 1a 20 0c 27 03 42 72 96 8a 26 4e d6"
458
      "54 0c 84 83 8d 89 f7 2c 24 46 1a ad 6d 26 f5 9e ca ba 9a cb bb"
459
      "31 7b 66 d9 02 f4 f2 92 a3 6a c1 b6 39 c6 37 ce 34 31 17 b6 59"
460
      "62 22 45 31 7b 49 ee da 0c 62 58 f1 00 d7 d9 61 ff b1 38 64 7e"
461
      "92 ea 33 0f ae ea 6d fa 31 c7 a8 4d c3 bd 7e 1b 7a 6c 71 78 af"
462
      "36 87 90 18 e3 f2 52 10 7f 24 3d 24 3d c7 33 9d 56 84 c8 b0 37"
463
      "8b f3 02 44 da 8c 87 c8 43 f5 e5 6e b4 c5 e8 28 0a 2b 48 05 2c"
464
      "f9 3b 16 49 9a 66 db 7c ca 71 e4 59 94 26 f7 d4 61 e6 6f 99 88"
465
      "2b d8 9f c5 08 00 be cc a6 2d 6c 74 11 6d bd 29 72 fd a1 fa 80"
466
      "f8 5d f8 81 ed be 5a 37 66 89 36 b3 35 58 3b 59 91 86 dc 5c 69"
467
      "18 a3 96 fa 48 a1 81 d6 b6 fa 4f 9d 62 d5 13 af bb 99 2f 2b 99"
468
      "2f 67 f8 af e6 7f 76 91 3f a3 88 cb 56 30 c8 ca 01 e0 c6 5d 11"
469
      "c6 6a 1e 2a c4 c8 59 77 b7 c7 a6 99 9b bf 10 dc 35 ae 69 f5 51"
470
      "56 14 63 6c 0b 9b 68 c1 9e d2 e3 1c 0b 3b 66 76 30 38 eb ba 42"
471
      "f3 b3 8e dc 03 99 f3 a9 f2 3f aa 63 97 8c 31 7f c9 fa 66 a7 3f"
472
      "60 f0 50 4d e9 3b 5b 84 5e 27 55 92 c1 23 35 ee 34 0b bc 4f dd"
473
      "d5 02 78 40 16 e4 b3 be 7e f0 4d da 49 f4 b4 40 a3 0c b5 d2 af"
474
      "93 98 28 fd 4a e3 79 4e 44 f9 4d f5 a6 31 ed e4 2c 17 19 bf da"
475
      "bf 02 53 fe 51 75 be 89 8e 75 0e dc 53 37 0d 2b");
1✔
476

477
   // the record above padded with 42 zeros
478
   const auto encrypted_record_with_padding = Botan::hex_decode(
1✔
479
      "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"
480
      "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"
481
      "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"
482
      "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"
483
      "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"
484
      "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"
485
      "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"
486
      "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"
487
      "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"
488
      "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"
489
      "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"
490
      "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"
491
      "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"
492
      "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"
493
      "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"
494
      "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"
495
      "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"
496
      "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"
497
      "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"
498
      "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"
499
      "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"
500
      "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"
501
      "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"
502
      "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"
503
      "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"
504
      "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"
505
      "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"
506
      "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"
507
      "fa 44 23 0a e0 c9 dd dd bb a8 be db d9 d7 f6 b8 3d 56 4c a5 47");
1✔
508

509
   auto parse_records = [](const std::vector<uint8_t>& data) {
12✔
510
      auto rl = record_layer_client(true);
11✔
511
      rl.copy_data(data);
11✔
512
      return rl;
11✔
513
   };
×
514

515
   return {
1✔
516
      Botan_Tests::CHECK(
517
         "read encrypted server hello extensions",
518
         [&](Test::Result& result) {
1✔
519
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
520
            auto rl = parse_records(encrypted_record);
1✔
521

522
            auto res = rl.next_record(cs.get());
1✔
523
            result.require("some records decrypted", !std::holds_alternative<Botan::TLS::BytesNeeded>(res));
1✔
524
            auto record = std::get<TLS::Record>(res);
1✔
525

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

529
            result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
530
         }),
3✔
531

532
      Botan_Tests::CHECK("premature application data",
533
                         [&](Test::Result& result) {
1✔
534
                            auto rl = record_layer_client(true);
1✔
535
                            rl.copy_data(encrypted_record);
1✔
536

537
                            result.test_throws<Botan::TLS::TLS_Exception>(
4✔
538
                               "cannot process encrypted data with uninitialized cipher state",
539
                               "premature Application Data received",
540
                               [&] { auto res = rl.next_record(nullptr); });
1✔
541
                         }),
1✔
542

543
      Botan_Tests::CHECK("decryption fails due to bad MAC",
544
                         [&](Test::Result& result) {
1✔
545
                            auto tampered_encrypted_record = encrypted_record;
1✔
546
                            tampered_encrypted_record.back() =
1✔
547
                               '\x42';  // changing one payload byte causes the MAC check to fails
548

549
                            result.test_throws<Botan::Invalid_Authentication_Tag>("broken record detected", [&] {
3✔
550
                               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
551
                               auto rl = parse_records(tampered_encrypted_record);
2✔
552
                               rl.next_record(cs.get());
1✔
553
                            });
1✔
554
                         }),
1✔
555

556
      Botan_Tests::CHECK("decryption fails due to too short record",
557
                         [&](Test::Result& result) {
1✔
558
                            const auto short_record = Botan::hex_decode("17 03 03 00 08 de ad be ef ba ad f0 0d");
1✔
559

560
                            result.test_throws<Botan::TLS::TLS_Exception>("too short to decrypt", [&] {
3✔
561
                               auto cs = rfc8448_rtt1_handshake_traffic();
1✔
562
                               auto rl = parse_records(short_record);
2✔
563
                               rl.next_record(cs.get());
1✔
564
                            });
1✔
565
                         }),
1✔
566

567
      Botan_Tests::CHECK("protected Change Cipher Spec message is illegal",
568
                         [&](Test::Result& result) {
1✔
569
                            // factored message, encrypted under the same key as `encrypted_record`
570
                            const auto protected_ccs =
1✔
571
                               Botan::hex_decode("1703030012D8EBBBE055C8167D5690EC67DEA9A525B036");
1✔
572

573
                            result.test_throws<Botan::TLS::TLS_Exception>(
4✔
574
                               "illegal state causes TLS alert", "protected change cipher spec received", [&] {
1✔
575
                                  auto cs = rfc8448_rtt1_handshake_traffic();
1✔
576
                                  auto rl = parse_records(protected_ccs);
2✔
577
                                  rl.next_record(cs.get());
1✔
578
                               });
1✔
579
                         }),
1✔
580

581
      Botan_Tests::CHECK("unprotected CCS is legal when encrypted traffic is expected",
582
                         [&](Test::Result& result) {
1✔
583
                            const auto ccs_record = Botan::hex_decode("14 03 03 00 01 01");
1✔
584

585
                            result.test_no_throw("CCS is acceptable", [&] {
3✔
586
                               auto cs = rfc8448_rtt1_handshake_traffic();  // expect encrypted traffic
1✔
587
                               auto rl = parse_records(ccs_record);
2✔
588
                               rl.next_record(cs.get());
2✔
589
                            });
1✔
590
                         }),
1✔
591

592
      Botan_Tests::CHECK("unprotected Alert message might be legal",
593
                         [&](Test::Result& result) {
1✔
594
                            const auto alert = Botan::hex_decode("15030300020232");  // decode error
1✔
595
                            const auto hsmsg = Botan::hex_decode(  // factored 'certificate_request' message
1✔
596
                               "160303002a0d000027000024000d0020001e040305030603"
597
                               "020308040805080604010501060102010402050206020202");
1✔
598

599
                            result.test_no_throw("Server allows unprotected alerts after its first flight", [&] {
2✔
600
                               auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Server);
1✔
601
                               auto rl = parse_records(alert);
2✔
602
                               rl.next_record(cs.get());
2✔
603
                            });
1✔
604

605
                            result.test_throws<Botan::TLS::TLS_Exception>(
3✔
606
                               "Unprotected handshake messages are not allowed for servers",
607
                               "unprotected record received where protected traffic was expected",
608
                               [&] {
1✔
609
                                  auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Server);
1✔
610
                                  auto rl = parse_records(hsmsg);
1✔
611
                                  rl.next_record(cs.get());
1✔
612
                               });
1✔
613

614
                            result.test_throws<Botan::TLS::TLS_Exception>(
3✔
615
                               "Clients don't allow unprotected alerts after Server Hello",
616
                               "unprotected record received where protected traffic was expected",
617
                               [&] {
1✔
618
                                  auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Client);
1✔
619
                                  auto rl = parse_records(alert);
1✔
620
                                  rl.next_record(cs.get());
1✔
621
                               });
1✔
622

623
                            result.test_throws<Botan::TLS::TLS_Exception>(
4✔
624
                               "Unprotected handshake messages are not allowed for clients",
625
                               "unprotected record received where protected traffic was expected",
626
                               [&] {
1✔
627
                                  auto cs = rfc8448_rtt1_handshake_traffic(TLS::Connection_Side::Client);
1✔
628
                                  auto rl = parse_records(hsmsg);
1✔
629
                                  rl.next_record(cs.get());
1✔
630
                               });
1✔
631
                         }),
2✔
632

633
      Botan_Tests::CHECK("unprotected traffic is illegal when encrypted traffic is expected",
634
                         [&](Test::Result& result) {
1✔
635
                            result.test_throws("unprotected record is unacceptable", [&] {
2✔
636
                               auto cs = rfc8448_rtt1_handshake_traffic();  // expect encrypted traffic
1✔
637
                               auto rl = parse_records(server_hello);
2✔
638
                               rl.next_record(cs.get());
1✔
639
                            });
1✔
640
                         }),
1✔
641

642
      Botan_Tests::CHECK(
643
         "read fragmented application data",
644
         [&](Test::Result& result) {
1✔
645
            const auto encrypted = Botan::hex_decode(
1✔
646
               "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"
647
               "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"
648
               "80 C2 E9 3D EC 84 3B 8D 41 30 D8 C8 C5 D8"
649
               "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"
650
               "14 72 39 6F A1 F3 D3 ");
1✔
651
            const std::vector<std::vector<uint8_t>> plaintext_records = {
1✔
652
               Botan::hex_decode("00 01 02 03 04 05 06 07 08"),
653
               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"),
654
               Botan::hex_decode("20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f")};
4✔
655

656
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
657
            // advance with arbitrary hashes that were used to produce the input data
658
            cs->advance_with_server_finished(
1✔
659
               Botan::hex_decode("e1935a480babfc4403b2517f0ad414bed0ca51fa671e2061804afa78fd71d55c"));
1✔
660
            cs->advance_with_client_finished(
1✔
661
               Botan::hex_decode("305e4a0a7cee581b282c571b251b20138a1a6a21918937a6bb95b1e9ba1b5cac"));
1✔
662

663
            auto rl = parse_records(encrypted);
1✔
664
            auto res = rl.next_record(cs.get());
1✔
665
            result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
666
            auto records = std::get<TLS::Record>(res);
1✔
667
            result.test_eq("first record", records.fragment, plaintext_records.at(0));
1✔
668

669
            res = rl.next_record(cs.get());
2✔
670
            result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
671
            records = std::get<TLS::Record>(res);
1✔
672
            result.test_eq("second record", records.fragment, plaintext_records.at(1));
1✔
673

674
            res = rl.next_record(cs.get());
2✔
675
            result.require("decrypted a record", std::holds_alternative<TLS::Record>(res));
1✔
676
            records = std::get<TLS::Record>(res);
1✔
677
            result.test_eq("third record", records.fragment, plaintext_records.at(2));
1✔
678

679
            result.confirm("no more records", std::holds_alternative<TLS::BytesNeeded>(rl.next_record()));
3✔
680
         }),
4✔
681

682
      Botan_Tests::CHECK(
683
         "read coalesced server hello and encrypted extensions",
684
         [&](Test::Result& result) {
1✔
685
            // contains the plaintext server hello and the encrypted extensions in one go
686
            auto coalesced = server_hello;
1✔
687
            coalesced.insert(coalesced.end(), encrypted_record.cbegin(), encrypted_record.cend());
1✔
688

689
            auto client = record_layer_client(true);
1✔
690
            client.copy_data(coalesced);
1✔
691

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

696
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
697
            const auto enc_exts = client.next_record(cs.get());
1✔
698
            result.confirm("read a record", std::holds_alternative<TLS::Record>(enc_exts));
2✔
699
            result.confirm("is handshake record", std::get<TLS::Record>(enc_exts).type == TLS::Record_Type::Handshake);
3✔
700
         }),
4✔
701

702
      Botan_Tests::CHECK("read a padded record",
703
                         [&](Test::Result& result) {
1✔
704
                            auto client = record_layer_client(true);
1✔
705
                            client.copy_data(encrypted_record_with_padding);
1✔
706

707
                            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
708
                            const auto record = client.next_record(cs.get());
1✔
709
                            result.confirm("read a record with padding", std::holds_alternative<TLS::Record>(record));
3✔
710
                         }),
2✔
711

712
      Botan_Tests::CHECK("read an empty encrypted record", [&](Test::Result& result) {
1✔
713
         auto client = record_layer_client(true);
1✔
714
         client.copy_data(Botan::hex_decode("1703030011CE43CA0D2F28336715E770071B2D5EE0FE"));
1✔
715

716
         auto cs = rfc8448_rtt1_handshake_traffic();
1✔
717
         const auto record = client.next_record(cs.get());
1✔
718
         result.confirm("read an empty record", std::holds_alternative<TLS::Record>(record));
3✔
719
      })};
15✔
720
}
3✔
721

722
std::vector<Test::Result> write_encrypted_records() {
1✔
723
   auto plaintext_msg = Botan::hex_decode(
1✔
724
      "14 00 00 20 a8 ec 43 6d 67 76 34 ae"
725
      "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✔
726

727
   auto cs = rfc8448_rtt1_handshake_traffic();
1✔
728
   return {Botan_Tests::CHECK("write encrypted client handshake finished",
1✔
729
                              [&](Test::Result& result) {
1✔
730
                                 auto ct = record_layer_client(true).prepare_records(
1✔
731
                                    TLS::Record_Type::Handshake, plaintext_msg, cs.get());
1✔
732
                                 auto expected_ct = Botan::hex_decode(
1✔
733
                                    "17 03 03 00 35 75 ec 4d c2 38 cc e6"
734
                                    "0b 29 80 44 a7 1e 21 9c 56 cc 77 b0 51 7f e9 b9 3c 7a 4b fc 44 d8 7f"
735
                                    "38 f8 03 38 ac 98 fc 46 de b3 84 bd 1c ae ac ab 68 67 d7 26 c4 05 46");
1✔
736
                                 result.test_eq("produced the expected ciphertext", ct, expected_ct);
2✔
737
                              }),
2✔
738

739
           Botan_Tests::CHECK("write a dummy CCS (that must not be encrypted)",
740
                              [&](auto& result) {
1✔
741
                                 std::array<uint8_t, 1> ccs_content = {0x01};
1✔
742
                                 auto record = record_layer_client(true).prepare_records(
1✔
743
                                    Botan::TLS::Record_Type::ChangeCipherSpec, ccs_content, cs.get());
1✔
744
                                 result.require("record was created and not encrypted",
2✔
745
                                                record.size() == Botan::TLS::TLS_HEADER_SIZE + 1);
1✔
746

747
                                 result.test_eq("CCS record is well-formed", record, Botan::hex_decode("140303000101"));
3✔
748
                              }),
1✔
749

750
           Botan_Tests::CHECK("write a lot of data producing two protected records", [&](Test::Result& result) {
1✔
751
              std::vector<uint8_t> big_data(TLS::MAX_PLAINTEXT_SIZE + TLS::MAX_PLAINTEXT_SIZE / 2);
1✔
752
              auto ct =
1✔
753
                 record_layer_client(true).prepare_records(TLS::Record_Type::ApplicationData, big_data, cs.get());
1✔
754
              result.require("encryption added some MAC and record headers",
1✔
755
                             ct.size() > big_data.size() + Botan::TLS::TLS_HEADER_SIZE * 2);
1✔
756

757
              auto read_record_header = [&](auto& reader) {
3✔
758
                 result.test_is_eq(
2✔
759
                    "APPLICATION_DATA", reader.get_byte(), static_cast<uint8_t>(TLS::Record_Type::ApplicationData));
2✔
760
                 result.test_is_eq("TLS legacy version", reader.get_uint16_t(), uint16_t(0x0303));
2✔
761

762
                 const auto fragment_length = reader.get_uint16_t();
2✔
763
                 result.test_lte("TLS limts", fragment_length, TLS::MAX_CIPHERTEXT_SIZE_TLS13);
2✔
764
                 result.require("enough data", fragment_length + Botan::TLS::TLS_HEADER_SIZE < ct.size());
2✔
765
                 return fragment_length;
2✔
766
              };
1✔
767

768
              TLS::TLS_Data_Reader reader("test reader", ct);
1✔
769
              const auto fragment_length1 = read_record_header(reader);
1✔
770
              reader.discard_next(fragment_length1);
1✔
771

772
              const auto fragment_length2 = read_record_header(reader);
1✔
773
              reader.discard_next(fragment_length2);
1✔
774

775
              result.confirm("consumed all bytes", !reader.has_remaining());
3✔
776
           })};
6✔
777
}
2✔
778

779
std::vector<Test::Result> legacy_version_handling() {
1✔
780
   // RFC 8446 5.1:
781
   // legacy_record_version:  MUST be set to 0x0303 for all records
782
   //    generated by a TLS 1.3 implementation other than an initial
783
   //    ClientHello (i.e., one not generated after a HelloRetryRequest),
784
   //    where it MAY also be 0x0301 for compatibility purposes.
785

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

789
      while(dr.has_remaining()) {
5✔
790
         dr.discard_next(1);  // record type
13✔
791
         if(dr.get_uint16_t() != version)
13✔
792
            return false;
793
         const auto record_size = dr.get_uint16_t();
13✔
794
         dr.discard_next(record_size);
31✔
795
      }
796

797
      dr.assert_done();
798
      return true;
799
   };
800

801
   auto parse_record = [](auto& record_layer, const std::vector<uint8_t>& data) {
11✔
802
      record_layer.copy_data(data);
11✔
803
      return record_layer.next_record();
11✔
804
   };
805

806
   return {
1✔
807
      Botan_Tests::CHECK("client side starts with version 0x0301",
808
                         [&](Test::Result& result) {
1✔
809
                            auto rl = record_layer_client();
1✔
810
                            auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
1✔
811
                            result.confirm("first record has version 0x0301", has_version(rec, 0x0301));
2✔
812

813
                            rl.disable_sending_compat_mode();
1✔
814

815
                            rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
2✔
816
                            result.confirm("next record has version 0x0303", has_version(rec, 0x0303));
3✔
817
                         }),
1✔
818

819
      Botan_Tests::CHECK("client side starts with version 0x0301 (even if multiple reconds are required)",
820
                         [&](Test::Result& result) {
1✔
821
                            auto rl = record_layer_client();
1✔
822
                            auto rec = rl.prepare_records(TLS::Record_Type::Handshake,
1✔
823
                                                          std::vector<uint8_t>(5 * Botan::TLS::MAX_PLAINTEXT_SIZE));
1✔
824
                            result.confirm("first record has version 0x0301", has_version(rec, 0x0301));
2✔
825

826
                            rl.disable_sending_compat_mode();
1✔
827

828
                            rec = rl.prepare_records(TLS::Record_Type::Handshake,
2✔
829
                                                     std::vector<uint8_t>(5 * Botan::TLS::MAX_PLAINTEXT_SIZE));
2✔
830
                            result.confirm("next record has version 0x0303", has_version(rec, 0x0303));
3✔
831
                         }),
1✔
832

833
      Botan_Tests::CHECK("server side starts with version 0x0303",
834
                         [&](Test::Result& result) {
1✔
835
                            auto rl = record_layer_server(true);
1✔
836
                            auto rec = rl.prepare_records(TLS::Record_Type::Handshake, std::vector<uint8_t>(5));
1✔
837
                            result.confirm("first record has version 0x0303", has_version(rec, 0x0303));
3✔
838
                         }),
1✔
839

840
      Botan_Tests::CHECK("server side accepts version 0x0301 for the first record",
841
                         [&](Test::Result& result) {
1✔
842
                            const auto first_record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00");
1✔
843
                            const auto second_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00");
1✔
844
                            auto rl = record_layer_server();
1✔
845
                            result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record); });
3✔
846
                            result.test_no_throw("parsing second record", [&] { parse_record(rl, second_record); });
4✔
847
                         }),
3✔
848

849
      Botan_Tests::CHECK("server side accepts version 0x0301 for the first record for partial records",
850
                         [&](Test::Result& result) {
1✔
851
                            const auto first_part = Botan::hex_decode("16 03 01");
1✔
852
                            const auto second_part = Botan::hex_decode("00 05 00 00 00 00 00");
1✔
853
                            auto rl = record_layer_server();
1✔
854
                            result.test_no_throw("parsing initial part", [&] { parse_record(rl, first_part); });
3✔
855
                            result.test_no_throw("parsing second part", [&] { parse_record(rl, second_part); });
4✔
856
                         }),
3✔
857

858
      Botan_Tests::CHECK("server side accepts version 0x0303 for the first record",
859
                         [&](Test::Result& result) {
1✔
860
                            const auto first_record = Botan::hex_decode("16 03 03 00 05 00 00 00 00 00");
1✔
861
                            auto rl = record_layer_server();
1✔
862
                            result.test_no_throw("parsing initial record", [&] { parse_record(rl, first_record); });
4✔
863
                         }),
2✔
864

865
      Botan_Tests::CHECK("server side does not accept version 0x0301 after receiving client hello",
866
                         [&](Test::Result& result) {
1✔
867
                            const auto record = Botan::hex_decode("16 03 01 00 05 00 00 00 00 00");
1✔
868
                            auto rl = record_layer_server();
1✔
869
                            result.test_no_throw("parsing initial record", [&] { parse_record(rl, record); });
3✔
870
                            rl.disable_receiving_compat_mode();
1✔
871
                            result.test_throws("parsing second record", [&] { parse_record(rl, record); });
4✔
872
                         }),
2✔
873

874
      Botan_Tests::CHECK(
875
         "server side does not accept other versions (after receiving client hello)",
876
         [&](Test::Result& result) {
1✔
877
            auto rl = record_layer_server(true);
1✔
878
            result.test_throws("does not accept 0x0300",
2✔
879
                               [&] { parse_record(rl, Botan::hex_decode("16 03 00 00 05 00 00 00 00 00")); });
2✔
880
            result.test_throws("does not accept 0x0302",
2✔
881
                               [&] { parse_record(rl, Botan::hex_decode("16 03 02 00 05 00 00 00 00 00")); });
1✔
882
            result.test_throws("does not accept 0x0304",
2✔
883
                               [&] { parse_record(rl, Botan::hex_decode("16 03 04 00 05 00 00 00 00 00")); });
1✔
884
            result.test_throws("does not accept 0x0305",
3✔
885
                               [&] { parse_record(rl, Botan::hex_decode("16 03 05 00 05 00 00 00 00 00")); });
1✔
886
         })
1✔
887

888
   };
9✔
889
}
890

891
std::vector<Test::Result> record_size_limits() {
1✔
892
   const auto count_records = [](auto& records) {
10✔
893
      Botan::TLS::TLS_Data_Reader reader("record counter", records);
10✔
894
      size_t record_count = 0;
10✔
895

896
      for(; reader.has_remaining(); ++record_count) {
25✔
897
         reader.discard_next(1);                               // record type
15✔
898
         BOTAN_ASSERT_NOMSG(reader.get_uint16_t() == 0x0303);  // record version
15✔
899
         reader.get_tls_length_value(2);                       // record length/content
30✔
900
      }
901

902
      return record_count;
10✔
903
   };
904

905
   const auto record_length = [](auto& result, auto record) {
3✔
906
      result.require("has record", std::holds_alternative<Botan::TLS::Record>(record));
6✔
907
      const auto& r = std::get<Botan::TLS::Record>(record);
3✔
908
      return r.fragment.size();
3✔
909
   };
910

911
   return {
1✔
912
      Botan_Tests::CHECK(
913
         "no specified limits means protocol defaults",
914
         [&](Test::Result& result) {
1✔
915
            auto csc = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Client);
1✔
916
            auto rlc = record_layer_client(true);
1✔
917

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

922
            const auto r2 = rlc.prepare_records(
1✔
923
               TLS::Record_Type::ApplicationData, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE + 1), csc.get());
1✔
924
            result.test_eq("two records generated", count_records(r2), 2);
1✔
925

926
            auto css = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Server);
1✔
927
            auto rls = record_layer_server(true);
1✔
928
            rls.copy_data(r1);
1✔
929

930
            result.test_eq("correct length record",
2✔
931
                           record_length(result, rls.next_record(css.get())),
2✔
932
                           Botan::TLS::MAX_PLAINTEXT_SIZE);
933
         }),
3✔
934

935
      Botan_Tests::CHECK(
936
         "outgoing record size limit",
937
         [&](Test::Result& result) {
1✔
938
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
939
            auto rl = record_layer_client(true);
1✔
940

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

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

946
            const auto r2 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(128), cs.get());
1✔
947
            result.test_eq("two records generated", count_records(r2), 2);
2✔
948
         }),
2✔
949

950
      Botan_Tests::CHECK(
951
         "outgoing record size limit can be changed",
952
         [&](Test::Result& result) {
1✔
953
            auto cs = rfc8448_rtt1_handshake_traffic();
1✔
954
            auto rl = record_layer_client(true);
1✔
955

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

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

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

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

969
            const auto r4 = rl.prepare_records(TLS::Record_Type::ApplicationData, std::vector<uint8_t>(128), cs.get());
1✔
970
            result.test_eq("two records generated", count_records(r4), 2);
2✔
971
         }),
4✔
972

973
      Botan_Tests::CHECK("outgoing record limit does not affect unencrypted records",
974
                         [&](Test::Result& result) {
1✔
975
                            auto rl = record_layer_client(true);
1✔
976

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

980
                            const auto r1 = rl.prepare_records(TLS::Record_Type::Handshake,
1✔
981
                                                               std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE));
1✔
982
                            result.test_eq("one record generated", count_records(r1), 1);
1✔
983

984
                            const auto r2 = rl.prepare_records(
1✔
985
                               TLS::Record_Type::Handshake, std::vector<uint8_t>(Botan::TLS::MAX_PLAINTEXT_SIZE + 1));
1✔
986
                            result.test_eq("two records generated", count_records(r2), 2);
2✔
987
                         }),
2✔
988

989
      Botan_Tests::CHECK("incoming limit is not checked on unprotected records",
990
                         [&](Test::Result& result) {
1✔
991
                            auto rlc = record_layer_client(true);
1✔
992

993
                            rlc.set_record_size_limits(Botan::TLS::MAX_PLAINTEXT_SIZE + 1, 95 + 1);
1✔
994

995
                            rlc.copy_data(
2✔
996
                               Botan::concat(Botan::hex_decode("16 03 03 00 80"), std::vector<uint8_t>(128)));
3✔
997
                            result.test_eq("correct length record", record_length(result, rlc.next_record()), 128);
3✔
998
                         }),
1✔
999

1000
      Botan_Tests::CHECK("incoming limit is checked on protected records",
1001
                         [&](Test::Result& result) {
1✔
1002
                            auto css = rfc8448_rtt1_handshake_traffic(Botan::TLS::Connection_Side::Server);
1✔
1003
                            auto rls = record_layer_server(true);
1✔
1004

1005
                            rls.set_record_size_limits(Botan::TLS::MAX_PLAINTEXT_SIZE + 1, 127 + 1);
1✔
1006
                            rls.copy_data(
1✔
1007
                               Botan::hex_decode("170303009061ec4de29020a5664ef670094c7b5daa2796aa52e128cfa8808d15c1"
1✔
1008
                                                 "ffc97a0aeeed62f9ea690bb753a03d000c5efac53c619face25ad234dffb63e611"
1009
                                                 "4619fb045e3a3a0dde4f22e2399b4891029eccb79ea4a29c45a999e72fc74157f0"
1010
                                                 "21db0afa05601af25b61df82fb728c772ad860081d96c86008c08d0c21f991cf0d"
1011
                                                 "4a0eadc840d1ea8fb1f5dd852980d78fcc"));
1012

1013
                            result.test_eq(
2✔
1014
                               "correct length record", record_length(result, rls.next_record(css.get())), 127);
1✔
1015

1016
                            rls.copy_data(
1✔
1017
                               Botan::hex_decode("1703030091234d4a480092fa6a55f1443345ee8d2250cd9c676370be68f86234db"
1✔
1018
                                                 "f5514c6dea8b3fa99c6146fefc780e36230858a53f4c0295b23a77dc5b495e0541"
1019
                                                 "093aa05ee6cf6f4a4996d9ffc829b638c822e4c36e4da50f1cf2845c12e4388d58"
1020
                                                 "e907e181f2dd38e61e78c13ebcbd562a23025fd327eb4db083330314e4641f3b4b"
1021
                                                 "43bf11dbb09f7a82443193dc9ece34dabd15"));
1022

1023
                            result.test_throws("overflow detected",
4✔
1024
                                               "Received an encrypted record that exceeds maximum plaintext size",
1025
                                               [&] { rls.next_record(css.get()); });
1✔
1026
                         }),
1✔
1027
   };
7✔
1028
}
1029

1030
}  // namespace
1031

1032
BOTAN_REGISTER_TEST_FN("tls",
1033
                       "tls_record_layer_13",
1034
                       basic_sanitization_parse_records_client,
1035
                       basic_sanitization_parse_records_server,
1036
                       read_full_records,
1037
                       read_fragmented_records,
1038
                       write_records,
1039
                       read_encrypted_records,
1040
                       write_encrypted_records,
1041
                       legacy_version_handling,
1042
                       record_size_limits);
1043

1044
}  // namespace Botan_Tests
1045

1046
#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