2 # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
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
12 use OpenSSL
::Test qw
/:DEFAULT cmdstr srctop_file bldtop_dir/;
13 use OpenSSL
::Test
::Utils
;
16 my $test_name = "test_sslrecords";
19 plan skip_all
=> "TLSProxy isn't usable on $^O"
22 plan skip_all
=> "$test_name needs the dynamic engine feature enabled"
23 if disabled
("engine") || disabled
("dynamic-engine");
25 plan skip_all
=> "$test_name needs the sock feature enabled"
28 plan skip_all
=> "$test_name needs TLSv1.2 enabled"
29 if disabled
("tls1_2");
31 $ENV{OPENSSL_ia32cap
} = '~0x200000200000000';
32 my $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
})
39 my $boundary_test_type;
40 my $fatal_alert = 0; # set by filters at expected fatal alerts
42 #Test 1: Injecting out of context empty records should fail
43 my $content_type = TLSProxy
::Record
::RT_APPLICATION_DATA
;
44 my $inject_recs_num = 1;
45 $proxy->serverflags("-tls1_2");
46 $proxy->start() or plan skip_all
=> "Unable to start up Proxy for tests";
48 ok
($fatal_alert, "Out of context empty records test");
50 #Test 2: Injecting in context empty records should succeed
52 $content_type = TLSProxy
::Record
::RT_HANDSHAKE
;
53 $proxy->serverflags("-tls1_2");
55 ok
(TLSProxy
::Message
->success(), "In context empty records test");
57 #Test 3: Injecting too many in context empty records should fail
60 #We allow 32 consecutive in context empty records
61 $inject_recs_num = 33;
62 $proxy->serverflags("-tls1_2");
64 ok
($fatal_alert, "Too many in context empty records test");
66 #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
67 # send back an alert of its own because it cannot handle fragmented
71 $proxy->filter(\
&add_frag_alert_filter
);
72 $proxy->serverflags("-tls1_2");
74 ok
($fatal_alert, "Fragmented alert records test");
76 #Run some SSLv2 ClientHello tests
79 TLSV1_2_IN_SSLV2
=> 0,
81 FRAGMENTED_IN_TLSV1_2
=> 2,
82 FRAGMENTED_IN_SSLV2
=> 3,
83 ALERT_BEFORE_SSLV2
=> 4
85 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
86 my $sslv2testtype = TLSV1_2_IN_SSLV2
;
88 $proxy->filter(\
&add_sslv2_filter
);
89 $proxy->serverflags("-tls1_2");
91 ok
(TLSProxy
::Message
->success(), "TLSv1.2 in SSLv2 ClientHello test");
93 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
94 # support this so it should fail. We actually treat it as an unknown
95 # protocol so we don't even send an alert in this case.
96 $sslv2testtype = SSLV2_IN_SSLV2
;
98 $proxy->serverflags("-tls1_2");
100 ok
(TLSProxy
::Message
->fail(), "SSLv2 in SSLv2 ClientHello test");
102 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
103 # at all, but it gives us confidence that Test 8 fails for the right
105 $sslv2testtype = FRAGMENTED_IN_TLSV1_2
;
107 $proxy->serverflags("-tls1_2");
109 ok
(TLSProxy
::Message
->success(), "Fragmented ClientHello in TLSv1.2 test");
111 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
112 # record; and another TLS1.2 record. This isn't allowed so should fail
113 $sslv2testtype = FRAGMENTED_IN_SSLV2
;
115 $proxy->serverflags("-tls1_2");
117 ok
(TLSProxy
::Message
->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
119 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
120 # fail because an SSLv2 ClientHello must be the first record.
121 $sslv2testtype = ALERT_BEFORE_SSLV2
;
123 $proxy->serverflags("-tls1_2");
125 ok
(TLSProxy
::Message
->fail(), "Alert before SSLv2 ClientHello test");
127 #Unrecognised record type tests
129 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
132 $proxy->serverflags("-tls1_2");
133 $proxy->filter(\
&add_unknown_record_type
);
135 ok
($fatal_alert, "Unrecognised record type in TLS1.2");
138 skip
"TLSv1.1 disabled", 1 if disabled
("tls1_1");
140 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
143 $proxy->clientflags("-tls1_1");
145 ok
($fatal_alert, "Unrecognised record type in TLS1.1");
148 #Test 12: Sending a different record version in TLS1.2 should fail
151 $proxy->clientflags("-tls1_2");
152 $proxy->filter(\
&change_version
);
154 ok
($fatal_alert, "Changed record version in TLS1.2");
156 #TLS1.3 specific tests
158 skip
"TLSv1.3 disabled", 8 if disabled
("tls1_3");
160 #Test 13: Sending a different record version in TLS1.3 should fail
162 $proxy->filter(\
&change_version
);
164 ok
(TLSProxy
::Message
->fail(), "Changed record version in TLS1.3");
166 #Test 14: Sending an unrecognised record type in TLS1.3 should fail
169 $proxy->filter(\
&add_unknown_record_type
);
171 ok
($fatal_alert, "Unrecognised record type in TLS1.3");
173 #Test 15: Sending an outer record type other than app data once encrypted
177 $proxy->filter(\
&change_outer_record_type
);
179 ok
($fatal_alert, "Wrong outer record type in TLS1.3");
182 DATA_AFTER_SERVER_HELLO
=> 0,
183 DATA_AFTER_FINISHED
=> 1,
184 DATA_AFTER_KEY_UPDATE
=> 2,
185 DATA_BETWEEN_KEY_UPDATE
=> 3,
186 NO_DATA_BETWEEN_KEY_UPDATE
=> 4,
189 #Test 16: Sending a ServerHello which doesn't end on a record boundary
193 $boundary_test_type = DATA_AFTER_SERVER_HELLO
;
194 $proxy->filter(\
¬_on_record_boundary
);
196 ok
($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)");
198 #Test 17: Sending a Finished which doesn't end on a record boundary
202 $boundary_test_type = DATA_AFTER_FINISHED
;
204 ok
($fatal_alert, "Record not on boundary in TLS1.3 (Finished)");
206 #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
210 $boundary_test_type = DATA_AFTER_KEY_UPDATE
;
212 ok
($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
214 #Test 19: Sending application data in the middle of a fragmented KeyUpdate
215 # should fail. Strictly speaking this is not a record boundary test
216 # but we use the same filter.
219 $boundary_test_type = DATA_BETWEEN_KEY_UPDATE
;
221 ok
($fatal_alert, "Data between KeyUpdate");
223 #Test 20: Fragmented KeyUpdate. This should succeed. Strictly speaking this
224 # is not a record boundary test but we use the same filter.
226 $boundary_test_type = NO_DATA_BETWEEN_KEY_UPDATE
;
228 ok
(TLSProxy
::Message
->success(), "No data between KeyUpdate");
232 sub add_empty_recs_filter
235 my $records = $proxy->record_list;
237 # We're only interested in the initial ClientHello
238 if ($proxy->flight != 0) {
239 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(1) == 10;
243 for (my $i = 0; $i < $inject_recs_num; $i++) {
244 my $record = TLSProxy
::Record
->new(
247 TLSProxy
::Record
::VERS_TLS_1_2
,
255 push @
{$records}, $record;
259 sub add_frag_alert_filter
262 my $records = $proxy->record_list;
265 # We're only interested in the initial ClientHello
266 if ($proxy->flight != 0) {
267 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(1) == 10;
271 # Add a zero length fragment first
272 #my $record = TLSProxy::Record->new(
274 # TLSProxy::Record::RT_ALERT,
275 # TLSProxy::Record::VERS_TLS_1_2,
282 #push @{$proxy->record_list}, $record;
284 # Now add the alert level (Fatal) as a separate record
285 $byte = pack('C', TLSProxy
::Message
::AL_LEVEL_FATAL
);
286 my $record = TLSProxy
::Record
->new(
288 TLSProxy
::Record
::RT_ALERT
,
289 TLSProxy
::Record
::VERS_TLS_1_2
,
297 push @
{$records}, $record;
299 # And finally the description (Unexpected message) in a third record
300 $byte = pack('C', TLSProxy
::Message
::AL_DESC_UNEXPECTED_MESSAGE
);
301 $record = TLSProxy
::Record
->new(
303 TLSProxy
::Record
::RT_ALERT
,
304 TLSProxy
::Record
::VERS_TLS_1_2
,
312 push @
{$records}, $record;
321 # We're only interested in the initial ClientHello
322 if ($proxy->flight != 0) {
326 # Ditch the real ClientHello - we're going to replace it with our own
327 shift @
{$proxy->record_list};
329 if ($sslv2testtype == ALERT_BEFORE_SSLV2
) {
330 my $alert = pack('CC', TLSProxy
::Message
::AL_LEVEL_FATAL
,
331 TLSProxy
::Message
::AL_DESC_NO_RENEGOTIATION
);
332 my $alertlen = length $alert;
333 $record = TLSProxy
::Record
->new(
335 TLSProxy
::Record
::RT_ALERT
,
336 TLSProxy
::Record
::VERS_TLS_1_2
,
345 push @
{$proxy->record_list}, $record;
348 if ($sslv2testtype == ALERT_BEFORE_SSLV2
349 || $sslv2testtype == TLSV1_2_IN_SSLV2
350 || $sslv2testtype == SSLV2_IN_SSLV2
) {
351 # This is an SSLv2 format ClientHello
356 0x00, 0x03, # Ciphersuites len
357 0x00, 0x00, # Session id len
358 0x00, 0x20, # Challenge len
359 0x00, 0x00, 0x2f, #AES128-SHA
360 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
361 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
362 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
364 if ($sslv2testtype == SSLV2_IN_SSLV2
) {
365 # Set the version to "real" SSLv2
366 vec($clienthello, 1, 8) = 0x00;
367 vec($clienthello, 2, 8) = 0x02;
370 my $chlen = length $clienthello;
372 $record = TLSProxy
::Record
->new(
374 TLSProxy
::Record
::RT_HANDSHAKE
,
375 TLSProxy
::Record
::VERS_TLS_1_2
,
384 push @
{$proxy->record_list}, $record;
386 # For this test we're using a real TLS ClientHello
390 0x00, 0x00, 0x2D, # Message length
391 0x03, 0x03, # TLSv1.2
392 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
393 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
394 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
395 0x00, # Session id len
396 0x00, 0x04, # Ciphersuites len
397 0x00, 0x2f, # AES128-SHA
398 0x00, 0xff, # Empty reneg info SCSV
399 0x01, # Compression methods len
400 0x00, # Null compression
401 0x00, 0x00; # Extensions len
403 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
404 # We deliberately split the second record prior to the Challenge/Random
405 # and set the first byte of the random to 1. This makes the second SSLv2
406 # record look like an SSLv2 ClientHello
407 my $frag1 = substr $clienthello, 0, 6;
408 my $frag2 = substr $clienthello, 6, 32;
409 my $frag3 = substr $clienthello, 38;
411 my $fraglen = length $frag1;
412 $record = TLSProxy
::Record
->new(
414 TLSProxy
::Record
::RT_HANDSHAKE
,
415 TLSProxy
::Record
::VERS_TLS_1_2
,
423 push @
{$proxy->record_list}, $record;
425 $fraglen = length $frag2;
427 if ($sslv2testtype == FRAGMENTED_IN_SSLV2
) {
432 $record = TLSProxy
::Record
->new(
434 TLSProxy
::Record
::RT_HANDSHAKE
,
435 TLSProxy
::Record
::VERS_TLS_1_2
,
443 push @
{$proxy->record_list}, $record;
445 $fraglen = length $frag3;
446 $record = TLSProxy
::Record
->new(
448 TLSProxy
::Record
::RT_HANDSHAKE
,
449 TLSProxy
::Record
::VERS_TLS_1_2
,
457 push @
{$proxy->record_list}, $record;
462 sub add_unknown_record_type
465 my $records = $proxy->record_list;
468 # We'll change a record after the initial version neg has taken place
469 if ($proxy->flight == 0) {
472 } elsif ($proxy->flight != 1 || $added_record) {
473 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(0) == 10;
477 my $record = TLSProxy
::Record
->new(
479 TLSProxy
::Record
::RT_UNKNOWN
,
480 @
{$records}[-1]->version(),
489 #Find ServerHello record and insert after that
491 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
496 splice @
{$proxy->record_list}, $i, 0, $record;
503 my $records = $proxy->record_list;
505 # We'll change a version after the initial version neg has taken place
506 if ($proxy->flight != 1) {
507 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(0) == 70;
511 if ($#{$records} > 1) {
512 # ... typically in ServerHelloDone
513 @
{$records}[-1]->version(TLSProxy
::Record
::VERS_TLS_1_1
);
517 sub change_outer_record_type
520 my $records = $proxy->record_list;
522 # We'll change a record after the initial version neg has taken place
523 if ($proxy->flight != 1) {
524 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(0) == 10;
528 # Find CCS record and change record after that
530 foreach my $record (@
{$records}) {
531 last if $record->content_type == TLSProxy
::Record
::RT_CCS
;
534 if (defined(${$records}[++$i])) {
535 ${$records}[$i]->outer_content_type(TLSProxy
::Record
::RT_HANDSHAKE
);
539 sub not_on_record_boundary
542 my $records = $proxy->record_list;
545 #Find server's first flight
546 if ($proxy->flight != 1) {
547 $fatal_alert = 1 if @
{$records}[-1]->is_fatal_alert(0) == 10;
551 if ($boundary_test_type == DATA_AFTER_SERVER_HELLO
) {
552 #Merge the ServerHello and EncryptedExtensions records into one
554 foreach my $record (@
{$records}) {
555 if ($record->content_type == TLSProxy
::Record
::RT_HANDSHAKE
) {
556 $record->{sent
} = 1; # pretend it's sent already
562 if (defined(${$records}[$i+1])) {
563 $data = ${$records}[$i]->data();
564 $data .= ${$records}[$i+1]->decrypt_data();
565 ${$records}[$i+1]->data($data);
566 ${$records}[$i+1]->len(length $data);
568 #Delete the old ServerHello record
569 splice @
{$records}, $i, 1;
571 } elsif ($boundary_test_type == DATA_AFTER_FINISHED
) {
572 return if @
{$proxy->{message_list
}}[-1]->{mt
}
573 != TLSProxy
::Message
::MT_FINISHED
;
575 my $last_record = @
{$records}[-1];
576 $data = $last_record->decrypt_data;
578 #Add a KeyUpdate message onto the end of the Finished record
579 my $keyupdate = pack "C5",
581 0x00, 0x00, 0x01, # Message length
582 0x00; # Update not requested
586 #Add content type and tag
587 $data .= pack("C", TLSProxy
::Record
::RT_HANDSHAKE
).("\0"x16
);
590 $last_record->data($data);
591 $last_record->len(length $data);
592 } elsif ($boundary_test_type == DATA_AFTER_KEY_UPDATE
) {
593 return if @
{$proxy->{message_list
}}[-1]->{mt
}
594 != TLSProxy
::Message
::MT_FINISHED
;
596 #KeyUpdates must end on a record boundary
598 my $record = TLSProxy
::Record
->new(
600 TLSProxy
::Record
::RT_APPLICATION_DATA
,
601 TLSProxy
::Record
::VERS_TLS_1_2
,
610 #Add two KeyUpdate messages into a single record
611 my $keyupdate = pack "C5",
613 0x00, 0x00, 0x01, # Message length
614 0x00; # Update not requested
616 $data = $keyupdate.$keyupdate;
618 #Add content type and tag
619 $data .= pack("C", TLSProxy
::Record
::RT_HANDSHAKE
).("\0"x16
);
621 $record->data($data);
622 $record->len(length $data);
623 push @
{$records}, $record;
625 return if @
{$proxy->{message_list
}}[-1]->{mt
}
626 != TLSProxy
::Message
::MT_FINISHED
;
628 my $record = TLSProxy
::Record
->new(
630 TLSProxy
::Record
::RT_APPLICATION_DATA
,
631 TLSProxy
::Record
::VERS_TLS_1_2
,
640 #Add a partial KeyUpdate message into the record
642 0x18; # KeyUpdate message type. Omit the rest of the message header
644 #Add content type and tag
645 $data .= pack("C", TLSProxy
::Record
::RT_HANDSHAKE
).("\0"x16
);
647 $record->data($data);
648 $record->len(length $data);
649 push @
{$records}, $record;
651 if ($boundary_test_type == DATA_BETWEEN_KEY_UPDATE
) {
652 #Now add an app data record
653 $record = TLSProxy
::Record
->new(
655 TLSProxy
::Record
::RT_APPLICATION_DATA
,
656 TLSProxy
::Record
::VERS_TLS_1_2
,
665 #Add an empty app data record (just content type and tag)
666 $data = pack("C", TLSProxy
::Record
::RT_APPLICATION_DATA
).("\0"x16
);
668 $record->data($data);
669 $record->len(length $data);
670 push @
{$records}, $record;
673 #Now add the rest of the KeyUpdate message
674 $record = TLSProxy
::Record
->new(
676 TLSProxy
::Record
::RT_APPLICATION_DATA
,
677 TLSProxy
::Record
::VERS_TLS_1_2
,
686 #Add the last 4 bytes of the KeyUpdate record
688 0x00, 0x00, 0x01, # Message length
689 0x00; # Update not requested
691 #Add content type and tag
692 $data .= pack("C", TLSProxy
::Record
::RT_HANDSHAKE
).("\0"x16
);
694 $record->data($data);
695 $record->len(length $data);
696 push @
{$records}, $record;