external/boringssl: Sync to 68f37b7a3f451aa1ca8c93669c024d01f6270ae8.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/3ef7697ed30f28367395a5aafb57a12a19906d96..68f37b7a3f451aa1ca8c93669c024d01f6270ae8

Test: cts-tradefed run cts -m CtsLibcoreOkHttpTestCases -a arm64-v8a
Test: cts-tradefed run cts -m CtsLibcoreTestCases -a arm64-v8a

Change-Id: I296d05afab7470335cdda2442414a858df591f6c
diff --git a/src/ssl/custom_extensions.c b/src/ssl/custom_extensions.c
index 780cdc6..46b5efb 100644
--- a/src/ssl/custom_extensions.c
+++ b/src/ssl/custom_extensions.c
@@ -90,7 +90,7 @@
             !CBB_add_bytes(&contents_cbb, contents, contents_len) ||
             !CBB_flush(extensions)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          ERR_add_error_dataf("extension: %u", (unsigned) ext->value);
+          ERR_add_error_dataf("extension %u", (unsigned) ext->value);
           if (ext->free_callback && 0 < contents_len) {
             ext->free_callback(ssl, ext->value, contents, ext->add_arg);
           }
@@ -113,7 +113,7 @@
       default:
         ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
         OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
-        ERR_add_error_dataf("extension: %u", (unsigned) ext->value);
+        ERR_add_error_dataf("extension %u", (unsigned) ext->value);
         return 0;
     }
   }
@@ -136,7 +136,7 @@
       /* Also, if we didn't send the extension, that's also unacceptable. */
       !(ssl->s3->hs->custom_extensions.sent & (1u << index))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    ERR_add_error_dataf("extension: %u", (unsigned)value);
+    ERR_add_error_dataf("extension %u", (unsigned)value);
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
     return 0;
   }
@@ -145,7 +145,7 @@
       !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
                            out_alert, ext->parse_arg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
-    ERR_add_error_dataf("extension: %u", (unsigned)ext->value);
+    ERR_add_error_dataf("extension %u", (unsigned)ext->value);
     return 0;
   }
 
@@ -169,7 +169,7 @@
       !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
                            out_alert, ext->parse_arg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
-    ERR_add_error_dataf("extension: %u", (unsigned)ext->value);
+    ERR_add_error_dataf("extension %u", (unsigned)ext->value);
     return 0;
   }
 
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 7bb2de2..f9bb8f4 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -433,7 +433,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return -1;
   }
-  if (hash_message == ssl_hash_message && !dtls1_hash_current_message(ssl)) {
+  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
     return -1;
   }
 
@@ -442,13 +442,12 @@
   return 1;
 }
 
-int dtls1_hash_current_message(SSL *ssl) {
+void dtls1_get_current_message(const SSL *ssl, CBS *out) {
   assert(dtls1_is_current_message_complete(ssl));
 
   hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
                                                  SSL_MAX_HANDSHAKE_FLIGHT];
-  return ssl3_update_handshake_hash(ssl, frag->data,
-                                    DTLS1_HM_HEADER_LENGTH + frag->msg_len);
+  CBS_init(out, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
 }
 
 void dtls1_release_current_message(SSL *ssl, int free_buffer) {
@@ -527,10 +526,10 @@
 /* dtls1_max_record_size returns the maximum record body length that may be
  * written without exceeding the MTU. It accounts for any buffering installed on
  * the write BIO. If no record may be written, it returns zero. */
-static size_t dtls1_max_record_size(SSL *ssl) {
+static size_t dtls1_max_record_size(const SSL *ssl) {
   size_t ret = ssl->d1->mtu;
 
-  size_t overhead = ssl_max_seal_overhead(ssl);
+  size_t overhead = SSL_max_seal_overhead(ssl);
   if (ret <= overhead) {
     return 0;
   }
@@ -736,21 +735,23 @@
   return 1;
 }
 
-int dtls1_finish_message(SSL *ssl, CBB *cbb) {
-  uint8_t *msg = NULL;
-  size_t len;
-  if (!CBB_finish(cbb, &msg, &len) ||
-      len > 0xffffffffu ||
-      len < DTLS1_HM_HEADER_LENGTH) {
+int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+                         size_t *out_len) {
+  *out_msg = NULL;
+  if (!CBB_finish(cbb, out_msg, out_len) ||
+      *out_len < DTLS1_HM_HEADER_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    OPENSSL_free(msg);
+    OPENSSL_free(*out_msg);
     return 0;
   }
 
   /* Fix up the header. Copy the fragment length into the total message
    * length. */
-  memcpy(msg + 1, msg + DTLS1_HM_HEADER_LENGTH - 3, 3);
+  memcpy(*out_msg + 1, *out_msg + DTLS1_HM_HEADER_LENGTH - 3, 3);
+  return 1;
+}
 
+int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
   ssl3_update_handshake_hash(ssl, msg, len);
 
   ssl->d1->handshake_write_seq++;
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 099de5d..155359c 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -377,7 +377,7 @@
     return -1;
   }
 
-  size_t max_out = len + ssl_max_seal_overhead(ssl);
+  size_t max_out = len + SSL_max_seal_overhead(ssl);
   uint8_t *out;
   size_t ciphertext_len;
   if (!ssl_write_buffer_init(ssl, &out, max_out) ||
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 9d791b5..8e92cc9 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -132,7 +132,7 @@
     dtls1_new,
     dtls1_free,
     dtls1_get_message,
-    dtls1_hash_current_message,
+    dtls1_get_current_message,
     dtls1_release_current_message,
     dtls1_read_app_data,
     dtls1_read_change_cipher_spec,
@@ -142,6 +142,7 @@
     dtls1_supports_cipher,
     dtls1_init_message,
     dtls1_finish_message,
+    dtls1_queue_message,
     dtls1_write_message,
     dtls1_send_change_cipher_spec,
     dtls1_expect_flight,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index a917e02..70d8d96 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -152,6 +152,7 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/aead.h>
 #include <openssl/bn.h>
 #include <openssl/buf.h>
 #include <openssl/bytestring.h>
@@ -447,7 +448,7 @@
         goto end;
 
       case SSL3_ST_CR_SESSION_TICKET_A:
-        if (ssl->tlsext_ticket_expected) {
+        if (ssl->s3->hs->ticket_expected) {
           ret = ssl3_get_new_session_ticket(ssl);
           if (ret <= 0) {
             goto end;
@@ -506,8 +507,6 @@
         break;
 
       case SSL_ST_OK:
-        /* Clean a few things up. */
-        ssl3_cleanup_key_block(ssl);
         ssl->method->release_current_message(ssl, 1 /* free_buffer */);
 
         SSL_SESSION_free(ssl->s3->established_session);
@@ -537,9 +536,6 @@
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(ssl);
 
-        ssl_handshake_free(ssl->s3->hs);
-        ssl->s3->hs = NULL;
-
         const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
         ssl->s3->initial_handshake_complete = 1;
         if (is_initial_handshake) {
@@ -547,6 +543,9 @@
           ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
         }
 
+        ssl_handshake_free(ssl->s3->hs);
+        ssl->s3->hs = NULL;
+
         ret = 1;
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         goto end;
@@ -605,30 +604,48 @@
     return 0;
   }
 
-  STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
-
-  int any_enabled = 0;
-  for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
-    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
-    /* Skip disabled ciphers */
-    if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
-        (cipher->algorithm_auth & ssl->cert->mask_a)) {
-      continue;
+  /* Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
+   * hardware support. */
+  if (max_version >= TLS1_3_VERSION) {
+    if (!EVP_has_aes_hardware() &&
+        !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
+      return 0;
     }
-    if (SSL_CIPHER_get_min_version(cipher) > max_version ||
-        SSL_CIPHER_get_max_version(cipher) < min_version) {
-      continue;
+    if (!CBB_add_u16(&child, TLS1_CK_AES_128_GCM_SHA256 & 0xffff) ||
+        !CBB_add_u16(&child, TLS1_CK_AES_256_GCM_SHA384 & 0xffff)) {
+      return 0;
     }
-    any_enabled = 1;
-    if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
+    if (EVP_has_aes_hardware() &&
+        !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
       return 0;
     }
   }
 
-  /* If all ciphers were disabled, return the error to the caller. */
-  if (!any_enabled) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
-    return 0;
+  if (min_version < TLS1_3_VERSION) {
+    STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
+    int any_enabled = 0;
+    for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
+      const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
+      /* Skip disabled ciphers */
+      if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
+          (cipher->algorithm_auth & ssl->cert->mask_a)) {
+        continue;
+      }
+      if (SSL_CIPHER_get_min_version(cipher) > max_version ||
+          SSL_CIPHER_get_max_version(cipher) < min_version) {
+        continue;
+      }
+      any_enabled = 1;
+      if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
+        return 0;
+      }
+    }
+
+    /* If all ciphers were disabled, return the error to the caller. */
+    if (!any_enabled && max_version < TLS1_3_VERSION) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
+      return 0;
+    }
   }
 
   /* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
@@ -649,43 +666,66 @@
   return CBB_flush(out);
 }
 
-int ssl_add_client_hello_body(SSL *ssl, CBB *body) {
+int ssl_write_client_hello(SSL *ssl) {
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
   }
 
+  CBB cbb, body;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO)) {
+    goto err;
+  }
+
   /* Renegotiations do not participate in session resumption. */
   int has_session = ssl->session != NULL &&
                     !ssl->s3->initial_handshake_complete;
 
   CBB child;
-  if (!CBB_add_u16(body, ssl->client_version) ||
-      !CBB_add_bytes(body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
-      !CBB_add_u8_length_prefixed(body, &child) ||
+  if (!CBB_add_u16(&body, ssl->client_version) ||
+      !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+      !CBB_add_u8_length_prefixed(&body, &child) ||
       (has_session &&
        !CBB_add_bytes(&child, ssl->session->session_id,
                       ssl->session->session_id_length))) {
-    return 0;
+    goto err;
   }
 
   if (SSL_is_dtls(ssl)) {
-    if (!CBB_add_u8_length_prefixed(body, &child) ||
+    if (!CBB_add_u8_length_prefixed(&body, &child) ||
         !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
-      return 0;
+      goto err;
     }
   }
 
   size_t header_len =
       SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
-  if (!ssl_write_client_cipher_list(ssl, body, min_version, max_version) ||
-      !CBB_add_u8(body, 1 /* one compression method */) ||
-      !CBB_add_u8(body, 0 /* null compression */) ||
-      !ssl_add_clienthello_tlsext(ssl, body, header_len + CBB_len(body))) {
-    return 0;
+  if (!ssl_write_client_cipher_list(ssl, &body, min_version, max_version) ||
+      !CBB_add_u8(&body, 1 /* one compression method */) ||
+      !CBB_add_u8(&body, 0 /* null compression */) ||
+      !ssl_add_clienthello_tlsext(ssl, &body, header_len + CBB_len(&body))) {
+    goto err;
   }
 
-  return 1;
+  uint8_t *msg = NULL;
+  size_t len;
+  if (!ssl->method->finish_message(ssl, &cbb, &msg, &len)) {
+    goto err;
+  }
+
+  /* Now that the length prefixes have been computed, fill in the placeholder
+   * PSK binder. */
+  if (ssl->s3->hs->needs_psk_binder &&
+      !tls13_write_psk_binder(ssl, msg, len)) {
+    OPENSSL_free(msg);
+    goto err;
+  }
+
+  return ssl->method->queue_message(ssl, msg, len);
+
+ err:
+  CBB_cleanup(&cbb);
+  return 0;
 }
 
 static int ssl3_send_client_hello(SSL *ssl) {
@@ -700,12 +740,9 @@
     return -1;
   }
 
-  CBB cbb;
-  CBB_zero(&cbb);
-
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    goto err;
+    return -1;
   }
 
   assert(ssl->state == SSL3_ST_CW_CLNT_HELLO_A);
@@ -741,22 +778,15 @@
    * renegerate the client_random. The random must be reused. */
   if ((!SSL_is_dtls(ssl) || !ssl->d1->send_cookie) &&
       !RAND_bytes(ssl->s3->client_random, sizeof(ssl->s3->client_random))) {
-    goto err;
+    return -1;
   }
 
-  CBB body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) ||
-      !ssl_add_client_hello_body(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
-    goto err;
+  if (!ssl_write_client_hello(ssl)) {
+    return -1;
   }
 
   ssl->state = SSL3_ST_CW_CLNT_HELLO_B;
   return ssl->method->write_message(ssl);
-
-err:
-  CBB_cleanup(&cbb);
-  return -1;
 }
 
 static int dtls1_get_hello_verify(SSL *ssl) {
@@ -802,8 +832,6 @@
 }
 
 static int ssl3_get_server_hello(SSL *ssl) {
-  STACK_OF(SSL_CIPHER) *sk;
-  const SSL_CIPHER *c;
   CERT *ct = ssl->cert;
   int al = SSL_AD_INTERNAL_ERROR;
   CBS server_hello, server_random, session_id;
@@ -911,26 +939,19 @@
            CBS_len(&session_id));
   }
 
-  c = SSL_get_cipher_by_value(cipher_suite);
+  const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
   if (c == NULL) {
     /* unknown cipher */
     al = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
     goto f_err;
   }
-  /* If the cipher is disabled then we didn't sent it in the ClientHello, so if
-   * the server selected it, it's an error. */
+
+  /* The cipher must be allowed in the selected version and enabled. */
   if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
       SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) {
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
-    goto f_err;
-  }
-
-  sk = ssl_get_ciphers_by_id(ssl);
-  if (!sk_SSL_CIPHER_find(sk, NULL, c)) {
-    /* we did not say we would use this cipher */
+      SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) ||
+      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     goto f_err;
@@ -1041,14 +1062,14 @@
     goto err;
   }
 
-  /* NOTE: Unlike the server half, the client's copy of |cert_chain| includes
+  /* NOTE: Unlike the server half, the client's copy of |x509_chain| includes
    * the leaf. */
-  sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
-  ssl->s3->new_session->cert_chain = chain;
+  sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+  ssl->s3->new_session->x509_chain = chain;
 
-  X509_free(ssl->s3->new_session->peer);
+  X509_free(ssl->s3->new_session->x509_peer);
   X509_up_ref(leaf);
-  ssl->s3->new_session->peer = leaf;
+  ssl->s3->new_session->x509_peer = leaf;
 
   return 1;
 
@@ -1100,7 +1121,7 @@
 
 static int ssl3_verify_server_cert(SSL *ssl) {
   if (!ssl_verify_cert_chain(ssl, &ssl->s3->new_session->verify_result,
-                             ssl->s3->new_session->cert_chain)) {
+                             ssl->s3->new_session->x509_chain)) {
     return -1;
   }
 
@@ -1274,7 +1295,7 @@
 
   /* ServerKeyExchange should be signed by the server's public key. */
   if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-    pkey = X509_get_pubkey(ssl->s3->new_session->peer);
+    pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
     if (pkey == NULL) {
       goto err;
     }
@@ -1455,6 +1476,7 @@
         return -1;
       }
       if (ret == 0) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return -1;
       }
@@ -1562,7 +1584,7 @@
       goto err;
     }
 
-    EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->peer);
+    EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
     if (pkey == NULL) {
       goto err;
     }
@@ -1668,7 +1690,7 @@
 
   /* The message must be added to the finished hash before calculating the
    * master secret. */
-  if (!ssl->method->finish_message(ssl, &cbb)) {
+  if (!ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
   ssl->state = SSL3_ST_CW_KEY_EXCH_B;
@@ -1785,7 +1807,7 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -1814,7 +1836,7 @@
                      ssl->s3->next_proto_negotiated_len) ||
       !CBB_add_u8_length_prefixed(&body, &child) ||
       !CBB_add_bytes(&child, kZero, padding_len) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
@@ -1831,16 +1853,8 @@
 
   assert(ssl->state == SSL3_ST_CW_CHANNEL_ID_A);
 
-  if (ssl->tlsext_channel_id_private == NULL &&
-      ssl->ctx->channel_id_cb != NULL) {
-    EVP_PKEY *key = NULL;
-    ssl->ctx->channel_id_cb(ssl, &key);
-    if (key != NULL &&
-        !SSL_set1_tls_channel_id(ssl, key)) {
-      EVP_PKEY_free(key);
-      return -1;
-    }
-    EVP_PKEY_free(key);
+  if (!ssl_do_channel_id_callback(ssl)) {
+    return -1;
   }
 
   if (ssl->tlsext_channel_id_private == NULL) {
@@ -1848,55 +1862,17 @@
     return -1;
   }
 
-  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
-  if (ec_key == NULL) {
+  CBB cbb, body;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+      !tls1_write_channel_id(ssl, &body) ||
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    CBB_cleanup(&cbb);
     return -1;
   }
 
-  int ret = -1;
-  BIGNUM *x = BN_new();
-  BIGNUM *y = BN_new();
-  ECDSA_SIG *sig = NULL;
-  if (x == NULL || y == NULL ||
-      !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
-                                           EC_KEY_get0_public_key(ec_key),
-                                           x, y, NULL)) {
-    goto err;
-  }
-
-  uint8_t digest[EVP_MAX_MD_SIZE];
-  size_t digest_len;
-  if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
-    goto err;
-  }
-
-  sig = ECDSA_do_sign(digest, digest_len, ec_key);
-  if (sig == NULL) {
-    goto err;
-  }
-
-  CBB cbb, body, child;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
-      !CBB_add_u16(&body, TLSEXT_TYPE_channel_id) ||
-      !CBB_add_u16_length_prefixed(&body, &child) ||
-      !BN_bn2cbb_padded(&child, 32, x) || !BN_bn2cbb_padded(&child, 32, y) ||
-      !BN_bn2cbb_padded(&child, 32, sig->r) ||
-      !BN_bn2cbb_padded(&child, 32, sig->s) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    CBB_cleanup(&cbb);
-    goto err;
-  }
-
   ssl->state = SSL3_ST_CW_CHANNEL_ID_B;
-  ret = ssl->method->write_message(ssl);
-
-err:
-  BN_free(x);
-  BN_free(y);
-  ECDSA_SIG_free(sig);
-  return ret;
+  return ssl->method->write_message(ssl);
 }
 
 static int ssl3_get_new_session_ticket(SSL *ssl) {
@@ -1919,10 +1895,9 @@
 
   if (CBS_len(&ticket) == 0) {
     /* RFC 5077 allows a server to change its mind and send no ticket after
-     * negotiating the extension. The value of |tlsext_ticket_expected| is
-     * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
-     * update. */
-    ssl->tlsext_ticket_expected = 0;
+     * negotiating the extension. The value of |ticket_expected| is checked in
+     * |ssl_update_cache| so is cleared here to avoid an unnecessary update. */
+    ssl->s3->hs->ticket_expected = 0;
     return 1;
   }
 
@@ -1940,6 +1915,9 @@
     }
   }
 
+  /* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */
+  ssl_session_refresh_time(ssl, session);
+
   if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index e018680..3b66ab7 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -231,6 +231,7 @@
       case SSL3_ST_SR_CLNT_HELLO_B:
       case SSL3_ST_SR_CLNT_HELLO_C:
       case SSL3_ST_SR_CLNT_HELLO_D:
+      case SSL3_ST_SR_CLNT_HELLO_E:
         ret = ssl3_get_client_hello(ssl);
         if (ssl->state == SSL_ST_TLS13) {
           break;
@@ -415,7 +416,7 @@
 
       case SSL3_ST_SW_SESSION_TICKET_A:
       case SSL3_ST_SW_SESSION_TICKET_B:
-        if (ssl->tlsext_ticket_expected) {
+        if (ssl->s3->hs->ticket_expected) {
           ret = ssl3_send_new_session_ticket(ssl);
           if (ret <= 0) {
             goto end;
@@ -476,18 +477,16 @@
         break;
 
       case SSL_ST_OK:
-        /* Clean a few things up. */
-        ssl3_cleanup_key_block(ssl);
         ssl->method->release_current_message(ssl, 1 /* free_buffer */);
 
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
         if (ssl->s3->new_session != NULL &&
-            ssl->ctx->retain_only_sha256_of_client_certs) {
-          X509_free(ssl->s3->new_session->peer);
-          ssl->s3->new_session->peer = NULL;
-          sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
-          ssl->s3->new_session->cert_chain = NULL;
+            ssl->retain_only_sha256_of_client_certs) {
+          X509_free(ssl->s3->new_session->x509_peer);
+          ssl->s3->new_session->x509_peer = NULL;
+          sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+          ssl->s3->new_session->x509_chain = NULL;
         }
 
         SSL_SESSION_free(ssl->s3->established_session);
@@ -503,15 +502,13 @@
         /* remove buffering on output */
         ssl_free_wbio_buffer(ssl);
 
+        ssl->s3->initial_handshake_complete = 1;
+        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
+
         ssl_handshake_free(ssl->s3->hs);
         ssl->s3->hs = NULL;
 
-        ssl->s3->initial_handshake_complete = 1;
-
-        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
-
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
-
         ret = 1;
         goto end;
 
@@ -556,7 +553,7 @@
 }
 
 static int negotiate_version(
-    SSL *ssl, int *out_alert,
+    SSL *ssl, uint8_t *out_alert,
     const struct ssl_early_callback_ctx *client_hello) {
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
@@ -578,6 +575,9 @@
       return 0;
     }
 
+    /* Choose the newest commonly-supported version advertised by the client.
+     * The client orders the versions according to its preferences, but we're
+     * not required to honor the client's preferences. */
     int found_version = 0;
     while (CBS_len(&versions) != 0) {
       uint16_t ext_version;
@@ -590,10 +590,10 @@
         continue;
       }
       if (min_version <= ext_version &&
-          ext_version <= max_version) {
+          ext_version <= max_version &&
+          (!found_version || version < ext_version)) {
         version = ext_version;
         found_version = 1;
-        break;
       }
     }
 
@@ -662,7 +662,8 @@
 }
 
 static int ssl3_get_client_hello(SSL *ssl) {
-  int al = SSL_AD_INTERNAL_ERROR, ret = -1;
+  uint8_t al = SSL_AD_INTERNAL_ERROR;
+  int ret = -1;
   SSL_SESSION *session = NULL;
 
   if (ssl->state == SSL3_ST_SR_CLNT_HELLO_A) {
@@ -729,76 +730,6 @@
     memcpy(ssl->s3->client_random, client_hello.random,
            client_hello.random_len);
 
-    /* Determine whether we are doing session resumption. */
-    int send_new_ticket = 0;
-    switch (
-        ssl_get_prev_session(ssl, &session, &send_new_ticket, &client_hello)) {
-      case ssl_session_success:
-        break;
-      case ssl_session_error:
-        goto err;
-      case ssl_session_retry:
-        ssl->rwstate = SSL_PENDING_SESSION;
-        goto err;
-    }
-    ssl->tlsext_ticket_expected = send_new_ticket;
-
-    /* The EMS state is needed when making the resumption decision, but
-     * extensions are not normally parsed until later. This detects the EMS
-     * extension for the resumption decision and it's checked against the result
-     * of the normal parse later in this function. */
-    CBS ems;
-    int have_extended_master_secret =
-        ssl->version != SSL3_VERSION &&
-        ssl_early_callback_get_extension(&client_hello, &ems,
-                                         TLSEXT_TYPE_extended_master_secret) &&
-        CBS_len(&ems) == 0;
-
-    int has_session = 0;
-    if (session != NULL) {
-      if (session->extended_master_secret &&
-          !have_extended_master_secret) {
-        /* A ClientHello without EMS that attempts to resume a session with EMS
-         * is fatal to the connection. */
-        al = SSL_AD_HANDSHAKE_FAILURE;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
-        goto f_err;
-      }
-
-      has_session =
-          /* Only resume if the session's version matches the negotiated
-           * version: most clients do not accept a mismatch. */
-          ssl->version == session->ssl_version &&
-          /* If the client offers the EMS extension, but the previous session
-           * didn't use it, then negotiate a new session. */
-          have_extended_master_secret == session->extended_master_secret;
-    }
-
-    if (has_session) {
-      /* Use the old session. */
-      ssl->session = session;
-      session = NULL;
-      ssl->s3->session_reused = 1;
-    } else {
-      ssl_set_session(ssl, NULL);
-      if (!ssl_get_new_session(ssl, 1 /* server */)) {
-        goto err;
-      }
-
-      /* Clear the session ID if we want the session to be single-use. */
-      if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
-        ssl->s3->new_session->session_id_length = 0;
-      }
-    }
-
-    if (ssl->ctx->dos_protection_cb != NULL &&
-        ssl->ctx->dos_protection_cb(&client_hello) == 0) {
-      /* Connection rejected for DOS reasons. */
-      al = SSL_AD_INTERNAL_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-      goto f_err;
-    }
-
     /* Only null compression is supported. */
     if (memchr(client_hello.compression_methods, 0,
                client_hello.compression_methods_len) == NULL) {
@@ -813,30 +744,10 @@
       goto err;
     }
 
-    if (have_extended_master_secret != ssl->s3->tmp.extended_master_secret) {
-      al = SSL_AD_INTERNAL_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_EMS_STATE_INCONSISTENT);
-      goto f_err;
-    }
-
     ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
   }
 
-  /* Determine the remaining connection parameters. This is a separate state so
-   * |cert_cb| does not cause earlier logic to run multiple times. */
-  assert(ssl->state == SSL3_ST_SR_CLNT_HELLO_D);
-
-  if (ssl->session != NULL) {
-    /* Check that the cipher is in the list. */
-    if (!ssl_client_cipher_list_contains_cipher(
-            &client_hello, (uint16_t)ssl->session->cipher->id)) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_REQUIRED_CIPHER_MISSING);
-      goto f_err;
-    }
-
-    ssl->s3->tmp.new_cipher = ssl->session->cipher;
-  } else {
+  if (ssl->state == SSL3_ST_SR_CLNT_HELLO_D) {
     /* Call |cert_cb| to update server certificates if required. */
     if (ssl->cert->cert_cb != NULL) {
       int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -851,16 +762,92 @@
       }
     }
 
-    const SSL_CIPHER *c =
+    /* Negotiate the cipher suite. This must be done after |cert_cb| so the
+     * certificate is finalized. */
+    ssl->s3->tmp.new_cipher =
         ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
-    if (c == NULL) {
+    if (ssl->s3->tmp.new_cipher == NULL) {
       al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
       goto f_err;
     }
 
-    ssl->s3->new_session->cipher = c;
-    ssl->s3->tmp.new_cipher = c;
+    ssl->state = SSL3_ST_SR_CLNT_HELLO_E;
+  }
+
+  assert(ssl->state == SSL3_ST_SR_CLNT_HELLO_E);
+
+  /* Determine whether we are doing session resumption. */
+  int tickets_supported = 0, renew_ticket = 0;
+  switch (ssl_get_prev_session(ssl, &session, &tickets_supported, &renew_ticket,
+                               &client_hello)) {
+    case ssl_session_success:
+      break;
+    case ssl_session_error:
+      goto err;
+    case ssl_session_retry:
+      ssl->rwstate = SSL_PENDING_SESSION;
+      goto err;
+  }
+
+  if (session != NULL) {
+    if (session->extended_master_secret &&
+        !ssl->s3->tmp.extended_master_secret) {
+      /* A ClientHello without EMS that attempts to resume a session with EMS
+       * is fatal to the connection. */
+      al = SSL_AD_HANDSHAKE_FAILURE;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
+      goto f_err;
+    }
+
+    if (!ssl_session_is_resumable(ssl, session) ||
+        /* If the client offers the EMS extension, but the previous session
+         * didn't use it, then negotiate a new session. */
+        ssl->s3->tmp.extended_master_secret !=
+            session->extended_master_secret) {
+      SSL_SESSION_free(session);
+      session = NULL;
+    }
+  }
+
+  if (session != NULL) {
+    /* Use the old session. */
+    ssl->s3->hs->ticket_expected = renew_ticket;
+    ssl->session = session;
+    session = NULL;
+    ssl->s3->session_reused = 1;
+  } else {
+    ssl->s3->hs->ticket_expected = tickets_supported;
+    ssl_set_session(ssl, NULL);
+    if (!ssl_get_new_session(ssl, 1 /* server */)) {
+      goto err;
+    }
+
+    /* Clear the session ID if we want the session to be single-use. */
+    if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
+      ssl->s3->new_session->session_id_length = 0;
+    }
+  }
+
+  if (ssl->ctx->dos_protection_cb != NULL &&
+      ssl->ctx->dos_protection_cb(&client_hello) == 0) {
+    /* Connection rejected for DOS reasons. */
+    al = SSL_AD_INTERNAL_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+    goto f_err;
+  }
+
+  if (ssl->session == NULL) {
+    ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
+
+    /* On new sessions, stash the SNI value in the session. */
+    if (ssl->s3->hs->hostname != NULL) {
+      ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+      if (ssl->s3->new_session->tlsext_hostname == NULL) {
+        al = SSL_AD_INTERNAL_ERROR;
+        goto f_err;
+      }
+    }
 
     /* Determine whether to request a client certificate. */
     ssl->s3->hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
@@ -881,7 +868,13 @@
     }
   }
 
-  /* Now that the cipher is known, initialize the handshake hash. */
+  /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
+   * deferred. Complete it now. */
+  if (!ssl_negotiate_alpn(ssl, &al, &client_hello)) {
+    goto f_err;
+  }
+
+  /* Now that all parameters are known, initialize the handshake hash. */
   if (!ssl3_init_handshake_hash(ssl)) {
     goto f_err;
   }
@@ -953,7 +946,7 @@
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u8(&body, 0 /* no compression */) ||
       !ssl_add_serverhello_tlsext(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
@@ -992,7 +985,7 @@
       !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
       !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
                      ssl->ctx->ocsp_response_length) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
@@ -1173,7 +1166,7 @@
     }
   }
 
-  if (!ssl->method->finish_message(ssl, &cbb)) {
+  if (!ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -1255,7 +1248,7 @@
   }
 
   if (!ssl_add_client_CA_list(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -1275,7 +1268,7 @@
 
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
@@ -1320,7 +1313,7 @@
   CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
   uint8_t alert;
   STACK_OF(X509) *chain = ssl_parse_cert_chain(
-      ssl, &alert, ssl->ctx->retain_only_sha256_of_client_certs
+      ssl, &alert, ssl->retain_only_sha256_of_client_certs
                        ? ssl->s3->new_session->peer_sha256
                        : NULL,
       &certificate_msg);
@@ -1359,7 +1352,7 @@
     ssl->s3->new_session->verify_result = X509_V_OK;
   } else {
     /* The hash would have been filled in. */
-    if (ssl->ctx->retain_only_sha256_of_client_certs) {
+    if (ssl->retain_only_sha256_of_client_certs) {
       ssl->s3->new_session->peer_sha256_valid = 1;
     }
 
@@ -1369,12 +1362,12 @@
     }
   }
 
-  X509_free(ssl->s3->new_session->peer);
-  ssl->s3->new_session->peer = sk_X509_shift(chain);
+  X509_free(ssl->s3->new_session->x509_peer);
+  ssl->s3->new_session->x509_peer = sk_X509_shift(chain);
 
-  sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
-  ssl->s3->new_session->cert_chain = chain;
-  /* Inconsistency alert: cert_chain does *not* include the peer's own
+  sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+  ssl->s3->new_session->x509_chain = chain;
+  /* Inconsistency alert: x509_chain does *not* include the peer's own
    * certificate, while we do include it in s3_clnt.c */
 
   return 1;
@@ -1656,7 +1649,7 @@
 static int ssl3_get_cert_verify(SSL *ssl) {
   int al, ret = 0;
   CBS certificate_verify, signature;
-  X509 *peer = ssl->s3->new_session->peer;
+  X509 *peer = ssl->s3->new_session->x509_peer;
   EVP_PKEY *pkey = NULL;
 
   /* Only RSA and ECDSA client certificates are supported, so a
@@ -1750,7 +1743,7 @@
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
   ssl3_free_handshake_buffer(ssl);
-  if (!ssl->method->hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(ssl)) {
     goto err;
   }
 
@@ -1796,109 +1789,17 @@
 
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
 static int ssl3_get_channel_id(SSL *ssl) {
-  int ret = -1;
-  uint8_t channel_id_hash[EVP_MAX_MD_SIZE];
-  size_t channel_id_hash_len;
-  const uint8_t *p;
-  uint16_t extension_type;
-  EC_GROUP *p256 = NULL;
-  EC_KEY *key = NULL;
-  EC_POINT *point = NULL;
-  ECDSA_SIG sig;
-  BIGNUM x, y;
-  CBS encrypted_extensions, extension;
-
   int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID,
                                              ssl_dont_hash_message);
   if (msg_ret <= 0) {
     return msg_ret;
   }
 
-  /* Before incorporating the EncryptedExtensions message to the handshake
-   * hash, compute the hash that should have been signed. */
-  if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) {
+  if (!tls1_verify_channel_id(ssl) ||
+      !ssl_hash_current_message(ssl)) {
     return -1;
   }
-  assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
-
-  if (!ssl->method->hash_current_message(ssl)) {
-    return -1;
-  }
-
-  CBS_init(&encrypted_extensions, ssl->init_msg, ssl->init_num);
-
-  /* EncryptedExtensions could include multiple extensions, but the only
-   * extension that could be negotiated is Channel ID, so there can only be one
-   * entry. */
-  if (!CBS_get_u16(&encrypted_extensions, &extension_type) ||
-      !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) ||
-      CBS_len(&encrypted_extensions) != 0 ||
-      extension_type != TLSEXT_TYPE_channel_id ||
-      CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return -1;
-  }
-
-  p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-  if (!p256) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
-    return -1;
-  }
-
-  BN_init(&x);
-  BN_init(&y);
-  sig.r = BN_new();
-  sig.s = BN_new();
-  if (sig.r == NULL || sig.s == NULL) {
-    goto err;
-  }
-
-  p = CBS_data(&extension);
-  if (BN_bin2bn(p + 0, 32, &x) == NULL ||
-      BN_bin2bn(p + 32, 32, &y) == NULL ||
-      BN_bin2bn(p + 64, 32, sig.r) == NULL ||
-      BN_bin2bn(p + 96, 32, sig.s) == NULL) {
-    goto err;
-  }
-
-  point = EC_POINT_new(p256);
-  if (!point ||
-      !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
-    goto err;
-  }
-
-  key = EC_KEY_new();
-  if (!key || !EC_KEY_set_group(key, p256) ||
-      !EC_KEY_set_public_key(key, point)) {
-    goto err;
-  }
-
-  /* We stored the handshake hash in |tlsext_channel_id| the first time that we
-   * were called. */
-  int sig_ok = ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key);
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  sig_ok = 1;
-#endif
-  if (!sig_ok) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
-    ssl->s3->tlsext_channel_id_valid = 0;
-    goto err;
-  }
-
-  memcpy(ssl->s3->tlsext_channel_id, p, 64);
-  ret = 1;
-
-err:
-  BN_free(&x);
-  BN_free(&y);
-  BN_free(sig.r);
-  BN_free(sig.s);
-  EC_KEY_free(key);
-  EC_POINT_free(point);
-  EC_GROUP_free(p256);
-  return ret;
+  return 1;
 }
 
 static int ssl3_send_new_session_ticket(SSL *ssl) {
@@ -1906,20 +1807,37 @@
     return ssl->method->write_message(ssl);
   }
 
+  const SSL_SESSION *session;
+  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);
+    session = ssl->s3->new_session;
+  } else {
+    /* We are renewing an existing session. Duplicate the session to adjust the
+     * timeout. */
+    session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
+    if (session_copy == NULL) {
+      return -1;
+    }
+
+    ssl_session_refresh_time(ssl, session_copy);
+    session = session_copy;
+  }
+
   CBB cbb, body, ticket;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_NEW_SESSION_TICKET) ||
-      /* Ticket lifetime hint (advisory only): We leave this unspecified for
-       * resumed session (for simplicity), and guess that tickets for new
-       * sessions will live as long as their sessions. */
-      !CBB_add_u32(&body,
-                   ssl->session != NULL ? 0 : ssl->s3->new_session->timeout) ||
-      !CBB_add_u16_length_prefixed(&body, &ticket) ||
-      !ssl_encrypt_ticket(ssl, &ticket, ssl->session != NULL
-                                            ? ssl->session
-                                            : ssl->s3->new_session) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
-    return 0;
+  int ok =
+      ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) &&
+      CBB_add_u32(&body, session->timeout) &&
+      CBB_add_u16_length_prefixed(&body, &ticket) &&
+      ssl_encrypt_ticket(ssl, &ticket, session) &&
+      ssl_complete_message(ssl, &cbb);
+
+  SSL_SESSION_free(session_copy);
+  CBB_cleanup(&cbb);
+
+  if (!ok) {
+    return -1;
   }
 
   ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 7e9954d..1d78ec1 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -230,14 +230,12 @@
 
 /* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
  * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
- * |ssl_cipher_preference_list_st| containing the result.
- * |*out_cipher_list_by_id| is set to a list of selected ciphers sorted by
- * id. It returns |(*out_cipher_list)->ciphers| on success and NULL on
+ * |ssl_cipher_preference_list_st| containing the result. It returns
+ * |(*out_cipher_list)->ciphers| on success and NULL on
  * failure. */
 STACK_OF(SSL_CIPHER) *
 ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
                        struct ssl_cipher_preference_list_st **out_cipher_list,
-                       STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
                        const char *rule_str);
 
 /* ssl_cipher_get_value returns the cipher suite id of |cipher|. */
@@ -269,7 +267,7 @@
 
 /* SSL_AEAD_CTX contains information about an AEAD that is being used to encrypt
  * an SSL connection. */
-struct ssl_aead_ctx_st {
+typedef struct ssl_aead_ctx_st {
   const SSL_CIPHER *cipher;
   EVP_AEAD_CTX ctx;
   /* fixed_nonce contains any bytes of the nonce that are fixed for all
@@ -294,7 +292,7 @@
   /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
    * variable nonce rather than prepended. */
   char xor_fixed_nonce;
-} /* SSL_AEAD_CTX */;
+} SSL_AEAD_CTX;
 
 /* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
  * key material. It returns NULL on error. Only one of |SSL_AEAD_CTX_open| or
@@ -420,13 +418,6 @@
  * buffer-free APIs are available. */
 size_t ssl_seal_align_prefix_len(const SSL *ssl);
 
-/* ssl_max_seal_overhead returns the maximum overhead of sealing a record with
- * |ssl|.
- *
- * TODO(davidben): Expose this as part of public API once the high-level
- * buffer-free APIs are available. */
-size_t ssl_max_seal_overhead(const SSL *ssl);
-
 /* tls_seal_record seals a new record of type |type| and body |in| and writes it
  * to |out|. At most |max_out| bytes will be written. It returns one on success
  * and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1
@@ -434,7 +425,7 @@
  *
  * For a large record, the bulk of the ciphertext will begin
  * |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may
- * improve performance. It writes at most |in_len| + |ssl_max_seal_overhead|
+ * improve performance. It writes at most |in_len| + |SSL_max_seal_overhead|
  * bytes to |out|.
  *
  * |in| and |out| may not alias. */
@@ -557,9 +548,11 @@
 
 /* ECDH groups. */
 
+typedef struct ssl_ecdh_ctx_st SSL_ECDH_CTX;
+
 /* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
  * TLS. */
-struct ssl_ecdh_method_st {
+typedef struct ssl_ecdh_method_st {
   int nid;
   uint16_t group_id;
   const char name[8];
@@ -599,7 +592,12 @@
    * be passed to |offer| or |accept|. It returns one on success and zero on
    * error. */
   int (*add_key)(CBB *cbb, CBB *out_contents);
-} /* SSL_ECDH_METHOD */;
+} SSL_ECDH_METHOD;
+
+struct ssl_ecdh_ctx_st {
+  const SSL_ECDH_METHOD *method;
+  void *data;
+};
 
 /* ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
  * sets |*out_group_id| to the group ID and returns one. Otherwise, it returns
@@ -750,6 +748,10 @@
  * configured and zero otherwise. */
 int ssl_has_certificate(const SSL *ssl);
 
+/* ssl_parse_x509 parses a X509 certificate from |cbs|. It returns NULL
+ * on error. */
+X509 *ssl_parse_x509(CBS *cbs);
+
 /* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
  * by a TLS Certificate message. On success, it returns a newly-allocated
  * |X509| list and advances |cbs|. Otherwise, it returns NULL and sets
@@ -794,21 +796,19 @@
 /* TLS 1.3 key derivation. */
 
 /* tls13_init_key_schedule initializes the handshake hash and key derivation
- * state with the given resumption context. The cipher suite and PRF hash must
- * have been selected at this point. It returns one on success and zero on
- * error. */
-int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
-                            size_t resumption_ctx_len);
+ * state. The cipher suite and PRF hash must have been selected at this point.
+ * It returns one on success and zero on error. */
+int tls13_init_key_schedule(SSL *ssl);
 
 /* tls13_advance_key_schedule incorporates |in| into the key schedule with
  * HKDF-Extract. It returns one on success and zero on error. */
 int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len);
 
-/* tls13_get_context_hashes writes Hash(Handshake Context) +
- * Hash(resumption_context) to |out| which much have room for at least 2 *
- * |EVP_MAX_MD_SIZE| bytes. On success, it returns one and sets |*out_len| to
- * the number of bytes written. Otherwise, it returns zero. */
-int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len);
+/* tls13_get_context_hash writes Hash(Handshake Context) to |out| which must
+ * have room for at least |EVP_MAX_MD_SIZE| bytes. On success, it returns one
+ * and sets |*out_len| to the number of bytes written. Otherwise, it returns
+ * zero. */
+int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len);
 
 enum tls_record_type_t {
   type_early_handshake,
@@ -817,11 +817,9 @@
   type_data,
 };
 
-/* tls13_set_traffic_key sets the read or write traffic keys to |traffic_secret|
- * for the given traffic phase |type|. It returns one on success and zero on
- * error. */
-int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
-                          enum evp_aead_direction_t direction,
+/* tls13_set_traffic_key sets the read or write traffic keys to
+ * |traffic_secret|. It returns one on success and zero on error. */
+int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
                           const uint8_t *traffic_secret,
                           size_t traffic_secret_len);
 
@@ -834,15 +832,15 @@
  * returns one on success and zero on error. */
 int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction);
 
-/* tls13_derive_traffic_secret_0 derives the initial application data traffic
- * secret based on the handshake transcripts and |master_secret|. It returns one
- * on success and zero on error. */
-int tls13_derive_traffic_secret_0(SSL *ssl);
+/* tls13_derive_application_secrets derives the initial application data traffic
+ * and exporter secrets based on the handshake transcripts and |master_secret|.
+ * It returns one on success and zero on error. */
+int tls13_derive_application_secrets(SSL *ssl);
 
-/* tls13_finalize_keys derives the |exporter_secret| and |resumption_secret|. */
-int tls13_finalize_keys(SSL *ssl);
+/* tls13_derive_resumption_secret derives the |resumption_secret|. */
+int tls13_derive_resumption_secret(SSL *ssl);
 
-/* tls13_export_keying_material provides and exporter interface to use the
+/* tls13_export_keying_material provides an exporter interface to use the
  * |exporter_secret|. */
 int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
                                  const char *label, size_t label_len,
@@ -855,17 +853,15 @@
  * 0 for the Client Finished. */
 int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server);
 
-/* tls13_resumption_psk calculates the PSK to use for the resumption of
- * |session| and stores the result in |out|. It returns one on success, and
- * zero on failure. */
-int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
-                         const SSL_SESSION *session);
+/* tls13_write_psk_binder calculates the PSK binder value and replaces the last
+ * bytes of |msg| with the resulting value. It returns 1 on success, and 0 on
+ * failure. */
+int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len);
 
-/* tls13_resumption_context derives the context to be used for the handshake
- * transcript on the resumption of |session|. It returns one on success, and
- * zero on failure. */
-int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
-                             const SSL_SESSION *session);
+/* tls13_verify_psk_binder verifies that the handshake transcript, truncated
+ * up to the binders has a valid signature using the value of |session|'s
+ * resumption secret. It returns 1 on success, and 0 on failure. */
+int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session, CBS *binders);
 
 
 /* Handshake functions. */
@@ -878,10 +874,11 @@
   ssl_hs_flush,
   ssl_hs_flush_and_read_message,
   ssl_hs_x509_lookup,
+  ssl_hs_channel_id_lookup,
   ssl_hs_private_key_operation,
 };
 
-struct ssl_handshake_st {
+typedef struct ssl_handshake_st {
   /* wait contains the operation |do_handshake| is currently blocking on or
    * |ssl_hs_ok| if none. */
   enum ssl_hs_wait_t wait;
@@ -894,7 +891,6 @@
   int state;
 
   size_t hash_len;
-  uint8_t resumption_hash[EVP_MAX_MD_SIZE];
   uint8_t secret[EVP_MAX_MD_SIZE];
   uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE];
   uint8_t server_traffic_secret_0[EVP_MAX_MD_SIZE];
@@ -923,8 +919,19 @@
   /* ecdh_ctx is the current ECDH instance. */
   SSL_ECDH_CTX ecdh_ctx;
 
+  /* scts_requested is one if the SCT extension is in the ClientHello. */
+  unsigned scts_requested:1;
+
+  /* needs_psk_binder if the ClientHello has a placeholder PSK binder to be
+   * filled in. */
+  unsigned needs_psk_binder:1;
+
   unsigned received_hello_retry_request:1;
 
+  /* accept_psk_mode stores whether the client's PSK mode is compatible with our
+   * preferences. */
+  unsigned accept_psk_mode:1;
+
   /* retry_group is the group ID selected by the server in HelloRetryRequest in
    * TLS 1.3. */
   uint16_t retry_group;
@@ -992,6 +999,10 @@
   /* next_proto_neg_seen is one of NPN was negotiated. */
   unsigned next_proto_neg_seen:1;
 
+  /* ticket_expected is one if a TLS 1.2 NewSessionTicket message is to be sent
+   * or received. */
+  unsigned ticket_expected:1;
+
   /* peer_psk_identity_hint, on the client, is the psk_identity_hint sent by the
    * server when using a TLS 1.2 PSK key exchange. */
   char *peer_psk_identity_hint;
@@ -1004,7 +1015,14 @@
    * received in a CertificateRequest message. */
   uint8_t *certificate_types;
   size_t num_certificate_types;
-} /* SSL_HANDSHAKE */;
+
+  /* key_block is the record-layer key block for TLS 1.2 and earlier. */
+  uint8_t *key_block;
+  uint8_t key_block_len;
+
+  /* hostname, on the server, is the value of the SNI extension. */
+  char *hostname;
+} SSL_HANDSHAKE;
 
 SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
 
@@ -1051,15 +1069,56 @@
                                              CBS *contents);
 int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
                                              SSL_SESSION **out_session,
+                                             CBS *out_binders,
                                              uint8_t *out_alert, CBS *contents);
 int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out);
 
-int ssl_add_client_hello_body(SSL *ssl, CBB *body);
+/* ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
+ * returns one iff it's valid. */
+int ssl_is_sct_list_valid(const CBS *contents);
+
+int ssl_write_client_hello(SSL *ssl);
 
 /* ssl_clear_tls13_state releases client state only needed for TLS 1.3. It
  * should be called once the version is known to be TLS 1.2 or earlier. */
 void ssl_clear_tls13_state(SSL *ssl);
 
+enum ssl_cert_verify_context_t {
+  ssl_cert_verify_server,
+  ssl_cert_verify_client,
+  ssl_cert_verify_channel_id,
+};
+
+/* tls13_get_cert_verify_signature_input generates the message to be signed for
+ * TLS 1.3's CertificateVerify message. |cert_verify_context| determines the
+ * type of signature. It sets |*out| and |*out_len| to a newly allocated buffer
+ * containing the result. The caller must free it with |OPENSSL_free| to release
+ * it. This function returns one on success and zero on failure. */
+int tls13_get_cert_verify_signature_input(
+    SSL *ssl, uint8_t **out, size_t *out_len,
+    enum ssl_cert_verify_context_t cert_verify_context);
+
+/* ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
+ * one on successful negotiation or if nothing was negotiated. It returns zero
+ * and sets |*out_alert| to an alert on error. */
+int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
+                       const struct ssl_early_callback_ctx *client_hello);
+
+typedef struct {
+  uint16_t type;
+  int *out_present;
+  CBS *out_data;
+} SSL_EXTENSION_TYPE;
+
+/* ssl_parse_extensions parses a TLS extensions block out of |cbs| and advances
+ * it. It writes the parsed extensions to pointers denoted by |ext_types|. On
+ * success, it fills in the |out_present| and |out_data| fields and returns one.
+ * Otherwise, it sets |*out_alert| to an alert to send and returns zero. Unknown
+ * extensions are rejected. */
+int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
+                         const SSL_EXTENSION_TYPE *ext_types,
+                         size_t num_ext_types);
+
 
 /* SSLKEYLOGFILE functions. */
 
@@ -1147,10 +1206,9 @@
 };
 
 typedef struct cert_st {
-  X509 *x509;
   EVP_PKEY *privatekey;
-  /* Chain for this certificate */
-  STACK_OF(X509) *chain;
+  X509 *x509_leaf;
+  STACK_OF(X509) *x509_chain;
 
   /* key_method, if non-NULL, is a set of callbacks to call for private key
    * operations. */
@@ -1220,10 +1278,9 @@
    * Otherwise, it returns <= 0. */
   int (*ssl_get_message)(SSL *ssl, int msg_type,
                          enum ssl_hash_message_t hash_message);
-  /* hash_current_message incorporates the current handshake message into the
-   * handshake hash. It returns one on success and zero on allocation
-   * failure. */
-  int (*hash_current_message)(SSL *ssl);
+  /* get_current_message sets |*out| to the current handshake message. This
+   * includes the protocol-specific message header. */
+  void (*get_current_message)(const SSL *ssl, CBS *out);
   /* release_current_message is called to release the current handshake message.
    * If |free_buffer| is one, buffers will also be released. */
   void (*release_current_message)(SSL *ssl, int free_buffer);
@@ -1245,9 +1302,15 @@
    * root CBB to be passed into |finish_message|. |*body| is set to a child CBB
    * the caller should write to. It returns one on success and zero on error. */
   int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-  /* finish_message finishes a handshake message and prepares it to be
-   * written. It returns one on success and zero on error. */
-  int (*finish_message)(SSL *ssl, CBB *cbb);
+  /* finish_message finishes a handshake message. It sets |*out_msg| to a
+   * newly-allocated buffer with the serialized message. The caller must
+   * release it with |OPENSSL_free| when done. It returns one on success and
+   * zero on error. */
+  int (*finish_message)(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
+  /* queue_message queues a handshake message and prepares it to be written. It
+   * takes ownership of |msg| and releases it with |OPENSSL_free| when done. It
+   * returns one on success and zero on error. */
+  int (*queue_message)(SSL *ssl, uint8_t *msg, size_t len);
   /* write_message writes the next message to the transport. It returns one on
    * success and <= 0 on error. */
   int (*write_message)(SSL *ssl);
@@ -1271,7 +1334,7 @@
 
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
  * of a mess of functions, but hell, think of it as an opaque structure. */
-struct ssl3_enc_method {
+typedef struct ssl3_enc_method {
   /* prf computes the PRF function for |ssl|. It writes |out_len| bytes to
    * |out|, using |secret| as the secret and |label| as the label. |seed1| and
    * |seed2| are concatenated to form the seed parameter. It returns one on
@@ -1281,7 +1344,7 @@
              size_t label_len, const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len);
   int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out);
-};
+} SSL3_ENC_METHOD;
 
 typedef struct ssl3_record_st {
   /* type is the record type. */
@@ -1415,9 +1478,6 @@
 
     int reuse_message;
 
-    uint8_t *key_block;
-    uint8_t key_block_length;
-
     uint8_t new_mac_secret_len;
     uint8_t new_key_len;
     uint8_t new_fixed_iv_len;
@@ -1569,12 +1629,9 @@
 extern const SSL3_ENC_METHOD TLSv1_enc_data;
 extern const SSL3_ENC_METHOD SSLv3_enc_data;
 
-/* From draft-ietf-tls-tls13-16, used in determining PSK modes. */
-#define SSL_PSK_KE        0x0
-#define SSL_PSK_DHE_KE    0x1
-
-#define SSL_PSK_AUTH      0x0
-#define SSL_PSK_SIGN_AUTH 0x1
+/* From draft-ietf-tls-tls13-18, used in determining PSK modes. */
+#define SSL_PSK_KE     0x0
+#define SSL_PSK_DHE_KE 0x1
 
 /* From draft-ietf-tls-tls13-16, used in determining whether to respond with a
  * KeyUpdate. */
@@ -1596,6 +1653,10 @@
  * it has expired. */
 int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session);
 
+/* ssl_session_is_resumable returns one if |session| is resumable for |ssl| and
+ * zero otherwise. */
+int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session);
+
 void ssl_set_session(SSL *ssl, SSL_SESSION *session);
 
 enum ssl_session_result_t {
@@ -1605,14 +1666,13 @@
 };
 
 /* ssl_get_prev_session looks up the previous session based on |ctx|. On
- * success, it sets |*out_session| to the session or NULL if none was found. It
- * sets |*out_send_ticket| to whether a ticket should be sent at the end of the
- * handshake. If the session could not be looked up synchronously, it returns
+ * success, it sets |*out_session| to the session or NULL if none was found. If
+ * the session could not be looked up synchronously, it returns
  * |ssl_session_retry| and should be called again. Otherwise, it returns
  * |ssl_session_error|.  */
 enum ssl_session_result_t ssl_get_prev_session(
-    SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
-    const struct ssl_early_callback_ctx *ctx);
+    SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
+    int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx);
 
 /* The following flags determine which parts of the session are duplicated. */
 #define SSL_SESSION_DUP_AUTH_ONLY 0x0
@@ -1627,9 +1687,17 @@
 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,
+ * adjusting the timeout so the expiration time is unchanged. */
+void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session);
+
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list);
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl);
+
+/* ssl_get_cipher_preferences returns the cipher preference list for TLS 1.2 and
+ * below. */
+const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(
+    const SSL *ssl);
 
 int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain);
 int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain);
@@ -1649,16 +1717,14 @@
 void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
                                        uint32_t *out_mask_a);
 
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl);
 int ssl_verify_alarm_type(long type);
 
 int ssl3_get_finished(SSL *ssl);
 int ssl3_send_change_cipher_spec(SSL *ssl);
-void ssl3_cleanup_key_block(SSL *ssl);
 int ssl3_send_alert(SSL *ssl, int level, int desc);
 int ssl3_get_message(SSL *ssl, int msg_type,
                      enum ssl_hash_message_t hash_message);
-int ssl3_hash_current_message(SSL *ssl);
+void ssl3_get_current_message(const SSL *ssl, CBS *out);
 void ssl3_release_current_message(SSL *ssl, int free_buffer);
 
 /* ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify hash into the
@@ -1679,6 +1745,7 @@
 int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
 int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
 int ssl3_output_cert_chain(SSL *ssl);
+
 const SSL_CIPHER *ssl3_choose_cipher(
     SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
     const struct ssl_cipher_preference_list_st *srvr);
@@ -1689,16 +1756,27 @@
 int ssl3_connect(SSL *ssl);
 
 int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-int ssl3_finish_message(SSL *ssl, CBB *cbb);
+int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
+int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len);
 int ssl3_write_message(SSL *ssl);
 
 void ssl3_expect_flight(SSL *ssl);
 void ssl3_received_flight(SSL *ssl);
 
 int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-int dtls1_finish_message(SSL *ssl, CBB *cbb);
+int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+                         size_t *out_len);
+int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len);
 int dtls1_write_message(SSL *ssl);
 
+/* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to
+ * queue the message for writing. */
+int ssl_complete_message(SSL *ssl, CBB *cbb);
+
+/* ssl_hash_current_message incorporates the current handshake message into the
+ * handshake hash. It returns one on success and zero on allocation failure. */
+int ssl_hash_current_message(SSL *ssl);
+
 /* dtls1_get_record reads a new input record. On success, it places it in
  * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
  * more data is needed. */
@@ -1740,7 +1818,7 @@
 void dtls1_free(SSL *ssl);
 
 int dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message);
-int dtls1_hash_current_message(SSL *ssl);
+void dtls1_get_current_message(const SSL *ssl, CBS *out);
 void dtls1_release_current_message(SSL *ssl, int free_buffer);
 int dtls1_dispatch_alert(SSL *ssl);
 
@@ -1752,7 +1830,6 @@
 void ssl_free_wbio_buffer(SSL *ssl);
 
 int tls1_change_cipher_state(SSL *ssl, int which);
-int tls1_setup_key_block(SSL *ssl);
 int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
                                 size_t premaster_len);
@@ -1808,6 +1885,16 @@
                        size_t ticket_len, const uint8_t *session_id,
                        size_t session_id_len);
 
+/* tls1_verify_channel_id processes the current message as a Channel ID message,
+ * and verifies the signature. If the key is valid, it saves the Channel ID and
+ * returns one. Otherwise, it returns zero. */
+int tls1_verify_channel_id(SSL *ssl);
+
+/* tls1_write_channel_id generates a Channel ID message and puts the output in
+ * |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling.
+ * This function returns one on success and zero on error. */
+int tls1_write_channel_id(SSL *ssl, CBB *cbb);
+
 /* tls1_channel_id_hash computes the hash to be signed by Channel ID and writes
  * it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns
  * one on success and zero on failure. */
@@ -1815,6 +1902,12 @@
 
 int tls1_record_handshake_hashes_for_channel_id(SSL *ssl);
 
+/* ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if
+ * necessary. It returns one on success and zero on fatal error. Note that, on
+ * success, |ssl->tlsext_channel_id_private| may be unset, in which case the
+ * operation should be retried later. */
+int ssl_do_channel_id_callback(SSL *ssl);
+
 /* ssl3_can_false_start returns one if |ssl| is allowed to False Start and zero
  * otherwise. */
 int ssl3_can_false_start(const SSL *ssl);
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 1e7e4e1..d872020 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -163,6 +163,13 @@
   OPENSSL_free(hs->peer_psk_identity_hint);
   sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
   OPENSSL_free(hs->certificate_types);
+
+  if (hs->key_block != NULL) {
+    OPENSSL_cleanse(hs->key_block, hs->key_block_len);
+    OPENSSL_free(hs->key_block);
+  }
+
+  OPENSSL_free(hs->hostname);
   OPENSSL_free(hs);
 }
 
@@ -199,18 +206,21 @@
   return 1;
 }
 
-int ssl3_finish_message(SSL *ssl, CBB *cbb) {
-  if (ssl->s3->pending_message != NULL) {
+int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+                        size_t *out_len) {
+  if (!CBB_finish(cbb, out_msg, out_len)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  uint8_t *msg = NULL;
-  size_t len;
-  if (!CBB_finish(cbb, &msg, &len) ||
+  return 1;
+}
+
+int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
+  if (ssl->s3->pending_message != NULL ||
       len > 0xffffffffu) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     OPENSSL_free(msg);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
@@ -221,6 +231,17 @@
   return 1;
 }
 
+int ssl_complete_message(SSL *ssl, CBB *cbb) {
+  uint8_t *msg;
+  size_t len;
+  if (!ssl->method->finish_message(ssl, cbb, &msg, &len) ||
+      !ssl->method->queue_message(ssl, msg, len)) {
+    return 0;
+  }
+
+  return 1;
+}
+
 int ssl3_write_message(SSL *ssl) {
   if (ssl->s3->pending_message == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -278,7 +299,7 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, finished, finished_len) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
@@ -300,7 +321,7 @@
   size_t finished_len =
       ssl->s3->enc_method->final_finish_mac(ssl, !ssl->server, finished);
   if (finished_len == 0 ||
-      !ssl->method->hash_current_message(ssl)) {
+      !ssl_hash_current_message(ssl)) {
     return -1;
   }
 
@@ -346,7 +367,7 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
       !ssl_add_cert_chain(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return 0;
@@ -640,16 +661,21 @@
   }
 
   /* Feed this message into MAC computation. */
-  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
+  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
     return -1;
   }
 
   return 1;
 }
 
-int ssl3_hash_current_message(SSL *ssl) {
-  return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
-                                    ssl->init_buf->length);
+void ssl3_get_current_message(const SSL *ssl, CBS *out) {
+  CBS_init(out, (uint8_t *)ssl->init_buf->data, ssl->init_buf->length);
+}
+
+int ssl_hash_current_message(SSL *ssl) {
+  CBS cbs;
+  ssl->method->get_current_message(ssl, &cbs);
+  return ssl3_update_handshake_hash(ssl, CBS_data(&cbs), CBS_len(&cbs));
 }
 
 void ssl3_release_current_message(SSL *ssl, int free_buffer) {
@@ -742,3 +768,51 @@
 
   return al;
 }
+
+int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
+                         const SSL_EXTENSION_TYPE *ext_types,
+                         size_t num_ext_types) {
+  /* Reset everything. */
+  for (size_t i = 0; i < num_ext_types; i++) {
+    *ext_types[i].out_present = 0;
+    CBS_init(ext_types[i].out_data, NULL, 0);
+  }
+
+  CBS copy = *cbs;
+  while (CBS_len(&copy) != 0) {
+    uint16_t type;
+    CBS data;
+    if (!CBS_get_u16(&copy, &type) ||
+        !CBS_get_u16_length_prefixed(&copy, &data)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return 0;
+    }
+
+    const SSL_EXTENSION_TYPE *ext_type = NULL;
+    for (size_t i = 0; i < num_ext_types; i++) {
+      if (type == ext_types[i].type) {
+        ext_type = &ext_types[i];
+        break;
+      }
+    }
+
+    if (ext_type == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+      *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
+      return 0;
+    }
+
+    /* Duplicate ext_types are forbidden. */
+    if (*ext_type->out_present) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+      return 0;
+    }
+
+    *ext_type->out_present = 1;
+    *ext_type->out_data = data;
+  }
+
+  return 1;
+}
diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c
index 2209e80..7cdc294 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/s3_enc.c
@@ -209,15 +209,6 @@
   return 1;
 }
 
-void ssl3_cleanup_key_block(SSL *ssl) {
-  if (ssl->s3->tmp.key_block != NULL) {
-    OPENSSL_cleanse(ssl->s3->tmp.key_block, ssl->s3->tmp.key_block_length);
-    OPENSSL_free(ssl->s3->tmp.key_block);
-    ssl->s3->tmp.key_block = NULL;
-  }
-  ssl->s3->tmp.key_block_length = 0;
-}
-
 int ssl3_init_handshake_buffer(SSL *ssl) {
   ssl3_free_handshake_buffer(ssl);
   ssl3_free_handshake_hash(ssl);
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 69d3a9d..901b8af 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -200,7 +200,6 @@
     return;
   }
 
-  ssl3_cleanup_key_block(ssl);
   ssl_read_buffer_clear(ssl);
   ssl_write_buffer_clear(ssl);
 
@@ -220,7 +219,8 @@
   ssl->s3 = NULL;
 }
 
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl) {
+const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(
+    const SSL *ssl) {
   if (ssl->cipher_list != NULL) {
     return ssl->cipher_list;
   }
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index fda9a25..e4116fb 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -245,8 +245,8 @@
 static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
                               unsigned int len) {
   if (ssl->s3->wpend_tot > (int)len ||
-      (ssl->s3->wpend_buf != buf &&
-       !(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
+      (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) &&
+       ssl->s3->wpend_buf != buf) ||
       ssl->s3->wpend_type != type) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
     return -1;
@@ -284,7 +284,7 @@
     return 0;
   }
 
-  size_t max_out = len + ssl_max_seal_overhead(ssl);
+  size_t max_out = len + SSL_max_seal_overhead(ssl);
   if (max_out < len) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return -1;
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 0f6f64f..b05df0b 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -34,8 +34,12 @@
                                const uint8_t *mac_key, size_t mac_key_len,
                                const uint8_t *fixed_iv, size_t fixed_iv_len) {
   const EVP_AEAD *aead;
-  size_t discard;
-  if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, cipher, version)) {
+  size_t expected_mac_key_len, expected_fixed_iv_len;
+  if (!ssl_cipher_get_evp_aead(&aead, &expected_mac_key_len,
+                               &expected_fixed_iv_len, cipher, version) ||
+      /* Ensure the caller returned correct key sizes. */
+      expected_fixed_iv_len != fixed_iv_len ||
+      expected_mac_key_len != mac_key_len) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index eeea826..2a7b7d4 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -97,13 +97,13 @@
 /* An SSL_SESSION is serialized as the following ASN.1 structure:
  *
  * SSLSession ::= SEQUENCE {
- *     version                     INTEGER (1),  -- ignored
+ *     version                     INTEGER (1),  -- session structure version
  *     sslVersion                  INTEGER,      -- protocol version number
  *     cipher                      OCTET STRING, -- two bytes long
  *     sessionID                   OCTET STRING,
  *     masterKey                   OCTET STRING,
- *     time                    [1] INTEGER OPTIONAL, -- seconds since UNIX epoch
- *     timeout                 [2] INTEGER OPTIONAL, -- in seconds
+ *     time                    [1] INTEGER, -- seconds since UNIX epoch
+ *     timeout                 [2] INTEGER, -- in seconds
  *     peer                    [3] Certificate OPTIONAL,
  *     sessionIDContext        [4] OCTET STRING OPTIONAL,
  *     verifyResult            [5] INTEGER OPTIONAL,  -- one of X509_V_* codes
@@ -196,30 +196,36 @@
     goto err;
   }
 
-  if (in->time != 0) {
-    if (!CBB_add_asn1(&session, &child, kTimeTag) ||
-        !CBB_add_asn1_uint64(&child, in->time)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
+  if (in->time < 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+    goto err;
   }
 
-  if (in->timeout != 0) {
-    if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
-        !CBB_add_asn1_uint64(&child, in->timeout)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
+  if (!CBB_add_asn1(&session, &child, kTimeTag) ||
+      !CBB_add_asn1_uint64(&child, in->time)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (in->timeout < 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+
+  if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
+      !CBB_add_asn1_uint64(&child, in->timeout)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
   }
 
   /* The peer certificate is only serialized if the SHA-256 isn't
    * serialized instead. */
-  if (in->peer && !in->peer_sha256_valid) {
+  if (in->x509_peer && !in->peer_sha256_valid) {
     if (!CBB_add_asn1(&session, &child, kPeerTag)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
-    if (!ssl_add_cert_to_cbb(&child, in->peer)) {
+    if (!ssl_add_cert_to_cbb(&child, in->x509_peer)) {
       goto err;
     }
   }
@@ -334,13 +340,13 @@
 
   /* The certificate chain is only serialized if the leaf's SHA-256 isn't
    * serialized instead. */
-  if (in->cert_chain != NULL && !in->peer_sha256_valid) {
+  if (in->x509_chain != NULL && !in->peer_sha256_valid) {
     if (!CBB_add_asn1(&session, &child, kCertChainTag)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
-    for (size_t i = 0; i < sk_X509_num(in->cert_chain); i++) {
-      if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->cert_chain, i))) {
+    for (size_t i = 0; i < sk_X509_num(in->x509_chain); i++) {
+      if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->x509_chain, i))) {
         goto err;
       }
     }
@@ -505,20 +511,6 @@
   return 1;
 }
 
-static X509 *parse_x509(CBS *cbs) {
-  if (CBS_len(cbs) > LONG_MAX) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
-    return NULL;
-  }
-  const uint8_t *ptr = CBS_data(cbs);
-  X509 *ret = d2i_X509(NULL, &ptr, (long)CBS_len(cbs));
-  if (ret == NULL) {
-    return NULL;
-  }
-  CBS_skip(cbs, ptr - CBS_data(cbs));
-  return ret;
-}
-
 static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
   SSL_SESSION *ret = SSL_SESSION_new();
   if (ret == NULL) {
@@ -563,23 +555,32 @@
   memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key));
   ret->master_key_length = CBS_len(&master_key);
 
-  if (!SSL_SESSION_parse_long(&session, &ret->time, kTimeTag, time(NULL)) ||
-      !SSL_SESSION_parse_long(&session, &ret->timeout, kTimeoutTag, 3)) {
+  CBS child;
+  uint64_t time, timeout;
+  if (!CBS_get_asn1(&session, &child, kTimeTag) ||
+      !CBS_get_asn1_uint64(&child, &time) ||
+      time > LONG_MAX ||
+      !CBS_get_asn1(&session, &child, kTimeoutTag) ||
+      !CBS_get_asn1_uint64(&child, &timeout) ||
+      timeout > LONG_MAX) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
   }
 
+  ret->time = (long)time;
+  ret->timeout = (long)timeout;
+
   CBS peer;
   int has_peer;
   if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
   }
-  X509_free(ret->peer);
-  ret->peer = NULL;
+  X509_free(ret->x509_peer);
+  ret->x509_peer = NULL;
   if (has_peer) {
-    ret->peer = parse_x509(&peer);
-    if (ret->peer == NULL) {
+    ret->x509_peer = ssl_parse_x509(&peer);
+    if (ret->x509_peer == NULL) {
       goto err;
     }
     if (CBS_len(&peer) != 0) {
@@ -605,7 +606,7 @@
   }
 
   if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) {
-    CBS child, peer_sha256;
+    CBS peer_sha256;
     if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) ||
         !CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) ||
         CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) ||
@@ -655,20 +656,20 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
   }
-  sk_X509_pop_free(ret->cert_chain, X509_free);
-  ret->cert_chain = NULL;
+  sk_X509_pop_free(ret->x509_chain, X509_free);
+  ret->x509_chain = NULL;
   if (has_cert_chain) {
-    ret->cert_chain = sk_X509_new_null();
-    if (ret->cert_chain == NULL) {
+    ret->x509_chain = sk_X509_new_null();
+    if (ret->x509_chain == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
     while (CBS_len(&cert_chain) > 0) {
-      X509 *x509 = parse_x509(&cert_chain);
+      X509 *x509 = ssl_parse_x509(&cert_chain);
       if (x509 == NULL) {
         goto err;
       }
-      if (!sk_X509_push(ret->cert_chain, x509)) {
+      if (!sk_X509_push(ret->x509_chain, x509)) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
         X509_free(x509);
         goto err;
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 55b464f..7f6cf63 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -115,6 +115,7 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/bn.h>
@@ -158,9 +159,9 @@
   }
   memset(ret, 0, sizeof(CERT));
 
-  if (cert->x509 != NULL) {
-    X509_up_ref(cert->x509);
-    ret->x509 = cert->x509;
+  if (cert->x509_leaf != NULL) {
+    X509_up_ref(cert->x509_leaf);
+    ret->x509_leaf = cert->x509_leaf;
   }
 
   if (cert->privatekey != NULL) {
@@ -168,9 +169,9 @@
     ret->privatekey = cert->privatekey;
   }
 
-  if (cert->chain) {
-    ret->chain = X509_chain_up_ref(cert->chain);
-    if (!ret->chain) {
+  if (cert->x509_chain) {
+    ret->x509_chain = X509_chain_up_ref(cert->x509_chain);
+    if (!ret->x509_chain) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
@@ -220,12 +221,12 @@
     return;
   }
 
-  X509_free(cert->x509);
-  cert->x509 = NULL;
+  X509_free(cert->x509_leaf);
+  cert->x509_leaf = NULL;
   EVP_PKEY_free(cert->privatekey);
   cert->privatekey = NULL;
-  sk_X509_pop_free(cert->chain, X509_free);
-  cert->chain = NULL;
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = NULL;
   cert->key_method = NULL;
 }
 
@@ -244,8 +245,8 @@
 }
 
 int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
-  sk_X509_pop_free(cert->chain, X509_free);
-  cert->chain = chain;
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = chain;
   return 1;
 }
 
@@ -269,10 +270,10 @@
 }
 
 int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
-  if (cert->chain == NULL) {
-    cert->chain = sk_X509_new_null();
+  if (cert->x509_chain == NULL) {
+    cert->x509_chain = sk_X509_new_null();
   }
-  if (cert->chain == NULL || !sk_X509_push(cert->chain, x509)) {
+  if (cert->x509_chain == NULL || !sk_X509_push(cert->x509_chain, x509)) {
     return 0;
   }
 
@@ -443,7 +444,21 @@
 }
 
 int ssl_has_certificate(const SSL *ssl) {
-  return ssl->cert->x509 != NULL && ssl_has_private_key(ssl);
+  return ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl);
+}
+
+X509 *ssl_parse_x509(CBS *cbs) {
+  if (CBS_len(cbs) > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return NULL;
+  }
+  const uint8_t *ptr = CBS_data(cbs);
+  X509 *ret = d2i_X509(NULL, &ptr, (long)CBS_len(cbs));
+  if (ret == NULL) {
+    return NULL;
+  }
+  CBS_skip(cbs, ptr - CBS_data(cbs));
+  return ret;
 }
 
 STACK_OF(X509) *ssl_parse_cert_chain(SSL *ssl, uint8_t *out_alert,
@@ -476,10 +491,8 @@
       SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
     }
 
-    /* A u24 length cannot overflow a long. */
-    const uint8_t *data = CBS_data(&certificate);
-    x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
-    if (x == NULL || data != CBS_data(&certificate) + CBS_len(&certificate)) {
+    x = ssl_parse_x509(&certificate);
+    if (x == NULL || CBS_len(&certificate) != 0) {
       *out_alert = SSL_AD_DECODE_ERROR;
       goto err;
     }
@@ -528,7 +541,7 @@
   }
 
   CERT *cert = ssl->cert;
-  X509 *x = cert->x509;
+  X509 *x = cert->x509_leaf;
 
   CBB child;
   if (!CBB_add_u24_length_prefixed(cbb, &child)) {
@@ -537,7 +550,7 @@
   }
 
   int no_chain = 0;
-  STACK_OF(X509) *chain = cert->chain;
+  STACK_OF(X509) *chain = cert->x509_chain;
   if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || chain != NULL) {
     no_chain = 1;
   }
@@ -763,7 +776,7 @@
 }
 
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
-  *out_chain = ctx->cert->chain;
+  *out_chain = ctx->cert->x509_chain;
   return 1;
 }
 
@@ -773,7 +786,7 @@
 }
 
 int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
-  *out_chain = ssl->cert->chain;
+  *out_chain = ssl->cert->x509_chain;
   return 1;
 }
 
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 6d48c89..99aba72 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -738,9 +738,6 @@
     {"TLSv1", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
     {"TLSv1.2", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
 
-    /* AEAD-only ciphers for TLS 1.3. */
-    {"GENERIC", SSL_kGENERIC, SSL_aGENERIC, ~0u, ~0u, 0},
-
     /* Legacy strength classes. */
     {"HIGH", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
     {"FIPS", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
@@ -761,10 +758,6 @@
   }
 }
 
-static int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **a, const SSL_CIPHER **b) {
-  return ssl_cipher_id_cmp(*a, *b);
-}
-
 const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
   SSL_CIPHER c;
 
@@ -781,125 +774,93 @@
   *out_mac_secret_len = 0;
   *out_fixed_iv_len = 0;
 
-  switch (cipher->algorithm_enc) {
-    case SSL_AES128GCM:
+  if (cipher->algorithm_mac == SSL_AEAD) {
+    if (cipher->algorithm_enc == SSL_AES128GCM) {
       *out_aead = EVP_aead_aes_128_gcm();
       *out_fixed_iv_len = 4;
-      break;
-
-    case SSL_AES256GCM:
+    } else if (cipher->algorithm_enc == SSL_AES256GCM) {
       *out_aead = EVP_aead_aes_256_gcm();
       *out_fixed_iv_len = 4;
-      break;
-
 #if !defined(BORINGSSL_ANDROID_SYSTEM)
-    case SSL_CHACHA20POLY1305_OLD:
+    } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305_OLD) {
       *out_aead = EVP_aead_chacha20_poly1305_old();
       *out_fixed_iv_len = 0;
-      break;
 #endif
-
-    case SSL_CHACHA20POLY1305:
+    } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305) {
       *out_aead = EVP_aead_chacha20_poly1305();
       *out_fixed_iv_len = 12;
-      break;
-
-    case SSL_AES128:
-      switch (cipher->algorithm_mac) {
-        case SSL_SHA1:
-          if (version == SSL3_VERSION) {
-            *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3();
-            *out_fixed_iv_len = 16;
-          } else if (version == TLS1_VERSION) {
-            *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
-            *out_fixed_iv_len = 16;
-          } else {
-            *out_aead = EVP_aead_aes_128_cbc_sha1_tls();
-          }
-          *out_mac_secret_len = SHA_DIGEST_LENGTH;
-          break;
-        case SSL_SHA256:
-          *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
-          *out_mac_secret_len = SHA256_DIGEST_LENGTH;
-          break;
-        default:
-          return 0;
-      }
-      break;
-
-    case SSL_AES256:
-      switch (cipher->algorithm_mac) {
-        case SSL_SHA1:
-          if (version == SSL3_VERSION) {
-            *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3();
-            *out_fixed_iv_len = 16;
-          } else if (version == TLS1_VERSION) {
-            *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
-            *out_fixed_iv_len = 16;
-          } else {
-            *out_aead = EVP_aead_aes_256_cbc_sha1_tls();
-          }
-          *out_mac_secret_len = SHA_DIGEST_LENGTH;
-          break;
-        case SSL_SHA256:
-          *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
-          *out_mac_secret_len = SHA256_DIGEST_LENGTH;
-          break;
-        case SSL_SHA384:
-          *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
-          *out_mac_secret_len = SHA384_DIGEST_LENGTH;
-          break;
-        default:
-          return 0;
-      }
-      break;
-
-    case SSL_3DES:
-      switch (cipher->algorithm_mac) {
-        case SSL_SHA1:
-          if (version == SSL3_VERSION) {
-            *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3();
-            *out_fixed_iv_len = 8;
-          } else if (version == TLS1_VERSION) {
-            *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
-            *out_fixed_iv_len = 8;
-          } else {
-            *out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
-          }
-          *out_mac_secret_len = SHA_DIGEST_LENGTH;
-          break;
-        default:
-          return 0;
-      }
-      break;
-
-    case SSL_eNULL:
-      switch (cipher->algorithm_mac) {
-        case SSL_SHA1:
-          if (version == SSL3_VERSION) {
-            *out_aead = EVP_aead_null_sha1_ssl3();
-          } else {
-            *out_aead = EVP_aead_null_sha1_tls();
-          }
-          *out_mac_secret_len = SHA_DIGEST_LENGTH;
-          break;
-        default:
-          return 0;
-      }
-      break;
-
-    default:
+    } else {
       return 0;
+    }
+
+    /* In TLS 1.3, the iv_len is equal to the AEAD nonce length whereas the code
+     * above computes the TLS 1.2 construction. */
+    if (version >= TLS1_3_VERSION) {
+      *out_fixed_iv_len = EVP_AEAD_nonce_length(*out_aead);
+    }
+  } else if (cipher->algorithm_mac == SSL_SHA1) {
+    if (cipher->algorithm_enc == SSL_eNULL) {
+      if (version == SSL3_VERSION) {
+        *out_aead = EVP_aead_null_sha1_ssl3();
+      } else {
+        *out_aead = EVP_aead_null_sha1_tls();
+      }
+    } else if (cipher->algorithm_enc == SSL_3DES) {
+      if (version == SSL3_VERSION) {
+        *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3();
+        *out_fixed_iv_len = 8;
+      } else if (version == TLS1_VERSION) {
+        *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
+        *out_fixed_iv_len = 8;
+      } else {
+        *out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
+      }
+    } else if (cipher->algorithm_enc == SSL_AES128) {
+      if (version == SSL3_VERSION) {
+        *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3();
+        *out_fixed_iv_len = 16;
+      } else if (version == TLS1_VERSION) {
+        *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
+        *out_fixed_iv_len = 16;
+      } else {
+        *out_aead = EVP_aead_aes_128_cbc_sha1_tls();
+      }
+    } else if (cipher->algorithm_enc == SSL_AES256) {
+      if (version == SSL3_VERSION) {
+        *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3();
+        *out_fixed_iv_len = 16;
+      } else if (version == TLS1_VERSION) {
+        *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
+        *out_fixed_iv_len = 16;
+      } else {
+        *out_aead = EVP_aead_aes_256_cbc_sha1_tls();
+      }
+    } else {
+      return 0;
+    }
+
+    *out_mac_secret_len = SHA_DIGEST_LENGTH;
+  } else if (cipher->algorithm_mac == SSL_SHA256) {
+    if (cipher->algorithm_enc == SSL_AES128) {
+      *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
+    } else if (cipher->algorithm_enc == SSL_AES256) {
+      *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
+    } else {
+      return 0;
+    }
+
+    *out_mac_secret_len = SHA256_DIGEST_LENGTH;
+  } else if (cipher->algorithm_mac == SSL_SHA384) {
+      if (cipher->algorithm_enc != SSL_AES256) {
+        return 0;
+      }
+
+      *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
+      *out_mac_secret_len = SHA384_DIGEST_LENGTH;
+  } else {
+    return 0;
   }
 
-  /* In TLS 1.3, the iv_len is equal to the AEAD nonce length whereas the code
-   * above computes the TLS 1.2 construction.
-   *
-   * TODO(davidben,svaldez): Avoid computing the wrong value and fixing it. */
-  if (version >= TLS1_3_VERSION) {
-    *out_fixed_iv_len = EVP_AEAD_nonce_length(*out_aead);
-    assert(*out_fixed_iv_len >= 8);
-  }
   return 1;
 }
 
@@ -975,7 +936,9 @@
   size_t co_list_num = 0;
   for (size_t i = 0; i < kCiphersLen; i++) {
     const SSL_CIPHER *cipher = &kCiphers[i];
-    if (ssl_method->supports_cipher(cipher)) {
+    if (ssl_method->supports_cipher(cipher) &&
+        /* TLS 1.3 ciphers do not participate in this mechanism. */
+        cipher->algorithm_mkey != SSL_kGENERIC) {
       co_list[co_list_num].cipher = cipher;
       co_list[co_list_num].next = NULL;
       co_list[co_list_num].prev = NULL;
@@ -1389,11 +1352,8 @@
 STACK_OF(SSL_CIPHER) *
 ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
                        struct ssl_cipher_preference_list_st **out_cipher_list,
-                       STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
                        const char *rule_str) {
-  int ok;
-  STACK_OF(SSL_CIPHER) *cipherstack = NULL, *tmp_cipher_list = NULL;
-  const char *rule_p;
+  STACK_OF(SSL_CIPHER) *cipherstack = NULL;
   CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;
   uint8_t *in_group_flags = NULL;
   unsigned int num_in_group_flags = 0;
@@ -1418,11 +1378,8 @@
   /* Now arrange all ciphers by preference:
    * TODO(davidben): Compute this order once and copy it. */
 
-  /* Everything else being equal, prefer TLS 1.3 ciphers then ECDHE_ECDSA then
-   * ECDHE_RSA over other key exchange mechanisms */
-
-  ssl_cipher_apply_rule(0, SSL_kGENERIC, SSL_aGENERIC, ~0u, ~0u, 0, CIPHER_ADD,
-                        -1, 0, &head, &tail);
+  /* Everything else being equal, prefer ECDHE_ECDSA and ECDHE_RSA over other
+   * key exchange mechanisms */
   ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, 0, CIPHER_ADD, -1,
                         0, &head, &tail);
   ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1477,22 +1434,20 @@
 
   /* If the rule_string begins with DEFAULT, apply the default rule before
    * using the (possibly available) additional rules. */
-  ok = 1;
-  rule_p = rule_str;
+  const char *rule_p = rule_str;
   if (strncmp(rule_str, "DEFAULT", 7) == 0) {
-    ok = ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head,
-                                    &tail);
+    if (!ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head,
+                                    &tail)) {
+      goto err;
+    }
     rule_p += 7;
     if (*rule_p == ':') {
       rule_p++;
     }
   }
 
-  if (ok && strlen(rule_p) > 0) {
-    ok = ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail);
-  }
-
-  if (!ok) {
+  if (*rule_p != '\0' &&
+      !ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail)) {
     goto err;
   }
 
@@ -1521,10 +1476,6 @@
   OPENSSL_free(co_list); /* Not needed any longer */
   co_list = NULL;
 
-  tmp_cipher_list = sk_SSL_CIPHER_dup(cipherstack);
-  if (tmp_cipher_list == NULL) {
-    goto err;
-  }
   pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));
   if (!pref_list) {
     goto err;
@@ -1543,26 +1494,12 @@
   *out_cipher_list = pref_list;
   pref_list = NULL;
 
-  if (out_cipher_list_by_id != NULL) {
-    sk_SSL_CIPHER_free(*out_cipher_list_by_id);
-    *out_cipher_list_by_id = tmp_cipher_list;
-    tmp_cipher_list = NULL;
-    (void) sk_SSL_CIPHER_set_cmp_func(*out_cipher_list_by_id,
-                                      ssl_cipher_ptr_id_cmp);
-
-    sk_SSL_CIPHER_sort(*out_cipher_list_by_id);
-  } else {
-    sk_SSL_CIPHER_free(tmp_cipher_list);
-    tmp_cipher_list = NULL;
-  }
-
   return cipherstack;
 
 err:
   OPENSSL_free(co_list);
   OPENSSL_free(in_group_flags);
   sk_SSL_CIPHER_free(cipherstack);
-  sk_SSL_CIPHER_free(tmp_cipher_list);
   if (pref_list) {
     OPENSSL_free(pref_list->in_group_flags);
   }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 491c408..70a39ea 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -276,7 +276,7 @@
   }
 
   ssl_create_cipher_list(ret->method, &ret->cipher_list,
-                         &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST);
+                         SSL_DEFAULT_CIPHER_LIST);
   if (ret->cipher_list == NULL ||
       sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS);
@@ -304,6 +304,10 @@
     ret->options |= SSL_OP_NO_TICKET;
   }
 
+  /* Disable the auto-chaining feature by default. Once this has stuck without
+   * problems, the feature will be removed entirely. */
+  ret->mode = SSL_MODE_NO_AUTO_CHAIN;
+
   /* Lock the SSL_CTX to the specified version, for compatibility with legacy
    * uses of SSL_METHOD. */
   if (!SSL_CTX_set_max_proto_version(ret, method->version) ||
@@ -348,7 +352,6 @@
   lh_SSL_SESSION_free(ctx->sessions);
   X509_STORE_free(ctx->cert_store);
   ssl_cipher_preference_list_free(ctx->cipher_list);
-  sk_SSL_CIPHER_free(ctx->cipher_list_by_id);
   ssl_cipher_preference_list_free(ctx->cipher_list_tls10);
   ssl_cipher_preference_list_free(ctx->cipher_list_tls11);
   ssl_cert_free(ctx->cert);
@@ -409,6 +412,8 @@
   assert(ssl->sid_ctx_length <= sizeof ssl->sid_ctx);
   memcpy(&ssl->sid_ctx, &ctx->sid_ctx, sizeof(ssl->sid_ctx));
   ssl->verify_callback = ctx->default_verify_callback;
+  ssl->retain_only_sha256_of_client_certs =
+      ctx->retain_only_sha256_of_client_certs;
 
   ssl->param = X509_VERIFY_PARAM_new();
   if (!ssl->param) {
@@ -500,7 +505,6 @@
 
   /* add extra stuff */
   ssl_cipher_preference_list_free(ssl->cipher_list);
-  sk_SSL_CIPHER_free(ssl->cipher_list_by_id);
 
   SSL_SESSION_free(ssl->session);
 
@@ -1046,11 +1050,11 @@
     return NULL;
   }
   SSL_SESSION *session = SSL_get_session(ssl);
-  if (session == NULL || session->peer == NULL) {
+  if (session == NULL || session->x509_peer == NULL) {
     return NULL;
   }
-  X509_up_ref(session->peer);
-  return session->peer;
+  X509_up_ref(session->x509_peer);
+  return session->x509_peer;
 }
 
 STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) {
@@ -1061,7 +1065,7 @@
   if (session == NULL) {
     return NULL;
   }
-  return session->cert_chain;
+  return session->x509_chain;
 }
 
 int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len,
@@ -1334,32 +1338,34 @@
 
 /* Fix this so it checks all the valid key/cert options */
 int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
-  if (ctx->cert->x509 == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
-  }
-
   if (ctx->cert->privatekey == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
     return 0;
   }
 
-  return X509_check_private_key(ctx->cert->x509, ctx->cert->privatekey);
-}
-
-/* Fix this function so that it takes an optional type parameter */
-int SSL_check_private_key(const SSL *ssl) {
-  if (ssl->cert->x509 == NULL) {
+  X509 *x509 = ctx->cert->x509_leaf;
+  if (x509 == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
     return 0;
   }
 
+  return X509_check_private_key(x509, ctx->cert->privatekey);
+}
+
+/* Fix this function so that it takes an optional type parameter */
+int SSL_check_private_key(const SSL *ssl) {
   if (ssl->cert->privatekey == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
     return 0;
   }
 
-  return X509_check_private_key(ssl->cert->x509, ssl->cert->privatekey);
+  X509 *x509 = ssl->cert->x509_leaf;
+  if (x509 == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
+    return 0;
+  }
+
+  return X509_check_private_key(x509, ssl->cert->privatekey);
 }
 
 long SSL_get_default_timeout(const SSL *ssl) {
@@ -1564,41 +1570,13 @@
     return NULL;
   }
 
-  if (ssl->cipher_list != NULL) {
-    return ssl->cipher_list->ciphers;
-  }
-
-  if (ssl->version >= TLS1_1_VERSION && ssl->ctx->cipher_list_tls11 != NULL) {
-    return ssl->ctx->cipher_list_tls11->ciphers;
-  }
-
-  if (ssl->version >= TLS1_VERSION && ssl->ctx->cipher_list_tls10 != NULL) {
-    return ssl->ctx->cipher_list_tls10->ciphers;
-  }
-
-  if (ssl->ctx->cipher_list != NULL) {
-    return ssl->ctx->cipher_list->ciphers;
-  }
-
-  return NULL;
-}
-
-/* return a STACK of the ciphers available for the SSL and in order of
- * algorithm id */
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl) {
-  if (ssl == NULL) {
+  const struct ssl_cipher_preference_list_st *prefs =
+      ssl_get_cipher_preferences(ssl);
+  if (prefs == NULL) {
     return NULL;
   }
 
-  if (ssl->cipher_list_by_id != NULL) {
-    return ssl->cipher_list_by_id;
-  }
-
-  if (ssl->ctx->cipher_list_by_id != NULL) {
-    return ssl->ctx->cipher_list_by_id;
-  }
-
-  return NULL;
+  return prefs->ciphers;
 }
 
 const char *SSL_get_cipher_list(const SSL *ssl, int n) {
@@ -1623,8 +1601,8 @@
 }
 
 int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) {
-  STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
-      ctx->method, &ctx->cipher_list, &ctx->cipher_list_by_id, str);
+  STACK_OF(SSL_CIPHER) *cipher_list =
+      ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str);
   if (cipher_list == NULL) {
     return 0;
   }
@@ -1639,8 +1617,8 @@
 }
 
 int SSL_CTX_set_cipher_list_tls10(SSL_CTX *ctx, const char *str) {
-  STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
-      ctx->method, &ctx->cipher_list_tls10, NULL, str);
+  STACK_OF(SSL_CIPHER) *cipher_list =
+      ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls10, str);
   if (cipher_list == NULL) {
     return 0;
   }
@@ -1655,8 +1633,8 @@
 }
 
 int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) {
-  STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
-      ctx->method, &ctx->cipher_list_tls11, NULL, str);
+  STACK_OF(SSL_CIPHER) *cipher_list =
+      ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls11, str);
   if (cipher_list == NULL) {
     return 0;
   }
@@ -1671,8 +1649,8 @@
 }
 
 int SSL_set_cipher_list(SSL *ssl, const char *str) {
-  STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
-      ssl->ctx->method, &ssl->cipher_list, &ssl->cipher_list_by_id, str);
+  STACK_OF(SSL_CIPHER) *cipher_list =
+      ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str);
   if (cipher_list == NULL) {
     return 0;
   }
@@ -1730,6 +1708,15 @@
     return ssl->tlsext_hostname;
   }
 
+  /* During the handshake, report the handshake value. */
+  if (ssl->s3->hs != NULL) {
+    return ssl->s3->hs->hostname;
+  }
+
+  /* SSL_get_servername may also be called after the handshake to look up the
+   * SNI value.
+   *
+   * TODO(davidben): This is almost unused. Can we remove it? */
   SSL_SESSION *session = SSL_get_session(ssl);
   if (session == NULL) {
     return NULL;
@@ -1792,16 +1779,15 @@
 
 int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list,
                                            size_t list_len) {
-  OPENSSL_free(ctx->signed_cert_timestamp_list);
-  ctx->signed_cert_timestamp_list_length = 0;
-
-  ctx->signed_cert_timestamp_list = BUF_memdup(list, list_len);
-  if (ctx->signed_cert_timestamp_list == NULL) {
+  CBS sct_list;
+  CBS_init(&sct_list, list, list_len);
+  if (!ssl_is_sct_list_valid(&sct_list)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST);
     return 0;
   }
-  ctx->signed_cert_timestamp_list_length = list_len;
 
-  return 1;
+  return CBS_stow(&sct_list, &ctx->signed_cert_timestamp_list,
+                  &ctx->signed_cert_timestamp_list_length);
 }
 
 int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
@@ -2056,7 +2042,7 @@
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
-  if (ssl->cert->x509 != NULL && ssl_has_private_key(ssl)) {
+  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
     int type = ssl_private_key_type(ssl);
     if (type == NID_rsaEncryption) {
       mask_k |= SSL_kRSA;
@@ -2105,7 +2091,7 @@
    * decides to renew the ticket. Once the handshake is completed, it should be
    * inserted into the cache. */
   if (ssl->s3->established_session != ssl->session ||
-      (!ssl->server && ssl->tlsext_ticket_expected)) {
+      (!ssl->server && ssl->s3->hs->ticket_expected)) {
     if (use_internal_cache) {
       SSL_CTX_add_session(ctx, ssl->s3->established_session);
     }
@@ -2177,7 +2163,7 @@
 
 X509 *SSL_get_certificate(const SSL *ssl) {
   if (ssl->cert != NULL) {
-    return ssl->cert->x509;
+    return ssl->cert->x509_leaf;
   }
 
   return NULL;
@@ -2193,7 +2179,7 @@
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
   if (ctx->cert != NULL) {
-    return ctx->cert->x509;
+    return ctx->cert->x509_leaf;
   }
 
   return NULL;
@@ -2923,6 +2909,10 @@
   return ssl->s3->tmp.new_cipher;
 }
 
+void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) {
+  ssl->retain_only_sha256_of_client_certs = !!enabled;
+}
+
 void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
   ctx->retain_only_sha256_of_client_certs = !!enabled;
 }
@@ -3060,7 +3050,7 @@
     return;
   }
 
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
   out_clock->tv_sec = 1234;
   out_clock->tv_usec = 1234;
 #elif defined(OPENSSL_WINDOWS)
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 29e5f19..e984012 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -134,13 +134,14 @@
     return 0;
   }
 
-  if (c->x509 != NULL) {
+  X509 *x509_leaf = c->x509_leaf;
+  if (x509_leaf != NULL) {
     /* Sanity-check that the private key and the certificate match, unless the
      * key is opaque (in case of, say, a smartcard). */
     if (!EVP_PKEY_is_opaque(pkey) &&
-        !X509_check_private_key(c->x509, pkey)) {
-      X509_free(c->x509);
-      c->x509 = NULL;
+        !X509_check_private_key(x509_leaf, pkey)) {
+      X509_free(c->x509_leaf);
+      c->x509_leaf = NULL;
       return 0;
     }
   }
@@ -248,9 +249,9 @@
 
   EVP_PKEY_free(pkey);
 
-  X509_free(c->x509);
+  X509_free(c->x509_leaf);
   X509_up_ref(x);
-  c->x509 = x;
+  c->x509_leaf = x;
 
   return 1;
 }
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index c2396b1..a6b669d 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -199,13 +199,13 @@
       goto err;
     }
   }
-  if (session->peer != NULL) {
-    X509_up_ref(session->peer);
-    new_session->peer = session->peer;
+  if (session->x509_peer != NULL) {
+    X509_up_ref(session->x509_peer);
+    new_session->x509_peer = session->x509_peer;
   }
-  if (session->cert_chain != NULL) {
-    new_session->cert_chain = X509_chain_up_ref(session->cert_chain);
-    if (new_session->cert_chain == NULL) {
+  if (session->x509_chain != NULL) {
+    new_session->x509_chain = X509_chain_up_ref(session->x509_chain);
+    if (new_session->x509_chain == NULL) {
       goto err;
     }
   }
@@ -234,6 +234,13 @@
   memcpy(new_session->peer_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH);
   new_session->peer_sha256_valid = session->peer_sha256_valid;
 
+  if (session->tlsext_hostname != NULL) {
+    new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
+    if (new_session->tlsext_hostname == NULL) {
+      goto err;
+    }
+  }
+
   new_session->timeout = session->timeout;
   new_session->time = session->time;
 
@@ -245,13 +252,6 @@
 
     new_session->key_exchange_info = session->key_exchange_info;
 
-    if (session->tlsext_hostname != NULL) {
-      new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
-      if (new_session->tlsext_hostname == NULL) {
-        goto err;
-      }
-    }
-
     memcpy(new_session->original_handshake_hash,
            session->original_handshake_hash,
            session->original_handshake_hash_len);
@@ -285,6 +285,31 @@
   return 0;
 }
 
+void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session) {
+  struct timeval now;
+  ssl_get_current_time(ssl, &now);
+
+  /* To avoid overflows and underflows, if we've gone back in time or any value
+   * is negative, update the time, but mark the session expired. */
+  if (session->time > now.tv_sec ||
+      session->time < 0 ||
+      now.tv_sec < 0) {
+    session->time = now.tv_sec;
+    session->timeout = 0;
+    return;
+  }
+
+  /* Adjust the session time and timeout. If the session has already expired,
+   * clamp the timeout at zero. */
+  long delta = now.tv_sec - session->time;
+  session->time = now.tv_sec;
+  if (session->timeout < delta) {
+    session->timeout = 0;
+  } else {
+    session->timeout -= delta;
+  }
+}
+
 int SSL_SESSION_up_ref(SSL_SESSION *session) {
   CRYPTO_refcount_inc(&session->references);
   return 1;
@@ -300,8 +325,8 @@
 
   OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
   OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
-  X509_free(session->peer);
-  sk_X509_pop_free(session->cert_chain, X509_free);
+  X509_free(session->x509_peer);
+  sk_X509_pop_free(session->x509_chain, X509_free);
   OPENSSL_free(session->tlsext_hostname);
   OPENSSL_free(session->tlsext_tick);
   OPENSSL_free(session->tlsext_signed_cert_timestamp_list);
@@ -332,7 +357,7 @@
 }
 
 X509 *SSL_SESSION_get0_peer(const SSL_SESSION *session) {
-  return session->peer;
+  return session->x509_peer;
 }
 
 size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, uint8_t *out,
@@ -448,7 +473,7 @@
   session->ssl_version = ssl->version;
 
   if (is_server) {
-    if (ssl->tlsext_ticket_expected) {
+    if (ssl->s3->hs->ticket_expected) {
       /* Don't set session IDs for sessions resumed with tickets. This will keep
        * them out of the session cache. */
       session->session_id_length = 0;
@@ -599,7 +624,30 @@
 
   struct timeval now;
   ssl_get_current_time(ssl, &now);
-  return session->timeout >= (long)now.tv_sec - session->time;
+
+  /* Reject tickets from the future to avoid underflow. */
+  if ((long)now.tv_sec < session->time) {
+    return 0;
+  }
+
+  return session->timeout > (long)now.tv_sec - session->time;
+}
+
+int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session) {
+  return ssl_session_is_context_valid(ssl, session) &&
+         /* The session must not be expired. */
+         ssl_session_is_time_valid(ssl, session) &&
+         /* Only resume if the session's version matches the negotiated
+           * version. */
+         ssl->version == session->ssl_version &&
+         /* Only resume if the session's cipher matches the negotiated one. */
+         ssl->s3->tmp.new_cipher == session->cipher &&
+         /* If the session contains a client certificate (either the full
+          * certificate or just the hash) then require that the form of the
+          * certificate matches the current configuration. */
+         ((session->x509_peer == NULL && !session->peer_sha256_valid) ||
+          session->peer_sha256_valid ==
+              ssl->retain_only_sha256_of_client_certs);
 }
 
 /* ssl_lookup_session looks up |session_id| in the session cache and sets
@@ -662,15 +710,8 @@
     }
   }
 
-  if (session == NULL) {
-    return ssl_session_success;
-  }
-
-  if (!ssl_session_is_context_valid(ssl, session)) {
-    /* The client did not offer a suitable ticket or session ID. */
-    SSL_SESSION_free(session);
-    session = NULL;
-  } else if (!ssl_session_is_time_valid(ssl, session)) {
+  if (session != NULL &&
+      !ssl_session_is_time_valid(ssl, session)) {
     /* The session was from the cache, so remove it. */
     SSL_CTX_remove_session(ssl->initial_ctx, session);
     SSL_SESSION_free(session);
@@ -682,8 +723,8 @@
 }
 
 enum ssl_session_result_t ssl_get_prev_session(
-    SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
-    const struct ssl_early_callback_ctx *ctx) {
+    SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
+    int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx) {
   /* This is used only by servers. */
   assert(ssl->server);
   SSL_SESSION *session = NULL;
@@ -712,11 +753,8 @@
   }
 
   *out_session = session;
-  if (session != NULL) {
-    *out_send_ticket = renew_ticket;
-  } else {
-    *out_send_ticket = tickets_supported;
-  }
+  *out_tickets_supported = tickets_supported;
+  *out_renew_ticket = renew_ticket;
   return ssl_session_success;
 }
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index cfce2d0..ae9d5ca 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -23,11 +23,14 @@
 
 #include <openssl/base64.h>
 #include <openssl/bio.h>
+#include <openssl/cipher.h>
 #include <openssl/crypto.h>
 #include <openssl/err.h>
+#include <openssl/hmac.h>
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/ssl.h>
+#include <openssl/rand.h>
 #include <openssl/x509.h>
 
 #include "internal.h"
@@ -269,7 +272,6 @@
   "SSLv3",
   "TLSv1",
   "TLSv1.2",
-  "GENERIC",
 };
 
 static const char *kMustNotIncludeCECPQ1[] = {
@@ -294,7 +296,6 @@
   "AES256",
   "AESGCM",
   "CHACHA20",
-  "GENERIC",
 };
 
 static const CurveTest kCurveTests[] = {
@@ -864,18 +865,22 @@
   return true;
 }
 
-// CreateSessionWithTicket returns a sample |SSL_SESSION| with the ticket
-// replaced for one of length |ticket_len| or nullptr on failure.
-static bssl::UniquePtr<SSL_SESSION> CreateSessionWithTicket(size_t ticket_len) {
+// CreateSessionWithTicket returns a sample |SSL_SESSION| with the specified
+// version and ticket length or nullptr on failure.
+static bssl::UniquePtr<SSL_SESSION> CreateSessionWithTicket(uint16_t version,
+                                                            size_t ticket_len) {
   std::vector<uint8_t> der;
   if (!DecodeBase64(&der, kOpenSSLSession)) {
     return nullptr;
   }
-  bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(der.data(), der.size()));
+  bssl::UniquePtr<SSL_SESSION> session(
+      SSL_SESSION_from_bytes(der.data(), der.size()));
   if (!session) {
     return nullptr;
   }
 
+  session->ssl_version = version;
+
   // Swap out the ticket for a garbage one.
   OPENSSL_free(session->tlsext_tick);
   session->tlsext_tick = reinterpret_cast<uint8_t*>(OPENSSL_malloc(ticket_len));
@@ -886,7 +891,11 @@
   session->tlsext_ticklen = ticket_len;
 
   // Fix up the timeout.
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+  session->time = 1234;
+#else
   session->time = time(NULL);
+#endif
   return session;
 }
 
@@ -914,27 +923,32 @@
   return true;
 }
 
-// GetClientHelloLen creates a client SSL connection with a ticket of length
-// |ticket_len| and records the ClientHello. It returns the length of the
-// ClientHello, not including the record header, on success and zero on error.
-static size_t GetClientHelloLen(size_t ticket_len) {
+// GetClientHelloLen creates a client SSL connection with the specified version
+// and ticket length. It returns the length of the ClientHello, not including
+// the record header, on success and zero on error.
+static size_t GetClientHelloLen(uint16_t max_version, uint16_t session_version,
+                                size_t ticket_len) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_SESSION> session = CreateSessionWithTicket(ticket_len);
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateSessionWithTicket(session_version, ticket_len);
   if (!ctx || !session) {
     return 0;
   }
+
+  // Set a one-element cipher list so the baseline ClientHello is unpadded.
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
-  // Test at TLS 1.2. TLS 1.3 adds enough extensions that the ClientHello is
-  // longer than our test vectors.
   if (!ssl || !SSL_set_session(ssl.get(), session.get()) ||
-      !SSL_set_max_proto_version(ssl.get(), TLS1_2_VERSION)) {
+      !SSL_set_cipher_list(ssl.get(), "ECDHE-RSA-AES128-GCM-SHA256") ||
+      !SSL_set_max_proto_version(ssl.get(), max_version)) {
     return 0;
   }
+
   std::vector<uint8_t> client_hello;
   if (!GetClientHello(ssl.get(), &client_hello) ||
       client_hello.size() <= SSL3_RT_HEADER_LENGTH) {
     return 0;
   }
+
   return client_hello.size() - SSL3_RT_HEADER_LENGTH;
 }
 
@@ -963,28 +977,37 @@
     {0x201, 0x201},
 };
 
-static bool TestPaddingExtension() {
+static bool TestPaddingExtension(uint16_t max_version,
+                                 uint16_t session_version) {
   // Sample a baseline length.
-  size_t base_len = GetClientHelloLen(1);
+  size_t base_len = GetClientHelloLen(max_version, session_version, 1);
   if (base_len == 0) {
     return false;
   }
 
   for (const PaddingTest &test : kPaddingTests) {
     if (base_len > test.input_len) {
-      fprintf(stderr, "Baseline ClientHello too long.\n");
+      fprintf(stderr,
+              "Baseline ClientHello too long (max_version = %04x, "
+              "session_version = %04x).\n",
+              max_version, session_version);
       return false;
     }
 
-    size_t padded_len = GetClientHelloLen(1 + test.input_len - base_len);
+    size_t padded_len = GetClientHelloLen(max_version, session_version,
+                                          1 + test.input_len - base_len);
     if (padded_len != test.padded_len) {
-      fprintf(stderr, "%u-byte ClientHello padded to %u bytes, not %u.\n",
+      fprintf(stderr,
+              "%u-byte ClientHello padded to %u bytes, not %u (max_version = "
+              "%04x, session_version = %04x).\n",
               static_cast<unsigned>(test.input_len),
               static_cast<unsigned>(padded_len),
-              static_cast<unsigned>(test.padded_len));
+              static_cast<unsigned>(test.padded_len), max_version,
+              session_version);
       return false;
     }
   }
+
   return true;
 }
 
@@ -1273,115 +1296,104 @@
   return true;
 }
 
-static uint16_t kTLSVersions[] = {
-    SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION, TLS1_3_VERSION,
-};
-
-static uint16_t kDTLSVersions[] = {
-    DTLS1_VERSION, DTLS1_2_VERSION,
-};
-
-static bool TestSequenceNumber() {
-  for (bool is_dtls : std::vector<bool>{false, true}) {
-    const SSL_METHOD *method = is_dtls ? DTLS_method() : TLS_method();
-    const uint16_t *versions = is_dtls ? kDTLSVersions : kTLSVersions;
-    size_t num_versions = is_dtls ? OPENSSL_ARRAY_SIZE(kDTLSVersions)
-                                  : OPENSSL_ARRAY_SIZE(kTLSVersions);
-    for (size_t i = 0; i < num_versions; i++) {
-      uint16_t version = versions[i];
-      bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
-      bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
-      if (!server_ctx || !client_ctx ||
-          !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
-          !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
-          !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
-          !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
-        return false;
-      }
-
-      bssl::UniquePtr<X509> cert = GetTestCertificate();
-      bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-      if (!cert || !key ||
-          !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-          !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
-        return false;
-      }
-
-      bssl::UniquePtr<SSL> client, server;
-      if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                  server_ctx.get(), nullptr /* no session */)) {
-        return false;
-      }
-
-      // Drain any post-handshake messages to ensure there are no unread records
-      // on either end.
-      uint8_t byte = 0;
-      if (SSL_read(client.get(), &byte, 1) > 0 ||
-          SSL_read(server.get(), &byte, 1) > 0) {
-        fprintf(stderr, "Received unexpected data.\n");
-        return false;
-      }
-
-      uint64_t client_read_seq = SSL_get_read_sequence(client.get());
-      uint64_t client_write_seq = SSL_get_write_sequence(client.get());
-      uint64_t server_read_seq = SSL_get_read_sequence(server.get());
-      uint64_t server_write_seq = SSL_get_write_sequence(server.get());
-
-      if (is_dtls) {
-        // Both client and server must be at epoch 1.
-        if (EpochFromSequence(client_read_seq) != 1 ||
-            EpochFromSequence(client_write_seq) != 1 ||
-            EpochFromSequence(server_read_seq) != 1 ||
-            EpochFromSequence(server_write_seq) != 1) {
-          fprintf(stderr, "Bad epochs.\n");
-          return false;
-        }
-
-        // The next record to be written should exceed the largest received.
-        if (client_write_seq <= server_read_seq ||
-            server_write_seq <= client_read_seq) {
-          fprintf(stderr, "Inconsistent sequence numbers.\n");
-          return false;
-        }
-      } else {
-        // The next record to be written should equal the next to be received.
-        if (client_write_seq != server_read_seq ||
-            server_write_seq != client_read_seq) {
-          fprintf(stderr, "Inconsistent sequence numbers.\n");
-          return false;
-        }
-      }
-
-      // Send a record from client to server.
-      if (SSL_write(client.get(), &byte, 1) != 1 ||
-          SSL_read(server.get(), &byte, 1) != 1) {
-        fprintf(stderr, "Could not send byte.\n");
-        return false;
-      }
-
-      // The client write and server read sequence numbers should have
-      // incremented.
-      if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) ||
-          server_read_seq + 1 != SSL_get_read_sequence(server.get())) {
-        fprintf(stderr, "Sequence numbers did not increment.\n");
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
-static bool TestOneSidedShutdown() {
-  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-  if (!client_ctx || !server_ctx) {
+static bool TestSequenceNumber(bool is_dtls, const SSL_METHOD *method,
+                               uint16_t version) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  if (!server_ctx || !client_ctx ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
     return false;
   }
 
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  if (!cert || !key ||
+  if (!cert || !key || !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get(), nullptr /* no session */)) {
+    return false;
+  }
+
+  // Drain any post-handshake messages to ensure there are no unread records
+  // on either end.
+  uint8_t byte = 0;
+  if (SSL_read(client.get(), &byte, 1) > 0 ||
+      SSL_read(server.get(), &byte, 1) > 0) {
+    fprintf(stderr, "Received unexpected data.\n");
+    return false;
+  }
+
+  uint64_t client_read_seq = SSL_get_read_sequence(client.get());
+  uint64_t client_write_seq = SSL_get_write_sequence(client.get());
+  uint64_t server_read_seq = SSL_get_read_sequence(server.get());
+  uint64_t server_write_seq = SSL_get_write_sequence(server.get());
+
+  if (is_dtls) {
+    // Both client and server must be at epoch 1.
+    if (EpochFromSequence(client_read_seq) != 1 ||
+        EpochFromSequence(client_write_seq) != 1 ||
+        EpochFromSequence(server_read_seq) != 1 ||
+        EpochFromSequence(server_write_seq) != 1) {
+      fprintf(stderr, "Bad epochs.\n");
+      return false;
+    }
+
+    // The next record to be written should exceed the largest received.
+    if (client_write_seq <= server_read_seq ||
+        server_write_seq <= client_read_seq) {
+      fprintf(stderr, "Inconsistent sequence numbers.\n");
+      return false;
+    }
+  } else {
+    // The next record to be written should equal the next to be received.
+    if (client_write_seq != server_read_seq ||
+        server_write_seq != client_read_seq) {
+      fprintf(stderr, "Inconsistent sequence numbers.\n");
+      return false;
+    }
+  }
+
+  // Send a record from client to server.
+  if (SSL_write(client.get(), &byte, 1) != 1 ||
+      SSL_read(server.get(), &byte, 1) != 1) {
+    fprintf(stderr, "Could not send byte.\n");
+    return false;
+  }
+
+  // The client write and server read sequence numbers should have
+  // incremented.
+  if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) ||
+      server_read_seq + 1 != SSL_get_read_sequence(server.get())) {
+    fprintf(stderr, "Sequence numbers did not increment.\n");
+    return false;
+  }
+
+  return true;
+}
+
+static bool TestOneSidedShutdown(bool is_dtls, const SSL_METHOD *method,
+                                 uint16_t version) {
+  // SSL_shutdown is a no-op in DTLS.
+  if (is_dtls) {
+    return true;
+  }
+
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  if (!client_ctx || !server_ctx || !cert || !key ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
       !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
       !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
     return false;
@@ -1427,6 +1439,7 @@
 
   return true;
 }
+
 static bool TestSessionDuplication() {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
@@ -1631,63 +1644,63 @@
 
 static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { return 1; }
 
-static bool TestGetPeerCertificate() {
+static bool TestGetPeerCertificate(bool is_dtls, const SSL_METHOD *method,
+                                   uint16_t version) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   if (!cert || !key) {
     return false;
   }
 
-  for (uint16_t version : kTLSVersions) {
-    // Configure both client and server to accept any certificate.
-    bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-    if (!ctx ||
-        !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
-        !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
-        !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
-      return false;
-    }
-    SSL_CTX_set_verify(
-        ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
-    SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
+  // Configure both client and server to accept any certificate.
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+  if (!ctx ||
+      !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
+    return false;
+  }
+  SSL_CTX_set_verify(
+      ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+  SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
 
-    bssl::UniquePtr<SSL> client, server;
-    if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
-                                nullptr /* no session */)) {
-      return false;
-    }
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+                              nullptr /* no session */)) {
+    return false;
+  }
 
-    // Client and server should both see the leaf certificate.
-    bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
-    if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
-      fprintf(stderr, "%x: Server peer certificate did not match.\n", version);
-      return false;
-    }
+  // Client and server should both see the leaf certificate.
+  bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
+  if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
+    fprintf(stderr, "Server peer certificate did not match.\n");
+    return false;
+  }
 
-    peer.reset(SSL_get_peer_certificate(client.get()));
-    if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
-      fprintf(stderr, "%x: Client peer certificate did not match.\n", version);
-      return false;
-    }
+  peer.reset(SSL_get_peer_certificate(client.get()));
+  if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
+    fprintf(stderr, "Client peer certificate did not match.\n");
+    return false;
+  }
 
-    // However, for historical reasons, the chain includes the leaf on the
-    // client, but does not on the server.
-    if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) {
-      fprintf(stderr, "%x: Client peer chain was incorrect.\n", version);
-      return false;
-    }
+  // However, for historical reasons, the chain includes the leaf on the
+  // client, but does not on the server.
+  if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) {
+    fprintf(stderr, "Client peer chain was incorrect.\n");
+    return false;
+  }
 
-    if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) {
-      fprintf(stderr, "%x: Server peer chain was incorrect.\n", version);
-      return false;
-    }
+  if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) {
+    fprintf(stderr, "Server peer chain was incorrect.\n");
+    return false;
   }
 
   return true;
 }
 
-static bool TestRetainOnlySHA256OfCerts() {
+static bool TestRetainOnlySHA256OfCerts(bool is_dtls, const SSL_METHOD *method,
+                                        uint16_t version) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   if (!cert || !key) {
@@ -1704,45 +1717,43 @@
   uint8_t cert_sha256[SHA256_DIGEST_LENGTH];
   SHA256(cert_der, cert_der_len, cert_sha256);
 
-  for (uint16_t version : kTLSVersions) {
-    // Configure both client and server to accept any certificate, but the
-    // server must retain only the SHA-256 of the peer.
-    bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-    if (!ctx ||
-        !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
-        !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
-        !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
-      return false;
-    }
-    SSL_CTX_set_verify(
-        ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
-    SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
-    SSL_CTX_set_retain_only_sha256_of_client_certs(ctx.get(), 1);
+  // Configure both client and server to accept any certificate, but the
+  // server must retain only the SHA-256 of the peer.
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+  if (!ctx ||
+      !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
+    return false;
+  }
+  SSL_CTX_set_verify(
+      ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+  SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
+  SSL_CTX_set_retain_only_sha256_of_client_certs(ctx.get(), 1);
 
-    bssl::UniquePtr<SSL> client, server;
-    if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
-                                nullptr /* no session */)) {
-      return false;
-    }
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+                              nullptr /* no session */)) {
+    return false;
+  }
 
-    // The peer certificate has been dropped.
-    bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
-    if (peer) {
-      fprintf(stderr, "%x: Peer certificate was retained.\n", version);
-      return false;
-    }
+  // The peer certificate has been dropped.
+  bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
+  if (peer) {
+    fprintf(stderr, "Peer certificate was retained.\n");
+    return false;
+  }
 
-    SSL_SESSION *session = SSL_get_session(server.get());
-    if (!session->peer_sha256_valid) {
-      fprintf(stderr, "%x: peer_sha256_valid was not set.\n", version);
-      return false;
-    }
+  SSL_SESSION *session = SSL_get_session(server.get());
+  if (!session->peer_sha256_valid) {
+    fprintf(stderr, "peer_sha256_valid was not set.\n");
+    return false;
+  }
 
-    if (memcmp(cert_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH) != 0) {
-      fprintf(stderr, "%x: peer_sha256 did not match.\n", version);
-      return false;
-    }
+  if (memcmp(cert_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH) != 0) {
+    fprintf(stderr, "peer_sha256 did not match.\n");
+    return false;
   }
 
   return true;
@@ -1900,8 +1911,8 @@
       0xc0, 0x28, 0x00, 0x39, 0x00, 0x6b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
       0x00, 0x3c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37,
       0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00,
-      0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x08, 0x06, 0x06, 0x01, 0x08,
-      0x05, 0x05, 0x01, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01, 0x04, 0x03, 0x02,
+      0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
+      0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02,
       0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00,
       0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
   };
@@ -1974,7 +1985,64 @@
   return true;
 }
 
-static bool TestSessionIDContext() {
+static bssl::UniquePtr<SSL_SESSION> 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<SSL> 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 int SwitchSessionIDContextSNI(SSL *ssl, int *out_alert, void *arg) {
+  static const uint8_t kContext[] = {3};
+
+  if (!SSL_set_session_id_context(ssl, kContext, sizeof(kContext))) {
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
+
+  return SSL_TLSEXT_ERR_OK;
+}
+
+static int SwitchSessionIDContextEarly(
+    const struct ssl_early_callback_ctx *ctx) {
+  static const uint8_t kContext[] = {3};
+
+  if (!SSL_set_session_id_context(ctx->ssl, kContext, sizeof(kContext))) {
+    return -1;
+  }
+
+  return 1;
+}
+
+static bool TestSessionIDContext(bool is_dtls, const SSL_METHOD *method,
+                                 uint16_t version) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   if (!cert || !key) {
@@ -1984,50 +2052,74 @@
   static const uint8_t kContext1[] = {1};
   static const uint8_t kContext2[] = {2};
 
-  for (uint16_t version : kTLSVersions) {
-    bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-    bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-    if (!server_ctx || !client_ctx ||
-        !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-        !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
-        !SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
-                                        sizeof(kContext1)) ||
-        !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
-        !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
-      return false;
-    }
+  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 ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+      !SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
+                                      sizeof(kContext1)) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
+    return false;
+  }
 
-    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);
+  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);
 
-    bssl::UniquePtr<SSL_SESSION> session =
-        CreateClientSession(client_ctx.get(), server_ctx.get());
-    if (!session) {
-      fprintf(stderr, "Error getting session (version = %04x).\n", version);
-      return false;
-    }
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  if (!session) {
+    fprintf(stderr, "Error getting session.\n");
+    return false;
+  }
 
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
-                             true /* expect session reused */)) {
-      fprintf(stderr, "Error resuming session (version = %04x).\n", version);
-      return false;
-    }
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           true /* expect session reused */)) {
+    fprintf(stderr, "Error resuming session.\n");
+    return false;
+  }
 
-    // Change the session ID context.
-    if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext2,
-                                        sizeof(kContext2))) {
-      return false;
-    }
+  // Change the session ID context.
+  if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext2,
+                                      sizeof(kContext2))) {
+    return false;
+  }
 
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
-                             false /* expect session not reused */)) {
-      fprintf(stderr,
-              "Error connection with different context (version = %04x).\n",
-              version);
-      return false;
-    }
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr, "Error connecting with a different context.\n");
+    return false;
+  }
+
+  // Change the session ID context back and install an SNI callback to switch
+  // it.
+  if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
+                                      sizeof(kContext1))) {
+    return false;
+  }
+
+  SSL_CTX_set_tlsext_servername_callback(server_ctx.get(),
+                                         SwitchSessionIDContextSNI);
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr, "Error connecting with a context switch on SNI callback.\n");
+    return false;
+  }
+
+  // Switch the session ID context with the early callback instead.
+  SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), nullptr);
+  SSL_CTX_set_select_certificate_cb(server_ctx.get(),
+                                    SwitchSessionIDContextEarly);
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr,
+            "Error connecting with a context switch on early callback.\n");
+    return false;
   }
 
   return true;
@@ -2039,16 +2131,78 @@
   *out_clock = g_current_time;
 }
 
-static bool TestSessionTimeout() {
+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 GetServerTicketTime(long *out, const SSL_SESSION *session) {
+  if (session->tlsext_ticklen < 16 + 16 + SHA256_DIGEST_LENGTH) {
+    return false;
+  }
+
+  const uint8_t *ciphertext = session->tlsext_tick + 16 + 16;
+  size_t len = session->tlsext_ticklen - 16 - 16 - SHA256_DIGEST_LENGTH;
+  std::unique_ptr<uint8_t[]> plaintext(new uint8_t[len]);
+
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  // Fuzzer-mode tickets are unencrypted.
+  memcpy(plaintext.get(), ciphertext, len);
+#else
+  static const uint8_t kZeros[16] = {0};
+  const uint8_t *iv = session->tlsext_tick + 16;
+  bssl::ScopedEVP_CIPHER_CTX ctx;
+  int len1, len2;
+  if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, kZeros, iv) ||
+      !EVP_DecryptUpdate(ctx.get(), plaintext.get(), &len1, ciphertext, len) ||
+      !EVP_DecryptFinal_ex(ctx.get(), plaintext.get() + len1, &len2)) {
+    return false;
+  }
+
+  len = static_cast<size_t>(len1 + len2);
+#endif
+
+  bssl::UniquePtr<SSL_SESSION> server_session(
+      SSL_SESSION_from_bytes(plaintext.get(), len));
+  if (!server_session) {
+    return false;
+  }
+
+  *out = server_session->time;
+  return true;
+}
+
+static bool TestSessionTimeout(bool is_dtls, const SSL_METHOD *method,
+                               uint16_t version) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   if (!cert || !key) {
     return false;
   }
 
-  for (uint16_t version : kTLSVersions) {
-    bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-    bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  for (bool server_test : std::vector<bool>{false, true}) {
+    static const int kStartTime = 1000;
+    g_current_time.tv_sec = kStartTime;
+
+    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 ||
         !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
         !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
@@ -2060,23 +2214,31 @@
     }
 
     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);
-    SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
+
+    // Both client and server must enforce session timeouts.
+    if (server_test) {
+      SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
+    } else {
+      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<SSL_SESSION> session =
         CreateClientSession(client_ctx.get(), server_ctx.get());
     if (!session) {
-      fprintf(stderr, "Error getting session (version = %04x).\n", version);
+      fprintf(stderr, "Error getting session.\n");
       return false;
     }
 
     // Advance the clock just behind the timeout.
-    g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT;
+    g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT - 1;
 
     if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
                              true /* expect session reused */)) {
-      fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+      fprintf(stderr, "Error resuming session.\n");
       return false;
     }
 
@@ -2085,7 +2247,71 @@
 
     if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
                              false /* expect session not reused */)) {
-      fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+      fprintf(stderr, "Error resuming session.\n");
+      return false;
+    }
+
+    // Rewind the clock to before the session was minted.
+    g_current_time.tv_sec = kStartTime - 1;
+
+    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                             false /* expect session not reused */)) {
+      fprintf(stderr, "Error resuming session.\n");
+      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<SSL_SESSION> new_session =
+        ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), session.get());
+    if (!new_session) {
+      fprintf(stderr, "Error renewing session.\n");
+      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.\n");
+      return false;
+    }
+
+    // Check the sessions have timestamps measured from issuance.
+    long session_time = 0;
+    if (server_test) {
+      if (!GetServerTicketTime(&session_time, new_session.get())) {
+        fprintf(stderr, "Failed to decode session ticket.\n");
+        return false;
+      }
+    } else {
+      session_time = new_session->time;
+    }
+
+    if (session_time != g_current_time.tv_sec) {
+      fprintf(stderr, "New session is not measured from issuance.\n");
+      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;
+    }
+
+    // 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;
     }
   }
@@ -2099,7 +2325,13 @@
   return SSL_TLSEXT_ERR_OK;
 }
 
-static bool TestSNICallback() {
+static bool TestSNICallback(bool is_dtls, const SSL_METHOD *method,
+                            uint16_t version) {
+  // SSL 3.0 lacks extensions.
+  if (version == SSL3_VERSION) {
+    return true;
+  }
+
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   bssl::UniquePtr<X509> cert2 = GetECDSATestCertificate();
@@ -2108,56 +2340,47 @@
     return false;
   }
 
-  // At each version, test that switching the |SSL_CTX| at the SNI callback
-  // behaves correctly.
-  for (uint16_t version : kTLSVersions) {
-    if (version == SSL3_VERSION) {
-      continue;
-    }
+  // Test that switching the |SSL_CTX| at the SNI callback behaves correctly.
+  static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256;
 
-    static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256;
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> server_ctx2(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  if (!server_ctx || !server_ctx2 || !client_ctx ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+      !SSL_CTX_use_certificate(server_ctx2.get(), cert2.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get()) ||
+      // Historically signing preferences would be lost in some cases with the
+      // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure
+      // this doesn't happen when |version| is TLS 1.2, configure the private
+      // key to only sign SHA-256.
+      !SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(), &kECDSAWithSHA256,
+                                           1) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx2.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx2.get(), version)) {
+    return false;
+  }
 
-    bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-    bssl::UniquePtr<SSL_CTX> server_ctx2(SSL_CTX_new(TLS_method()));
-    bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-    if (!server_ctx || !server_ctx2 || !client_ctx ||
-        !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-        !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
-        !SSL_CTX_use_certificate(server_ctx2.get(), cert2.get()) ||
-        !SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get()) ||
-        // Historically signing preferences would be lost in some cases with the
-        // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure
-        // this doesn't happen when |version| is TLS 1.2, configure the private
-        // key to only sign SHA-256.
-        !SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(),
-                                             &kECDSAWithSHA256, 1) ||
-        !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
-        !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
-        !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
-        !SSL_CTX_set_min_proto_version(server_ctx2.get(), version) ||
-        !SSL_CTX_set_max_proto_version(server_ctx2.get(), version)) {
-      return false;
-    }
+  SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), SwitchContext);
+  SSL_CTX_set_tlsext_servername_arg(server_ctx.get(), server_ctx2.get());
 
-    SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), SwitchContext);
-    SSL_CTX_set_tlsext_servername_arg(server_ctx.get(), server_ctx2.get());
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get(), nullptr)) {
+    fprintf(stderr, "Handshake failed.\n");
+    return false;
+  }
 
-    bssl::UniquePtr<SSL> client, server;
-    if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                server_ctx.get(), nullptr)) {
-      fprintf(stderr, "Handshake failed at version %04x.\n", version);
-      return false;
-    }
-
-    // The client should have received |cert2|.
-    bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(client.get()));
-    if (!peer ||
-        X509_cmp(peer.get(), cert2.get()) != 0) {
-      fprintf(stderr, "Incorrect certificate received at version %04x.\n",
-              version);
-      return false;
-    }
+  // The client should have received |cert2|.
+  bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(client.get()));
+  if (!peer || X509_cmp(peer.get(), cert2.get()) != 0) {
+    fprintf(stderr, "Incorrect certificate received.\n");
+    return false;
   }
 
   return true;
@@ -2280,45 +2503,120 @@
   return true;
 }
 
-static bool TestVersions() {
+static bool TestVersion(bool is_dtls, const SSL_METHOD *method,
+                        uint16_t version) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   if (!cert || !key) {
     return false;
   }
 
-  for (bool is_dtls : std::vector<bool>{false, true}) {
-    const SSL_METHOD *method = is_dtls ? DTLS_method() : TLS_method();
-    const char *name = is_dtls ? "DTLS" : "TLS";
-    const uint16_t *versions = is_dtls ? kDTLSVersions : kTLSVersions;
-    size_t num_versions = is_dtls ? OPENSSL_ARRAY_SIZE(kDTLSVersions)
-                                  : OPENSSL_ARRAY_SIZE(kTLSVersions);
-    for (size_t i = 0; i < num_versions; i++) {
-      uint16_t version = versions[i];
-      bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
-      bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
-      bssl::UniquePtr<SSL> client, server;
-      if (!server_ctx || !client_ctx ||
-          !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-          !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
-          !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
-          !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
-          !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
-          !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
-          !ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                  server_ctx.get(), nullptr /* no session */)) {
-        fprintf(stderr, "Failed to connect %s at version %04x.\n", name,
-                version);
-        return false;
-      }
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL> client, server;
+  if (!server_ctx || !client_ctx ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+      !ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get(), nullptr /* no session */)) {
+    fprintf(stderr, "Failed to connect.\n");
+    return false;
+  }
 
-      if (SSL_version(client.get()) != version ||
-          SSL_version(server.get()) != version) {
-        fprintf(stderr,
-                "%s version mismatch. Got %04x and %04x, wanted %04x.\n", name,
-                SSL_version(client.get()), SSL_version(server.get()), version);
-        return false;
-      }
+  if (SSL_version(client.get()) != version ||
+      SSL_version(server.get()) != version) {
+    fprintf(stderr, "Version mismatch. Got %04x and %04x, wanted %04x.\n",
+            SSL_version(client.get()), SSL_version(server.get()), version);
+    return false;
+  }
+
+  return true;
+}
+
+// Tests that that |SSL_get_pending_cipher| is available during the ALPN
+// selection callback.
+static bool TestALPNCipherAvailable(bool is_dtls, const SSL_METHOD *method,
+                                    uint16_t version) {
+  // SSL 3.0 lacks extensions.
+  if (version == SSL3_VERSION) {
+    return true;
+  }
+
+  static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'};
+
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  if (!cert || !key) {
+    return false;
+  }
+
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+  if (!ctx || !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(ctx.get(), version) ||
+      SSL_CTX_set_alpn_protos(ctx.get(), kALPNProtos, sizeof(kALPNProtos)) !=
+          0) {
+    return false;
+  }
+
+  // The ALPN callback does not fail the handshake on error, so have the
+  // callback write a boolean.
+  std::pair<uint16_t, bool> callback_state(version, false);
+  SSL_CTX_set_alpn_select_cb(
+      ctx.get(),
+      [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in,
+         unsigned in_len, void *arg) -> int {
+        auto state = reinterpret_cast<std::pair<uint16_t, bool> *>(arg);
+        if (SSL_get_pending_cipher(ssl) != nullptr &&
+            SSL_version(ssl) == state->first) {
+          state->second = true;
+        }
+        return SSL_TLSEXT_ERR_NOACK;
+      },
+      &callback_state);
+
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+                              nullptr /* no session */)) {
+    return false;
+  }
+
+  if (!callback_state.second) {
+    fprintf(stderr, "The pending cipher was not known in the ALPN callback.\n");
+    return false;
+  }
+
+  return true;
+}
+
+static bool ForEachVersion(bool (*test_func)(bool is_dtls,
+                                             const SSL_METHOD *method,
+                                             uint16_t version)) {
+  static uint16_t kTLSVersions[] = {
+      SSL3_VERSION,   TLS1_VERSION,   TLS1_1_VERSION,
+      TLS1_2_VERSION, TLS1_3_VERSION,
+  };
+
+  static uint16_t kDTLSVersions[] = {
+      DTLS1_VERSION, DTLS1_2_VERSION,
+  };
+
+  for (uint16_t version : kTLSVersions) {
+    if (!test_func(false, TLS_method(), version)) {
+      fprintf(stderr, "Test failed at TLS version %04x.\n", version);
+      return false;
+    }
+  }
+
+  for (uint16_t version : kDTLSVersions) {
+    if (!test_func(true, DTLS_method(), version)) {
+      fprintf(stderr, "Test failed at DTLS version %04x.\n", version);
+      return false;
     }
   }
 
@@ -2346,23 +2644,31 @@
       !TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &DTLSv1_method) ||
       !TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method) ||
       !TestCipherGetRFCName() ||
-      !TestPaddingExtension() ||
+      // Test the padding extension at TLS 1.2.
+      !TestPaddingExtension(TLS1_2_VERSION, TLS1_2_VERSION) ||
+      // Test the padding extension at TLS 1.3 with a TLS 1.2 session, so there
+      // will be no PSK binder after the padding extension.
+      !TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) ||
+      // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
+      // will be a PSK binder after the padding extension.
+      !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT_VERSION) ||
       !TestClientCAList() ||
       !TestInternalSessionCache() ||
-      !TestSequenceNumber() ||
-      !TestOneSidedShutdown() ||
+      !ForEachVersion(TestSequenceNumber) ||
+      !ForEachVersion(TestOneSidedShutdown) ||
       !TestSessionDuplication() ||
       !TestSetFD() ||
       !TestSetBIO() ||
-      !TestGetPeerCertificate() ||
-      !TestRetainOnlySHA256OfCerts() ||
+      !ForEachVersion(TestGetPeerCertificate) ||
+      !ForEachVersion(TestRetainOnlySHA256OfCerts) ||
       !TestClientHello() ||
-      !TestSessionIDContext() ||
-      !TestSessionTimeout() ||
-      !TestSNICallback() ||
+      !ForEachVersion(TestSessionIDContext) ||
+      !ForEachVersion(TestSessionTimeout) ||
+      !ForEachVersion(TestSNICallback) ||
       !TestEarlyCallbackVersionSwitch() ||
       !TestSetVersion() ||
-      !TestVersions()) {
+      !ForEachVersion(TestVersion) ||
+      !ForEachVersion(TestALPNCipherAvailable)) {
     ERR_print_errors_fp(stderr);
     return 1;
   }
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 4b8fe45..4c7d3ee 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -258,6 +258,63 @@
   return 1;
 }
 
+static int tls1_setup_key_block(SSL *ssl) {
+  if (ssl->s3->hs->key_block_len != 0) {
+    return 1;
+  }
+
+  SSL_SESSION *session = ssl->session;
+  if (ssl->s3->new_session != NULL) {
+    session = ssl->s3->new_session;
+  }
+
+  const EVP_AEAD *aead = NULL;
+  size_t mac_secret_len, fixed_iv_len;
+  if (session->cipher == NULL ||
+      !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+                               session->cipher, ssl3_protocol_version(ssl))) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
+    return 0;
+  }
+  size_t key_len = EVP_AEAD_key_length(aead);
+  if (mac_secret_len > 0) {
+    /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
+     * key length reported by |EVP_AEAD_key_length| will include the MAC key
+     * bytes and initial implicit IV. */
+    if (key_len < mac_secret_len + fixed_iv_len) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
+    }
+    key_len -= mac_secret_len + fixed_iv_len;
+  }
+
+  assert(mac_secret_len < 256);
+  assert(key_len < 256);
+  assert(fixed_iv_len < 256);
+
+  ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
+  ssl->s3->tmp.new_key_len = (uint8_t)key_len;
+  ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+
+  size_t key_block_len = SSL_get_key_block_len(ssl);
+
+  uint8_t *keyblock = OPENSSL_malloc(key_block_len);
+  if (keyblock == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) {
+    OPENSSL_free(keyblock);
+    return 0;
+  }
+
+  assert(key_block_len < 256);
+  ssl->s3->hs->key_block_len = (uint8_t)key_block_len;
+  ssl->s3->hs->key_block = keyblock;
+  return 1;
+}
+
 int tls1_change_cipher_state(SSL *ssl, int which) {
   /* Ensure the key block is set up. */
   if (!tls1_setup_key_block(ssl)) {
@@ -276,10 +333,9 @@
   size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
   size_t key_len = ssl->s3->tmp.new_key_len;
   size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
-  assert((mac_secret_len + key_len + iv_len) * 2 ==
-         ssl->s3->tmp.key_block_length);
+  assert((mac_secret_len + key_len + iv_len) * 2 == ssl->s3->hs->key_block_len);
 
-  const uint8_t *key_data = ssl->s3->tmp.key_block;
+  const uint8_t *key_data = ssl->s3->hs->key_block;
   const uint8_t *client_write_mac_secret = key_data;
   key_data += mac_secret_len;
   const uint8_t *server_write_mac_secret = key_data;
@@ -333,65 +389,6 @@
       ssl->s3->client_random, SSL3_RANDOM_SIZE);
 }
 
-int tls1_setup_key_block(SSL *ssl) {
-  if (ssl->s3->tmp.key_block_length != 0) {
-    return 1;
-  }
-
-  SSL_SESSION *session = ssl->session;
-  if (ssl->s3->new_session != NULL) {
-    session = ssl->s3->new_session;
-  }
-
-  const EVP_AEAD *aead = NULL;
-  size_t mac_secret_len, fixed_iv_len;
-  if (session->cipher == NULL ||
-      !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
-                               session->cipher, ssl3_protocol_version(ssl))) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
-    return 0;
-  }
-  size_t key_len = EVP_AEAD_key_length(aead);
-  if (mac_secret_len > 0) {
-    /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
-     * key length reported by |EVP_AEAD_key_length| will include the MAC key
-     * bytes and initial implicit IV. */
-    if (key_len < mac_secret_len + fixed_iv_len) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-    key_len -= mac_secret_len + fixed_iv_len;
-  }
-
-  assert(mac_secret_len < 256);
-  assert(key_len < 256);
-  assert(fixed_iv_len < 256);
-
-  ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
-  ssl->s3->tmp.new_key_len = (uint8_t)key_len;
-  ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
-
-  size_t key_block_len = SSL_get_key_block_len(ssl);
-
-  ssl3_cleanup_key_block(ssl);
-
-  uint8_t *keyblock = OPENSSL_malloc(key_block_len);
-  if (keyblock == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
-  if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) {
-    OPENSSL_free(keyblock);
-    return 0;
-  }
-
-  assert(key_block_len < 256);
-  ssl->s3->tmp.key_block_length = (uint8_t)key_block_len;
-  ssl->s3->tmp.key_block = keyblock;
-  return 1;
-}
-
 static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len,
                          size_t max_out) {
   int ret = 0;
@@ -505,6 +502,11 @@
     return 0;
   }
 
+  /* Exporters may not be used in the middle of a renegotiation. */
+  if (SSL_in_init(ssl) && !SSL_in_false_start(ssl)) {
+    return 0;
+  }
+
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     return tls13_export_keying_material(ssl, out, out_len, label, label_len,
                                         context, context_len, use_context);
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 9655b83..421232f 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -442,62 +442,73 @@
 }
 
 /* kVerifySignatureAlgorithms is the default list of accepted signature
- * algorithms for verifying. */
+ * algorithms for verifying.
+ *
+ * For now, RSA-PSS signature algorithms are not enabled on Android's system
+ * BoringSSL. Once the change in Chrome has stuck and the values are finalized,
+ * restore them. */
 static const uint16_t kVerifySignatureAlgorithms[] = {
-    /* For now, do not enable RSA-PSS signature algorithms on Android's system
-     * BoringSSL. Once TLS 1.3 is finalized and the change in Chrome has stuck,
-     * restore them. */
+    /* Prefer SHA-256 algorithms. */
+    SSL_SIGN_ECDSA_SECP256R1_SHA256,
 #if !defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_RSA_PSS_SHA512,
+    SSL_SIGN_RSA_PSS_SHA256,
 #endif
-    SSL_SIGN_RSA_PKCS1_SHA512,
+    SSL_SIGN_RSA_PKCS1_SHA256,
+
+    /* Larger hashes are acceptable. */
+    SSL_SIGN_ECDSA_SECP384R1_SHA384,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    SSL_SIGN_RSA_PSS_SHA384,
+#endif
+    SSL_SIGN_RSA_PKCS1_SHA384,
+
     /* TODO(davidben): Remove this entry and SSL_CURVE_SECP521R1 from
      * kDefaultGroups. */
 #if defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_ECDSA_SECP521R1_SHA512,
 #endif
-
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_RSA_PSS_SHA384,
-#endif
-    SSL_SIGN_RSA_PKCS1_SHA384,
-    SSL_SIGN_ECDSA_SECP384R1_SHA384,
-
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_RSA_PSS_SHA256,
-#endif
-    SSL_SIGN_RSA_PKCS1_SHA256,
-    SSL_SIGN_ECDSA_SECP256R1_SHA256,
-
-    SSL_SIGN_RSA_PKCS1_SHA1,
-};
-
-/* kSignSignatureAlgorithms is the default list of supported signature
- * algorithms for signing. */
-static const uint16_t kSignSignatureAlgorithms[] = {
-    /* For now, do not enable RSA-PSS signature algorithms on Android's system
-     * BoringSSL. Once TLS 1.3 is finalized and the change in Chrome has stuck,
-     * restore them. */
 #if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA512,
 #endif
     SSL_SIGN_RSA_PKCS1_SHA512,
-    SSL_SIGN_ECDSA_SECP521R1_SHA512,
 
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_RSA_PSS_SHA384,
-#endif
-    SSL_SIGN_RSA_PKCS1_SHA384,
-    SSL_SIGN_ECDSA_SECP384R1_SHA384,
+    /* For now, SHA-1 is still accepted but least preferable. */
+    SSL_SIGN_RSA_PKCS1_SHA1,
 
+};
+
+/* kSignSignatureAlgorithms is the default list of supported signature
+ * algorithms for signing.
+ *
+ * For now, RSA-PSS signature algorithms are not enabled on Android's system
+ * BoringSSL. Once the change in Chrome has stuck and the values are finalized,
+ * restore them. */
+static const uint16_t kSignSignatureAlgorithms[] = {
+    /* Prefer SHA-256 algorithms. */
+    SSL_SIGN_ECDSA_SECP256R1_SHA256,
 #if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA256,
 #endif
     SSL_SIGN_RSA_PKCS1_SHA256,
-    SSL_SIGN_ECDSA_SECP256R1_SHA256,
 
-    SSL_SIGN_RSA_PKCS1_SHA1,
+    /* If needed, sign larger hashes.
+     *
+     * TODO(davidben): Determine which of these may be pruned. */
+    SSL_SIGN_ECDSA_SECP384R1_SHA384,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    SSL_SIGN_RSA_PSS_SHA384,
+#endif
+    SSL_SIGN_RSA_PKCS1_SHA384,
+
+    SSL_SIGN_ECDSA_SECP521R1_SHA512,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    SSL_SIGN_RSA_PSS_SHA512,
+#endif
+    SSL_SIGN_RSA_PKCS1_SHA512,
+
+    /* If the peer supports nothing else, sign with SHA-1. */
     SSL_SIGN_ECDSA_SHA1,
+    SSL_SIGN_RSA_PKCS1_SHA1,
 };
 
 size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out) {
@@ -694,28 +705,19 @@
     return 0;
   }
 
-  /* TODO(davidben): SNI should be resolved before resumption. We have the
-   * early callback as a replacement, but we should fix the current callback
-   * and avoid the need for |SSL_CTX_set_session_id_context|. */
-  if (ssl->session == NULL) {
-    assert(ssl->s3->new_session->tlsext_hostname == NULL);
-
-    /* Copy the hostname as a string. */
-    if (!CBS_strdup(&host_name, &ssl->s3->new_session->tlsext_hostname)) {
-      *out_alert = SSL_AD_INTERNAL_ERROR;
-      return 0;
-    }
-
-    ssl->s3->hs->should_ack_sni = 1;
+  /* Copy the hostname as a string. */
+  if (!CBS_strdup(&host_name, &ssl->s3->hs->hostname)) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
   }
 
+  ssl->s3->hs->should_ack_sni = 1;
   return 1;
 }
 
 static int ext_sni_add_serverhello(SSL *ssl, CBB *out) {
-  if (ssl->session != NULL ||
-      !ssl->s3->hs->should_ack_sni ||
-      ssl->s3->new_session->tlsext_hostname == NULL) {
+  if (ssl->s3->session_reused ||
+      !ssl->s3->hs->should_ack_sni) {
     return 1;
   }
 
@@ -1019,8 +1021,6 @@
 
 static int ext_ticket_parse_serverhello(SSL *ssl, uint8_t *out_alert,
                                         CBS *contents) {
-  ssl->tlsext_ticket_expected = 0;
-
   if (contents == NULL) {
     return 1;
   }
@@ -1038,17 +1038,16 @@
     return 0;
   }
 
-  ssl->tlsext_ticket_expected = 1;
+  ssl->s3->hs->ticket_expected = 1;
   return 1;
 }
 
 static int ext_ticket_add_serverhello(SSL *ssl, CBB *out) {
-  if (!ssl->tlsext_ticket_expected) {
+  if (!ssl->s3->hs->ticket_expected) {
     return 1;
   }
 
-  /* If |SSL_OP_NO_TICKET| is set, |tlsext_ticket_expected| should never be
-   * true. */
+  /* If |SSL_OP_NO_TICKET| is set, |ticket_expected| should never be true. */
   assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0);
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) ||
@@ -1152,42 +1151,22 @@
     return 1;
   }
 
-  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-    /* OCSP stapling is forbidden on non-certificate ciphers. */
-    if (CBS_len(contents) != 0 ||
-        !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-      return 0;
-    }
-
-    /* Note this does not check for resumption in TLS 1.2. Sending
-     * status_request here does not make sense, but OpenSSL does so and the
-     * specification does not say anything. Tolerate it but ignore it. */
-
-    ssl->s3->hs->certificate_status_expected = 1;
-    return 1;
-  }
-
-  /* In TLS 1.3, OCSP stapling is forbidden on resumption. */
-  if (ssl->s3->session_reused) {
+  /* TLS 1.3 OCSP responses are included in the Certificate extensions. */
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 0;
   }
 
-  uint8_t status_type;
-  CBS ocsp_response;
-  if (!CBS_get_u8(contents, &status_type) ||
-      status_type != TLSEXT_STATUSTYPE_ocsp ||
-      !CBS_get_u24_length_prefixed(contents, &ocsp_response) ||
-      CBS_len(&ocsp_response) == 0 ||
-      CBS_len(contents) != 0) {
+  /* OCSP stapling is forbidden on non-certificate ciphers. */
+  if (CBS_len(contents) != 0 ||
+      !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     return 0;
   }
 
-  if (!CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
-                &ssl->s3->new_session->ocsp_response_length)) {
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
-  }
+  /* Note this does not check for resumption in TLS 1.2. Sending
+   * status_request here does not make sense, but OpenSSL does so and the
+   * specification does not say anything. Tolerate it but ignore it. */
 
+  ssl->s3->hs->certificate_status_expected = 1;
   return 1;
 }
 
@@ -1210,34 +1189,18 @@
 }
 
 static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
-  if (!ssl->s3->hs->ocsp_stapling_requested ||
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+      !ssl->s3->hs->ocsp_stapling_requested ||
       ssl->ctx->ocsp_response_length == 0 ||
       ssl->s3->session_reused ||
-      (ssl3_protocol_version(ssl) < TLS1_3_VERSION &&
-       !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher))) {
+      !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     return 1;
   }
 
-  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-    /* The extension shouldn't be sent when resuming sessions. */
-    if (ssl->session != NULL) {
-      return 1;
-    }
+  ssl->s3->hs->certificate_status_expected = 1;
 
-    ssl->s3->hs->certificate_status_expected = 1;
-
-    return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
-           CBB_add_u16(out, 0 /* length */);
-  }
-
-  CBB body, ocsp_response;
   return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
-         CBB_add_u16_length_prefixed(out, &body) &&
-         CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) &&
-         CBB_add_u24_length_prefixed(&body, &ocsp_response) &&
-         CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
-                       ssl->ctx->ocsp_response_length) &&
-         CBB_flush(out);
+         CBB_add_u16(out, 0 /* length */);
 }
 
 
@@ -1331,10 +1294,6 @@
 
   if (contents == NULL ||
       ssl->s3->initial_handshake_complete ||
-      /* If the ALPN extension is seen before NPN, ignore it. (If ALPN is seen
-       * afterwards, parsing the ALPN extension will clear
-       * |next_proto_neg_seen|. */
-      ssl->s3->alpn_selected != NULL ||
       ssl->ctx->next_protos_advertised_cb == NULL ||
       SSL_is_dtls(ssl)) {
     return 1;
@@ -1396,11 +1355,17 @@
     return 1;
   }
 
+  /* TLS 1.3 SCTs are included in the Certificate extensions. */
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
   /* If this is false then we should never have sent the SCT extension in the
    * ClientHello and thus this function should never have been called. */
   assert(ssl->signed_cert_timestamps_enabled);
 
-  if (CBS_len(contents) == 0) {
+  if (!ssl_is_sct_list_valid(contents)) {
     *out_alert = SSL_AD_DECODE_ERROR;
     return 0;
   }
@@ -1424,12 +1389,22 @@
 
 static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                      CBS *contents) {
-  return contents == NULL || CBS_len(contents) == 0;
+  if (contents == NULL) {
+    return 1;
+  }
+
+  if (CBS_len(contents) != 0) {
+    return 0;
+  }
+
+  ssl->s3->hs->scts_requested = 1;
+  return 1;
 }
 
 static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
   /* The extension shouldn't be sent when resuming sessions. */
-  if (ssl->s3->session_reused ||
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+      ssl->s3->session_reused ||
       ssl->ctx->signed_cert_timestamp_list_length == 0) {
     return 1;
   }
@@ -1447,11 +1422,6 @@
  *
  * https://tools.ietf.org/html/rfc7301 */
 
-static void ext_alpn_init(SSL *ssl) {
-  OPENSSL_free(ssl->s3->alpn_selected);
-  ssl->s3->alpn_selected = NULL;
-}
-
 static int ext_alpn_add_clienthello(SSL *ssl, CBB *out) {
   if (ssl->alpn_client_proto_list == NULL ||
       ssl->s3->initial_handshake_complete) {
@@ -1534,14 +1504,14 @@
   return 1;
 }
 
-static int ext_alpn_parse_clienthello(SSL *ssl, uint8_t *out_alert,
-                                      CBS *contents) {
-  if (contents == NULL) {
-    return 1;
-  }
-
+int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
+                       const struct ssl_early_callback_ctx *client_hello) {
+  CBS contents;
   if (ssl->ctx->alpn_select_cb == NULL ||
-      ssl->s3->initial_handshake_complete) {
+      !ssl_early_callback_get_extension(
+          client_hello, &contents,
+          TLSEXT_TYPE_application_layer_protocol_negotiation)) {
+    /* Ignore ALPN if not configured or no extension was supplied. */
     return 1;
   }
 
@@ -1549,9 +1519,11 @@
   ssl->s3->hs->next_proto_neg_seen = 0;
 
   CBS protocol_name_list;
-  if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) ||
-      CBS_len(contents) != 0 ||
+  if (!CBS_get_u16_length_prefixed(&contents, &protocol_name_list) ||
+      CBS_len(&contents) != 0 ||
       CBS_len(&protocol_name_list) < 2) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    *out_alert = SSL_AD_DECODE_ERROR;
     return 0;
   }
 
@@ -1563,6 +1535,8 @@
     if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name) ||
         /* Empty protocol names are forbidden. */
         CBS_len(&protocol_name) == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+      *out_alert = SSL_AD_DECODE_ERROR;
       return 0;
     }
   }
@@ -1633,10 +1607,6 @@
     return 1;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 0;
-  }
-
   assert(!SSL_is_dtls(ssl));
   assert(ssl->tlsext_channel_id_enabled);
 
@@ -1665,10 +1635,6 @@
 }
 
 static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) {
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 1;
-  }
-
   if (!ssl->s3->tlsext_channel_id_valid) {
     return 1;
   }
@@ -1836,26 +1802,6 @@
  *
  * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
 
-static int ssl_any_ec_cipher_suites_enabled(const SSL *ssl) {
-  if (ssl->version < TLS1_VERSION && !SSL_is_dtls(ssl)) {
-    return 0;
-  }
-
-  const STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl);
-
-  for (size_t i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
-    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(cipher_stack, i);
-
-    const uint32_t alg_k = cipher->algorithm_mkey;
-    const uint32_t alg_a = cipher->algorithm_auth;
-    if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) {
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
 static int ext_ec_point_add_extension(SSL *ssl, CBB *out) {
   CBB contents, formats;
   if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
@@ -1870,7 +1816,13 @@
 }
 
 static int ext_ec_point_add_clienthello(SSL *ssl, CBB *out) {
-  if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+    return 0;
+  }
+
+  /* The point format extension is unneccessary in TLS 1.3. */
+  if (min_version >= TLS1_3_VERSION) {
     return 1;
   }
 
@@ -1929,9 +1881,30 @@
   return ext_ec_point_add_extension(ssl, out);
 }
 
+
 /* Pre Shared Key
  *
- * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.6 */
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.6 */
+
+static size_t ext_pre_shared_key_clienthello_length(SSL *ssl) {
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+    return 0;
+  }
+
+  uint16_t session_version;
+  if (max_version < TLS1_3_VERSION || ssl->session == NULL ||
+      !ssl->method->version_from_wire(&session_version,
+                                      ssl->session->ssl_version) ||
+      session_version < TLS1_3_VERSION) {
+    return 0;
+  }
+
+  const EVP_MD *digest =
+      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+  size_t binder_len = EVP_MD_size(digest);
+  return 15 + ssl->session->tlsext_ticklen + binder_len;
+}
 
 static int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
   uint16_t min_version, max_version;
@@ -1947,20 +1920,33 @@
     return 1;
   }
 
-  CBB contents, identity, ke_modes, auth_modes, ticket;
+  struct timeval now;
+  ssl_get_current_time(ssl, &now);
+  uint32_t ticket_age = 1000 * (now.tv_sec - ssl->session->time);
+  uint32_t obfuscated_ticket_age = ticket_age + ssl->session->ticket_age_add;
+
+  /* Fill in a placeholder zero binder of the appropriate length. It will be
+   * computed and filled in later after length prefixes are computed. */
+  uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
+  const EVP_MD *digest =
+      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+  size_t binder_len = EVP_MD_size(digest);
+
+  CBB contents, identity, ticket, binders, binder;
   if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &identity) ||
-      !CBB_add_u8_length_prefixed(&identity, &ke_modes) ||
-      !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
-      !CBB_add_u8_length_prefixed(&identity, &auth_modes) ||
-      !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
       !CBB_add_u16_length_prefixed(&identity, &ticket) ||
       !CBB_add_bytes(&ticket, ssl->session->tlsext_tick,
-                     ssl->session->tlsext_ticklen)) {
+                     ssl->session->tlsext_ticklen) ||
+      !CBB_add_u32(&identity, obfuscated_ticket_age) ||
+      !CBB_add_u16_length_prefixed(&contents, &binders) ||
+      !CBB_add_u8_length_prefixed(&binders, &binder) ||
+      !CBB_add_bytes(&binder, zero_binder, binder_len)) {
     return 0;
   }
 
+  ssl->s3->hs->needs_psk_binder = 1;
   return CBB_flush(out);
 }
 
@@ -1974,6 +1960,7 @@
     return 0;
   }
 
+  /* We only advertise one PSK identity, so the only legal index is zero. */
   if (psk_id != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
     *out_alert = SSL_AD_UNKNOWN_PSK_IDENTITY;
@@ -1985,32 +1972,45 @@
 
 int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
                                              SSL_SESSION **out_session,
+                                             CBS *out_binders,
                                              uint8_t *out_alert,
                                              CBS *contents) {
   /* We only process the first PSK identity since we don't support pure PSK. */
-  CBS identity, ke_modes, auth_modes, ticket;
+  uint32_t obfuscated_ticket_age;
+  CBS identity, ticket, binders;
   if (!CBS_get_u16_length_prefixed(contents, &identity) ||
-      !CBS_get_u8_length_prefixed(&identity, &ke_modes) ||
-      !CBS_get_u8_length_prefixed(&identity, &auth_modes) ||
       !CBS_get_u16_length_prefixed(&identity, &ticket) ||
-      CBS_len(&identity) != 0) {
+      !CBS_get_u32(&identity, &obfuscated_ticket_age) ||
+      !CBS_get_u16_length_prefixed(contents, &binders) ||
+      CBS_len(contents) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     *out_alert = SSL_AD_DECODE_ERROR;
     return 0;
   }
 
-  /* We only support tickets with PSK_DHE_KE and PSK_AUTH. */
-  if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) == NULL ||
-      memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) ==
-          NULL) {
-    *out_session = NULL;
-    return 1;
+  *out_binders = binders;
+
+  /* The PSK identity must have a corresponding binder. */
+  CBS binder;
+  if (!CBS_get_u8_length_prefixed(&binders, &binder)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
   }
 
+  /* TODO(svaldez): Check that the ticket_age is valid when attempting to use
+   * the PSK for 0-RTT. http://crbug.com/boringssl/113 */
+
   /* TLS 1.3 session tickets are renewed separately as part of the
    * NewSessionTicket. */
-  int renew;
-  return tls_process_ticket(ssl, out_session, &renew, CBS_data(&ticket),
-                            CBS_len(&ticket), NULL, 0);
+  int unused_renew;
+  if (!tls_process_ticket(ssl, out_session, &unused_renew, CBS_data(&ticket),
+                          CBS_len(&ticket), NULL, 0)) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  return 1;
 }
 
 int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out) {
@@ -2031,6 +2031,53 @@
 }
 
 
+/* Pre-Shared Key Exchange Modes
+ *
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.7 */
+static int ext_psk_key_exchange_modes_add_clienthello(SSL *ssl, CBB *out) {
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+    return 0;
+  }
+
+  if (max_version < TLS1_3_VERSION) {
+    return 1;
+  }
+
+  CBB contents, ke_modes;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_psk_key_exchange_modes) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u8_length_prefixed(&contents, &ke_modes) ||
+      !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE)) {
+    return 0;
+  }
+
+  return CBB_flush(out);
+}
+
+static int ext_psk_key_exchange_modes_parse_clienthello(SSL *ssl,
+                                                        uint8_t *out_alert,
+                                                        CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  CBS ke_modes;
+  if (!CBS_get_u8_length_prefixed(contents, &ke_modes) ||
+      CBS_len(&ke_modes) == 0 ||
+      CBS_len(contents) != 0) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  /* We only support tickets with PSK_DHE_KE. */
+  ssl->s3->hs->accept_psk_mode =
+      memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL;
+
+  return 1;
+}
+
+
 /* Key Share
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.5 */
@@ -2052,21 +2099,18 @@
     return 0;
   }
 
-  uint16_t group_id;
+  uint16_t group_id = ssl->s3->hs->retry_group;
   if (ssl->s3->hs->received_hello_retry_request) {
-    /* Replay the old key shares. */
-    if (!CBB_add_bytes(&kse_bytes, ssl->s3->hs->key_share_bytes,
+    /* We received a HelloRetryRequest without a new curve, so there is no new
+     * share to append. Leave |ecdh_ctx| as-is. */
+    if (group_id == 0 &&
+        !CBB_add_bytes(&kse_bytes, ssl->s3->hs->key_share_bytes,
                        ssl->s3->hs->key_share_bytes_len)) {
       return 0;
     }
     OPENSSL_free(ssl->s3->hs->key_share_bytes);
     ssl->s3->hs->key_share_bytes = NULL;
     ssl->s3->hs->key_share_bytes_len = 0;
-
-    group_id = ssl->s3->hs->retry_group;
-
-    /* We received a HelloRetryRequest without a new curve, so there is no new
-     * share to append. Leave |ecdh_ctx| as-is. */
     if (group_id == 0) {
       return CBB_flush(out);
     }
@@ -2319,10 +2363,6 @@
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.4 */
 
 static int ext_supported_groups_add_clienthello(SSL *ssl, CBB *out) {
-  if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
-    return 1;
-  }
-
   CBB contents, groups_bytes;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -2473,10 +2513,11 @@
   },
   {
     TLSEXT_TYPE_application_layer_protocol_negotiation,
-    ext_alpn_init,
+    NULL,
     ext_alpn_add_clienthello,
     ext_alpn_parse_serverhello,
-    ext_alpn_parse_clienthello,
+    /* ALPN is negotiated late in |ssl_negotiate_alpn|. */
+    ignore_parse_clienthello,
     ext_alpn_add_serverhello,
   },
   {
@@ -2512,11 +2553,11 @@
     dont_add_serverhello,
   },
   {
-    TLSEXT_TYPE_pre_shared_key,
+    TLSEXT_TYPE_psk_key_exchange_modes,
     NULL,
-    ext_pre_shared_key_add_clienthello,
+    ext_psk_key_exchange_modes_add_clienthello,
     forbid_parse_serverhello,
-    ignore_parse_clienthello,
+    ext_psk_key_exchange_modes_parse_clienthello,
     dont_add_serverhello,
   },
   {
@@ -2611,7 +2652,7 @@
     const size_t len_before = CBB_len(&extensions);
     if (!kExtensions[i].add_clienthello(ssl, &extensions)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
-      ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+      ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       goto err;
     }
 
@@ -2643,7 +2684,8 @@
   }
 
   if (!SSL_is_dtls(ssl)) {
-    header_len += 2 + CBB_len(&extensions);
+    size_t psk_extension_len = ext_pre_shared_key_clienthello_length(ssl);
+    header_len += 2 + CBB_len(&extensions) + psk_extension_len;
     if (header_len > 0xff && header_len < 0x200) {
       /* Add padding to workaround bugs in F5 terminators. See RFC 7685.
        *
@@ -2671,6 +2713,11 @@
     }
   }
 
+  /* The PSK extension must be last, including after the padding. */
+  if (!ext_pre_shared_key_add_clienthello(ssl, &extensions)) {
+    goto err;
+  }
+
   /* Discard empty extensions blocks. */
   if (CBB_len(&extensions) == 0) {
     CBB_discard_child(out);
@@ -2698,7 +2745,7 @@
 
     if (!kExtensions[i].add_serverhello(ssl, &extensions)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
-      ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+      ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       goto err;
     }
   }
@@ -2768,7 +2815,7 @@
     if (!ext->parse_clienthello(ssl, &alert, &extension)) {
       *out_alert = alert;
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
-      ERR_add_error_dataf("extension: %u", (unsigned)type);
+      ERR_add_error_dataf("extension %u", (unsigned)type);
       return 0;
     }
   }
@@ -2796,7 +2843,7 @@
     uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!kExtensions[i].parse_clienthello(ssl, &alert, contents)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
-      ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+      ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       *out_alert = alert;
       return 0;
     }
@@ -2875,7 +2922,7 @@
     uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!ext->parse_serverhello(ssl, &alert, &extension)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
-      ERR_add_error_dataf("extension: %u", (unsigned)type);
+      ERR_add_error_dataf("extension %u", (unsigned)type);
       *out_alert = alert;
       return 0;
     }
@@ -2888,7 +2935,7 @@
       uint8_t alert = SSL_AD_DECODE_ERROR;
       if (!kExtensions[i].parse_serverhello(ssl, &alert, NULL)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
-        ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+        ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
         *out_alert = alert;
         return 0;
       }
@@ -3090,12 +3137,6 @@
   memcpy(session->session_id, session_id, session_id_len);
   session->session_id_length = session_id_len;
 
-  if (!ssl_session_is_context_valid(ssl, session) ||
-      !ssl_session_is_time_valid(ssl, session)) {
-    SSL_SESSION_free(session);
-    session = NULL;
-  }
-
   *out_session = session;
 
 done:
@@ -3207,45 +3248,184 @@
   return 0;
 }
 
-int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+int tls1_verify_channel_id(SSL *ssl) {
   int ret = 0;
-  EVP_MD_CTX ctx;
+  uint16_t extension_type;
+  CBS extension, channel_id;
 
-  EVP_MD_CTX_init(&ctx);
-  if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL)) {
+  /* A Channel ID handshake message is structured to contain multiple
+   * extensions, but the only one that can be present is Channel ID. */
+  CBS_init(&channel_id, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u16(&channel_id, &extension_type) ||
+      !CBS_get_u16_length_prefixed(&channel_id, &extension) ||
+      CBS_len(&channel_id) != 0 ||
+      extension_type != TLSEXT_TYPE_channel_id ||
+      CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return 0;
+  }
+
+  EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+  if (!p256) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
+    return 0;
+  }
+
+  EC_KEY *key = NULL;
+  EC_POINT *point = NULL;
+  BIGNUM x, y;
+  ECDSA_SIG sig;
+  BN_init(&x);
+  BN_init(&y);
+  sig.r = BN_new();
+  sig.s = BN_new();
+  if (sig.r == NULL || sig.s == NULL) {
     goto err;
   }
 
+  const uint8_t *p = CBS_data(&extension);
+  if (BN_bin2bn(p + 0, 32, &x) == NULL ||
+      BN_bin2bn(p + 32, 32, &y) == NULL ||
+      BN_bin2bn(p + 64, 32, sig.r) == NULL ||
+      BN_bin2bn(p + 96, 32, sig.s) == NULL) {
+    goto err;
+  }
+
+  point = EC_POINT_new(p256);
+  if (point == NULL ||
+      !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
+    goto err;
+  }
+
+  key = EC_KEY_new();
+  if (key == NULL ||
+      !EC_KEY_set_group(key, p256) ||
+      !EC_KEY_set_public_key(key, point)) {
+    goto err;
+  }
+
+  uint8_t digest[EVP_MAX_MD_SIZE];
+  size_t digest_len;
+  if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+    goto err;
+  }
+
+  int sig_ok = ECDSA_do_verify(digest, digest_len, &sig, key);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  sig_ok = 1;
+#endif
+  if (!sig_ok) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl->s3->tlsext_channel_id_valid = 0;
+    goto err;
+  }
+
+  memcpy(ssl->s3->tlsext_channel_id, p, 64);
+  ret = 1;
+
+err:
+  BN_free(&x);
+  BN_free(&y);
+  BN_free(sig.r);
+  BN_free(sig.s);
+  EC_KEY_free(key);
+  EC_POINT_free(point);
+  EC_GROUP_free(p256);
+  return ret;
+}
+
+int tls1_write_channel_id(SSL *ssl, CBB *cbb) {
+  uint8_t digest[EVP_MAX_MD_SIZE];
+  size_t digest_len;
+  if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+    return 0;
+  }
+
+  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+  if (ec_key == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  int ret = 0;
+  BIGNUM *x = BN_new();
+  BIGNUM *y = BN_new();
+  ECDSA_SIG *sig = NULL;
+  if (x == NULL || y == NULL ||
+      !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
+                                           EC_KEY_get0_public_key(ec_key),
+                                           x, y, NULL)) {
+    goto err;
+  }
+
+  sig = ECDSA_do_sign(digest, digest_len, ec_key);
+  if (sig == NULL) {
+    goto err;
+  }
+
+  CBB child;
+  if (!CBB_add_u16(cbb, TLSEXT_TYPE_channel_id) ||
+      !CBB_add_u16_length_prefixed(cbb, &child) ||
+      !BN_bn2cbb_padded(&child, 32, x) ||
+      !BN_bn2cbb_padded(&child, 32, y) ||
+      !BN_bn2cbb_padded(&child, 32, sig->r) ||
+      !BN_bn2cbb_padded(&child, 32, sig->s) ||
+      !CBB_flush(cbb)) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  BN_free(x);
+  BN_free(y);
+  ECDSA_SIG_free(sig);
+  return ret;
+}
+
+int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    uint8_t *msg;
+    size_t msg_len;
+    if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+                                               ssl_cert_verify_channel_id)) {
+      return 0;
+    }
+    SHA256(msg, msg_len, out);
+    *out_len = SHA256_DIGEST_LENGTH;
+    OPENSSL_free(msg);
+    return 1;
+  }
+
+  SHA256_CTX ctx;
+
+  SHA256_Init(&ctx);
   static const char kClientIDMagic[] = "TLS Channel ID signature";
-  EVP_DigestUpdate(&ctx, kClientIDMagic, sizeof(kClientIDMagic));
+  SHA256_Update(&ctx, kClientIDMagic, sizeof(kClientIDMagic));
 
   if (ssl->session != NULL) {
     static const char kResumptionMagic[] = "Resumption";
-    EVP_DigestUpdate(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
+    SHA256_Update(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
     if (ssl->session->original_handshake_hash_len == 0) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
+      return 0;
     }
-    EVP_DigestUpdate(&ctx, ssl->session->original_handshake_hash,
-                     ssl->session->original_handshake_hash_len);
+    SHA256_Update(&ctx, ssl->session->original_handshake_hash,
+                  ssl->session->original_handshake_hash_len);
   }
 
   uint8_t handshake_hash[EVP_MAX_MD_SIZE];
   int handshake_hash_len = tls1_handshake_digest(ssl, handshake_hash,
                                                  sizeof(handshake_hash));
   if (handshake_hash_len < 0) {
-    goto err;
+    return 0;
   }
-  EVP_DigestUpdate(&ctx, handshake_hash, (size_t)handshake_hash_len);
-  unsigned len_u;
-  EVP_DigestFinal_ex(&ctx, out, &len_u);
-  *out_len = len_u;
-
-  ret = 1;
-
-err:
-  EVP_MD_CTX_cleanup(&ctx);
-  return ret;
+  SHA256_Update(&ctx, handshake_hash, (size_t)handshake_hash_len);
+  SHA256_Final(out, &ctx);
+  *out_len = SHA256_DIGEST_LENGTH;
+  return 1;
 }
 
 /* tls1_record_handshake_hashes_for_channel_id records the current handshake
@@ -3272,3 +3452,44 @@
 
   return 1;
 }
+
+int ssl_do_channel_id_callback(SSL *ssl) {
+  if (ssl->tlsext_channel_id_private != NULL ||
+      ssl->ctx->channel_id_cb == NULL) {
+    return 1;
+  }
+
+  EVP_PKEY *key = NULL;
+  ssl->ctx->channel_id_cb(ssl, &key);
+  if (key == NULL) {
+    /* The caller should try again later. */
+    return 1;
+  }
+
+  int ret = SSL_set1_tls_channel_id(ssl, key);
+  EVP_PKEY_free(key);
+  return ret;
+}
+
+int ssl_is_sct_list_valid(const CBS *contents) {
+  /* Shallow parse the SCT list for sanity. By the RFC
+   * (https://tools.ietf.org/html/rfc6962#section-3.3) neither the list nor any
+   * of the SCTs may be empty. */
+  CBS copy = *contents;
+  CBS sct_list;
+  if (!CBS_get_u16_length_prefixed(&copy, &sct_list) ||
+      CBS_len(&copy) != 0 ||
+      CBS_len(&sct_list) == 0) {
+    return 0;
+  }
+
+  while (CBS_len(&sct_list) > 0) {
+    CBS sct;
+    if (!CBS_get_u16_length_prefixed(&sct_list, &sct) ||
+        CBS_len(&sct) == 0) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 76caa4e..97860c4 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -40,6 +40,7 @@
 #include <inttypes.h>
 #include <string.h>
 
+#include <openssl/aead.h>
 #include <openssl/bio.h>
 #include <openssl/buf.h>
 #include <openssl/bytestring.h>
@@ -137,12 +138,46 @@
   return (TestState *)SSL_get_ex_data(ssl, g_state_index);
 }
 
-static bssl::UniquePtr<X509> LoadCertificate(const std::string &file) {
+static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
+                            bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+                            const std::string &file) {
   bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
   if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
-    return nullptr;
+    return false;
   }
-  return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL));
+
+  out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+  if (!*out_x509) {
+    return false;
+  }
+
+  out_chain->reset(sk_X509_new_null());
+  if (!*out_chain) {
+    return false;
+  }
+
+  // Keep reading the certificate chain.
+  for (;;) {
+    bssl::UniquePtr<X509> cert(
+        PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+    if (!cert) {
+      break;
+    }
+
+    if (!sk_X509_push(out_chain->get(), cert.get())) {
+      return false;
+    }
+    cert.release();  // sk_X509_push takes ownership.
+  }
+
+  uint32_t err = ERR_peek_last_error();
+  if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+      ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
+    return false;
+}
+
+  ERR_clear_error();
+  return true;
 }
 
 static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
@@ -320,6 +355,7 @@
 };
 
 static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
+                           bssl::UniquePtr<STACK_OF(X509)> *out_chain,
                            bssl::UniquePtr<EVP_PKEY> *out_pkey) {
   const TestConfig *config = GetTestConfig(ssl);
 
@@ -358,14 +394,12 @@
       return false;
     }
   }
-  if (!config->cert_file.empty()) {
-    *out_x509 = LoadCertificate(config->cert_file.c_str());
-    if (!*out_x509) {
-      return false;
-    }
+  if (!config->cert_file.empty() &&
+      !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
+    return false;
   }
   if (!config->ocsp_response.empty() &&
-      !SSL_CTX_set_ocsp_response(ssl->ctx,
+      !SSL_CTX_set_ocsp_response(SSL_get_SSL_CTX(ssl),
                                  (const uint8_t *)config->ocsp_response.data(),
                                  config->ocsp_response.size())) {
     return false;
@@ -375,8 +409,9 @@
 
 static bool InstallCertificate(SSL *ssl) {
   bssl::UniquePtr<X509> x509;
+  bssl::UniquePtr<STACK_OF(X509)> chain;
   bssl::UniquePtr<EVP_PKEY> pkey;
-  if (!GetCertificate(ssl, &x509, &pkey)) {
+  if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
     return false;
   }
 
@@ -395,6 +430,11 @@
     return false;
   }
 
+  if (sk_X509_num(chain.get()) > 0 &&
+      !SSL_set1_chain(ssl, chain.get())) {
+    return false;
+  }
+
   return true;
 }
 
@@ -456,8 +496,9 @@
   }
 
   bssl::UniquePtr<X509> x509;
+  bssl::UniquePtr<STACK_OF(X509)> chain;
   bssl::UniquePtr<EVP_PKEY> pkey;
-  if (!GetCertificate(ssl, &x509, &pkey)) {
+  if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
     return -1;
   }
 
@@ -466,7 +507,7 @@
     return 0;
   }
 
-  // Asynchronous private keys are not supported with client_cert_cb.
+  // Chains and asynchronous private keys are not supported with client_cert_cb.
   *out_x509 = x509.release();
   *out_pkey = pkey.release();
   return 1;
@@ -594,8 +635,10 @@
   return config->psk.size();
 }
 
+static timeval g_clock;
+
 static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
-  *out_clock = PacketedBioGetClock(GetTestState(ssl)->packeted_bio);
+  *out_clock = g_clock;
 }
 
 static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
@@ -622,6 +665,10 @@
     }
   }
 
+  if (config->fail_cert_callback) {
+    return 0;
+  }
+
   // The certificate will be installed via other means.
   if (!config->async || config->use_early_callback ||
       config->use_old_client_cert_callback) {
@@ -774,6 +821,20 @@
   return 1;
 }
 
+static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) {
+  // SNI must be accessible from the SNI callback.
+  const TestConfig *config = GetTestConfig(ssl);
+  const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (server_name == nullptr ||
+      std::string(server_name) != config->expected_server_name) {
+    fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name,
+            config->expected_server_name.c_str());
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
+
+  return SSL_TLSEXT_ERR_OK;
+}
+
 // Connect returns a new socket connected to localhost on |port| or -1 on
 // error.
 static int Connect(uint16_t port) {
@@ -923,9 +984,7 @@
   SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
   SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
 
-  if (config->is_dtls) {
-    SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
-  }
+  SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
 
   SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
   SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
@@ -971,6 +1030,16 @@
     SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
   }
 
+  if (!config->expected_server_name.empty()) {
+    SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
+  }
+
+  if (!config->ticket_key.empty() &&
+      !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), config->ticket_key.data(),
+                                      config->ticket_key.size())) {
+    return nullptr;
+  }
+
   return ssl_ctx;
 }
 
@@ -1054,6 +1123,16 @@
     if (config->async) {
       AsyncBioEnforceWriteQuota(test_state->async_bio, true);
     }
+
+    // Run the exporter after each read. This is to test that the exporter fails
+    // during a renegotiation.
+    if (config->use_exporter_between_reads) {
+      uint8_t buf;
+      if (!SSL_export_keying_material(ssl, &buf, 1, NULL, 0, NULL, 0, 0)) {
+        fprintf(stderr, "failed to export keying material\n");
+        return -1;
+      }
+    }
   } while (config->async && RetryAsync(ssl, ret));
 
   if (config->peek_then_read && ret > 0) {
@@ -1171,7 +1250,8 @@
   if (!config->expected_server_name.empty()) {
     const char *server_name =
         SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-    if (server_name != config->expected_server_name) {
+    if (server_name == nullptr ||
+        server_name != config->expected_server_name) {
       fprintf(stderr, "servername mismatch (got %s; want %s)\n",
               server_name, config->expected_server_name.c_str());
       return false;
@@ -1285,6 +1365,25 @@
     }
   }
 
+  uint16_t cipher_id =
+      static_cast<uint16_t>(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)));
+  if (config->expect_cipher_aes != 0 &&
+      EVP_has_aes_hardware() &&
+      static_cast<uint16_t>(config->expect_cipher_aes) != cipher_id) {
+    fprintf(stderr, "Cipher ID was %04x, wanted %04x (has AES hardware)\n",
+            cipher_id, static_cast<uint16_t>(config->expect_cipher_aes));
+    return false;
+  }
+
+  if (config->expect_cipher_no_aes != 0 &&
+      !EVP_has_aes_hardware() &&
+      static_cast<uint16_t>(config->expect_cipher_no_aes) != cipher_id) {
+    fprintf(stderr, "Cipher ID was %04x, wanted %04x (no AES hardware)\n",
+            cipher_id, static_cast<uint16_t>(config->expect_cipher_no_aes));
+    return false;
+  }
+
+
   if (!config->psk.empty()) {
     if (SSL_get_peer_cert_chain(ssl) != nullptr) {
       fprintf(stderr, "Received peer certificate on a PSK cipher.\n");
@@ -1297,6 +1396,65 @@
     }
   }
 
+  if (!config->expect_peer_cert_file.empty()) {
+    bssl::UniquePtr<X509> expect_leaf;
+    bssl::UniquePtr<STACK_OF(X509)> expect_chain;
+    if (!LoadCertificate(&expect_leaf, &expect_chain,
+                         config->expect_peer_cert_file)) {
+      return false;
+    }
+
+    // For historical reasons, clients report a chain with a leaf and servers
+    // without.
+    if (!config->is_server) {
+      if (!sk_X509_insert(expect_chain.get(), expect_leaf.get(), 0)) {
+        return false;
+      }
+      X509_up_ref(expect_leaf.get());  // sk_X509_push takes ownership.
+    }
+
+    bssl::UniquePtr<X509> leaf(SSL_get_peer_certificate(ssl));
+    STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl);
+    if (X509_cmp(leaf.get(), expect_leaf.get()) != 0) {
+      fprintf(stderr, "Received a different leaf certificate than expected.\n");
+      return false;
+    }
+
+    if (sk_X509_num(chain) != sk_X509_num(expect_chain.get())) {
+      fprintf(stderr, "Received a chain of length %zu instead of %zu.\n",
+              sk_X509_num(chain), sk_X509_num(expect_chain.get()));
+      return false;
+    }
+
+    for (size_t i = 0; i < sk_X509_num(chain); i++) {
+      if (X509_cmp(sk_X509_value(chain, i),
+                   sk_X509_value(expect_chain.get(), i)) != 0) {
+        fprintf(stderr, "Chain certificate %zu did not match.\n",
+                i + 1);
+        return false;
+      }
+    }
+  }
+
+  bool expected_sha256_client_cert = config->expect_sha256_client_cert_initial;
+  if (is_resume) {
+    expected_sha256_client_cert = config->expect_sha256_client_cert_resume;
+  }
+
+  if (SSL_get_session(ssl)->peer_sha256_valid != expected_sha256_client_cert) {
+    fprintf(stderr,
+            "Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n",
+            expected_sha256_client_cert, is_resume);
+    return false;
+  }
+
+  if (expected_sha256_client_cert &&
+      SSL_get_session(ssl)->x509_peer != nullptr) {
+    fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
+            is_resume);
+    return false;
+  }
+
   return true;
 }
 
@@ -1456,6 +1614,12 @@
   if (config->max_cert_list > 0) {
     SSL_set_max_cert_list(ssl.get(), config->max_cert_list);
   }
+  if (!is_resume && config->retain_only_sha256_client_cert_initial) {
+    SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
+  }
+  if (is_resume && config->retain_only_sha256_client_cert_resume) {
+    SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
@@ -1468,7 +1632,7 @@
     return false;
   }
   if (config->is_dtls) {
-    bssl::UniquePtr<BIO> packeted = PacketedBioCreate(!config->async);
+    bssl::UniquePtr<BIO> packeted = PacketedBioCreate(&g_clock, !config->async);
     if (!packeted) {
       return false;
     }
@@ -1743,6 +1907,11 @@
     return Usage(argv[0]);
   }
 
+  // Some code treats the zero time special, so initialize the clock to a
+  // non-zero time.
+  g_clock.tv_sec = 1234;
+  g_clock.tv_usec = 1234;
+
   bssl::UniquePtr<SSL_CTX> ssl_ctx = SetupCtx(&config);
   if (!ssl_ctx) {
     ERR_print_errors_fp(stderr);
@@ -1764,6 +1933,10 @@
       ERR_print_errors_fp(stderr);
       return 1;
     }
+
+    if (config.resumption_delay != 0) {
+      g_clock.tv_sec += config.resumption_delay;
+    }
   }
 
   return 0;
diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc
index f7267fc..8331b4b 100644
--- a/src/ssl/test/packeted_bio.cc
+++ b/src/ssl/test/packeted_bio.cc
@@ -31,10 +31,9 @@
 const uint8_t kOpcodeTimeoutAck = 't';
 
 struct PacketedBio {
-  explicit PacketedBio(bool advance_clock_arg)
-      : advance_clock(advance_clock_arg) {
+  PacketedBio(timeval *clock_arg, bool advance_clock_arg)
+      : clock(clock_arg), advance_clock(advance_clock_arg) {
     memset(&timeout, 0, sizeof(timeout));
-    memset(&clock, 0, sizeof(clock));
     memset(&read_deadline, 0, sizeof(read_deadline));
   }
 
@@ -47,14 +46,14 @@
       return true;
     }
 
-    if (clock.tv_sec == read_deadline.tv_sec) {
-      return clock.tv_usec < read_deadline.tv_usec;
+    if (clock->tv_sec == read_deadline.tv_sec) {
+      return clock->tv_usec < read_deadline.tv_usec;
     }
-    return clock.tv_sec < read_deadline.tv_sec;
+    return clock->tv_sec < read_deadline.tv_sec;
   }
 
   timeval timeout;
-  timeval clock;
+  timeval *clock;
   timeval read_deadline;
   bool advance_clock;
 };
@@ -66,10 +65,6 @@
   return (PacketedBio *)bio->ptr;
 }
 
-const PacketedBio *GetData(const BIO *bio) {
-  return GetData(const_cast<BIO*>(bio));
-}
-
 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
 // 0 or -1 on error.
 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
@@ -272,19 +267,15 @@
 
 }  // namespace
 
-bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock) {
+bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock, bool advance_clock) {
   bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method));
   if (!bio) {
     return nullptr;
   }
-  bio->ptr = new PacketedBio(advance_clock);
+  bio->ptr = new PacketedBio(clock, advance_clock);
   return bio;
 }
 
-timeval PacketedBioGetClock(const BIO *bio) {
-  return GetData(bio)->clock;
-}
-
 bool PacketedBioAdvanceClock(BIO *bio) {
   PacketedBio *data = GetData(bio);
   if (data == nullptr) {
@@ -295,10 +286,10 @@
     return false;
   }
 
-  data->clock.tv_usec += data->timeout.tv_usec;
-  data->clock.tv_sec += data->clock.tv_usec / 1000000;
-  data->clock.tv_usec %= 1000000;
-  data->clock.tv_sec += data->timeout.tv_sec;
+  data->clock->tv_usec += data->timeout.tv_usec;
+  data->clock->tv_sec += data->clock->tv_usec / 1000000;
+  data->clock->tv_usec %= 1000000;
+  data->clock->tv_sec += data->timeout.tv_sec;
   memset(&data->timeout, 0, sizeof(data->timeout));
   return true;
 }
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 07930d4..9d4cdcb 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -28,21 +28,18 @@
 
 
 // PacketedBioCreate creates a filter BIO which implements a reliable in-order
-// blocking datagram socket. It internally maintains a clock and honors
-// |BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT| based on it.
+// blocking datagram socket. It uses the value of |*clock| as the clock and
+// honors |BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT| based on it.
 //
 // During a |BIO_read|, the peer may signal the filter BIO to simulate a
 // timeout. If |advance_clock| is true, it automatically advances the clock and
 // continues reading, subject to the read deadline. Otherwise, it fails
 // immediately. The caller must then call |PacketedBioAdvanceClock| before
 // retrying |BIO_read|.
-bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock);
+bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock, bool advance_clock);
 
-// PacketedBioGetClock returns the current time for |bio|.
-timeval PacketedBioGetClock(const BIO *bio);
-
-// PacketedBioAdvanceClock advances |bio|'s internal clock and returns true if
-// there is a pending timeout. Otherwise, it returns false.
+// PacketedBioAdvanceClock advances |bio|'s clock and returns true if there is a
+// pending timeout. Otherwise, it returns false.
 bool PacketedBioAdvanceClock(BIO *bio);
 
 
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 656a3d0..a997016 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -480,12 +480,16 @@
 func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
 	for _, id := range have {
 		if id == want {
-			for _, suite := range cipherSuites {
-				if suite.id == want {
-					return suite
-				}
-			}
-			return nil
+			return cipherSuiteFromID(id)
+		}
+	}
+	return nil
+}
+
+func cipherSuiteFromID(id uint16) *cipherSuite {
+	for _, suite := range cipherSuites {
+		if suite.id == id {
+			return suite
 		}
 	}
 	return nil
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 62c8dd3..a6496fd 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -27,7 +27,7 @@
 )
 
 // A draft version of TLS 1.3 that is sent over the wire for the current draft.
-const tls13DraftVersion = 0x7f10
+const tls13DraftVersion = 0x7f12
 
 const (
 	maxPlaintext        = 16384        // maximum plaintext payload length
@@ -94,6 +94,7 @@
 	extensionEarlyData                  uint16 = 42    // draft-ietf-tls-tls13-16
 	extensionSupportedVersions          uint16 = 43    // draft-ietf-tls-tls13-16
 	extensionCookie                     uint16 = 44    // draft-ietf-tls-tls13-16
+	extensionPSKKeyExchangeModes        uint16 = 45    // draft-ietf-tls-tls13-18
 	extensionCustom                     uint16 = 1234  // not IANA assigned
 	extensionNextProtoNeg               uint16 = 13172 // not IANA assigned
 	extensionRenegotiationInfo          uint16 = 0xff01
@@ -200,12 +201,6 @@
 	pskDHEKEMode = 1
 )
 
-// PskAuthenticationMode values (see draft-ietf-tls-tls13-16)
-const (
-	pskAuthMode     = 0
-	pskSignAuthMode = 1
-)
-
 // KeyUpdateRequest values (see draft-ietf-tls-tls13-16, section 4.5.3)
 const (
 	keyUpdateNotRequested = 0
@@ -259,6 +254,7 @@
 	ocspResponse         []byte
 	ticketCreationTime   time.Time
 	ticketExpiration     time.Time
+	ticketAgeAdd         uint32
 }
 
 // ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -661,9 +657,9 @@
 	// TLS 1.2 and 1.3 extensions.
 	SendBothTickets bool
 
-	// CorruptTicket causes a client to corrupt a session ticket before
-	// sending it in a resume handshake.
-	CorruptTicket bool
+	// FilterTicket, if not nil, causes the client to modify a session
+	// ticket before sending it in a resume handshake.
+	FilterTicket func([]byte) ([]byte, error)
 
 	// OversizedSessionId causes the session id that is sent with a ticket
 	// resumption attempt to be too large (33 bytes).
@@ -753,9 +749,17 @@
 	RequireSameRenegoClientVersion bool
 
 	// ExpectInitialRecordVersion, if non-zero, is the expected value of
-	// record-layer version field before the version is determined.
+	// record-layer version field before the protocol version is determined.
 	ExpectInitialRecordVersion uint16
 
+	// SendRecordVersion, if non-zero, is the value to send as the
+	// record-layer version.
+	SendRecordVersion uint16
+
+	// SendInitialRecordVersion, if non-zero, is the value to send as the
+	// record-layer version before the protocol version is determined.
+	SendInitialRecordVersion uint16
+
 	// MaxPacketLength, if non-zero, is the maximum acceptable size for a
 	// packet.
 	MaxPacketLength int
@@ -765,6 +769,11 @@
 	// the server believes it has actually negotiated.
 	SendCipherSuite uint16
 
+	// SendCipherSuites, if not nil, is the cipher suite list that the
+	// client will send in the ClientHello. This does not affect the cipher
+	// the client believes it has actually offered.
+	SendCipherSuites []uint16
+
 	// AppDataBeforeHandshake, if not nil, causes application data to be
 	// sent immediately before the first handshake message.
 	AppDataBeforeHandshake []byte
@@ -939,12 +948,17 @@
 	// session ticket.
 	SendEmptySessionTicket bool
 
-	// SnedPSKKeyExchangeModes, if present, determines the PSK key exchange modes
+	// SendPSKKeyExchangeModes, if present, determines the PSK key exchange modes
 	// to send.
 	SendPSKKeyExchangeModes []byte
 
-	// SendPSKAuthModes, if present, determines the PSK auth modes to send.
-	SendPSKAuthModes []byte
+	// ExpectNoNewSessionTicket, if present, means that the client will fail upon
+	// receipt of a NewSessionTicket message.
+	ExpectNoNewSessionTicket bool
+
+	// ExpectTicketAge, if non-zero, is the expected age of the ticket that the
+	// server receives from the client.
+	ExpectTicketAge time.Duration
 
 	// FailIfSessionOffered, if true, causes the server to fail any
 	// connections where the client offers a non-empty session ID or session
@@ -991,6 +1005,26 @@
 	// supplied stapled response.
 	SendOCSPResponseOnResume []byte
 
+	// SendExtensionOnCertificate, if not nil, causes the runner to send the
+	// supplied bytes in the extensions on the Certificate message.
+	SendExtensionOnCertificate []byte
+
+	// SendOCSPOnIntermediates, if not nil, causes the server to send the
+	// supplied OCSP on intermediate certificates in the Certificate message.
+	SendOCSPOnIntermediates []byte
+
+	// SendSCTOnIntermediates, if not nil, causes the server to send the
+	// supplied SCT on intermediate certificates in the Certificate message.
+	SendSCTOnIntermediates []byte
+
+	// SendDuplicateCertExtensions, if true, causes the server to send an extra
+	// copy of the OCSP/SCT extensions in the Certificate message.
+	SendDuplicateCertExtensions bool
+
+	// ExpectNoExtensionsOnIntermediate, if true, causes the client to
+	// reject extensions on intermediate certificates.
+	ExpectNoExtensionsOnIntermediate bool
+
 	// CECPQ1BadX25519Part corrupts the X25519 part of a CECPQ1 key exchange, as
 	// a trivial proof that it is actually used.
 	CECPQ1BadX25519Part bool
@@ -1028,10 +1062,6 @@
 	// Renegotiation Info to be negotiated at all versions.
 	NegotiateRenegotiationInfoAtAllVersions bool
 
-	// NegotiateChannelIDAtAllVersions, if true, causes Channel ID to be
-	// negotiated at all versions.
-	NegotiateChannelIDAtAllVersions bool
-
 	// NegotiateNPNAtAllVersions, if true, causes NPN to be negotiated at
 	// all versions.
 	NegotiateNPNAtAllVersions bool
@@ -1056,13 +1086,9 @@
 	// the specified PSK identity index rather than the actual value.
 	SelectPSKIdentityOnResume uint16
 
-	// OmitServerHelloSignatureAlgorithms, if true, causes the server to omit the
-	// signature_algorithms extension in the ServerHello.
-	OmitServerHelloSignatureAlgorithms bool
-
-	// IncludeServerHelloSignatureAlgorithms, if true, causes the server to
-	// include the signature_algorithms extension in all ServerHellos.
-	IncludeServerHelloSignatureAlgorithms bool
+	// ExtraPSKIdentity, if true, causes the client to send an extra PSK
+	// identity.
+	ExtraPSKIdentity bool
 
 	// MissingKeyShare, if true, causes the TLS 1.3 implementation to skip
 	// sending a key_share extension and use the zero ECDHE secret
@@ -1160,6 +1186,29 @@
 	// ExpectGREASE, if true, causes messages without GREASE values to be
 	// rejected. See draft-davidben-tls-grease-01.
 	ExpectGREASE bool
+
+	// SendShortPSKBinder, if true, causes the client to send a PSK binder
+	// that is one byte shorter than it should be.
+	SendShortPSKBinder bool
+
+	// SendInvalidPSKBinder, if true, causes the client to send an invalid
+	// PSK binder.
+	SendInvalidPSKBinder bool
+
+	// SendNoPSKBinder, if true, causes the client to send no PSK binders.
+	SendNoPSKBinder bool
+
+	// PSKBinderFirst, if true, causes the client to send the PSK Binder
+	// extension as the first extension instead of the last extension.
+	PSKBinderFirst bool
+
+	// NoOCSPStapling, if true, causes the client to not request OCSP
+	// stapling.
+	NoOCSPStapling bool
+
+	// NoSignedCertificateTimestamps, if true, causes the client to not
+	// request signed certificate timestamps.
+	NoSignedCertificateTimestamps bool
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index f5014d4..39c2785 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -208,9 +208,9 @@
 }
 
 // useTrafficSecret sets the current cipher state for TLS 1.3.
-func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) {
+func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) {
 	hc.version = version
-	hc.cipher = deriveTrafficAEAD(version, suite, secret, phase, side)
+	hc.cipher = deriveTrafficAEAD(version, suite, secret, side)
 	if hc.config.Bugs.NullAllCiphers {
 		hc.cipher = nullCipher{}
 	}
@@ -223,7 +223,7 @@
 	if c.isClient == isOutgoing {
 		side = clientWrite
 	}
-	hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), applicationPhase, side)
+	hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
 }
 
 // incSeq increments the sequence number.
@@ -753,16 +753,18 @@
 	// record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer
 	// version is irrelevant.)
 	if typ != recordTypeAlert {
+		var expect uint16
 		if c.haveVers {
-			if vers != c.vers && c.vers < VersionTLS13 {
-				c.sendAlert(alertProtocolVersion)
-				return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
+			expect = c.vers
+			if c.vers >= VersionTLS13 {
+				expect = VersionTLS10
 			}
 		} else {
-			if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect {
-				c.sendAlert(alertProtocolVersion)
-				return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, expect))
-			}
+			expect = c.config.Bugs.ExpectInitialRecordVersion
+		}
+		if expect != 0 && vers != expect {
+			c.sendAlert(alertProtocolVersion)
+			return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, expect))
 		}
 	}
 	if n > maxCiphertext {
@@ -1063,6 +1065,12 @@
 			// layer to {3, 1}.
 			vers = VersionTLS10
 		}
+		if c.config.Bugs.SendRecordVersion != 0 {
+			vers = c.config.Bugs.SendRecordVersion
+		}
+		if c.vers == 0 && c.config.Bugs.SendInitialRecordVersion != 0 {
+			vers = c.config.Bugs.SendInitialRecordVersion
+		}
 		b.data[1] = byte(vers >> 8)
 		b.data[2] = byte(vers)
 		b.data[3] = byte(m >> 8)
@@ -1396,27 +1404,14 @@
 				return errors.New("tls: no GREASE ticket extension found")
 			}
 
+			if c.config.Bugs.ExpectNoNewSessionTicket {
+				return errors.New("tls: received unexpected NewSessionTicket")
+			}
+
 			if c.config.ClientSessionCache == nil || newSessionTicket.ticketLifetime == 0 {
 				return nil
 			}
 
-			var foundKE, foundAuth bool
-			for _, mode := range newSessionTicket.keModes {
-				if mode == pskDHEKEMode {
-					foundKE = true
-				}
-			}
-			for _, mode := range newSessionTicket.authModes {
-				if mode == pskAuthMode {
-					foundAuth = true
-				}
-			}
-
-			// Ignore the ticket if the server preferences do not match a mode we implement.
-			if !foundKE || !foundAuth {
-				return nil
-			}
-
 			session := &ClientSessionState{
 				sessionTicket:      newSessionTicket.ticket,
 				vers:               c.vers,
@@ -1427,6 +1422,7 @@
 				ocspResponse:       c.ocspResponse,
 				ticketCreationTime: c.config.time(),
 				ticketExpiration:   c.config.time().Add(time.Duration(newSessionTicket.ticketLifetime) * time.Second),
+				ticketAgeAdd:       newSessionTicket.ticketAgeAdd,
 			}
 
 			cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
@@ -1709,20 +1705,20 @@
 		peerCertificatesRaw = append(peerCertificatesRaw, cert.Raw)
 	}
 
+	addBuffer := make([]byte, 4)
+	_, err := io.ReadFull(c.config.rand(), addBuffer)
+	if err != nil {
+		c.sendAlert(alertInternalError)
+		return errors.New("tls: short read from Rand: " + err.Error())
+	}
+	ticketAgeAdd := uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0])
+
 	// TODO(davidben): Allow configuring these values.
 	m := &newSessionTicketMsg{
 		version:         c.vers,
 		ticketLifetime:  uint32(24 * time.Hour / time.Second),
-		keModes:         []byte{pskDHEKEMode},
-		authModes:       []byte{pskAuthMode},
 		customExtension: c.config.Bugs.CustomTicketExtension,
-	}
-
-	if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
-		m.keModes = c.config.Bugs.SendPSKKeyExchangeModes
-	}
-	if len(c.config.Bugs.SendPSKAuthModes) != 0 {
-		m.authModes = c.config.Bugs.SendPSKAuthModes
+		ticketAgeAdd:    ticketAgeAdd,
 	}
 
 	state := sessionState{
@@ -1732,6 +1728,7 @@
 		certificates:       peerCertificatesRaw,
 		ticketCreationTime: c.config.time(),
 		ticketExpiration:   c.config.time().Add(time.Duration(m.ticketLifetime) * time.Second),
+		ticketAgeAdd:       uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0]),
 	}
 
 	if !c.config.Bugs.SendEmptySessionTicket {
@@ -1744,7 +1741,7 @@
 
 	c.out.Lock()
 	defer c.out.Unlock()
-	_, err := c.writeRecord(recordTypeHandshake, m.marshal())
+	_, err = c.writeRecord(recordTypeHandshake, m.marshal())
 	return err
 }
 
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 8fc1a56..24f3bd6 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -1,14 +1,14 @@
 {
   "DisabledTests": {
     "BadCBCPadding*": "Fuzzer mode has no CBC padding.",
-    "BadECDSA-*": "Fuzzer mode ignores invalid signatures.",
-    "*-InvalidSignature-*": "Fuzzer mode ignores invalid signatures.",
+
     "BadFinished-*": "Fuzzer mode ignores Finished checks.",
     "FalseStart-BadFinished": "Fuzzer mode ignores Finished checks.",
     "TrailingMessageData-*Finished*": "Fuzzer mode ignores Finished checks.",
 
     "DTLSIgnoreBadPackets*": "Fuzzer mode has no bad packets.",
     "TLSFatalBadPackets": "Fuzzer mode has no bad packets.",
+    "*-BadRecord": "Fuzzer mode has no bad packets.",
 
     "BadRSAClientKeyExchange*": "Fuzzer mode does not notice a bad premaster secret.",
     "CECPQ1-*-BadNewhopePart": "Fuzzer mode does not notice a bad premaster secret.",
@@ -19,10 +19,20 @@
     "UnknownUnencryptedExtension-Client-TLS13": "Fuzzer mode will not read the peer's alert as a MAC error",
     "WrongMessageType-TLS13-ServerHello": "Fuzzer mode will not read the peer's alert as a MAC error",
 
+    "BadECDSA-*": "Fuzzer mode always accepts a signature.",
+    "*-InvalidSignature-*": "Fuzzer mode always accepts a signature.",
     "*Auth-Verify-RSA-PKCS1-*-TLS13": "Fuzzer mode always accepts a signature.",
     "*Auth-Verify-ECDSA-SHA1-TLS13": "Fuzzer mode always accepts a signature.",
     "Verify-*Auth-SignatureType*": "Fuzzer mode always accepts a signature.",
     "ECDSACurveMismatch-Verify-TLS13": "Fuzzer mode always accepts a signature.",
-    "InvalidChannelIDSignature": "Fuzzer mode always accepts a signature."
+    "InvalidChannelIDSignature-*": "Fuzzer mode always accepts a signature.",
+
+    "Resume-Server-DeclineBadCipher*": "Fuzzer mode does not encrypt tickets.",
+    "Resume-Server-DeclineCrossVersion*": "Fuzzer mode does not encrypt tickets.",
+    "TicketCallback-SingleCall-*": "Fuzzer mode does not encrypt tickets.",
+    "CorruptTicket-*": "Fuzzer mode does not encrypt tickets.",
+    "ShimTicketRewritable": "Fuzzer mode does not encrypt tickets.",
+
+    "Resume-Server-*Binder*": "Fuzzer mode does not check binders."
   }
 }
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index ae3228a..d074778 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -64,21 +64,23 @@
 		vers:                    versionToWire(maxVersion, c.isDTLS),
 		compressionMethods:      []uint8{compressionNone},
 		random:                  make([]byte, 32),
-		ocspStapling:            true,
-		sctListSupported:        true,
+		ocspStapling:            !c.config.Bugs.NoOCSPStapling,
+		sctListSupported:        !c.config.Bugs.NoSignedCertificateTimestamps,
 		serverName:              c.config.ServerName,
 		supportedCurves:         c.config.curvePreferences(),
+		pskKEModes:              []byte{pskDHEKEMode},
 		supportedPoints:         []uint8{pointFormatUncompressed},
 		nextProtoNeg:            len(c.config.NextProtos) > 0,
 		secureRenegotiation:     []byte{},
 		alpnProtocols:           c.config.NextProtos,
 		duplicateExtension:      c.config.Bugs.DuplicateExtension,
 		channelIDSupported:      c.config.ChannelID != nil,
-		npnLast:                 c.config.Bugs.SwapNPNAndALPN,
+		npnAfterAlpn:            c.config.Bugs.SwapNPNAndALPN,
 		extendedMasterSecret:    maxVersion >= VersionTLS10,
 		srtpProtectionProfiles:  c.config.SRTPProtectionProfiles,
 		srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
 		customExtension:         c.config.Bugs.CustomExtension,
+		pskBinderFirst:          c.config.Bugs.PSKBinderFirst,
 	}
 
 	disableEMS := c.config.Bugs.NoExtendedMasterSecret
@@ -94,6 +96,10 @@
 		hello.supportedCurves = nil
 	}
 
+	if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
+		hello.pskKEModes = c.config.Bugs.SendPSKKeyExchangeModes
+	}
+
 	if c.config.Bugs.SendCompressionMethods != nil {
 		hello.compressionMethods = c.config.Bugs.SendCompressionMethods
 	}
@@ -217,11 +223,17 @@
 			// Check that the ciphersuite/version used for the
 			// previous session are still valid.
 			cipherSuiteOk := false
-			for _, id := range hello.cipherSuites {
-				if id == candidateSession.cipherSuite {
-					cipherSuiteOk = true
-					break
+			if candidateSession.vers <= VersionTLS12 {
+				for _, id := range hello.cipherSuites {
+					if id == candidateSession.cipherSuite {
+						cipherSuiteOk = true
+						break
+					}
 				}
+			} else {
+				// TLS 1.3 allows the cipher to change on
+				// resumption.
+				cipherSuiteOk = true
 			}
 
 			versOk := candidateSession.vers >= minVersion &&
@@ -232,34 +244,37 @@
 		}
 	}
 
+	var pskCipherSuite *cipherSuite
 	if session != nil && c.config.time().Before(session.ticketExpiration) {
 		ticket := session.sessionTicket
-		if c.config.Bugs.CorruptTicket && len(ticket) > 0 {
+		if c.config.Bugs.FilterTicket != nil && len(ticket) > 0 {
+			// Copy the ticket so FilterTicket may act in-place.
 			ticket = make([]byte, len(session.sessionTicket))
 			copy(ticket, session.sessionTicket)
-			offset := 40
-			if offset >= len(ticket) {
-				offset = len(ticket) - 1
+
+			ticket, err = c.config.Bugs.FilterTicket(ticket)
+			if err != nil {
+				return err
 			}
-			ticket[offset] ^= 0x40
 		}
 
 		if session.vers >= VersionTLS13 || c.config.Bugs.SendBothTickets {
+			pskCipherSuite = cipherSuiteFromID(session.cipherSuite)
+			if pskCipherSuite == nil {
+				return errors.New("tls: client session cache has invalid cipher suite")
+			}
 			// TODO(nharper): Support sending more
 			// than one PSK identity.
+			ticketAge := uint32(c.config.time().Sub(session.ticketCreationTime) / time.Millisecond)
 			psk := pskIdentity{
-				keModes:   []byte{pskDHEKEMode},
-				authModes: []byte{pskAuthMode},
-				ticket:    ticket,
+				ticket:              ticket,
+				obfuscatedTicketAge: session.ticketAgeAdd + ticketAge,
 			}
-			if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
-				psk.keModes = c.config.Bugs.SendPSKKeyExchangeModes
-			}
-			if len(c.config.Bugs.SendPSKAuthModes) != 0 {
-				psk.authModes = c.config.Bugs.SendPSKAuthModes
-			}
-
 			hello.pskIdentities = []pskIdentity{psk}
+
+			if c.config.Bugs.ExtraPSKIdentity {
+				hello.pskIdentities = append(hello.pskIdentities, psk)
+			}
 		}
 
 		if session.vers < VersionTLS13 || c.config.Bugs.SendBothTickets {
@@ -300,6 +315,10 @@
 		hello.vers = c.config.Bugs.SendClientVersion
 	}
 
+	if c.config.Bugs.SendCipherSuites != nil {
+		hello.cipherSuites = c.config.Bugs.SendCipherSuites
+	}
+
 	var helloBytes []byte
 	if c.config.Bugs.SendV2ClientHello {
 		// Test that the peer left-pads random.
@@ -314,7 +333,11 @@
 		helloBytes = v2Hello.marshal()
 		c.writeV2Record(helloBytes)
 	} else {
+		if len(hello.pskIdentities) > 0 {
+			generatePSKBinders(hello, pskCipherSuite, session.masterSecret, []byte{}, c.config)
+		}
 		helloBytes = hello.marshal()
+
 		if c.config.Bugs.PartialClientFinishedWithClientHello {
 			// Include one byte of Finished. We can compute it
 			// without completing the handshake. This assumes we
@@ -341,7 +364,7 @@
 	if c.isDTLS {
 		helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg)
 		if ok {
-			if helloVerifyRequest.vers != VersionTLS10 {
+			if helloVerifyRequest.vers != versionToWire(VersionTLS10, c.isDTLS) {
 				// Per RFC 6347, the version field in
 				// HelloVerifyRequest SHOULD be always DTLS
 				// 1.0. Enforce this for testing purposes.
@@ -419,10 +442,10 @@
 				return err
 			}
 			keyShares[group] = curve
-			hello.keyShares = append(hello.keyShares, keyShareEntry{
+			hello.keyShares = []keyShareEntry{{
 				group:       group,
 				keyExchange: publicKey,
-			})
+			}}
 		}
 
 		if c.config.Bugs.SecondClientHelloMissingKeyShare {
@@ -430,9 +453,11 @@
 		}
 
 		hello.hasEarlyData = false
-		hello.earlyDataContext = nil
 		hello.raw = nil
 
+		if len(hello.pskIdentities) > 0 {
+			generatePSKBinders(hello, pskCipherSuite, session.masterSecret, append(helloBytes, helloRetryRequest.marshal()...), c.config)
+		}
 		secondHelloBytes = hello.marshal()
 		c.writeRecord(recordTypeHandshake, secondHelloBytes)
 		c.flushHandshake()
@@ -599,35 +624,29 @@
 	// 0-RTT is implemented.
 	var psk []byte
 	if hs.serverHello.hasPSKIdentity {
-		if hs.serverHello.useCertAuth || !hs.serverHello.hasKeyShare {
-			c.sendAlert(alertUnsupportedExtension)
-			return errors.New("tls: server omitted KeyShare or included SignatureAlgorithms on resumption.")
-		}
-
 		// We send at most one PSK identity.
 		if hs.session == nil || hs.serverHello.pskIdentity != 0 {
 			c.sendAlert(alertUnknownPSKIdentity)
 			return errors.New("tls: server sent unknown PSK identity")
 		}
-		if hs.session.cipherSuite != hs.suite.id {
+		sessionCipher := cipherSuiteFromID(hs.session.cipherSuite)
+		if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() {
 			c.sendAlert(alertHandshakeFailure)
-			return errors.New("tls: server sent invalid cipher suite")
+			return errors.New("tls: server resumed an invalid session for the cipher suite")
 		}
-		psk = deriveResumptionPSK(hs.suite, hs.session.masterSecret)
-		hs.finishedHash.setResumptionContext(deriveResumptionContext(hs.suite, hs.session.masterSecret))
+		psk = hs.session.masterSecret
 		c.didResume = true
 	} else {
-		if !hs.serverHello.useCertAuth || !hs.serverHello.hasKeyShare {
-			c.sendAlert(alertUnsupportedExtension)
-			return errors.New("tls: server omitted KeyShare and SignatureAlgorithms on non-resumption.")
-		}
-
 		psk = zeroSecret
-		hs.finishedHash.setResumptionContext(zeroSecret)
 	}
 
 	earlySecret := hs.finishedHash.extractKey(zeroSecret, psk)
 
+	if !hs.serverHello.hasKeyShare {
+		c.sendAlert(alertUnsupportedExtension)
+		return errors.New("tls: server omitted KeyShare on resumption.")
+	}
+
 	// Resolve ECDHE and compute the handshake secret.
 	var ecdheSecret []byte
 	if !c.config.Bugs.MissingKeyShare && !c.config.Bugs.SecondClientHelloMissingKeyShare {
@@ -652,9 +671,9 @@
 
 	// Switch to handshake traffic keys.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
-	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
-	c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -675,24 +694,12 @@
 
 	var chainToSend *Certificate
 	var certReq *certificateRequestMsg
-	if !hs.serverHello.useCertAuth {
-		if encryptedExtensions.extensions.ocspResponse != nil {
-			c.sendAlert(alertUnsupportedExtension)
-			return errors.New("tls: server sent OCSP response without a certificate")
-		}
-		if encryptedExtensions.extensions.sctList != nil {
-			c.sendAlert(alertUnsupportedExtension)
-			return errors.New("tls: server sent SCT list without a certificate")
-		}
-
+	if c.didResume {
 		// Copy over authentication from the session.
 		c.peerCertificates = hs.session.serverCertificates
 		c.sctList = hs.session.sctList
 		c.ocspResponse = hs.session.ocspResponse
 	} else {
-		c.ocspResponse = encryptedExtensions.extensions.ocspResponse
-		c.sctList = encryptedExtensions.extensions.sctList
-
 		msg, err := c.readHandshake()
 		if err != nil {
 			return err
@@ -729,10 +736,28 @@
 		}
 		hs.writeServerHash(certMsg.marshal())
 
+		// Check for unsolicited extensions.
+		for i, cert := range certMsg.certificates {
+			if c.config.Bugs.NoOCSPStapling && cert.ocspResponse != nil {
+				c.sendAlert(alertUnsupportedExtension)
+				return errors.New("tls: unexpected OCSP response in the server certificate")
+			}
+			if c.config.Bugs.NoSignedCertificateTimestamps && cert.sctList != nil {
+				c.sendAlert(alertUnsupportedExtension)
+				return errors.New("tls: unexpected SCT list in the server certificate")
+			}
+			if i > 0 && c.config.Bugs.ExpectNoExtensionsOnIntermediate && (cert.ocspResponse != nil || cert.sctList != nil) {
+				c.sendAlert(alertUnsupportedExtension)
+				return errors.New("tls: unexpected extensions in the server certificate")
+			}
+		}
+
 		if err := hs.verifyCertificates(certMsg); err != nil {
 			return err
 		}
 		leaf := c.peerCertificates[0]
+		c.ocspResponse = certMsg.certificates[0].ocspResponse
+		c.sctList = certMsg.certificates[0].sctList
 
 		msg, err = c.readHandshake()
 		if err != nil {
@@ -785,7 +810,12 @@
 			requestContext:    certReq.requestContext,
 		}
 		if chainToSend != nil {
-			certMsg.certificates = chainToSend.Certificate
+			for _, certData := range chainToSend.Certificate {
+				certMsg.certificates = append(certMsg.certificates, certificateEntry{
+					data:           certData,
+					extraExtension: c.config.Bugs.SendExtensionOnCertificate,
+				})
+			}
 		}
 		hs.writeClientHash(certMsg.marshal())
 		c.writeRecord(recordTypeHandshake, certMsg.marshal())
@@ -820,6 +850,17 @@
 		}
 	}
 
+	if encryptedExtensions.extensions.channelIDRequested {
+		channelIDHash := crypto.SHA256.New()
+		channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13))
+		channelIDMsgBytes, err := hs.writeChannelIDMessage(channelIDHash.Sum(nil))
+		if err != nil {
+			return err
+		}
+		hs.writeClientHash(channelIDMsgBytes)
+		c.writeRecord(recordTypeHandshake, channelIDMsgBytes)
+	}
+
 	// Send a client Finished message.
 	finished := new(finishedMsg)
 	finished.verifyData = hs.finishedHash.clientSum(clientHandshakeTrafficSecret)
@@ -839,8 +880,8 @@
 	c.flushHandshake()
 
 	// Switch to application data keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
 	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
@@ -953,7 +994,11 @@
 		} else if !c.config.Bugs.SkipClientCertificate {
 			certMsg := new(certificateMsg)
 			if chainToSend != nil {
-				certMsg.certificates = chainToSend.Certificate
+				for _, certData := range chainToSend.Certificate {
+					certMsg.certificates = append(certMsg.certificates, certificateEntry{
+						data: certData,
+					})
+				}
 			}
 			hs.writeClientHash(certMsg.marshal())
 			c.writeRecord(recordTypeHandshake, certMsg.marshal())
@@ -1041,8 +1086,8 @@
 	}
 
 	certs := make([]*x509.Certificate, len(certMsg.certificates))
-	for i, asn1Data := range certMsg.certificates {
-		cert, err := x509.ParseCertificate(asn1Data)
+	for i, certEntry := range certMsg.certificates {
+		cert, err := x509.ParseCertificate(certEntry.data)
 		if err != nil {
 			c.sendAlert(alertBadCertificate)
 			return errors.New("tls: failed to parse certificate from server: " + err.Error())
@@ -1169,11 +1214,6 @@
 		return errors.New("server advertised unrequested Channel ID extension")
 	}
 
-	if serverExtensions.channelIDRequested && c.vers >= VersionTLS13 {
-		c.sendAlert(alertHandshakeFailure)
-		return errors.New("server advertised Channel ID over TLS 1.3")
-	}
-
 	if serverExtensions.extendedMasterSecret && c.vers >= VersionTLS13 {
 		return errors.New("tls: server advertised extended master secret over TLS 1.3")
 	}
@@ -1182,6 +1222,22 @@
 		return errors.New("tls: server advertised ticket extension over TLS 1.3")
 	}
 
+	if serverExtensions.ocspStapling && c.vers >= VersionTLS13 {
+		return errors.New("tls: server advertised OCSP in ServerHello over TLS 1.3")
+	}
+
+	if serverExtensions.ocspStapling && c.config.Bugs.NoOCSPStapling {
+		return errors.New("tls: server advertised unrequested OCSP extension")
+	}
+
+	if len(serverExtensions.sctList) > 0 && c.vers >= VersionTLS13 {
+		return errors.New("tls: server advertised SCTs in ServerHello over TLS 1.3")
+	}
+
+	if len(serverExtensions.sctList) > 0 && c.config.Bugs.NoSignedCertificateTimestamps {
+		return errors.New("tls: server advertised unrequested SCTs")
+	}
+
 	if serverExtensions.srtpProtectionProfile != 0 {
 		if serverExtensions.srtpMasterKeyIdentifier != "" {
 			return errors.New("tls: server selected SRTP MKI value")
@@ -1287,7 +1343,7 @@
 		vers:               c.vers,
 		cipherSuite:        hs.suite.id,
 		masterSecret:       hs.masterSecret,
-		handshakeHash:      hs.finishedHash.server.Sum(nil),
+		handshakeHash:      hs.finishedHash.Sum(),
 		serverCertificates: c.peerCertificates,
 		sctList:            c.sctList,
 		ocspResponse:       c.ocspResponse,
@@ -1346,31 +1402,14 @@
 	}
 
 	if hs.serverHello.extensions.channelIDRequested {
-		channelIDMsg := new(channelIDMsg)
-		if c.config.ChannelID.Curve != elliptic.P256() {
-			return fmt.Errorf("tls: Channel ID is not on P-256.")
-		}
 		var resumeHash []byte
 		if isResume {
 			resumeHash = hs.session.handshakeHash
 		}
-		r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, hs.finishedHash.hashForChannelID(resumeHash))
+		channelIDMsgBytes, err := hs.writeChannelIDMessage(hs.finishedHash.hashForChannelID(resumeHash))
 		if err != nil {
 			return err
 		}
-		channelID := make([]byte, 128)
-		writeIntPadded(channelID[0:32], c.config.ChannelID.X)
-		writeIntPadded(channelID[32:64], c.config.ChannelID.Y)
-		writeIntPadded(channelID[64:96], r)
-		writeIntPadded(channelID[96:128], s)
-		if c.config.Bugs.InvalidChannelIDSignature {
-			channelID[64] ^= 1
-		}
-		channelIDMsg.channelID = channelID
-
-		c.channelID = &c.config.ChannelID.PublicKey
-
-		channelIDMsgBytes := channelIDMsg.marshal()
 		hs.writeHash(channelIDMsgBytes, seqno)
 		seqno++
 		postCCSMsgs = append(postCCSMsgs, channelIDMsgBytes)
@@ -1431,6 +1470,31 @@
 	return nil
 }
 
+func (hs *clientHandshakeState) writeChannelIDMessage(channelIDHash []byte) ([]byte, error) {
+	c := hs.c
+	channelIDMsg := new(channelIDMsg)
+	if c.config.ChannelID.Curve != elliptic.P256() {
+		return nil, fmt.Errorf("tls: Channel ID is not on P-256.")
+	}
+	r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, channelIDHash)
+	if err != nil {
+		return nil, err
+	}
+	channelID := make([]byte, 128)
+	writeIntPadded(channelID[0:32], c.config.ChannelID.X)
+	writeIntPadded(channelID[32:64], c.config.ChannelID.Y)
+	writeIntPadded(channelID[64:96], r)
+	writeIntPadded(channelID[96:128], s)
+	if c.config.Bugs.InvalidChannelIDSignature {
+		channelID[64] ^= 1
+	}
+	channelIDMsg.channelID = channelID
+
+	c.channelID = &c.config.ChannelID.PublicKey
+
+	return channelIDMsg.marshal(), nil
+}
+
 func (hs *clientHandshakeState) writeClientHash(msg []byte) {
 	// writeClientHash is called before writeRecord.
 	hs.writeHash(msg, hs.c.sendHandshakeSeq)
@@ -1573,3 +1637,39 @@
 	xb := x.Bytes()
 	copy(b[len(b)-len(xb):], xb)
 }
+
+func generatePSKBinders(hello *clientHelloMsg, pskCipherSuite *cipherSuite, psk, transcript []byte, config *Config) {
+	if config.Bugs.SendNoPSKBinder {
+		return
+	}
+
+	binderLen := pskCipherSuite.hash().Size()
+	if config.Bugs.SendShortPSKBinder {
+		binderLen--
+	}
+
+	// Fill hello.pskBinders with appropriate length arrays of zeros so the
+	// length prefixes are correct when computing the binder over the truncated
+	// ClientHello message.
+	hello.pskBinders = make([][]byte, len(hello.pskIdentities))
+	for i := range hello.pskIdentities {
+		hello.pskBinders[i] = make([]byte, binderLen)
+	}
+
+	helloBytes := hello.marshal()
+	binderSize := len(hello.pskBinders)*(binderLen+1) + 2
+	truncatedHello := helloBytes[:len(helloBytes)-binderSize]
+	binder := computePSKBinder(psk, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello)
+	if config.Bugs.SendShortPSKBinder {
+		binder = binder[:binderLen]
+	}
+	if config.Bugs.SendInvalidPSKBinder {
+		binder[0] ^= 1
+	}
+
+	for i := range hello.pskBinders {
+		hello.pskBinders[i] = binder
+	}
+
+	hello.raw = nil
+}
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 285587e..8a338f0 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -125,9 +125,8 @@
 }
 
 type pskIdentity struct {
-	keModes   []byte
-	authModes []byte
-	ticket    []uint8
+	ticket              []uint8
+	obfuscatedTicketAge uint32
 }
 
 type clientHelloMsg struct {
@@ -148,8 +147,9 @@
 	keyShares               []keyShareEntry
 	trailingKeyShareData    bool
 	pskIdentities           []pskIdentity
+	pskKEModes              []byte
+	pskBinders              [][]uint8
 	hasEarlyData            bool
-	earlyDataContext        []byte
 	tls13Cookie             []byte
 	ticketSupported         bool
 	sessionTicket           []uint8
@@ -159,13 +159,14 @@
 	alpnProtocols           []string
 	duplicateExtension      bool
 	channelIDSupported      bool
-	npnLast                 bool
+	npnAfterAlpn            bool
 	extendedMasterSecret    bool
 	srtpProtectionProfiles  []uint16
 	srtpMasterKeyIdentifier string
 	sctListSupported        bool
 	customExtension         string
 	hasGREASEExtension      bool
+	pskBinderFirst          bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -191,8 +192,9 @@
 		eqKeyShareEntryLists(m.keyShares, m1.keyShares) &&
 		m.trailingKeyShareData == m1.trailingKeyShareData &&
 		eqPSKIdentityLists(m.pskIdentities, m1.pskIdentities) &&
+		bytes.Equal(m.pskKEModes, m1.pskKEModes) &&
+		eqByteSlices(m.pskBinders, m1.pskBinders) &&
 		m.hasEarlyData == m1.hasEarlyData &&
-		bytes.Equal(m.earlyDataContext, m1.earlyDataContext) &&
 		bytes.Equal(m.tls13Cookie, m1.tls13Cookie) &&
 		m.ticketSupported == m1.ticketSupported &&
 		bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
@@ -203,13 +205,14 @@
 		eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
 		m.duplicateExtension == m1.duplicateExtension &&
 		m.channelIDSupported == m1.channelIDSupported &&
-		m.npnLast == m1.npnLast &&
+		m.npnAfterAlpn == m1.npnAfterAlpn &&
 		m.extendedMasterSecret == m1.extendedMasterSecret &&
 		eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
 		m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
 		m.sctListSupported == m1.sctListSupported &&
 		m.customExtension == m1.customExtension &&
-		m.hasGREASEExtension == m1.hasGREASEExtension
+		m.hasGREASEExtension == m1.hasGREASEExtension &&
+		m.pskBinderFirst == m1.pskBinderFirst
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -236,12 +239,26 @@
 	compressionMethods.addBytes(m.compressionMethods)
 
 	extensions := hello.addU16LengthPrefixed()
+	if len(m.pskIdentities) > 0 && m.pskBinderFirst {
+		extensions.addU16(extensionPreSharedKey)
+		pskExtension := extensions.addU16LengthPrefixed()
+
+		pskIdentities := pskExtension.addU16LengthPrefixed()
+		for _, psk := range m.pskIdentities {
+			pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
+			pskIdentities.addU32(psk.obfuscatedTicketAge)
+		}
+		pskBinders := pskExtension.addU16LengthPrefixed()
+		for _, binder := range m.pskBinders {
+			pskBinders.addU8LengthPrefixed().addBytes(binder)
+		}
+	}
 	if m.duplicateExtension {
 		// Add a duplicate bogus extension at the beginning and end.
 		extensions.addU16(0xffff)
 		extensions.addU16(0) // 0-length for empty extension
 	}
-	if m.nextProtoNeg && !m.npnLast {
+	if m.nextProtoNeg && !m.npnAfterAlpn {
 		extensions.addU16(extensionNextProtoNeg)
 		extensions.addU16(0) // The length is always 0
 	}
@@ -316,23 +333,14 @@
 			keyShares.addU8(0)
 		}
 	}
-	if len(m.pskIdentities) > 0 {
-		extensions.addU16(extensionPreSharedKey)
-		pskExtension := extensions.addU16LengthPrefixed()
-
-		pskIdentities := pskExtension.addU16LengthPrefixed()
-		for _, psk := range m.pskIdentities {
-			pskIdentities.addU8LengthPrefixed().addBytes(psk.keModes)
-			pskIdentities.addU8LengthPrefixed().addBytes(psk.authModes)
-			pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
-		}
+	if len(m.pskKEModes) > 0 {
+		extensions.addU16(extensionPSKKeyExchangeModes)
+		pskModesExtension := extensions.addU16LengthPrefixed()
+		pskModesExtension.addU8LengthPrefixed().addBytes(m.pskKEModes)
 	}
 	if m.hasEarlyData {
 		extensions.addU16(extensionEarlyData)
-		earlyDataIndication := extensions.addU16LengthPrefixed()
-
-		context := earlyDataIndication.addU8LengthPrefixed()
-		context.addBytes(m.earlyDataContext)
+		extensions.addU16(0) // The length is zero.
 	}
 	if len(m.tls13Cookie) > 0 {
 		extensions.addU16(extensionCookie)
@@ -383,7 +391,7 @@
 		extensions.addU16(extensionChannelID)
 		extensions.addU16(0) // Length is always 0
 	}
-	if m.nextProtoNeg && m.npnLast {
+	if m.nextProtoNeg && m.npnAfterAlpn {
 		extensions.addU16(extensionNextProtoNeg)
 		extensions.addU16(0) // Length is always 0
 	}
@@ -422,6 +430,21 @@
 		customExt := extensions.addU16LengthPrefixed()
 		customExt.addBytes([]byte(m.customExtension))
 	}
+	// The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6).
+	if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
+		extensions.addU16(extensionPreSharedKey)
+		pskExtension := extensions.addU16LengthPrefixed()
+
+		pskIdentities := pskExtension.addU16LengthPrefixed()
+		for _, psk := range m.pskIdentities {
+			pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
+			pskIdentities.addU32(psk.obfuscatedTicketAge)
+		}
+		pskBinders := pskExtension.addU16LengthPrefixed()
+		for _, binder := range m.pskBinders {
+			pskBinders.addU8LengthPrefixed().addBytes(binder)
+		}
+	}
 
 	if extensions.len() == 0 {
 		hello.discardChild()
@@ -490,7 +513,6 @@
 	m.keyShares = nil
 	m.pskIdentities = nil
 	m.hasEarlyData = false
-	m.earlyDataContext = nil
 	m.ticketSupported = false
 	m.sessionTicket = nil
 	m.signatureAlgorithms = nil
@@ -614,63 +636,75 @@
 				m.keyShares = append(m.keyShares, entry)
 			}
 		case extensionPreSharedKey:
-			// draft-ietf-tls-tls13 section 6.3.2.4
+			// draft-ietf-tls-tls13-18 section 4.2.6
 			if length < 2 {
 				return false
 			}
 			l := int(data[0])<<8 | int(data[1])
-			if l != length-2 {
-				return false
-			}
-			d := data[2:length]
+			d := data[2 : l+2]
+			// Parse PSK identities.
 			for len(d) > 0 {
-				var psk pskIdentity
-
-				if len(d) < 1 {
-					return false
-				}
-				keModesLen := int(d[0])
-				d = d[1:]
-				if len(d) < keModesLen {
-					return false
-				}
-				psk.keModes = d[:keModesLen]
-				d = d[keModesLen:]
-
-				if len(d) < 1 {
-					return false
-				}
-				authModesLen := int(d[0])
-				d = d[1:]
-				if len(d) < authModesLen {
-					return false
-				}
-				psk.authModes = d[:authModesLen]
-				d = d[authModesLen:]
 				if len(d) < 2 {
 					return false
 				}
 				pskLen := int(d[0])<<8 | int(d[1])
 				d = d[2:]
 
-				if len(d) < pskLen {
+				if len(d) < pskLen+4 {
 					return false
 				}
-				psk.ticket = d[:pskLen]
+				ticket := d[:pskLen]
+				obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3])
+				psk := pskIdentity{
+					ticket:              ticket,
+					obfuscatedTicketAge: obfuscatedTicketAge,
+				}
 				m.pskIdentities = append(m.pskIdentities, psk)
-				d = d[pskLen:]
+				d = d[pskLen+4:]
 			}
-		case extensionEarlyData:
-			// draft-ietf-tls-tls13 section 6.3.2.5
+			d = data[l+2:]
+			if len(d) < 2 {
+				return false
+			}
+			l = int(d[0])<<8 | int(d[1])
+			d = d[2:]
+			if l != len(d) {
+				return false
+			}
+			// Parse PSK binders.
+			for len(d) > 0 {
+				if len(d) < 1 {
+					return false
+				}
+				binderLen := int(d[0])
+				d = d[1:]
+				if binderLen > len(d) {
+					return false
+				}
+				m.pskBinders = append(m.pskBinders, d[:binderLen])
+				d = d[binderLen:]
+			}
+
+			// There must be the same number of identities as binders.
+			if len(m.pskIdentities) != len(m.pskBinders) {
+				return false
+			}
+		case extensionPSKKeyExchangeModes:
+			// draft-ietf-tls-tls13-18 section 4.2.7
 			if length < 1 {
 				return false
 			}
 			l := int(data[0])
-			if length != l+1 {
+			if l != length-1 {
+				return false
+			}
+			m.pskKEModes = data[1:length]
+		case extensionEarlyData:
+			// draft-ietf-tls-tls13 section 6.3.2.5
+			if length != 0 {
 				return false
 			}
 			m.hasEarlyData = true
-			m.earlyDataContext = data[1:length]
 		case extensionCookie:
 			if length < 2 {
 				return false
@@ -793,7 +827,6 @@
 	keyShare            keyShareEntry
 	hasPSKIdentity      bool
 	pskIdentity         uint16
-	useCertAuth         bool
 	earlyDataIndication bool
 	compressionMethod   uint8
 	customExtension     string
@@ -848,10 +881,6 @@
 			extensions.addU16(2) // Length
 			extensions.addU16(m.pskIdentity)
 		}
-		if m.useCertAuth {
-			extensions.addU16(extensionSignatureAlgorithms)
-			extensions.addU16(0) // Length
-		}
 		if m.earlyDataIndication {
 			extensions.addU16(extensionEarlyData)
 			extensions.addU16(0) // Length
@@ -870,7 +899,7 @@
 			protocolName.addBytes([]byte(m.unencryptedALPN))
 		}
 	} else {
-		m.extensions.marshal(extensions, vers)
+		m.extensions.marshal(extensions)
 		if extensions.len() == 0 {
 			hello.discardChild()
 		}
@@ -962,11 +991,6 @@
 				}
 				m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
 				m.hasPSKIdentity = true
-			case extensionSignatureAlgorithms:
-				if len(d) != 0 {
-					return false
-				}
-				m.useCertAuth = true
 			case extensionEarlyData:
 				if len(d) != 0 {
 					return false
@@ -1001,7 +1025,7 @@
 	encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed()
 	if !m.empty {
 		extensions := encryptedExtensions.addU16LengthPrefixed()
-		m.extensions.marshal(extensions, VersionTLS13)
+		m.extensions.marshal(extensions)
 	}
 
 	m.raw = encryptedExtensionsMsg.finish()
@@ -1033,7 +1057,6 @@
 	nextProtoNeg            bool
 	nextProtos              []string
 	ocspStapling            bool
-	ocspResponse            []byte
 	ticketSupported         bool
 	secureRenegotiation     []byte
 	alpnProtocol            string
@@ -1045,18 +1068,18 @@
 	srtpMasterKeyIdentifier string
 	sctList                 []byte
 	customExtension         string
-	npnLast                 bool
+	npnAfterAlpn            bool
 	hasKeyShare             bool
 	keyShare                keyShareEntry
 }
 
-func (m *serverExtensions) marshal(extensions *byteBuilder, version uint16) {
+func (m *serverExtensions) marshal(extensions *byteBuilder) {
 	if m.duplicateExtension {
 		// Add a duplicate bogus extension at the beginning and end.
 		extensions.addU16(0xffff)
 		extensions.addU16(0) // length = 0 for empty extension
 	}
-	if m.nextProtoNeg && !m.npnLast {
+	if m.nextProtoNeg && !m.npnAfterAlpn {
 		extensions.addU16(extensionNextProtoNeg)
 		extension := extensions.addU16LengthPrefixed()
 
@@ -1068,19 +1091,9 @@
 			npn.addBytes([]byte(v))
 		}
 	}
-	if version >= VersionTLS13 {
-		if m.ocspResponse != nil {
-			extensions.addU16(extensionStatusRequest)
-			body := extensions.addU16LengthPrefixed()
-			body.addU8(statusTypeOCSP)
-			response := body.addU24LengthPrefixed()
-			response.addBytes(m.ocspResponse)
-		}
-	} else {
-		if m.ocspStapling {
-			extensions.addU16(extensionStatusRequest)
-			extensions.addU16(0)
-		}
+	if m.ocspStapling {
+		extensions.addU16(extensionStatusRequest)
+		extensions.addU16(0)
 	}
 	if m.ticketSupported {
 		extensions.addU16(extensionSessionTicket)
@@ -1133,7 +1146,7 @@
 		customExt := extensions.addU16LengthPrefixed()
 		customExt.addBytes([]byte(m.customExtension))
 	}
-	if m.nextProtoNeg && m.npnLast {
+	if m.nextProtoNeg && m.npnAfterAlpn {
 		extensions.addU16(extensionNextProtoNeg)
 		extension := extensions.addU16LengthPrefixed()
 
@@ -1183,25 +1196,10 @@
 				d = d[l:]
 			}
 		case extensionStatusRequest:
-			if version >= VersionTLS13 {
-				if length < 4 {
-					return false
-				}
-				d := data[:length]
-				if d[0] != statusTypeOCSP {
-					return false
-				}
-				respLen := int(d[1])<<16 | int(d[2])<<8 | int(d[3])
-				if respLen+4 != len(d) || respLen == 0 {
-					return false
-				}
-				m.ocspResponse = d[4:]
-			} else {
-				if length > 0 {
-					return false
-				}
-				m.ocspStapling = true
+			if length > 0 {
+				return false
 			}
+			m.ocspStapling = true
 		case extensionSessionTicket:
 			if length > 0 {
 				return false
@@ -1377,11 +1375,19 @@
 	return true
 }
 
+type certificateEntry struct {
+	data                []byte
+	ocspResponse        []byte
+	sctList             []byte
+	duplicateExtensions bool
+	extraExtension      []byte
+}
+
 type certificateMsg struct {
 	raw               []byte
 	hasRequestContext bool
 	requestContext    []byte
-	certificates      [][]byte
+	certificates      []certificateEntry
 }
 
 func (m *certificateMsg) marshal() (x []byte) {
@@ -1399,7 +1405,33 @@
 	certificateList := certificate.addU24LengthPrefixed()
 	for _, cert := range m.certificates {
 		certEntry := certificateList.addU24LengthPrefixed()
-		certEntry.addBytes(cert)
+		certEntry.addBytes(cert.data)
+		if m.hasRequestContext {
+			extensions := certificateList.addU16LengthPrefixed()
+			count := 1
+			if cert.duplicateExtensions {
+				count = 2
+			}
+
+			for i := 0; i < count; i++ {
+				if cert.ocspResponse != nil {
+					extensions.addU16(extensionStatusRequest)
+					body := extensions.addU16LengthPrefixed()
+					body.addU8(statusTypeOCSP)
+					response := body.addU24LengthPrefixed()
+					response.addBytes(cert.ocspResponse)
+				}
+
+				if cert.sctList != nil {
+					extensions.addU16(extensionSignedCertificateTimestamp)
+					extension := extensions.addU16LengthPrefixed()
+					extension.addBytes(cert.sctList)
+				}
+			}
+			if cert.extraExtension != nil {
+				extensions.addBytes(cert.extraExtension)
+			}
+		}
 	}
 
 	m.raw = certMsg.finish()
@@ -1436,27 +1468,62 @@
 		return false
 	}
 
-	numCerts := 0
-	d := data
-	for certsLen > 0 {
-		if len(d) < 4 {
+	m.certificates = nil
+	for len(data) != 0 {
+		if len(data) < 3 {
 			return false
 		}
-		certLen := int(d[0])<<16 | int(d[1])<<8 | int(d[2])
-		if len(d) < 3+certLen {
+		certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
+		if len(data) < 3+certLen {
 			return false
 		}
-		d = d[3+certLen:]
-		certsLen -= 3 + certLen
-		numCerts++
-	}
+		cert := certificateEntry{
+			data: data[3 : 3+certLen],
+		}
+		data = data[3+certLen:]
+		if m.hasRequestContext {
+			if len(data) < 2 {
+				return false
+			}
+			extensionsLen := int(data[0])<<8 | int(data[1])
+			if len(data) < 2+extensionsLen {
+				return false
+			}
+			extensions := data[2 : 2+extensionsLen]
+			data = data[2+extensionsLen:]
+			for len(extensions) != 0 {
+				if len(extensions) < 4 {
+					return false
+				}
+				extension := uint16(extensions[0])<<8 | uint16(extensions[1])
+				length := int(extensions[2])<<8 | int(extensions[3])
+				if len(extensions) < 4+length {
+					return false
+				}
+				contents := extensions[4 : 4+length]
+				extensions = extensions[4+length:]
 
-	m.certificates = make([][]byte, numCerts)
-	d = data
-	for i := 0; i < numCerts; i++ {
-		certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
-		m.certificates[i] = d[3 : 3+certLen]
-		d = d[3+certLen:]
+				switch extension {
+				case extensionStatusRequest:
+					if length < 4 {
+						return false
+					}
+					if contents[0] != statusTypeOCSP {
+						return false
+					}
+					respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3])
+					if respLen+4 != len(contents) || respLen == 0 {
+						return false
+					}
+					cert.ocspResponse = contents[4:]
+				case extensionSignedCertificateTimestamp:
+					cert.sctList = contents
+				default:
+					return false
+				}
+			}
+		}
+		m.certificates = append(m.certificates, cert)
 	}
 
 	return true
@@ -1904,8 +1971,7 @@
 	raw                []byte
 	version            uint16
 	ticketLifetime     uint32
-	keModes            []byte
-	authModes          []byte
+	ticketAgeAdd       uint32
 	ticket             []byte
 	customExtension    string
 	hasGREASEExtension bool
@@ -1922,8 +1988,7 @@
 	body := ticketMsg.addU24LengthPrefixed()
 	body.addU32(m.ticketLifetime)
 	if m.version >= VersionTLS13 {
-		body.addU8LengthPrefixed().addBytes(m.keModes)
-		body.addU8LengthPrefixed().addBytes(m.authModes)
+		body.addU32(m.ticketAgeAdd)
 	}
 
 	ticket := body.addU16LengthPrefixed()
@@ -1951,25 +2016,11 @@
 	data = data[8:]
 
 	if m.version >= VersionTLS13 {
-		if len(data) < 1 {
+		if len(data) < 4 {
 			return false
 		}
-		keModesLength := int(data[0])
-		if len(data)-1 < keModesLength {
-			return false
-		}
-		m.keModes = data[1 : 1+keModesLength]
-		data = data[1+keModesLength:]
-
-		if len(data) < 1 {
-			return false
-		}
-		authModesLength := int(data[0])
-		if len(data)-1 < authModesLength {
-			return false
-		}
-		m.authModes = data[1 : 1+authModesLength]
-		data = data[1+authModesLength:]
+		m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+		data = data[4:]
 	}
 
 	if len(data) < 2 {
@@ -2268,7 +2319,7 @@
 		return false
 	}
 	for i, v := range x {
-		if !bytes.Equal(y[i].keModes, v.keModes) || !bytes.Equal(y[i].authModes, v.authModes) || !bytes.Equal(y[i].ticket, v.ticket) {
+		if !bytes.Equal(y[i].ticket, v.ticket) || y[i].obfuscatedTicketAge != v.obfuscatedTicketAge {
 			return false
 		}
 	}
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 9147134..67950ba 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -16,6 +16,7 @@
 	"fmt"
 	"io"
 	"math/big"
+	"time"
 )
 
 // serverHandshakeState contains details of a server handshake in progress.
@@ -158,7 +159,7 @@
 		// Per RFC 6347, the version field in HelloVerifyRequest SHOULD
 		// be always DTLS 1.0
 		helloVerifyRequest := &helloVerifyRequestMsg{
-			vers:   VersionTLS10,
+			vers:   versionToWire(VersionTLS10, c.isDTLS),
 			cookie: make([]byte, 32),
 		}
 		if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil {
@@ -384,6 +385,36 @@
 		return err
 	}
 
+	// Select the cipher suite.
+	var preferenceList, supportedList []uint16
+	if config.PreferServerCipherSuites {
+		preferenceList = config.cipherSuites()
+		supportedList = hs.clientHello.cipherSuites
+	} else {
+		preferenceList = hs.clientHello.cipherSuites
+		supportedList = config.cipherSuites()
+	}
+
+	for _, id := range preferenceList {
+		if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, true, true); hs.suite != nil {
+			break
+		}
+	}
+
+	if hs.suite == nil {
+		c.sendAlert(alertHandshakeFailure)
+		return errors.New("tls: no cipher suite supported by both client and server")
+	}
+
+	hs.hello.cipherSuite = hs.suite.id
+	if c.config.Bugs.SendCipherSuite != 0 {
+		hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
+	}
+
+	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+	hs.finishedHash.discardHandshakeBuffer()
+	hs.writeClientHash(hs.clientHello.marshal())
+
 	supportedCurve := false
 	var selectedCurve CurveID
 	preferredCurves := config.curvePreferences()
@@ -404,78 +435,55 @@
 	}
 
 	pskIdentities := hs.clientHello.pskIdentities
+	pskKEModes := hs.clientHello.pskKEModes
+
 	if len(pskIdentities) == 0 && len(hs.clientHello.sessionTicket) > 0 && c.config.Bugs.AcceptAnySession {
 		psk := pskIdentity{
-			keModes:   []byte{pskDHEKEMode},
-			authModes: []byte{pskAuthMode},
-			ticket:    hs.clientHello.sessionTicket,
+			ticket: hs.clientHello.sessionTicket,
 		}
 		pskIdentities = []pskIdentity{psk}
+		pskKEModes = []byte{pskDHEKEMode}
 	}
-	for i, pskIdentity := range pskIdentities {
-		foundKE := false
-		foundAuth := false
 
-		for _, keMode := range pskIdentity.keModes {
-			if keMode == pskDHEKEMode {
-				foundKE = true
-			}
-		}
-
-		for _, authMode := range pskIdentity.authModes {
-			if authMode == pskAuthMode {
-				foundAuth = true
-			}
-		}
-
-		if !foundKE || !foundAuth {
-			continue
-		}
-
-		sessionState, ok := c.decryptTicket(pskIdentity.ticket)
-		if !ok {
-			continue
-		}
-		if config.Bugs.AcceptAnySession {
-			// Replace the cipher suite with one known to work, to
-			// test cross-version resumption attempts.
-			sessionState.cipherSuite = TLS_AES_128_GCM_SHA256
-		} else {
-			if sessionState.vers != c.vers && c.config.Bugs.AcceptAnySession {
-				continue
-			}
-			if sessionState.ticketExpiration.Before(c.config.time()) {
+	var pskIndex int
+	foundKEMode := bytes.IndexByte(pskKEModes, pskDHEKEMode) >= 0
+	if foundKEMode {
+		for i, pskIdentity := range pskIdentities {
+			// TODO(svaldez): Check the obfuscatedTicketAge before accepting 0-RTT.
+			sessionState, ok := c.decryptTicket(pskIdentity.ticket)
+			if !ok {
 				continue
 			}
 
-			cipherSuiteOk := false
-			// Check that the client is still offering the ciphersuite in the session.
-			for _, id := range hs.clientHello.cipherSuites {
-				if id == sessionState.cipherSuite {
-					cipherSuiteOk = true
-					break
+			if !config.Bugs.AcceptAnySession {
+				if sessionState.vers != c.vers {
+					continue
+				}
+				if sessionState.ticketExpiration.Before(c.config.time()) {
+					continue
+				}
+				sessionCipher := cipherSuiteFromID(sessionState.cipherSuite)
+				if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() {
+					continue
 				}
 			}
-			if !cipherSuiteOk {
-				continue
+
+			clientTicketAge := time.Duration(uint32(pskIdentity.obfuscatedTicketAge-sessionState.ticketAgeAdd)) * time.Millisecond
+			if config.Bugs.ExpectTicketAge != 0 && clientTicketAge != config.Bugs.ExpectTicketAge {
+				c.sendAlert(alertHandshakeFailure)
+				return errors.New("tls: invalid ticket age")
 			}
-		}
 
-		// Check that we also support the ciphersuite from the session.
-		suite := c.tryCipherSuite(sessionState.cipherSuite, c.config.cipherSuites(), c.vers, true, true)
-		if suite == nil {
-			continue
+			hs.sessionState = sessionState
+			hs.hello.hasPSKIdentity = true
+			hs.hello.pskIdentity = uint16(i)
+			pskIndex = i
+			if config.Bugs.SelectPSKIdentityOnResume != 0 {
+				hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
+			}
+			c.didResume = true
+			break
 		}
-
-		hs.sessionState = sessionState
-		hs.suite = suite
-		hs.hello.hasPSKIdentity = true
-		hs.hello.pskIdentity = uint16(i)
-		if config.Bugs.SelectPSKIdentityOnResume != 0 {
-			hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
-		}
-		c.didResume = true
-		break
 	}
 
 	if config.Bugs.AlwaysSelectPSKIdentity {
@@ -483,58 +491,25 @@
 		hs.hello.pskIdentity = 0
 	}
 
-	// If not resuming, select the cipher suite.
-	if hs.suite == nil {
-		var preferenceList, supportedList []uint16
-		if config.PreferServerCipherSuites {
-			preferenceList = config.cipherSuites()
-			supportedList = hs.clientHello.cipherSuites
-		} else {
-			preferenceList = hs.clientHello.cipherSuites
-			supportedList = config.cipherSuites()
-		}
-
-		for _, id := range preferenceList {
-			if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, true, true); hs.suite != nil {
-				break
-			}
+	// Verify the PSK binder. Note there may not be a PSK binder if
+	// AcceptAnyBinder is set. See https://crbug.com/boringssl/115.
+	if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+		binderToVerify := hs.clientHello.pskBinders[pskIndex]
+		if err := verifyPSKBinder(hs.clientHello, hs.sessionState, binderToVerify, []byte{}); err != nil {
+			return err
 		}
 	}
 
-	if hs.suite == nil {
-		c.sendAlert(alertHandshakeFailure)
-		return errors.New("tls: no cipher suite supported by both client and server")
-	}
-
-	hs.hello.cipherSuite = hs.suite.id
-	if c.config.Bugs.SendCipherSuite != 0 {
-		hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
-	}
-
-	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
-	hs.finishedHash.discardHandshakeBuffer()
-	hs.writeClientHash(hs.clientHello.marshal())
-
-	hs.hello.useCertAuth = hs.sessionState == nil
-
 	// Resolve PSK and compute the early secret.
 	var psk []byte
 	if hs.sessionState != nil {
-		psk = deriveResumptionPSK(hs.suite, hs.sessionState.masterSecret)
-		hs.finishedHash.setResumptionContext(deriveResumptionContext(hs.suite, hs.sessionState.masterSecret))
+		psk = hs.sessionState.masterSecret
 	} else {
 		psk = hs.finishedHash.zeroSecret()
-		hs.finishedHash.setResumptionContext(hs.finishedHash.zeroSecret())
 	}
 
 	earlySecret := hs.finishedHash.extractKey(hs.finishedHash.zeroSecret(), psk)
 
-	if config.Bugs.OmitServerHelloSignatureAlgorithms {
-		hs.hello.useCertAuth = false
-	} else if config.Bugs.IncludeServerHelloSignatureAlgorithms {
-		hs.hello.useCertAuth = true
-	}
-
 	hs.hello.hasKeyShare = true
 	if hs.sessionState != nil && config.Bugs.NegotiatePSKResumption {
 		hs.hello.hasKeyShare = false
@@ -598,6 +573,7 @@
 	}
 
 	if sendHelloRetryRequest {
+		oldClientHelloBytes := hs.clientHello.marshal()
 		hs.writeServerHash(helloRetryRequest.marshal())
 		c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
 		c.flushHandshake()
@@ -621,17 +597,16 @@
 		oldClientHelloCopy := *hs.clientHello
 		oldClientHelloCopy.raw = nil
 		oldClientHelloCopy.hasEarlyData = false
-		oldClientHelloCopy.earlyDataContext = nil
 		newClientHelloCopy := *newClientHello
 		newClientHelloCopy.raw = nil
 
 		if helloRetryRequest.hasSelectedGroup {
 			newKeyShares := newClientHelloCopy.keyShares
-			if len(newKeyShares) == 0 || newKeyShares[len(newKeyShares)-1].group != helloRetryRequest.selectedGroup {
-				return errors.New("tls: KeyShare from HelloRetryRequest not present in new ClientHello")
+			if len(newKeyShares) != 1 || newKeyShares[0].group != helloRetryRequest.selectedGroup {
+				return errors.New("tls: KeyShare from HelloRetryRequest not in new ClientHello")
 			}
-			selectedKeyShare = &newKeyShares[len(newKeyShares)-1]
-			newClientHelloCopy.keyShares = newKeyShares[:len(newKeyShares)-1]
+			selectedKeyShare = &newKeyShares[0]
+			newClientHelloCopy.keyShares = oldClientHelloCopy.keyShares
 		}
 
 		if len(helloRetryRequest.cookie) > 0 {
@@ -641,6 +616,16 @@
 			newClientHelloCopy.tls13Cookie = nil
 		}
 
+		// PSK binders and obfuscated ticket age are both updated in the
+		// second ClientHello.
+		if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) {
+			return errors.New("tls: PSK identity count from old and new ClientHello do not match")
+		}
+		for i, identity := range oldClientHelloCopy.pskIdentities {
+			newClientHelloCopy.pskIdentities[i].obfuscatedTicketAge = identity.obfuscatedTicketAge
+		}
+		newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders
+
 		if !oldClientHelloCopy.equal(&newClientHelloCopy) {
 			return errors.New("tls: new ClientHello does not match")
 		}
@@ -649,6 +634,16 @@
 			firstHelloRetryRequest = false
 			goto ResendHelloRetryRequest
 		}
+
+		// Verify the PSK binder. Note there may not be a PSK binder if
+		// AcceptAnyBinder is set. See https://crbug.com/115.
+		if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+			binderToVerify := newClientHello.pskBinders[pskIndex]
+			err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...))
+			if err != nil {
+				return err
+			}
+		}
 	}
 
 	// Resolve ECDHE and compute the handshake secret.
@@ -728,25 +723,9 @@
 
 	// Switch to handshake traffic keys.
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
-	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
-	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
-
-	if hs.hello.useCertAuth {
-		if hs.clientHello.ocspStapling {
-			encryptedExtensions.extensions.ocspResponse = hs.cert.OCSPStaple
-		}
-		if hs.clientHello.sctListSupported {
-			encryptedExtensions.extensions.sctList = hs.cert.SignedCertificateTimestampList
-		}
-	} else {
-		if config.Bugs.SendOCSPResponseOnResume != nil {
-			encryptedExtensions.extensions.ocspResponse = config.Bugs.SendOCSPResponseOnResume
-		}
-		if config.Bugs.SendSCTListOnResume != nil {
-			encryptedExtensions.extensions.sctList = config.Bugs.SendSCTListOnResume
-		}
-	}
+	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
 	// Send EncryptedExtensions.
 	hs.writeServerHash(encryptedExtensions.marshal())
@@ -757,7 +736,7 @@
 		c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal())
 	}
 
-	if hs.hello.useCertAuth {
+	if hs.sessionState == nil {
 		if config.ClientAuth >= RequestClientCert {
 			// Request a client certificate
 			certReq := &certificateRequestMsg{
@@ -785,7 +764,29 @@
 			hasRequestContext: true,
 		}
 		if !config.Bugs.EmptyCertificateList {
-			certMsg.certificates = hs.cert.Certificate
+			for i, certData := range hs.cert.Certificate {
+				cert := certificateEntry{
+					data: certData,
+				}
+				if i == 0 {
+					if hs.clientHello.ocspStapling {
+						cert.ocspResponse = hs.cert.OCSPStaple
+					}
+					if hs.clientHello.sctListSupported {
+						cert.sctList = hs.cert.SignedCertificateTimestampList
+					}
+					cert.duplicateExtensions = config.Bugs.SendDuplicateCertExtensions
+					cert.extraExtension = config.Bugs.SendExtensionOnCertificate
+				} else {
+					if config.Bugs.SendOCSPOnIntermediates != nil {
+						cert.ocspResponse = config.Bugs.SendOCSPOnIntermediates
+					}
+					if config.Bugs.SendSCTOnIntermediates != nil {
+						cert.sctList = config.Bugs.SendSCTOnIntermediates
+					}
+				}
+				certMsg.certificates = append(certMsg.certificates, cert)
+			}
 		}
 		certMsgBytes := certMsg.marshal()
 		hs.writeServerHash(certMsgBytes)
@@ -844,10 +845,11 @@
 	masterSecret := hs.finishedHash.extractKey(handshakeSecret, hs.finishedHash.zeroSecret())
 	clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
 	serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
+	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
@@ -873,7 +875,17 @@
 			}
 		}
 
-		pub, err := hs.processCertsFromClient(certMsg.certificates)
+		var certs [][]byte
+		for _, cert := range certMsg.certificates {
+			certs = append(certs, cert.data)
+			// OCSP responses and SCT lists are not negotiated in
+			// client certificates.
+			if cert.ocspResponse != nil || cert.sctList != nil {
+				c.sendAlert(alertUnsupportedExtension)
+				return errors.New("tls: unexpected extensions in the client certificate")
+			}
+		}
+		pub, err := hs.processCertsFromClient(certs)
 		if err != nil {
 			return err
 		}
@@ -900,6 +912,27 @@
 		}
 	}
 
+	if encryptedExtensions.extensions.channelIDRequested {
+		msg, err := c.readHandshake()
+		if err != nil {
+			return err
+		}
+		channelIDMsg, ok := msg.(*channelIDMsg)
+		if !ok {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(channelIDMsg, msg)
+		}
+		channelIDHash := crypto.SHA256.New()
+		channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13))
+		channelID, err := verifyChannelIDMessage(channelIDMsg, channelIDHash.Sum(nil))
+		if err != nil {
+			return err
+		}
+		c.channelID = channelID
+
+		hs.writeClientHash(channelIDMsg.marshal())
+	}
+
 	// Read the client Finished message.
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -920,15 +953,14 @@
 	hs.writeClientHash(clientFinished.marshal())
 
 	// Switch to application data keys on read.
-	c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
+	c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
 
 	c.cipherSuite = hs.suite
-	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
 
 	// TODO(davidben): Allow configuring the number of tickets sent for
 	// testing.
-	if !c.config.SessionTicketsDisabled {
+	if !c.config.SessionTicketsDisabled && foundKEMode {
 		ticketCount := 2
 		for i := 0; i < ticketCount; i++ {
 			c.SendNewSessionTicket()
@@ -1113,7 +1145,7 @@
 			if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
 				serverExtensions.nextProtoNeg = true
 				serverExtensions.nextProtos = config.NextProtos
-				serverExtensions.npnLast = config.Bugs.SwapNPNAndALPN
+				serverExtensions.npnAfterAlpn = config.Bugs.SwapNPNAndALPN
 			}
 		}
 	}
@@ -1126,10 +1158,8 @@
 		serverExtensions.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !disableEMS
 	}
 
-	if c.vers < VersionTLS13 || config.Bugs.NegotiateChannelIDAtAllVersions {
-		if hs.clientHello.channelIDSupported && config.RequestChannelID {
-			serverExtensions.channelIDRequested = true
-		}
+	if hs.clientHello.channelIDSupported && config.RequestChannelID {
+		serverExtensions.channelIDRequested = true
 	}
 
 	if hs.clientHello.srtpProtectionProfiles != nil {
@@ -1321,7 +1351,11 @@
 	if !isPSK {
 		certMsg := new(certificateMsg)
 		if !config.Bugs.EmptyCertificateList {
-			certMsg.certificates = hs.cert.Certificate
+			for _, certData := range hs.cert.Certificate {
+				certMsg.certificates = append(certMsg.certificates, certificateEntry{
+					data: certData,
+				})
+			}
 		}
 		if !config.Bugs.UnauthenticatedECDH {
 			certMsgBytes := certMsg.marshal()
@@ -1409,7 +1443,9 @@
 			}
 
 			hs.writeClientHash(certMsg.marshal())
-			certificates = certMsg.certificates
+			for _, cert := range certMsg.certificates {
+				certificates = append(certificates, cert.data)
+			}
 		} else if c.vers != VersionSSL30 {
 			// In TLS, the Certificate message is required. In SSL
 			// 3.0, the peer skips it when sending no certificates.
@@ -1568,20 +1604,13 @@
 			c.sendAlert(alertUnexpectedMessage)
 			return unexpectedMessageError(channelIDMsg, msg)
 		}
-		x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32])
-		y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64])
-		r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96])
-		s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128])
-		if !elliptic.P256().IsOnCurve(x, y) {
-			return errors.New("tls: invalid channel ID public key")
-		}
-		channelID := &ecdsa.PublicKey{elliptic.P256(), x, y}
 		var resumeHash []byte
 		if isResume {
 			resumeHash = hs.sessionState.handshakeHash
 		}
-		if !ecdsa.Verify(channelID, hs.finishedHash.hashForChannelID(resumeHash), r, s) {
-			return errors.New("tls: invalid channel ID signature")
+		channelID, err := verifyChannelIDMessage(channelIDMsg, hs.finishedHash.hashForChannelID(resumeHash))
+		if err != nil {
+			return err
 		}
 		c.channelID = channelID
 
@@ -1618,7 +1647,7 @@
 		cipherSuite:   hs.suite.id,
 		masterSecret:  hs.masterSecret,
 		certificates:  hs.certsFromClient,
-		handshakeHash: hs.finishedHash.server.Sum(nil),
+		handshakeHash: hs.finishedHash.Sum(),
 	}
 
 	if !hs.hello.extensions.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket {
@@ -1765,6 +1794,21 @@
 	return nil, nil
 }
 
+func verifyChannelIDMessage(channelIDMsg *channelIDMsg, channelIDHash []byte) (*ecdsa.PublicKey, error) {
+	x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32])
+	y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64])
+	r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96])
+	s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128])
+	if !elliptic.P256().IsOnCurve(x, y) {
+		return nil, errors.New("tls: invalid channel ID public key")
+	}
+	channelID := &ecdsa.PublicKey{elliptic.P256(), x, y}
+	if !ecdsa.Verify(channelID, channelIDHash, r, s) {
+		return nil, errors.New("tls: invalid channel ID signature")
+	}
+	return channelID, nil
+}
+
 func (hs *serverHandshakeState) writeServerHash(msg []byte) {
 	// writeServerHash is called before writeRecord.
 	hs.writeHash(msg, hs.c.sendHandshakeSeq)
@@ -1849,3 +1893,24 @@
 func isGREASEValue(val uint16) bool {
 	return val&0x0f0f == 0x0a0a && val&0xff == val>>8
 }
+
+func verifyPSKBinder(clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, transcript []byte) error {
+	binderLen := 2
+	for _, binder := range clientHello.pskBinders {
+		binderLen += 1 + len(binder)
+	}
+
+	truncatedHello := clientHello.marshal()
+	truncatedHello = truncatedHello[:len(truncatedHello)-binderLen]
+	pskCipherSuite := cipherSuiteFromID(sessionState.cipherSuite)
+	if pskCipherSuite == nil {
+		return errors.New("tls: Unknown cipher suite for PSK in session")
+	}
+
+	binder := computePSKBinder(sessionState.masterSecret, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello)
+	if !bytes.Equal(binder, binderToVerify) {
+		return errors.New("tls: PSK binder does not verify")
+	}
+
+	return nil
+}
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index ffa68e9..c311e99 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -230,10 +230,6 @@
 	// full buffer is required.
 	buffer []byte
 
-	// TLS 1.3 has a resumption context which is carried over on PSK
-	// resumption.
-	resumptionContextHash []byte
-
 	version uint16
 	prf     func(result, secret, label, seed []byte)
 }
@@ -357,7 +353,7 @@
 		hash.Write(channelIDResumeLabel)
 		hash.Write(resumeHash)
 	}
-	hash.Write(h.server.Sum(nil))
+	hash.Write(h.Sum())
 	return hash.Sum(nil)
 }
 
@@ -374,13 +370,6 @@
 	return make([]byte, h.hash.Size())
 }
 
-// setResumptionContext sets the TLS 1.3 resumption context.
-func (h *finishedHash) setResumptionContext(resumptionContext []byte) {
-	hash := h.hash.New()
-	hash.Write(resumptionContext)
-	h.resumptionContextHash = hash.Sum(nil)
-}
-
 // extractKey combines two secrets together with HKDF-Expand in the TLS 1.3 key
 // derivation schedule.
 func (h *finishedHash) extractKey(salt, ikm []byte) []byte {
@@ -412,12 +401,13 @@
 // resumption context hash, as used in TLS 1.3.
 func (h *finishedHash) appendContextHashes(b []byte) []byte {
 	b = h.client.Sum(b)
-	b = append(b, h.resumptionContextHash...)
 	return b
 }
 
 // The following are labels for traffic secret derivation in TLS 1.3.
 var (
+	externalPSKBinderLabel        = []byte("external psk binder key")
+	resumptionPSKBinderLabel      = []byte("resumption psk binder key")
 	earlyTrafficLabel             = []byte("client early traffic secret")
 	clientHandshakeTrafficLabel   = []byte("client handshake traffic secret")
 	serverHandshakeTrafficLabel   = []byte("server handshake traffic secret")
@@ -431,10 +421,6 @@
 // deriveSecret implements TLS 1.3's Derive-Secret function, as defined in
 // section 7.1 of draft ietf-tls-tls13-16.
 func (h *finishedHash) deriveSecret(secret, label []byte) []byte {
-	if h.resumptionContextHash == nil {
-		panic("Resumption context not set.")
-	}
-
 	return hkdfExpandLabel(h.hash, secret, label, h.appendContextHashes(nil), h.hash.Size())
 }
 
@@ -442,6 +428,7 @@
 var (
 	clientCertificateVerifyContextTLS13 = []byte("TLS 1.3, client CertificateVerify")
 	serverCertificateVerifyContextTLS13 = []byte("TLS 1.3, server CertificateVerify")
+	channelIDContextTLS13               = []byte("TLS 1.3, Channel ID")
 )
 
 // certificateVerifyMessage returns the input to be signed for CertificateVerify
@@ -458,14 +445,6 @@
 	return b
 }
 
-// The following are phase values for traffic key derivation in TLS 1.3.
-var (
-	earlyHandshakePhase   = []byte("early handshake key expansion")
-	earlyApplicationPhase = []byte("early application data key expansion")
-	handshakePhase        = []byte("handshake key expansion")
-	applicationPhase      = []byte("application data key expansion")
-)
-
 type trafficDirection int
 
 const (
@@ -473,17 +452,16 @@
 	serverWrite
 )
 
+var (
+	keyTLS13 = []byte("key")
+	ivTLS13  = []byte("iv")
+)
+
 // deriveTrafficAEAD derives traffic keys and constructs an AEAD given a traffic
 // secret.
-func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) interface{} {
-	label := make([]byte, 0, len(phase)+2+16)
-	label = append(label, phase...)
-	label = append(label, []byte(", key")...)
-	key := hkdfExpandLabel(suite.hash(), secret, label, nil, suite.keyLen)
-
-	label = label[:len(label)-3] // Remove "key" from the end.
-	label = append(label, []byte("iv")...)
-	iv := hkdfExpandLabel(suite.hash(), secret, label, nil, suite.ivLen(version))
+func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) interface{} {
+	key := hkdfExpandLabel(suite.hash(), secret, keyTLS13, nil, suite.keyLen)
+	iv := hkdfExpandLabel(suite.hash(), secret, ivTLS13, nil, suite.ivLen(version))
 
 	return suite.aead(version, key, iv)
 }
@@ -492,10 +470,11 @@
 	return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size())
 }
 
-func deriveResumptionPSK(suite *cipherSuite, resumptionSecret []byte) []byte {
-	return hkdfExpandLabel(suite.hash(), resumptionSecret, []byte("resumption psk"), nil, suite.hash().Size())
-}
-
-func deriveResumptionContext(suite *cipherSuite, resumptionSecret []byte) []byte {
-	return hkdfExpandLabel(suite.hash(), resumptionSecret, []byte("resumption context"), nil, suite.hash().Size())
+func computePSKBinder(psk, label []byte, cipherSuite *cipherSuite, transcript, truncatedHello []byte) []byte {
+	finishedHash := newFinishedHash(VersionTLS13, cipherSuite)
+	earlySecret := finishedHash.extractKey(finishedHash.zeroSecret(), psk)
+	binderKey := finishedHash.deriveSecret(earlySecret, label)
+	finishedHash.Write(transcript)
+	finishedHash.Write(truncatedHello)
+	return finishedHash.clientSum(binderKey)
 }
diff --git a/src/ssl/test/runner/rsa_chain_cert.pem b/src/ssl/test/runner/rsa_chain_cert.pem
new file mode 100644
index 0000000..8eb6e60
--- /dev/null
+++ b/src/ssl/test/runner/rsa_chain_cert.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs
+aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8
+CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/
+kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3
+tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c
+IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1
+z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V
+iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
+KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw
+rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7
+AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w
+giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW
+ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK
+MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC
+1ngWZ7Ih
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS
+b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE
+AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D
+GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ
+3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l
+HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF
+Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7
+6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM
+cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ
+BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1
+QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye
+NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b
+WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv
+XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=
+-----END CERTIFICATE-----
diff --git a/src/ssl/test/runner/rsa_chain_key.pem b/src/ssl/test/runner/rsa_chain_key.pem
new file mode 100644
index 0000000..d94d704
--- /dev/null
+++ b/src/ssl/test/runner/rsa_chain_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC
+afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M
+/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P
+Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l
+P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50
+Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ
+i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0
+YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk
+wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe
+iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ
+HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9
+042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn
+1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f
+CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE
+NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f
+AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z
+YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt
+Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA
+UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP
+2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS
+fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy
+xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN
+FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ
+2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk
+buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi
+SxpiPQ8d/hmSGwn4ksrWUsJD
+-----END PRIVATE KEY-----
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 367fef1..683f07c 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -62,6 +62,7 @@
 	looseErrors        = flag.Bool("loose-errors", false, "If true, allow shims to report an untranslated error code.")
 	shimConfigFile     = flag.String("shim-config", "", "A config file to use to configure the tests for this shim.")
 	includeDisabled    = flag.Bool("include-disabled", false, "If true, also runs disabled tests.")
+	repeatUntilFailure = flag.Bool("repeat-until-failure", false, "If true, the first selected test will be run repeatedly until failure.")
 )
 
 // ShimConfigurations is used with the “json” package and represents a shim
@@ -87,6 +88,7 @@
 const (
 	testCertRSA testCert = iota
 	testCertRSA1024
+	testCertRSAChain
 	testCertECDSAP256
 	testCertECDSAP384
 	testCertECDSAP521
@@ -95,6 +97,7 @@
 const (
 	rsaCertificateFile       = "cert.pem"
 	rsa1024CertificateFile   = "rsa_1024_cert.pem"
+	rsaChainCertificateFile  = "rsa_chain_cert.pem"
 	ecdsaP256CertificateFile = "ecdsa_p256_cert.pem"
 	ecdsaP384CertificateFile = "ecdsa_p384_cert.pem"
 	ecdsaP521CertificateFile = "ecdsa_p521_cert.pem"
@@ -103,6 +106,7 @@
 const (
 	rsaKeyFile       = "key.pem"
 	rsa1024KeyFile   = "rsa_1024_key.pem"
+	rsaChainKeyFile  = "rsa_chain_key.pem"
 	ecdsaP256KeyFile = "ecdsa_p256_key.pem"
 	ecdsaP384KeyFile = "ecdsa_p384_key.pem"
 	ecdsaP521KeyFile = "ecdsa_p521_key.pem"
@@ -112,6 +116,7 @@
 var (
 	rsaCertificate       Certificate
 	rsa1024Certificate   Certificate
+	rsaChainCertificate  Certificate
 	ecdsaP256Certificate Certificate
 	ecdsaP384Certificate Certificate
 	ecdsaP521Certificate Certificate
@@ -135,6 +140,12 @@
 		cert:     &rsa1024Certificate,
 	},
 	{
+		id:       testCertRSAChain,
+		certFile: rsaChainCertificateFile,
+		keyFile:  rsaChainKeyFile,
+		cert:     &rsaChainCertificate,
+	},
+	{
 		id:       testCertECDSAP256,
 		certFile: ecdsaP256CertificateFile,
 		keyFile:  ecdsaP256KeyFile,
@@ -158,7 +169,10 @@
 var channelIDBytes []byte
 
 var testOCSPResponse = []byte{1, 2, 3, 4}
-var testSCTList = []byte{5, 6, 7, 8}
+var testSCTList = []byte{0, 6, 0, 4, 5, 6, 7, 8}
+
+var testOCSPExtension = append([]byte{byte(extensionStatusRequest) >> 8, byte(extensionStatusRequest), 0, 8, statusTypeOCSP, 0, 0, 4}, testOCSPResponse...)
+var testSCTExtension = append([]byte{byte(extensionSignedCertificateTimestamp) >> 8, byte(extensionSignedCertificateTimestamp), 0, byte(len(testSCTList))}, testSCTList...)
 
 func initCertificates() {
 	for i := range testCerts {
@@ -364,6 +378,9 @@
 	// expectMessageDropped, if true, means the test message is expected to
 	// be dropped by the client rather than echoed back.
 	expectMessageDropped bool
+	// expectPeerCertificate, if not nil, is the certificate chain the peer
+	// is expected to send.
+	expectPeerCertificate *Certificate
 }
 
 var testCases []testCase
@@ -581,6 +598,17 @@
 		return fmt.Errorf("expected peer to use curve %04x, but got %04x", expected, connState.CurveID)
 	}
 
+	if test.expectPeerCertificate != nil {
+		if len(connState.PeerCertificates) != len(test.expectPeerCertificate.Certificate) {
+			return fmt.Errorf("expected peer to send %d certificates, but got %d", len(connState.PeerCertificates), len(test.expectPeerCertificate.Certificate))
+		}
+		for i, cert := range connState.PeerCertificates {
+			if !bytes.Equal(cert.Raw, test.expectPeerCertificate.Certificate[i]) {
+				return fmt.Errorf("peer certificate %d did not match", i+1)
+			}
+		}
+	}
+
 	if test.exportKeyingMaterial > 0 {
 		actual := make([]byte, test.exportKeyingMaterial)
 		if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -1682,6 +1710,50 @@
 			expectedLocalError: "remote error: handshake failure",
 		},
 		{
+			name: "FailCertCallback-Client-TLS12",
+			config: Config{
+				MaxVersion: VersionTLS12,
+				ClientAuth: RequestClientCert,
+			},
+			flags:              []string{"-fail-cert-callback"},
+			shouldFail:         true,
+			expectedError:      ":CERT_CB_ERROR:",
+			expectedLocalError: "remote error: internal error",
+		},
+		{
+			testType: serverTest,
+			name:     "FailCertCallback-Server-TLS12",
+			config: Config{
+				MaxVersion: VersionTLS12,
+			},
+			flags:              []string{"-fail-cert-callback"},
+			shouldFail:         true,
+			expectedError:      ":CERT_CB_ERROR:",
+			expectedLocalError: "remote error: internal error",
+		},
+		{
+			name: "FailCertCallback-Client-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				ClientAuth: RequestClientCert,
+			},
+			flags:              []string{"-fail-cert-callback"},
+			shouldFail:         true,
+			expectedError:      ":CERT_CB_ERROR:",
+			expectedLocalError: "remote error: internal error",
+		},
+		{
+			testType: serverTest,
+			name:     "FailCertCallback-Server-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			flags:              []string{"-fail-cert-callback"},
+			shouldFail:         true,
+			expectedError:      ":CERT_CB_ERROR:",
+			expectedLocalError: "remote error: internal error",
+		},
+		{
 			protocol: dtls,
 			name:     "FragmentMessageTypeMismatch-DTLS",
 			config: Config{
@@ -2299,10 +2371,13 @@
 			config: Config{
 				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
+					// TLS 1.3 servers are expected to
+					// always enable GREASE. TLS 1.3 is new,
+					// so there is no existing ecosystem to
+					// worry about.
 					ExpectGREASE: true,
 				},
 			},
-			flags: []string{"-enable-grease"},
 		},
 	}
 	testCases = append(testCases, basicTests...)
@@ -2485,7 +2560,8 @@
 				if !shouldClientFail {
 					// Ensure the maximum record size is accepted.
 					testCases = append(testCases, testCase{
-						name: prefix + ver.name + "-" + suite.name + "-LargeRecord",
+						protocol: protocol,
+						name:     prefix + ver.name + "-" + suite.name + "-LargeRecord",
 						config: Config{
 							MinVersion:           ver.version,
 							MaxVersion:           ver.version,
@@ -2497,6 +2573,33 @@
 						flags:      flags,
 						messageLen: maxPlaintext,
 					})
+
+					// Test bad records for all ciphers. Bad records are fatal in TLS
+					// and ignored in DTLS.
+					var shouldFail bool
+					var expectedError string
+					if protocol == tls {
+						shouldFail = true
+						expectedError = ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"
+					}
+
+					testCases = append(testCases, testCase{
+						protocol: protocol,
+						name:     prefix + ver.name + "-" + suite.name + "-BadRecord",
+						config: Config{
+							MinVersion:           ver.version,
+							MaxVersion:           ver.version,
+							CipherSuites:         []uint16{suite.id},
+							Certificates:         []Certificate{cert},
+							PreSharedKey:         []byte(psk),
+							PreSharedKeyIdentity: pskIdentity,
+						},
+						flags:            flags,
+						damageFirstWrite: true,
+						messageLen:       maxPlaintext,
+						shouldFail:       shouldFail,
+						expectedError:    expectedError,
+					})
 				}
 			}
 		}
@@ -3940,33 +4043,54 @@
 			},
 		})
 
-		// Client sends a Channel ID.
-		tests = append(tests, testCase{
-			name: "ChannelID-Client",
-			config: Config{
-				MaxVersion:       VersionTLS12,
-				RequestChannelID: true,
-			},
-			flags:           []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
-			resumeSession:   true,
-			expectChannelID: true,
-		})
+		// Test Channel ID
+		for _, ver := range tlsVersions {
+			if ver.version < VersionTLS10 {
+				continue
+			}
+			// Client sends a Channel ID.
+			tests = append(tests, testCase{
+				name: "ChannelID-Client-" + ver.name,
+				config: Config{
+					MaxVersion:       ver.version,
+					RequestChannelID: true,
+				},
+				flags:           []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
+				resumeSession:   true,
+				expectChannelID: true,
+			})
 
-		// Server accepts a Channel ID.
-		tests = append(tests, testCase{
-			testType: serverTest,
-			name:     "ChannelID-Server",
-			config: Config{
-				MaxVersion: VersionTLS12,
-				ChannelID:  channelIDKey,
-			},
-			flags: []string{
-				"-expect-channel-id",
-				base64.StdEncoding.EncodeToString(channelIDBytes),
-			},
-			resumeSession:   true,
-			expectChannelID: true,
-		})
+			// Server accepts a Channel ID.
+			tests = append(tests, testCase{
+				testType: serverTest,
+				name:     "ChannelID-Server-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					ChannelID:  channelIDKey,
+				},
+				flags: []string{
+					"-expect-channel-id",
+					base64.StdEncoding.EncodeToString(channelIDBytes),
+				},
+				resumeSession:   true,
+				expectChannelID: true,
+			})
+
+			tests = append(tests, testCase{
+				testType: serverTest,
+				name:     "InvalidChannelIDSignature-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					ChannelID:  channelIDKey,
+					Bugs: ProtocolBugs{
+						InvalidChannelIDSignature: true,
+					},
+				},
+				flags:         []string{"-enable-channel-id"},
+				shouldFail:    true,
+				expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
+			})
+		}
 
 		// Channel ID and NPN at the same time, to ensure their relative
 		// ordering is correct.
@@ -4333,6 +4457,19 @@
 		expectedVersion: VersionTLS12,
 	})
 
+	// Test that the maximum version is selected regardless of the
+	// client-sent order.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "IgnoreClientVersionOrder",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendSupportedVersions: []uint16{VersionTLS12, tls13DraftVersion},
+			},
+		},
+		expectedVersion: VersionTLS13,
+	})
+
 	// Test for version tolerance.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -4715,6 +4852,8 @@
 			config: Config{
 				MaxVersion: ver.version,
 				NextProtos: []string{"foo", "bar", "baz"},
+				// Prior to TLS 1.3, exercise the asynchronous session callback.
+				SessionTicketsDisabled: ver.version < VersionTLS13,
 			},
 			flags: []string{
 				"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
@@ -4859,7 +4998,10 @@
 			config: Config{
 				MaxVersion: ver.version,
 				Bugs: ProtocolBugs{
-					CorruptTicket: true,
+					FilterTicket: func(in []byte) ([]byte, error) {
+						in[len(in)-1] ^= 1
+						return in, nil
+					},
 				},
 			},
 			resumeSession:        true,
@@ -4897,7 +5039,10 @@
 			config: Config{
 				MaxVersion: ver.version,
 				Bugs: ProtocolBugs{
-					CorruptTicket: true,
+					FilterTicket: func(in []byte) ([]byte, error) {
+						in[len(in)-1] ^= 1
+						return in, nil
+					},
 				},
 			},
 			resumeSession:        true,
@@ -5024,6 +5169,10 @@
 			resumeSession: true,
 		})
 
+		var differentSCTList []byte
+		differentSCTList = append(differentSCTList, testSCTList...)
+		differentSCTList[len(differentSCTList)-1] ^= 1
+
 		// The SCT extension did not specify that it must only be sent on resumption as it
 		// should have, so test that we tolerate but ignore it.
 		testCases = append(testCases, testCase{
@@ -5031,7 +5180,7 @@
 			config: Config{
 				MaxVersion: ver.version,
 				Bugs: ProtocolBugs{
-					SendSCTListOnResume: []byte("bogus"),
+					SendSCTListOnResume: differentSCTList,
 				},
 			},
 			flags: []string{
@@ -5055,6 +5204,61 @@
 			expectedSCTList: testSCTList,
 			resumeSession:   true,
 		})
+
+		emptySCTListCert := *testCerts[0].cert
+		emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0}
+
+		// Test empty SCT list.
+		testCases = append(testCases, testCase{
+			name:     "SignedCertificateTimestampListEmpty-Client-" + ver.name,
+			testType: clientTest,
+			config: Config{
+				MaxVersion:   ver.version,
+				Certificates: []Certificate{emptySCTListCert},
+			},
+			flags: []string{
+				"-enable-signed-cert-timestamps",
+			},
+			shouldFail:    true,
+			expectedError: ":ERROR_PARSING_EXTENSION:",
+		})
+
+		emptySCTCert := *testCerts[0].cert
+		emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0}
+
+		// Test empty SCT in non-empty list.
+		testCases = append(testCases, testCase{
+			name:     "SignedCertificateTimestampListEmptySCT-Client-" + ver.name,
+			testType: clientTest,
+			config: Config{
+				MaxVersion:   ver.version,
+				Certificates: []Certificate{emptySCTCert},
+			},
+			flags: []string{
+				"-enable-signed-cert-timestamps",
+			},
+			shouldFail:    true,
+			expectedError: ":ERROR_PARSING_EXTENSION:",
+		})
+
+		// Test that certificate-related extensions are not sent unsolicited.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "UnsolicitedCertificateExtensions-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					NoOCSPStapling:                true,
+					NoSignedCertificateTimestamps: true,
+				},
+			},
+			flags: []string{
+				"-ocsp-response",
+				base64.StdEncoding.EncodeToString(testOCSPResponse),
+				"-signed-cert-timestamps",
+				base64.StdEncoding.EncodeToString(testSCTList),
+			},
+		})
 	}
 
 	testCases = append(testCases, testCase{
@@ -5171,19 +5375,6 @@
 		expectedError: ":ERROR_PARSING_EXTENSION:",
 	})
 	testCases = append(testCases, testCase{
-		name: "ChannelID-Forbidden-TLS13",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			RequestChannelID: true,
-			Bugs: ProtocolBugs{
-				NegotiateChannelIDAtAllVersions: true,
-			},
-		},
-		flags:         []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
-		shouldFail:    true,
-		expectedError: ":ERROR_PARSING_EXTENSION:",
-	})
-	testCases = append(testCases, testCase{
 		name: "Ticket-Forbidden-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS12,
@@ -5205,15 +5396,6 @@
 	// implicit in every test.)
 	testCases = append(testCases, testCase{
 		testType: serverTest,
-		name:     "ChannelID-Declined-TLS13",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			ChannelID:  channelIDKey,
-		},
-		flags: []string{"-enable-channel-id"},
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
 		name:     "NPN-Declined-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
@@ -5222,22 +5404,6 @@
 		flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"},
 	})
 
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "InvalidChannelIDSignature",
-		config: Config{
-			MaxVersion: VersionTLS12,
-			ChannelID:  channelIDKey,
-			Bugs: ProtocolBugs{
-				InvalidChannelIDSignature: true,
-			},
-		},
-		flags:              []string{"-enable-channel-id"},
-		shouldFail:         true,
-		expectedError:      ":CHANNEL_ID_SIGNATURE_INVALID:",
-		expectedLocalError: "remote error: error decrypting message",
-	})
-
 	// OpenSSL sends the status_request extension on resumption in TLS 1.2. Test that this is
 	// tolerated.
 	testCases = append(testCases, testCase{
@@ -5256,23 +5422,154 @@
 		resumeSession: true,
 	})
 
-	// Beginning TLS 1.3, enforce this does not happen.
 	testCases = append(testCases, testCase{
-		name: "SendOCSPResponseOnResume-TLS13",
+		name: "SendUnsolicitedOCSPOnCertificate-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
-				SendOCSPResponseOnResume: []byte("bogus"),
+				SendExtensionOnCertificate: testOCSPExtension,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "SendUnsolicitedSCTOnCertificate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendExtensionOnCertificate: testSCTExtension,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
+	// Test that extensions on client certificates are never accepted.
+	testCases = append(testCases, testCase{
+		name:     "SendExtensionOnClientCertificate-TLS13",
+		testType: serverTest,
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaCertificate},
+			Bugs: ProtocolBugs{
+				SendExtensionOnCertificate: testOCSPExtension,
+			},
+		},
+		flags: []string{
+			"-enable-ocsp-stapling",
+			"-require-any-client-certificate",
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "SendUnknownExtensionOnCertificate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendExtensionOnCertificate: []byte{0x00, 0x7f, 0, 0},
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
+	var differentSCTList []byte
+	differentSCTList = append(differentSCTList, testSCTList...)
+	differentSCTList[len(differentSCTList)-1] ^= 1
+
+	// Test that extensions on intermediates are allowed but ignored.
+	testCases = append(testCases, testCase{
+		name: "IgnoreExtensionsOnIntermediates-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaChainCertificate},
+			Bugs: ProtocolBugs{
+				// Send different values on the intermediate. This tests
+				// the intermediate's extensions do not override the
+				// leaf's.
+				SendOCSPOnIntermediates: []byte{1, 3, 3, 7},
+				SendSCTOnIntermediates:  differentSCTList,
 			},
 		},
 		flags: []string{
 			"-enable-ocsp-stapling",
 			"-expect-ocsp-response",
 			base64.StdEncoding.EncodeToString(testOCSPResponse),
+			"-enable-signed-cert-timestamps",
+			"-expect-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString(testSCTList),
+		},
+		resumeSession: true,
+	})
+
+	// Test that extensions are not sent on intermediates when configured
+	// only for a leaf.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SendNoExtensionsOnIntermediate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				ExpectNoExtensionsOnIntermediate: true,
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+			"-ocsp-response",
+			base64.StdEncoding.EncodeToString(testOCSPResponse),
+			"-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString(testSCTList),
+		},
+	})
+
+	// Test that extensions are not sent on client certificates.
+	testCases = append(testCases, testCase{
+		name: "SendNoClientCertificateExtensions-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-ocsp-response",
+			base64.StdEncoding.EncodeToString(testOCSPResponse),
+			"-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString(testSCTList),
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		name: "SendDuplicateExtensionsOnCerts-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendDuplicateCertExtensions: true,
+			},
+		},
+		flags: []string{
+			"-enable-ocsp-stapling",
+			"-enable-signed-cert-timestamps",
 		},
 		resumeSession: true,
 		shouldFail:    true,
-		expectedError: ":ERROR_PARSING_EXTENSION:",
+		expectedError: ":DUPLICATE_EXTENSION:",
+	})
+
+	testCases = append(testCases, testCase{
+		name:     "SignedCertificateTimestampListInvalid-Server",
+		testType: serverTest,
+		flags: []string{
+			"-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString([]byte{0, 0}),
+		},
+		shouldFail: true,
+		expectedError: ":INVALID_SCT_LIST:",
 	})
 }
 
@@ -5381,6 +5678,223 @@
 		}
 	}
 
+	// Make sure shim ticket mutations are functional.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "ShimTicketRewritable",
+		resumeSession: true,
+		config: Config{
+			MaxVersion:   VersionTLS12,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				FilterTicket: func(in []byte) ([]byte, error) {
+					in, err := SetShimTicketVersion(in, VersionTLS12)
+					if err != nil {
+						return nil, err
+					}
+					return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+	})
+
+	// Resumptions are declined if the version does not match.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-DeclineCrossVersion",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectNewTicket: true,
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketVersion(in, VersionTLS13)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		expectResumeRejected: true,
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-DeclineCrossVersion-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketVersion(in, VersionTLS12)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		expectResumeRejected: true,
+	})
+
+	// Resumptions are declined if the cipher is invalid or disabled.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-DeclineBadCipher",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectNewTicket: true,
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		expectResumeRejected: true,
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-DeclineBadCipher-2",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectNewTicket: true,
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
+				},
+			},
+		},
+		flags: []string{
+			"-cipher", "AES128",
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		expectResumeRejected: true,
+	})
+
+	// Sessions are not resumed if they do not use the preferred cipher.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-CipherNotPreferred",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectNewTicket: true,
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		shouldFail:           false,
+		expectResumeRejected: true,
+	})
+
+	// TLS 1.3 allows sessions to be resumed at a different cipher if their
+	// PRF hashes match, but BoringSSL will always decline such resumptions.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-CipherNotPreferred-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				FilterTicket: func(in []byte) ([]byte, error) {
+					// If the client (runner) offers ChaCha20-Poly1305 first, the
+					// server (shim) always prefers it. Switch it to AES-GCM.
+					return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		shouldFail:           false,
+		expectResumeRejected: true,
+	})
+
+	// Sessions may not be resumed if they contain another version's cipher.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-DeclineBadCipher-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				FilterTicket: func(in []byte) ([]byte, error) {
+					return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
+				},
+			},
+		},
+		flags: []string{
+			"-ticket-key",
+			base64.StdEncoding.EncodeToString(TestShimTicketKey),
+		},
+		expectResumeRejected: true,
+	})
+
+	// If the client does not offer the cipher from the session, decline to
+	// resume. Clients are forbidden from doing this, but BoringSSL selects
+	// the cipher first, so we only decline.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-UnofferedCipher",
+		resumeSession: true,
+		config: Config{
+			MaxVersion:   VersionTLS12,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+		},
+		resumeConfig: &Config{
+			MaxVersion:   VersionTLS12,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+			Bugs: ProtocolBugs{
+				SendCipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			},
+		},
+		expectResumeRejected: true,
+	})
+
+	// In TLS 1.3, clients may advertise a cipher list which does not
+	// include the selected cipher. Test that we tolerate this. Servers may
+	// resume at another cipher if the PRF matches, but BoringSSL will
+	// always decline.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-UnofferedCipher-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+		},
+		resumeConfig: &Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+			Bugs: ProtocolBugs{
+				SendCipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
+			},
+		},
+		expectResumeRejected: true,
+	})
+
+	// Sessions may not be resumed at a different cipher.
 	testCases = append(testCases, testCase{
 		name:          "Resume-Client-CipherMismatch",
 		resumeSession: true,
@@ -5399,6 +5913,8 @@
 		expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
 	})
 
+	// Session resumption in TLS 1.3 may change the cipher suite if the PRF
+	// matches.
 	testCases = append(testCases, testCase{
 		name:          "Resume-Client-CipherMismatch-TLS13",
 		resumeSession: true,
@@ -5408,13 +5924,87 @@
 		},
 		resumeConfig: &Config{
 			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+		},
+	})
+
+	// Session resumption in TLS 1.3 is forbidden if the PRF does not match.
+	testCases = append(testCases, testCase{
+		name:          "Resume-Client-PRFMismatch-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
+		},
+		resumeConfig: &Config{
+			MaxVersion:   VersionTLS13,
 			CipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
 			Bugs: ProtocolBugs{
 				SendCipherSuite: TLS_AES_256_GCM_SHA384,
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
+		expectedError: ":OLD_SESSION_PRF_HASH_MISMATCH:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-BinderWrongLength",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendShortPSKBinder: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decrypting message",
+		expectedError:      ":DIGEST_CHECK_FAILED:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-NoPSKBinder",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendNoPSKBinder: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decoding message",
+		expectedError:      ":DECODE_ERROR:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-InvalidPSKBinder",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendInvalidPSKBinder: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decrypting message",
+		expectedError:      ":DIGEST_CHECK_FAILED:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-PSKBinderFirstExtension",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				PSKBinderFirst: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: illegal parameter",
+		expectedError:      ":PRE_SHARED_KEY_MUST_BE_LAST:",
 	})
 }
 
@@ -5579,6 +6169,8 @@
 			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
 			Bugs: ProtocolBugs{
 				NegotiateVersionOnRenego: VersionTLS11,
+				// Avoid failing early at the record layer.
+				SendRecordVersion: VersionTLS12,
 			},
 		},
 		renegotiate: 1,
@@ -6874,6 +7466,7 @@
 			useExportContext:     true,
 		})
 	}
+
 	testCases = append(testCases, testCase{
 		name: "ExportKeyingMaterial-SSL3",
 		config: Config{
@@ -6886,6 +7479,47 @@
 		shouldFail:           true,
 		expectedError:        "failed to export keying material",
 	})
+
+	// Exporters work during a False Start.
+	testCases = append(testCases, testCase{
+		name: "ExportKeyingMaterial-FalseStart",
+		config: Config{
+			MaxVersion:   VersionTLS12,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				ExpectFalseStart: true,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst:      true,
+		exportKeyingMaterial: 1024,
+		exportLabel:          "label",
+		exportContext:        "context",
+		useExportContext:     true,
+	})
+
+	// Exporters do not work in the middle of a renegotiation. Test this by
+	// triggering the exporter after every SSL_read call and configuring the
+	// shim to run asynchronously.
+	testCases = append(testCases, testCase{
+		name: "ExportKeyingMaterial-Renegotiate",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		renegotiate: 1,
+		flags: []string{
+			"-async",
+			"-use-exporter-between-reads",
+			"-renegotiate-freely",
+			"-expect-total-renegotiations", "1",
+		},
+		shouldFail:    true,
+		expectedError: "failed to export keying material",
+	})
 }
 
 func addTLSUniqueTests() {
@@ -7636,19 +8270,34 @@
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
 				SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
-				SendPSKAuthModes:        []byte{0x1a, pskAuthMode, 0x2a},
 			},
 		},
 		resumeSession:         true,
 		expectedResumeVersion: VersionTLS13,
 	})
 
-	// Test that the server declines sessions with no matching key exchange mode.
+	// Test that the server does not send session tickets with no matching key exchange mode.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-ExpectNoSessionTicketOnBadKEMode-Server",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendPSKKeyExchangeModes:  []byte{0x1a},
+				ExpectNoNewSessionTicket: true,
+			},
+		},
+	})
+
+	// Test that the server does not accept a session with no matching key exchange mode.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "TLS13-SendBadKEModeSessionTicket-Server",
 		config: Config{
 			MaxVersion: VersionTLS13,
+		},
+		resumeConfig: &Config{
+			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
 				SendPSKKeyExchangeModes: []byte{0x1a},
 			},
@@ -7657,60 +8306,37 @@
 		expectResumeRejected: true,
 	})
 
-	// Test that the server declines sessions with no matching auth mode.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-SendBadAuthModeSessionTicket-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendPSKAuthModes: []byte{0x1a},
-			},
-		},
-		resumeSession:        true,
-		expectResumeRejected: true,
-	})
-
-	// Test that the client ignores unknown PSK modes.
+	// Test that the client ticket age is sent correctly.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-SendUnknownModeSessionTicket-Client",
+		name:     "TLS13-TestValidTicketAge-Client",
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
-				SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
-				SendPSKAuthModes:        []byte{0x1a, pskAuthMode, 0x2a},
+				ExpectTicketAge: 10 * time.Second,
 			},
 		},
-		resumeSession:         true,
-		expectedResumeVersion: VersionTLS13,
+		resumeSession: true,
+		flags: []string{
+			"-resumption-delay", "10",
+		},
 	})
 
-	// Test that the client ignores tickets with no matching key exchange mode.
+	// Test that the client ticket age is enforced.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-SendBadKEModeSessionTicket-Client",
+		name:     "TLS13-TestBadTicketAge-Client",
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
-				SendPSKKeyExchangeModes: []byte{0x1a},
+				ExpectTicketAge: 1000 * time.Second,
 			},
 		},
-		flags: []string{"-expect-no-session"},
+		resumeSession:      true,
+		shouldFail:         true,
+		expectedLocalError: "tls: invalid ticket age",
 	})
 
-	// Test that the client ignores tickets with no matching auth mode.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-SendBadAuthModeSessionTicket-Client",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendPSKAuthModes: []byte{0x1a},
-			},
-		},
-		flags: []string{"-expect-no-session"},
-	})
 }
 
 func addChangeCipherSpecTests() {
@@ -8369,33 +8995,6 @@
 
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "OmitServerHelloSignatureAlgorithms",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				OmitServerHelloSignatureAlgorithms: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "IncludeServerHelloSignatureAlgorithms",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				IncludeServerHelloSignatureAlgorithms: true,
-			},
-		},
-		resumeSession: true,
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
 		name:     "MissingKeyShare-Client",
 		config: Config{
 			MaxVersion: VersionTLS13,
@@ -8759,6 +9358,18 @@
 		expectedError: ":PSK_IDENTITY_NOT_FOUND:",
 	})
 
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-ExtraPSKIdentity",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				ExtraPSKIdentity: true,
+			},
+		},
+		resumeSession: true,
+	})
+
 	// Test that unknown NewSessionTicket extensions are tolerated.
 	testCases = append(testCases, testCase{
 		name: "TLS13-CustomTicketExtension",
@@ -8771,6 +9382,58 @@
 	})
 }
 
+func addTLS13CipherPreferenceTests() {
+	// Test that client preference is honored if the shim has AES hardware
+	// and ChaCha20-Poly1305 is preferred otherwise.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-CipherPreference-Server-ChaCha20-AES",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			CipherSuites: []uint16{
+				TLS_CHACHA20_POLY1305_SHA256,
+				TLS_AES_128_GCM_SHA256,
+			},
+		},
+		flags: []string{
+			"-expect-cipher-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+			"-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-CipherPreference-Server-AES-ChaCha20",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			CipherSuites: []uint16{
+				TLS_AES_128_GCM_SHA256,
+				TLS_CHACHA20_POLY1305_SHA256,
+			},
+		},
+		flags: []string{
+			"-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)),
+			"-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+		},
+	})
+
+	// Test that the client orders ChaCha20-Poly1305 and AES-GCM based on
+	// whether it has AES hardware.
+	testCases = append(testCases, testCase{
+		name: "TLS13-CipherPreference-Client",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			// Use the client cipher order. (This is the default but
+			// is listed to be explicit.)
+			PreferServerCipherSuites: false,
+		},
+		flags: []string{
+			"-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)),
+			"-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+		},
+	})
+}
+
 func addPeekTests() {
 	// Test SSL_peek works, including on empty records.
 	testCases = append(testCases, testCase{
@@ -8841,16 +9504,177 @@
 	})
 }
 
+func addRecordVersionTests() {
+	for _, ver := range tlsVersions {
+		// Test that the record version is enforced.
+		testCases = append(testCases, testCase{
+			name: "CheckRecordVersion-" + ver.name,
+			config: Config{
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					SendRecordVersion: 0x03ff,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":WRONG_VERSION_NUMBER:",
+		})
+
+		// Test that the ClientHello may use any record version, for
+		// compatibility reasons.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "LooseInitialRecordVersion-" + ver.name,
+			config: Config{
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					SendInitialRecordVersion: 0x03ff,
+				},
+			},
+		})
+
+		// Test that garbage ClientHello record versions are rejected.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "GarbageInitialRecordVersion-" + ver.name,
+			config: Config{
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					SendInitialRecordVersion: 0xffff,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":WRONG_VERSION_NUMBER:",
+		})
+	}
+}
+
+func addCertificateTests() {
+	// Test that a certificate chain with intermediate may be sent and
+	// received as both client and server.
+	for _, ver := range tlsVersions {
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "SendReceiveIntermediate-Client-" + ver.name,
+			config: Config{
+				Certificates: []Certificate{rsaChainCertificate},
+				ClientAuth:   RequireAnyClientCert,
+			},
+			expectPeerCertificate: &rsaChainCertificate,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+				"-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SendReceiveIntermediate-Server-" + ver.name,
+			config: Config{
+				Certificates: []Certificate{rsaChainCertificate},
+			},
+			expectPeerCertificate: &rsaChainCertificate,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+				"-require-any-client-certificate",
+				"-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+			},
+		})
+	}
+}
+
+func addRetainOnlySHA256ClientCertTests() {
+	for _, ver := range tlsVersions {
+		// Test that enabling
+		// SSL_CTX_set_retain_only_sha256_of_client_certs without
+		// actually requesting a client certificate is a no-op.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "RetainOnlySHA256-NoCert-" + ver.name,
+			config: Config{
+				MinVersion: ver.version,
+				MaxVersion: ver.version,
+			},
+			flags: []string{
+				"-retain-only-sha256-client-cert-initial",
+				"-retain-only-sha256-client-cert-resume",
+			},
+			resumeSession: true,
+		})
+
+		// Test that when retaining only a SHA-256 certificate is
+		// enabled, the hash appears as expected.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "RetainOnlySHA256-Cert-" + ver.name,
+			config: Config{
+				MinVersion:   ver.version,
+				MaxVersion:   ver.version,
+				Certificates: []Certificate{rsaCertificate},
+			},
+			flags: []string{
+				"-verify-peer",
+				"-retain-only-sha256-client-cert-initial",
+				"-retain-only-sha256-client-cert-resume",
+				"-expect-sha256-client-cert-initial",
+				"-expect-sha256-client-cert-resume",
+			},
+			resumeSession: true,
+		})
+
+		// Test that when the config changes from on to off, a
+		// resumption is rejected because the server now wants the full
+		// certificate chain.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "RetainOnlySHA256-OnOff-" + ver.name,
+			config: Config{
+				MinVersion:   ver.version,
+				MaxVersion:   ver.version,
+				Certificates: []Certificate{rsaCertificate},
+			},
+			flags: []string{
+				"-verify-peer",
+				"-retain-only-sha256-client-cert-initial",
+				"-expect-sha256-client-cert-initial",
+			},
+			resumeSession:        true,
+			expectResumeRejected: true,
+		})
+
+		// Test that when the config changes from off to on, a
+		// resumption is rejected because the server now wants just the
+		// hash.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "RetainOnlySHA256-OffOn-" + ver.name,
+			config: Config{
+				MinVersion:   ver.version,
+				MaxVersion:   ver.version,
+				Certificates: []Certificate{rsaCertificate},
+			},
+			flags: []string{
+				"-verify-peer",
+				"-retain-only-sha256-client-cert-resume",
+				"-expect-sha256-client-cert-resume",
+			},
+			resumeSession:        true,
+			expectResumeRejected: true,
+		})
+	}
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
 	for test := range c {
 		var err error
 
-		if *mallocTest < 0 {
-			statusChan <- statusMsg{test: test, started: true}
-			err = runTest(test, shimPath, -1)
-		} else {
+		if *mallocTest >= 0 {
 			for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ {
 				statusChan <- statusMsg{test: test, started: true}
 				if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs {
@@ -8860,6 +9684,14 @@
 					break
 				}
 			}
+		} else if *repeatUntilFailure {
+			for err == nil {
+				statusChan <- statusMsg{test: test, started: true}
+				err = runTest(test, shimPath, -1)
+			}
+		} else {
+			statusChan <- statusMsg{test: test, started: true}
+			err = runTest(test, shimPath, -1)
 		}
 		statusChan <- statusMsg{test: test, err: err}
 	}
@@ -8958,7 +9790,11 @@
 	addWrongMessageTypeTests()
 	addTrailingMessageDataTests()
 	addTLS13HandshakeTests()
+	addTLS13CipherPreferenceTests()
 	addPeekTests()
+	addRecordVersionTests()
+	addCertificateTests()
+	addRetainOnlySHA256ClientCertTests()
 
 	var wg sync.WaitGroup
 
@@ -9016,6 +9852,11 @@
 		if matched {
 			foundTest = true
 			testChan <- &testCases[i]
+
+			// Only run one test if repeating until failure.
+			if *repeatUntilFailure {
+				break
+			}
 		}
 	}
 
diff --git a/src/ssl/test/runner/shim_ticket.go b/src/ssl/test/runner/shim_ticket.go
new file mode 100644
index 0000000..9e57d48
--- /dev/null
+++ b/src/ssl/test/runner/shim_ticket.go
@@ -0,0 +1,249 @@
+// Copyright (c) 2016, Google Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package runner
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/asn1"
+	"errors"
+)
+
+// TestShimTicketKey is the testing key assumed for the shim.
+var TestShimTicketKey = make([]byte, 48)
+
+func DecryptShimTicket(in []byte) ([]byte, error) {
+	name := TestShimTicketKey[:16]
+	macKey := TestShimTicketKey[16:32]
+	encKey := TestShimTicketKey[32:48]
+
+	h := hmac.New(sha256.New, macKey)
+
+	block, err := aes.NewCipher(encKey)
+	if err != nil {
+		panic(err)
+	}
+
+	if len(in) < len(name)+block.BlockSize()+1+h.Size() {
+		return nil, errors.New("tls: shim ticket too short")
+	}
+
+	// Check the key name.
+	if !bytes.Equal(name, in[:len(name)]) {
+		return nil, errors.New("tls: shim ticket name mismatch")
+	}
+
+	// Check the MAC at the end of the ticket.
+	mac := in[len(in)-h.Size():]
+	in = in[:len(in)-h.Size()]
+	h.Write(in)
+	if !hmac.Equal(mac, h.Sum(nil)) {
+		return nil, errors.New("tls: shim ticket MAC mismatch")
+	}
+
+	// The MAC covers the key name, but the encryption does not.
+	in = in[len(name):]
+
+	// Decrypt in-place.
+	iv := in[:block.BlockSize()]
+	in = in[block.BlockSize():]
+	if l := len(in); l == 0 || l%block.BlockSize() != 0 {
+		return nil, errors.New("tls: ticket ciphertext not a multiple of the block size")
+	}
+	out := make([]byte, len(in))
+	cbc := cipher.NewCBCDecrypter(block, iv)
+	cbc.CryptBlocks(out, in)
+
+	// Remove the padding.
+	pad := int(out[len(out)-1])
+	if pad == 0 || pad > block.BlockSize() || pad > len(in) {
+		return nil, errors.New("tls: bad shim ticket CBC pad")
+	}
+
+	for i := 0; i < pad; i++ {
+		if out[len(out)-1-i] != byte(pad) {
+			return nil, errors.New("tls: bad shim ticket CBC pad")
+		}
+	}
+
+	return out[:len(out)-pad], nil
+}
+
+func EncryptShimTicket(in []byte) []byte {
+	name := TestShimTicketKey[:16]
+	macKey := TestShimTicketKey[16:32]
+	encKey := TestShimTicketKey[32:48]
+
+	h := hmac.New(sha256.New, macKey)
+
+	block, err := aes.NewCipher(encKey)
+	if err != nil {
+		panic(err)
+	}
+
+	// Use the zero IV for rewritten tickets.
+	iv := make([]byte, block.BlockSize())
+	cbc := cipher.NewCBCEncrypter(block, iv)
+	pad := block.BlockSize() - (len(in) % block.BlockSize())
+
+	out := make([]byte, 0, len(name)+len(iv)+len(in)+pad+h.Size())
+	out = append(out, name...)
+	out = append(out, iv...)
+	out = append(out, in...)
+	for i := 0; i < pad; i++ {
+		out = append(out, byte(pad))
+	}
+
+	ciphertext := out[len(name)+len(iv):]
+	cbc.CryptBlocks(ciphertext, ciphertext)
+
+	h.Write(out)
+	return h.Sum(out)
+}
+
+const asn1Constructed = 0x20
+
+func parseDERElement(in []byte) (tag byte, body, rest []byte, ok bool) {
+	rest = in
+	if len(rest) < 1 {
+		return
+	}
+
+	tag = rest[0]
+	rest = rest[1:]
+
+	if tag&0x1f == 0x1f {
+		// Long-form tags not supported.
+		return
+	}
+
+	if len(rest) < 1 {
+		return
+	}
+
+	length := int(rest[0])
+	rest = rest[1:]
+	if length > 0x7f {
+		lengthLength := length & 0x7f
+		length = 0
+		if lengthLength == 0 {
+			// No indefinite-length encoding.
+			return
+		}
+
+		// Decode long-form lengths.
+		for lengthLength > 0 {
+			if len(rest) < 1 || (length<<8)>>8 != length {
+				return
+			}
+			if length == 0 && rest[0] == 0 {
+				// Length not minimally-encoded.
+				return
+			}
+			length <<= 8
+			length |= int(rest[0])
+			rest = rest[1:]
+			lengthLength--
+		}
+
+		if length < 0x80 {
+			// Length not minimally-encoded.
+			return
+		}
+	}
+
+	if len(rest) < length {
+		return
+	}
+
+	body = rest[:length]
+	rest = rest[length:]
+	ok = true
+	return
+}
+
+func SetShimTicketVersion(in []byte, vers uint16) ([]byte, error) {
+	plaintext, err := DecryptShimTicket(in)
+	if err != nil {
+		return nil, err
+	}
+
+	tag, session, _, ok := parseDERElement(plaintext)
+	if !ok || tag != asn1.TagSequence|asn1Constructed {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// Skip the session version.
+	tag, _, session, ok = parseDERElement(session)
+	if !ok || tag != asn1.TagInteger {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// Next field is the protocol version.
+	tag, version, _, ok := parseDERElement(session)
+	if !ok || tag != asn1.TagInteger {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// This code assumes both old and new versions are encoded in two
+	// bytes. This isn't quite right as INTEGERs are minimally-encoded, but
+	// we do not need to support other caess for now.
+	if len(version) != 2 || vers < 0x80 || vers >= 0x8000 {
+		return nil, errors.New("tls: unsupported version in shim session")
+	}
+
+	version[0] = byte(vers >> 8)
+	version[1] = byte(vers)
+
+	return EncryptShimTicket(plaintext), nil
+}
+
+func SetShimTicketCipherSuite(in []byte, id uint16) ([]byte, error) {
+	plaintext, err := DecryptShimTicket(in)
+	if err != nil {
+		return nil, err
+	}
+
+	tag, session, _, ok := parseDERElement(plaintext)
+	if !ok || tag != asn1.TagSequence|asn1Constructed {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// Skip the session version.
+	tag, _, session, ok = parseDERElement(session)
+	if !ok || tag != asn1.TagInteger {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// Skip the protocol version.
+	tag, _, session, ok = parseDERElement(session)
+	if !ok || tag != asn1.TagInteger {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	// Next field is the cipher suite.
+	tag, cipherSuite, _, ok := parseDERElement(session)
+	if !ok || tag != asn1.TagOctetString || len(cipherSuite) != 2 {
+		return nil, errors.New("tls: could not decode shim session")
+	}
+
+	cipherSuite[0] = byte(id >> 8)
+	cipherSuite[1] = byte(id)
+
+	return EncryptShimTicket(plaintext), nil
+}
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 425664d..940e676 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -75,6 +75,7 @@
   { "-install-ddos-callback", &TestConfig::install_ddos_callback },
   { "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
   { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
+  { "-fail-cert-callback", &TestConfig::fail_cert_callback },
   { "-handshake-never-done", &TestConfig::handshake_never_done },
   { "-use-export-context", &TestConfig::use_export_context },
   { "-tls-unique", &TestConfig::tls_unique },
@@ -106,6 +107,15 @@
   { "-send-alert", &TestConfig::send_alert },
   { "-peek-then-read", &TestConfig::peek_then_read },
   { "-enable-grease", &TestConfig::enable_grease },
+  { "-use-exporter-between-reads", &TestConfig::use_exporter_between_reads },
+  { "-retain-only-sha256-client-cert-initial",
+    &TestConfig::retain_only_sha256_client_cert_initial },
+  { "-retain-only-sha256-client-cert-resume",
+    &TestConfig::retain_only_sha256_client_cert_resume },
+  { "-expect-sha256-client-cert-initial",
+    &TestConfig::expect_sha256_client_cert_initial },
+  { "-expect-sha256-client-cert-resume",
+    &TestConfig::expect_sha256_client_cert_resume },
 };
 
 const Flag<std::string> kStringFlags[] = {
@@ -130,6 +140,7 @@
   { "-cipher-tls11", &TestConfig::cipher_tls11 },
   { "-export-label", &TestConfig::export_label },
   { "-export-context", &TestConfig::export_context },
+  { "-expect-peer-cert-file", &TestConfig::expect_peer_cert_file },
 };
 
 const Flag<std::string> kBase64Flags[] = {
@@ -140,6 +151,7 @@
     &TestConfig::expected_signed_cert_timestamps },
   { "-ocsp-response", &TestConfig::ocsp_response },
   { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps },
+  { "-ticket-key", &TestConfig::ticket_key },
 };
 
 const Flag<int> kIntFlags[] = {
@@ -156,6 +168,9 @@
   { "-expect-dhe-group-size", &TestConfig::expect_dhe_group_size },
   { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
   { "-max-cert-list", &TestConfig::max_cert_list },
+  { "-expect-cipher-aes", &TestConfig::expect_cipher_aes },
+  { "-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes },
+  { "-resumption-delay", &TestConfig::resumption_delay },
 };
 
 const Flag<std::vector<int>> kIntVectorFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9f74297..ed1a47b 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -73,6 +73,7 @@
   bool install_ddos_callback = false;
   bool fail_ddos_callback = false;
   bool fail_second_ddos_callback = false;
+  bool fail_cert_callback = false;
   std::string cipher;
   std::string cipher_tls10;
   std::string cipher_tls11;
@@ -115,6 +116,16 @@
   bool peek_then_read = false;
   bool enable_grease = false;
   int max_cert_list = 0;
+  std::string ticket_key;
+  bool use_exporter_between_reads = false;
+  int expect_cipher_aes = 0;
+  int expect_cipher_no_aes = 0;
+  std::string expect_peer_cert_file;
+  int resumption_delay = 0;
+  bool retain_only_sha256_client_cert_initial = false;
+  bool retain_only_sha256_client_cert_resume = false;
+  bool expect_sha256_client_cert_initial = false;
+  bool expect_sha256_client_cert_resume = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index e32464d..17f7161 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -25,6 +25,7 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#include "../crypto/internal.h"
 #include "internal.h"
 
 
@@ -79,6 +80,11 @@
         hs->wait = ssl_hs_ok;
         return -1;
 
+      case ssl_hs_channel_id_lookup:
+        ssl->rwstate = SSL_CHANNEL_ID_LOOKUP;
+        hs->wait = ssl_hs_ok;
+        return -1;
+
       case ssl_hs_private_key_operation:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
         hs->wait = ssl_hs_ok;
@@ -105,8 +111,9 @@
   }
 }
 
-static int tls13_get_cert_verify_signature_input(SSL *ssl, uint8_t **out,
-                                                 size_t *out_len, int server) {
+int tls13_get_cert_verify_signature_input(
+    SSL *ssl, uint8_t **out, size_t *out_len,
+    enum ssl_cert_verify_context_t cert_verify_context) {
   CBB cbb;
   if (!CBB_init(&cbb, 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) {
     goto err;
@@ -118,23 +125,33 @@
     }
   }
 
-  if (server) {
+  const uint8_t *context;
+  size_t context_len;
+  if (cert_verify_context == ssl_cert_verify_server) {
     /* Include the NUL byte. */
     static const char kContext[] = "TLS 1.3, server CertificateVerify";
-    if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
-      goto err;
-    }
-  } else {
+    context = (const uint8_t *)kContext;
+    context_len = sizeof(kContext);
+  } else if (cert_verify_context == ssl_cert_verify_client) {
     static const char kContext[] = "TLS 1.3, client CertificateVerify";
-    if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
-      goto err;
-    }
+    context = (const uint8_t *)kContext;
+    context_len = sizeof(kContext);
+  } else if (cert_verify_context == ssl_cert_verify_channel_id) {
+    static const char kContext[] = "TLS 1.3, Channel ID";
+    context = (const uint8_t *)kContext;
+    context_len = sizeof(kContext);
+  } else {
+    goto err;
   }
 
-  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
-  size_t context_hashes_len;
-  if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
-      !CBB_add_bytes(&cbb, context_hashes, context_hashes_len) ||
+  if (!CBB_add_bytes(&cbb, context, context_len)) {
+    goto err;
+  }
+
+  uint8_t context_hash[EVP_MAX_MD_SIZE];
+  size_t context_hash_len;
+  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
+      !CBB_add_bytes(&cbb, context_hash, context_hash_len) ||
       !CBB_finish(&cbb, out, out_len)) {
     goto err;
   }
@@ -148,7 +165,7 @@
 }
 
 int tls13_process_certificate(SSL *ssl, int allow_anonymous) {
-  CBS cbs, context;
+  CBS cbs, context, certificate_list;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
       CBS_len(&context) != 0) {
@@ -158,17 +175,117 @@
   }
 
   const int retain_sha256 =
-      ssl->server && ssl->ctx->retain_only_sha256_of_client_certs;
+      ssl->server && ssl->retain_only_sha256_of_client_certs;
   int ret = 0;
-  uint8_t alert;
-  STACK_OF(X509) *chain = ssl_parse_cert_chain(
-      ssl, &alert, retain_sha256 ? ssl->s3->new_session->peer_sha256 : NULL,
-      &cbs);
+
+  STACK_OF(X509) *chain = sk_X509_new_null();
   if (chain == NULL) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
   }
 
+  if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto err;
+  }
+
+  while (CBS_len(&certificate_list) > 0) {
+    CBS certificate, extensions;
+    if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) ||
+        !CBS_get_u16_length_prefixed(&certificate_list, &extensions)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
+      goto err;
+    }
+
+    /* Retain the hash of the leaf certificate if requested. */
+    if (sk_X509_num(chain) == 0 && retain_sha256) {
+      SHA256(CBS_data(&certificate), CBS_len(&certificate),
+             ssl->s3->new_session->peer_sha256);
+    }
+
+    X509 *x = ssl_parse_x509(&certificate);
+    if (x == NULL || CBS_len(&certificate) != 0) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      X509_free(x);
+      goto err;
+    }
+    if (!sk_X509_push(chain, x)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      X509_free(x);
+      goto err;
+    }
+
+    /* Parse out the extensions. */
+    int have_status_request = 0, have_sct = 0;
+    CBS status_request, sct;
+    const SSL_EXTENSION_TYPE ext_types[] = {
+        {TLSEXT_TYPE_status_request, &have_status_request, &status_request},
+        {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct},
+    };
+
+    uint8_t alert;
+    if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                              OPENSSL_ARRAY_SIZE(ext_types))) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      goto err;
+    }
+
+    /* All Certificate extensions are parsed, but only the leaf extensions are
+     * stored. */
+    if (have_status_request) {
+      if (ssl->server || !ssl->ocsp_stapling_enabled) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        goto err;
+      }
+
+      uint8_t status_type;
+      CBS ocsp_response;
+      if (!CBS_get_u8(&status_request, &status_type) ||
+          status_type != TLSEXT_STATUSTYPE_ocsp ||
+          !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) ||
+          CBS_len(&ocsp_response) == 0 ||
+          CBS_len(&status_request) != 0) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        goto err;
+      }
+
+      if (sk_X509_num(chain) == 1 &&
+          !CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
+                    &ssl->s3->new_session->ocsp_response_length)) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        goto err;
+      }
+    }
+
+    if (have_sct) {
+      if (ssl->server || !ssl->signed_cert_timestamps_enabled) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        goto err;
+      }
+
+      if (!ssl_is_sct_list_valid(&sct)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        goto err;
+      }
+
+      if (sk_X509_num(chain) == 1 &&
+          !CBS_stow(&sct,
+                    &ssl->s3->new_session->tlsext_signed_cert_timestamp_list,
+                    &ssl->s3->new_session
+                         ->tlsext_signed_cert_timestamp_list_length)) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        goto err;
+      }
+    }
+  }
+
   if (CBS_len(&cbs) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -198,13 +315,13 @@
     goto err;
   }
 
-  X509_free(ssl->s3->new_session->peer);
+  X509_free(ssl->s3->new_session->x509_peer);
   X509 *leaf = sk_X509_value(chain, 0);
   X509_up_ref(leaf);
-  ssl->s3->new_session->peer = leaf;
+  ssl->s3->new_session->x509_peer = leaf;
 
-  sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
-  ssl->s3->new_session->cert_chain = chain;
+  sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+  ssl->s3->new_session->x509_chain = chain;
   chain = NULL;
 
   ret = 1;
@@ -216,7 +333,7 @@
 
 int tls13_process_certificate_verify(SSL *ssl) {
   int ret = 0;
-  X509 *peer = ssl->s3->new_session->peer;
+  X509 *peer = ssl->s3->new_session->x509_peer;
   EVP_PKEY *pkey = NULL;
   uint8_t *msg = NULL;
   size_t msg_len;
@@ -245,8 +362,9 @@
   }
   ssl->s3->tmp.peer_signature_algorithm = signature_algorithm;
 
-  if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
-                                             !ssl->server)) {
+  if (!tls13_get_cert_verify_signature_input(
+          ssl, &msg, &msg_len,
+          ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     goto err;
   }
@@ -307,17 +425,79 @@
 }
 
 int tls13_prepare_certificate(SSL *ssl) {
-  CBB cbb, body;
+  CBB cbb, body, certificate_list;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
       /* The request context is always empty in the handshake. */
       !CBB_add_u8(&body, 0) ||
-      !ssl_add_cert_chain(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
-    return 0;
+      !CBB_add_u24_length_prefixed(&body, &certificate_list)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  if (!ssl_has_certificate(ssl)) {
+    if (!ssl_complete_message(ssl, &cbb)) {
+      goto err;
+    }
+
+    return 1;
+  }
+
+  CERT *cert = ssl->cert;
+  CBB leaf, extensions;
+  if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
+      !ssl_add_cert_to_cbb(&leaf, cert->x509_leaf) ||
+      !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  if (ssl->s3->hs->scts_requested &&
+      ssl->ctx->signed_cert_timestamp_list_length != 0) {
+    CBB contents;
+    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
+        !CBB_add_u16_length_prefixed(&extensions, &contents) ||
+        !CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
+                       ssl->ctx->signed_cert_timestamp_list_length) ||
+        !CBB_flush(&extensions)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+  }
+
+  if (ssl->s3->hs->ocsp_stapling_requested &&
+      ssl->ctx->ocsp_response_length != 0) {
+    CBB contents, ocsp_response;
+    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) ||
+        !CBB_add_u16_length_prefixed(&extensions, &contents) ||
+        !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) ||
+        !CBB_add_u24_length_prefixed(&contents, &ocsp_response) ||
+        !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
+                       ssl->ctx->ocsp_response_length) ||
+        !CBB_flush(&extensions)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+  }
+
+  for (size_t i = 0; i < sk_X509_num(cert->x509_chain); i++) {
+    CBB child;
+    if (!CBB_add_u24_length_prefixed(&certificate_list, &child) ||
+        !ssl_add_cert_to_cbb(&child, sk_X509_value(cert->x509_chain, i)) ||
+        !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+  }
+
+  if (!ssl_complete_message(ssl, &cbb)) {
+    goto err;
   }
 
   return 1;
+
+err:
+  CBB_cleanup(&cbb);
+  return 0;
 }
 
 enum ssl_private_key_result_t tls13_prepare_certificate_verify(
@@ -352,8 +532,9 @@
 
   enum ssl_private_key_result_t sign_result;
   if (is_first_run) {
-    if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
-                                               ssl->server)) {
+    if (!tls13_get_cert_verify_signature_input(
+            ssl, &msg, &msg_len,
+            ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       goto err;
     }
@@ -369,7 +550,7 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -394,7 +575,7 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, verify_data, verify_data_len) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return 0;
   }
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 87ccdc4..4a30ce3 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -24,6 +24,7 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
+#include "../crypto/internal.h"
 #include "internal.h"
 
 
@@ -41,6 +42,7 @@
   state_send_client_certificate,
   state_send_client_certificate_verify,
   state_complete_client_certificate_verify,
+  state_send_channel_id,
   state_send_client_finished,
   state_flush,
   state_done,
@@ -66,97 +68,73 @@
     return ssl_hs_error;
   }
 
-  while (CBS_len(&extensions) != 0) {
-    uint16_t type;
-    CBS extension;
-    if (!CBS_get_u16(&extensions, &type) ||
-        !CBS_get_u16_length_prefixed(&extensions, &extension)) {
+  int have_cookie, have_key_share;
+  CBS cookie, key_share;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+      {TLSEXT_TYPE_cookie, &have_cookie, &cookie},
+  };
+
+  uint8_t alert;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                            OPENSSL_ARRAY_SIZE(ext_types))) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
+
+  if (have_cookie) {
+    CBS cookie_value;
+    if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
+        CBS_len(&cookie_value) == 0 ||
+        CBS_len(&cookie) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
-    switch (type) {
-      case TLSEXT_TYPE_cookie: {
-        if (hs->cookie != NULL) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          return ssl_hs_error;
-        }
-
-        /* Cookies may be requested whether or not advertised, so no need to
-         * check. */
-
-        CBS cookie;
-        if (!CBS_get_u16_length_prefixed(&extension, &cookie) ||
-            CBS_len(&cookie) == 0 ||
-            CBS_len(&extension) != 0) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-          return ssl_hs_error;
-        }
-
-        if (!CBS_stow(&cookie, &hs->cookie, &hs->cookie_len)) {
-          return ssl_hs_error;
-        }
-        break;
-      }
-
-      case TLSEXT_TYPE_key_share: {
-        if (hs->retry_group != 0) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          return ssl_hs_error;
-        }
-
-        /* key_share is always advertised, so no need to check. */
-
-        uint16_t group_id;
-        if (!CBS_get_u16(&extension, &group_id) ||
-            CBS_len(&extension) != 0) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-          return ssl_hs_error;
-        }
-
-        /* The group must be supported. */
-        const uint16_t *groups;
-        size_t groups_len;
-        tls1_get_grouplist(ssl, &groups, &groups_len);
-        int found = 0;
-        for (size_t i = 0; i < groups_len; i++) {
-          if (groups[i] == group_id) {
-            found = 1;
-            break;
-          }
-        }
-
-        if (!found) {
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-          return ssl_hs_error;
-        }
-
-        /* Check that the HelloRetryRequest does not request the key share that
-         * was provided in the initial ClientHello. */
-        if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) {
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-          return ssl_hs_error;
-        }
-
-        SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
-        hs->retry_group = group_id;
-        break;
-      }
-
-      default:
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-        return ssl_hs_error;
+    if (!CBS_stow(&cookie_value, &hs->cookie, &hs->cookie_len)) {
+      return ssl_hs_error;
     }
   }
 
+  if (have_key_share) {
+    uint16_t group_id;
+    if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return ssl_hs_error;
+    }
+
+    /* The group must be supported. */
+    const uint16_t *groups;
+    size_t groups_len;
+    tls1_get_grouplist(ssl, &groups, &groups_len);
+    int found = 0;
+    for (size_t i = 0; i < groups_len; i++) {
+      if (groups[i] == group_id) {
+        found = 1;
+        break;
+      }
+    }
+
+    if (!found) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+      return ssl_hs_error;
+    }
+
+    /* Check that the HelloRetryRequest does not request the key share that
+     * was provided in the initial ClientHello. */
+    if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+      return ssl_hs_error;
+    }
+
+    SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
+    hs->retry_group = group_id;
+  }
+
   hs->received_hello_retry_request = 1;
   hs->state = state_send_second_client_hello;
   return ssl_hs_ok;
@@ -164,11 +142,7 @@
 
 static enum ssl_hs_wait_t do_send_second_client_hello(SSL *ssl,
                                                       SSL_HANDSHAKE *hs) {
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) ||
-      !ssl_add_client_hello_body(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
+  if (!ssl_write_client_hello(ssl)) {
     return ssl_hs_error;
   }
 
@@ -217,73 +191,37 @@
     return ssl_hs_error;
   }
 
-  /* Check if the cipher is disabled. */
-  if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
-      (cipher->algorithm_auth & ssl->cert->mask_a) ||
-      SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
-      !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) {
+  /* Check if the cipher is a TLS 1.3 cipher. */
+  if (SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
   /* Parse out the extensions. */
-  int have_key_share = 0, have_pre_shared_key = 0, have_sigalgs = 0;
-  CBS key_share, pre_shared_key, sigalgs;
-  while (CBS_len(&extensions) != 0) {
-    uint16_t type;
-    CBS extension;
-    if (!CBS_get_u16(&extensions, &type) ||
-        !CBS_get_u16_length_prefixed(&extensions, &extension)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      return ssl_hs_error;
-    }
+  int have_key_share = 0, have_pre_shared_key = 0;
+  CBS key_share, pre_shared_key;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+      {TLSEXT_TYPE_pre_shared_key, &have_pre_shared_key, &pre_shared_key},
+  };
 
-    switch (type) {
-      case TLSEXT_TYPE_key_share:
-        if (have_key_share) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          return ssl_hs_error;
-        }
-        key_share = extension;
-        have_key_share = 1;
-        break;
-      case TLSEXT_TYPE_pre_shared_key:
-        if (have_pre_shared_key) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          return ssl_hs_error;
-        }
-        pre_shared_key = extension;
-        have_pre_shared_key = 1;
-        break;
-      case TLSEXT_TYPE_signature_algorithms:
-        if (have_sigalgs) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-          return ssl_hs_error;
-        }
-        sigalgs = extension;
-        have_sigalgs = 1;
-        break;
-      default:
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-        return ssl_hs_error;
-    }
+  uint8_t alert;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                            OPENSSL_ARRAY_SIZE(ext_types))) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
   }
 
-  /* We only support PSK_AUTH and PSK_DHE_KE. */
-  if (!have_key_share || have_sigalgs == have_pre_shared_key) {
+  /* We only support PSK_DHE_KE. */
+  if (!have_key_share) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  uint8_t alert = SSL_AD_DECODE_ERROR;
+  alert = SSL_AD_DECODE_ERROR;
   if (have_pre_shared_key) {
     if (ssl->session == NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
@@ -303,8 +241,8 @@
       return ssl_hs_error;
     }
 
-    if (ssl->session->cipher != cipher) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
+    if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
@@ -339,20 +277,17 @@
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
 
   /* Derive resumption material. */
-  uint8_t resumption_ctx[EVP_MAX_MD_SIZE] = {0};
   uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
   if (ssl->s3->session_reused) {
-    if (!tls13_resumption_context(ssl, resumption_ctx, hash_len,
-                                  ssl->s3->new_session) ||
-        !tls13_resumption_psk(ssl, psk_secret, hash_len,
-                              ssl->s3->new_session)) {
+    if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
       return ssl_hs_error;
     }
+    memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
   }
 
   /* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
    * into the running secret. */
-  if (!tls13_init_key_schedule(ssl, resumption_ctx, hash_len) ||
+  if (!tls13_init_key_schedule(ssl) ||
       !tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
     return ssl_hs_error;
   }
@@ -372,16 +307,10 @@
   }
   OPENSSL_free(dhe_secret);
 
-  if (have_sigalgs &&
-      CBS_len(&sigalgs) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
   /* If there was no HelloRetryRequest, the version negotiation logic has
    * already hashed the message. */
   if (hs->received_hello_retry_request &&
-      !ssl->method->hash_current_message(ssl)) {
+      !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -411,7 +340,7 @@
     return ssl_hs_error;
   }
 
-  if (!ssl->method->hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -467,7 +396,7 @@
   sk_X509_NAME_pop_free(ssl->s3->hs->ca_names, X509_NAME_free);
   ssl->s3->hs->ca_names = ca_sk;
 
-  if (!ssl->method->hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -479,7 +408,7 @@
                                                         SSL_HANDSHAKE *hs) {
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(ssl, 0 /* certificate required */) ||
-      !ssl->method->hash_current_message(ssl)) {
+      !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -491,8 +420,8 @@
     SSL *ssl, SSL_HANDSHAKE *hs) {
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(ssl) ||
-      !ssl->method->hash_current_message(ssl)) {
-    return 0;
+      !ssl_hash_current_message(ssl)) {
+    return ssl_hs_error;
   }
 
   hs->state = state_process_server_finished;
@@ -504,10 +433,10 @@
   static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
   if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(ssl) ||
-      !ssl->method->hash_current_message(ssl) ||
+      !ssl_hash_current_message(ssl) ||
       /* Update the secret to the master secret and derive traffic keys. */
       !tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
-      !tls13_derive_traffic_secret_0(ssl)) {
+      !tls13_derive_application_secrets(ssl)) {
     return ssl_hs_error;
   }
 
@@ -519,7 +448,7 @@
 static enum ssl_hs_wait_t do_certificate_callback(SSL *ssl, SSL_HANDSHAKE *hs) {
   /* The peer didn't request a certificate. */
   if (!ssl->s3->hs->cert_request) {
-    hs->state = state_send_client_finished;
+    hs->state = state_send_channel_id;
     return ssl_hs_ok;
   }
 
@@ -566,13 +495,13 @@
                                                             int is_first_run) {
   /* Don't send CertificateVerify if there is no certificate. */
   if (!ssl_has_certificate(ssl)) {
-    hs->state = state_send_client_finished;
+    hs->state = state_send_channel_id;
     return ssl_hs_ok;
   }
 
   switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
     case ssl_private_key_success:
-      hs->state = state_send_client_finished;
+      hs->state = state_send_channel_id;
       return ssl_hs_write_message;
 
     case ssl_private_key_retry:
@@ -587,6 +516,32 @@
   return ssl_hs_error;
 }
 
+static enum ssl_hs_wait_t do_send_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!ssl->s3->tlsext_channel_id_valid) {
+    hs->state = state_send_client_finished;
+    return ssl_hs_ok;
+  }
+
+  if (!ssl_do_channel_id_callback(ssl)) {
+    return ssl_hs_error;
+  }
+
+  if (ssl->tlsext_channel_id_private == NULL) {
+    return ssl_hs_channel_id_lookup;
+  }
+
+  CBB cbb, body;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+      !tls1_write_channel_id(ssl, &body) ||
+      !ssl_complete_message(ssl, &cbb)) {
+    CBB_cleanup(&cbb);
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_client_finished;
+  return ssl_hs_write_message;
+}
+
 static enum ssl_hs_wait_t do_send_client_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
   if (!tls13_prepare_finished(ssl)) {
     return ssl_hs_error;
@@ -597,11 +552,11 @@
 }
 
 static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
-  if (!tls13_set_traffic_key(ssl, type_data, evp_aead_open,
-                             hs->server_traffic_secret_0, hs->hash_len) ||
-      !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
-                             hs->client_traffic_secret_0, hs->hash_len) ||
-      !tls13_finalize_keys(ssl)) {
+  if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0,
+                             hs->hash_len) ||
+      !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0,
+                             hs->hash_len) ||
+      !tls13_derive_resumption_secret(ssl)) {
     return ssl_hs_error;
   }
 
@@ -651,10 +606,13 @@
         break;
       case state_send_client_certificate_verify:
         ret = do_send_client_certificate_verify(ssl, hs, 1 /* first run */);
-      break;
+        break;
       case state_complete_client_certificate_verify:
         ret = do_send_client_certificate_verify(ssl, hs, 0 /* complete */);
-      break;
+        break;
+      case state_send_channel_id:
+        ret = do_send_channel_id(ssl, hs);
+        break;
       case state_send_client_finished:
         ret = do_send_client_finished(ssl, hs);
         break;
@@ -682,13 +640,12 @@
     return 0;
   }
 
-  CBS cbs, ke_modes, auth_modes, ticket, extensions;
+  ssl_session_refresh_time(ssl, session);
+
+  CBS cbs, ticket, extensions;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
-      !CBS_get_u8_length_prefixed(&cbs, &ke_modes) ||
-      CBS_len(&ke_modes) == 0 ||
-      !CBS_get_u8_length_prefixed(&cbs, &auth_modes) ||
-      CBS_len(&auth_modes) == 0 ||
+      !CBS_get_u32(&cbs, &session->ticket_age_add) ||
       !CBS_get_u16_length_prefixed(&cbs, &ticket) ||
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
       !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
@@ -699,13 +656,10 @@
     return 0;
   }
 
+  session->ticket_age_add_valid = 1;
   session->not_resumable = 0;
 
-  /* Ignore the ticket unless the server preferences are compatible with us. */
-  if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL &&
-      memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) !=
-          NULL &&
-      ssl->ctx->new_session_cb != NULL &&
+  if (ssl->ctx->new_session_cb != NULL &&
       ssl->ctx->new_session_cb(ssl, session)) {
     /* |new_session_cb|'s return value signals that it took ownership. */
     return 1;
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index 1fcde51..d87d8a6 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -20,27 +20,19 @@
 #include <openssl/aead.h>
 #include <openssl/bytestring.h>
 #include <openssl/digest.h>
-#include <openssl/hmac.h>
 #include <openssl/hkdf.h>
+#include <openssl/hmac.h>
 #include <openssl/mem.h>
 
 #include "internal.h"
 
 
-int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
-                            size_t resumption_ctx_len) {
+int tls13_init_key_schedule(SSL *ssl) {
   SSL_HANDSHAKE *hs = ssl->s3->hs;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
   hs->hash_len = EVP_MD_size(digest);
 
-  /* Save the hash of the resumption context. */
-  unsigned resumption_hash_len;
-  if (!EVP_Digest(resumption_ctx, resumption_ctx_len, hs->resumption_hash,
-                  &resumption_hash_len, digest, NULL)) {
-    return 0;
-  }
-
   /* Initialize the secret to the zero key. */
   memset(hs->secret, 0, hs->hash_len);
 
@@ -89,22 +81,17 @@
   return ret;
 }
 
-int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
   EVP_MD_CTX ctx;
   EVP_MD_CTX_init(&ctx);
   unsigned handshake_len = 0;
   int ok = EVP_MD_CTX_copy_ex(&ctx, &ssl->s3->handshake_hash) &&
            EVP_DigestFinal_ex(&ctx, out, &handshake_len);
   EVP_MD_CTX_cleanup(&ctx);
-  if (!ok) {
-    return 0;
+  if (ok) {
+    *out_len = handshake_len;
   }
-
-  memcpy(out + handshake_len, hs->resumption_hash, hs->hash_len);
-  *out_len = handshake_len + hs->hash_len;
-  return 1;
+  return ok;
 }
 
 /* derive_secret derives a secret of length |len| and writes the result in |out|
@@ -115,18 +102,17 @@
   SSL_HANDSHAKE *hs = ssl->s3->hs;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
-  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
-  size_t context_hashes_len;
-  if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len)) {
+  uint8_t context_hash[EVP_MAX_MD_SIZE];
+  size_t context_hash_len;
+  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len)) {
     return 0;
   }
 
   return hkdf_expand_label(out, digest, hs->secret, hs->hash_len, label,
-                           label_len, context_hashes, context_hashes_len, len);
+                           label_len, context_hash, context_hash_len, len);
 }
 
-int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
-                          enum evp_aead_direction_t direction,
+int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
                           const uint8_t *traffic_secret,
                           size_t traffic_secret_len) {
   if (traffic_secret_len > 0xff) {
@@ -134,33 +120,11 @@
     return 0;
   }
 
-  const char *key_label, *iv_label;
-  switch (type) {
-    case type_early_handshake:
-      key_label = "early handshake key expansion, key";
-      iv_label = "early handshake key expansion, iv";
-      break;
-    case type_early_data:
-      key_label = "early application data key expansion, key";
-      iv_label = "early application data key expansion, iv";
-      break;
-    case type_handshake:
-      key_label = "handshake key expansion, key";
-      iv_label = "handshake key expansion, iv";
-      break;
-    case type_data:
-      key_label = "application data key expansion, key";
-      iv_label = "application data key expansion, iv";
-      break;
-    default:
-      return 0;
-  }
-
   /* Look up cipher suite properties. */
   const EVP_AEAD *aead;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
-  size_t mac_secret_len, fixed_iv_len;
-  if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+  size_t discard;
+  if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard,
                                SSL_get_session(ssl)->cipher,
                                ssl3_protocol_version(ssl))) {
     return 0;
@@ -170,8 +134,7 @@
   size_t key_len = EVP_AEAD_key_length(aead);
   uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
   if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len,
-                         (const uint8_t *)key_label, strlen(key_label), NULL, 0,
-                         key_len)) {
+                         (const uint8_t *)"key", 3, NULL, 0, key_len)) {
     return 0;
   }
 
@@ -179,8 +142,7 @@
   size_t iv_len = EVP_AEAD_nonce_length(aead);
   uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
   if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len,
-                         (const uint8_t *)iv_label, strlen(iv_label), NULL, 0,
-                         iv_len)) {
+                         (const uint8_t *)"iv", 2, NULL, 0, iv_len)) {
     return 0;
   }
 
@@ -241,26 +203,29 @@
   }
 
   if (ssl->server) {
-    if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
-                               client_traffic_secret, hs->hash_len) ||
-        !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
-                               server_traffic_secret, hs->hash_len)) {
+    if (!tls13_set_traffic_key(ssl, evp_aead_open, client_traffic_secret,
+                               hs->hash_len) ||
+        !tls13_set_traffic_key(ssl, evp_aead_seal, server_traffic_secret,
+                               hs->hash_len)) {
       return 0;
     }
   } else {
-    if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
-                               server_traffic_secret, hs->hash_len) ||
-        !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
-                               client_traffic_secret, hs->hash_len)) {
+    if (!tls13_set_traffic_key(ssl, evp_aead_open, server_traffic_secret,
+                               hs->hash_len) ||
+        !tls13_set_traffic_key(ssl, evp_aead_seal, client_traffic_secret,
+                               hs->hash_len)) {
       return 0;
     }
   }
   return 1;
 }
 
-int tls13_derive_traffic_secret_0(SSL *ssl) {
+static const char kTLS13LabelExporter[] = "exporter master secret";
+
+int tls13_derive_application_secrets(SSL *ssl) {
   SSL_HANDSHAKE *hs = ssl->s3->hs;
 
+  ssl->s3->exporter_secret_len = hs->hash_len;
   return derive_secret(ssl, hs->client_traffic_secret_0, hs->hash_len,
                        (const uint8_t *)kTLS13LabelClientApplicationTraffic,
                        strlen(kTLS13LabelClientApplicationTraffic)) &&
@@ -270,7 +235,10 @@
                        (const uint8_t *)kTLS13LabelServerApplicationTraffic,
                        strlen(kTLS13LabelServerApplicationTraffic)) &&
          ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
-                        hs->server_traffic_secret_0, hs->hash_len);
+                        hs->server_traffic_secret_0, hs->hash_len) &&
+         derive_secret(ssl, ssl->s3->exporter_secret, hs->hash_len,
+                       (const uint8_t *)kTLS13LabelExporter,
+                       strlen(kTLS13LabelExporter));
 }
 
 static const char kTLS13LabelApplicationTraffic[] =
@@ -296,27 +264,36 @@
     return 0;
   }
 
-  return tls13_set_traffic_key(ssl, type_data, direction, secret, secret_len);
+  return tls13_set_traffic_key(ssl, direction, secret, secret_len);
 }
 
-static const char kTLS13LabelExporter[] = "exporter master secret";
 static const char kTLS13LabelResumption[] = "resumption master secret";
 
-int tls13_finalize_keys(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+int tls13_derive_resumption_secret(SSL *ssl) {
+  ssl->s3->new_session->master_key_length = ssl->s3->hs->hash_len;
+  return derive_secret(ssl, ssl->s3->new_session->master_key,
+                       ssl->s3->new_session->master_key_length,
+                       (const uint8_t *)kTLS13LabelResumption,
+                       strlen(kTLS13LabelResumption));
+}
 
-  ssl->s3->exporter_secret_len = hs->hash_len;
-  ssl->s3->new_session->master_key_length = hs->hash_len;
-  if (!derive_secret(
-          ssl, ssl->s3->exporter_secret, ssl->s3->exporter_secret_len,
-          (const uint8_t *)kTLS13LabelExporter, strlen(kTLS13LabelExporter)) ||
-      !derive_secret(ssl, ssl->s3->new_session->master_key,
-                     ssl->s3->new_session->master_key_length,
-                     (const uint8_t *)kTLS13LabelResumption,
-                     strlen(kTLS13LabelResumption))) {
+static const char kTLS13LabelFinished[] = "finished";
+
+/* tls13_verify_data sets |out| to be the HMAC of |context| using a derived
+ * Finished key for both Finished messages and the PSK binder. */
+static int tls13_verify_data(const EVP_MD *digest, uint8_t *out,
+                             size_t *out_len, const uint8_t *secret,
+                             size_t hash_len, uint8_t *context,
+                             size_t context_len) {
+  uint8_t key[EVP_MAX_MD_SIZE];
+  unsigned len;
+  if (!hkdf_expand_label(key, digest, secret, hash_len,
+                         (const uint8_t *)kTLS13LabelFinished,
+                         strlen(kTLS13LabelFinished), NULL, 0, hash_len) ||
+      HMAC(digest, key, hash_len, context, context_len, out, &len) == NULL) {
     return 0;
   }
-
+  *out_len = len;
   return 1;
 }
 
@@ -324,54 +301,23 @@
   SSL_HANDSHAKE *hs = ssl->s3->hs;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
-  uint8_t key[EVP_MAX_MD_SIZE];
-  size_t key_len = EVP_MD_size(digest);
-
   const uint8_t *traffic_secret;
-  const char *label = "finished";
   if (is_server == ssl->server) {
     traffic_secret = ssl->s3->write_traffic_secret;
   } else {
     traffic_secret = ssl->s3->read_traffic_secret;
   }
 
-  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
-  size_t context_hashes_len;
-  unsigned len;
-  if (!hkdf_expand_label(key, digest, traffic_secret, hs->hash_len,
-                         (const uint8_t *)label, strlen(label), NULL, 0,
-                         hs->hash_len) ||
-      !tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
-      HMAC(digest, key, key_len, context_hashes, context_hashes_len, out,
-           &len) == NULL) {
+  uint8_t context_hash[EVP_MAX_MD_SIZE];
+  size_t context_hash_len;
+  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
+      !tls13_verify_data(digest, out, out_len, traffic_secret, hs->hash_len,
+                         context_hash, context_hash_len)) {
     return 0;
   }
-  *out_len = len;
   return 1;
 }
 
-static const char kTLS13LabelResumptionPSK[] = "resumption psk";
-static const char kTLS13LabelResumptionContext[] = "resumption context";
-
-int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
-                         const SSL_SESSION *session) {
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
-  return hkdf_expand_label(out, digest, session->master_key,
-                           session->master_key_length,
-                           (const uint8_t *)kTLS13LabelResumptionPSK,
-                           strlen(kTLS13LabelResumptionPSK), NULL, 0, out_len);
-}
-
-int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
-                             const SSL_SESSION *session) {
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
-  return hkdf_expand_label(out, digest, session->master_key,
-                           session->master_key_length,
-                           (const uint8_t *)kTLS13LabelResumptionContext,
-                           strlen(kTLS13LabelResumptionContext), NULL, 0,
-                           out_len);
-}
-
 int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
                                  const char *label, size_t label_len,
                                  const uint8_t *context, size_t context_len,
@@ -388,3 +334,119 @@
                            ssl->s3->exporter_secret_len, (const uint8_t *)label,
                            label_len, hash, hash_len, out_len);
 }
+
+static const char kTLS13LabelPSKBinder[] = "resumption psk binder key";
+
+static int tls13_psk_binder(SSL *ssl, uint8_t *out, const EVP_MD *digest,
+                            uint8_t *psk, size_t psk_len, uint8_t *context,
+                            size_t context_len, size_t hash_len) {
+  uint8_t binder_context[EVP_MAX_MD_SIZE];
+  unsigned binder_context_len;
+  if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
+    return 0;
+  }
+
+  uint8_t early_secret[EVP_MAX_MD_SIZE] = {0};
+  size_t early_secret_len;
+  if (!HKDF_extract(early_secret, &early_secret_len, digest, psk, hash_len,
+                    NULL, 0)) {
+    return 0;
+  }
+
+  uint8_t binder_key[EVP_MAX_MD_SIZE] = {0};
+  size_t len;
+  if (!hkdf_expand_label(binder_key, digest, early_secret, hash_len,
+                         (const uint8_t *)kTLS13LabelPSKBinder,
+                         strlen(kTLS13LabelPSKBinder), binder_context,
+                         binder_context_len, hash_len) ||
+      !tls13_verify_data(digest, out, &len, binder_key, hash_len, context,
+                         context_len)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len) {
+  const EVP_MD *digest =
+      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+  size_t hash_len = EVP_MD_size(digest);
+
+  if (len < hash_len + 3) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  EVP_MD_CTX ctx;
+  EVP_MD_CTX_init(&ctx);
+  uint8_t context[EVP_MAX_MD_SIZE];
+  unsigned context_len;
+  if (!EVP_DigestInit_ex(&ctx, digest, NULL) ||
+      !EVP_DigestUpdate(&ctx, ssl->s3->handshake_buffer->data,
+                        ssl->s3->handshake_buffer->length) ||
+      !EVP_DigestUpdate(&ctx, msg, len - hash_len - 3) ||
+      !EVP_DigestFinal_ex(&ctx, context, &context_len)) {
+    EVP_MD_CTX_cleanup(&ctx);
+    return 0;
+  }
+
+  EVP_MD_CTX_cleanup(&ctx);
+
+  uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
+  if (!tls13_psk_binder(ssl, verify_data, digest, ssl->session->master_key,
+                        ssl->session->master_key_length, context,
+                        context_len, hash_len)) {
+    return 0;
+  }
+
+  memcpy(msg + len - hash_len, verify_data, hash_len);
+  return 1;
+}
+
+int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session,
+                            CBS *binders) {
+  const EVP_MD *digest =
+      ssl_get_handshake_digest(session->cipher->algorithm_prf);
+  size_t hash_len = EVP_MD_size(digest);
+
+  /* Get the full ClientHello, including message header. It must be large enough
+   * to exclude the binders. */
+  CBS message;
+  ssl->method->get_current_message(ssl, &message);
+  if (CBS_len(&message) < CBS_len(binders) + 2) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  /* Hash a ClientHello prefix up to the binders. For now, this assumes we only
+   * ever verify PSK binders on initial ClientHellos. */
+  uint8_t context[EVP_MAX_MD_SIZE];
+  unsigned context_len;
+  if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2,
+                  context, &context_len, digest, NULL)) {
+    return 0;
+  }
+
+  uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
+  CBS binder;
+  if (!tls13_psk_binder(ssl, verify_data, digest, session->master_key,
+                        session->master_key_length, context, context_len,
+                        hash_len) ||
+      /* We only consider the first PSK, so compare against the first binder. */
+      !CBS_get_u8_length_prefixed(binders, &binder)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  int binder_ok = CBS_len(&binder) == hash_len &&
+                  CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  binder_ok = 1;
+#endif
+  if (!binder_ok) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 2570069..83ef679 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -17,6 +17,7 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/aead.h>
 #include <openssl/bytestring.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
@@ -43,9 +44,10 @@
   state_flush,
   state_process_client_certificate,
   state_process_client_certificate_verify,
+  state_process_channel_id,
   state_process_client_finished,
   state_send_new_session_ticket,
-  state_flush_new_session_ticket,
+  state_flush_new_session_tickets,
   state_done,
 };
 
@@ -61,7 +63,7 @@
                                         TLSEXT_TYPE_key_share)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
-    return ssl_hs_error;
+    return 0;
   }
 
   int found_key_share;
@@ -103,53 +105,9 @@
   /* Load the client random. */
   if (client_hello.random_len != SSL3_RANDOM_SIZE) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
-  }
-  memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
-
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  SSL_SESSION *session = NULL;
-  CBS pre_shared_key;
-  if (ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
-                                       TLSEXT_TYPE_pre_shared_key) &&
-      !ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &alert,
-                                                &pre_shared_key)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return 0;
-  }
-
-  if (session != NULL &&
-      /* Only resume if the session's version matches. */
-      (session->ssl_version != ssl->version ||
-       !ssl_client_cipher_list_contains_cipher(
-           &client_hello, (uint16_t)SSL_CIPHER_get_id(session->cipher)))) {
-    SSL_SESSION_free(session);
-    session = NULL;
-  }
-
-  if (session == NULL) {
-    if (!ssl_get_new_session(ssl, 1 /* server */)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return ssl_hs_error;
-    }
-  } else {
-    /* Only authentication information carries over in TLS 1.3. */
-    ssl->s3->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
-    if (ssl->s3->new_session == NULL) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return ssl_hs_error;
-    }
-    ssl->s3->session_reused = 1;
-    SSL_SESSION_free(session);
-  }
-
-  if (ssl->ctx->dos_protection_cb != NULL &&
-      ssl->ctx->dos_protection_cb(&client_hello) == 0) {
-    /* Connection rejected for DOS reasons. */
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
+  memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
 
   /* TLS 1.3 requires the peer only advertise the null compression. */
   if (client_hello.compression_methods_len != 1 ||
@@ -169,20 +127,64 @@
   return ssl_hs_ok;
 }
 
+static const SSL_CIPHER *choose_tls13_cipher(
+    const SSL *ssl, const struct ssl_early_callback_ctx *client_hello) {
+  if (client_hello->cipher_suites_len % 2 != 0) {
+    return NULL;
+  }
+
+  CBS cipher_suites;
+  CBS_init(&cipher_suites, client_hello->cipher_suites,
+           client_hello->cipher_suites_len);
+
+  const int aes_is_fine = EVP_has_aes_hardware();
+  const uint16_t version = ssl3_protocol_version(ssl);
+
+  const SSL_CIPHER *best = NULL;
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t cipher_suite;
+    if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
+      return NULL;
+    }
+
+    /* Limit to TLS 1.3 ciphers we know about. */
+    const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite);
+    if (candidate == NULL ||
+        SSL_CIPHER_get_min_version(candidate) > version ||
+        SSL_CIPHER_get_max_version(candidate) < version) {
+      continue;
+    }
+
+    /* TLS 1.3 removes legacy ciphers, so honor the client order, but prefer
+     * ChaCha20 if we do not have AES hardware. */
+    if (aes_is_fine) {
+      return candidate;
+    }
+
+    if (candidate->algorithm_enc == SSL_CHACHA20POLY1305) {
+      return candidate;
+    }
+
+    if (best == NULL) {
+      best = candidate;
+    }
+  }
+
+  return best;
+}
+
 static enum ssl_hs_wait_t do_select_parameters(SSL *ssl, SSL_HANDSHAKE *hs) {
-  if (!ssl->s3->session_reused) {
-    /* Call |cert_cb| to update server certificates if required. */
-    if (ssl->cert->cert_cb != NULL) {
-      int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
-      if (rv == 0) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        return ssl_hs_error;
-      }
-      if (rv < 0) {
-        hs->state = state_select_parameters;
-        return ssl_hs_x509_lookup;
-      }
+  /* Call |cert_cb| to update server certificates if required. */
+  if (ssl->cert->cert_cb != NULL) {
+    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+    if (rv == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+    if (rv < 0) {
+      hs->state = state_select_parameters;
+      return ssl_hs_x509_lookup;
     }
   }
 
@@ -194,45 +196,116 @@
     return ssl_hs_error;
   }
 
-  if (!ssl->s3->session_reused) {
-    const SSL_CIPHER *cipher =
-        ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
-    if (cipher == NULL) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+  /* Negotiate the cipher suite. */
+  ssl->s3->tmp.new_cipher = choose_tls13_cipher(ssl, &client_hello);
+  if (ssl->s3->tmp.new_cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    return ssl_hs_error;
+  }
+
+  /* Decode the ticket if we agree on a PSK key exchange mode. */
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  SSL_SESSION *session = NULL;
+  CBS pre_shared_key, binders;
+  if (hs->accept_psk_mode &&
+      ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
+                                       TLSEXT_TYPE_pre_shared_key)) {
+    /* Verify that the pre_shared_key extension is the last extension in
+     * ClientHello. */
+    if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) !=
+        client_hello.extensions + client_hello.extensions_len) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PRE_SHARED_KEY_MUST_BE_LAST);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
-    ssl->s3->new_session->cipher = cipher;
+    if (!ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &binders,
+                                                  &alert, &pre_shared_key)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return ssl_hs_error;
+    }
   }
 
-  ssl->s3->tmp.new_cipher = ssl->s3->new_session->cipher;
-  ssl->method->received_flight(ssl);
+  if (session != NULL &&
+      !ssl_session_is_resumable(ssl, session)) {
+    SSL_SESSION_free(session);
+    session = NULL;
+  }
 
+  /* Set up the new session, either using the original one as a template or
+   * creating a fresh one. */
+  if (session == NULL) {
+    if (!ssl_get_new_session(ssl, 1 /* server */)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+
+    ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
+
+    /* On new sessions, stash the SNI value in the session. */
+    if (ssl->s3->hs->hostname != NULL) {
+      ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+      if (ssl->s3->new_session->tlsext_hostname == NULL) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        return ssl_hs_error;
+      }
+    }
+  } else {
+    /* Check the PSK binder. */
+    if (!tls13_verify_psk_binder(ssl, session, &binders)) {
+      SSL_SESSION_free(session);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      return ssl_hs_error;
+    }
+
+    /* Only authentication information carries over in TLS 1.3. */
+    ssl->s3->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
+    if (ssl->s3->new_session == NULL) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+    ssl->s3->session_reused = 1;
+    SSL_SESSION_free(session);
+  }
+
+  if (ssl->ctx->dos_protection_cb != NULL &&
+      ssl->ctx->dos_protection_cb(&client_hello) == 0) {
+    /* Connection rejected for DOS reasons. */
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return ssl_hs_error;
+  }
+
+  /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
+   * deferred. Complete it now. */
+  if (!ssl_negotiate_alpn(ssl, &alert, &client_hello)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
 
   /* The PRF hash is now known. */
   size_t hash_len =
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
 
   /* Derive resumption material. */
-  uint8_t resumption_ctx[EVP_MAX_MD_SIZE] = {0};
   uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
   if (ssl->s3->session_reused) {
-    if (!tls13_resumption_context(ssl, resumption_ctx, hash_len,
-                                  ssl->s3->new_session) ||
-        !tls13_resumption_psk(ssl, psk_secret, hash_len,
-                              ssl->s3->new_session)) {
+    if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
       return ssl_hs_error;
     }
+    memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
   }
 
   /* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
    * into the running secret. */
-  if (!tls13_init_key_schedule(ssl, resumption_ctx, hash_len) ||
+  if (!tls13_init_key_schedule(ssl) ||
       !tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
     return ssl_hs_error;
   }
 
+  ssl->method->received_flight(ssl);
+
   /* Resolve ECDHE and incorporate it into the secret. */
   int need_retry;
   if (!resolve_ecdhe_secret(ssl, &need_retry, &client_hello)) {
@@ -259,7 +332,7 @@
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
       !CBB_add_u16(&extensions, group_id) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return ssl_hs_error;
   }
@@ -298,7 +371,7 @@
     return ssl_hs_error;
   }
 
-  if (!ssl->method->hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -316,18 +389,8 @@
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !ssl_ext_pre_shared_key_add_serverhello(ssl, &extensions) ||
-      !ssl_ext_key_share_add_serverhello(ssl, &extensions)) {
-    goto err;
-  }
-
-  if (!ssl->s3->session_reused) {
-    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_signature_algorithms) ||
-        !CBB_add_u16(&extensions, 0)) {
-      goto err;
-    }
-  }
-
-  if (!ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_ext_key_share_add_serverhello(ssl, &extensions) ||
+      !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -349,7 +412,7 @@
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_ENCRYPTED_EXTENSIONS) ||
       !ssl_add_serverhello_tlsext(ssl, &body) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return ssl_hs_error;
   }
@@ -394,7 +457,7 @@
 
   if (!ssl_add_client_CA_list(ssl, &body) ||
       !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
-      !ssl->method->finish_message(ssl, &cbb)) {
+      !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
@@ -458,9 +521,9 @@
 static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
   /* Update the secret to the master secret and derive traffic keys. */
   if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
-      !tls13_derive_traffic_secret_0(ssl) ||
-      !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
-                             hs->server_traffic_secret_0, hs->hash_len)) {
+      !tls13_derive_application_secrets(ssl) ||
+      !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
+                             hs->hash_len)) {
     return ssl_hs_error;
   }
 
@@ -476,7 +539,7 @@
     ssl->s3->new_session->verify_result = X509_V_OK;
 
     /* Skip this state. */
-    hs->state = state_process_client_finished;
+    hs->state = state_process_channel_id;
     return ssl_hs_ok;
   }
 
@@ -485,14 +548,14 @@
 
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(ssl, allow_anonymous) ||
-      !ssl->method->hash_current_message(ssl)) {
+      !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
   /* For historical reasons, the server's copy of the chain does not include the
    * leaf while the client's does. */
-  if (sk_X509_num(ssl->s3->new_session->cert_chain) > 0) {
-    X509_free(sk_X509_shift(ssl->s3->new_session->cert_chain));
+  if (sk_X509_num(ssl->s3->new_session->x509_chain) > 0) {
+    X509_free(sk_X509_shift(ssl->s3->new_session->x509_chain));
   }
 
   hs->state = state_process_client_certificate_verify;
@@ -501,16 +564,32 @@
 
 static enum ssl_hs_wait_t do_process_client_certificate_verify(
     SSL *ssl, SSL_HANDSHAKE *hs) {
-  if (ssl->s3->new_session->peer == NULL) {
+  if (ssl->s3->new_session->x509_peer == NULL) {
     /* Skip this state. */
-    hs->state = state_process_client_finished;
+    hs->state = state_process_channel_id;
     return ssl_hs_ok;
   }
 
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(ssl) ||
-      !ssl->method->hash_current_message(ssl)) {
-    return 0;
+      !ssl_hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_channel_id;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!ssl->s3->tlsext_channel_id_valid) {
+    hs->state = state_process_client_finished;
+    return ssl_hs_ok;
+  }
+
+  if (!tls13_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(ssl) ||
+      !ssl_hash_current_message(ssl)) {
+    return ssl_hs_error;
   }
 
   hs->state = state_process_client_finished;
@@ -521,35 +600,49 @@
                                                      SSL_HANDSHAKE *hs) {
   if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(ssl) ||
-      !ssl->method->hash_current_message(ssl) ||
+      !ssl_hash_current_message(ssl) ||
       /* evp_aead_seal keys have already been switched. */
-      !tls13_set_traffic_key(ssl, type_data, evp_aead_open,
-                             hs->client_traffic_secret_0, hs->hash_len) ||
-      !tls13_finalize_keys(ssl)) {
+      !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
+                             hs->hash_len) ||
+      !tls13_derive_resumption_secret(ssl)) {
     return ssl_hs_error;
   }
 
   ssl->method->received_flight(ssl);
+
+  /* Refresh the session timestamp so that it is measured from ticket
+   * issuance. */
+  ssl_session_refresh_time(ssl, ssl->s3->new_session);
   hs->state = state_send_new_session_ticket;
   return ssl_hs_ok;
 }
 
+/* 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;
+
 static enum ssl_hs_wait_t do_send_new_session_ticket(SSL *ssl,
                                                      SSL_HANDSHAKE *hs) {
+  /* If the client doesn't accept resumption with PSK_DHE_KE, don't send a
+   * session ticket. */
+  if (!hs->accept_psk_mode) {
+    hs->state = state_done;
+    return ssl_hs_ok;
+  }
+
   SSL_SESSION *session = ssl->s3->new_session;
-  session->tlsext_tick_lifetime_hint = session->timeout;
+  if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
+    goto err;
+  }
 
   /* TODO(svaldez): Add support for sending 0RTT through TicketEarlyDataInfo
    * extension. */
 
-  CBB cbb, body, ke_modes, auth_modes, ticket, extensions;
+  CBB cbb, body, ticket, extensions;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_NEW_SESSION_TICKET) ||
-      !CBB_add_u32(&body, session->tlsext_tick_lifetime_hint) ||
-      !CBB_add_u8_length_prefixed(&body, &ke_modes) ||
-      !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
-      !CBB_add_u8_length_prefixed(&body, &auth_modes) ||
-      !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
+      !CBB_add_u32(&body, session->timeout) ||
+      !CBB_add_u32(&body, session->ticket_age_add) ||
       !CBB_add_u16_length_prefixed(&body, &ticket) ||
       !ssl_encrypt_ticket(ssl, &ticket, session) ||
       !CBB_add_u16_length_prefixed(&body, &extensions)) {
@@ -557,21 +650,23 @@
   }
 
   /* Add a fake extension. See draft-davidben-tls-grease-01. */
-  if (ssl->ctx->grease_enabled) {
-    if (!CBB_add_u16(&extensions,
-                     ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
-        !CBB_add_u16(&extensions, 0 /* empty */)) {
-      goto err;
-    }
+  if (!CBB_add_u16(&extensions,
+                   ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
+      !CBB_add_u16(&extensions, 0 /* empty */)) {
+    goto err;
   }
 
-  if (!ssl->method->finish_message(ssl, &cbb)) {
+  if (!ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
 
   hs->session_tickets_sent++;
+  if (hs->session_tickets_sent >= kNumTickets) {
+    hs->state = state_flush_new_session_tickets;
+  } else {
+    hs->state = state_send_new_session_ticket;
+  }
 
-  hs->state = state_flush_new_session_ticket;
   return ssl_hs_write_message;
 
 err:
@@ -579,17 +674,9 @@
   return ssl_hs_error;
 }
 
-/* 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;
-
-static enum ssl_hs_wait_t do_flush_new_session_ticket(SSL *ssl,
-                                                      SSL_HANDSHAKE *hs) {
-  if (hs->session_tickets_sent >= kNumTickets) {
-    hs->state = state_done;
-  } else {
-    hs->state = state_send_new_session_ticket;
-  }
+static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL *ssl,
+                                                       SSL_HANDSHAKE *hs) {
+  hs->state = state_done;
   return ssl_hs_flush;
 }
 
@@ -645,14 +732,17 @@
       case state_process_client_certificate_verify:
         ret = do_process_client_certificate_verify(ssl, hs);
         break;
+      case state_process_channel_id:
+        ret = do_process_channel_id(ssl, hs);
+        break;
       case state_process_client_finished:
         ret = do_process_client_finished(ssl, hs);
         break;
       case state_send_new_session_ticket:
         ret = do_send_new_session_ticket(ssl, hs);
         break;
-      case state_flush_new_session_ticket:
-        ret = do_flush_new_session_ticket(ssl, hs);
+      case state_flush_new_session_tickets:
+        ret = do_flush_new_session_tickets(ssl, hs);
         break;
       case state_done:
         ret = ssl_hs_ok;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 8bcdf8f..ce42904 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -130,7 +130,7 @@
     ssl3_new,
     ssl3_free,
     ssl3_get_message,
-    ssl3_hash_current_message,
+    ssl3_get_current_message,
     ssl3_release_current_message,
     ssl3_read_app_data,
     ssl3_read_change_cipher_spec,
@@ -140,6 +140,7 @@
     ssl3_supports_cipher,
     ssl3_init_message,
     ssl3_finish_message,
+    ssl3_queue_message,
     ssl3_write_message,
     ssl3_send_change_cipher_spec,
     ssl3_expect_flight,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 7041ce3..5931922 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -174,7 +174,7 @@
   }
 }
 
-size_t ssl_max_seal_overhead(const SSL *ssl) {
+size_t SSL_max_seal_overhead(const SSL *ssl) {
   size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   if (SSL_is_dtls(ssl)) {
     ret += DTLS1_RT_HEADER_LENGTH;
@@ -210,9 +210,20 @@
     return ssl_open_record_partial;
   }
 
-  /* Check that the major version in the record matches. As of TLS 1.3, the
-   * minor version is no longer checked. */
-  if ((version >> 8) != SSL3_VERSION_MAJOR) {
+  int version_ok;
+  if (ssl->s3->aead_read_ctx == NULL) {
+    /* Only check the first byte. Enforcing beyond that can prevent decoding
+     * version negotiation failure alerts. */
+    version_ok = (version >> 8) == SSL3_VERSION_MAJOR;
+  } else if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+    /* Earlier versions of TLS switch the record version. */
+    version_ok = version == ssl->version;
+  } else {
+    /* Starting TLS 1.3, the version field is frozen at {3, 1}. */
+    version_ok = version == TLS1_VERSION;
+  }
+
+  if (!version_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     *out_alert = SSL_AD_PROTOCOL_VERSION;
     return ssl_open_record_error;