Rotate the default ticket encryption key.

The ticket encryption key is rotated automatically once every 24 hours,
unless a key has been configured manually (i.e. using
|SSL_CTX_set_tlsext_ticket_keys|) or one of the custom ticket encryption
methods is used.

Change-Id: I0dfff28b33e58e96b3bbf7f94dcd6d2642f37aec
Reviewed-on: https://boringssl-review.googlesource.com/18924
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index a1c21dc..dad0656 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -437,6 +437,59 @@
   return 1;
 }
 
+int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx) {
+  OPENSSL_timeval now;
+  ssl_ctx_get_current_time(ctx, &now);
+  {
+    /* Avoid acquiring a write lock in the common case (i.e. a non-default key
+     * is used or the default keys have not expired yet). */
+    MutexReadLock lock(&ctx->lock);
+    if (ctx->tlsext_ticket_key_current &&
+        (ctx->tlsext_ticket_key_current->next_rotation_tv_sec == 0 ||
+         ctx->tlsext_ticket_key_current->next_rotation_tv_sec > now.tv_sec) &&
+        (!ctx->tlsext_ticket_key_prev ||
+         ctx->tlsext_ticket_key_prev->next_rotation_tv_sec > now.tv_sec)) {
+      return 1;
+    }
+  }
+
+  MutexWriteLock lock(&ctx->lock);
+  if (!ctx->tlsext_ticket_key_current ||
+      (ctx->tlsext_ticket_key_current->next_rotation_tv_sec != 0 &&
+       ctx->tlsext_ticket_key_current->next_rotation_tv_sec <= now.tv_sec)) {
+    /* The current key has not been initialized or it is expired. */
+    auto new_key = bssl::MakeUnique<struct tlsext_ticket_key>();
+    if (!new_key) {
+      return 0;
+    }
+    OPENSSL_memset(new_key.get(), 0, sizeof(struct tlsext_ticket_key));
+    if (ctx->tlsext_ticket_key_current) {
+      /* The current key expired. Rotate it to prev and bump up its rotation
+       * timestamp. Note that even with the new rotation time it may still be
+       * expired and get droppped below. */
+      ctx->tlsext_ticket_key_current->next_rotation_tv_sec +=
+          SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL;
+      OPENSSL_free(ctx->tlsext_ticket_key_prev);
+      ctx->tlsext_ticket_key_prev = ctx->tlsext_ticket_key_current;
+    }
+    ctx->tlsext_ticket_key_current = new_key.release();
+    RAND_bytes(ctx->tlsext_ticket_key_current->name, 16);
+    RAND_bytes(ctx->tlsext_ticket_key_current->hmac_key, 16);
+    RAND_bytes(ctx->tlsext_ticket_key_current->aes_key, 16);
+    ctx->tlsext_ticket_key_current->next_rotation_tv_sec =
+        now.tv_sec + SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL;
+  }
+
+  /* Drop an expired prev key. */
+  if (ctx->tlsext_ticket_key_prev &&
+      ctx->tlsext_ticket_key_prev->next_rotation_tv_sec <= now.tv_sec) {
+    OPENSSL_free(ctx->tlsext_ticket_key_prev);
+    ctx->tlsext_ticket_key_prev = nullptr;
+  }
+
+  return 1;
+}
+
 static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
                                               const uint8_t *session_buf,
                                               size_t session_len) {
@@ -464,14 +517,19 @@
       return 0;
     }
   } else {
+    /* Rotate ticket key if necessary. */
+    if (!ssl_ctx_rotate_ticket_encryption_key(tctx)) {
+      return 0;
+    }
+    MutexReadLock lock(&tctx->lock);
     if (!RAND_bytes(iv, 16) ||
         !EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), NULL,
-                            tctx->tlsext_tick_aes_key, iv) ||
-        !HMAC_Init_ex(hctx.get(), tctx->tlsext_tick_hmac_key, 16,
+                            tctx->tlsext_ticket_key_current->aes_key, iv) ||
+        !HMAC_Init_ex(hctx.get(), tctx->tlsext_ticket_key_current->hmac_key, 16,
                       tlsext_tick_md(), NULL)) {
       return 0;
     }
-    OPENSSL_memcpy(key_name, tctx->tlsext_tick_key_name, 16);
+    OPENSSL_memcpy(key_name, tctx->tlsext_ticket_key_current->name, 16);
   }
 
   uint8_t *ptr;