Files
cli/vendor/aws-lc-sys/aws-lc/ssl/ssl_version_test.cc

2465 lines
94 KiB
C++
Raw Normal View History

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <thread>
#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<VersionParam, int>> {
protected:
SSLVersionTest() : cert_(GetTestCertificate()), key_(GetTestKey()) {}
void SetUp() { ResetContexts(); }
bssl::UniquePtr<SSL_CTX> CreateContext() const {
const SSL_METHOD *method = is_dtls() ? DTLS_method() : TLS_method();
bssl::UniquePtr<SSL_CTX> 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<SSL> client_, server_;
bssl::UniquePtr<SSL_CTX> server_ctx_, client_ctx_;
bssl::UniquePtr<X509> cert_;
bssl::UniquePtr<EVP_PKEY> 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<VersionParam, int>>
&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<BIO> 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<BIO> client_wbio = bssl::UpRef(SSL_get_wbio(client_.get()));
bssl::UniquePtr<BIO> 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<BIO> 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(
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<uint8_t> 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<BIO> rbio(BIO_new_mem_buf(invalid.data(), invalid.size()));
ASSERT_TRUE(rbio);
SSL_set0_rbio(ssl.get(), rbio.release());
bssl::UniquePtr<BIO> 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<X509> 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<X509> 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<uint8_t> 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<X509> 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<SSL_SESSION> 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<SSL_SESSION> 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<SSL> 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<uint8_t[]> 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<int>(len)) ||
!EVP_DecryptFinal_ex(ctx.get(), plaintext.get() + len1, &len2)) {
return false;
}
len = static_cast<size_t>(len1 + len2);
#endif
bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
if (!ssl_ctx) {
return false;
}
bssl::UniquePtr<SSL_SESSION> 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<SSL_SESSION> 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<SSL_SESSION> 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<uint32_t>::max());
SSL_CTX_set_session_psk_dhe_timeout(server_ctx_.get(),
std::numeric_limits<uint32_t>::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<SSL> client, server;
bssl::UniquePtr<SSL_SESSION> 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<SSL_SESSION> 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<SSL_CTX *>(arg);
SSL_set_SSL_CTX(ssl, ctx);
return SSL_TLSEXT_ERR_OK;
}
TEST_P(SSLVersionTest, SNICallback) {
bssl::UniquePtr<X509> cert2 = GetECDSATestCertificate();
ASSERT_TRUE(cert2);
bssl::UniquePtr<EVP_PKEY> 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<SSL_CTX> 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<X509> 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<uint16_t, bool> 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<std::pair<uint16_t, bool> *>(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<X509> 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<SSL_SESSION> session1 =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(session1);
bssl::UniquePtr<SSL_SESSION> session2 =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(session2);
auto connect_with_session = [&](SSL_SESSION *session) {
ClientConfig config;
config.session = session;
UniquePtr<SSL> client, server;
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
server_ctx_.get(), config));
};
// Resume sessions in parallel with establishing new ones.
{
std::vector<std::thread> 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<std::thread> 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<SSL_SESSION> expired_session1 =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(expired_session1);
bssl::UniquePtr<SSL_SESSION> 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<std::thread> 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<SSL_SESSION> session1 =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(session1);
bssl::UniquePtr<SSL_SESSION> session2 =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(session2);
auto connect_with_session = [&](SSL_SESSION *session) {
ClientConfig config;
config.session = session;
UniquePtr<SSL> client, server;
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
server_ctx_.get(), config));
};
// Resume sessions in parallel with establishing new ones.
{
std::vector<std::thread> 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<const uint8_t *>(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<CallbackType *>(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<ClientConfig *>(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<SSL_SESSION> 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<SSL> 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<int>(sizeof(kMessage)));
EXPECT_EQ(client_ret, static_cast<int>(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<SSL_SESSION> 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(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(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<SSL> 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<SSL_SESSION> 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<SSL_SESSION> session =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
ASSERT_TRUE(session);
// Resume with it twice.
UniquePtr<SSL> 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<X509> 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<std::thread> 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<SSL> 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<STACK_OF(X509)> chain(sk_X509_new_null());
bssl::UniquePtr<X509> cert1 = GetECDSATestCertificate();
ASSERT_TRUE(sk_X509_push(chain.get(), cert1.get()));
X509_up_ref(cert1.get());
bssl::UniquePtr<X509> 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<SSL> 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<SSL> 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<SSL> client, server;
// Make some sessions at an arbitrary start time. Then expire them.
g_current_time.tv_sec = 1000;
bssl::UniquePtr<SSL_SESSION> 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> 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<SSL> client, server;
bssl::UniquePtr<SSL_SESSION> 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_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<SSL *>(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<SSL *>(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<BIO> 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<BIO> 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<SSL> 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<SSL> 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<SSL_CTX> 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<SSL_SESSION> 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<SSL> 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<SSL_CTX> 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<SSL_SESSION> 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<SSL> 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<SSL_CTX> 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<SSL_SESSION> 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<SSL> 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<const uint8_t> 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<SSL_SESSION> session =
CreateClientSession(client_ctx_.get(), server_ctx_.get());
bssl::UniquePtr<SSL> 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<EVP_PKEY> 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<EVP_PKEY> 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<EVP_PKEY> 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<uint8_t> 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<uint8_t> 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<uint8_t> test_data(large_capacity);
for (size_t i = 0; i < large_capacity; i++) {
test_data[i] = static_cast<uint8_t>(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<uint8_t> 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