2 # Copyright 2016 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"
18 if $^O =~ /^(VMS|MSWin32)$/;
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 #Test 1: Injecting out of context empty records should fail
38 my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
39 my $inject_recs_num = 1;
40 $proxy->serverflags("-tls1_2");
41 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
43 if (!disabled("tls1_1")) {
46 if (!disabled("tls1_3")) {
49 plan tests => $num_tests;
50 ok(TLSProxy::Message->fail(), "Out of context empty records test");
52 #Test 2: Injecting in context empty records should succeed
54 $content_type = TLSProxy::Record::RT_HANDSHAKE;
55 $proxy->serverflags("-tls1_2");
57 ok(TLSProxy::Message->success(), "In context empty records test");
59 #Test 3: Injecting too many in context empty records should fail
61 #We allow 32 consecutive in context empty records
62 $inject_recs_num = 33;
63 $proxy->serverflags("-tls1_2");
65 ok(TLSProxy::Message->fail(), "Too many in context empty records test");
67 #Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
68 # alerts to be sent from either side because *we* injected the fatal
69 # alert, i.e. this will look like a disorderly close
71 $proxy->filter(\&add_frag_alert_filter);
72 $proxy->serverflags("-tls1_2");
74 ok(!TLSProxy::Message->end(), "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->end(), "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 #Unregcognised record type tests
129 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
131 $proxy->serverflags("-tls1_2");
132 $proxy->filter(\&add_unknown_record_type);
134 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
136 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
137 if (!disabled("tls1_1")) {
139 $proxy->clientflags("-tls1_1");
141 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
144 #Test 12: Sending a different record version in TLS1.2 should fail
146 $proxy->clientflags("-tls1_2");
147 $proxy->filter(\&change_version);
149 ok(TLSProxy::Message->fail(), "Changed record version in TLS1.2");
151 #Test 13: Sending a different record version in TLS1.3 should succeed
152 if (!disabled("tls1_3")) {
154 $proxy->filter(\&change_version);
156 ok(TLSProxy::Message->success(), "Changed record version in TLS1.3");
159 sub add_empty_recs_filter
163 # We're only interested in the initial ClientHello
164 if ($proxy->flight != 0) {
168 for (my $i = 0; $i < $inject_recs_num; $i++) {
169 my $record = TLSProxy::Record->new(
172 TLSProxy::Record::VERS_TLS_1_2,
181 push @{$proxy->record_list}, $record;
185 sub add_frag_alert_filter
190 # We're only interested in the initial ClientHello
191 if ($proxy->flight != 0) {
195 # Add a zero length fragment first
196 #my $record = TLSProxy::Record->new(
198 # TLSProxy::Record::RT_ALERT,
199 # TLSProxy::Record::VERS_TLS_1_2,
206 #push @{$proxy->record_list}, $record;
208 # Now add the alert level (Fatal) as a separate record
209 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
210 my $record = TLSProxy::Record->new(
212 TLSProxy::Record::RT_ALERT,
213 TLSProxy::Record::VERS_TLS_1_2,
221 push @{$proxy->record_list}, $record;
223 # And finally the description (Unexpected message) in a third record
224 $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
225 $record = TLSProxy::Record->new(
227 TLSProxy::Record::RT_ALERT,
228 TLSProxy::Record::VERS_TLS_1_2,
236 push @{$proxy->record_list}, $record;
245 # We're only interested in the initial ClientHello
246 if ($proxy->flight != 0) {
250 # Ditch the real ClientHello - we're going to replace it with our own
251 shift @{$proxy->record_list};
253 if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
254 my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
255 TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
256 my $alertlen = length $alert;
257 $record = TLSProxy::Record->new(
259 TLSProxy::Record::RT_ALERT,
260 TLSProxy::Record::VERS_TLS_1_2,
269 push @{$proxy->record_list}, $record;
272 if ($sslv2testtype == ALERT_BEFORE_SSLV2
273 || $sslv2testtype == TLSV1_2_IN_SSLV2
274 || $sslv2testtype == SSLV2_IN_SSLV2) {
275 # This is an SSLv2 format ClientHello
280 0x00, 0x03, # Ciphersuites len
281 0x00, 0x00, # Session id len
282 0x00, 0x20, # Challenge len
283 0x00, 0x00, 0x2f, #AES128-SHA
284 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
285 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
286 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
288 if ($sslv2testtype == SSLV2_IN_SSLV2) {
289 # Set the version to "real" SSLv2
290 vec($clienthello, 1, 8) = 0x00;
291 vec($clienthello, 2, 8) = 0x02;
294 my $chlen = length $clienthello;
296 $record = TLSProxy::Record->new(
298 TLSProxy::Record::RT_HANDSHAKE,
299 TLSProxy::Record::VERS_TLS_1_2,
308 push @{$proxy->record_list}, $record;
310 # For this test we're using a real TLS ClientHello
314 0x00, 0x00, 0x2D, # Message length
315 0x03, 0x03, # TLSv1.2
316 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
317 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
318 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
319 0x00, # Session id len
320 0x00, 0x04, # Ciphersuites len
321 0x00, 0x2f, # AES128-SHA
322 0x00, 0xff, # Empty reneg info SCSV
323 0x01, # Compression methods len
324 0x00, # Null compression
325 0x00, 0x00; # Extensions len
327 # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
328 # We deliberately split the second record prior to the Challenge/Random
329 # and set the first byte of the random to 1. This makes the second SSLv2
330 # record look like an SSLv2 ClientHello
331 my $frag1 = substr $clienthello, 0, 6;
332 my $frag2 = substr $clienthello, 6, 32;
333 my $frag3 = substr $clienthello, 38;
335 my $fraglen = length $frag1;
336 $record = TLSProxy::Record->new(
338 TLSProxy::Record::RT_HANDSHAKE,
339 TLSProxy::Record::VERS_TLS_1_2,
347 push @{$proxy->record_list}, $record;
349 $fraglen = length $frag2;
351 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
356 $record = TLSProxy::Record->new(
358 TLSProxy::Record::RT_HANDSHAKE,
359 TLSProxy::Record::VERS_TLS_1_2,
367 push @{$proxy->record_list}, $record;
369 $fraglen = length $frag3;
370 $record = TLSProxy::Record->new(
372 TLSProxy::Record::RT_HANDSHAKE,
373 TLSProxy::Record::VERS_TLS_1_2,
381 push @{$proxy->record_list}, $record;
386 sub add_unknown_record_type
390 # We'll change a record after the initial version neg has taken place
391 if ($proxy->flight != 2) {
395 my $lastrec = ${$proxy->record_list}[-1];
396 my $record = TLSProxy::Record->new(
398 TLSProxy::Record::RT_UNKNOWN,
408 unshift @{$proxy->record_list}, $record;
415 # We'll change a version after the initial version neg has taken place
416 if ($proxy->flight != 2) {
420 (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1);