1#! /usr/bin/env perl
2# Copyright 2016-2021 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 => 20;
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 extentions 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", 8
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
252
253sub add_empty_recs_filter
254{
255    my $proxy = shift;
256    my $records = $proxy->record_list;
257
258    # We're only interested in the initial ClientHello
259    if ($proxy->flight != 0) {
260        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
261        return;
262    }
263
264    for (my $i = 0; $i < $inject_recs_num; $i++) {
265        my $record = TLSProxy::Record->new(
266            0,
267            $content_type,
268            TLSProxy::Record::VERS_TLS_1_2,
269            0,
270            0,
271            0,
272            0,
273            "",
274            ""
275        );
276        push @{$records}, $record;
277    }
278}
279
280sub add_frag_alert_filter
281{
282    my $proxy = shift;
283    my $records = $proxy->record_list;
284    my $byte;
285
286    # We're only interested in the initial ClientHello
287    if ($proxy->flight != 0) {
288        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
289        return;
290    }
291
292    # Add a zero length fragment first
293    #my $record = TLSProxy::Record->new(
294    #    0,
295    #    TLSProxy::Record::RT_ALERT,
296    #    TLSProxy::Record::VERS_TLS_1_2,
297    #    0,
298    #    0,
299    #    0,
300    #    "",
301    #    ""
302    #);
303    #push @{$proxy->record_list}, $record;
304
305    # Now add the alert level (Fatal) as a separate record
306    $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
307    my $record = TLSProxy::Record->new(
308        0,
309        TLSProxy::Record::RT_ALERT,
310        TLSProxy::Record::VERS_TLS_1_2,
311        1,
312        0,
313        1,
314        1,
315        $byte,
316        $byte
317    );
318    push @{$records}, $record;
319
320    # And finally the description (Unexpected message) in a third record
321    $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
322    $record = TLSProxy::Record->new(
323        0,
324        TLSProxy::Record::RT_ALERT,
325        TLSProxy::Record::VERS_TLS_1_2,
326        1,
327        0,
328        1,
329        1,
330        $byte,
331        $byte
332    );
333    push @{$records}, $record;
334}
335
336sub add_sslv2_filter
337{
338    my $proxy = shift;
339    my $clienthello;
340    my $record;
341
342    # We're only interested in the initial ClientHello
343    if ($proxy->flight != 0) {
344        return;
345    }
346
347    # Ditch the real ClientHello - we're going to replace it with our own
348    shift @{$proxy->record_list};
349
350    if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
351        my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
352                               TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
353        my $alertlen = length $alert;
354        $record = TLSProxy::Record->new(
355            0,
356            TLSProxy::Record::RT_ALERT,
357            TLSProxy::Record::VERS_TLS_1_2,
358            $alertlen,
359            0,
360            $alertlen,
361            $alertlen,
362            $alert,
363            $alert
364        );
365
366        push @{$proxy->record_list}, $record;
367    }
368
369    if ($sslv2testtype == ALERT_BEFORE_SSLV2
370            || $sslv2testtype == TLSV1_2_IN_SSLV2
371            || $sslv2testtype == SSLV2_IN_SSLV2) {
372        # This is an SSLv2 format ClientHello
373        $clienthello =
374            pack "C44",
375            0x01, # ClientHello
376            0x03, 0x03, #TLSv1.2
377            0x00, 0x03, # Ciphersuites len
378            0x00, 0x00, # Session id len
379            0x00, 0x20, # Challenge len
380            0x00, 0x00, 0x2f, #AES128-SHA
381            0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
382            0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
383            0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
384
385        if ($sslv2testtype == SSLV2_IN_SSLV2) {
386            # Set the version to "real" SSLv2
387            vec($clienthello, 1, 8) = 0x00;
388            vec($clienthello, 2, 8) = 0x02;
389        }
390
391        my $chlen = length $clienthello;
392
393        $record = TLSProxy::Record->new(
394            0,
395            TLSProxy::Record::RT_HANDSHAKE,
396            TLSProxy::Record::VERS_TLS_1_2,
397            $chlen,
398            1, #SSLv2
399            $chlen,
400            $chlen,
401            $clienthello,
402            $clienthello
403        );
404
405        push @{$proxy->record_list}, $record;
406    } else {
407        # For this test we're using a real TLS ClientHello
408        $clienthello =
409            pack "C49",
410            0x01, # ClientHello
411            0x00, 0x00, 0x2D, # Message length
412            0x03, 0x03, # TLSv1.2
413            0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
414            0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
415            0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
416            0x00, # Session id len
417            0x00, 0x04, # Ciphersuites len
418            0x00, 0x2f, # AES128-SHA
419            0x00, 0xff, # Empty reneg info SCSV
420            0x01, # Compression methods len
421            0x00, # Null compression
422            0x00, 0x00; # Extensions len
423
424        # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
425        # We deliberately split the second record prior to the Challenge/Random
426        # and set the first byte of the random to 1. This makes the second SSLv2
427        # record look like an SSLv2 ClientHello
428        my $frag1 = substr $clienthello, 0, 6;
429        my $frag2 = substr $clienthello, 6, 32;
430        my $frag3 = substr $clienthello, 38;
431
432        my $fraglen = length $frag1;
433        $record = TLSProxy::Record->new(
434            0,
435            TLSProxy::Record::RT_HANDSHAKE,
436            TLSProxy::Record::VERS_TLS_1_2,
437            $fraglen,
438            0,
439            $fraglen,
440            $fraglen,
441            $frag1,
442            $frag1
443        );
444        push @{$proxy->record_list}, $record;
445
446        $fraglen = length $frag2;
447        my $recvers;
448        if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
449            $recvers = 1;
450        } else {
451            $recvers = 0;
452        }
453        $record = TLSProxy::Record->new(
454            0,
455            TLSProxy::Record::RT_HANDSHAKE,
456            TLSProxy::Record::VERS_TLS_1_2,
457            $fraglen,
458            $recvers,
459            $fraglen,
460            $fraglen,
461            $frag2,
462            $frag2
463        );
464        push @{$proxy->record_list}, $record;
465
466        $fraglen = length $frag3;
467        $record = TLSProxy::Record->new(
468            0,
469            TLSProxy::Record::RT_HANDSHAKE,
470            TLSProxy::Record::VERS_TLS_1_2,
471            $fraglen,
472            0,
473            $fraglen,
474            $fraglen,
475            $frag3,
476            $frag3
477        );
478        push @{$proxy->record_list}, $record;
479    }
480
481}
482
483sub add_unknown_record_type
484{
485    my $proxy = shift;
486    my $records = $proxy->record_list;
487    state $added_record;
488
489    # We'll change a record after the initial version neg has taken place
490    if ($proxy->flight == 0) {
491        $added_record = 0;
492        return;
493    } elsif ($proxy->flight != 1 || $added_record) {
494        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
495        return;
496    }
497
498    my $record = TLSProxy::Record->new(
499        1,
500        TLSProxy::Record::RT_UNKNOWN,
501        @{$records}[-1]->version(),
502        1,
503        0,
504        1,
505        1,
506        "X",
507        "X"
508    );
509
510    #Find ServerHello record and insert after that
511    my $i;
512    for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
513        next;
514    }
515    $i++;
516
517    splice @{$proxy->record_list}, $i, 0, $record;
518    $added_record = 1;
519}
520
521sub change_version
522{
523    my $proxy = shift;
524    my $records = $proxy->record_list;
525
526    # We'll change a version after the initial version neg has taken place
527    if ($proxy->flight != 1) {
528        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 70;
529        return;
530    }
531
532    if ($#{$records} > 1) {
533        # ... typically in ServerHelloDone
534        @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
535    }
536}
537
538sub change_outer_record_type
539{
540    my $proxy = shift;
541    my $records = $proxy->record_list;
542
543    # We'll change a record after the initial version neg has taken place
544    if ($proxy->flight != 1) {
545        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
546        return;
547    }
548
549    # Find CCS record and change record after that
550    my $i = 0;
551    foreach my $record (@{$records}) {
552        last if $record->content_type == TLSProxy::Record::RT_CCS;
553        $i++;
554    }
555    if (defined(${$records}[++$i])) {
556        ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
557    }
558}
559
560sub not_on_record_boundary
561{
562    my $proxy = shift;
563    my $records = $proxy->record_list;
564    my $data;
565
566    #Find server's first flight
567    if ($proxy->flight != 1) {
568        $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
569        return;
570    }
571
572    if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
573        #Merge the ServerHello and EncryptedExtensions records into one
574        my $i = 0;
575        foreach my $record (@{$records}) {
576            if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
577                $record->{sent} = 1;    # pretend it's sent already
578                last;
579            }
580            $i++;
581        }
582
583        if (defined(${$records}[$i+1])) {
584            $data = ${$records}[$i]->data();
585            $data .= ${$records}[$i+1]->decrypt_data();
586            ${$records}[$i+1]->data($data);
587            ${$records}[$i+1]->len(length $data);
588
589            #Delete the old ServerHello record
590            splice @{$records}, $i, 1;
591        }
592    } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
593        return if @{$proxy->{message_list}}[-1]->{mt}
594                  != TLSProxy::Message::MT_FINISHED;
595
596        my $last_record = @{$records}[-1];
597        $data = $last_record->decrypt_data;
598
599        #Add a KeyUpdate message onto the end of the Finished record
600        my $keyupdate = pack "C5",
601            0x18, # KeyUpdate
602            0x00, 0x00, 0x01, # Message length
603            0x00; # Update not requested
604
605        $data .= $keyupdate;
606
607        #Add content type and tag
608        $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
609
610        #Update the record
611        $last_record->data($data);
612        $last_record->len(length $data);
613    } elsif ($boundary_test_type == DATA_AFTER_KEY_UPDATE) {
614        return if @{$proxy->{message_list}}[-1]->{mt}
615                  != TLSProxy::Message::MT_FINISHED;
616
617        #KeyUpdates must end on a record boundary
618
619        my $record = TLSProxy::Record->new(
620            1,
621            TLSProxy::Record::RT_APPLICATION_DATA,
622            TLSProxy::Record::VERS_TLS_1_2,
623            0,
624            0,
625            0,
626            0,
627            "",
628            ""
629        );
630
631        #Add two KeyUpdate messages into a single record
632        my $keyupdate = pack "C5",
633            0x18, # KeyUpdate
634            0x00, 0x00, 0x01, # Message length
635            0x00; # Update not requested
636
637        $data = $keyupdate.$keyupdate;
638
639        #Add content type and tag
640        $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
641
642        $record->data($data);
643        $record->len(length $data);
644        push @{$records}, $record;
645    } else {
646        return if @{$proxy->{message_list}}[-1]->{mt}
647                  != TLSProxy::Message::MT_FINISHED;
648
649        my $record = TLSProxy::Record->new(
650            1,
651            TLSProxy::Record::RT_APPLICATION_DATA,
652            TLSProxy::Record::VERS_TLS_1_2,
653            0,
654            0,
655            0,
656            0,
657            "",
658            ""
659        );
660
661        #Add a partial KeyUpdate message into the record
662        $data = pack "C1",
663            0x18; # KeyUpdate message type. Omit the rest of the message header
664
665        #Add content type and tag
666        $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
667
668        $record->data($data);
669        $record->len(length $data);
670        push @{$records}, $record;
671
672        if ($boundary_test_type == DATA_BETWEEN_KEY_UPDATE) {
673            #Now add an app data record
674            $record = TLSProxy::Record->new(
675                1,
676                TLSProxy::Record::RT_APPLICATION_DATA,
677                TLSProxy::Record::VERS_TLS_1_2,
678                0,
679                0,
680                0,
681                0,
682                "",
683                ""
684            );
685
686            #Add an empty app data record (just content type and tag)
687            $data = pack("C", TLSProxy::Record::RT_APPLICATION_DATA).("\0"x16);
688
689            $record->data($data);
690            $record->len(length $data);
691            push @{$records}, $record;
692        }
693
694        #Now add the rest of the KeyUpdate message
695        $record = TLSProxy::Record->new(
696            1,
697            TLSProxy::Record::RT_APPLICATION_DATA,
698            TLSProxy::Record::VERS_TLS_1_2,
699            0,
700            0,
701            0,
702            0,
703            "",
704            ""
705        );
706
707        #Add the last 4 bytes of the KeyUpdate record
708        $data = pack "C4",
709            0x00, 0x00, 0x01, # Message length
710            0x00; # Update not requested
711
712        #Add content type and tag
713        $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
714
715        $record->data($data);
716        $record->len(length $data);
717        push @{$records}, $record;
718
719    }
720}
721