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