TLSProxy: Handle partial messages with DTLS

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26532)
This commit is contained in:
Frederik Wedel-Heinen 2025-01-22 16:48:06 +01:00 committed by Tomas Mraz
parent 6a233c31de
commit 0c37be3a7c
6 changed files with 92 additions and 114 deletions

View file

@ -26,10 +26,6 @@ plan skip_all => "$test_name needs the dynamic engine feature enabled"
plan skip_all => "$test_name needs the sock feature enabled"
if disabled("sock");
# TODO(DTLSv1.3): Implement support for partial messages for DTLS
plan skip_all => "DTLSProxy does not support partial messages"
if disabled("ec");
plan skip_all => "$test_name needs DTLSv1.3 enabled"
if disabled("dtls1_3");

View file

@ -38,8 +38,6 @@ SKIP: {
SKIP: {
skip "DTLS 1.3 is disabled", $testcount if disabled("dtls1_3");
skip "DTLSProxy does not support partial messages that are sent when EC is disabled",
$testcount if disabled("ec");
skip "DTLSProxy does not work on Windows", $testcount if $^O =~ /^(MSWin32)$/;
run_tests(1);
}

View file

@ -280,7 +280,7 @@ sub run_tests
"Both support certificate compression, but no client auth");
SKIP: {
skip "TLSProxy does not support partial messages for dtls", 2
skip "TODO(DTLSv1.3): Server hangs on client certificate + finish", 2
if $run_test_as_dtls == 1;
#Test 4: Both send cert comp, with client auth
$proxy->clear();

View file

@ -50,10 +50,6 @@ SKIP: {
SKIP: {
skip "DTLS 1.3 is disabled", $testcount if disabled("dtls1_3");
skip "DTLSProxy does not support partial messages that are sent when EC is disabled",
$testcount if disabled("ec");
skip "This test fails in several configurations because DTLSProxy does not support"
." partial messages that are sent", $testcount;
skip "DTLSProxy does not work on Windows", $testcount if $^O =~ /^(MSWin32)$/;
run_tests(1);
}
@ -61,12 +57,11 @@ SKIP: {
sub run_tests
{
my $run_test_as_dtls = shift;
my $proxy_start_success = 0;
my $proxy;
if ($run_test_as_dtls == 1) {
$proxy = TLSProxy::Proxy->new_dtls(
undef,
\&hrr_filter,
cmdstr(app([ "openssl" ]), display => 1),
srctop_file("apps", "server.pem"),
(!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
@ -74,7 +69,7 @@ sub run_tests
}
else {
$proxy = TLSProxy::Proxy->new(
undef,
\&hrr_filter,
cmdstr(app([ "openssl" ]), display => 1),
srctop_file("apps", "server.pem"),
(!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
@ -82,12 +77,11 @@ sub run_tests
}
SKIP: {
skip "TODO(DTLSv1.3): When ECX is disabled running this test with DTLS will hang"
." waiting for s_server to close", 2 if $run_test_as_dtls == 1 && disabled("ecx");
skip "TODO(DTLSv1.3): Test stalls when server sends its ServerHello.",
1 if $run_test_as_dtls == 1 && disabled("ecx");
#Test 1: A client should fail if the server changes the ciphersuite between the
# HRR and the SH
$proxy->clear();
$proxy->filter(\&hrr_filter);
if (disabled("ec")) {
$proxy->serverflags("-curves ffdhe3072");
}
@ -95,28 +89,30 @@ sub run_tests
$proxy->serverflags("-curves P-256");
}
$testtype = CHANGE_HRR_CIPHERSUITE;
$proxy_start_success = $proxy->start();
skip "TLSProxy did not start correctly", 2 if $proxy_start_success == 0;
# Skip tests if TLSProxy if it fails to start. For DTLS this return is always
# false when the handshake fails so we skip the check.
skip "TLSProxy did not start correctly", $testcount if $proxy->start() == 0
&& $run_test_as_dtls == 0;
ok(TLSProxy::Message->fail(), "Server ciphersuite changes");
#Test 2: It is an error if the client changes the offered ciphersuites so that
# we end up selecting a different ciphersuite between HRR and the SH
$proxy->clear();
if (disabled("ec")) {
$proxy->serverflags("-curves ffdhe3072");
}
else {
$proxy->serverflags("-curves P-384");
}
$proxy->ciphersuitess("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384");
$testtype = CHANGE_CH1_CIPHERSUITE;
$proxy->start();
ok(TLSProxy::Message->fail(), "Client ciphersuite changes");
}
#Test 2: It is an error if the client changes the offered ciphersuites so that
# we end up selecting a different ciphersuite between HRR and the SH
$proxy->clear();
if (disabled("ec")) {
$proxy->serverflags("-curves ffdhe3072");
}
else {
$proxy->serverflags("-curves P-384");
}
$proxy->ciphersuitess("TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384");
$testtype = CHANGE_CH1_CIPHERSUITE;
$proxy->start();
ok(TLSProxy::Message->fail(), "Client ciphersuite changes");
SKIP: {
skip "DTLSProxy does not support partial messages that are sent when ECX is disabled",
1 if $run_test_as_dtls == 1 && disabled("ecx");
skip "TODO(DTLSv1.3): Re-enable when #26465 has been merged.",
1 if $run_test_as_dtls == 1;
#Test 3: A client should fail with unexpected_message alert if the server
# sends more than 1 HRR
$fatal_alert = 0;
@ -141,7 +137,9 @@ sub run_tests
SKIP: {
skip "EC/(D)TLSv1.2 is disabled in this build", 1
if disabled("ec") || ($run_test_as_dtls == 0 && disabled("tls1_2"))
|| ($run_test_as_dtls == 1 && disabled("dtls1_2"));
|| $run_test_as_dtls == 1;
#TODO(DTLSv1.3): Fails when running with DTLS.
# || ($run_test_as_dtls == 1 && disabled("dtls1_2"));
$proxy->clear();
$proxy->clientflags("-groups P-256:brainpoolP512r1:P-521");

View file

@ -289,55 +289,47 @@ sub run_tests
checkhandshake::DEFAULT_EXTENSIONS,
"status_request handshake test (server)");
SKIP: {
skip "TLSProxy does not support partial messages for dtls", 2
if $run_test_as_dtls == 1;
#Test 5: A status_request handshake (client and server)
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -status");
$proxy->serverflags("-no_rx_cert_comp -status_file "
. srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::STATUS_REQUEST_CLI_EXTENSION
| checkhandshake::STATUS_REQUEST_SRV_EXTENSION,
"status_request handshake test");
#Test 6: A status_request handshake (client and server) with client auth
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -status -enable_pha -cert "
. srctop_file("apps", "server.pem"));
$proxy->serverflags("-no_rx_cert_comp -Verify 5 -status_file "
. srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::STATUS_REQUEST_CLI_EXTENSION
| checkhandshake::STATUS_REQUEST_SRV_EXTENSION
| checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION,
"status_request handshake with client auth test");
}
}
SKIP: {
skip "TLSProxy does not support partial messages for dtls", 1
if $run_test_as_dtls == 1;
#Test 7: A client auth handshake
#Test 5: A status_request handshake (client and server)
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -enable_pha -cert " . srctop_file("apps", "server.pem"));
$proxy->serverflags("-no_rx_cert_comp -Verify 5");
$proxy_start_success = $proxy->start();
skip "TLSProxy did not start correctly", $testcount - 6 if $proxy_start_success == 0;
$proxy->clientflags("-no_rx_cert_comp -status");
$proxy->serverflags("-no_rx_cert_comp -status_file "
. srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::STATUS_REQUEST_CLI_EXTENSION
| checkhandshake::STATUS_REQUEST_SRV_EXTENSION,
"status_request handshake test");
#Test 6: A status_request handshake (client and server) with client auth
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -status -enable_pha -cert "
. srctop_file("apps", "server.pem"));
$proxy->serverflags("-no_rx_cert_comp -Verify 5 -status_file "
. srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS |
checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION,
"Client auth handshake test");
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::STATUS_REQUEST_CLI_EXTENSION
| checkhandshake::STATUS_REQUEST_SRV_EXTENSION
| checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION,
"status_request handshake with client auth test");
}
#Test 7: A client auth handshake
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -enable_pha -cert " . srctop_file("apps", "server.pem"));
$proxy->serverflags("-no_rx_cert_comp -Verify 5");
$proxy_start_success = $proxy->start();
skip "TLSProxy did not start correctly", $testcount - 6 if $proxy_start_success == 0;
checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION,
"Client auth handshake test");
#Test 8: Server name handshake (no client request)
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
@ -403,10 +395,11 @@ sub run_tests
"ALPN handshake test");
SKIP: {
# TODO(DTLSv1.3): When ecx is disabled the test reports "Invalid
# CertificateVerify signature length" when running with DTLS.
skip "No CT, EC or OCSP support in this OpenSSL build", 1
if disabled("ct") || disabled("ec") || disabled("ocsp");
skip "TLSProxy does not support partial messages for dtls", 1
if $run_test_as_dtls == 1;
if disabled("ct") || disabled("ec") || disabled("ocsp")
|| ($run_test_as_dtls == 1 && disabled("ecx"));
#Test 14: SCT handshake (client request only)
$proxy->clear();
@ -427,7 +420,7 @@ sub run_tests
}
SKIP: {
skip "TLSProxy does not support partial messages for dtls", 1
skip "TODO(DTLSv1.3): Re-enable when #26465 is merged.", 1
if $run_test_as_dtls == 1;
#Test 15: HRR Handshake
$proxy->clear();
@ -458,20 +451,15 @@ sub run_tests
"Resumption handshake with HRR test");
}
SKIP: {
skip "TLSProxy does not support partial messages for dtls", 1
if $run_test_as_dtls == 1;
#Test 17: Acceptable but non preferred key_share
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -curves P-384");
$proxy->start();
checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::SUPPORTED_GROUPS_SRV_EXTENSION,
"Acceptable but non preferred key_share");
}
#Test 17: Acceptable but non preferred key_share
$proxy->clear();
$proxy->cipherc("DEFAULT:\@SECLEVEL=2");
$proxy->clientflags("-no_rx_cert_comp -curves P-384");
$proxy->start();
checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::SUPPORTED_GROUPS_SRV_EXTENSION,
"Acceptable but non preferred key_share");
unlink $session;
}

View file

@ -149,6 +149,9 @@ use constant {
my $payload = "";
my $messlen = -1;
my $messseq = -1;
my $messfraglen = -1;
my $messfragoffs = -1;
my $mt;
my $startoffset = -1;
my $server = 0;
@ -164,6 +167,9 @@ sub clear
{
$payload = "";
$messlen = -1;
$messseq = -1;
$messfraglen = -1;
$messfragoffs = -1;
$startoffset = -1;
$server = 0;
$success = 0;
@ -225,13 +231,8 @@ sub get_messages
$recoffset = $messlen - length($payload);
$payload .= substr($record->decrypt_data, 0, $recoffset);
push @message_frag_lens, $recoffset;
if ($isdtls) {
# We must set $msgseq, $msgfraglen, $msgfragoffs
die "Internal error: cannot handle partial dtls messages\n"
}
$message = create_message($server, $mt,
#$msgseq, $msgfraglen, $msgfragoffs,
0, 0, 0,
$messseq, $messfraglen, $messfragoffs,
$payload, $startoffset, $isdtls);
push @messages, $message;
@ -256,18 +257,15 @@ sub get_messages
@message_rec_list = ($record);
my $lenhi;
my $lenlo;
my $msgseq;
my $msgfraglen;
my $msgfragoffs;
if ($isdtls) {
my $msgfraglenhi;
my $msgfraglenlo;
my $msgfragoffshi;
my $msgfragoffslo;
($mt, $lenhi, $lenlo, $msgseq, $msgfragoffshi, $msgfragoffslo, $msgfraglenhi, $msgfraglenlo) =
($mt, $lenhi, $lenlo, $messseq, $msgfragoffshi, $msgfragoffslo, $msgfraglenhi, $msgfraglenlo) =
unpack('CnCnnCnC', substr($record->decrypt_data, $recoffset));
$msgfraglen = ($msgfraglenhi << 8) | $msgfraglenlo;
$msgfragoffs = ($msgfragoffshi << 8) | $msgfragoffslo;
$messfraglen = ($msgfraglenhi << 8) | $msgfraglenlo;
$messfragoffs = ($msgfragoffshi << 8) | $msgfragoffslo;
} else {
($mt, $lenhi, $lenlo) =
unpack('CnC', substr($record->decrypt_data, $recoffset));
@ -276,8 +274,8 @@ sub get_messages
print " Message type: $message_type{$mt}($mt)\n";
print " Message Length: $messlen\n";
if ($isdtls) {
print " Message fragment length: $msgfraglen\n";
print " Message fragment offset: $msgfragoffs\n";
print " Message fragment length: $messfraglen\n";
print " Message fragment offset: $messfragoffs\n";
}
$startoffset = $recoffset;
$recoffset += $msgheaderlen;
@ -291,8 +289,8 @@ sub get_messages
$messlen);
$recoffset += $messlen;
push @message_frag_lens, $messlen;
$message = create_message($server, $mt, $msgseq,
$msgfraglen, $msgfragoffs,
$message = create_message($server, $mt, $messseq,
$messfraglen, $messfragoffs,
$payload, $startoffset, $isdtls);
push @messages, $message;