summaryrefslogtreecommitdiffstats
path: root/src/ssl/test
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2015-09-25 02:46:08 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-09-25 02:46:08 +0000
commit217603a4384148cd37c60a42f945449641617d27 (patch)
treedd743d9d64af3145fe96b8d5fc2f3427544794bd /src/ssl/test
parente246de8f184e644debf965ecdca552f006b56881 (diff)
parentd947d006e7a7ebcfdfe642e686250caf2028c2c1 (diff)
downloadexternal_boringssl-217603a4384148cd37c60a42f945449641617d27.zip
external_boringssl-217603a4384148cd37c60a42f945449641617d27.tar.gz
external_boringssl-217603a4384148cd37c60a42f945449641617d27.tar.bz2
am d947d006: am b8494591: Revert "Revert "external/boringssl: sync with upstream.""
* commit 'd947d006e7a7ebcfdfe642e686250caf2028c2c1': Revert "Revert "external/boringssl: sync with upstream.""
Diffstat (limited to 'src/ssl/test')
-rw-r--r--src/ssl/test/bssl_shim.cc731
-rw-r--r--src/ssl/test/runner/cipher_suites.go13
-rw-r--r--src/ssl/test/runner/common.go104
-rw-r--r--src/ssl/test/runner/conn.go117
-rw-r--r--src/ssl/test/runner/dtls.go15
-rw-r--r--src/ssl/test/runner/handshake_client.go42
-rw-r--r--src/ssl/test/runner/handshake_messages.go236
-rw-r--r--src/ssl/test/runner/handshake_server.go32
-rw-r--r--src/ssl/test/runner/runner.go2916
-rw-r--r--src/ssl/test/test_config.cc25
-rw-r--r--src/ssl/test/test_config.h22
11 files changed, 2776 insertions, 1477 deletions
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 3b95d7e..edae67b 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -38,10 +38,14 @@
#include <openssl/bio.h>
#include <openssl/buf.h>
#include <openssl/bytestring.h>
+#include <openssl/cipher.h>
#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <memory>
+#include <string>
#include <vector>
#include "../../crypto/test/scoped_types.h"
@@ -90,10 +94,17 @@ struct TestState {
ScopedSSL_SESSION pending_session;
bool early_callback_called = false;
bool handshake_done = false;
+ // private_key is the underlying private key used when testing custom keys.
+ ScopedEVP_PKEY private_key;
+ std::vector<uint8_t> signature;
+ // signature_retries is the number of times an asynchronous sign operation has
+ // been retried.
+ unsigned signature_retries = 0;
+ bool got_new_session = false;
};
static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
- int index, long argl, void *argp) {
+ int index, long argl, void *argp) {
delete ((TestState *)ptr);
}
@@ -129,18 +140,137 @@ static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) {
return pkey;
}
+static int AsyncPrivateKeyType(SSL *ssl) {
+ return EVP_PKEY_id(GetTestState(ssl)->private_key.get());
+}
+
+static size_t AsyncPrivateKeyMaxSignatureLen(SSL *ssl) {
+ return EVP_PKEY_size(GetTestState(ssl)->private_key.get());
+}
+
+static ssl_private_key_result_t AsyncPrivateKeySign(
+ SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+ const EVP_MD *md, const uint8_t *in, size_t in_len) {
+ TestState *test_state = GetTestState(ssl);
+ if (!test_state->signature.empty()) {
+ fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
+ abort();
+ }
+
+ ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(test_state->private_key.get(),
+ nullptr));
+ if (!ctx) {
+ return ssl_private_key_failure;
+ }
+
+ // Write the signature into |test_state|.
+ size_t len = 0;
+ if (!EVP_PKEY_sign_init(ctx.get()) ||
+ !EVP_PKEY_CTX_set_signature_md(ctx.get(), md) ||
+ !EVP_PKEY_sign(ctx.get(), nullptr, &len, in, in_len)) {
+ return ssl_private_key_failure;
+ }
+ test_state->signature.resize(len);
+ if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(&test_state->signature), &len,
+ in, in_len)) {
+ return ssl_private_key_failure;
+ }
+ test_state->signature.resize(len);
+
+ // The signature will be released asynchronously in |AsyncPrivateKeySignComplete|.
+ return ssl_private_key_retry;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeySignComplete(
+ SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) {
+ TestState *test_state = GetTestState(ssl);
+ if (test_state->signature.empty()) {
+ fprintf(stderr,
+ "AsyncPrivateKeySignComplete called without operation pending.\n");
+ abort();
+ }
+
+ if (test_state->signature_retries < 2) {
+ // Only return the signature on the second attempt, to test both incomplete
+ // |sign| and |sign_complete|.
+ return ssl_private_key_retry;
+ }
+
+ if (max_out < test_state->signature.size()) {
+ fprintf(stderr, "Output buffer too small.\n");
+ return ssl_private_key_failure;
+ }
+ memcpy(out, bssl::vector_data(&test_state->signature),
+ test_state->signature.size());
+ *out_len = test_state->signature.size();
+
+ test_state->signature.clear();
+ test_state->signature_retries = 0;
+ return ssl_private_key_success;
+}
+
+static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
+ AsyncPrivateKeyType,
+ AsyncPrivateKeyMaxSignatureLen,
+ AsyncPrivateKeySign,
+ AsyncPrivateKeySignComplete,
+};
+
+template<typename T>
+struct Free {
+ void operator()(T *buf) {
+ free(buf);
+ }
+};
+
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;
+ TestState *test_state = GetTestState(ssl);
+
+ if (!config->digest_prefs.empty()) {
+ std::unique_ptr<char, Free<char>> digest_prefs(
+ strdup(config->digest_prefs.c_str()));
+ std::vector<int> digest_list;
+
+ for (;;) {
+ char *token =
+ strtok(digest_list.empty() ? digest_prefs.get() : nullptr, ",");
+ if (token == nullptr) {
+ break;
+ }
+
+ digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token)));
+ }
+
+ if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(),
+ digest_list.size())) {
+ return false;
+ }
+ }
+
+ if (!config->key_file.empty()) {
+ if (config->use_async_private_key) {
+ test_state->private_key = LoadPrivateKey(config->key_file.c_str());
+ if (!test_state->private_key) {
+ return false;
+ }
+ SSL_set_private_key_method(ssl, &g_async_private_key_method);
+ } else if (!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;
}
+ if (!config->ocsp_response.empty() &&
+ !SSL_CTX_set_ocsp_response(ssl->ctx,
+ (const uint8_t *)config->ocsp_response.data(),
+ config->ocsp_response.size())) {
+ return false;
+ }
return true;
}
@@ -196,10 +326,29 @@ static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
return 1;
}
-static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) {
+static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) {
+ SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ const TestConfig *config = GetConfigPtr(ssl);
+
+ if (!config->expected_ocsp_response.empty()) {
+ const uint8_t *data;
+ size_t len;
+ SSL_get0_ocsp_response(ssl, &data, &len);
+ if (len == 0) {
+ fprintf(stderr, "OCSP response not available in verify callback\n");
+ return 0;
+ }
+ }
+
return 1;
}
+static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) {
+ store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
+ return 0;
+}
+
static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
unsigned int *out_len, void *arg) {
const TestConfig *config = GetConfigPtr(ssl);
@@ -341,6 +490,94 @@ static void InfoCallback(const SSL *ssl, int type, int val) {
}
}
+static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
+ GetTestState(ssl)->got_new_session = true;
+ // BoringSSL passes a reference to |session|.
+ SSL_SESSION_free(session);
+ return 1;
+}
+
+static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
+ EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
+ int encrypt) {
+ // This is just test code, so use the all-zeros key.
+ static const uint8_t kZeros[16] = {0};
+
+ if (encrypt) {
+ memcpy(key_name, kZeros, sizeof(kZeros));
+ RAND_bytes(iv, 16);
+ } else if (memcmp(key_name, kZeros, 16) != 0) {
+ return 0;
+ }
+
+ if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) ||
+ !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) {
+ return -1;
+ }
+
+ if (!encrypt) {
+ return GetConfigPtr(ssl)->renew_ticket ? 2 : 1;
+ }
+ return 1;
+}
+
+// kCustomExtensionValue is the extension value that the custom extension
+// callbacks will add.
+static const uint16_t kCustomExtensionValue = 1234;
+static void *const kCustomExtensionAddArg =
+ reinterpret_cast<void *>(kCustomExtensionValue);
+static void *const kCustomExtensionParseArg =
+ reinterpret_cast<void *>(kCustomExtensionValue + 1);
+static const char kCustomExtensionContents[] = "custom extension";
+
+static int CustomExtensionAddCallback(SSL *ssl, unsigned extension_value,
+ const uint8_t **out, size_t *out_len,
+ int *out_alert_value, void *add_arg) {
+ if (extension_value != kCustomExtensionValue ||
+ add_arg != kCustomExtensionAddArg) {
+ abort();
+ }
+
+ if (GetConfigPtr(ssl)->custom_extension_skip) {
+ return 0;
+ }
+ if (GetConfigPtr(ssl)->custom_extension_fail_add) {
+ return -1;
+ }
+
+ *out = reinterpret_cast<const uint8_t*>(kCustomExtensionContents);
+ *out_len = sizeof(kCustomExtensionContents) - 1;
+
+ return 1;
+}
+
+static void CustomExtensionFreeCallback(SSL *ssl, unsigned extension_value,
+ const uint8_t *out, void *add_arg) {
+ if (extension_value != kCustomExtensionValue ||
+ add_arg != kCustomExtensionAddArg ||
+ out != reinterpret_cast<const uint8_t *>(kCustomExtensionContents)) {
+ abort();
+ }
+}
+
+static int CustomExtensionParseCallback(SSL *ssl, unsigned extension_value,
+ const uint8_t *contents,
+ size_t contents_len,
+ int *out_alert_value, void *parse_arg) {
+ if (extension_value != kCustomExtensionValue ||
+ parse_arg != kCustomExtensionParseArg) {
+ abort();
+ }
+
+ if (contents_len != sizeof(kCustomExtensionContents) - 1 ||
+ memcmp(contents, kCustomExtensionContents, contents_len) != 0) {
+ *out_alert_value = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ return 1;
+}
+
// Connect returns a new socket connected to localhost on |port| or -1 on
// error.
static int Connect(uint16_t port) {
@@ -406,7 +643,23 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) {
return nullptr;
}
- if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) {
+ std::string cipher_list = "ALL";
+ if (!config->cipher.empty()) {
+ cipher_list = config->cipher;
+ SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+ if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), cipher_list.c_str())) {
+ return nullptr;
+ }
+
+ if (!config->cipher_tls10.empty() &&
+ !SSL_CTX_set_cipher_list_tls10(ssl_ctx.get(),
+ config->cipher_tls10.c_str())) {
+ return nullptr;
+ }
+ if (!config->cipher_tls11.empty() &&
+ !SSL_CTX_set_cipher_list_tls11(ssl_ctx.get(),
+ config->cipher_tls11.c_str())) {
return nullptr;
}
@@ -438,12 +691,46 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) {
SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
}
- ssl_ctx->tlsext_channel_id_enabled_new = 1;
+ SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
ssl_ctx->current_time_cb = CurrentTimeCallback;
SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
+ SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
+
+ if (config->use_ticket_callback) {
+ SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback);
+ }
+
+ if (config->enable_client_custom_extension &&
+ !SSL_CTX_add_client_custom_ext(
+ ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback,
+ CustomExtensionFreeCallback, kCustomExtensionAddArg,
+ CustomExtensionParseCallback, kCustomExtensionParseArg)) {
+ return nullptr;
+ }
+
+ if (config->enable_server_custom_extension &&
+ !SSL_CTX_add_server_custom_ext(
+ ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback,
+ CustomExtensionFreeCallback, kCustomExtensionAddArg,
+ CustomExtensionParseCallback, kCustomExtensionParseArg)) {
+ return nullptr;
+ }
+
+ if (config->verify_fail) {
+ SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL);
+ } else {
+ SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL);
+ }
+
+ if (!config->signed_cert_timestamps.empty() &&
+ !SSL_CTX_set_signed_cert_timestamp_list(
+ ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(),
+ config->signed_cert_timestamps.size())) {
+ return nullptr;
+ }
return ssl_ctx;
}
@@ -500,6 +787,9 @@ static bool RetryAsync(SSL *ssl, int ret) {
case SSL_ERROR_PENDING_CERTIFICATE:
// The handshake will resume without a second call to the early callback.
return InstallCertificate(ssl);
+ case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
+ test_state->signature_retries++;
+ return true;
default:
return false;
}
@@ -531,6 +821,177 @@ static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) {
return ret;
}
+// DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It
+// returns the result of the final |SSL_shutdown| call.
+static int DoShutdown(SSL *ssl) {
+ const TestConfig *config = GetConfigPtr(ssl);
+ int ret;
+ do {
+ ret = SSL_shutdown(ssl);
+ } while (config->async && RetryAsync(ssl, ret));
+ return ret;
+}
+
+// CheckHandshakeProperties checks, immediately after |ssl| completes its
+// initial handshake (or False Starts), whether all the properties are
+// consistent with the test configuration and invariants.
+static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
+ const TestConfig *config = GetConfigPtr(ssl);
+
+ if (SSL_get_current_cipher(ssl) == nullptr) {
+ fprintf(stderr, "null cipher after handshake\n");
+ return false;
+ }
+
+ if (is_resume &&
+ (!!SSL_session_reused(ssl) == config->expect_session_miss)) {
+ fprintf(stderr, "session was%s reused\n",
+ SSL_session_reused(ssl) ? "" : " not");
+ return false;
+ }
+
+ bool expect_handshake_done = is_resume || !config->false_start;
+ if (expect_handshake_done != GetTestState(ssl)->handshake_done) {
+ fprintf(stderr, "handshake was%s completed\n",
+ GetTestState(ssl)->handshake_done ? "" : " not");
+ return false;
+ }
+
+ if (expect_handshake_done && !config->is_server) {
+ bool expect_new_session =
+ !config->expect_no_session &&
+ (!SSL_session_reused(ssl) || config->expect_ticket_renewal);
+ if (expect_new_session != GetTestState(ssl)->got_new_session) {
+ fprintf(stderr,
+ "new session was%s established, but we expected the opposite\n",
+ GetTestState(ssl)->got_new_session ? "" : " not");
+ return false;
+ }
+ }
+
+ if (config->is_server && !GetTestState(ssl)->early_callback_called) {
+ fprintf(stderr, "early callback not called\n");
+ 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 false;
+ }
+ }
+
+ if (!config->expected_certificate_types.empty()) {
+ const uint8_t *certificate_types;
+ size_t certificate_types_len =
+ SSL_get0_certificate_types(ssl, &certificate_types);
+ if (certificate_types_len != config->expected_certificate_types.size() ||
+ memcmp(certificate_types,
+ config->expected_certificate_types.data(),
+ certificate_types_len) != 0) {
+ fprintf(stderr, "certificate types mismatch\n");
+ 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 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 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 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 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 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 false;
+ }
+ }
+
+ if (config->expect_verify_result) {
+ int expected_verify_result = config->verify_fail ?
+ X509_V_ERR_APPLICATION_VERIFICATION :
+ X509_V_OK;
+
+ if (SSL_get_verify_result(ssl) != expected_verify_result) {
+ fprintf(stderr, "Wrong certificate verification result\n");
+ return false;
+ }
+ }
+
+ if (!config->is_server) {
+ /* Clients should expect a peer certificate chain iff this was not a PSK
+ * cipher suite. */
+ if (config->psk.empty()) {
+ if (SSL_get_peer_cert_chain(ssl) == nullptr) {
+ fprintf(stderr, "Missing peer certificate chain!\n");
+ return false;
+ }
+ } else if (SSL_get_peer_cert_chain(ssl) != nullptr) {
+ fprintf(stderr, "Unexpected peer certificate chain!\n");
+ return false;
+ }
+ }
+ return true;
+}
+
// 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
@@ -562,7 +1023,10 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
}
if (config->require_any_client_certificate) {
SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- SkipVerify);
+ NULL);
+ }
+ if (config->verify_peer) {
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL);
}
if (config->false_start) {
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
@@ -588,8 +1052,8 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
if (config->tls_d5_bug) {
SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG);
}
- if (config->allow_unsafe_legacy_renegotiation) {
- SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ if (config->microsoft_big_sslv3_buffer) {
+ SSL_set_options(ssl.get(), SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
}
if (config->no_legacy_server_connect) {
SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT);
@@ -637,7 +1101,6 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
!SSL_enable_signed_cert_timestamps(ssl.get())) {
return false;
}
- SSL_enable_fastradio_padding(ssl.get(), config->fastradio_padding);
if (config->min_version != 0) {
SSL_set_min_version(ssl.get(), (uint16_t)config->min_version);
}
@@ -651,14 +1114,13 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
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) {
/* Renegotiations are disabled by default. */
SSL_set_reject_peer_renegotiations(ssl.get(), 0);
}
+ if (!config->check_close_notify) {
+ SSL_set_quiet_shutdown(ssl.get(), 1);
+ }
int sock = Connect(config->port);
if (sock == -1) {
@@ -719,139 +1181,14 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
ret = SSL_connect(ssl.get());
}
} while (config->async && RetryAsync(ssl.get(), ret));
- if (ret != 1) {
- return false;
- }
-
- if (SSL_get_current_cipher(ssl.get()) == nullptr) {
- fprintf(stderr, "null cipher after handshake\n");
- return false;
- }
-
- 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;
- }
-
- 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 (config->is_server && !GetTestState(ssl.get())->early_callback_called) {
- fprintf(stderr, "early callback not called\n");
+ if (ret != 1 ||
+ !CheckHandshakeProperties(ssl.get(), is_resume)) {
return false;
}
- 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_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_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_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.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 false;
- }
- }
-
- 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.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->is_server) {
- /* Clients should expect a peer certificate chain iff this was not a PSK
- * cipher suite. */
- if (config->psk.empty()) {
- if (SSL_get_peer_cert_chain(ssl.get()) == nullptr) {
- fprintf(stderr, "Missing peer certificate chain!\n");
- return false;
- }
- } else if (SSL_get_peer_cert_chain(ssl.get()) != nullptr) {
- fprintf(stderr, "Unexpected peer certificate chain!\n");
- return false;
- }
- }
+ // Reset the state to assert later that the callback isn't called in
+ // renegotations.
+ GetTestState(ssl.get())->got_new_session = false;
}
if (config->export_keying_material > 0) {
@@ -897,18 +1234,19 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
}
// This mode writes a number of different record sizes in an attempt to
// trip up the CBC record splitting code.
- uint8_t buf[32769];
- memset(buf, 0x42, sizeof(buf));
+ static const size_t kBufLen = 32769;
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]);
+ memset(buf.get(), 0x42, kBufLen);
static const size_t kRecordSizes[] = {
0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]);
i++) {
const size_t len = kRecordSizes[i];
- if (len > sizeof(buf)) {
+ if (len > kBufLen) {
fprintf(stderr, "Bad kRecordSizes value.\n");
return false;
}
- if (WriteAll(ssl.get(), buf, len) < 0) {
+ if (WriteAll(ssl.get(), buf.get(), len) < 0) {
return false;
}
}
@@ -919,53 +1257,82 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
return false;
}
}
- for (;;) {
- uint8_t buf[512];
- 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");
+ if (!config->shim_shuts_down) {
+ for (;;) {
+ static const size_t kBufLen = 16384;
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]);
+
+ // Read only 512 bytes at a time in TLS to ensure records may be
+ // returned in multiple reads.
+ int n = DoRead(ssl.get(), buf.get(), config->is_dtls ? kBufLen : 512);
+ 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 false;
+ }
+ // Stop on either clean or unclean shutdown.
+ break;
+ } else if (err != SSL_ERROR_NONE) {
+ if (n > 0) {
+ fprintf(stderr, "Invalid SSL_get_error output\n");
+ return false;
+ }
return false;
}
- // 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) {
+ // Successfully read data.
+ if (n <= 0) {
fprintf(stderr, "Invalid SSL_get_error output\n");
return false;
}
- return false;
- }
- // Successfully read data.
- if (n <= 0) {
- fprintf(stderr, "Invalid SSL_get_error output\n");
- 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;
- }
+ // 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;
- }
- if (WriteAll(ssl.get(), buf, n) < 0) {
- return false;
+ for (int i = 0; i < n; i++) {
+ buf[i] ^= 0xff;
+ }
+ if (WriteAll(ssl.get(), buf.get(), n) < 0) {
+ return false;
+ }
}
}
}
+ if (!config->is_server && !config->false_start &&
+ !config->implicit_handshake &&
+ GetTestState(ssl.get())->got_new_session) {
+ fprintf(stderr, "new session was established after the handshake\n");
+ return false;
+ }
+
if (out_session) {
out_session->reset(SSL_get1_session(ssl.get()));
}
- SSL_shutdown(ssl.get());
+ ret = DoShutdown(ssl.get());
+
+ if (config->shim_shuts_down && config->check_close_notify) {
+ // We initiate shutdown, so |SSL_shutdown| will return in two stages. First
+ // it returns zero when our close_notify is sent, then one when the peer's
+ // is received.
+ if (ret != 0) {
+ fprintf(stderr, "Unexpected SSL_shutdown result: %d != 0\n", ret);
+ return false;
+ }
+ ret = DoShutdown(ssl.get());
+ }
+
+ if (ret != 1) {
+ fprintf(stderr, "Unexpected SSL_shutdown result: %d != 1\n", ret);
+ return false;
+ }
+
return true;
}
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 70c7262..ffc056d 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -102,7 +102,6 @@ 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},
@@ -120,12 +119,18 @@ var cipherSuites = []*cipherSuite{
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
{TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, dheRSAKA, 0, cipher3DES, macSHA1, nil},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
- {TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdhePSKKA, suiteECDHE | suiteTLS12 | suitePSK, nil, nil, aeadAESGCM},
{TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil},
{TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
{TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_NULL_SHA, 0, 20, 0, rsaKA, suiteNoDTLS, cipherNull, macSHA1, nil},
+}
+
+type nullCipher struct{}
+
+func cipherNull(key, iv []byte, isRead bool) interface{} {
+ return nullCipher{}
}
func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -370,6 +375,7 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
// A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const (
+ TLS_RSA_WITH_NULL_SHA uint16 = 0x0002
TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
@@ -406,13 +412,12 @@ const (
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xc035
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xc036
+ renegotiationSCSV uint16 = 0x00ff
fallbackSCSV uint16 = 0x5600
)
// Additional cipher suite IDs, not IANA-assigned.
const (
- 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 edebba1..77be9f6 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -82,6 +82,7 @@ const (
extensionSignedCertificateTimestamp uint16 = 18
extensionExtendedMasterSecret uint16 = 23
extensionSessionTicket uint16 = 35
+ extensionCustom uint16 = 1234 // not IANA assigned
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
extensionChannelID uint16 = 30032 // not IANA assigned
@@ -188,7 +189,9 @@ type ConnectionState struct {
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
ChannelID *ecdsa.PublicKey // the channel ID for this connection
SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile
- TLSUnique []byte
+ TLSUnique []byte // the tls-unique channel binding
+ SCTList []byte // signed certificate timestamp list
+ ClientCertSignatureHash uint8 // TLS id of the hash used by the client to sign the handshake
}
// ClientAuthType declares the policy the server will follow for
@@ -214,6 +217,8 @@ type ClientSessionState struct {
handshakeHash []byte // Handshake hash for Channel ID purposes.
serverCertificates []*x509.Certificate // Certificate chain presented by the server
extendedMasterSecret bool // Whether an extended master secret was used to generate the session
+ sctList []byte
+ ocspResponse []byte
}
// ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -399,6 +404,10 @@ type ProtocolBugs struct {
// ServerKeyExchange message should be invalid.
InvalidSKXSignature bool
+ // InvalidCertVerifySignature specifies that the signature in a
+ // CertificateVerify message should be invalid.
+ InvalidCertVerifySignature bool
+
// InvalidSKXCurve causes the curve ID in the ServerKeyExchange message
// to be wrong.
InvalidSKXCurve bool
@@ -476,6 +485,10 @@ type ProtocolBugs struct {
// TLS_FALLBACK_SCSV in the ClientHello.
SendFallbackSCSV bool
+ // SendRenegotiationSCSV causes the client to include the renegotiation
+ // SCSV in the ClientHello.
+ SendRenegotiationSCSV bool
+
// MaxHandshakeRecordLength, if non-zero, is the maximum size of a
// handshake record. Handshake messages will be split into multiple
// records at the specified size, except that the client_version will
@@ -535,11 +548,14 @@ type ProtocolBugs struct {
// must specify in the server_name extension.
ExpectServerName string
- // SwapNPNAndALPN switches the relative order between NPN and
- // ALPN on the server. This is to test that server preference
- // of ALPN works regardless of their relative order.
+ // SwapNPNAndALPN switches the relative order between NPN and ALPN in
+ // both ClientHello and ServerHello.
SwapNPNAndALPN bool
+ // ALPNProtocol, if not nil, sets the ALPN protocol that a server will
+ // return.
+ ALPNProtocol *string
+
// AllowSessionVersionMismatch causes the server to resume sessions
// regardless of the version associated with the session.
AllowSessionVersionMismatch bool
@@ -572,11 +588,15 @@ type ProtocolBugs struct {
// didn't support the renegotiation info extension.
NoRenegotiationInfo bool
- // SequenceNumberIncrement, if non-zero, causes outgoing sequence
- // numbers in DTLS to increment by that value rather by 1. This is to
- // stress the replay bitmap window by simulating extreme packet loss and
- // retransmit at the record layer.
- SequenceNumberIncrement uint64
+ // RequireRenegotiationInfo, if true, causes the client to return an
+ // error if the server doesn't reply with the renegotiation extension.
+ RequireRenegotiationInfo bool
+
+ // SequenceNumberMapping, if non-nil, is the mapping function to apply
+ // to the sequence number of outgoing packets. For both TLS and DTLS,
+ // the two most-significant bytes in the resulting sequence number are
+ // ignored so that the DTLS epoch cannot be changed.
+ SequenceNumberMapping func(uint64) uint64
// RSAEphemeralKey, if true, causes the server to send a
// ServerKeyExchange message containing an ephemeral key (as in
@@ -609,10 +629,6 @@ type ProtocolBugs struct {
// across a renego.
RequireSameRenegoClientVersion bool
- // RequireFastradioPadding, if true, requires that ClientHello messages
- // be at least 1000 bytes long.
- RequireFastradioPadding bool
-
// ExpectInitialRecordVersion, if non-zero, is the expected
// version of the records before the version is determined.
ExpectInitialRecordVersion uint16
@@ -626,7 +642,11 @@ type ProtocolBugs struct {
// the server believes it has actually negotiated.
SendCipherSuite uint16
- // AppDataAfterChangeCipherSpec, if not null, causes application data to
+ // AppDataBeforeHandshake, if not nil, causes application data to be
+ // sent immediately before the first handshake message.
+ AppDataBeforeHandshake []byte
+
+ // AppDataAfterChangeCipherSpec, if not nil, causes application data to
// be sent immediately after ChangeCipherSpec.
AppDataAfterChangeCipherSpec []byte
@@ -668,17 +688,10 @@ type ProtocolBugs struct {
// 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
+ // SplitFragments, if non-zero, causes the handshake fragments in DTLS
+ // to be split across two records. The value of |SplitFragments| is the
+ // number of bytes in the first fragment.
+ SplitFragments int
// SendEmptyFragments, if true, causes handshakes to include empty
// fragments in DTLS.
@@ -705,10 +718,6 @@ type ProtocolBugs struct {
// 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
@@ -727,6 +736,43 @@ type ProtocolBugs struct {
// EnableAllCiphersInDTLS, if true, causes RC4 to be enabled in DTLS.
EnableAllCiphersInDTLS bool
+
+ // EmptyCertificateList, if true, causes the server to send an empty
+ // certificate list in the Certificate message.
+ EmptyCertificateList bool
+
+ // ExpectNewTicket, if true, causes the client to abort if it does not
+ // receive a new ticket.
+ ExpectNewTicket bool
+
+ // RequireClientHelloSize, if not zero, is the required length in bytes
+ // of the ClientHello /record/. This is checked by the server.
+ RequireClientHelloSize int
+
+ // CustomExtension, if not empty, contains the contents of an extension
+ // that will be added to client/server hellos.
+ CustomExtension string
+
+ // ExpectedCustomExtension, if not nil, contains the expected contents
+ // of a custom extension.
+ ExpectedCustomExtension *string
+
+ // NoCloseNotify, if true, causes the close_notify alert to be skipped
+ // on connection shutdown.
+ NoCloseNotify bool
+
+ // ExpectCloseNotify, if true, requires a close_notify from the peer on
+ // shutdown. Records from the peer received after close_notify is sent
+ // are not discard.
+ ExpectCloseNotify bool
+
+ // SendLargeRecords, if true, allows outgoing records to be sent
+ // arbitrarily large.
+ SendLargeRecords bool
+
+ // NegotiateALPNAndNPN, if true, causes the server to negotiate both
+ // ALPN and NPN in the same connetion.
+ NegotiateALPNAndNPN bool
}
func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index adbc1c3..39bdfda 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -12,6 +12,7 @@ import (
"crypto/ecdsa"
"crypto/subtle"
"crypto/x509"
+ "encoding/binary"
"errors"
"fmt"
"io"
@@ -39,6 +40,7 @@ type Conn struct {
extendedMasterSecret bool // whether this session used an extended master secret
cipherSuite *cipherSuite
ocspResponse []byte // stapled OCSP response
+ sctList []byte // signed certificate timestamp list
peerCertificates []*x509.Certificate
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
@@ -48,6 +50,11 @@ type Conn struct {
// firstFinished contains the first Finished hash sent during the
// handshake. This is the "tls-unique" channel binding value.
firstFinished [12]byte
+ // clientCertSignatureHash contains the TLS hash id for the hash that
+ // was used by the client to sign the handshake with a client
+ // certificate. This is only set by a server and is zero if no client
+ // certificates were used.
+ clientCertSignatureHash uint8
clientRandom, serverRandom [32]byte
masterSecret [48]byte
@@ -87,6 +94,8 @@ func (c *Conn) init() {
c.out.isDTLS = c.isDTLS
c.in.config = c.config
c.out.config = c.config
+
+ c.out.updateOutSeq()
}
// Access to net.Conn methods.
@@ -134,6 +143,7 @@ type halfConn struct {
cipher interface{} // cipher algorithm
mac macFunction
seq [8]byte // 64-bit sequence number
+ outSeq [8]byte // Mapped sequence number
bfree *block // list of free blocks
nextCipher interface{} // next encryption state
@@ -189,10 +199,6 @@ func (hc *halfConn) incSeq(isOutgoing bool) {
if hc.isDTLS {
// Increment up to the epoch in DTLS.
limit = 2
-
- if isOutgoing && hc.config.Bugs.SequenceNumberIncrement != 0 {
- increment = hc.config.Bugs.SequenceNumberIncrement
- }
}
for i := 7; i >= limit; i-- {
increment += uint64(hc.seq[i])
@@ -206,6 +212,8 @@ func (hc *halfConn) incSeq(isOutgoing bool) {
if increment != 0 {
panic("TLS: sequence number wraparound")
}
+
+ hc.updateOutSeq()
}
// incNextSeq increments the starting sequence number for the next epoch.
@@ -241,6 +249,22 @@ func (hc *halfConn) incEpoch() {
hc.seq[i] = 0
}
}
+
+ hc.updateOutSeq()
+}
+
+func (hc *halfConn) updateOutSeq() {
+ if hc.config.Bugs.SequenceNumberMapping != nil {
+ seqU64 := binary.BigEndian.Uint64(hc.seq[:])
+ seqU64 = hc.config.Bugs.SequenceNumberMapping(seqU64)
+ binary.BigEndian.PutUint64(hc.outSeq[:], seqU64)
+
+ // The DTLS epoch cannot be changed.
+ copy(hc.outSeq[:2], hc.seq[:2])
+ return
+ }
+
+ copy(hc.outSeq[:], hc.seq[:])
}
func (hc *halfConn) recordHeaderLen() int {
@@ -397,6 +421,8 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
//
// However, our behavior matches OpenSSL, so we leak
// only as much as they do.
+ case nullCipher:
+ break
default:
panic("unknown cipher type")
}
@@ -460,7 +486,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
// mac
if hc.mac != nil {
- mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
+ mac := hc.mac.MAC(hc.outDigestBuf, hc.outSeq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
n := len(b.data)
b.resize(n + len(mac))
@@ -478,7 +504,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
case *tlsAead:
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
b.resize(len(b.data) + c.Overhead())
- nonce := hc.seq[:]
+ nonce := hc.outSeq[:]
if c.explicitNonce {
nonce = b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
}
@@ -486,7 +512,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
payload = payload[:payloadLen]
var additionalData [13]byte
- copy(additionalData[:], hc.seq[:])
+ copy(additionalData[:], hc.outSeq[:])
copy(additionalData[8:], b.data[:3])
additionalData[11] = byte(payloadLen >> 8)
additionalData[12] = byte(payloadLen)
@@ -502,6 +528,8 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
+ case nullCipher:
+ break
default:
panic("unknown cipher type")
}
@@ -630,10 +658,10 @@ func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil {
// RFC suggests that EOF without an alertCloseNotify is
// an error, but popular web sites seem to do this,
- // so we can't make it an error.
- // if err == io.EOF {
- // err = io.ErrUnexpectedEOF
- // }
+ // so we can't make it an error, outside of tests.
+ if err == io.EOF && c.config.Bugs.ExpectCloseNotify {
+ err = io.ErrUnexpectedEOF
+ }
if e, ok := err.(net.Error); !ok || !e.Temporary() {
c.in.setErrorLocked(err)
}
@@ -722,6 +750,10 @@ func (c *Conn) readRecord(want recordType) error {
c.sendAlert(alertInternalError)
return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
}
+ case recordTypeAlert:
+ // Looking for a close_notify. Note: unlike a real
+ // implementation, this is not tolerant of additional records.
+ // See the documentation for ExpectCloseNotify.
}
Again:
@@ -784,7 +816,7 @@ Again:
// A client might need to process a HelloRequest from
// the server, thus receiving a handshake message when
// application data is expected is ok.
- if !c.isClient {
+ if !c.isClient || want != recordTypeApplicationData {
return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
}
}
@@ -799,13 +831,8 @@ Again:
// sendAlert sends a TLS alert message.
// c.out.Mutex <= L.
-func (c *Conn) sendAlertLocked(err alert) error {
- switch err {
- case alertNoRenegotiation, alertCloseNotify:
- c.tmp[0] = alertLevelWarning
- default:
- c.tmp[0] = alertLevelError
- }
+func (c *Conn) sendAlertLocked(level byte, err alert) error {
+ c.tmp[0] = level
c.tmp[1] = byte(err)
if c.config.Bugs.FragmentAlert {
c.writeRecord(recordTypeAlert, c.tmp[0:1])
@@ -813,8 +840,8 @@ func (c *Conn) sendAlertLocked(err alert) error {
} else {
c.writeRecord(recordTypeAlert, c.tmp[0:2])
}
- // closeNotify is a special case in that it isn't an error:
- if err != alertCloseNotify {
+ // Error alerts are fatal to the connection.
+ if level == alertLevelError {
return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
}
return nil
@@ -823,9 +850,17 @@ func (c *Conn) sendAlertLocked(err alert) error {
// sendAlert sends a TLS alert message.
// L < c.out.Mutex.
func (c *Conn) sendAlert(err alert) error {
+ level := byte(alertLevelError)
+ if err == alertNoRenegotiation || err == alertCloseNotify {
+ level = alertLevelWarning
+ }
+ return c.SendAlert(level, err)
+}
+
+func (c *Conn) SendAlert(level byte, err alert) error {
c.out.Lock()
defer c.out.Unlock()
- return c.sendAlertLocked(err)
+ return c.sendAlertLocked(level, err)
}
// writeV2Record writes a record for a V2ClientHello.
@@ -841,13 +876,6 @@ 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)
}
@@ -856,9 +884,9 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
b := c.out.newBlock()
first := true
isClientHello := typ == recordTypeHandshake && len(data) > 0 && data[0] == typeClientHello
- for len(data) > 0 {
+ for len(data) > 0 || first {
m := len(data)
- if m > maxPlaintext {
+ if m > maxPlaintext && !c.config.Bugs.SendLargeRecords {
m = maxPlaintext
}
if typ == recordTypeHandshake && c.config.Bugs.MaxHandshakeRecordLength > 0 && m > c.config.Bugs.MaxHandshakeRecordLength {
@@ -1038,6 +1066,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
// sequence number expectations but otherwise ignores them.
func (c *Conn) skipPacket(packet []byte) error {
for len(packet) > 0 {
+ if len(packet) < 13 {
+ return errors.New("tls: bad packet")
+ }
// 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
@@ -1057,6 +1088,9 @@ func (c *Conn) skipPacket(packet []byte) error {
}
c.in.incNextSeq()
}
+ if len(packet) < 13+int(length) {
+ return errors.New("tls: bad packet")
+ }
packet = packet[13+length:]
}
return nil
@@ -1113,7 +1147,7 @@ func (c *Conn) Write(b []byte) (int, error) {
}
if c.config.Bugs.SendSpuriousAlert != 0 {
- c.sendAlertLocked(c.config.Bugs.SendSpuriousAlert)
+ c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert)
}
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
@@ -1240,10 +1274,22 @@ func (c *Conn) Close() error {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
- if c.handshakeComplete {
+ if c.handshakeComplete && !c.config.Bugs.NoCloseNotify {
alertErr = c.sendAlert(alertCloseNotify)
}
+ // Consume a close_notify from the peer if one hasn't been received
+ // already. This avoids the peer from failing |SSL_shutdown| due to a
+ // write failing.
+ if c.handshakeComplete && alertErr == nil && c.config.Bugs.ExpectCloseNotify {
+ for c.in.error() == nil {
+ c.readRecord(recordTypeAlert)
+ }
+ if c.in.error() != io.EOF {
+ alertErr = c.in.error()
+ }
+ }
+
if err := c.conn.Close(); err != nil {
return err
}
@@ -1273,6 +1319,9 @@ func (c *Conn) Handshake() error {
})
c.conn.Write([]byte{alertLevelError, byte(alertInternalError)})
}
+ if data := c.config.Bugs.AppDataBeforeHandshake; data != nil {
+ c.writeRecord(recordTypeApplicationData, data)
+ }
if c.isClient {
c.handshakeErr = c.clientHandshake()
} else {
@@ -1304,6 +1353,8 @@ func (c *Conn) ConnectionState() ConnectionState {
state.ChannelID = c.channelID
state.SRTPProtectionProfile = c.srtpProtectionProfile
state.TLSUnique = c.firstFinished[:]
+ state.SCTList = c.sctList
+ state.ClientCertSignatureHash = c.clientCertSignatureHash
}
return state
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index 50f7786..5c59dea 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -216,13 +216,10 @@ func (c *Conn) dtlsFlushHandshake() error {
// Pack handshake fragments into records.
var records [][]byte
for _, fragment := range fragments {
- if c.config.Bugs.SplitFragmentHeader {
- records = append(records, fragment[:2])
- records = append(records, fragment[2:])
- } else if c.config.Bugs.SplitFragmentBody {
- if len(fragment) > 12 {
- records = append(records, fragment[:13])
- records = append(records, fragment[13:])
+ if n := c.config.Bugs.SplitFragments; n > 0 {
+ if len(fragment) > n {
+ records = append(records, fragment[:n])
+ records = append(records, fragment[n:])
} else {
records = append(records, fragment)
}
@@ -301,13 +298,13 @@ func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error)
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:])
+ copy(b.data[3:11], c.out.outSeq[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[:])
+ copy(explicitIV, c.out.outSeq[:])
} else {
if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
return
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index a950313..a3ce686 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -45,7 +45,7 @@ func (c *Conn) clientHandshake() error {
nextProtosLength := 0
for _, proto := range c.config.NextProtos {
- if l := len(proto); l == 0 || l > 255 {
+ if l := len(proto); l > 255 {
return errors.New("tls: invalid NextProtos value")
} else {
nextProtosLength += 1 + l
@@ -61,6 +61,7 @@ func (c *Conn) clientHandshake() error {
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
+ sctListSupported: true,
serverName: c.config.ServerName,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
@@ -73,6 +74,7 @@ func (c *Conn) clientHandshake() error {
extendedMasterSecret: c.config.maxVersion() >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
+ customExtension: c.config.Bugs.CustomExtension,
}
if c.config.Bugs.SendClientVersion != 0 {
@@ -123,6 +125,10 @@ NextCipherSuite:
}
}
+ if c.config.Bugs.SendRenegotiationSCSV {
+ hello.cipherSuites = append(hello.cipherSuites, renegotiationSCSV)
+ }
+
if c.config.Bugs.SendFallbackSCSV {
hello.cipherSuites = append(hello.cipherSuites, fallbackSCSV)
}
@@ -272,6 +278,10 @@ NextCipherSuite:
return fmt.Errorf("tls: server selected an unsupported cipher suite")
}
+ if c.config.Bugs.RequireRenegotiationInfo && serverHello.secureRenegotiation == nil {
+ return errors.New("tls: renegotiation extension missing")
+ }
+
if len(c.clientVerify) > 0 && !c.config.Bugs.NoRenegotiationInfo {
var expectedRenegInfo []byte
expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...)
@@ -282,6 +292,12 @@ NextCipherSuite:
}
}
+ if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil {
+ if serverHello.customExtension != *expected {
+ return fmt.Errorf("tls: bad custom extension contents %q", serverHello.customExtension)
+ }
+ }
+
hs := &clientHandshakeState{
c: c,
serverHello: serverHello,
@@ -356,6 +372,7 @@ NextCipherSuite:
copy(c.clientRandom[:], hs.hello.random)
copy(c.serverRandom[:], hs.serverHello.random)
copy(c.masterSecret[:], hs.masterSecret)
+
return nil
}
@@ -607,6 +624,9 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.sendAlert(alertInternalError)
return err
}
+ if c.config.Bugs.InvalidCertVerifySignature {
+ digest[0] ^= 0x80
+ }
switch key := c.config.Certificates[0].PrivateKey.(type) {
case *ecdsa.PrivateKey:
@@ -730,13 +750,28 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, errors.New("tls: server resumed session on renegotiation")
}
+ if hs.serverHello.sctList != nil {
+ return false, errors.New("tls: server sent SCT extension on session resumption")
+ }
+
+ if hs.serverHello.ocspStapling {
+ return false, errors.New("tls: server sent OCSP extension on session resumption")
+ }
+
// Restore masterSecret and peerCerts from previous state
hs.masterSecret = hs.session.masterSecret
c.peerCertificates = hs.session.serverCertificates
c.extendedMasterSecret = hs.session.extendedMasterSecret
+ c.sctList = hs.session.sctList
+ c.ocspResponse = hs.session.ocspResponse
hs.finishedHash.discardHandshakeBuffer()
return true, nil
}
+
+ if hs.serverHello.sctList != nil {
+ c.sctList = hs.serverHello.sctList
+ }
+
return false, nil
}
@@ -783,9 +818,14 @@ func (hs *clientHandshakeState) readSessionTicket() error {
masterSecret: hs.masterSecret,
handshakeHash: hs.finishedHash.server.Sum(nil),
serverCertificates: c.peerCertificates,
+ sctList: c.sctList,
+ ocspResponse: c.ocspResponse,
}
if !hs.serverHello.ticketSupported {
+ if c.config.Bugs.ExpectNewTicket {
+ return errors.New("tls: expected new ticket")
+ }
if hs.session == nil && len(hs.serverHello.sessionId) > 0 {
session.sessionId = hs.serverHello.sessionId
hs.session = session
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index ce214fd..da85e7a 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -32,6 +32,7 @@ type clientHelloMsg struct {
srtpProtectionProfiles []uint16
srtpMasterKeyIdentifier string
sctListSupported bool
+ customExtension string
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -65,7 +66,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.extendedMasterSecret == m1.extendedMasterSecret &&
eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
- m.sctListSupported == m1.sctListSupported
+ m.sctListSupported == m1.sctListSupported &&
+ m.customExtension == m1.customExtension
}
func (m *clientHelloMsg) marshal() []byte {
@@ -119,7 +121,7 @@ func (m *clientHelloMsg) marshal() []byte {
if len(m.alpnProtocols) > 0 {
extensionsLength += 2
for _, s := range m.alpnProtocols {
- if l := len(s); l == 0 || l > 255 {
+ if l := len(s); l > 255 {
panic("invalid ALPN protocol")
}
extensionsLength++
@@ -138,6 +140,10 @@ func (m *clientHelloMsg) marshal() []byte {
if m.sctListSupported {
numExtensions++
}
+ if l := len(m.customExtension); l > 0 {
+ extensionsLength += l
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -376,6 +382,14 @@ func (m *clientHelloMsg) marshal() []byte {
z[1] = byte(extensionSignedCertificateTimestamp & 0xff)
z = z[4:]
}
+ if l := len(m.customExtension); l > 0 {
+ z[0] = byte(extensionCustom >> 8)
+ z[1] = byte(extensionCustom & 0xff)
+ z[2] = byte(l >> 8)
+ z[3] = byte(l & 0xff)
+ copy(z[4:], []byte(m.customExtension))
+ z = z[4+l:]
+ }
m.raw = x
@@ -443,6 +457,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.signatureAndHashes = nil
m.alpnProtocols = nil
m.extendedMasterSecret = false
+ m.customExtension = ""
if len(data) == 0 {
// ClientHello is optionally followed by extension data
@@ -604,6 +619,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false
}
m.sctListSupported = true
+ case extensionCustom:
+ m.customExtension = string(data[:length])
}
data = data[length:]
}
@@ -625,40 +642,15 @@ type serverHelloMsg struct {
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
+ alpnProtocolEmpty bool
duplicateExtension bool
channelIDRequested bool
extendedMasterSecret bool
srtpProtectionProfile uint16
srtpMasterKeyIdentifier string
sctList []byte
-}
-
-func (m *serverHelloMsg) equal(i interface{}) bool {
- m1, ok := i.(*serverHelloMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.isDTLS == m1.isDTLS &&
- m.vers == m1.vers &&
- bytes.Equal(m.random, m1.random) &&
- bytes.Equal(m.sessionId, m1.sessionId) &&
- m.cipherSuite == m1.cipherSuite &&
- m.compressionMethod == m1.compressionMethod &&
- m.nextProtoNeg == m1.nextProtoNeg &&
- eqStrings(m.nextProtos, m1.nextProtos) &&
- m.ocspStapling == m1.ocspStapling &&
- m.ticketSupported == m1.ticketSupported &&
- bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
- (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) &&
- m.alpnProtocol == m1.alpnProtocol &&
- m.duplicateExtension == m1.duplicateExtension &&
- m.channelIDRequested == m1.channelIDRequested &&
- m.extendedMasterSecret == m1.extendedMasterSecret &&
- m.srtpProtectionProfile == m1.srtpProtectionProfile &&
- m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
- bytes.Equal(m.sctList, m1.sctList)
+ customExtension string
+ npnLast bool
}
func (m *serverHelloMsg) marshal() []byte {
@@ -695,7 +687,7 @@ func (m *serverHelloMsg) marshal() []byte {
if m.channelIDRequested {
numExtensions++
}
- if alpnLen := len(m.alpnProtocol); alpnLen > 0 {
+ if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty {
if alpnLen >= 256 {
panic("invalid ALPN protocol")
}
@@ -713,6 +705,10 @@ func (m *serverHelloMsg) marshal() []byte {
extensionsLength += len(m.sctList)
numExtensions++
}
+ if l := len(m.customExtension); l > 0 {
+ extensionsLength += l
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
@@ -747,7 +743,7 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = 0xff
z = z[4:]
}
- if m.nextProtoNeg {
+ if m.nextProtoNeg && !m.npnLast {
z[0] = byte(extensionNextProtoNeg >> 8)
z[1] = byte(extensionNextProtoNeg & 0xff)
z[2] = byte(nextProtoLen >> 8)
@@ -784,7 +780,7 @@ func (m *serverHelloMsg) marshal() []byte {
copy(z, m.secureRenegotiation)
z = z[len(m.secureRenegotiation):]
}
- if alpnLen := len(m.alpnProtocol); alpnLen > 0 {
+ if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty {
z[0] = byte(extensionALPN >> 8)
z[1] = byte(extensionALPN & 0xff)
l := 2 + 1 + alpnLen
@@ -838,6 +834,31 @@ func (m *serverHelloMsg) marshal() []byte {
copy(z[4:], m.sctList)
z = z[4+l:]
}
+ if l := len(m.customExtension); l > 0 {
+ z[0] = byte(extensionCustom >> 8)
+ z[1] = byte(extensionCustom & 0xff)
+ z[2] = byte(l >> 8)
+ z[3] = byte(l & 0xff)
+ copy(z[4:], []byte(m.customExtension))
+ z = z[4+l:]
+ }
+ if m.nextProtoNeg && m.npnLast {
+ z[0] = byte(extensionNextProtoNeg >> 8)
+ z[1] = byte(extensionNextProtoNeg & 0xff)
+ z[2] = byte(nextProtoLen >> 8)
+ z[3] = byte(nextProtoLen)
+ z = z[4:]
+
+ for _, v := range m.nextProtos {
+ l := len(v)
+ if l > 255 {
+ l = 255
+ }
+ z[0] = byte(l)
+ copy(z[1:], []byte(v[0:l]))
+ z = z[1+l:]
+ }
+ }
m.raw = x
@@ -869,7 +890,9 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
m.ocspStapling = false
m.ticketSupported = false
m.alpnProtocol = ""
+ m.alpnProtocolEmpty = false
m.extendedMasterSecret = false
+ m.customExtension = ""
if len(data) == 0 {
// ServerHello is optionally followed by extension data
@@ -940,6 +963,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
}
d = d[1:]
m.alpnProtocol = string(d)
+ m.alpnProtocolEmpty = len(d) == 0
case extensionChannelID:
if length > 0 {
return false
@@ -965,14 +989,9 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
}
m.srtpMasterKeyIdentifier = string(d[1:])
case extensionSignedCertificateTimestamp:
- if length < 2 {
- return false
- }
- l := int(data[0])<<8 | int(data[1])
- if l != len(data)-2 {
- return false
- }
- m.sctList = data[2:length]
+ m.sctList = data[:length]
+ case extensionCustom:
+ m.customExtension = string(data[:length])
}
data = data[length:]
}
@@ -985,16 +1004,6 @@ type certificateMsg struct {
certificates [][]byte
}
-func (m *certificateMsg) equal(i interface{}) bool {
- m1, ok := i.(*certificateMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- eqByteSlices(m.certificates, m1.certificates)
-}
-
func (m *certificateMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -1072,16 +1081,6 @@ type serverKeyExchangeMsg struct {
key []byte
}
-func (m *serverKeyExchangeMsg) equal(i interface{}) bool {
- m1, ok := i.(*serverKeyExchangeMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.key, m1.key)
-}
-
func (m *serverKeyExchangeMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1113,17 +1112,6 @@ type certificateStatusMsg struct {
response []byte
}
-func (m *certificateStatusMsg) equal(i interface{}) bool {
- m1, ok := i.(*certificateStatusMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.statusType == m1.statusType &&
- bytes.Equal(m.response, m1.response)
-}
-
func (m *certificateStatusMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1175,11 +1163,6 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool {
type serverHelloDoneMsg struct{}
-func (m *serverHelloDoneMsg) equal(i interface{}) bool {
- _, ok := i.(*serverHelloDoneMsg)
- return ok
-}
-
func (m *serverHelloDoneMsg) marshal() []byte {
x := make([]byte, 4)
x[0] = typeServerHelloDone
@@ -1195,16 +1178,6 @@ type clientKeyExchangeMsg struct {
ciphertext []byte
}
-func (m *clientKeyExchangeMsg) equal(i interface{}) bool {
- m1, ok := i.(*clientKeyExchangeMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.ciphertext, m1.ciphertext)
-}
-
func (m *clientKeyExchangeMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1239,16 +1212,6 @@ type finishedMsg struct {
verifyData []byte
}
-func (m *finishedMsg) equal(i interface{}) bool {
- m1, ok := i.(*finishedMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.verifyData, m1.verifyData)
-}
-
func (m *finishedMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -1276,16 +1239,6 @@ type nextProtoMsg struct {
proto string
}
-func (m *nextProtoMsg) equal(i interface{}) bool {
- m1, ok := i.(*nextProtoMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.proto == m1.proto
-}
-
func (m *nextProtoMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1353,18 +1306,6 @@ type certificateRequestMsg struct {
certificateAuthorities [][]byte
}
-func (m *certificateRequestMsg) equal(i interface{}) bool {
- m1, ok := i.(*certificateRequestMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
- eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
- eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
-}
-
func (m *certificateRequestMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -1507,19 +1448,6 @@ type certificateVerifyMsg struct {
signature []byte
}
-func (m *certificateVerifyMsg) equal(i interface{}) bool {
- m1, ok := i.(*certificateVerifyMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.hasSignatureAndHash == m1.hasSignatureAndHash &&
- m.signatureAndHash.hash == m1.signatureAndHash.hash &&
- m.signatureAndHash.signature == m1.signatureAndHash.signature &&
- bytes.Equal(m.signature, m1.signature)
-}
-
func (m *certificateVerifyMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -1589,16 +1517,6 @@ type newSessionTicketMsg struct {
ticket []byte
}
-func (m *newSessionTicketMsg) equal(i interface{}) bool {
- m1, ok := i.(*newSessionTicketMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.ticket, m1.ticket)
-}
-
func (m *newSessionTicketMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
@@ -1651,19 +1569,6 @@ type v2ClientHelloMsg struct {
challenge []byte
}
-func (m *v2ClientHelloMsg) equal(i interface{}) bool {
- m1, ok := i.(*v2ClientHelloMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.vers == m1.vers &&
- eqUint16s(m.cipherSuites, m1.cipherSuites) &&
- bytes.Equal(m.sessionId, m1.sessionId) &&
- bytes.Equal(m.challenge, m1.challenge)
-}
-
func (m *v2ClientHelloMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1703,17 +1608,6 @@ type helloVerifyRequestMsg struct {
cookie []byte
}
-func (m *helloVerifyRequestMsg) equal(i interface{}) bool {
- m1, ok := i.(*helloVerifyRequestMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- m.vers == m1.vers &&
- bytes.Equal(m.cookie, m1.cookie)
-}
-
func (m *helloVerifyRequestMsg) marshal() []byte {
if m.raw != nil {
return m.raw
@@ -1755,16 +1649,6 @@ type encryptedExtensionsMsg struct {
channelID []byte
}
-func (m *encryptedExtensionsMsg) equal(i interface{}) bool {
- m1, ok := i.(*encryptedExtensionsMsg)
- if !ok {
- return false
- }
-
- return bytes.Equal(m.raw, m1.raw) &&
- bytes.Equal(m.channelID, m1.channelID)
-}
-
func (m *encryptedExtensionsMsg) marshal() []byte {
if m.raw != nil {
return m.raw
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 85cc0d2..068dff9 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -139,8 +139,8 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
c.sendAlert(alertUnexpectedMessage)
return false, unexpectedMessageError(hs.clientHello, msg)
}
- if config.Bugs.RequireFastradioPadding && len(hs.clientHello.raw) < 1000 {
- return false, errors.New("tls: ClientHello record size should be larger than 1000 bytes when padding enabled.")
+ if size := config.Bugs.RequireClientHelloSize; size != 0 && len(hs.clientHello.raw) != size {
+ return false, fmt.Errorf("tls: ClientHello record size is %d, but expected %d", len(hs.clientHello.raw), size)
}
if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest {
@@ -210,8 +210,11 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
}
c.haveVers = true
- hs.hello = new(serverHelloMsg)
- hs.hello.isDTLS = c.isDTLS
+ hs.hello = &serverHelloMsg{
+ isDTLS: c.isDTLS,
+ customExtension: config.Bugs.CustomExtension,
+ npnLast: config.Bugs.SwapNPNAndALPN,
+ }
supportedCurve := false
preferredCurves := config.curvePreferences()
@@ -285,12 +288,18 @@ Curves:
}
if len(hs.clientHello.alpnProtocols) > 0 {
- if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
+ if proto := c.config.Bugs.ALPNProtocol; proto != nil {
+ hs.hello.alpnProtocol = *proto
+ hs.hello.alpnProtocolEmpty = len(*proto) == 0
+ c.clientProtocol = *proto
+ c.usedALPN = true
+ } else if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
hs.hello.alpnProtocol = selectedProto
c.clientProtocol = selectedProto
c.usedALPN = true
}
- } else {
+ }
+ if len(hs.clientHello.alpnProtocols) == 0 || c.config.Bugs.NegotiateALPNAndNPN {
// Although sending an empty NPN extension is reasonable, Firefox has
// had a bug around this. Best to send nothing at all if
// config.NextProtos is empty. See
@@ -335,6 +344,12 @@ Curves:
hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile
}
+ if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil {
+ if hs.clientHello.customExtension != *expected {
+ return false, fmt.Errorf("tls: bad custom extension contents %q", hs.clientHello.customExtension)
+ }
+ }
+
_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
// For test purposes, check that the peer never offers a session when
@@ -516,7 +531,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if !isPSK {
certMsg := new(certificateMsg)
- certMsg.certificates = hs.cert.Certificate
+ if !config.Bugs.EmptyCertificateList {
+ certMsg.certificates = hs.cert.Certificate
+ }
if !config.Bugs.UnauthenticatedECDH {
certMsgBytes := certMsg.marshal()
if config.Bugs.WrongCertificateMessageType {
@@ -668,6 +685,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
return errors.New("tls: unsupported hash function for client certificate")
}
+ c.clientCertSignatureHash = signatureAndHash.hash
} else {
// Before TLS 1.2 the signature algorithm was implicit
// from the key type, and only one hash per signature
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 94c1d32..269a955 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -32,6 +32,10 @@ var (
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.")
+ testToRun = flag.String("test", "", "The name of a test to run, or empty to run all tests")
+ numWorkers = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
+ shimPath = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.")
+ resourceDir = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.")
)
const (
@@ -54,21 +58,21 @@ var testSCTList = []byte{5, 6, 7, 8}
func initCertificates() {
var err error
- rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
+ rsaCertificate, err = LoadX509KeyPair(path.Join(*resourceDir, rsaCertificateFile), path.Join(*resourceDir, rsaKeyFile))
if err != nil {
panic(err)
}
rsaCertificate.OCSPStaple = testOCSPResponse
rsaCertificate.SignedCertificateTimestampList = testSCTList
- ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
+ ecdsaCertificate, err = LoadX509KeyPair(path.Join(*resourceDir, ecdsaCertificateFile), path.Join(*resourceDir, ecdsaKeyFile))
if err != nil {
panic(err)
}
ecdsaCertificate.OCSPStaple = testOCSPResponse
ecdsaCertificate.SignedCertificateTimestampList = testSCTList
- channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile)
+ channelIDPEMBlock, err := ioutil.ReadFile(path.Join(*resourceDir, channelIDKeyFile))
if err != nil {
panic(err)
}
@@ -151,9 +155,21 @@ type testCase struct {
// expectedSRTPProtectionProfile is the DTLS-SRTP profile that
// should be negotiated. If zero, none should be negotiated.
expectedSRTPProtectionProfile uint16
+ // expectedOCSPResponse, if not nil, is the expected OCSP response to be received.
+ expectedOCSPResponse []uint8
+ // expectedSCTList, if not nil, is the expected SCT list to be received.
+ expectedSCTList []uint8
+ // expectedClientCertSignatureHash, if not zero, is the TLS id of the
+ // hash function that the client should have used when signing the
+ // handshake with a client certificate.
+ expectedClientCertSignatureHash uint8
// messageLen is the length, in bytes, of the test message that will be
// sent.
messageLen int
+ // messageCount is the number of test messages that will be sent.
+ messageCount int
+ // digestPrefs is the list of digest preferences from the client.
+ digestPrefs string
// certFile is the path to the certificate to use for the server.
certFile string
// keyFile is the path to the private key to use for the server.
@@ -174,12 +190,19 @@ type testCase struct {
// newSessionsOnResume, if true, will cause resumeConfig to
// use a different session resumption context.
newSessionsOnResume bool
+ // noSessionCache, if true, will cause the server to run without a
+ // session cache.
+ noSessionCache bool
// sendPrefix sends a prefix on the socket before actually performing a
// handshake.
sendPrefix string
// shimWritesFirst controls whether the shim sends an initial "hello"
// message before doing a roundtrip with the runner.
shimWritesFirst bool
+ // shimShutsDown, if true, runs a test where the shim shuts down the
+ // connection immediately after the handshake rather than echoing
+ // messages from the runner.
+ shimShutsDown bool
// renegotiate indicates the the connection should be renegotiated
// during the exchange.
renegotiate bool
@@ -204,941 +227,20 @@ type testCase struct {
// testTLSUnique, if true, causes the shim to send the tls-unique value
// which will be compared against the expected value.
testTLSUnique bool
+ // sendEmptyRecords is the number of consecutive empty records to send
+ // before and after the test message.
+ sendEmptyRecords int
+ // sendWarningAlerts is the number of consecutive warning alerts to send
+ // before and after the test message.
+ sendWarningAlerts int
+ // expectMessageDropped, if true, means the test message is expected to
+ // be dropped by the client rather than echoed back.
+ expectMessageDropped bool
}
-var testCases = []testCase{
- {
- name: "BadRSASignature",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- Bugs: ProtocolBugs{
- InvalidSKXSignature: true,
- },
- },
- shouldFail: true,
- expectedError: ":BAD_SIGNATURE:",
- },
- {
- name: "BadECDSASignature",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
- Bugs: ProtocolBugs{
- InvalidSKXSignature: true,
- },
- Certificates: []Certificate{getECDSACertificate()},
- },
- shouldFail: true,
- expectedError: ":BAD_SIGNATURE:",
- },
- {
- name: "BadECDSACurve",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
- Bugs: ProtocolBugs{
- InvalidSKXCurve: true,
- },
- Certificates: []Certificate{getECDSACertificate()},
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- },
- {
- testType: serverTest,
- name: "BadRSAVersion",
- config: Config{
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- Bugs: ProtocolBugs{
- RsaClientKeyExchangeVersion: VersionTLS11,
- },
- },
- shouldFail: true,
- expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
- },
- {
- name: "NoFallbackSCSV",
- config: Config{
- Bugs: ProtocolBugs{
- FailIfNotFallbackSCSV: true,
- },
- },
- shouldFail: true,
- expectedLocalError: "no fallback SCSV found",
- },
- {
- name: "SendFallbackSCSV",
- config: Config{
- Bugs: ProtocolBugs{
- FailIfNotFallbackSCSV: true,
- },
- },
- flags: []string{"-fallback-scsv"},
- },
- {
- name: "ClientCertificateTypes",
- config: Config{
- ClientAuth: RequestClientCert,
- ClientCertificateTypes: []byte{
- CertTypeDSSSign,
- CertTypeRSASign,
- CertTypeECDSASign,
- },
- },
- flags: []string{
- "-expect-certificate-types",
- base64.StdEncoding.EncodeToString([]byte{
- CertTypeDSSSign,
- CertTypeRSASign,
- CertTypeECDSASign,
- }),
- },
- },
- {
- name: "NoClientCertificate",
- config: Config{
- ClientAuth: RequireAnyClientCert,
- },
- shouldFail: true,
- expectedLocalError: "client didn't provide a certificate",
- },
- {
- name: "UnauthenticatedECDH",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- Bugs: ProtocolBugs{
- UnauthenticatedECDH: true,
- },
- },
- shouldFail: true,
- 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},
- Bugs: ProtocolBugs{
- SkipServerKeyExchange: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- },
- {
- name: "SkipChangeCipherSpec-Client",
- config: Config{
- Bugs: ProtocolBugs{
- SkipChangeCipherSpec: true,
- },
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- testType: serverTest,
- name: "SkipChangeCipherSpec-Server",
- config: Config{
- Bugs: ProtocolBugs{
- SkipChangeCipherSpec: true,
- },
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- testType: serverTest,
- name: "SkipChangeCipherSpec-Server-NPN",
- config: Config{
- NextProtos: []string{"bar"},
- Bugs: ProtocolBugs{
- SkipChangeCipherSpec: true,
- },
- },
- flags: []string{
- "-advertise-npn", "\x03foo\x03bar\x03baz",
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- name: "FragmentAcrossChangeCipherSpec-Client",
- config: Config{
- Bugs: ProtocolBugs{
- FragmentAcrossChangeCipherSpec: true,
- },
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- testType: serverTest,
- name: "FragmentAcrossChangeCipherSpec-Server",
- config: Config{
- Bugs: ProtocolBugs{
- FragmentAcrossChangeCipherSpec: true,
- },
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- testType: serverTest,
- name: "FragmentAcrossChangeCipherSpec-Server-NPN",
- config: Config{
- NextProtos: []string{"bar"},
- Bugs: ProtocolBugs{
- FragmentAcrossChangeCipherSpec: true,
- },
- },
- flags: []string{
- "-advertise-npn", "\x03foo\x03bar\x03baz",
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
- },
- {
- 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: alertRecordOverflow,
- },
- },
- shouldFail: true,
- expectedError: ":BAD_ALERT:",
- },
- {
- protocol: dtls,
- testType: serverTest,
- name: "FragmentAlert-DTLS",
- config: Config{
- Bugs: ProtocolBugs{
- FragmentAlert: true,
- SendSpuriousAlert: alertRecordOverflow,
- },
- },
- shouldFail: true,
- expectedError: ":BAD_ALERT:",
- },
- {
- testType: serverTest,
- name: "EarlyChangeCipherSpec-server-1",
- config: Config{
- Bugs: ProtocolBugs{
- EarlyChangeCipherSpec: 1,
- },
- },
- shouldFail: true,
- expectedError: ":CCS_RECEIVED_EARLY:",
- },
- {
- testType: serverTest,
- name: "EarlyChangeCipherSpec-server-2",
- config: Config{
- Bugs: ProtocolBugs{
- EarlyChangeCipherSpec: 2,
- },
- },
- shouldFail: true,
- expectedError: ":CCS_RECEIVED_EARLY:",
- },
- {
- name: "SkipNewSessionTicket",
- config: Config{
- Bugs: ProtocolBugs{
- SkipNewSessionTicket: true,
- },
- },
- shouldFail: true,
- expectedError: ":CCS_RECEIVED_EARLY:",
- },
- {
- testType: serverTest,
- name: "FallbackSCSV",
- config: Config{
- MaxVersion: VersionTLS11,
- Bugs: ProtocolBugs{
- SendFallbackSCSV: true,
- },
- },
- shouldFail: true,
- expectedError: ":INAPPROPRIATE_FALLBACK:",
- },
- {
- testType: serverTest,
- name: "FallbackSCSV-VersionMatch",
- config: Config{
- Bugs: ProtocolBugs{
- SendFallbackSCSV: true,
- },
- },
- },
- {
- testType: serverTest,
- name: "FragmentedClientVersion",
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: 1,
- FragmentClientVersion: true,
- },
- },
- expectedVersion: VersionTLS12,
- },
- {
- testType: serverTest,
- name: "MinorVersionTolerance",
- config: Config{
- Bugs: ProtocolBugs{
- SendClientVersion: 0x03ff,
- },
- },
- expectedVersion: VersionTLS12,
- },
- {
- testType: serverTest,
- name: "MajorVersionTolerance",
- config: Config{
- Bugs: ProtocolBugs{
- SendClientVersion: 0x0400,
- },
- },
- expectedVersion: VersionTLS12,
- },
- {
- testType: serverTest,
- name: "VersionTooLow",
- config: Config{
- Bugs: ProtocolBugs{
- SendClientVersion: 0x0200,
- },
- },
- shouldFail: true,
- expectedError: ":UNSUPPORTED_PROTOCOL:",
- },
- {
- testType: serverTest,
- name: "HttpGET",
- sendPrefix: "GET / HTTP/1.0\n",
- shouldFail: true,
- expectedError: ":HTTP_REQUEST:",
- },
- {
- testType: serverTest,
- name: "HttpPOST",
- sendPrefix: "POST / HTTP/1.0\n",
- shouldFail: true,
- expectedError: ":HTTP_REQUEST:",
- },
- {
- testType: serverTest,
- name: "HttpHEAD",
- sendPrefix: "HEAD / HTTP/1.0\n",
- shouldFail: true,
- expectedError: ":HTTP_REQUEST:",
- },
- {
- testType: serverTest,
- name: "HttpPUT",
- sendPrefix: "PUT / HTTP/1.0\n",
- shouldFail: true,
- expectedError: ":HTTP_REQUEST:",
- },
- {
- testType: serverTest,
- name: "HttpCONNECT",
- sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
- shouldFail: true,
- expectedError: ":HTTPS_PROXY_REQUEST:",
- },
- {
- testType: serverTest,
- name: "Garbage",
- sendPrefix: "blah",
- shouldFail: true,
- expectedError: ":UNKNOWN_PROTOCOL:",
- },
- {
- name: "SkipCipherVersionCheck",
- config: Config{
- CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
- MaxVersion: VersionTLS11,
- Bugs: ProtocolBugs{
- SkipCipherVersionCheck: true,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CIPHER_RETURNED:",
- },
- {
- name: "RSAEphemeralKey",
- config: Config{
- CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
- Bugs: ProtocolBugs{
- RSAEphemeralKey: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- },
- {
- name: "DisableEverything",
- flags: []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
- shouldFail: true,
- expectedError: ":WRONG_SSL_VERSION:",
- },
- {
- protocol: dtls,
- name: "DisableEverything-DTLS",
- flags: []string{"-no-tls12", "-no-tls1"},
- shouldFail: true,
- expectedError: ":WRONG_SSL_VERSION:",
- },
- {
- name: "NoSharedCipher",
- config: Config{
- CipherSuites: []uint16{},
- },
- shouldFail: true,
- expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:",
- },
- {
- protocol: dtls,
- testType: serverTest,
- name: "MTU",
- config: Config{
- Bugs: ProtocolBugs{
- MaxPacketLength: 256,
- },
- },
- flags: []string{"-mtu", "256"},
- },
- {
- protocol: dtls,
- testType: serverTest,
- name: "MTUExceeded",
- config: Config{
- Bugs: ProtocolBugs{
- MaxPacketLength: 255,
- },
- },
- flags: []string{"-mtu", "256"},
- shouldFail: true,
- expectedLocalError: "dtls: exceeded maximum packet length",
- },
- {
- name: "CertMismatchRSA",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
- Certificates: []Certificate{getECDSACertificate()},
- Bugs: ProtocolBugs{
- SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CERTIFICATE_TYPE:",
- },
- {
- name: "CertMismatchECDSA",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- Certificates: []Certificate{getRSACertificate()},
- Bugs: ProtocolBugs{
- SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CERTIFICATE_TYPE:",
- },
- {
- name: "TLSFatalBadPackets",
- damageFirstWrite: true,
- shouldFail: true,
- expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
- },
- {
- protocol: dtls,
- name: "DTLSIgnoreBadPackets",
- damageFirstWrite: true,
- },
- {
- protocol: dtls,
- name: "DTLSIgnoreBadPackets-Async",
- damageFirstWrite: true,
- flags: []string{"-async"},
- },
- {
- name: "AppDataAfterChangeCipherSpec",
- config: Config{
- Bugs: ProtocolBugs{
- AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"),
- },
- },
- shouldFail: true,
- expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:",
- },
- {
- protocol: dtls,
- name: "AppDataAfterChangeCipherSpec-DTLS",
- config: Config{
- Bugs: ProtocolBugs{
- 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,
- },
- {
- protocol: dtls,
- name: "SendSplitAlert-Sync",
- config: Config{
- Bugs: ProtocolBugs{
- SendSplitAlert: true,
- },
- },
- },
- {
- protocol: dtls,
- name: "SendSplitAlert-Async",
- config: Config{
- Bugs: ProtocolBugs{
- SendSplitAlert: true,
- },
- },
- flags: []string{"-async"},
- },
- {
- protocol: dtls,
- name: "PackDTLSHandshake",
- config: Config{
- Bugs: ProtocolBugs{
- MaxHandshakeRecordLength: 2,
- PackHandshakeFragments: 20,
- PackHandshakeRecords: 200,
- },
- },
- },
- {
- testType: serverTest,
- protocol: dtls,
- name: "NoRC4-DTLS",
- config: Config{
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA},
- Bugs: ProtocolBugs{
- EnableAllCiphersInDTLS: true,
- },
- },
- shouldFail: true,
- expectedError: ":NO_SHARED_CIPHER:",
- },
-}
+var testCases []testCase
-func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, isResume bool) error {
+func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) error {
var connDebug *recordingConn
var connDamage *damageAdaptor
if *flagDebug {
@@ -1183,6 +285,7 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
tlsConn = Client(conn, config)
}
}
+ defer tlsConn.Close()
if err := tlsConn.Handshake(); err != nil {
return err
@@ -1235,6 +338,18 @@ 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.expectedOCSPResponse != nil && !bytes.Equal(test.expectedOCSPResponse, tlsConn.OCSPResponse()) {
+ return fmt.Errorf("OCSP Response mismatch")
+ }
+
+ if test.expectedSCTList != nil && !bytes.Equal(test.expectedSCTList, connState.SCTList) {
+ return fmt.Errorf("SCT list mismatch")
+ }
+
+ if expected := test.expectedClientCertSignatureHash; expected != 0 && expected != connState.ClientCertSignatureHash {
+ return fmt.Errorf("expected client to sign handshake with hash %d, but got %d", expected, connState.ClientCertSignatureHash)
+ }
+
if test.exportKeyingMaterial > 0 {
actual := make([]byte, test.exportKeyingMaterial)
if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -1271,6 +386,14 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
}
}
+ for i := 0; i < test.sendEmptyRecords; i++ {
+ tlsConn.Write(nil)
+ }
+
+ for i := 0; i < test.sendWarningAlerts; i++ {
+ tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage)
+ }
+
if test.renegotiate {
if test.renegotiateCiphers != nil {
config.CipherSuites = test.renegotiateCiphers
@@ -1288,6 +411,7 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
connDamage.setDamage(false)
}
+ messageLen := test.messageLen
if messageLen < 0 {
if test.protocol == dtls {
return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
@@ -1296,37 +420,57 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
_, err := io.Copy(ioutil.Discard, tlsConn)
return err
}
-
if messageLen == 0 {
messageLen = 32
}
- testMessage := make([]byte, messageLen)
- for i := range testMessage {
- testMessage[i] = 0x42
+
+ messageCount := test.messageCount
+ if messageCount == 0 {
+ messageCount = 1
}
- tlsConn.Write(testMessage)
- buf := make([]byte, len(testMessage))
- if test.protocol == dtls {
- bufTmp := make([]byte, len(buf)+1)
- n, err := tlsConn.Read(bufTmp)
- if err != nil {
- return err
+ for j := 0; j < messageCount; j++ {
+ testMessage := make([]byte, messageLen)
+ for i := range testMessage {
+ testMessage[i] = 0x42 ^ byte(j)
}
- if n != len(buf) {
- return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
+ tlsConn.Write(testMessage)
+
+ for i := 0; i < test.sendEmptyRecords; i++ {
+ tlsConn.Write(nil)
}
- copy(buf, bufTmp)
- } else {
- _, err := io.ReadFull(tlsConn, buf)
- if err != nil {
- return err
+
+ for i := 0; i < test.sendWarningAlerts; i++ {
+ tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage)
+ }
+
+ if test.shimShutsDown || test.expectMessageDropped {
+ // The shim will not respond.
+ continue
+ }
+
+ buf := make([]byte, len(testMessage))
+ if test.protocol == dtls {
+ bufTmp := make([]byte, len(buf)+1)
+ n, err := tlsConn.Read(bufTmp)
+ if err != nil {
+ return err
+ }
+ if n != len(buf) {
+ return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
+ }
+ copy(buf, bufTmp)
+ } else {
+ _, err := io.ReadFull(tlsConn, buf)
+ if err != nil {
+ return err
+ }
}
- }
- for i, v := range buf {
- if v != testMessage[i]^0xff {
- return fmt.Errorf("bad reply contents at byte %d", i)
+ for i, v := range buf {
+ if v != testMessage[i]^0xff {
+ return fmt.Errorf("bad reply contents at byte %d", i)
+ }
}
}
@@ -1382,7 +526,7 @@ func acceptOrWait(listener net.Listener, waitChan chan error) (net.Conn, error)
}
}
-func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
+func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) {
panic("Error expected without shouldFail in " + test.name)
}
@@ -1391,6 +535,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
panic("expectResumeRejected without resumeSession in " + test.name)
}
+ if test.testType != clientTest && test.expectedClientCertSignatureHash != 0 {
+ panic("expectedClientCertSignatureHash non-zero with serverTest in " + test.name)
+ }
+
listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
if err != nil {
panic(err)
@@ -1401,26 +549,30 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
}
}()
- shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
flags := []string{"-port", strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)}
if test.testType == serverTest {
flags = append(flags, "-server")
flags = append(flags, "-key-file")
if test.keyFile == "" {
- flags = append(flags, rsaKeyFile)
+ flags = append(flags, path.Join(*resourceDir, rsaKeyFile))
} else {
- flags = append(flags, test.keyFile)
+ flags = append(flags, path.Join(*resourceDir, test.keyFile))
}
flags = append(flags, "-cert-file")
if test.certFile == "" {
- flags = append(flags, rsaCertificateFile)
+ flags = append(flags, path.Join(*resourceDir, rsaCertificateFile))
} else {
- flags = append(flags, test.certFile)
+ flags = append(flags, path.Join(*resourceDir, test.certFile))
}
}
+ if test.digestPrefs != "" {
+ flags = append(flags, "-digest-prefs")
+ flags = append(flags, test.digestPrefs)
+ }
+
if test.protocol == dtls {
flags = append(flags, "-dtls")
}
@@ -1433,6 +585,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
flags = append(flags, "-shim-writes-first")
}
+ if test.shimShutsDown {
+ flags = append(flags, "-shim-shuts-down")
+ }
+
if test.exportKeyingMaterial > 0 {
flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial))
flags = append(flags, "-export-label", test.exportLabel)
@@ -1453,11 +609,11 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
var shim *exec.Cmd
if *useValgrind {
- shim = valgrindOf(false, shim_path, flags...)
+ shim = valgrindOf(false, shimPath, flags...)
} else if *useGDB {
- shim = gdbOf(shim_path, flags...)
+ shim = gdbOf(shimPath, flags...)
} else {
- shim = exec.Command(shim_path, flags...)
+ shim = exec.Command(shimPath, flags...)
}
shim.Stdin = os.Stdin
var stdoutBuf, stderrBuf bytes.Buffer
@@ -1467,7 +623,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
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")
+ shim.Env = append(shim.Env, "MALLOC_BREAK_ON_FAIL=1")
}
shim.Env = append(shim.Env, "_MALLOC_CHECK=1")
}
@@ -1479,8 +635,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
go func() { waitChan <- shim.Wait() }()
config := test.config
- config.ClientSessionCache = NewLRUClientSessionCache(1)
- config.ServerSessionCache = NewLRUServerSessionCache(1)
+ if !test.noSessionCache {
+ config.ClientSessionCache = NewLRUClientSessionCache(1)
+ config.ServerSessionCache = NewLRUServerSessionCache(1)
+ }
if test.testType == clientTest {
if len(config.Certificates) == 0 {
config.Certificates = []Certificate{getRSACertificate()}
@@ -1495,7 +653,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
conn, err := acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &config, conn, test.messageLen, false /* not a resumption */)
+ err = doExchange(test, &config, conn, false /* not a resumption */)
conn.Close()
}
@@ -1509,7 +667,12 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
if len(resumeConfig.Certificates) == 0 {
resumeConfig.Certificates = []Certificate{getRSACertificate()}
}
- if !test.newSessionsOnResume {
+ if test.newSessionsOnResume {
+ if !test.noSessionCache {
+ resumeConfig.ClientSessionCache = NewLRUClientSessionCache(1)
+ resumeConfig.ServerSessionCache = NewLRUServerSessionCache(1)
+ }
+ } else {
resumeConfig.SessionTicketKey = config.SessionTicketKey
resumeConfig.ClientSessionCache = config.ClientSessionCache
resumeConfig.ServerSessionCache = config.ServerSessionCache
@@ -1520,7 +683,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
var connResume net.Conn
connResume, err = acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &resumeConfig, connResume, test.messageLen, true /* resumption */)
+ err = doExchange(test, &resumeConfig, connResume, true /* resumption */)
connResume.Close()
}
}
@@ -1606,7 +769,6 @@ 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},
@@ -1630,6 +792,7 @@ var testCipherSuites = []struct {
{"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA},
{"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
+ {"NULL-SHA", TLS_RSA_WITH_NULL_SHA},
}
func hasComponent(suiteName, component string) bool {
@@ -1644,7 +807,7 @@ func isTLS12Only(suiteName string) bool {
}
func isDTLSCipher(suiteName string) bool {
- return !hasComponent(suiteName, "RC4")
+ return !hasComponent(suiteName, "RC4") && !hasComponent(suiteName, "NULL")
}
func bigFromHex(hex string) *big.Int {
@@ -1655,6 +818,1152 @@ func bigFromHex(hex string) *big.Int {
return ret
}
+func addBasicTests() {
+ basicTests := []testCase{
+ {
+ name: "BadRSASignature",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ InvalidSKXSignature: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":BAD_SIGNATURE:",
+ },
+ {
+ name: "BadECDSASignature",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ InvalidSKXSignature: true,
+ },
+ Certificates: []Certificate{getECDSACertificate()},
+ },
+ shouldFail: true,
+ expectedError: ":BAD_SIGNATURE:",
+ },
+ {
+ testType: serverTest,
+ name: "BadRSASignature-ClientAuth",
+ config: Config{
+ Bugs: ProtocolBugs{
+ InvalidCertVerifySignature: true,
+ },
+ Certificates: []Certificate{getRSACertificate()},
+ },
+ shouldFail: true,
+ expectedError: ":BAD_SIGNATURE:",
+ flags: []string{"-require-any-client-certificate"},
+ },
+ {
+ testType: serverTest,
+ name: "BadECDSASignature-ClientAuth",
+ config: Config{
+ Bugs: ProtocolBugs{
+ InvalidCertVerifySignature: true,
+ },
+ Certificates: []Certificate{getECDSACertificate()},
+ },
+ shouldFail: true,
+ expectedError: ":BAD_SIGNATURE:",
+ flags: []string{"-require-any-client-certificate"},
+ },
+ {
+ name: "BadECDSACurve",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ InvalidSKXCurve: true,
+ },
+ Certificates: []Certificate{getECDSACertificate()},
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ },
+ {
+ testType: serverTest,
+ name: "BadRSAVersion",
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+ Bugs: ProtocolBugs{
+ RsaClientKeyExchangeVersion: VersionTLS11,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+ },
+ {
+ name: "NoFallbackSCSV",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FailIfNotFallbackSCSV: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "no fallback SCSV found",
+ },
+ {
+ name: "SendFallbackSCSV",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FailIfNotFallbackSCSV: true,
+ },
+ },
+ flags: []string{"-fallback-scsv"},
+ },
+ {
+ name: "ClientCertificateTypes",
+ config: Config{
+ ClientAuth: RequestClientCert,
+ ClientCertificateTypes: []byte{
+ CertTypeDSSSign,
+ CertTypeRSASign,
+ CertTypeECDSASign,
+ },
+ },
+ flags: []string{
+ "-expect-certificate-types",
+ base64.StdEncoding.EncodeToString([]byte{
+ CertTypeDSSSign,
+ CertTypeRSASign,
+ CertTypeECDSASign,
+ }),
+ },
+ },
+ {
+ name: "NoClientCertificate",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ },
+ shouldFail: true,
+ expectedLocalError: "client didn't provide a certificate",
+ },
+ {
+ name: "UnauthenticatedECDH",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ UnauthenticatedECDH: true,
+ },
+ },
+ shouldFail: true,
+ 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},
+ Bugs: ProtocolBugs{
+ SkipServerKeyExchange: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ },
+ {
+ name: "SkipChangeCipherSpec-Client",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SkipChangeCipherSpec: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ testType: serverTest,
+ name: "SkipChangeCipherSpec-Server",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SkipChangeCipherSpec: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ testType: serverTest,
+ name: "SkipChangeCipherSpec-Server-NPN",
+ config: Config{
+ NextProtos: []string{"bar"},
+ Bugs: ProtocolBugs{
+ SkipChangeCipherSpec: true,
+ },
+ },
+ flags: []string{
+ "-advertise-npn", "\x03foo\x03bar\x03baz",
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ name: "FragmentAcrossChangeCipherSpec-Client",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FragmentAcrossChangeCipherSpec: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ testType: serverTest,
+ name: "FragmentAcrossChangeCipherSpec-Server",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FragmentAcrossChangeCipherSpec: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ testType: serverTest,
+ name: "FragmentAcrossChangeCipherSpec-Server-NPN",
+ config: Config{
+ NextProtos: []string{"bar"},
+ Bugs: ProtocolBugs{
+ FragmentAcrossChangeCipherSpec: true,
+ },
+ },
+ flags: []string{
+ "-advertise-npn", "\x03foo\x03bar\x03baz",
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+ },
+ {
+ 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: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":BAD_ALERT:",
+ },
+ {
+ protocol: dtls,
+ testType: serverTest,
+ name: "FragmentAlert-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FragmentAlert: true,
+ SendSpuriousAlert: alertRecordOverflow,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":BAD_ALERT:",
+ },
+ {
+ testType: serverTest,
+ name: "EarlyChangeCipherSpec-server-1",
+ config: Config{
+ Bugs: ProtocolBugs{
+ EarlyChangeCipherSpec: 1,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":CCS_RECEIVED_EARLY:",
+ },
+ {
+ testType: serverTest,
+ name: "EarlyChangeCipherSpec-server-2",
+ config: Config{
+ Bugs: ProtocolBugs{
+ EarlyChangeCipherSpec: 2,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":CCS_RECEIVED_EARLY:",
+ },
+ {
+ name: "SkipNewSessionTicket",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SkipNewSessionTicket: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":CCS_RECEIVED_EARLY:",
+ },
+ {
+ testType: serverTest,
+ name: "FallbackSCSV",
+ config: Config{
+ MaxVersion: VersionTLS11,
+ Bugs: ProtocolBugs{
+ SendFallbackSCSV: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":INAPPROPRIATE_FALLBACK:",
+ },
+ {
+ testType: serverTest,
+ name: "FallbackSCSV-VersionMatch",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendFallbackSCSV: true,
+ },
+ },
+ },
+ {
+ testType: serverTest,
+ name: "FragmentedClientVersion",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 1,
+ FragmentClientVersion: true,
+ },
+ },
+ expectedVersion: VersionTLS12,
+ },
+ {
+ testType: serverTest,
+ name: "MinorVersionTolerance",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendClientVersion: 0x03ff,
+ },
+ },
+ expectedVersion: VersionTLS12,
+ },
+ {
+ testType: serverTest,
+ name: "MajorVersionTolerance",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendClientVersion: 0x0400,
+ },
+ },
+ expectedVersion: VersionTLS12,
+ },
+ {
+ testType: serverTest,
+ name: "VersionTooLow",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendClientVersion: 0x0200,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNSUPPORTED_PROTOCOL:",
+ },
+ {
+ testType: serverTest,
+ name: "HttpGET",
+ sendPrefix: "GET / HTTP/1.0\n",
+ shouldFail: true,
+ expectedError: ":HTTP_REQUEST:",
+ },
+ {
+ testType: serverTest,
+ name: "HttpPOST",
+ sendPrefix: "POST / HTTP/1.0\n",
+ shouldFail: true,
+ expectedError: ":HTTP_REQUEST:",
+ },
+ {
+ testType: serverTest,
+ name: "HttpHEAD",
+ sendPrefix: "HEAD / HTTP/1.0\n",
+ shouldFail: true,
+ expectedError: ":HTTP_REQUEST:",
+ },
+ {
+ testType: serverTest,
+ name: "HttpPUT",
+ sendPrefix: "PUT / HTTP/1.0\n",
+ shouldFail: true,
+ expectedError: ":HTTP_REQUEST:",
+ },
+ {
+ testType: serverTest,
+ name: "HttpCONNECT",
+ sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
+ shouldFail: true,
+ expectedError: ":HTTPS_PROXY_REQUEST:",
+ },
+ {
+ testType: serverTest,
+ name: "Garbage",
+ sendPrefix: "blah",
+ shouldFail: true,
+ expectedError: ":WRONG_VERSION_NUMBER:",
+ },
+ {
+ name: "SkipCipherVersionCheck",
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+ MaxVersion: VersionTLS11,
+ Bugs: ProtocolBugs{
+ SkipCipherVersionCheck: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CIPHER_RETURNED:",
+ },
+ {
+ name: "RSAEphemeralKey",
+ config: Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ RSAEphemeralKey: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ },
+ {
+ name: "DisableEverything",
+ flags: []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
+ shouldFail: true,
+ expectedError: ":WRONG_SSL_VERSION:",
+ },
+ {
+ protocol: dtls,
+ name: "DisableEverything-DTLS",
+ flags: []string{"-no-tls12", "-no-tls1"},
+ shouldFail: true,
+ expectedError: ":WRONG_SSL_VERSION:",
+ },
+ {
+ name: "NoSharedCipher",
+ config: Config{
+ CipherSuites: []uint16{},
+ },
+ shouldFail: true,
+ expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:",
+ },
+ {
+ protocol: dtls,
+ testType: serverTest,
+ name: "MTU",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxPacketLength: 256,
+ },
+ },
+ flags: []string{"-mtu", "256"},
+ },
+ {
+ protocol: dtls,
+ testType: serverTest,
+ name: "MTUExceeded",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxPacketLength: 255,
+ },
+ },
+ flags: []string{"-mtu", "256"},
+ shouldFail: true,
+ expectedLocalError: "dtls: exceeded maximum packet length",
+ },
+ {
+ name: "CertMismatchRSA",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ Certificates: []Certificate{getECDSACertificate()},
+ Bugs: ProtocolBugs{
+ SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CERTIFICATE_TYPE:",
+ },
+ {
+ name: "CertMismatchECDSA",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Certificates: []Certificate{getRSACertificate()},
+ Bugs: ProtocolBugs{
+ SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CERTIFICATE_TYPE:",
+ },
+ {
+ name: "EmptyCertificateList",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ EmptyCertificateList: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ },
+ {
+ name: "TLSFatalBadPackets",
+ damageFirstWrite: true,
+ shouldFail: true,
+ expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+ },
+ {
+ protocol: dtls,
+ name: "DTLSIgnoreBadPackets",
+ damageFirstWrite: true,
+ },
+ {
+ protocol: dtls,
+ name: "DTLSIgnoreBadPackets-Async",
+ damageFirstWrite: true,
+ flags: []string{"-async"},
+ },
+ {
+ name: "AppDataBeforeHandshake",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataBeforeHandshake: []byte("TEST MESSAGE"),
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ name: "AppDataBeforeHandshake-Empty",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataBeforeHandshake: []byte{},
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ protocol: dtls,
+ name: "AppDataBeforeHandshake-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataBeforeHandshake: []byte("TEST MESSAGE"),
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ protocol: dtls,
+ name: "AppDataBeforeHandshake-DTLS-Empty",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataBeforeHandshake: []byte{},
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ },
+ {
+ name: "AppDataAfterChangeCipherSpec",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"),
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:",
+ },
+ {
+ name: "AppDataAfterChangeCipherSpec-Empty",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataAfterChangeCipherSpec: []byte{},
+ },
+ },
+ shouldFail: true,
+ expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:",
+ },
+ {
+ protocol: dtls,
+ name: "AppDataAfterChangeCipherSpec-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"),
+ },
+ },
+ // BoringSSL's DTLS implementation will drop the out-of-order
+ // application data.
+ },
+ {
+ protocol: dtls,
+ name: "AppDataAfterChangeCipherSpec-DTLS-Empty",
+ config: Config{
+ Bugs: ProtocolBugs{
+ AppDataAfterChangeCipherSpec: []byte{},
+ },
+ },
+ // 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: "SplitFragments-Header-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SplitFragments: 2,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ },
+ {
+ protocol: dtls,
+ name: "SplitFragments-Boundary-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SplitFragments: dtlsRecordHeaderLen,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":EXCESSIVE_MESSAGE_SIZE:",
+ },
+ {
+ protocol: dtls,
+ name: "SplitFragments-Body-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SplitFragments: dtlsRecordHeaderLen + 1,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":EXCESSIVE_MESSAGE_SIZE:",
+ },
+ {
+ 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: "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,
+ },
+ {
+ protocol: dtls,
+ name: "SendSplitAlert-Sync",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSplitAlert: true,
+ },
+ },
+ },
+ {
+ protocol: dtls,
+ name: "SendSplitAlert-Async",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSplitAlert: true,
+ },
+ },
+ flags: []string{"-async"},
+ },
+ {
+ protocol: dtls,
+ name: "PackDTLSHandshake",
+ config: Config{
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: 2,
+ PackHandshakeFragments: 20,
+ PackHandshakeRecords: 200,
+ },
+ },
+ },
+ {
+ testType: serverTest,
+ protocol: dtls,
+ name: "NoRC4-DTLS",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+ Bugs: ProtocolBugs{
+ EnableAllCiphersInDTLS: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":NO_SHARED_CIPHER:",
+ },
+ {
+ name: "SendEmptyRecords-Pass",
+ sendEmptyRecords: 32,
+ },
+ {
+ name: "SendEmptyRecords",
+ sendEmptyRecords: 33,
+ shouldFail: true,
+ expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+ },
+ {
+ name: "SendEmptyRecords-Async",
+ sendEmptyRecords: 33,
+ flags: []string{"-async"},
+ shouldFail: true,
+ expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+ },
+ {
+ name: "SendWarningAlerts-Pass",
+ sendWarningAlerts: 4,
+ },
+ {
+ protocol: dtls,
+ name: "SendWarningAlerts-DTLS-Pass",
+ sendWarningAlerts: 4,
+ },
+ {
+ name: "SendWarningAlerts",
+ sendWarningAlerts: 5,
+ shouldFail: true,
+ expectedError: ":TOO_MANY_WARNING_ALERTS:",
+ },
+ {
+ name: "SendWarningAlerts-Async",
+ sendWarningAlerts: 5,
+ flags: []string{"-async"},
+ shouldFail: true,
+ expectedError: ":TOO_MANY_WARNING_ALERTS:",
+ },
+ {
+ name: "EmptySessionID",
+ config: Config{
+ SessionTicketsDisabled: true,
+ },
+ noSessionCache: true,
+ flags: []string{"-expect-no-session"},
+ },
+ {
+ name: "Unclean-Shutdown",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NoCloseNotify: true,
+ ExpectCloseNotify: true,
+ },
+ },
+ shimShutsDown: true,
+ flags: []string{"-check-close-notify"},
+ shouldFail: true,
+ expectedError: "Unexpected SSL_shutdown result: -1 != 1",
+ },
+ {
+ name: "Unclean-Shutdown-Ignored",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NoCloseNotify: true,
+ },
+ },
+ shimShutsDown: true,
+ },
+ {
+ name: "LargePlaintext",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: maxPlaintext + 1,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ },
+ {
+ protocol: dtls,
+ name: "LargePlaintext-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: maxPlaintext + 1,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ },
+ {
+ name: "LargeCiphertext",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: maxPlaintext * 2,
+ shouldFail: true,
+ expectedError: ":ENCRYPTED_LENGTH_TOO_LONG:",
+ },
+ {
+ protocol: dtls,
+ name: "LargeCiphertext-DTLS",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: maxPlaintext * 2,
+ // Unlike the other four cases, DTLS drops records which
+ // are invalid before authentication, so the connection
+ // does not fail.
+ expectMessageDropped: true,
+ },
+ }
+ testCases = append(testCases, basicTests...)
+}
+
func addCipherSuiteTests() {
for _, suite := range testCipherSuites {
const psk = "12345"
@@ -1679,6 +1988,10 @@ func addCipherSuiteTests() {
"-psk", psk,
"-psk-identity", pskIdentity)
}
+ if hasComponent(suite.name, "NULL") {
+ // NULL ciphers must be explicitly enabled.
+ flags = append(flags, "-cipher", "DEFAULT:NULL-SHA")
+ }
for _, ver := range tlsVersions {
if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
@@ -1752,6 +2065,47 @@ func addCipherSuiteTests() {
})
}
}
+
+ // Ensure both TLS and DTLS accept their maximum record sizes.
+ testCases = append(testCases, testCase{
+ name: suite.name + "-LargeRecord",
+ config: Config{
+ CipherSuites: []uint16{suite.id},
+ Certificates: []Certificate{cert},
+ PreSharedKey: []byte(psk),
+ PreSharedKeyIdentity: pskIdentity,
+ },
+ flags: flags,
+ messageLen: maxPlaintext,
+ })
+ testCases = append(testCases, testCase{
+ name: suite.name + "-LargeRecord-Extra",
+ config: Config{
+ CipherSuites: []uint16{suite.id},
+ Certificates: []Certificate{cert},
+ PreSharedKey: []byte(psk),
+ PreSharedKeyIdentity: pskIdentity,
+ Bugs: ProtocolBugs{
+ SendLargeRecords: true,
+ },
+ },
+ flags: append(flags, "-microsoft-big-sslv3-buffer"),
+ messageLen: maxPlaintext + 16384,
+ })
+ if isDTLSCipher(suite.name) {
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ name: suite.name + "-LargeRecord-DTLS",
+ config: Config{
+ CipherSuites: []uint16{suite.id},
+ Certificates: []Certificate{cert},
+ PreSharedKey: []byte(psk),
+ PreSharedKeyIdentity: pskIdentity,
+ },
+ flags: flags,
+ messageLen: maxPlaintext,
+ })
+ }
}
testCases = append(testCases, testCase{
@@ -1768,6 +2122,94 @@ func addCipherSuiteTests() {
shouldFail: true,
expectedError: "BAD_DH_P_LENGTH",
})
+
+ // versionSpecificCiphersTest specifies a test for the TLS 1.0 and TLS
+ // 1.1 specific cipher suite settings. A server is setup with the given
+ // cipher lists and then a connection is made for each member of
+ // expectations. The cipher suite that the server selects must match
+ // the specified one.
+ var versionSpecificCiphersTest = []struct {
+ ciphersDefault, ciphersTLS10, ciphersTLS11 string
+ // expectations is a map from TLS version to cipher suite id.
+ expectations map[uint16]uint16
+ }{
+ {
+ // Test that the null case (where no version-specific ciphers are set)
+ // works as expected.
+ "RC4-SHA:AES128-SHA", // default ciphers
+ "", // no ciphers specifically for TLS ≥ 1.0
+ "", // no ciphers specifically for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS11: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS12: TLS_RSA_WITH_RC4_128_SHA,
+ },
+ },
+ {
+ // With ciphers_tls10 set, TLS 1.0, 1.1 and 1.2 should get a different
+ // cipher.
+ "RC4-SHA:AES128-SHA", // default
+ "AES128-SHA", // these ciphers for TLS ≥ 1.0
+ "", // no ciphers specifically for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ },
+ {
+ // With ciphers_tls11 set, TLS 1.1 and 1.2 should get a different
+ // cipher.
+ "RC4-SHA:AES128-SHA", // default
+ "", // no ciphers specifically for TLS ≥ 1.0
+ "AES128-SHA", // these ciphers for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ },
+ {
+ // With both ciphers_tls10 and ciphers_tls11 set, ciphers_tls11 should
+ // mask ciphers_tls10 for TLS 1.1 and 1.2.
+ "RC4-SHA:AES128-SHA", // default
+ "AES128-SHA", // these ciphers for TLS ≥ 1.0
+ "AES256-SHA", // these ciphers for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_256_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ },
+ }
+
+ for i, test := range versionSpecificCiphersTest {
+ for version, expectedCipherSuite := range test.expectations {
+ flags := []string{"-cipher", test.ciphersDefault}
+ if len(test.ciphersTLS10) > 0 {
+ flags = append(flags, "-cipher-tls10", test.ciphersTLS10)
+ }
+ if len(test.ciphersTLS11) > 0 {
+ flags = append(flags, "-cipher-tls11", test.ciphersTLS11)
+ }
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: fmt.Sprintf("VersionSpecificCiphersTest-%d-%x", i, version),
+ config: Config{
+ MaxVersion: version,
+ MinVersion: version,
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA},
+ },
+ flags: flags,
+ expectedCipher: expectedCipherSuite,
+ })
+ }
+ }
}
func addBadECDSASignatureTests() {
@@ -1837,7 +2279,8 @@ func addCBCSplittingTests() {
MinVersion: VersionTLS10,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
},
- messageLen: -1, // read until EOF
+ messageLen: -1, // read until EOF
+ resumeSession: true,
flags: []string{
"-async",
"-write-different-record-sizes",
@@ -1882,8 +2325,8 @@ func addClientAuthTests() {
ClientCAs: certPool,
},
flags: []string{
- "-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
},
})
testCases = append(testCases, testCase{
@@ -1917,8 +2360,8 @@ func addClientAuthTests() {
ClientCAs: certPool,
},
flags: []string{
- "-cert-file", ecdsaCertificateFile,
- "-key-file", ecdsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, ecdsaKeyFile),
},
})
}
@@ -2075,6 +2518,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
RenewTicketOnResume: true,
},
},
+ flags: []string{"-expect-ticket-renewal"},
resumeSession: true,
})
tests = append(tests, testCase{
@@ -2123,10 +2567,42 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
ClientAuth: RequireAnyClientCert,
},
flags: []string{
- "-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
},
})
+ if async {
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "ClientAuth-Client-AsyncKey",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-use-async-private-key",
+ },
+ })
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-RSAAsyncKey",
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-use-async-private-key",
+ },
+ })
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-ECDSAAsyncKey",
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, ecdsaKeyFile),
+ "-use-async-private-key",
+ },
+ })
+ }
tests = append(tests, testCase{
testType: serverTest,
name: "ClientAuth-Server",
@@ -2171,6 +2647,57 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
flags: []string{"-psk", "secret"},
})
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "OCSPStapling-Client",
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-expect-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-verify-peer",
+ },
+ resumeSession: true,
+ })
+
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "OCSPStapling-Server",
+ expectedOCSPResponse: testOCSPResponse,
+ flags: []string{
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ },
+ resumeSession: true,
+ })
+
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "CertificateVerificationSucceed",
+ flags: []string{
+ "-verify-peer",
+ },
+ })
+
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "CertificateVerificationFail",
+ flags: []string{
+ "-verify-fail",
+ "-verify-peer",
+ },
+ shouldFail: true,
+ expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+ })
+
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "CertificateVerificationSoftFail",
+ flags: []string{
+ "-verify-fail",
+ "-expect-verify-result",
+ },
+ })
+
if protocol == tls {
tests = append(tests, testCase{
name: "Renegotiate-Client",
@@ -2292,7 +2819,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
config: Config{
RequestChannelID: true,
},
- flags: []string{"-send-channel-id", channelIDKeyFile},
+ flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
resumeSession: true,
expectChannelID: true,
})
@@ -2311,6 +2838,33 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
resumeSession: true,
expectChannelID: true,
})
+
+ // Bidirectional shutdown with the runner initiating.
+ tests = append(tests, testCase{
+ name: "Shutdown-Runner",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ExpectCloseNotify: true,
+ },
+ },
+ flags: []string{"-check-close-notify"},
+ })
+
+ // Bidirectional shutdown with the shim initiating. The runner,
+ // in the meantime, sends garbage before the close_notify which
+ // the shim must ignore.
+ tests = append(tests, testCase{
+ name: "Shutdown-Shim",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ExpectCloseNotify: true,
+ },
+ },
+ shimShutsDown: true,
+ sendEmptyRecords: 1,
+ sendWarningAlerts: 1,
+ flags: []string{"-check-close-notify"},
+ })
} else {
tests = append(tests, testCase{
name: "SkipHelloVerifyRequest",
@@ -2721,6 +3275,70 @@ func addExtensionTests() {
expectedNextProtoType: alpn,
resumeSession: true,
})
+ var emptyString string
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ALPNClient-EmptyProtocolName",
+ config: Config{
+ NextProtos: []string{""},
+ Bugs: ProtocolBugs{
+ // A server returning an empty ALPN protocol
+ // should be rejected.
+ ALPNProtocol: &emptyString,
+ },
+ },
+ flags: []string{
+ "-advertise-alpn", "\x03foo",
+ },
+ shouldFail: true,
+ expectedError: ":PARSE_TLSEXT:",
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ALPNServer-EmptyProtocolName",
+ config: Config{
+ // A ClientHello containing an empty ALPN protocol
+ // should be rejected.
+ NextProtos: []string{"foo", "", "baz"},
+ },
+ flags: []string{
+ "-select-alpn", "foo",
+ },
+ shouldFail: true,
+ expectedError: ":PARSE_TLSEXT:",
+ })
+ // Test that negotiating both NPN and ALPN is forbidden.
+ testCases = append(testCases, testCase{
+ name: "NegotiateALPNAndNPN",
+ config: Config{
+ NextProtos: []string{"foo", "bar", "baz"},
+ Bugs: ProtocolBugs{
+ NegotiateALPNAndNPN: true,
+ },
+ },
+ flags: []string{
+ "-advertise-alpn", "\x03foo",
+ "-select-next-proto", "foo",
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
+ })
+ testCases = append(testCases, testCase{
+ name: "NegotiateALPNAndNPN-Swapped",
+ config: Config{
+ NextProtos: []string{"foo", "bar", "baz"},
+ Bugs: ProtocolBugs{
+ NegotiateALPNAndNPN: true,
+ SwapNPNAndALPN: true,
+ },
+ },
+ flags: []string{
+ "-advertise-alpn", "\x03foo",
+ "-select-next-proto", "foo",
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
+ })
// Resume with a corrupt ticket.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -2733,6 +3351,24 @@ func addExtensionTests() {
resumeSession: true,
expectResumeRejected: true,
})
+ // Test the ticket callback, with and without renewal.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TicketCallback",
+ resumeSession: true,
+ flags: []string{"-use-ticket-callback"},
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TicketCallback-Renew",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ },
+ },
+ flags: []string{"-use-ticket-callback", "-renew-ticket"},
+ resumeSession: true,
+ })
// Resume with an oversized session id.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -2822,22 +3458,39 @@ func addExtensionTests() {
shouldFail: true,
expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:",
})
- // Test OCSP stapling and SCT list.
+ // Test SCT list.
testCases = append(testCases, testCase{
- name: "OCSPStapling",
+ name: "SignedCertificateTimestampList-Client",
+ testType: clientTest,
flags: []string{
- "-enable-ocsp-stapling",
- "-expect-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-enable-signed-cert-timestamps",
+ "-expect-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
},
+ resumeSession: true,
})
testCases = append(testCases, testCase{
- name: "SignedCertificateTimestampList",
+ name: "SignedCertificateTimestampList-Server",
+ testType: serverTest,
flags: []string{
- "-enable-signed-cert-timestamps",
- "-expect-signed-cert-timestamps",
+ "-signed-cert-timestamps",
base64.StdEncoding.EncodeToString(testSCTList),
},
+ expectedSCTList: testSCTList,
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ClientHelloPadding",
+ config: Config{
+ Bugs: ProtocolBugs{
+ RequireClientHelloSize: 512,
+ },
+ },
+ // This hostname just needs to be long enough to push the
+ // ClientHello into F5's danger zone between 256 and 511 bytes
+ // long.
+ flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"},
})
}
@@ -2956,7 +3609,33 @@ func addRenegotiationTests() {
expectedError: ":NO_RENEGOTIATION:",
expectedLocalError: "remote error: no renegotiation",
})
- // TODO(agl): test the renegotiation info SCSV.
+ // The server shouldn't echo the renegotiation extension unless
+ // requested by the client.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Renegotiate-Server-NoExt",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NoRenegotiationInfo: true,
+ RequireRenegotiationInfo: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "renegotiation extension missing",
+ })
+ // The renegotiation SCSV should be sufficient for the server to echo
+ // the extension.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Renegotiate-Server-NoExt-SCSV",
+ config: Config{
+ Bugs: ProtocolBugs{
+ NoRenegotiationInfo: true,
+ SendRenegotiationSCSV: true,
+ RequireRenegotiationInfo: true,
+ },
+ },
+ })
testCases = append(testCases, testCase{
name: "Renegotiate-Client",
config: Config{
@@ -2989,8 +3668,7 @@ func addRenegotiationTests() {
expectedError: ":RENEGOTIATION_MISMATCH:",
})
testCases = append(testCases, testCase{
- name: "Renegotiate-Client-NoExt",
- renegotiate: true,
+ name: "Renegotiate-Client-NoExt",
config: Config{
Bugs: ProtocolBugs{
NoRenegotiationInfo: true,
@@ -3043,6 +3721,19 @@ func addRenegotiationTests() {
},
},
})
+ testCases = append(testCases, testCase{
+ name: "Renegotiate-FalseStart",
+ renegotiate: true,
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ },
+ flags: []string{
+ "-false-start",
+ "-select-next-proto", "foo",
+ },
+ shimWritesFirst: true,
+ })
}
func addDTLSReplayTests() {
@@ -3050,43 +3741,39 @@ func addDTLSReplayTests() {
testCases = append(testCases, testCase{
protocol: dtls,
name: "DTLS-Replay",
+ messageCount: 200,
replayWrites: true,
})
- // Test the outgoing sequence number skipping by values larger
+ // Test the incoming sequence number skipping by values larger
// than the retransmit window.
testCases = append(testCases, testCase{
protocol: dtls,
name: "DTLS-Replay-LargeGaps",
config: Config{
Bugs: ProtocolBugs{
- SequenceNumberIncrement: 127,
+ SequenceNumberMapping: func(in uint64) uint64 {
+ return in * 127
+ },
},
},
+ messageCount: 200,
replayWrites: true,
})
-}
-func addFastRadioPaddingTests() {
- testCases = append(testCases, testCase{
- protocol: tls,
- name: "FastRadio-Padding",
- config: Config{
- Bugs: ProtocolBugs{
- RequireFastradioPadding: true,
- },
- },
- flags: []string{"-fastradio-padding"},
- })
+ // Test the incoming sequence number changing non-monotonically.
testCases = append(testCases, testCase{
protocol: dtls,
- name: "FastRadio-Padding-DTLS",
+ name: "DTLS-Replay-NonMonotonic",
config: Config{
Bugs: ProtocolBugs{
- RequireFastradioPadding: true,
+ SequenceNumberMapping: func(in uint64) uint64 {
+ return in ^ 31
+ },
},
},
- flags: []string{"-fastradio-padding"},
+ messageCount: 200,
+ replayWrites: true,
})
}
@@ -3116,8 +3803,8 @@ func addSigningHashTests() {
},
},
flags: []string{
- "-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
},
})
@@ -3147,8 +3834,8 @@ func addSigningHashTests() {
},
},
flags: []string{
- "-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
},
})
@@ -3178,8 +3865,8 @@ func addSigningHashTests() {
},
},
flags: []string{
- "-cert-file", rsaCertificateFile,
- "-key-file", rsaKeyFile,
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
},
})
@@ -3235,6 +3922,73 @@ func addSigningHashTests() {
shouldFail: true,
expectedError: ":WRONG_SIGNATURE_TYPE:",
})
+
+ // Test that the agreed upon digest respects the client preferences and
+ // the server digests.
+ testCases = append(testCases, testCase{
+ name: "Agree-Digest-Fallback",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA512},
+ {signatureRSA, hashSHA1},
+ },
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ digestPrefs: "SHA256",
+ expectedClientCertSignatureHash: hashSHA1,
+ })
+ testCases = append(testCases, testCase{
+ name: "Agree-Digest-SHA256",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA1},
+ {signatureRSA, hashSHA256},
+ },
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ digestPrefs: "SHA256,SHA1",
+ expectedClientCertSignatureHash: hashSHA256,
+ })
+ testCases = append(testCases, testCase{
+ name: "Agree-Digest-SHA1",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA1},
+ },
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ digestPrefs: "SHA512,SHA256,SHA1",
+ expectedClientCertSignatureHash: hashSHA1,
+ })
+ testCases = append(testCases, testCase{
+ name: "Agree-Digest-Default",
+ config: Config{
+ ClientAuth: RequireAnyClientCert,
+ SignatureAndHashes: []signatureAndHash{
+ {signatureRSA, hashSHA256},
+ {signatureECDSA, hashSHA256},
+ {signatureRSA, hashSHA1},
+ {signatureECDSA, hashSHA1},
+ },
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ expectedClientCertSignatureHash: hashSHA256,
+ })
}
// timeouts is the retransmit schedule for BoringSSL. It doubles and
@@ -3441,7 +4195,111 @@ func addTLSUniqueTests() {
}
}
-func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
+func addCustomExtensionTests() {
+ expectedContents := "custom extension"
+ emptyString := ""
+
+ for _, isClient := range []bool{false, true} {
+ suffix := "Server"
+ flag := "-enable-server-custom-extension"
+ testType := serverTest
+ if isClient {
+ suffix = "Client"
+ flag = "-enable-client-custom-extension"
+ testType = clientTest
+ }
+
+ testCases = append(testCases, testCase{
+ testType: testType,
+ name: "CustomExtensions-" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ CustomExtension: expectedContents,
+ ExpectedCustomExtension: &expectedContents,
+ },
+ },
+ flags: []string{flag},
+ })
+
+ // If the parse callback fails, the handshake should also fail.
+ testCases = append(testCases, testCase{
+ testType: testType,
+ name: "CustomExtensions-ParseError-" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ CustomExtension: expectedContents + "foo",
+ ExpectedCustomExtension: &expectedContents,
+ },
+ },
+ flags: []string{flag},
+ shouldFail: true,
+ expectedError: ":CUSTOM_EXTENSION_ERROR:",
+ })
+
+ // If the add callback fails, the handshake should also fail.
+ testCases = append(testCases, testCase{
+ testType: testType,
+ name: "CustomExtensions-FailAdd-" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ CustomExtension: expectedContents,
+ ExpectedCustomExtension: &expectedContents,
+ },
+ },
+ flags: []string{flag, "-custom-extension-fail-add"},
+ shouldFail: true,
+ expectedError: ":CUSTOM_EXTENSION_ERROR:",
+ })
+
+ // If the add callback returns zero, no extension should be
+ // added.
+ skipCustomExtension := expectedContents
+ if isClient {
+ // For the case where the client skips sending the
+ // custom extension, the server must not “echo” it.
+ skipCustomExtension = ""
+ }
+ testCases = append(testCases, testCase{
+ testType: testType,
+ name: "CustomExtensions-Skip-" + suffix,
+ config: Config{
+ Bugs: ProtocolBugs{
+ CustomExtension: skipCustomExtension,
+ ExpectedCustomExtension: &emptyString,
+ },
+ },
+ flags: []string{flag, "-custom-extension-skip"},
+ })
+ }
+
+ // The custom extension add callback should not be called if the client
+ // doesn't send the extension.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CustomExtensions-NotCalled-Server",
+ config: Config{
+ Bugs: ProtocolBugs{
+ ExpectedCustomExtension: &emptyString,
+ },
+ },
+ flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
+ })
+
+ // Test an unknown extension from the server.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "UnknownExtension-Client",
+ config: Config{
+ Bugs: ProtocolBugs{
+ CustomExtension: expectedContents,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+}
+
+func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
for test := range c {
@@ -3449,11 +4307,11 @@ func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sy
if *mallocTest < 0 {
statusChan <- statusMsg{test: test, started: true}
- err = runTest(test, buildDir, -1)
+ err = runTest(test, shimPath, -1)
} else {
for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ {
statusChan <- statusMsg{test: test, started: true}
- if err = runTest(test, buildDir, mallocNumToFail); err != errMoreMallocs {
+ if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs {
if err != nil {
fmt.Printf("\n\nmalloc test failed at %d: %s\n", mallocNumToFail, err)
}
@@ -3515,12 +4373,10 @@ func statusPrinter(doneChan chan *testOutput, statusChan chan statusMsg, total i
}
func main() {
- var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
- var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
- var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
-
flag.Parse()
+ *resourceDir = path.Clean(*resourceDir)
+ addBasicTests()
addCipherSuiteTests()
addBadECDSASignatureTests()
addCBCPaddingTests()
@@ -3536,10 +4392,10 @@ func main() {
addRenegotiationTests()
addDTLSReplayTests()
addSigningHashTests()
- addFastRadioPaddingTests()
addDTLSRetransmitTests()
addExportKeyingMaterialTests()
addTLSUniqueTests()
+ addCustomExtensionTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {
@@ -3550,21 +4406,19 @@ func main() {
var wg sync.WaitGroup
- numWorkers := *flagNumWorkers
-
- statusChan := make(chan statusMsg, numWorkers)
- testChan := make(chan *testCase, numWorkers)
+ statusChan := make(chan statusMsg, *numWorkers)
+ testChan := make(chan *testCase, *numWorkers)
doneChan := make(chan *testOutput)
go statusPrinter(doneChan, statusChan, len(testCases))
- for i := 0; i < numWorkers; i++ {
+ for i := 0; i < *numWorkers; i++ {
wg.Add(1)
- go worker(statusChan, testChan, *flagBuildDir, &wg)
+ go worker(statusChan, testChan, *shimPath, &wg)
}
for i := range testCases {
- if len(*flagTest) == 0 || *flagTest == testCases[i].name {
+ if len(*testToRun) == 0 || *testToRun == testCases[i].name {
testChan <- &testCases[i]
}
}
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 363b6f3..1c42b2e 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -65,12 +65,9 @@ const Flag<bool> kBoolFlags[] = {
{ "-expect-session-miss", &TestConfig::expect_session_miss },
{ "-expect-extended-master-secret",
&TestConfig::expect_extended_master_secret },
- { "-allow-unsafe-legacy-renegotiation",
- &TestConfig::allow_unsafe_legacy_renegotiation },
{ "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
{ "-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 },
@@ -82,9 +79,27 @@ const Flag<bool> kBoolFlags[] = {
{ "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations },
{ "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect },
{ "-tls-unique", &TestConfig::tls_unique },
+ { "-use-async-private-key", &TestConfig::use_async_private_key },
+ { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal },
+ { "-expect-no-session", &TestConfig::expect_no_session },
+ { "-use-ticket-callback", &TestConfig::use_ticket_callback },
+ { "-renew-ticket", &TestConfig::renew_ticket },
+ { "-enable-client-custom-extension",
+ &TestConfig::enable_client_custom_extension },
+ { "-enable-server-custom-extension",
+ &TestConfig::enable_server_custom_extension },
+ { "-custom-extension-skip", &TestConfig::custom_extension_skip },
+ { "-custom-extension-fail-add", &TestConfig::custom_extension_fail_add },
+ { "-check-close-notify", &TestConfig::check_close_notify },
+ { "-shim-shuts-down", &TestConfig::shim_shuts_down },
+ { "-microsoft-big-sslv3-buffer", &TestConfig::microsoft_big_sslv3_buffer },
+ { "-verify-fail", &TestConfig::verify_fail },
+ { "-verify-peer", &TestConfig::verify_peer },
+ { "-expect-verify-result", &TestConfig::expect_verify_result }
};
const Flag<std::string> kStringFlags[] = {
+ { "-digest-prefs", &TestConfig::digest_prefs },
{ "-key-file", &TestConfig::key_file },
{ "-cert-file", &TestConfig::cert_file },
{ "-expect-server-name", &TestConfig::expected_server_name },
@@ -101,6 +116,8 @@ const Flag<std::string> kStringFlags[] = {
{ "-psk-identity", &TestConfig::psk_identity },
{ "-srtp-profiles", &TestConfig::srtp_profiles },
{ "-cipher", &TestConfig::cipher },
+ { "-cipher-tls10", &TestConfig::cipher_tls10 },
+ { "-cipher-tls11", &TestConfig::cipher_tls11 },
{ "-export-label", &TestConfig::export_label },
{ "-export-context", &TestConfig::export_context },
};
@@ -111,6 +128,8 @@ const Flag<std::string> kBase64Flags[] = {
{ "-expect-ocsp-response", &TestConfig::expected_ocsp_response },
{ "-expect-signed-cert-timestamps",
&TestConfig::expected_signed_cert_timestamps },
+ { "-ocsp-response", &TestConfig::ocsp_response },
+ { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps },
};
const Flag<int> kIntFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 5d753c8..9dea8e9 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -24,6 +24,7 @@ struct TestConfig {
bool is_dtls = false;
bool resume = false;
bool fallback_scsv = false;
+ std::string digest_prefs;
std::string key_file;
std::string cert_file;
std::string expected_server_name;
@@ -54,13 +55,11 @@ struct TestConfig {
bool expect_extended_master_secret = false;
std::string psk;
std::string psk_identity;
- bool allow_unsafe_legacy_renegotiation = false;
std::string srtp_profiles;
bool enable_ocsp_stapling = false;
std::string expected_ocsp_response;
bool enable_signed_cert_timestamps = false;
std::string expected_signed_cert_timestamps;
- bool fastradio_padding = false;
int min_version = 0;
int max_version = 0;
int mtu = 0;
@@ -71,6 +70,8 @@ struct TestConfig {
bool fail_ddos_callback = false;
bool fail_second_ddos_callback = false;
std::string cipher;
+ std::string cipher_tls10;
+ std::string cipher_tls11;
bool handshake_never_done = false;
int export_keying_material = 0;
std::string export_label;
@@ -79,6 +80,23 @@ struct TestConfig {
bool reject_peer_renegotiations = false;
bool no_legacy_server_connect = false;
bool tls_unique = false;
+ bool use_async_private_key = false;
+ bool expect_ticket_renewal = false;
+ bool expect_no_session = false;
+ bool use_ticket_callback = false;
+ bool renew_ticket = false;
+ bool enable_client_custom_extension = false;
+ bool enable_server_custom_extension = false;
+ bool custom_extension_skip = false;
+ bool custom_extension_fail_add = false;
+ std::string ocsp_response;
+ bool check_close_notify = false;
+ bool shim_shuts_down = false;
+ bool microsoft_big_sslv3_buffer = false;
+ bool verify_fail = false;
+ bool verify_peer = false;
+ bool expect_verify_result = false;
+ std::string signed_cert_timestamps;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);