Implement SSL_CTX_set_num_tickets.

CPython and wpa_supplicant are using this nowadays. To avoid needing to
tweak the ticket nonce derivation, I've just internally capped the
number of tickets at 16, which should be plenty.

Change-Id: Ie84c15b81a2abe8ec729992e515e0bd4cc351037
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/52465
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
fips-20220613
David Benjamin 3 years ago committed by Boringssl LUCI CQ
parent df6311bc6c
commit 3f180b8221
  1. 7
      include/openssl/ssl.h
  2. 10
      ssl/internal.h
  3. 9
      ssl/ssl_lib.cc
  4. 46
      ssl/ssl_test.cc
  5. 10
      ssl/tls13_server.cc

@ -2281,6 +2281,13 @@ OPENSSL_EXPORT void SSL_CTX_set_ticket_aead_method(
OPENSSL_EXPORT SSL_SESSION *SSL_process_tls13_new_session_ticket( OPENSSL_EXPORT SSL_SESSION *SSL_process_tls13_new_session_ticket(
SSL *ssl, const uint8_t *buf, size_t buf_len); SSL *ssl, const uint8_t *buf, size_t buf_len);
// SSL_CTX_set_num_tickets configures |ctx| to send |num_tickets| immediately
// after a successful TLS 1.3 handshake as a server. It returns one. Large
// values of |num_tickets| will be capped within the library.
//
// By default, BoringSSL sends two tickets.
OPENSSL_EXPORT int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets);
// Elliptic curve Diffie-Hellman. // Elliptic curve Diffie-Hellman.
// //

@ -2056,6 +2056,11 @@ struct SSL_HANDSHAKE {
uint8_t grease_seed[ssl_grease_last_index + 1] = {0}; uint8_t grease_seed[ssl_grease_last_index + 1] = {0};
}; };
// kMaxTickets is the maximum number of tickets to send immediately after the
// handshake. We use a one-byte ticket nonce, and there is no point in sending
// so many tickets.
constexpr size_t kMaxTickets = 16;
UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl); UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl);
// ssl_check_message_type checks if |msg| has type |type|. If so it returns // ssl_check_message_type checks if |msg| has type |type|. If so it returns
@ -3416,6 +3421,11 @@ struct ssl_ctx_st {
// and is further constrainted by |SSL_OP_NO_*|. // and is further constrainted by |SSL_OP_NO_*|.
uint16_t conf_min_version = 0; uint16_t conf_min_version = 0;
// num_tickets is the number of tickets to send immediately after the TLS 1.3
// handshake. TLS 1.3 recommends single-use tickets so, by default, issue two
/// in case the client makes several connections before getting a renewal.
uint8_t num_tickets = 2;
// quic_method is the method table corresponding to the QUIC hooks. // quic_method is the method table corresponding to the QUIC hooks.
const SSL_QUIC_METHOD *quic_method = nullptr; const SSL_QUIC_METHOD *quic_method = nullptr;

@ -140,6 +140,8 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <algorithm>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -3025,6 +3027,13 @@ SSL_SESSION *SSL_process_tls13_new_session_ticket(SSL *ssl, const uint8_t *buf,
return session.release(); return session.release();
} }
int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets) {
num_tickets = std::min(num_tickets, kMaxTickets);
static_assert(kMaxTickets <= 0xff, "Too many tickets.");
ctx->num_tickets = static_cast<uint8_t>(num_tickets);
return 1;
}
int SSL_set_tlsext_status_type(SSL *ssl, int type) { int SSL_set_tlsext_status_type(SSL *ssl, int type) {
if (!ssl->config) { if (!ssl->config) {
return 0; return 0;

@ -8113,5 +8113,51 @@ RVHWbCvFvNZAoWiIJ2z34RLGInyZvCZ8xLAvsuaWULDDaoeDl1M0t4Hm
} }
} }
TEST(SSLTest, NumTickets) {
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(server_ctx);
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(client_ctx);
bssl::UniquePtr<X509> cert = GetTestCertificate();
ASSERT_TRUE(cert);
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
ASSERT_TRUE(key);
ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
static size_t ticket_count;
SSL_CTX_sess_set_new_cb(client_ctx.get(), [](SSL *, SSL_SESSION *) -> int {
ticket_count++;
return 0;
});
auto count_tickets = [&]() -> size_t {
ticket_count = 0;
bssl::UniquePtr<SSL> client, server;
if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
server_ctx.get()) ||
!FlushNewSessionTickets(client.get(), server.get())) {
ADD_FAILURE() << "Could not run handshake";
return 0;
}
return ticket_count;
};
// By default, we should send two tickets.
EXPECT_EQ(count_tickets(), 2u);
for (size_t num_tickets : {0, 1, 2, 3, 4, 5}) {
SCOPED_TRACE(num_tickets);
ASSERT_TRUE(SSL_CTX_set_num_tickets(server_ctx.get(), num_tickets));
EXPECT_EQ(count_tickets(), num_tickets);
}
// Configuring too many tickets causes us to stop at some point.
ASSERT_TRUE(SSL_CTX_set_num_tickets(server_ctx.get(), 100000));
EXPECT_EQ(count_tickets(), 16u);
}
} // namespace } // namespace
BSSL_NAMESPACE_END BSSL_NAMESPACE_END

@ -131,15 +131,12 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
return true; return true;
} }
// TLS 1.3 recommends single-use tickets, so issue multiple tickets in case
// the client makes several connections before getting a renewal.
static const int kNumTickets = 2;
// Rebase the session timestamp so that it is measured from ticket // Rebase the session timestamp so that it is measured from ticket
// issuance. // issuance.
ssl_session_rebase_time(ssl, hs->new_session.get()); ssl_session_rebase_time(ssl, hs->new_session.get());
for (int i = 0; i < kNumTickets; i++) { assert(ssl->session_ctx->num_tickets <= kMaxTickets);
for (size_t i = 0; i < ssl->session_ctx->num_tickets; i++) {
UniquePtr<SSL_SESSION> session( UniquePtr<SSL_SESSION> session(
SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_INCLUDE_NONAUTH)); SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_INCLUDE_NONAUTH));
if (!session) { if (!session) {
@ -160,7 +157,8 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
ssl->quic_method != nullptr ? 0xffffffff : kMaxEarlyDataAccepted; ssl->quic_method != nullptr ? 0xffffffff : kMaxEarlyDataAccepted;
} }
static_assert(kNumTickets < 256, "Too many tickets"); static_assert(kMaxTickets < 256, "Too many tickets");
assert(i < 256);
uint8_t nonce[] = {static_cast<uint8_t>(i)}; uint8_t nonce[] = {static_cast<uint8_t>(i)};
ScopedCBB cbb; ScopedCBB cbb;

Loading…
Cancel
Save