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