From 3c51d9b1b93bb85eeed6f6b35c8b14efb451a637 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 1 Nov 2016 17:50:42 -0400 Subject: [PATCH] Test that session renewals interact with lifetimes correctly. A renewed session does not refresh the timeout. Add tests for this in preparation for future changes which will revise this logic. Specifically, TLS 1.3 draft 18's ticket_age_add logic will require some tweaks in lifetime tracking to record when the ticket was minted. We'll also likely wish to tweak the parameters for 1.3 to account for (a) ECDHE-PSK means we're only worried about expiring a short-circuited authentication rather than forward secrecy and (b) two hours is too short for a QUIC 0-RTT replacement. Change-Id: I0f1edd09151e7fcb5aee2742ef8600fbd7080df6 Reviewed-on: https://boringssl-review.googlesource.com/12002 Reviewed-by: David Benjamin Commit-Queue: David Benjamin CQ-Verified: CQ bot account: commit-bot@chromium.org --- ssl/ssl_test.cc | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index ed32c212..30c37e24 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -23,11 +23,14 @@ #include #include +#include #include #include +#include #include #include #include +#include #include #include "internal.h" @@ -1972,6 +1975,41 @@ static bool ExpectSessionReused(SSL_CTX *client_ctx, SSL_CTX *server_ctx, return true; } +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; + if (!ConnectClientAndServer(&client, &server, client_ctx, + server_ctx, session)) { + 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; + } + + // Run the read loop to account for post-handshake tickets in TLS 1.3. + SSL_read(client.get(), nullptr, 0); + + 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 bool TestSessionIDContext() { bssl::UniquePtr cert = GetTestCertificate(); bssl::UniquePtr key = GetTestKey(); @@ -2037,6 +2075,28 @@ static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { *out_clock = g_current_time; } +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) { + memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (memcmp(key_name, kZeros, 16) != 0) { + return 0; + } + + if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) || + !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) { + return -1; + } + + // Returning two from the callback in decrypt mode renews the + // session in TLS 1.2 and below. + return encrypt ? 1 : 2; +} + static bool TestSessionTimeout() { bssl::UniquePtr cert = GetTestCertificate(); bssl::UniquePtr key = GetTestKey(); @@ -2071,6 +2131,9 @@ static bool TestSessionTimeout() { SSL_CTX_set_current_time_cb(client_ctx.get(), CurrentTimeCallback); } + // 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()); if (!session) { @@ -2107,6 +2170,49 @@ static bool TestSessionTimeout() { fprintf(stderr, "Error resuming session (version = %04x).\n", version); return false; } + + // SSL 3.0 cannot renew sessions. + if (version == SSL3_VERSION) { + continue; + } + + // Renew the session 10 seconds before expiration. + g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 10; + bssl::UniquePtr new_session = ExpectSessionRenewed( + client_ctx.get(), server_ctx.get(), session.get()); + if (!new_session) { + fprintf(stderr, "Error renewing session (version = %04x).\n", version); + return false; + } + + // This new session is not the same object as before. + if (session.get() == new_session.get()) { + fprintf(stderr, "New and old sessions alias (version = %04x).\n", + version); + return false; + } + + // The new session is usable just before the old expiration. + g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming renewed session (version = %04x).\n", + version); + return false; + } + + // Renewal does not extend the lifetime, so it is not usable beyond the + // old expiration. + g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT + 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + false /* expect session not reused */)) { + fprintf(stderr, + "Renewed session's lifetime is too long (version = %04x).\n", + version); + return false; + } } }