]>
Commit | Line | Data |
---|---|---|
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 | ||
9 | use strict; | |
c4220c0f AP |
10 | use feature 'state'; |
11 | ||
4f0c4757 MC |
12 | use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; |
13 | use OpenSSL::Test::Utils; | |
14 | use TLSProxy::Proxy; | |
a1c72cc2 | 15 | use TLSProxy::Message; |
4f0c4757 MC |
16 | |
17 | my $test_name = "test_sslrecords"; | |
18 | setup($test_name); | |
19 | ||
20 | plan skip_all => "TLSProxy isn't usable on $^O" | |
c5856878 | 21 | if $^O =~ /^(VMS)$/; |
4f0c4757 MC |
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 | ||
80f397e2 MC |
29 | plan skip_all => "$test_name needs TLSv1.2 enabled" |
30 | if disabled("tls1_2"); | |
4f0c4757 | 31 | |
4d7f5b82 FWH |
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 | # Run tests with TLS | |
41 | run_tests(0); | |
42 | # Run tests with DTLS | |
43 | run_tests(1); | |
44 | ||
45 | sub 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(\¬_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 |
332 | sub 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 | |
377 | sub 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 | |
433 | sub 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 | |
580 | sub 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 | |
637 | sub 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 |
654 | sub 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 |
674 | sub 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 | |
696 | sub 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 | } |