Loading test/recipes/70-test_sslrecords.t +197 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ my $proxy = TLSProxy::Proxy->new( my $content_type = TLSProxy::Record::RT_APPLICATION_DATA; my $inject_recs_num = 1; $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; plan tests => 4; plan tests => 9; ok(TLSProxy::Message->fail(), "Out of context empty records test"); #Test 2: Injecting in context empty records should succeed Loading @@ -62,6 +62,52 @@ $proxy->filter(\&add_frag_alert_filter); $proxy->start(); ok(!TLSProxy::Message->end(), "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; my $sslv2testtype = TLSV1_2_IN_SSLV2; $proxy->clear(); $proxy->filter(\&add_sslv2_filter); $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->start(); ok(!TLSProxy::Message->end(), "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->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->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->start(); ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test"); sub add_empty_recs_filter { my $proxy = shift; Loading @@ -79,6 +125,7 @@ sub add_empty_recs_filter 0, 0, 0, 0, "", "" ); Loading Loading @@ -117,6 +164,7 @@ sub add_frag_alert_filter TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, 1, 0, 1, 1, $byte, Loading @@ -131,6 +179,7 @@ sub add_frag_alert_filter TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, 1, 0, 1, 1, $byte, Loading @@ -138,3 +187,150 @@ sub add_frag_alert_filter ); 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; } } util/TLSProxy/Message.pm +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ use constant { #Alert descriptions use constant { AL_DESC_CLOSE_NOTIFY => 0, AL_DESC_UNEXPECTED_MESSAGE => 10 AL_DESC_UNEXPECTED_MESSAGE => 10, AL_DESC_NO_RENEGOTIATION => 100 }; my %message_type = ( Loading util/TLSProxy/Record.pm +13 −1 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ sub get_records $content_type, $version, $len, 0, $len_real, $decrypt_len, substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real), Loading Loading @@ -167,6 +168,7 @@ sub new $content_type, $version, $len, $sslv2, $len_real, $decrypt_len, $data, Loading @@ -177,6 +179,7 @@ sub new content_type => $content_type, version => $version, len => $len, sslv2 => $sslv2, len_real => $len_real, decrypt_len => $decrypt_len, data => $data, Loading Loading @@ -247,7 +250,11 @@ sub reconstruct_record my $self = shift; my $data; if ($self->sslv2) { $data = pack('n', $self->len | 0x8000); } else { $data = pack('Cnn', $self->content_type, $self->version, $self->len); } $data .= $self->data; return $data; Loading @@ -269,6 +276,11 @@ sub version my $self = shift; return $self->{version}; } sub sslv2 { my $self = shift; return $self->{sslv2}; } sub len_real { my $self = shift; Loading util/TLSProxy/ServerHello.pm +16 −4 Original line number Diff line number Diff line Loading @@ -56,14 +56,26 @@ sub parse my $comp_meth = unpack('C', substr($self->data, $ptr)); $ptr++; my $extensions_len = unpack('n', substr($self->data, $ptr)); if (!defined $extensions_len) { $extensions_len = 0; } else { $ptr += 2; } #For now we just deal with this as a block of data. In the future we will #want to parse this my $extension_data = substr($self->data, $ptr); my $extension_data; if ($extensions_len != 0) { $extension_data = substr($self->data, $ptr); if (length($extension_data) != $extensions_len) { die "Invalid extension length\n"; } } else { if (length($self->data) != $ptr) { die "Invalid extension length\n"; } $extension_data = ""; } my %extensions = (); while (length($extension_data) >= 4) { my ($type, $size) = unpack("nn", $extension_data); Loading Loading
test/recipes/70-test_sslrecords.t +197 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ my $proxy = TLSProxy::Proxy->new( my $content_type = TLSProxy::Record::RT_APPLICATION_DATA; my $inject_recs_num = 1; $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; plan tests => 4; plan tests => 9; ok(TLSProxy::Message->fail(), "Out of context empty records test"); #Test 2: Injecting in context empty records should succeed Loading @@ -62,6 +62,52 @@ $proxy->filter(\&add_frag_alert_filter); $proxy->start(); ok(!TLSProxy::Message->end(), "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; my $sslv2testtype = TLSV1_2_IN_SSLV2; $proxy->clear(); $proxy->filter(\&add_sslv2_filter); $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->start(); ok(!TLSProxy::Message->end(), "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->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->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->start(); ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test"); sub add_empty_recs_filter { my $proxy = shift; Loading @@ -79,6 +125,7 @@ sub add_empty_recs_filter 0, 0, 0, 0, "", "" ); Loading Loading @@ -117,6 +164,7 @@ sub add_frag_alert_filter TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, 1, 0, 1, 1, $byte, Loading @@ -131,6 +179,7 @@ sub add_frag_alert_filter TLSProxy::Record::RT_ALERT, TLSProxy::Record::VERS_TLS_1_2, 1, 0, 1, 1, $byte, Loading @@ -138,3 +187,150 @@ sub add_frag_alert_filter ); 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; } }
util/TLSProxy/Message.pm +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ use constant { #Alert descriptions use constant { AL_DESC_CLOSE_NOTIFY => 0, AL_DESC_UNEXPECTED_MESSAGE => 10 AL_DESC_UNEXPECTED_MESSAGE => 10, AL_DESC_NO_RENEGOTIATION => 100 }; my %message_type = ( Loading
util/TLSProxy/Record.pm +13 −1 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ sub get_records $content_type, $version, $len, 0, $len_real, $decrypt_len, substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real), Loading Loading @@ -167,6 +168,7 @@ sub new $content_type, $version, $len, $sslv2, $len_real, $decrypt_len, $data, Loading @@ -177,6 +179,7 @@ sub new content_type => $content_type, version => $version, len => $len, sslv2 => $sslv2, len_real => $len_real, decrypt_len => $decrypt_len, data => $data, Loading Loading @@ -247,7 +250,11 @@ sub reconstruct_record my $self = shift; my $data; if ($self->sslv2) { $data = pack('n', $self->len | 0x8000); } else { $data = pack('Cnn', $self->content_type, $self->version, $self->len); } $data .= $self->data; return $data; Loading @@ -269,6 +276,11 @@ sub version my $self = shift; return $self->{version}; } sub sslv2 { my $self = shift; return $self->{sslv2}; } sub len_real { my $self = shift; Loading
util/TLSProxy/ServerHello.pm +16 −4 Original line number Diff line number Diff line Loading @@ -56,14 +56,26 @@ sub parse my $comp_meth = unpack('C', substr($self->data, $ptr)); $ptr++; my $extensions_len = unpack('n', substr($self->data, $ptr)); if (!defined $extensions_len) { $extensions_len = 0; } else { $ptr += 2; } #For now we just deal with this as a block of data. In the future we will #want to parse this my $extension_data = substr($self->data, $ptr); my $extension_data; if ($extensions_len != 0) { $extension_data = substr($self->data, $ptr); if (length($extension_data) != $extensions_len) { die "Invalid extension length\n"; } } else { if (length($self->data) != $ptr) { die "Invalid extension length\n"; } $extension_data = ""; } my %extensions = (); while (length($extension_data) >= 4) { my ($type, $size) = unpack("nn", $extension_data); Loading