summaryrefslogtreecommitdiffstats
path: root/src/ssl/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl/test')
-rw-r--r--src/ssl/test/CMakeLists.txt3
-rw-r--r--src/ssl/test/async_bio.cc62
-rw-r--r--src/ssl/test/async_bio.h18
-rw-r--r--src/ssl/test/bssl_shim.cc1079
-rw-r--r--src/ssl/test/malloc.cc19
-rw-r--r--src/ssl/test/packeted_bio.cc145
-rw-r--r--src/ssl/test/packeted_bio.h28
-rw-r--r--src/ssl/test/runner/chacha20_poly1305.go159
-rw-r--r--src/ssl/test/runner/chacha20_poly1305_test.go99
-rw-r--r--src/ssl/test/runner/cipher_suites.go29
-rw-r--r--src/ssl/test/runner/common.go130
-rw-r--r--src/ssl/test/runner/conn.go178
-rw-r--r--src/ssl/test/runner/dtls.go288
-rw-r--r--src/ssl/test/runner/handshake_client.go111
-rw-r--r--src/ssl/test/runner/handshake_server.go75
-rw-r--r--src/ssl/test/runner/key_agreement.go81
-rw-r--r--src/ssl/test/runner/packet_adapter.go101
-rw-r--r--src/ssl/test/runner/poly1305.go1540
-rw-r--r--src/ssl/test/runner/prf.go4
-rw-r--r--src/ssl/test/runner/runner.go1074
-rw-r--r--src/ssl/test/runner/test_output.go79
-rw-r--r--src/ssl/test/scoped_types.h28
-rw-r--r--src/ssl/test/test_config.cc45
-rw-r--r--src/ssl/test/test_config.h69
24 files changed, 4540 insertions, 904 deletions
diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt
index 9992360..a0d7a5e 100644
--- a/src/ssl/test/CMakeLists.txt
+++ b/src/ssl/test/CMakeLists.txt
@@ -11,6 +11,3 @@ add_executable(
)
target_link_libraries(bssl_shim ssl crypto)
-if (NOT APPLE AND NOT WIN32)
- target_link_libraries(bssl_shim dl)
-endif()
diff --git a/src/ssl/test/async_bio.cc b/src/ssl/test/async_bio.cc
index c007ffa..0534845 100644
--- a/src/ssl/test/async_bio.cc
+++ b/src/ssl/test/async_bio.cc
@@ -22,23 +22,23 @@
namespace {
-extern const BIO_METHOD async_bio_method;
+extern const BIO_METHOD g_async_bio_method;
-struct async_bio {
+struct AsyncBio {
bool datagram;
size_t read_quota;
size_t write_quota;
};
-async_bio *get_data(BIO *bio) {
- if (bio->method != &async_bio_method) {
+AsyncBio *GetData(BIO *bio) {
+ if (bio->method != &g_async_bio_method) {
return NULL;
}
- return (async_bio *)bio->ptr;
+ return (AsyncBio *)bio->ptr;
}
-static int async_write(BIO *bio, const char *in, int inl) {
- async_bio *a = get_data(bio);
+static int AsyncWrite(BIO *bio, const char *in, int inl) {
+ AsyncBio *a = GetData(bio);
if (a == NULL || bio->next_bio == NULL) {
return 0;
}
@@ -69,8 +69,8 @@ static int async_write(BIO *bio, const char *in, int inl) {
return ret;
}
-static int async_read(BIO *bio, char *out, int outl) {
- async_bio *a = get_data(bio);
+static int AsyncRead(BIO *bio, char *out, int outl) {
+ AsyncBio *a = GetData(bio);
if (a == NULL || bio->next_bio == NULL) {
return 0;
}
@@ -95,7 +95,7 @@ static int async_read(BIO *bio, char *out, int outl) {
return ret;
}
-static long async_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
if (bio->next_bio == NULL) {
return 0;
}
@@ -105,8 +105,8 @@ static long async_ctrl(BIO *bio, int cmd, long num, void *ptr) {
return ret;
}
-static int async_new(BIO *bio) {
- async_bio *a = (async_bio *)OPENSSL_malloc(sizeof(*a));
+static int AsyncNew(BIO *bio) {
+ AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
if (a == NULL) {
return 0;
}
@@ -116,7 +116,7 @@ static int async_new(BIO *bio) {
return 1;
}
-static int async_free(BIO *bio) {
+static int AsyncFree(BIO *bio) {
if (bio == NULL) {
return 0;
}
@@ -128,51 +128,51 @@ static int async_free(BIO *bio) {
return 1;
}
-static long async_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+static long AsyncCallbackCtrl(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 async_bio_method = {
+const BIO_METHOD g_async_bio_method = {
BIO_TYPE_FILTER,
"async bio",
- async_write,
- async_read,
+ AsyncWrite,
+ AsyncRead,
NULL /* puts */,
NULL /* gets */,
- async_ctrl,
- async_new,
- async_free,
- async_callback_ctrl,
+ AsyncCtrl,
+ AsyncNew,
+ AsyncFree,
+ AsyncCallbackCtrl,
};
} // namespace
-BIO *async_bio_create() {
- return BIO_new(&async_bio_method);
+ScopedBIO AsyncBioCreate() {
+ return ScopedBIO(BIO_new(&g_async_bio_method));
}
-BIO *async_bio_create_datagram() {
- BIO *ret = BIO_new(&async_bio_method);
+ScopedBIO AsyncBioCreateDatagram() {
+ ScopedBIO ret(BIO_new(&g_async_bio_method));
if (!ret) {
- return NULL;
+ return nullptr;
}
- get_data(ret)->datagram = true;
+ GetData(ret.get())->datagram = true;
return ret;
}
-void async_bio_allow_read(BIO *bio, size_t count) {
- async_bio *a = get_data(bio);
+void AsyncBioAllowRead(BIO *bio, size_t count) {
+ AsyncBio *a = GetData(bio);
if (a == NULL) {
return;
}
a->read_quota += count;
}
-void async_bio_allow_write(BIO *bio, size_t count) {
- async_bio *a = get_data(bio);
+void AsyncBioAllowWrite(BIO *bio, size_t count) {
+ AsyncBio *a = GetData(bio);
if (a == NULL) {
return;
}
diff --git a/src/ssl/test/async_bio.h b/src/ssl/test/async_bio.h
index 2904036..1ccdf9b 100644
--- a/src/ssl/test/async_bio.h
+++ b/src/ssl/test/async_bio.h
@@ -17,24 +17,26 @@
#include <openssl/bio.h>
+#include "../../crypto/test/scoped_types.h"
-// async_bio_create creates a filter BIO for testing asynchronous state
+
+// AsyncBioCreate 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();
+ScopedBIO AsyncBioCreate();
-// async_bio_create_datagram creates a filter BIO for testing for
+// AsyncBioCreateDatagram 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();
+ScopedBIO AsyncBioCreateDatagram();
-// async_bio_allow_read increments |bio|'s read quota by |count|.
-void async_bio_allow_read(BIO *bio, size_t count);
+// AsyncBioAllowRead increments |bio|'s read quota by |count|.
+void AsyncBioAllowRead(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);
+// AsyncBioAllowWrite increments |bio|'s write quota by |count|.
+void AsyncBioAllowWrite(BIO *bio, size_t count);
#endif // HEADER_ASYNC_BIO
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 37891b9..1cf96f2 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -17,9 +17,19 @@
#if !defined(OPENSSL_WINDOWS)
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <signal.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
+#else
+#include <io.h>
+#pragma warning(push, 3)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma warning(pop)
+
+#pragma comment(lib, "Ws2_32.lib")
#endif
#include <string.h>
@@ -28,126 +38,198 @@
#include <openssl/bio.h>
#include <openssl/buf.h>
#include <openssl/bytestring.h>
+#include <openssl/err.h>
#include <openssl/ssl.h>
+#include <memory>
+#include <vector>
+
+#include "../../crypto/test/scoped_types.h"
#include "async_bio.h"
#include "packeted_bio.h"
+#include "scoped_types.h"
#include "test_config.h"
-static int usage(const char *program) {
- fprintf(stderr, "Usage: %s [flags...]\n",
- program);
+
+#if !defined(OPENSSL_WINDOWS)
+static int closesocket(int sock) {
+ return close(sock);
+}
+
+static void PrintSocketError(const char *func) {
+ perror(func);
+}
+#else
+static void PrintSocketError(const char *func) {
+ fprintf(stderr, "%s: %d\n", func, WSAGetLastError());
+}
+#endif
+
+static int Usage(const char *program) {
+ fprintf(stderr, "Usage: %s [flags...]\n", program);
return 1;
}
-static int g_ex_data_index = 0;
+struct TestState {
+ TestState() {
+ // MSVC cannot initialize these inline.
+ memset(&clock, 0, sizeof(clock));
+ memset(&clock_delta, 0, sizeof(clock_delta));
+ }
+
+ // async_bio is async BIO which pauses reads and writes.
+ BIO *async_bio = nullptr;
+ // clock is the current time for the SSL connection.
+ timeval clock;
+ // clock_delta is how far the clock advanced in the most recent failed
+ // |BIO_read|.
+ timeval clock_delta;
+ ScopedEVP_PKEY channel_id;
+ bool cert_ready = false;
+ ScopedSSL_SESSION session;
+ ScopedSSL_SESSION pending_session;
+ bool early_callback_called = false;
+ bool handshake_done = false;
+};
+
+static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+ int index, long argl, void *argp) {
+ delete ((TestState *)ptr);
+}
+
+static int g_config_index = 0;
+static int g_state_index = 0;
static bool SetConfigPtr(SSL *ssl, const TestConfig *config) {
- return SSL_set_ex_data(ssl, g_ex_data_index, (void *)config) == 1;
+ return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
}
-static const TestConfig *GetConfigPtr(SSL *ssl) {
- return (const TestConfig *)SSL_get_ex_data(ssl, g_ex_data_index);
+static const TestConfig *GetConfigPtr(const SSL *ssl) {
+ return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
}
-static EVP_PKEY *LoadPrivateKey(const std::string &file) {
- BIO *bio = BIO_new(BIO_s_file());
- if (bio == NULL) {
- return NULL;
+static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> async) {
+ if (SSL_set_ex_data(ssl, g_state_index, (void *)async.get()) == 1) {
+ async.release();
+ return true;
}
- if (!BIO_read_filename(bio, file.c_str())) {
- BIO_free(bio);
- return NULL;
+ return false;
+}
+
+static TestState *GetTestState(const SSL *ssl) {
+ return (TestState *)SSL_get_ex_data(ssl, g_state_index);
+}
+
+static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) {
+ ScopedBIO bio(BIO_new(BIO_s_file()));
+ if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
+ return nullptr;
}
- EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
- BIO_free(bio);
+ ScopedEVP_PKEY pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
return pkey;
}
-static int early_callback_called = 0;
-
-static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) {
- early_callback_called = 1;
+static bool InstallCertificate(SSL *ssl) {
+ const TestConfig *config = GetConfigPtr(ssl);
+ if (!config->key_file.empty() &&
+ !SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
+ SSL_FILETYPE_PEM)) {
+ return false;
+ }
+ if (!config->cert_file.empty() &&
+ !SSL_use_certificate_file(ssl, config->cert_file.c_str(),
+ SSL_FILETYPE_PEM)) {
+ return false;
+ }
+ return true;
+}
+static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
const TestConfig *config = GetConfigPtr(ctx->ssl);
+ GetTestState(ctx->ssl)->early_callback_called = true;
- if (config->expected_server_name.empty()) {
- return 1;
- }
+ if (!config->expected_server_name.empty()) {
+ const uint8_t *extension_data;
+ size_t extension_len;
+ CBS extension, server_name_list, host_name;
+ uint8_t name_type;
+
+ if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
+ &extension_data,
+ &extension_len)) {
+ fprintf(stderr, "Could not find server_name extension.\n");
+ return -1;
+ }
- const uint8_t *extension_data;
- size_t extension_len;
- CBS extension, server_name_list, host_name;
- uint8_t name_type;
+ CBS_init(&extension, extension_data, extension_len);
+ if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
+ CBS_len(&extension) != 0 ||
+ !CBS_get_u8(&server_name_list, &name_type) ||
+ name_type != TLSEXT_NAMETYPE_host_name ||
+ !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
+ CBS_len(&server_name_list) != 0) {
+ fprintf(stderr, "Could not decode server_name extension.\n");
+ return -1;
+ }
- if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
- &extension_data,
- &extension_len)) {
- fprintf(stderr, "Could not find server_name extension.\n");
- return -1;
+ if (!CBS_mem_equal(&host_name,
+ (const uint8_t*)config->expected_server_name.data(),
+ config->expected_server_name.size())) {
+ fprintf(stderr, "Server name mismatch.\n");
+ }
}
- CBS_init(&extension, extension_data, extension_len);
- if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
- CBS_len(&extension) != 0 ||
- !CBS_get_u8(&server_name_list, &name_type) ||
- name_type != TLSEXT_NAMETYPE_host_name ||
- !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
- CBS_len(&server_name_list) != 0) {
- fprintf(stderr, "Could not decode server_name extension.\n");
+ if (config->fail_early_callback) {
return -1;
}
- if (!CBS_mem_equal(&host_name,
- (const uint8_t*)config->expected_server_name.data(),
- config->expected_server_name.size())) {
- fprintf(stderr, "Server name mismatch.\n");
+ // Install the certificate in the early callback.
+ if (config->use_early_callback) {
+ if (config->async) {
+ // Install the certificate asynchronously.
+ return 0;
+ }
+ if (!InstallCertificate(ctx->ssl)) {
+ return -1;
+ }
}
-
return 1;
}
-static int skip_verify(int preverify_ok, X509_STORE_CTX *store_ctx) {
+static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) {
return 1;
}
-static int next_protos_advertised_callback(SSL *ssl,
- const uint8_t **out,
- unsigned int *out_len,
- void *arg) {
+static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
+ unsigned int *out_len, void *arg) {
const TestConfig *config = GetConfigPtr(ssl);
- if (config->advertise_npn.empty())
+ if (config->advertise_npn.empty()) {
return SSL_TLSEXT_ERR_NOACK;
+ }
*out = (const uint8_t*)config->advertise_npn.data();
*out_len = config->advertise_npn.size();
return SSL_TLSEXT_ERR_OK;
}
-static int next_proto_select_callback(SSL* ssl,
- uint8_t** out,
- uint8_t* outlen,
- const uint8_t* in,
- unsigned inlen,
- void* arg) {
+static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
+ const uint8_t* in, unsigned inlen, void* arg) {
const TestConfig *config = GetConfigPtr(ssl);
- if (config->select_next_proto.empty())
+ if (config->select_next_proto.empty()) {
return SSL_TLSEXT_ERR_NOACK;
+ }
*out = (uint8_t*)config->select_next_proto.data();
*outlen = config->select_next_proto.size();
return SSL_TLSEXT_ERR_OK;
}
-static int alpn_select_callback(SSL* ssl,
- const uint8_t** out,
- uint8_t* outlen,
- const uint8_t* in,
- unsigned inlen,
- void* arg) {
+static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,
+ const uint8_t* in, unsigned inlen, void* arg) {
const TestConfig *config = GetConfigPtr(ssl);
- if (config->select_alpn.empty())
+ if (config->select_alpn.empty()) {
return SSL_TLSEXT_ERR_NOACK;
+ }
if (!config->expected_advertised_alpn.empty() &&
(config->expected_advertised_alpn.size() != inlen ||
@@ -162,34 +244,10 @@ static int alpn_select_callback(SSL* ssl,
return SSL_TLSEXT_ERR_OK;
}
-static int cookie_generate_callback(SSL *ssl, uint8_t *cookie, size_t *cookie_len) {
- if (*cookie_len < 32) {
- fprintf(stderr, "Insufficient space for cookie\n");
- return 0;
- }
- *cookie_len = 32;
- memset(cookie, 42, *cookie_len);
- return 1;
-}
-
-static int cookie_verify_callback(SSL *ssl, const uint8_t *cookie, size_t 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 unsigned psk_client_callback(SSL *ssl, const char *hint,
- char *out_identity,
- unsigned max_identity_len,
- uint8_t *out_psk, unsigned max_psk_len) {
+static unsigned PskClientCallback(SSL *ssl, const char *hint,
+ char *out_identity,
+ unsigned max_identity_len,
+ uint8_t *out_psk, unsigned max_psk_len) {
const TestConfig *config = GetConfigPtr(ssl);
if (strcmp(hint ? hint : "", config->psk_identity.c_str()) != 0) {
@@ -210,8 +268,8 @@ static unsigned psk_client_callback(SSL *ssl, const char *hint,
return config->psk.size();
}
-static unsigned psk_server_callback(SSL *ssl, const char *identity,
- uint8_t *out_psk, unsigned max_psk_len) {
+static unsigned PskServerCallback(SSL *ssl, const char *identity,
+ uint8_t *out_psk, unsigned max_psk_len) {
const TestConfig *config = GetConfigPtr(ssl);
if (strcmp(identity, config->psk_identity.c_str()) != 0) {
@@ -228,13 +286,124 @@ static unsigned psk_server_callback(SSL *ssl, const char *identity,
return config->psk.size();
}
-static SSL_CTX *setup_ctx(const TestConfig *config) {
- SSL_CTX *ssl_ctx = NULL;
- DH *dh = NULL;
+static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
+ *out_clock = GetTestState(ssl)->clock;
+}
- ssl_ctx = SSL_CTX_new(config->is_dtls ? DTLS_method() : TLS_method());
- if (ssl_ctx == NULL) {
- goto err;
+static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
+ *out_pkey = GetTestState(ssl)->channel_id.release();
+}
+
+static int CertCallback(SSL *ssl, void *arg) {
+ if (!GetTestState(ssl)->cert_ready) {
+ return -1;
+ }
+ if (!InstallCertificate(ssl)) {
+ return 0;
+ }
+ return 1;
+}
+
+static SSL_SESSION *GetSessionCallback(SSL *ssl, uint8_t *data, int len,
+ int *copy) {
+ TestState *async_state = GetTestState(ssl);
+ if (async_state->session) {
+ *copy = 0;
+ return async_state->session.release();
+ } else if (async_state->pending_session) {
+ return SSL_magic_pending_session_ptr();
+ } else {
+ return NULL;
+ }
+}
+
+static int DDoSCallback(const struct ssl_early_callback_ctx *early_context) {
+ const TestConfig *config = GetConfigPtr(early_context->ssl);
+ static int callback_num = 0;
+
+ callback_num++;
+ if (config->fail_ddos_callback ||
+ (config->fail_second_ddos_callback && callback_num == 2)) {
+ return 0;
+ }
+ return 1;
+}
+
+static void InfoCallback(const SSL *ssl, int type, int val) {
+ if (type == SSL_CB_HANDSHAKE_DONE) {
+ if (GetConfigPtr(ssl)->handshake_never_done) {
+ fprintf(stderr, "handshake completed\n");
+ // Abort before any expected error code is printed, to ensure the overall
+ // test fails.
+ abort();
+ }
+ GetTestState(ssl)->handshake_done = true;
+ }
+}
+
+// Connect returns a new socket connected to localhost on |port| or -1 on
+// error.
+static int Connect(uint16_t port) {
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == -1) {
+ PrintSocketError("socket");
+ return -1;
+ }
+ int nodelay = 1;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<const char*>(&nodelay), sizeof(nodelay)) != 0) {
+ PrintSocketError("setsockopt");
+ closesocket(sock);
+ return -1;
+ }
+ sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ if (!inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr)) {
+ PrintSocketError("inet_pton");
+ closesocket(sock);
+ return -1;
+ }
+ if (connect(sock, reinterpret_cast<const sockaddr*>(&sin),
+ sizeof(sin)) != 0) {
+ PrintSocketError("connect");
+ closesocket(sock);
+ return -1;
+ }
+ return sock;
+}
+
+class SocketCloser {
+ public:
+ explicit SocketCloser(int sock) : sock_(sock) {}
+ ~SocketCloser() {
+ // Half-close and drain the socket before releasing it. This seems to be
+ // necessary for graceful shutdown on Windows. It will also avoid write
+ // failures in the test runner.
+#if defined(OPENSSL_WINDOWS)
+ shutdown(sock_, SD_SEND);
+#else
+ shutdown(sock_, SHUT_WR);
+#endif
+ while (true) {
+ char buf[1024];
+ if (recv(sock_, buf, sizeof(buf), 0) <= 0) {
+ break;
+ }
+ }
+ closesocket(sock_);
+ }
+
+ private:
+ const int sock_;
+};
+
+static ScopedSSL_CTX SetupCtx(const TestConfig *config) {
+ ScopedSSL_CTX ssl_ctx(SSL_CTX_new(
+ config->is_dtls ? DTLS_method() : TLS_method()));
+ if (!ssl_ctx) {
+ return nullptr;
}
if (config->is_dtls) {
@@ -242,376 +411,473 @@ static SSL_CTX *setup_ctx(const TestConfig *config) {
//
// TODO(davidben): this should not be necessary. DTLS code should only
// expect a datagram BIO.
- SSL_CTX_set_read_ahead(ssl_ctx, 1);
+ SSL_CTX_set_read_ahead(ssl_ctx.get(), 1);
}
- if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) {
- goto err;
+ if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) {
+ return nullptr;
}
- if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) {
- goto err;
+ ScopedDH dh(DH_get_2048_256(NULL));
+ if (!dh || !SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) {
+ return nullptr;
}
- dh = DH_get_2048_256(NULL);
- if (dh == NULL ||
- !SSL_CTX_set_tmp_dh(ssl_ctx, dh)) {
- goto err;
+ if (config->async && config->is_server) {
+ // Disable the internal session cache. To test asynchronous session lookup,
+ // we use an external session cache.
+ SSL_CTX_set_session_cache_mode(
+ ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback);
+ } else {
+ SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH);
}
- SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
-
- ssl_ctx->select_certificate_cb = select_certificate_callback;
+ ssl_ctx->select_certificate_cb = SelectCertificateCallback;
SSL_CTX_set_next_protos_advertised_cb(
- ssl_ctx, next_protos_advertised_callback, NULL);
+ ssl_ctx.get(), NextProtosAdvertisedCallback, NULL);
if (!config->select_next_proto.empty()) {
- SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, NULL);
+ SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback,
+ NULL);
}
if (!config->select_alpn.empty()) {
- SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_callback, NULL);
+ SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
}
- SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
- SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
-
ssl_ctx->tlsext_channel_id_enabled_new = 1;
+ SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
- DH_free(dh);
- return ssl_ctx;
+ ssl_ctx->current_time_cb = CurrentTimeCallback;
- err:
- if (dh != NULL) {
- DH_free(dh);
- }
- if (ssl_ctx != NULL) {
- SSL_CTX_free(ssl_ctx);
- }
- return NULL;
+ SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
+
+ return ssl_ctx;
}
-static int retry_async(SSL *ssl, int ret, BIO *bio) {
+// RetryAsync is called after a failed operation on |ssl| with return code
+// |ret|. If the operation should be retried, it simulates one asynchronous
+// event and returns true. Otherwise it returns false.
+static bool RetryAsync(SSL *ssl, int ret) {
// No error; don't retry.
if (ret >= 0) {
- return 0;
+ return false;
+ }
+
+ TestState *test_state = GetTestState(ssl);
+ if (test_state->clock_delta.tv_usec != 0 ||
+ test_state->clock_delta.tv_sec != 0) {
+ // Process the timeout and retry.
+ test_state->clock.tv_usec += test_state->clock_delta.tv_usec;
+ test_state->clock.tv_sec += test_state->clock.tv_usec / 1000000;
+ test_state->clock.tv_usec %= 1000000;
+ test_state->clock.tv_sec += test_state->clock_delta.tv_sec;
+ memset(&test_state->clock_delta, 0, sizeof(test_state->clock_delta));
+
+ if (DTLSv1_handle_timeout(ssl) < 0) {
+ fprintf(stderr, "Error retransmitting.\n");
+ return false;
+ }
+ return true;
}
+
// See if we needed to read or write more. If so, allow one byte through on
// the appropriate end to maximally stress the state machine.
- int err = SSL_get_error(ssl, ret);
- if (err == SSL_ERROR_WANT_READ) {
- async_bio_allow_read(bio, 1);
- return 1;
- } else if (err == SSL_ERROR_WANT_WRITE) {
- async_bio_allow_write(bio, 1);
- return 1;
+ switch (SSL_get_error(ssl, ret)) {
+ case SSL_ERROR_WANT_READ:
+ AsyncBioAllowRead(test_state->async_bio, 1);
+ return true;
+ case SSL_ERROR_WANT_WRITE:
+ AsyncBioAllowWrite(test_state->async_bio, 1);
+ return true;
+ case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
+ ScopedEVP_PKEY pkey = LoadPrivateKey(GetConfigPtr(ssl)->send_channel_id);
+ if (!pkey) {
+ return false;
+ }
+ test_state->channel_id = std::move(pkey);
+ return true;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ test_state->cert_ready = true;
+ return true;
+ case SSL_ERROR_PENDING_SESSION:
+ test_state->session = std::move(test_state->pending_session);
+ return true;
+ case SSL_ERROR_PENDING_CERTIFICATE:
+ // The handshake will resume without a second call to the early callback.
+ return InstallCertificate(ssl);
+ default:
+ return false;
}
- return 0;
}
-static int do_exchange(SSL_SESSION **out_session,
- SSL_CTX *ssl_ctx,
- const TestConfig *config,
- bool is_resume,
- int fd,
- SSL_SESSION *session) {
- early_callback_called = 0;
+// DoRead reads from |ssl|, resolving any asynchronous operations. It returns
+// the result value of the final |SSL_read| call.
+static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
+ const TestConfig *config = GetConfigPtr(ssl);
+ int ret;
+ do {
+ ret = SSL_read(ssl, out, max_out);
+ } while (config->async && RetryAsync(ssl, ret));
+ return ret;
+}
- SSL *ssl = SSL_new(ssl_ctx);
- if (ssl == NULL) {
- BIO_print_errors_fp(stdout);
- return 1;
- }
+// WriteAll writes |in_len| bytes from |in| to |ssl|, resolving any asynchronous
+// operations. It returns the result of the final |SSL_write| call.
+static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) {
+ const TestConfig *config = GetConfigPtr(ssl);
+ int ret;
+ do {
+ ret = SSL_write(ssl, in, in_len);
+ if (ret > 0) {
+ in += ret;
+ in_len -= ret;
+ }
+ } while ((config->async && RetryAsync(ssl, ret)) || (ret > 0 && in_len > 0));
+ return ret;
+}
- if (!SetConfigPtr(ssl, config)) {
- BIO_print_errors_fp(stdout);
- return 1;
+// DoExchange runs a test SSL exchange against the peer. On success, it returns
+// true and sets |*out_session| to the negotiated SSL session. If the test is a
+// resumption attempt, |is_resume| is true and |session| is the session from the
+// previous exchange.
+static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
+ const TestConfig *config, bool is_resume,
+ SSL_SESSION *session) {
+ ScopedSSL ssl(SSL_new(ssl_ctx));
+ if (!ssl) {
+ return false;
}
- if (config->fallback_scsv) {
- if (!SSL_enable_fallback_scsv(ssl)) {
- BIO_print_errors_fp(stdout);
- return 1;
- }
+ if (!SetConfigPtr(ssl.get(), config) ||
+ !SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) {
+ return false;
}
- if (!config->key_file.empty()) {
- if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
- SSL_FILETYPE_PEM)) {
- BIO_print_errors_fp(stdout);
- return 1;
- }
+
+ if (config->fallback_scsv &&
+ !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
+ return false;
}
- if (!config->cert_file.empty()) {
- if (!SSL_use_certificate_file(ssl, config->cert_file.c_str(),
- SSL_FILETYPE_PEM)) {
- BIO_print_errors_fp(stdout);
- return 1;
+ if (!config->use_early_callback) {
+ if (config->async) {
+ // TODO(davidben): Also test |s->ctx->client_cert_cb| on the client.
+ SSL_set_cert_cb(ssl.get(), CertCallback, NULL);
+ } else if (!InstallCertificate(ssl.get())) {
+ return false;
}
}
if (config->require_any_client_certificate) {
- SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- skip_verify);
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ SkipVerify);
}
if (config->false_start) {
- SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
+ SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
}
if (config->cbc_record_splitting) {
- SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING);
+ SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING);
}
if (config->partial_write) {
- SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
}
if (config->no_tls12) {
- SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
}
if (config->no_tls11) {
- SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1);
}
if (config->no_tls1) {
- SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1);
}
if (config->no_ssl3) {
- SSL_set_options(ssl, SSL_OP_NO_SSLv3);
- }
- if (config->cookie_exchange) {
- SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
+ SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3);
}
if (config->tls_d5_bug) {
- SSL_set_options(ssl, SSL_OP_TLS_D5_BUG);
+ SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG);
}
if (config->allow_unsafe_legacy_renegotiation) {
- SSL_set_options(ssl, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
if (!config->expected_channel_id.empty()) {
- SSL_enable_tls_channel_id(ssl);
+ SSL_enable_tls_channel_id(ssl.get());
}
if (!config->send_channel_id.empty()) {
- EVP_PKEY *pkey = LoadPrivateKey(config->send_channel_id);
- if (pkey == NULL) {
- BIO_print_errors_fp(stdout);
- return 1;
- }
- SSL_enable_tls_channel_id(ssl);
- if (!SSL_set1_tls_channel_id(ssl, pkey)) {
- EVP_PKEY_free(pkey);
- BIO_print_errors_fp(stdout);
- return 1;
+ SSL_enable_tls_channel_id(ssl.get());
+ if (!config->async) {
+ // The async case will be supplied by |ChannelIdCallback|.
+ ScopedEVP_PKEY pkey = LoadPrivateKey(config->send_channel_id);
+ if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
+ return false;
+ }
}
- EVP_PKEY_free(pkey);
}
- if (!config->host_name.empty()) {
- SSL_set_tlsext_host_name(ssl, config->host_name.c_str());
+ if (!config->host_name.empty() &&
+ !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) {
+ return false;
}
- if (!config->advertise_alpn.empty()) {
- SSL_set_alpn_protos(ssl, (const uint8_t *)config->advertise_alpn.data(),
- config->advertise_alpn.size());
+ if (!config->advertise_alpn.empty() &&
+ SSL_set_alpn_protos(ssl.get(),
+ (const uint8_t *)config->advertise_alpn.data(),
+ config->advertise_alpn.size()) != 0) {
+ return false;
}
if (!config->psk.empty()) {
- SSL_set_psk_client_callback(ssl, psk_client_callback);
- SSL_set_psk_server_callback(ssl, psk_server_callback);
+ SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
+ SSL_set_psk_server_callback(ssl.get(), PskServerCallback);
}
if (!config->psk_identity.empty() &&
- !SSL_use_psk_identity_hint(ssl, config->psk_identity.c_str())) {
- BIO_print_errors_fp(stdout);
- return 1;
+ !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) {
+ return false;
}
if (!config->srtp_profiles.empty() &&
- !SSL_set_srtp_profiles(ssl, config->srtp_profiles.c_str())) {
- BIO_print_errors_fp(stdout);
- return 1;
+ !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) {
+ return false;
}
if (config->enable_ocsp_stapling &&
- !SSL_enable_ocsp_stapling(ssl)) {
- BIO_print_errors_fp(stdout);
- return 1;
+ !SSL_enable_ocsp_stapling(ssl.get())) {
+ return false;
}
if (config->enable_signed_cert_timestamps &&
- !SSL_enable_signed_cert_timestamps(ssl)) {
- BIO_print_errors_fp(stdout);
- return 1;
+ !SSL_enable_signed_cert_timestamps(ssl.get())) {
+ return false;
}
- SSL_enable_fastradio_padding(ssl, config->fastradio_padding);
+ SSL_enable_fastradio_padding(ssl.get(), config->fastradio_padding);
if (config->min_version != 0) {
- SSL_set_min_version(ssl, (uint16_t)config->min_version);
+ SSL_set_min_version(ssl.get(), (uint16_t)config->min_version);
}
if (config->max_version != 0) {
- SSL_set_max_version(ssl, (uint16_t)config->max_version);
+ SSL_set_max_version(ssl.get(), (uint16_t)config->max_version);
}
if (config->mtu != 0) {
- SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU);
- SSL_set_mtu(ssl, config->mtu);
+ SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
+ SSL_set_mtu(ssl.get(), config->mtu);
+ }
+ if (config->install_ddos_callback) {
+ SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
+ }
+ if (!config->cipher.empty() &&
+ !SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) {
+ return false;
+ }
+ if (config->reject_peer_renegotiations) {
+ SSL_set_reject_peer_renegotiations(ssl.get(), 1);
}
- BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
- if (bio == NULL) {
- BIO_print_errors_fp(stdout);
- return 1;
+ int sock = Connect(config->port);
+ if (sock == -1) {
+ return false;
+ }
+ SocketCloser closer(sock);
+
+ ScopedBIO bio(BIO_new_socket(sock, BIO_NOCLOSE));
+ if (!bio) {
+ return false;
}
if (config->is_dtls) {
- BIO *packeted = packeted_bio_create();
- BIO_push(packeted, bio);
- bio = packeted;
+ ScopedBIO packeted =
+ PacketedBioCreate(&GetTestState(ssl.get())->clock_delta);
+ BIO_push(packeted.get(), bio.release());
+ bio = std::move(packeted);
}
if (config->async) {
- BIO *async =
- config->is_dtls ? async_bio_create_datagram() : async_bio_create();
- BIO_push(async, bio);
- bio = async;
+ ScopedBIO async_scoped =
+ config->is_dtls ? AsyncBioCreateDatagram() : AsyncBioCreate();
+ BIO_push(async_scoped.get(), bio.release());
+ GetTestState(ssl.get())->async_bio = async_scoped.get();
+ bio = std::move(async_scoped);
}
- SSL_set_bio(ssl, bio, bio);
+ SSL_set_bio(ssl.get(), bio.get(), bio.get());
+ bio.release(); // SSL_set_bio takes ownership.
if (session != NULL) {
- if (SSL_set_session(ssl, session) != 1) {
- fprintf(stderr, "failed to set session\n");
- return 2;
+ if (!config->is_server) {
+ if (SSL_set_session(ssl.get(), session) != 1) {
+ return false;
+ }
+ } else if (config->async) {
+ // The internal session cache is disabled, so install the session
+ // manually.
+ GetTestState(ssl.get())->pending_session.reset(
+ SSL_SESSION_up_ref(session));
}
}
int ret;
- do {
+ if (config->implicit_handshake) {
if (config->is_server) {
- ret = SSL_accept(ssl);
+ SSL_set_accept_state(ssl.get());
} else {
- ret = SSL_connect(ssl);
+ SSL_set_connect_state(ssl.get());
+ }
+ } else {
+ do {
+ if (config->is_server) {
+ ret = SSL_accept(ssl.get());
+ } else {
+ ret = SSL_connect(ssl.get());
+ }
+ } while (config->async && RetryAsync(ssl.get(), ret));
+ if (ret != 1) {
+ return false;
}
- } while (config->async && retry_async(ssl, ret, bio));
- if (ret != 1) {
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 2;
- }
- if (is_resume && (!!SSL_session_reused(ssl) == config->expect_session_miss)) {
- fprintf(stderr, "session was%s reused\n",
- SSL_session_reused(ssl) ? "" : " not");
- return 2;
- }
+ if (is_resume &&
+ (!!SSL_session_reused(ssl.get()) == config->expect_session_miss)) {
+ fprintf(stderr, "session was%s reused\n",
+ SSL_session_reused(ssl.get()) ? "" : " not");
+ return false;
+ }
- if (!config->expected_server_name.empty()) {
- const char *server_name =
- SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (server_name != config->expected_server_name) {
- fprintf(stderr, "servername mismatch (got %s; want %s)\n",
- server_name, config->expected_server_name.c_str());
- return 2;
+ bool expect_handshake_done = is_resume || !config->false_start;
+ if (expect_handshake_done != GetTestState(ssl.get())->handshake_done) {
+ fprintf(stderr, "handshake was%s completed\n",
+ GetTestState(ssl.get())->handshake_done ? "" : " not");
+ return false;
}
- if (!early_callback_called) {
+ if (config->is_server && !GetTestState(ssl.get())->early_callback_called) {
fprintf(stderr, "early callback not called\n");
- return 2;
+ return false;
}
- }
- if (!config->expected_certificate_types.empty()) {
- uint8_t *certificate_types;
- int num_certificate_types =
- SSL_get0_certificate_types(ssl, &certificate_types);
- if (num_certificate_types !=
- (int)config->expected_certificate_types.size() ||
- memcmp(certificate_types,
- config->expected_certificate_types.data(),
- num_certificate_types) != 0) {
- fprintf(stderr, "certificate types mismatch\n");
- return 2;
+ if (!config->expected_server_name.empty()) {
+ const char *server_name =
+ SSL_get_servername(ssl.get(), TLSEXT_NAMETYPE_host_name);
+ if (server_name != config->expected_server_name) {
+ fprintf(stderr, "servername mismatch (got %s; want %s)\n",
+ server_name, config->expected_server_name.c_str());
+ return false;
+ }
}
- }
- if (!config->expected_next_proto.empty()) {
- const uint8_t *next_proto;
- unsigned next_proto_len;
- SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
- if (next_proto_len != config->expected_next_proto.size() ||
- memcmp(next_proto, config->expected_next_proto.data(),
- next_proto_len) != 0) {
- fprintf(stderr, "negotiated next proto mismatch\n");
- return 2;
+ if (!config->expected_certificate_types.empty()) {
+ uint8_t *certificate_types;
+ int num_certificate_types =
+ SSL_get0_certificate_types(ssl.get(), &certificate_types);
+ if (num_certificate_types !=
+ (int)config->expected_certificate_types.size() ||
+ memcmp(certificate_types,
+ config->expected_certificate_types.data(),
+ num_certificate_types) != 0) {
+ fprintf(stderr, "certificate types mismatch\n");
+ return false;
+ }
}
- }
- if (!config->expected_alpn.empty()) {
- const uint8_t *alpn_proto;
- unsigned alpn_proto_len;
- SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len);
- if (alpn_proto_len != config->expected_alpn.size() ||
- memcmp(alpn_proto, config->expected_alpn.data(),
- alpn_proto_len) != 0) {
- fprintf(stderr, "negotiated alpn proto mismatch\n");
- return 2;
+ if (!config->expected_next_proto.empty()) {
+ const uint8_t *next_proto;
+ unsigned next_proto_len;
+ SSL_get0_next_proto_negotiated(ssl.get(), &next_proto, &next_proto_len);
+ if (next_proto_len != config->expected_next_proto.size() ||
+ memcmp(next_proto, config->expected_next_proto.data(),
+ next_proto_len) != 0) {
+ fprintf(stderr, "negotiated next proto mismatch\n");
+ return false;
+ }
}
- }
- if (!config->expected_channel_id.empty()) {
- uint8_t channel_id[64];
- if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
- fprintf(stderr, "no channel id negotiated\n");
- return 2;
+ if (!config->expected_alpn.empty()) {
+ const uint8_t *alpn_proto;
+ unsigned alpn_proto_len;
+ SSL_get0_alpn_selected(ssl.get(), &alpn_proto, &alpn_proto_len);
+ if (alpn_proto_len != config->expected_alpn.size() ||
+ memcmp(alpn_proto, config->expected_alpn.data(),
+ alpn_proto_len) != 0) {
+ fprintf(stderr, "negotiated alpn proto mismatch\n");
+ return false;
+ }
}
- if (config->expected_channel_id.size() != 64 ||
- memcmp(config->expected_channel_id.data(),
- channel_id, 64) != 0) {
- fprintf(stderr, "channel id mismatch\n");
- return 2;
+
+ if (!config->expected_channel_id.empty()) {
+ uint8_t channel_id[64];
+ if (!SSL_get_tls_channel_id(ssl.get(), channel_id, sizeof(channel_id))) {
+ fprintf(stderr, "no channel id negotiated\n");
+ return false;
+ }
+ if (config->expected_channel_id.size() != 64 ||
+ memcmp(config->expected_channel_id.data(),
+ channel_id, 64) != 0) {
+ fprintf(stderr, "channel id mismatch\n");
+ return false;
+ }
}
- }
- if (config->expect_extended_master_secret) {
- if (!ssl->session->extended_master_secret) {
- fprintf(stderr, "No EMS for session when expected");
- return 2;
+ if (config->expect_extended_master_secret) {
+ if (!ssl->session->extended_master_secret) {
+ fprintf(stderr, "No EMS for session when expected");
+ return false;
+ }
}
- }
- if (!config->expected_ocsp_response.empty()) {
- const uint8_t *data;
- size_t len;
- SSL_get0_ocsp_response(ssl, &data, &len);
- if (config->expected_ocsp_response.size() != len ||
- memcmp(config->expected_ocsp_response.data(), data, len) != 0) {
- fprintf(stderr, "OCSP response mismatch\n");
- return 2;
+ if (!config->expected_ocsp_response.empty()) {
+ const uint8_t *data;
+ size_t len;
+ SSL_get0_ocsp_response(ssl.get(), &data, &len);
+ if (config->expected_ocsp_response.size() != len ||
+ memcmp(config->expected_ocsp_response.data(), data, len) != 0) {
+ fprintf(stderr, "OCSP response mismatch\n");
+ return false;
+ }
}
- }
- if (!config->expected_signed_cert_timestamps.empty()) {
- const uint8_t *data;
- size_t len;
- SSL_get0_signed_cert_timestamp_list(ssl, &data, &len);
- if (config->expected_signed_cert_timestamps.size() != len ||
- memcmp(config->expected_signed_cert_timestamps.data(),
- data, len) != 0) {
- fprintf(stderr, "SCT list mismatch\n");
- return 2;
+ if (!config->expected_signed_cert_timestamps.empty()) {
+ const uint8_t *data;
+ size_t len;
+ SSL_get0_signed_cert_timestamp_list(ssl.get(), &data, &len);
+ if (config->expected_signed_cert_timestamps.size() != len ||
+ memcmp(config->expected_signed_cert_timestamps.data(),
+ data, len) != 0) {
+ fprintf(stderr, "SCT list mismatch\n");
+ return false;
+ }
}
}
if (config->renegotiate) {
if (config->async) {
- fprintf(stderr, "--renegotiate is not supported with --async.\n");
- return 2;
+ fprintf(stderr, "-renegotiate is not supported with -async.\n");
+ return false;
+ }
+ if (config->implicit_handshake) {
+ fprintf(stderr, "-renegotiate is not supported with -implicit-handshake.\n");
+ return false;
}
- SSL_renegotiate(ssl);
+ SSL_renegotiate(ssl.get());
- ret = SSL_do_handshake(ssl);
+ ret = SSL_do_handshake(ssl.get());
if (ret != 1) {
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 2;
+ return false;
}
- SSL_set_state(ssl, SSL_ST_ACCEPT);
- ret = SSL_do_handshake(ssl);
+ SSL_set_state(ssl.get(), SSL_ST_ACCEPT);
+ ret = SSL_do_handshake(ssl.get());
if (ret != 1) {
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 2;
+ return false;
+ }
+ }
+
+ if (config->export_keying_material > 0) {
+ std::vector<uint8_t> result(
+ static_cast<size_t>(config->export_keying_material));
+ if (!SSL_export_keying_material(
+ ssl.get(), result.data(), result.size(),
+ config->export_label.data(), config->export_label.size(),
+ reinterpret_cast<const uint8_t*>(config->export_context.data()),
+ config->export_context.size(), config->use_export_context)) {
+ fprintf(stderr, "failed to export keying material\n");
+ return false;
+ }
+ if (WriteAll(ssl.get(), result.data(), result.size()) < 0) {
+ return false;
}
}
if (config->write_different_record_sizes) {
if (config->is_dtls) {
fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
- return 6;
+ return false;
}
// This mode writes a number of different record sizes in an attempt to
// trip up the CBC record splitting code.
@@ -621,138 +887,123 @@ static int do_exchange(SSL_SESSION **out_session,
0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]);
i++) {
- int w;
const size_t len = kRecordSizes[i];
- size_t off = 0;
-
if (len > sizeof(buf)) {
fprintf(stderr, "Bad kRecordSizes value.\n");
- return 5;
+ return false;
}
-
- do {
- w = SSL_write(ssl, buf + off, len - off);
- if (w > 0) {
- off += (size_t) w;
- }
- } while ((config->async && retry_async(ssl, w, bio)) ||
- (w > 0 && off < len));
-
- if (w < 0 || off != len) {
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 4;
+ if (WriteAll(ssl.get(), buf, len) < 0) {
+ return false;
}
}
} else {
if (config->shim_writes_first) {
- int w;
- do {
- w = SSL_write(ssl, "hello", 5);
- } while (config->async && retry_async(ssl, w, bio));
+ if (WriteAll(ssl.get(), reinterpret_cast<const uint8_t *>("hello"),
+ 5) < 0) {
+ return false;
+ }
}
for (;;) {
uint8_t buf[512];
- int n;
- do {
- n = SSL_read(ssl, buf, sizeof(buf));
- } while (config->async && retry_async(ssl, n, bio));
- int err = SSL_get_error(ssl, n);
+ int n = DoRead(ssl.get(), buf, sizeof(buf));
+ int err = SSL_get_error(ssl.get(), n);
if (err == SSL_ERROR_ZERO_RETURN ||
(n == 0 && err == SSL_ERROR_SYSCALL)) {
if (n != 0) {
fprintf(stderr, "Invalid SSL_get_error output\n");
- return 3;
+ return false;
}
- /* Accept shutdowns with or without close_notify.
- * TODO(davidben): Write tests which distinguish these two cases. */
+ // Accept shutdowns with or without close_notify.
+ // TODO(davidben): Write tests which distinguish these two cases.
break;
} else if (err != SSL_ERROR_NONE) {
if (n > 0) {
fprintf(stderr, "Invalid SSL_get_error output\n");
- return 3;
+ return false;
}
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 3;
+ return false;
}
- /* Successfully read data. */
+ // Successfully read data.
if (n <= 0) {
fprintf(stderr, "Invalid SSL_get_error output\n");
- return 3;
+ return false;
}
+
+ // After a successful read, with or without False Start, the handshake
+ // must be complete.
+ if (!GetTestState(ssl.get())->handshake_done) {
+ fprintf(stderr, "handshake was not completed after SSL_read\n");
+ return false;
+ }
+
for (int i = 0; i < n; i++) {
buf[i] ^= 0xff;
}
- int w;
- do {
- w = SSL_write(ssl, buf, n);
- } while (config->async && retry_async(ssl, w, bio));
- if (w != n) {
- SSL_free(ssl);
- BIO_print_errors_fp(stdout);
- return 4;
+ if (WriteAll(ssl.get(), buf, n) < 0) {
+ return false;
}
}
}
if (out_session) {
- *out_session = SSL_get1_session(ssl);
+ out_session->reset(SSL_get1_session(ssl.get()));
}
- SSL_shutdown(ssl);
- SSL_free(ssl);
- return 0;
+ SSL_shutdown(ssl.get());
+ return true;
}
int main(int argc, char **argv) {
-#if !defined(OPENSSL_WINDOWS)
+#if defined(OPENSSL_WINDOWS)
+ /* Initialize Winsock. */
+ WORD wsa_version = MAKEWORD(2, 2);
+ WSADATA wsa_data;
+ int wsa_err = WSAStartup(wsa_version, &wsa_data);
+ if (wsa_err != 0) {
+ fprintf(stderr, "WSAStartup failed: %d\n", wsa_err);
+ return 1;
+ }
+ if (wsa_data.wVersion != wsa_version) {
+ fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion);
+ return 1;
+ }
+#else
signal(SIGPIPE, SIG_IGN);
#endif
if (!SSL_library_init()) {
return 1;
}
- g_ex_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- if (g_ex_data_index < 0) {
+ g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree);
+ if (g_config_index < 0 || g_state_index < 0) {
return 1;
}
TestConfig config;
if (!ParseConfig(argc - 1, argv + 1, &config)) {
- return usage(argv[0]);
+ return Usage(argv[0]);
}
- SSL_CTX *ssl_ctx = setup_ctx(&config);
- if (ssl_ctx == NULL) {
- BIO_print_errors_fp(stdout);
+ ScopedSSL_CTX ssl_ctx = SetupCtx(&config);
+ if (!ssl_ctx) {
+ ERR_print_errors_fp(stderr);
return 1;
}
- SSL_SESSION *session = NULL;
- int ret = do_exchange(&session,
- ssl_ctx, &config,
- false /* is_resume */,
- 3 /* fd */, NULL /* session */);
- if (ret != 0) {
- goto out;
+ ScopedSSL_SESSION session;
+ if (!DoExchange(&session, ssl_ctx.get(), &config, false /* is_resume */,
+ NULL /* session */)) {
+ ERR_print_errors_fp(stderr);
+ return 1;
}
- if (config.resume) {
- ret = do_exchange(NULL,
- ssl_ctx, &config,
- true /* is_resume */,
- 4 /* fd */,
- config.is_server ? NULL : session);
- if (ret != 0) {
- goto out;
- }
+ if (config.resume &&
+ !DoExchange(NULL, ssl_ctx.get(), &config, true /* is_resume */,
+ session.get())) {
+ ERR_print_errors_fp(stderr);
+ return 1;
}
- ret = 0;
-
-out:
- SSL_SESSION_free(session);
- SSL_CTX_free(ssl_ctx);
- return ret;
+ return 0;
}
diff --git a/src/ssl/test/malloc.cc b/src/ssl/test/malloc.cc
index 6cc0b33..2ec5582 100644
--- a/src/ssl/test/malloc.cc
+++ b/src/ssl/test/malloc.cc
@@ -14,15 +14,24 @@
#include <openssl/base.h>
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define OPENSSL_ASAN
+#endif
+#endif
+
// This file isn't built on ARM or Aarch64 because we link statically in those
-// builds and trying to override malloc in a static link doesn't work.
-#if defined(__linux__) && !defined(OPENSSL_ARM) && !defined(OPENSSL_AARCH64)
+// builds and trying to override malloc in a static link doesn't work. It's also
+// disabled on ASan builds as this interferes with ASan's malloc interceptor.
+//
+// TODO(davidben): See if this and ASan's interceptors can be made to coexist.
+#if defined(__linux__) && !defined(OPENSSL_ARM) && \
+ !defined(OPENSSL_AARCH64) && !defined(OPENSSL_ASAN)
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <unistd.h>
-#include <stdio.h>
#include <new>
@@ -127,4 +136,4 @@ void *realloc(void *ptr, size_t size) {
} // extern "C"
-#endif /* defined(linux) && !ARM && !AARCH64 */
+#endif /* defined(linux) && !ARM && !AARCH64 && !ASAN */
diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc
index 93b2164..e831082 100644
--- a/src/ssl/test/packeted_bio.cc
+++ b/src/ssl/test/packeted_bio.cc
@@ -15,7 +15,8 @@
#include "packeted_bio.h"
#include <assert.h>
-#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
#include <string.h>
#include <openssl/mem.h>
@@ -23,58 +24,135 @@
namespace {
-extern const BIO_METHOD packeted_bio_method;
+extern const BIO_METHOD g_packeted_bio_method;
+
+const uint8_t kOpcodePacket = 'P';
+const uint8_t kOpcodeTimeout = 'T';
+const uint8_t kOpcodeTimeoutAck = 't';
+
+// ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
+// 0 or -1 on error.
+static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
+ while (len > 0) {
+ int chunk_len = INT_MAX;
+ if (len <= INT_MAX) {
+ chunk_len = (int)len;
+ }
+ int ret = BIO_read(bio, out, chunk_len);
+ if (ret <= 0) {
+ return ret;
+ }
+ out += ret;
+ len -= ret;
+ }
+ return 1;
+}
-static int packeted_write(BIO *bio, const char *in, int inl) {
+static int PacketedWrite(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));
+ // Write the header.
+ uint8_t header[5];
+ header[0] = kOpcodePacket;
+ header[1] = (inl >> 24) & 0xff;
+ header[2] = (inl >> 16) & 0xff;
+ header[3] = (inl >> 8) & 0xff;
+ header[4] = inl & 0xff;
+ int ret = BIO_write(bio->next_bio, header, sizeof(header));
if (ret <= 0) {
BIO_copy_next_retry(bio);
return ret;
}
- // Write the buffer. BIOs for which this operation fails are not supported.
+ // Write the buffer.
ret = BIO_write(bio->next_bio, in, inl);
+ if (ret < 0 || (inl > 0 && ret == 0)) {
+ BIO_copy_next_retry(bio);
+ return ret;
+ }
assert(ret == inl);
return ret;
}
-static int packeted_read(BIO *bio, char *out, int outl) {
+static int PacketedRead(BIO *bio, char *out, int outl) {
if (bio->next_bio == NULL) {
return 0;
}
BIO_clear_retry_flags(bio);
+ // Read the opcode.
+ uint8_t opcode;
+ int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
+ if (ret <= 0) {
+ BIO_copy_next_retry(bio);
+ return ret;
+ }
+
+ if (opcode == kOpcodeTimeout) {
+ // Process the timeout.
+ uint8_t buf[8];
+ ret = ReadAll(bio->next_bio, buf, sizeof(buf));
+ if (ret <= 0) {
+ BIO_copy_next_retry(bio);
+ return ret;
+ }
+ uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
+ (static_cast<uint64_t>(buf[1]) << 48) |
+ (static_cast<uint64_t>(buf[2]) << 40) |
+ (static_cast<uint64_t>(buf[3]) << 32) |
+ (static_cast<uint64_t>(buf[4]) << 24) |
+ (static_cast<uint64_t>(buf[5]) << 16) |
+ (static_cast<uint64_t>(buf[6]) << 8) |
+ static_cast<uint64_t>(buf[7]);
+ timeout /= 1000; // Convert nanoseconds to microseconds.
+ timeval *out_timeout = reinterpret_cast<timeval *>(bio->ptr);
+ assert(out_timeout->tv_usec == 0);
+ assert(out_timeout->tv_sec == 0);
+ out_timeout->tv_usec = timeout % 1000000;
+ out_timeout->tv_sec = timeout / 1000000;
+
+ // Send an ACK to the peer.
+ ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
+ if (ret <= 0) {
+ return ret;
+ }
+ assert(ret == 1);
+
+ // Signal to the caller to retry the read, after processing the
+ // new clock.
+ BIO_set_retry_read(bio);
+ return -1;
+ }
+
+ if (opcode != kOpcodePacket) {
+ fprintf(stderr, "Unknown opcode, %u\n", opcode);
+ return -1;
+ }
+
// Read the length prefix.
uint8_t len_bytes[4];
- int ret = BIO_read(bio->next_bio, &len_bytes, sizeof(len_bytes));
+ ret = ReadAll(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);
+ uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
if (buf == NULL) {
return -1;
}
- ret = BIO_read(bio->next_bio, buf, len);
- assert(ret == (int)len);
+ ret = ReadAll(bio->next_bio, buf, len);
+ if (ret <= 0) {
+ fprintf(stderr, "Packeted BIO was truncated\n");
+ return -1;
+ }
if (outl > (int)len) {
outl = len;
@@ -84,7 +162,7 @@ static int packeted_read(BIO *bio, char *out, int outl) {
return outl;
}
-static long packeted_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
if (bio->next_bio == NULL) {
return 0;
}
@@ -94,12 +172,12 @@ static long packeted_ctrl(BIO *bio, int cmd, long num, void *ptr) {
return ret;
}
-static int packeted_new(BIO *bio) {
+static int PacketedNew(BIO *bio) {
bio->init = 1;
return 1;
}
-static int packeted_free(BIO *bio) {
+static int PacketedFree(BIO *bio) {
if (bio == NULL) {
return 0;
}
@@ -108,28 +186,33 @@ static int packeted_free(BIO *bio) {
return 1;
}
-static long packeted_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+static long PacketedCallbackCtrl(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 = {
+const BIO_METHOD g_packeted_bio_method = {
BIO_TYPE_FILTER,
"packeted bio",
- packeted_write,
- packeted_read,
+ PacketedWrite,
+ PacketedRead,
NULL /* puts */,
NULL /* gets */,
- packeted_ctrl,
- packeted_new,
- packeted_free,
- packeted_callback_ctrl,
+ PacketedCtrl,
+ PacketedNew,
+ PacketedFree,
+ PacketedCallbackCtrl,
};
} // namespace
-BIO *packeted_bio_create() {
- return BIO_new(&packeted_bio_method);
+ScopedBIO PacketedBioCreate(timeval *out_timeout) {
+ ScopedBIO bio(BIO_new(&g_packeted_bio_method));
+ if (!bio) {
+ return nullptr;
+ }
+ bio->ptr = out_timeout;
+ return bio;
}
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 384bd64..30697a5 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -15,18 +15,30 @@
#ifndef HEADER_PACKETED_BIO
#define HEADER_PACKETED_BIO
+#include <openssl/base.h>
#include <openssl/bio.h>
+#include "../../crypto/test/scoped_types.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.
+#if defined(OPENSSL_WINDOWS)
+#pragma warning(push, 3)
+#include <winsock2.h>
+#pragma warning(pop)
+#else
+#include <sys/types.h>
+#endif
+
+
+// PacketedBioCreate creates a filter BIO which implements a reliable in-order
+// blocking datagram socket. The resulting BIO, on |BIO_read|, may simulate a
+// timeout which sets |*out_timeout| to the timeout and fails the read.
+// |*out_timeout| must be zero on entry to |BIO_read|; it is an error to not
+// apply the timeout before the next |BIO_read|.
//
-// 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();
+// Note: The read timeout simulation is intended to be used with the async BIO
+// wrapper. It doesn't simulate BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, used in DTLS's
+// blocking mode.
+ScopedBIO PacketedBioCreate(timeval *out_timeout);
#endif // HEADER_PACKETED_BIO
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
new file mode 100644
index 0000000..42911d4
--- /dev/null
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -0,0 +1,159 @@
+package main
+
+import (
+ "crypto/cipher"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+)
+
+// See draft-agl-tls-chacha20poly1305-04 and
+// draft-irtf-cfrg-chacha20-poly1305-10. Where the two differ, the
+// draft-agl-tls-chacha20poly1305-04 variant is implemented.
+
+func leftRotate(a uint32, n uint) uint32 {
+ return (a << n) | (a >> (32 - n))
+}
+
+func chaChaQuarterRound(state *[16]uint32, a, b, c, d int) {
+ state[a] += state[b]
+ state[d] = leftRotate(state[d]^state[a], 16)
+
+ state[c] += state[d]
+ state[b] = leftRotate(state[b]^state[c], 12)
+
+ state[a] += state[b]
+ state[d] = leftRotate(state[d]^state[a], 8)
+
+ state[c] += state[d]
+ state[b] = leftRotate(state[b]^state[c], 7)
+}
+
+func chaCha20Block(state *[16]uint32, out []byte) {
+ var workingState [16]uint32
+ copy(workingState[:], state[:])
+ for i := 0; i < 10; i++ {
+ chaChaQuarterRound(&workingState, 0, 4, 8, 12)
+ chaChaQuarterRound(&workingState, 1, 5, 9, 13)
+ chaChaQuarterRound(&workingState, 2, 6, 10, 14)
+ chaChaQuarterRound(&workingState, 3, 7, 11, 15)
+ chaChaQuarterRound(&workingState, 0, 5, 10, 15)
+ chaChaQuarterRound(&workingState, 1, 6, 11, 12)
+ chaChaQuarterRound(&workingState, 2, 7, 8, 13)
+ chaChaQuarterRound(&workingState, 3, 4, 9, 14)
+ }
+ for i := 0; i < 16; i++ {
+ binary.LittleEndian.PutUint32(out[i*4:i*4+4], workingState[i]+state[i])
+ }
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+ if total := len(in) + n; cap(in) >= total {
+ head = in[:total]
+ } else {
+ head = make([]byte, total)
+ copy(head, in)
+ }
+ tail = head[len(in):]
+ return
+}
+
+type chaCha20Poly1305 struct {
+ key [32]byte
+}
+
+func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) {
+ if len(key) != 32 {
+ return nil, errors.New("bad key length")
+ }
+ aead := new(chaCha20Poly1305)
+ copy(aead.key[:], key)
+ return aead, nil
+}
+
+func (c *chaCha20Poly1305) NonceSize() int { return 8 }
+func (c *chaCha20Poly1305) Overhead() int { return 16 }
+
+func (c *chaCha20Poly1305) chaCha20(out, in, nonce []byte, counter uint64) {
+ var state [16]uint32
+ state[0] = 0x61707865
+ state[1] = 0x3320646e
+ state[2] = 0x79622d32
+ state[3] = 0x6b206574
+ for i := 0; i < 8; i++ {
+ state[4+i] = binary.LittleEndian.Uint32(c.key[i*4 : i*4+4])
+ }
+ state[14] = binary.LittleEndian.Uint32(nonce[0:4])
+ state[15] = binary.LittleEndian.Uint32(nonce[4:8])
+
+ for i := 0; i < len(in); i += 64 {
+ state[12] = uint32(counter & 0xffffffff)
+ state[13] = uint32(counter >> 32)
+
+ var tmp [64]byte
+ chaCha20Block(&state, tmp[:])
+ count := 64
+ if len(in)-i < count {
+ count = len(in) - i
+ }
+ for j := 0; j < count; j++ {
+ out[i+j] = in[i+j] ^ tmp[j]
+ }
+
+ counter++
+ }
+}
+
+func (c *chaCha20Poly1305) poly1305(tag *[16]byte, nonce, ciphertext, additionalData []byte) {
+ input := make([]byte, 0, len(additionalData)+8+len(ciphertext)+8)
+ input = append(input, additionalData...)
+ input, out := sliceForAppend(input, 8)
+ binary.LittleEndian.PutUint64(out, uint64(len(additionalData)))
+ input = append(input, ciphertext...)
+ input, out = sliceForAppend(input, 8)
+ binary.LittleEndian.PutUint64(out, uint64(len(ciphertext)))
+
+ var poly1305Key [32]byte
+ c.chaCha20(poly1305Key[:], poly1305Key[:], nonce, 0)
+
+ poly1305Sum(tag, input, &poly1305Key)
+}
+
+func (c *chaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+ if len(nonce) != 8 {
+ panic("Bad nonce length")
+ }
+
+ ret, out := sliceForAppend(dst, len(plaintext)+16)
+ c.chaCha20(out[:len(plaintext)], plaintext, nonce, 1)
+
+ var tag [16]byte
+ c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+ copy(out[len(plaintext):], tag[:])
+
+ return ret
+}
+
+func (c *chaCha20Poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ if len(nonce) != 8 {
+ panic("Bad nonce length")
+ }
+ if len(ciphertext) < 16 {
+ return nil, errors.New("chacha20: message authentication failed")
+ }
+ plaintextLen := len(ciphertext) - 16
+
+ var tag [16]byte
+ c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+ if subtle.ConstantTimeCompare(tag[:], ciphertext[plaintextLen:]) != 1 {
+ return nil, errors.New("chacha20: message authentication failed")
+ }
+
+ ret, out := sliceForAppend(dst, plaintextLen)
+ c.chaCha20(out, ciphertext[:plaintextLen], nonce, 1)
+ return ret, nil
+}
diff --git a/src/ssl/test/runner/chacha20_poly1305_test.go b/src/ssl/test/runner/chacha20_poly1305_test.go
new file mode 100644
index 0000000..726f482
--- /dev/null
+++ b/src/ssl/test/runner/chacha20_poly1305_test.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+ "bytes"
+ "encoding/hex"
+ "testing"
+)
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.1.1.
+func TestChaChaQuarterRound(t *testing.T) {
+ state := [16]uint32{0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567}
+ chaChaQuarterRound(&state, 0, 1, 2, 3)
+
+ a, b, c, d := state[0], state[1], state[2], state[3]
+ if a != 0xea2a92f4 || b != 0xcb1cf8ce || c != 0x4581472e || d != 0x5881c4bb {
+ t.Errorf("Incorrect results: %x", state)
+ }
+}
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.2.1.
+func TestChaChaQuarterRoundState(t *testing.T) {
+ state := [16]uint32{
+ 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
+ 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320,
+ }
+ chaChaQuarterRound(&state, 2, 7, 8, 13)
+
+ expected := [16]uint32{
+ 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2,
+ 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320,
+ }
+ for i := range state {
+ if state[i] != expected[i] {
+ t.Errorf("Mismatch at %d: %x vs %x", i, state, expected)
+ }
+ }
+}
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.3.2.
+func TestChaCha20Block(t *testing.T) {
+ state := [16]uint32{
+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+ 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
+ 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
+ 0x00000001, 0x09000000, 0x4a000000, 0x00000000,
+ }
+ out := make([]byte, 64)
+ chaCha20Block(&state, out)
+
+ expected := []byte{
+ 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
+ 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
+ 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
+ 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
+ 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
+ 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
+ 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
+ 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
+ }
+ if !bytes.Equal(out, expected) {
+ t.Errorf("Got %x, wanted %x", out, expected)
+ }
+}
+
+// See draft-agl-tls-chacha20poly1305-04, section 7.
+func TestChaCha20Poly1305(t *testing.T) {
+ key, _ := hex.DecodeString("4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
+ input, _ := hex.DecodeString("86d09974840bded2a5ca")
+ nonce, _ := hex.DecodeString("cd7cf67be39c794a")
+ ad, _ := hex.DecodeString("87e229d4500845a079c0")
+ output, _ := hex.DecodeString("e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6")
+
+ aead, err := newChaCha20Poly1305(key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ out, err := aead.Open(nil, nonce, output, ad)
+ if err != nil {
+ t.Errorf("Open failed: %s", err)
+ } else if !bytes.Equal(out, input) {
+ t.Errorf("Open gave %x, wanted %x", out, input)
+ }
+
+ out = aead.Seal(nil, nonce, input, ad)
+ if !bytes.Equal(out, output) {
+ t.Errorf("Open gave %x, wanted %x", out, output)
+ }
+
+ out[0]++
+ _, err = aead.Open(nil, nonce, out, ad)
+ if err == nil {
+ t.Errorf("Open on malformed data unexpectedly succeeded")
+ }
+}
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 89e75c8..162c0c0 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -62,6 +62,11 @@ const (
suitePSK
)
+type tlsAead struct {
+ cipher.AEAD
+ explicitNonce bool
+}
+
// A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct {
@@ -75,12 +80,14 @@ type cipherSuite struct {
flags int
cipher func(key, iv []byte, isRead bool) interface{}
mac func(version uint16, macKey []byte) macFunction
- aead func(key, fixedNonce []byte) cipher.AEAD
+ aead func(key, fixedNonce []byte) *tlsAead
}
var cipherSuites = []*cipherSuite{
// Ciphersuite order is chosen so that ECDHE comes before plain RSA
// and RC4 comes before AES (because of the Lucky13 attack).
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
@@ -95,6 +102,7 @@ var cipherSuites = []*cipherSuite{
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+ {TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, dheRSAKA, suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil},
@@ -216,7 +224,7 @@ func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]b
return f.aead.Open(out, f.openNonce, plaintext, additionalData)
}
-func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
+func aeadAESGCM(key, fixedNonce []byte) *tlsAead {
aes, err := aes.NewCipher(key)
if err != nil {
panic(err)
@@ -230,7 +238,15 @@ func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
copy(nonce1, fixedNonce)
copy(nonce2, fixedNonce)
- return &fixedNonceAEAD{nonce1, nonce2, aead}
+ return &tlsAead{&fixedNonceAEAD{nonce1, nonce2, aead}, true}
+}
+
+func aeadCHACHA20POLY1305(key, fixedNonce []byte) *tlsAead {
+ aead, err := newChaCha20Poly1305(key)
+ if err != nil {
+ panic(err)
+ }
+ return &tlsAead{aead, false}
}
// ssl30MAC implements the SSLv3 MAC function, as defined in
@@ -289,7 +305,7 @@ func (s tls10MAC) MAC(digestBuf, seq, header, length, data []byte) []byte {
}
func rsaKA(version uint16) keyAgreement {
- return &rsaKeyAgreement{}
+ return &rsaKeyAgreement{version: version}
}
func ecdheECDSAKA(version uint16) keyAgreement {
@@ -391,5 +407,8 @@ const (
// Additional cipher suite IDs, not IANA-assigned.
const (
- TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe
+ TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc13
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc14
+ TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc15
)
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 7aaf9a2..4ac7250 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -97,6 +97,7 @@ const (
type CurveID uint16
const (
+ CurveP224 CurveID = 21
CurveP256 CurveID = 23
CurveP384 CurveID = 24
CurveP521 CurveID = 25
@@ -429,15 +430,32 @@ type ProtocolBugs struct {
// ServerKeyExchange.
UnauthenticatedECDH bool
+ // SkipHelloVerifyRequest causes a DTLS server to skip the
+ // HelloVerifyRequest message.
+ SkipHelloVerifyRequest bool
+
+ // SkipCertificateStatus, if true, causes the server to skip the
+ // CertificateStatus message. This is legal because CertificateStatus is
+ // optional, even with a status_request in ServerHello.
+ SkipCertificateStatus bool
+
// SkipServerKeyExchange causes the server to skip sending
// ServerKeyExchange messages.
SkipServerKeyExchange bool
+ // SkipNewSessionTicket causes the server to skip sending the
+ // NewSessionTicket message despite promising to in ServerHello.
+ SkipNewSessionTicket bool
+
// SkipChangeCipherSpec causes the implementation to skip
// sending the ChangeCipherSpec message (and adjusting cipher
// state accordingly for the Finished message).
SkipChangeCipherSpec bool
+ // SkipFinished causes the implementation to skip sending the Finished
+ // message.
+ SkipFinished bool
+
// EarlyChangeCipherSpec causes the client to send an early
// ChangeCipherSpec message before the ClientKeyExchange. A value of
// zero disables this behavior. One and two configure variants for 0.9.8
@@ -449,10 +467,6 @@ type ProtocolBugs struct {
// messages.
FragmentAcrossChangeCipherSpec bool
- // SkipNewSessionTicket causes the server to skip sending the
- // NewSessionTicket message despite promising to in ServerHello.
- SkipNewSessionTicket bool
-
// SendV2ClientHello causes the client to send a V2ClientHello
// instead of a normal ClientHello.
SendV2ClientHello bool
@@ -475,8 +489,9 @@ type ProtocolBugs struct {
// two records.
FragmentAlert bool
- // SendSpuriousAlert will cause an spurious, unwanted alert to be sent.
- SendSpuriousAlert bool
+ // SendSpuriousAlert, if non-zero, will cause an spurious, unwanted
+ // alert to be sent.
+ SendSpuriousAlert alert
// RsaClientKeyExchangeVersion, if non-zero, causes the client to send a
// ClientKeyExchange with the specified version rather than the
@@ -491,16 +506,19 @@ type ProtocolBugs struct {
// TLS version in the ClientHello than the maximum supported version.
SendClientVersion uint16
- // SkipHelloVerifyRequest causes a DTLS server to skip the
- // HelloVerifyRequest message.
- SkipHelloVerifyRequest bool
-
// ExpectFalseStart causes the server to, on full handshakes,
// expect the peer to False Start; the server Finished message
// isn't sent until we receive an application data record
// from the peer.
ExpectFalseStart bool
+ // AlertBeforeFalseStartTest, if non-zero, causes the server to, on full
+ // handshakes, send an alert just before reading the application data
+ // record to test False Start. This can be used in a negative False
+ // Start test to determine whether the peer processed the alert (and
+ // closed the connection) before or after sending app data.
+ AlertBeforeFalseStartTest alert
+
// SSL3RSAKeyExchange causes the client to always send an RSA
// ClientKeyExchange message without the two-byte length
// prefix, as if it were SSL3.
@@ -557,9 +575,10 @@ type ProtocolBugs struct {
// retransmit at the record layer.
SequenceNumberIncrement uint64
- // RSAServerKeyExchange, if true, causes the server to send a
- // ServerKeyExchange message in the plain RSA key exchange.
- RSAServerKeyExchange bool
+ // RSAEphemeralKey, if true, causes the server to send a
+ // ServerKeyExchange message containing an ephemeral key (as in
+ // RSA_EXPORT) in the plain RSA key exchange.
+ RSAEphemeralKey bool
// SRTPMasterKeyIdentifer, if not empty, is the SRTP MKI value that the
// client offers when negotiating SRTP. MKI support is still missing so
@@ -578,6 +597,10 @@ type ProtocolBugs struct {
// still be enforced.
NoSignatureAndHashes bool
+ // NoSupportedCurves, if true, causes the client to omit the
+ // supported_curves extension.
+ NoSupportedCurves bool
+
// RequireSameRenegoClientVersion, if true, causes the server
// to require that all ClientHellos match in offered version
// across a renego.
@@ -603,6 +626,87 @@ type ProtocolBugs struct {
// AppDataAfterChangeCipherSpec, if not null, causes application data to
// be sent immediately after ChangeCipherSpec.
AppDataAfterChangeCipherSpec []byte
+
+ // AlertAfterChangeCipherSpec, if non-zero, causes an alert to be sent
+ // immediately after ChangeCipherSpec.
+ AlertAfterChangeCipherSpec alert
+
+ // TimeoutSchedule is the schedule of packet drops and simulated
+ // timeouts for before each handshake leg from the peer.
+ TimeoutSchedule []time.Duration
+
+ // PacketAdaptor is the packetAdaptor to use to simulate timeouts.
+ PacketAdaptor *packetAdaptor
+
+ // ReorderHandshakeFragments, if true, causes handshake fragments in
+ // DTLS to overlap and be sent in the wrong order. It also causes
+ // pre-CCS flights to be sent twice. (Post-CCS flights consist of
+ // Finished and will trigger a spurious retransmit.)
+ ReorderHandshakeFragments bool
+
+ // MixCompleteMessageWithFragments, if true, causes handshake
+ // messages in DTLS to redundantly both fragment the message
+ // and include a copy of the full one.
+ MixCompleteMessageWithFragments bool
+
+ // SendInvalidRecordType, if true, causes a record with an invalid
+ // content type to be sent immediately following the handshake.
+ SendInvalidRecordType bool
+
+ // WrongCertificateMessageType, if true, causes Certificate message to
+ // be sent with the wrong message type.
+ WrongCertificateMessageType bool
+
+ // FragmentMessageTypeMismatch, if true, causes all non-initial
+ // handshake fragments in DTLS to have the wrong message type.
+ FragmentMessageTypeMismatch bool
+
+ // FragmentMessageLengthMismatch, if true, causes all non-initial
+ // handshake fragments in DTLS to have the wrong message length.
+ FragmentMessageLengthMismatch bool
+
+ // SplitFragmentHeader, if true, causes the handshake fragments in DTLS
+ // to be split across two records.
+ SplitFragmentHeader bool
+
+ // SplitFragmentBody, if true, causes the handshake bodies in DTLS to be
+ // split across two records.
+ //
+ // TODO(davidben): There's one final split to test: when the header and
+ // body are split across two records. But those are (incorrectly)
+ // accepted right now.
+ SplitFragmentBody bool
+
+ // SendEmptyFragments, if true, causes handshakes to include empty
+ // fragments in DTLS.
+ SendEmptyFragments bool
+
+ // NeverResumeOnRenego, if true, causes renegotiations to always be full
+ // handshakes.
+ NeverResumeOnRenego bool
+
+ // NoSignatureAlgorithmsOnRenego, if true, causes renegotiations to omit
+ // the signature_algorithms extension.
+ NoSignatureAlgorithmsOnRenego bool
+
+ // IgnorePeerCipherPreferences, if true, causes the peer's cipher
+ // preferences to be ignored.
+ IgnorePeerCipherPreferences bool
+
+ // IgnorePeerSignatureAlgorithmPreferences, if true, causes the peer's
+ // signature algorithm preferences to be ignored.
+ IgnorePeerSignatureAlgorithmPreferences bool
+
+ // IgnorePeerCurvePreferences, if true, causes the peer's curve
+ // preferences to be ignored.
+ IgnorePeerCurvePreferences bool
+
+ // SendWarningAlerts, if non-zero, causes every record to be prefaced by
+ // a warning alert.
+ SendWarningAlerts alert
+
+ // BadFinished, if true, causes the Finished hash to be broken.
+ BadFinished bool
}
func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index d4a6817..fd198ca 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -37,14 +37,16 @@ type Conn struct {
handshakeComplete bool
didResume bool // whether this connection was a session resumption
extendedMasterSecret bool // whether this session used an extended master secret
- cipherSuite uint16
+ cipherSuite *cipherSuite
ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
- serverName string
+ serverName string
+ clientRandom, serverRandom [32]byte
+ masterSecret [48]byte
clientProtocol string
clientProtocolFallback bool
@@ -69,8 +71,9 @@ type Conn struct {
// DTLS state
sendHandshakeSeq uint16
recvHandshakeSeq uint16
- handMsg []byte // pending assembled handshake message
- handMsgLen int // handshake message length, not including the header
+ handMsg []byte // pending assembled handshake message
+ handMsgLen int // handshake message length, not including the header
+ pendingFragments [][]byte // pending outgoing handshake fragments.
tmp [16]byte
}
@@ -131,6 +134,7 @@ type halfConn struct {
nextCipher interface{} // next encryption state
nextMac macFunction // next MAC algorithm
+ nextSeq [6]byte // next epoch's starting sequence number in DTLS
// used to save allocating a new buffer for each MAC.
inDigestBuf, outDigestBuf []byte
@@ -200,10 +204,20 @@ func (hc *halfConn) incSeq(isOutgoing bool) {
}
}
-// incEpoch resets the sequence number. In DTLS, it increments the
-// epoch half of the sequence number.
+// incNextSeq increments the starting sequence number for the next epoch.
+func (hc *halfConn) incNextSeq() {
+ for i := len(hc.nextSeq) - 1; i >= 0; i-- {
+ hc.nextSeq[i]++
+ if hc.nextSeq[i] != 0 {
+ return
+ }
+ }
+ panic("TLS: sequence number wraparound")
+}
+
+// incEpoch resets the sequence number. In DTLS, it also increments the epoch
+// half of the sequence number.
func (hc *halfConn) incEpoch() {
- limit := 0
if hc.isDTLS {
for i := 1; i >= 0; i-- {
hc.seq[i]++
@@ -214,11 +228,14 @@ func (hc *halfConn) incEpoch() {
panic("TLS: epoch number wraparound")
}
}
- limit = 2
- }
- seq := hc.seq[limit:]
- for i := range seq {
- seq[i] = 0
+ copy(hc.seq[2:], hc.nextSeq[:])
+ for i := range hc.nextSeq {
+ hc.nextSeq[i] = 0
+ }
+ } else {
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
}
}
@@ -321,13 +338,16 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
switch c := hc.cipher.(type) {
case cipher.Stream:
c.XORKeyStream(payload, payload)
- case cipher.AEAD:
- explicitIVLen = 8
- if len(payload) < explicitIVLen {
- return false, 0, alertBadRecordMAC
+ case *tlsAead:
+ nonce := seq
+ if c.explicitNonce {
+ explicitIVLen = 8
+ if len(payload) < explicitIVLen {
+ return false, 0, alertBadRecordMAC
+ }
+ nonce = payload[:8]
+ payload = payload[8:]
}
- nonce := payload[:8]
- payload = payload[8:]
var additionalData [13]byte
copy(additionalData[:], seq)
@@ -451,10 +471,13 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
switch c := hc.cipher.(type) {
case cipher.Stream:
c.XORKeyStream(payload, payload)
- case cipher.AEAD:
+ case *tlsAead:
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
b.resize(len(b.data) + c.Overhead())
- nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+ nonce := hc.seq[:]
+ if c.explicitNonce {
+ nonce = b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+ }
payload := b.data[recordHeaderLen+explicitIVLen:]
payload = payload[:payloadLen]
@@ -756,11 +779,8 @@ Again:
if typ != want {
// A client might need to process a HelloRequest from
// the server, thus receiving a handshake message when
- // application data is expected is ok. Moreover, a DTLS
- // peer who sends Finished second may retransmit the
- // final leg. BoringSSL retrainsmits on an internal
- // timer, so this may also occur in test code.
- if !c.isClient && !c.isDTLS {
+ // application data is expected is ok.
+ if !c.isClient {
return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
}
}
@@ -817,6 +837,13 @@ func (c *Conn) writeV2Record(data []byte) (n int, err error) {
// to the connection and updates the record layer state.
// c.out.Mutex <= L.
func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
+ if typ != recordTypeAlert && c.config.Bugs.SendWarningAlerts != 0 {
+ alert := make([]byte, 2)
+ alert[0] = alertLevelWarning
+ alert[1] = byte(c.config.Bugs.SendWarningAlerts)
+ c.writeRecord(recordTypeAlert, alert)
+ }
+
if c.isDTLS {
return c.dtlsWriteRecord(typ, data)
}
@@ -851,7 +878,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
}
}
if explicitIVLen == 0 {
- if _, ok := c.out.cipher.(cipher.AEAD); ok {
+ if aead, ok := c.out.cipher.(*tlsAead); ok && aead.explicitNonce {
explicitIVLen = 8
// The AES-GCM construction in TLS has an
// explicit nonce so that the nonce can be
@@ -1003,6 +1030,67 @@ func (c *Conn) readHandshake() (interface{}, error) {
return m, nil
}
+// skipPacket processes all the DTLS records in packet. It updates
+// sequence number expectations but otherwise ignores them.
+func (c *Conn) skipPacket(packet []byte) error {
+ for len(packet) > 0 {
+ // Dropped packets are completely ignored save to update
+ // expected sequence numbers for this and the next epoch. (We
+ // don't assert on the contents of the packets both for
+ // simplicity and because a previous test with one shorter
+ // timeout schedule would have done so.)
+ epoch := packet[3:5]
+ seq := packet[5:11]
+ length := uint16(packet[11])<<8 | uint16(packet[12])
+ if bytes.Equal(c.in.seq[:2], epoch) {
+ if !bytes.Equal(c.in.seq[2:], seq) {
+ return errors.New("tls: sequence mismatch")
+ }
+ c.in.incSeq(false)
+ } else {
+ if !bytes.Equal(c.in.nextSeq[:], seq) {
+ return errors.New("tls: sequence mismatch")
+ }
+ c.in.incNextSeq()
+ }
+ packet = packet[13+length:]
+ }
+ return nil
+}
+
+// simulatePacketLoss simulates the loss of a handshake leg from the
+// peer based on the schedule in c.config.Bugs. If resendFunc is
+// non-nil, it is called after each simulated timeout to retransmit
+// handshake messages from the local end. This is used in cases where
+// the peer retransmits on a stale Finished rather than a timeout.
+func (c *Conn) simulatePacketLoss(resendFunc func()) error {
+ if len(c.config.Bugs.TimeoutSchedule) == 0 {
+ return nil
+ }
+ if !c.isDTLS {
+ return errors.New("tls: TimeoutSchedule may only be set in DTLS")
+ }
+ if c.config.Bugs.PacketAdaptor == nil {
+ return errors.New("tls: TimeoutSchedule set without PacketAdapter")
+ }
+ for _, timeout := range c.config.Bugs.TimeoutSchedule {
+ // Simulate a timeout.
+ packets, err := c.config.Bugs.PacketAdaptor.SendReadTimeout(timeout)
+ if err != nil {
+ return err
+ }
+ for _, packet := range packets {
+ if err := c.skipPacket(packet); err != nil {
+ return err
+ }
+ }
+ if resendFunc != nil {
+ resendFunc()
+ }
+ }
+ return nil
+}
+
// Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) {
if err := c.Handshake(); err != nil {
@@ -1020,8 +1108,8 @@ func (c *Conn) Write(b []byte) (int, error) {
return 0, alertInternalError
}
- if c.config.Bugs.SendSpuriousAlert {
- c.sendAlertLocked(alertRecordOverflow)
+ if c.config.Bugs.SendSpuriousAlert != 0 {
+ c.sendAlertLocked(c.config.Bugs.SendSpuriousAlert)
}
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
@@ -1096,9 +1184,9 @@ func (c *Conn) Read(b []byte) (n int, err error) {
// Soft error, like EAGAIN
return 0, err
}
- if c.hand.Len() > 0 && !c.isDTLS {
+ if c.hand.Len() > 0 {
// We received handshake bytes, indicating the
- // start of a renegotiation or a DTLS retransmit.
+ // start of a renegotiation.
if err := c.handleRenegotiation(); err != nil {
return 0, err
}
@@ -1177,6 +1265,9 @@ func (c *Conn) Handshake() error {
} else {
c.handshakeErr = c.serverHandshake()
}
+ if c.handshakeErr == nil && c.config.Bugs.SendInvalidRecordType {
+ c.writeRecord(recordType(42), []byte("invalid record"))
+ }
return c.handshakeErr
}
@@ -1193,7 +1284,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.DidResume = c.didResume
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.NegotiatedProtocolFromALPN = c.usedALPN
- state.CipherSuite = c.cipherSuite
+ state.CipherSuite = c.cipherSuite.id
state.PeerCertificates = c.peerCertificates
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
@@ -1227,3 +1318,28 @@ func (c *Conn) VerifyHostname(host string) error {
}
return c.peerCertificates[0].VerifyHostname(host)
}
+
+// ExportKeyingMaterial exports keying material from the current connection
+// state, as per RFC 5705.
+func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContext bool) ([]byte, error) {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+ if !c.handshakeComplete {
+ return nil, errors.New("tls: handshake has not yet been performed")
+ }
+
+ seedLen := len(c.clientRandom) + len(c.serverRandom)
+ if useContext {
+ seedLen += 2 + len(context)
+ }
+ seed := make([]byte, 0, seedLen)
+ seed = append(seed, c.clientRandom[:]...)
+ seed = append(seed, c.serverRandom[:]...)
+ if useContext {
+ seed = append(seed, byte(len(context)>>8), byte(len(context)))
+ seed = append(seed, context...)
+ }
+ result := make([]byte, length)
+ prfForVersion(c.vers, c.cipherSuite)(result, c.masterSecret[:], label, seed)
+ return result, nil
+}
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index a395980..85c4247 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -16,10 +16,10 @@ package main
import (
"bytes"
- "crypto/cipher"
"errors"
"fmt"
"io"
+ "math/rand"
"net"
)
@@ -38,7 +38,6 @@ func wireToVersion(vers uint16, isDTLS bool) uint16 {
}
func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
-Again:
recordHeaderLen := dtlsRecordHeaderLen
if c.rawInput == nil {
@@ -82,13 +81,6 @@ Again:
}
}
seq := b.data[3:11]
- if !bytes.Equal(seq[:2], c.in.seq[:2]) {
- // If the epoch didn't match, silently drop the record.
- // BoringSSL retransmits on an internal timer, so it may flakily
- // revisit the previous epoch if retransmiting ChangeCipherSpec
- // and Finished.
- goto Again
- }
// For test purposes, we assume a reliable channel. Require
// that the explicit sequence number matches the incrementing
// one we maintain. A real implementation would maintain a
@@ -113,127 +105,196 @@ Again:
return typ, b, nil
}
+func (c *Conn) makeFragment(header, data []byte, fragOffset, fragLen int) []byte {
+ fragment := make([]byte, 0, 12+fragLen)
+ fragment = append(fragment, header...)
+ fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq))
+ fragment = append(fragment, byte(fragOffset>>16), byte(fragOffset>>8), byte(fragOffset))
+ fragment = append(fragment, byte(fragLen>>16), byte(fragLen>>8), byte(fragLen))
+ fragment = append(fragment, data[fragOffset:fragOffset+fragLen]...)
+ return fragment
+}
+
func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
- recordHeaderLen := dtlsRecordHeaderLen
+ if typ != recordTypeHandshake {
+ // Only handshake messages are fragmented.
+ return c.dtlsWriteRawRecord(typ, data)
+ }
+
maxLen := c.config.Bugs.MaxHandshakeRecordLength
if maxLen <= 0 {
maxLen = 1024
}
- b := c.out.newBlock()
+ // Handshake messages have to be modified to include fragment
+ // offset and length and with the header replicated. Save the
+ // TLS header here.
+ //
+ // TODO(davidben): This assumes that data contains exactly one
+ // handshake message. This is incompatible with
+ // FragmentAcrossChangeCipherSpec. (Which is unfortunate
+ // because OpenSSL's DTLS implementation will probably accept
+ // such fragmentation and could do with a fix + tests.)
+ header := data[:4]
+ data = data[4:]
- var header []byte
- if typ == recordTypeHandshake {
- // Handshake messages have to be modified to include
- // fragment offset and length and with the header
- // replicated. Save the header here.
- //
- // TODO(davidben): This assumes that data contains
- // exactly one handshake message. This is incompatible
- // with FragmentAcrossChangeCipherSpec. (Which is
- // unfortunate because OpenSSL's DTLS implementation
- // will probably accept such fragmentation and could
- // do with a fix + tests.)
- if len(data) < 4 {
- // This should not happen.
- panic(data)
- }
- header = data[:4]
- data = data[4:]
+ isFinished := header[0] == typeFinished
+
+ if c.config.Bugs.SendEmptyFragments {
+ fragment := c.makeFragment(header, data, 0, 0)
+ c.pendingFragments = append(c.pendingFragments, fragment)
}
firstRun := true
- for firstRun || len(data) > 0 {
+ fragOffset := 0
+ for firstRun || fragOffset < len(data) {
firstRun = false
- m := len(data)
- var fragment []byte
- // Handshake messages get fragmented. Other records we
- // pass-through as is. DTLS should be a packet
- // interface.
- if typ == recordTypeHandshake {
- if m > maxLen {
- m = maxLen
- }
+ fragLen := len(data) - fragOffset
+ if fragLen > maxLen {
+ fragLen = maxLen
+ }
- // Standard handshake header.
- fragment = make([]byte, 0, 12+m)
- fragment = append(fragment, header...)
- // message_seq
- fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq))
- // fragment_offset
- fragment = append(fragment, byte(n>>16), byte(n>>8), byte(n))
- // fragment_length
- fragment = append(fragment, byte(m>>16), byte(m>>8), byte(m))
- fragment = append(fragment, data[:m]...)
- } else {
- fragment = data[:m]
+ fragment := c.makeFragment(header, data, fragOffset, fragLen)
+ if c.config.Bugs.FragmentMessageTypeMismatch && fragOffset > 0 {
+ fragment[0]++
+ }
+ if c.config.Bugs.FragmentMessageLengthMismatch && fragOffset > 0 {
+ fragment[3]++
}
- // Send the fragment.
- explicitIVLen := 0
- explicitIVIsSeq := false
+ // Buffer the fragment for later. They will be sent (and
+ // reordered) on flush.
+ c.pendingFragments = append(c.pendingFragments, fragment)
+ if c.config.Bugs.ReorderHandshakeFragments {
+ // Don't duplicate Finished to avoid the peer
+ // interpreting it as a retransmit request.
+ if !isFinished {
+ c.pendingFragments = append(c.pendingFragments, fragment)
+ }
- if cbc, ok := c.out.cipher.(cbcMode); ok {
- // Block cipher modes have an explicit IV.
- explicitIVLen = cbc.BlockSize()
- } else if _, ok := c.out.cipher.(cipher.AEAD); ok {
- explicitIVLen = 8
- // The AES-GCM construction in TLS has an
- // explicit nonce so that the nonce can be
- // random. However, the nonce is only 8 bytes
- // which is too small for a secure, random
- // nonce. Therefore we use the sequence number
- // as the nonce.
- explicitIVIsSeq = true
- } else if c.out.cipher != nil {
- panic("Unknown cipher")
+ if fragLen > (maxLen+1)/2 {
+ // Overlap each fragment by half.
+ fragLen = (maxLen + 1) / 2
+ }
}
- b.resize(recordHeaderLen + explicitIVLen + len(fragment))
- b.data[0] = byte(typ)
- vers := c.vers
- if vers == 0 {
- // Some TLS servers fail if the record version is
- // greater than TLS 1.0 for the initial ClientHello.
- vers = VersionTLS10
+ fragOffset += fragLen
+ n += fragLen
+ }
+ if !isFinished && c.config.Bugs.MixCompleteMessageWithFragments {
+ fragment := c.makeFragment(header, data, 0, len(data))
+ c.pendingFragments = append(c.pendingFragments, fragment)
+ }
+
+ // Increment the handshake sequence number for the next
+ // handshake message.
+ c.sendHandshakeSeq++
+ return
+}
+
+func (c *Conn) dtlsFlushHandshake() error {
+ if !c.isDTLS {
+ return nil
+ }
+
+ var fragments [][]byte
+ fragments, c.pendingFragments = c.pendingFragments, fragments
+
+ if c.config.Bugs.ReorderHandshakeFragments {
+ perm := rand.New(rand.NewSource(0)).Perm(len(fragments))
+ tmp := make([][]byte, len(fragments))
+ for i := range tmp {
+ tmp[i] = fragments[perm[i]]
}
- vers = versionToWire(vers, c.isDTLS)
- b.data[1] = byte(vers >> 8)
- b.data[2] = byte(vers)
- // DTLS records include an explicit sequence number.
- copy(b.data[3:11], c.out.seq[0:])
- b.data[11] = byte(len(fragment) >> 8)
- b.data[12] = byte(len(fragment))
- if explicitIVLen > 0 {
- explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
- if explicitIVIsSeq {
- copy(explicitIV, c.out.seq[:])
- } else {
- if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
- break
- }
+ fragments = tmp
+ }
+
+ // Send them all.
+ for _, fragment := range fragments {
+ if c.config.Bugs.SplitFragmentHeader {
+ if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:2]); err != nil {
+ return err
}
+ fragment = fragment[2:]
+ } else if c.config.Bugs.SplitFragmentBody && len(fragment) > 12 {
+ if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:13]); err != nil {
+ return err
+ }
+ fragment = fragment[13:]
}
- copy(b.data[recordHeaderLen+explicitIVLen:], fragment)
- c.out.encrypt(b, explicitIVLen)
// TODO(davidben): A real DTLS implementation needs to
- // retransmit handshake messages. For testing
- // purposes, we don't actually care.
- _, err = c.conn.Write(b.data)
- if err != nil {
- break
+ // retransmit handshake messages. For testing purposes, we don't
+ // actually care.
+ if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment); err != nil {
+ return err
}
- n += m
- data = data[m:]
}
- c.out.freeBlock(b)
+ return nil
+}
- // Increment the handshake sequence number for the next
- // handshake message.
- if typ == recordTypeHandshake {
- c.sendHandshakeSeq++
+func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) {
+ recordHeaderLen := dtlsRecordHeaderLen
+ maxLen := c.config.Bugs.MaxHandshakeRecordLength
+ if maxLen <= 0 {
+ maxLen = 1024
}
+ b := c.out.newBlock()
+
+ explicitIVLen := 0
+ explicitIVIsSeq := false
+
+ if cbc, ok := c.out.cipher.(cbcMode); ok {
+ // Block cipher modes have an explicit IV.
+ explicitIVLen = cbc.BlockSize()
+ } else if aead, ok := c.out.cipher.(*tlsAead); ok {
+ if aead.explicitNonce {
+ explicitIVLen = 8
+ // The AES-GCM construction in TLS has an explicit nonce so that
+ // the nonce can be random. However, the nonce is only 8 bytes
+ // which is too small for a secure, random nonce. Therefore we
+ // use the sequence number as the nonce.
+ explicitIVIsSeq = true
+ }
+ } else if c.out.cipher != nil {
+ panic("Unknown cipher")
+ }
+ b.resize(recordHeaderLen + explicitIVLen + len(data))
+ b.data[0] = byte(typ)
+ vers := c.vers
+ if vers == 0 {
+ // Some TLS servers fail if the record version is greater than
+ // TLS 1.0 for the initial ClientHello.
+ vers = VersionTLS10
+ }
+ vers = versionToWire(vers, c.isDTLS)
+ b.data[1] = byte(vers >> 8)
+ b.data[2] = byte(vers)
+ // DTLS records include an explicit sequence number.
+ copy(b.data[3:11], c.out.seq[0:])
+ b.data[11] = byte(len(data) >> 8)
+ b.data[12] = byte(len(data))
+ if explicitIVLen > 0 {
+ explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+ if explicitIVIsSeq {
+ copy(explicitIV, c.out.seq[:])
+ } else {
+ if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
+ return
+ }
+ }
+ }
+ copy(b.data[recordHeaderLen+explicitIVLen:], data)
+ c.out.encrypt(b, explicitIVLen)
+
+ _, err = c.conn.Write(b.data)
+ if err != nil {
+ return
+ }
+ n = len(data)
+
+ c.out.freeBlock(b)
+
if typ == recordTypeChangeCipherSpec {
err = c.out.changeCipherSpec(c.config)
if err != nil {
@@ -250,9 +311,9 @@ func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
func (c *Conn) dtlsDoReadHandshake() ([]byte, error) {
// Assemble a full handshake message. For test purposes, this
- // implementation assumes fragments arrive in order, but tolerates
- // retransmits. It may need to be cleverer if we ever test BoringSSL's
- // retransmit behavior.
+ // implementation assumes fragments arrive in order. It may
+ // need to be cleverer if we ever test BoringSSL's retransmit
+ // behavior.
for len(c.handMsg) < 4+c.handMsgLen {
// Get a new handshake record if the previous has been
// exhausted.
@@ -281,16 +342,9 @@ func (c *Conn) dtlsDoReadHandshake() ([]byte, error) {
}
fragment := c.hand.Next(fragLen)
- if fragSeq < c.recvHandshakeSeq {
- // BoringSSL retransmits based on an internal timer, so
- // it may flakily retransmit part of a handshake
- // message. Ignore those fragments.
- //
- // TODO(davidben): Revise this if BoringSSL's retransmit
- // logic is made more deterministic.
- continue
- } else if fragSeq > c.recvHandshakeSeq {
- return nil, errors.New("dtls: handshake messages sent out of order")
+ // Check it's a fragment for the right message.
+ if fragSeq != c.recvHandshakeSeq {
+ return nil, errors.New("dtls: bad handshake sequence number")
}
// Check that the length is consistent.
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index f297fc1..0dac05d 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -6,7 +6,6 @@ package main
import (
"bytes"
- "crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
@@ -22,13 +21,14 @@ import (
)
type clientHandshakeState struct {
- c *Conn
- serverHello *serverHelloMsg
- hello *clientHelloMsg
- suite *cipherSuite
- finishedHash finishedHash
- masterSecret []byte
- session *ClientSessionState
+ c *Conn
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ suite *cipherSuite
+ finishedHash finishedHash
+ masterSecret []byte
+ session *ClientSessionState
+ finishedBytes []byte
}
func (c *Conn) clientHandshake() error {
@@ -83,6 +83,10 @@ func (c *Conn) clientHandshake() error {
hello.extendedMasterSecret = false
}
+ if c.config.Bugs.NoSupportedCurves {
+ hello.supportedCurves = nil
+ }
+
if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo {
if c.config.Bugs.BadRenegotiationInfo {
hello.secureRenegotiation = append(hello.secureRenegotiation, c.clientVerify...)
@@ -129,13 +133,16 @@ NextCipherSuite:
return errors.New("tls: short read from Rand: " + err.Error())
}
- if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
+ if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes && (c.cipherSuite == nil || !c.config.Bugs.NoSignatureAlgorithmsOnRenego) {
hello.signatureAndHashes = c.config.signatureAndHashesForClient()
}
var session *ClientSessionState
var cacheKey string
sessionCache := c.config.ClientSessionCache
+ if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
+ sessionCache = nil
+ }
if sessionCache != nil {
hello.ticketSupported = !c.config.SessionTicketsDisabled
@@ -213,7 +220,11 @@ NextCipherSuite:
helloBytes = hello.marshal()
c.writeRecord(recordTypeHandshake, helloBytes)
}
+ c.dtlsFlushHandshake()
+ if err := c.simulatePacketLoss(nil); err != nil {
+ return err
+ }
msg, err := c.readHandshake()
if err != nil {
return err
@@ -233,7 +244,11 @@ NextCipherSuite:
hello.cookie = helloVerifyRequest.cookie
helloBytes = hello.marshal()
c.writeRecord(recordTypeHandshake, helloBytes)
+ c.dtlsFlushHandshake()
+ if err := c.simulatePacketLoss(nil); err != nil {
+ return err
+ }
msg, err = c.readHandshake()
if err != nil {
return err
@@ -317,6 +332,15 @@ NextCipherSuite:
if err := hs.sendFinished(isResume); err != nil {
return err
}
+ // Most retransmits are triggered by a timeout, but the final
+ // leg of the handshake is retransmited upon re-receiving a
+ // Finished.
+ if err := c.simulatePacketLoss(func() {
+ c.writeRecord(recordTypeHandshake, hs.finishedBytes)
+ c.dtlsFlushHandshake()
+ }); err != nil {
+ return err
+ }
if err := hs.readSessionTicket(); err != nil {
return err
}
@@ -331,7 +355,10 @@ NextCipherSuite:
c.didResume = isResume
c.handshakeComplete = true
- c.cipherSuite = suite.id
+ c.cipherSuite = suite
+ copy(c.clientRandom[:], hs.hello.random)
+ copy(c.serverRandom[:], hs.serverHello.random)
+ copy(c.masterSecret[:], hs.masterSecret)
return nil
}
@@ -559,33 +586,39 @@ func (hs *clientHandshakeState) doFullHandshake() error {
hasSignatureAndHash: c.vers >= VersionTLS12,
}
+ // Determine the hash to sign.
+ var signatureType uint8
+ switch c.config.Certificates[0].PrivateKey.(type) {
+ case *ecdsa.PrivateKey:
+ signatureType = signatureECDSA
+ case *rsa.PrivateKey:
+ signatureType = signatureRSA
+ default:
+ c.sendAlert(alertInternalError)
+ return errors.New("unknown private key type")
+ }
+ if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
+ certReq.signatureAndHashes = c.config.signatureAndHashesForClient()
+ }
+ certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, c.config.signatureAndHashesForClient(), signatureType)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
switch key := c.config.Certificates[0].PrivateKey.(type) {
case *ecdsa.PrivateKey:
- certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureECDSA)
- if err != nil {
- break
- }
- var digest []byte
- digest, _, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
- if err != nil {
- break
- }
var r, s *big.Int
r, s, err = ecdsa.Sign(c.config.rand(), key, digest)
if err == nil {
signed, err = asn1.Marshal(ecdsaSignature{r, s})
}
case *rsa.PrivateKey:
- certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureRSA)
- if err != nil {
- break
- }
- var digest []byte
- var hashFunc crypto.Hash
- digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
- if err != nil {
- break
- }
signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
default:
err = errors.New("unknown private key type")
@@ -599,6 +632,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
hs.writeClientHash(certVerify.marshal())
c.writeRecord(recordTypeHandshake, certVerify.marshal())
}
+ c.dtlsFlushHandshake()
hs.finishedHash.discardHandshakeBuffer()
@@ -825,15 +859,19 @@ func (hs *clientHandshakeState) sendFinished(isResume bool) error {
} else {
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
}
+ if c.config.Bugs.BadFinished {
+ finished.verifyData[0]++
+ }
c.clientVerify = append(c.clientVerify[:0], finished.verifyData...)
- finishedBytes := finished.marshal()
- hs.writeHash(finishedBytes, seqno)
- postCCSBytes = append(postCCSBytes, finishedBytes...)
+ hs.finishedBytes = finished.marshal()
+ hs.writeHash(hs.finishedBytes, seqno)
+ postCCSBytes = append(postCCSBytes, hs.finishedBytes...)
if c.config.Bugs.FragmentAcrossChangeCipherSpec {
c.writeRecord(recordTypeHandshake, postCCSBytes[:5])
postCCSBytes = postCCSBytes[5:]
}
+ c.dtlsFlushHandshake()
if !c.config.Bugs.SkipChangeCipherSpec &&
c.config.Bugs.EarlyChangeCipherSpec == 0 {
@@ -843,8 +881,15 @@ func (hs *clientHandshakeState) sendFinished(isResume bool) error {
if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec)
}
+ if c.config.Bugs.AlertAfterChangeCipherSpec != 0 {
+ c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec)
+ return errors.New("tls: simulating post-CCS alert")
+ }
- c.writeRecord(recordTypeHandshake, postCCSBytes)
+ if !c.config.Bugs.SkipFinished {
+ c.writeRecord(recordTypeHandshake, postCCSBytes)
+ c.dtlsFlushHandshake()
+ }
return nil
}
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 1234a57..59ed9df 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -33,6 +33,7 @@ type serverHandshakeState struct {
masterSecret []byte
certsFromClient [][]byte
cert *Certificate
+ finishedBytes []byte
}
// serverHandshake performs a TLS handshake as a server.
@@ -71,6 +72,15 @@ func (c *Conn) serverHandshake() error {
if err := hs.sendFinished(); err != nil {
return err
}
+ // Most retransmits are triggered by a timeout, but the final
+ // leg of the handshake is retransmited upon re-receiving a
+ // Finished.
+ if err := c.simulatePacketLoss(func() {
+ c.writeRecord(recordTypeHandshake, hs.finishedBytes)
+ c.dtlsFlushHandshake()
+ }); err != nil {
+ return err
+ }
if err := hs.readFinished(isResume); err != nil {
return err
}
@@ -87,9 +97,12 @@ func (c *Conn) serverHandshake() error {
if err := hs.readFinished(isResume); err != nil {
return err
}
+ if c.config.Bugs.AlertBeforeFalseStartTest != 0 {
+ c.sendAlert(c.config.Bugs.AlertBeforeFalseStartTest)
+ }
if c.config.Bugs.ExpectFalseStart {
if err := c.readRecord(recordTypeApplicationData); err != nil {
- return err
+ return fmt.Errorf("tls: peer did not false start: %s", err)
}
}
if err := hs.sendSessionTicket(); err != nil {
@@ -100,6 +113,9 @@ func (c *Conn) serverHandshake() error {
}
}
c.handshakeComplete = true
+ copy(c.clientRandom[:], hs.clientHello.random)
+ copy(c.serverRandom[:], hs.hello.random)
+ copy(c.masterSecret[:], hs.masterSecret)
return nil
}
@@ -110,6 +126,9 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
config := hs.c.config
c := hs.c
+ if err := c.simulatePacketLoss(nil); err != nil {
+ return false, err
+ }
msg, err := c.readHandshake()
if err != nil {
return false, err
@@ -136,7 +155,11 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
return false, errors.New("dtls: short read from Rand: " + err.Error())
}
c.writeRecord(recordTypeHandshake, helloVerifyRequest.marshal())
+ c.dtlsFlushHandshake()
+ if err := c.simulatePacketLoss(nil); err != nil {
+ return false, err
+ }
msg, err := c.readHandshake()
if err != nil {
return false, err
@@ -176,6 +199,9 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAndHashes) > 0 {
return false, fmt.Errorf("tls: client included signature_algorithms before TLS 1.2")
}
+ if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
+ hs.clientHello.signatureAndHashes = config.signatureAndHashesForServer()
+ }
c.vers, ok = config.mutualVersion(hs.clientHello.vers)
if !ok {
@@ -189,6 +215,9 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
supportedCurve := false
preferredCurves := config.curvePreferences()
+ if config.Bugs.IgnorePeerCurvePreferences {
+ hs.clientHello.supportedCurves = preferredCurves
+ }
Curves:
for _, curve := range hs.clientHello.supportedCurves {
for _, supported := range preferredCurves {
@@ -323,6 +352,9 @@ Curves:
return false, errors.New("tls: fallback SCSV found when not expected")
}
+ if config.Bugs.IgnorePeerCipherPreferences {
+ hs.clientHello.cipherSuites = c.config.cipherSuites()
+ }
var preferenceList, supportedList []uint16
if c.config.PreferServerCipherSuites {
preferenceList = c.config.cipherSuites()
@@ -350,6 +382,10 @@ Curves:
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
+ if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
+ return false
+ }
+
if len(hs.clientHello.sessionTicket) > 0 {
if c.config.SessionTicketsDisabled {
return false
@@ -410,6 +446,9 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
c := hs.c
hs.hello.cipherSuite = hs.suite.id
+ if c.config.Bugs.SendCipherSuite != 0 {
+ hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
+ }
// We echo the client's session ID in the ServerHello to let it know
// that we're doing a resumption.
hs.hello.sessionId = hs.clientHello.sessionId
@@ -473,12 +512,16 @@ func (hs *serverHandshakeState) doFullHandshake() error {
certMsg := new(certificateMsg)
certMsg.certificates = hs.cert.Certificate
if !config.Bugs.UnauthenticatedECDH {
- hs.writeServerHash(certMsg.marshal())
- c.writeRecord(recordTypeHandshake, certMsg.marshal())
+ certMsgBytes := certMsg.marshal()
+ if config.Bugs.WrongCertificateMessageType {
+ certMsgBytes[0] += 42
+ }
+ hs.writeServerHash(certMsgBytes)
+ c.writeRecord(recordTypeHandshake, certMsgBytes)
}
}
- if hs.hello.ocspStapling {
+ if hs.hello.ocspStapling && !c.config.Bugs.SkipCertificateStatus {
certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP
certStatus.response = hs.cert.OCSPStaple
@@ -530,9 +573,13 @@ func (hs *serverHandshakeState) doFullHandshake() error {
helloDone := new(serverHelloDoneMsg)
hs.writeServerHash(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal())
+ c.dtlsFlushHandshake()
var pub crypto.PublicKey // public key for client auth, if any
+ if err := c.simulatePacketLoss(nil); err != nil {
+ return err
+ }
msg, err := c.readHandshake()
if err != nil {
return err
@@ -811,14 +858,19 @@ func (hs *serverHandshakeState) sendFinished() error {
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ if c.config.Bugs.BadFinished {
+ finished.verifyData[0]++
+ }
c.serverVerify = append(c.serverVerify[:0], finished.verifyData...)
- postCCSBytes := finished.marshal()
- hs.writeServerHash(postCCSBytes)
+ hs.finishedBytes = finished.marshal()
+ hs.writeServerHash(hs.finishedBytes)
+ postCCSBytes := hs.finishedBytes
if c.config.Bugs.FragmentAcrossChangeCipherSpec {
c.writeRecord(recordTypeHandshake, postCCSBytes[:5])
postCCSBytes = postCCSBytes[5:]
}
+ c.dtlsFlushHandshake()
if !c.config.Bugs.SkipChangeCipherSpec {
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@@ -827,10 +879,17 @@ func (hs *serverHandshakeState) sendFinished() error {
if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec)
}
+ if c.config.Bugs.AlertAfterChangeCipherSpec != 0 {
+ c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec)
+ return errors.New("tls: simulating post-CCS alert")
+ }
- c.writeRecord(recordTypeHandshake, postCCSBytes)
+ if !c.config.Bugs.SkipFinished {
+ c.writeRecord(recordTypeHandshake, postCCSBytes)
+ c.dtlsFlushHandshake()
+ }
- c.cipherSuite = hs.suite.id
+ c.cipherSuite = hs.suite
return nil
}
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 116dfd8..5e44b54 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -25,19 +25,73 @@ var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
// rsaKeyAgreement implements the standard TLS key agreement where the client
// encrypts the pre-master secret to the server's public key.
type rsaKeyAgreement struct {
+ version uint16
clientVersion uint16
+ exportKey *rsa.PrivateKey
}
func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
// Save the client version for comparison later.
ka.clientVersion = versionToWire(clientHello.vers, clientHello.isDTLS)
- if config.Bugs.RSAServerKeyExchange {
- // Send an empty ServerKeyExchange message.
- return &serverKeyExchangeMsg{}, nil
+ if !config.Bugs.RSAEphemeralKey {
+ return nil, nil
}
- return nil, nil
+ // Generate an ephemeral RSA key to use instead of the real
+ // one, as in RSA_EXPORT.
+ key, err := rsa.GenerateKey(config.rand(), 512)
+ if err != nil {
+ return nil, err
+ }
+ ka.exportKey = key
+
+ modulus := key.N.Bytes()
+ exponent := big.NewInt(int64(key.E)).Bytes()
+ serverRSAParams := make([]byte, 0, 2+len(modulus)+2+len(exponent))
+ serverRSAParams = append(serverRSAParams, byte(len(modulus)>>8), byte(len(modulus)))
+ serverRSAParams = append(serverRSAParams, modulus...)
+ serverRSAParams = append(serverRSAParams, byte(len(exponent)>>8), byte(len(exponent)))
+ serverRSAParams = append(serverRSAParams, exponent...)
+
+ var tls12HashId uint8
+ if ka.version >= VersionTLS12 {
+ if tls12HashId, err = pickTLS12HashForSignature(signatureRSA, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
+ return nil, err
+ }
+ }
+
+ digest, hashFunc, err := hashForServerKeyExchange(signatureRSA, tls12HashId, ka.version, clientHello.random, hello.random, serverRSAParams)
+ if err != nil {
+ return nil, err
+ }
+ privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("RSA ephemeral key requires an RSA server private key")
+ }
+ sig, err := rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
+ if err != nil {
+ return nil, errors.New("failed to sign RSA parameters: " + err.Error())
+ }
+
+ skx := new(serverKeyExchangeMsg)
+ sigAndHashLen := 0
+ if ka.version >= VersionTLS12 {
+ sigAndHashLen = 2
+ }
+ skx.key = make([]byte, len(serverRSAParams)+sigAndHashLen+2+len(sig))
+ copy(skx.key, serverRSAParams)
+ k := skx.key[len(serverRSAParams):]
+ if ka.version >= VersionTLS12 {
+ k[0] = tls12HashId
+ k[1] = signatureRSA
+ k = k[2:]
+ }
+ k[0] = byte(len(sig) >> 8)
+ k[1] = byte(len(sig))
+ copy(k[2:], sig)
+
+ return skx, nil
}
func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
@@ -60,7 +114,11 @@ func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certif
ciphertext = ckx.ciphertext[2:]
}
- err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
+ key := cert.PrivateKey.(*rsa.PrivateKey)
+ if ka.exportKey != nil {
+ key = ka.exportKey
+ }
+ err = rsa.DecryptPKCS1v15SessionKey(config.rand(), key, ciphertext, preMasterSecret)
if err != nil {
return nil, err
}
@@ -154,20 +212,19 @@ func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ..
// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
// ServerKeyExchange given the signature type being used and the client's
// advertized list of supported signature and hash combinations.
-func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
- if len(clientSignatureAndHashes) == 0 {
+func pickTLS12HashForSignature(sigType uint8, clientList, serverList []signatureAndHash) (uint8, error) {
+ if len(clientList) == 0 {
// If the client didn't specify any signature_algorithms
// extension then we can assume that it supports SHA1. See
// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
return hashSHA1, nil
}
- for _, sigAndHash := range clientSignatureAndHashes {
+ for _, sigAndHash := range clientList {
if sigAndHash.signature != sigType {
continue
}
- switch sigAndHash.hash {
- case hashSHA1, hashSHA256:
+ if isSupportedSignatureAndHash(sigAndHash, serverList) {
return sigAndHash.hash, nil
}
}
@@ -177,6 +234,8 @@ func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatu
func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
switch id {
+ case CurveP224:
+ return elliptic.P224(), true
case CurveP256:
return elliptic.P256(), true
case CurveP384:
@@ -221,7 +280,7 @@ func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate,
var tls12HashId uint8
var err error
if ka.version >= VersionTLS12 {
- if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
+ if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
return nil, err
}
}
diff --git a/src/ssl/test/runner/packet_adapter.go b/src/ssl/test/runner/packet_adapter.go
index 671b413..bbcd388 100644
--- a/src/ssl/test/runner/packet_adapter.go
+++ b/src/ssl/test/runner/packet_adapter.go
@@ -6,50 +6,115 @@ package main
import (
"encoding/binary"
- "errors"
+ "fmt"
+ "io"
"net"
+ "time"
)
+// opcodePacket signals a packet, encoded with a 32-bit length prefix, followed
+// by the payload.
+const opcodePacket = byte('P')
+
+// opcodeTimeout signals a read timeout, encoded by a 64-bit number of
+// nanoseconds. On receipt, the peer should reply with
+// opcodeTimeoutAck. opcodeTimeout may only be sent by the Go side.
+const opcodeTimeout = byte('T')
+
+// opcodeTimeoutAck acknowledges a read timeout. This opcode has no payload and
+// may only be sent by the C side. Timeout ACKs act as a synchronization point
+// at the timeout, to bracket one flight of messages from C.
+const opcodeTimeoutAck = byte('t')
+
type packetAdaptor struct {
net.Conn
}
-// newPacketAdaptor wraps a reliable streaming net.Conn into a
-// reliable packet-based net.Conn. Every packet is encoded with a
-// 32-bit length prefix as a framing layer.
-func newPacketAdaptor(conn net.Conn) net.Conn {
+// newPacketAdaptor wraps a reliable streaming net.Conn into a reliable
+// packet-based net.Conn. The stream contains packets and control commands,
+// distinguished by a one byte opcode.
+func newPacketAdaptor(conn net.Conn) *packetAdaptor {
return &packetAdaptor{conn}
}
-func (p *packetAdaptor) Read(b []byte) (int, error) {
+func (p *packetAdaptor) readOpcode() (byte, error) {
+ out := make([]byte, 1)
+ if _, err := io.ReadFull(p.Conn, out); err != nil {
+ return 0, err
+ }
+ return out[0], nil
+}
+
+func (p *packetAdaptor) readPacketBody() ([]byte, error) {
var length uint32
if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil {
- return 0, err
+ return nil, err
}
out := make([]byte, length)
- n, err := p.Conn.Read(out)
+ if _, err := io.ReadFull(p.Conn, out); err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (p *packetAdaptor) Read(b []byte) (int, error) {
+ opcode, err := p.readOpcode()
if err != nil {
return 0, err
}
- if n != int(length) {
- return 0, errors.New("internal error: length mismatch!")
+ if opcode != opcodePacket {
+ return 0, fmt.Errorf("unexpected opcode '%d'", opcode)
+ }
+ out, err := p.readPacketBody()
+ if err != nil {
+ return 0, err
}
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 {
+ payload := make([]byte, 1+4+len(b))
+ payload[0] = opcodePacket
+ binary.BigEndian.PutUint32(payload[1:5], uint32(len(b)))
+ copy(payload[5:], b)
+ if _, err := p.Conn.Write(payload); err != nil {
return 0, err
}
- n, err := p.Conn.Write(b)
- if err != nil {
- return 0, err
+ return len(b), nil
+}
+
+// SendReadTimeout instructs the peer to simulate a read timeout. It then waits
+// for acknowledgement of the timeout, buffering any packets received since
+// then. The packets are then returned.
+func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) {
+ payload := make([]byte, 1+8)
+ payload[0] = opcodeTimeout
+ binary.BigEndian.PutUint64(payload[1:], uint64(d.Nanoseconds()))
+ if _, err := p.Conn.Write(payload); err != nil {
+ return nil, err
}
- if n != len(b) {
- return 0, errors.New("internal error: length mismatch!")
+
+ var packets [][]byte
+ for {
+ opcode, err := p.readOpcode()
+ if err != nil {
+ return nil, err
+ }
+ switch opcode {
+ case opcodeTimeoutAck:
+ // Done! Return the packets buffered and continue.
+ return packets, nil
+ case opcodePacket:
+ // Buffer the packet for the caller to process.
+ packet, err := p.readPacketBody()
+ if err != nil {
+ return nil, err
+ }
+ packets = append(packets, packet)
+ default:
+ return nil, fmt.Errorf("unexpected opcode '%d'", opcode)
+ }
}
- return len(b), nil
}
type replayAdaptor struct {
diff --git a/src/ssl/test/runner/poly1305.go b/src/ssl/test/runner/poly1305.go
new file mode 100644
index 0000000..51a1009
--- /dev/null
+++ b/src/ssl/test/runner/poly1305.go
@@ -0,0 +1,1540 @@
+// Copyright 2012 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
+
+// Based on original, public domain implementation from NaCl by D. J.
+// Bernstein.
+
+import (
+ "crypto/subtle"
+ "math"
+)
+
+const (
+ alpham80 = 0.00000000558793544769287109375
+ alpham48 = 24.0
+ alpham16 = 103079215104.0
+ alpha0 = 6755399441055744.0
+ alpha18 = 1770887431076116955136.0
+ alpha32 = 29014219670751100192948224.0
+ alpha50 = 7605903601369376408980219232256.0
+ alpha64 = 124615124604835863084731911901282304.0
+ alpha82 = 32667107224410092492483962313449748299776.0
+ alpha96 = 535217884764734955396857238543560676143529984.0
+ alpha112 = 35076039295941670036888435985190792471742381031424.0
+ alpha130 = 9194973245195333150150082162901855101712434733101613056.0
+ scale = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125
+ offset0 = 6755408030990331.0
+ offset1 = 29014256564239239022116864.0
+ offset2 = 124615283061160854719918951570079744.0
+ offset3 = 535219245894202480694386063513315216128475136.0
+)
+
+// poly1305Verify returns true if mac is a valid authenticator for m with the
+// given key.
+func poly1305Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+ var tmp [16]byte
+ poly1305Sum(&tmp, m, key)
+ return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
+
+// poly1305Sum generates an authenticator for m using a one-time key and puts
+// the 16-byte result into out. Authenticating two different messages with the
+// same key allows an attacker to forge messages at will.
+func poly1305Sum(out *[16]byte, m []byte, key *[32]byte) {
+ r := key
+ s := key[16:]
+ var (
+ y7 float64
+ y6 float64
+ y1 float64
+ y0 float64
+ y5 float64
+ y4 float64
+ x7 float64
+ x6 float64
+ x1 float64
+ x0 float64
+ y3 float64
+ y2 float64
+ x5 float64
+ r3lowx0 float64
+ x4 float64
+ r0lowx6 float64
+ x3 float64
+ r3highx0 float64
+ x2 float64
+ r0highx6 float64
+ r0lowx0 float64
+ sr1lowx6 float64
+ r0highx0 float64
+ sr1highx6 float64
+ sr3low float64
+ r1lowx0 float64
+ sr2lowx6 float64
+ r1highx0 float64
+ sr2highx6 float64
+ r2lowx0 float64
+ sr3lowx6 float64
+ r2highx0 float64
+ sr3highx6 float64
+ r1highx4 float64
+ r1lowx4 float64
+ r0highx4 float64
+ r0lowx4 float64
+ sr3highx4 float64
+ sr3lowx4 float64
+ sr2highx4 float64
+ sr2lowx4 float64
+ r0lowx2 float64
+ r0highx2 float64
+ r1lowx2 float64
+ r1highx2 float64
+ r2lowx2 float64
+ r2highx2 float64
+ sr3lowx2 float64
+ sr3highx2 float64
+ z0 float64
+ z1 float64
+ z2 float64
+ z3 float64
+ m0 int64
+ m1 int64
+ m2 int64
+ m3 int64
+ m00 uint32
+ m01 uint32
+ m02 uint32
+ m03 uint32
+ m10 uint32
+ m11 uint32
+ m12 uint32
+ m13 uint32
+ m20 uint32
+ m21 uint32
+ m22 uint32
+ m23 uint32
+ m30 uint32
+ m31 uint32
+ m32 uint32
+ m33 uint64
+ lbelow2 int32
+ lbelow3 int32
+ lbelow4 int32
+ lbelow5 int32
+ lbelow6 int32
+ lbelow7 int32
+ lbelow8 int32
+ lbelow9 int32
+ lbelow10 int32
+ lbelow11 int32
+ lbelow12 int32
+ lbelow13 int32
+ lbelow14 int32
+ lbelow15 int32
+ s00 uint32
+ s01 uint32
+ s02 uint32
+ s03 uint32
+ s10 uint32
+ s11 uint32
+ s12 uint32
+ s13 uint32
+ s20 uint32
+ s21 uint32
+ s22 uint32
+ s23 uint32
+ s30 uint32
+ s31 uint32
+ s32 uint32
+ s33 uint32
+ bits32 uint64
+ f uint64
+ f0 uint64
+ f1 uint64
+ f2 uint64
+ f3 uint64
+ f4 uint64
+ g uint64
+ g0 uint64
+ g1 uint64
+ g2 uint64
+ g3 uint64
+ g4 uint64
+ )
+
+ var p int32
+
+ l := int32(len(m))
+
+ r00 := uint32(r[0])
+
+ r01 := uint32(r[1])
+
+ r02 := uint32(r[2])
+ r0 := int64(2151)
+
+ r03 := uint32(r[3])
+ r03 &= 15
+ r0 <<= 51
+
+ r10 := uint32(r[4])
+ r10 &= 252
+ r01 <<= 8
+ r0 += int64(r00)
+
+ r11 := uint32(r[5])
+ r02 <<= 16
+ r0 += int64(r01)
+
+ r12 := uint32(r[6])
+ r03 <<= 24
+ r0 += int64(r02)
+
+ r13 := uint32(r[7])
+ r13 &= 15
+ r1 := int64(2215)
+ r0 += int64(r03)
+
+ d0 := r0
+ r1 <<= 51
+ r2 := int64(2279)
+
+ r20 := uint32(r[8])
+ r20 &= 252
+ r11 <<= 8
+ r1 += int64(r10)
+
+ r21 := uint32(r[9])
+ r12 <<= 16
+ r1 += int64(r11)
+
+ r22 := uint32(r[10])
+ r13 <<= 24
+ r1 += int64(r12)
+
+ r23 := uint32(r[11])
+ r23 &= 15
+ r2 <<= 51
+ r1 += int64(r13)
+
+ d1 := r1
+ r21 <<= 8
+ r2 += int64(r20)
+
+ r30 := uint32(r[12])
+ r30 &= 252
+ r22 <<= 16
+ r2 += int64(r21)
+
+ r31 := uint32(r[13])
+ r23 <<= 24
+ r2 += int64(r22)
+
+ r32 := uint32(r[14])
+ r2 += int64(r23)
+ r3 := int64(2343)
+
+ d2 := r2
+ r3 <<= 51
+
+ r33 := uint32(r[15])
+ r33 &= 15
+ r31 <<= 8
+ r3 += int64(r30)
+
+ r32 <<= 16
+ r3 += int64(r31)
+
+ r33 <<= 24
+ r3 += int64(r32)
+
+ r3 += int64(r33)
+ h0 := alpha32 - alpha32
+
+ d3 := r3
+ h1 := alpha32 - alpha32
+
+ h2 := alpha32 - alpha32
+
+ h3 := alpha32 - alpha32
+
+ h4 := alpha32 - alpha32
+
+ r0low := math.Float64frombits(uint64(d0))
+ h5 := alpha32 - alpha32
+
+ r1low := math.Float64frombits(uint64(d1))
+ h6 := alpha32 - alpha32
+
+ r2low := math.Float64frombits(uint64(d2))
+ h7 := alpha32 - alpha32
+
+ r0low -= alpha0
+
+ r1low -= alpha32
+
+ r2low -= alpha64
+
+ r0high := r0low + alpha18
+
+ r3low := math.Float64frombits(uint64(d3))
+
+ r1high := r1low + alpha50
+ sr1low := scale * r1low
+
+ r2high := r2low + alpha82
+ sr2low := scale * r2low
+
+ r0high -= alpha18
+ r0high_stack := r0high
+
+ r3low -= alpha96
+
+ r1high -= alpha50
+ r1high_stack := r1high
+
+ sr1high := sr1low + alpham80
+
+ r0low -= r0high
+
+ r2high -= alpha82
+ sr3low = scale * r3low
+
+ sr2high := sr2low + alpham48
+
+ r1low -= r1high
+ r1low_stack := r1low
+
+ sr1high -= alpham80
+ sr1high_stack := sr1high
+
+ r2low -= r2high
+ r2low_stack := r2low
+
+ sr2high -= alpham48
+ sr2high_stack := sr2high
+
+ r3high := r3low + alpha112
+ r0low_stack := r0low
+
+ sr1low -= sr1high
+ sr1low_stack := sr1low
+
+ sr3high := sr3low + alpham16
+ r2high_stack := r2high
+
+ sr2low -= sr2high
+ sr2low_stack := sr2low
+
+ r3high -= alpha112
+ r3high_stack := r3high
+
+ sr3high -= alpham16
+ sr3high_stack := sr3high
+
+ r3low -= r3high
+ r3low_stack := r3low
+
+ sr3low -= sr3high
+ sr3low_stack := sr3low
+
+ if l < 16 {
+ goto addatmost15bytes
+ }
+
+ m00 = uint32(m[p+0])
+ m0 = 2151
+
+ m0 <<= 51
+ m1 = 2215
+ m01 = uint32(m[p+1])
+
+ m1 <<= 51
+ m2 = 2279
+ m02 = uint32(m[p+2])
+
+ m2 <<= 51
+ m3 = 2343
+ m03 = uint32(m[p+3])
+
+ m10 = uint32(m[p+4])
+ m01 <<= 8
+ m0 += int64(m00)
+
+ m11 = uint32(m[p+5])
+ m02 <<= 16
+ m0 += int64(m01)
+
+ m12 = uint32(m[p+6])
+ m03 <<= 24
+ m0 += int64(m02)
+
+ m13 = uint32(m[p+7])
+ m3 <<= 51
+ m0 += int64(m03)
+
+ m20 = uint32(m[p+8])
+ m11 <<= 8
+ m1 += int64(m10)
+
+ m21 = uint32(m[p+9])
+ m12 <<= 16
+ m1 += int64(m11)
+
+ m22 = uint32(m[p+10])
+ m13 <<= 24
+ m1 += int64(m12)
+
+ m23 = uint32(m[p+11])
+ m1 += int64(m13)
+
+ m30 = uint32(m[p+12])
+ m21 <<= 8
+ m2 += int64(m20)
+
+ m31 = uint32(m[p+13])
+ m22 <<= 16
+ m2 += int64(m21)
+
+ m32 = uint32(m[p+14])
+ m23 <<= 24
+ m2 += int64(m22)
+
+ m33 = uint64(m[p+15])
+ m2 += int64(m23)
+
+ d0 = m0
+ m31 <<= 8
+ m3 += int64(m30)
+
+ d1 = m1
+ m32 <<= 16
+ m3 += int64(m31)
+
+ d2 = m2
+ m33 += 256
+
+ m33 <<= 24
+ m3 += int64(m32)
+
+ m3 += int64(m33)
+ d3 = m3
+
+ p += 16
+ l -= 16
+
+ z0 = math.Float64frombits(uint64(d0))
+
+ z1 = math.Float64frombits(uint64(d1))
+
+ z2 = math.Float64frombits(uint64(d2))
+
+ z3 = math.Float64frombits(uint64(d3))
+
+ z0 -= alpha0
+
+ z1 -= alpha32
+
+ z2 -= alpha64
+
+ z3 -= alpha96
+
+ h0 += z0
+
+ h1 += z1
+
+ h3 += z2
+
+ h5 += z3
+
+ if l < 16 {
+ goto multiplyaddatmost15bytes
+ }
+
+multiplyaddatleast16bytes:
+
+ m2 = 2279
+ m20 = uint32(m[p+8])
+ y7 = h7 + alpha130
+
+ m2 <<= 51
+ m3 = 2343
+ m21 = uint32(m[p+9])
+ y6 = h6 + alpha130
+
+ m3 <<= 51
+ m0 = 2151
+ m22 = uint32(m[p+10])
+ y1 = h1 + alpha32
+
+ m0 <<= 51
+ m1 = 2215
+ m23 = uint32(m[p+11])
+ y0 = h0 + alpha32
+
+ m1 <<= 51
+ m30 = uint32(m[p+12])
+ y7 -= alpha130
+
+ m21 <<= 8
+ m2 += int64(m20)
+ m31 = uint32(m[p+13])
+ y6 -= alpha130
+
+ m22 <<= 16
+ m2 += int64(m21)
+ m32 = uint32(m[p+14])
+ y1 -= alpha32
+
+ m23 <<= 24
+ m2 += int64(m22)
+ m33 = uint64(m[p+15])
+ y0 -= alpha32
+
+ m2 += int64(m23)
+ m00 = uint32(m[p+0])
+ y5 = h5 + alpha96
+
+ m31 <<= 8
+ m3 += int64(m30)
+ m01 = uint32(m[p+1])
+ y4 = h4 + alpha96
+
+ m32 <<= 16
+ m02 = uint32(m[p+2])
+ x7 = h7 - y7
+ y7 *= scale
+
+ m33 += 256
+ m03 = uint32(m[p+3])
+ x6 = h6 - y6
+ y6 *= scale
+
+ m33 <<= 24
+ m3 += int64(m31)
+ m10 = uint32(m[p+4])
+ x1 = h1 - y1
+
+ m01 <<= 8
+ m3 += int64(m32)
+ m11 = uint32(m[p+5])
+ x0 = h0 - y0
+
+ m3 += int64(m33)
+ m0 += int64(m00)
+ m12 = uint32(m[p+6])
+ y5 -= alpha96
+
+ m02 <<= 16
+ m0 += int64(m01)
+ m13 = uint32(m[p+7])
+ y4 -= alpha96
+
+ m03 <<= 24
+ m0 += int64(m02)
+ d2 = m2
+ x1 += y7
+
+ m0 += int64(m03)
+ d3 = m3
+ x0 += y6
+
+ m11 <<= 8
+ m1 += int64(m10)
+ d0 = m0
+ x7 += y5
+
+ m12 <<= 16
+ m1 += int64(m11)
+ x6 += y4
+
+ m13 <<= 24
+ m1 += int64(m12)
+ y3 = h3 + alpha64
+
+ m1 += int64(m13)
+ d1 = m1
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+ z2 = math.Float64frombits(uint64(d2))
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+ z3 = math.Float64frombits(uint64(d3))
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+ z2 -= alpha64
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+ z3 -= alpha96
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ p += 16
+ l -= 16
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ z1 = math.Float64frombits(uint64(d1))
+ h0 += sr3lowx2
+
+ z0 = math.Float64frombits(uint64(d0))
+ h1 += sr3highx2
+
+ z1 -= alpha32
+
+ z0 -= alpha0
+
+ h5 += z3
+
+ h3 += z2
+
+ h1 += z1
+
+ h0 += z0
+
+ if l >= 16 {
+ goto multiplyaddatleast16bytes
+ }
+
+multiplyaddatmost15bytes:
+
+ y7 = h7 + alpha130
+
+ y6 = h6 + alpha130
+
+ y1 = h1 + alpha32
+
+ y0 = h0 + alpha32
+
+ y7 -= alpha130
+
+ y6 -= alpha130
+
+ y1 -= alpha32
+
+ y0 -= alpha32
+
+ y5 = h5 + alpha96
+
+ y4 = h4 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ x6 = h6 - y6
+ y6 *= scale
+
+ x1 = h1 - y1
+
+ x0 = h0 - y0
+
+ y5 -= alpha96
+
+ y4 -= alpha96
+
+ x1 += y7
+
+ x0 += y6
+
+ x7 += y5
+
+ x6 += y4
+
+ y3 = h3 + alpha64
+
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ h0 += sr3lowx2
+
+ h1 += sr3highx2
+
+addatmost15bytes:
+
+ if l == 0 {
+ goto nomorebytes
+ }
+
+ lbelow2 = l - 2
+
+ lbelow3 = l - 3
+
+ lbelow2 >>= 31
+ lbelow4 = l - 4
+
+ m00 = uint32(m[p+0])
+ lbelow3 >>= 31
+ p += lbelow2
+
+ m01 = uint32(m[p+1])
+ lbelow4 >>= 31
+ p += lbelow3
+
+ m02 = uint32(m[p+2])
+ p += lbelow4
+ m0 = 2151
+
+ m03 = uint32(m[p+3])
+ m0 <<= 51
+ m1 = 2215
+
+ m0 += int64(m00)
+ m01 &^= uint32(lbelow2)
+
+ m02 &^= uint32(lbelow3)
+ m01 -= uint32(lbelow2)
+
+ m01 <<= 8
+ m03 &^= uint32(lbelow4)
+
+ m0 += int64(m01)
+ lbelow2 -= lbelow3
+
+ m02 += uint32(lbelow2)
+ lbelow3 -= lbelow4
+
+ m02 <<= 16
+ m03 += uint32(lbelow3)
+
+ m03 <<= 24
+ m0 += int64(m02)
+
+ m0 += int64(m03)
+ lbelow5 = l - 5
+
+ lbelow6 = l - 6
+ lbelow7 = l - 7
+
+ lbelow5 >>= 31
+ lbelow8 = l - 8
+
+ lbelow6 >>= 31
+ p += lbelow5
+
+ m10 = uint32(m[p+4])
+ lbelow7 >>= 31
+ p += lbelow6
+
+ m11 = uint32(m[p+5])
+ lbelow8 >>= 31
+ p += lbelow7
+
+ m12 = uint32(m[p+6])
+ m1 <<= 51
+ p += lbelow8
+
+ m13 = uint32(m[p+7])
+ m10 &^= uint32(lbelow5)
+ lbelow4 -= lbelow5
+
+ m10 += uint32(lbelow4)
+ lbelow5 -= lbelow6
+
+ m11 &^= uint32(lbelow6)
+ m11 += uint32(lbelow5)
+
+ m11 <<= 8
+ m1 += int64(m10)
+
+ m1 += int64(m11)
+ m12 &^= uint32(lbelow7)
+
+ lbelow6 -= lbelow7
+ m13 &^= uint32(lbelow8)
+
+ m12 += uint32(lbelow6)
+ lbelow7 -= lbelow8
+
+ m12 <<= 16
+ m13 += uint32(lbelow7)
+
+ m13 <<= 24
+ m1 += int64(m12)
+
+ m1 += int64(m13)
+ m2 = 2279
+
+ lbelow9 = l - 9
+ m3 = 2343
+
+ lbelow10 = l - 10
+ lbelow11 = l - 11
+
+ lbelow9 >>= 31
+ lbelow12 = l - 12
+
+ lbelow10 >>= 31
+ p += lbelow9
+
+ m20 = uint32(m[p+8])
+ lbelow11 >>= 31
+ p += lbelow10
+
+ m21 = uint32(m[p+9])
+ lbelow12 >>= 31
+ p += lbelow11
+
+ m22 = uint32(m[p+10])
+ m2 <<= 51
+ p += lbelow12
+
+ m23 = uint32(m[p+11])
+ m20 &^= uint32(lbelow9)
+ lbelow8 -= lbelow9
+
+ m20 += uint32(lbelow8)
+ lbelow9 -= lbelow10
+
+ m21 &^= uint32(lbelow10)
+ m21 += uint32(lbelow9)
+
+ m21 <<= 8
+ m2 += int64(m20)
+
+ m2 += int64(m21)
+ m22 &^= uint32(lbelow11)
+
+ lbelow10 -= lbelow11
+ m23 &^= uint32(lbelow12)
+
+ m22 += uint32(lbelow10)
+ lbelow11 -= lbelow12
+
+ m22 <<= 16
+ m23 += uint32(lbelow11)
+
+ m23 <<= 24
+ m2 += int64(m22)
+
+ m3 <<= 51
+ lbelow13 = l - 13
+
+ lbelow13 >>= 31
+ lbelow14 = l - 14
+
+ lbelow14 >>= 31
+ p += lbelow13
+ lbelow15 = l - 15
+
+ m30 = uint32(m[p+12])
+ lbelow15 >>= 31
+ p += lbelow14
+
+ m31 = uint32(m[p+13])
+ p += lbelow15
+ m2 += int64(m23)
+
+ m32 = uint32(m[p+14])
+ m30 &^= uint32(lbelow13)
+ lbelow12 -= lbelow13
+
+ m30 += uint32(lbelow12)
+ lbelow13 -= lbelow14
+
+ m3 += int64(m30)
+ m31 &^= uint32(lbelow14)
+
+ m31 += uint32(lbelow13)
+ m32 &^= uint32(lbelow15)
+
+ m31 <<= 8
+ lbelow14 -= lbelow15
+
+ m3 += int64(m31)
+ m32 += uint32(lbelow14)
+ d0 = m0
+
+ m32 <<= 16
+ m33 = uint64(lbelow15 + 1)
+ d1 = m1
+
+ m33 <<= 24
+ m3 += int64(m32)
+ d2 = m2
+
+ m3 += int64(m33)
+ d3 = m3
+
+ z3 = math.Float64frombits(uint64(d3))
+
+ z2 = math.Float64frombits(uint64(d2))
+
+ z1 = math.Float64frombits(uint64(d1))
+
+ z0 = math.Float64frombits(uint64(d0))
+
+ z3 -= alpha96
+
+ z2 -= alpha64
+
+ z1 -= alpha32
+
+ z0 -= alpha0
+
+ h5 += z3
+
+ h3 += z2
+
+ h1 += z1
+
+ h0 += z0
+
+ y7 = h7 + alpha130
+
+ y6 = h6 + alpha130
+
+ y1 = h1 + alpha32
+
+ y0 = h0 + alpha32
+
+ y7 -= alpha130
+
+ y6 -= alpha130
+
+ y1 -= alpha32
+
+ y0 -= alpha32
+
+ y5 = h5 + alpha96
+
+ y4 = h4 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ x6 = h6 - y6
+ y6 *= scale
+
+ x1 = h1 - y1
+
+ x0 = h0 - y0
+
+ y5 -= alpha96
+
+ y4 -= alpha96
+
+ x1 += y7
+
+ x0 += y6
+
+ x7 += y5
+
+ x6 += y4
+
+ y3 = h3 + alpha64
+
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ h0 += sr3lowx2
+
+ h1 += sr3highx2
+
+nomorebytes:
+
+ y7 = h7 + alpha130
+
+ y0 = h0 + alpha32
+
+ y1 = h1 + alpha32
+
+ y2 = h2 + alpha64
+
+ y7 -= alpha130
+
+ y3 = h3 + alpha64
+
+ y4 = h4 + alpha96
+
+ y5 = h5 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ y0 -= alpha32
+
+ y1 -= alpha32
+
+ y2 -= alpha64
+
+ h6 += x7
+
+ y3 -= alpha64
+
+ y4 -= alpha96
+
+ y5 -= alpha96
+
+ y6 = h6 + alpha130
+
+ x0 = h0 - y0
+
+ x1 = h1 - y1
+
+ x2 = h2 - y2
+
+ y6 -= alpha130
+
+ x0 += y7
+
+ x3 = h3 - y3
+
+ x4 = h4 - y4
+
+ x5 = h5 - y5
+
+ x6 = h6 - y6
+
+ y6 *= scale
+
+ x2 += y0
+
+ x3 += y1
+
+ x4 += y2
+
+ x0 += y6
+
+ x5 += y3
+
+ x6 += y4
+
+ x2 += x3
+
+ x0 += x1
+
+ x4 += x5
+
+ x6 += y5
+
+ x2 += offset1
+ d1 = int64(math.Float64bits(x2))
+
+ x0 += offset0
+ d0 = int64(math.Float64bits(x0))
+
+ x4 += offset2
+ d2 = int64(math.Float64bits(x4))
+
+ x6 += offset3
+ d3 = int64(math.Float64bits(x6))
+
+ f0 = uint64(d0)
+
+ f1 = uint64(d1)
+ bits32 = math.MaxUint64
+
+ f2 = uint64(d2)
+ bits32 >>= 32
+
+ f3 = uint64(d3)
+ f = f0 >> 32
+
+ f0 &= bits32
+ f &= 255
+
+ f1 += f
+ g0 = f0 + 5
+
+ g = g0 >> 32
+ g0 &= bits32
+
+ f = f1 >> 32
+ f1 &= bits32
+
+ f &= 255
+ g1 = f1 + g
+
+ g = g1 >> 32
+ f2 += f
+
+ f = f2 >> 32
+ g1 &= bits32
+
+ f2 &= bits32
+ f &= 255
+
+ f3 += f
+ g2 = f2 + g
+
+ g = g2 >> 32
+ g2 &= bits32
+
+ f4 = f3 >> 32
+ f3 &= bits32
+
+ f4 &= 255
+ g3 = f3 + g
+
+ g = g3 >> 32
+ g3 &= bits32
+
+ g4 = f4 + g
+
+ g4 = g4 - 4
+ s00 = uint32(s[0])
+
+ f = uint64(int64(g4) >> 63)
+ s01 = uint32(s[1])
+
+ f0 &= f
+ g0 &^= f
+ s02 = uint32(s[2])
+
+ f1 &= f
+ f0 |= g0
+ s03 = uint32(s[3])
+
+ g1 &^= f
+ f2 &= f
+ s10 = uint32(s[4])
+
+ f3 &= f
+ g2 &^= f
+ s11 = uint32(s[5])
+
+ g3 &^= f
+ f1 |= g1
+ s12 = uint32(s[6])
+
+ f2 |= g2
+ f3 |= g3
+ s13 = uint32(s[7])
+
+ s01 <<= 8
+ f0 += uint64(s00)
+ s20 = uint32(s[8])
+
+ s02 <<= 16
+ f0 += uint64(s01)
+ s21 = uint32(s[9])
+
+ s03 <<= 24
+ f0 += uint64(s02)
+ s22 = uint32(s[10])
+
+ s11 <<= 8
+ f1 += uint64(s10)
+ s23 = uint32(s[11])
+
+ s12 <<= 16
+ f1 += uint64(s11)
+ s30 = uint32(s[12])
+
+ s13 <<= 24
+ f1 += uint64(s12)
+ s31 = uint32(s[13])
+
+ f0 += uint64(s03)
+ f1 += uint64(s13)
+ s32 = uint32(s[14])
+
+ s21 <<= 8
+ f2 += uint64(s20)
+ s33 = uint32(s[15])
+
+ s22 <<= 16
+ f2 += uint64(s21)
+
+ s23 <<= 24
+ f2 += uint64(s22)
+
+ s31 <<= 8
+ f3 += uint64(s30)
+
+ s32 <<= 16
+ f3 += uint64(s31)
+
+ s33 <<= 24
+ f3 += uint64(s32)
+
+ f2 += uint64(s23)
+ f3 += uint64(s33)
+
+ out[0] = byte(f0)
+ f0 >>= 8
+ out[1] = byte(f0)
+ f0 >>= 8
+ out[2] = byte(f0)
+ f0 >>= 8
+ out[3] = byte(f0)
+ f0 >>= 8
+ f1 += f0
+
+ out[4] = byte(f1)
+ f1 >>= 8
+ out[5] = byte(f1)
+ f1 >>= 8
+ out[6] = byte(f1)
+ f1 >>= 8
+ out[7] = byte(f1)
+ f1 >>= 8
+ f2 += f1
+
+ out[8] = byte(f2)
+ f2 >>= 8
+ out[9] = byte(f2)
+ f2 >>= 8
+ out[10] = byte(f2)
+ f2 >>= 8
+ out[11] = byte(f2)
+ f2 >>= 8
+ f3 += f2
+
+ out[12] = byte(f3)
+ f3 >>= 8
+ out[13] = byte(f3)
+ f3 >>= 8
+ out[14] = byte(f3)
+ f3 >>= 8
+ out[15] = byte(f3)
+}
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index 75a8933..d445e76 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -323,14 +323,14 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
// selectClientCertSignatureAlgorithm returns a signatureAndHash to sign a
// client's CertificateVerify with, or an error if none can be found.
-func (h finishedHash) selectClientCertSignatureAlgorithm(serverList []signatureAndHash, sigType uint8) (signatureAndHash, error) {
+func (h finishedHash) selectClientCertSignatureAlgorithm(serverList, clientList []signatureAndHash, sigType uint8) (signatureAndHash, error) {
if h.version < VersionTLS12 {
// Nothing to negotiate before TLS 1.2.
return signatureAndHash{signature: sigType}, nil
}
for _, v := range serverList {
- if v.signature == sigType && v.hash == hashSHA256 {
+ if v.signature == sigType && isSupportedSignatureAndHash(v, clientList) {
return v, nil
}
}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index aaa2a4d..ec2fede 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -20,14 +20,17 @@ import (
"strings"
"sync"
"syscall"
+ "time"
)
var (
- useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
- useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
- flagDebug *bool = flag.Bool("debug", false, "Hexdump the contents of the connection")
- mallocTest *int64 = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
- mallocTestDebug *bool = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
+ useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
+ useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+ flagDebug = flag.Bool("debug", false, "Hexdump the contents of the connection")
+ mallocTest = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
+ mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
+ jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
+ pipe = flag.Bool("pipe", false, "If true, print status output suitable for piping into another program.")
)
const (
@@ -132,6 +135,9 @@ type testCase struct {
// expectedResumeVersion, if non-zero, specifies the TLS version that
// must be negotiated on resumption. If zero, expectedVersion is used.
expectedResumeVersion uint16
+ // expectedCipher, if non-zero, specifies the TLS cipher suite that
+ // should be negotiated.
+ expectedCipher uint16
// expectChannelID controls whether the connection should have
// negotiated a Channel ID with channelIDKey.
expectChannelID bool
@@ -181,6 +187,12 @@ type testCase struct {
// damageFirstWrite, if true, configures the underlying transport to
// damage the final byte of the first application data write.
damageFirstWrite bool
+ // exportKeyingMaterial, if non-zero, configures the test to exchange
+ // keying material and verify they match.
+ exportKeyingMaterial int
+ exportLabel string
+ exportContext string
+ useExportContext bool
// flags, if not empty, contains a list of command-line flags that will
// be passed to the shim program.
flags []string
@@ -292,6 +304,18 @@ var testCases = []testCase{
expectedError: ":UNEXPECTED_MESSAGE:",
},
{
+ name: "SkipCertificateStatus",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ SkipCertificateStatus: true,
+ },
+ },
+ flags: []string{
+ "-enable-ocsp-stapling",
+ },
+ },
+ {
name: "SkipServerKeyExchange",
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
@@ -376,11 +400,47 @@ var testCases = []testCase{
},
{
testType: serverTest,
+ name: "Alert",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSpuriousAlert: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+ },
+ {
+ protocol: dtls,
+ testType: serverTest,
+ name: "Alert-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSpuriousAlert: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+ },
+ {
+ testType: serverTest,
name: "FragmentAlert",
config: Config{
Bugs: ProtocolBugs{
FragmentAlert: true,
- SendSpuriousAlert: true,
+ SendSpuriousAlert: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":BAD_ALERT:",
+ },
+ {
+ protocol: dtls,
+ testType: serverTest,
+ name: "FragmentAlert-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FragmentAlert: true,
+ SendSpuriousAlert: alertRecordOverflow,
},
},
shouldFail: true,
@@ -536,11 +596,11 @@ var testCases = []testCase{
expectedError: ":WRONG_CIPHER_RETURNED:",
},
{
- name: "RSAServerKeyExchange",
+ name: "RSAEphemeralKey",
config: Config{
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
Bugs: ProtocolBugs{
- RSAServerKeyExchange: true,
+ RSAEphemeralKey: true,
},
},
shouldFail: true,
@@ -650,6 +710,380 @@ var testCases = []testCase{
AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"),
},
},
+ // BoringSSL's DTLS implementation will drop the out-of-order
+ // application data.
+ },
+ {
+ name: "AlertAfterChangeCipherSpec",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AlertAfterChangeCipherSpec: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+ },
+ {
+ protocol: dtls,
+ name: "AlertAfterChangeCipherSpec-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AlertAfterChangeCipherSpec: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+ },
+ {
+ protocol: dtls,
+ name: "ReorderHandshakeFragments-Small-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ReorderHandshakeFragments: true,
+ // Small enough that every handshake message is
+ // fragmented.
+ MaxHandshakeRecordLength: 2,
+ },
+ },
+ },
+ {
+ protocol: dtls,
+ name: "ReorderHandshakeFragments-Large-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ReorderHandshakeFragments: true,
+ // Large enough that no handshake message is
+ // fragmented.
+ MaxHandshakeRecordLength: 2048,
+ },
+ },
+ },
+ {
+ protocol: dtls,
+ name: "MixCompleteMessageWithFragments-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ReorderHandshakeFragments: true,
+ MixCompleteMessageWithFragments: true,
+ MaxHandshakeRecordLength: 2,
+ },
+ },
+ },
+ {
+ name: "SendInvalidRecordType",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendInvalidRecordType: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ protocol: dtls,
+ name: "SendInvalidRecordType-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendInvalidRecordType: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ name: "FalseStart-SkipServerSecondLeg",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ SkipNewSessionTicket: true,
+ SkipChangeCipherSpec: true,
+ SkipFinished: true,
+ ExpectFalseStart: true,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-handshake-never-done",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ name: "FalseStart-SkipServerSecondLeg-Implicit",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ SkipNewSessionTicket: true,
+ SkipChangeCipherSpec: true,
+ SkipFinished: true,
+ },
+ },
+ flags: []string{
+ "-implicit-handshake",
+ "-false-start",
+ "-handshake-never-done",
+ "-advertise-alpn", "\x03foo",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ testType: serverTest,
+ name: "FailEarlyCallback",
+ flags: []string{"-fail-early-callback"},
+ shouldFail: true,
+ expectedError: ":CONNECTION_REJECTED:",
+ expectedLocalError: "remote error: access denied",
+ },
+ {
+ name: "WrongMessageType",
+ config: Config{
+ Bugs: ProtocolBugs{
+ WrongCertificateMessageType: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ expectedLocalError: "remote error: unexpected message",
+ },
+ {
+ protocol: dtls,
+ name: "WrongMessageType-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ WrongCertificateMessageType: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ expectedLocalError: "remote error: unexpected message",
+ },
+ {
+ protocol: dtls,
+ name: "FragmentMessageTypeMismatch-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 2,
+ FragmentMessageTypeMismatch: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":FRAGMENT_MISMATCH:",
+ },
+ {
+ protocol: dtls,
+ name: "FragmentMessageLengthMismatch-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 2,
+ FragmentMessageLengthMismatch: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":FRAGMENT_MISMATCH:",
+ },
+ {
+ protocol: dtls,
+ name: "SplitFragmentHeader-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SplitFragmentHeader: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ },
+ {
+ protocol: dtls,
+ name: "SplitFragmentBody-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SplitFragmentBody: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ },
+ {
+ protocol: dtls,
+ name: "SendEmptyFragments-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendEmptyFragments: true,
+ },
+ },
+ },
+ {
+ name: "UnsupportedCipherSuite",
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+ Bugs: ProtocolBugs{
+ IgnorePeerCipherPreferences: true,
+ },
+ },
+ flags: []string{"-cipher", "DEFAULT:!RC4"},
+ shouldFail: true,
+ expectedError: ":WRONG_CIPHER_RETURNED:",
+ },
+ {
+ name: "UnsupportedCurve",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ // BoringSSL implements P-224 but doesn't enable it by
+ // default.
+ CurvePreferences: []CurveID{CurveP224},
+ Bugs: ProtocolBugs{
+ IgnorePeerCurvePreferences: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ },
+ {
+ name: "SendWarningAlerts",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendWarningAlerts: alertAccessDenied,
+ },
+ },
+ },
+ {
+ protocol: dtls,
+ name: "SendWarningAlerts-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendWarningAlerts: alertAccessDenied,
+ },
+ },
+ },
+ {
+ name: "BadFinished",
+ config: Config{
+ Bugs: ProtocolBugs{
+ BadFinished: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ },
+ {
+ name: "FalseStart-BadFinished",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ BadFinished: true,
+ ExpectFalseStart: true,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-handshake-never-done",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ },
+ {
+ name: "NoFalseStart-NoALPN",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ ExpectFalseStart: true,
+ AlertBeforeFalseStartTest: alertAccessDenied,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_ACCESS_DENIED:",
+ expectedLocalError: "tls: peer did not false start: EOF",
+ },
+ {
+ name: "NoFalseStart-NoAEAD",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ ExpectFalseStart: true,
+ AlertBeforeFalseStartTest: alertAccessDenied,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_ACCESS_DENIED:",
+ expectedLocalError: "tls: peer did not false start: EOF",
+ },
+ {
+ name: "NoFalseStart-RSA",
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ ExpectFalseStart: true,
+ AlertBeforeFalseStartTest: alertAccessDenied,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_ACCESS_DENIED:",
+ expectedLocalError: "tls: peer did not false start: EOF",
+ },
+ {
+ name: "NoFalseStart-DHE_RSA",
+ config: Config{
+ CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ ExpectFalseStart: true,
+ AlertBeforeFalseStartTest: alertAccessDenied,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ shouldFail: true,
+ expectedError: ":TLSV1_ALERT_ACCESS_DENIED:",
+ expectedLocalError: "tls: peer did not false start: EOF",
+ },
+ {
+ testType: serverTest,
+ name: "NoSupportedCurves",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ NoSupportedCurves: true,
+ },
+ },
+ },
+ {
+ testType: serverTest,
+ name: "NoCommonCurves",
+ config: Config{
+ CipherSuites: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ CurvePreferences: []CurveID{CurveP224},
+ },
+ expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
},
}
@@ -665,7 +1099,8 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
}
if test.protocol == dtls {
- conn = newPacketAdaptor(conn)
+ config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
+ conn = config.Bugs.PacketAdaptor
if test.replayWrites {
conn = newReplayAdaptor(conn)
}
@@ -713,6 +1148,10 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
return fmt.Errorf("got version %x, expected %x", vers, expectedVersion)
}
+ if cipher := tlsConn.ConnectionState().CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher {
+ return fmt.Errorf("got cipher %x, expected %x", cipher, test.expectedCipher)
+ }
+
if test.expectChannelID {
channelID := tlsConn.ConnectionState().ChannelID
if channelID == nil {
@@ -741,6 +1180,20 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile)
}
+ if test.exportKeyingMaterial > 0 {
+ actual := make([]byte, test.exportKeyingMaterial)
+ if _, err := io.ReadFull(tlsConn, actual); err != nil {
+ return err
+ }
+ expected, err := tlsConn.ExportKeyingMaterial(test.exportKeyingMaterial, []byte(test.exportLabel), []byte(test.exportContext), test.useExportContext)
+ if err != nil {
+ return err
+ }
+ if !bytes.Equal(actual, expected) {
+ return fmt.Errorf("keying material mismatch")
+ }
+ }
+
if test.shimWritesFirst {
var buf [5]byte
_, err := io.ReadFull(tlsConn, buf[:])
@@ -778,21 +1231,14 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
return err
}
- var testMessage []byte
- if config.Bugs.AppDataAfterChangeCipherSpec != nil {
- // We've already sent a message. Expect the shim to echo it
- // back.
- testMessage = config.Bugs.AppDataAfterChangeCipherSpec
- } else {
- if messageLen == 0 {
- messageLen = 32
- }
- testMessage = make([]byte, messageLen)
- for i := range testMessage {
- testMessage[i] = 0x42
- }
- tlsConn.Write(testMessage)
+ if messageLen == 0 {
+ messageLen = 32
}
+ testMessage := make([]byte, messageLen)
+ for i := range testMessage {
+ testMessage[i] = 0x42
+ }
+ tlsConn.Write(testMessage)
buf := make([]byte, len(testMessage))
if test.protocol == dtls {
@@ -840,27 +1286,6 @@ func gdbOf(path string, args ...string) *exec.Cmd {
return exec.Command("xterm", xtermArgs...)
}
-func openSocketPair() (shimEnd *os.File, conn net.Conn) {
- socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
- if err != nil {
- panic(err)
- }
-
- syscall.CloseOnExec(socks[0])
- syscall.CloseOnExec(socks[1])
- shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
- connFile := os.NewFile(uintptr(socks[1]), "our end")
- conn, err = net.FileConn(connFile)
- if err != nil {
- panic(err)
- }
- connFile.Close()
- if err != nil {
- panic(err)
- }
- return shimEnd, conn
-}
-
type moreMallocsError struct{}
func (moreMallocsError) Error() string {
@@ -869,16 +1294,45 @@ func (moreMallocsError) Error() string {
var errMoreMallocs = moreMallocsError{}
+// accept accepts a connection from listener, unless waitChan signals a process
+// exit first.
+func acceptOrWait(listener net.Listener, waitChan chan error) (net.Conn, error) {
+ type connOrError struct {
+ conn net.Conn
+ err error
+ }
+ connChan := make(chan connOrError, 1)
+ go func() {
+ conn, err := listener.Accept()
+ connChan <- connOrError{conn, err}
+ close(connChan)
+ }()
+ select {
+ case result := <-connChan:
+ return result.conn, result.err
+ case childErr := <-waitChan:
+ waitChan <- childErr
+ return nil, fmt.Errorf("child exited early: %s", childErr)
+ }
+}
+
func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) {
panic("Error expected without shouldFail in " + test.name)
}
- shimEnd, conn := openSocketPair()
- shimEndResume, connResume := openSocketPair()
+ listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
+ if err != nil {
+ panic(err)
+ }
+ defer func() {
+ if listener != nil {
+ listener.Close()
+ }
+ }()
shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
- var flags []string
+ flags := []string{"-port", strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)}
if test.testType == serverTest {
flags = append(flags, "-server")
@@ -909,6 +1363,15 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
flags = append(flags, "-shim-writes-first")
}
+ if test.exportKeyingMaterial > 0 {
+ flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial))
+ flags = append(flags, "-export-label", test.exportLabel)
+ flags = append(flags, "-export-context", test.exportContext)
+ if test.useExportContext {
+ flags = append(flags, "-use-export-context")
+ }
+ }
+
flags = append(flags, test.flags...)
var shim *exec.Cmd
@@ -919,13 +1382,13 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
} else {
shim = exec.Command(shim_path, flags...)
}
- shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
shim.Stdin = os.Stdin
var stdoutBuf, stderrBuf bytes.Buffer
shim.Stdout = &stdoutBuf
shim.Stderr = &stderrBuf
if mallocNumToFail >= 0 {
- shim.Env = []string{"MALLOC_NUMBER_TO_FAIL=" + strconv.FormatInt(mallocNumToFail, 10)}
+ shim.Env = os.Environ()
+ shim.Env = append(shim.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10))
if *mallocTestDebug {
shim.Env = append(shim.Env, "MALLOC_ABORT_ON_FAIL=1")
}
@@ -935,8 +1398,8 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
if err := shim.Start(); err != nil {
panic(err)
}
- shimEnd.Close()
- shimEndResume.Close()
+ waitChan := make(chan error, 1)
+ go func() { waitChan <- shim.Wait() }()
config := test.config
config.ClientSessionCache = NewLRUClientSessionCache(1)
@@ -945,16 +1408,27 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
if len(config.Certificates) == 0 {
config.Certificates = []Certificate{getRSACertificate()}
}
+ } else {
+ // Supply a ServerName to ensure a constant session cache key,
+ // rather than falling back to net.Conn.RemoteAddr.
+ if len(config.ServerName) == 0 {
+ config.ServerName = "test"
+ }
}
- err := doExchange(test, &config, conn, test.messageLen,
- false /* not a resumption */)
- conn.Close()
+ conn, err := acceptOrWait(listener, waitChan)
+ if err == nil {
+ err = doExchange(test, &config, conn, test.messageLen, false /* not a resumption */)
+ conn.Close()
+ }
if err == nil && test.resumeSession {
var resumeConfig Config
if test.resumeConfig != nil {
resumeConfig = *test.resumeConfig
+ if len(resumeConfig.ServerName) == 0 {
+ resumeConfig.ServerName = config.ServerName
+ }
if len(resumeConfig.Certificates) == 0 {
resumeConfig.Certificates = []Certificate{getRSACertificate()}
}
@@ -966,12 +1440,20 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
} else {
resumeConfig = config
}
- err = doExchange(test, &resumeConfig, connResume, test.messageLen,
- true /* resumption */)
+ var connResume net.Conn
+ connResume, err = acceptOrWait(listener, waitChan)
+ if err == nil {
+ err = doExchange(test, &resumeConfig, connResume, test.messageLen, true /* resumption */)
+ connResume.Close()
+ }
}
- connResume.Close()
- childErr := shim.Wait()
+ // Close the listener now. This is to avoid hangs should the shim try to
+ // open more connections than expected.
+ listener.Close()
+ listener = nil
+
+ childErr := <-waitChan
if exitError, ok := childErr.(*exec.ExitError); ok {
if exitError.Sys().(syscall.WaitStatus).ExitStatus() == 88 {
return errMoreMallocs
@@ -981,7 +1463,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
stdout := string(stdoutBuf.Bytes())
stderr := string(stderrBuf.Bytes())
failed := err != nil || childErr != nil
- correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
+ correctFailure := len(test.expectedError) == 0 || strings.Contains(stderr, test.expectedError)
localError := "none"
if err != nil {
localError = err.Error()
@@ -1008,10 +1490,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
panic("internal error")
}
- return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
+ return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, stdout, stderr)
}
- if !*useValgrind && len(stderr) > 0 {
+ if !*useValgrind && !failed && len(stderr) > 0 {
println(stderr)
}
@@ -1047,12 +1529,14 @@ var testCipherSuites = []struct {
{"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
{"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
{"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
+ {"DHE-RSA-CHACHA20-POLY1305", TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
{"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
{"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
{"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
{"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
{"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
+ {"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
{"ECDHE-PSK-AES128-GCM-SHA256", TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256},
{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
@@ -1061,6 +1545,7 @@ var testCipherSuites = []struct {
{"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
{"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
+ {"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
@@ -1076,7 +1561,8 @@ func hasComponent(suiteName, component string) bool {
func isTLS12Only(suiteName string) bool {
return hasComponent(suiteName, "GCM") ||
hasComponent(suiteName, "SHA256") ||
- hasComponent(suiteName, "SHA384")
+ hasComponent(suiteName, "SHA384") ||
+ hasComponent(suiteName, "POLY1305")
}
func isDTLSCipher(suiteName string) bool {
@@ -1454,6 +1940,17 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
})
testCases = append(testCases, testCase{
protocol: protocol,
+ name: "Basic-Client-Implicit" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: append(flags, "-implicit-handshake"),
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
testType: serverTest,
name: "Basic-Server" + suffix,
config: Config{
@@ -1477,6 +1974,30 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
flags: flags,
resumeSession: true,
})
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "Basic-Server-Implicit" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: append(flags, "-implicit-handshake"),
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "Basic-Server-EarlyCallback" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: append(flags, "-use-early-callback"),
+ resumeSession: true,
+ })
// TLS client auth.
testCases = append(testCases, testCase{
@@ -1588,6 +2109,8 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
expectedNextProtoType: npn,
})
+ // TODO(davidben): Add tests for when False Start doesn't trigger.
+
// Client does False Start and negotiates NPN.
testCases = append(testCases, testCase{
protocol: protocol,
@@ -1626,9 +2149,27 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
resumeSession: true,
})
+ // Client does False Start but doesn't explicitly call
+ // SSL_connect.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "FalseStart-Implicit" + suffix,
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: append(flags,
+ "-implicit-handshake",
+ "-false-start",
+ "-advertise-alpn", "\x03foo"),
+ })
+
// False Start without session tickets.
testCases = append(testCases, testCase{
- name: "FalseStart-SessionTicketsDisabled",
+ name: "FalseStart-SessionTicketsDisabled" + suffix,
config: Config{
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"foo"},
@@ -1710,17 +2251,36 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
},
flags: flags,
})
+ }
+}
+
+func addDDoSCallbackTests() {
+ // DDoS callback.
+
+ for _, resume := range []bool{false, true} {
+ suffix := "Resume"
+ if resume {
+ suffix = "No" + suffix
+ }
testCases = append(testCases, testCase{
- testType: serverTest,
- protocol: protocol,
- name: "CookieExchange" + suffix,
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: maxHandshakeRecordLength,
- },
- },
- flags: append(flags, "-cookie-exchange"),
+ testType: serverTest,
+ name: "Server-DDoS-OK-" + suffix,
+ flags: []string{"-install-ddos-callback"},
+ resumeSession: resume,
+ })
+
+ failFlag := "-fail-ddos-callback"
+ if resume {
+ failFlag = "-fail-second-ddos-callback"
+ }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Server-DDoS-Reject-" + suffix,
+ flags: []string{"-install-ddos-callback", failFlag},
+ resumeSession: resume,
+ shouldFail: true,
+ expectedError: ":CONNECTION_REJECTED:",
})
}
}
@@ -1976,7 +2536,7 @@ func addExtensionTests() {
})
testCases = append(testCases, testCase{
testType: clientTest,
- name: "ServerNameExtensionClient",
+ name: "ServerNameExtensionClientMismatch",
config: Config{
Bugs: ProtocolBugs{
ExpectServerName: "mismatch.com",
@@ -1988,7 +2548,7 @@ func addExtensionTests() {
})
testCases = append(testCases, testCase{
testType: clientTest,
- name: "ServerNameExtensionClient",
+ name: "ServerNameExtensionClientMissing",
config: Config{
Bugs: ProtocolBugs{
ExpectServerName: "missing.com",
@@ -2201,27 +2761,40 @@ func addResumptionVersionTests() {
suffix += "-DTLS"
}
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Resume-Client" + suffix,
- resumeSession: true,
- config: Config{
- MaxVersion: sessionVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
- Bugs: ProtocolBugs{
- AllowSessionVersionMismatch: true,
+ if sessionVers.version == resumeVers.version {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "Resume-Client" + suffix,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
},
- },
- expectedVersion: sessionVers.version,
- resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
- Bugs: ProtocolBugs{
- AllowSessionVersionMismatch: true,
+ expectedVersion: sessionVers.version,
+ expectedResumeVersion: resumeVers.version,
+ })
+ } else {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "Resume-Client-Mismatch" + suffix,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
},
- },
- expectedResumeVersion: resumeVers.version,
- })
+ expectedVersion: sessionVers.version,
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ AllowSessionVersionMismatch: true,
+ },
+ },
+ expectedResumeVersion: resumeVers.version,
+ shouldFail: true,
+ expectedError: ":OLD_SESSION_VERSION_NOT_RETURNED:",
+ })
+ }
testCases = append(testCases, testCase{
protocol: protocol,
@@ -2265,6 +2838,22 @@ func addResumptionVersionTests() {
}
}
}
+
+ testCases = append(testCases, testCase{
+ name: "Resume-Client-CipherMismatch",
+ resumeSession: true,
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+ },
+ resumeConfig: &Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ SendCipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
+ })
}
func addRenegotiationTests() {
@@ -2276,6 +2865,17 @@ func addRenegotiationTests() {
})
testCases = append(testCases, testCase{
testType: serverTest,
+ name: "Renegotiate-Server-Full",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NeverResumeOnRenego: true,
+ },
+ },
+ flags: []string{"-renegotiate"},
+ shimWritesFirst: true,
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
name: "Renegotiate-Server-EmptyExt",
config: Config{
Bugs: ProtocolBugs{
@@ -2328,12 +2928,43 @@ func addRenegotiationTests() {
},
flags: []string{"-allow-unsafe-legacy-renegotiation"},
})
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Renegotiate-Server-ClientInitiated-Forbidden",
+ renegotiate: true,
+ flags: []string{"-reject-peer-renegotiations"},
+ shouldFail: true,
+ expectedError: ":NO_RENEGOTIATION:",
+ expectedLocalError: "remote error: no renegotiation",
+ })
+ // Regression test for CVE-2015-0291.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Renegotiate-Server-NoSignatureAlgorithms",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NeverResumeOnRenego: true,
+ NoSignatureAlgorithmsOnRenego: true,
+ },
+ },
+ flags: []string{"-renegotiate"},
+ shimWritesFirst: true,
+ })
// TODO(agl): test the renegotiation info SCSV.
testCases = append(testCases, testCase{
name: "Renegotiate-Client",
renegotiate: true,
})
testCases = append(testCases, testCase{
+ name: "Renegotiate-Client-Full",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NeverResumeOnRenego: true,
+ },
+ },
+ renegotiate: true,
+ })
+ testCases = append(testCases, testCase{
name: "Renegotiate-Client-EmptyExt",
renegotiate: true,
config: Config{
@@ -2372,6 +3003,14 @@ func addRenegotiationTests() {
renegotiateCiphers: []uint16{TLS_RSA_WITH_RC4_128_SHA},
})
testCases = append(testCases, testCase{
+ name: "Renegotiate-Client-Forbidden",
+ renegotiate: true,
+ flags: []string{"-reject-peer-renegotiations"},
+ shouldFail: true,
+ expectedError: ":NO_RENEGOTIATION:",
+ expectedLocalError: "remote error: no renegotiation",
+ })
+ testCases = append(testCases, testCase{
name: "Renegotiate-SameClientVersion",
renegotiate: true,
config: Config{
@@ -2418,7 +3057,7 @@ func addFastRadioPaddingTests() {
})
testCases = append(testCases, testCase{
protocol: dtls,
- name: "FastRadio-Padding",
+ name: "FastRadio-Padding-DTLS",
config: Config{
Bugs: ProtocolBugs{
RequireFastradioPadding: true,
@@ -2534,6 +3173,196 @@ func addSigningHashTests() {
},
},
})
+
+ // Test that hash preferences are enforced. BoringSSL defaults to
+ // rejecting MD5 signatures.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SigningHash-ClientAuth-Enforced",
+ config: Config{
+ Certificates: []Certificate{rsaCertificate},
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashMD5},
+ // Advertise SHA-1 so the handshake will
+ // proceed, but the shim's preferences will be
+ // ignored in CertificateVerify generation, so
+ // MD5 will be chosen.
+ {signatureRSA, hashSHA1},
+ },
+ Bugs: ProtocolBugs{
+ IgnorePeerSignatureAlgorithmPreferences: true,
+ },
+ },
+ flags: []string{"-require-any-client-certificate"},
+ shouldFail: true,
+ expectedError: ":WRONG_SIGNATURE_TYPE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SigningHash-ServerKeyExchange-Enforced",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashMD5},
+ },
+ Bugs: ProtocolBugs{
+ IgnorePeerSignatureAlgorithmPreferences: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_SIGNATURE_TYPE:",
+ })
+}
+
+// timeouts is the retransmit schedule for BoringSSL. It doubles and
+// caps at 60 seconds. On the 13th timeout, it gives up.
+var timeouts = []time.Duration{
+ 1 * time.Second,
+ 2 * time.Second,
+ 4 * time.Second,
+ 8 * time.Second,
+ 16 * time.Second,
+ 32 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+}
+
+func addDTLSRetransmitTests() {
+ // Test that this is indeed the timeout schedule. Stress all
+ // four patterns of handshake.
+ for i := 1; i < len(timeouts); i++ {
+ number := strconv.Itoa(i)
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ name: "DTLS-Retransmit-Client-" + number,
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: timeouts[:i],
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async"},
+ })
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ testType: serverTest,
+ name: "DTLS-Retransmit-Server-" + number,
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: timeouts[:i],
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async"},
+ })
+ }
+
+ // Test that exceeding the timeout schedule hits a read
+ // timeout.
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ name: "DTLS-Retransmit-Timeout",
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: timeouts,
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async"},
+ shouldFail: true,
+ expectedError: ":READ_TIMEOUT_EXPIRED:",
+ })
+
+ // Test that timeout handling has a fudge factor, due to API
+ // problems.
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ name: "DTLS-Retransmit-Fudge",
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: []time.Duration{
+ timeouts[0] - 10*time.Millisecond,
+ },
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async"},
+ })
+
+ // Test that the final Finished retransmitting isn't
+ // duplicated if the peer badly fragments everything.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: dtls,
+ name: "DTLS-Retransmit-Fragmented",
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: []time.Duration{timeouts[0]},
+ MaxHandshakeRecordLength: 2,
+ },
+ },
+ flags: []string{"-async"},
+ })
+}
+
+func addExportKeyingMaterialTests() {
+ for _, vers := range tlsVersions {
+ if vers.version == VersionSSL30 {
+ continue
+ }
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ exportKeyingMaterial: 1024,
+ exportLabel: "label",
+ exportContext: "context",
+ useExportContext: true,
+ })
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-NoContext-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ exportKeyingMaterial: 1024,
+ })
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-EmptyContext-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ exportKeyingMaterial: 1024,
+ useExportContext: true,
+ })
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-Small-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ exportKeyingMaterial: 1,
+ exportLabel: "label",
+ exportContext: "context",
+ useExportContext: true,
+ })
+ }
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-SSL3",
+ config: Config{
+ MaxVersion: VersionSSL30,
+ },
+ exportKeyingMaterial: 1024,
+ exportLabel: "label",
+ exportContext: "context",
+ useExportContext: true,
+ shouldFail: true,
+ expectedError: "failed to export keying material",
+ })
}
func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
@@ -2566,27 +3395,47 @@ type statusMsg struct {
err error
}
-func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
+func statusPrinter(doneChan chan *testOutput, statusChan chan statusMsg, total int) {
var started, done, failed, lineLen int
- defer close(doneChan)
+ testOutput := newTestOutput()
for msg := range statusChan {
+ if !*pipe {
+ // Erase the previous status line.
+ var erase string
+ for i := 0; i < lineLen; i++ {
+ erase += "\b \b"
+ }
+ fmt.Print(erase)
+ }
+
if msg.started {
started++
} else {
done++
- }
- fmt.Printf("\x1b[%dD\x1b[K", lineLen)
+ if msg.err != nil {
+ fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
+ failed++
+ testOutput.addResult(msg.test.name, "FAIL")
+ } else {
+ if *pipe {
+ // Print each test instead of a status line.
+ fmt.Printf("PASSED (%s)\n", msg.test.name)
+ }
+ testOutput.addResult(msg.test.name, "PASS")
+ }
+ }
- if msg.err != nil {
- fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
- failed++
+ if !*pipe {
+ // Print a new status line.
+ line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
+ lineLen = len(line)
+ os.Stdout.WriteString(line)
}
- line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
- lineLen = len(line)
- os.Stdout.WriteString(line)
}
+
+ doneChan <- testOutput
}
func main() {
@@ -2601,6 +3450,7 @@ func main() {
addCBCPaddingTests()
addCBCSplittingTests()
addClientAuthTests()
+ addDDoSCallbackTests()
addVersionNegotiationTests()
addMinimumVersionTests()
addD5BugTests()
@@ -2611,6 +3461,8 @@ func main() {
addDTLSReplayTests()
addSigningHashTests()
addFastRadioPaddingTests()
+ addDTLSRetransmitTests()
+ addExportKeyingMaterialTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {
@@ -2625,7 +3477,7 @@ func main() {
statusChan := make(chan statusMsg, numWorkers)
testChan := make(chan *testCase, numWorkers)
- doneChan := make(chan struct{})
+ doneChan := make(chan *testOutput)
go statusPrinter(doneChan, statusChan, len(testCases))
@@ -2643,7 +3495,17 @@ func main() {
close(testChan)
wg.Wait()
close(statusChan)
- <-doneChan
+ testOutput := <-doneChan
fmt.Printf("\n")
+
+ if *jsonOutput != "" {
+ if err := testOutput.writeTo(*jsonOutput); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %s\n", err)
+ }
+ }
+
+ if !testOutput.allPassed {
+ os.Exit(1)
+ }
}
diff --git a/src/ssl/test/runner/test_output.go b/src/ssl/test/runner/test_output.go
new file mode 100644
index 0000000..bcb7a93
--- /dev/null
+++ b/src/ssl/test/runner/test_output.go
@@ -0,0 +1,79 @@
+/* Copyright (c) 2015, 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. */
+
+package main
+
+import (
+ "encoding/json"
+ "os"
+ "time"
+)
+
+// testOutput is a representation of Chromium's JSON test result format. See
+// https://www.chromium.org/developers/the-json-test-results-format
+type testOutput struct {
+ Version int `json:"version"`
+ Interrupted bool `json:"interrupted"`
+ PathDelimiter string `json:"path_delimiter"`
+ SecondsSinceEpoch float64 `json:"seconds_since_epoch"`
+ NumFailuresByType map[string]int `json:"num_failures_by_type"`
+ Tests map[string]testResult `json:"tests"`
+ allPassed bool
+}
+
+type testResult struct {
+ Actual string `json:"actual"`
+ Expected string `json:"expected"`
+ IsUnexpected bool `json:"is_unexpected"`
+}
+
+func newTestOutput() *testOutput {
+ return &testOutput{
+ Version: 3,
+ PathDelimiter: ".",
+ SecondsSinceEpoch: float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond),
+ NumFailuresByType: make(map[string]int),
+ Tests: make(map[string]testResult),
+ allPassed: true,
+ }
+}
+
+func (t *testOutput) addResult(name, result string) {
+ if _, found := t.Tests[name]; found {
+ panic(name)
+ }
+ t.Tests[name] = testResult{
+ Actual: result,
+ Expected: "PASS",
+ IsUnexpected: result != "PASS",
+ }
+ t.NumFailuresByType[result]++
+ if result != "PASS" {
+ t.allPassed = false
+ }
+}
+
+func (t *testOutput) writeTo(name string) error {
+ file, err := os.Create(name)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ out, err := json.MarshalIndent(t, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = file.Write(out)
+ return err
+}
diff --git a/src/ssl/test/scoped_types.h b/src/ssl/test/scoped_types.h
new file mode 100644
index 0000000..7e92cee
--- /dev/null
+++ b/src/ssl/test/scoped_types.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2015, 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 OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
+#define OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
+
+#include <openssl/ssl.h>
+
+#include "../../crypto/test/scoped_types.h"
+
+
+using ScopedSSL = ScopedOpenSSLType<SSL, SSL_free>;
+using ScopedSSL_CTX = ScopedOpenSSLType<SSL_CTX, SSL_CTX_free>;
+using ScopedSSL_SESSION = ScopedOpenSSLType<SSL_SESSION, SSL_SESSION_free>;
+
+
+#endif // OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index c032d96..25906f7 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -60,7 +60,6 @@ const Flag<bool> kBoolFlags[] = {
{ "-no-tls11", &TestConfig::no_tls11 },
{ "-no-tls1", &TestConfig::no_tls1 },
{ "-no-ssl3", &TestConfig::no_ssl3 },
- { "-cookie-exchange", &TestConfig::cookie_exchange },
{ "-shim-writes-first", &TestConfig::shim_writes_first },
{ "-tls-d5-bug", &TestConfig::tls_d5_bug },
{ "-expect-session-miss", &TestConfig::expect_session_miss },
@@ -73,6 +72,15 @@ const Flag<bool> kBoolFlags[] = {
{ "-enable-signed-cert-timestamps",
&TestConfig::enable_signed_cert_timestamps },
{ "-fastradio-padding", &TestConfig::fastradio_padding },
+ { "-implicit-handshake", &TestConfig::implicit_handshake },
+ { "-use-early-callback", &TestConfig::use_early_callback },
+ { "-fail-early-callback", &TestConfig::fail_early_callback },
+ { "-install-ddos-callback", &TestConfig::install_ddos_callback },
+ { "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
+ { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
+ { "-handshake-never-done", &TestConfig::handshake_never_done },
+ { "-use-export-context", &TestConfig::use_export_context },
+ { "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations },
};
const Flag<std::string> kStringFlags[] = {
@@ -91,6 +99,9 @@ const Flag<std::string> kStringFlags[] = {
{ "-psk", &TestConfig::psk },
{ "-psk-identity", &TestConfig::psk_identity },
{ "-srtp-profiles", &TestConfig::srtp_profiles },
+ { "-cipher", &TestConfig::cipher },
+ { "-export-label", &TestConfig::export_label },
+ { "-export-context", &TestConfig::export_context },
};
const Flag<std::string> kBase64Flags[] = {
@@ -102,43 +113,15 @@ const Flag<std::string> kBase64Flags[] = {
};
const Flag<int> kIntFlags[] = {
+ { "-port", &TestConfig::port },
{ "-min-version", &TestConfig::min_version },
{ "-max-version", &TestConfig::max_version },
{ "-mtu", &TestConfig::mtu },
+ { "-export-keying-material", &TestConfig::export_keying_material },
};
} // namespace
-TestConfig::TestConfig()
- : is_server(false),
- is_dtls(false),
- resume(false),
- fallback_scsv(false),
- require_any_client_certificate(false),
- false_start(false),
- async(false),
- write_different_record_sizes(false),
- cbc_record_splitting(false),
- partial_write(false),
- no_tls12(false),
- no_tls11(false),
- no_tls1(false),
- no_ssl3(false),
- cookie_exchange(false),
- shim_writes_first(false),
- tls_d5_bug(false),
- expect_session_miss(false),
- expect_extended_master_secret(false),
- renegotiate(false),
- allow_unsafe_legacy_renegotiation(false),
- enable_ocsp_stapling(false),
- enable_signed_cert_timestamps(false),
- fastradio_padding(false),
- min_version(0),
- max_version(0),
- mtu(0) {
-}
-
bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
for (int i = 0; i < argc; i++) {
bool *bool_field = FindField(out_config, kBoolFlags, argv[i]);
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index ba54227..f107a0f 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -19,54 +19,65 @@
struct TestConfig {
- TestConfig();
-
- bool is_server;
- bool is_dtls;
- bool resume;
- bool fallback_scsv;
+ int port = 0;
+ bool is_server = false;
+ bool is_dtls = false;
+ bool resume = false;
+ bool fallback_scsv = false;
std::string key_file;
std::string cert_file;
std::string expected_server_name;
std::string expected_certificate_types;
- bool require_any_client_certificate;
+ bool require_any_client_certificate = false;
std::string advertise_npn;
std::string expected_next_proto;
- bool false_start;
+ bool false_start = false;
std::string select_next_proto;
- bool async;
- bool write_different_record_sizes;
- bool cbc_record_splitting;
- bool partial_write;
- bool no_tls12;
- bool no_tls11;
- bool no_tls1;
- bool no_ssl3;
- bool cookie_exchange;
+ bool async = false;
+ bool write_different_record_sizes = false;
+ bool cbc_record_splitting = false;
+ bool partial_write = false;
+ bool no_tls12 = false;
+ bool no_tls11 = false;
+ bool no_tls1 = false;
+ bool no_ssl3 = false;
std::string expected_channel_id;
std::string send_channel_id;
- bool shim_writes_first;
- bool tls_d5_bug;
+ bool shim_writes_first = false;
+ bool tls_d5_bug = false;
std::string host_name;
std::string advertise_alpn;
std::string expected_alpn;
std::string expected_advertised_alpn;
std::string select_alpn;
- bool expect_session_miss;
- bool expect_extended_master_secret;
+ bool expect_session_miss = false;
+ bool expect_extended_master_secret = false;
std::string psk;
std::string psk_identity;
- bool renegotiate;
- bool allow_unsafe_legacy_renegotiation;
+ bool renegotiate = false;
+ bool allow_unsafe_legacy_renegotiation = false;
std::string srtp_profiles;
- bool enable_ocsp_stapling;
+ bool enable_ocsp_stapling = false;
std::string expected_ocsp_response;
- bool enable_signed_cert_timestamps;
+ bool enable_signed_cert_timestamps = false;
std::string expected_signed_cert_timestamps;
- bool fastradio_padding;
- int min_version;
- int max_version;
- int mtu;
+ bool fastradio_padding = false;
+ int min_version = 0;
+ int max_version = 0;
+ int mtu = 0;
+ bool implicit_handshake = false;
+ bool use_early_callback = false;
+ bool fail_early_callback = false;
+ bool install_ddos_callback = false;
+ bool fail_ddos_callback = false;
+ bool fail_second_ddos_callback = false;
+ std::string cipher;
+ bool handshake_never_done = false;
+ int export_keying_material = 0;
+ std::string export_label;
+ std::string export_context;
+ bool use_export_context = false;
+ bool reject_peer_renegotiations = false;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);