Add initial DTLS tests.
Change-Id: I7407261bdb2d788c879f2e67e617a615d9ff8f8b
Reviewed-on: https://boringssl-review.googlesource.com/1505
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/CMakeLists.txt b/ssl/test/CMakeLists.txt
index 5cb7e80..27d9596 100644
--- a/ssl/test/CMakeLists.txt
+++ b/ssl/test/CMakeLists.txt
@@ -5,6 +5,7 @@
async_bio.cc
bssl_shim.cc
+ packeted_bio.cc
test_config.cc
)
diff --git a/ssl/test/async_bio.cc b/ssl/test/async_bio.cc
index f30f412..4861579 100644
--- a/ssl/test/async_bio.cc
+++ b/ssl/test/async_bio.cc
@@ -22,6 +22,7 @@
extern const BIO_METHOD async_bio_method;
struct async_bio {
+ bool datagram;
size_t read_quota;
size_t write_quota;
};
@@ -39,6 +40,12 @@
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 @@
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 @@
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 @@
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;
}
diff --git a/ssl/test/async_bio.h b/ssl/test/async_bio.h
index 7bec5fe..2904036 100644
--- a/ssl/test/async_bio.h
+++ b/ssl/test/async_bio.h
@@ -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
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 19c5f83..4f26bfb 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -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 @@
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 @@
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 @@
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 @@
}
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];
diff --git a/ssl/test/packeted_bio.cc b/ssl/test/packeted_bio.cc
new file mode 100644
index 0000000..629d6c5
--- /dev/null
+++ b/ssl/test/packeted_bio.cc
@@ -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);
+}
diff --git a/ssl/test/packeted_bio.h b/ssl/test/packeted_bio.h
new file mode 100644
index 0000000..384bd64
--- /dev/null
+++ b/ssl/test/packeted_bio.h
@@ -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
diff --git a/ssl/test/runner/packet_adapter.go b/ssl/test/runner/packet_adapter.go
new file mode 100644
index 0000000..b2f2765
--- /dev/null
+++ b/ssl/test/runner/packet_adapter.go
@@ -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
+}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 4fd5a1d..1d44f99 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -63,8 +63,16 @@
serverTest
)
+type protocol int
+
+const (
+ tls protocol = iota
+ dtls
+)
+
type testCase struct {
testType testType
+ protocol protocol
name string
config Config
shouldFail bool
@@ -341,18 +349,6 @@
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",
config: Config{
@@ -454,16 +450,30 @@
}
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 @@
}
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 @@
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 @@
}
}
+ if test.protocol == dtls {
+ flags = append(flags, "-dtls")
+ }
+
if test.resumeSession {
flags = append(flags, "-resume")
}
@@ -746,6 +775,36 @@
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 @@
// 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 @@
// 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 @@
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 @@
resumeSession: true,
})
testCases = append(testCases, testCase{
+ protocol: protocol,
testType: serverTest,
name: "Basic-Server" + suffix,
config: Config{
@@ -967,9 +1032,36 @@
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 @@
flags: flags,
})
testCases = append(testCases, testCase{
+ protocol: protocol,
testType: serverTest,
name: "SessionTicketsDisabled-Server" + suffix,
config: Config{
@@ -990,88 +1083,108 @@
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 @@
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)
+ }
}
}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index b25f7e6..9716227 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -34,6 +34,7 @@
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 @@
{ "-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 @@
TestConfig::TestConfig()
: is_server(false),
+ is_dtls(false),
resume(false),
fallback_scsv(false),
require_any_client_certificate(false),
@@ -82,7 +85,8 @@
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) {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 558d033..34d720e 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -22,6 +22,7 @@
TestConfig();
bool is_server;
+ bool is_dtls;
bool resume;
bool fallback_scsv;
std::string key_file;
@@ -41,6 +42,7 @@
bool no_tls11;
bool no_tls1;
bool no_ssl3;
+ bool cookie_exchange;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);