// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC #include #include #include #include #include "internal.h" #include "ssl_common_test.h" #include "../crypto/test/test_util.h" BSSL_NAMESPACE_BEGIN // SSLVersionTest executes its test cases under all available protocol versions. // Test cases call |Connect| to create a connection using context objects with // the protocol version fixed to the current version under test. class SSLVersionTest : public ::testing::TestWithParam<::std::tuple> { protected: SSLVersionTest() : cert_(GetTestCertificate()), key_(GetTestKey()) {} void SetUp() { ResetContexts(); } bssl::UniquePtr CreateContext() const { const SSL_METHOD *method = is_dtls() ? DTLS_method() : TLS_method(); bssl::UniquePtr ctx(SSL_CTX_new(method)); if (!ctx || !SSL_CTX_set_min_proto_version(ctx.get(), version()) || !SSL_CTX_set_max_proto_version(ctx.get(), version())) { return nullptr; } if (enable_read_ahead()) { SSL_CTX_set_read_ahead(ctx.get(), 1); SSL_CTX_set_default_read_buffer_len(ctx.get(), read_ahead_buffer_size()); } return ctx; } void ResetContexts() { ASSERT_TRUE(cert_); ASSERT_TRUE(key_); client_ctx_ = CreateContext(); ASSERT_TRUE(client_ctx_); server_ctx_ = CreateContext(); ASSERT_TRUE(server_ctx_); // Set up a server cert. Client certs can be set up explicitly. ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); } bool UseCertAndKey(SSL_CTX *ctx) const { return SSL_CTX_use_certificate(ctx, cert_.get()) && SSL_CTX_use_PrivateKey(ctx, key_.get()); } bool Connect(const ClientConfig &config = ClientConfig()) { bool connected = ConnectClientAndServer( &client_, &server_, client_ctx_.get(), server_ctx_.get(), config, shed_handshake_config_); if (connected) { TransferServerSSL(); } return connected; } VersionParam getVersionParam() const { return std::get<0>(GetParam()); } void TransferServerSSL() { if (!getVersionParam().transfer_ssl) { return; } // |server_| is reset to hold the transferred SSL. TransferSSL(&server_, server_ctx_.get(), nullptr); } uint16_t version() const { return getVersionParam().version; } bool is_dtls() const { return getVersionParam().ssl_method == VersionParam::is_dtls; } size_t read_ahead_buffer_size() const { return std::get<1>(GetParam()); } bool enable_read_ahead() const { return read_ahead_buffer_size() != 0; } void CheckCounterInit() { EXPECT_EQ(SSL_CTX_sess_connect(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_connect(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_connect_good(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_connect_good(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_connect_renegotiate(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_connect_renegotiate(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept_renegotiate(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept_renegotiate(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept_good(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_accept_good(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_hits(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_cb_hits(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_cb_hits(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_misses(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_misses(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_timeouts(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_timeouts(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_cache_full(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_cache_full(server_ctx_.get()), 0); } bool shed_handshake_config_ = true; bssl::UniquePtr client_, server_; bssl::UniquePtr server_ctx_, client_ctx_; bssl::UniquePtr cert_; bssl::UniquePtr key_; }; INSTANTIATE_TEST_SUITE_P( WithVersion, SSLVersionTest, testing::Combine(::testing::ValuesIn(kAllVersions), testing::Values(0, 128, 512, 8192, 65535, 131072)), [](const testing::TestParamInfo<::std::tuple> &test_info) { std::string test_name = std::string(std::get<0>(test_info.param).name) + "_BufferSize_"; return test_name + std::to_string(std::get<1>(test_info.param)); }); TEST_P(SSLVersionTest, SequenceNumber) { CheckCounterInit(); ASSERT_TRUE(Connect()); // Counters should count one two-way successful connection. EXPECT_EQ(SSL_CTX_sess_connect(client_ctx_.get()), 1); EXPECT_EQ(SSL_CTX_sess_connect_good(client_ctx_.get()), 1); EXPECT_EQ(SSL_CTX_sess_accept(server_ctx_.get()), 1); EXPECT_EQ(SSL_CTX_sess_accept_good(server_ctx_.get()), 1); // Drain any post-handshake messages to ensure there are no unread records // on either end. ASSERT_TRUE(FlushNewSessionTickets(client_.get(), server_.get())); uint64_t client_read_seq = SSL_get_read_sequence(client_.get()); uint64_t client_write_seq = SSL_get_write_sequence(client_.get()); uint64_t server_read_seq = SSL_get_read_sequence(server_.get()); uint64_t server_write_seq = SSL_get_write_sequence(server_.get()); if (is_dtls()) { // Both client and server must be at epoch 1. EXPECT_EQ(EpochFromSequence(client_read_seq), 1); EXPECT_EQ(EpochFromSequence(client_write_seq), 1); EXPECT_EQ(EpochFromSequence(server_read_seq), 1); EXPECT_EQ(EpochFromSequence(server_write_seq), 1); // The next record to be written should exceed the largest received. EXPECT_GT(client_write_seq, server_read_seq); EXPECT_GT(server_write_seq, client_read_seq); } else { // The next record to be written should equal the next to be received. EXPECT_EQ(client_write_seq, server_read_seq); EXPECT_EQ(server_write_seq, client_read_seq); } // Send a record from client to server. uint8_t byte = 0; EXPECT_EQ(SSL_write(client_.get(), &byte, 1), 1); EXPECT_EQ(SSL_read(server_.get(), &byte, 1), 1); // The client write and server read sequence numbers should have // incremented. EXPECT_EQ(client_write_seq + 1, SSL_get_write_sequence(client_.get())); EXPECT_EQ(server_read_seq + 1, SSL_get_read_sequence(server_.get())); } TEST_P(SSLVersionTest, OneSidedShutdown) { // SSL_shutdown is a no-op in DTLS. if (is_dtls()) { return; } ASSERT_TRUE(Connect()); // Shut down half the connection. |SSL_shutdown| will return 0 to signal only // one side has shut down. ASSERT_EQ(SSL_shutdown(client_.get()), 0); // Reading from the server should consume the EOF. uint8_t byte = 0; ASSERT_EQ(SSL_read(server_.get(), &byte, 1), 0); ASSERT_EQ(SSL_get_error(server_.get(), 0), SSL_ERROR_ZERO_RETURN); // However, the server may continue to write data and then shut down the // connection. byte = 42; ASSERT_EQ(SSL_write(server_.get(), &byte, 1), 1); ASSERT_EQ(SSL_read(client_.get(), &byte, 1), 1); ASSERT_EQ(byte, 42); // The server may then shutdown the connection. EXPECT_EQ(SSL_shutdown(server_.get()), 1); EXPECT_EQ(SSL_shutdown(client_.get()), 1); } // Test that, after calling |SSL_shutdown|, |SSL_write| fails. TEST_P(SSLVersionTest, WriteAfterShutdown) { ASSERT_TRUE(Connect()); for (SSL *ssl : {client_.get(), server_.get()}) { SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client"); bssl::UniquePtr mem(BIO_new(BIO_s_mem())); ASSERT_TRUE(mem); SSL_set0_wbio(ssl, bssl::UpRef(mem).release()); // Shut down half the connection. |SSL_shutdown| will return 0 to signal // only one side has shut down. ASSERT_EQ(SSL_shutdown(ssl), 0); // |ssl| should have written an alert to the transport. const uint8_t *unused = nullptr; size_t len = 0; ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_NE(0u, len); EXPECT_TRUE(BIO_reset(mem.get())); // Writing should fail. EXPECT_EQ(-1, SSL_write(ssl, "a", 1)); // Nothing should be written to the transport. ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_EQ(0u, len); } } // Test that, after sending a fatal alert in a failed |SSL_read|, |SSL_write| // fails. TEST_P(SSLVersionTest, WriteAfterReadSentFatalAlert) { // Decryption failures are not fatal in DTLS. if (is_dtls()) { return; } ASSERT_TRUE(Connect()); // Save the write |BIO|s as the test will overwrite them. bssl::UniquePtr client_wbio = bssl::UpRef(SSL_get_wbio(client_.get())); bssl::UniquePtr server_wbio = bssl::UpRef(SSL_get_wbio(server_.get())); for (bool test_server : {false, true}) { SCOPED_TRACE(test_server ? "server" : "client"); SSL *ssl = test_server ? server_.get() : client_.get(); BIO *other_wbio = test_server ? client_wbio.get() : server_wbio.get(); bssl::UniquePtr mem(BIO_new(BIO_s_mem())); ASSERT_TRUE(mem); SSL_set0_wbio(ssl, bssl::UpRef(mem).release()); // Read an invalid record from the peer. static const uint8_t kInvalidRecord[] = "invalid record"; EXPECT_EQ(int{sizeof(kInvalidRecord)}, BIO_write(other_wbio, kInvalidRecord, sizeof(kInvalidRecord))); char buf[256]; EXPECT_EQ(-1, SSL_read(ssl, buf, sizeof(buf))); // |ssl| should have written an alert to the transport. const uint8_t *unused = nullptr; size_t len = 0; ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_NE(0u, len); EXPECT_TRUE(BIO_reset(mem.get())); // Writing should fail. EXPECT_EQ(-1, SSL_write(ssl, "a", 1)); // Nothing should be written to the transport. ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_EQ(0u, len); } } static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { return 1; } // Test that, after sending a fatal alert from the handshake, |SSL_write| fails. TEST_P(SSLVersionTest, WriteAfterHandshakeSentFatalAlert) { for (bool test_server : {false, true}) { SCOPED_TRACE(test_server ? "server" : "client"); bssl::UniquePtr ssl( SSL_new(test_server ? server_ctx_.get() : client_ctx_.get())); ASSERT_TRUE(ssl); if (test_server) { SSL_set_accept_state(ssl.get()); } else { SSL_set_connect_state(ssl.get()); } std::vector invalid; if (is_dtls()) { // In DTLS, invalid records are discarded. To cause the handshake to fail, // use a valid handshake record with invalid contents. invalid.push_back(SSL3_RT_HANDSHAKE); invalid.push_back(DTLS1_VERSION >> 8); invalid.push_back(DTLS1_VERSION & 0xff); // epoch and sequence_number for (int i = 0; i < 8; i++) { invalid.push_back(0); } // A one-byte fragment is invalid. invalid.push_back(0); invalid.push_back(1); // Arbitrary contents. invalid.push_back(0); } else { invalid = {'i', 'n', 'v', 'a', 'l', 'i', 'd'}; } bssl::UniquePtr rbio(BIO_new_mem_buf(invalid.data(), invalid.size())); ASSERT_TRUE(rbio); SSL_set0_rbio(ssl.get(), rbio.release()); bssl::UniquePtr mem(BIO_new(BIO_s_mem())); ASSERT_TRUE(mem); SSL_set0_wbio(ssl.get(), bssl::UpRef(mem).release()); // The handshake should fail. EXPECT_EQ(-1, SSL_do_handshake(ssl.get())); EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), -1)); uint32_t err = ERR_get_error(); // |ssl| should have written an alert (and, in the client's case, a // ClientHello) to the transport. const uint8_t *unused = nullptr; size_t len = 0; ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_NE(0u, len); EXPECT_TRUE(BIO_reset(mem.get())); // Writing should fail, with the same error as the handshake. EXPECT_EQ(-1, SSL_write(ssl.get(), "a", 1)); EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), -1)); EXPECT_EQ(err, ERR_get_error()); // Nothing should be written to the transport. ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len)); EXPECT_EQ(0u, len); } } TEST_P(SSLVersionTest, GetPeerCertificate) { ASSERT_TRUE(UseCertAndKey(client_ctx_.get())); // Configure both client and server to accept any certificate. SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_cert_verify_callback(client_ctx_.get(), VerifySucceed, NULL); SSL_CTX_set_verify(server_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_cert_verify_callback(server_ctx_.get(), VerifySucceed, NULL); ASSERT_TRUE(Connect()); // Client and server should both see the leaf certificate. bssl::UniquePtr peer(SSL_get_peer_certificate(server_.get())); ASSERT_TRUE(peer); ASSERT_EQ(X509_cmp(cert_.get(), peer.get()), 0); peer.reset(SSL_get_peer_certificate(client_.get())); ASSERT_TRUE(peer); ASSERT_EQ(X509_cmp(cert_.get(), peer.get()), 0); // However, for historical reasons, the X509 chain includes the leaf on the // client, but does not on the server. EXPECT_EQ(sk_X509_num(SSL_get_peer_cert_chain(client_.get())), 1u); EXPECT_EQ(sk_CRYPTO_BUFFER_num(SSL_get0_peer_certificates(client_.get())), 1u); EXPECT_EQ(sk_X509_num(SSL_get_peer_cert_chain(server_.get())), 0u); EXPECT_EQ(sk_CRYPTO_BUFFER_num(SSL_get0_peer_certificates(server_.get())), 1u); } TEST_P(SSLVersionTest, NoPeerCertificate) { SSL_CTX_set_verify(server_ctx_.get(), SSL_VERIFY_PEER, nullptr); SSL_CTX_set_cert_verify_callback(server_ctx_.get(), VerifySucceed, NULL); SSL_CTX_set_cert_verify_callback(client_ctx_.get(), VerifySucceed, NULL); ASSERT_TRUE(Connect()); // Server should not see a peer certificate. bssl::UniquePtr peer(SSL_get_peer_certificate(server_.get())); ASSERT_FALSE(peer); ASSERT_FALSE(SSL_get0_peer_certificates(server_.get())); } TEST_P(SSLVersionTest, RetainOnlySHA256OfCerts) { uint8_t *cert_der = NULL; int cert_der_len = i2d_X509(cert_.get(), &cert_der); ASSERT_GE(cert_der_len, 0); bssl::UniquePtr free_cert_der(cert_der); uint8_t cert_sha256[SHA256_DIGEST_LENGTH]; SHA256(cert_der, cert_der_len, cert_sha256); ASSERT_TRUE(UseCertAndKey(client_ctx_.get())); // Configure both client and server to accept any certificate, but the // server must retain only the SHA-256 of the peer. SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_verify(server_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_cert_verify_callback(client_ctx_.get(), VerifySucceed, NULL); SSL_CTX_set_cert_verify_callback(server_ctx_.get(), VerifySucceed, NULL); SSL_CTX_set_retain_only_sha256_of_client_certs(server_ctx_.get(), 1); ASSERT_TRUE(Connect()); // The peer certificate has been dropped. bssl::UniquePtr peer(SSL_get_peer_certificate(server_.get())); EXPECT_FALSE(peer); SSL_SESSION *session = SSL_get_session(server_.get()); EXPECT_TRUE(SSL_SESSION_has_peer_sha256(session)); const uint8_t *peer_sha256 = nullptr; size_t peer_sha256_len = 0; SSL_SESSION_get0_peer_sha256(session, &peer_sha256, &peer_sha256_len); EXPECT_EQ(Bytes(cert_sha256), Bytes(peer_sha256, peer_sha256_len)); } static int SwitchSessionIDContextSNI(SSL *ssl, int *out_alert, void *arg) { static const uint8_t kContext[] = {3}; if (!SSL_set_session_id_context(ssl, kContext, sizeof(kContext))) { return SSL_TLSEXT_ERR_ALERT_FATAL; } return SSL_TLSEXT_ERR_OK; } TEST_P(SSLVersionTest, SessionIDContext) { static const uint8_t kContext1[] = {1}; static const uint8_t kContext2[] = {2}; ASSERT_TRUE(SSL_CTX_set_session_id_context(server_ctx_.get(), kContext1, sizeof(kContext1))); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), true /* expect session reused */)); // Change the session ID context. ASSERT_TRUE(SSL_CTX_set_session_id_context(server_ctx_.get(), kContext2, sizeof(kContext2))); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* expect session not reused */)); // Change the session ID context back and install an SNI callback to switch // it. ASSERT_TRUE(SSL_CTX_set_session_id_context(server_ctx_.get(), kContext1, sizeof(kContext1))); SSL_CTX_set_tlsext_servername_callback(server_ctx_.get(), SwitchSessionIDContextSNI); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* expect session not reused */)); // Switch the session ID context with the early callback instead. SSL_CTX_set_tlsext_servername_callback(server_ctx_.get(), nullptr); SSL_CTX_set_select_certificate_cb( server_ctx_.get(), [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t { static const uint8_t kContext[] = {3}; if (!SSL_set_session_id_context(client_hello->ssl, kContext, sizeof(kContext))) { return ssl_select_cert_error; } return ssl_select_cert_success; }); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* expect session not reused */)); } static timeval g_current_time; static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { *out_clock = g_current_time; } static bssl::UniquePtr ExpectSessionRenewed(SSL_CTX *client_ctx, SSL_CTX *server_ctx, SSL_SESSION *session) { g_last_session = nullptr; SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession); bssl::UniquePtr client, server; ClientConfig config; config.session = session; if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx, config) || !FlushNewSessionTickets(client.get(), server.get())) { fprintf(stderr, "Failed to connect client and server.\n"); return nullptr; } if (SSL_session_reused(client.get()) != SSL_session_reused(server.get())) { fprintf(stderr, "Client and server were inconsistent.\n"); return nullptr; } if (!SSL_session_reused(client.get())) { fprintf(stderr, "Session was not reused.\n"); return nullptr; } SSL_CTX_sess_set_new_cb(client_ctx, nullptr); if (!g_last_session) { fprintf(stderr, "Client did not receive a renewed session.\n"); return nullptr; } return std::move(g_last_session); } static const size_t kTicketKeyLen = 48; static void ExpectTicketKeyChanged(SSL_CTX *ctx, uint8_t *inout_key, bool changed) { uint8_t new_key[kTicketKeyLen]; // May return 0, 1 or 48. ASSERT_EQ(SSL_CTX_get_tlsext_ticket_keys(ctx, new_key, kTicketKeyLen), 1); if (changed) { ASSERT_NE(Bytes(inout_key, kTicketKeyLen), Bytes(new_key)); } else { ASSERT_EQ(Bytes(inout_key, kTicketKeyLen), Bytes(new_key)); } OPENSSL_memcpy(inout_key, new_key, kTicketKeyLen); } static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, int encrypt) { 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; } // Returning two from the callback in decrypt mode renews the // session in TLS 1.2 and below. return encrypt ? 1 : 2; } static bool GetServerTicketTime(long *out, const SSL_SESSION *session) { const uint8_t *ticket = nullptr; size_t ticket_len = 0; SSL_SESSION_get0_ticket(session, &ticket, &ticket_len); if (ticket_len < 16 + 16 + SHA256_DIGEST_LENGTH) { return false; } const uint8_t *ciphertext = ticket + 16 + 16; size_t len = ticket_len - 16 - 16 - SHA256_DIGEST_LENGTH; std::unique_ptr plaintext(new uint8_t[len]); #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) // Fuzzer-mode tickets are unencrypted. OPENSSL_memcpy(plaintext.get(), ciphertext, len); #else static const uint8_t kZeros[16] = {0}; const uint8_t *iv = ticket + 16; bssl::ScopedEVP_CIPHER_CTX ctx; int len1 = 0, len2 = 0; if (len > INT_MAX || !EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, kZeros, iv) || !EVP_DecryptUpdate(ctx.get(), plaintext.get(), &len1, ciphertext, static_cast(len)) || !EVP_DecryptFinal_ex(ctx.get(), plaintext.get() + len1, &len2)) { return false; } len = static_cast(len1 + len2); #endif bssl::UniquePtr ssl_ctx(SSL_CTX_new(TLS_method())); if (!ssl_ctx) { return false; } bssl::UniquePtr server_session( SSL_SESSION_from_bytes(plaintext.get(), len, ssl_ctx.get())); if (!server_session) { return false; } *out = SSL_SESSION_get_time(server_session.get()); return true; } TEST_P(SSLVersionTest, SessionTimeout) { for (bool server_test : {false, true}) { SCOPED_TRACE(server_test); ASSERT_NO_FATAL_FAILURE(ResetContexts()); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); static const time_t kStartTime = 1000; g_current_time.tv_sec = kStartTime; // We are willing to use a longer lifetime for TLS 1.3 sessions as // resumptions still perform ECDHE. const time_t timeout = version() == TLS1_3_VERSION ? SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT : SSL_DEFAULT_SESSION_TIMEOUT; // Both client and server must enforce session timeouts. We configure the // other side with a frozen clock so it never expires tickets. if (server_test) { SSL_CTX_set_current_time_cb(client_ctx_.get(), FrozenTimeCallback); SSL_CTX_set_current_time_cb(server_ctx_.get(), CurrentTimeCallback); } else { SSL_CTX_set_current_time_cb(client_ctx_.get(), CurrentTimeCallback); SSL_CTX_set_current_time_cb(server_ctx_.get(), FrozenTimeCallback); } // Configure a ticket callback which renews tickets. SSL_CTX_set_tlsext_ticket_key_cb(server_ctx_.get(), RenewTicketCallback); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); // Advance the clock just behind the timeout. g_current_time.tv_sec += timeout - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), true /* expect session reused */)); // Advance the clock one more second. g_current_time.tv_sec++; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* expect session not reused */)); // Rewind the clock to before the session was minted. g_current_time.tv_sec = kStartTime - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* expect session not reused */)); // Renew the session 10 seconds before expiration. time_t new_start_time = kStartTime + timeout - 10; g_current_time.tv_sec = new_start_time; bssl::UniquePtr new_session = ExpectSessionRenewed( client_ctx_.get(), server_ctx_.get(), session.get()); ASSERT_TRUE(new_session); // This new session is not the same object as before. EXPECT_NE(session.get(), new_session.get()); // Check the sessions have timestamps measured from issuance. long session_time = 0; if (server_test) { ASSERT_TRUE(GetServerTicketTime(&session_time, new_session.get())); } else { session_time = SSL_SESSION_get_time(new_session.get()); } ASSERT_EQ(session_time, g_current_time.tv_sec); if (version() == TLS1_3_VERSION) { // Renewal incorporates fresh key material in TLS 1.3, so we extend the // lifetime TLS 1.3. g_current_time.tv_sec = new_start_time + timeout - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), true /* expect session reused */)); // The new session expires after the new timeout. g_current_time.tv_sec = new_start_time + timeout + 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), false /* expect session ot reused */)); // Renew the session until it begins just past the auth timeout. time_t auth_end_time = kStartTime + SSL_DEFAULT_SESSION_AUTH_TIMEOUT; while (new_start_time < auth_end_time - 1000) { // Get as close as possible to target start time. new_start_time = std::min(auth_end_time - 1000, new_start_time + timeout - 1); g_current_time.tv_sec = new_start_time; new_session = ExpectSessionRenewed(client_ctx_.get(), server_ctx_.get(), new_session.get()); ASSERT_TRUE(new_session); } // Now the session's lifetime is bound by the auth timeout. g_current_time.tv_sec = auth_end_time - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), true /* expect session reused */)); g_current_time.tv_sec = auth_end_time + 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), false /* expect session ot reused */)); } else { // The new session is usable just before the old expiration. g_current_time.tv_sec = kStartTime + timeout - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), true /* expect session reused */)); // Renewal does not extend the lifetime, so it is not usable beyond the // old expiration. g_current_time.tv_sec = kStartTime + timeout + 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), false /* expect session not reused */)); } } } TEST_P(SSLVersionTest, DefaultTicketKeyInitialization) { // Do not make static and const. See t/P118709392. // It can trigger https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189 leading // to transient errors. uint8_t kZeroKey[kTicketKeyLen] = {0}; uint8_t ticket_key[kTicketKeyLen]; ASSERT_EQ(1, SSL_CTX_get_tlsext_ticket_keys(server_ctx_.get(), ticket_key, kTicketKeyLen)); ASSERT_NE(0, OPENSSL_memcmp(ticket_key, kZeroKey, kTicketKeyLen)); } TEST_P(SSLVersionTest, DefaultTicketKeyRotation) { static const time_t kStartTime = 1001; g_current_time.tv_sec = kStartTime; // We use session reuse as a proxy for ticket decryption success, hence // disable session timeouts. SSL_CTX_set_timeout(server_ctx_.get(), std::numeric_limits::max()); SSL_CTX_set_session_psk_dhe_timeout(server_ctx_.get(), std::numeric_limits::max()); SSL_CTX_set_current_time_cb(client_ctx_.get(), FrozenTimeCallback); SSL_CTX_set_current_time_cb(server_ctx_.get(), CurrentTimeCallback); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_OFF); // Initialize ticket_key with the current key and check that it was // initialized to something, not all zeros. uint8_t ticket_key[kTicketKeyLen] = {0}; TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key, true /* changed */)); // Verify ticket resumption actually works. bssl::UniquePtr client, server; bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), true /* reused */)); // Advance time to just before key rotation. g_current_time.tv_sec += SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL - 1; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), true /* reused */)); TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key, false /* NOT changed */)); // Force key rotation. g_current_time.tv_sec += 1; bssl::UniquePtr new_session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key, true /* changed */)); // Resumption with both old and new ticket should work. TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), true /* reused */)); TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), true /* reused */)); TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key, false /* NOT changed */)); // Force key rotation again. Resumption with the old ticket now fails. g_current_time.tv_sec += SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL; TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), session.get(), false /* NOT reused */)); TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key, true /* changed */)); // But resumption with the newer session still works. TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), new_session.get(), true /* reused */)); } static int SwitchContext(SSL *ssl, int *out_alert, void *arg) { SSL_CTX *ctx = reinterpret_cast(arg); SSL_set_SSL_CTX(ssl, ctx); return SSL_TLSEXT_ERR_OK; } TEST_P(SSLVersionTest, SNICallback) { bssl::UniquePtr cert2 = GetECDSATestCertificate(); ASSERT_TRUE(cert2); bssl::UniquePtr key2 = GetECDSATestKey(); ASSERT_TRUE(key2); // Test that switching the |SSL_CTX| at the SNI callback behaves correctly. static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256; static const uint8_t kSCTList[] = {0, 6, 0, 4, 5, 6, 7, 8}; static const uint8_t kOCSPResponse[] = {1, 2, 3, 4}; bssl::UniquePtr server_ctx2 = CreateContext(); ASSERT_TRUE(server_ctx2); ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx2.get(), cert2.get())); ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get())); ASSERT_TRUE(SSL_CTX_set_signed_cert_timestamp_list( server_ctx2.get(), kSCTList, sizeof(kSCTList))); ASSERT_TRUE(SSL_CTX_set_ocsp_response(server_ctx2.get(), kOCSPResponse, sizeof(kOCSPResponse))); // Historically signing preferences would be lost in some cases with the // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure // this doesn't happen when |version| is TLS 1.2, configure the private // key to only sign SHA-256. ASSERT_TRUE(SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(), &kECDSAWithSHA256, 1)); SSL_CTX_set_tlsext_servername_callback(server_ctx_.get(), SwitchContext); SSL_CTX_set_tlsext_servername_arg(server_ctx_.get(), server_ctx2.get()); SSL_CTX_enable_signed_cert_timestamps(client_ctx_.get()); SSL_CTX_enable_ocsp_stapling(client_ctx_.get()); ASSERT_TRUE(Connect()); // The client should have received |cert2|. bssl::UniquePtr peer(SSL_get_peer_certificate(client_.get())); ASSERT_TRUE(peer); EXPECT_EQ(X509_cmp(peer.get(), cert2.get()), 0); // The client should have received |server_ctx2|'s SCT list. const uint8_t *data = nullptr; size_t len = 0; SSL_get0_signed_cert_timestamp_list(client_.get(), &data, &len); EXPECT_EQ(Bytes(kSCTList), Bytes(data, len)); // The client should have received |server_ctx2|'s OCSP response. SSL_get0_ocsp_response(client_.get(), &data, &len); EXPECT_EQ(Bytes(kOCSPResponse), Bytes(data, len)); } TEST_P(SSLVersionTest, Version) { ASSERT_TRUE(Connect()); EXPECT_EQ(SSL_version(client_.get()), version()); EXPECT_EQ(SSL_version(server_.get()), version()); // Test the version name is reported as expected. const char *version_name = GetVersionName(version()); EXPECT_EQ(strcmp(version_name, SSL_get_version(client_.get())), 0); EXPECT_EQ(strcmp(version_name, SSL_get_version(server_.get())), 0); // Test SSL_SESSION reports the same name. const char *client_name = SSL_SESSION_get_version(SSL_get_session(client_.get())); const char *server_name = SSL_SESSION_get_version(SSL_get_session(server_.get())); EXPECT_EQ(strcmp(version_name, client_name), 0); EXPECT_EQ(strcmp(version_name, server_name), 0); // Client/server version equality asserted above, assert equality for cipher // here. ASSERT_TRUE(SSL_get_current_cipher(client_.get())); ASSERT_TRUE(SSL_get_current_cipher(server_.get())); EXPECT_EQ(SSL_get_current_cipher(client_.get())->id, SSL_get_current_cipher(server_.get())->id); const uint16_t version = SSL_version(client_.get()); if (version == TLS1_2_VERSION || version == TLS1_3_VERSION) { const char *version_str = SSL_get_version(client_.get()); EXPECT_STREQ(version_str, SSL_CIPHER_get_version(SSL_get_current_cipher(client_.get()))); } else if (version == DTLS1_2_VERSION) { // ciphers don't differentiate D/TLS EXPECT_STREQ("TLSv1.2", SSL_CIPHER_get_version(SSL_get_current_cipher(client_.get()))); } else { EXPECT_STREQ("TLSv1/SSLv3", SSL_CIPHER_get_version(SSL_get_current_cipher(client_.get()))); } } // Tests that that |SSL_get_pending_cipher| is available during the ALPN // selection callback. TEST_P(SSLVersionTest, ALPNCipherAvailable) { ASSERT_TRUE(UseCertAndKey(client_ctx_.get())); static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'}; ASSERT_EQ(SSL_CTX_set_alpn_protos(client_ctx_.get(), kALPNProtos, sizeof(kALPNProtos)), 0); // The ALPN callback does not fail the handshake on error, so have the // callback write a boolean. std::pair callback_state(version(), false); SSL_CTX_set_alpn_select_cb( server_ctx_.get(), [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in, unsigned in_len, void *arg) -> int { auto state = reinterpret_cast *>(arg); if (SSL_get_pending_cipher(ssl) != nullptr && SSL_version(ssl) == state->first) { state->second = true; } return SSL_TLSEXT_ERR_NOACK; }, &callback_state); ASSERT_TRUE(Connect()); ASSERT_TRUE(callback_state.second); } TEST_P(SSLVersionTest, SSLClearSessionResumption) { // Skip this for TLS 1.3. TLS 1.3's ticket mechanism is incompatible with this // API pattern. if (version() == TLS1_3_VERSION) { return; } shed_handshake_config_ = false; ASSERT_TRUE(Connect()); EXPECT_FALSE(SSL_session_reused(client_.get())); EXPECT_FALSE(SSL_session_reused(server_.get())); // Reset everything. ASSERT_TRUE(SSL_clear(client_.get())); ASSERT_TRUE(SSL_clear(server_.get())); // Attempt to connect a second time. ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get())); // |SSL_clear| should implicitly offer the previous session to the server. EXPECT_TRUE(SSL_session_reused(client_.get())); EXPECT_TRUE(SSL_session_reused(server_.get())); } TEST_P(SSLVersionTest, SSLClearFailsWithShedding) { shed_handshake_config_ = false; ASSERT_TRUE(Connect()); ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get())); // Reset everything. ASSERT_TRUE(SSL_clear(client_.get())); ASSERT_TRUE(SSL_clear(server_.get())); // Now enable shedding, and connect a second time. shed_handshake_config_ = true; ASSERT_TRUE(Connect()); ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get())); // |SSL_clear| should now fail. ASSERT_FALSE(SSL_clear(client_.get())); ASSERT_FALSE(SSL_clear(server_.get())); } TEST_P(SSLVersionTest, SSLClientCiphers) { // Client ciphers ARE NOT SERIALIZED, so skip tests that rely on transfer or // serialization of |ssl| and accompanying objects under test. if (getVersionParam().transfer_ssl) { return; } EXPECT_FALSE(SSL_get_client_ciphers(client_.get())); EXPECT_FALSE(SSL_get_client_ciphers(server_.get())); shed_handshake_config_ = false; ASSERT_TRUE(Connect()); // The client should still have no view of the server's preferences, but the // server should have seen at least one cipher from the client. EXPECT_FALSE(SSL_get_client_ciphers(client_.get())); EXPECT_GT(sk_SSL_CIPHER_num(SSL_get_client_ciphers(server_.get())), (size_t)0); // With config shedding disabled, clearing |server| shouldn't error and // should reset server's client ciphers ASSERT_TRUE(SSL_clear(server_.get())); EXPECT_FALSE(SSL_get_client_ciphers(server_.get())); shed_handshake_config_ = true; ASSERT_TRUE(Connect()); // These should be unaffected by config shedding EXPECT_FALSE(SSL_get_client_ciphers(client_.get())); EXPECT_GT(sk_SSL_CIPHER_num(SSL_get_client_ciphers(server_.get())), (size_t)0); } TEST_P(SSLVersionTest, AutoChain) { cert_ = GetChainTestCertificate(); ASSERT_TRUE(cert_); key_ = GetChainTestKey(); ASSERT_TRUE(key_); bssl::UniquePtr intermediate = GetChainTestIntermediate(); ASSERT_TRUE(intermediate); ASSERT_TRUE(UseCertAndKey(client_ctx_.get())); ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); // Configure both client and server to accept any certificate. Add // |intermediate| to the cert store. ASSERT_TRUE(X509_STORE_add_cert(SSL_CTX_get_cert_store(client_ctx_.get()), intermediate.get())); ASSERT_TRUE(X509_STORE_add_cert(SSL_CTX_get_cert_store(server_ctx_.get()), intermediate.get())); SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_verify(server_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); SSL_CTX_set_cert_verify_callback(client_ctx_.get(), VerifySucceed, NULL); SSL_CTX_set_cert_verify_callback(server_ctx_.get(), VerifySucceed, NULL); // By default, the client and server should each only send the leaf. ASSERT_TRUE(Connect()); EXPECT_TRUE( ChainsEqual(SSL_get_peer_full_cert_chain(client_.get()), {cert_.get()})); EXPECT_TRUE( ChainsEqual(SSL_get_peer_full_cert_chain(server_.get()), {cert_.get()})); // This test overrides the verification logic with VerifySucceed to always // succeed without actually verifying anything and setting the verified chain // on success EXPECT_EQ(SSL_get0_verified_chain(server_.get()), nullptr); EXPECT_EQ(SSL_get0_verified_chain(client_.get()), nullptr); // If auto-chaining is enabled, then the intermediate is sent. SSL_CTX_clear_mode(client_ctx_.get(), SSL_MODE_NO_AUTO_CHAIN); SSL_CTX_clear_mode(server_ctx_.get(), SSL_MODE_NO_AUTO_CHAIN); ASSERT_TRUE(Connect()); EXPECT_TRUE(ChainsEqual(SSL_get_peer_full_cert_chain(client_.get()), {cert_.get(), intermediate.get()})); EXPECT_TRUE(ChainsEqual(SSL_get_peer_full_cert_chain(server_.get()), {cert_.get(), intermediate.get()})); // Auto-chaining does not override explicitly-configured intermediates. ASSERT_TRUE(SSL_CTX_add1_chain_cert(client_ctx_.get(), cert_.get())); ASSERT_TRUE(SSL_CTX_add1_chain_cert(server_ctx_.get(), cert_.get())); ASSERT_TRUE(Connect()); EXPECT_TRUE(ChainsEqual(SSL_get_peer_full_cert_chain(client_.get()), {cert_.get(), cert_.get()})); EXPECT_TRUE(ChainsEqual(SSL_get_peer_full_cert_chain(server_.get()), {cert_.get(), cert_.get()})); } // These tests test multi-threaded behavior. They are intended to run with // ThreadSanitizer. #if defined(OPENSSL_THREADS) TEST_P(SSLVersionTest, SessionCacheThreads) { SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); if (version() == TLS1_3_VERSION) { // Our TLS 1.3 implementation does not support stateful resumption. ASSERT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); return; } // Establish two client sessions to test with. bssl::UniquePtr session1 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session1); bssl::UniquePtr session2 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session2); auto connect_with_session = [&](SSL_SESSION *session) { ClientConfig config; config.session = session; UniquePtr client, server; ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); }; // Resume sessions in parallel with establishing new ones. { std::vector threads; threads.emplace_back([&] { connect_with_session(nullptr); }); threads.emplace_back([&] { connect_with_session(nullptr); }); threads.emplace_back([&] { connect_with_session(session1.get()); }); threads.emplace_back([&] { connect_with_session(session1.get()); }); threads.emplace_back([&] { connect_with_session(session2.get()); }); threads.emplace_back([&] { connect_with_session(session2.get()); }); for (auto &thread : threads) { thread.join(); } } // Hit the maximum session cache size across multiple threads, to test the // size enforcement logic. size_t over_limit = 2; size_t limit = SSL_CTX_sess_number(server_ctx_.get()) + over_limit; SSL_CTX_sess_set_cache_size(server_ctx_.get(), limit); { std::vector threads; for (int i = 0; i < 4; i++) { threads.emplace_back([&]() { connect_with_session(nullptr); EXPECT_LE(SSL_CTX_sess_number(server_ctx_.get()), limit); }); } for (auto &thread : threads) { thread.join(); } EXPECT_EQ(SSL_CTX_sess_number(server_ctx_.get()), limit); // We go over the cache limit by |over_limit|. EXPECT_EQ(SSL_CTX_sess_cache_full(server_ctx_.get()), (int)over_limit); } // Reset the session cache, this time with a mock clock. ASSERT_NO_FATAL_FAILURE(ResetContexts()); SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_current_time_cb(server_ctx_.get(), CurrentTimeCallback); // Make some sessions at an arbitrary start time. Then expire them. g_current_time.tv_sec = 1000; bssl::UniquePtr expired_session1 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(expired_session1); bssl::UniquePtr expired_session2 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(expired_session2); g_current_time.tv_sec += 100 * SSL_DEFAULT_SESSION_TIMEOUT; session1 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session1); // Every 256 connections, we flush stale sessions from the session cache. Test // this logic is correctly synchronized with other connection attempts. static const int kNumConnections = 256; { std::vector threads; threads.emplace_back([&] { for (int i = 0; i < kNumConnections; i++) { connect_with_session(nullptr); } }); threads.emplace_back([&] { for (int i = 0; i < kNumConnections; i++) { connect_with_session(nullptr); } }); threads.emplace_back([&] { // Never connect with |expired_session2|. The session cache eagerly // removes expired sessions when it sees them. Leaving |expired_session2| // untouched ensures it is instead cleared by periodic flushing. for (int i = 0; i < kNumConnections; i++) { connect_with_session(expired_session1.get()); } }); threads.emplace_back([&] { for (int i = 0; i < kNumConnections; i++) { connect_with_session(session1.get()); } }); for (auto &thread : threads) { thread.join(); } } } TEST_P(SSLVersionTest, SessionTicketThreads) { for (bool renew_ticket : {false, true}) { SCOPED_TRACE(renew_ticket); ASSERT_NO_FATAL_FAILURE(ResetContexts()); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); if (renew_ticket) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx_.get(), RenewTicketCallback); } // Establish two client sessions to test with. bssl::UniquePtr session1 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session1); bssl::UniquePtr session2 = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session2); auto connect_with_session = [&](SSL_SESSION *session) { ClientConfig config; config.session = session; UniquePtr client, server; ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); }; // Resume sessions in parallel with establishing new ones. { std::vector threads; threads.emplace_back([&] { connect_with_session(nullptr); }); threads.emplace_back([&] { connect_with_session(nullptr); }); threads.emplace_back([&] { connect_with_session(session1.get()); }); threads.emplace_back([&] { connect_with_session(session1.get()); }); threads.emplace_back([&] { connect_with_session(session2.get()); }); threads.emplace_back([&] { connect_with_session(session2.get()); }); for (auto &thread : threads) { thread.join(); } } } } #endif // OPENSSL_THREADS TEST_P(SSLVersionTest, SSLWriteRetry) { if (is_dtls()) { return; } for (bool enable_partial_write : {false, true}) { SCOPED_TRACE(enable_partial_write); // Connect a client and server. ASSERT_TRUE(Connect()); if (enable_partial_write) { SSL_set_mode(client_.get(), SSL_MODE_ENABLE_PARTIAL_WRITE); } // Write without reading until the buffer is full and we have an unfinished // write. Keep a count so we may reread it again later. "hello!" will be // written in two chunks, "hello" and "!". char data[] = "hello!"; static const int kChunkLen = 5; // The length of "hello". unsigned count = 0; for (;;) { int ret = SSL_write(client_.get(), data, kChunkLen); if (ret <= 0) { ASSERT_EQ(SSL_get_error(client_.get(), ret), SSL_ERROR_WANT_WRITE); break; } ASSERT_EQ(ret, 5); count++; } // Retrying with the same parameters is legal. ASSERT_EQ( SSL_get_error(client_.get(), SSL_write(client_.get(), data, kChunkLen)), SSL_ERROR_WANT_WRITE); // Retrying with the same buffer but shorter length is not legal. ASSERT_EQ(SSL_get_error(client_.get(), SSL_write(client_.get(), data, kChunkLen - 1)), SSL_ERROR_SSL); ASSERT_TRUE(ExpectSingleError(ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY)); // Retrying with a different buffer pointer is not legal. char data2[] = "hello"; ASSERT_EQ(SSL_get_error(client_.get(), SSL_write(client_.get(), data2, kChunkLen)), SSL_ERROR_SSL); ASSERT_TRUE(ExpectSingleError(ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY)); // With |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER|, the buffer may move. SSL_set_mode(client_.get(), SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); ASSERT_EQ(SSL_get_error(client_.get(), SSL_write(client_.get(), data2, kChunkLen)), SSL_ERROR_WANT_WRITE); // |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER| does not disable length checks. ASSERT_EQ(SSL_get_error(client_.get(), SSL_write(client_.get(), data2, kChunkLen - 1)), SSL_ERROR_SSL); ASSERT_TRUE(ExpectSingleError(ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY)); // Retrying with a larger buffer is legal. ASSERT_EQ(SSL_get_error(client_.get(), SSL_write(client_.get(), data, kChunkLen + 1)), SSL_ERROR_WANT_WRITE); // Drain the buffer. char buf[20]; for (unsigned i = 0; i < count; i++) { ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); } // Now that there is space, a retry with a larger buffer should flush the // pending record, skip over that many bytes of input (on assumption they // are the same), and write the remainder. If SSL_MODE_ENABLE_PARTIAL_WRITE // is set, this will complete in two steps. char data_longer[] = "_____!!!!!"; if (enable_partial_write) { ASSERT_EQ(SSL_write(client_.get(), data_longer, 2 * kChunkLen), kChunkLen); ASSERT_EQ(SSL_write(client_.get(), data_longer + kChunkLen, kChunkLen), kChunkLen); } else { ASSERT_EQ(SSL_write(client_.get(), data_longer, 2 * kChunkLen), 2 * kChunkLen); } // Check the last write was correct. The data will be spread over two // records, so SSL_read returns twice. ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "!!!!!", kChunkLen), 0); // Fill the transport buffer again. This time only leave room for one // record. count = 0; for (;;) { int ret = SSL_write(client_.get(), data, kChunkLen); if (ret <= 0) { ASSERT_EQ(SSL_get_error(client_.get(), ret), SSL_ERROR_WANT_WRITE); break; } ASSERT_EQ(ret, 5); count++; } ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); count--; // Retry the last write, with a longer input. The first half is the most // recently failed write, from filling the buffer. |SSL_write| should write // that to the transport, and then attempt to write the second half. int ret = SSL_write(client_.get(), data_longer, 2 * kChunkLen); if (enable_partial_write) { // If partial writes are allowed, the write will succeed partially. ASSERT_EQ(ret, kChunkLen); // Check the first half and make room for another record. ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); count--; // Finish writing the input. ASSERT_EQ(SSL_write(client_.get(), data_longer + kChunkLen, kChunkLen), kChunkLen); } else { if (enable_read_ahead()) { // The client and server are sharing a BIO_pair which by default only // allows 17 * 1024 bytes to be buffered in the shared BIO. This test // relies on the buffer being full here. But if the client is reading // ahead it is pulling data out of the BIO_pair's buffer and into it's // own SSLBuffer freeing up space for the write above ASSERT_EQ(ret, 2 * kChunkLen); } else { // Otherwise, although the first half made it to the transport, the // second half is blocked. ASSERT_EQ(ret, -1); ASSERT_EQ(SSL_get_error(client_.get(), -1), SSL_ERROR_WANT_WRITE); // Check the first half and make room for another record. ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); count--; // Retrying with fewer bytes than previously attempted is an error. If // the input length is less than the number of bytes successfully // written, the check happens at a different point, with a different // error. // // TODO(davidben): Should these cases use the same error? ASSERT_EQ( SSL_get_error(client_.get(), SSL_write(client_.get(), data_longer, kChunkLen - 1)), SSL_ERROR_SSL); ASSERT_TRUE(ExpectSingleError(ERR_LIB_SSL, SSL_R_BAD_LENGTH)); // Complete the write with the correct retry. ASSERT_EQ(SSL_write(client_.get(), data_longer, 2 * kChunkLen), 2 * kChunkLen); } } // Drain the input and ensure everything was written correctly. for (unsigned i = 0; i < count; i++) { ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); } // The final write is spread over two records. ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "hello", kChunkLen), 0); ASSERT_EQ(SSL_read(server_.get(), buf, sizeof(buf)), kChunkLen); ASSERT_EQ(OPENSSL_memcmp(buf, "!!!!!", kChunkLen), 0); } } TEST_P(SSLVersionTest, RecordCallback) { for (bool test_server : {true, false}) { SCOPED_TRACE(test_server); ASSERT_NO_FATAL_FAILURE(ResetContexts()); bool read_seen = false; bool write_seen = false; auto cb = [&](int is_write, int cb_version, int cb_type, const void *buf, size_t len, SSL *ssl) { if (cb_type != SSL3_RT_HEADER) { return; } // The callback does not report a version for records. EXPECT_EQ(0, cb_version); if (is_write) { write_seen = true; } else { read_seen = true; } // Sanity-check that the record header is plausible. CBS cbs; CBS_init(&cbs, reinterpret_cast(buf), len); uint8_t type = 0; uint16_t record_version = 0, length = 0; ASSERT_TRUE(CBS_get_u8(&cbs, &type)); ASSERT_TRUE(CBS_get_u16(&cbs, &record_version)); EXPECT_EQ(record_version & 0xff00, version() & 0xff00); if (is_dtls()) { uint16_t epoch = 0; ASSERT_TRUE(CBS_get_u16(&cbs, &epoch)); EXPECT_TRUE(epoch == 0 || epoch == 1) << "Invalid epoch: " << epoch; ASSERT_TRUE(CBS_skip(&cbs, 6)); } ASSERT_TRUE(CBS_get_u16(&cbs, &length)); EXPECT_EQ(0u, CBS_len(&cbs)); }; using CallbackType = decltype(cb); SSL_CTX *ctx = test_server ? server_ctx_.get() : client_ctx_.get(); SSL_CTX_set_msg_callback( ctx, [](int is_write, int cb_version, int cb_type, const void *buf, size_t len, SSL *ssl, void *arg) { CallbackType *cb_ptr = reinterpret_cast(arg); (*cb_ptr)(is_write, cb_version, cb_type, buf, len, ssl); }); SSL_CTX_set_msg_callback_arg(ctx, &cb); ASSERT_TRUE(Connect()); EXPECT_TRUE(read_seen); EXPECT_TRUE(write_seen); } } TEST_P(SSLVersionTest, GetServerName) { ClientConfig config; config.servername = "host1"; SSL_CTX_set_tlsext_servername_callback( server_ctx_.get(), [](SSL *ssl, int *out_alert, void *arg) -> int { // During the handshake, |SSL_get_servername| must match |config|. ClientConfig *config_p = reinterpret_cast(arg); EXPECT_STREQ(config_p->servername.c_str(), SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)); return SSL_TLSEXT_ERR_OK; }); SSL_CTX_set_tlsext_servername_arg(server_ctx_.get(), &config); ASSERT_TRUE(Connect(config)); // After the handshake, it must also be available. EXPECT_STREQ(config.servername.c_str(), SSL_get_servername(server_.get(), TLSEXT_NAMETYPE_host_name)); // Establish a session under host1. SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get(), config); // If the client resumes a session with a different name, |SSL_get_servername| // must return the new name. ASSERT_TRUE(session); config.session = session.get(); config.servername = "host2"; ASSERT_TRUE(Connect(config)); EXPECT_STREQ(config.servername.c_str(), SSL_get_servername(server_.get(), TLSEXT_NAMETYPE_host_name)); } TEST_P(SSLVersionTest, GetState) { ClientConfig config; ASSERT_TRUE(Connect(config)); int server_state = SSL_get_state(server_.get()); EXPECT_EQ(server_state, TLS_ST_OK); EXPECT_EQ(server_state, SSL_ST_OK); int client_state = SSL_state(client_.get()); EXPECT_EQ(client_state, TLS_ST_OK); EXPECT_EQ(client_state, SSL_ST_OK); } // Test that session cache mode bits are honored in the client session callback. TEST_P(SSLVersionTest, ClientSessionCacheMode) { SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_OFF); EXPECT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_CLIENT); EXPECT_TRUE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_SERVER); EXPECT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); } // Test that all versions survive tiny write buffers. In particular, TLS 1.3 // NewSessionTickets are written post-handshake. Servers that block // |SSL_do_handshake| on writing them will deadlock if clients are not draining // the buffer. Test that we do not do this. TEST_P(SSLVersionTest, SmallBuffer) { // DTLS is a datagram protocol and requires packet-sized buffers. if (is_dtls()) { return; } // Test both flushing NewSessionTickets with a zero-sized write and // non-zero-sized write. for (bool use_zero_write : {false, true}) { SCOPED_TRACE(use_zero_write); g_last_session = nullptr; SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_sess_set_new_cb(client_ctx_.get(), SaveLastSession); bssl::UniquePtr client(SSL_new(client_ctx_.get())), server(SSL_new(server_ctx_.get())); ASSERT_TRUE(client); ASSERT_TRUE(server); SSL_set_connect_state(client.get()); SSL_set_accept_state(server.get()); // Use a tiny buffer. BIO *bio1 = nullptr, *bio2 = nullptr; ASSERT_TRUE(BIO_new_bio_pair(&bio1, 1, &bio2, 1)); // SSL_set_bio takes ownership. SSL_set_bio(client.get(), bio1, bio1); SSL_set_bio(server.get(), bio2, bio2); ASSERT_TRUE(CompleteHandshakes(client.get(), server.get())); if (version() >= TLS1_3_VERSION) { // The post-handshake ticket should not have been processed yet. EXPECT_FALSE(g_last_session); } if (use_zero_write) { ASSERT_TRUE(FlushNewSessionTickets(client.get(), server.get())); EXPECT_TRUE(g_last_session); } // Send some data from server to client. If |use_zero_write| is false, this // will also flush the NewSessionTickets. static const char kMessage[] = "hello world"; char buf[sizeof(kMessage)]; for (;;) { int server_ret = SSL_write(server.get(), kMessage, sizeof(kMessage)); int server_err = SSL_get_error(server.get(), server_ret); int client_ret = SSL_read(client.get(), buf, sizeof(buf)); int client_err = SSL_get_error(client.get(), client_ret); // The server will write a single record, so every iteration should see // |SSL_ERROR_WANT_WRITE| and |SSL_ERROR_WANT_READ|, until the final // iteration, where both will complete. if (server_ret > 0) { EXPECT_EQ(server_ret, static_cast(sizeof(kMessage))); EXPECT_EQ(client_ret, static_cast(sizeof(kMessage))); EXPECT_EQ(Bytes(buf), Bytes(kMessage)); break; } ASSERT_EQ(server_ret, -1); ASSERT_EQ(server_err, SSL_ERROR_WANT_WRITE); ASSERT_EQ(client_ret, -1); ASSERT_EQ(client_err, SSL_ERROR_WANT_READ); } // The NewSessionTickets should have been flushed and processed. EXPECT_TRUE(g_last_session); } } TEST_P(SSLVersionTest, SessionVersion) { SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); EXPECT_EQ(version(), SSL_SESSION_get_protocol_version(session.get())); // Sessions in TLS 1.3 and later should be single-use. EXPECT_EQ(version() == TLS1_3_VERSION, !!SSL_SESSION_should_be_single_use(session.get())); // Making fake sessions for testing works. session.reset(SSL_SESSION_new(client_ctx_.get())); ASSERT_TRUE(session); ASSERT_TRUE(SSL_SESSION_set_protocol_version(session.get(), version())); EXPECT_EQ(version(), SSL_SESSION_get_protocol_version(session.get())); } TEST_P(SSLVersionTest, SSLPending) { UniquePtr ssl(SSL_new(client_ctx_.get())); ASSERT_TRUE(ssl); EXPECT_EQ(0, SSL_pending(ssl.get())); ASSERT_TRUE(Connect()); EXPECT_EQ(0, SSL_pending(client_.get())); EXPECT_EQ(0, SSL_has_pending(client_.get())); ASSERT_EQ(5, SSL_write(server_.get(), "hello", 5)); ASSERT_EQ(5, SSL_write(server_.get(), "world", 5)); EXPECT_EQ(0, SSL_pending(client_.get())); EXPECT_EQ(0, SSL_has_pending(client_.get())); char buf[10]; ASSERT_EQ(1, SSL_peek(client_.get(), buf, 1)); EXPECT_EQ(5, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(1, SSL_read(client_.get(), buf, 1)); EXPECT_EQ(4, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(4, SSL_read(client_.get(), buf, 10)); EXPECT_EQ(0, SSL_pending(client_.get())); if (is_dtls()) { // In DTLS, the two records would have been read as a single datagram and // buffered inside |client_|. Thus, |SSL_has_pending| should return true. // // This test is slightly unrealistic. It relies on |ConnectClientAndServer| // using a |BIO| pair, which does not preserve datagram boundaries. Reading // 1 byte, then 4 bytes, from the first record also relies on // https://crbug.com/boringssl/65. But it does test the codepaths. When // fixing either of these bugs, this test may need to be redone. EXPECT_EQ(1, SSL_has_pending(client_.get())); } else { // In TLS if read ahead is enabled the two records would also have been read // in a single call. EXPECT_EQ(enable_read_ahead(), SSL_has_pending(client_.get())); } ASSERT_EQ(2, SSL_read(client_.get(), buf, 2)); EXPECT_EQ(3, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); } // Identical test to the |SSLPending| test suite above, but with // |SSL_(read/peek/write)_ex| operations instead. TEST_P(SSLVersionTest, SSLPendingEx) { UniquePtr ssl(SSL_new(client_ctx_.get())); ASSERT_TRUE(ssl); EXPECT_EQ(0, SSL_pending(ssl.get())); ASSERT_TRUE(Connect()); EXPECT_EQ(0, SSL_pending(client_.get())); EXPECT_EQ(0, SSL_has_pending(client_.get())); size_t buf_len = 0; ASSERT_EQ(1, SSL_write_ex(server_.get(), "hello", 5, &buf_len)); ASSERT_EQ(buf_len, (size_t)5); ASSERT_EQ(1, SSL_write_ex(server_.get(), "world", 5, &buf_len)); ASSERT_EQ(buf_len, (size_t)5); EXPECT_EQ(0, SSL_pending(client_.get())); EXPECT_EQ(0, SSL_has_pending(client_.get())); char buf[10]; ASSERT_EQ(1, SSL_peek_ex(client_.get(), buf, 1, &buf_len)); ASSERT_EQ(buf_len, (size_t)1); EXPECT_EQ(5, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(1, SSL_read_ex(client_.get(), buf, 1, &buf_len)); ASSERT_EQ(buf_len, (size_t)1); EXPECT_EQ(4, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(1, SSL_read_ex(client_.get(), buf, 10, &buf_len)); ASSERT_EQ(buf_len, (size_t)4); EXPECT_EQ(0, SSL_pending(client_.get())); if (is_dtls()) { EXPECT_EQ(1, SSL_has_pending(client_.get())); } else { // In TLS if read ahead is enabled the two records would also have been read // in a single call. EXPECT_EQ(enable_read_ahead(), SSL_has_pending(client_.get())); } ASSERT_EQ(1, SSL_read_ex(client_.get(), buf, 2, &buf_len)); ASSERT_EQ(buf_len, (size_t)2); EXPECT_EQ(3, SSL_pending(client_.get())); EXPECT_EQ(1, SSL_has_pending(client_.get())); // 0-sized IO with valid inputs should succeed but not read/write nor effect // buffer state. However, NULL |read_bytes|/|written| pointer should fail. const int client_pending = SSL_pending(client_.get()); ASSERT_EQ(1, SSL_read_ex(client_.get(), (void *)"", 0, &buf_len)); ASSERT_EQ(0UL, buf_len); ASSERT_EQ(client_pending, SSL_pending(client_.get())); ASSERT_EQ(1, SSL_write_ex(client_.get(), (void *)"", 0, &buf_len)); ASSERT_EQ(0UL, buf_len); ASSERT_EQ(client_pending, SSL_pending(client_.get())); ASSERT_EQ(0, SSL_read_ex(client_.get(), (void *)"", 0, nullptr)); ASSERT_EQ(0, SSL_write_ex(client_.get(), (void *)"", 0, nullptr)); } TEST_P(SSLVersionTest, ReadAhead) { ASSERT_TRUE(Connect()); size_t buf_len = 0; std::string test_string = "Hello, world!"; for (char &i : test_string) { ASSERT_EQ(1, SSL_write_ex(server_.get(), &i, 1, &buf_len)); } char buf[13]; size_t starting_size = BIO_pending(client_.get()->rbio.get()); ASSERT_NE(0UL, starting_size); ASSERT_EQ(1, SSL_read_ex(client_.get(), buf, 1, &buf_len)); if (enable_read_ahead() || is_dtls()) { // Even though we didn't request the full string (13 * record overhead) // everything should be read from the BIO if (read_ahead_buffer_size() > starting_size || is_dtls()) { ASSERT_EQ(0UL, BIO_pending(client_.get()->rbio.get())); } else { // Depending on TLS version each record will be a different size and a // variable but non-zero amount of data will remain ASSERT_NE(0UL, BIO_pending(client_.get()->rbio.get())); } } else { // Only the requested 1 byte + TLS record overhead should have been read, // the remaining 12 letters each in its own record should be in the BIO // not the SSLBuffer ASSERT_NE(0UL, BIO_pending(client_.get()->rbio.get())); } } TEST_P(SSLVersionTest, VerifyBeforeCertRequest) { // Configure the server to request client certificates. SSL_CTX_set_custom_verify( server_ctx_.get(), SSL_VERIFY_PEER, [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_ok; }); // Configure the client to reject the server certificate. SSL_CTX_set_custom_verify( client_ctx_.get(), SSL_VERIFY_PEER, [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_invalid; }); // cert_cb should not be called. Verification should fail first. SSL_CTX_set_cert_cb( client_ctx_.get(), [](SSL *ssl, void *arg) { ADD_FAILURE() << "cert_cb unexpectedly called"; return 0; }, nullptr); bssl::UniquePtr client, server; EXPECT_FALSE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get())); } // Test that ticket-based sessions on the client get fake session IDs. TEST_P(SSLVersionTest, FakeIDsForTickets) { SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); EXPECT_TRUE(SSL_SESSION_has_ticket(session.get())); unsigned session_id_length = 0; SSL_SESSION_get_id(session.get(), &session_id_length); EXPECT_NE(session_id_length, 0u); } // Functions which access properties on the negotiated session are thread-safe // where needed. Prior to TLS 1.3, clients resuming sessions and servers // performing stateful resumption will share an underlying SSL_SESSION object, // potentially across threads. TEST_P(SSLVersionTest, SessionPropertiesThreads) { if (version() == TLS1_3_VERSION) { // Our TLS 1.3 implementation does not support stateful resumption. ASSERT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); return; } SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); ASSERT_TRUE(UseCertAndKey(client_ctx_.get())); ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); // Configure mutual authentication, so we have more session state. SSL_CTX_set_custom_verify( client_ctx_.get(), SSL_VERIFY_PEER, [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_ok; }); SSL_CTX_set_custom_verify( server_ctx_.get(), SSL_VERIFY_PEER, [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_ok; }); // Establish a client session to test with. bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); // Resume with it twice. UniquePtr ssls[4]; ClientConfig config; config.session = session.get(); ASSERT_TRUE(ConnectClientAndServer(&ssls[0], &ssls[1], client_ctx_.get(), server_ctx_.get(), config)); ASSERT_TRUE(ConnectClientAndServer(&ssls[2], &ssls[3], client_ctx_.get(), server_ctx_.get(), config)); // Read properties in parallel. auto read_properties = [](const SSL *ssl) { EXPECT_TRUE(SSL_get_peer_cert_chain(ssl)); bssl::UniquePtr peer(SSL_get_peer_certificate(ssl)); EXPECT_TRUE(peer); STACK_OF(X509) *verified_chain = SSL_get0_verified_chain(ssl); // This test sets a custom verifier callback which doesn't actually do any // verification EXPECT_FALSE(verified_chain); EXPECT_TRUE(SSL_get_current_cipher(ssl)); EXPECT_TRUE(SSL_get_group_id(ssl)); }; std::vector threads; for (const auto &ssl_ptr : ssls) { const SSL *ssl = ssl_ptr.get(); threads.emplace_back([=] { read_properties(ssl); }); } for (auto &thread : threads) { thread.join(); } // Session has been resumed twice. EXPECT_EQ(SSL_CTX_sess_hits(server_ctx_.get()), 2); EXPECT_EQ(SSL_CTX_sess_hits(client_ctx_.get()), 2); } TEST_P(SSLVersionTest, SimpleVerifiedChain) { ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); ASSERT_TRUE(X509_STORE_add_cert(SSL_CTX_get_cert_store(client_ctx_.get()), cert_.get())); SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(client_ctx_.get()), X509_V_FLAG_NO_CHECK_TIME); UniquePtr client_ssl, server_ssl; ClientConfig config; ASSERT_TRUE(ConnectClientAndServer( &client_ssl, &server_ssl, client_ctx_.get(), server_ctx_.get(), config)); STACK_OF(X509) *client_chain = SSL_get_peer_full_cert_chain(client_ssl.get()); STACK_OF(X509) *verified_client_chain = SSL_get0_verified_chain(client_ssl.get()); EXPECT_TRUE(verified_client_chain); STACK_OF(X509) *verified_server_chain = SSL_get0_verified_chain(server_ssl.get()); // The client didn't send a certificate so the server shouldn't have anything EXPECT_FALSE(verified_server_chain); // UseCertAndKey sets a single cert that is directly trusted, it is the only // one sent, and only one needed for verification EXPECT_EQ(sk_X509_num(client_chain), 1UL); EXPECT_EQ(X509_cmp(sk_X509_value(client_chain, 0), cert_.get()), 0); EXPECT_EQ(sk_X509_num(verified_client_chain), 1UL); EXPECT_EQ(X509_cmp(sk_X509_value(verified_client_chain, 0), cert_.get()), 0); } TEST_P(SSLVersionTest, VerifiedChain) { ASSERT_TRUE(X509_STORE_add_cert(SSL_CTX_get_cert_store(client_ctx_.get()), cert_.get())); SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(client_ctx_.get()), X509_V_FLAG_NO_CHECK_TIME); // UseCertAndKey sets the leaf cert the server will use and ensures the client // trusts the server's cert ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); // Add two extra certs to the chain bssl::UniquePtr chain(sk_X509_new_null()); bssl::UniquePtr cert1 = GetECDSATestCertificate(); ASSERT_TRUE(sk_X509_push(chain.get(), cert1.get())); X509_up_ref(cert1.get()); bssl::UniquePtr cert2 = GetTestCertificate(); ASSERT_TRUE(sk_X509_push(chain.get(), cert2.get())); X509_up_ref(cert2.get()); SSL_CTX_set1_chain(server_ctx_.get(), chain.get()); UniquePtr client_ssl, server_ssl; ClientConfig config; ASSERT_TRUE(ConnectClientAndServer( &client_ssl, &server_ssl, client_ctx_.get(), server_ctx_.get(), config)); // The client didn't send a certificate so the server shouldn't have anything STACK_OF(X509) *verified_client_chain = SSL_get0_verified_chain(server_ssl.get()); EXPECT_FALSE(verified_client_chain); STACK_OF(X509) *client_chain = SSL_get_peer_full_cert_chain(server_ssl.get()); EXPECT_FALSE(client_chain); // The server sent a chain that the client can verify, the client directly // trusts the server's certificate STACK_OF(X509) *verified_server_chain = SSL_get0_verified_chain(client_ssl.get()); EXPECT_EQ(sk_X509_num(verified_server_chain), 1UL); EXPECT_EQ(X509_cmp(sk_X509_value(verified_server_chain, 0), cert_.get()), 0); // The server sent two extra certs that are unneeded for verification, // but it is included in the unverified chain STACK_OF(X509) *server_chain = SSL_get_peer_full_cert_chain(client_ssl.get()); EXPECT_EQ(sk_X509_num(server_chain), 3UL); EXPECT_EQ(X509_cmp(sk_X509_value(server_chain, 0), cert_.get()), 0); EXPECT_EQ(X509_cmp(sk_X509_value(server_chain, 1), cert1.get()), 0); EXPECT_EQ(X509_cmp(sk_X509_value(server_chain, 2), cert2.get()), 0); } TEST_P(SSLVersionTest, FailedHandshakeVerifiedChain) { SSL_CTX_set_verify(client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(client_ctx_.get()), X509_V_FLAG_NO_CHECK_TIME); ASSERT_TRUE(UseCertAndKey(server_ctx_.get())); UniquePtr client_ssl, server_ssl; ASSERT_TRUE(CreateClientAndServer(&client_ssl, &server_ssl, client_ctx_.get(), server_ctx_.get())); ASSERT_FALSE(CompleteHandshakes(client_ssl.get(), server_ssl.get())); EXPECT_NE(SSL_get_verify_result(client_ssl.get()), X509_V_OK); STACK_OF(X509) *client_chain = SSL_get_peer_full_cert_chain(client_ssl.get()); ASSERT_TRUE(client_chain); EXPECT_EQ(sk_X509_num(client_chain), 1UL); EXPECT_EQ(X509_cmp(sk_X509_value(client_chain, 0), cert_.get()), 0); // For a failed handshake SSL_get0_verified_chain will return null STACK_OF(X509) *verified_client_chain = SSL_get0_verified_chain(client_ssl.get()); EXPECT_FALSE(verified_client_chain); } TEST_P(SSLVersionTest, SessionMissCache) { if (version() == TLS1_3_VERSION) { // Our TLS 1.3 implementation does not support stateful resumption. ASSERT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); return; } SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_current_time_cb(server_ctx_.get(), CurrentTimeCallback); ClientConfig config; bssl::UniquePtr client, server; // Make some sessions at an arbitrary start time. Then expire them. g_current_time.tv_sec = 1000; bssl::UniquePtr expired_session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(expired_session); g_current_time.tv_sec += 100 * SSL_DEFAULT_SESSION_TIMEOUT; static const int kNumConnections = 2; config.session = expired_session.get(); EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); // First connection is not an |sess_miss|, but failed to connect successfully. // Subsequent connections will all be both timeouts and misses. EXPECT_EQ(SSL_CTX_sess_misses(server_ctx_.get()), kNumConnections - 1); EXPECT_EQ(SSL_CTX_sess_timeouts(server_ctx_.get()), kNumConnections); // Check that |sess_hits| is not incorrectly incremented on either end. EXPECT_EQ(SSL_CTX_sess_hits(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx_.get()), 0); } // Callback function to force an external session cache counter update. // This intentionally always returns a value, to verify that the counter is // updated as intended. // Allocating any memory within the callback function will cause the address // sanitizers to fail, so we manage the memory externally. static bssl::UniquePtr ssl_session; static SSL_SESSION *get_session(SSL *ssl, const unsigned char *id, int idlen, int *do_copy) { return ssl_session.release(); } TEST_P(SSLVersionTest, SessionExternalCacheHit) { if (version() == TLS1_3_VERSION) { // Our TLS 1.3 implementation does not support stateful resumption. ASSERT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get())); return; } SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode( server_ctx_.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL); SSL_CTX_sess_set_get_cb(server_ctx_.get(), get_session); ClientConfig config; bssl::UniquePtr client, server; bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); static const int kNumConnections = 2; config.session = session.get(); for (int i = 0; i < kNumConnections; i++) { ssl_session.reset(session.get()); EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); } EXPECT_EQ(SSL_CTX_sess_cb_hits(server_ctx_.get()), kNumConnections); } TEST_P(SSLVersionTest, DoubleSSLError) { // Connect the inner SSL connections. ASSERT_TRUE(Connect()); // Make a pair of |BIO|s which wrap |client_| and |server_|. UniquePtr bio_method(BIO_meth_new(0, nullptr)); ASSERT_TRUE(bio_method); ASSERT_TRUE(BIO_meth_set_read( bio_method.get(), [](BIO *bio, char *out, int len) -> int { SSL *ssl = static_cast(BIO_get_data(bio)); int ret = SSL_read(ssl, out, len); int ssl_ret = SSL_get_error(ssl, ret); if (ssl_ret == SSL_ERROR_WANT_READ) { BIO_set_retry_read(bio); } return ret; })); ASSERT_TRUE(BIO_meth_set_write( bio_method.get(), [](BIO *bio, const char *in, int len) -> int { SSL *ssl = static_cast(BIO_get_data(bio)); int ret = SSL_write(ssl, in, len); int ssl_ret = SSL_get_error(ssl, ret); if (ssl_ret == SSL_ERROR_WANT_WRITE) { BIO_set_retry_write(bio); } return ret; })); ASSERT_TRUE(BIO_meth_set_ctrl( bio_method.get(), [](BIO *bio, int cmd, long larg, void *parg) -> long { // |SSL| objects require |BIO_flush| support. if (cmd == BIO_CTRL_FLUSH) { return 1; } return 0; })); UniquePtr client_bio(BIO_new(bio_method.get())); ASSERT_TRUE(client_bio); BIO_set_data(client_bio.get(), client_.get()); BIO_set_init(client_bio.get(), 1); UniquePtr server_bio(BIO_new(bio_method.get())); ASSERT_TRUE(server_bio); BIO_set_data(server_bio.get(), server_.get()); BIO_set_init(server_bio.get(), 1); // Wrap the inner connections in another layer of SSL. UniquePtr client_outer(SSL_new(client_ctx_.get())); ASSERT_TRUE(client_outer); SSL_set_connect_state(client_outer.get()); SSL_set_bio(client_outer.get(), client_bio.get(), client_bio.get()); client_bio.release(); // |SSL_set_bio| takes ownership. UniquePtr server_outer(SSL_new(server_ctx_.get())); ASSERT_TRUE(server_outer); SSL_set_accept_state(server_outer.get()); SSL_set_bio(server_outer.get(), server_bio.get(), server_bio.get()); server_bio.release(); // |SSL_set_bio| takes ownership. // Configure |client_outer| to reject the server certificate. SSL_set_custom_verify( client_outer.get(), SSL_VERIFY_PEER, [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t { return ssl_verify_invalid; }); for (;;) { int client_ret = SSL_do_handshake(client_outer.get()); int client_err = SSL_get_error(client_outer.get(), client_ret); if (client_err != SSL_ERROR_WANT_READ && client_err != SSL_ERROR_WANT_WRITE) { // The client handshake should terminate on a certificate verification // error. EXPECT_EQ(SSL_ERROR_SSL, client_err); EXPECT_TRUE(ErrorEquals(ERR_peek_error(), ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED)); break; } // Run the server handshake and continue. int server_ret = SSL_do_handshake(server_outer.get()); int server_err = SSL_get_error(server_outer.get(), server_ret); ASSERT_TRUE(server_err == SSL_ERROR_NONE || server_err == SSL_ERROR_WANT_READ || server_err == SSL_ERROR_WANT_WRITE); } } TEST_P(SSLVersionTest, SameKeyResume) { uint8_t key[48]; RAND_bytes(key, sizeof(key)); bssl::UniquePtr server_ctx2 = CreateContext(); ASSERT_TRUE(server_ctx2); ASSERT_TRUE(UseCertAndKey(server_ctx2.get())); ASSERT_TRUE( SSL_CTX_set_tlsext_ticket_keys(server_ctx_.get(), key, sizeof(key))); ASSERT_TRUE( SSL_CTX_set_tlsext_ticket_keys(server_ctx2.get(), key, sizeof(key))); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx2.get(), SSL_SESS_CACHE_BOTH); // Establish a session for |server_ctx_|. bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); ClientConfig config; config.session = session.get(); // No hits before we resume the connection EXPECT_EQ(SSL_CTX_sess_hits(client_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx_.get()), 0); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx2.get()), 0); // Resuming with |server_ctx_| again works. bssl::UniquePtr client, server; ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); EXPECT_TRUE(SSL_session_reused(client.get())); EXPECT_TRUE(SSL_session_reused(server.get())); // Resuming with |server_ctx2| also works. ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx2.get(), config)); EXPECT_TRUE(SSL_session_reused(client.get())); EXPECT_TRUE(SSL_session_reused(server.get())); // By this point, the session has been resumed twice on the client side and // once for each server context. EXPECT_EQ(SSL_CTX_sess_hits(client_ctx_.get()), 2); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx_.get()), 1); EXPECT_EQ(SSL_CTX_sess_hits(server_ctx2.get()), 1); } TEST_P(SSLVersionTest, DifferentKeyNoResume) { uint8_t key1[48], key2[48]; RAND_bytes(key1, sizeof(key1)); RAND_bytes(key2, sizeof(key2)); bssl::UniquePtr server_ctx2 = CreateContext(); ASSERT_TRUE(server_ctx2); ASSERT_TRUE(UseCertAndKey(server_ctx2.get())); ASSERT_TRUE( SSL_CTX_set_tlsext_ticket_keys(server_ctx_.get(), key1, sizeof(key1))); ASSERT_TRUE( SSL_CTX_set_tlsext_ticket_keys(server_ctx2.get(), key2, sizeof(key2))); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx2.get(), SSL_SESS_CACHE_BOTH); // Establish a session for |server_ctx_|. bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); ClientConfig config; config.session = session.get(); // Resuming with |server_ctx_| again works. bssl::UniquePtr client, server; ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); EXPECT_TRUE(SSL_session_reused(client.get())); EXPECT_TRUE(SSL_session_reused(server.get())); // Resuming with |server_ctx2| does not work. ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx2.get(), config)); EXPECT_FALSE(SSL_session_reused(client.get())); EXPECT_FALSE(SSL_session_reused(server.get())); } TEST_P(SSLVersionTest, UnrelatedServerNoResume) { bssl::UniquePtr server_ctx2 = CreateContext(); ASSERT_TRUE(server_ctx2); ASSERT_TRUE(UseCertAndKey(server_ctx2.get())); SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx2.get(), SSL_SESS_CACHE_BOTH); // Establish a session for |server_ctx_|. bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); ASSERT_TRUE(session); ClientConfig config; config.session = session.get(); // Resuming with |server_ctx_| again works. bssl::UniquePtr client, server; ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); EXPECT_TRUE(SSL_session_reused(client.get())); EXPECT_TRUE(SSL_session_reused(server.get())); // Resuming with |server_ctx2| does not work. ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx2.get(), config)); EXPECT_FALSE(SSL_session_reused(client.get())); EXPECT_FALSE(SSL_session_reused(server.get())); } static Span SessionIDOf(const SSL *ssl) { const SSL_SESSION *session = SSL_get_session(ssl); unsigned len = 0; const uint8_t *data = SSL_SESSION_get_id(session, &len); return MakeConstSpan(data, len); } TEST_P(SSLVersionTest, TicketSessionIDsMatch) { // This checks that the session IDs at client and server match after a ticket // resumption. It's unclear whether this should be true, but Envoy depends // on it in their tests so this will give an early signal if we break it. SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH); SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH); bssl::UniquePtr session = CreateClientSession(client_ctx_.get(), server_ctx_.get()); bssl::UniquePtr client, server; ClientConfig config; config.session = session.get(); ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(), server_ctx_.get(), config)); EXPECT_TRUE(SSL_session_reused(client.get())); EXPECT_TRUE(SSL_session_reused(server.get())); EXPECT_EQ(Bytes(SessionIDOf(client.get())), Bytes(SessionIDOf(server.get()))); } TEST_P(SSLVersionTest, PeerTmpKey) { if (getVersionParam().transfer_ssl) { // The peer's temporary key is not within the boundary of the SSL transfer // feature. GTEST_SKIP(); } ASSERT_TRUE(Connect()); for (SSL *ssl : {client_.get(), server_.get()}) { SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client"); EVP_PKEY *key = nullptr; if (getVersionParam().version == TLS1_3_VERSION) { // TLS 1.3 default should be using X25519MLKEM768 as the key exchange. // We expect SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE because there is no EVP_PKEY type // for hybrid keys, only individual X25519 or MLKEM768 keys. ERR_clear_error(); EXPECT_FALSE(SSL_get_peer_tmp_key(ssl, &key)); ErrorEquals(ERR_get_error(), ERR_LIB_SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE); } else { EXPECT_TRUE(SSL_get_peer_tmp_key(ssl, &key)); EXPECT_EQ(EVP_PKEY_id(key), EVP_PKEY_X25519); bssl::UniquePtr pkey(key); } } // Check that x25519 works. ASSERT_TRUE(SSL_CTX_set1_groups_list(server_ctx_.get(), "x25519")); ASSERT_TRUE(Connect()); for (SSL *ssl : {client_.get(), server_.get()}) { SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client"); EVP_PKEY *key = nullptr; EXPECT_TRUE(SSL_get_peer_tmp_key(ssl, &key)); EXPECT_EQ(EVP_PKEY_id(key), EVP_PKEY_X25519); bssl::UniquePtr pkey(key); } // Check that EC Groups for the key exchange also work. ASSERT_TRUE(SSL_CTX_set1_groups_list(server_ctx_.get(), "P-384")); ASSERT_TRUE(Connect()); for (SSL *ssl : {client_.get(), server_.get()}) { SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client"); EVP_PKEY *key = nullptr; EXPECT_TRUE(SSL_get_peer_tmp_key(ssl, &key)); EXPECT_EQ(EVP_PKEY_id(key), EVP_PKEY_EC); EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(key); EXPECT_TRUE(ec_key); EXPECT_EQ(EC_KEY_get0_group(ec_key), EC_group_p384()); bssl::UniquePtr pkey(key); } } TEST_P(SSLVersionTest, GetFinished) { // Test that contents of |finished| and the peer's |finished| align. ASSERT_TRUE(Connect()); for (SSL *ssl : {client_.get(), server_.get()}) { SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client"); size_t finished_size = SSL_get_finished(ssl, nullptr, 0); EXPECT_TRUE(finished_size); bssl::UniquePtr finished((uint8_t *)OPENSSL_malloc(finished_size)); ASSERT_TRUE(finished); EXPECT_TRUE(SSL_get_finished(ssl, finished.get(), finished_size)); size_t peer_finished_size = SSL_get_peer_finished(ssl, nullptr, 0); EXPECT_TRUE(peer_finished_size); bssl::UniquePtr peer_finished( (uint8_t *)OPENSSL_malloc(peer_finished_size)); ASSERT_TRUE(peer_finished); EXPECT_TRUE(SSL_get_finished(ssl, peer_finished.get(), peer_finished_size)); EXPECT_EQ(Bytes(finished.get(), finished_size), Bytes(peer_finished.get(), peer_finished_size)); } } // Test the specific scenario that was failing: buffer // serialization/deserialization with sizes larger than uint16_t max (65535) TEST(SSLBufferSizeFailureTest, SerDeLargeBuffer) { // Create a buffer with capacity larger than the old uint16_t limit of 65535 SSLBuffer buffer; const size_t large_capacity = 70000; // Test that we can allocate a buffer larger than 65535 bytes EXPECT_TRUE(buffer.EnsureCap(0, large_capacity)); // Fill the buffer with test data std::vector test_data(large_capacity); for (size_t i = 0; i < large_capacity; i++) { test_data[i] = static_cast(i % 256); } // Write data to the buffer using the correct API OPENSSL_memcpy(buffer.data(), test_data.data(), test_data.size()); buffer.DidWrite(test_data.size()); EXPECT_EQ(buffer.size(), large_capacity); // Test serialization (this would have failed before the fix due to uint16_t // overflow) bssl::ScopedCBB cbb; EXPECT_TRUE(CBB_init(cbb.get(), 0)); EXPECT_TRUE(buffer.DoSerialization(*cbb.get())); uint8_t *serialized_data = nullptr; size_t serialized_len = 0; EXPECT_TRUE(CBB_finish(cbb.get(), &serialized_data, &serialized_len)); bssl::UniquePtr serialized_ptr(serialized_data); EXPECT_GT(serialized_len, 0u); // Test deserialization (this would have failed before the fix) SSLBuffer deserialized_buffer; CBS cbs; CBS_init(&cbs, serialized_data, serialized_len); EXPECT_TRUE(deserialized_buffer.DoDeserialization(cbs)); // Verify the deserialized buffer has the correct size and capacity EXPECT_EQ(deserialized_buffer.size(), large_capacity); EXPECT_GE(deserialized_buffer.cap(), large_capacity); // Verify the data integrity EXPECT_EQ(0, OPENSSL_memcmp(deserialized_buffer.data(), test_data.data(), large_capacity)); } // Test that specifically targets the internal buffer allocation logic TEST(SSLVersionTest, InternalBufferAllocationLimits) { // This test directly exercises the SSLBuffer class to ensure it properly // handles large buffer size requests SSLBuffer buffer; // Test various sizes around the boundary EXPECT_TRUE(buffer.EnsureCap(5, 65535)); // Old limit buffer.Clear(); EXPECT_TRUE(buffer.EnsureCap(5, 65536)); // Just above old limit buffer.Clear(); // These should fail - beyond the maximum capacity EXPECT_FALSE(buffer.EnsureCap(5, UINT32_MAX)); EXPECT_FALSE(buffer.EnsureCap(5, SIZE_MAX)); } BSSL_NAMESPACE_END