X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=test%2Frecipes%2F70-test_sslrecords.t;h=bac738c311334f159be3b13d37782db7c016d584;hp=beacc4ad9adb830f77290b7ea620d1ec90e62170;hb=bd990e2535ca387def9a01218a813dc3fa547e3c;hpb=4f0c475719defd7c051964ef9964cc6e5b3a63bf diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t index beacc4ad9a..bac738c311 100644 --- a/test/recipes/70-test_sslrecords.t +++ b/test/recipes/70-test_sslrecords.t @@ -23,8 +23,8 @@ plan skip_all => "$test_name needs the dynamic engine feature enabled" plan skip_all => "$test_name needs the sock feature enabled" if disabled("sock"); -plan skip_all => "$test_name needs TLS enabled" - if alldisabled(available_protocols("tls")); +plan skip_all => "$test_name needs TLSv1.2 enabled" + if disabled("tls1_2"); $ENV{OPENSSL_ia32cap} = '~0x200000200000000'; my $proxy = TLSProxy::Proxy->new( @@ -34,17 +34,20 @@ my $proxy = TLSProxy::Proxy->new( (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) ); -plan tests => 3; +my $boundary_test_type; #Test 1: Injecting out of context empty records should fail my $content_type = TLSProxy::Record::RT_APPLICATION_DATA; my $inject_recs_num = 1; -$proxy->start(); +$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"); #Test 2: Injecting in context empty records should succeed $proxy->clear(); $content_type = TLSProxy::Record::RT_HANDSHAKE; +$proxy->serverflags("-tls1_2"); $proxy->start(); ok(TLSProxy::Message->success(), "In context empty records test"); @@ -52,9 +55,151 @@ ok(TLSProxy::Message->success(), "In context empty records test"); $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"); +#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 +$proxy->clear(); +$proxy->filter(\&add_frag_alert_filter); +$proxy->serverflags("-tls1_2"); +$proxy->start(); +ok(TLSProxy::Message->fail(), "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 +}; +#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->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->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->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->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->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 +$proxy->clear(); +$proxy->serverflags("-tls1_2"); +$proxy->filter(\&add_unknown_record_type); +$proxy->start(); +ok(TLSProxy::Message->fail(), "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 + $proxy->clear(); + $proxy->clientflags("-tls1_1"); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.1"); +} + +#Test 12: Sending a different record version in TLS1.2 should fail +$proxy->clear(); +$proxy->clientflags("-tls1_2"); +$proxy->filter(\&change_version); +$proxy->start(); +ok(TLSProxy::Message->fail(), "Changed record version in TLS1.2"); + +#TLS1.3 specific tests +SKIP: { + skip "TLSv1.3 disabled", 6 if disabled("tls1_3"); + + #Test 13: Sending a different record version in TLS1.3 should succeed + $proxy->clear(); + $proxy->filter(\&change_version); + $proxy->start(); + ok(TLSProxy::Message->success(), "Changed record version in TLS1.3"); + + #Test 14: Sending an unrecognised record type in TLS1.3 should fail + $proxy->clear(); + $proxy->filter(\&add_unknown_record_type); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.3"); + + #Test 15: Sending an outer record type other than app data once encrypted + #should fail + $proxy->clear(); + $proxy->filter(\&change_outer_record_type); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Wrong outer record type in TLS1.3"); + + use constant { + DATA_AFTER_SERVER_HELLO => 0, + DATA_AFTER_FINISHED => 1, + DATA_AFTER_KEY_UPDATE => 2 + }; + + #Test 16: Sending a ServerHello which doesn't end on a record boundary + # should fail + $proxy->clear(); + $boundary_test_type = DATA_AFTER_SERVER_HELLO; + $proxy->filter(\¬_on_record_boundary); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (ServerHello)"); + + #Test 17: Sending a Finished which doesn't end on a record boundary + # should fail + $proxy->clear(); + $boundary_test_type = DATA_AFTER_FINISHED; + $proxy->filter(\¬_on_record_boundary); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (Finished)"); + + #Test 18: Sending a KeyUpdate which doesn't end on a record boundary + # should fail + $proxy->clear(); + $boundary_test_type = DATA_AFTER_KEY_UPDATE; + $proxy->filter(\¬_on_record_boundary); + $proxy->start(); + ok(TLSProxy::Message->fail(), "Record not on bounday in TLS1.3 (KeyUpdate)"); + } + + sub add_empty_recs_filter { my $proxy = shift; @@ -72,6 +217,7 @@ sub add_empty_recs_filter 0, 0, 0, + 0, "", "" ); @@ -79,3 +225,338 @@ sub add_empty_recs_filter push @{$proxy->record_list}, $record; } } + +sub add_frag_alert_filter +{ + my $proxy = shift; + my $byte; + + # We're only interested in the initial ClientHello + if ($proxy->flight != 0) { + return; + } + + # Add a zero length fragment first + #my $record = TLSProxy::Record->new( + # 0, + # TLSProxy::Record::RT_ALERT, + # TLSProxy::Record::VERS_TLS_1_2, + # 0, + # 0, + # 0, + # "", + # "" + #); + #push @{$proxy->record_list}, $record; + + # Now add the alert level (Fatal) as a separate record + $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL); + my $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_ALERT, + TLSProxy::Record::VERS_TLS_1_2, + 1, + 0, + 1, + 1, + $byte, + $byte + ); + push @{$proxy->record_list}, $record; + + # And finally the description (Unexpected message) in a third record + $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE); + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_ALERT, + TLSProxy::Record::VERS_TLS_1_2, + 1, + 0, + 1, + 1, + $byte, + $byte + ); + push @{$proxy->record_list}, $record; +} + +sub add_sslv2_filter +{ + my $proxy = shift; + my $clienthello; + my $record; + + # We're only interested in the initial ClientHello + if ($proxy->flight != 0) { + return; + } + + # Ditch the real ClientHello - we're going to replace it with our own + shift @{$proxy->record_list}; + + if ($sslv2testtype == ALERT_BEFORE_SSLV2) { + my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL, + TLSProxy::Message::AL_DESC_NO_RENEGOTIATION); + my $alertlen = length $alert; + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_ALERT, + TLSProxy::Record::VERS_TLS_1_2, + $alertlen, + 0, + $alertlen, + $alertlen, + $alert, + $alert + ); + + push @{$proxy->record_list}, $record; + } + + if ($sslv2testtype == ALERT_BEFORE_SSLV2 + || $sslv2testtype == TLSV1_2_IN_SSLV2 + || $sslv2testtype == SSLV2_IN_SSLV2) { + # This is an SSLv2 format ClientHello + $clienthello = + pack "C44", + 0x01, # ClientHello + 0x03, 0x03, #TLSv1.2 + 0x00, 0x03, # Ciphersuites len + 0x00, 0x00, # Session id len + 0x00, 0x20, # Challenge len + 0x00, 0x00, 0x2f, #AES128-SHA + 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90, + 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56, + 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge + + if ($sslv2testtype == SSLV2_IN_SSLV2) { + # Set the version to "real" SSLv2 + vec($clienthello, 1, 8) = 0x00; + vec($clienthello, 2, 8) = 0x02; + } + + my $chlen = length $clienthello; + + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_HANDSHAKE, + TLSProxy::Record::VERS_TLS_1_2, + $chlen, + 1, #SSLv2 + $chlen, + $chlen, + $clienthello, + $clienthello + ); + + push @{$proxy->record_list}, $record; + } else { + # For this test we're using a real TLS ClientHello + $clienthello = + pack "C49", + 0x01, # ClientHello + 0x00, 0x00, 0x2D, # Message length + 0x03, 0x03, # TLSv1.2 + 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90, + 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56, + 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random + 0x00, # Session id len + 0x00, 0x04, # Ciphersuites len + 0x00, 0x2f, # AES128-SHA + 0x00, 0xff, # Empty reneg info SCSV + 0x01, # Compression methods len + 0x00, # Null compression + 0x00, 0x00; # Extensions len + + # Split this into 3: A TLS record; a SSLv2 record and a TLS record. + # We deliberately split the second record prior to the Challenge/Random + # and set the first byte of the random to 1. This makes the second SSLv2 + # record look like an SSLv2 ClientHello + my $frag1 = substr $clienthello, 0, 6; + my $frag2 = substr $clienthello, 6, 32; + my $frag3 = substr $clienthello, 38; + + my $fraglen = length $frag1; + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_HANDSHAKE, + TLSProxy::Record::VERS_TLS_1_2, + $fraglen, + 0, + $fraglen, + $fraglen, + $frag1, + $frag1 + ); + push @{$proxy->record_list}, $record; + + $fraglen = length $frag2; + my $recvers; + if ($sslv2testtype == FRAGMENTED_IN_SSLV2) { + $recvers = 1; + } else { + $recvers = 0; + } + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_HANDSHAKE, + TLSProxy::Record::VERS_TLS_1_2, + $fraglen, + $recvers, + $fraglen, + $fraglen, + $frag2, + $frag2 + ); + push @{$proxy->record_list}, $record; + + $fraglen = length $frag3; + $record = TLSProxy::Record->new( + 0, + TLSProxy::Record::RT_HANDSHAKE, + TLSProxy::Record::VERS_TLS_1_2, + $fraglen, + 0, + $fraglen, + $fraglen, + $frag3, + $frag3 + ); + push @{$proxy->record_list}, $record; + } + +} + +sub add_unknown_record_type +{ + my $proxy = shift; + + # We'll change a record after the initial version neg has taken place + if ($proxy->flight != 1) { + return; + } + + my $lastrec = ${$proxy->record_list}[-1]; + my $record = TLSProxy::Record->new( + 1, + TLSProxy::Record::RT_UNKNOWN, + $lastrec->version(), + 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; +} + +sub change_version +{ + my $proxy = shift; + + # We'll change a version after the initial version neg has taken place + if ($proxy->flight != 2) { + return; + } + + (${$proxy->record_list}[-1])->version(TLSProxy::Record::VERS_TLS_1_1); +} + +sub change_outer_record_type +{ + my $proxy = shift; + + # We'll change a record after the initial version neg has taken place + if ($proxy->flight != 1) { + return; + } + + #Find ServerHello record and change record after that + my $i; + for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) { + next; + } + $i++; + ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE); +} + +sub not_on_record_boundary +{ + my $proxy = shift; + my $data; + + #Find server's first flight + if ($proxy->flight != 1) { + 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; + } + $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; + } elsif ($boundary_test_type == DATA_AFTER_FINISHED) { + $data = ${$proxy->record_list}[-1]->decrypt_data; + + #Add a KeyUpdate message onto the end of the Finished record + my $keyupdate = pack "C5", + 0x18, # KeyUpdate + 0x00, 0x00, 0x01, # Message length + 0x00; # Update not requested + + $data .= $keyupdate; + + #Add content type and tag + $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); + } else { + #KeyUpdates must end on a record boundary + + my $record = TLSProxy::Record->new( + 1, + TLSProxy::Record::RT_APPLICATION_DATA, + TLSProxy::Record::VERS_TLS_1_0, + 0, + 0, + 0, + 0, + "", + "" + ); + + #Add two KeyUpdate messages into a single record + my $keyupdate = pack "C5", + 0x18, # KeyUpdate + 0x00, 0x00, 0x01, # Message length + 0x00; # Update not requested + + $data = $keyupdate.$keyupdate; + + #Add content type and tag + $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16); + + $record->data($data); + $record->len(length $data); + push @{$proxy->record_list}, $record; + } +}