diff options
-rw-r--r-- | test/recipes/70-test_dtlsrecords.t | 153 | ||||
-rw-r--r-- | test/recipes/70-test_sslrecords.t | 581 | ||||
-rw-r--r-- | util/perl/TLSProxy/Proxy.pm | 6 |
3 files changed, 346 insertions, 394 deletions
diff --git a/test/recipes/70-test_dtlsrecords.t b/test/recipes/70-test_dtlsrecords.t deleted file mode 100644 index dc223ea759..0000000000 --- a/test/recipes/70-test_dtlsrecords.t +++ /dev/null @@ -1,153 +0,0 @@ -#! /usr/bin/env perl -# Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. -# -# Licensed under the Apache License 2.0 (the "License"). You may not use -# this file except in compliance with the License. You can obtain a copy -# in the file LICENSE in the source distribution or at -# https://www.openssl.org/source/license.html - -use strict; -use feature 'state'; - -use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; -use OpenSSL::Test::Utils; -use TLSProxy::Proxy; -use TLSProxy::Message; - -my $test_name = "test_dtlsrecords"; -setup($test_name); - -plan skip_all => "TLSProxy isn't usable on $^O" - if $^O =~ /^(VMS|MSWin32)$/; - -plan skip_all => "$test_name needs the dynamic engine feature enabled" - if disabled("engine") || disabled("dynamic-engine"); - -plan skip_all => "$test_name needs the sock feature enabled" - if disabled("sock"); - -plan skip_all => "$test_name needs DTLSv1.2 enabled" - if disabled("dtls1_2"); - -my $proxy = TLSProxy::Proxy->new_dtls( - undef, - cmdstr(app(["openssl"]), display => 1), - srctop_file("apps", "server.pem"), - (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) -); - -plan tests => 4; - -my $fatal_alert = 0; # set by filters at expected fatal alerts -my $inject_recs_num = 0; # used by add_empty_recs_filter -my $proxy_start_success = 0; - -#Test 1: Injecting out of context empty records should succeed -my $content_type = TLSProxy::Record::RT_APPLICATION_DATA; -$inject_recs_num = 1; -$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2"); -$proxy->clientflags("-max_protocol DTLSv1.2"); -$proxy->filter(\&add_empty_recs_filter); -$proxy_start_success = $proxy->start(); -ok($proxy_start_success && TLSProxy::Message->success(), "Out of context empty records test"); - -#Test 2: Injecting in context empty records should succeed -$proxy->clear(); -$content_type = TLSProxy::Record::RT_HANDSHAKE; -$inject_recs_num = 1; -$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2"); -$proxy->clientflags("-max_protocol DTLSv1.2"); -$proxy->filter(\&add_empty_recs_filter); -$proxy_start_success = $proxy->start(); -ok($proxy_start_success && TLSProxy::Message->success(), "In context empty records test"); - -#Unrecognised record type tests - -#Test 3: Sending an unrecognised record type in DTLSv1.2 should fail -$fatal_alert = 0; -$proxy->clear(); -$proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2"); -$proxy->clientflags("-max_protocol DTLSv1.2"); -$proxy->filter(\&add_unknown_record_type); -ok($proxy->start() == 0, "Unrecognised record type in DTLS1.2"); - -SKIP: { - skip "DTLSv1 disabled", 1 if disabled("dtls1"); - - #Test 4: Sending an unrecognised record type in DTLSv1 should fail - $fatal_alert = 0; - $proxy->clear(); - $proxy->clientflags("-min_protocol DTLSv1 -max_protocol DTLSv1 -cipher DEFAULT:\@SECLEVEL=0"); - $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); - $proxy->filter(\&add_unknown_record_type); - ok($proxy->start() == 0, "Unrecognised record type in DTLSv1"); -} - -sub add_empty_recs_filter -{ - my $proxy = shift; - my $records = $proxy->record_list; - - # We're only interested in the initial ClientHello - if ($proxy->flight != 0) { - $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE; - return; - } - - for (my $i = 0; $i < $inject_recs_num; $i++) { - my $record = TLSProxy::Record->new_dtls( - 0, - $content_type, - TLSProxy::Record::VERS_TLS_1_2, - 0, - 0, - 0, - 0, - 0, - 0, - "", - "" - ); - push @{$records}, $record; - } -} - -sub add_unknown_record_type -{ - my $proxy = shift; - my $records = $proxy->record_list; - state $added_record; - - # We'll change a record after the initial version neg has taken place - if ($proxy->flight == 0) { - $added_record = 0; - return; - } elsif ($proxy->flight != 1 || $added_record) { - $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE; - return; - } - - my $record = TLSProxy::Record->new_dtls( - 1, - TLSProxy::Record::RT_UNKNOWN, - @{$records}[-1]->version(), - @{$records}[-1]->epoch(), - @{$records}[-1]->seq() +1, - 1, - 0, - 1, - 1, - "X", - "X" - ); - - #Find ServerHello record and insert after that - my $i; - for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) { - next; - } - $i++; - - splice @{$proxy->record_list}, $i, 0, $record; - $added_record = 1; -} diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t index 43e288b63e..02110714e9 100644 --- a/test/recipes/70-test_sslrecords.t +++ b/test/recipes/70-test_sslrecords.t @@ -29,248 +29,311 @@ plan skip_all => "$test_name needs the sock feature enabled" plan skip_all => "$test_name needs TLSv1.2 enabled" if disabled("tls1_2"); -my $proxy = TLSProxy::Proxy->new( - \&add_empty_recs_filter, - cmdstr(app(["openssl"]), display => 1), - srctop_file("apps", "server.pem"), - (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) -); - -my $boundary_test_type; -my $fatal_alert = 0; # set by filters at expected fatal alerts - -#Test 1: Injecting out of context empty records should fail -my $content_type = TLSProxy::Record::RT_APPLICATION_DATA; -my $inject_recs_num = 1; -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; -plan tests => 21; -ok($fatal_alert, "Out of context empty records test"); - -#Test 2: Injecting in context empty records should succeed -$proxy->clear(); -$content_type = TLSProxy::Record::RT_HANDSHAKE; -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->start(); -ok(TLSProxy::Message->success(), "In context empty records test"); - -#Test 3: Injecting too many in context empty records should fail -$fatal_alert = 0; -$proxy->clear(); -#We allow 32 consecutive in context empty records -$inject_recs_num = 33; -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->start(); -ok($fatal_alert, "Too many in context empty records test"); - -#Test 4: Injecting a fragmented fatal alert should fail. We expect the server to -# send back an alert of its own because it cannot handle fragmented -# alerts -$fatal_alert = 0; -$proxy->clear(); -$proxy->filter(\&add_frag_alert_filter); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->start(); -ok($fatal_alert, "Fragmented alert records test"); - -#Run some SSLv2 ClientHello tests - -use constant { - TLSV1_2_IN_SSLV2 => 0, - SSLV2_IN_SSLV2 => 1, - FRAGMENTED_IN_TLSV1_2 => 2, - FRAGMENTED_IN_SSLV2 => 3, - ALERT_BEFORE_SSLV2 => 4 -}; - -# The TLSv1.2 in SSLv2 ClientHello need to run at security level 0 -# because in a SSLv2 ClientHello we can't send extensions to indicate -# which signature algorithm we want to use, and the default is SHA1. - -#Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello -my $sslv2testtype = TLSV1_2_IN_SSLV2; -$proxy->clear(); -$proxy->filter(\&add_sslv2_filter); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3 -legacy_renegotiation"); -$proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); -$proxy->start(); -ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test"); - -#Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't -# support this so it should fail. We actually treat it as an unknown -# protocol so we don't even send an alert in this case. -$sslv2testtype = SSLV2_IN_SSLV2; -$proxy->clear(); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); -$proxy->start(); -ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test"); - -#Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test -# at all, but it gives us confidence that Test 8 fails for the right -# reasons -$sslv2testtype = FRAGMENTED_IN_TLSV1_2; -$proxy->clear(); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); -$proxy->start(); -ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test"); - -#Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2 -# record; and another TLS1.2 record. This isn't allowed so should fail -$sslv2testtype = FRAGMENTED_IN_SSLV2; -$proxy->clear(); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); -$proxy->start(); -ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test"); - -#Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should -# fail because an SSLv2 ClientHello must be the first record. -$sslv2testtype = ALERT_BEFORE_SSLV2; -$proxy->clear(); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); -$proxy->start(); -ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test"); - -#Unrecognised record type tests - -#Test 10: Sending an unrecognised record type in TLS1.2 should fail -$fatal_alert = 0; -$proxy->clear(); -$proxy->serverflags("-tls1_2"); -$proxy->clientflags("-no_tls1_3"); -$proxy->filter(\&add_unknown_record_type); -$proxy->start(); -ok($fatal_alert, "Unrecognised record type in TLS1.2"); - -SKIP: { - skip "TLSv1.1 disabled", 1 if disabled("tls1_1"); - - #Test 11: Sending an unrecognised record type in TLS1.1 should fail - $fatal_alert = 0; +my $testplanisset = 0; +my $inject_recs_num = undef; +my $content_type = undef; +my $boundary_test_type = undef; +my $fatal_alert = undef; # set by filters at expected fatal alerts +my $sslv2testtype = undef; +my $proxy_start_success = 0; + +# Run tests with TLS +run_tests(0); +# Run tests with DTLS +run_tests(1); + +sub run_tests +{ + my $run_test_as_dtls = shift; + my $proxy; + if ($run_test_as_dtls == 1) { + $proxy = TLSProxy::Proxy->new_dtls( + \&add_empty_recs_filter, + cmdstr(app([ "openssl" ]), display => 1), + srctop_file("apps", "server.pem"), + (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) + ); + } else { + $proxy = TLSProxy::Proxy->new( + \&add_empty_recs_filter, + cmdstr(app([ "openssl" ]), display => 1), + srctop_file("apps", "server.pem"), + (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) + ); + } + + $fatal_alert = 0; # set by filters at expected fatal alerts + SKIP: { + skip "Record tests not intended for dtls", 1 if $run_test_as_dtls == 1; + #Test 1: Injecting out of context empty records should fail + $content_type = TLSProxy::Record::RT_APPLICATION_DATA; + $inject_recs_num = 1; + $fatal_alert = 0; + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; + if ($testplanisset == 0) { + plan tests => 42; + $testplanisset = 1; + } + ok($fatal_alert, "Out of context empty records test"); + } + + #Test 2: Injecting in context empty records should succeed $proxy->clear(); - $proxy->clientflags("-tls1_1 -cipher DEFAULT:\@SECLEVEL=0"); - $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); - $proxy->start(); - ok($fatal_alert, "Unrecognised record type in TLS1.1"); -} + $content_type = TLSProxy::Record::RT_HANDSHAKE; + if ($run_test_as_dtls == 1) { + $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2"); + $proxy->clientflags("-max_protocol DTLSv1.2"); + } else { + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + } + $proxy_start_success = $proxy->start(); + ok($proxy_start_success && TLSProxy::Message->success(), + "In context empty records test".($run_test_as_dtls == 1) ? " for DTLS" : " for TLS"); -#Test 12: Sending a different record version in TLS1.2 should fail -$fatal_alert = 0; -$proxy->clear(); -$proxy->clientflags("-tls1_2"); -$proxy->filter(\&change_version); -$proxy->start(); -ok($fatal_alert, "Changed record version in TLS1.2"); + SKIP: { + skip "Record tests not intended for dtls", 7 if $run_test_as_dtls == 1; + #Test 3: Injecting too many in context empty records should fail + $fatal_alert = 0; + $proxy->clear(); + #We allow 32 consecutive in context empty records + $inject_recs_num = 33; + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->start(); + ok($fatal_alert, "Too many in context empty records test"); -#TLS1.3 specific tests -SKIP: { - skip "TLSv1.3 disabled", 9 - if disabled("tls1_3") || (disabled("ec") && disabled("dh")); + #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to + # send back an alert of its own because it cannot handle fragmented + # alerts + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&add_frag_alert_filter); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->start(); + ok($fatal_alert, "Fragmented alert records test"); - #Test 13: Sending a different record version in TLS1.3 should fail - $proxy->clear(); - $proxy->filter(\&change_version); - $proxy->start(); - ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3"); + #Run some SSLv2 ClientHello tests - #Test 14: Sending an unrecognised record type in TLS1.3 should fail - $fatal_alert = 0; - $proxy->clear(); - $proxy->filter(\&add_unknown_record_type); - $proxy->start(); - ok($fatal_alert, "Unrecognised record type in TLS1.3"); + use constant { + TLSV1_2_IN_SSLV2 => 0, + SSLV2_IN_SSLV2 => 1, + FRAGMENTED_IN_TLSV1_2 => 2, + FRAGMENTED_IN_SSLV2 => 3, + ALERT_BEFORE_SSLV2 => 4 + }; - #Test 15: Sending an outer record type other than app data once encrypted - #should fail - $fatal_alert = 0; - $proxy->clear(); - $proxy->filter(\&change_outer_record_type); - $proxy->start(); - ok($fatal_alert, "Wrong outer record type in TLS1.3"); - - use constant { - DATA_AFTER_SERVER_HELLO => 0, - DATA_AFTER_FINISHED => 1, - DATA_AFTER_KEY_UPDATE => 2, - DATA_BETWEEN_KEY_UPDATE => 3, - NO_DATA_BETWEEN_KEY_UPDATE => 4, - }; - - #Test 16: Sending a ServerHello which doesn't end on a record boundary - # should fail - $fatal_alert = 0; - $proxy->clear(); - $boundary_test_type = DATA_AFTER_SERVER_HELLO; - $proxy->filter(\¬_on_record_boundary); - $proxy->start(); - ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)"); + # The TLSv1.2 in SSLv2 ClientHello need to run at security level 0 + # because in a SSLv2 ClientHello we can't send extensions to indicate + # which signature algorithm we want to use, and the default is SHA1. - #Test 17: Sending a Finished which doesn't end on a record boundary - # should fail - $fatal_alert = 0; - $proxy->clear(); - $boundary_test_type = DATA_AFTER_FINISHED; - $proxy->start(); - ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)"); + #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello + $sslv2testtype = TLSV1_2_IN_SSLV2; + $proxy->clear(); + $proxy->filter(\&add_sslv2_filter); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3 -legacy_renegotiation"); + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy->start(); + ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test"); - #Test 18: Sending a KeyUpdate which doesn't end on a record boundary - # should fail - $fatal_alert = 0; - $proxy->clear(); - $boundary_test_type = DATA_AFTER_KEY_UPDATE; - $proxy->start(); - ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)"); + #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't + # support this so it should fail. We actually treat it as an unknown + # protocol so we don't even send an alert in this case. + $sslv2testtype = SSLV2_IN_SSLV2; + $proxy->clear(); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy->start(); + ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test"); + + #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test + # at all, but it gives us confidence that Test 8 fails for the right + # reasons + $sslv2testtype = FRAGMENTED_IN_TLSV1_2; + $proxy->clear(); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy->start(); + ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test"); - #Test 19: Sending application data in the middle of a fragmented KeyUpdate - # should fail. Strictly speaking this is not a record boundary test - # but we use the same filter. + #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2 + # record; and another TLS1.2 record. This isn't allowed so should fail + $sslv2testtype = FRAGMENTED_IN_SSLV2; + $proxy->clear(); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test"); + + #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should + # fail because an SSLv2 ClientHello must be the first record. + $sslv2testtype = ALERT_BEFORE_SSLV2; + $proxy->clear(); + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test"); + } + #Unrecognised record type tests + + #Test 10: Sending an unrecognised record type in TLS1.2 should fail $fatal_alert = 0; $proxy->clear(); - $boundary_test_type = DATA_BETWEEN_KEY_UPDATE; - $proxy->start(); - ok($fatal_alert, "Data between KeyUpdate"); + if ($run_test_as_dtls == 1) { + $proxy->serverflags("-min_protocol DTLSv1.2 -max_protocol DTLSv1.2"); + $proxy->clientflags("-max_protocol DTLSv1.2"); + } else { + $proxy->serverflags("-tls1_2"); + $proxy->clientflags("-no_tls1_3"); + } + $proxy->filter(\&add_unknown_record_type); + $proxy_start_success = $proxy->start(); - #Test 20: Fragmented KeyUpdate. This should succeed. Strictly speaking this - # is not a record boundary test but we use the same filter. - $proxy->clear(); - $boundary_test_type = NO_DATA_BETWEEN_KEY_UPDATE; - $proxy->start(); - ok(TLSProxy::Message->success(), "No data between KeyUpdate"); + if ($run_test_as_dtls == 1) { + ok($proxy_start_success == 0, "Unrecognised record type in DTLS1.2"); + } else { + ok($fatal_alert, "Unrecognised record type in TLS1.2"); + } + + SKIP: { + skip "TLSv1.1 or DTLSv1 disabled", 1 if ($run_test_as_dtls == 0 && disabled("tls1_1")) + || ($run_test_as_dtls == 1 && disabled("dtls1")); + + #Test 11: Sending an unrecognised record type in TLS1.1 should fail + $fatal_alert = 0; + $proxy->clear(); + if ($run_test_as_dtls == 1) { + $proxy->clientflags("-min_protocol DTLSv1 -max_protocol DTLSv1 -cipher DEFAULT:\@SECLEVEL=0"); + } else { + $proxy->clientflags("-tls1_1 -cipher DEFAULT:\@SECLEVEL=0"); + } + $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); + $proxy_start_success = $proxy->start(); + if ($run_test_as_dtls == 1) { + ok($proxy_start_success == 0, "Unrecognised record type in DTLSv1"); + } else { + ok($fatal_alert, "Unrecognised record type in TLSv1.1"); + } + } SKIP: { - skip "EC disabled", 1 if disabled("ec"); - - #Test 21: Force an HRR and change the "real" ServerHello to have a protocol - # record version of 0x0301 (TLSv1.0). At this point we have already - # decided that we are doing TLSv1.3 but are still using plaintext - # records. The server should be sending a record version of 0x303 - # (TLSv1.2), but the RFC requires us to ignore this field so we - # should tolerate the incorrect version. + skip "Record tests not intended for dtls", 10 if $run_test_as_dtls == 1; + #Test 12: Sending a different record version in TLS1.2 should fail + $fatal_alert = 0; $proxy->clear(); - $proxy->filter(\&change_server_hello_version); - $proxy->serverflags("-groups P-256"); # Force an HRR + $proxy->clientflags("-tls1_2"); + $proxy->filter(\&change_version); $proxy->start(); - ok(TLSProxy::Message->success(), "Bad ServerHello record version after HRR"); + ok($fatal_alert, "Changed record version in TLS1.2"); + + #TLS1.3 specific tests + SKIP: { + skip "TLSv1.3 disabled", 9 + if disabled("tls1_3") || (disabled("ec") && disabled("dh")); + + #Test 13: Sending a different record version in TLS1.3 should fail + $proxy->clear(); + $proxy->filter(\&change_version); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3"); + + #Test 14: Sending an unrecognised record type in TLS1.3 should fail + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&add_unknown_record_type); + $proxy->start(); + ok($fatal_alert, "Unrecognised record type in TLS1.3"); + + #Test 15: Sending an outer record type other than app data once encrypted + #should fail + $fatal_alert = 0; + $proxy->clear(); + $proxy->filter(\&change_outer_record_type); + $proxy->start(); + ok($fatal_alert, "Wrong outer record type in TLS1.3"); + + use constant { + DATA_AFTER_SERVER_HELLO => 0, + DATA_AFTER_FINISHED => 1, + DATA_AFTER_KEY_UPDATE => 2, + DATA_BETWEEN_KEY_UPDATE => 3, + NO_DATA_BETWEEN_KEY_UPDATE => 4, + }; + + #Test 16: Sending a ServerHello which doesn't end on a record boundary + # should fail + $fatal_alert = 0; + $proxy->clear(); + $boundary_test_type = DATA_AFTER_SERVER_HELLO; + $proxy->filter(\¬_on_record_boundary); + $proxy->start(); + ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)"); + + #Test 17: Sending a Finished which doesn't end on a record boundary + # should fail + $fatal_alert = 0; + $proxy->clear(); + $boundary_test_type = DATA_AFTER_FINISHED; + $proxy->start(); + ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)"); + + #Test 18: Sending a KeyUpdate which doesn't end on a record boundary + # should fail + $fatal_alert = 0; + $proxy->clear(); + $boundary_test_type = DATA_AFTER_KEY_UPDATE; + $proxy->start(); + ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)"); + + #Test 19: Sending application data in the middle of a fragmented KeyUpdate + # should fail. Strictly speaking this is not a record boundary test + # but we use the same filter. + $fatal_alert = 0; + $proxy->clear(); + $boundary_test_type = DATA_BETWEEN_KEY_UPDATE; + $proxy->start(); + ok($fatal_alert, "Data between KeyUpdate"); + + #Test 20: Fragmented KeyUpdate. This should succeed. Strictly speaking this + # is not a record boundary test but we use the same filter. + $proxy->clear(); + $boundary_test_type = NO_DATA_BETWEEN_KEY_UPDATE; + $proxy->start(); + ok(TLSProxy::Message->success(), "No data between KeyUpdate"); + + SKIP: { + skip "EC disabled", 1 if disabled("ec"); + + #Test 21: Force an HRR and change the "real" ServerHello to have a protocol + # record version of 0x0301 (TLSv1.0). At this point we have already + # decided that we are doing TLSv1.3 but are still using plaintext + # records. The server should be sending a record version of 0x303 + # (TLSv1.2), but the RFC requires us to ignore this field so we + # should tolerate the incorrect version. + $proxy->clear(); + $proxy->filter(\&change_server_hello_version); + $proxy->serverflags("-groups P-256"); # Force an HRR + $proxy->start(); + ok(TLSProxy::Message->success(), "Bad ServerHello record version after HRR"); + } + } } - } +} sub add_empty_recs_filter { my $proxy = shift; my $records = $proxy->record_list; + my $isdtls = $proxy->isdtls(); # We're only interested in the initial ClientHello if ($proxy->flight != 0) { @@ -279,17 +342,34 @@ sub add_empty_recs_filter } for (my $i = 0; $i < $inject_recs_num; $i++) { - my $record = TLSProxy::Record->new( - 0, - $content_type, - TLSProxy::Record::VERS_TLS_1_2, - 0, - 0, - 0, - 0, - "", - "" - ); + my $record; + if ($isdtls == 1) { + $record = TLSProxy::Record->new_dtls( + 0, + $content_type, + TLSProxy::Record::VERS_DTLS_1_2, + 0, + 0, + 0, + 0, + 0, + 0, + "", + "" + ); + } else { + $record = TLSProxy::Record->new( + 0, + $content_type, + TLSProxy::Record::VERS_TLS_1_2, + 0, + 0, + 0, + 0, + "", + "" + ); + } push @{$records}, $record; } } @@ -501,6 +581,7 @@ sub add_unknown_record_type { my $proxy = shift; my $records = $proxy->record_list; + my $isdtls = $proxy->isdtls; state $added_record; # We'll change a record after the initial version neg has taken place @@ -512,17 +593,35 @@ sub add_unknown_record_type return; } - my $record = TLSProxy::Record->new( - 1, - TLSProxy::Record::RT_UNKNOWN, - @{$records}[-1]->version(), - 1, - 0, - 1, - 1, - "X", - "X" - ); + my $record; + + if ($isdtls) { + $record = TLSProxy::Record->new_dtls( + 1, + TLSProxy::Record::RT_UNKNOWN, + @{$records}[-1]->version(), + @{$records}[-1]->epoch(), + @{$records}[-1]->seq() +1, + 1, + 0, + 1, + 1, + "X", + "X" + ); + } else { + $record = TLSProxy::Record->new( + 1, + TLSProxy::Record::RT_UNKNOWN, + @{$records}[-1]->version(), + 1, + 0, + 1, + 1, + "X", + "X" + ); + } #Find ServerHello record and insert after that my $i; diff --git a/util/perl/TLSProxy/Proxy.pm b/util/perl/TLSProxy/Proxy.pm index 3ecbd1893e..06de4fbb39 100644 --- a/util/perl/TLSProxy/Proxy.pm +++ b/util/perl/TLSProxy/Proxy.pm @@ -750,4 +750,10 @@ sub ciphersuite return $ciphersuite; } +sub isdtls +{ + my $self = shift; + return $self->{isdtls}; #read-only +} + 1; |