diff options
Diffstat (limited to 'src/ssl/test/bssl_shim.cc')
-rw-r--r-- | src/ssl/test/bssl_shim.cc | 731 |
1 files changed, 182 insertions, 549 deletions
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc index edae67b..3b95d7e 100644 --- a/src/ssl/test/bssl_shim.cc +++ b/src/ssl/test/bssl_shim.cc @@ -38,14 +38,10 @@ #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" @@ -94,17 +90,10 @@ 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); } @@ -140,137 +129,18 @@ 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); - 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->key_file.empty() && + !SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), + SSL_FILETYPE_PEM)) { + return false; } if (!config->cert_file.empty() && !SSL_use_certificate_file(ssl, config->cert_file.c_str(), SSL_FILETYPE_PEM)) { return false; } - 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; } @@ -326,29 +196,10 @@ static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) { return 1; } -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; - } - } - +static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) { 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); @@ -490,94 +341,6 @@ 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) { @@ -643,23 +406,7 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { return nullptr; } - 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())) { + if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) { return nullptr; } @@ -691,46 +438,12 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); } - SSL_CTX_enable_tls_channel_id(ssl_ctx.get()); + ssl_ctx->tlsext_channel_id_enabled_new = 1; 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; } @@ -787,9 +500,6 @@ 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; } @@ -821,177 +531,6 @@ 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 @@ -1023,10 +562,7 @@ 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, - NULL); - } - if (config->verify_peer) { - SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL); + SkipVerify); } if (config->false_start) { SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); @@ -1052,8 +588,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->microsoft_big_sslv3_buffer) { - SSL_set_options(ssl.get(), SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); + if (config->allow_unsafe_legacy_renegotiation) { + SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); } if (config->no_legacy_server_connect) { SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT); @@ -1101,6 +637,7 @@ 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); } @@ -1114,13 +651,14 @@ 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) { @@ -1181,14 +719,139 @@ 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 || - !CheckHandshakeProperties(ssl.get(), is_resume)) { + 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"); 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->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; + } + } } if (config->export_keying_material > 0) { @@ -1234,19 +897,18 @@ 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. - static const size_t kBufLen = 32769; - std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); - memset(buf.get(), 0x42, kBufLen); + uint8_t buf[32769]; + memset(buf, 0x42, sizeof(buf)); 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 > kBufLen) { + if (len > sizeof(buf)) { fprintf(stderr, "Bad kRecordSizes value.\n"); return false; } - if (WriteAll(ssl.get(), buf.get(), len) < 0) { + if (WriteAll(ssl.get(), buf, len) < 0) { return false; } } @@ -1257,82 +919,53 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, return false; } } - 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; - } + 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"); return false; } - // Successfully read data. - if (n <= 0) { + // Accept shutdowns with or without close_notify. + // TODO(davidben): Write tests which distinguish these two cases. + break; + } else if (err != SSL_ERROR_NONE) { + if (n > 0) { fprintf(stderr, "Invalid SSL_get_error output\n"); return 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.get(), n) < 0) { - return false; - } + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + if (WriteAll(ssl.get(), buf, 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())); } - 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; - } - + SSL_shutdown(ssl.get()); return true; } |