Commit 9ce3ed2a authored by Matt Caswell's avatar Matt Caswell
Browse files

Add tests for new extension code



Extend test_tls13messages to additionally check the expected extensions
under different options given to s_client/s_server.

Perl changes reviewed by Richard Levitte. Non-perl changes reviewed by Rich
Salz

Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
Reviewed-by: default avatarRichard Levitte <levitte@openssl.org>
parent 3434f40b
Loading
Loading
Loading
Loading
+154 −9
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ plan skip_all => "$test_name needs TLSv1.3 enabled"
    if disabled("tls1_3");

$ENV{OPENSSL_ia32cap} = '~0x200000200000000';
$ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");

use constant {
    DEFAULT_HANDSHAKE => 1,
@@ -36,6 +37,17 @@ use constant {
    ALL_HANDSHAKES => 15
};

use constant {
    DEFAULT_EXTENSIONS => 0x00000001,
    SERVER_NAME_CLI_EXTENSION => 0x00000002,
    SERVER_NAME_SRV_EXTENSION => 0x00000004,
    STATUS_REQUEST_CLI_EXTENSION => 0x00000008,
    STATUS_REQUEST_SRV_EXTENSION => 0x00000010,
    ALPN_CLI_EXTENSION => 0x00000020,
    ALPN_SRV_EXTENSION => 0x00000040,
    SCT_CLI_EXTENSION => 0x00000080
};

my @handmessages = (
    [TLSProxy::Message::MT_CLIENT_HELLO, ALL_HANDSHAKES],
    [TLSProxy::Message::MT_SERVER_HELLO, ALL_HANDSHAKES],
@@ -50,6 +62,28 @@ my @handmessages = (
    [0, 0]
);

my @extensions = (
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME, SERVER_NAME_CLI_EXTENSION],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST, STATUS_REQUEST_CLI_EXTENSION],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN, ALPN_CLI_EXTENSION],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT, SCT_CLI_EXTENSION],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE, DEFAULT_EXTENSIONS],
    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS, DEFAULT_EXTENSIONS],

    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE, DEFAULT_EXTENSIONS],

    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_SERVER_NAME, SERVER_NAME_SRV_EXTENSION],
    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_STATUS_REQUEST, STATUS_REQUEST_SRV_EXTENSION],
    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_ALPN, ALPN_SRV_EXTENSION],
    [0,0,0]
);

my $proxy = TLSProxy::Proxy->new(
    undef,
    cmdstr(app(["openssl"]), display => 1),
@@ -57,15 +91,15 @@ my $proxy = TLSProxy::Proxy->new(
    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
);

sub checkmessages($$);
sub checkmessages($$$);

#Test 1: Check we get all the right messages for a default handshake
(undef, my $session) = tempfile();
#$proxy->serverconnects(2);
$proxy->clientflags("-sess_out ".$session);
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 3;
checkmessages(DEFAULT_HANDSHAKE, "Default handshake test");
plan tests => 12;
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS, "Default handshake test");

#TODO(TLS1.3): Test temporarily disabled until we implement TLS1.3 resumption
#Test 2: Resumption handshake
@@ -75,7 +109,23 @@ checkmessages(DEFAULT_HANDSHAKE, "Default handshake test");
#checkmessages(RESUME_HANDSHAKE, "Resumption handshake test");
unlink $session;

#Test 3: A default handshake, but with a CertificateStatus message
#Test 3: A status_request handshake (client request only)
$proxy->clear();
$proxy->clientflags("-status");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE,
              DEFAULT_EXTENSIONS | STATUS_REQUEST_CLI_EXTENSION,
              "status_request handshake test (client)");

#Test 4: A status_request handshake (server support only)
$proxy->clear();
$proxy->serverflags("-status_file "
                    .srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS,
              "status_request handshake test (server)");

#Test 5: A status_request handshake (client and server)
#TODO(TLS1.3): TLS1.3 doesn't actually have CertificateStatus messages. This is
#a temporary test until such time as we do proper TLS1.3 style certificate
#status
@@ -84,28 +134,102 @@ $proxy->clientflags("-status");
$proxy->serverflags("-status_file "
                    .srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkmessages(OCSP_HANDSHAKE, "OCSP handshake test");
checkmessages(OCSP_HANDSHAKE,
              DEFAULT_EXTENSIONS | STATUS_REQUEST_CLI_EXTENSION
              | STATUS_REQUEST_SRV_EXTENSION,
              "status_request handshake test");

#Test 4: A client auth handshake
#Test 6: A client auth handshake
$proxy->clear();
$proxy->clientflags("-cert ".srctop_file("apps", "server.pem"));
$proxy->serverflags("-Verify 5");
$proxy->start();
checkmessages(CLIENT_AUTH_HANDSHAKE, "Client auth handshake test");
checkmessages(CLIENT_AUTH_HANDSHAKE, DEFAULT_EXTENSIONS,
              "Client auth handshake test");

#Test 7: Server name handshake (client request only)
$proxy->clear();
$proxy->clientflags("-servername testhost");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS | SERVER_NAME_CLI_EXTENSION,
              "Server name handshake test (client)");

#Test 8: Server name handshake (server support only)
$proxy->clear();
$proxy->serverflags("-servername testhost");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS,
              "Server name handshake test (server)");

sub checkmessages($$)
#Test 9: Server name handshake (client and server)
$proxy->clear();
$proxy->clientflags("-servername testhost");
$proxy->serverflags("-servername testhost");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE,
              DEFAULT_EXTENSIONS | SERVER_NAME_CLI_EXTENSION
              | SERVER_NAME_SRV_EXTENSION,
              "Server name handshake test");

#Test 10: ALPN handshake (client request only)
$proxy->clear();
$proxy->clientflags("-alpn test");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS | ALPN_CLI_EXTENSION,
              "ALPN handshake test (client)");

#Test 11: ALPN handshake (server support only)
$proxy->clear();
$proxy->serverflags("-alpn test");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE, DEFAULT_EXTENSIONS,
              "ALPN handshake test (server)");
              
#Test 12: ALPN handshake (client and server)
$proxy->clear();
$proxy->clientflags("-alpn test");
$proxy->serverflags("-alpn test");
$proxy->start();
checkmessages(DEFAULT_HANDSHAKE,
              DEFAULT_EXTENSIONS | ALPN_CLI_EXTENSION | ALPN_SRV_EXTENSION,
              "ALPN handshake test");

#Test 13: SCT handshake (client request only)
#TODO(TLS1.3): This only checks that the client side extension appears. The
#SCT extension is unusual in that we have no built-in server side implementation
#The server side implementation can nomrally be added using the custom
#extensions framework (e.g. by using the "-serverinfo" s_server option). However
#currently we only support <= TLS1.2 for custom extensions because the existing
#framework and API has no knowledge of the TLS1.3 messages
$proxy->clear();
#Note: -ct also sends status_request
$proxy->clientflags("-ct");
$proxy->serverflags("-status_file "
                    .srctop_file("test", "recipes", "ocsp-response.der"));
$proxy->start();
checkmessages(OCSP_HANDSHAKE,
              DEFAULT_EXTENSIONS | SCT_CLI_EXTENSION
              | STATUS_REQUEST_CLI_EXTENSION | STATUS_REQUEST_SRV_EXTENSION,
              "SCT handshake test");

sub checkmessages($$$)
{
    my ($handtype, $testname) = @_;
    my ($handtype, $exttype, $testname) = @_;

    subtest $testname => sub {
        my $loop = 0;
        my $numtests;
        my $extcount;

        #First count the number of tests
        for ($numtests = 1; $handmessages[$loop][1] != 0; $loop++) {
            $numtests++ if (($handmessages[$loop][1] & $handtype) != 0);
        }

        #Add number of extensions we check plus 3 for the number of messages
        #that contain extensions
        $numtests += $#extensions + 3;

        plan tests => $numtests;

        $loop = 0;
@@ -119,6 +243,27 @@ sub checkmessages($$)
               "Message type check. Got ".$message->mt
               .", expected ".$handmessages[$loop][0]);
            $loop++;


            next if ($message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
                    && $message->mt() != TLSProxy::Message::MT_SERVER_HELLO
                    && $message->mt() !=
                       TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS);
             #Now check that we saw the extensions we expected
             my $msgexts = $message->extension_data();
             for (my $extloop = 0, $extcount = 0; $extensions[$extloop][2] != 0;
                                $extloop++) {
                next if ($message->mt() != $extensions[$extloop][0]);
                ok (($extensions[$extloop][2] & $exttype) == 0
                      || defined ($msgexts->{$extensions[$extloop][1]}),
                    "Extension presence check (Message: ".$message->mt()
                    ." Extension: ".($extensions[$extloop][2] & $exttype).", "
                    .$extloop.")");
                $extcount++ if (($extensions[$extloop][2] & $exttype) != 0);
             }
            ok($extcount == keys %$msgexts, "Extensions count mismatch ("
                                            .$extcount.", ".(keys %$msgexts)
                                            .")");
        }
        ok($handmessages[$loop][1] == 0, "All expected messages processed");
    }
+115 −0
Original line number Diff line number Diff line
# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (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;

package TLSProxy::EncryptedExtensions;

use vars '@ISA';
push @ISA, 'TLSProxy::Message';

sub new
{
    my $class = shift;
    my ($server,
        $data,
        $records,
        $startoffset,
        $message_frag_lens) = @_;
    
    my $self = $class->SUPER::new(
        $server,
        TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS,
        $data,
        $records,
        $startoffset,
        $message_frag_lens);

    $self->{extension_data} = "";

    return $self;
}

sub parse
{
    my $self = shift;

    my $extensions_len = unpack('n', $self->data);
    if (!defined $extensions_len) {
        $extensions_len = 0;
    }

    my $extension_data;
    if ($extensions_len != 0) {
        $extension_data = substr($self->data, 2);
    
        if (length($extension_data) != $extensions_len) {
            die "Invalid extension length\n";
        }
    } else {
        if (length($self->data) != 2) {
            die "Invalid extension length\n";
        }
        $extension_data = "";
    }
    my %extensions = ();
    while (length($extension_data) >= 4) {
        my ($type, $size) = unpack("nn", $extension_data);
        my $extdata = substr($extension_data, 4, $size);
        $extension_data = substr($extension_data, 4 + $size);
        $extensions{$type} = $extdata;
    }

    $self->extension_data(\%extensions);

    print "    Extensions Len:".$extensions_len."\n";
}

#Reconstruct the on-the-wire message data following changes
sub set_message_contents
{
    my $self = shift;
    my $data;
    my $extensions = "";

    foreach my $key (keys %{$self->extension_data}) {
        my $extdata = ${$self->extension_data}{$key};
        $extensions .= pack("n", $key);
        $extensions .= pack("n", length($extdata));
        $extensions .= $extdata;
        if ($key == TLSProxy::Message::EXT_DUPLICATE_EXTENSION) {
          $extensions .= pack("n", $key);
          $extensions .= pack("n", length($extdata));
          $extensions .= $extdata;
        }
    }

    $data = pack('n', length($extensions));
    $data .= $extensions;
    $self->data($data);
}

#Read/write accessors
sub extension_data
{
    my $self = shift;
    if (@_) {
      $self->{extension_data} = shift;
    }
    return $self->{extension_data};
}
sub set_extension
{
    my ($self, $ext_type, $ext_data) = @_;
    $self->{extension_data}{$ext_type} = $ext_data;
}
sub delete_extension
{
    my ($self, $ext_type) = @_;
    delete $self->{extension_data}{$ext_type};
}
1;
+20 −1
Original line number Diff line number Diff line
@@ -60,13 +60,23 @@ my %message_type = (
);

use constant {
    EXT_SERVER_NAME => 0,
    EXT_STATUS_REQUEST => 5,
    EXT_SUPPORTED_GROUPS => 10,
    EXT_EC_POINT_FORMATS => 11,
    EXT_SRP => 12,
    EXT_SIG_ALGS => 13,
    EXT_USE_SRTP => 14,
    EXT_ALPN => 16,
    EXT_SCT => 18,
    EXT_PADDING => 21,
    EXT_ENCRYPT_THEN_MAC => 22,
    EXT_EXTENDED_MASTER_SECRET => 23,
    EXT_SESSION_TICKET => 35,
    EXT_SUPPORTED_VERSIONS => 43,
    EXT_KEY_SHARE => 40,
    EXT_SUPPORTED_VERSIONS => 43,
    EXT_RENEGOTIATE => 65281,
    EXT_NPN => 13172,
    # This extension is an unofficial extension only ever written by OpenSSL
    # (i.e. not read), and even then only when enabled. We use it to test
    # handling of duplicate extensions.
@@ -245,6 +255,15 @@ sub create_message
            [@message_frag_lens]
        );
        $message->parse();
    } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) {
        $message = TLSProxy::EncryptedExtensions->new(
            $server,
            $data,
            [@message_rec_list],
            $startoffset,
            [@message_frag_lens]
        );
        $message->parse();
    } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
        $message = TLSProxy::ServerKeyExchange->new(
            $server,
+3 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ use TLSProxy::Record;
use TLSProxy::Message;
use TLSProxy::ClientHello;
use TLSProxy::ServerHello;
use TLSProxy::EncryptedExtensions;
use TLSProxy::ServerKeyExchange;
use TLSProxy::NewSessionTicket;

@@ -153,7 +154,8 @@ sub start
        my $execcmd = $self->execute
            ." s_server -no_comp -rev -engine ossltest -accept "
            .($self->server_port)
            ." -cert ".$self->cert." -naccept ".$self->serverconnects;
            ." -cert ".$self->cert." -cert2 ".$self->cert
            ." -naccept ".$self->serverconnects;
        if ($self->ciphers ne "") {
            $execcmd .= " -cipher ".$self->ciphers;
        }