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