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