2 # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
4 # Licensed under the OpenSSL license (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
10 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11 use OpenSSL::Test::Utils;
14 my $test_name = "test_sslrecords";
17 plan skip_all => "TLSProxy isn't usable on $^O"
20 plan skip_all => "$test_name needs the dynamic engine feature enabled"
21 if disabled("engine") || disabled("dynamic-engine");
23 plan skip_all => "$test_name needs the sock feature enabled"
26 plan skip_all => "$test_name needs TLSv1.2 enabled"
27 if disabled("tls1_2");
29 $ENV{OPENSSL_ia32cap} = '~0x200000200000000';
30 my $proxy = TLSProxy::Proxy->new(
31 \&add_empty_recs_filter,
32 cmdstr(app(["openssl"]), display => 1),
33 srctop_file("apps", "server.pem"),
34 (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
37 my $boundary_test_type;
39 #Test 1: Injecting out of context empty records should fail
40 my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
41 my $inject_recs_num = 1;
42 $proxy->serverflags("-tls1_2");
43 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
45 ok(TLSProxy::Message->fail(), "Out of context empty records test");
47 #Test 2: Injecting in context empty records should succeed
49 $content_type = TLSProxy::Record::RT_HANDSHAKE;
50 $proxy->serverflags("-tls1_2");
52 ok(TLSProxy::Message->success(), "In context empty records test");
54 #Test 3: Injecting too many in context empty records should fail
56 #We allow 32 consecutive in context empty records
57 $inject_recs_num = 33;
58 $proxy->serverflags("-tls1_2");
60 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
62 #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
63 # send back an alert of its own because it cannot handle fragmented
66 $proxy->filter(\&add_frag_alert_filter);
67 $proxy->serverflags("-tls1_2");
69 ok(TLSProxy::Message->fail(), "Fragmented alert records test");
71 #Run some SSLv2 ClientHello tests
74 TLSV1_2_IN_SSLV2 => 0,
76 FRAGMENTED_IN_TLSV1_2 => 2,
77 FRAGMENTED_IN_SSLV2 => 3,
78 ALERT_BEFORE_SSLV2 => 4
80 #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
81 my $sslv2testtype = TLSV1_2_IN_SSLV2;
83 $proxy->filter(\&add_sslv2_filter);
84 $proxy->serverflags("-tls1_2");
86 ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
88 #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
89 # support this so it should fail. We actually treat it as an unknown
90 # protocol so we don't even send an alert in this case.
91 $sslv2testtype = SSLV2_IN_SSLV2;
93 $proxy->serverflags("-tls1_2");
95 ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
97 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
98 # at all, but it gives us confidence that Test 8 fails for the right
100 $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
102 $proxy->serverflags("-tls1_2");
104 ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
106 #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
107 # record; and another TLS1.2 record. This isn't allowed so should fail
108 $sslv2testtype = FRAGMENTED_IN_SSLV2;
110 $proxy->serverflags("-tls1_2");
112 ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
114 #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
115 # fail because an SSLv2 ClientHello must be the first record.
116 $sslv2testtype = ALERT_BEFORE_SSLV2;
118 $proxy->serverflags("-tls1_2");
120 ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
122 #Unrecognised record type tests
124 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
126 $proxy->serverflags("-tls1_2");
127 $proxy->filter(\&add_unknown_record_type);
129 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
132 skip "TLSv1.1 disabled", 1 if disabled("tls1_1");
134 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
136 $proxy->clientflags("-tls1_1");
138 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
141 #Test 12: Sending a different record version in TLS1.2 should fail
143 $proxy->clientflags("-tls1_2");
144 $proxy->filter(\&change_version);
146 ok(TLSProxy::Message->fail(), "Changed record version in TLS1.2");
148 #TLS1.3 specific tests
150 skip "TLSv1.3 disabled", 6 if disabled("tls1_3");
152 #Test 13: Sending a different record version in TLS1.3 should fail
154 $proxy->filter(\&change_version);
156 ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3");
158 #Test 14: Sending an unrecognised record type in TLS1.3 should fail
160 $proxy->filter(\&add_unknown_record_type);
162 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.3");
164 #Test 15: Sending an outer record type other than app data once encrypted
167 $proxy->filter(\&change_outer_record_type);
169 ok(TLSProxy::Message->fail(), "Wrong outer record type in TLS1.3");
172 DATA_AFTER_SERVER_HELLO => 0,
173 DATA_AFTER_FINISHED => 1,
174 DATA_AFTER_KEY_UPDATE => 2
177 #Test 16: Sending a ServerHello which doesn't end on a record boundary
180 $boundary_test_type = DATA_AFTER_SERVER_HELLO;
181 $proxy->filter(\¬_on_record_boundary);
183 ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (ServerHello)");
185 #Test 17: Sending a Finished which doesn't end on a record boundary
188 $boundary_test_type = DATA_AFTER_FINISHED;
189 $proxy->filter(\¬_on_record_boundary);
191 ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (Finished)");
193 #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
196 $boundary_test_type = DATA_AFTER_KEY_UPDATE;
197 $proxy->filter(\¬_on_record_boundary);
199 ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (KeyUpdate)");
203 sub add_empty_recs_filter
207 # We're only interested in the initial ClientHello
208 if ($proxy->flight != 0) {
212 for (my $i = 0; $i < $inject_recs_num; $i++) {
213 my $record = TLSProxy::Record->new(
216 TLSProxy::Record::VERS_TLS_1_2,
225 push @{$proxy->record_list}, $record;
229 sub add_frag_alert_filter
234 # We're only interested in the initial ClientHello
235 if ($proxy->flight != 0) {
239 # Add a zero length fragment first
240 #my $record = TLSProxy::Record->new(
242 # TLSProxy::Record::RT_ALERT,
243 # TLSProxy::Record::VERS_TLS_1_2,
250 #push @{$proxy->record_list}, $record;
252 # Now add the alert level (Fatal) as a separate record
253 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
254 my $record = TLSProxy::Record->new(
256 TLSProxy::Record::RT_ALERT,
257 TLSProxy::Record::VERS_TLS_1_2,
265 push @{$proxy->record_list}, $record;
267 # And finally the description (Unexpected message) in a third record
268 $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
269 $record = TLSProxy::Record->new(
271 TLSProxy::Record::RT_ALERT,
272 TLSProxy::Record::VERS_TLS_1_2,
280 push @{$proxy->record_list}, $record;
289 # We're only interested in the initial ClientHello
290 if ($proxy->flight != 0) {
294 # Ditch the real ClientHello - we're going to replace it with our own
295 shift @{$proxy->record_list};
297 if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
298 my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
299 TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
300 my $alertlen = length $alert;
301 $record = TLSProxy::Record->new(
303 TLSProxy::Record::RT_ALERT,
304 TLSProxy::Record::VERS_TLS_1_2,
313 push @{$proxy->record_list}, $record;
316 if ($sslv2testtype == ALERT_BEFORE_SSLV2
317 || $sslv2testtype == TLSV1_2_IN_SSLV2
318 || $sslv2testtype == SSLV2_IN_SSLV2) {
319 # This is an SSLv2 format ClientHello
324 0x00, 0x03, # Ciphersuites len
325 0x00, 0x00, # Session id len
326 0x00, 0x20, # Challenge len
327 0x00, 0x00, 0x2f, #AES128-SHA
328 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
329 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
330 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
332 if ($sslv2testtype == SSLV2_IN_SSLV2) {
333 # Set the version to "real" SSLv2
334 vec($clienthello, 1, 8) = 0x00;
335 vec($clienthello, 2, 8) = 0x02;
338 my $chlen = length $clienthello;
340 $record = TLSProxy::Record->new(
342 TLSProxy::Record::RT_HANDSHAKE,
343 TLSProxy::Record::VERS_TLS_1_2,
352 push @{$proxy->record_list}, $record;
354 # For this test we're using a real TLS ClientHello
358 0x00, 0x00, 0x2D, # Message length
359 0x03, 0x03, # TLSv1.2
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, # Random
363 0x00, # Session id len
364 0x00, 0x04, # Ciphersuites len
365 0x00, 0x2f, # AES128-SHA
366 0x00, 0xff, # Empty reneg info SCSV
367 0x01, # Compression methods len
368 0x00, # Null compression
369 0x00, 0x00; # Extensions len
371 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
372 # We deliberately split the second record prior to the Challenge/Random
373 # and set the first byte of the random to 1. This makes the second SSLv2
374 # record look like an SSLv2 ClientHello
375 my $frag1 = substr $clienthello, 0, 6;
376 my $frag2 = substr $clienthello, 6, 32;
377 my $frag3 = substr $clienthello, 38;
379 my $fraglen = length $frag1;
380 $record = TLSProxy::Record->new(
382 TLSProxy::Record::RT_HANDSHAKE,
383 TLSProxy::Record::VERS_TLS_1_2,
391 push @{$proxy->record_list}, $record;
393 $fraglen = length $frag2;
395 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
400 $record = TLSProxy::Record->new(
402 TLSProxy::Record::RT_HANDSHAKE,
403 TLSProxy::Record::VERS_TLS_1_2,
411 push @{$proxy->record_list}, $record;
413 $fraglen = length $frag3;
414 $record = TLSProxy::Record->new(
416 TLSProxy::Record::RT_HANDSHAKE,
417 TLSProxy::Record::VERS_TLS_1_2,
425 push @{$proxy->record_list}, $record;
430 sub add_unknown_record_type
434 # We'll change a record after the initial version neg has taken place
435 if ($proxy->flight != 1) {
439 my $lastrec = ${$proxy->record_list}[-1];
440 my $record = TLSProxy::Record->new(
442 TLSProxy::Record::RT_UNKNOWN,
452 #Find ServerHello record and insert after that
454 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
459 splice @{$proxy->record_list}, $i, 0, $record;
466 # We'll change a version after the initial version neg has taken place
467 if ($proxy->flight != 1) {
471 (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1);
474 sub change_outer_record_type
478 # We'll change a record after the initial version neg has taken place
479 if ($proxy->flight != 1) {
483 #Find ServerHello record and change record after that
485 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
488 #Skip CCS and ServerHello
490 ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
493 sub not_on_record_boundary
498 #Find server's first flight
499 if ($proxy->flight != 1) {
503 if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
504 #Merge the ServerHello and EncryptedExtensions records into one
506 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
509 $data = ${$proxy->record_list}[$i]->data();
510 $data .= ${$proxy->record_list}[$i + 1]->decrypt_data();
511 ${$proxy->record_list}[$i]->data($data);
512 ${$proxy->record_list}[$i]->len(length $data);
514 #Delete the old EncryptedExtensions record
515 splice @{$proxy->record_list}, $i + 1, 1;
516 } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
517 $data = ${$proxy->record_list}[-1]->decrypt_data;
519 #Add a KeyUpdate message onto the end of the Finished record
520 my $keyupdate = pack "C5",
522 0x00, 0x00, 0x01, # Message length
523 0x00; # Update not requested
527 #Add content type and tag
528 $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
531 ${$proxy->record_list}[-1]->data($data);
532 ${$proxy->record_list}[-1]->len(length $data);
534 #KeyUpdates must end on a record boundary
536 my $record = TLSProxy::Record->new(
538 TLSProxy::Record::RT_APPLICATION_DATA,
539 TLSProxy::Record::VERS_TLS_1_0,
548 #Add two KeyUpdate messages into a single record
549 my $keyupdate = pack "C5",
551 0x00, 0x00, 0x01, # Message length
552 0x00; # Update not requested
554 $data = $keyupdate.$keyupdate;
556 #Add content type and tag
557 $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
559 $record->data($data);
560 $record->len(length $data);
561 push @{$proxy->record_list}, $record;