shim: move |TestState| and |TestConfig| to their own files. This makes |TestState| and |TestConfig| accessible outside bssl_shim.cc, as well as the functions SetupCtx() and NewSSL(), which become methods on |TestConfig|. A whole mess of callbacks move in order to support this change. Along the way, some bits of global state are moved (e.g. the global test clock) and made self-initializing. This helps with creating a separate binary to perform split handshakes. Change-Id: I39b00a1819074882353f5f04ed01312916f3cccb Reviewed-on: https://boringssl-review.googlesource.com/29345 Commit-Queue: Matt Braithwaite <mab@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/CMakeLists.txt b/ssl/test/CMakeLists.txt index 5a73319..2fdee73 100644 --- a/ssl/test/CMakeLists.txt +++ b/ssl/test/CMakeLists.txt
@@ -8,6 +8,7 @@ packeted_bio.cc settings_writer.cc test_config.cc + test_state.cc $<TARGET_OBJECTS:test_support> )
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 27a0783..f73fc69 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc
@@ -67,10 +67,9 @@ #include "packeted_bio.h" #include "settings_writer.h" #include "test_config.h" +#include "test_state.h" -static CRYPTO_BUFFER_POOL *g_pool = nullptr; - #if !defined(OPENSSL_WINDOWS) static int closesocket(int sock) { return close(sock); @@ -90,79 +89,6 @@ return 1; } -struct TestState { - // async_bio is async BIO which pauses reads and writes. - BIO *async_bio = nullptr; - // packeted_bio is the packeted BIO which simulates read timeouts. - BIO *packeted_bio = nullptr; - bssl::UniquePtr<EVP_PKEY> channel_id; - bool cert_ready = false; - bssl::UniquePtr<SSL_SESSION> session; - bssl::UniquePtr<SSL_SESSION> pending_session; - bool early_callback_called = false; - bool handshake_done = false; - // private_key is the underlying private key used when testing custom keys. - bssl::UniquePtr<EVP_PKEY> private_key; - std::vector<uint8_t> private_key_result; - // private_key_retries is the number of times an asynchronous private key - // operation has been retried. - unsigned private_key_retries = 0; - bool got_new_session = false; - bssl::UniquePtr<SSL_SESSION> new_session; - bool ticket_decrypt_done = false; - bool alpn_select_done = false; - bool is_resume = false; - bool early_callback_ready = false; - bool custom_verify_ready = false; - std::string msg_callback_text; - bool msg_callback_ok = true; - // cert_verified is true if certificate verification has been driven to - // completion. This tests that the callback is not called again after this. - bool cert_verified = false; -}; - -static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int index, long argl, void *argp) { - delete ((TestState *)ptr); -} - -static int g_config_index = 0; -static int g_state_index = 0; - -static bool SetTestConfig(SSL *ssl, const TestConfig *config) { - return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1; -} - -static const TestConfig *GetTestConfig(const SSL *ssl) { - return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index); -} - -static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) { - // |SSL_set_ex_data| takes ownership of |state| only on success. - if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) { - state.release(); - return true; - } - return false; -} - -static TestState *GetTestState(const SSL *ssl) { - return (TestState *)SSL_get_ex_data(ssl, g_state_index); -} - -static bool MoveExData(SSL *dest, SSL *src) { - TestState *state = GetTestState(src); - const TestConfig *config = GetTestConfig(src); - if (!SSL_set_ex_data(src, g_state_index, nullptr) || - !SSL_set_ex_data(dest, g_state_index, state) || - !SSL_set_ex_data(src, g_config_index, nullptr) || - !SSL_set_ex_data(dest, g_config_index, (void *) config)) { - return false; - } - - return true; -} - // MoveBIOs moves the |BIO|s of |src| to |dst|. It is used for handoff. static void MoveBIOs(SSL *dest, SSL *src) { BIO *rbio = SSL_get_rbio(src); @@ -177,262 +103,6 @@ SSL_set0_wbio(src, nullptr); } -static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509, - bssl::UniquePtr<STACK_OF(X509)> *out_chain, - const std::string &file) { - bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); - if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { - return false; - } - - out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); - if (!*out_x509) { - return false; - } - - out_chain->reset(sk_X509_new_null()); - if (!*out_chain) { - return false; - } - - // Keep reading the certificate chain. - for (;;) { - bssl::UniquePtr<X509> cert( - PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); - if (!cert) { - break; - } - - if (!bssl::PushToStack(out_chain->get(), std::move(cert))) { - return false; - } - } - - uint32_t err = ERR_peek_last_error(); - if (ERR_GET_LIB(err) != ERR_LIB_PEM || - ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { - return false; -} - - ERR_clear_error(); - return true; -} - -static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) { - bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); - if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { - return nullptr; - } - return bssl::UniquePtr<EVP_PKEY>( - PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL)); -} - -static bool FromHexDigit(uint8_t *out, char c) { - if ('0' <= c && c <= '9') { - *out = c - '0'; - return true; - } - if ('a' <= c && c <= 'f') { - *out = c - 'a' + 10; - return true; - } - if ('A' <= c && c <= 'F') { - *out = c - 'A' + 10; - return true; - } - return false; -} - -static bool HexDecode(std::string *out, const std::string &in) { - if ((in.size() & 1) != 0) { - return false; - } - - std::unique_ptr<uint8_t[]> buf(new uint8_t[in.size() / 2]); - for (size_t i = 0; i < in.size() / 2; i++) { - uint8_t high, low; - if (!FromHexDigit(&high, in[i*2]) || - !FromHexDigit(&low, in[i*2+1])) { - return false; - } - buf[i] = (high << 4) | low; - } - - out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2); - return true; -} - -static std::vector<std::string> SplitParts(const std::string &in, - const char delim) { - std::vector<std::string> ret; - size_t start = 0; - - for (size_t i = 0; i < in.size(); i++) { - if (in[i] == delim) { - ret.push_back(in.substr(start, i - start)); - start = i + 1; - } - } - - ret.push_back(in.substr(start, std::string::npos)); - return ret; -} - -static std::vector<std::string> DecodeHexStrings( - const std::string &hex_strings) { - std::vector<std::string> ret; - const std::vector<std::string> parts = SplitParts(hex_strings, ','); - - for (const auto &part : parts) { - std::string binary; - if (!HexDecode(&binary, part)) { - fprintf(stderr, "Bad hex string: %s\n", part.c_str()); - return ret; - } - - ret.push_back(binary); - } - - return ret; -} - -static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names( - const std::string &hex_names) { - const std::vector<std::string> der_names = DecodeHexStrings(hex_names); - bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null()); - if (!ret) { - return nullptr; - } - - for (const auto &der_name : der_names) { - const uint8_t *const data = - reinterpret_cast<const uint8_t *>(der_name.data()); - const uint8_t *derp = data; - bssl::UniquePtr<X509_NAME> name( - d2i_X509_NAME(nullptr, &derp, der_name.size())); - if (!name || derp != data + der_name.size()) { - fprintf(stderr, "Failed to parse X509_NAME.\n"); - return nullptr; - } - - if (!bssl::PushToStack(ret.get(), std::move(name))) { - return nullptr; - } - } - - return ret; -} - -static ssl_private_key_result_t AsyncPrivateKeySign( - SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, - uint16_t signature_algorithm, const uint8_t *in, size_t in_len) { - TestState *test_state = GetTestState(ssl); - if (!test_state->private_key_result.empty()) { - fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n"); - abort(); - } - - if (EVP_PKEY_id(test_state->private_key.get()) != - SSL_get_signature_algorithm_key_type(signature_algorithm)) { - fprintf(stderr, "Key type does not match signature algorithm.\n"); - abort(); - } - - // Determine the hash. - const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm); - bssl::ScopedEVP_MD_CTX ctx; - EVP_PKEY_CTX *pctx; - if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr, - test_state->private_key.get())) { - return ssl_private_key_failure; - } - - // Configure additional signature parameters. - if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { - if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || - !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) { - return ssl_private_key_failure; - } - } - - // Write the signature into |test_state|. - size_t len = 0; - if (!EVP_DigestSign(ctx.get(), nullptr, &len, in, in_len)) { - return ssl_private_key_failure; - } - test_state->private_key_result.resize(len); - if (!EVP_DigestSign(ctx.get(), test_state->private_key_result.data(), &len, - in, in_len)) { - return ssl_private_key_failure; - } - test_state->private_key_result.resize(len); - - // The signature will be released asynchronously in |AsyncPrivateKeyComplete|. - return ssl_private_key_retry; -} - -static ssl_private_key_result_t AsyncPrivateKeyDecrypt( - SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, - const uint8_t *in, size_t in_len) { - TestState *test_state = GetTestState(ssl); - if (!test_state->private_key_result.empty()) { - fprintf(stderr, - "AsyncPrivateKeyDecrypt called with operation pending.\n"); - abort(); - } - - RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get()); - if (rsa == NULL) { - fprintf(stderr, - "AsyncPrivateKeyDecrypt called with incorrect key type.\n"); - abort(); - } - test_state->private_key_result.resize(RSA_size(rsa)); - if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(), - RSA_size(rsa), in, in_len, RSA_NO_PADDING)) { - return ssl_private_key_failure; - } - - test_state->private_key_result.resize(*out_len); - - // The decryption will be released asynchronously in |AsyncPrivateComplete|. - return ssl_private_key_retry; -} - -static ssl_private_key_result_t AsyncPrivateKeyComplete( - SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) { - TestState *test_state = GetTestState(ssl); - if (test_state->private_key_result.empty()) { - fprintf(stderr, - "AsyncPrivateKeyComplete called without operation pending.\n"); - abort(); - } - - if (test_state->private_key_retries < 2) { - // Only return the decryption on the second attempt, to test both incomplete - // |decrypt| and |decrypt_complete|. - return ssl_private_key_retry; - } - - if (max_out < test_state->private_key_result.size()) { - fprintf(stderr, "Output buffer too small.\n"); - return ssl_private_key_failure; - } - OPENSSL_memcpy(out, test_state->private_key_result.data(), - test_state->private_key_result.size()); - *out_len = test_state->private_key_result.size(); - - test_state->private_key_result.clear(); - test_state->private_key_retries = 0; - return ssl_private_key_success; -} - -static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = { - AsyncPrivateKeySign, - AsyncPrivateKeyDecrypt, - AsyncPrivateKeyComplete, -}; - template<typename T> struct Free { void operator()(T *buf) { @@ -440,651 +110,6 @@ } }; -static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509, - bssl::UniquePtr<STACK_OF(X509)> *out_chain, - bssl::UniquePtr<EVP_PKEY> *out_pkey) { - const TestConfig *config = GetTestConfig(ssl); - - if (!config->signing_prefs.empty()) { - std::vector<uint16_t> u16s(config->signing_prefs.begin(), - config->signing_prefs.end()); - if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) { - return false; - } - } - - if (!config->key_file.empty()) { - *out_pkey = LoadPrivateKey(config->key_file.c_str()); - if (!*out_pkey) { - return false; - } - } - if (!config->cert_file.empty() && - !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) { - return false; - } - if (!config->ocsp_response.empty() && - !config->set_ocsp_in_callback && - !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), - config->ocsp_response.size())) { - return false; - } - return true; -} - -static bool InstallCertificate(SSL *ssl) { - bssl::UniquePtr<X509> x509; - bssl::UniquePtr<STACK_OF(X509)> chain; - bssl::UniquePtr<EVP_PKEY> pkey; - if (!GetCertificate(ssl, &x509, &chain, &pkey)) { - return false; - } - - if (pkey) { - TestState *test_state = GetTestState(ssl); - const TestConfig *config = GetTestConfig(ssl); - if (config->async) { - test_state->private_key = std::move(pkey); - SSL_set_private_key_method(ssl, &g_async_private_key_method); - } else if (!SSL_use_PrivateKey(ssl, pkey.get())) { - return false; - } - } - - if (x509 && !SSL_use_certificate(ssl, x509.get())) { - return false; - } - - if (sk_X509_num(chain.get()) > 0 && - !SSL_set1_chain(ssl, chain.get())) { - return false; - } - - return true; -} - -static enum ssl_select_cert_result_t SelectCertificateCallback( - const SSL_CLIENT_HELLO *client_hello) { - const TestConfig *config = GetTestConfig(client_hello->ssl); - GetTestState(client_hello->ssl)->early_callback_called = true; - - if (!config->expected_server_name.empty()) { - const uint8_t *extension_data; - size_t extension_len; - CBS extension, server_name_list, host_name; - uint8_t name_type; - - if (!SSL_early_callback_ctx_extension_get( - client_hello, TLSEXT_TYPE_server_name, &extension_data, - &extension_len)) { - fprintf(stderr, "Could not find server_name extension.\n"); - return ssl_select_cert_error; - } - - CBS_init(&extension, extension_data, extension_len); - if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) || - CBS_len(&extension) != 0 || - !CBS_get_u8(&server_name_list, &name_type) || - name_type != TLSEXT_NAMETYPE_host_name || - !CBS_get_u16_length_prefixed(&server_name_list, &host_name) || - CBS_len(&server_name_list) != 0) { - fprintf(stderr, "Could not decode server_name extension.\n"); - return ssl_select_cert_error; - } - - if (!CBS_mem_equal(&host_name, - (const uint8_t*)config->expected_server_name.data(), - config->expected_server_name.size())) { - fprintf(stderr, "Server name mismatch.\n"); - } - } - - if (config->fail_early_callback) { - return ssl_select_cert_error; - } - - // Install the certificate in the early callback. - if (config->use_early_callback) { - bool early_callback_ready = - GetTestState(client_hello->ssl)->early_callback_ready; - if (config->async && !early_callback_ready) { - // Install the certificate asynchronously. - return ssl_select_cert_retry; - } - if (!InstallCertificate(client_hello->ssl)) { - return ssl_select_cert_error; - } - } - return ssl_select_cert_success; -} - -static bool CheckCertificateRequest(SSL *ssl) { - const TestConfig *config = GetTestConfig(ssl); - - 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() || - OPENSSL_memcmp(certificate_types, - config->expected_certificate_types.data(), - certificate_types_len) != 0) { - fprintf(stderr, "certificate types mismatch\n"); - return false; - } - } - - if (!config->expected_client_ca_list.empty()) { - bssl::UniquePtr<STACK_OF(X509_NAME)> expected = - DecodeHexX509Names(config->expected_client_ca_list); - const size_t num_expected = sk_X509_NAME_num(expected.get()); - - const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl); - const size_t num_received = sk_X509_NAME_num(received); - - if (num_received != num_expected) { - fprintf(stderr, "expected %u names in CertificateRequest but got %u\n", - static_cast<unsigned>(num_expected), - static_cast<unsigned>(num_received)); - return false; - } - - for (size_t i = 0; i < num_received; i++) { - if (X509_NAME_cmp(sk_X509_NAME_value(received, i), - sk_X509_NAME_value(expected.get(), i)) != 0) { - fprintf(stderr, "names in CertificateRequest differ at index #%d\n", - static_cast<unsigned>(i)); - return false; - } - } - - const STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl); - if (sk_CRYPTO_BUFFER_num(buffers) != num_received) { - fprintf(stderr, - "Mismatch between SSL_get_server_requested_CAs and " - "SSL_get_client_CA_list.\n"); - return false; - } - } - - return true; -} - -static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) { - if (!CheckCertificateRequest(ssl)) { - return -1; - } - - if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) { - return -1; - } - - bssl::UniquePtr<X509> x509; - bssl::UniquePtr<STACK_OF(X509)> chain; - bssl::UniquePtr<EVP_PKEY> pkey; - if (!GetCertificate(ssl, &x509, &chain, &pkey)) { - return -1; - } - - // Return zero for no certificate. - if (!x509) { - return 0; - } - - // Chains and asynchronous private keys are not supported with client_cert_cb. - *out_x509 = x509.release(); - *out_pkey = pkey.release(); - return 1; -} - -static int CertCallback(SSL *ssl, void *arg) { - const TestConfig *config = GetTestConfig(ssl); - - // Check the CertificateRequest metadata is as expected. - if (!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) { - return -1; - } - - if (config->fail_cert_callback) { - return 0; - } - - // The certificate will be installed via other means. - if (!config->async || config->use_early_callback) { - return 1; - } - - if (!GetTestState(ssl)->cert_ready) { - return -1; - } - if (!InstallCertificate(ssl)) { - return 0; - } - return 1; -} - -static bool CheckVerifyCallback(SSL *ssl) { - const TestConfig *config = GetTestConfig(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 false; - } - } - - if (GetTestState(ssl)->cert_verified) { - fprintf(stderr, "Certificate verified twice.\n"); - return false; - } - - return true; -} - -static int CertVerifyCallback(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 = GetTestConfig(ssl); - if (!CheckVerifyCallback(ssl)) { - return 0; - } - - GetTestState(ssl)->cert_verified = true; - if (config->verify_fail) { - store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; - return 0; - } - - return 1; -} - -static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) { - const TestConfig *config = GetTestConfig(ssl); - if (!CheckVerifyCallback(ssl)) { - return ssl_verify_invalid; - } - - if (config->async && !GetTestState(ssl)->custom_verify_ready) { - return ssl_verify_retry; - } - - GetTestState(ssl)->cert_verified = true; - if (config->verify_fail) { - return ssl_verify_invalid; - } - - return ssl_verify_ok; -} - -static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, - unsigned int *out_len, void *arg) { - const TestConfig *config = GetTestConfig(ssl); - if (config->advertise_npn.empty()) { - return SSL_TLSEXT_ERR_NOACK; - } - - *out = (const uint8_t*)config->advertise_npn.data(); - *out_len = config->advertise_npn.size(); - return SSL_TLSEXT_ERR_OK; -} - -static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen, - const uint8_t* in, unsigned inlen, void* arg) { - const TestConfig *config = GetTestConfig(ssl); - if (config->select_next_proto.empty()) { - return SSL_TLSEXT_ERR_NOACK; - } - - *out = (uint8_t*)config->select_next_proto.data(); - *outlen = config->select_next_proto.size(); - return SSL_TLSEXT_ERR_OK; -} - -static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen, - const uint8_t* in, unsigned inlen, void* arg) { - if (GetTestState(ssl)->alpn_select_done) { - fprintf(stderr, "AlpnSelectCallback called after completion.\n"); - exit(1); - } - - GetTestState(ssl)->alpn_select_done = true; - - const TestConfig *config = GetTestConfig(ssl); - if (config->decline_alpn) { - return SSL_TLSEXT_ERR_NOACK; - } - - if (!config->expected_advertised_alpn.empty() && - (config->expected_advertised_alpn.size() != inlen || - OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) != - 0)) { - fprintf(stderr, "bad ALPN select callback inputs\n"); - exit(1); - } - - assert(config->select_alpn.empty() || !config->select_empty_alpn); - *out = (const uint8_t*)config->select_alpn.data(); - *outlen = config->select_alpn.size(); - return SSL_TLSEXT_ERR_OK; -} - -static unsigned PskClientCallback(SSL *ssl, const char *hint, - char *out_identity, - unsigned max_identity_len, - uint8_t *out_psk, unsigned max_psk_len) { - const TestConfig *config = GetTestConfig(ssl); - - if (config->psk_identity.empty()) { - if (hint != nullptr) { - fprintf(stderr, "Server PSK hint was non-null.\n"); - return 0; - } - } else if (hint == nullptr || - strcmp(hint, config->psk_identity.c_str()) != 0) { - fprintf(stderr, "Server PSK hint did not match.\n"); - return 0; - } - - // Account for the trailing '\0' for the identity. - if (config->psk_identity.size() >= max_identity_len || - config->psk.size() > max_psk_len) { - fprintf(stderr, "PSK buffers too small\n"); - return 0; - } - - BUF_strlcpy(out_identity, config->psk_identity.c_str(), - max_identity_len); - OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); - return config->psk.size(); -} - -static unsigned PskServerCallback(SSL *ssl, const char *identity, - uint8_t *out_psk, unsigned max_psk_len) { - const TestConfig *config = GetTestConfig(ssl); - - if (strcmp(identity, config->psk_identity.c_str()) != 0) { - fprintf(stderr, "Client PSK identity did not match.\n"); - return 0; - } - - if (config->psk.size() > max_psk_len) { - fprintf(stderr, "PSK buffers too small\n"); - return 0; - } - - OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); - return config->psk.size(); -} - -static timeval g_clock; - -static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { - *out_clock = g_clock; -} - -static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) { - *out_pkey = GetTestState(ssl)->channel_id.release(); -} - -static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len, - int *copy) { - TestState *async_state = GetTestState(ssl); - if (async_state->session) { - *copy = 0; - return async_state->session.release(); - } else if (async_state->pending_session) { - return SSL_magic_pending_session_ptr(); - } else { - return NULL; - } -} - -static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) { - const TestConfig *config = GetTestConfig(client_hello->ssl); - return config->fail_ddos_callback ? 0 : 1; -} - -static void InfoCallback(const SSL *ssl, int type, int val) { - if (type == SSL_CB_HANDSHAKE_DONE) { - if (GetTestConfig(ssl)->handshake_never_done) { - fprintf(stderr, "Handshake unexpectedly completed.\n"); - // Abort before any expected error code is printed, to ensure the overall - // test fails. - abort(); - } - // This callback is called when the handshake completes. |SSL_get_session| - // must continue to work and |SSL_in_init| must return false. - if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) { - fprintf(stderr, "Invalid state for SSL_CB_HANDSHAKE_DONE.\n"); - abort(); - } - GetTestState(ssl)->handshake_done = true; - - // Callbacks may be called again on a new handshake. - GetTestState(ssl)->ticket_decrypt_done = false; - GetTestState(ssl)->alpn_select_done = false; - } -} - -static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) { - // This callback is called as the handshake completes. |SSL_get_session| - // must continue to work and, historically, |SSL_in_init| returned false at - // this point. - if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) { - fprintf(stderr, "Invalid state for NewSessionCallback.\n"); - abort(); - } - - GetTestState(ssl)->got_new_session = true; - GetTestState(ssl)->new_session.reset(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) { - if (!encrypt) { - if (GetTestState(ssl)->ticket_decrypt_done) { - fprintf(stderr, "TicketKeyCallback called after completion.\n"); - return -1; - } - - GetTestState(ssl)->ticket_decrypt_done = true; - } - - // This is just test code, so use the all-zeros key. - static const uint8_t kZeros[16] = {0}; - - if (encrypt) { - OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros)); - RAND_bytes(iv, 16); - } else if (OPENSSL_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 GetTestConfig(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 (GetTestConfig(ssl)->custom_extension_skip) { - return 0; - } - if (GetTestConfig(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 || - OPENSSL_memcmp(contents, kCustomExtensionContents, contents_len) != 0) { - *out_alert_value = SSL_AD_DECODE_ERROR; - return 0; - } - - return 1; -} - -static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) { - // SNI must be accessible from the SNI callback. - const TestConfig *config = GetTestConfig(ssl); - const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (server_name == nullptr || - std::string(server_name) != config->expected_server_name) { - fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name, - config->expected_server_name.c_str()); - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - - return SSL_TLSEXT_ERR_OK; -} - -static void MessageCallback(int is_write, int version, int content_type, - const void *buf, size_t len, SSL *ssl, void *arg) { - const uint8_t *buf_u8 = reinterpret_cast<const uint8_t *>(buf); - const TestConfig *config = GetTestConfig(ssl); - TestState *state = GetTestState(ssl); - if (!state->msg_callback_ok) { - return; - } - - if (content_type == SSL3_RT_HEADER) { - if (len != - (config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH)) { - fprintf(stderr, "Incorrect length for record header: %zu\n", len); - state->msg_callback_ok = false; - } - return; - } - - state->msg_callback_text += is_write ? "write " : "read "; - switch (content_type) { - case 0: - if (version != SSL2_VERSION) { - fprintf(stderr, "Incorrect version for V2ClientHello: %x\n", version); - state->msg_callback_ok = false; - return; - } - state->msg_callback_text += "v2clienthello\n"; - return; - - case SSL3_RT_HANDSHAKE: { - CBS cbs; - CBS_init(&cbs, buf_u8, len); - uint8_t type; - uint32_t msg_len; - if (!CBS_get_u8(&cbs, &type) || - // TODO(davidben): Reporting on entire messages would be more - // consistent than fragments. - (config->is_dtls && - !CBS_skip(&cbs, 3 /* total */ + 2 /* seq */ + 3 /* frag_off */)) || - !CBS_get_u24(&cbs, &msg_len) || - !CBS_skip(&cbs, msg_len) || - CBS_len(&cbs) != 0) { - fprintf(stderr, "Could not parse handshake message.\n"); - state->msg_callback_ok = false; - return; - } - char text[16]; - snprintf(text, sizeof(text), "hs %d\n", type); - state->msg_callback_text += text; - return; - } - - case SSL3_RT_CHANGE_CIPHER_SPEC: - if (len != 1 || buf_u8[0] != 1) { - fprintf(stderr, "Invalid ChangeCipherSpec.\n"); - state->msg_callback_ok = false; - return; - } - state->msg_callback_text += "ccs\n"; - return; - - case SSL3_RT_ALERT: - if (len != 2) { - fprintf(stderr, "Invalid alert.\n"); - state->msg_callback_ok = false; - return; - } - char text[16]; - snprintf(text, sizeof(text), "alert %d %d\n", buf_u8[0], buf_u8[1]); - state->msg_callback_text += text; - return; - - default: - fprintf(stderr, "Invalid content_type: %d\n", content_type); - state->msg_callback_ok = false; - } -} - -static int LegacyOCSPCallback(SSL *ssl, void *arg) { - const TestConfig *config = GetTestConfig(ssl); - if (!SSL_is_server(ssl)) { - return !config->fail_ocsp_callback; - } - - if (!config->ocsp_response.empty() && - config->set_ocsp_in_callback && - !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), - config->ocsp_response.size())) { - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - if (config->fail_ocsp_callback) { - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - if (config->decline_ocsp_callback) { - return SSL_TLSEXT_ERR_NOACK; - } - return SSL_TLSEXT_ERR_OK; -} - // Connect returns a new socket connected to localhost on |port| or -1 on // error. static int Connect(uint16_t port) { @@ -1162,231 +187,6 @@ const int sock_; }; -static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) { - SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param); - bssl::UniquePtr<SSL_SESSION> new_session = bssl::SSL_SESSION_dup( - session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET); - if (new_session != nullptr) { - SSL_CTX_add_session(ctx, new_session.get()); - } -} - -static bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx, - const TestConfig *config) { - bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new( - config->is_dtls ? DTLS_method() : TLS_method())); - if (!ssl_ctx) { - return nullptr; - } - - SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool); - - // Enable TLS 1.3 for tests. - if (!config->is_dtls && - !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION)) { - 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_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) { - return nullptr; - } - - if (config->async && config->is_server) { - // Disable the internal session cache. To test asynchronous session lookup, - // we use an external session cache. - SSL_CTX_set_session_cache_mode( - ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback); - } else { - SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH); - } - - SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback); - - if (config->use_old_client_cert_callback) { - SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback); - } - - SSL_CTX_set_next_protos_advertised_cb( - ssl_ctx.get(), NextProtosAdvertisedCallback, NULL); - if (!config->select_next_proto.empty()) { - SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback, - NULL); - } - - if (!config->select_alpn.empty() || config->decline_alpn || - config->select_empty_alpn) { - SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); - } - - SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback); - - SSL_CTX_set_current_time_cb(ssl_ctx.get(), 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->use_custom_verify_callback) { - SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, 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; - } - - if (!config->use_client_ca_list.empty()) { - if (config->use_client_ca_list == "<NULL>") { - SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr); - } else if (config->use_client_ca_list == "<EMPTY>") { - bssl::UniquePtr<STACK_OF(X509_NAME)> names; - SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); - } else { - bssl::UniquePtr<STACK_OF(X509_NAME)> names = - DecodeHexX509Names(config->use_client_ca_list); - SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); - } - } - - if (config->enable_grease) { - SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1); - } - - if (!config->expected_server_name.empty()) { - SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback); - } - - if (!config->ticket_key.empty() && - !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), config->ticket_key.data(), - config->ticket_key.size())) { - return nullptr; - } - - if (config->enable_early_data) { - SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); - } - - SSL_CTX_set_tls13_variant( - ssl_ctx.get(), static_cast<enum tls13_variant_t>(config->tls13_variant)); - - if (config->allow_unknown_alpn_protos) { - SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1); - } - - if (config->enable_ed25519) { - SSL_CTX_set_ed25519_enabled(ssl_ctx.get(), 1); - } - if (config->no_rsa_pss_rsae_certs) { - SSL_CTX_set_rsa_pss_rsae_certs_enabled(ssl_ctx.get(), 0); - } - - if (!config->verify_prefs.empty()) { - std::vector<uint16_t> u16s(config->verify_prefs.begin(), - config->verify_prefs.end()); - if (!SSL_CTX_set_verify_algorithm_prefs(ssl_ctx.get(), u16s.data(), - u16s.size())) { - return nullptr; - } - } - - SSL_CTX_set_msg_callback(ssl_ctx.get(), MessageCallback); - - if (config->allow_false_start_without_alpn) { - SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1); - } - - if (config->use_ocsp_callback) { - SSL_CTX_set_tlsext_status_cb(ssl_ctx.get(), LegacyOCSPCallback); - } - - if (old_ctx) { - uint8_t keys[48]; - if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) || - !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), keys, sizeof(keys))) { - return nullptr; - } - lh_SSL_SESSION_doall_arg(old_ctx->sessions, ssl_ctx_add_session, - ssl_ctx.get()); - } - - if (config->install_cert_compression_algs && - (!SSL_CTX_add_cert_compression_alg( - ssl_ctx.get(), 0xff02, - [](SSL *ssl, CBB *out, bssl::Span<const uint8_t> in) -> bool { - if (!CBB_add_u8(out, 1) || - !CBB_add_u8(out, 2) || - !CBB_add_u8(out, 3) || - !CBB_add_u8(out, 4) || - !CBB_add_bytes(out, in.data(), in.size())) { - return false; - } - return true; - }, - [](SSL *ssl, bssl::UniquePtr<CRYPTO_BUFFER> *out, - size_t uncompressed_len, bssl::Span<const uint8_t> in) -> bool { - if (in.size() < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 || - in[3] != 4 || uncompressed_len != in.size() - 4) { - return false; - } - const bssl::Span<const uint8_t> uncompressed(in.subspan(4)); - out->reset(CRYPTO_BUFFER_new(uncompressed.data(), - uncompressed.size(), nullptr)); - return true; - }) || - !SSL_CTX_add_cert_compression_alg( - ssl_ctx.get(), 0xff01, - [](SSL *ssl, CBB *out, bssl::Span<const uint8_t> in) -> bool { - if (in.size() < 2 || in[0] != 0 || in[1] != 0) { - return false; - } - return CBB_add_bytes(out, in.data() + 2, in.size() - 2); - }, - [](SSL *ssl, bssl::UniquePtr<CRYPTO_BUFFER> *out, - size_t uncompressed_len, bssl::Span<const uint8_t> in) -> bool { - if (uncompressed_len != 2 + in.size()) { - return false; - } - std::unique_ptr<uint8_t[]> buf(new uint8_t[2 + in.size()]); - buf[0] = 0; - buf[1] = 0; - OPENSSL_memcpy(&buf[2], in.data(), in.size()); - out->reset(CRYPTO_BUFFER_new(buf.get(), 2 + in.size(), nullptr)); - return true; - }))) { - fprintf(stderr, "SSL_CTX_add_cert_compression_alg failed.\n"); - abort(); - } - - return ssl_ctx; -} - // RetryAsync is called after a failed operation on |ssl| with return code // |ret|. If the operation should be retried, it simulates one asynchronous // event and returns true. Otherwise it returns false. @@ -1934,219 +734,6 @@ return true; } -static bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, const TestConfig *config, - SSL_SESSION *session, bool is_resume, - std::unique_ptr<TestState> test_state) { - bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx)); - if (!ssl) { - return nullptr; - } - - if (!SetTestConfig(ssl.get(), config)) { - return nullptr; - } - if (test_state != nullptr) { - if (!SetTestState(ssl.get(), std::move(test_state))) { - return nullptr; - } - GetTestState(ssl.get())->is_resume = is_resume; - } - - if (config->fallback_scsv && - !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) { - return nullptr; - } - // Install the certificate synchronously if nothing else will handle it. - if (!config->use_early_callback && - !config->use_old_client_cert_callback && - !config->async && - !InstallCertificate(ssl.get())) { - return nullptr; - } - if (!config->use_old_client_cert_callback) { - SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); - } - int mode = SSL_VERIFY_NONE; - if (config->require_any_client_certificate) { - mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - } - if (config->verify_peer) { - mode = SSL_VERIFY_PEER; - } - if (config->verify_peer_if_no_obc) { - // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client - // certificates were requested is easy. - mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - } - if (config->use_custom_verify_callback) { - SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback); - } else if (mode != SSL_VERIFY_NONE) { - SSL_set_verify(ssl.get(), mode, NULL); - } - if (config->false_start) { - SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); - } - if (config->cbc_record_splitting) { - SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING); - } - if (config->partial_write) { - SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE); - } - if (config->no_tls13) { - SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3); - } - if (config->no_tls12) { - SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2); - } - if (config->no_tls11) { - SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1); - } - if (config->no_tls1) { - SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1); - } - if (!config->expected_channel_id.empty() || - config->enable_channel_id) { - SSL_set_tls_channel_id_enabled(ssl.get(), 1); - } - if (!config->send_channel_id.empty()) { - SSL_set_tls_channel_id_enabled(ssl.get(), 1); - if (!config->async) { - // The async case will be supplied by |ChannelIdCallback|. - bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id); - if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) { - return nullptr; - } - } - } - if (!config->send_token_binding_params.empty()) { - SSL_set_token_binding_params(ssl.get(), - reinterpret_cast<const uint8_t *>( - config->send_token_binding_params.data()), - config->send_token_binding_params.length()); - } - if (!config->host_name.empty() && - !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) { - return nullptr; - } - if (!config->advertise_alpn.empty() && - SSL_set_alpn_protos(ssl.get(), - (const uint8_t *)config->advertise_alpn.data(), - config->advertise_alpn.size()) != 0) { - return nullptr; - } - if (!config->psk.empty()) { - SSL_set_psk_client_callback(ssl.get(), PskClientCallback); - SSL_set_psk_server_callback(ssl.get(), PskServerCallback); - } - if (!config->psk_identity.empty() && - !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) { - return nullptr; - } - if (!config->srtp_profiles.empty() && - !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) { - return nullptr; - } - if (config->enable_ocsp_stapling) { - SSL_enable_ocsp_stapling(ssl.get()); - } - if (config->enable_signed_cert_timestamps) { - SSL_enable_signed_cert_timestamps(ssl.get()); - } - if (config->min_version != 0 && - !SSL_set_min_proto_version(ssl.get(), (uint16_t)config->min_version)) { - return nullptr; - } - if (config->max_version != 0 && - !SSL_set_max_proto_version(ssl.get(), (uint16_t)config->max_version)) { - return nullptr; - } - if (config->mtu != 0) { - SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU); - SSL_set_mtu(ssl.get(), config->mtu); - } - if (config->install_ddos_callback) { - SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback); - } - SSL_set_shed_handshake_config(ssl.get(), true); - if (config->renegotiate_once) { - SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once); - } - if (config->renegotiate_freely || - config->forbid_renegotiation_after_handshake) { - // |forbid_renegotiation_after_handshake| will disable renegotiation later. - SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); - } - if (config->renegotiate_ignore) { - SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore); - } - if (!config->check_close_notify) { - SSL_set_quiet_shutdown(ssl.get(), 1); - } - if (config->p384_only) { - int nid = NID_secp384r1; - if (!SSL_set1_curves(ssl.get(), &nid, 1)) { - return nullptr; - } - } - if (config->enable_all_curves) { - static const int kAllCurves[] = { - NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1, - NID_secp521r1, NID_X25519, - }; - if (!SSL_set1_curves(ssl.get(), kAllCurves, - OPENSSL_ARRAY_SIZE(kAllCurves))) { - return nullptr; - } - } - if (config->initial_timeout_duration_ms > 0) { - DTLSv1_set_initial_timeout_duration(ssl.get(), - config->initial_timeout_duration_ms); - } - if (config->max_cert_list > 0) { - SSL_set_max_cert_list(ssl.get(), config->max_cert_list); - } - if (config->retain_only_sha256_client_cert) { - SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1); - } - if (config->max_send_fragment > 0) { - SSL_set_max_send_fragment(ssl.get(), config->max_send_fragment); - } - if (config->dummy_pq_padding_len > 0 && - !SSL_set_dummy_pq_padding_size(ssl.get(), config->dummy_pq_padding_len)) { - return nullptr; - } - if (!config->quic_transport_params.empty()) { - if (!SSL_set_quic_transport_params( - ssl.get(), - reinterpret_cast<const uint8_t *>( - config->quic_transport_params.data()), - config->quic_transport_params.size())) { - return nullptr; - } - } - - if (session != NULL) { - if (!config->is_server) { - if (SSL_set_session(ssl.get(), session) != 1) { - return nullptr; - } - } else if (config->async) { - // The internal session cache is disabled, so install the session - // manually. - SSL_SESSION_up_ref(session); - GetTestState(ssl.get())->pending_session.reset(session); - } - } - - if (SSL_get_current_cipher(ssl.get()) != nullptr) { - fprintf(stderr, "non-null cipher before handshake\n"); - return nullptr; - } - - return ssl; -} - static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, bssl::UniquePtr<SSL> *ssl_uniqueptr, const TestConfig *config, bool is_resume, bool is_retry, @@ -2160,8 +747,8 @@ SSL_CTX *ssl_ctx, const TestConfig *config, const TestConfig *retry_config, bool is_resume, SSL_SESSION *session, SettingsWriter *writer) { - bssl::UniquePtr<SSL> ssl = NewSSL(ssl_ctx, config, session, is_resume, - std::unique_ptr<TestState>(new TestState)); + bssl::UniquePtr<SSL> ssl = config->NewSSL( + ssl_ctx, session, is_resume, std::unique_ptr<TestState>(new TestState)); if (!ssl) { return false; } @@ -2183,7 +770,7 @@ return false; } if (config->is_dtls) { - bssl::UniquePtr<BIO> packeted = PacketedBioCreate(&g_clock); + bssl::UniquePtr<BIO> packeted = PacketedBioCreate(GetClock()); if (!packeted) { return false; } @@ -2280,19 +867,20 @@ if (!config->implicit_handshake) { if (config->handoff) { - bssl::UniquePtr<SSL_CTX> ctx_handoff = SetupCtx(ssl->ctx, config); + bssl::UniquePtr<SSL_CTX> ctx_handoff = config->SetupCtx(ssl->ctx); if (!ctx_handoff) { return false; } SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1); bssl::UniquePtr<SSL> ssl_handoff = - NewSSL(ctx_handoff.get(), config, nullptr, false, nullptr); + config->NewSSL(ctx_handoff.get(), nullptr, false, nullptr); if (!ssl_handoff) { return false; } SSL_set_accept_state(ssl_handoff.get()); - if (!MoveExData(ssl_handoff.get(), ssl)) { + if (!MoveTestConfig(ssl_handoff.get(), ssl) || + !MoveTestState(ssl_handoff.get(), ssl)) { return false; } MoveBIOs(ssl_handoff.get(), ssl); @@ -2322,7 +910,8 @@ } MoveBIOs(ssl, ssl_handoff.get()); - if (!MoveExData(ssl, ssl_handoff.get())) { + if (!MoveTestConfig(ssl, ssl_handoff.get()) || + !MoveTestState(ssl, ssl_handoff.get())) { return false; } @@ -2354,17 +943,18 @@ return false; } - bssl::UniquePtr<SSL_CTX> ctx_handback = SetupCtx(ssl->ctx, config); + bssl::UniquePtr<SSL_CTX> ctx_handback = config->SetupCtx(ssl->ctx); if (!ctx_handback) { return false; } bssl::UniquePtr<SSL> ssl_handback = - NewSSL(ctx_handback.get(), config, nullptr, false, nullptr); + config->NewSSL(ctx_handback.get(), nullptr, false, nullptr); if (!ssl_handback) { return false; } MoveBIOs(ssl_handback.get(), ssl); - if (!MoveExData(ssl_handback.get(), ssl)) { + if (!MoveTestConfig(ssl_handback.get(), ssl) || + !MoveTestState(ssl_handback.get(), ssl)) { return false; } @@ -2391,8 +981,7 @@ return false; } - lh_SSL_SESSION_doall_arg(ssl->ctx->sessions, ssl_ctx_add_session, - session_ctx); + CopySessions(session_ctx, ssl->ctx); if (is_resume && !is_retry && !config->is_server && config->expect_no_offer_early_data && SSL_in_early_data(ssl)) { @@ -2702,11 +1291,6 @@ #endif CRYPTO_library_init(); - g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree); - if (g_config_index < 0 || g_state_index < 0) { - return 1; - } TestConfig initial_config, resume_config, retry_config; if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config, @@ -2714,20 +1298,13 @@ return Usage(argv[0]); } - g_pool = CRYPTO_BUFFER_POOL_new(); - - // Some code treats the zero time special, so initialize the clock to a - // non-zero time. - g_clock.tv_sec = 1234; - g_clock.tv_usec = 1234; - bssl::UniquePtr<SSL_CTX> ssl_ctx; bssl::UniquePtr<SSL_SESSION> session; for (int i = 0; i < initial_config.resume_count + 1; i++) { bool is_resume = i > 0; TestConfig *config = is_resume ? &resume_config : &initial_config; - ssl_ctx = SetupCtx(ssl_ctx.get(), config); + ssl_ctx = config->SetupCtx(ssl_ctx.get()); if (!ssl_ctx) { ERR_print_errors_fp(stderr); return 1; @@ -2757,7 +1334,7 @@ } if (config->resumption_delay != 0) { - g_clock.tv_sec += config->resumption_delay; + AdvanceClock(config->resumption_delay); } }
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index 0ae3a73..f7804d6 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc
@@ -14,6 +14,7 @@ #include "test_config.h" +#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -21,6 +22,12 @@ #include <memory> #include <openssl/base64.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> + +#include "../../crypto/internal.h" +#include "../internal.h" +#include "test_state.h" namespace { @@ -339,3 +346,1348 @@ return true; } + +static CRYPTO_once_t once = CRYPTO_ONCE_INIT; +static int g_config_index = 0; +static CRYPTO_BUFFER_POOL *g_pool = nullptr; + +static void init_once() { + g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (g_config_index < 0) { + abort(); + } + g_pool = CRYPTO_BUFFER_POOL_new(); + if (!g_pool) { + abort(); + } +} + +bool SetTestConfig(SSL *ssl, const TestConfig *config) { + CRYPTO_once(&once, init_once); + return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1; +} + +const TestConfig *GetTestConfig(const SSL *ssl) { + CRYPTO_once(&once, init_once); + return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index); +} + +bool MoveTestConfig(SSL *dest, SSL *src) { + const TestConfig *config = GetTestConfig(src); + if (!SSL_set_ex_data(src, g_config_index, nullptr) || + !SSL_set_ex_data(dest, g_config_index, (void *)config)) { + return false; + } + + return true; +} + +static int LegacyOCSPCallback(SSL *ssl, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + if (!SSL_is_server(ssl)) { + return !config->fail_ocsp_callback; + } + + if (!config->ocsp_response.empty() && config->set_ocsp_in_callback && + !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + if (config->fail_ocsp_callback) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + if (config->decline_ocsp_callback) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} + +// 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 (GetTestConfig(ssl)->custom_extension_skip) { + return 0; + } + if (GetTestConfig(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 || + OPENSSL_memcmp(contents, kCustomExtensionContents, contents_len) != 0) { + *out_alert_value = SSL_AD_DECODE_ERROR; + return 0; + } + + return 1; +} + +static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) { + // SNI must be accessible from the SNI callback. + const TestConfig *config = GetTestConfig(ssl); + const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr || + std::string(server_name) != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name, + config->expected_server_name.c_str()); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + return SSL_TLSEXT_ERR_OK; +} + +static int NextProtoSelectCallback(SSL *ssl, uint8_t **out, uint8_t *outlen, + const uint8_t *in, unsigned inlen, + void *arg) { + const TestConfig *config = GetTestConfig(ssl); + if (config->select_next_proto.empty()) { + return SSL_TLSEXT_ERR_NOACK; + } + + *out = (uint8_t *)config->select_next_proto.data(); + *outlen = config->select_next_proto.size(); + return SSL_TLSEXT_ERR_OK; +} + +static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, + unsigned int *out_len, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + if (config->advertise_npn.empty()) { + return SSL_TLSEXT_ERR_NOACK; + } + + *out = (const uint8_t *)config->advertise_npn.data(); + *out_len = config->advertise_npn.size(); + return SSL_TLSEXT_ERR_OK; +} + +static void MessageCallback(int is_write, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg) { + const uint8_t *buf_u8 = reinterpret_cast<const uint8_t *>(buf); + const TestConfig *config = GetTestConfig(ssl); + TestState *state = GetTestState(ssl); + if (!state->msg_callback_ok) { + return; + } + + if (content_type == SSL3_RT_HEADER) { + if (len != + (config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH)) { + fprintf(stderr, "Incorrect length for record header: %zu\n", len); + state->msg_callback_ok = false; + } + return; + } + + state->msg_callback_text += is_write ? "write " : "read "; + switch (content_type) { + case 0: + if (version != SSL2_VERSION) { + fprintf(stderr, "Incorrect version for V2ClientHello: %x\n", version); + state->msg_callback_ok = false; + return; + } + state->msg_callback_text += "v2clienthello\n"; + return; + + case SSL3_RT_HANDSHAKE: { + CBS cbs; + CBS_init(&cbs, buf_u8, len); + uint8_t type; + uint32_t msg_len; + if (!CBS_get_u8(&cbs, &type) || + // TODO(davidben): Reporting on entire messages would be more + // consistent than fragments. + (config->is_dtls && + !CBS_skip(&cbs, 3 /* total */ + 2 /* seq */ + 3 /* frag_off */)) || + !CBS_get_u24(&cbs, &msg_len) || !CBS_skip(&cbs, msg_len) || + CBS_len(&cbs) != 0) { + fprintf(stderr, "Could not parse handshake message.\n"); + state->msg_callback_ok = false; + return; + } + char text[16]; + snprintf(text, sizeof(text), "hs %d\n", type); + state->msg_callback_text += text; + return; + } + + case SSL3_RT_CHANGE_CIPHER_SPEC: + if (len != 1 || buf_u8[0] != 1) { + fprintf(stderr, "Invalid ChangeCipherSpec.\n"); + state->msg_callback_ok = false; + return; + } + state->msg_callback_text += "ccs\n"; + return; + + case SSL3_RT_ALERT: + if (len != 2) { + fprintf(stderr, "Invalid alert.\n"); + state->msg_callback_ok = false; + return; + } + char text[16]; + snprintf(text, sizeof(text), "alert %d %d\n", buf_u8[0], buf_u8[1]); + state->msg_callback_text += text; + return; + + default: + fprintf(stderr, "Invalid content_type: %d\n", content_type); + state->msg_callback_ok = false; + } +} + +static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt) { + if (!encrypt) { + if (GetTestState(ssl)->ticket_decrypt_done) { + fprintf(stderr, "TicketKeyCallback called after completion.\n"); + return -1; + } + + GetTestState(ssl)->ticket_decrypt_done = true; + } + + // This is just test code, so use the all-zeros key. + static const uint8_t kZeros[16] = {0}; + + if (encrypt) { + OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (OPENSSL_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 GetTestConfig(ssl)->renew_ticket ? 2 : 1; + } + return 1; +} + +static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) { + // This callback is called as the handshake completes. |SSL_get_session| + // must continue to work and, historically, |SSL_in_init| returned false at + // this point. + if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) { + fprintf(stderr, "Invalid state for NewSessionCallback.\n"); + abort(); + } + + GetTestState(ssl)->got_new_session = true; + GetTestState(ssl)->new_session.reset(session); + return 1; +} + +static void InfoCallback(const SSL *ssl, int type, int val) { + if (type == SSL_CB_HANDSHAKE_DONE) { + if (GetTestConfig(ssl)->handshake_never_done) { + fprintf(stderr, "Handshake unexpectedly completed.\n"); + // Abort before any expected error code is printed, to ensure the overall + // test fails. + abort(); + } + // This callback is called when the handshake completes. |SSL_get_session| + // must continue to work and |SSL_in_init| must return false. + if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) { + fprintf(stderr, "Invalid state for SSL_CB_HANDSHAKE_DONE.\n"); + abort(); + } + GetTestState(ssl)->handshake_done = true; + + // Callbacks may be called again on a new handshake. + GetTestState(ssl)->ticket_decrypt_done = false; + GetTestState(ssl)->alpn_select_done = false; + } +} + +static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) { + *out_pkey = GetTestState(ssl)->channel_id.release(); +} + +static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len, + int *copy) { + TestState *async_state = GetTestState(ssl); + if (async_state->session) { + *copy = 0; + return async_state->session.release(); + } else if (async_state->pending_session) { + return SSL_magic_pending_session_ptr(); + } else { + return NULL; + } +} + +static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { + *out_clock = *GetClock(); +} + +static int AlpnSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *outlen, + const uint8_t *in, unsigned inlen, void *arg) { + if (GetTestState(ssl)->alpn_select_done) { + fprintf(stderr, "AlpnSelectCallback called after completion.\n"); + exit(1); + } + + GetTestState(ssl)->alpn_select_done = true; + + const TestConfig *config = GetTestConfig(ssl); + if (config->decline_alpn) { + return SSL_TLSEXT_ERR_NOACK; + } + + if (!config->expected_advertised_alpn.empty() && + (config->expected_advertised_alpn.size() != inlen || + OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) != + 0)) { + fprintf(stderr, "bad ALPN select callback inputs\n"); + exit(1); + } + + assert(config->select_alpn.empty() || !config->select_empty_alpn); + *out = (const uint8_t *)config->select_alpn.data(); + *outlen = config->select_alpn.size(); + return SSL_TLSEXT_ERR_OK; +} + +static bool CheckVerifyCallback(SSL *ssl) { + const TestConfig *config = GetTestConfig(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 false; + } + } + + if (GetTestState(ssl)->cert_verified) { + fprintf(stderr, "Certificate verified twice.\n"); + return false; + } + + return true; +} + +static int CertVerifyCallback(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 = GetTestConfig(ssl); + if (!CheckVerifyCallback(ssl)) { + return 0; + } + + GetTestState(ssl)->cert_verified = true; + if (config->verify_fail) { + store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + return 0; + } + + return 1; +} + +bool LoadCertificate(bssl::UniquePtr<X509> *out_x509, + bssl::UniquePtr<STACK_OF(X509)> *out_chain, + const std::string &file) { + bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); + if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { + return false; + } + + out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (!*out_x509) { + return false; + } + + out_chain->reset(sk_X509_new_null()); + if (!*out_chain) { + return false; + } + + // Keep reading the certificate chain. + for (;;) { + bssl::UniquePtr<X509> cert( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (!cert) { + break; + } + + if (!bssl::PushToStack(out_chain->get(), std::move(cert))) { + return false; + } + } + + uint32_t err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) != ERR_LIB_PEM || + ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { + return false; + } + + ERR_clear_error(); + return true; +} + +bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) { + bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); + if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { + return nullptr; + } + return bssl::UniquePtr<EVP_PKEY>( + PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL)); +} + +static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509, + bssl::UniquePtr<STACK_OF(X509)> *out_chain, + bssl::UniquePtr<EVP_PKEY> *out_pkey) { + const TestConfig *config = GetTestConfig(ssl); + + if (!config->signing_prefs.empty()) { + std::vector<uint16_t> u16s(config->signing_prefs.begin(), + config->signing_prefs.end()); + if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) { + return false; + } + } + + if (!config->key_file.empty()) { + *out_pkey = LoadPrivateKey(config->key_file.c_str()); + if (!*out_pkey) { + return false; + } + } + if (!config->cert_file.empty() && + !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) { + return false; + } + if (!config->ocsp_response.empty() && !config->set_ocsp_in_callback && + !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return false; + } + return true; +} + +static bool FromHexDigit(uint8_t *out, char c) { + if ('0' <= c && c <= '9') { + *out = c - '0'; + return true; + } + if ('a' <= c && c <= 'f') { + *out = c - 'a' + 10; + return true; + } + if ('A' <= c && c <= 'F') { + *out = c - 'A' + 10; + return true; + } + return false; +} + +static bool HexDecode(std::string *out, const std::string &in) { + if ((in.size() & 1) != 0) { + return false; + } + + std::unique_ptr<uint8_t[]> buf(new uint8_t[in.size() / 2]); + for (size_t i = 0; i < in.size() / 2; i++) { + uint8_t high, low; + if (!FromHexDigit(&high, in[i * 2]) || !FromHexDigit(&low, in[i * 2 + 1])) { + return false; + } + buf[i] = (high << 4) | low; + } + + out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2); + return true; +} + +static std::vector<std::string> SplitParts(const std::string &in, + const char delim) { + std::vector<std::string> ret; + size_t start = 0; + + for (size_t i = 0; i < in.size(); i++) { + if (in[i] == delim) { + ret.push_back(in.substr(start, i - start)); + start = i + 1; + } + } + + ret.push_back(in.substr(start, std::string::npos)); + return ret; +} + +static std::vector<std::string> DecodeHexStrings( + const std::string &hex_strings) { + std::vector<std::string> ret; + const std::vector<std::string> parts = SplitParts(hex_strings, ','); + + for (const auto &part : parts) { + std::string binary; + if (!HexDecode(&binary, part)) { + fprintf(stderr, "Bad hex string: %s\n", part.c_str()); + return ret; + } + + ret.push_back(binary); + } + + return ret; +} + +static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names( + const std::string &hex_names) { + const std::vector<std::string> der_names = DecodeHexStrings(hex_names); + bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null()); + if (!ret) { + return nullptr; + } + + for (const auto &der_name : der_names) { + const uint8_t *const data = + reinterpret_cast<const uint8_t *>(der_name.data()); + const uint8_t *derp = data; + bssl::UniquePtr<X509_NAME> name( + d2i_X509_NAME(nullptr, &derp, der_name.size())); + if (!name || derp != data + der_name.size()) { + fprintf(stderr, "Failed to parse X509_NAME.\n"); + return nullptr; + } + + if (!bssl::PushToStack(ret.get(), std::move(name))) { + return nullptr; + } + } + + return ret; +} + +static bool CheckCertificateRequest(SSL *ssl) { + const TestConfig *config = GetTestConfig(ssl); + + 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() || + OPENSSL_memcmp(certificate_types, + config->expected_certificate_types.data(), + certificate_types_len) != 0) { + fprintf(stderr, "certificate types mismatch\n"); + return false; + } + } + + if (!config->expected_client_ca_list.empty()) { + bssl::UniquePtr<STACK_OF(X509_NAME)> expected = + DecodeHexX509Names(config->expected_client_ca_list); + const size_t num_expected = sk_X509_NAME_num(expected.get()); + + const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl); + const size_t num_received = sk_X509_NAME_num(received); + + if (num_received != num_expected) { + fprintf(stderr, "expected %u names in CertificateRequest but got %u\n", + static_cast<unsigned>(num_expected), + static_cast<unsigned>(num_received)); + return false; + } + + for (size_t i = 0; i < num_received; i++) { + if (X509_NAME_cmp(sk_X509_NAME_value(received, i), + sk_X509_NAME_value(expected.get(), i)) != 0) { + fprintf(stderr, "names in CertificateRequest differ at index #%d\n", + static_cast<unsigned>(i)); + return false; + } + } + + const STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl); + if (sk_CRYPTO_BUFFER_num(buffers) != num_received) { + fprintf(stderr, + "Mismatch between SSL_get_server_requested_CAs and " + "SSL_get_client_CA_list.\n"); + return false; + } + } + + return true; +} + +static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) { + if (!CheckCertificateRequest(ssl)) { + return -1; + } + + if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) { + return -1; + } + + bssl::UniquePtr<X509> x509; + bssl::UniquePtr<STACK_OF(X509)> chain; + bssl::UniquePtr<EVP_PKEY> pkey; + if (!GetCertificate(ssl, &x509, &chain, &pkey)) { + return -1; + } + + // Return zero for no certificate. + if (!x509) { + return 0; + } + + // Chains and asynchronous private keys are not supported with client_cert_cb. + *out_x509 = x509.release(); + *out_pkey = pkey.release(); + return 1; +} + +static ssl_private_key_result_t AsyncPrivateKeySign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint16_t signature_algorithm, const uint8_t *in, size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->private_key_result.empty()) { + fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n"); + abort(); + } + + if (EVP_PKEY_id(test_state->private_key.get()) != + SSL_get_signature_algorithm_key_type(signature_algorithm)) { + fprintf(stderr, "Key type does not match signature algorithm.\n"); + abort(); + } + + // Determine the hash. + const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm); + bssl::ScopedEVP_MD_CTX ctx; + EVP_PKEY_CTX *pctx; + if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr, + test_state->private_key.get())) { + return ssl_private_key_failure; + } + + // Configure additional signature parameters. + if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { + if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || + !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) { + return ssl_private_key_failure; + } + } + + // Write the signature into |test_state|. + size_t len = 0; + if (!EVP_DigestSign(ctx.get(), nullptr, &len, in, in_len)) { + return ssl_private_key_failure; + } + test_state->private_key_result.resize(len); + if (!EVP_DigestSign(ctx.get(), test_state->private_key_result.data(), &len, + in, in_len)) { + return ssl_private_key_failure; + } + test_state->private_key_result.resize(len); + + // The signature will be released asynchronously in |AsyncPrivateKeyComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out, + size_t *out_len, + size_t max_out, + const uint8_t *in, + size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->private_key_result.empty()) { + fprintf(stderr, "AsyncPrivateKeyDecrypt called with operation pending.\n"); + abort(); + } + + RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get()); + if (rsa == NULL) { + fprintf(stderr, "AsyncPrivateKeyDecrypt called with incorrect key type.\n"); + abort(); + } + test_state->private_key_result.resize(RSA_size(rsa)); + if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(), + RSA_size(rsa), in, in_len, RSA_NO_PADDING)) { + return ssl_private_key_failure; + } + + test_state->private_key_result.resize(*out_len); + + // The decryption will be released asynchronously in |AsyncPrivateComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out, + size_t *out_len, + size_t max_out) { + TestState *test_state = GetTestState(ssl); + if (test_state->private_key_result.empty()) { + fprintf(stderr, + "AsyncPrivateKeyComplete called without operation pending.\n"); + abort(); + } + + if (test_state->private_key_retries < 2) { + // Only return the decryption on the second attempt, to test both incomplete + // |decrypt| and |decrypt_complete|. + return ssl_private_key_retry; + } + + if (max_out < test_state->private_key_result.size()) { + fprintf(stderr, "Output buffer too small.\n"); + return ssl_private_key_failure; + } + OPENSSL_memcpy(out, test_state->private_key_result.data(), + test_state->private_key_result.size()); + *out_len = test_state->private_key_result.size(); + + test_state->private_key_result.clear(); + test_state->private_key_retries = 0; + return ssl_private_key_success; +} + +static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = { + AsyncPrivateKeySign, + AsyncPrivateKeyDecrypt, + AsyncPrivateKeyComplete, +}; + +static bool InstallCertificate(SSL *ssl) { + bssl::UniquePtr<X509> x509; + bssl::UniquePtr<STACK_OF(X509)> chain; + bssl::UniquePtr<EVP_PKEY> pkey; + if (!GetCertificate(ssl, &x509, &chain, &pkey)) { + return false; + } + + if (pkey) { + TestState *test_state = GetTestState(ssl); + const TestConfig *config = GetTestConfig(ssl); + if (config->async) { + test_state->private_key = std::move(pkey); + SSL_set_private_key_method(ssl, &g_async_private_key_method); + } else if (!SSL_use_PrivateKey(ssl, pkey.get())) { + return false; + } + } + + if (x509 && !SSL_use_certificate(ssl, x509.get())) { + return false; + } + + if (sk_X509_num(chain.get()) > 0 && !SSL_set1_chain(ssl, chain.get())) { + return false; + } + + return true; +} + +static enum ssl_select_cert_result_t SelectCertificateCallback( + const SSL_CLIENT_HELLO *client_hello) { + const TestConfig *config = GetTestConfig(client_hello->ssl); + GetTestState(client_hello->ssl)->early_callback_called = true; + + if (!config->expected_server_name.empty()) { + const uint8_t *extension_data; + size_t extension_len; + CBS extension, server_name_list, host_name; + uint8_t name_type; + + if (!SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_server_name, &extension_data, + &extension_len)) { + fprintf(stderr, "Could not find server_name extension.\n"); + return ssl_select_cert_error; + } + + CBS_init(&extension, extension_data, extension_len); + if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) || + CBS_len(&extension) != 0 || + !CBS_get_u8(&server_name_list, &name_type) || + name_type != TLSEXT_NAMETYPE_host_name || + !CBS_get_u16_length_prefixed(&server_name_list, &host_name) || + CBS_len(&server_name_list) != 0) { + fprintf(stderr, "Could not decode server_name extension.\n"); + return ssl_select_cert_error; + } + + if (!CBS_mem_equal(&host_name, + (const uint8_t *)config->expected_server_name.data(), + config->expected_server_name.size())) { + fprintf(stderr, "Server name mismatch.\n"); + } + } + + if (config->fail_early_callback) { + return ssl_select_cert_error; + } + + // Install the certificate in the early callback. + if (config->use_early_callback) { + bool early_callback_ready = + GetTestState(client_hello->ssl)->early_callback_ready; + if (config->async && !early_callback_ready) { + // Install the certificate asynchronously. + return ssl_select_cert_retry; + } + if (!InstallCertificate(client_hello->ssl)) { + return ssl_select_cert_error; + } + } + return ssl_select_cert_success; +} + +bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const { + bssl::UniquePtr<SSL_CTX> ssl_ctx( + SSL_CTX_new(is_dtls ? DTLS_method() : TLS_method())); + if (!ssl_ctx) { + return nullptr; + } + + CRYPTO_once(&once, init_once); + SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool); + + // Enable TLS 1.3 for tests. + if (!is_dtls && + !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION)) { + return nullptr; + } + + std::string cipher_list = "ALL"; + if (!cipher.empty()) { + cipher_list = cipher; + SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); + } + if (!SSL_CTX_set_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) { + return nullptr; + } + + if (async && is_server) { + // Disable the internal session cache. To test asynchronous session lookup, + // we use an external session cache. + SSL_CTX_set_session_cache_mode( + ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback); + } else { + SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH); + } + + SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback); + + if (use_old_client_cert_callback) { + SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback); + } + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx.get(), + NextProtosAdvertisedCallback, NULL); + if (!select_next_proto.empty()) { + SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback, + NULL); + } + + if (!select_alpn.empty() || decline_alpn || select_empty_alpn) { + SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); + } + + SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback); + + SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback); + + SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback); + SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback); + + if (use_ticket_callback) { + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback); + } + + if (enable_client_custom_extension && + !SSL_CTX_add_client_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (enable_server_custom_extension && + !SSL_CTX_add_server_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (!use_custom_verify_callback) { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, NULL); + } + + if (!signed_cert_timestamps.empty() && + !SSL_CTX_set_signed_cert_timestamp_list( + ssl_ctx.get(), (const uint8_t *)signed_cert_timestamps.data(), + signed_cert_timestamps.size())) { + return nullptr; + } + + if (!use_client_ca_list.empty()) { + if (use_client_ca_list == "<NULL>") { + SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr); + } else if (use_client_ca_list == "<EMPTY>") { + bssl::UniquePtr<STACK_OF(X509_NAME)> names; + SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); + } else { + bssl::UniquePtr<STACK_OF(X509_NAME)> names = + DecodeHexX509Names(use_client_ca_list); + SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); + } + } + + if (enable_grease) { + SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1); + } + + if (!expected_server_name.empty()) { + SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback); + } + + if (!ticket_key.empty() && + !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), ticket_key.data(), + ticket_key.size())) { + return nullptr; + } + + if (enable_early_data) { + SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); + } + + SSL_CTX_set_tls13_variant(ssl_ctx.get(), + static_cast<enum tls13_variant_t>(tls13_variant)); + + if (allow_unknown_alpn_protos) { + SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1); + } + + if (enable_ed25519) { + SSL_CTX_set_ed25519_enabled(ssl_ctx.get(), 1); + } + if (no_rsa_pss_rsae_certs) { + SSL_CTX_set_rsa_pss_rsae_certs_enabled(ssl_ctx.get(), 0); + } + + if (!verify_prefs.empty()) { + std::vector<uint16_t> u16s(verify_prefs.begin(), verify_prefs.end()); + if (!SSL_CTX_set_verify_algorithm_prefs(ssl_ctx.get(), u16s.data(), + u16s.size())) { + return nullptr; + } + } + + SSL_CTX_set_msg_callback(ssl_ctx.get(), MessageCallback); + + if (allow_false_start_without_alpn) { + SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1); + } + + if (use_ocsp_callback) { + SSL_CTX_set_tlsext_status_cb(ssl_ctx.get(), LegacyOCSPCallback); + } + + if (old_ctx) { + uint8_t keys[48]; + if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) || + !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), keys, sizeof(keys))) { + return nullptr; + } + CopySessions(ssl_ctx.get(), old_ctx); + } + + if (install_cert_compression_algs && + (!SSL_CTX_add_cert_compression_alg( + ssl_ctx.get(), 0xff02, + [](SSL *ssl, CBB *out, bssl::Span<const uint8_t> in) -> bool { + if (!CBB_add_u8(out, 1) || !CBB_add_u8(out, 2) || + !CBB_add_u8(out, 3) || !CBB_add_u8(out, 4) || + !CBB_add_bytes(out, in.data(), in.size())) { + return false; + } + return true; + }, + [](SSL *ssl, bssl::UniquePtr<CRYPTO_BUFFER> *out, + size_t uncompressed_len, bssl::Span<const uint8_t> in) -> bool { + if (in.size() < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 || + in[3] != 4 || uncompressed_len != in.size() - 4) { + return false; + } + const bssl::Span<const uint8_t> uncompressed(in.subspan(4)); + out->reset(CRYPTO_BUFFER_new(uncompressed.data(), + uncompressed.size(), nullptr)); + return true; + }) || + !SSL_CTX_add_cert_compression_alg( + ssl_ctx.get(), 0xff01, + [](SSL *ssl, CBB *out, bssl::Span<const uint8_t> in) -> bool { + if (in.size() < 2 || in[0] != 0 || in[1] != 0) { + return false; + } + return CBB_add_bytes(out, in.data() + 2, in.size() - 2); + }, + [](SSL *ssl, bssl::UniquePtr<CRYPTO_BUFFER> *out, + size_t uncompressed_len, bssl::Span<const uint8_t> in) -> bool { + if (uncompressed_len != 2 + in.size()) { + return false; + } + std::unique_ptr<uint8_t[]> buf(new uint8_t[2 + in.size()]); + buf[0] = 0; + buf[1] = 0; + OPENSSL_memcpy(&buf[2], in.data(), in.size()); + out->reset(CRYPTO_BUFFER_new(buf.get(), 2 + in.size(), nullptr)); + return true; + }))) { + fprintf(stderr, "SSL_CTX_add_cert_compression_alg failed.\n"); + abort(); + } + + return ssl_ctx; +} + +static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) { + const TestConfig *config = GetTestConfig(client_hello->ssl); + return config->fail_ddos_callback ? 0 : 1; +} + +static unsigned PskClientCallback(SSL *ssl, const char *hint, + char *out_identity, unsigned max_identity_len, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetTestConfig(ssl); + + if (config->psk_identity.empty()) { + if (hint != nullptr) { + fprintf(stderr, "Server PSK hint was non-null.\n"); + return 0; + } + } else if (hint == nullptr || + strcmp(hint, config->psk_identity.c_str()) != 0) { + fprintf(stderr, "Server PSK hint did not match.\n"); + return 0; + } + + // Account for the trailing '\0' for the identity. + if (config->psk_identity.size() >= max_identity_len || + config->psk.size() > max_psk_len) { + fprintf(stderr, "PSK buffers too small\n"); + return 0; + } + + BUF_strlcpy(out_identity, config->psk_identity.c_str(), max_identity_len); + OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static unsigned PskServerCallback(SSL *ssl, const char *identity, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetTestConfig(ssl); + + if (strcmp(identity, config->psk_identity.c_str()) != 0) { + fprintf(stderr, "Client PSK identity did not match.\n"); + return 0; + } + + if (config->psk.size() > max_psk_len) { + fprintf(stderr, "PSK buffers too small\n"); + return 0; + } + + OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) { + const TestConfig *config = GetTestConfig(ssl); + if (!CheckVerifyCallback(ssl)) { + return ssl_verify_invalid; + } + + if (config->async && !GetTestState(ssl)->custom_verify_ready) { + return ssl_verify_retry; + } + + GetTestState(ssl)->cert_verified = true; + if (config->verify_fail) { + return ssl_verify_invalid; + } + + return ssl_verify_ok; +} + +static int CertCallback(SSL *ssl, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + + // Check the CertificateRequest metadata is as expected. + if (!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) { + return -1; + } + + if (config->fail_cert_callback) { + return 0; + } + + // The certificate will be installed via other means. + if (!config->async || config->use_early_callback) { + return 1; + } + + if (!GetTestState(ssl)->cert_ready) { + return -1; + } + if (!InstallCertificate(ssl)) { + return 0; + } + return 1; +} + +bssl::UniquePtr<SSL> TestConfig::NewSSL( + SSL_CTX *ssl_ctx, SSL_SESSION *session, bool is_resume, + std::unique_ptr<TestState> test_state) const { + bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx)); + if (!ssl) { + return nullptr; + } + + if (!SetTestConfig(ssl.get(), this)) { + return nullptr; + } + if (test_state != nullptr) { + if (!SetTestState(ssl.get(), std::move(test_state))) { + return nullptr; + } + GetTestState(ssl.get())->is_resume = is_resume; + } + + if (fallback_scsv && !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) { + return nullptr; + } + // Install the certificate synchronously if nothing else will handle it. + if (!use_early_callback && !use_old_client_cert_callback && !async && + !InstallCertificate(ssl.get())) { + return nullptr; + } + if (!use_old_client_cert_callback) { + SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); + } + int mode = SSL_VERIFY_NONE; + if (require_any_client_certificate) { + mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + if (verify_peer) { + mode = SSL_VERIFY_PEER; + } + if (verify_peer_if_no_obc) { + // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client + // certificates were requested is easy. + mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + if (use_custom_verify_callback) { + SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback); + } else if (mode != SSL_VERIFY_NONE) { + SSL_set_verify(ssl.get(), mode, NULL); + } + if (false_start) { + SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); + } + if (cbc_record_splitting) { + SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING); + } + if (partial_write) { + SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE); + } + if (no_tls13) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3); + } + if (no_tls12) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2); + } + if (no_tls11) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1); + } + if (no_tls1) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1); + } + if (!expected_channel_id.empty() || enable_channel_id) { + SSL_set_tls_channel_id_enabled(ssl.get(), 1); + } + if (!send_channel_id.empty()) { + SSL_set_tls_channel_id_enabled(ssl.get(), 1); + if (!async) { + // The async case will be supplied by |ChannelIdCallback|. + bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(send_channel_id); + if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) { + return nullptr; + } + } + } + if (!send_token_binding_params.empty()) { + SSL_set_token_binding_params( + ssl.get(), + reinterpret_cast<const uint8_t *>(send_token_binding_params.data()), + send_token_binding_params.length()); + } + if (!host_name.empty() && + !SSL_set_tlsext_host_name(ssl.get(), host_name.c_str())) { + return nullptr; + } + if (!advertise_alpn.empty() && + SSL_set_alpn_protos(ssl.get(), (const uint8_t *)advertise_alpn.data(), + advertise_alpn.size()) != 0) { + return nullptr; + } + if (!psk.empty()) { + SSL_set_psk_client_callback(ssl.get(), PskClientCallback); + SSL_set_psk_server_callback(ssl.get(), PskServerCallback); + } + if (!psk_identity.empty() && + !SSL_use_psk_identity_hint(ssl.get(), psk_identity.c_str())) { + return nullptr; + } + if (!srtp_profiles.empty() && + !SSL_set_srtp_profiles(ssl.get(), srtp_profiles.c_str())) { + return nullptr; + } + if (enable_ocsp_stapling) { + SSL_enable_ocsp_stapling(ssl.get()); + } + if (enable_signed_cert_timestamps) { + SSL_enable_signed_cert_timestamps(ssl.get()); + } + if (min_version != 0 && + !SSL_set_min_proto_version(ssl.get(), (uint16_t)min_version)) { + return nullptr; + } + if (max_version != 0 && + !SSL_set_max_proto_version(ssl.get(), (uint16_t)max_version)) { + return nullptr; + } + if (mtu != 0) { + SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU); + SSL_set_mtu(ssl.get(), mtu); + } + if (install_ddos_callback) { + SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback); + } + SSL_set_shed_handshake_config(ssl.get(), true); + if (renegotiate_once) { + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once); + } + if (renegotiate_freely || forbid_renegotiation_after_handshake) { + // |forbid_renegotiation_after_handshake| will disable renegotiation later. + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); + } + if (renegotiate_ignore) { + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore); + } + if (!check_close_notify) { + SSL_set_quiet_shutdown(ssl.get(), 1); + } + if (p384_only) { + int nid = NID_secp384r1; + if (!SSL_set1_curves(ssl.get(), &nid, 1)) { + return nullptr; + } + } + if (enable_all_curves) { + static const int kAllCurves[] = { + NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1, + NID_secp521r1, NID_X25519, + }; + if (!SSL_set1_curves(ssl.get(), kAllCurves, + OPENSSL_ARRAY_SIZE(kAllCurves))) { + return nullptr; + } + } + if (initial_timeout_duration_ms > 0) { + DTLSv1_set_initial_timeout_duration(ssl.get(), initial_timeout_duration_ms); + } + if (max_cert_list > 0) { + SSL_set_max_cert_list(ssl.get(), max_cert_list); + } + if (retain_only_sha256_client_cert) { + SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1); + } + if (max_send_fragment > 0) { + SSL_set_max_send_fragment(ssl.get(), max_send_fragment); + } + if (dummy_pq_padding_len > 0 && + !SSL_set_dummy_pq_padding_size(ssl.get(), dummy_pq_padding_len)) { + return nullptr; + } + if (!quic_transport_params.empty()) { + if (!SSL_set_quic_transport_params( + ssl.get(), + reinterpret_cast<const uint8_t *>(quic_transport_params.data()), + quic_transport_params.size())) { + return nullptr; + } + } + + if (session != NULL) { + if (!is_server) { + if (SSL_set_session(ssl.get(), session) != 1) { + return nullptr; + } + } else if (async) { + // The internal session cache is disabled, so install the session + // manually. + SSL_SESSION_up_ref(session); + GetTestState(ssl.get())->pending_session.reset(session); + } + } + + if (SSL_get_current_cipher(ssl.get()) != nullptr) { + fprintf(stderr, "non-null cipher before handshake\n"); + return nullptr; + } + + return ssl; +}
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index d380007..930100e 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h
@@ -18,6 +18,10 @@ #include <string> #include <vector> +#include <openssl/base.h> +#include <openssl/x509.h> + +#include "test_state.h" struct TestConfig { int port = 0; @@ -160,10 +164,27 @@ bool decline_ocsp_callback = false; bool fail_ocsp_callback = false; bool install_cert_compression_algs = false; + + bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx) const; + + bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, SSL_SESSION *session, + bool is_resume, + std::unique_ptr<TestState> test_state) const; }; bool ParseConfig(int argc, char **argv, TestConfig *out_initial, TestConfig *out_resume, TestConfig *out_retry); +bool SetTestConfig(SSL *ssl, const TestConfig *config); + +const TestConfig *GetTestConfig(const SSL *ssl); + +bool MoveTestConfig(SSL *dest, SSL *src); + +bool LoadCertificate(bssl::UniquePtr<X509> *out_x509, + bssl::UniquePtr<STACK_OF(X509)> *out_chain, + const std::string &file); + +bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file); #endif // HEADER_TEST_CONFIG
diff --git a/ssl/test/test_state.cc b/ssl/test/test_state.cc new file mode 100644 index 0000000..14bd4a1 --- /dev/null +++ b/ssl/test/test_state.cc
@@ -0,0 +1,86 @@ +/* Copyright (c) 2018, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "test_state.h" + +#include <openssl/ssl.h> + +#include "../../crypto/internal.h" +#include "../internal.h" + +static CRYPTO_once_t g_once = CRYPTO_ONCE_INIT; +static int g_state_index = 0; +// Some code treats the zero time special, so initialize the clock to a +// non-zero time. +static timeval g_clock = { 1234, 1234 }; + +static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int index, long argl, void *argp) { + delete ((TestState *)ptr); +} + +static void init_once() { + g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree); + if (g_state_index < 0) { + abort(); + } +} + +struct timeval *GetClock() { + CRYPTO_once(&g_once, init_once); + return &g_clock; +} + +void AdvanceClock(unsigned seconds) { + CRYPTO_once(&g_once, init_once); + g_clock.tv_sec += seconds; +} + +bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) { + CRYPTO_once(&g_once, init_once); + // |SSL_set_ex_data| takes ownership of |state| only on success. + if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) { + state.release(); + return true; + } + return false; +} + +TestState *GetTestState(const SSL *ssl) { + CRYPTO_once(&g_once, init_once); + return (TestState *)SSL_get_ex_data(ssl, g_state_index); +} + +bool MoveTestState(SSL *dest, SSL *src) { + TestState *state = GetTestState(src); + if (!SSL_set_ex_data(src, g_state_index, nullptr) || + !SSL_set_ex_data(dest, g_state_index, state)) { + return false; + } + + return true; +} + +static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) { + SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param); + bssl::UniquePtr<SSL_SESSION> new_session = bssl::SSL_SESSION_dup( + session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET); + if (new_session != nullptr) { + SSL_CTX_add_session(ctx, new_session.get()); + } +} + +void CopySessions(SSL_CTX *dst, const SSL_CTX *src) { + lh_SSL_SESSION_doall_arg(src->sessions, ssl_ctx_add_session, dst); +}
diff --git a/ssl/test/test_state.h b/ssl/test/test_state.h new file mode 100644 index 0000000..3fe2972 --- /dev/null +++ b/ssl/test/test_state.h
@@ -0,0 +1,67 @@ +/* Copyright (c) 2018, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef HEADER_TEST_STATE +#define HEADER_TEST_STATE + +#include <memory> +#include <string> +#include <vector> + +#include <openssl/base.h> + +struct TestState { + // async_bio is async BIO which pauses reads and writes. + BIO *async_bio = nullptr; + // packeted_bio is the packeted BIO which simulates read timeouts. + BIO *packeted_bio = nullptr; + bssl::UniquePtr<EVP_PKEY> channel_id; + bool cert_ready = false; + bssl::UniquePtr<SSL_SESSION> session; + bssl::UniquePtr<SSL_SESSION> pending_session; + bool early_callback_called = false; + bool handshake_done = false; + // private_key is the underlying private key used when testing custom keys. + bssl::UniquePtr<EVP_PKEY> private_key; + std::vector<uint8_t> private_key_result; + // private_key_retries is the number of times an asynchronous private key + // operation has been retried. + unsigned private_key_retries = 0; + bool got_new_session = false; + bssl::UniquePtr<SSL_SESSION> new_session; + bool ticket_decrypt_done = false; + bool alpn_select_done = false; + bool is_resume = false; + bool early_callback_ready = false; + bool custom_verify_ready = false; + std::string msg_callback_text; + bool msg_callback_ok = true; + // cert_verified is true if certificate verification has been driven to + // completion. This tests that the callback is not called again after this. + bool cert_verified = false; +}; + +bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state); + +TestState *GetTestState(const SSL *ssl); + +bool MoveTestState(SSL *dest, SSL *src); + +struct timeval *GetClock(); + +void AdvanceClock(unsigned seconds); + +void CopySessions(SSL_CTX *dest, const SSL_CTX *src); + +#endif // HEADER_TEST_STATE