hkdf: when HMAC key is all zeros, still set a valid key length
[openssl.git] / test / recipes / 70-test_sslrecords.t
1 #! /usr/bin/env perl
2 # Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the Apache License 2.0 (the "License").  You may not use
5 # this file except in compliance with the License.  You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
8
9 use strict;
10 use feature 'state';
11
12 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
13 use OpenSSL::Test::Utils;
14 use TLSProxy::Proxy;
15 use TLSProxy::Message;
16
17 my $test_name = "test_sslrecords";
18 setup($test_name);
19
20 plan skip_all => "TLSProxy isn't usable on $^O"
21     if $^O =~ /^(VMS)$/;
22
23 plan skip_all => "$test_name needs the dynamic engine feature enabled"
24     if disabled("engine") || disabled("dynamic-engine");
25
26 plan skip_all => "$test_name needs the sock feature enabled"
27     if disabled("sock");
28
29 my $inject_recs_num = undef;
30 my $content_type = undef;
31 my $boundary_test_type = undef;
32 my $fatal_alert = undef; # set by filters at expected fatal alerts
33 my $sslv2testtype = undef;
34 my $proxy_start_success = 0;
35
36 plan tests => 42;
37
38 SKIP: {
39     skip "TLS 1.2 is disabled", 21 if disabled("tls1_2");
40     # Run tests with TLS
41     run_tests(0);
42 }
43
44 SKIP: {
45     skip "DTLS 1.2 is disabled", 21 if disabled("dtls1_2");
46     skip "DTLSProxy does not work on Windows", 21 if $^O =~ /^(MSWin32)$/;
47     run_tests(1);
48 }
49
50 sub run_tests
51 {
52     my $run_test_as_dtls = shift;
53
54     my $proxy;
55     if ($run_test_as_dtls == 1) {
56         $proxy = TLSProxy::Proxy->new_dtls(
57             \&add_empty_recs_filter,
58             cmdstr(app([ "openssl" ]), display => 1),
59             srctop_file("apps", "server.pem"),
60             (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
61         );
62     } else {
63         $proxy = TLSProxy::Proxy->new(
64             \&add_empty_recs_filter,
65             cmdstr(app([ "openssl" ]), display => 1),
66             srctop_file("apps", "server.pem"),
67             (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
68         );
69     }
70
71     $fatal_alert = 0; # set by filters at expected fatal alerts
72     SKIP: {
73         skip "Record tests not intended for dtls", 1 if $run_test_as_dtls == 1;
74         #Test 1: Injecting out of context empty records should fail
75         $proxy->clear();
76         $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
77         $inject_recs_num = 1;
78         $fatal_alert = 0;
79         $proxy->serverflags("-tls1_2");
80         $proxy->clientflags("-no_tls1_3");
81         $proxy_start_success = $proxy->start();
82         ok($fatal_alert, "Out of context empty records test");
83     }
84
85     skip "TLSProxy did not start correctly", 21 if $proxy_start_success == 0
86                                                    && $run_test_as_dtls == 0;
87
88     #Test 2: Injecting in context empty records should succeed
89     $proxy->clear();
90     $content_type = TLSProxy::Record::RT_HANDSHAKE;
91     if ($run_test_as_dtls == 1) {
92         $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
93         $proxy->clientflags("-max_protocol DTLSv1.2");
94     } else {
95         $proxy->serverflags("-tls1_2");
96         $proxy->clientflags("-no_tls1_3");
97     }
98     $proxy_start_success = $proxy->start();
99
100     skip "TLSProxy did not start correctly", 20 if $proxy_start_success == 0
101                                                    && $run_test_as_dtls == 1;
102
103     ok($proxy_start_success && TLSProxy::Message->success(),
104        "In context empty records test".($run_test_as_dtls == 1) ? " for DTLS" : " for TLS");
105
106     SKIP: {
107         skip "Record tests not intended for dtls", 7 if $run_test_as_dtls == 1;
108         #Test 3: Injecting too many in context empty records should fail
109         $fatal_alert = 0;
110         $proxy->clear();
111         #We allow 32 consecutive in context empty records
112         $inject_recs_num = 33;
113         $proxy->serverflags("-tls1_2");
114         $proxy->clientflags("-no_tls1_3");
115         $proxy->start();
116         ok($fatal_alert, "Too many in context empty records test");
117
118         #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
119         #        send back an alert of its own because it cannot handle fragmented
120         #        alerts
121         $fatal_alert = 0;
122         $proxy->clear();
123         $proxy->filter(\&add_frag_alert_filter);
124         $proxy->serverflags("-tls1_2");
125         $proxy->clientflags("-no_tls1_3");
126         $proxy->start();
127         ok($fatal_alert, "Fragmented alert records test");
128
129         #Run some SSLv2 ClientHello tests
130
131         use constant {
132             TLSV1_2_IN_SSLV2      => 0,
133             SSLV2_IN_SSLV2        => 1,
134             FRAGMENTED_IN_TLSV1_2 => 2,
135             FRAGMENTED_IN_SSLV2   => 3,
136             ALERT_BEFORE_SSLV2    => 4
137         };
138
139         # The TLSv1.2 in SSLv2 ClientHello need to run at security level 0
140         # because in a SSLv2 ClientHello we can't send extensions to indicate
141         # which signature algorithm we want to use, and the default is SHA1.
142
143         #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
144         $sslv2testtype = TLSV1_2_IN_SSLV2;
145         $proxy->clear();
146         $proxy->filter(\&add_sslv2_filter);
147         $proxy->serverflags("-tls1_2");
148         $proxy->clientflags("-no_tls1_3 -legacy_renegotiation");
149         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
150         $proxy->start();
151         ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
152
153         #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
154         #        support this so it should fail. We actually treat it as an unknown
155         #        protocol so we don't even send an alert in this case.
156         $sslv2testtype = SSLV2_IN_SSLV2;
157         $proxy->clear();
158         $proxy->serverflags("-tls1_2");
159         $proxy->clientflags("-no_tls1_3");
160         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
161         $proxy->start();
162         ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
163
164         #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
165         #        at all, but it gives us confidence that Test 8 fails for the right
166         #        reasons
167         $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
168         $proxy->clear();
169         $proxy->serverflags("-tls1_2");
170         $proxy->clientflags("-no_tls1_3");
171         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
172         $proxy->start();
173         ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
174
175         #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
176         #        record; and another TLS1.2 record. This isn't allowed so should fail
177         $sslv2testtype = FRAGMENTED_IN_SSLV2;
178         $proxy->clear();
179         $proxy->serverflags("-tls1_2");
180         $proxy->clientflags("-no_tls1_3");
181         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
182         $proxy->start();
183         ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
184
185         #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
186         #        fail because an SSLv2 ClientHello must be the first record.
187         $sslv2testtype = ALERT_BEFORE_SSLV2;
188         $proxy->clear();
189         $proxy->serverflags("-tls1_2");
190         $proxy->clientflags("-no_tls1_3");
191         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
192         $proxy->start();
193         ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
194    }
195     #Unrecognised record type tests
196
197     #Test 10: Sending an unrecognised record type in TLS1.2 should fail
198     $fatal_alert = 0;
199     $proxy->clear();
200     if ($run_test_as_dtls == 1) {
201         $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2");
202         $proxy->clientflags("-max_protocol DTLSv1.2");
203     } else {
204         $proxy->serverflags("-tls1_2");
205         $proxy->clientflags("-no_tls1_3");
206     }
207     $proxy->filter(\&add_unknown_record_type);
208     $proxy_start_success = $proxy->start();
209
210     if ($run_test_as_dtls == 1) {
211         ok($proxy_start_success == 0, "Unrecognised record type in DTLS1.2");
212     } else {
213         ok($fatal_alert, "Unrecognised record type in TLS1.2");
214     }
215
216     SKIP: {
217         skip "TLSv1.1 or DTLSv1 disabled", 1 if ($run_test_as_dtls == 0 && disabled("tls1_1"))
218                                                  || ($run_test_as_dtls == 1 && disabled("dtls1"));
219
220         #Test 11: Sending an unrecognised record type in TLS1.1 should fail
221         $fatal_alert = 0;
222         $proxy->clear();
223         if ($run_test_as_dtls == 1) {
224             $proxy->clientflags("-min_protocol DTLSv1 -max_protocol DTLSv1 -cipher DEFAULT:\@SECLEVEL=0");
225         } else {
226             $proxy->clientflags("-tls1_1 -cipher DEFAULT:\@SECLEVEL=0");
227         }
228         $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
229         $proxy_start_success = $proxy->start();
230         if ($run_test_as_dtls == 1) {
231             ok($proxy_start_success == 0, "Unrecognised record type in DTLSv1");
232         } else {
233             ok($fatal_alert, "Unrecognised record type in TLSv1.1");
234         }
235     }
236
237     SKIP: {
238         skip "Record tests not intended for dtls", 10 if $run_test_as_dtls == 1;
239         #Test 12: Sending a different record version in TLS1.2 should fail
240         $fatal_alert = 0;
241         $proxy->clear();
242         $proxy->clientflags("-tls1_2");
243         $proxy->filter(\&change_version);
244         $proxy->start();
245         ok($fatal_alert, "Changed record version in TLS1.2");
246
247         #TLS1.3 specific tests
248         SKIP: {
249             skip "TLSv1.3 disabled", 9
250                 if disabled("tls1_3") || (disabled("ec") && disabled("dh"));
251
252             #Test 13: Sending a different record version in TLS1.3 should fail
253             $proxy->clear();
254             $proxy->filter(\&change_version);
255             $proxy->start();
256             ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3");
257
258             #Test 14: Sending an unrecognised record type in TLS1.3 should fail
259             $fatal_alert = 0;
260             $proxy->clear();
261             $proxy->filter(\&add_unknown_record_type);
262             $proxy->start();
263             ok($fatal_alert, "Unrecognised record type in TLS1.3");
264
265             #Test 15: Sending an outer record type other than app data once encrypted
266             #should fail
267             $fatal_alert = 0;
268             $proxy->clear();
269             $proxy->filter(\&change_outer_record_type);
270             $proxy->start();
271             ok($fatal_alert, "Wrong outer record type in TLS1.3");
272
273             use constant {
274                 DATA_AFTER_SERVER_HELLO    => 0,
275                 DATA_AFTER_FINISHED        => 1,
276                 DATA_AFTER_KEY_UPDATE      => 2,
277                 DATA_BETWEEN_KEY_UPDATE    => 3,
278                 NO_DATA_BETWEEN_KEY_UPDATE => 4,
279             };
280
281             #Test 16: Sending a ServerHello which doesn't end on a record boundary
282             #         should fail
283             $fatal_alert = 0;
284             $proxy->clear();
285             $boundary_test_type = DATA_AFTER_SERVER_HELLO;
286             $proxy->filter(\&not_on_record_boundary);
287             $proxy->start();
288             ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)");
289
290             #Test 17: Sending a Finished which doesn't end on a record boundary
291             #         should fail
292             $fatal_alert = 0;
293             $proxy->clear();
294             $boundary_test_type = DATA_AFTER_FINISHED;
295             $proxy->start();
296             ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)");
297
298             #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
299             #         should fail
300             $fatal_alert = 0;
301             $proxy->clear();
302             $boundary_test_type = DATA_AFTER_KEY_UPDATE;
303             $proxy->start();
304             ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
305
306             #Test 19: Sending application data in the middle of a fragmented KeyUpdate
307             #         should fail. Strictly speaking this is not a record boundary test
308             #         but we use the same filter.
309             $fatal_alert = 0;
310             $proxy->clear();
311             $boundary_test_type = DATA_BETWEEN_KEY_UPDATE;
312             $proxy->start();
313             ok($fatal_alert, "Data between KeyUpdate");
314
315             #Test 20: Fragmented KeyUpdate. This should succeed. Strictly speaking this
316             #         is not a record boundary test but we use the same filter.
317             $proxy->clear();
318             $boundary_test_type = NO_DATA_BETWEEN_KEY_UPDATE;
319             $proxy->start();
320             ok(TLSProxy::Message->success(), "No data between KeyUpdate");
321
322             SKIP: {
323                 skip "EC disabled", 1 if disabled("ec");
324
325                 #Test 21: Force an HRR and change the "real" ServerHello to have a protocol
326                 #         record version of 0x0301 (TLSv1.0). At this point we have already
327                 #         decided that we are doing TLSv1.3 but are still using plaintext
328                 #         records. The server should be sending a record version of 0x303
329                 #         (TLSv1.2), but the RFC requires us to ignore this field so we
330                 #         should tolerate the incorrect version.
331                 $proxy->clear();
332                 $proxy->filter(\&change_server_hello_version);
333                 $proxy->serverflags("-groups P-256"); # Force an HRR
334                 $proxy->start();
335                 ok(TLSProxy::Message->success(), "Bad ServerHello record version after HRR");
336             }
337         }
338     }
339 }
340
341
342 sub add_empty_recs_filter
343 {
344     my $proxy = shift;
345     my $records = $proxy->record_list;
346     my $isdtls = $proxy->isdtls();
347
348     # We're only interested in the initial ClientHello
349     if ($proxy->flight != 0) {
350         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
351         return;
352     }
353
354     for (my $i = 0; $i < $inject_recs_num; $i++) {
355         my $record;
356         if ($isdtls == 1) {
357             $record = TLSProxy::Record->new_dtls(
358                 0,
359                 $content_type,
360                 TLSProxy::Record::VERS_DTLS_1_2,
361                 0,
362                 0,
363                 0,
364                 0,
365                 0,
366                 0,
367                 "",
368                 ""
369             );
370         } else {
371             $record = TLSProxy::Record->new(
372                 0,
373                 $content_type,
374                 TLSProxy::Record::VERS_TLS_1_2,
375                 0,
376                 0,
377                 0,
378                 0,
379                 "",
380                 ""
381             );
382         }
383         push @{$records}, $record;
384     }
385 }
386
387 sub add_frag_alert_filter
388 {
389     my $proxy = shift;
390     my $records = $proxy->record_list;
391     my $byte;
392
393     # We're only interested in the initial ClientHello
394     if ($proxy->flight != 0) {
395         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
396         return;
397     }
398
399     # Add a zero length fragment first
400     #my $record = TLSProxy::Record->new(
401     #    0,
402     #    TLSProxy::Record::RT_ALERT,
403     #    TLSProxy::Record::VERS_TLS_1_2,
404     #    0,
405     #    0,
406     #    0,
407     #    "",
408     #    ""
409     #);
410     #push @{$proxy->record_list}, $record;
411
412     # Now add the alert level (Fatal) as a separate record
413     $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
414     my $record = TLSProxy::Record->new(
415         0,
416         TLSProxy::Record::RT_ALERT,
417         TLSProxy::Record::VERS_TLS_1_2,
418         1,
419         0,
420         1,
421         1,
422         $byte,
423         $byte
424     );
425     push @{$records}, $record;
426
427     # And finally the description (Unexpected message) in a third record
428     $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
429     $record = TLSProxy::Record->new(
430         0,
431         TLSProxy::Record::RT_ALERT,
432         TLSProxy::Record::VERS_TLS_1_2,
433         1,
434         0,
435         1,
436         1,
437         $byte,
438         $byte
439     );
440     push @{$records}, $record;
441 }
442
443 sub add_sslv2_filter
444 {
445     my $proxy = shift;
446     my $clienthello;
447     my $record;
448
449     # We're only interested in the initial ClientHello
450     if ($proxy->flight != 0) {
451         return;
452     }
453
454     # Ditch the real ClientHello - we're going to replace it with our own
455     shift @{$proxy->record_list};
456
457     if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
458         my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
459                                TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
460         my $alertlen = length $alert;
461         $record = TLSProxy::Record->new(
462             0,
463             TLSProxy::Record::RT_ALERT,
464             TLSProxy::Record::VERS_TLS_1_2,
465             $alertlen,
466             0,
467             $alertlen,
468             $alertlen,
469             $alert,
470             $alert
471         );
472
473         push @{$proxy->record_list}, $record;
474     }
475
476     if ($sslv2testtype == ALERT_BEFORE_SSLV2
477             || $sslv2testtype == TLSV1_2_IN_SSLV2
478             || $sslv2testtype == SSLV2_IN_SSLV2) {
479         # This is an SSLv2 format ClientHello
480         $clienthello =
481             pack "C44",
482             0x01, # ClientHello
483             0x03, 0x03, #TLSv1.2
484             0x00, 0x03, # Ciphersuites len
485             0x00, 0x00, # Session id len
486             0x00, 0x20, # Challenge len
487             0x00, 0x00, 0x2f, #AES128-SHA
488             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
489             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
490             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
491
492         if ($sslv2testtype == SSLV2_IN_SSLV2) {
493             # Set the version to "real" SSLv2
494             vec($clienthello, 1, 8) = 0x00;
495             vec($clienthello, 2, 8) = 0x02;
496         }
497
498         my $chlen = length $clienthello;
499
500         $record = TLSProxy::Record->new(
501             0,
502             TLSProxy::Record::RT_HANDSHAKE,
503             TLSProxy::Record::VERS_TLS_1_2,
504             $chlen,
505             1, #SSLv2
506             $chlen,
507             $chlen,
508             $clienthello,
509             $clienthello
510         );
511
512         push @{$proxy->record_list}, $record;
513     } else {
514         # For this test we're using a real TLS ClientHello
515         $clienthello =
516             pack "C49",
517             0x01, # ClientHello
518             0x00, 0x00, 0x2D, # Message length
519             0x03, 0x03, # TLSv1.2
520             0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
521             0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
522             0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
523             0x00, # Session id len
524             0x00, 0x04, # Ciphersuites len
525             0x00, 0x2f, # AES128-SHA
526             0x00, 0xff, # Empty reneg info SCSV
527             0x01, # Compression methods len
528             0x00, # Null compression
529             0x00, 0x00; # Extensions len
530
531         # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
532         # We deliberately split the second record prior to the Challenge/Random
533         # and set the first byte of the random to 1. This makes the second SSLv2
534         # record look like an SSLv2 ClientHello
535         my $frag1 = substr $clienthello, 0, 6;
536         my $frag2 = substr $clienthello, 6, 32;
537         my $frag3 = substr $clienthello, 38;
538
539         my $fraglen = length $frag1;
540         $record = TLSProxy::Record->new(
541             0,
542             TLSProxy::Record::RT_HANDSHAKE,
543             TLSProxy::Record::VERS_TLS_1_2,
544             $fraglen,
545             0,
546             $fraglen,
547             $fraglen,
548             $frag1,
549             $frag1
550         );
551         push @{$proxy->record_list}, $record;
552
553         $fraglen = length $frag2;
554         my $recvers;
555         if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
556             $recvers = 1;
557         } else {
558             $recvers = 0;
559         }
560         $record = TLSProxy::Record->new(
561             0,
562             TLSProxy::Record::RT_HANDSHAKE,
563             TLSProxy::Record::VERS_TLS_1_2,
564             $fraglen,
565             $recvers,
566             $fraglen,
567             $fraglen,
568             $frag2,
569             $frag2
570         );
571         push @{$proxy->record_list}, $record;
572
573         $fraglen = length $frag3;
574         $record = TLSProxy::Record->new(
575             0,
576             TLSProxy::Record::RT_HANDSHAKE,
577             TLSProxy::Record::VERS_TLS_1_2,
578             $fraglen,
579             0,
580             $fraglen,
581             $fraglen,
582             $frag3,
583             $frag3
584         );
585         push @{$proxy->record_list}, $record;
586     }
587
588 }
589
590 sub add_unknown_record_type
591 {
592     my $proxy = shift;
593     my $records = $proxy->record_list;
594     my $isdtls = $proxy->isdtls;
595     state $added_record;
596
597     # We'll change a record after the initial version neg has taken place
598     if ($proxy->flight == 0) {
599         $added_record = 0;
600         return;
601     } elsif ($proxy->flight != 1 || $added_record) {
602         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
603         return;
604     }
605
606     my $record;
607
608     if ($isdtls) {
609         $record = TLSProxy::Record->new_dtls(
610             1,
611             TLSProxy::Record::RT_UNKNOWN,
612             @{$records}[-1]->version(),
613             @{$records}[-1]->epoch(),
614             @{$records}[-1]->seq() +1,
615             1,
616             0,
617             1,
618             1,
619             "X",
620             "X"
621         );
622     } else {
623         $record = TLSProxy::Record->new(
624             1,
625             TLSProxy::Record::RT_UNKNOWN,
626             @{$records}[-1]->version(),
627             1,
628             0,
629             1,
630             1,
631             "X",
632             "X"
633         );
634     }
635
636     #Find ServerHello record and insert after that
637     my $i;
638     for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
639         next;
640     }
641     $i++;
642
643     splice @{$proxy->record_list}, $i, 0, $record;
644     $added_record = 1;
645 }
646
647 sub change_version
648 {
649     my $proxy = shift;
650     my $records = $proxy->record_list;
651
652     # We'll change a version after the initial version neg has taken place
653     if ($proxy->flight != 1) {
654         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_PROTOCOL_VERSION;
655         return;
656     }
657
658     if ($#{$records} > 1) {
659         # ... typically in ServerHelloDone
660         @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
661     }
662 }
663
664 sub change_server_hello_version
665 {
666     my $proxy = shift;
667     my $records = $proxy->record_list;
668
669     # We're only interested in changing the ServerHello after an HRR
670     if ($proxy->flight != 3) {
671         return;
672     }
673
674     # The ServerHello has index 5
675     # 0 - ClientHello
676     # 1 - HRR
677     # 2 - CCS
678     # 3 - ClientHello(2)
679     # 4 - CCS
680     # 5 - ServerHello
681     @{$records}[5]->version(TLSProxy::Record::VERS_TLS_1_0);
682 }
683
684 sub change_outer_record_type
685 {
686     my $proxy = shift;
687     my $records = $proxy->record_list;
688
689     # We'll change a record after the initial version neg has taken place
690     if ($proxy->flight != 1) {
691         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
692         return;
693     }
694
695     # Find CCS record and change record after that
696     my $i = 0;
697     foreach my $record (@{$records}) {
698         last if $record->content_type == TLSProxy::Record::RT_CCS;
699         $i++;
700     }
701     if (defined(${$records}[++$i])) {
702         ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
703     }
704 }
705
706 sub not_on_record_boundary
707 {
708     my $proxy = shift;
709     my $records = $proxy->record_list;
710     my $data;
711
712     #Find server's first flight
713     if ($proxy->flight != 1) {
714         $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE;
715         return;
716     }
717
718     if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
719         #Merge the ServerHello and EncryptedExtensions records into one
720         my $i = 0;
721         foreach my $record (@{$records}) {
722             if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
723                 $record->{sent} = 1;    # pretend it's sent already
724                 last;
725             }
726             $i++;
727         }
728
729         if (defined(${$records}[$i+1])) {
730             $data = ${$records}[$i]->data();
731             $data .= ${$records}[$i+1]->decrypt_data();
732             ${$records}[$i+1]->data($data);
733             ${$records}[$i+1]->len(length $data);
734
735             #Delete the old ServerHello record
736             splice @{$records}, $i, 1;
737         }
738     } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
739         return if @{$proxy->{message_list}}[-1]->{mt}
740                   != TLSProxy::Message::MT_FINISHED;
741
742         my $last_record = @{$records}[-1];
743         $data = $last_record->decrypt_data;
744
745         #Add a KeyUpdate message onto the end of the Finished record
746         my $keyupdate = pack "C5",
747             0x18, # KeyUpdate
748             0x00, 0x00, 0x01, # Message length
749             0x00; # Update not requested
750
751         $data .= $keyupdate;
752
753         #Add content type and tag
754         $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
755
756         #Update the record
757         $last_record->data($data);
758         $last_record->len(length $data);
759     } elsif ($boundary_test_type == DATA_AFTER_KEY_UPDATE) {
760         return if @{$proxy->{message_list}}[-1]->{mt}
761                   != TLSProxy::Message::MT_FINISHED;
762
763         #KeyUpdates must end on a record boundary
764
765         my $record = TLSProxy::Record->new(
766             1,
767             TLSProxy::Record::RT_APPLICATION_DATA,
768             TLSProxy::Record::VERS_TLS_1_2,
769             0,
770             0,
771             0,
772             0,
773             "",
774             ""
775         );
776
777         #Add two KeyUpdate messages into a single record
778         my $keyupdate = pack "C5",
779             0x18, # KeyUpdate
780             0x00, 0x00, 0x01, # Message length
781             0x00; # Update not requested
782
783         $data = $keyupdate.$keyupdate;
784
785         #Add content type and tag
786         $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
787
788         $record->data($data);
789         $record->len(length $data);
790         push @{$records}, $record;
791     } else {
792         return if @{$proxy->{message_list}}[-1]->{mt}
793                   != TLSProxy::Message::MT_FINISHED;
794
795         my $record = TLSProxy::Record->new(
796             1,
797             TLSProxy::Record::RT_APPLICATION_DATA,
798             TLSProxy::Record::VERS_TLS_1_2,
799             0,
800             0,
801             0,
802             0,
803             "",
804             ""
805         );
806
807         #Add a partial KeyUpdate message into the record
808         $data = pack "C1",
809             0x18; # KeyUpdate message type. Omit the rest of the message header
810
811         #Add content type and tag
812         $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
813
814         $record->data($data);
815         $record->len(length $data);
816         push @{$records}, $record;
817
818         if ($boundary_test_type == DATA_BETWEEN_KEY_UPDATE) {
819             #Now add an app data record
820             $record = TLSProxy::Record->new(
821                 1,
822                 TLSProxy::Record::RT_APPLICATION_DATA,
823                 TLSProxy::Record::VERS_TLS_1_2,
824                 0,
825                 0,
826                 0,
827                 0,
828                 "",
829                 ""
830             );
831
832             #Add an empty app data record (just content type and tag)
833             $data = pack("C", TLSProxy::Record::RT_APPLICATION_DATA).("\0"x16);
834
835             $record->data($data);
836             $record->len(length $data);
837             push @{$records}, $record;
838         }
839
840         #Now add the rest of the KeyUpdate message
841         $record = TLSProxy::Record->new(
842             1,
843             TLSProxy::Record::RT_APPLICATION_DATA,
844             TLSProxy::Record::VERS_TLS_1_2,
845             0,
846             0,
847             0,
848             0,
849             "",
850             ""
851         );
852
853         #Add the last 4 bytes of the KeyUpdate record
854         $data = pack "C4",
855             0x00, 0x00, 0x01, # Message length
856             0x00; # Update not requested
857
858         #Add content type and tag
859         $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
860
861         $record->data($data);
862         $record->len(length $data);
863         push @{$records}, $record;
864
865     }
866 }