Use a separate timeout scheme for TLS 1.3.
In TLS 1.2, resumption's benefits are more-or-less subsumed by False
Start. TLS 1.2 resumption lifetime is bounded by how much traffic we are
willing to encrypt without fresh key material, so the lifetime is short.
Renewal uses the same key, so we do not allow it to increase lifetimes.
In TLS 1.3, resumption unlocks 0-RTT. We do not implement psk_ke, so
resumption incorporates fresh key material into both encrypted traffic
(except for early data) and renewed tickets. Thus we are both more
willing to and more interested in longer lifetimes for tickets. Renewal
is also not useless. Thus in TLS 1.3, lifetime is bound separately by
the lifetime of a given secret as a psk_dhe_ke authenticator and the
lifetime of the online signature which authenticated the initial
handshake.
This change maintains two lifetimes on an SSL_SESSION: timeout which is
the renewable lifetime of this ticket, and auth_timeout which is the
non-renewable cliff. It also separates the TLS 1.2 and TLS 1.3 timeouts.
The old session timeout defaults and configuration apply to TLS 1.3, and
we define new ones for TLS 1.3.
Finally, this makes us honor the NewSessionTicket timeout in TLS 1.3.
It's no longer a "hint" in 1.3 and there's probably value in avoiding
known-useless 0-RTT offers.
BUG=120
Change-Id: Iac46d56e5a6a377d8b88b8fa31f492d534cb1b85
Reviewed-on: https://boringssl-review.googlesource.com/13503
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 351c1b8..23a4cff 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -1890,7 +1890,7 @@
}
/* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */
- ssl_session_refresh_time(ssl, session);
+ ssl_session_rebase_time(ssl, session);
if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index df8f7a5..8025030 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -1930,7 +1930,7 @@
SSL_SESSION *session_copy = NULL;
if (ssl->session == NULL) {
/* Fix the timeout to measure from the ticket issuance time. */
- ssl_session_refresh_time(ssl, ssl->s3->new_session);
+ ssl_session_rebase_time(ssl, ssl->s3->new_session);
session = ssl->s3->new_session;
} else {
/* We are renewing an existing session. Duplicate the session to adjust the
@@ -1940,7 +1940,7 @@
return -1;
}
- ssl_session_refresh_time(ssl, session_copy);
+ ssl_session_rebase_time(ssl, session_copy);
session = session_copy;
}
diff --git a/ssl/internal.h b/ssl/internal.h
index 2eae6fa..cba14cd 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1753,9 +1753,14 @@
OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
int dup_flags);
-/* ssl_session_refresh_time updates |session|'s start time to the current time,
+/* ssl_session_rebase_time updates |session|'s start time to the current time,
* adjusting the timeout so the expiration time is unchanged. */
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session);
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session);
+
+/* ssl_session_renew_timeout calls |ssl_session_rebase_time| and renews
+ * |session|'s timeout to |timeout| (measured from the current time). The
+ * renewal is clamped to the session's auth_timeout. */
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout);
void ssl_cipher_preference_list_free(
struct ssl_cipher_preference_list_st *cipher_list);
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index c9dfdcc..4c1ee89 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -129,10 +129,11 @@
* isServer [22] BOOLEAN DEFAULT TRUE,
* peerSignatureAlgorithm [23] INTEGER OPTIONAL,
* ticketMaxEarlyData [24] INTEGER OPTIONAL,
+ * authTimeout [25] INTEGER OPTIONAL, -- defaults to timeout
* }
*
* Note: historically this serialization has included other optional
- * fields. Their presense is currently treated as a parse error:
+ * fields. Their presence is currently treated as a parse error:
*
* keyArg [0] IMPLICIT OCTET STRING OPTIONAL,
* pskIdentityHint [7] OCTET STRING OPTIONAL,
@@ -183,6 +184,8 @@
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23;
static const int kTicketMaxEarlyDataTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24;
+static const int kAuthTimeoutTag =
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25;
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
size_t *out_len, int for_ticket) {
@@ -402,6 +405,13 @@
goto err;
}
+ if (in->timeout != in->auth_timeout &&
+ (!CBB_add_asn1(&session, &child, kAuthTimeoutTag) ||
+ !CBB_add_asn1_uint64(&child, in->auth_timeout))) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
if (!CBB_finish(&cbb, out_data, out_len)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
@@ -787,6 +797,8 @@
kPeerSignatureAlgorithmTag, 0) ||
!SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data,
kTicketMaxEarlyDataTag, 0) ||
+ !SSL_SESSION_parse_long(&session, &ret->auth_timeout, kAuthTimeoutTag,
+ ret->timeout) ||
CBS_len(&session) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index ded5aef..851c81f 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -254,8 +254,8 @@
ret->session_cache_mode = SSL_SESS_CACHE_SERVER;
ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
- /* We take the system default */
ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+ ret->session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT;
ret->references = 1;
@@ -425,22 +425,21 @@
ssl->initial_ctx = ctx;
if (ctx->supported_group_list) {
- ssl->supported_group_list =
- BUF_memdup(ctx->supported_group_list,
- ctx->supported_group_list_len * 2);
+ ssl->supported_group_list = BUF_memdup(ctx->supported_group_list,
+ ctx->supported_group_list_len * 2);
if (!ssl->supported_group_list) {
goto err;
}
ssl->supported_group_list_len = ctx->supported_group_list_len;
}
- if (ssl->ctx->alpn_client_proto_list) {
- ssl->alpn_client_proto_list = BUF_memdup(
- ssl->ctx->alpn_client_proto_list, ssl->ctx->alpn_client_proto_list_len);
+ if (ctx->alpn_client_proto_list) {
+ ssl->alpn_client_proto_list = BUF_memdup(ctx->alpn_client_proto_list,
+ ctx->alpn_client_proto_list_len);
if (ssl->alpn_client_proto_list == NULL) {
goto err;
}
- ssl->alpn_client_proto_list_len = ssl->ctx->alpn_client_proto_list_len;
+ ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
}
ssl->method = ctx->method;
@@ -469,11 +468,11 @@
ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
}
- ssl->signed_cert_timestamps_enabled =
- ssl->ctx->signed_cert_timestamps_enabled;
- ssl->ocsp_stapling_enabled = ssl->ctx->ocsp_stapling_enabled;
+ ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
+ ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
ssl->session_timeout = ctx->session_timeout;
+ ssl->session_psk_dhe_timeout = ctx->session_psk_dhe_timeout;
/* If the context has an OCSP response, use it. */
if (ctx->ocsp_response != NULL) {
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index 805bd48..5dffc70 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -171,6 +171,7 @@
session->verify_result = X509_V_ERR_INVALID_CALL;
session->references = 1;
session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+ session->auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
session->time = (long)time(NULL);
CRYPTO_new_ex_data(&session->ex_data);
return session;
@@ -259,6 +260,7 @@
new_session->peer_signature_algorithm = session->peer_signature_algorithm;
new_session->timeout = session->timeout;
+ new_session->auth_timeout = session->auth_timeout;
new_session->time = session->time;
/* Copy non-authentication connection properties. */
@@ -303,7 +305,7 @@
return 0;
}
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session) {
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session) {
struct timeval now;
ssl_get_current_time(ssl, &now);
@@ -314,11 +316,12 @@
now.tv_sec < 0) {
session->time = now.tv_sec;
session->timeout = 0;
+ session->auth_timeout = 0;
return;
}
- /* Adjust the session time and timeout. If the session has already expired,
- * clamp the timeout at zero. */
+ /* Adjust the session time and timeouts. If the session has already expired,
+ * clamp the timeouts at zero. */
long delta = now.tv_sec - session->time;
session->time = now.tv_sec;
if (session->timeout < delta) {
@@ -326,6 +329,26 @@
} else {
session->timeout -= delta;
}
+ if (session->auth_timeout < delta) {
+ session->auth_timeout = 0;
+ } else {
+ session->auth_timeout -= delta;
+ }
+}
+
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout) {
+ /* Rebase the timestamp relative to the current time so |timeout| is measured
+ * correctly. */
+ ssl_session_rebase_time(ssl, session);
+
+ if (session->timeout > timeout) {
+ return;
+ }
+
+ session->timeout = timeout;
+ if (session->timeout > session->auth_timeout) {
+ session->timeout = session->auth_timeout;
+ }
}
int SSL_SESSION_up_ref(SSL_SESSION *session) {
@@ -408,6 +431,7 @@
}
session->timeout = timeout;
+ session->auth_timeout = timeout;
return 1;
}
@@ -490,10 +514,21 @@
ssl_get_current_time(ssl, &now);
session->time = now.tv_sec;
- session->timeout = ssl->session_timeout;
+ uint16_t version = ssl3_protocol_version(ssl);
+ if (version >= TLS1_3_VERSION) {
+ /* TLS 1.3 uses tickets as authenticators, so we are willing to use them for
+ * longer. */
+ session->timeout = ssl->session_psk_dhe_timeout;
+ session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
+ } else {
+ /* TLS 1.2 resumption does not incorporate new key material, so we use a
+ * much shorter timeout. */
+ session->timeout = ssl->session_timeout;
+ session->auth_timeout = ssl->session_timeout;
+ }
if (is_server) {
- if (hs->ticket_expected || ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ if (hs->ticket_expected || version >= TLS1_3_VERSION) {
/* Don't set session IDs for sessions resumed with tickets. This will keep
* them out of the session cache. */
session->session_id_length = 0;
@@ -952,12 +987,20 @@
return ctx->session_timeout;
}
+void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx, long timeout) {
+ ctx->session_psk_dhe_timeout = timeout;
+}
+
long SSL_set_session_timeout(SSL *ssl, long timeout) {
long old_timeout = ssl->session_timeout;
ssl->session_timeout = timeout;
return old_timeout;
}
+void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout) {
+ ssl->session_psk_dhe_timeout = timeout;
+}
+
typedef struct timeout_param_st {
SSL_CTX *ctx;
long time;
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 99e9af2..41572c4 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -2154,6 +2154,11 @@
*out_clock = g_current_time;
}
+static void FrozenTimeCallback(const SSL *ssl, timeval *out_clock) {
+ out_clock->tv_sec = 1000;
+ out_clock->tv_usec = 0;
+}
+
static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
int encrypt) {
@@ -2221,9 +2226,15 @@
}
for (bool server_test : std::vector<bool>{false, true}) {
- static const int kStartTime = 1000;
+ 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;
+
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
if (!server_ctx || !client_ctx ||
@@ -2239,11 +2250,14 @@
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);
- // Both client and server must enforce session timeouts.
+ // 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.
@@ -2257,7 +2271,7 @@
}
// Advance the clock just behind the timeout.
- g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT - 1;
+ g_current_time.tv_sec += timeout - 1;
if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
true /* expect session reused */)) {
@@ -2289,7 +2303,8 @@
}
// Renew the session 10 seconds before expiration.
- g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 10;
+ 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());
if (!new_session) {
@@ -2319,23 +2334,76 @@
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.\n");
- return false;
- }
+ 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;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ true /* expect session reused */)) {
+ fprintf(stderr, "Error resuming renewed session.\n");
+ 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.\n");
- return false;
+ // The new session expires after the new timeout.
+ g_current_time.tv_sec = new_start_time + timeout + 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ false /* expect session ot reused */)) {
+ fprintf(stderr, "Renewed session's lifetime is too long.\n");
+ return false;
+ }
+
+ // 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());
+ if (!new_session) {
+ fprintf(stderr, "Error renewing session.\n");
+ return false;
+ }
+ }
+
+ // Now the session's lifetime is bound by the auth timeout.
+ g_current_time.tv_sec = auth_end_time - 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ true /* expect session reused */)) {
+ fprintf(stderr, "Error resuming renewed session.\n");
+ return false;
+ }
+
+ g_current_time.tv_sec = auth_end_time + 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ false /* expect session ot reused */)) {
+ fprintf(stderr, "Renewed session's lifetime is too long.\n");
+ return false;
+ }
+ } else {
+ // The new session is usable just before the old expiration.
+ g_current_time.tv_sec = kStartTime + timeout - 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ true /* expect session reused */)) {
+ fprintf(stderr, "Error resuming renewed session.\n");
+ return false;
+ }
+
+ // Renewal does not extend the lifetime, so it is not usable beyond the
+ // old expiration.
+ g_current_time.tv_sec = kStartTime + 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.\n");
+ return false;
+ }
}
}
@@ -2348,9 +2416,20 @@
return 1;
}
+static int SetSessionTimeoutCallbackTLS13(SSL *ssl, void *arg) {
+ long timeout = *(long *) arg;
+ SSL_set_session_psk_dhe_timeout(ssl, timeout);
+ return 1;
+}
+
static bool TestSessionTimeoutCertCallback(bool is_dtls,
const SSL_METHOD *method,
uint16_t version) {
+ if (version == TLS1_3_VERSION) {
+ // |SSL_set_session_timeout| only applies to TLS 1.2 style resumption.
+ return true;
+ }
+
static const int kStartTime = 1000;
g_current_time.tv_sec = kStartTime;
@@ -2378,7 +2457,12 @@
SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
long timeout = 25;
- SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+ if (version == TLS1_3_VERSION) {
+ SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallbackTLS13,
+ &timeout);
+ } else {
+ SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+ }
bssl::UniquePtr<SSL_SESSION> session =
CreateClientSession(client_ctx.get(), server_ctx.get());
@@ -2428,7 +2512,11 @@
timeout = 25;
g_current_time.tv_sec = kStartTime;
- SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+ if (version == TLS1_3_VERSION) {
+ SSL_CTX_set_session_psk_dhe_timeout(server_ctx.get(), timeout - 10);
+ } else {
+ SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+ }
bssl::UniquePtr<SSL_SESSION> ctx_and_cb_session =
CreateClientSession(client_ctx.get(), server_ctx.get());
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 9192e2e..92f3e15 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1270,6 +1270,10 @@
// MaxReceivePlaintext, if non-zero, is the maximum plaintext record
// length accepted from the peer.
MaxReceivePlaintext int
+
+ // SendTicketLifetime, if non-zero, is the ticket lifetime to send in
+ // NewSessionTicket messages.
+ SendTicketLifetime time.Duration
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 065d23d..0cc0b38 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -1795,6 +1795,10 @@
ticketAgeAdd: ticketAgeAdd,
}
+ if c.config.Bugs.SendTicketLifetime != 0 {
+ m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+ }
+
state := sessionState{
vers: c.vers,
cipherSuite: c.cipherSuite.id,
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 1fc741f..38925e9 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -1681,6 +1681,9 @@
}
m := new(newSessionTicketMsg)
+ if c.config.Bugs.SendTicketLifetime != 0 {
+ m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+ }
if !c.config.Bugs.SendEmptySessionTicket {
var err error
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 5103c55..8b02c3d 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -8493,6 +8493,40 @@
"-enable-early-data",
},
})
+
+ // Test that, in TLS 1.3, the server-offered NewSessionTicket lifetime
+ // is honored.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-HonorServerSessionTicketLifetime",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketLifetime: 20 * time.Second,
+ },
+ },
+ flags: []string{
+ "-resumption-delay", "19",
+ },
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-HonorServerSessionTicketLifetime-2",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketLifetime: 20 * time.Second,
+ // The client should not offer the expired session.
+ ExpectNoTLS13PSK: true,
+ },
+ },
+ flags: []string{
+ "-resumption-delay", "21",
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
}
func addChangeCipherSpecTests() {
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 08e4edd..74d3646 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -15,6 +15,7 @@
#include <openssl/ssl.h>
#include <assert.h>
+#include <limits.h>
#include <string.h>
#include <openssl/bytestring.h>
@@ -257,6 +258,10 @@
return ssl_hs_error;
}
ssl_set_session(ssl, NULL);
+
+ /* Resumption incorporates fresh key material, so refresh the timeout. */
+ ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+ ssl->session_psk_dhe_timeout);
} else if (!ssl_get_new_session(hs, 0)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -618,11 +623,12 @@
return 0;
}
- ssl_session_refresh_time(ssl, session);
+ ssl_session_rebase_time(ssl, session);
+ uint32_t server_timeout;
CBS cbs, ticket, extensions;
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
- if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
+ if (!CBS_get_u32(&cbs, &server_timeout) ||
!CBS_get_u32(&cbs, &session->ticket_age_add) ||
!CBS_get_u16_length_prefixed(&cbs, &ticket) ||
!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
@@ -633,6 +639,21 @@
goto err;
}
+ /* Cap the renewable lifetime by the server advertised value. This avoids
+ * wasting bandwidth on 0-RTT when we know the server will reject it.
+ *
+ * TODO(davidben): This dance where we're not sure if long or uint32_t is
+ * bigger is silly. session->timeout should not be a long to begin with.
+ * https://crbug.com/boringssl/155. */
+#if LONG_MAX < 0xffffffff
+ if (server_timeout > LONG_MAX) {
+ server_timeout = LONG_MAX;
+ }
+#endif
+ if (session->timeout > (long)server_timeout) {
+ session->timeout = (long)server_timeout;
+ }
+
/* Parse out the extensions. */
int have_early_data_info = 0;
CBS early_data_info;
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index 8a28b13..80bcf77 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -281,6 +281,10 @@
}
ssl->s3->session_reused = 1;
SSL_SESSION_free(session);
+
+ /* Resumption incorporates fresh key material, so refresh the timeout. */
+ ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+ ssl->session_psk_dhe_timeout);
}
if (ssl->ctx->dos_protection_cb != NULL &&
@@ -598,9 +602,9 @@
ssl->method->received_flight(ssl);
- /* Refresh the session timestamp so that it is measured from ticket
+ /* Rebase the session timestamp so that it is measured from ticket
* issuance. */
- ssl_session_refresh_time(ssl, ssl->s3->new_session);
+ ssl_session_rebase_time(ssl, ssl->s3->new_session);
hs->tls13_state = state_send_new_session_ticket;
return ssl_hs_ok;
}