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/include/openssl/ssl.h b/include/openssl/ssl.h
index 01433d5..326a82b 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1726,19 +1726,36 @@
 OPENSSL_EXPORT SSL_SESSION *SSL_get1_session(SSL *ssl);
 
 /* SSL_DEFAULT_SESSION_TIMEOUT is the default lifetime, in seconds, of a
- * session. */
+ * session in TLS 1.2 or earlier. This is how long we are willing to use the
+ * secret to encrypt traffic without fresh key material. */
 #define SSL_DEFAULT_SESSION_TIMEOUT (2 * 60 * 60)
 
-/* SSL_CTX_set_timeout sets the lifetime, in seconds, of sessions created in
- * |ctx| to |timeout|. */
+/* SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT is the default lifetime, in seconds, of a
+ * session for TLS 1.3 psk_dhe_ke. This is how long we are willing to use the
+ * secret as an authenticator. */
+#define SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT (2 * 24 * 60 * 60)
+
+/* SSL_DEFAULT_SESSION_AUTH_TIMEOUT is the default non-renewable lifetime, in
+ * seconds, of a TLS 1.3 session. This is how long we are willing to trust the
+ * signature in the initial handshake. */
+#define SSL_DEFAULT_SESSION_AUTH_TIMEOUT (7 * 24 * 60 * 60)
+
+/* SSL_CTX_set_timeout sets the lifetime, in seconds, of TLS 1.2 (or earlier)
+ * sessions created in |ctx| to |timeout|. */
 OPENSSL_EXPORT long SSL_CTX_set_timeout(SSL_CTX *ctx, long timeout);
 
-/* SSL_CTX_get_timeout returns the lifetime, in seconds, of sessions created in
- * |ctx|. */
+/* SSL_CTX_set_session_psk_dhe_timeout sets the lifetime, in seconds, of TLS 1.3
+ * sessions created in |ctx| to |timeout|. */
+OPENSSL_EXPORT void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx,
+                                                        long timeout);
+
+/* SSL_CTX_get_timeout returns the lifetime, in seconds, of TLS 1.2 (or earlier)
+ * sessions created in |ctx|. */
 OPENSSL_EXPORT long SSL_CTX_get_timeout(const SSL_CTX *ctx);
 
-/* SSL_set_session_timeout sets the default lifetime, in seconds, of the
- * session created in |ssl| to |timeout|, and returns the old value.
+/* SSL_set_session_timeout sets the default lifetime, in seconds, of a TLS 1.2
+ * (or earlier) session created in |ssl| to |timeout|, and returns the old
+ * value.
  *
  * By default the value |SSL_DEFAULT_SESSION_TIMEOUT| is used, which can be
  * overridden at the context level by calling |SSL_CTX_set_timeout|.
@@ -1746,6 +1763,10 @@
  * If |timeout| is zero the newly created session will not be resumable. */
 OPENSSL_EXPORT long SSL_set_session_timeout(SSL *ssl, long timeout);
 
+/* SSL_set_session_psk_dhe_timeout sets the lifetime, in seconds, of TLS 1.3
+ * sessions created in |ssl| to |timeout|. */
+OPENSSL_EXPORT void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout);
+
 /* SSL_CTX_set_session_id_context sets |ctx|'s session ID context to |sid_ctx|.
  * It returns one on success and zero on error. The session ID context is an
  * application-defined opaque byte string. A session will not be used in a
@@ -3736,9 +3757,14 @@
    * non-fatal certificate errors. */
   long verify_result;
 
-  /* timeout is the lifetime of the session in seconds, measured from |time|. */
+  /* timeout is the lifetime of the session in seconds, measured from |time|.
+   * This is renewable up to |auth_timeout|. */
   long timeout;
 
+  /* auth_timeout is the non-renewable lifetime of the session in seconds,
+   * measured from |time|. */
+  long auth_timeout;
+
   /* time is the time the session was issued, measured in seconds from the UNIX
    * epoch. */
   long time;
@@ -3872,9 +3898,14 @@
    * SSL_accept which cache SSL_SESSIONS. */
   int session_cache_mode;
 
-  /* session_timeout is the default lifetime for new sessions, in seconds. */
+  /* session_timeout is the default lifetime for new sessions in TLS 1.2 and
+   * earlier, in seconds. */
   long session_timeout;
 
+  /* session_psk_dhe_timeout is the default lifetime for new sessions in TLS
+   * 1.3, in seconds. */
+  long session_psk_dhe_timeout;
+
   /* If this callback is not null, it will be called each time a session id is
    * added to the cache.  If this function returns 1, it means that the
    * callback will do a SSL_SESSION_free() when it has finished using it.
@@ -4237,9 +4268,13 @@
   unsigned retain_only_sha256_of_client_certs:1;
 
   /* session_timeout is the default lifetime in seconds of the session
-   * created in this connection. */
+   * created in this connection at TLS 1.2 and earlier. */
   long session_timeout;
 
+  /* session_psk_dhe_timeout is the default lifetime in seconds of sessions
+   * created in this connection at TLS 1.3. */
+  long session_psk_dhe_timeout;
+
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
 };
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;
 }