external/boringssl: Sync to 14308731e5446a73ac2258688a9688b524483cb6.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/ee7aa02744a78bf4630913b1c83d0fe36aa45efc..14308731e5446a73ac2258688a9688b524483cb6

Test: BoringSSL CTS Presubmits.
Change-Id: I73bf80fa018c2a65ca9842f1c2f95d64586bdffc
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 0c09443..b6f4451 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -3,42 +3,41 @@
 add_library(
   ssl
 
-  bio_ssl.c
-  custom_extensions.c
-  d1_both.c
-  d1_lib.c
-  d1_pkt.c
-  d1_srtp.c
-  dtls_method.c
-  dtls_record.c
-  handshake_client.c
-  handshake_server.c
-  s3_both.c
-  s3_lib.c
-  s3_pkt.c
-  ssl_aead_ctx.c
-  ssl_asn1.c
-  ssl_buffer.c
-  ssl_cert.c
-  ssl_cipher.c
-  ssl_ecdh.c
-  ssl_file.c
-  ssl_lib.c
-  ssl_privkey.c
-  ssl_privkey_cc.cc
-  ssl_session.c
-  ssl_stat.c
-  ssl_transcript.c
-  ssl_versions.c
-  ssl_x509.c
-  t1_enc.c
-  t1_lib.c
-  tls_method.c
-  tls_record.c
-  tls13_both.c
-  tls13_client.c
-  tls13_enc.c
-  tls13_server.c
+  bio_ssl.cc
+  custom_extensions.cc
+  d1_both.cc
+  d1_lib.cc
+  d1_pkt.cc
+  d1_srtp.cc
+  dtls_method.cc
+  dtls_record.cc
+  handshake_client.cc
+  handshake_server.cc
+  s3_both.cc
+  s3_lib.cc
+  s3_pkt.cc
+  ssl_aead_ctx.cc
+  ssl_asn1.cc
+  ssl_buffer.cc
+  ssl_cert.cc
+  ssl_cipher.cc
+  ssl_ecdh.cc
+  ssl_file.cc
+  ssl_lib.cc
+  ssl_privkey.cc
+  ssl_session.cc
+  ssl_stat.cc
+  ssl_transcript.cc
+  ssl_versions.cc
+  ssl_x509.cc
+  t1_enc.cc
+  t1_lib.cc
+  tls_method.cc
+  tls_record.cc
+  tls13_both.cc
+  tls13_client.cc
+  tls13_enc.cc
+  tls13_server.cc
 )
 
 target_link_libraries(ssl crypto)
diff --git a/src/ssl/bio_ssl.c b/src/ssl/bio_ssl.cc
similarity index 93%
rename from src/ssl/bio_ssl.c
rename to src/ssl/bio_ssl.cc
index ad8f5d8..61afee5 100644
--- a/src/ssl/bio_ssl.c
+++ b/src/ssl/bio_ssl.cc
@@ -12,8 +12,12 @@
 #include <openssl/bio.h>
 
 
+static SSL *get_ssl(BIO *bio) {
+  return reinterpret_cast<SSL *>(bio->ptr);
+}
+
 static int ssl_read(BIO *bio, char *out, int outl) {
-  SSL *ssl = bio->ptr;
+  SSL *ssl = get_ssl(bio);
   if (ssl == NULL) {
     return 0;
   }
@@ -53,7 +57,7 @@
 }
 
 static int ssl_write(BIO *bio, const char *out, int outl) {
-  SSL *ssl = bio->ptr;
+  SSL *ssl = get_ssl(bio);
   if (ssl == NULL) {
     return 0;
   }
@@ -87,7 +91,7 @@
 }
 
 static long ssl_ctrl(BIO *bio, int cmd, long num, void *ptr) {
-  SSL *ssl = bio->ptr;
+  SSL *ssl = get_ssl(bio);
   if (ssl == NULL && cmd != BIO_C_SET_SSL) {
     return 0;
   }
@@ -134,7 +138,7 @@
 }
 
 static int ssl_free(BIO *bio) {
-  SSL *ssl = bio->ptr;
+  SSL *ssl = get_ssl(bio);
 
   if (ssl == NULL) {
     return 1;
@@ -149,7 +153,7 @@
 }
 
 static long ssl_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
-  SSL *ssl = bio->ptr;
+  SSL *ssl = get_ssl(bio);
   if (ssl == NULL) {
     return 0;
   }
diff --git a/src/ssl/custom_extensions.c b/src/ssl/custom_extensions.cc
similarity index 98%
rename from src/ssl/custom_extensions.c
rename to src/ssl/custom_extensions.cc
index ac18517..f438f73 100644
--- a/src/ssl/custom_extensions.c
+++ b/src/ssl/custom_extensions.cc
@@ -214,7 +214,8 @@
     return 0;
   }
 
-  SSL_CUSTOM_EXTENSION *ext = OPENSSL_malloc(sizeof(SSL_CUSTOM_EXTENSION));
+  SSL_CUSTOM_EXTENSION *ext =
+      (SSL_CUSTOM_EXTENSION *)OPENSSL_malloc(sizeof(SSL_CUSTOM_EXTENSION));
   if (ext == NULL) {
     return 0;
   }
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.cc
similarity index 97%
rename from src/ssl/d1_both.c
rename to src/ssl/d1_both.cc
index 44e3f2e..ee0ec4f 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.cc
@@ -122,7 +122,6 @@
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
-#include <openssl/type_check.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
@@ -153,7 +152,7 @@
 }
 
 static hm_fragment *dtls1_hm_fragment_new(const struct hm_header_st *msg_hdr) {
-  hm_fragment *frag = OPENSSL_malloc(sizeof(hm_fragment));
+  hm_fragment *frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment));
   if (frag == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -164,7 +163,8 @@
   frag->msg_len = msg_hdr->msg_len;
 
   /* Allocate space for the reassembled message and fill in the header. */
-  frag->data = OPENSSL_malloc(DTLS1_HM_HEADER_LENGTH + msg_hdr->msg_len);
+  frag->data =
+      (uint8_t *)OPENSSL_malloc(DTLS1_HM_HEADER_LENGTH + msg_hdr->msg_len);
   if (frag->data == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
@@ -191,7 +191,7 @@
       goto err;
     }
     size_t bitmask_len = (msg_hdr->msg_len + 7) / 8;
-    frag->reassembly = OPENSSL_malloc(bitmask_len);
+    frag->reassembly = (uint8_t *)OPENSSL_malloc(bitmask_len);
     if (frag->reassembly == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
@@ -537,9 +537,9 @@
  * it takes ownership of |data| and releases it with |OPENSSL_free| when
  * done. */
 static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) {
-  OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT <
-                             (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
-                         outgoing_messages_len_is_too_small);
+  static_assert(SSL_MAX_HANDSHAKE_FLIGHT <
+                    (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
+                "outgoing_messages_len is too small");
   if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
     assert(0);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -760,7 +760,7 @@
   dtls1_update_mtu(ssl);
 
   int ret = -1;
-  uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu);
+  uint8_t *packet = (uint8_t *)OPENSSL_malloc(ssl->d1->mtu);
   if (packet == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.cc
similarity index 98%
rename from src/ssl/d1_lib.c
rename to src/ssl/d1_lib.cc
index ef15252..0074855 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.cc
@@ -78,12 +78,10 @@
 #define DTLS1_MAX_TIMEOUTS                     12
 
 int dtls1_new(SSL *ssl) {
-  DTLS1_STATE *d1;
-
   if (!ssl3_new(ssl)) {
     return 0;
   }
-  d1 = OPENSSL_malloc(sizeof *d1);
+  DTLS1_STATE *d1 = (DTLS1_STATE *)OPENSSL_malloc(sizeof *d1);
   if (d1 == NULL) {
     ssl3_free(ssl);
     return 0;
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.cc
similarity index 99%
rename from src/ssl/d1_pkt.c
rename to src/ssl/d1_pkt.cc
index e2c7315..1ae55eb 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.cc
@@ -171,7 +171,7 @@
       /* Impossible in DTLS. */
       break;
 
-    case ssl_open_record_success:
+    case ssl_open_record_success: {
       if (CBS_len(&body) > 0xffff) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
         return -1;
@@ -182,6 +182,7 @@
       rr->length = (uint16_t)CBS_len(&body);
       rr->data = (uint8_t *)CBS_data(&body);
       return 1;
+    }
 
     case ssl_open_record_discard:
       goto again;
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.cc
similarity index 100%
rename from src/ssl/d1_srtp.c
rename to src/ssl/d1_srtp.cc
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.cc
similarity index 100%
rename from src/ssl/dtls_method.c
rename to src/ssl/dtls_method.cc
diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.cc
similarity index 100%
rename from src/ssl/dtls_record.c
rename to src/ssl/dtls_record.cc
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.cc
similarity index 93%
rename from src/ssl/handshake_client.c
rename to src/ssl/handshake_client.cc
index c772f77..9efbf0a 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.cc
@@ -647,29 +647,43 @@
 
 int ssl_write_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO)) {
-    goto err;
+  bssl::ScopedCBB cbb;
+  CBB body;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO)) {
+    return 0;
   }
 
   /* Renegotiations do not participate in session resumption. */
-  int has_session = ssl->session != NULL &&
-                    !ssl->s3->initial_handshake_complete;
+  int has_session_id = ssl->session != NULL &&
+                       !ssl->s3->initial_handshake_complete &&
+                       ssl->session->session_id_length > 0;
 
   CBB child;
   if (!CBB_add_u16(&body, hs->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))) {
-    goto err;
+      !CBB_add_u8_length_prefixed(&body, &child)) {
+    return 0;
+  }
+
+  if (has_session_id) {
+    if (!CBB_add_bytes(&child, ssl->session->session_id,
+                       ssl->session->session_id_length)) {
+      return 0;
+    }
+  } else {
+    /* In TLS 1.3 experimental encodings, send a fake placeholder session ID
+     * when we do not otherwise have one to send. */
+    if (hs->max_version >= TLS1_3_VERSION &&
+        ssl->tls13_variant == tls13_experiment &&
+        !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
+      return 0;
+    }
   }
 
   if (SSL_is_dtls(ssl)) {
     if (!CBB_add_u8_length_prefixed(&body, &child) ||
         !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
-      goto err;
+      return 0;
     }
   }
 
@@ -679,13 +693,13 @@
       !CBB_add_u8(&body, 1 /* one compression method */) ||
       !CBB_add_u8(&body, 0 /* null compression */) ||
       !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) {
-    goto err;
+    return 0;
   }
 
   uint8_t *msg = NULL;
   size_t len;
-  if (!ssl->method->finish_message(ssl, &cbb, &msg, &len)) {
-    goto err;
+  if (!ssl->method->finish_message(ssl, cbb.get(), &msg, &len)) {
+    return 0;
   }
 
   /* Now that the length prefixes have been computed, fill in the placeholder
@@ -693,14 +707,10 @@
   if (hs->needs_psk_binder &&
       !tls13_write_psk_binder(hs, msg, len)) {
     OPENSSL_free(msg);
-    goto err;
+    return 0;
   }
 
   return ssl->method->add_message(ssl, msg, len);
-
- err:
-  CBB_cleanup(&cbb);
-  return 0;
 }
 
 static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) {
@@ -748,6 +758,14 @@
     return -1;
   }
 
+  /* Initialize a random session ID for the experimental TLS 1.3 variant. */
+  if (ssl->tls13_variant == tls13_experiment) {
+    hs->session_id_len = sizeof(hs->session_id);
+    if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
+      return -1;
+    }
+  }
+
   if (!ssl_write_client_hello(hs)) {
     return -1;
   }
@@ -788,12 +806,80 @@
   return 1;
 }
 
+static int parse_server_version(SSL_HANDSHAKE *hs, uint16_t *out) {
+  SSL *const ssl = hs->ssl;
+  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO &&
+      ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return 0;
+  }
+
+  CBS server_hello;
+  CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u16(&server_hello, out)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return 0;
+  }
+
+  /* The server version may also be in the supported_versions extension if
+   * applicable. */
+  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO ||
+      *out != TLS1_2_VERSION) {
+    return 1;
+  }
+
+  uint8_t sid_length;
+  if (!CBS_skip(&server_hello, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8(&server_hello, &sid_length) ||
+      !CBS_skip(&server_hello, sid_length + 2 /* cipher_suite */ +
+                1 /* compression_method */)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return 0;
+  }
+
+  /* The extensions block may not be present. */
+  if (CBS_len(&server_hello) == 0) {
+    return 1;
+  }
+
+  CBS extensions;
+  if (!CBS_get_u16_length_prefixed(&server_hello, &extensions) ||
+      CBS_len(&server_hello) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return 0;
+  }
+
+  int have_supported_versions;
+  CBS supported_versions;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+    {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+     &supported_versions},
+  };
+
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                            OPENSSL_ARRAY_SIZE(ext_types),
+                            1 /* ignore unknown */)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return 0;
+  }
+
+  if (have_supported_versions &&
+      (!CBS_get_u16(&supported_versions, out) ||
+       CBS_len(&supported_versions) != 0)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return 0;
+  }
+
+  return 1;
+}
+
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBS server_hello, server_random, session_id;
-  uint16_t server_version, cipher_suite;
-  uint8_t compression_method;
-
   int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     uint32_t err = ERR_peek_error();
@@ -810,18 +896,8 @@
     return ret;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO &&
-      ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    return -1;
-  }
-
-  CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
-
-  if (!CBS_get_u16(&server_hello, &server_version)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  uint16_t server_version;
+  if (!parse_server_version(hs, &server_version)) {
     return -1;
   }
 
@@ -861,7 +937,12 @@
     return -1;
   }
 
-  if (!CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
+  CBS server_hello, server_random, session_id;
+  uint16_t cipher_suite;
+  uint8_t compression_method;
+  CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
+  if (!CBS_skip(&server_hello, 2 /* version */) ||
+      !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
       !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
       CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
       !CBS_get_u16(&server_hello, &cipher_suite) ||
@@ -1431,19 +1512,20 @@
   return 1;
 }
 
-OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
-                       SIZE_T_IS_SMALLER_THAN_UNSIGNED);
+static_assert(sizeof(size_t) >= sizeof(unsigned),
+              "size_t is smaller than unsigned");
 
 static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint8_t *pms = NULL;
-  size_t pms_len = 0;
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
+  bssl::ScopedCBB cbb;
+  CBB body;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                  SSL3_MT_CLIENT_KEY_EXCHANGE)) {
-    goto err;
+    return -1;
   }
 
+  uint8_t *pms = NULL;
+  size_t pms_len = 0;
   uint32_t alg_k = hs->new_cipher->algorithm_mkey;
   uint32_t alg_a = hs->new_cipher->algorithm_auth;
 
@@ -1488,7 +1570,7 @@
   /* Depending on the key exchange method, compute |pms| and |pms_len|. */
   if (alg_k & SSL_kRSA) {
     pms_len = SSL_MAX_MASTER_KEY_LENGTH;
-    pms = OPENSSL_malloc(pms_len);
+    pms = (uint8_t *)OPENSSL_malloc(pms_len);
     if (pms == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
@@ -1551,7 +1633,7 @@
     /* For plain PSK, other_secret is a block of 0s with the same length as
      * the pre-shared key. */
     pms_len = psk_len;
-    pms = OPENSSL_malloc(pms_len);
+    pms = (uint8_t *)OPENSSL_malloc(pms_len);
     if (pms == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
@@ -1589,7 +1671,7 @@
 
   /* The message must be added to the finished hash before calculating the
    * master secret. */
-  if (!ssl_add_message_cbb(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, cbb.get())) {
     goto err;
   }
 
@@ -1605,7 +1687,6 @@
   return 1;
 
 err:
-  CBB_cleanup(&cbb);
   if (pms != NULL) {
     OPENSSL_cleanse(pms, pms_len);
     OPENSSL_free(pms);
@@ -1617,21 +1698,22 @@
   SSL *const ssl = hs->ssl;
   assert(ssl_has_private_key(ssl));
 
-  CBB cbb, body, child;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
+  bssl::ScopedCBB cbb;
+  CBB body, child;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                  SSL3_MT_CERTIFICATE_VERIFY)) {
-    goto err;
+    return -1;
   }
 
   uint16_t signature_algorithm;
   if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
-    goto err;
+    return -1;
   }
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     /* Write out the digest type in TLS 1.2. */
     if (!CBB_add_u16(&body, signature_algorithm)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
+      return -1;
     }
   }
 
@@ -1640,7 +1722,7 @@
   uint8_t *ptr;
   if (!CBB_add_u16_length_prefixed(&body, &child) ||
       !CBB_reserve(&child, &ptr, max_sig_len)) {
-    goto err;
+    return -1;
   }
 
   size_t sig_len = max_sig_len;
@@ -1649,7 +1731,7 @@
   if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
     if (ssl->cert->key_method != NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
-      goto err;
+      return -1;
     }
 
     uint8_t digest[EVP_MAX_MD_SIZE];
@@ -1657,7 +1739,7 @@
     if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest,
                                               &digest_len, hs->new_session,
                                               signature_algorithm)) {
-      goto err;
+      return -1;
     }
 
     EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL);
@@ -1666,7 +1748,7 @@
              EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len);
     EVP_PKEY_CTX_free(pctx);
     if (!ok) {
-      goto err;
+      return -1;
     }
   } else {
     switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
@@ -1676,25 +1758,21 @@
       case ssl_private_key_success:
         break;
       case ssl_private_key_failure:
-        goto err;
+        return -1;
       case ssl_private_key_retry:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
-        goto err;
+        return -1;
     }
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_add_message_cbb(ssl, &cbb)) {
-    goto err;
+      !ssl_add_message_cbb(ssl, cbb.get())) {
+    return -1;
   }
 
   /* The handshake buffer is no longer necessary. */
   SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   return 1;
-
-err:
-  CBB_cleanup(&cbb);
-  return -1;
 }
 
 static int ssl3_send_next_proto(SSL_HANDSHAKE *hs) {
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.cc
similarity index 96%
rename from src/ssl/handshake_server.c
rename to src/ssl/handshake_server.cc
index 64abd5d..ee5358c 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.cc
@@ -800,9 +800,6 @@
 
 static int ssl3_select_parameters(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = -1;
-  SSL_SESSION *session = NULL;
-
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
                              ssl->init_num)) {
@@ -811,49 +808,52 @@
 
   /* 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)) {
+  /* TODO(davidben): Switch |ssl_get_prev_session| to take a |bssl::UniquePtr|
+   * output and simplify this. */
+  SSL_SESSION *session_raw = nullptr;
+  auto session_ret = ssl_get_prev_session(ssl, &session_raw, &tickets_supported,
+                                          &renew_ticket, &client_hello);
+  bssl::UniquePtr<SSL_SESSION> session(session_raw);
+  switch (session_ret) {
     case ssl_session_success:
       break;
     case ssl_session_error:
-      goto err;
+      return -1;
     case ssl_session_retry:
       ssl->rwstate = SSL_PENDING_SESSION;
-      goto err;
+      return -1;
     case ssl_session_ticket_retry:
       ssl->rwstate = SSL_PENDING_TICKET;
-      goto err;
+      return -1;
   }
 
-  if (session != NULL) {
+  if (session) {
     if (session->extended_master_secret && !hs->extended_master_secret) {
       /* A ClientHello without EMS that attempts to resume a session with EMS
        * is fatal to the connection. */
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-      goto err;
+      return -1;
     }
 
-    if (!ssl_session_is_resumable(hs, session) ||
+    if (!ssl_session_is_resumable(hs, session.get()) ||
         /* If the client offers the EMS extension, but the previous session
          * didn't use it, then negotiate a new session. */
         hs->extended_master_secret != session->extended_master_secret) {
-      SSL_SESSION_free(session);
-      session = NULL;
+      session.reset();
     }
   }
 
-  if (session != NULL) {
+  if (session) {
     /* Use the old session. */
     hs->ticket_expected = renew_ticket;
-    ssl->session = session;
-    session = NULL;
+    ssl->session = session.release();
     ssl->s3->session_reused = 1;
   } else {
     hs->ticket_expected = tickets_supported;
     ssl_set_session(ssl, NULL);
     if (!ssl_get_new_session(hs, 1 /* server */)) {
-      goto err;
+      return -1;
     }
 
     /* Clear the session ID if we want the session to be single-use. */
@@ -867,7 +867,7 @@
     /* Connection rejected for DOS reasons. */
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    goto err;
+    return -1;
   }
 
   if (ssl->session == NULL) {
@@ -879,7 +879,7 @@
       hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
       if (hs->new_session->tlsext_hostname == NULL) {
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        goto err;
+        return -1;
       }
     }
 
@@ -907,7 +907,7 @@
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-    goto err;
+    return -1;
   }
 
   /* Now that all parameters are known, initialize the handshake hash and hash
@@ -916,7 +916,7 @@
                                 hs->new_cipher->algorithm_prf) ||
       !ssl_hash_current_message(hs)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    goto err;
+    return -1;
   }
 
   /* Release the handshake buffer if client authentication isn't required. */
@@ -924,11 +924,7 @@
     SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   }
 
-  ret = 1;
-
-err:
-  SSL_SESSION_free(session);
-  return ret;
+  return 1;
 }
 
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs) {
@@ -988,32 +984,30 @@
 
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = -1;
-  CBB cbb;
-  CBB_zero(&cbb);
+  bssl::ScopedCBB cbb;
 
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
     if (!ssl_has_certificate(ssl)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-      goto err;
+      return -1;
     }
 
     if (!ssl3_output_cert_chain(ssl)) {
-      goto err;
+      return -1;
     }
 
     if (hs->certificate_status_expected) {
       CBB body, ocsp_response;
-      if (!ssl->method->init_message(ssl, &cbb, &body,
+      if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                      SSL3_MT_CERTIFICATE_STATUS) ||
           !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
           !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
           !CBB_add_bytes(&ocsp_response,
                          CRYPTO_BUFFER_data(ssl->cert->ocsp_response),
                          CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) ||
-          !ssl_add_message_cbb(ssl, &cbb)) {
+          !ssl_add_message_cbb(ssl, cbb.get())) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
+        return -1;
       }
     }
   }
@@ -1027,20 +1021,20 @@
     /* Pre-allocate enough room to comfortably fit an ECDHE public key. Prepend
      * the client and server randoms for the signing transcript. */
     CBB child;
-    if (!CBB_init(&cbb, SSL3_RANDOM_SIZE * 2 + 128) ||
-        !CBB_add_bytes(&cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
-        !CBB_add_bytes(&cbb, ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
-      goto err;
+    if (!CBB_init(cbb.get(), SSL3_RANDOM_SIZE * 2 + 128) ||
+        !CBB_add_bytes(cbb.get(), ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+        !CBB_add_bytes(cbb.get(), ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
+      return -1;
     }
 
     /* PSK ciphers begin with an identity hint. */
     if (alg_a & SSL_aPSK) {
       size_t len =
           (ssl->psk_identity_hint == NULL) ? 0 : strlen(ssl->psk_identity_hint);
-      if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+      if (!CBB_add_u16_length_prefixed(cbb.get(), &child) ||
           !CBB_add_bytes(&child, (const uint8_t *)ssl->psk_identity_hint,
                          len)) {
-        goto err;
+        return -1;
       }
     }
 
@@ -1050,32 +1044,28 @@
       if (!tls1_get_shared_group(hs, &group_id)) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-        goto err;
-      }
+        return -1;
+       }
       hs->new_session->group_id = group_id;
 
       /* Set up ECDH, generate a key, and emit the public half. */
       if (!SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) ||
-          !CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
-          !CBB_add_u16(&cbb, group_id) ||
-          !CBB_add_u8_length_prefixed(&cbb, &child) ||
+          !CBB_add_u8(cbb.get(), NAMED_CURVE_TYPE) ||
+          !CBB_add_u16(cbb.get(), group_id) ||
+          !CBB_add_u8_length_prefixed(cbb.get(), &child) ||
           !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) {
-        goto err;
+        return -1;
       }
     } else {
       assert(alg_k & SSL_kPSK);
     }
 
-    if (!CBB_finish(&cbb, &hs->server_params, &hs->server_params_len)) {
-      goto err;
+    if (!CBB_finish(cbb.get(), &hs->server_params, &hs->server_params_len)) {
+      return -1;
     }
   }
 
-  ret = 1;
-
-err:
-  CBB_cleanup(&cbb);
-  return ret;
+  return 1;
 }
 
 static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
@@ -1348,7 +1338,7 @@
 
     /* Allocate a buffer large enough for an RSA decryption. */
     const size_t rsa_size = EVP_PKEY_size(hs->local_pubkey);
-    decrypt_buf = OPENSSL_malloc(rsa_size);
+    decrypt_buf = (uint8_t *)OPENSSL_malloc(rsa_size);
     if (decrypt_buf == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
@@ -1379,7 +1369,7 @@
     /* Prepare a random premaster, to be used on invalid padding. See RFC 5246,
      * section 7.4.7.1. */
     premaster_secret_len = SSL_MAX_MASTER_KEY_LENGTH;
-    premaster_secret = OPENSSL_malloc(premaster_secret_len);
+    premaster_secret = (uint8_t *)OPENSSL_malloc(premaster_secret_len);
     if (premaster_secret == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
@@ -1476,7 +1466,7 @@
       /* In plain PSK, other_secret is a block of 0s with the same length as the
        * pre-shared key. */
       premaster_secret_len = psk_len;
-      premaster_secret = OPENSSL_malloc(premaster_secret_len);
+      premaster_secret = (uint8_t *)OPENSSL_malloc(premaster_secret_len);
       if (premaster_secret == NULL) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
         goto err;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index f6cea7a..6b88070 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -217,38 +217,38 @@
 /* Cipher suites. */
 
 /* Bits for |algorithm_mkey| (key exchange algorithm). */
-#define SSL_kRSA 0x00000001L
-#define SSL_kECDHE 0x00000002L
+#define SSL_kRSA 0x00000001u
+#define SSL_kECDHE 0x00000002u
 /* SSL_kPSK is only set for plain PSK, not ECDHE_PSK. */
-#define SSL_kPSK 0x00000004L
-#define SSL_kGENERIC 0x00000008L
+#define SSL_kPSK 0x00000004u
+#define SSL_kGENERIC 0x00000008u
 
 /* Bits for |algorithm_auth| (server authentication). */
-#define SSL_aRSA 0x00000001L
-#define SSL_aECDSA 0x00000002L
+#define SSL_aRSA 0x00000001u
+#define SSL_aECDSA 0x00000002u
 /* SSL_aPSK is set for both PSK and ECDHE_PSK. */
-#define SSL_aPSK 0x00000004L
-#define SSL_aGENERIC 0x00000008L
+#define SSL_aPSK 0x00000004u
+#define SSL_aGENERIC 0x00000008u
 
 #define SSL_aCERT (SSL_aRSA | SSL_aECDSA)
 
 /* Bits for |algorithm_enc| (symmetric encryption). */
-#define SSL_3DES                 0x00000001L
-#define SSL_AES128               0x00000002L
-#define SSL_AES256               0x00000004L
-#define SSL_AES128GCM            0x00000008L
-#define SSL_AES256GCM            0x00000010L
-#define SSL_eNULL                0x00000020L
-#define SSL_CHACHA20POLY1305     0x00000040L
+#define SSL_3DES                 0x00000001u
+#define SSL_AES128               0x00000002u
+#define SSL_AES256               0x00000004u
+#define SSL_AES128GCM            0x00000008u
+#define SSL_AES256GCM            0x00000010u
+#define SSL_eNULL                0x00000020u
+#define SSL_CHACHA20POLY1305     0x00000040u
 
 #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
 
 /* Bits for |algorithm_mac| (symmetric authentication). */
-#define SSL_SHA1 0x00000001L
-#define SSL_SHA256 0x00000002L
-#define SSL_SHA384 0x00000004L
+#define SSL_SHA1 0x00000001u
+#define SSL_SHA256 0x00000002u
+#define SSL_SHA384 0x00000004u
 /* SSL_AEAD is set for all AEADs. */
-#define SSL_AEAD 0x00000008L
+#define SSL_AEAD 0x00000008u
 
 /* Bits for |algorithm_prf| (handshake digest). */
 #define SSL_HANDSHAKE_MAC_DEFAULT 0x1
@@ -447,6 +447,13 @@
  * |SSL_AEAD_CTX_seal|. |ctx| may be NULL to denote the null cipher. */
 size_t SSL_AEAD_CTX_max_overhead(const SSL_AEAD_CTX *ctx);
 
+/* SSL_AEAD_CTX_max_suffix_len returns the maximum suffix length written by
+ * |SSL_AEAD_CTX_seal_scatter|. |ctx| may be NULL to denote the null cipher.
+ * |extra_in_len| should equal the argument of the same name passed to
+ * |SSL_AEAD_CTX_seal_scatter|. */
+size_t SSL_AEAD_CTX_max_suffix_len(const SSL_AEAD_CTX *ctx,
+                                   size_t extra_in_len);
+
 /* SSL_AEAD_CTX_open authenticates and decrypts |in_len| bytes from |in|
  * in-place. On success, it sets |*out| to the plaintext in |in| and returns
  * one. Otherwise, it returns zero. |ctx| may be NULL to denote the null cipher.
@@ -465,6 +472,31 @@
                       const uint8_t seqnum[8], const uint8_t *in,
                       size_t in_len);
 
+/* SSL_AEAD_CTX_seal_scatter encrypts and authenticates |in_len| bytes from |in|
+ * and splits the result between |out_prefix|, |out| and |out_suffix|. It
+ * returns one on success and zero on error. |ctx| may be NULL to denote the
+ * null cipher.
+ *
+ * On successful return, exactly |SSL_AEAD_CTX_explicit_nonce_len| bytes are
+ * written to |out_prefix|, |in_len| bytes to |out|, and up to
+ * |SSL_AEAD_CTX_max_suffix_len| bytes to |out_suffix|. |*out_suffix_len| is set
+ * to the actual number of bytes written to |out_suffix|.
+ *
+ * |extra_in| may point to an additional plaintext buffer. If present,
+ * |extra_in_len| additional bytes are encrypted and authenticated, and the
+ * ciphertext is written to the beginning of |out_suffix|.
+ * |SSL_AEAD_CTX_max_suffix_len| may be used to size |out_suffix| accordingly.
+ *
+ * If |in| and |out| alias then |out| must be == |in|. Other arguments may not
+ * alias anything. */
+int SSL_AEAD_CTX_seal_scatter(SSL_AEAD_CTX *aead, uint8_t *out_prefix,
+                              uint8_t *out, uint8_t *out_suffix,
+                              size_t *out_suffix_len, size_t max_out_suffix_len,
+                              uint8_t type, uint16_t wire_version,
+                              const uint8_t seqnum[8], const uint8_t *in,
+                              size_t in_len, const uint8_t *extra_in,
+                              size_t extra_in_len);
+
 
 /* DTLS replay bitmap. */
 
@@ -974,6 +1006,7 @@
   ssl_hs_pending_ticket,
   ssl_hs_early_data_rejected,
   ssl_hs_read_end_of_early_data,
+  ssl_hs_read_change_cipher_spec,
 };
 
 struct ssl_handshake_st {
@@ -1007,6 +1040,11 @@
    * |SSL_OP_NO_*| and |SSL_CTX_set_max_proto_version| APIs. */
   uint16_t max_version;
 
+  /* session_id is the session ID in the ClientHello, used for the experimental
+   * TLS 1.3 variant. */
+  uint8_t session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
+  uint8_t session_id_len;
+
   size_t hash_len;
   uint8_t secret[EVP_MAX_MD_SIZE];
   uint8_t early_traffic_secret[EVP_MAX_MD_SIZE];
@@ -1896,6 +1934,10 @@
    * further constrainted by |SSL_OP_NO_*|. */
   uint16_t conf_min_version;
 
+  /* tls13_variant is the variant of TLS 1.3 we are using for this
+   * configuration. */
+  enum tls13_variant_t tls13_variant;
+
   uint16_t max_send_fragment;
 
   /* There are 2 BIO's even though they are normally both the same. This is so
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.cc
similarity index 98%
rename from src/ssl/s3_both.c
rename to src/ssl/s3_both.cc
index 65d438a..79f71fa 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.cc
@@ -131,7 +131,7 @@
 
 
 SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) {
-  SSL_HANDSHAKE *hs = OPENSSL_malloc(sizeof(SSL_HANDSHAKE));
+  SSL_HANDSHAKE *hs = (SSL_HANDSHAKE *)OPENSSL_malloc(sizeof(SSL_HANDSHAKE));
   if (hs == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -266,7 +266,15 @@
       todo = ssl->max_send_fragment;
     }
 
-    if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) {
+    uint8_t type = SSL3_RT_HANDSHAKE;
+    if (ssl->server &&
+        ssl->s3->have_version &&
+        ssl->version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION &&
+        ssl->s3->aead_write_ctx == NULL) {
+      type = SSL3_RT_PLAINTEXT_HANDSHAKE;
+    }
+
+    if (!add_record_to_flight(ssl, type, msg + added, todo)) {
       goto err;
     }
     added += todo;
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.cc
similarity index 98%
rename from src/ssl/s3_lib.c
rename to src/ssl/s3_lib.cc
index ac8bb67..9548bbd 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.cc
@@ -163,9 +163,7 @@
 
 
 int ssl3_new(SSL *ssl) {
-  SSL3_STATE *s3;
-
-  s3 = OPENSSL_malloc(sizeof *s3);
+  SSL3_STATE *s3 = (SSL3_STATE *)OPENSSL_malloc(sizeof *s3);
   if (s3 == NULL) {
     return 0;
   }
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.cc
similarity index 97%
rename from src/ssl/s3_pkt.c
rename to src/ssl/s3_pkt.cc
index 445f882..4ae2e34 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.cc
@@ -157,7 +157,7 @@
       goto again;
     }
 
-    case ssl_open_record_success:
+    case ssl_open_record_success: {
       if (CBS_len(&body) > 0xffff) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
         return -1;
@@ -168,6 +168,7 @@
       rr->length = (uint16_t)CBS_len(&body);
       rr->data = (uint8_t *)CBS_data(&body);
       return 1;
+    }
 
     case ssl_open_record_discard:
       goto again;
@@ -522,7 +523,13 @@
       return -1;
     }
 
-    if (rr->type != SSL3_RT_HANDSHAKE) {
+    /* Accept server_plaintext_handshake records when the content type TLS 1.3
+     * variant is enabled. */
+    if (rr->type != SSL3_RT_HANDSHAKE &&
+        !(!ssl->server &&
+          ssl->tls13_variant == tls13_record_type_experiment &&
+          ssl->s3->aead_read_ctx == NULL &&
+          rr->type == SSL3_RT_PLAINTEXT_HANDSHAKE)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return -1;
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.cc
similarity index 79%
rename from src/ssl/ssl_aead_ctx.c
rename to src/ssl/ssl_aead_ctx.cc
index 1b9dcd2..5264a65 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -20,7 +20,6 @@
 #include <openssl/aead.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
-#include <openssl/type_check.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
@@ -61,7 +60,7 @@
     enc_key_len += fixed_iv_len;
   }
 
-  SSL_AEAD_CTX *aead_ctx = OPENSSL_malloc(sizeof(SSL_AEAD_CTX));
+  SSL_AEAD_CTX *aead_ctx = (SSL_AEAD_CTX *)OPENSSL_malloc(sizeof(SSL_AEAD_CTX));
   if (aead_ctx == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -78,8 +77,8 @@
   }
 
   assert(EVP_AEAD_nonce_length(aead) <= EVP_AEAD_MAX_NONCE_LENGTH);
-  OPENSSL_COMPILE_ASSERT(EVP_AEAD_MAX_NONCE_LENGTH < 256,
-                         variable_nonce_len_doesnt_fit_in_uint8_t);
+  static_assert(EVP_AEAD_MAX_NONCE_LENGTH < 256,
+                "variable_nonce_len doesn't fit in uint8_t");
   aead_ctx->variable_nonce_len = (uint8_t)EVP_AEAD_nonce_length(aead);
   if (mac_key_len == 0) {
     assert(fixed_iv_len <= sizeof(aead_ctx->fixed_nonce));
@@ -140,16 +139,19 @@
   return 0;
 }
 
-size_t SSL_AEAD_CTX_max_overhead(const SSL_AEAD_CTX *aead) {
+size_t SSL_AEAD_CTX_max_suffix_len(const SSL_AEAD_CTX *aead,
+                                   size_t extra_in_len) {
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   aead = NULL;
 #endif
 
-  if (aead == NULL) {
-    return 0;
-  }
-  return EVP_AEAD_max_overhead(aead->ctx.aead) +
-         SSL_AEAD_CTX_explicit_nonce_len(aead);
+  return extra_in_len +
+         (aead == NULL ? 0 : EVP_AEAD_max_overhead(aead->ctx.aead));
+}
+
+size_t SSL_AEAD_CTX_max_overhead(const SSL_AEAD_CTX *aead) {
+  return SSL_AEAD_CTX_explicit_nonce_len(aead) +
+         SSL_AEAD_CTX_max_suffix_len(aead, 0);
 }
 
 /* ssl_aead_ctx_get_ad writes the additional data for |aead| into |out| and
@@ -252,22 +254,32 @@
   return 1;
 }
 
-int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len,
-                      size_t max_out, uint8_t type, uint16_t wire_version,
-                      const uint8_t seqnum[8], const uint8_t *in,
-                      size_t in_len) {
+int SSL_AEAD_CTX_seal_scatter(SSL_AEAD_CTX *aead, uint8_t *out_prefix,
+                              uint8_t *out, uint8_t *out_suffix,
+                              size_t *out_suffix_len, size_t max_out_suffix_len,
+                              uint8_t type, uint16_t wire_version,
+                              const uint8_t seqnum[8], const uint8_t *in,
+                              size_t in_len, const uint8_t *extra_in,
+                              size_t extra_in_len) {
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   aead = NULL;
 #endif
 
+  if ((in != out && buffers_alias(in, in_len, out, in_len)) ||
+      buffers_alias(in, in_len, out_suffix, max_out_suffix_len)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
+    return 0;
+  }
+  if (extra_in_len > max_out_suffix_len) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
   if (aead == NULL) {
     /* Handle the initial NULL cipher. */
-    if (in_len > max_out) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-      return 0;
-    }
     OPENSSL_memmove(out, in, in_len);
-    *out_len = in_len;
+    OPENSSL_memmove(out_suffix, extra_in, extra_in_len);
+    *out_suffix_len = extra_in_len;
     return 1;
   }
 
@@ -303,22 +315,14 @@
   nonce_len += aead->variable_nonce_len;
 
   /* Emit the variable nonce if included in the record. */
-  size_t extra_len = 0;
   if (aead->variable_nonce_included_in_record) {
     assert(!aead->xor_fixed_nonce);
-    if (max_out < aead->variable_nonce_len) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-      return 0;
-    }
-    if (out < in + in_len && in < out + aead->variable_nonce_len) {
+    if (buffers_alias(in, in_len, out_prefix, aead->variable_nonce_len)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
       return 0;
     }
-    OPENSSL_memcpy(out, nonce + aead->fixed_nonce_len,
+    OPENSSL_memcpy(out_prefix, nonce + aead->fixed_nonce_len,
                    aead->variable_nonce_len);
-    extra_len = aead->variable_nonce_len;
-    out += aead->variable_nonce_len;
-    max_out -= aead->variable_nonce_len;
   }
 
   /* XOR the fixed nonce, if necessary. */
@@ -329,10 +333,33 @@
     }
   }
 
-  if (!EVP_AEAD_CTX_seal(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
-                         in, in_len, ad, ad_len)) {
+  return EVP_AEAD_CTX_seal_scatter(&aead->ctx, out, out_suffix, out_suffix_len,
+                                   max_out_suffix_len, nonce, nonce_len, in,
+                                   in_len, extra_in, extra_in_len, ad, ad_len);
+}
+
+int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len,
+                      size_t max_out_len, uint8_t type, uint16_t wire_version,
+                      const uint8_t seqnum[8], const uint8_t *in,
+                      size_t in_len) {
+  size_t prefix_len = SSL_AEAD_CTX_explicit_nonce_len(aead);
+  if (in_len + prefix_len < in_len) {
+    OPENSSL_PUT_ERROR(CIPHER, SSL_R_RECORD_TOO_LARGE);
     return 0;
   }
-  *out_len += extra_len;
+  if (in_len + prefix_len > max_out_len) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  size_t suffix_len;
+  if (!SSL_AEAD_CTX_seal_scatter(aead, out, out + prefix_len,
+                                 out + prefix_len + in_len, &suffix_len,
+                                 max_out_len - prefix_len - in_len, type,
+                                 wire_version, seqnum, in, in_len, 0, 0)) {
+    return 0;
+  }
+  assert(suffix_len <= SSL_AEAD_CTX_max_suffix_len(aead, 0));
+  *out_len = prefix_len + in_len + suffix_len;
   return 1;
 }
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.cc
similarity index 98%
rename from src/ssl/ssl_asn1.c
rename to src/ssl/ssl_asn1.cc
index cc6a559..1d6140e 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.cc
@@ -80,6 +80,13 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
+/* Per C99, various stdint.h macros are unavailable in C++ unless some macros
+ * are defined. C++11 overruled this decision, but older Android NDKs still
+ * require it. */
+#if !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS
+#endif
+
 #include <openssl/ssl.h>
 
 #include <limits.h>
@@ -425,7 +432,7 @@
     static const char kNotResumableSession[] = "NOT RESUMABLE";
 
     *out_len = strlen(kNotResumableSession);
-    *out_data = BUF_memdup(kNotResumableSession, *out_len);
+    *out_data = (uint8_t *)BUF_memdup(kNotResumableSession, *out_len);
     if (*out_data == NULL) {
       return 0;
     }
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.cc
similarity index 88%
rename from src/ssl/ssl_buffer.c
rename to src/ssl/ssl_buffer.cc
index 9ea5c68..579899b 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.cc
@@ -22,16 +22,17 @@
 #include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
-#include <openssl/type_check.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
 
 
-OPENSSL_COMPILE_ASSERT(0xffff <= INT_MAX, uint16_fits_in_int);
+/* BIO uses int instead of size_t. No lengths will exceed uint16_t, so this will
+ * not overflow. */
+static_assert(0xffff <= INT_MAX, "uint16_t does not fit in int");
 
-OPENSSL_COMPILE_ASSERT((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
-                       align_to_a_power_of_two);
+static_assert((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
+              "SSL3_ALIGN_PAYLOAD must be a power of 2");
 
 /* ensure_buffer ensures |buf| has capacity at least |cap|, aligned such that
  * data written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
@@ -47,7 +48,7 @@
   }
 
   /* Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment. */
-  uint8_t *new_buf = OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+  uint8_t *new_buf = (uint8_t *)OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -142,9 +143,9 @@
   ssl_read_buffer_discard(ssl);
 
   if (SSL_is_dtls(ssl)) {
-    OPENSSL_COMPILE_ASSERT(
+    static_assert(
         DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <= 0xffff,
-        dtls_read_buffer_too_large);
+        "DTLS read buffer is too large");
 
     /* The |len| parameter is ignored in DTLS. */
     len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
@@ -203,15 +204,16 @@
   return ssl->s3->write_buffer.len > 0;
 }
 
-OPENSSL_COMPILE_ASSERT(SSL3_RT_HEADER_LENGTH * 2 +
-                           SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
-                           SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff,
-                       maximum_tls_write_buffer_too_large);
+static_assert(SSL3_RT_HEADER_LENGTH * 2 +
+                      SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
+                      SSL3_RT_MAX_PLAIN_LENGTH <=
+                  0xffff,
+              "maximum TLS write buffer is too large");
 
-OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH +
-                           SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD +
-                           SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff,
-                       maximum_dtls_write_buffer_too_large);
+static_assert(DTLS1_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD +
+                      SSL3_RT_MAX_PLAIN_LENGTH <=
+                  0xffff,
+              "maximum DTLS write buffer is too large");
 
 int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) {
   SSL3_BUFFER *buf = &ssl->s3->write_buffer;
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.cc
similarity index 98%
rename from src/ssl/ssl_cert.c
rename to src/ssl/ssl_cert.cc
index 674db10..df4b9c8 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.cc
@@ -132,7 +132,7 @@
 
 
 CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method) {
-  CERT *ret = OPENSSL_malloc(sizeof(CERT));
+  CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -149,7 +149,7 @@
 }
 
 CERT *ssl_cert_dup(CERT *cert) {
-  CERT *ret = OPENSSL_malloc(sizeof(CERT));
+  CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -168,8 +168,8 @@
   ret->x509_method = cert->x509_method;
 
   if (cert->sigalgs != NULL) {
-    ret->sigalgs =
-        BUF_memdup(cert->sigalgs, cert->num_sigalgs * sizeof(cert->sigalgs[0]));
+    ret->sigalgs = (uint16_t *)BUF_memdup(
+        cert->sigalgs, cert->num_sigalgs * sizeof(cert->sigalgs[0]));
     if (ret->sigalgs == NULL) {
       goto err;
     }
@@ -496,7 +496,8 @@
 
   CBB certs;
   if (!CBB_add_u24_length_prefixed(cbb, &certs)) {
-    goto err;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
   }
 
   STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain;
@@ -507,15 +508,12 @@
         !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
                        CRYPTO_BUFFER_len(buffer)) ||
         !CBB_flush(&certs)) {
-      goto err;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
     }
   }
 
   return CBB_flush(cbb);
-
-err:
-  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return 0;
 }
 
 /* ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.cc
similarity index 98%
rename from src/ssl/ssl_cipher.c
rename to src/ssl/ssl_cipher.cc
index 5d88878..c0f4122 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.cc
@@ -631,8 +631,8 @@
 static const size_t kCipherAliasesLen = OPENSSL_ARRAY_SIZE(kCipherAliases);
 
 static int ssl_cipher_id_cmp(const void *in_a, const void *in_b) {
-  const SSL_CIPHER *a = in_a;
-  const SSL_CIPHER *b = in_b;
+  const SSL_CIPHER *a = reinterpret_cast<const SSL_CIPHER *>(in_a);
+  const SSL_CIPHER *b = reinterpret_cast<const SSL_CIPHER *>(in_b);
 
   if (a->id > b->id) {
     return 1;
@@ -647,8 +647,8 @@
   SSL_CIPHER c;
 
   c.id = 0x03000000L | value;
-  return bsearch(&c, kCiphers, kCiphersLen, sizeof(SSL_CIPHER),
-                 ssl_cipher_id_cmp);
+  return reinterpret_cast<const SSL_CIPHER *>(bsearch(
+      &c, kCiphers, kCiphersLen, sizeof(SSL_CIPHER), ssl_cipher_id_cmp));
 }
 
 int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
@@ -1001,7 +1001,7 @@
     curr = curr->next;
   }
 
-  number_uses = OPENSSL_malloc((max_strength_bits + 1) * sizeof(int));
+  number_uses = (int *)OPENSSL_malloc((max_strength_bits + 1) * sizeof(int));
   if (!number_uses) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -1227,7 +1227,7 @@
   /* Now we have to collect the available ciphers from the compiled in ciphers.
    * We cannot get more than the number compiled in, so it is used for
    * allocation. */
-  co_list = OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen);
+  co_list = (CIPHER_ORDER *)OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen);
   if (co_list == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -1314,7 +1314,7 @@
     goto err;
   }
 
-  in_group_flags = OPENSSL_malloc(kCiphersLen);
+  in_group_flags = (uint8_t *)OPENSSL_malloc(kCiphersLen);
   if (!in_group_flags) {
     goto err;
   }
@@ -1332,12 +1332,13 @@
   OPENSSL_free(co_list); /* Not needed any longer */
   co_list = NULL;
 
-  pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));
+  pref_list = (ssl_cipher_preference_list_st *)OPENSSL_malloc(
+      sizeof(struct ssl_cipher_preference_list_st));
   if (!pref_list) {
     goto err;
   }
   pref_list->ciphers = cipherstack;
-  pref_list->in_group_flags = OPENSSL_malloc(num_in_group_flags);
+  pref_list->in_group_flags = (uint8_t *)OPENSSL_malloc(num_in_group_flags);
   if (!pref_list->in_group_flags) {
     goto err;
   }
@@ -1672,7 +1673,7 @@
 
   if (buf == NULL) {
     len = 128;
-    buf = OPENSSL_malloc(len);
+    buf = (char *)OPENSSL_malloc(len);
     if (buf == NULL) {
       return NULL;
     }
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.cc
similarity index 80%
rename from src/ssl/ssl_ecdh.c
rename to src/ssl/ssl_ecdh.cc
index 49652f2..fa1cbe9 100644
--- a/src/ssl/ssl_ecdh.c
+++ b/src/ssl/ssl_ecdh.cc
@@ -37,49 +37,35 @@
 }
 
 static int ssl_ec_point_offer(SSL_ECDH_CTX *ctx, CBB *out) {
-  assert(ctx->data == NULL);
-  BIGNUM *private_key = BN_new();
-  if (private_key == NULL) {
-    return 0;
-  }
-  ctx->data = private_key;
-
   /* Set up a shared |BN_CTX| for all operations. */
-  BN_CTX *bn_ctx = BN_CTX_new();
-  if (bn_ctx == NULL) {
+  bssl::UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
+  if (!bn_ctx) {
     return 0;
   }
-  BN_CTX_start(bn_ctx);
-
-  int ret = 0;
-  EC_POINT *public_key = NULL;
-  EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
-  if (group == NULL) {
-    goto err;
-  }
+  bssl::BN_CTXScope scope(bn_ctx.get());
 
   /* Generate a private key. */
-  if (!BN_rand_range_ex(private_key, 1, EC_GROUP_get0_order(group))) {
-    goto err;
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(ctx->method->nid));
+  bssl::UniquePtr<BIGNUM> private_key(BN_new());
+  if (!group || !private_key ||
+      !BN_rand_range_ex(private_key.get(), 1,
+                        EC_GROUP_get0_order(group.get()))) {
+    return 0;
   }
 
   /* Compute the corresponding public key and serialize it. */
-  public_key = EC_POINT_new(group);
-  if (public_key == NULL ||
-      !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx) ||
-      !EC_POINT_point2cbb(out, group, public_key, POINT_CONVERSION_UNCOMPRESSED,
-                          bn_ctx)) {
-    goto err;
+  bssl::UniquePtr<EC_POINT> public_key(EC_POINT_new(group.get()));
+  if (!public_key ||
+      !EC_POINT_mul(group.get(), public_key.get(), private_key.get(), NULL,
+                    NULL, bn_ctx.get()) ||
+      !EC_POINT_point2cbb(out, group.get(), public_key.get(),
+                          POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) {
+    return 0;
   }
 
-  ret = 1;
-
-err:
-  EC_GROUP_free(group);
-  EC_POINT_free(public_key);
-  BN_CTX_end(bn_ctx);
-  BN_CTX_free(bn_ctx);
-  return ret;
+  assert(ctx->data == NULL);
+  ctx->data = private_key.release();
+  return 1;
 }
 
 static int ssl_ec_point_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
@@ -90,59 +76,48 @@
   *out_alert = SSL_AD_INTERNAL_ERROR;
 
   /* Set up a shared |BN_CTX| for all operations. */
-  BN_CTX *bn_ctx = BN_CTX_new();
-  if (bn_ctx == NULL) {
+  bssl::UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
+  if (!bn_ctx) {
     return 0;
   }
-  BN_CTX_start(bn_ctx);
+  bssl::BN_CTXScope scope(bn_ctx.get());
 
-  int ret = 0;
-  EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
-  EC_POINT *peer_point = NULL, *result = NULL;
-  uint8_t *secret = NULL;
-  if (group == NULL) {
-    goto err;
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(ctx->method->nid));
+  if (!group) {
+    return 0;
+  }
+
+  bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group.get()));
+  bssl::UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
+  BIGNUM *x = BN_CTX_get(bn_ctx.get());
+  if (!peer_point || !result || !x) {
+    return 0;
+  }
+
+  if (!EC_POINT_oct2point(group.get(), peer_point.get(), peer_key, peer_key_len,
+                          bn_ctx.get())) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
   }
 
   /* Compute the x-coordinate of |peer_key| * |private_key|. */
-  peer_point = EC_POINT_new(group);
-  result = EC_POINT_new(group);
-  if (peer_point == NULL || result == NULL) {
-    goto err;
-  }
-  BIGNUM *x = BN_CTX_get(bn_ctx);
-  if (x == NULL) {
-    goto err;
-  }
-  if (!EC_POINT_oct2point(group, peer_point, peer_key, peer_key_len, bn_ctx)) {
-    *out_alert = SSL_AD_DECODE_ERROR;
-    goto err;
-  }
-  if (!EC_POINT_mul(group, result, NULL, peer_point, private_key, bn_ctx) ||
-      !EC_POINT_get_affine_coordinates_GFp(group, result, x, NULL, bn_ctx)) {
-    goto err;
+  if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(),
+                    private_key, bn_ctx.get()) ||
+      !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL,
+                                           bn_ctx.get())) {
+    return 0;
   }
 
   /* Encode the x-coordinate left-padded with zeros. */
-  size_t secret_len = (EC_GROUP_get_degree(group) + 7) / 8;
-  secret = OPENSSL_malloc(secret_len);
-  if (secret == NULL || !BN_bn2bin_padded(secret, secret_len, x)) {
-    goto err;
+  size_t secret_len = (EC_GROUP_get_degree(group.get()) + 7) / 8;
+  bssl::UniquePtr<uint8_t> secret((uint8_t *)OPENSSL_malloc(secret_len));
+  if (!secret || !BN_bn2bin_padded(secret.get(), secret_len, x)) {
+    return 0;
   }
 
-  *out_secret = secret;
+  *out_secret = secret.release();
   *out_secret_len = secret_len;
-  secret = NULL;
-  ret = 1;
-
-err:
-  EC_GROUP_free(group);
-  EC_POINT_free(peer_point);
-  EC_POINT_free(result);
-  BN_CTX_end(bn_ctx);
-  BN_CTX_free(bn_ctx);
-  OPENSSL_free(secret);
-  return ret;
+  return 1;
 }
 
 static int ssl_ec_point_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
@@ -187,7 +162,7 @@
   assert(ctx->data != NULL);
   *out_alert = SSL_AD_INTERNAL_ERROR;
 
-  uint8_t *secret = OPENSSL_malloc(32);
+  uint8_t *secret = (uint8_t *)OPENSSL_malloc(32);
   if (secret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
diff --git a/src/ssl/ssl_file.c b/src/ssl/ssl_file.cc
similarity index 100%
rename from src/ssl/ssl_file.c
rename to src/ssl/ssl_file.cc
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.cc
similarity index 97%
rename from src/ssl/ssl_lib.c
rename to src/ssl/ssl_lib.cc
index 109dfd0..7441925 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.cc
@@ -173,9 +173,9 @@
 
 /* Some error codes are special. Ensure the make_errors.go script never
  * regresses this. */
-OPENSSL_COMPILE_ASSERT(SSL_R_TLSV1_ALERT_NO_RENEGOTIATION ==
-                           SSL_AD_NO_RENEGOTIATION + SSL_AD_REASON_OFFSET,
-                       ssl_alert_reason_code_mismatch);
+static_assert(SSL_R_TLSV1_ALERT_NO_RENEGOTIATION ==
+                  SSL_AD_NO_RENEGOTIATION + SSL_AD_REASON_OFFSET,
+              "alert reason code mismatch");
 
 /* kMaxHandshakeSize is the maximum size, in bytes, of a handshake message. */
 static const size_t kMaxHandshakeSize = (1u << 24) - 1;
@@ -234,7 +234,7 @@
     return NULL;
   }
 
-  ret = OPENSSL_malloc(sizeof(SSL_CTX));
+  ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
   if (ret == NULL) {
     goto err;
   }
@@ -365,7 +365,7 @@
     return NULL;
   }
 
-  SSL *ssl = OPENSSL_malloc(sizeof(SSL));
+  SSL *ssl = (SSL *)OPENSSL_malloc(sizeof(SSL));
   if (ssl == NULL) {
     goto err;
   }
@@ -373,6 +373,7 @@
 
   ssl->conf_min_version = ctx->conf_min_version;
   ssl->conf_max_version = ctx->conf_max_version;
+  ssl->tls13_variant = ctx->tls13_variant;
 
   /* RFC 6347 states that implementations SHOULD use an initial timer value of
    * 1 second. */
@@ -407,8 +408,8 @@
   }
 
   if (ctx->supported_group_list) {
-    ssl->supported_group_list = BUF_memdup(ctx->supported_group_list,
-                                           ctx->supported_group_list_len * 2);
+    ssl->supported_group_list = (uint16_t *)BUF_memdup(
+        ctx->supported_group_list, ctx->supported_group_list_len * 2);
     if (!ssl->supported_group_list) {
       goto err;
     }
@@ -416,8 +417,8 @@
   }
 
   if (ctx->alpn_client_proto_list) {
-    ssl->alpn_client_proto_list = BUF_memdup(ctx->alpn_client_proto_list,
-                                             ctx->alpn_client_proto_list_len);
+    ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(
+        ctx->alpn_client_proto_list, ctx->alpn_client_proto_list_len);
     if (ssl->alpn_client_proto_list == NULL) {
       goto err;
     }
@@ -719,7 +720,8 @@
     }
 
     int got_handshake;
-    int ret = ssl->method->read_app_data(ssl, &got_handshake, buf, num, peek);
+    int ret = ssl->method->read_app_data(ssl, &got_handshake, (uint8_t *)buf,
+                                         num, peek);
     if (ret > 0 || !got_handshake) {
       ssl->s3->key_update_count = 0;
       return ret;
@@ -774,7 +776,8 @@
       }
     }
 
-    ret = ssl->method->write_app_data(ssl, &needs_handshake, buf, num);
+    ret = ssl->method->write_app_data(ssl, &needs_handshake,
+                                      (const uint8_t *)buf, num);
   } while (needs_handshake);
   return ret;
 }
@@ -845,6 +848,14 @@
   ctx->cert->enable_early_data = !!enabled;
 }
 
+void SSL_CTX_set_tls13_variant(SSL_CTX *ctx, enum tls13_variant_t variant) {
+  ctx->tls13_variant = variant;
+}
+
+void SSL_set_tls13_variant(SSL *ssl, enum tls13_variant_t variant) {
+  ssl->tls13_variant = variant;
+}
+
 void SSL_set_early_data_enabled(SSL *ssl, int enabled) {
   ssl->cert->enable_early_data = !!enabled;
 }
@@ -1032,11 +1043,14 @@
 
 int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len,
                        size_t max_out) {
+  *out_len = 0;
+  OPENSSL_memset(out, 0, max_out);
+
   /* tls-unique is not defined for SSL 3.0 or TLS 1.3. */
   if (!ssl->s3->initial_handshake_complete ||
       ssl3_protocol_version(ssl) < TLS1_VERSION ||
       ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    goto err;
+    return 0;
   }
 
   /* The tls-unique value is the first Finished message in the handshake, which
@@ -1047,7 +1061,7 @@
   if (ssl->session != NULL) {
     /* tls-unique is broken for resumed sessions unless EMS is used. */
     if (!ssl->session->extended_master_secret) {
-      goto err;
+      return 0;
     }
     finished = ssl->s3->previous_server_finished;
     finished_len = ssl->s3->previous_server_finished_len;
@@ -1060,11 +1074,6 @@
 
   OPENSSL_memcpy(out, finished, *out_len);
   return 1;
-
-err:
-  *out_len = 0;
-  OPENSSL_memset(out, 0, max_out);
-  return 0;
 }
 
 static int set_session_id_context(CERT *cert, const uint8_t *sid_ctx,
@@ -1074,7 +1083,7 @@
     return 0;
   }
 
-  OPENSSL_COMPILE_ASSERT(sizeof(cert->sid_ctx) < 256, sid_ctx_too_large);
+  static_assert(sizeof(cert->sid_ctx) < 256, "sid_ctx too large");
   cert->sid_ctx_length = (uint8_t)sid_ctx_len;
   OPENSSL_memcpy(cert->sid_ctx, sid_ctx, sid_ctx_len);
   return 1;
@@ -1383,7 +1392,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
     return 0;
   }
-  uint8_t *out_bytes = out;
+  uint8_t *out_bytes = reinterpret_cast<uint8_t *>(out);
   OPENSSL_memcpy(out_bytes, ctx->tlsext_tick_key_name, 16);
   OPENSSL_memcpy(out_bytes + 16, ctx->tlsext_tick_hmac_key, 16);
   OPENSSL_memcpy(out_bytes + 32, ctx->tlsext_tick_aes_key, 16);
@@ -1398,7 +1407,7 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
     return 0;
   }
-  const uint8_t *in_bytes = in;
+  const uint8_t *in_bytes = reinterpret_cast<const uint8_t *>(in);
   OPENSSL_memcpy(ctx->tlsext_tick_key_name, in_bytes, 16);
   OPENSSL_memcpy(ctx->tlsext_tick_hmac_key, in_bytes + 16, 16);
   OPENSSL_memcpy(ctx->tlsext_tick_aes_key, in_bytes + 32, 16);
@@ -1682,7 +1691,7 @@
 int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos,
                             unsigned protos_len) {
   OPENSSL_free(ctx->alpn_client_proto_list);
-  ctx->alpn_client_proto_list = BUF_memdup(protos, protos_len);
+  ctx->alpn_client_proto_list = (uint8_t *)BUF_memdup(protos, protos_len);
   if (!ctx->alpn_client_proto_list) {
     return 1;
   }
@@ -1693,7 +1702,7 @@
 
 int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) {
   OPENSSL_free(ssl->alpn_client_proto_list);
-  ssl->alpn_client_proto_list = BUF_memdup(protos, protos_len);
+  ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(protos, protos_len);
   if (!ssl->alpn_client_proto_list) {
     return 1;
   }
diff --git a/src/ssl/ssl_privkey.c b/src/ssl/ssl_privkey.cc
similarity index 96%
rename from src/ssl/ssl_privkey.c
rename to src/ssl/ssl_privkey.cc
index 257d03e..5b620f8 100644
--- a/src/ssl/ssl_privkey.c
+++ b/src/ssl/ssl_privkey.cc
@@ -64,7 +64,6 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
-#include <openssl/type_check.h>
 
 #include "internal.h"
 #include "../crypto/internal.h"
@@ -119,6 +118,16 @@
   return ret;
 }
 
+int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
+  bssl::UniquePtr<RSA> rsa(RSA_private_key_from_bytes(der, der_len));
+  if (!rsa) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+    return 0;
+  }
+
+  return SSL_use_RSAPrivateKey(ssl, rsa.get());
+}
+
 int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
@@ -228,7 +237,7 @@
   OPENSSL_free(*out_prefs);
 
   *out_num_prefs = 0;
-  *out_prefs = BUF_memdup(prefs, num_prefs * sizeof(prefs[0]));
+  *out_prefs = (uint16_t *)BUF_memdup(prefs, num_prefs * sizeof(prefs[0]));
   if (*out_prefs == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -261,11 +270,12 @@
                                      size_t num_digests) {
   OPENSSL_free(ssl->cert->sigalgs);
 
-  OPENSSL_COMPILE_ASSERT(sizeof(int) >= 2 * sizeof(uint16_t),
-                         digest_list_conversion_cannot_overflow);
+  static_assert(sizeof(int) >= 2 * sizeof(uint16_t),
+                "sigalgs allocation may overflow");
 
   ssl->cert->num_sigalgs = 0;
-  ssl->cert->sigalgs = OPENSSL_malloc(sizeof(uint16_t) * 2 * num_digests);
+  ssl->cert->sigalgs =
+      (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * 2 * num_digests);
   if (ssl->cert->sigalgs == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
diff --git a/src/ssl/ssl_privkey_cc.cc b/src/ssl/ssl_privkey_cc.cc
deleted file mode 100644
index 653308c..0000000
--- a/src/ssl/ssl_privkey_cc.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.] */
-
-#include <openssl/ssl.h>
-
-#include <openssl/err.h>
-#include <openssl/rsa.h>
-
-
-/* This function has been converted to C++ to check if all of libssl's
- * consumers' toolchains are capable of handling C++11. Once all problems in
- * consumer toolchains are found and fixed, we will convert the rest of
- * libssl. */
-
-int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
-  bssl::UniquePtr<RSA> rsa(RSA_private_key_from_bytes(der, der_len));
-  if (!rsa) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-    return 0;
-  }
-
-  return SSL_use_RSAPrivateKey(ssl, rsa.get());
-}
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.cc
similarity index 95%
rename from src/ssl/ssl_session.c
rename to src/ssl/ssl_session.cc
index 3e2c9f4..9cb78cc 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.cc
@@ -161,7 +161,7 @@
 static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock);
 
 SSL_SESSION *ssl_session_new(const SSL_X509_METHOD *x509_method) {
-  SSL_SESSION *session = OPENSSL_malloc(sizeof(SSL_SESSION));
+  SSL_SESSION *session = (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION));
   if (session == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -228,8 +228,8 @@
 
   new_session->ocsp_response_length = session->ocsp_response_length;
   if (session->ocsp_response != NULL) {
-    new_session->ocsp_response = BUF_memdup(session->ocsp_response,
-                                            session->ocsp_response_length);
+    new_session->ocsp_response = (uint8_t *)BUF_memdup(
+        session->ocsp_response, session->ocsp_response_length);
     if (new_session->ocsp_response == NULL) {
       goto err;
     }
@@ -238,9 +238,9 @@
   new_session->tlsext_signed_cert_timestamp_list_length =
       session->tlsext_signed_cert_timestamp_list_length;
   if (session->tlsext_signed_cert_timestamp_list != NULL) {
-    new_session->tlsext_signed_cert_timestamp_list =
-        BUF_memdup(session->tlsext_signed_cert_timestamp_list,
-                   session->tlsext_signed_cert_timestamp_list_length);
+    new_session->tlsext_signed_cert_timestamp_list = (uint8_t *)BUF_memdup(
+        session->tlsext_signed_cert_timestamp_list,
+        session->tlsext_signed_cert_timestamp_list_length);
     if (new_session->tlsext_signed_cert_timestamp_list == NULL) {
       goto err;
     }
@@ -283,7 +283,7 @@
 
     if (session->early_alpn != NULL) {
       new_session->early_alpn =
-          BUF_memdup(session->early_alpn, session->early_alpn_len);
+          (uint8_t *)BUF_memdup(session->early_alpn, session->early_alpn_len);
       if (new_session->early_alpn == NULL) {
         goto err;
       }
@@ -295,7 +295,7 @@
   if (dup_flags & SSL_SESSION_INCLUDE_TICKET) {
     if (session->tlsext_tick != NULL) {
       new_session->tlsext_tick =
-          BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
+          (uint8_t *)BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
       if (new_session->tlsext_tick == NULL) {
         goto err;
       }
@@ -595,12 +595,8 @@
 static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
                                               const uint8_t *session_buf,
                                               size_t session_len) {
-  int ret = 0;
-
-  EVP_CIPHER_CTX ctx;
-  EVP_CIPHER_CTX_init(&ctx);
-  HMAC_CTX hctx;
-  HMAC_CTX_init(&hctx);
+  bssl::ScopedEVP_CIPHER_CTX ctx;
+  bssl::ScopedHMAC_CTX hctx;
 
   /* If the session is too long, emit a dummy value rather than abort the
    * connection. */
@@ -608,11 +604,8 @@
       16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
   if (session_len > 0xffff - kMaxTicketOverhead) {
     static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
-    if (CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder,
-                      strlen(kTicketPlaceholder))) {
-      ret = 1;
-    }
-    goto err;
+    return CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder,
+                         strlen(kTicketPlaceholder));
   }
 
   /* Initialize HMAC and cipher contexts. If callback present it does all the
@@ -621,26 +614,26 @@
   uint8_t iv[EVP_MAX_IV_LENGTH];
   uint8_t key_name[16];
   if (tctx->tlsext_ticket_key_cb != NULL) {
-    if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
+    if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, ctx.get(), hctx.get(),
                                    1 /* encrypt */) < 0) {
-      goto err;
+      return 0;
     }
   } else {
     if (!RAND_bytes(iv, 16) ||
-        !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
+        !EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), NULL,
                             tctx->tlsext_tick_aes_key, iv) ||
-        !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
-                      NULL)) {
-      goto err;
+        !HMAC_Init_ex(hctx.get(), tctx->tlsext_tick_hmac_key, 16,
+                      tlsext_tick_md(), NULL)) {
+      return 0;
     }
     OPENSSL_memcpy(key_name, tctx->tlsext_tick_key_name, 16);
   }
 
   uint8_t *ptr;
   if (!CBB_add_bytes(out, key_name, 16) ||
-      !CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(&ctx)) ||
+      !CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(ctx.get())) ||
       !CBB_reserve(out, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) {
-    goto err;
+    return 0;
   }
 
   size_t total = 0;
@@ -649,33 +642,28 @@
   total = session_len;
 #else
   int len;
-  if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session_buf, session_len)) {
-    goto err;
+  if (!EVP_EncryptUpdate(ctx.get(), ptr + total, &len, session_buf, session_len)) {
+    return 0;
   }
   total += len;
-  if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) {
-    goto err;
+  if (!EVP_EncryptFinal_ex(ctx.get(), ptr + total, &len)) {
+    return 0;
   }
   total += len;
 #endif
   if (!CBB_did_write(out, total)) {
-    goto err;
+    return 0;
   }
 
   unsigned hlen;
-  if (!HMAC_Update(&hctx, CBB_data(out), CBB_len(out)) ||
+  if (!HMAC_Update(hctx.get(), CBB_data(out), CBB_len(out)) ||
       !CBB_reserve(out, &ptr, EVP_MAX_MD_SIZE) ||
-      !HMAC_Final(&hctx, ptr, &hlen) ||
+      !HMAC_Final(hctx.get(), ptr, &hlen) ||
       !CBB_did_write(out, hlen)) {
-    goto err;
+    return 0;
   }
 
-  ret = 1;
-
-err:
-  EVP_CIPHER_CTX_cleanup(&ctx);
-  HMAC_CTX_cleanup(&hctx);
-  return ret;
+  return 1;
 }
 
 static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out,
@@ -1027,7 +1015,7 @@
 } TIMEOUT_PARAM;
 
 static void timeout_doall_arg(SSL_SESSION *session, void *void_param) {
-  TIMEOUT_PARAM *param = void_param;
+  TIMEOUT_PARAM *param = reinterpret_cast<TIMEOUT_PARAM *>(void_param);
 
   if (param->time == 0 ||
       session->time + session->timeout < session->time ||
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.cc
similarity index 100%
rename from src/ssl/ssl_stat.c
rename to src/ssl/ssl_stat.cc
diff --git a/src/ssl/ssl_transcript.c b/src/ssl/ssl_transcript.cc
similarity index 100%
rename from src/ssl/ssl_transcript.c
rename to src/ssl/ssl_transcript.cc
diff --git a/src/ssl/ssl_versions.c b/src/ssl/ssl_versions.cc
similarity index 87%
rename from src/ssl/ssl_versions.c
rename to src/ssl/ssl_versions.cc
index 5d92771..8b54bd2 100644
--- a/src/ssl/ssl_versions.c
+++ b/src/ssl/ssl_versions.cc
@@ -33,6 +33,8 @@
       return 1;
 
     case TLS1_3_DRAFT_VERSION:
+    case TLS1_3_EXPERIMENT_VERSION:
+    case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION:
       *out = TLS1_3_VERSION;
       return 1;
 
@@ -54,6 +56,8 @@
  * decreasing preference. */
 
 static const uint16_t kTLSVersions[] = {
+    TLS1_3_EXPERIMENT_VERSION,
+    TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION,
     TLS1_3_DRAFT_VERSION,
     TLS1_2_VERSION,
     TLS1_1_VERSION,
@@ -95,7 +99,9 @@
   /* The public API uses wire versions, except we use |TLS1_3_VERSION|
    * everywhere to refer to any draft TLS 1.3 versions. In this direction, we
    * map it to some representative TLS 1.3 draft version. */
-  if (version == TLS1_3_DRAFT_VERSION) {
+  if (version == TLS1_3_DRAFT_VERSION ||
+      version == TLS1_3_EXPERIMENT_VERSION ||
+      version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
     return 0;
   }
@@ -235,7 +241,8 @@
 int SSL_version(const SSL *ssl) {
   uint16_t ret = ssl_version(ssl);
   /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
-  if (ret == TLS1_3_DRAFT_VERSION) {
+  if (ret == TLS1_3_DRAFT_VERSION || ret == TLS1_3_EXPERIMENT_VERSION ||
+      ret == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) {
     return TLS1_3_VERSION;
   }
   return ret;
@@ -245,6 +252,8 @@
   switch (version) {
     /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
     case TLS1_3_DRAFT_VERSION:
+    case TLS1_3_EXPERIMENT_VERSION:
+    case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION:
       return "TLSv1.3";
 
     case TLS1_2_VERSION:
@@ -291,8 +300,29 @@
 }
 
 int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
+  SSL *const ssl = hs->ssl;
+  /* As a client, only allow the configured TLS 1.3 variant. As a server,
+   * support all TLS 1.3 variants as long as tls13_variant is set to a
+   * non-default value. */
+  if (ssl->server) {
+    if (ssl->tls13_variant == tls13_default &&
+        (version == TLS1_3_EXPERIMENT_VERSION ||
+         version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION)) {
+      return 0;
+    }
+  } else {
+    if ((ssl->tls13_variant != tls13_experiment &&
+         version == TLS1_3_EXPERIMENT_VERSION) ||
+        (ssl->tls13_variant != tls13_record_type_experiment &&
+         version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) ||
+        (ssl->tls13_variant != tls13_default &&
+         version == TLS1_3_DRAFT_VERSION)) {
+      return 0;
+    }
+  }
+
   uint16_t protocol_version;
-  return method_supports_version(hs->ssl->method, version) &&
+  return method_supports_version(ssl->method, version) &&
          ssl_protocol_version_from_wire(&protocol_version, version) &&
          hs->min_version <= protocol_version &&
          protocol_version <= hs->max_version;
diff --git a/src/ssl/ssl_x509.c b/src/ssl/ssl_x509.cc
similarity index 98%
rename from src/ssl/ssl_x509.c
rename to src/ssl/ssl_x509.cc
index 65405aa..77fc0e2 100644
--- a/src/ssl/ssl_x509.c
+++ b/src/ssl/ssl_x509.cc
@@ -494,14 +494,13 @@
 }
 
 static int ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) {
-  STACK_OF(X509) *chain = NULL;
+  bssl::UniquePtr<STACK_OF(X509)> chain;
   const size_t num_certs = sk_CRYPTO_BUFFER_num(sess->certs);
-
   if (num_certs > 0) {
-    chain = sk_X509_new_null();
-    if (chain == NULL) {
+    chain.reset(sk_X509_new_null());
+    if (!chain) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
+      return 0;
     }
   }
 
@@ -510,12 +509,12 @@
     X509 *x509 = X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(sess->certs, i));
     if (x509 == NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto err;
+      return 0;
     }
-    if (!sk_X509_push(chain, x509)) {
+    if (!sk_X509_push(chain.get(), x509)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       X509_free(x509);
-      goto err;
+      return 0;
     }
     if (i == 0) {
       leaf = x509;
@@ -523,7 +522,7 @@
   }
 
   sk_X509_pop_free(sess->x509_chain, X509_free);
-  sess->x509_chain = chain;
+  sess->x509_chain = chain.release();
   sk_X509_pop_free(sess->x509_chain_without_leaf, X509_free);
   sess->x509_chain_without_leaf = NULL;
 
@@ -532,12 +531,7 @@
     X509_up_ref(leaf);
   }
   sess->x509_peer = leaf;
-
   return 1;
-
-err:
-  sk_X509_pop_free(chain, X509_free);
-  return 0;
 }
 
 static int ssl_crypto_x509_session_dup(SSL_SESSION *new_session,
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.cc
similarity index 98%
rename from src/ssl/t1_enc.c
rename to src/ssl/t1_enc.cc
index 6aa5e0c..c224240 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.cc
@@ -365,7 +365,7 @@
 
   size_t key_block_len = SSL_get_key_block_len(ssl);
 
-  uint8_t *keyblock = OPENSSL_malloc(key_block_len);
+  uint8_t *keyblock = (uint8_t *)OPENSSL_malloc(key_block_len);
   if (keyblock == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -533,7 +533,7 @@
     }
     seed_len += 2 + context_len;
   }
-  uint8_t *seed = OPENSSL_malloc(seed_len);
+  uint8_t *seed = (uint8_t *)OPENSSL_malloc(seed_len);
   if (seed == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.cc
similarity index 95%
rename from src/ssl/t1_lib.c
rename to src/ssl/t1_lib.cc
index 8e858c4..76469eb 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.cc
@@ -121,7 +121,6 @@
 #include <openssl/mem.h>
 #include <openssl/nid.h>
 #include <openssl/rand.h>
-#include <openssl/type_check.h>
 
 #include "internal.h"
 #include "../crypto/internal.h"
@@ -168,7 +167,8 @@
     return 1;
   }
 
-  extension_types = OPENSSL_malloc(sizeof(uint16_t) * num_extensions);
+  extension_types =
+      (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * num_extensions);
   if (extension_types == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto done;
@@ -362,9 +362,7 @@
 
 int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
                     const int *curves, size_t ncurves) {
-  uint16_t *group_ids;
-
-  group_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
+  uint16_t *group_ids = (uint16_t *)OPENSSL_malloc(ncurves * sizeof(uint16_t));
   if (group_ids == NULL) {
     return 0;
   }
@@ -400,8 +398,8 @@
       goto err;
     }
 
-    uint16_t *new_group_ids = OPENSSL_realloc(group_ids,
-                                              (ncurves + 1) * sizeof(uint16_t));
+    uint16_t *new_group_ids = (uint16_t *)OPENSSL_realloc(
+        group_ids, (ncurves + 1) * sizeof(uint16_t));
     if (new_group_ids == NULL) {
       goto err;
     }
@@ -1233,7 +1231,8 @@
   }
 
   OPENSSL_free(ssl->s3->next_proto_negotiated);
-  ssl->s3->next_proto_negotiated = BUF_memdup(selected, selected_len);
+  ssl->s3->next_proto_negotiated =
+      (uint8_t *)BUF_memdup(selected, selected_len);
   if (ssl->s3->next_proto_negotiated == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
@@ -1522,7 +1521,7 @@
           CBS_len(&protocol_name_list),
           ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
     OPENSSL_free(ssl->s3->alpn_selected);
-    ssl->s3->alpn_selected = BUF_memdup(selected, selected_len);
+    ssl->s3->alpn_selected = (uint8_t *)BUF_memdup(selected, selected_len);
     if (ssl->s3->alpn_selected == NULL) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return 0;
@@ -2197,7 +2196,8 @@
     /* Save the contents of the extension to repeat it in the second
      * ClientHello. */
     hs->key_share_bytes_len = CBB_len(&kse_bytes);
-    hs->key_share_bytes = BUF_memdup(CBB_data(&kse_bytes), CBB_len(&kse_bytes));
+    hs->key_share_bytes =
+        (uint8_t *)BUF_memdup(CBB_data(&kse_bytes), CBB_len(&kse_bytes));
     if (hs->key_share_bytes == NULL) {
       return 0;
     }
@@ -2451,7 +2451,7 @@
   }
 
   hs->peer_supported_group_list =
-      OPENSSL_malloc(CBS_len(&supported_group_list));
+      (uint16_t *)OPENSSL_malloc(CBS_len(&supported_group_list));
   if (hs->peer_supported_group_list == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
@@ -2638,12 +2638,12 @@
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
 
-OPENSSL_COMPILE_ASSERT(kNumExtensions <=
-                           sizeof(((SSL_HANDSHAKE *)NULL)->extensions.sent) * 8,
-                       too_many_extensions_for_sent_bitset);
-OPENSSL_COMPILE_ASSERT(
-    kNumExtensions <= sizeof(((SSL_HANDSHAKE *)NULL)->extensions.received) * 8,
-    too_many_extensions_for_received_bitset);
+static_assert(kNumExtensions <=
+                  sizeof(((SSL_HANDSHAKE *)NULL)->extensions.sent) * 8,
+              "too many extensions for sent bitset");
+static_assert(kNumExtensions <=
+                  sizeof(((SSL_HANDSHAKE *)NULL)->extensions.received) * 8,
+              "too many extensions for received bitset");
 
 static const struct tls_extension *tls_extension_find(uint32_t *out_index,
                                                       uint16_t value) {
@@ -2674,7 +2674,8 @@
 
   CBB extensions;
   if (!CBB_add_u16_length_prefixed(out, &extensions)) {
-    goto err;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
   }
 
   hs->extensions.sent = 0;
@@ -2692,7 +2693,8 @@
     grease_ext1 = ssl_get_grease_value(ssl, ssl_grease_extension1);
     if (!CBB_add_u16(&extensions, grease_ext1) ||
         !CBB_add_u16(&extensions, 0 /* zero length */)) {
-      goto err;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
     }
   }
 
@@ -2701,7 +2703,7 @@
     if (!kExtensions[i].add_clienthello(hs, &extensions)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
-      goto err;
+      return 0;
     }
 
     if (CBB_len(&extensions) != len_before) {
@@ -2710,7 +2712,8 @@
   }
 
   if (!custom_ext_add_clienthello(hs, &extensions)) {
-    goto err;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
   }
 
   if (ssl->ctx->grease_enabled) {
@@ -2727,7 +2730,8 @@
     if (!CBB_add_u16(&extensions, grease_ext2) ||
         !CBB_add_u16(&extensions, 1 /* one byte length */) ||
         !CBB_add_u8(&extensions, 0 /* single zero byte as contents */)) {
-      goto err;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
     }
   }
 
@@ -2754,7 +2758,8 @@
       if (!CBB_add_u16(&extensions, TLSEXT_TYPE_padding) ||
           !CBB_add_u16(&extensions, padding_len) ||
           !CBB_add_space(&extensions, &padding_bytes, padding_len)) {
-        goto err;
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        return 0;
       }
 
       OPENSSL_memset(padding_bytes, 0, padding_len);
@@ -2763,7 +2768,8 @@
 
   /* The PSK extension must be last, including after the padding. */
   if (!ext_pre_shared_key_add_clienthello(hs, &extensions)) {
-    goto err;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
   }
 
   /* Discard empty extensions blocks. */
@@ -2772,10 +2778,6 @@
   }
 
   return CBB_flush(out);
-
-err:
-  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return 0;
 }
 
 int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out) {
@@ -2957,8 +2959,8 @@
       continue;
     }
 
-    OPENSSL_COMPILE_ASSERT(kNumExtensions <= sizeof(hs->extensions.sent) * 8,
-                           too_many_bits);
+    static_assert(kNumExtensions <= sizeof(hs->extensions.sent) * 8,
+                  "too many bits");
 
     if (!(hs->extensions.sent & (1u << ext_index)) &&
         type != TLSEXT_TYPE_renegotiate) {
@@ -3040,33 +3042,28 @@
 ssl_decrypt_ticket_with_cipher_ctx(SSL *ssl, uint8_t **out, size_t *out_len,
                                    int *out_renew_ticket, const uint8_t *ticket,
                                    size_t ticket_len) {
-  enum ssl_ticket_aead_result_t ret = ssl_ticket_aead_ignore_ticket;
   const SSL_CTX *const ssl_ctx = ssl->session_ctx;
-  uint8_t *plaintext = NULL;
 
-  HMAC_CTX hmac_ctx;
-  HMAC_CTX_init(&hmac_ctx);
-  EVP_CIPHER_CTX cipher_ctx;
-  EVP_CIPHER_CTX_init(&cipher_ctx);
+  bssl::ScopedHMAC_CTX hmac_ctx;
+  bssl::ScopedEVP_CIPHER_CTX cipher_ctx;
 
   /* Ensure there is room for the key name and the largest IV
    * |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but
    * the maximum IV length should be well under the minimum size for the
    * session material and HMAC. */
   if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
-    goto out;
+    return ssl_ticket_aead_ignore_ticket;
   }
   const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
 
   if (ssl_ctx->tlsext_ticket_key_cb != NULL) {
     int cb_ret = ssl_ctx->tlsext_ticket_key_cb(
-        ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, &cipher_ctx,
-        &hmac_ctx, 0 /* decrypt */);
+        ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, cipher_ctx.get(),
+        hmac_ctx.get(), 0 /* decrypt */);
     if (cb_ret < 0) {
-      ret = ssl_ticket_aead_error;
-      goto out;
+      return ssl_ticket_aead_error;
     } else if (cb_ret == 0) {
-      goto out;
+      return ssl_ticket_aead_ignore_ticket;
     } else if (cb_ret == 2) {
       *out_renew_ticket = 1;
     }
@@ -3074,80 +3071,71 @@
     /* Check the key name matches. */
     if (OPENSSL_memcmp(ticket, ssl_ctx->tlsext_tick_key_name,
                        SSL_TICKET_KEY_NAME_LEN) != 0) {
-      goto out;
+      return ssl_ticket_aead_ignore_ticket;
     }
-    if (!HMAC_Init_ex(&hmac_ctx, ssl_ctx->tlsext_tick_hmac_key,
+    if (!HMAC_Init_ex(hmac_ctx.get(), ssl_ctx->tlsext_tick_hmac_key,
                       sizeof(ssl_ctx->tlsext_tick_hmac_key), tlsext_tick_md(),
                       NULL) ||
-        !EVP_DecryptInit_ex(&cipher_ctx, EVP_aes_128_cbc(), NULL,
+        !EVP_DecryptInit_ex(cipher_ctx.get(), EVP_aes_128_cbc(), NULL,
                             ssl_ctx->tlsext_tick_aes_key, iv)) {
-      ret = ssl_ticket_aead_error;
-      goto out;
+      return ssl_ticket_aead_error;
     }
   }
-  size_t iv_len = EVP_CIPHER_CTX_iv_length(&cipher_ctx);
+  size_t iv_len = EVP_CIPHER_CTX_iv_length(cipher_ctx.get());
 
   /* Check the MAC at the end of the ticket. */
   uint8_t mac[EVP_MAX_MD_SIZE];
-  size_t mac_len = HMAC_size(&hmac_ctx);
+  size_t mac_len = HMAC_size(hmac_ctx.get());
   if (ticket_len < SSL_TICKET_KEY_NAME_LEN + iv_len + 1 + mac_len) {
     /* The ticket must be large enough for key name, IV, data, and MAC. */
-    goto out;
+    return ssl_ticket_aead_ignore_ticket;
   }
-  HMAC_Update(&hmac_ctx, ticket, ticket_len - mac_len);
-  HMAC_Final(&hmac_ctx, mac, NULL);
+  HMAC_Update(hmac_ctx.get(), ticket, ticket_len - mac_len);
+  HMAC_Final(hmac_ctx.get(), mac, NULL);
   int mac_ok =
       CRYPTO_memcmp(mac, ticket + (ticket_len - mac_len), mac_len) == 0;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   mac_ok = 1;
 #endif
   if (!mac_ok) {
-    goto out;
+    return ssl_ticket_aead_ignore_ticket;
   }
 
   /* Decrypt the session data. */
   const uint8_t *ciphertext = ticket + SSL_TICKET_KEY_NAME_LEN + iv_len;
   size_t ciphertext_len = ticket_len - SSL_TICKET_KEY_NAME_LEN - iv_len -
                           mac_len;
-  plaintext = OPENSSL_malloc(ciphertext_len);
-  if (plaintext == NULL) {
-    ret = ssl_ticket_aead_error;
-    goto out;
+  bssl::UniquePtr<uint8_t> plaintext((uint8_t *)OPENSSL_malloc(ciphertext_len));
+  if (!plaintext) {
+    return ssl_ticket_aead_error;
   }
   size_t plaintext_len;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  OPENSSL_memcpy(plaintext, ciphertext, ciphertext_len);
+  OPENSSL_memcpy(plaintext.get(), ciphertext, ciphertext_len);
   plaintext_len = ciphertext_len;
 #else
   if (ciphertext_len >= INT_MAX) {
-    goto out;
+    return ssl_ticket_aead_ignore_ticket;
   }
   int len1, len2;
-  if (!EVP_DecryptUpdate(&cipher_ctx, plaintext, &len1, ciphertext,
+  if (!EVP_DecryptUpdate(cipher_ctx.get(), plaintext.get(), &len1, ciphertext,
                          (int)ciphertext_len) ||
-      !EVP_DecryptFinal_ex(&cipher_ctx, plaintext + len1, &len2)) {
+      !EVP_DecryptFinal_ex(cipher_ctx.get(), plaintext.get() + len1, &len2)) {
     ERR_clear_error();
-    goto out;
+    return ssl_ticket_aead_ignore_ticket;
   }
   plaintext_len = (size_t)(len1) + len2;
 #endif
 
-  *out = plaintext;
-  plaintext = NULL;
+  *out = plaintext.release();
   *out_len = plaintext_len;
-  ret = ssl_ticket_aead_success;
-
-out:
-  OPENSSL_free(plaintext);
-  HMAC_CTX_cleanup(&hmac_ctx);
-  EVP_CIPHER_CTX_cleanup(&cipher_ctx);
-  return ret;
+  return ssl_ticket_aead_success;
 }
 
 static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method(
     SSL *ssl, uint8_t **out, size_t *out_len, int *out_renew_ticket,
     const uint8_t *ticket, size_t ticket_len) {
-  uint8_t *plaintext = OPENSSL_malloc(ticket_len);
+  uint8_t *plaintext = (uint8_t *)OPENSSL_malloc(ticket_len);
   if (plaintext == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return ssl_ticket_aead_error;
@@ -3238,7 +3226,7 @@
 
   /* This multiplication doesn't overflow because sizeof(uint16_t) is two
    * and we just divided |num_sigalgs| by two. */
-  hs->peer_sigalgs = OPENSSL_malloc(num_sigalgs * sizeof(uint16_t));
+  hs->peer_sigalgs = (uint16_t *)OPENSSL_malloc(num_sigalgs * sizeof(uint16_t));
   if (hs->peer_sigalgs == NULL) {
     return 0;
   }
@@ -3324,7 +3312,6 @@
 
 int tls1_verify_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = 0;
   uint16_t extension_type;
   CBS extension, channel_id;
 
@@ -3341,52 +3328,44 @@
     return 0;
   }
 
-  EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+  bssl::UniquePtr<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;
+  bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
+  bssl::UniquePtr<BIGNUM> x(BN_new()), y(BN_new());
+  if (!sig || !x || !y) {
+    return 0;
   }
 
   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;
+  if (BN_bin2bn(p + 0, 32, x.get()) == NULL ||
+      BN_bin2bn(p + 32, 32, y.get()) == NULL ||
+      BN_bin2bn(p + 64, 32, sig->r) == NULL ||
+      BN_bin2bn(p + 96, 32, sig->s) == NULL) {
+    return 0;
   }
 
-  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;
+  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+  bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+  if (!key || !point ||
+      !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
+                                           y.get(), nullptr) ||
+      !EC_KEY_set_group(key.get(), p256.get()) ||
+      !EC_KEY_set_public_key(key.get(), point.get())) {
+    return 0;
   }
 
   uint8_t digest[EVP_MAX_MD_SIZE];
   size_t digest_len;
   if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
-    goto err;
+    return 0;
   }
 
-  int sig_ok = ECDSA_do_verify(digest, digest_len, &sig, key);
+  int sig_ok = ECDSA_do_verify(digest, digest_len, sig.get(), key.get());
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   sig_ok = 1;
 #endif
@@ -3394,21 +3373,11 @@
     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;
+    return 0;
   }
 
   OPENSSL_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;
 }
 
 int tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) {
@@ -3516,9 +3485,9 @@
     return -1;
   }
 
-  OPENSSL_COMPILE_ASSERT(
+  static_assert(
       sizeof(hs->new_session->original_handshake_hash) == EVP_MAX_MD_SIZE,
-      original_handshake_hash_is_too_small);
+      "original_handshake_hash is too small");
 
   size_t digest_len;
   if (!SSL_TRANSCRIPT_get_hash(&hs->transcript,
@@ -3527,7 +3496,8 @@
     return -1;
   }
 
-  OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE <= 0xff, max_md_size_is_too_large);
+  static_assert(EVP_MAX_MD_SIZE <= 0xff,
+                "EVP_MAX_MD_SIZE does not fit in uint8_t");
   hs->new_session->original_handshake_hash_len = (uint8_t)digest_len;
 
   return 1;
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 6d3e6d1..cd846be 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -39,6 +39,7 @@
 #include <assert.h>
 #include <inttypes.h>
 #include <string.h>
+#include <time.h>
 
 #include <openssl/aead.h>
 #include <openssl/bio.h>
@@ -969,6 +970,7 @@
 // Connect returns a new socket connected to localhost on |port| or -1 on
 // error.
 static int Connect(uint16_t port) {
+  time_t start_time = time(nullptr);
   for (int af : { AF_INET6, AF_INET }) {
     int sock = socket(af, SOCK_STREAM, 0);
     if (sock == -1) {
@@ -1013,6 +1015,13 @@
     }
     closesocket(sock);
   }
+
+  PrintSocketError("connect");
+  // TODO(davidben): Remove this logging when https://crbug.com/boringssl/199 is
+  // resolved.
+  fprintf(stderr, "start_time = %lld, end_time = %lld\n",
+          static_cast<long long>(start_time),
+          static_cast<long long>(time(nullptr)));
   return -1;
 }
 
@@ -1171,6 +1180,9 @@
     SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
   }
 
+  SSL_CTX_set_tls13_variant(
+      ssl_ctx.get(), static_cast<enum tls13_variant_t>(config->tls13_variant));
+
   if (config->allow_unknown_alpn_protos) {
     SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1);
   }
@@ -1691,6 +1703,12 @@
     return false;
   }
 
+  if (config->tls13_variant != 0 &&
+      (!CBB_add_u16(cbb.get(), kTLS13Variant) ||
+       !CBB_add_u8(cbb.get(), static_cast<uint8_t>(config->tls13_variant)))) {
+    return false;
+  }
+
   uint8_t *settings;
   size_t settings_len;
   if (!CBB_add_u16(cbb.get(), kDataTag) ||
diff --git a/src/ssl/test/fuzzer.h b/src/ssl/test/fuzzer.h
index 2f81653..e52f55e 100644
--- a/src/ssl/test/fuzzer.h
+++ b/src/ssl/test/fuzzer.h
@@ -40,12 +40,16 @@
 // certificates.
 static const uint16_t kRequestClientCert = 2;
 
+// kTLS13Variant is followed by a u8 denoting the TLS 1.3 variant to configure.
+static const uint16_t kTLS13Variant = 3;
+
 // SetupTest parses parameters from |cbs| and returns a newly-configured |SSL|
 // object or nullptr on error. On success, the caller should feed the remaining
 // input in |cbs| to the SSL stack.
 static inline bssl::UniquePtr<SSL> SetupTest(CBS *cbs, SSL_CTX *ctx,
                                              bool is_server) {
-  // Clear any sessions saved in |ctx| from the previous run.
+  // |ctx| is shared between runs, so we must clear any modifications to it made
+  // later on in this function.
   SSL_CTX_flush_sessions(ctx, 0);
 
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
@@ -89,6 +93,18 @@
         }
         SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
         break;
+
+      case kTLS13Variant: {
+        uint8_t variant;
+        if (!CBS_get_u8(cbs, &variant)) {
+          return nullptr;
+        }
+        SSL_set_tls13_variant(ssl.get(), static_cast<tls13_variant_t>(variant));
+        break;
+      }
+
+      default:
+        return nullptr;
     }
   }
 }
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 9e7b204..fd9fb3d 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -32,10 +32,22 @@
 )
 
 // A draft version of TLS 1.3 that is sent over the wire for the current draft.
-const tls13DraftVersion = 0x7f12
+const (
+	tls13DraftVersion                = 0x7f12
+	tls13ExperimentVersion           = 0x7e01
+	tls13RecordTypeExperimentVersion = 0x7a12
+)
+
+const (
+	TLS13Default              = 0
+	TLS13Experiment           = 1
+	TLS13RecordTypeExperiment = 2
+)
 
 var allTLSWireVersions = []uint16{
 	tls13DraftVersion,
+	tls13ExperimentVersion,
+	tls13RecordTypeExperimentVersion,
 	VersionTLS12,
 	VersionTLS11,
 	VersionTLS10,
@@ -62,10 +74,11 @@
 type recordType uint8
 
 const (
-	recordTypeChangeCipherSpec recordType = 20
-	recordTypeAlert            recordType = 21
-	recordTypeHandshake        recordType = 22
-	recordTypeApplicationData  recordType = 23
+	recordTypeChangeCipherSpec   recordType = 20
+	recordTypeAlert              recordType = 21
+	recordTypeHandshake          recordType = 22
+	recordTypeApplicationData    recordType = 23
+	recordTypePlaintextHandshake recordType = 24
 )
 
 // TLS handshake message types.
@@ -404,6 +417,9 @@
 	// which is currently TLS 1.2.
 	MaxVersion uint16
 
+	// TLS13Variant is the variant of TLS 1.3 to use.
+	TLS13Variant int
+
 	// CurvePreferences contains the elliptic curves that will be used in
 	// an ECDHE handshake, in preference order. If empty, the default will
 	// be used.
@@ -1250,6 +1266,10 @@
 	// specified value in ServerHello version field.
 	SendServerHelloVersion uint16
 
+	// SendServerSupportedExtensionVersion, if non-zero, causes the server to send
+	// the specified value in supported_versions extension in the ServerHello.
+	SendServerSupportedExtensionVersion uint16
+
 	// SkipHelloRetryRequest, if true, causes the TLS 1.3 server to not send
 	// HelloRetryRequest.
 	SkipHelloRetryRequest bool
@@ -1364,6 +1384,14 @@
 	// RejectUnsolicitedKeyUpdate, if true, causes all unsolicited
 	// KeyUpdates from the peer to be rejected.
 	RejectUnsolicitedKeyUpdate bool
+
+	// OmitExtensions, if true, causes the extensions field in ClientHello
+	// and ServerHello messages to be omitted.
+	OmitExtensions bool
+
+	// EmptyExtensions, if true, causese the extensions field in ClientHello
+	// and ServerHello messages to be present, but empty.
+	EmptyExtensions bool
 }
 
 func (c *Config) serverInit() {
@@ -1468,6 +1496,12 @@
 // it returns true and the corresponding protocol version. Otherwise, it returns
 // false.
 func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
+	if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) ||
+		(c.TLS13Variant != TLS13RecordTypeExperiment && wireVers == tls13RecordTypeExperimentVersion) ||
+		(c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) {
+		return 0, false
+	}
+
 	vers, ok := wireToVersion(wireVers, isDTLS)
 	if !ok || c.minVersion(isDTLS) > vers || vers > c.maxVersion(isDTLS) {
 		return 0, false
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 61fc9d3..c974bd4 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -762,6 +762,11 @@
 		return 0, nil, c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
 	}
 
+	// Accept server_plaintext_handshake records when the content type TLS 1.3 variant is enabled.
+	if c.isClient && c.in.cipher == nil && c.config.TLS13Variant == TLS13RecordTypeExperiment && want == recordTypeHandshake && typ == recordTypePlaintextHandshake {
+		typ = recordTypeHandshake
+	}
+
 	vers := uint16(b.data[1])<<8 | uint16(b.data[2])
 	n := int(b.data[3])<<8 | int(b.data[4])
 
@@ -916,9 +921,11 @@
 			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 			break
 		}
-		err := c.in.changeCipherSpec(c.config)
-		if err != nil {
-			c.in.setErrorLocked(c.sendAlert(err.(alert)))
+		if c.wireVersion != tls13ExperimentVersion {
+			err := c.in.changeCipherSpec(c.config)
+			if err != nil {
+				c.in.setErrorLocked(c.sendAlert(err.(alert)))
+			}
 		}
 
 	case recordTypeApplicationData:
@@ -1138,15 +1145,10 @@
 	}
 	c.out.freeBlock(b)
 
-	if typ == recordTypeChangeCipherSpec {
+	if typ == recordTypeChangeCipherSpec && c.wireVersion != tls13ExperimentVersion {
 		err = c.out.changeCipherSpec(c.config)
 		if err != nil {
-			// Cannot call sendAlert directly,
-			// because we already hold c.out.Mutex.
-			c.tmp[0] = alertLevelError
-			c.tmp[1] = byte(err.(alert))
-			c.writeRecord(recordTypeAlert, c.tmp[0:2])
-			return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+			return n, c.sendAlertLocked(alertLevelError, err.(alert))
 		}
 	}
 	return
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index d46b247..72369d6 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -35,7 +35,7 @@
 		switch vers {
 		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
 			return vers, true
-		case tls13DraftVersion:
+		case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion:
 			return VersionTLS13, true
 		}
 	}
@@ -159,12 +159,7 @@
 		if typ == recordTypeChangeCipherSpec {
 			err = c.out.changeCipherSpec(c.config)
 			if err != nil {
-				// Cannot call sendAlert directly,
-				// because we already hold c.out.Mutex.
-				c.tmp[0] = alertLevelError
-				c.tmp[1] = byte(err.(alert))
-				c.writeRecord(recordTypeAlert, c.tmp[0:2])
-				return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+				return n, c.sendAlertLocked(alertLevelError, err.(alert))
 			}
 		}
 		return
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index d2f64ef..3957bea 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -19,9 +19,9 @@
 
     "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.",
-    "*Auth-Verify-ECDSA-P224-*-TLS13": "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.",
+    "*Auth-Verify-ECDSA-P224-*-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.",
@@ -36,17 +36,17 @@
     "Resume-Server-*Binder*": "Fuzzer mode does not check binders.",
 
     "SkipEarlyData*": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyDataChannelID-OfferBoth-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-NonZeroRTTSession-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-SkipEndOfEarlyData": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNMismatch-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNOmitted1-Client": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNOmitted2-Client": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNOmitted1-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyDataChannelID-OfferBoth-Server": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-NonZeroRTTSession-Server": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-SkipEndOfEarlyData": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-ALPNMismatch-Server": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-ALPNOmitted1-Client": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-ALPNOmitted2-Client": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-ALPNOmitted1-Server": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
+    "*-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher.",
 
     "Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info."
   }
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index f0bfca4..05e7311 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -84,7 +84,6 @@
 		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{},
@@ -97,6 +96,8 @@
 		srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
 		customExtension:         c.config.Bugs.CustomExtension,
 		pskBinderFirst:          c.config.Bugs.PSKBinderFirst,
+		omitExtensions:          c.config.Bugs.OmitExtensions,
+		emptyExtensions:         c.config.Bugs.EmptyExtensions,
 	}
 
 	if maxVersion >= VersionTLS13 {
@@ -104,6 +105,7 @@
 		if !c.config.Bugs.OmitSupportedVersions {
 			hello.supportedVersions = c.config.supportedVersions(c.isDTLS)
 		}
+		hello.pskKEModes = []byte{pskDHEKEMode}
 	} else {
 		hello.vers = mapClientHelloVersion(maxVersion, c.isDTLS)
 	}
@@ -732,6 +734,12 @@
 		hs.finishedHash.addEntropy(zeroSecret)
 	}
 
+	if c.wireVersion == tls13ExperimentVersion {
+		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
+			return err
+		}
+	}
+
 	// Derive handshake traffic keys and switch read key to handshake
 	// traffic key.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
@@ -911,6 +919,11 @@
 		}
 		c.sendAlert(alertEndOfEarlyData)
 	}
+
+	if c.wireVersion == tls13ExperimentVersion {
+		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+	}
+
 	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 86e2821..4be873d 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -7,6 +7,7 @@
 import (
 	"bytes"
 	"encoding/binary"
+	"fmt"
 )
 
 func writeLen(buf []byte, v, size int) {
@@ -35,6 +36,11 @@
 	return len(*bb.buf) - bb.start - bb.prefixLen
 }
 
+func (bb *byteBuilder) data() []byte {
+	bb.flush()
+	return (*bb.buf)[bb.start+bb.prefixLen:]
+}
+
 func (bb *byteBuilder) flush() {
 	if bb.child == nil {
 		return
@@ -112,11 +118,11 @@
 }
 
 func (bb *byteBuilder) discardChild() {
-	if bb.child != nil {
+	if bb.child == nil {
 		return
 	}
+	*bb.buf = (*bb.buf)[:bb.child.start]
 	bb.child = nil
-	*bb.buf = (*bb.buf)[:bb.start]
 }
 
 type keyShareEntry struct {
@@ -167,6 +173,8 @@
 	customExtension         string
 	hasGREASEExtension      bool
 	pskBinderFirst          bool
+	omitExtensions          bool
+	emptyExtensions         bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -212,7 +220,9 @@
 		m.sctListSupported == m1.sctListSupported &&
 		m.customExtension == m1.customExtension &&
 		m.hasGREASEExtension == m1.hasGREASEExtension &&
-		m.pskBinderFirst == m1.pskBinderFirst
+		m.pskBinderFirst == m1.pskBinderFirst &&
+		m.omitExtensions == m1.omitExtensions &&
+		m.emptyExtensions == m1.emptyExtensions
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -444,8 +454,12 @@
 		}
 	}
 
-	if extensions.len() == 0 {
+	if m.omitExtensions || m.emptyExtensions {
+		// Silently erase any extensions which were sent.
 		hello.discardChild()
+		if m.emptyExtensions {
+			hello.addU16(0)
+		}
 	}
 
 	m.raw = handshakeMsg.finish()
@@ -813,21 +827,24 @@
 }
 
 type serverHelloMsg struct {
-	raw               []byte
-	isDTLS            bool
-	vers              uint16
-	versOverride      uint16
-	random            []byte
-	sessionId         []byte
-	cipherSuite       uint16
-	hasKeyShare       bool
-	keyShare          keyShareEntry
-	hasPSKIdentity    bool
-	pskIdentity       uint16
-	compressionMethod uint8
-	customExtension   string
-	unencryptedALPN   string
-	extensions        serverExtensions
+	raw                   []byte
+	isDTLS                bool
+	vers                  uint16
+	versOverride          uint16
+	supportedVersOverride uint16
+	random                []byte
+	sessionId             []byte
+	cipherSuite           uint16
+	hasKeyShare           bool
+	keyShare              keyShareEntry
+	hasPSKIdentity        bool
+	pskIdentity           uint16
+	compressionMethod     uint8
+	customExtension       string
+	unencryptedALPN       string
+	omitExtensions        bool
+	emptyExtensions       bool
+	extensions            serverExtensions
 }
 
 func (m *serverHelloMsg) marshal() []byte {
@@ -848,17 +865,19 @@
 	}
 	if m.versOverride != 0 {
 		hello.addU16(m.versOverride)
+	} else if m.vers == tls13ExperimentVersion {
+		hello.addU16(VersionTLS12)
 	} else {
 		hello.addU16(m.vers)
 	}
 
 	hello.addBytes(m.random)
-	if vers < VersionTLS13 {
+	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
 		sessionId := hello.addU8LengthPrefixed()
 		sessionId.addBytes(m.sessionId)
 	}
 	hello.addU16(m.cipherSuite)
-	if vers < VersionTLS13 {
+	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
 		hello.addU8(m.compressionMethod)
 	}
 
@@ -877,6 +896,15 @@
 			extensions.addU16(2) // Length
 			extensions.addU16(m.pskIdentity)
 		}
+		if m.vers == tls13ExperimentVersion || m.supportedVersOverride != 0 {
+			extensions.addU16(extensionSupportedVersions)
+			extensions.addU16(2) // Length
+			if m.supportedVersOverride != 0 {
+				extensions.addU16(m.supportedVersOverride)
+			} else {
+				extensions.addU16(m.vers)
+			}
+		}
 		if len(m.customExtension) > 0 {
 			extensions.addU16(extensionCustom)
 			customExt := extensions.addU16LengthPrefixed()
@@ -892,8 +920,17 @@
 		}
 	} else {
 		m.extensions.marshal(extensions)
-		if extensions.len() == 0 {
+		if m.omitExtensions || m.emptyExtensions {
+			// Silently erasing server extensions will break the handshake. Instead,
+			// assert that tests which use this field also disable all features which
+			// would write an extension.
+			if extensions.len() != 0 {
+				panic(fmt.Sprintf("ServerHello unexpectedly contained extensions: %x, %+v", extensions.data(), m))
+			}
 			hello.discardChild()
+			if m.emptyExtensions {
+				hello.addU16(0)
+			}
 		}
 	}
 
@@ -913,7 +950,7 @@
 	}
 	m.random = data[6:38]
 	data = data[38:]
-	if vers < VersionTLS13 {
+	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
 		sessionIdLen := int(data[0])
 		if sessionIdLen > 32 || len(data) < 1+sessionIdLen {
 			return false
@@ -926,7 +963,7 @@
 	}
 	m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
 	data = data[2:]
-	if vers < VersionTLS13 {
+	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
 		if len(data) < 1 {
 			return false
 		}
@@ -949,6 +986,36 @@
 		return false
 	}
 
+	// Parse out the version from supported_versions if available.
+	if m.vers == VersionTLS12 {
+		vdata := data
+		for len(vdata) != 0 {
+			if len(vdata) < 4 {
+				return false
+			}
+			extension := uint16(vdata[0])<<8 | uint16(vdata[1])
+			length := int(vdata[2])<<8 | int(vdata[3])
+			vdata = vdata[4:]
+
+			if len(vdata) < length {
+				return false
+			}
+			d := vdata[:length]
+			vdata = vdata[length:]
+
+			if extension == extensionSupportedVersions {
+				if len(d) < 2 {
+					return false
+				}
+				m.vers = uint16(d[0])<<8 | uint16(d[1])
+				vers, ok = wireToVersion(m.vers, m.isDTLS)
+				if !ok {
+					return false
+				}
+			}
+		}
+	}
+
 	if vers >= VersionTLS13 {
 		for len(data) != 0 {
 			if len(data) < 4 {
@@ -983,6 +1050,10 @@
 				}
 				m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
 				m.hasPSKIdentity = true
+			case extensionSupportedVersions:
+				if m.vers != tls13ExperimentVersion {
+					return false
+				}
 			default:
 				// Only allow the 3 extensions that are sent in
 				// the clear in TLS 1.3.
@@ -1059,6 +1130,7 @@
 	hasKeyShare             bool
 	hasEarlyData            bool
 	keyShare                keyShareEntry
+	supportedVersion        uint16
 	supportedPoints         []uint8
 	serverNameAck           bool
 }
@@ -1155,6 +1227,11 @@
 		keyExchange := keyShare.addU16LengthPrefixed()
 		keyExchange.addBytes(m.keyShare.keyExchange)
 	}
+	if m.supportedVersion != 0 {
+		extensions.addU16(extensionSupportedVersions)
+		extensions.addU16(2) // Length
+		extensions.addU16(m.supportedVersion)
+	}
 	if len(m.supportedPoints) > 0 {
 		// http://tools.ietf.org/html/rfc4492#section-5.1.2
 		extensions.addU16(extensionSupportedPoints)
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index f70f469..b31a562 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -281,7 +281,7 @@
 	}
 
 	if config.Bugs.ExpectNoTLS12Session {
-		if len(hs.clientHello.sessionId) > 0 {
+		if len(hs.clientHello.sessionId) > 0 && c.wireVersion != tls13ExperimentVersion {
 			return fmt.Errorf("tls: client offered an unexpected session ID")
 		}
 		if len(hs.clientHello.sessionTicket) > 0 {
@@ -359,11 +359,13 @@
 	config := c.config
 
 	hs.hello = &serverHelloMsg{
-		isDTLS:          c.isDTLS,
-		vers:            c.wireVersion,
-		versOverride:    config.Bugs.SendServerHelloVersion,
-		customExtension: config.Bugs.CustomUnencryptedExtension,
-		unencryptedALPN: config.Bugs.SendUnencryptedALPN,
+		isDTLS:                c.isDTLS,
+		vers:                  c.wireVersion,
+		sessionId:             hs.clientHello.sessionId,
+		versOverride:          config.Bugs.SendServerHelloVersion,
+		supportedVersOverride: config.Bugs.SendServerSupportedExtensionVersion,
+		customExtension:       config.Bugs.CustomUnencryptedExtension,
+		unencryptedALPN:       config.Bugs.SendUnencryptedALPN,
 	}
 
 	hs.hello.random = make([]byte, 32)
@@ -571,7 +573,11 @@
 	if sendHelloRetryRequest {
 		oldClientHelloBytes := hs.clientHello.marshal()
 		hs.writeServerHash(helloRetryRequest.marshal())
-		c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
+		if c.vers == tls13RecordTypeExperimentVersion {
+			c.writeRecord(recordTypePlaintextHandshake, helloRetryRequest.marshal())
+		} else {
+			c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
+		}
 		c.flushHandshake()
 
 		if hs.clientHello.hasEarlyData {
@@ -749,10 +755,18 @@
 		toWrite = append(toWrite, typeEncryptedExtensions)
 		c.writeRecord(recordTypeHandshake, toWrite)
 	} else {
-		c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+		if c.vers == tls13RecordTypeExperimentVersion {
+			c.writeRecord(recordTypePlaintextHandshake, hs.hello.marshal())
+		} else {
+			c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+		}
 	}
 	c.flushHandshake()
 
+	if c.wireVersion == tls13ExperimentVersion {
+		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+	}
+
 	// Switch to handshake traffic keys.
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
 	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
@@ -913,6 +927,12 @@
 		}
 	}
 
+	if c.wireVersion == tls13ExperimentVersion && !c.skipEarlyData {
+		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
+			return err
+		}
+	}
+
 	// Switch input stream to handshake traffic keys.
 	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
@@ -1045,6 +1065,11 @@
 		vers:              c.wireVersion,
 		versOverride:      config.Bugs.SendServerHelloVersion,
 		compressionMethod: config.Bugs.SendCompressionMethod,
+		extensions: serverExtensions{
+			supportedVersion: config.Bugs.SendServerSupportedExtensionVersion,
+		},
+		omitExtensions:  config.Bugs.OmitExtensions,
+		emptyExtensions: config.Bugs.EmptyExtensions,
 	}
 
 	hs.hello.random = make([]byte, 32)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 03ba755..29747db 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -263,12 +263,9 @@
 	panic("Unknown test certificate")
 }
 
-// configVersionToWire maps a protocol version to the default wire version to
-// test at that protocol.
-//
-// TODO(davidben): Rather than mapping these, make tlsVersions contains a list
-// of wire versions and test all of them.
-func configVersionToWire(vers uint16, protocol protocol) uint16 {
+// recordVersionToWire maps a record-layer protocol version to its wire
+// representation.
+func recordVersionToWire(vers uint16, protocol protocol) uint16 {
 	if protocol == dtls {
 		switch vers {
 		case VersionTLS12:
@@ -280,8 +277,6 @@
 		switch vers {
 		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
 			return vers
-		case VersionTLS13:
-			return tls13DraftVersion
 		}
 	}
 
@@ -461,6 +456,10 @@
 	// resumeShimPrefix is the prefix that the shim will send to the server on a
 	// resumption.
 	resumeShimPrefix string
+	// tls13Variant, if non-zero, causes both runner and shim to be
+	// configured with the specified TLS 1.3 variant. This is a convenience
+	// option for configuring both concurrently.
+	tls13Variant int
 }
 
 var testCases []testCase
@@ -882,18 +881,26 @@
 // exit first.
 func acceptOrWait(listener *net.TCPListener, waitChan chan error) (net.Conn, error) {
 	type connOrError struct {
-		conn net.Conn
-		err  error
+		conn               net.Conn
+		err                error
+		startTime, endTime time.Time
 	}
 	connChan := make(chan connOrError, 1)
 	go func() {
+		startTime := time.Now()
 		listener.SetDeadline(time.Now().Add(*idleTimeout))
 		conn, err := listener.Accept()
-		connChan <- connOrError{conn, err}
+		endTime := time.Now()
+		connChan <- connOrError{conn, err, startTime, endTime}
 		close(connChan)
 	}()
 	select {
 	case result := <-connChan:
+		if result.err != nil {
+			// TODO(davidben): Remove this logging when
+			// https://crbug.com/boringssl/199 is resolved.
+			fmt.Fprintf(os.Stderr, "acceptOrWait failed, startTime=%v, endTime=%v\n", result.startTime, result.endTime)
+		}
 		return result.conn, result.err
 	case childErr := <-waitChan:
 		waitChan <- childErr
@@ -935,11 +942,23 @@
 			continue
 		}
 
-		if test.config.MaxVersion != 0 || test.config.MinVersion != 0 || test.expectedVersion != 0 {
-			continue
+		if test.config.MaxVersion == 0 && test.config.MinVersion == 0 && test.expectedVersion == 0 {
+			panic(fmt.Sprintf("The name of test %q suggests that it's version specific, but min/max version in the Config is %x/%x. One of them should probably be %x", test.name, test.config.MinVersion, test.config.MaxVersion, ver.version))
 		}
 
-		panic(fmt.Sprintf("The name of test %q suggests that it's version specific, but min/max version in the Config is %x/%x. One of them should probably be %x", test.name, test.config.MinVersion, test.config.MaxVersion, ver.version))
+		if ver.tls13Variant != 0 {
+			var foundFlag bool
+			for _, flag := range test.flags {
+				if flag == "-tls13-variant" {
+					foundFlag = true
+					break
+				}
+			}
+			if !foundFlag && test.config.TLS13Variant != ver.tls13Variant && test.tls13Variant != ver.tls13Variant {
+				panic(fmt.Sprintf("The name of test %q suggests that uses an experimental TLS 1.3 variant, but neither the shim nor the runner configures it", test.name))
+			}
+		}
+
 	}
 
 	listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv6loopback})
@@ -1018,6 +1037,11 @@
 		flags = append(flags, "-tls-unique")
 	}
 
+	if test.tls13Variant != 0 {
+		test.config.TLS13Variant = test.tls13Variant
+		flags = append(flags, "-tls13-variant", strconv.Itoa(test.tls13Variant))
+	}
+
 	var transcriptPrefix string
 	if len(*transcriptDir) != 0 {
 		protocol := "tls"
@@ -1191,29 +1215,100 @@
 }
 
 type tlsVersion struct {
-	name    string
+	name string
+	// version is the protocol version.
 	version uint16
 	// excludeFlag is the legacy shim flag to disable the version.
 	excludeFlag string
 	hasDTLS     bool
-	// shimTLS and shimDTLS are values the shim uses to refer to these
-	// versions in TLS and DTLS, respectively.
-	shimTLS, shimDTLS int
+	// versionDTLS, if non-zero, is the DTLS-specific representation of the version.
+	versionDTLS uint16
+	// versionWire, if non-zero, is the wire representation of the
+	// version. Otherwise the wire version is the protocol version or
+	// versionDTLS.
+	versionWire  uint16
+	tls13Variant int
 }
 
 func (vers tlsVersion) shimFlag(protocol protocol) string {
-	if protocol == dtls {
-		return strconv.Itoa(vers.shimDTLS)
+	// The shim uses the protocol version in its public API, but uses the
+	// DTLS-specific version if it exists.
+	if protocol == dtls && vers.versionDTLS != 0 {
+		return strconv.Itoa(int(vers.versionDTLS))
 	}
-	return strconv.Itoa(vers.shimTLS)
+	return strconv.Itoa(int(vers.version))
+}
+
+func (vers tlsVersion) wire(protocol protocol) uint16 {
+	if protocol == dtls && vers.versionDTLS != 0 {
+		return vers.versionDTLS
+	}
+	if vers.versionWire != 0 {
+		return vers.versionWire
+	}
+	return vers.version
 }
 
 var tlsVersions = []tlsVersion{
-	{"SSL3", VersionSSL30, "-no-ssl3", false, VersionSSL30, 0},
-	{"TLS1", VersionTLS10, "-no-tls1", true, VersionTLS10, VersionDTLS10},
-	{"TLS11", VersionTLS11, "-no-tls11", false, VersionTLS11, 0},
-	{"TLS12", VersionTLS12, "-no-tls12", true, VersionTLS12, VersionDTLS12},
-	{"TLS13", VersionTLS13, "-no-tls13", false, VersionTLS13, 0},
+	{
+		name:        "SSL3",
+		version:     VersionSSL30,
+		excludeFlag: "-no-ssl3",
+	},
+	{
+		name:        "TLS1",
+		version:     VersionTLS10,
+		excludeFlag: "-no-tls1",
+		hasDTLS:     true,
+		versionDTLS: VersionDTLS10,
+	},
+	{
+		name:        "TLS11",
+		version:     VersionTLS11,
+		excludeFlag: "-no-tls11",
+	},
+	{
+		name:        "TLS12",
+		version:     VersionTLS12,
+		excludeFlag: "-no-tls12",
+		hasDTLS:     true,
+		versionDTLS: VersionDTLS12,
+	},
+	{
+		name:         "TLS13",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13DraftVersion,
+		tls13Variant: TLS13Default,
+	},
+	{
+		name:         "TLS13Experiment",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13ExperimentVersion,
+		tls13Variant: TLS13Experiment,
+	},
+	{
+		name:         "TLS13RecordTypeExperiment",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13RecordTypeExperimentVersion,
+		tls13Variant: TLS13RecordTypeExperiment,
+	},
+}
+
+func allVersions(protocol protocol) []tlsVersion {
+	if protocol == tls {
+		return tlsVersions
+	}
+
+	var ret []tlsVersion
+	for _, vers := range tlsVersions {
+		if vers.hasDTLS {
+			ret = append(ret, vers)
+		}
+	}
+	return ret
 }
 
 type testCipherSuite struct {
@@ -2735,6 +2830,7 @@
 				AdvertiseAllConfiguredCiphers: true,
 			},
 		},
+		tls13Variant:         ver.tls13Variant,
 		certFile:             certFile,
 		keyFile:              keyFile,
 		flags:                flags,
@@ -2760,6 +2856,7 @@
 				SendCipherSuite:             sendCipherSuite,
 			},
 		},
+		tls13Variant:         ver.tls13Variant,
 		flags:                flags,
 		resumeSession:        true,
 		shouldFail:           shouldClientFail,
@@ -2783,8 +2880,9 @@
 			PreSharedKey:         []byte(psk),
 			PreSharedKeyIdentity: pskIdentity,
 		},
-		flags:      flags,
-		messageLen: maxPlaintext,
+		tls13Variant: ver.tls13Variant,
+		flags:        flags,
+		messageLen:   maxPlaintext,
 	})
 
 	// Test bad records for all ciphers. Bad records are fatal in TLS
@@ -2807,6 +2905,7 @@
 			PreSharedKey:         []byte(psk),
 			PreSharedKeyIdentity: pskIdentity,
 		},
+		tls13Variant:     ver.tls13Variant,
 		flags:            flags,
 		damageFirstWrite: true,
 		messageLen:       maxPlaintext,
@@ -3274,6 +3373,7 @@
 				ClientAuth: RequireAnyClientCert,
 				ClientCAs:  certPool,
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 				"-key-file", path.Join(*resourceDir, rsaKeyFile),
@@ -3287,7 +3387,8 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaCertificate},
 			},
-			flags: []string{"-require-any-client-certificate"},
+			tls13Variant: ver.tls13Variant,
+			flags:        []string{"-require-any-client-certificate"},
 		})
 		if ver.version != VersionSSL30 {
 			testCases = append(testCases, testCase{
@@ -3298,7 +3399,8 @@
 					MaxVersion:   ver.version,
 					Certificates: []Certificate{ecdsaP256Certificate},
 				},
-				flags: []string{"-require-any-client-certificate"},
+				tls13Variant: ver.tls13Variant,
+				flags:        []string{"-require-any-client-certificate"},
 			})
 			testCases = append(testCases, testCase{
 				testType: clientTest,
@@ -3309,6 +3411,7 @@
 					ClientAuth: RequireAnyClientCert,
 					ClientCAs:  certPool,
 				},
+				tls13Variant: ver.tls13Variant,
 				flags: []string{
 					"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
 					"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
@@ -3323,6 +3426,7 @@
 				MaxVersion: ver.version,
 				ClientAuth: RequireAnyClientCert,
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedLocalError: "client didn't provide a certificate",
 		})
@@ -3336,6 +3440,7 @@
 				MinVersion: ver.version,
 				MaxVersion: ver.version,
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-expect-verify-result",
 			},
@@ -3351,6 +3456,7 @@
 				MinVersion: ver.version,
 				MaxVersion: ver.version,
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-expect-verify-result",
 				"-verify-peer",
@@ -3372,6 +3478,7 @@
 				MaxVersion: ver.version,
 			},
 			flags:              []string{"-require-any-client-certificate"},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedError:      ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
 			expectedLocalError: certificateRequired,
@@ -3390,6 +3497,7 @@
 				},
 				// Setting SSL_VERIFY_PEER allows anonymous clients.
 				flags:         []string{"-verify-peer"},
+				tls13Variant:  ver.tls13Variant,
 				shouldFail:    true,
 				expectedError: ":UNEXPECTED_MESSAGE:",
 			})
@@ -3405,6 +3513,7 @@
 					"-enable-channel-id",
 					"-verify-peer-if-no-obc",
 				},
+				tls13Variant:       ver.tls13Variant,
 				shouldFail:         true,
 				expectedError:      ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
 				expectedLocalError: certificateRequired,
@@ -3419,6 +3528,7 @@
 					ChannelID:  channelIDKey,
 				},
 				expectChannelID: true,
+				tls13Variant:    ver.tls13Variant,
 				flags: []string{
 					"-enable-channel-id",
 					"-verify-peer-if-no-obc",
@@ -3437,6 +3547,7 @@
 					ExpectCertificateReqNames: caNames,
 				},
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-require-any-client-certificate",
 				"-use-client-ca-list", encodeDERValues(caNames),
@@ -3453,6 +3564,7 @@
 				ClientAuth:   RequireAnyClientCert,
 				ClientCAs:    certPool,
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 				"-key-file", path.Join(*resourceDir, rsaKeyFile),
@@ -3552,8 +3664,9 @@
 							RequireExtendedMasterSecret: with,
 						},
 					},
-					flags:      flags,
-					shouldFail: ver.version == VersionSSL30 && with,
+					tls13Variant: ver.tls13Variant,
+					flags:        flags,
+					shouldFail:   ver.version == VersionSSL30 && with,
 				}
 				if test.shouldFail {
 					test.expectedLocalError = "extended master secret required but not supported by peer"
@@ -3892,6 +4005,62 @@
 
 		tests = append(tests, testCase{
 			testType: clientTest,
+			name:     "TLS13Experiment-EarlyData-Client",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				TLS13Variant:     TLS13Experiment,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				TLS13Variant:     TLS13Experiment,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
+				},
+			},
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
+				"-tls13-variant", "1",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "TLS13RecordTypeExperiment-EarlyData-Client",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				TLS13Variant:     TLS13RecordTypeExperiment,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				TLS13Variant:     TLS13RecordTypeExperiment,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
+				},
+			},
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
+				"-tls13-variant", "2",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: clientTest,
 			name:     "TLS13-EarlyData-TooMuchData-Client",
 			config: Config{
 				MaxVersion:       VersionTLS13,
@@ -3998,6 +4167,50 @@
 
 		tests = append(tests, testCase{
 			testType: serverTest,
+			name:     "TLS13Experiment-EarlyData-Server",
+			config: Config{
+				MaxVersion:   VersionTLS13,
+				MinVersion:   VersionTLS13,
+				TLS13Variant: TLS13Experiment,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				},
+			},
+			messageCount:  2,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+				"-tls13-variant", "1",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: serverTest,
+			name:     "TLS13RecordTypeExperiment-EarlyData-Server",
+			config: Config{
+				MaxVersion:   VersionTLS13,
+				MinVersion:   VersionTLS13,
+				TLS13Variant: TLS13RecordTypeExperiment,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				},
+			},
+			messageCount:  2,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+				"-tls13-variant", "2",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: serverTest,
 			name:     "TLS13-MaxEarlyData-Server",
 			config: Config{
 				MaxVersion: VersionTLS13,
@@ -4353,6 +4566,7 @@
 					MaxVersion:   vers.version,
 					Certificates: []Certificate{rsaCertificate},
 				},
+				tls13Variant: vers.tls13Variant,
 				flags: []string{
 					flag,
 					"-expect-verify-result",
@@ -4366,6 +4580,7 @@
 					MaxVersion:   vers.version,
 					Certificates: []Certificate{rsaCertificate},
 				},
+				tls13Variant: vers.tls13Variant,
 				flags: []string{
 					flag,
 					"-verify-fail",
@@ -4384,6 +4599,7 @@
 				MaxVersion:   vers.version,
 				Certificates: []Certificate{rsaCertificate},
 			},
+			tls13Variant: vers.tls13Variant,
 			flags: []string{
 				"-verify-fail",
 				"-expect-verify-result",
@@ -4543,6 +4759,7 @@
 					MaxVersion:       ver.version,
 					RequestChannelID: true,
 				},
+				tls13Variant:    ver.tls13Variant,
 				flags:           []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
 				resumeSession:   true,
 				expectChannelID: true,
@@ -4556,6 +4773,7 @@
 					MaxVersion: ver.version,
 					ChannelID:  channelIDKey,
 				},
+				tls13Variant: ver.tls13Variant,
 				flags: []string{
 					"-expect-channel-id",
 					base64.StdEncoding.EncodeToString(channelIDBytes),
@@ -4574,6 +4792,7 @@
 						InvalidChannelIDSignature: true,
 					},
 				},
+				tls13Variant:  ver.tls13Variant,
 				flags:         []string{"-enable-channel-id"},
 				shouldFail:    true,
 				expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
@@ -4782,24 +5001,42 @@
 }
 
 func addVersionNegotiationTests() {
-	for i, shimVers := range tlsVersions {
-		// Assemble flags to disable all newer versions on the shim.
-		var flags []string
-		for _, vers := range tlsVersions[i+1:] {
-			flags = append(flags, vers.excludeFlag)
-		}
-
-		// Test configuring the runner's maximum version.
-		for _, runnerVers := range tlsVersions {
-			protocols := []protocol{tls}
-			if runnerVers.hasDTLS && shimVers.hasDTLS {
-				protocols = append(protocols, dtls)
+	for _, protocol := range []protocol{tls, dtls} {
+		for _, shimVers := range allVersions(protocol) {
+			// Assemble flags to disable all newer versions on the shim.
+			var flags []string
+			for _, vers := range allVersions(protocol) {
+				if vers.version > shimVers.version {
+					flags = append(flags, vers.excludeFlag)
+				}
 			}
-			for _, protocol := range protocols {
+
+			flags2 := []string{"-max-version", shimVers.shimFlag(protocol)}
+
+			if shimVers.tls13Variant != 0 {
+				flags = append(flags, "-tls13-variant", strconv.Itoa(shimVers.tls13Variant))
+				flags2 = append(flags2, "-tls13-variant", strconv.Itoa(shimVers.tls13Variant))
+			}
+
+			// Test configuring the runner's maximum version.
+			for _, runnerVers := range allVersions(protocol) {
 				expectedVersion := shimVers.version
 				if runnerVers.version < shimVers.version {
 					expectedVersion = runnerVers.version
 				}
+				// When running and shim have different TLS 1.3 variants enabled,
+				// shim clients are expected to fall back to TLS 1.2, while shim
+				// servers support both variants when enabled when the experiment is
+				// enabled.
+				expectedServerVersion := expectedVersion
+				expectedClientVersion := expectedVersion
+				if expectedVersion == VersionTLS13 && runnerVers.tls13Variant != shimVers.tls13Variant {
+					expectedClientVersion = VersionTLS12
+					expectedServerVersion = VersionTLS12
+					if shimVers.tls13Variant != TLS13Default {
+						expectedServerVersion = VersionTLS13
+					}
+				}
 
 				suffix := shimVers.name + "-" + runnerVers.name
 				if protocol == dtls {
@@ -4811,38 +5048,40 @@
 				if clientVers > VersionTLS10 {
 					clientVers = VersionTLS10
 				}
-				clientVers = configVersionToWire(clientVers, protocol)
-				serverVers := expectedVersion
-				if expectedVersion >= VersionTLS13 {
+				clientVers = recordVersionToWire(clientVers, protocol)
+				serverVers := expectedServerVersion
+				if expectedServerVersion >= VersionTLS13 {
 					serverVers = VersionTLS10
 				}
-				serverVers = configVersionToWire(serverVers, protocol)
+				serverVers = recordVersionToWire(serverVers, protocol)
 
 				testCases = append(testCases, testCase{
 					protocol: protocol,
 					testType: clientTest,
 					name:     "VersionNegotiation-Client-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							ExpectInitialRecordVersion: clientVers,
 						},
 					},
 					flags:           flags,
-					expectedVersion: expectedVersion,
+					expectedVersion: expectedClientVersion,
 				})
 				testCases = append(testCases, testCase{
 					protocol: protocol,
 					testType: clientTest,
 					name:     "VersionNegotiation-Client2-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							ExpectInitialRecordVersion: clientVers,
 						},
 					},
-					flags:           []string{"-max-version", shimVers.shimFlag(protocol)},
-					expectedVersion: expectedVersion,
+					flags:           flags2,
+					expectedVersion: expectedClientVersion,
 				})
 
 				testCases = append(testCases, testCase{
@@ -4850,26 +5089,28 @@
 					testType: serverTest,
 					name:     "VersionNegotiation-Server-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							ExpectInitialRecordVersion: serverVers,
 						},
 					},
 					flags:           flags,
-					expectedVersion: expectedVersion,
+					expectedVersion: expectedServerVersion,
 				})
 				testCases = append(testCases, testCase{
 					protocol: protocol,
 					testType: serverTest,
 					name:     "VersionNegotiation-Server2-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							ExpectInitialRecordVersion: serverVers,
 						},
 					},
-					flags:           []string{"-max-version", shimVers.shimFlag(protocol)},
-					expectedVersion: expectedVersion,
+					flags:           flags2,
+					expectedVersion: expectedServerVersion,
 				})
 			}
 		}
@@ -4887,20 +5128,20 @@
 				suffix += "-DTLS"
 			}
 
-			wireVersion := configVersionToWire(vers.version, protocol)
 			testCases = append(testCases, testCase{
 				protocol: protocol,
 				testType: serverTest,
 				name:     "VersionNegotiationExtension-" + suffix,
 				config: Config{
+					TLS13Variant: vers.tls13Variant,
 					Bugs: ProtocolBugs{
-						SendSupportedVersions: []uint16{0x1111, wireVersion, 0x2222},
+						SendSupportedVersions: []uint16{0x1111, vers.wire(protocol), 0x2222},
 					},
 				},
 				expectedVersion: vers.version,
+				flags:           []string{"-tls13-variant", strconv.Itoa(vers.tls13Variant)},
 			})
 		}
-
 	}
 
 	// If all versions are unknown, negotiation fails.
@@ -4980,6 +5221,36 @@
 		expectedVersion: VersionTLS12,
 	})
 
+	// Test that TLS 1.2 isn't negotiated by the supported_versions extension in
+	// the ServerHello.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "SupportedVersionSelection-TLS12",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				SendServerSupportedExtensionVersion: VersionTLS12,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
+	// Test that the non-experimental TLS 1.3 isn't negotiated by the
+	// supported_versions extension in the ServerHello.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "SupportedVersionSelection-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendServerSupportedExtensionVersion: tls13DraftVersion,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
+
 	// Test that the maximum version is selected regardless of the
 	// client-sent order.
 	testCases = append(testCases, testCase{
@@ -5121,19 +5392,34 @@
 }
 
 func addMinimumVersionTests() {
-	for i, shimVers := range tlsVersions {
-		// Assemble flags to disable all older versions on the shim.
-		var flags []string
-		for _, vers := range tlsVersions[:i] {
-			flags = append(flags, vers.excludeFlag)
-		}
-
-		for _, runnerVers := range tlsVersions {
-			protocols := []protocol{tls}
-			if runnerVers.hasDTLS && shimVers.hasDTLS {
-				protocols = append(protocols, dtls)
+	for _, protocol := range []protocol{tls, dtls} {
+		for _, shimVers := range allVersions(protocol) {
+			// Assemble flags to disable all older versions on the shim.
+			var flags []string
+			for _, vers := range allVersions(protocol) {
+				if vers.version < shimVers.version {
+					flags = append(flags, vers.excludeFlag)
+				}
 			}
-			for _, protocol := range protocols {
+
+			flags2 := []string{"-min-version", shimVers.shimFlag(protocol)}
+
+			if shimVers.tls13Variant != 0 {
+				flags = append(flags, "-tls13-variant", strconv.Itoa(shimVers.tls13Variant))
+				flags2 = append(flags2, "-tls13-variant", strconv.Itoa(shimVers.tls13Variant))
+			}
+
+			for _, runnerVers := range allVersions(protocol) {
+				// Different TLS 1.3 variants are incompatible with each other and don't
+				// produce consistent minimum versions.
+				//
+				// TODO(davidben): Fold these tests (the main value is in the
+				// NegotiateVersion bug) into addVersionNegotiationTests and test based
+				// on intended shim behavior, not the shim + runner combination.
+				if shimVers.tls13Variant != runnerVers.tls13Variant {
+					continue
+				}
+
 				suffix := shimVers.name + "-" + runnerVers.name
 				if protocol == dtls {
 					suffix += "-DTLS"
@@ -5155,12 +5441,13 @@
 					testType: clientTest,
 					name:     "MinimumVersion-Client-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							// Ensure the server does not decline to
 							// select a version (versions extension) or
 							// cipher (some ciphers depend on versions).
-							NegotiateVersion:            configVersionToWire(runnerVers.version, protocol),
+							NegotiateVersion:            runnerVers.wire(protocol),
 							IgnorePeerCipherPreferences: shouldFail,
 						},
 					},
@@ -5175,16 +5462,17 @@
 					testType: clientTest,
 					name:     "MinimumVersion-Client2-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							// Ensure the server does not decline to
 							// select a version (versions extension) or
 							// cipher (some ciphers depend on versions).
-							NegotiateVersion:            configVersionToWire(runnerVers.version, protocol),
+							NegotiateVersion:            runnerVers.wire(protocol),
 							IgnorePeerCipherPreferences: shouldFail,
 						},
 					},
-					flags:              []string{"-min-version", shimVers.shimFlag(protocol)},
+					flags:              flags2,
 					expectedVersion:    expectedVersion,
 					shouldFail:         shouldFail,
 					expectedError:      expectedError,
@@ -5196,7 +5484,8 @@
 					testType: serverTest,
 					name:     "MinimumVersion-Server-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 					},
 					flags:              flags,
 					expectedVersion:    expectedVersion,
@@ -5209,9 +5498,10 @@
 					testType: serverTest,
 					name:     "MinimumVersion-Server2-" + suffix,
 					config: Config{
-						MaxVersion: runnerVers.version,
+						MaxVersion:   runnerVers.version,
+						TLS13Variant: runnerVers.tls13Variant,
 					},
-					flags:              []string{"-min-version", shimVers.shimFlag(protocol)},
+					flags:              flags2,
 					expectedVersion:    expectedVersion,
 					shouldFail:         shouldFail,
 					expectedError:      expectedError,
@@ -5243,6 +5533,7 @@
 					DuplicateExtension: true,
 				},
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedLocalError: "remote error: error decoding message",
 		})
@@ -5255,6 +5546,7 @@
 					DuplicateExtension: true,
 				},
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedLocalError: "remote error: error decoding message",
 		})
@@ -5269,7 +5561,8 @@
 					ExpectServerName: "example.com",
 				},
 			},
-			flags: []string{"-host-name", "example.com"},
+			tls13Variant: ver.tls13Variant,
+			flags:        []string{"-host-name", "example.com"},
 		})
 		testCases = append(testCases, testCase{
 			testType: clientTest,
@@ -5281,6 +5574,7 @@
 				},
 			},
 			flags:              []string{"-host-name", "example.com"},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedLocalError: "tls: unexpected server name",
 		})
@@ -5293,6 +5587,7 @@
 					ExpectServerName: "missing.com",
 				},
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedLocalError: "tls: unexpected server name",
 		})
@@ -5305,6 +5600,7 @@
 					SendServerNameAck: true,
 				},
 			},
+			tls13Variant:  ver.tls13Variant,
 			flags:         []string{"-host-name", "example.com"},
 			resumeSession: true,
 		})
@@ -5317,6 +5613,7 @@
 					SendServerNameAck: true,
 				},
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedError:      ":UNEXPECTED_EXTENSION:",
 			expectedLocalError: "remote error: unsupported extension",
@@ -5328,6 +5625,7 @@
 				MaxVersion: ver.version,
 				ServerName: "example.com",
 			},
+			tls13Variant:  ver.tls13Variant,
 			flags:         []string{"-expect-server-name", "example.com"},
 			resumeSession: true,
 		})
@@ -5344,6 +5642,7 @@
 				"-advertise-alpn", "\x03foo\x03bar\x03baz",
 				"-expect-alpn", "foo",
 			},
+			tls13Variant:          ver.tls13Variant,
 			expectedNextProto:     "foo",
 			expectedNextProtoType: alpn,
 			resumeSession:         true,
@@ -5360,6 +5659,7 @@
 			flags: []string{
 				"-advertise-alpn", "\x03foo\x03bar",
 			},
+			tls13Variant:       ver.tls13Variant,
 			shouldFail:         true,
 			expectedError:      ":INVALID_ALPN_PROTOCOL:",
 			expectedLocalError: "remote error: illegal parameter",
@@ -5378,6 +5678,7 @@
 				"-allow-unknown-alpn-protos",
 				"-expect-alpn", "baz",
 			},
+			tls13Variant: ver.tls13Variant,
 		})
 		testCases = append(testCases, testCase{
 			testType: serverTest,
@@ -5390,6 +5691,7 @@
 				"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
 				"-select-alpn", "foo",
 			},
+			tls13Variant:          ver.tls13Variant,
 			expectedNextProto:     "foo",
 			expectedNextProtoType: alpn,
 			resumeSession:         true,
@@ -5402,6 +5704,7 @@
 				NextProtos: []string{"foo", "bar", "baz"},
 			},
 			flags:             []string{"-decline-alpn"},
+			tls13Variant:      ver.tls13Variant,
 			expectNoNextProto: true,
 			resumeSession:     true,
 		})
@@ -5422,6 +5725,7 @@
 				"-select-alpn", "foo",
 				"-async",
 			},
+			tls13Variant:          ver.tls13Variant,
 			expectedNextProto:     "foo",
 			expectedNextProtoType: alpn,
 			resumeSession:         true,
@@ -5443,6 +5747,7 @@
 			flags: []string{
 				"-advertise-alpn", "\x03foo",
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":PARSE_TLSEXT:",
 		})
@@ -5458,6 +5763,7 @@
 			flags: []string{
 				"-select-alpn", "foo",
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":PARSE_TLSEXT:",
 		})
@@ -5477,6 +5783,7 @@
 					"-select-alpn", "foo",
 					"-advertise-npn", "\x03foo\x03bar\x03baz",
 				},
+				tls13Variant:          ver.tls13Variant,
 				expectedNextProto:     "foo",
 				expectedNextProtoType: alpn,
 				resumeSession:         true,
@@ -5496,6 +5803,7 @@
 					"-select-alpn", "foo",
 					"-advertise-npn", "\x03foo\x03bar\x03baz",
 				},
+				tls13Variant:          ver.tls13Variant,
 				expectedNextProto:     "foo",
 				expectedNextProtoType: alpn,
 				resumeSession:         true,
@@ -5515,6 +5823,7 @@
 					"-advertise-alpn", "\x03foo",
 					"-select-next-proto", "foo",
 				},
+				tls13Variant:  ver.tls13Variant,
 				shouldFail:    true,
 				expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
 			})
@@ -5532,6 +5841,7 @@
 					"-advertise-alpn", "\x03foo",
 					"-select-next-proto", "foo",
 				},
+				tls13Variant:  ver.tls13Variant,
 				shouldFail:    true,
 				expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
 			})
@@ -5552,6 +5862,7 @@
 					},
 				},
 			},
+			tls13Variant:         ver.tls13Variant,
 			resumeSession:        true,
 			expectResumeRejected: true,
 		})
@@ -5562,6 +5873,7 @@
 			config: Config{
 				MaxVersion: ver.version,
 			},
+			tls13Variant:  ver.tls13Variant,
 			resumeSession: true,
 			flags:         []string{"-use-ticket-callback"},
 		})
@@ -5574,6 +5886,7 @@
 					ExpectNewTicket: true,
 				},
 			},
+			tls13Variant:  ver.tls13Variant,
 			flags:         []string{"-use-ticket-callback", "-renew-ticket"},
 			resumeSession: true,
 		})
@@ -5593,6 +5906,7 @@
 					},
 				},
 			},
+			tls13Variant:         ver.tls13Variant,
 			resumeSession:        true,
 			expectResumeRejected: true,
 			flags: []string{
@@ -5748,6 +6062,7 @@
 				"-expect-signed-cert-timestamps",
 				base64.StdEncoding.EncodeToString(testSCTList),
 			},
+			tls13Variant:  ver.tls13Variant,
 			resumeSession: true,
 		})
 
@@ -5770,6 +6085,7 @@
 				"-expect-signed-cert-timestamps",
 				base64.StdEncoding.EncodeToString(testSCTList),
 			},
+			tls13Variant:  ver.tls13Variant,
 			resumeSession: true,
 		})
 
@@ -5783,6 +6099,7 @@
 				"-signed-cert-timestamps",
 				base64.StdEncoding.EncodeToString(testSCTList),
 			},
+			tls13Variant:    ver.tls13Variant,
 			expectedSCTList: testSCTList,
 			resumeSession:   true,
 		})
@@ -5801,6 +6118,7 @@
 			flags: []string{
 				"-enable-signed-cert-timestamps",
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":ERROR_PARSING_EXTENSION:",
 		})
@@ -5819,6 +6137,7 @@
 			flags: []string{
 				"-enable-signed-cert-timestamps",
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":ERROR_PARSING_EXTENSION:",
 		})
@@ -5834,6 +6153,7 @@
 					NoSignedCertificateTimestamps: true,
 				},
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-ocsp-response",
 				base64.StdEncoding.EncodeToString(testOCSPResponse),
@@ -6178,13 +6498,20 @@
 					suffix += "-DTLS"
 				}
 
+				// We can't resume across TLS 1.3 variants and error out earlier in the
+				// session resumption.
+				if sessionVers.tls13Variant != resumeVers.tls13Variant {
+					continue
+				}
+
 				if sessionVers.version == resumeVers.version {
 					testCases = append(testCases, testCase{
 						protocol:      protocol,
 						name:          "Resume-Client" + suffix,
 						resumeSession: true,
 						config: Config{
-							MaxVersion: sessionVers.version,
+							MaxVersion:   sessionVers.version,
+							TLS13Variant: sessionVers.tls13Variant,
 							Bugs: ProtocolBugs{
 								ExpectNoTLS12Session: sessionVers.version >= VersionTLS13,
 								ExpectNoTLS13PSK:     sessionVers.version < VersionTLS13,
@@ -6192,6 +6519,9 @@
 						},
 						expectedVersion:       sessionVers.version,
 						expectedResumeVersion: resumeVers.version,
+						flags: []string{
+							"-tls13-variant", strconv.Itoa(sessionVers.tls13Variant),
+						},
 					})
 				} else {
 					error := ":OLD_SESSION_VERSION_NOT_RETURNED:"
@@ -6209,11 +6539,13 @@
 						name:          "Resume-Client-Mismatch" + suffix,
 						resumeSession: true,
 						config: Config{
-							MaxVersion: sessionVers.version,
+							MaxVersion:   sessionVers.version,
+							TLS13Variant: sessionVers.tls13Variant,
 						},
 						expectedVersion: sessionVers.version,
 						resumeConfig: &Config{
-							MaxVersion: resumeVers.version,
+							MaxVersion:   resumeVers.version,
+							TLS13Variant: resumeVers.tls13Variant,
 							Bugs: ProtocolBugs{
 								AcceptAnySession: true,
 							},
@@ -6221,6 +6553,10 @@
 						expectedResumeVersion: resumeVers.version,
 						shouldFail:            true,
 						expectedError:         error,
+						flags: []string{
+							"-on-initial-tls13-variant", strconv.Itoa(sessionVers.tls13Variant),
+							"-on-resume-tls13-variant", strconv.Itoa(resumeVers.tls13Variant),
+						},
 					})
 				}
 
@@ -6229,15 +6565,21 @@
 					name:          "Resume-Client-NoResume" + suffix,
 					resumeSession: true,
 					config: Config{
-						MaxVersion: sessionVers.version,
+						MaxVersion:   sessionVers.version,
+						TLS13Variant: sessionVers.tls13Variant,
 					},
 					expectedVersion: sessionVers.version,
 					resumeConfig: &Config{
-						MaxVersion: resumeVers.version,
+						MaxVersion:   resumeVers.version,
+						TLS13Variant: resumeVers.tls13Variant,
 					},
 					newSessionsOnResume:   true,
 					expectResumeRejected:  true,
 					expectedResumeVersion: resumeVers.version,
+					flags: []string{
+						"-on-initial-tls13-variant", strconv.Itoa(sessionVers.tls13Variant),
+						"-on-resume-tls13-variant", strconv.Itoa(resumeVers.tls13Variant),
+					},
 				})
 
 				testCases = append(testCases, testCase{
@@ -6246,17 +6588,23 @@
 					name:          "Resume-Server" + suffix,
 					resumeSession: true,
 					config: Config{
-						MaxVersion: sessionVers.version,
+						MaxVersion:   sessionVers.version,
+						TLS13Variant: sessionVers.tls13Variant,
 					},
 					expectedVersion:      sessionVers.version,
-					expectResumeRejected: sessionVers.version != resumeVers.version,
+					expectResumeRejected: sessionVers != resumeVers,
 					resumeConfig: &Config{
-						MaxVersion: resumeVers.version,
+						MaxVersion:   resumeVers.version,
+						TLS13Variant: resumeVers.tls13Variant,
 						Bugs: ProtocolBugs{
 							SendBothTickets: true,
 						},
 					},
 					expectedResumeVersion: resumeVers.version,
+					flags: []string{
+						"-on-initial-tls13-variant", strconv.Itoa(sessionVers.tls13Variant),
+						"-on-resume-tls13-variant", strconv.Itoa(resumeVers.tls13Variant),
+					},
 				})
 			}
 		}
@@ -7252,6 +7600,7 @@
 					"-enable-all-curves",
 					"-enable-ed25519",
 				},
+				tls13Variant:                   ver.tls13Variant,
 				shouldFail:                     shouldSignFail,
 				expectedError:                  signError,
 				expectedPeerSignatureAlgorithm: alg.id,
@@ -7273,6 +7622,7 @@
 						IgnorePeerSignatureAlgorithmPreferences: shouldVerifyFail,
 					},
 				},
+				tls13Variant: ver.tls13Variant,
 				flags: []string{
 					"-require-any-client-certificate",
 					"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)),
@@ -7300,6 +7650,7 @@
 							fakeSigAlg2,
 						},
 					},
+					tls13Variant: ver.tls13Variant,
 					flags: []string{
 						"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
 						"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
@@ -7328,6 +7679,7 @@
 						IgnorePeerSignatureAlgorithmPreferences: shouldVerifyFail,
 					},
 				},
+				tls13Variant: ver.tls13Variant,
 				flags: []string{
 					"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)),
 					"-enable-all-curves",
@@ -7354,6 +7706,7 @@
 							InvalidSignature: true,
 						},
 					},
+					tls13Variant: ver.tls13Variant,
 					flags: []string{
 						"-require-any-client-certificate",
 						"-enable-all-curves",
@@ -7376,6 +7729,7 @@
 							InvalidSignature: true,
 						},
 					},
+					tls13Variant: ver.tls13Variant,
 					flags: []string{
 						"-enable-all-curves",
 						"-enable-ed25519",
@@ -7393,6 +7747,7 @@
 						ClientAuth:                RequireAnyClientCert,
 						VerifySignatureAlgorithms: allAlgorithms,
 					},
+					tls13Variant: ver.tls13Variant,
 					flags: []string{
 						"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
 						"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
@@ -7411,6 +7766,7 @@
 						CipherSuites:              signingCiphers,
 						VerifySignatureAlgorithms: allAlgorithms,
 					},
+					tls13Variant: ver.tls13Variant,
 					flags: []string{
 						"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
 						"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
@@ -8341,6 +8697,7 @@
 			config: Config{
 				MaxVersion: vers.version,
 			},
+			tls13Variant:         vers.tls13Variant,
 			exportKeyingMaterial: 1024,
 			exportLabel:          "label",
 			exportContext:        "context",
@@ -8351,6 +8708,7 @@
 			config: Config{
 				MaxVersion: vers.version,
 			},
+			tls13Variant:         vers.tls13Variant,
 			exportKeyingMaterial: 1024,
 		})
 		testCases = append(testCases, testCase{
@@ -8358,6 +8716,7 @@
 			config: Config{
 				MaxVersion: vers.version,
 			},
+			tls13Variant:         vers.tls13Variant,
 			exportKeyingMaterial: 1024,
 			useExportContext:     true,
 		})
@@ -8366,6 +8725,7 @@
 			config: Config{
 				MaxVersion: vers.version,
 			},
+			tls13Variant:         vers.tls13Variant,
 			exportKeyingMaterial: 1,
 			exportLabel:          "label",
 			exportContext:        "context",
@@ -10169,6 +10529,32 @@
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
+		name:     "SkipEarlyData-TLS13Experiment",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			TLS13Variant: TLS13Experiment,
+			Bugs: ProtocolBugs{
+				SendFakeEarlyDataLength: 4,
+			},
+		},
+		flags: []string{"-tls13-variant", "1"},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-TLS13RecordTypeExperiment",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			TLS13Variant: TLS13RecordTypeExperiment,
+			Bugs: ProtocolBugs{
+				SendFakeEarlyDataLength: 4,
+			},
+		},
+		flags: []string{"-tls13-variant", "2"},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
 		name:     "SkipEarlyData-OmitEarlyDataExtension",
 		config: Config{
 			MaxVersion: VersionTLS13,
@@ -10669,6 +11055,58 @@
 
 	testCases = append(testCases, testCase{
 		testType: clientTest,
+		name:     "TLS13Experiment-EarlyData-Reject-Client",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			TLS13Variant:     TLS13Experiment,
+		},
+		resumeConfig: &Config{
+			MaxVersion:       VersionTLS13,
+			TLS13Variant:     TLS13Experiment,
+			MaxEarlyDataSize: 16384,
+			Bugs: ProtocolBugs{
+				AlwaysRejectEarlyData: true,
+			},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-reject-early-data",
+			"-on-resume-shim-writes-first",
+			"-tls13-variant", "1",
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13RecordTypeExperiment-EarlyData-Reject-Client",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			TLS13Variant:     TLS13RecordTypeExperiment,
+		},
+		resumeConfig: &Config{
+			MaxVersion:       VersionTLS13,
+			TLS13Variant:     TLS13RecordTypeExperiment,
+			MaxEarlyDataSize: 16384,
+			Bugs: ProtocolBugs{
+				AlwaysRejectEarlyData: true,
+			},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-reject-early-data",
+			"-on-resume-shim-writes-first",
+			"-tls13-variant", "2",
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
 		name:     "TLS13-EarlyData-RejectTicket-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
@@ -11309,6 +11747,7 @@
 					SendRecordVersion: 0x03ff,
 				},
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":WRONG_VERSION_NUMBER:",
 		})
@@ -11325,6 +11764,7 @@
 					SendInitialRecordVersion: 0x03ff,
 				},
 			},
+			tls13Variant: ver.tls13Variant,
 		})
 
 		// Test that garbage ClientHello record versions are rejected.
@@ -11338,6 +11778,7 @@
 					SendInitialRecordVersion: 0xffff,
 				},
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":WRONG_VERSION_NUMBER:",
 		})
@@ -11357,6 +11798,7 @@
 				Certificates: []Certificate{rsaChainCertificate},
 				ClientAuth:   RequireAnyClientCert,
 			},
+			tls13Variant:          ver.tls13Variant,
 			expectPeerCertificate: &rsaChainCertificate,
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
@@ -11373,6 +11815,7 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaChainCertificate},
 			},
+			tls13Variant:          ver.tls13Variant,
 			expectPeerCertificate: &rsaChainCertificate,
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
@@ -11396,6 +11839,7 @@
 				MinVersion: ver.version,
 				MaxVersion: ver.version,
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-retain-only-sha256-client-cert-initial",
 				"-retain-only-sha256-client-cert-resume",
@@ -11413,6 +11857,7 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaCertificate},
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-verify-peer",
 				"-retain-only-sha256-client-cert-initial",
@@ -11434,6 +11879,7 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaCertificate},
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-verify-peer",
 				"-retain-only-sha256-client-cert-initial",
@@ -11454,6 +11900,7 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaCertificate},
 			},
+			tls13Variant: ver.tls13Variant,
 			flags: []string{
 				"-verify-peer",
 				"-retain-only-sha256-client-cert-resume",
@@ -11516,6 +11963,7 @@
 				MaxVersion:   ver.version,
 				Certificates: []Certificate{cert},
 			},
+			tls13Variant:  ver.tls13Variant,
 			shouldFail:    true,
 			expectedError: ":ECC_CERT_NOT_FOR_SIGNING:",
 		})
@@ -11626,6 +12074,75 @@
 	})
 }
 
+// Test that omitted and empty extensions blocks are tolerated.
+func addOmitExtensionsTests() {
+	for _, ver := range tlsVersions {
+		if ver.version > VersionTLS12 {
+			continue
+		}
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "OmitExtensions-ClientHello-" + ver.name,
+			config: Config{
+				MinVersion:             ver.version,
+				MaxVersion:             ver.version,
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					OmitExtensions: true,
+				},
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EmptyExtensions-ClientHello-" + ver.name,
+			config: Config{
+				MinVersion:             ver.version,
+				MaxVersion:             ver.version,
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					EmptyExtensions: true,
+				},
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "OmitExtensions-ServerHello-" + ver.name,
+			config: Config{
+				MinVersion:             ver.version,
+				MaxVersion:             ver.version,
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					OmitExtensions: true,
+					// Disable all ServerHello extensions so
+					// OmitExtensions works.
+					NoExtendedMasterSecret: true,
+					NoRenegotiationInfo:    true,
+				},
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EmptyExtensions-ServerHello-" + ver.name,
+			config: Config{
+				MinVersion:             ver.version,
+				MaxVersion:             ver.version,
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					EmptyExtensions: true,
+					// Disable all ServerHello extensions so
+					// EmptyExtensions works.
+					NoExtendedMasterSecret: true,
+					NoRenegotiationInfo:    true,
+				},
+			},
+		})
+	}
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -11753,6 +12270,7 @@
 	addRetainOnlySHA256ClientCertTests()
 	addECDSAKeyUsageTests()
 	addExtraHandshakeTests()
+	addOmitExtensionsTests()
 
 	var wg sync.WaitGroup
 
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 195371f..f925504 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -189,6 +189,7 @@
   { "-max-send-fragment", &TestConfig::max_send_fragment },
   { "-read-size", &TestConfig::read_size },
   { "-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew },
+  { "-tls13-variant", &TestConfig::tls13_variant },
 };
 
 const Flag<std::vector<int>> kIntVectorFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 7b4b342..e157936 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -93,6 +93,7 @@
   bool use_ticket_callback = false;
   bool renew_ticket = false;
   bool enable_early_data = false;
+  int tls13_variant = 0;
   bool enable_client_custom_extension = false;
   bool enable_server_custom_extension = false;
   bool custom_extension_skip = false;
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.cc
similarity index 94%
rename from src/ssl/tls13_both.c
rename to src/ssl/tls13_both.cc
index 6fdfb26..763dc0e 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.cc
@@ -64,6 +64,14 @@
         break;
       }
 
+      case ssl_hs_read_change_cipher_spec: {
+        int ret = ssl->method->read_change_cipher_spec(ssl);
+        if (ret <= 0) {
+          return ret;
+        }
+        break;
+      }
+
       case ssl_hs_read_end_of_early_data: {
         if (ssl->s3->hs->can_early_read) {
           /* While we are processing early data, the handshake returns early. */
@@ -365,12 +373,9 @@
 
 int tls13_process_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = 0;
-  uint8_t *msg = NULL;
-  size_t msg_len;
-
   if (hs->peer_pubkey == NULL) {
-    goto err;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
   }
 
   CBS cbs, signature;
@@ -381,22 +386,25 @@
       CBS_len(&cbs) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    goto err;
+    return 0;
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-    goto err;
+    return 0;
   }
   hs->new_session->peer_signature_algorithm = signature_algorithm;
 
+  uint8_t *msg = NULL;
+  size_t msg_len;
   if (!tls13_get_cert_verify_signature_input(
           hs, &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;
+    return 0;
   }
+  bssl::UniquePtr<uint8_t> free_msg(msg);
 
   int sig_ok =
       ssl_public_key_verify(ssl, CBS_data(&signature), CBS_len(&signature),
@@ -408,14 +416,10 @@
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
-    goto err;
+    return 0;
   }
 
-  ret = 1;
-
-err:
-  OPENSSL_free(msg);
-  return ret;
+  return 1;
 }
 
 int tls13_process_finished(SSL_HANDSHAKE *hs, int use_saved_value) {
@@ -452,21 +456,18 @@
 
 int tls13_add_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBB cbb, body, certificate_list;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
+  bssl::ScopedCBB cbb;
+  CBB body, certificate_list;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
       /* The request context is always empty in the handshake. */
       !CBB_add_u8(&body, 0) ||
       !CBB_add_u24_length_prefixed(&body, &certificate_list)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    goto err;
+    return 0;
   }
 
   if (!ssl_has_certificate(ssl)) {
-    if (!ssl_add_message_cbb(ssl, &cbb)) {
-      goto err;
-    }
-
-    return 1;
+    return ssl_add_message_cbb(ssl, cbb.get());
   }
 
   CERT *cert = ssl->cert;
@@ -477,7 +478,7 @@
                      CRYPTO_BUFFER_len(leaf_buf)) ||
       !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    goto err;
+    return 0;
   }
 
   if (hs->scts_requested && ssl->cert->signed_cert_timestamp_list != NULL) {
@@ -490,7 +491,7 @@
             CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list)) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
+      return 0;
     }
   }
 
@@ -506,7 +507,7 @@
                        CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
+      return 0;
     }
   }
 
@@ -518,38 +519,27 @@
                        CRYPTO_BUFFER_len(cert_buf)) ||
         !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
+      return 0;
     }
   }
 
-  if (!ssl_add_message_cbb(ssl, &cbb)) {
-    goto err;
-  }
-
-  return 1;
-
-err:
-  CBB_cleanup(&cbb);
-  return 0;
+  return ssl_add_message_cbb(ssl, cbb.get());
 }
 
 enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  enum ssl_private_key_result_t ret = ssl_private_key_failure;
-  uint8_t *msg = NULL;
-  size_t msg_len;
-  CBB cbb, body;
-  CBB_zero(&cbb);
-
   uint16_t signature_algorithm;
   if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
-    goto err;
+    return ssl_private_key_failure;
   }
-  if (!ssl->method->init_message(ssl, &cbb, &body,
+
+  bssl::ScopedCBB cbb;
+  CBB body;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                  SSL3_MT_CERTIFICATE_VERIFY) ||
       !CBB_add_u16(&body, signature_algorithm)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    goto err;
+    return ssl_private_key_failure;
   }
 
   /* Sign the digest. */
@@ -560,34 +550,31 @@
   if (!CBB_add_u16_length_prefixed(&body, &child) ||
       !CBB_reserve(&child, &sig, max_sig_len)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    goto err;
+    return ssl_private_key_failure;
   }
 
+  uint8_t *msg = NULL;
+  size_t msg_len;
   if (!tls13_get_cert_verify_signature_input(
           hs, &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;
+    return ssl_private_key_failure;
   }
+  bssl::UniquePtr<uint8_t> free_msg(msg);
 
   enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
       hs, sig, &sig_len, max_sig_len, signature_algorithm, msg, msg_len);
   if (sign_result != ssl_private_key_success) {
-    ret = sign_result;
-    goto err;
+    return sign_result;
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_add_message_cbb(ssl, &cbb)) {
-    goto err;
+      !ssl_add_message_cbb(ssl, cbb.get())) {
+    return ssl_private_key_failure;
   }
 
-  ret = ssl_private_key_success;
-
-err:
-  CBB_cleanup(&cbb);
-  OPENSSL_free(msg);
-  return ret;
+  return ssl_private_key_success;
 }
 
 int tls13_add_finished(SSL_HANDSHAKE *hs) {
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.cc
similarity index 88%
rename from src/ssl/tls13_client.c
rename to src/ssl/tls13_client.cc
index 92ea4f8..7f961bf 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.cc
@@ -32,6 +32,7 @@
   state_process_hello_retry_request = 0,
   state_send_second_client_hello,
   state_process_server_hello,
+  state_process_change_cipher_spec,
   state_process_encrypted_extensions,
   state_continue_second_server_flight,
   state_process_certificate_request,
@@ -55,9 +56,9 @@
   }
 
   CBS cbs, extensions;
-  uint16_t server_wire_version;
+  uint16_t server_version;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&cbs, &server_wire_version) ||
+  if (!CBS_get_u16(&cbs, &server_version) ||
       !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
       /* HelloRetryRequest may not be empty. */
       CBS_len(&extensions) == 0 ||
@@ -165,13 +166,18 @@
     return ssl_hs_error;
   }
 
-  CBS cbs, server_random, extensions;
-  uint16_t server_wire_version;
+  CBS cbs, server_random, session_id, extensions;
+  uint16_t server_version;
   uint16_t cipher_suite;
+  uint8_t compression_method;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&cbs, &server_wire_version) ||
+  if (!CBS_get_u16(&cbs, &server_version) ||
       !CBS_get_bytes(&cbs, &server_random, SSL3_RANDOM_SIZE) ||
+      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+       !CBS_get_u8_length_prefixed(&cbs, &session_id)) ||
       !CBS_get_u16(&cbs, &cipher_suite) ||
+      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+       (!CBS_get_u8(&cbs, &compression_method) || compression_method != 0)) ||
       !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
       CBS_len(&cbs) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -179,7 +185,9 @@
     return ssl_hs_error;
   }
 
-  if (server_wire_version != ssl->version) {
+  uint16_t expected_version =
+      ssl->version == TLS1_3_EXPERIMENT_VERSION ? TLS1_2_VERSION : ssl->version;
+  if (server_version != expected_version) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     return ssl_hs_error;
@@ -205,11 +213,13 @@
   }
 
   /* Parse out the extensions. */
-  int have_key_share = 0, have_pre_shared_key = 0;
-  CBS key_share, pre_shared_key;
+  int have_key_share = 0, have_pre_shared_key = 0, have_supported_versions = 0;
+  CBS key_share, pre_shared_key, supported_versions;
   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},
+      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+       &supported_versions},
   };
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
@@ -220,6 +230,14 @@
     return ssl_hs_error;
   }
 
+  /* supported_versions is parsed in handshake_client to select the experimental
+   * TLS 1.3 version. */
+  if (have_supported_versions && ssl->version != TLS1_3_EXPERIMENT_VERSION) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+    return ssl_hs_error;
+  }
+
   alert = SSL_AD_DECODE_ERROR;
   if (have_pre_shared_key) {
     if (ssl->session == NULL) {
@@ -313,18 +331,31 @@
   OPENSSL_free(dhe_secret);
 
   if (!ssl_hash_current_message(hs) ||
-      !tls13_derive_handshake_secrets(hs) ||
-      !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
+      !tls13_derive_handshake_secrets(hs)) {
+    return ssl_hs_error;
+  }
+  hs->tls13_state = state_process_change_cipher_spec;
+  return ssl->version == TLS1_3_EXPERIMENT_VERSION
+             ? ssl_hs_read_change_cipher_spec
+             : ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
                              hs->hash_len)) {
     return ssl_hs_error;
   }
 
-  /* If not sending early data, set client traffic keys now so that alerts are
-   * encrypted. */
-  if (!hs->early_data_offered &&
-      !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
-                             hs->hash_len)) {
-    return ssl_hs_error;
+  if (!hs->early_data_offered) {
+    /* If not sending early data, set client traffic keys now so that alerts are
+     * encrypted. */
+    if ((ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+         !ssl3_add_change_cipher_spec(ssl)) ||
+        !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
+                               hs->hash_len)) {
+      return ssl_hs_error;
+    }
   }
 
   hs->tls13_state = state_process_encrypted_extensions;
@@ -351,8 +382,8 @@
 
   /* Store the negotiated ALPN in the session. */
   if (ssl->s3->alpn_selected != NULL) {
-    hs->new_session->early_alpn =
-        BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+    hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
+        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
     if (hs->new_session->early_alpn == NULL) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
@@ -500,10 +531,13 @@
     }
   }
 
-  if (hs->early_data_offered &&
-      !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
-                             hs->hash_len)) {
-    return ssl_hs_error;
+  if (hs->early_data_offered) {
+    if ((ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+         !ssl3_add_change_cipher_spec(ssl)) ||
+        !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
+                               hs->hash_len)) {
+      return ssl_hs_error;
+    }
   }
 
   hs->tls13_state = state_send_client_certificate;
@@ -611,7 +645,8 @@
 enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs) {
   while (hs->tls13_state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
-    enum client_hs_state_t state = hs->tls13_state;
+    enum client_hs_state_t state =
+        static_cast<enum client_hs_state_t>(hs->tls13_state);
     switch (state) {
       case state_process_hello_retry_request:
         ret = do_process_hello_retry_request(hs);
@@ -622,6 +657,9 @@
       case state_process_server_hello:
         ret = do_process_server_hello(hs);
         break;
+      case state_process_change_cipher_spec:
+        ret = do_process_change_cipher_spec(hs);
+        break;
       case state_process_encrypted_extensions:
         ret = do_process_encrypted_extensions(hs);
         break;
@@ -666,14 +704,13 @@
 }
 
 int tls13_process_new_session_ticket(SSL *ssl) {
-  int ret = 0;
-  SSL_SESSION *session = SSL_SESSION_dup(ssl->s3->established_session,
-                                         SSL_SESSION_INCLUDE_NONAUTH);
-  if (session == NULL) {
+  bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_dup(
+      ssl->s3->established_session, SSL_SESSION_INCLUDE_NONAUTH));
+  if (!session) {
     return 0;
   }
 
-  ssl_session_rebase_time(ssl, session);
+  ssl_session_rebase_time(ssl, session.get());
 
   uint32_t server_timeout;
   CBS cbs, ticket, extensions;
@@ -686,7 +723,7 @@
       CBS_len(&cbs) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto err;
+    return 0;
   }
 
   /* Cap the renewable lifetime by the server advertised value. This avoids
@@ -708,7 +745,7 @@
                             OPENSSL_ARRAY_SIZE(ext_types),
                             1 /* ignore unknown */)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-    goto err;
+    return 0;
   }
 
   if (have_early_data_info && ssl->cert->enable_early_data) {
@@ -716,7 +753,7 @@
         CBS_len(&early_data_info) != 0) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto err;
+      return 0;
     }
   }
 
@@ -724,16 +761,12 @@
   session->not_resumable = 0;
 
   if (ssl->ctx->new_session_cb != NULL &&
-      ssl->ctx->new_session_cb(ssl, session)) {
+      ssl->ctx->new_session_cb(ssl, session.get())) {
     /* |new_session_cb|'s return value signals that it took ownership. */
-    session = NULL;
+    session.release();
   }
 
-  ret = 1;
-
-err:
-  SSL_SESSION_free(session);
-  return ret;
+  return 1;
 }
 
 void ssl_clear_tls13_state(SSL_HANDSHAKE *hs) {
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.cc
similarity index 100%
rename from src/ssl/tls13_enc.c
rename to src/ssl/tls13_enc.cc
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.cc
similarity index 91%
rename from src/ssl/tls13_server.c
rename to src/ssl/tls13_server.cc
index fe2463b..4e66016 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.cc
@@ -12,6 +12,13 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+/* Per C99, various stdint.h macros are unavailable in C++ unless some macros
+ * are defined. C++11 overruled this decision, but older Android NDKs still
+ * require it. */
+#if !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS
+#endif
+
 #include <openssl/ssl.h>
 
 #include <assert.h>
@@ -38,6 +45,7 @@
   state_send_server_certificate_verify,
   state_send_server_finished,
   state_read_second_client_flight,
+  state_process_change_cipher_spec,
   state_process_end_of_early_data,
   state_process_client_certificate,
   state_process_client_certificate_verify,
@@ -84,6 +92,19 @@
   return ok;
 }
 
+static int ssl_ext_supported_versions_add_serverhello(SSL_HANDSHAKE *hs,
+                                                      CBB *out) {
+  CBB contents;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16(&contents, hs->ssl->version) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
 static const SSL_CIPHER *choose_tls13_cipher(
     const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) {
   if (client_hello->cipher_suites_len % 2 != 0) {
@@ -199,12 +220,17 @@
 
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+                             ssl->init_num) ||
+      client_hello.session_id_len > sizeof(hs->session_id)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
+  OPENSSL_memcpy(hs->session_id, client_hello.session_id,
+                 client_hello.session_id_len);
+  hs->session_id_len = client_hello.session_id_len;
+
   /* Negotiate the cipher suite. */
   hs->new_cipher = choose_tls13_cipher(ssl, &client_hello);
   if (hs->new_cipher == NULL) {
@@ -398,8 +424,8 @@
 
   /* Store the initial negotiated ALPN in the session. */
   if (ssl->s3->alpn_selected != NULL) {
-    hs->new_session->early_alpn =
-        BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+    hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
+        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
     if (hs->new_session->early_alpn == NULL) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
@@ -508,20 +534,36 @@
 static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
+  uint16_t version = ssl->version;
+  if (ssl->version == TLS1_3_EXPERIMENT_VERSION) {
+    version = TLS1_2_VERSION;
+  }
+
   /* Send a ServerHello. */
-  CBB cbb, body, extensions;
+  CBB cbb, body, extensions, session_id;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
-      !CBB_add_u16(&body, ssl->version) ||
+      !CBB_add_u16(&body, version) ||
       !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) ||
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
+      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+       (!CBB_add_u8_length_prefixed(&body, &session_id) ||
+        !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len))) ||
       !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) ||
+      (ssl->version == TLS1_3_EXPERIMENT_VERSION && !CBB_add_u8(&body, 0)) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
       !ssl_ext_key_share_add_serverhello(hs, &extensions) ||
+      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+       !ssl_ext_supported_versions_add_serverhello(hs, &extensions)) ||
       !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
+  if (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+      !ssl3_add_change_cipher_spec(ssl)) {
+    goto err;
+  }
+
   /* Derive and enable the handshake traffic secrets. */
   if (!tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret,
@@ -635,7 +677,8 @@
      *
      * TODO(davidben): This will need to be updated for DTLS 1.3. */
     assert(!SSL_is_dtls(hs->ssl));
-    uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0, hs->hash_len};
+    assert(hs->hash_len <= 0xff);
+    uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0, static_cast<uint8_t>(hs->hash_len)};
     if (!SSL_TRANSCRIPT_update(&hs->transcript, header, sizeof(header)) ||
         !SSL_TRANSCRIPT_update(&hs->transcript, hs->expected_client_finished,
                                hs->hash_len) ||
@@ -667,6 +710,18 @@
 }
 
 static enum ssl_hs_wait_t do_process_end_of_early_data(SSL_HANDSHAKE *hs) {
+  hs->tls13_state = state_process_change_cipher_spec;
+  /* If early data was accepted, the ChangeCipherSpec message will be in the
+   * discarded early data. */
+  if (hs->early_data_offered && !hs->ssl->early_data_accepted) {
+    return ssl_hs_ok;
+  }
+  return hs->ssl->version == TLS1_3_EXPERIMENT_VERSION
+             ? ssl_hs_read_change_cipher_spec
+             : ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
                              hs->hash_len)) {
@@ -785,7 +840,8 @@
 enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
   while (hs->tls13_state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
-    enum server_hs_state_t state = hs->tls13_state;
+    enum server_hs_state_t state =
+        static_cast<enum server_hs_state_t>(hs->tls13_state);
     switch (state) {
       case state_select_parameters:
         ret = do_select_parameters(hs);
@@ -814,6 +870,9 @@
       case state_process_end_of_early_data:
         ret = do_process_end_of_early_data(hs);
         break;
+      case state_process_change_cipher_spec:
+        ret = do_process_change_cipher_spec(hs);
+        break;
       case state_process_client_certificate:
         ret = do_process_client_certificate(hs);
         break;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.cc
similarity index 100%
rename from src/ssl/tls_method.c
rename to src/ssl/tls_method.cc
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.cc
similarity index 76%
rename from src/ssl/tls_record.c
rename to src/ssl/tls_record.cc
index 3bc0b29..4708296 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.cc
@@ -139,10 +139,14 @@
 /* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
  * state needs record-splitting and zero otherwise. */
 static int ssl_needs_record_splitting(const SSL *ssl) {
+#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   return ssl->s3->aead_write_ctx != NULL &&
          ssl->s3->aead_write_ctx->version < TLS1_1_VERSION &&
          (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
          SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher);
+#else
+  return 0;
+#endif
 }
 
 int ssl_record_sequence_update(uint8_t *seq, size_t seq_len) {
@@ -358,30 +362,24 @@
   return ssl_open_record_discard;
 }
 
-static int do_seal_record(SSL *ssl, uint8_t *out, size_t *out_len,
-                          size_t max_out, uint8_t type, const uint8_t *in,
-                          size_t in_len) {
-  assert(!buffers_alias(in, in_len, out, max_out));
+static int do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
+                          uint8_t *out_suffix, size_t *out_suffix_len,
+                          const size_t max_out_suffix_len, uint8_t type,
+                          const uint8_t *in, const size_t in_len) {
+  assert(in == out || !buffers_alias(in, in_len, out, in_len));
+  assert(!buffers_alias(in, in_len, out_prefix, ssl_record_prefix_len(ssl)));
+  assert(!buffers_alias(in, in_len, out_suffix, max_out_suffix_len));
 
   /* TLS 1.3 hides the actual record type inside the encrypted data. */
+  uint8_t *extra_in = NULL;
+  size_t extra_in_len = 0;
   if (ssl->s3->aead_write_ctx != NULL &&
       ssl->s3->aead_write_ctx->version >= TLS1_3_VERSION) {
-    if (in_len > in_len + SSL3_RT_HEADER_LENGTH + 1 ||
-        max_out < in_len + SSL3_RT_HEADER_LENGTH + 1) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-      return 0;
-    }
-
-    OPENSSL_memcpy(out + SSL3_RT_HEADER_LENGTH, in, in_len);
-    out[SSL3_RT_HEADER_LENGTH + in_len] = type;
-    in = out + SSL3_RT_HEADER_LENGTH;
-    type = SSL3_RT_APPLICATION_DATA;
-    in_len++;
-  }
-
-  if (max_out < SSL3_RT_HEADER_LENGTH) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-    return 0;
+    extra_in = &type;
+    extra_in_len = 1;
+    out_prefix[0] = SSL3_RT_APPLICATION_DATA;
+  } else {
+    out_prefix[0] = type;
   }
 
   /* The TLS record-layer version number is meaningless and, starting in
@@ -395,65 +393,142 @@
   if (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
     wire_version = ssl->version;
   }
-
-  /* Write the non-length portions of the header. */
-  out[0] = type;
-  out[1] = wire_version >> 8;
-  out[2] = wire_version & 0xff;
+  out_prefix[1] = wire_version >> 8;
+  out_prefix[2] = wire_version & 0xff;
 
   /* Write the ciphertext, leaving two bytes for the length. */
-  size_t ciphertext_len;
-  if (!SSL_AEAD_CTX_seal(ssl->s3->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH,
-                         &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH, type,
-                         wire_version, ssl->s3->write_sequence, in, in_len) ||
+  if (!SSL_AEAD_CTX_seal_scatter(
+          ssl->s3->aead_write_ctx, out_prefix + SSL3_RT_HEADER_LENGTH, out,
+          out_suffix, out_suffix_len, max_out_suffix_len, type, wire_version,
+          ssl->s3->write_sequence, in, in_len, extra_in, extra_in_len) ||
       !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) {
     return 0;
   }
 
   /* Fill in the length. */
+  const size_t ciphertext_len =
+      SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx) + in_len +
+      *out_suffix_len;
   if (ciphertext_len >= 1 << 15) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
-  out[3] = ciphertext_len >> 8;
-  out[4] = ciphertext_len & 0xff;
+  out_prefix[3] = ciphertext_len >> 8;
+  out_prefix[4] = ciphertext_len & 0xff;
 
-  *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len;
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out,
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out_prefix,
                       SSL3_RT_HEADER_LENGTH);
   return 1;
 }
 
-int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+static size_t tls_seal_scatter_prefix_len(const SSL *ssl, uint8_t type,
+                                          size_t in_len) {
+  size_t ret = SSL3_RT_HEADER_LENGTH;
+  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
+      ssl_needs_record_splitting(ssl)) {
+    /* In the case of record splitting, the 1-byte record (of the 1/n-1 split)
+     * will be placed in the prefix, as will four of the five bytes of the
+     * record header for the main record. The final byte will replace the first
+     * byte of the plaintext that was used in the small record. */
+    ret += ssl_cipher_get_record_split_len(ssl->s3->aead_write_ctx->cipher);
+    ret += SSL3_RT_HEADER_LENGTH - 1;
+  } else {
+    ret += SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx);
+  }
+  return ret;
+}
+
+/* tls_seal_scatter_record seals a new record of type |type| and body |in| and
+ * splits it between |out_prefix|, |out|, and |out_suffix|. Exactly
+ * |tls_seal_scatter_prefix_len| bytes are written to |out_prefix|, |in_len|
+ * bytes to |out|, and up to 1 + |SSL_AEAD_CTX_max_overhead| bytes to
+ * |out_suffix|. |*out_suffix_len| is set to the actual number of bytes written
+ * to |out_suffix|. It returns one on success and zero on error. If enabled,
+ * |tls_seal_scatter_record| implements TLS 1.0 CBC 1/n-1 record splitting and
+ * may write two records concatenated. */
+static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
+                                   uint8_t *out_suffix, size_t *out_suffix_len,
+                                   size_t max_out_suffix_len, uint8_t type,
+                                   const uint8_t *in, size_t in_len) {
+  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
+      ssl_needs_record_splitting(ssl)) {
+    assert(SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx) == 0);
+    const size_t prefix_len = SSL3_RT_HEADER_LENGTH;
+
+    /* Write the 1-byte fragment into |out_prefix|. */
+    uint8_t *split_body = out_prefix + prefix_len;
+    uint8_t *split_suffix = split_body + 1;
+
+    /* TODO(martinkr): Make AEAD code not complain if max_suffix_len is lower
+     * than |EVP_AEAD_max_overhead| but still sufficiently large. */
+    size_t split_max_suffix_len =
+        SSL_AEAD_CTX_max_suffix_len(ssl->s3->aead_write_ctx, 0);
+    size_t split_suffix_len = 0;
+    if (!do_seal_record(ssl, out_prefix, split_body, split_suffix,
+                        &split_suffix_len, split_max_suffix_len, type, in, 1)) {
+      return 0;
+    }
+
+    size_t split_record_len = prefix_len + 1 + split_suffix_len;
+
+    assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len(
+                                       ssl->s3->aead_write_ctx->cipher) ==
+           split_record_len);
+
+    /* Write the n-1-byte fragment. The header gets split between |out_prefix|
+     * (header[:-1]) and |out| (header[-1:]). */
+    uint8_t tmp_prefix[SSL3_RT_HEADER_LENGTH];
+    if (!do_seal_record(ssl, tmp_prefix, out + 1, out_suffix, out_suffix_len,
+                        max_out_suffix_len, type, in + 1, in_len - 1)) {
+      return 0;
+    }
+    assert(tls_seal_scatter_prefix_len(ssl, type, in_len) ==
+           split_record_len + SSL3_RT_HEADER_LENGTH - 1);
+    OPENSSL_memcpy(out_prefix + split_record_len, tmp_prefix,
+                   SSL3_RT_HEADER_LENGTH - 1);
+    OPENSSL_memcpy(out, tmp_prefix + SSL3_RT_HEADER_LENGTH - 1, 1);
+    return 1;
+  }
+
+  return do_seal_record(ssl, out_prefix, out, out_suffix, out_suffix_len,
+                        max_out_suffix_len, type, in, in_len);
+}
+
+int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len,
                     uint8_t type, const uint8_t *in, size_t in_len) {
-  if (buffers_alias(in, in_len, out, max_out)) {
+  if (buffers_alias(in, in_len, out, max_out_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
     return 0;
   }
 
-  size_t frag_len = 0;
-  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
-      ssl_needs_record_splitting(ssl)) {
-    if (!do_seal_record(ssl, out, &frag_len, max_out, type, in, 1)) {
-      return 0;
-    }
-    in++;
-    in_len--;
-    out += frag_len;
-    max_out -= frag_len;
+  const size_t prefix_len = tls_seal_scatter_prefix_len(ssl, type, in_len);
 
-#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-    assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len(
-                                       ssl->s3->aead_write_ctx->cipher) ==
-           frag_len);
-#endif
-  }
-
-  if (!do_seal_record(ssl, out, out_len, max_out, type, in, in_len)) {
+  if (in_len + prefix_len < in_len) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
     return 0;
   }
-  *out_len += frag_len;
+  if (max_out_len < in_len + prefix_len) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  uint8_t *prefix = out;
+  uint8_t *body = out + prefix_len;
+  uint8_t *suffix = body + in_len;
+  size_t max_suffix_len = max_out_len - prefix_len - in_len;
+  size_t suffix_len = 0;
+
+  if (!tls_seal_scatter_record(ssl, prefix, body, suffix, &suffix_len,
+                               max_suffix_len, type, in, in_len)) {
+    return 0;
+  }
+
+  if (prefix_len + in_len + suffix_len < prefix_len + in_len) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
+    return 0;
+  }
+
+  *out_len = prefix_len + in_len + suffix_len;
   return 1;
 }