]> git.ipfire.org Git - thirdparty/openssl.git/blame - test/recipes/70-test_sslrecords.t
More typo fixes
[thirdparty/openssl.git] / test / recipes / 70-test_sslrecords.t
CommitLineData
4f0c4757
MC
1#! /usr/bin/env perl
2# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
3#
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
8
9use strict;
10use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
11use OpenSSL::Test::Utils;
12use TLSProxy::Proxy;
13
14my $test_name = "test_sslrecords";
15setup($test_name);
16
17plan skip_all => "TLSProxy isn't usable on $^O"
18 if $^O =~ /^(VMS|MSWin32)$/;
19
20plan skip_all => "$test_name needs the dynamic engine feature enabled"
21 if disabled("engine") || disabled("dynamic-engine");
22
23plan skip_all => "$test_name needs the sock feature enabled"
24 if disabled("sock");
25
80f397e2
MC
26plan skip_all => "$test_name needs TLSv1.2 enabled"
27 if disabled("tls1_2");
4f0c4757
MC
28
29$ENV{OPENSSL_ia32cap} = '~0x200000200000000';
30my $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})
35);
36
774c909b
MC
37my $boundary_test_type;
38
4f0c4757
MC
39#Test 1: Injecting out of context empty records should fail
40my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
41my $inject_recs_num = 1;
837e591d 42$proxy->serverflags("-tls1_2");
b02b5743 43$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
774c909b 44plan tests => 18;
4f0c4757
MC
45ok(TLSProxy::Message->fail(), "Out of context empty records test");
46
47#Test 2: Injecting in context empty records should succeed
48$proxy->clear();
49$content_type = TLSProxy::Record::RT_HANDSHAKE;
837e591d 50$proxy->serverflags("-tls1_2");
4f0c4757
MC
51$proxy->start();
52ok(TLSProxy::Message->success(), "In context empty records test");
53
54#Test 3: Injecting too many in context empty records should fail
55$proxy->clear();
56#We allow 32 consecutive in context empty records
57$inject_recs_num = 33;
837e591d 58$proxy->serverflags("-tls1_2");
4f0c4757
MC
59$proxy->start();
60ok(TLSProxy::Message->fail(), "Too many in context empty records test");
61
c3fd55d4
MC
62#Test 4: Injecting a fragmented fatal alert should fail. We actually expect no
63# alerts to be sent from either side because *we* injected the fatal
64# alert, i.e. this will look like a disorderly close
65$proxy->clear();
66$proxy->filter(\&add_frag_alert_filter);
837e591d 67$proxy->serverflags("-tls1_2");
c3fd55d4
MC
68$proxy->start();
69ok(!TLSProxy::Message->end(), "Fragmented alert records test");
70
a2a0c86b
MC
71#Run some SSLv2 ClientHello tests
72
73use constant {
74 TLSV1_2_IN_SSLV2 => 0,
75 SSLV2_IN_SSLV2 => 1,
76 FRAGMENTED_IN_TLSV1_2 => 2,
77 FRAGMENTED_IN_SSLV2 => 3,
78 ALERT_BEFORE_SSLV2 => 4
79};
80#Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
a2a0c86b
MC
81my $sslv2testtype = TLSV1_2_IN_SSLV2;
82$proxy->clear();
83$proxy->filter(\&add_sslv2_filter);
837e591d 84$proxy->serverflags("-tls1_2");
a2a0c86b
MC
85$proxy->start();
86ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
87
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;
92$proxy->clear();
837e591d 93$proxy->serverflags("-tls1_2");
a2a0c86b 94$proxy->start();
0f82d2f5 95ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
a2a0c86b
MC
96
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
99# reasons
100$sslv2testtype = FRAGMENTED_IN_TLSV1_2;
101$proxy->clear();
837e591d 102$proxy->serverflags("-tls1_2");
a2a0c86b
MC
103$proxy->start();
104ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
105
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;
109$proxy->clear();
837e591d 110$proxy->serverflags("-tls1_2");
a2a0c86b
MC
111$proxy->start();
112ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
113
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;
117$proxy->clear();
837e591d 118$proxy->serverflags("-tls1_2");
a2a0c86b
MC
119$proxy->start();
120ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
1f3e70a4 121
69687aa8 122#Unrecognised record type tests
1f3e70a4
MC
123
124#Test 10: Sending an unrecognised record type in TLS1.2 should fail
125$proxy->clear();
9970290e 126$proxy->serverflags("-tls1_2");
1f3e70a4
MC
127$proxy->filter(\&add_unknown_record_type);
128$proxy->start();
129ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
130
774c909b
MC
131SKIP: {
132 skip "TLSv1.1 disabled", 1 if disabled("tls1_1");
133
134 #Test 11: Sending an unrecognised record type in TLS1.1 should fail
1f3e70a4
MC
135 $proxy->clear();
136 $proxy->clientflags("-tls1_1");
137 $proxy->start();
138 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
139}
140
8e47ee18
MC
141#Test 12: Sending a different record version in TLS1.2 should fail
142$proxy->clear();
143$proxy->clientflags("-tls1_2");
144$proxy->filter(\&change_version);
145$proxy->start();
146ok(TLSProxy::Message->fail(), "Changed record version in TLS1.2");
147
b4c6e37e 148#TLS1.3 specific tests
774c909b 149SKIP: {
75e314f2 150 skip "TLSv1.3 disabled", 6 if disabled("tls1_3");
774c909b 151
b4c6e37e 152 #Test 13: Sending a different record version in TLS1.3 should succeed
8e47ee18
MC
153 $proxy->clear();
154 $proxy->filter(\&change_version);
155 $proxy->start();
156 ok(TLSProxy::Message->success(), "Changed record version in TLS1.3");
b4c6e37e
MC
157
158 #Test 14: Sending an unrecognised record type in TLS1.3 should fail
159 $proxy->clear();
160 $proxy->filter(\&add_unknown_record_type);
161 $proxy->start();
162 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.3");
163
164 #Test 15: Sending an outer record type other than app data once encrypted
165 #should fail
166 $proxy->clear();
167 $proxy->filter(\&change_outer_record_type);
168 $proxy->start();
169 ok(TLSProxy::Message->fail(), "Wrong outer record type in TLS1.3");
774c909b
MC
170
171 use constant {
172 DATA_AFTER_SERVER_HELLO => 0,
173 DATA_AFTER_FINISHED => 1,
174 DATA_AFTER_KEY_UPDATE => 2
175 };
176
177 #Test 16: Sending a ServerHello which doesn't end on a record boundary
178 # should fail
179 $proxy->clear();
180 $boundary_test_type = DATA_AFTER_SERVER_HELLO;
181 $proxy->filter(\&not_on_record_boundary);
182 $proxy->start();
183 ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (ServerHello)");
184
185 #Test 17: Sending a Finished which doesn't end on a record boundary
186 # should fail
187 $proxy->clear();
188 $boundary_test_type = DATA_AFTER_FINISHED;
189 $proxy->filter(\&not_on_record_boundary);
190 $proxy->start();
191 ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (Finished)");
192
193 #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
194 # should fail
195 $proxy->clear();
196 $boundary_test_type = DATA_AFTER_KEY_UPDATE;
197 $proxy->filter(\&not_on_record_boundary);
198 $proxy->start();
199 ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (KeyUpdate)");
b4c6e37e
MC
200 }
201
8e47ee18 202
4f0c4757
MC
203sub add_empty_recs_filter
204{
205 my $proxy = shift;
206
207 # We're only interested in the initial ClientHello
208 if ($proxy->flight != 0) {
209 return;
210 }
211
212 for (my $i = 0; $i < $inject_recs_num; $i++) {
213 my $record = TLSProxy::Record->new(
214 0,
215 $content_type,
216 TLSProxy::Record::VERS_TLS_1_2,
217 0,
218 0,
219 0,
a2a0c86b 220 0,
4f0c4757
MC
221 "",
222 ""
223 );
224
225 push @{$proxy->record_list}, $record;
226 }
227}
c3fd55d4
MC
228
229sub add_frag_alert_filter
230{
231 my $proxy = shift;
232 my $byte;
233
234 # We're only interested in the initial ClientHello
235 if ($proxy->flight != 0) {
236 return;
237 }
238
239 # Add a zero length fragment first
240 #my $record = TLSProxy::Record->new(
241 # 0,
242 # TLSProxy::Record::RT_ALERT,
243 # TLSProxy::Record::VERS_TLS_1_2,
244 # 0,
245 # 0,
246 # 0,
247 # "",
248 # ""
249 #);
250 #push @{$proxy->record_list}, $record;
251
60250017 252 # Now add the alert level (Fatal) as a separate record
c3fd55d4
MC
253 $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
254 my $record = TLSProxy::Record->new(
255 0,
256 TLSProxy::Record::RT_ALERT,
257 TLSProxy::Record::VERS_TLS_1_2,
258 1,
a2a0c86b 259 0,
c3fd55d4
MC
260 1,
261 1,
262 $byte,
263 $byte
264 );
265 push @{$proxy->record_list}, $record;
266
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(
270 0,
271 TLSProxy::Record::RT_ALERT,
272 TLSProxy::Record::VERS_TLS_1_2,
273 1,
a2a0c86b 274 0,
c3fd55d4
MC
275 1,
276 1,
277 $byte,
278 $byte
279 );
280 push @{$proxy->record_list}, $record;
281}
a2a0c86b
MC
282
283sub add_sslv2_filter
284{
285 my $proxy = shift;
286 my $clienthello;
287 my $record;
288
289 # We're only interested in the initial ClientHello
290 if ($proxy->flight != 0) {
291 return;
292 }
293
294 # Ditch the real ClientHello - we're going to replace it with our own
295 shift @{$proxy->record_list};
296
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(
302 0,
303 TLSProxy::Record::RT_ALERT,
304 TLSProxy::Record::VERS_TLS_1_2,
305 $alertlen,
306 0,
307 $alertlen,
308 $alertlen,
309 $alert,
310 $alert
311 );
312
313 push @{$proxy->record_list}, $record;
314 }
315
316 if ($sslv2testtype == ALERT_BEFORE_SSLV2
317 || $sslv2testtype == TLSV1_2_IN_SSLV2
318 || $sslv2testtype == SSLV2_IN_SSLV2) {
319 # This is an SSLv2 format ClientHello
320 $clienthello =
321 pack "C44",
322 0x01, # ClientHello
323 0x03, 0x03, #TLSv1.2
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
331
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;
336 }
337
338 my $chlen = length $clienthello;
339
340 $record = TLSProxy::Record->new(
341 0,
342 TLSProxy::Record::RT_HANDSHAKE,
343 TLSProxy::Record::VERS_TLS_1_2,
344 $chlen,
345 1, #SSLv2
346 $chlen,
347 $chlen,
348 $clienthello,
349 $clienthello
350 );
351
352 push @{$proxy->record_list}, $record;
353 } else {
354 # For this test we're using a real TLS ClientHello
355 $clienthello =
356 pack "C49",
357 0x01, # 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
370
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;
378
379 my $fraglen = length $frag1;
380 $record = TLSProxy::Record->new(
381 0,
382 TLSProxy::Record::RT_HANDSHAKE,
383 TLSProxy::Record::VERS_TLS_1_2,
384 $fraglen,
385 0,
386 $fraglen,
387 $fraglen,
388 $frag1,
389 $frag1
390 );
391 push @{$proxy->record_list}, $record;
392
393 $fraglen = length $frag2;
394 my $recvers;
395 if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
396 $recvers = 1;
397 } else {
398 $recvers = 0;
399 }
400 $record = TLSProxy::Record->new(
401 0,
402 TLSProxy::Record::RT_HANDSHAKE,
403 TLSProxy::Record::VERS_TLS_1_2,
404 $fraglen,
405 $recvers,
406 $fraglen,
407 $fraglen,
408 $frag2,
409 $frag2
410 );
411 push @{$proxy->record_list}, $record;
412
413 $fraglen = length $frag3;
414 $record = TLSProxy::Record->new(
415 0,
416 TLSProxy::Record::RT_HANDSHAKE,
417 TLSProxy::Record::VERS_TLS_1_2,
418 $fraglen,
419 0,
420 $fraglen,
421 $fraglen,
422 $frag3,
423 $frag3
424 );
425 push @{$proxy->record_list}, $record;
426 }
427
428}
1f3e70a4
MC
429
430sub add_unknown_record_type
431{
432 my $proxy = shift;
433
434 # We'll change a record after the initial version neg has taken place
b4c6e37e 435 if ($proxy->flight != 1) {
1f3e70a4
MC
436 return;
437 }
438
439 my $lastrec = ${$proxy->record_list}[-1];
440 my $record = TLSProxy::Record->new(
b4c6e37e 441 1,
1f3e70a4
MC
442 TLSProxy::Record::RT_UNKNOWN,
443 $lastrec->version(),
444 1,
445 0,
446 1,
447 1,
448 "X",
449 "X"
450 );
451
b4c6e37e
MC
452 #Find ServerHello record and insert after that
453 my $i;
454 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
455 next;
456 }
457 $i++;
458
459 splice @{$proxy->record_list}, $i, 0, $record;
1f3e70a4 460}
8e47ee18
MC
461
462sub change_version
463{
464 my $proxy = shift;
465
466 # We'll change a version after the initial version neg has taken place
467 if ($proxy->flight != 2) {
468 return;
469 }
470
471 (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1);
472}
b4c6e37e
MC
473
474sub change_outer_record_type
475{
476 my $proxy = shift;
477
478 # We'll change a record after the initial version neg has taken place
479 if ($proxy->flight != 1) {
480 return;
481 }
482
483 #Find ServerHello record and change record after that
484 my $i;
485 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
486 next;
487 }
488 $i++;
489 ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
490}
774c909b
MC
491
492sub not_on_record_boundary
493{
494 my $proxy = shift;
495 my $data;
496
497 #Find server's first flight
498 if ($proxy->flight != 1) {
499 return;
500 }
501
502 if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
503 #Merge the ServerHello and EncryptedExtensions records into one
504 my $i;
505 for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
506 next;
507 }
508 $data = ${$proxy->record_list}[$i]->data();
509 $data .= ${$proxy->record_list}[$i + 1]->decrypt_data();
510 ${$proxy->record_list}[$i]->data($data);
511 ${$proxy->record_list}[$i]->len(length $data);
512
513 #Delete the old EncryptedExtensions record
514 splice @{$proxy->record_list}, $i + 1, 1;
515 } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
516 $data = ${$proxy->record_list}[-1]->decrypt_data;
517
518 #Add a KeyUpdate message onto the end of the Finished record
519 my $keyupdate = pack "C5",
520 0x18, # KeyUpdate
521 0x00, 0x00, 0x01, # Message length
522 0x00; # Update not requested
523
524 $data .= $keyupdate;
525
526 #Add content type and tag
527 $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
528
529 #Update the record
530 ${$proxy->record_list}[-1]->data($data);
531 ${$proxy->record_list}[-1]->len(length $data);
532 } else {
533 #KeyUpdates must end on a record boundary
534
535 my $record = TLSProxy::Record->new(
536 1,
537 TLSProxy::Record::RT_APPLICATION_DATA,
538 TLSProxy::Record::VERS_TLS_1_0,
539 0,
540 0,
541 0,
542 0,
543 "",
544 ""
545 );
546
547 #Add two KeyUpdate messages into a single record
548 my $keyupdate = pack "C5",
549 0x18, # KeyUpdate
550 0x00, 0x00, 0x01, # Message length
551 0x00; # Update not requested
552
553 $data = $keyupdate.$keyupdate;
554
555 #Add content type and tag
556 $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
557
558 $record->data($data);
559 $record->len(length $data);
560 push @{$proxy->record_list}, $record;
561 }
562}