Add initial DTLS tests.
Change-Id: I7407261bdb2d788c879f2e67e617a615d9ff8f8b Reviewed-on: https://boringssl-review.googlesource.com/1505 Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
committed by
Adam Langley
parent
c10dde465e
commit
6fd297bb62
@@ -5,6 +5,7 @@ add_executable(
|
||||
|
||||
async_bio.cc
|
||||
bssl_shim.cc
|
||||
packeted_bio.cc
|
||||
test_config.cc
|
||||
)
|
||||
|
||||
|
||||
+26
-10
@@ -22,6 +22,7 @@ namespace {
|
||||
extern const BIO_METHOD async_bio_method;
|
||||
|
||||
struct async_bio {
|
||||
bool datagram;
|
||||
size_t read_quota;
|
||||
size_t write_quota;
|
||||
};
|
||||
@@ -39,6 +40,12 @@ static int async_write(BIO *bio, const char *in, int inl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->datagram) {
|
||||
// Perform writes synchronously; the DTLS implementation drops any packets
|
||||
// that failed to send.
|
||||
return BIO_write(bio->next_bio, in, inl);
|
||||
}
|
||||
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
if (a->write_quota == 0) {
|
||||
@@ -47,7 +54,7 @@ static int async_write(BIO *bio, const char *in, int inl) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)inl > a->write_quota) {
|
||||
if (!a->datagram && (size_t)inl > a->write_quota) {
|
||||
inl = a->write_quota;
|
||||
}
|
||||
int ret = BIO_write(bio->next_bio, in, inl);
|
||||
@@ -73,14 +80,14 @@ static int async_read(BIO *bio, char *out, int outl) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)outl > a->read_quota) {
|
||||
if (!a->datagram && (size_t)outl > a->read_quota) {
|
||||
outl = a->read_quota;
|
||||
}
|
||||
int ret = BIO_read(bio->next_bio, out, outl);
|
||||
if (ret <= 0) {
|
||||
BIO_copy_next_retry(bio);
|
||||
} else {
|
||||
a->read_quota -= ret;
|
||||
a->read_quota -= (a->datagram ? 1 : ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -144,18 +151,27 @@ BIO *async_bio_create() {
|
||||
return BIO_new(&async_bio_method);
|
||||
}
|
||||
|
||||
void async_bio_allow_read(BIO *bio, size_t bytes) {
|
||||
async_bio *a = get_data(bio);
|
||||
if (a == NULL) {
|
||||
return;
|
||||
BIO *async_bio_create_datagram() {
|
||||
BIO *ret = BIO_new(&async_bio_method);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
a->read_quota += bytes;
|
||||
get_data(ret)->datagram = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void async_bio_allow_write(BIO *bio, size_t bytes) {
|
||||
void async_bio_allow_read(BIO *bio, size_t count) {
|
||||
async_bio *a = get_data(bio);
|
||||
if (a == NULL) {
|
||||
return;
|
||||
}
|
||||
a->write_quota += bytes;
|
||||
a->read_quota += count;
|
||||
}
|
||||
|
||||
void async_bio_allow_write(BIO *bio, size_t count) {
|
||||
async_bio *a = get_data(bio);
|
||||
if (a == NULL) {
|
||||
return;
|
||||
}
|
||||
a->write_quota += count;
|
||||
}
|
||||
|
||||
+14
-9
@@ -18,18 +18,23 @@
|
||||
#include <openssl/bio.h>
|
||||
|
||||
|
||||
// async_bio_create creates a filter BIO for testing asynchronous
|
||||
// state machines. Reads and writes will fail and return EAGAIN unless
|
||||
// explicitly allowed. Each async BIO has a read quota and a write
|
||||
// quota. Initially both are zero. As each is incremented, bytes are
|
||||
// allowed to flow through the BIO.
|
||||
// async_bio_create creates a filter BIO for testing asynchronous state
|
||||
// machines which consume a stream socket. Reads and writes will fail
|
||||
// and return EAGAIN unless explicitly allowed. Each async BIO has a
|
||||
// read quota and a write quota. Initially both are zero. As each is
|
||||
// incremented, bytes are allowed to flow through the BIO.
|
||||
BIO *async_bio_create();
|
||||
|
||||
// async_bio_allow_read increments |bio|'s read quota by |bytes|.
|
||||
void async_bio_allow_read(BIO *bio, size_t bytes);
|
||||
// async_bio_create_datagram creates a filter BIO for testing for
|
||||
// asynchronous state machines which consume datagram sockets. The read
|
||||
// and write quota count in packets rather than bytes.
|
||||
BIO *async_bio_create_datagram();
|
||||
|
||||
// async_bio_allow_write increments |bio|'s write quota by |bytes|.
|
||||
void async_bio_allow_write(BIO *bio, size_t bytes);
|
||||
// async_bio_allow_read increments |bio|'s read quota by |count|.
|
||||
void async_bio_allow_read(BIO *bio, size_t count);
|
||||
|
||||
// async_bio_allow_write increments |bio|'s write quota by |count|.
|
||||
void async_bio_allow_write(BIO *bio, size_t count);
|
||||
|
||||
|
||||
#endif // HEADER_ASYNC_BIO
|
||||
|
||||
+65
-3
@@ -29,6 +29,7 @@
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "async_bio.h"
|
||||
#include "packeted_bio.h"
|
||||
#include "test_config.h"
|
||||
|
||||
static int usage(const char *program) {
|
||||
@@ -124,16 +125,61 @@ static int next_proto_select_callback(SSL* ssl,
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int cookie_generate_callback(SSL *ssl, uint8_t *cookie, unsigned *cookie_len) {
|
||||
*cookie_len = 32;
|
||||
memset(cookie, 42, *cookie_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cookie_verify_callback(SSL *ssl, uint8_t *cookie, unsigned cookie_len) {
|
||||
if (cookie_len != 32) {
|
||||
fprintf(stderr, "Cookie length mismatch.\n");
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < cookie_len; i++) {
|
||||
if (cookie[i] != 42) {
|
||||
fprintf(stderr, "Cookie mismatch.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SSL_CTX *setup_ctx(const TestConfig *config) {
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
DH *dh = NULL;
|
||||
|
||||
ssl_ctx = SSL_CTX_new(
|
||||
config->is_server ? SSLv23_server_method() : SSLv23_client_method());
|
||||
const SSL_METHOD *method;
|
||||
if (config->is_dtls) {
|
||||
// TODO(davidben): Get DTLS 1.2 working and test the version negotiation
|
||||
// codepath. This doesn't currently work because
|
||||
// - Session resumption is broken: https://crbug.com/403378
|
||||
// - DTLS hasn't been updated for EVP_AEAD.
|
||||
if (config->is_server) {
|
||||
method = DTLSv1_server_method();
|
||||
} else {
|
||||
method = DTLSv1_client_method();
|
||||
}
|
||||
} else {
|
||||
if (config->is_server) {
|
||||
method = SSLv23_server_method();
|
||||
} else {
|
||||
method = SSLv23_client_method();
|
||||
}
|
||||
}
|
||||
ssl_ctx = SSL_CTX_new(method);
|
||||
if (ssl_ctx == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (config->is_dtls) {
|
||||
// DTLS needs read-ahead to function on a datagram BIO.
|
||||
//
|
||||
// TODO(davidben): this should not be necessary. DTLS code should only
|
||||
// expect a datagram BIO.
|
||||
SSL_CTX_set_read_ahead(ssl_ctx, 1);
|
||||
}
|
||||
|
||||
if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) {
|
||||
goto err;
|
||||
}
|
||||
@@ -156,6 +202,9 @@ static SSL_CTX *setup_ctx(const TestConfig *config) {
|
||||
SSL_CTX_set_next_proto_select_cb(
|
||||
ssl_ctx, next_proto_select_callback, NULL);
|
||||
|
||||
SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
|
||||
SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
|
||||
|
||||
DH_free(dh);
|
||||
return ssl_ctx;
|
||||
|
||||
@@ -248,14 +297,23 @@ static int do_exchange(SSL_SESSION **out_session,
|
||||
if (config->no_ssl3) {
|
||||
SSL_set_options(ssl, SSL_OP_NO_SSLv3);
|
||||
}
|
||||
if (config->cookie_exchange) {
|
||||
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
|
||||
}
|
||||
|
||||
BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
|
||||
if (bio == NULL) {
|
||||
BIO_print_errors_fp(stdout);
|
||||
return 1;
|
||||
}
|
||||
if (config->is_dtls) {
|
||||
BIO *packeted = packeted_bio_create();
|
||||
BIO_push(packeted, bio);
|
||||
bio = packeted;
|
||||
}
|
||||
if (config->async) {
|
||||
BIO *async = async_bio_create();
|
||||
BIO *async =
|
||||
config->is_dtls ? async_bio_create_datagram() : async_bio_create();
|
||||
BIO_push(async, bio);
|
||||
bio = async;
|
||||
}
|
||||
@@ -329,6 +387,10 @@ static int do_exchange(SSL_SESSION **out_session,
|
||||
}
|
||||
|
||||
if (config->write_different_record_sizes) {
|
||||
if (config->is_dtls) {
|
||||
fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
|
||||
return 6;
|
||||
}
|
||||
// This mode writes a number of different record sizes in an attempt to
|
||||
// trip up the CBC record splitting code.
|
||||
uint8_t buf[32769];
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/* Copyright (c) 2014, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#include "packeted_bio.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/mem.h>
|
||||
|
||||
namespace {
|
||||
|
||||
extern const BIO_METHOD packeted_bio_method;
|
||||
|
||||
static int packeted_write(BIO *bio, const char *in, int inl) {
|
||||
if (bio->next_bio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
// Write the length prefix.
|
||||
uint8_t len_bytes[4];
|
||||
len_bytes[0] = (inl >> 24) & 0xff;
|
||||
len_bytes[1] = (inl >> 16) & 0xff;
|
||||
len_bytes[2] = (inl >> 8) & 0xff;
|
||||
len_bytes[3] = inl & 0xff;
|
||||
int ret = BIO_write(bio->next_bio, len_bytes, sizeof(len_bytes));
|
||||
if (ret <= 0) {
|
||||
BIO_copy_next_retry(bio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write the buffer. BIOs for which this operation fails are not supported.
|
||||
ret = BIO_write(bio->next_bio, in, inl);
|
||||
assert(ret == inl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int packeted_read(BIO *bio, char *out, int outl) {
|
||||
if (bio->next_bio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
// Read the length prefix.
|
||||
uint8_t len_bytes[4];
|
||||
int ret = BIO_read(bio->next_bio, &len_bytes, sizeof(len_bytes));
|
||||
if (ret <= 0) {
|
||||
BIO_copy_next_retry(bio);
|
||||
return ret;
|
||||
}
|
||||
// BIOs for which a partial length comes back are not supported.
|
||||
assert(ret == 4);
|
||||
|
||||
uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
|
||||
(len_bytes[2] << 8) | len_bytes[3];
|
||||
char *buf = (char *)OPENSSL_malloc(len);
|
||||
assert(buf != NULL);
|
||||
ret = BIO_read(bio->next_bio, buf, len);
|
||||
assert(ret == (int)len);
|
||||
|
||||
if (outl > (int)len) {
|
||||
outl = len;
|
||||
}
|
||||
memcpy(out, buf, outl);
|
||||
OPENSSL_free(buf);
|
||||
return outl;
|
||||
}
|
||||
|
||||
static long packeted_ctrl(BIO *bio, int cmd, long num, void *ptr) {
|
||||
if (bio->next_bio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
BIO_clear_retry_flags(bio);
|
||||
int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
|
||||
BIO_copy_next_retry(bio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int packeted_new(BIO *bio) {
|
||||
bio->init = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int packeted_free(BIO *bio) {
|
||||
if (bio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bio->init = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long packeted_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
|
||||
if (bio->next_bio == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return BIO_callback_ctrl(bio->next_bio, cmd, fp);
|
||||
}
|
||||
|
||||
const BIO_METHOD packeted_bio_method = {
|
||||
BIO_TYPE_FILTER,
|
||||
"packeted bio",
|
||||
packeted_write,
|
||||
packeted_read,
|
||||
NULL /* puts */,
|
||||
NULL /* gets */,
|
||||
packeted_ctrl,
|
||||
packeted_new,
|
||||
packeted_free,
|
||||
packeted_callback_ctrl,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
BIO *packeted_bio_create() {
|
||||
return BIO_new(&packeted_bio_method);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (c) 2014, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#ifndef HEADER_PACKETED_BIO
|
||||
#define HEADER_PACKETED_BIO
|
||||
|
||||
#include <openssl/bio.h>
|
||||
|
||||
|
||||
// packeted_bio_create creates a filter BIO for testing protocols which expect
|
||||
// datagram BIOs. It implements a reliable datagram socket and reads and writes
|
||||
// packets by prefixing each packet with a big-endian 32-bit length. It must be
|
||||
// layered over a reliable blocking stream BIO.
|
||||
//
|
||||
// Note: packeted_bio_create exists because a SOCK_DGRAM socketpair on OS X is
|
||||
// does not block the caller, unlike on Linux. Writes simply fail with
|
||||
// ENOBUFS. POSIX also does not guarantee that such sockets are reliable.
|
||||
BIO *packeted_bio_create();
|
||||
|
||||
|
||||
#endif // HEADER_PACKETED_BIO
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
type packetAdaptor struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func newPacketAdaptor(conn net.Conn) net.Conn {
|
||||
return &packetAdaptor{conn}
|
||||
}
|
||||
|
||||
func (p *packetAdaptor) Read(b []byte) (int, error) {
|
||||
var length uint32
|
||||
if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
out := make([]byte, length)
|
||||
n, err := p.Conn.Read(out)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != int(length) {
|
||||
return 0, errors.New("internal error: length mismatch!")
|
||||
}
|
||||
return copy(b, out), nil
|
||||
}
|
||||
|
||||
func (p *packetAdaptor) Write(b []byte) (int, error) {
|
||||
length := uint32(len(b))
|
||||
if err := binary.Write(p.Conn, binary.BigEndian, length); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := p.Conn.Write(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != len(b) {
|
||||
return 0, errors.New("internal error: length mismatch!")
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
+215
-100
@@ -63,8 +63,16 @@ const (
|
||||
serverTest
|
||||
)
|
||||
|
||||
type protocol int
|
||||
|
||||
const (
|
||||
tls protocol = iota
|
||||
dtls
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
testType testType
|
||||
protocol protocol
|
||||
name string
|
||||
config Config
|
||||
shouldFail bool
|
||||
@@ -340,18 +348,6 @@ var testCases = []testCase{
|
||||
shouldFail: true,
|
||||
expectedError: ":CCS_RECEIVED_EARLY:",
|
||||
},
|
||||
{
|
||||
name: "FalseStart-SessionTicketsDisabled",
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
SessionTicketsDisabled: true,
|
||||
},
|
||||
flags: []string{
|
||||
"-false-start",
|
||||
"-select-next-proto", "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
testType: serverTest,
|
||||
name: "FallbackSCSV",
|
||||
@@ -454,16 +450,30 @@ var testCases = []testCase{
|
||||
}
|
||||
|
||||
func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
|
||||
if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
|
||||
return err
|
||||
if test.protocol == dtls {
|
||||
conn = newPacketAdaptor(conn)
|
||||
}
|
||||
|
||||
if test.sendPrefix != "" {
|
||||
if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var tlsConn *Conn
|
||||
if test.testType == clientTest {
|
||||
tlsConn = Server(conn, config)
|
||||
if test.protocol == dtls {
|
||||
tlsConn = DTLSServer(conn, config)
|
||||
} else {
|
||||
tlsConn = Server(conn, config)
|
||||
}
|
||||
} else {
|
||||
config.InsecureSkipVerify = true
|
||||
tlsConn = Client(conn, config)
|
||||
if test.protocol == dtls {
|
||||
tlsConn = DTLSClient(conn, config)
|
||||
} else {
|
||||
tlsConn = Client(conn, config)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
@@ -475,6 +485,9 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) e
|
||||
}
|
||||
|
||||
if messageLen < 0 {
|
||||
if test.protocol == dtls {
|
||||
return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
|
||||
}
|
||||
// Read until EOF.
|
||||
_, err := io.Copy(ioutil.Discard, tlsConn)
|
||||
return err
|
||||
@@ -490,9 +503,21 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) e
|
||||
tlsConn.Write(testMessage)
|
||||
|
||||
buf := make([]byte, len(testMessage))
|
||||
_, err := io.ReadFull(tlsConn, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
if test.protocol == dtls {
|
||||
bufTmp := make([]byte, len(buf)+1)
|
||||
n, err := tlsConn.Read(bufTmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(buf) {
|
||||
return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
|
||||
}
|
||||
copy(buf, bufTmp)
|
||||
} else {
|
||||
_, err := io.ReadFull(tlsConn, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range buf {
|
||||
@@ -568,6 +593,10 @@ func runTest(test *testCase, buildDir string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if test.protocol == dtls {
|
||||
flags = append(flags, "-dtls")
|
||||
}
|
||||
|
||||
if test.resumeSession {
|
||||
flags = append(flags, "-resume")
|
||||
}
|
||||
@@ -746,6 +775,36 @@ func addCipherSuiteTests() {
|
||||
resumeSession: resumeSession,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(davidben): Fix DTLS 1.2 support and test that.
|
||||
if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
|
||||
testCases = append(testCases, testCase{
|
||||
testType: clientTest,
|
||||
protocol: dtls,
|
||||
name: "D" + ver.name + "-" + suite.name + "-client",
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
CipherSuites: []uint16{suite.id},
|
||||
Certificates: []Certificate{cert},
|
||||
},
|
||||
resumeSession: resumeSession,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
protocol: dtls,
|
||||
name: "D" + ver.name + "-" + suite.name + "-server",
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
CipherSuites: []uint16{suite.id},
|
||||
Certificates: []Certificate{cert},
|
||||
},
|
||||
certFile: certFile,
|
||||
keyFile: keyFile,
|
||||
resumeSession: resumeSession,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,15 +977,18 @@ func addClientAuthTests() {
|
||||
// Adds tests that try to cover the range of the handshake state machine, under
|
||||
// various conditions. Some of these are redundant with other tests, but they
|
||||
// only cover the synchronous case.
|
||||
func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
|
||||
var suffix string
|
||||
var flags []string
|
||||
var maxHandshakeRecordLength int
|
||||
if protocol == dtls {
|
||||
suffix = "-DTLS"
|
||||
}
|
||||
if async {
|
||||
suffix = "-Async"
|
||||
suffix += "-Async"
|
||||
flags = append(flags, "-async")
|
||||
} else {
|
||||
suffix = "-Sync"
|
||||
suffix += "-Sync"
|
||||
}
|
||||
if splitHandshake {
|
||||
suffix += "-SplitHandshakeRecords"
|
||||
@@ -935,7 +997,8 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
|
||||
// Basic handshake, with resumption. Client and server.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "Basic-Client" + suffix,
|
||||
protocol: protocol,
|
||||
name: "Basic-Client" + suffix,
|
||||
config: Config{
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
@@ -945,7 +1008,8 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
resumeSession: true,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
name: "Basic-Client-RenewTicket" + suffix,
|
||||
protocol: protocol,
|
||||
name: "Basic-Client-RenewTicket" + suffix,
|
||||
config: Config{
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
@@ -956,6 +1020,7 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
resumeSession: true,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: serverTest,
|
||||
name: "Basic-Server" + suffix,
|
||||
config: Config{
|
||||
@@ -967,9 +1032,36 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// TLS client auth.
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: clientTest,
|
||||
name: "ClientAuth-Client" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
|
||||
ClientAuth: RequireAnyClientCert,
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
flags: append(flags,
|
||||
"-cert-file", rsaCertificateFile,
|
||||
"-key-file", rsaKeyFile),
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: serverTest,
|
||||
name: "ClientAuth-Server" + suffix,
|
||||
config: Config{
|
||||
Certificates: []Certificate{rsaCertificate},
|
||||
},
|
||||
flags: append(flags, "-require-any-client-certificate"),
|
||||
})
|
||||
|
||||
// No session ticket support; server doesn't send NewSessionTicket.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "SessionTicketsDisabled-Client" + suffix,
|
||||
protocol: protocol,
|
||||
name: "SessionTicketsDisabled-Client" + suffix,
|
||||
config: Config{
|
||||
SessionTicketsDisabled: true,
|
||||
Bugs: ProtocolBugs{
|
||||
@@ -979,6 +1071,7 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
flags: flags,
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: serverTest,
|
||||
name: "SessionTicketsDisabled-Server" + suffix,
|
||||
config: Config{
|
||||
@@ -990,88 +1083,108 @@ func addStateMachineCoverageTests(async bool, splitHandshake bool) {
|
||||
flags: flags,
|
||||
})
|
||||
|
||||
// NPN on client and server; results in post-handshake message.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "NPN-Client" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
if protocol == tls {
|
||||
// NPN on client and server; results in post-handshake message.
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
name: "NPN-Client" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
},
|
||||
flags: append(flags, "-select-next-proto", "foo"),
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "NPN-Server" + suffix,
|
||||
config: Config{
|
||||
NextProtos: []string{"bar"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
flags: append(flags, "-select-next-proto", "foo"),
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: serverTest,
|
||||
name: "NPN-Server" + suffix,
|
||||
config: Config{
|
||||
NextProtos: []string{"bar"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
},
|
||||
flags: append(flags,
|
||||
"-advertise-npn", "\x03foo\x03bar\x03baz",
|
||||
"-expect-next-proto", "bar"),
|
||||
})
|
||||
flags: append(flags,
|
||||
"-advertise-npn", "\x03foo\x03bar\x03baz",
|
||||
"-expect-next-proto", "bar"),
|
||||
})
|
||||
|
||||
// Client does False Start and negotiates NPN.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "FalseStart" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
// Client does False Start and negotiates NPN.
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
name: "FalseStart" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
},
|
||||
flags: append(flags,
|
||||
"-false-start",
|
||||
"-select-next-proto", "foo"),
|
||||
resumeSession: true,
|
||||
})
|
||||
flags: append(flags,
|
||||
"-false-start",
|
||||
"-select-next-proto", "foo"),
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// TLS client auth.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: clientTest,
|
||||
name: "ClientAuth-Client" + suffix,
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
ClientAuth: RequireAnyClientCert,
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
// False Start without session tickets.
|
||||
testCases = append(testCases, testCase{
|
||||
name: "FalseStart-SessionTicketsDisabled",
|
||||
config: Config{
|
||||
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
NextProtos: []string{"foo"},
|
||||
SessionTicketsDisabled: true,
|
||||
},
|
||||
},
|
||||
flags: append(flags,
|
||||
"-cert-file", rsaCertificateFile,
|
||||
"-key-file", rsaKeyFile),
|
||||
})
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "ClientAuth-Server" + suffix,
|
||||
config: Config{
|
||||
Certificates: []Certificate{rsaCertificate},
|
||||
},
|
||||
flags: append(flags, "-require-any-client-certificate"),
|
||||
})
|
||||
flags: []string{
|
||||
"-false-start",
|
||||
"-select-next-proto", "foo",
|
||||
},
|
||||
})
|
||||
|
||||
// Client sends a V2ClientHello.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "SendV2ClientHello" + suffix,
|
||||
config: Config{
|
||||
// Choose a cipher suite that does not involve
|
||||
// elliptic curves, so no extensions are
|
||||
// involved.
|
||||
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
SendV2ClientHello: true,
|
||||
// Client sends a V2ClientHello.
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
testType: serverTest,
|
||||
name: "SendV2ClientHello" + suffix,
|
||||
config: Config{
|
||||
// Choose a cipher suite that does not involve
|
||||
// elliptic curves, so no extensions are
|
||||
// involved.
|
||||
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
SendV2ClientHello: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
flags: flags,
|
||||
})
|
||||
flags: flags,
|
||||
})
|
||||
} else {
|
||||
testCases = append(testCases, testCase{
|
||||
protocol: protocol,
|
||||
name: "SkipHelloVerifyRequest" + suffix,
|
||||
config: Config{
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
SkipHelloVerifyRequest: true,
|
||||
},
|
||||
},
|
||||
flags: flags,
|
||||
})
|
||||
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
protocol: protocol,
|
||||
name: "CookieExchange" + suffix,
|
||||
config: Config{
|
||||
Bugs: ProtocolBugs{
|
||||
MaxHandshakeRecordLength: maxHandshakeRecordLength,
|
||||
},
|
||||
},
|
||||
flags: append(flags, "-cookie-exchange"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func addVersionNegotiationTests() {
|
||||
@@ -1169,7 +1282,9 @@ func main() {
|
||||
addVersionNegotiationTests()
|
||||
for _, async := range []bool{false, true} {
|
||||
for _, splitHandshake := range []bool{false, true} {
|
||||
addStateMachineCoverageTests(async, splitHandshake)
|
||||
for _, protocol := range []protocol{tls, dtls} {
|
||||
addStateMachineCoverageTests(async, splitHandshake, protocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ struct StringFlag {
|
||||
|
||||
const BoolFlag kBoolFlags[] = {
|
||||
{ "-server", &TestConfig::is_server },
|
||||
{ "-dtls", &TestConfig::is_dtls },
|
||||
{ "-resume", &TestConfig::resume },
|
||||
{ "-fallback-scsv", &TestConfig::fallback_scsv },
|
||||
{ "-require-any-client-certificate",
|
||||
@@ -48,6 +49,7 @@ const BoolFlag kBoolFlags[] = {
|
||||
{ "-no-tls11", &TestConfig::no_tls11 },
|
||||
{ "-no-tls1", &TestConfig::no_tls1 },
|
||||
{ "-no-ssl3", &TestConfig::no_ssl3 },
|
||||
{ "-cookie-exchange", &TestConfig::cookie_exchange },
|
||||
};
|
||||
|
||||
const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]);
|
||||
@@ -71,6 +73,7 @@ const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);
|
||||
|
||||
TestConfig::TestConfig()
|
||||
: is_server(false),
|
||||
is_dtls(false),
|
||||
resume(false),
|
||||
fallback_scsv(false),
|
||||
require_any_client_certificate(false),
|
||||
@@ -82,7 +85,8 @@ TestConfig::TestConfig()
|
||||
no_tls12(false),
|
||||
no_tls11(false),
|
||||
no_tls1(false),
|
||||
no_ssl3(false) {
|
||||
no_ssl3(false),
|
||||
cookie_exchange(false) {
|
||||
}
|
||||
|
||||
bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
|
||||
|
||||
@@ -22,6 +22,7 @@ struct TestConfig {
|
||||
TestConfig();
|
||||
|
||||
bool is_server;
|
||||
bool is_dtls;
|
||||
bool resume;
|
||||
bool fallback_scsv;
|
||||
std::string key_file;
|
||||
@@ -41,6 +42,7 @@ struct TestConfig {
|
||||
bool no_tls11;
|
||||
bool no_tls1;
|
||||
bool no_ssl3;
|
||||
bool cookie_exchange;
|
||||
};
|
||||
|
||||
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
|
||||
|
||||
Reference in New Issue
Block a user