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