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