# 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;
# Test that maximally-padded records are accepted.
my $bad_padding_offset = -1;
$proxy->serverflags("-tls1_2");
+$proxy->serverconnects(1 + scalar(@test_offsets));
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 1 + scalar(@test_offsets);
ok(TLSProxy::Message->success(), "Maximally-padded record test");
# Test that invalid padding is rejected.
+my $fatal_alert; # set by add_maximal_padding_filter on client's fatal alert
+
foreach my $offset (@test_offsets) {
- $proxy->clear();
- $proxy->serverflags("-tls1_2");
$bad_padding_offset = $offset;
- $proxy->start();
- ok(TLSProxy::Message->fail(), "Invalid padding byte $bad_padding_offset");
+ $fatal_alert = 0;
+ $proxy->clearClient();
+ $proxy->clientstart();
+ ok($fatal_alert, "Invalid padding byte $bad_padding_offset");
}
sub add_maximal_padding_filter
{
my $proxy = shift;
+ my $messages = $proxy->message_list;
+ state $sent_corrupted_payload;
if ($proxy->flight == 0) {
# Disable Encrypt-then-MAC.
- foreach my $message (@{$proxy->message_list}) {
+ foreach my $message (@{$messages}) {
if ($message->mt != TLSProxy::Message::MT_CLIENT_HELLO) {
next;
}
$message->process_extensions();
$message->repack();
}
+ $sent_corrupted_payload = 0;
+ return;
}
- if ($proxy->flight == 3) {
+ my $last_message = @{$messages}[-1];
+ if (defined($last_message)
+ && $last_message->server
+ && $last_message->mt == TLSProxy::Message::MT_FINISHED
+ && !@{$last_message->records}[0]->{sent}) {
+
# Insert a maximally-padded record. Assume a block size of 16 (AES) and
# a MAC length of 20 (SHA-1).
my $block_size = 16;
# Add padding.
for (my $i = 0; $i < 256; $i++) {
if ($i == $bad_padding_offset) {
+ $sent_corrupted_payload = 1;
$data .= "\xfe";
} else {
$data .= "\xff";
# Send the record immediately after the server Finished.
push @{$proxy->record_list}, $record;
+ } elsif ($sent_corrupted_payload) {
+ # Check for bad_record_mac from client
+ my $last_record = @{$proxy->record_list}[-1];
+ $fatal_alert = 1 if $last_record->is_fatal_alert(0) == 20;
}
}
# 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;
};
my $testtype;
+my $fatal_alert = 0; # set by filter on fatal alert
$ENV{OPENSSL_ia32cap} = '~0x200000200000000';
my $proxy = TLSProxy::Proxy->new(
my $proxy = shift;
# We're only interested in the initial ClientHello
- if ($proxy->flight != 0) {
+ if ($proxy->flight == 0) {
+ inject_duplicate_extension($proxy, TLSProxy::Message::MT_CLIENT_HELLO);
return;
}
- inject_duplicate_extension($proxy, TLSProxy::Message::MT_CLIENT_HELLO);
+ my $last_record = @{$proxy->{record_list}}[-1];
+ $fatal_alert = 1 if $last_record->is_fatal_alert(1);
}
sub inject_duplicate_extension_serverhello
my $proxy = shift;
# We're only interested in the initial ServerHello
- if ($proxy->flight != 1) {
+ if ($proxy->flight == 0) {
+ return;
+ } elsif ($proxy->flight == 1) {
+ inject_duplicate_extension($proxy, TLSProxy::Message::MT_SERVER_HELLO);
return;
}
- inject_duplicate_extension($proxy, TLSProxy::Message::MT_SERVER_HELLO);
+ my $last_record = @{$proxy->{record_list}}[-1];
+ $fatal_alert = 1 if $last_record->is_fatal_alert(0);
}
sub inject_unsolicited_extension
{
my $proxy = shift;
my $message;
+ state $sent_unsolisited_extension;
+
+ if ($proxy->flight == 0) {
+ $sent_unsolisited_extension = 0;
+ return;
+ }
# We're only interested in the initial ServerHello/EncryptedExtensions
if ($proxy->flight != 1) {
+ if ($sent_unsolisited_extension) {
+ my $last_record = @{$proxy->record_list}[-1];
+ $fatal_alert = 1 if $last_record->is_fatal_alert(0);
+ }
return;
}
if ($testtype == UNSOLICITED_SERVER_NAME_TLS13) {
- $message = ${$proxy->message_list}[2];
- die "Expecting EE message ".($message->mt).", ".${$proxy->message_list}[1]->mt.", ".${$proxy->message_list}[3]->mt if $message->mt != TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS;
+ return if (!defined($message = ${$proxy->message_list}[2]));
+ die "Expecting EE message ".($message->mt).","
+ .${$proxy->message_list}[1]->mt.", "
+ .${$proxy->message_list}[3]->mt
+ if $message->mt != TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS;
} else {
$message = ${$proxy->message_list}[1];
}
}
$message->set_extension($type, $ext);
$message->repack();
+ $sent_unsolisited_extension = 1;
}
# Test 1-2: Sending a duplicate extension should fail.
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 7;
-ok(TLSProxy::Message->fail(), "Duplicate ClientHello extension");
+ok($fatal_alert, "Duplicate ClientHello extension");
+$fatal_alert = 0;
$proxy->clear();
$proxy->filter(\&inject_duplicate_extension_serverhello);
$proxy->start();
-ok(TLSProxy::Message->fail(), "Duplicate ServerHello extension");
+ok($fatal_alert, "Duplicate ServerHello extension");
SKIP: {
skip "TLS <= 1.2 disabled", 3 if $no_below_tls13;
ok(TLSProxy::Message->success, "Zero extension length test");
#Test 4: Inject an unsolicited extension (<= TLSv1.2)
+ $fatal_alert = 0;
$proxy->clear();
$proxy->filter(\&inject_unsolicited_extension);
$testtype = UNSOLICITED_SERVER_NAME;
$proxy->clientflags("-no_tls1_3 -noservername");
$proxy->start();
- ok(TLSProxy::Message->fail(), "Unsolicited server name extension");
+ ok($fatal_alert, "Unsolicited server name extension");
#Test 5: Inject a noncompliant supported_groups extension (<= TLSv1.2)
$proxy->clear();
skip "TLS <= 1.2 or CT disabled", 1
if $no_below_tls13 || disabled("ct");
#Test 6: Same as above for the SCT extension which has special handling
+ $fatal_alert = 0;
$proxy->clear();
$testtype = UNSOLICITED_SCT;
$proxy->clientflags("-no_tls1_3");
$proxy->start();
- ok(TLSProxy::Message->fail(), "Unsolicited sct extension");
+ ok($fatal_alert, "Unsolicited sct extension");
}
SKIP: {
skip "TLS 1.3 disabled", 1 if disabled("tls1_3");
#Test 7: Inject an unsolicited extension (TLSv1.3)
+ $fatal_alert = 0;
$proxy->clear();
$proxy->filter(\&inject_unsolicited_extension);
$testtype = UNSOLICITED_SERVER_NAME_TLS13;
$proxy->clientflags("-noservername");
$proxy->start();
- ok(TLSProxy::Message->fail(), "Unsolicited server name extension (TLSv1.3)");
+ ok($fatal_alert, "Unsolicited server name extension (TLSv1.3)");
}
# 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;
);
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;
$proxy->serverflags("-tls1_2");
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 18;
-ok(TLSProxy::Message->fail(), "Out of context empty records test");
+ok($fatal_alert, "Out of context empty records test");
#Test 2: Injecting in context empty records should succeed
$proxy->clear();
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->start();
-ok(TLSProxy::Message->fail(), "Too many in context empty records test");
+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->start();
-ok(TLSProxy::Message->fail(), "Fragmented alert records test");
+ok($fatal_alert, "Fragmented alert records test");
#Run some SSLv2 ClientHello tests
#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->filter(\&add_unknown_record_type);
$proxy->start();
-ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
+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;
$proxy->clear();
$proxy->clientflags("-tls1_1");
$proxy->start();
- ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1");
+ ok($fatal_alert, "Unrecognised record type in TLS1.1");
}
#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(TLSProxy::Message->fail(), "Changed record version in TLS1.2");
+ok($fatal_alert, "Changed record version in TLS1.2");
#TLS1.3 specific tests
SKIP: {
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(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.3");
+ 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(TLSProxy::Message->fail(), "Wrong outer record type in TLS1.3");
+ ok($fatal_alert, "Wrong outer record type in TLS1.3");
use constant {
DATA_AFTER_SERVER_HELLO => 0,
#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(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (ServerHello)");
+ 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->filter(\¬_on_record_boundary);
$proxy->start();
- ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (Finished)");
+ 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->filter(\¬_on_record_boundary);
$proxy->start();
- ok(TLSProxy::Message->fail(), "Record not on boundary in TLS1.3 (KeyUpdate)");
+ ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
}
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) == 10;
return;
}
"",
""
);
-
- push @{$proxy->record_list}, $record;
+ push @{$records}, $record;
}
}
sub add_frag_alert_filter
{
my $proxy = shift;
+ my $records = $proxy->record_list;
my $byte;
# We're only interested in the initial ClientHello
if ($proxy->flight != 0) {
+ $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
return;
}
$byte,
$byte
);
- push @{$proxy->record_list}, $record;
+ push @{$records}, $record;
# And finally the description (Unexpected message) in a third record
$byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
$byte,
$byte
);
- push @{$proxy->record_list}, $record;
+ push @{$records}, $record;
}
sub add_sslv2_filter
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 != 1) {
+ if ($proxy->flight == 0) {
+ $added_record = 0;
+ return;
+ } elsif ($proxy->flight != 1 || $added_record) {
+ $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
return;
}
- my $lastrec = ${$proxy->record_list}[-1];
my $record = TLSProxy::Record->new(
1,
TLSProxy::Record::RT_UNKNOWN,
- $lastrec->version(),
+ @{$records}[-1]->version(),
1,
0,
1,
$i++;
splice @{$proxy->record_list}, $i, 0, $record;
+ $added_record = 1;
}
sub change_version
{
my $proxy = shift;
+ my $records = $proxy->record_list;
# We'll change a version after the initial version neg has taken place
if ($proxy->flight != 1) {
+ $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 70;
return;
}
- (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1);
+ if ($#{$records} > 1) {
+ # ... typically in ServerHelloDone
+ @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
+ }
}
sub change_outer_record_type
{
my $proxy = shift;
+ my $records = $proxy->record_list;
# We'll change a record after the initial version neg has taken place
if ($proxy->flight != 1) {
+ $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
return;
}
- #Find ServerHello record and change record after that
- my $i;
- for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
- next;
+ # Find CCS record and change record after that
+ my $i = 0;
+ foreach my $record (@{$records}) {
+ last if $record->content_type == TLSProxy::Record::RT_CCS;
+ $i++;
+ }
+ if (defined(${$records}[++$i])) {
+ ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
}
- #Skip CCS and ServerHello
- $i += 2;
- ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
}
sub not_on_record_boundary
{
my $proxy = shift;
+ my $records = $proxy->record_list;
my $data;
#Find server's first flight
if ($proxy->flight != 1) {
+ $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
return;
}
if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
#Merge the ServerHello and EncryptedExtensions records into one
- my $i;
- for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
- next;
+ my $i = 0;
+ foreach my $record (@{$records}) {
+ if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
+ $record->{sent} = 1; # pretend it's sent already
+ last;
+ }
+ $i++;
}
- $data = ${$proxy->record_list}[$i]->data();
- $data .= ${$proxy->record_list}[$i + 1]->decrypt_data();
- ${$proxy->record_list}[$i]->data($data);
- ${$proxy->record_list}[$i]->len(length $data);
- #Delete the old EncryptedExtensions record
- splice @{$proxy->record_list}, $i + 1, 1;
+ if (defined(${$records}[$i+1])) {
+ $data = ${$records}[$i]->data();
+ $data .= ${$records}[$i+1]->decrypt_data();
+ ${$records}[$i+1]->data($data);
+ ${$records}[$i+1]->len(length $data);
+
+ #Delete the old ServerHello record
+ splice @{$records}, $i, 1;
+ }
} elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
- $data = ${$proxy->record_list}[-1]->decrypt_data;
+ return if @{$proxy->{message_list}}[-1]->{mt}
+ != TLSProxy::Message::MT_FINISHED;
+
+ my $last_record = @{$records}[-1];
+ $data = $last_record->decrypt_data;
#Add a KeyUpdate message onto the end of the Finished record
my $keyupdate = pack "C5",
$data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
#Update the record
- ${$proxy->record_list}[-1]->data($data);
- ${$proxy->record_list}[-1]->len(length $data);
+ $last_record->data($data);
+ $last_record->len(length $data);
} else {
+ return if @{$proxy->{message_list}}[-1]->{mt}
+ != TLSProxy::Message::MT_FINISHED;
+
#KeyUpdates must end on a record boundary
my $record = TLSProxy::Record->new(
1,
TLSProxy::Record::RT_APPLICATION_DATA,
- TLSProxy::Record::VERS_TLS_1_0,
+ TLSProxy::Record::VERS_TLS_1_2,
0,
0,
0,
$record->data($data);
$record->len(length $data);
- push @{$proxy->record_list}, $record;
+ push @{$records}, $record;
}
}