openssl/test/recipes/70-test_dtls13ack.t

229 lines
7.5 KiB
Perl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /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;
use Cwd qw(abs_path);
my $test_name = "test_dtls13ack";
setup($test_name);
# TODO(DTLSv1.3): The test currently does not work as changes to ´engines/e_ossltest.c´
# in #25119 should be ported to the ossltest provider.
plan skip_all => "This doesn't work properly currently";
plan skip_all => "TLSProxy isn't usable on $^O"
if $^O =~ /^(VMS)$/ || $^O =~ /^(MSWin32)$/;
plan skip_all => "$test_name needs the module feature enabled"
if disabled("module");
plan skip_all => "$test_name needs the sock feature enabled"
if disabled("sock");
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");
$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("test"));
my $proxy = TLSProxy::Proxy->new_dtls(
undef,
cmdstr(app(["openssl"]), display => 1),
srctop_file("apps", "server.pem"),
(!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
);
my $testcount = 3;
plan tests => $testcount;
#Test 1: Check that records are acked during an uninterrupted handshake
$proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3");
$proxy->clientflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3");
TLSProxy::Message->successondata(1);
skip "TLSProxy could not start", $testcount if !$proxy->start();
my @expected = get_expected_ack_record_numbers();
my @actual = get_actual_acked_record_numbers();
my @missing = record_numbers_missing(\@expected, \@actual);
my $expected_count = @expected;
my $missing_count = @missing;
ok($missing_count == 0 && $expected_count == 1,
"Check that all record numbers are acked");
# Test 2: Check that records that are missing are not acked during a handshake
$proxy->clear();
my $found_first_client_finish_msg = 0;
$proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3");
$proxy->clientflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3");
$proxy->filter(\&drop_first_client_finish_filter);
TLSProxy::Message->successondata(1);
$proxy->start();
@expected = get_expected_ack_record_numbers();
@actual = get_actual_acked_record_numbers();
@missing = record_numbers_missing(\@expected, \@actual);
$expected_count = @expected;
$missing_count = @missing;
ok($missing_count == 1 && $expected_count == 2,
"Check that all record numbers except one are acked");
SKIP: {
skip "TODO(DTLSv1.3): This test fails because the client does not properly
handle when the last flight is dropped when it includes a
CompressedCertificate.", 1
if !disabled("zlib") || !disabled("zstd") || !disabled("brotli");
# Test 3: Check that client cert and verify messages are also acked
$proxy->clear();
$proxy->filter(undef);
$found_first_client_finish_msg = 0;
$proxy->serverflags("-min_protocol DTLSv1.3 -max_protocol DTLSv1.3 -Verify 1");
$proxy->clientflags("-mtu 2000 -min_protocol DTLSv1.3 -max_protocol DTLSv1.3"
." -cert ".srctop_file("apps", "server.pem"));
TLSProxy::Message->successondata(1);
$proxy->start();
@expected = get_expected_ack_record_numbers();
@actual = get_actual_acked_record_numbers();
@missing = record_numbers_missing(\@expected, \@actual);
$expected_count = @expected;
$missing_count = @missing;
ok($missing_count == 0 && $expected_count == 3,
"Check that all record numbers are acked");
}
sub get_expected_ack_record_numbers
{
my $records = $proxy->record_list;
my @record_numbers = ();
foreach (@{$records}) {
my $record = $_;
if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE
&& $record->{sent}) {
my $epoch = $record->epoch;
my $seqnum = $record->seq;
my $serverissender = $record->serverissender;
my $recnum = TLSProxy::RecordNumber->new($epoch, $seqnum);
my @messages = TLSProxy::Message->get_messages($record);
my $record_should_be_acked = 0;
foreach (@messages) {
my $message = $_;
if (!$serverissender
&& ($message->mt == TLSProxy::Message::MT_FINISHED
|| $message->mt == TLSProxy::Message::MT_CERTIFICATE
|| $message->mt == TLSProxy::Message::MT_COMPRESSED_CERTIFICATE
|| $message->mt == TLSProxy::Message::MT_CERTIFICATE_VERIFY)
# TODO(DTLSv1.3): The ACK of the following messages are never processed
# by the proxy because s_client is closed too early send it:
# || $message->mt == TLSProxy::Message::MT_KEY_UPDATE
# || $message->mt == TLSProxy::Message::MT_NEW_SESSION_TICKET
) {
$record_should_be_acked = 1;
}
}
push(@record_numbers, $recnum) if ($record_should_be_acked == 1);
}
}
return @record_numbers;
}
sub get_actual_acked_record_numbers
{
my @records = @{$proxy->record_list};
my @record_numbers = ();
foreach (@records) {
my $record = $_;
if ($record->content_type == TLSProxy::Record::RT_ACK) {
my $recnum_count = unpack('n', $record->decrypt_data) / 16;
my $ptr = 2;
for (my $idx = 0; $idx < $recnum_count; $idx++) {
my $epoch_lo;
my $epoch_hi;
my $msgseq_lo;
my $msgseq_hi;
($epoch_hi, $epoch_lo, $msgseq_hi, $msgseq_lo)
= unpack('NNNN', substr($record->decrypt_data, $ptr));
$ptr = $ptr + 16;
my $epoch = ($epoch_hi << 32) | $epoch_lo;
my $msgseq = ($msgseq_hi << 32) | $msgseq_lo;
my $recnum = TLSProxy::RecordNumber->new($epoch, $msgseq);
push(@record_numbers, $recnum);
}
}
}
return @record_numbers;
}
sub record_numbers_missing
{
my @expected_record_numbers = @{$_[0]};
my @actual_record_numbers = @{$_[1]};
my @missing_record_numbers = ();
foreach (@expected_record_numbers)
{
my $found = 0;
my $expected = $_;
foreach (@actual_record_numbers) {
my $actual = $_;
if ($actual->epoch() == $expected->epoch()
&& $actual->seqnum() == $expected->seqnum()) {
$found = 1
}
}
if ($found == 0) {
push(@missing_record_numbers, $expected);
}
}
return @missing_record_numbers;
}
sub drop_first_client_finish_filter
{
my $inproxy = shift;
foreach my $record (@{$inproxy->record_list}) {
next if ($record->{sent} == 1 || $record->serverissender || $found_first_client_finish_msg == 1);
my @messages = TLSProxy::Message->get_messages($record);
foreach my $message (@messages) {
if ($message->mt == TLSProxy::Message::MT_FINISHED) {
$record->{sent} = 1;
$found_first_client_finish_msg = 1;
last;
}
}
}
}