Merge "external/boringssl: Sync to 14308731e5446a73ac2258688a9688b524483cb6."
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 959b0f0..5388f09 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-ee7aa02744a78bf4630913b1c83d0fe36aa45efc
+14308731e5446a73ac2258688a9688b524483cb6
diff --git a/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index d149d0f..a6f5e07 100644
--- a/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -41,6 +41,7 @@
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00
+.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
 
 .type	poly_hash_ad_internal,@function
 .align	64
@@ -2124,7 +2125,9 @@
 .cfi_offset	r15, -56
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
-	movq	%rdx,8+32(%rbp)
+	movq	56(%r9),%rbx
+	addq	%rdx,%rbx
+	movq	%rbx,8+32(%rbp)
 	movq	%r8,0+32(%rbp)
 	movq	%rdx,%rbx
 
@@ -3587,11 +3590,9 @@
 
 seal_sse_tail_16:
 	testq	%rbx,%rbx
-	jz	seal_sse_finalize
+	jz	process_blocks_of_extra_in
 
 	movq	%rbx,%r8
-	shlq	$4,%r8
-	leaq	.and_masks(%rip),%r13
 	movq	%rbx,%rcx
 	leaq	-1(%rsi,%rbx), %rsi
 	pxor	%xmm15,%xmm15
@@ -3615,7 +3616,72 @@
 	subq	$1,%rcx
 	jnz	2b
 
-	pand	-16(%r13,%r8), %xmm15
+
+
+
+
+
+
+
+	movq	288+32(%rsp),%r9
+	movq	56(%r9),%r14
+	movq	48(%r9),%r13
+	testq	%r14,%r14
+	jz	process_partial_block
+
+	movq	$16,%r15
+	subq	%rbx,%r15
+	cmpq	%r15,%r14
+
+	jge	load_extra_in
+	movq	%r14,%r15
+
+load_extra_in:
+
+
+	leaq	-1(%r13,%r15), %rsi
+
+
+	addq	%r15,%r13
+	subq	%r15,%r14
+	movq	%r13,48(%r9)
+	movq	%r14,56(%r9)
+
+
+
+	addq	%r15,%r8
+
+
+	pxor	%xmm11,%xmm11
+3:
+	pslldq	$1,%xmm11
+	pinsrb	$0,(%rsi),%xmm11
+	leaq	-1(%rsi),%rsi
+	subq	$1,%r15
+	jnz	3b
+
+
+
+
+	movq	%rbx,%r15
+
+4:
+	pslldq	$1,%xmm11
+	subq	$1,%r15
+	jnz	4b
+
+
+
+
+	leaq	.and_masks(%rip),%r15
+	shlq	$4,%rbx
+	pand	-16(%r15,%rbx), %xmm15
+
+
+	por	%xmm11,%xmm15
+
+
+
 .byte	102,77,15,126,253
 	pextrq	$1,%xmm15,%r14
 	addq	%r13,%r10
@@ -3660,7 +3726,127 @@
 	adcq	%r9,%r11
 	adcq	$0,%r12
 
-seal_sse_finalize:
+
+process_blocks_of_extra_in:
+
+	movq	288+32(%rsp),%r9
+	movq	48(%r9),%rsi
+	movq	56(%r9),%r8
+	movq	%r8,%rcx
+	shrq	$4,%r8
+
+5:
+	jz	process_extra_in_trailer
+	addq	0(%rsi),%r10
+	adcq	8+0(%rsi),%r11
+	adcq	$1,%r12
+	movq	0+0(%rbp),%rax
+	movq	%rax,%r15
+	mulq	%r10
+	movq	%rax,%r13
+	movq	%rdx,%r14
+	movq	0+0(%rbp),%rax
+	mulq	%r11
+	imulq	%r12,%r15
+	addq	%rax,%r14
+	adcq	%rdx,%r15
+	movq	8+0(%rbp),%rax
+	movq	%rax,%r9
+	mulq	%r10
+	addq	%rax,%r14
+	adcq	$0,%rdx
+	movq	%rdx,%r10
+	movq	8+0(%rbp),%rax
+	mulq	%r11
+	addq	%rax,%r15
+	adcq	$0,%rdx
+	imulq	%r12,%r9
+	addq	%r10,%r15
+	adcq	%rdx,%r9
+	movq	%r13,%r10
+	movq	%r14,%r11
+	movq	%r15,%r12
+	andq	$3,%r12
+	movq	%r15,%r13
+	andq	$-4,%r13
+	movq	%r9,%r14
+	shrdq	$2,%r9,%r15
+	shrq	$2,%r9
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$0,%r12
+	addq	%r15,%r10
+	adcq	%r9,%r11
+	adcq	$0,%r12
+
+	leaq	16(%rsi),%rsi
+	subq	$1,%r8
+	jmp	5b
+
+process_extra_in_trailer:
+	andq	$15,%rcx
+	movq	%rcx,%rbx
+	jz	do_length_block
+	leaq	-1(%rsi,%rcx), %rsi
+
+6:
+	pslldq	$1,%xmm15
+	pinsrb	$0,(%rsi),%xmm15
+	leaq	-1(%rsi),%rsi
+	subq	$1,%rcx
+	jnz	6b
+
+process_partial_block:
+
+	leaq	.and_masks(%rip),%r15
+	shlq	$4,%rbx
+	pand	-16(%r15,%rbx), %xmm15
+.byte	102,77,15,126,253
+	pextrq	$1,%xmm15,%r14
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$1,%r12
+	movq	0+0(%rbp),%rax
+	movq	%rax,%r15
+	mulq	%r10
+	movq	%rax,%r13
+	movq	%rdx,%r14
+	movq	0+0(%rbp),%rax
+	mulq	%r11
+	imulq	%r12,%r15
+	addq	%rax,%r14
+	adcq	%rdx,%r15
+	movq	8+0(%rbp),%rax
+	movq	%rax,%r9
+	mulq	%r10
+	addq	%rax,%r14
+	adcq	$0,%rdx
+	movq	%rdx,%r10
+	movq	8+0(%rbp),%rax
+	mulq	%r11
+	addq	%rax,%r15
+	adcq	$0,%rdx
+	imulq	%r12,%r9
+	addq	%r10,%r15
+	adcq	%rdx,%r9
+	movq	%r13,%r10
+	movq	%r14,%r11
+	movq	%r15,%r12
+	andq	$3,%r12
+	movq	%r15,%r13
+	andq	$-4,%r13
+	movq	%r9,%r14
+	shrdq	$2,%r9,%r15
+	shrq	$2,%r9
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$0,%r12
+	addq	%r15,%r10
+	adcq	%r9,%r11
+	adcq	$0,%r12
+
+
+do_length_block:
 	addq	32(%rbp),%r10
 	adcq	8+32(%rbp),%r11
 	adcq	$1,%r12
diff --git a/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index 9db2a58..c90447a 100644
--- a/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -40,6 +40,7 @@
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00
 .byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00
+.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
 
 
 .p2align	6
@@ -2123,7 +2124,9 @@
 
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
-	movq	%rdx,8+32(%rbp)
+	movq	56(%r9),%rbx
+	addq	%rdx,%rbx
+	movq	%rbx,8+32(%rbp)
 	movq	%r8,0+32(%rbp)
 	movq	%rdx,%rbx
 
@@ -3586,11 +3589,9 @@
 
 seal_sse_tail_16:
 	testq	%rbx,%rbx
-	jz	seal_sse_finalize
+	jz	process_blocks_of_extra_in
 
 	movq	%rbx,%r8
-	shlq	$4,%r8
-	leaq	.and_masks(%rip),%r13
 	movq	%rbx,%rcx
 	leaq	-1(%rsi,%rbx), %rsi
 	pxor	%xmm15,%xmm15
@@ -3614,7 +3615,72 @@
 	subq	$1,%rcx
 	jnz	2b
 
-	pand	-16(%r13,%r8), %xmm15
+
+
+
+
+
+
+
+	movq	288+32(%rsp),%r9
+	movq	56(%r9),%r14
+	movq	48(%r9),%r13
+	testq	%r14,%r14
+	jz	process_partial_block
+
+	movq	$16,%r15
+	subq	%rbx,%r15
+	cmpq	%r15,%r14
+
+	jge	load_extra_in
+	movq	%r14,%r15
+
+load_extra_in:
+
+
+	leaq	-1(%r13,%r15), %rsi
+
+
+	addq	%r15,%r13
+	subq	%r15,%r14
+	movq	%r13,48(%r9)
+	movq	%r14,56(%r9)
+
+
+
+	addq	%r15,%r8
+
+
+	pxor	%xmm11,%xmm11
+3:
+	pslldq	$1,%xmm11
+	pinsrb	$0,(%rsi),%xmm11
+	leaq	-1(%rsi),%rsi
+	subq	$1,%r15
+	jnz	3b
+
+
+
+
+	movq	%rbx,%r15
+
+4:
+	pslldq	$1,%xmm11
+	subq	$1,%r15
+	jnz	4b
+
+
+
+
+	leaq	.and_masks(%rip),%r15
+	shlq	$4,%rbx
+	pand	-16(%r15,%rbx), %xmm15
+
+
+	por	%xmm11,%xmm15
+
+
+
 .byte	102,77,15,126,253
 	pextrq	$1,%xmm15,%r14
 	addq	%r13,%r10
@@ -3659,7 +3725,127 @@
 	adcq	%r9,%r11
 	adcq	$0,%r12
 
-seal_sse_finalize:
+
+process_blocks_of_extra_in:
+
+	movq	288+32(%rsp),%r9
+	movq	48(%r9),%rsi
+	movq	56(%r9),%r8
+	movq	%r8,%rcx
+	shrq	$4,%r8
+
+5:
+	jz	process_extra_in_trailer
+	addq	0(%rsi),%r10
+	adcq	8+0(%rsi),%r11
+	adcq	$1,%r12
+	movq	0+0(%rbp),%rax
+	movq	%rax,%r15
+	mulq	%r10
+	movq	%rax,%r13
+	movq	%rdx,%r14
+	movq	0+0(%rbp),%rax
+	mulq	%r11
+	imulq	%r12,%r15
+	addq	%rax,%r14
+	adcq	%rdx,%r15
+	movq	8+0(%rbp),%rax
+	movq	%rax,%r9
+	mulq	%r10
+	addq	%rax,%r14
+	adcq	$0,%rdx
+	movq	%rdx,%r10
+	movq	8+0(%rbp),%rax
+	mulq	%r11
+	addq	%rax,%r15
+	adcq	$0,%rdx
+	imulq	%r12,%r9
+	addq	%r10,%r15
+	adcq	%rdx,%r9
+	movq	%r13,%r10
+	movq	%r14,%r11
+	movq	%r15,%r12
+	andq	$3,%r12
+	movq	%r15,%r13
+	andq	$-4,%r13
+	movq	%r9,%r14
+	shrdq	$2,%r9,%r15
+	shrq	$2,%r9
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$0,%r12
+	addq	%r15,%r10
+	adcq	%r9,%r11
+	adcq	$0,%r12
+
+	leaq	16(%rsi),%rsi
+	subq	$1,%r8
+	jmp	5b
+
+process_extra_in_trailer:
+	andq	$15,%rcx
+	movq	%rcx,%rbx
+	jz	do_length_block
+	leaq	-1(%rsi,%rcx), %rsi
+
+6:
+	pslldq	$1,%xmm15
+	pinsrb	$0,(%rsi),%xmm15
+	leaq	-1(%rsi),%rsi
+	subq	$1,%rcx
+	jnz	6b
+
+process_partial_block:
+
+	leaq	.and_masks(%rip),%r15
+	shlq	$4,%rbx
+	pand	-16(%r15,%rbx), %xmm15
+.byte	102,77,15,126,253
+	pextrq	$1,%xmm15,%r14
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$1,%r12
+	movq	0+0(%rbp),%rax
+	movq	%rax,%r15
+	mulq	%r10
+	movq	%rax,%r13
+	movq	%rdx,%r14
+	movq	0+0(%rbp),%rax
+	mulq	%r11
+	imulq	%r12,%r15
+	addq	%rax,%r14
+	adcq	%rdx,%r15
+	movq	8+0(%rbp),%rax
+	movq	%rax,%r9
+	mulq	%r10
+	addq	%rax,%r14
+	adcq	$0,%rdx
+	movq	%rdx,%r10
+	movq	8+0(%rbp),%rax
+	mulq	%r11
+	addq	%rax,%r15
+	adcq	$0,%rdx
+	imulq	%r12,%r9
+	addq	%r10,%r15
+	adcq	%rdx,%r9
+	movq	%r13,%r10
+	movq	%r14,%r11
+	movq	%r15,%r12
+	andq	$3,%r12
+	movq	%r15,%r13
+	andq	$-4,%r13
+	movq	%r9,%r14
+	shrdq	$2,%r9,%r15
+	shrq	$2,%r9
+	addq	%r13,%r10
+	adcq	%r14,%r11
+	adcq	$0,%r12
+	addq	%r15,%r10
+	adcq	%r9,%r11
+	adcq	$0,%r12
+
+
+do_length_block:
 	addq	32(%rbp),%r10
 	adcq	8+32(%rbp),%r11
 	adcq	$1,%r12
diff --git a/sources.bp b/sources.bp
index 4343bb7..d95a64e 100644
--- a/sources.bp
+++ b/sources.bp
@@ -349,42 +349,41 @@
 cc_defaults {
     name: "libssl_sources",
     srcs: [
-        "src/ssl/bio_ssl.c",
-        "src/ssl/custom_extensions.c",
-        "src/ssl/d1_both.c",
-        "src/ssl/d1_lib.c",
-        "src/ssl/d1_pkt.c",
-        "src/ssl/d1_srtp.c",
-        "src/ssl/dtls_method.c",
-        "src/ssl/dtls_record.c",
-        "src/ssl/handshake_client.c",
-        "src/ssl/handshake_server.c",
-        "src/ssl/s3_both.c",
-        "src/ssl/s3_lib.c",
-        "src/ssl/s3_pkt.c",
-        "src/ssl/ssl_aead_ctx.c",
-        "src/ssl/ssl_asn1.c",
-        "src/ssl/ssl_buffer.c",
-        "src/ssl/ssl_cert.c",
-        "src/ssl/ssl_cipher.c",
-        "src/ssl/ssl_ecdh.c",
-        "src/ssl/ssl_file.c",
-        "src/ssl/ssl_lib.c",
-        "src/ssl/ssl_privkey.c",
-        "src/ssl/ssl_privkey_cc.cc",
-        "src/ssl/ssl_session.c",
-        "src/ssl/ssl_stat.c",
-        "src/ssl/ssl_transcript.c",
-        "src/ssl/ssl_versions.c",
-        "src/ssl/ssl_x509.c",
-        "src/ssl/t1_enc.c",
-        "src/ssl/t1_lib.c",
-        "src/ssl/tls13_both.c",
-        "src/ssl/tls13_client.c",
-        "src/ssl/tls13_enc.c",
-        "src/ssl/tls13_server.c",
-        "src/ssl/tls_method.c",
-        "src/ssl/tls_record.c",
+        "src/ssl/bio_ssl.cc",
+        "src/ssl/custom_extensions.cc",
+        "src/ssl/d1_both.cc",
+        "src/ssl/d1_lib.cc",
+        "src/ssl/d1_pkt.cc",
+        "src/ssl/d1_srtp.cc",
+        "src/ssl/dtls_method.cc",
+        "src/ssl/dtls_record.cc",
+        "src/ssl/handshake_client.cc",
+        "src/ssl/handshake_server.cc",
+        "src/ssl/s3_both.cc",
+        "src/ssl/s3_lib.cc",
+        "src/ssl/s3_pkt.cc",
+        "src/ssl/ssl_aead_ctx.cc",
+        "src/ssl/ssl_asn1.cc",
+        "src/ssl/ssl_buffer.cc",
+        "src/ssl/ssl_cert.cc",
+        "src/ssl/ssl_cipher.cc",
+        "src/ssl/ssl_ecdh.cc",
+        "src/ssl/ssl_file.cc",
+        "src/ssl/ssl_lib.cc",
+        "src/ssl/ssl_privkey.cc",
+        "src/ssl/ssl_session.cc",
+        "src/ssl/ssl_stat.cc",
+        "src/ssl/ssl_transcript.cc",
+        "src/ssl/ssl_versions.cc",
+        "src/ssl/ssl_x509.cc",
+        "src/ssl/t1_enc.cc",
+        "src/ssl/t1_lib.cc",
+        "src/ssl/tls13_both.cc",
+        "src/ssl/tls13_client.cc",
+        "src/ssl/tls13_enc.cc",
+        "src/ssl/tls13_server.cc",
+        "src/ssl/tls_method.cc",
+        "src/ssl/tls_record.cc",
     ],
 }
 
@@ -466,6 +465,8 @@
         "src/crypto/test/gtest_main.cc",
         "src/crypto/thread_test.cc",
         "src/crypto/x509/x509_test.cc",
+        "src/crypto/x509v3/tab_test.cc",
+        "src/crypto/x509v3/v3name_test.cc",
     ],
 }
 
@@ -477,11 +478,3 @@
     ],
 }
 
-cc_defaults {
-    name: "boringssl_tests_sources",
-    srcs: [
-        "src/crypto/fipsmodule/ec/example_mul.c",
-        "src/crypto/x509v3/tab_test.c",
-        "src/crypto/x509v3/v3name_test.c",
-    ],
-}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index deab75b..7b7f934 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -43,6 +43,16 @@
   endif()
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_CXX_FLAGS} -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${C_CXX_FLAGS} -Wmissing-declarations")
+  # In GCC, -Wmissing-declarations is the C++ spelling of -Wmissing-prototypes
+  # and using the wrong one is an error. In Clang, -Wmissing-prototypes is the
+  # spelling for both and -Wmissing-declarations is some other warning.
+  #
+  # https://gcc.gnu.org/onlinedocs/gcc-7.1.0/gcc/Warning-Options.html#Warning-Options
+  # https://clang.llvm.org/docs/DiagnosticsReference.html#wmissing-prototypes
+  # https://clang.llvm.org/docs/DiagnosticsReference.html#wmissing-declarations
+  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-prototypes")
+  endif()
 elseif(MSVC)
   set(MSVC_DISABLED_WARNINGS_LIST
       "C4061" # enumerator 'identifier' in switch of enum 'enumeration' is not
@@ -299,7 +309,7 @@
     add_library(Fuzzer STATIC ${LIBFUZZER_SOURCES})
     # libFuzzer does not pass our aggressive warnings. It also must be built
     # without -fsanitize-coverage options or clang crashes.
-    set_target_properties(Fuzzer PROPERTIES COMPILE_FLAGS "-Wno-shadow -Wno-format-nonliteral -fsanitize-coverage=0")
+    set_target_properties(Fuzzer PROPERTIES COMPILE_FLAGS "-Wno-shadow -Wno-format-nonliteral -Wno-missing-prototypes -fsanitize-coverage=0")
   endif()
 
   add_subdirectory(fuzz)
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index b941f67..651793f 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -260,6 +260,8 @@
   test/file_test_gtest.cc
   thread_test.cc
   x509/x509_test.cc
+  x509v3/tab_test.cc
+  x509v3/v3name_test.cc
 
   $<TARGET_OBJECTS:crypto_test_data>
   $<TARGET_OBJECTS:gtest_main>
diff --git a/src/crypto/cipher_extra/aead_test.cc b/src/crypto/cipher_extra/aead_test.cc
index 949c800..a699890 100644
--- a/src/crypto/cipher_extra/aead_test.cc
+++ b/src/crypto/cipher_extra/aead_test.cc
@@ -240,6 +240,7 @@
 
     for (size_t extra_in_size = 0; extra_in_size < in.size(); extra_in_size++) {
       size_t tag_bytes_written;
+      SCOPED_TRACE(extra_in_size);
       ASSERT_TRUE(EVP_AEAD_CTX_seal_scatter(
           ctx.get(), out.data(), out_tag.data(), &tag_bytes_written,
           out_tag.size(), nonce.data(), nonce.size(), in.data(),
diff --git a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
index 857f1d5..0e32279 100644
--- a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
+++ b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
@@ -78,6 +78,7 @@
 .byte 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00
 .byte 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00
 .byte 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00
+.byte 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
 ___
 
 my ($oup,$inp,$inl,$adp,$keyp,$itr1,$itr2)=("%rdi","%rsi","%rbx","%rcx","%r9","%rcx","%r8");
@@ -856,7 +857,9 @@
 .cfi_offset r15, -56
     lea 32(%rsp), %rbp
     and \$-32, %rbp
-    mov %rdx, 8+$len_store
+    mov 56($keyp), $inl  # extra_in_len
+    addq %rdx, $inl
+    mov $inl, 8+$len_store
     mov %r8, 0+$len_store
     mov %rdx, $inl\n"; $code.="
     mov OPENSSL_ia32cap_P+8(%rip), %eax
@@ -1093,11 +1096,9 @@
 
 seal_sse_tail_16:
     test $inl, $inl
-    jz seal_sse_finalize
+    jz process_blocks_of_extra_in
     # We can only load the PT one byte at a time to avoid buffer overread
     mov $inl, $itr2
-    shl \$4, $itr2
-    lea .and_masks(%rip), $t0
     mov $inl, $itr1
     lea -1($inp, $inl), $inp
     pxor $T3, $T3
@@ -1106,7 +1107,7 @@
         pinsrb \$0, ($inp), $T3
         lea -1($inp), $inp
         dec $itr1
-    jne 1b
+        jne 1b
 
     # XOR the keystream with the plaintext.
     pxor $A0, $T3
@@ -1121,14 +1122,121 @@
         sub \$1, $itr1
         jnz 2b
 
-    pand -16($t0, $itr2), $T3
+    # $T3 contains the final (partial, non-empty) block of ciphertext which
+    # needs to be fed into the Poly1305 state. The right-most $inl bytes of it
+    # are valid. We need to fill it with extra_in bytes until full, or until we
+    # run out of bytes.
+    #
+    # $keyp points to the tag output, which is actually a struct with the
+    # extra_in pointer and length at offset 48.
+    movq 288+32(%rsp), $keyp
+    movq 56($keyp), $t1  # extra_in_len
+    movq 48($keyp), $t0  # extra_in
+    test $t1, $t1
+    jz process_partial_block  # Common case: no bytes of extra_in
+
+    movq \$16, $t2
+    subq $inl, $t2  # 16-$inl is the number of bytes that fit into $T3.
+    cmpq $t2, $t1   # if extra_in_len < 16-$inl, only copy extra_in_len
+                    # (note that AT&T syntax reverses the arguments)
+    jge load_extra_in
+    movq $t1, $t2
+
+load_extra_in:
+    # $t2 contains the number of bytes of extra_in (pointed to by $t0) to load
+    # into $T3. They are loaded in reverse order.
+    leaq -1($t0, $t2), $inp
+    # Update extra_in and extra_in_len to reflect the bytes that are about to
+    # be read.
+    addq $t2, $t0
+    subq $t2, $t1
+    movq $t0, 48($keyp)
+    movq $t1, 56($keyp)
+
+    # Update $itr2, which is used to select the mask later on, to reflect the
+    # extra bytes about to be added.
+    addq $t2, $itr2
+
+    # Load $t2 bytes of extra_in into $T2.
+    pxor $T2, $T2
+3:
+        pslldq \$1, $T2
+        pinsrb \$0, ($inp), $T2
+        lea -1($inp), $inp
+        sub \$1, $t2
+        jnz 3b
+
+    # Shift $T2 up the length of the remainder from the main encryption. Sadly,
+    # the shift for an XMM register has to be a constant, thus we loop to do
+    # this.
+    movq $inl, $t2
+
+4:
+        pslldq \$1, $T2
+        sub \$1, $t2
+        jnz 4b
+
+    # Mask $T3 (the remainder from the main encryption) so that superfluous
+    # bytes are zero. This means that the non-zero bytes in $T2 and $T3 are
+    # disjoint and so we can merge them with an OR.
+    lea .and_masks(%rip), $t2
+    shl \$4, $inl
+    pand -16($t2, $inl), $T3
+
+    # Merge $T2 into $T3, forming the remainder block.
+    por $T2, $T3
+
+    # The block of ciphertext + extra_in is ready to be included in the
+    # Poly1305 state.
     movq $T3, $t0
     pextrq \$1, $T3, $t1
     add $t0, $acc0
     adc $t1, $acc1
     adc \$1, $acc2\n";
     &poly_mul(); $code.="
-seal_sse_finalize:\n";
+
+process_blocks_of_extra_in:
+    # There may be additional bytes of extra_in to process.
+    movq 288+32(%rsp), $keyp
+    movq 48($keyp), $inp   # extra_in
+    movq 56($keyp), $itr2  # extra_in_len
+    movq $itr2, $itr1
+    shr \$4, $itr2         # number of blocks
+
+5:
+        jz process_extra_in_trailer\n";
+        &poly_add("0($inp)");
+        &poly_mul(); $code.="
+        leaq 16($inp), $inp
+        subq \$1, $itr2
+        jmp 5b
+
+process_extra_in_trailer:
+    andq \$15, $itr1       # remaining num bytes (<16) of extra_in
+    movq $itr1, $inl
+    jz do_length_block
+    leaq -1($inp, $itr1), $inp
+
+6:
+        pslldq \$1, $T3
+        pinsrb \$0, ($inp), $T3
+        lea -1($inp), $inp
+        sub \$1, $itr1
+        jnz 6b
+
+process_partial_block:
+    # $T3 contains $inl bytes of data to be fed into Poly1305. $inl != 0
+    lea .and_masks(%rip), $t2
+    shl \$4, $inl
+    pand -16($t2, $inl), $T3
+    movq $T3, $t0
+    pextrq \$1, $T3, $t1
+    add $t0, $acc0
+    adc $t1, $acc1
+    adc \$1, $acc2\n";
+    &poly_mul(); $code.="
+
+do_length_block:\n";
     &poly_add($len_store);
     &poly_mul(); $code.="
     # Final reduce
diff --git a/src/crypto/cipher_extra/e_aesgcmsiv.c b/src/crypto/cipher_extra/e_aesgcmsiv.c
index 190a1b9..2dd1267 100644
--- a/src/crypto/cipher_extra/e_aesgcmsiv.c
+++ b/src/crypto/cipher_extra/e_aesgcmsiv.c
@@ -13,6 +13,9 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
 #include <openssl/aead.h>
+
+#include <assert.h>
+
 #include <openssl/cipher.h>
 #include <openssl/cpu.h>
 #include <openssl/crypto.h>
@@ -29,7 +32,7 @@
 /* Optimised AES-GCM-SIV */
 
 struct aead_aes_gcm_siv_asm_ctx {
-  alignas(64) uint8_t key[16*15];
+  alignas(16) uint8_t key[16*15];
   int is_128_bit;
 };
 
@@ -67,6 +70,9 @@
     return 0;
   }
 
+  /* malloc should return a 16-byte-aligned address. */
+  assert((((uintptr_t)gcm_siv_ctx) & 15) == 0);
+
   if (key_bits == 128) {
     aes128gcmsiv_aes_ks(key, &gcm_siv_ctx->key[0]);
     gcm_siv_ctx->is_128_bit = 1;
diff --git a/src/crypto/cipher_extra/e_chacha20poly1305.c b/src/crypto/cipher_extra/e_chacha20poly1305.c
index 6cfc856..515b60f 100644
--- a/src/crypto/cipher_extra/e_chacha20poly1305.c
+++ b/src/crypto/cipher_extra/e_chacha20poly1305.c
@@ -124,33 +124,34 @@
   CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
 }
 
-static void poly1305_update_padded_16(poly1305_state *poly1305,
-                                      const uint8_t *data, size_t data_len) {
-  static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
-
-  CRYPTO_poly1305_update(poly1305, data, data_len);
-  if (data_len % 16 != 0) {
-    CRYPTO_poly1305_update(poly1305, padding,
-                           sizeof(padding) - (data_len % 16));
-  }
-}
-
 /* calc_tag fills |tag| with the authentication tag for the given inputs. */
 static void calc_tag(uint8_t tag[POLY1305_TAG_LEN],
                      const struct aead_chacha20_poly1305_ctx *c20_ctx,
                      const uint8_t nonce[12], const uint8_t *ad, size_t ad_len,
-                     const uint8_t *ciphertext, size_t ciphertext_len) {
+                     const uint8_t *ciphertext, size_t ciphertext_len,
+                     const uint8_t *ciphertext_extra,
+                     size_t ciphertext_extra_len) {
   alignas(16) uint8_t poly1305_key[32];
   OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key));
   CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
                    c20_ctx->key, nonce, 0);
 
+  static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
   poly1305_state ctx;
   CRYPTO_poly1305_init(&ctx, poly1305_key);
-  poly1305_update_padded_16(&ctx, ad, ad_len);
-  poly1305_update_padded_16(&ctx, ciphertext, ciphertext_len);
+  CRYPTO_poly1305_update(&ctx, ad, ad_len);
+  if (ad_len % 16 != 0) {
+    CRYPTO_poly1305_update(&ctx, padding, sizeof(padding) - (ad_len % 16));
+  }
+  CRYPTO_poly1305_update(&ctx, ciphertext, ciphertext_len);
+  CRYPTO_poly1305_update(&ctx, ciphertext_extra, ciphertext_extra_len);
+  const size_t ciphertext_total = ciphertext_len + ciphertext_extra_len;
+  if (ciphertext_total % 16 != 0) {
+    CRYPTO_poly1305_update(&ctx, padding,
+                           sizeof(padding) - (ciphertext_total % 16));
+  }
   poly1305_update_length(&ctx, ad_len);
-  poly1305_update_length(&ctx, ciphertext_len);
+  poly1305_update_length(&ctx, ciphertext_total);
   CRYPTO_poly1305_finish(&ctx, tag);
 }
 
@@ -161,6 +162,14 @@
     size_t extra_in_len, const uint8_t *ad, size_t ad_len) {
   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
 
+  if (extra_in_len + ctx->tag_len < ctx->tag_len) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
+    return 0;
+  }
+  if (max_out_tag_len < ctx->tag_len + extra_in_len) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
   if (nonce_len != 12) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
     return 0;
@@ -183,20 +192,43 @@
     return 0;
   }
 
-  alignas(16) uint8_t tag[48];
+  /* The the extra input is given, it is expected to be very short and so is
+   * encrypted byte-by-byte first. */
+  if (extra_in_len) {
+    static const size_t kChaChaBlockSize = 64;
+    uint32_t block_counter = 1 + (in_len / kChaChaBlockSize);
+    size_t offset = in_len % kChaChaBlockSize;
+    uint8_t block[64 /* kChaChaBlockSize */];
+
+    for (size_t done = 0; done < extra_in_len; block_counter++) {
+      memset(block, 0, sizeof(block));
+      CRYPTO_chacha_20(block, block, sizeof(block), c20_ctx->key, nonce,
+                       block_counter);
+      for (size_t i = offset; i < sizeof(block) && done < extra_in_len;
+           i++, done++) {
+        out_tag[done] = extra_in[done] ^ block[i];
+      }
+      offset = 0;
+    }
+  }
+
+  alignas(16) uint8_t tag[48 + 8 + 8];
 
   if (asm_capable()) {
     OPENSSL_memcpy(tag, c20_ctx->key, 32);
     OPENSSL_memset(tag + 32, 0, 4);
     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
+    OPENSSL_memcpy(tag + 48, &out_tag, sizeof(out_tag));
+    OPENSSL_memcpy(tag + 56, &extra_in_len, sizeof(extra_in_len));
     chacha20_poly1305_seal(out, in, in_len, ad, ad_len, tag);
   } else {
     CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
-    calc_tag(tag, c20_ctx, nonce, ad, ad_len, out, in_len);
+    calc_tag(tag, c20_ctx, nonce, ad, ad_len, out, in_len,
+             out_tag, extra_in_len);
   }
 
-  OPENSSL_memcpy(out_tag, tag, ctx->tag_len);
-  *out_tag_len = ctx->tag_len;
+  OPENSSL_memcpy(out_tag + extra_in_len, tag, ctx->tag_len);
+  *out_tag_len = extra_in_len + ctx->tag_len;
   return 1;
 }
 
@@ -236,7 +268,7 @@
     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
     chacha20_poly1305_open(out, in, in_len, ad, ad_len, tag);
   } else {
-    calc_tag(tag, c20_ctx, nonce, ad, ad_len, in, in_len);
+    calc_tag(tag, c20_ctx, nonce, ad, ad_len, in, in_len, NULL, 0);
     CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
   }
 
@@ -253,7 +285,7 @@
     12,               /* nonce len */
     POLY1305_TAG_LEN, /* overhead */
     POLY1305_TAG_LEN, /* max tag length */
-    0,                /* seal_scatter_supports_extra_in */
+    1,                /* seal_scatter_supports_extra_in */
 
     aead_chacha20_poly1305_init,
     NULL, /* init_with_direction */
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index a1bc6dc..e83c483 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -193,14 +193,3 @@
     ${BCM_ASM_SOURCES}
   )
 endif()
-
-add_executable(
-  example_mul
-
-  ec/example_mul.c
-
-  $<TARGET_OBJECTS:test_support>
-)
-
-target_link_libraries(example_mul crypto)
-add_dependencies(all_tests example_mul)
diff --git a/src/crypto/fipsmodule/bn/exponentiation.c b/src/crypto/fipsmodule/bn/exponentiation.c
index e5521d6..187b845 100644
--- a/src/crypto/fipsmodule/bn/exponentiation.c
+++ b/src/crypto/fipsmodule/bn/exponentiation.c
@@ -1085,11 +1085,12 @@
       while (bits >= 0) {
         /* Read five bits from |bits-4| through |bits|, inclusive. */
         int first_bit = bits - 4;
-        wvalue = *(const uint16_t *) (p_bytes + (first_bit >> 3));
-        wvalue >>= first_bit & 7;
-        wvalue &= 0x1f;
+        uint16_t val;
+        OPENSSL_memcpy(&val, p_bytes + (first_bit >> 3), sizeof(val));
+        val >>= first_bit & 7;
+        val &= 0x1f;
         bits -= 5;
-        bn_power5(tmp.d, tmp.d, powerbuf, np, n0, top, wvalue);
+        bn_power5(tmp.d, tmp.d, powerbuf, np, n0, top, val);
       }
     }
 
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 71f9fd8..48b60ee 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -380,6 +380,31 @@
       << "p * 0 did not return point at infinity.";
 }
 
+// Test that 10×∞ + G = G.
+TEST_P(ECCurveTest, Mul) {
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
+  ASSERT_TRUE(group);
+  bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group.get()));
+  ASSERT_TRUE(p);
+  bssl::UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
+  ASSERT_TRUE(result);
+  bssl::UniquePtr<BIGNUM> n(BN_new());
+  ASSERT_TRUE(n);
+  ASSERT_TRUE(EC_POINT_set_to_infinity(group.get(), p.get()));
+  ASSERT_TRUE(BN_set_word(n.get(), 10));
+
+  // First check that 10×∞ = ∞.
+  ASSERT_TRUE(EC_POINT_mul(group.get(), result.get(), nullptr, p.get(), n.get(),
+                           nullptr));
+  EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), result.get()));
+
+  // Now check that 10×∞ + G = G.
+  const EC_POINT *generator = EC_GROUP_get0_generator(group.get());
+  ASSERT_TRUE(EC_POINT_mul(group.get(), result.get(), BN_value_one(), p.get(),
+                           n.get(), nullptr));
+  EXPECT_EQ(0, EC_POINT_cmp(group.get(), result.get(), generator, nullptr));
+}
+
 static std::vector<EC_builtin_curve> AllCurves() {
   const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
   std::vector<EC_builtin_curve> curves(num_curves);
diff --git a/src/crypto/fipsmodule/ec/example_mul.c b/src/crypto/fipsmodule/ec/example_mul.c
deleted file mode 100644
index a2bdd52..0000000
--- a/src/crypto/fipsmodule/ec/example_mul.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Originally written by Bodo Moeller for the OpenSSL project.
- * ====================================================================
- * Copyright (c) 1998-2005 The OpenSSL Project.  All rights reserved.
- *
- * 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 above 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 acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    openssl-core@openssl.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED 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 OpenSSL PROJECT OR
- * ITS 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.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
- *
- */
-/* ====================================================================
- * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
- *
- * Portions of the attached software ("Contribution") are developed by
- * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
- *
- * The Contribution is licensed pursuant to the OpenSSL open source
- * license provided above.
- *
- * The elliptic curve binary polynomial software is originally written by
- * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
- * Laboratories. */
-
-#include <stdio.h>
-
-#include <openssl/bn.h>
-#include <openssl/crypto.h>
-#include <openssl/ec.h>
-#include <openssl/nid.h>
-
-
-static int example_EC_POINT_mul(void) {
-  /* This example ensures that 10×∞ + G = G, in P-256. */
-  EC_GROUP *group = NULL;
-  EC_POINT *p = NULL, *result = NULL;
-  BIGNUM *n = NULL;
-  int ret = 0;
-  const EC_POINT *generator;
-
-  group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-  p = EC_POINT_new(group);
-  result = EC_POINT_new(group);
-  n = BN_new();
-
-  if (p == NULL ||
-      result == NULL ||
-      group == NULL ||
-      n == NULL ||
-      !EC_POINT_set_to_infinity(group, p) ||
-      !BN_set_word(n, 10)) {
-    goto err;
-  }
-
-  /* First check that 10×∞ = ∞. */
-  if (!EC_POINT_mul(group, result, NULL, p, n, NULL) ||
-      !EC_POINT_is_at_infinity(group, result)) {
-    goto err;
-  }
-
-  generator = EC_GROUP_get0_generator(group);
-
-  /* Now check that 10×∞ + G = G. */
-  if (!EC_POINT_mul(group, result, BN_value_one(), p, n, NULL) ||
-      EC_POINT_cmp(group, result, generator, NULL) != 0) {
-    goto err;
-  }
-
-  ret = 1;
-
-err:
-  BN_free(n);
-  EC_POINT_free(result);
-  EC_POINT_free(p);
-  EC_GROUP_free(group);
-
-  return ret;
-}
-
-int main(void) {
-  CRYPTO_library_init();
-
-  if (!example_EC_POINT_mul()) {
-    fprintf(stderr, "failed\n");
-    return 1;
-  }
-
-  printf("PASS\n");
-  return 0;
-}
diff --git a/src/crypto/fipsmodule/ec/p224-64.c b/src/crypto/fipsmodule/ec/p224-64.c
index 31097d4..67dfcc8 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -181,12 +181,18 @@
       {0x32477c61b6e8c6, 0xb46a97570f018b, 0x91176d0a7e95d1, 0x3df90fbc4c7d0e},
       {1, 0, 0, 0}}}};
 
+static uint64_t p224_load_u64(const uint8_t in[8]) {
+  uint64_t ret;
+  OPENSSL_memcpy(&ret, in, sizeof(ret));
+  return ret;
+}
+
 /* Helper functions to convert field elements to/from internal representation */
 static void p224_bin28_to_felem(p224_felem out, const uint8_t in[28]) {
-  out[0] = *((const uint64_t *)(in)) & 0x00ffffffffffffff;
-  out[1] = (*((const uint64_t *)(in + 7))) & 0x00ffffffffffffff;
-  out[2] = (*((const uint64_t *)(in + 14))) & 0x00ffffffffffffff;
-  out[3] = (*((const uint64_t *)(in + 20))) >> 8;
+  out[0] = p224_load_u64(in) & 0x00ffffffffffffff;
+  out[1] = p224_load_u64(in + 7) & 0x00ffffffffffffff;
+  out[2] = p224_load_u64(in + 14) & 0x00ffffffffffffff;
+  out[3] = p224_load_u64(in + 20) >> 8;
 }
 
 static void p224_felem_to_bin28(uint8_t out[28], const p224_felem in) {
diff --git a/src/crypto/fipsmodule/ec/p256-64.c b/src/crypto/fipsmodule/ec/p256-64.c
index de1edc2..8952aa2 100644
--- a/src/crypto/fipsmodule/ec/p256-64.c
+++ b/src/crypto/fipsmodule/ec/p256-64.c
@@ -71,22 +71,32 @@
                               0xffffffff00000001ul};
 static const uint64_t bottom63bits = 0x7ffffffffffffffful;
 
+static uint64_t load_u64(const uint8_t in[8]) {
+  uint64_t ret;
+  OPENSSL_memcpy(&ret, in, sizeof(ret));
+  return ret;
+}
+
+static void store_u64(uint8_t out[8], uint64_t in) {
+  OPENSSL_memcpy(out, &in, sizeof(in));
+}
+
 /* bin32_to_felem takes a little-endian byte array and converts it into felem
  * form. This assumes that the CPU is little-endian. */
 static void bin32_to_felem(felem out, const uint8_t in[32]) {
-  out[0] = *((const uint64_t *)&in[0]);
-  out[1] = *((const uint64_t *)&in[8]);
-  out[2] = *((const uint64_t *)&in[16]);
-  out[3] = *((const uint64_t *)&in[24]);
+  out[0] = load_u64(&in[0]);
+  out[1] = load_u64(&in[8]);
+  out[2] = load_u64(&in[16]);
+  out[3] = load_u64(&in[24]);
 }
 
 /* smallfelem_to_bin32 takes a smallfelem and serialises into a little endian,
  * 32 byte array. This assumes that the CPU is little-endian. */
 static void smallfelem_to_bin32(uint8_t out[32], const smallfelem in) {
-  *((uint64_t *)&out[0]) = in[0];
-  *((uint64_t *)&out[8]) = in[1];
-  *((uint64_t *)&out[16]) = in[2];
-  *((uint64_t *)&out[24]) = in[3];
+  store_u64(&out[0], in[0]);
+  store_u64(&out[8], in[1]);
+  store_u64(&out[16], in[2]);
+  store_u64(&out[24], in[3]);
 }
 
 /* To preserve endianness when using BN_bn2bin and BN_bin2bn. */
diff --git a/src/crypto/x509v3/CMakeLists.txt b/src/crypto/x509v3/CMakeLists.txt
index cf2474a..b2eb618 100644
--- a/src/crypto/x509v3/CMakeLists.txt
+++ b/src/crypto/x509v3/CMakeLists.txt
@@ -42,25 +42,3 @@
   v3_sxnet.c
   v3_utl.c
 )
-
-add_executable(
-  v3name_test
-
-  v3name_test.c
-
-  $<TARGET_OBJECTS:test_support>
-)
-
-target_link_libraries(v3name_test crypto)
-add_dependencies(all_tests v3name_test)
-
-add_executable(
-  tab_test
-
-  tab_test.c
-
-  $<TARGET_OBJECTS:test_support>
-)
-
-target_link_libraries(tab_test crypto)
-add_dependencies(all_tests tab_test)
diff --git a/src/crypto/x509v3/ext_dat.h b/src/crypto/x509v3/ext_dat.h
index 9ece19c..78fa793 100644
--- a/src/crypto/x509v3/ext_dat.h
+++ b/src/crypto/x509v3/ext_dat.h
@@ -56,6 +56,10 @@
 
 /* This file contains a table of "standard" extensions */
 
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
 extern const X509V3_EXT_METHOD v3_bcons, v3_nscert, v3_key_usage, v3_ext_ku;
 extern const X509V3_EXT_METHOD v3_pkey_usage_period, v3_sxnet, v3_info,
     v3_sinfo;
@@ -133,3 +137,7 @@
 /* Number of standard extensions */
 
 #define STANDARD_EXTENSION_COUNT (sizeof(standard_exts)/sizeof(X509V3_EXT_METHOD *))
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
diff --git a/src/crypto/x509v3/tab_test.c b/src/crypto/x509v3/tab_test.cc
similarity index 70%
rename from src/crypto/x509v3/tab_test.c
rename to src/crypto/x509v3/tab_test.cc
index 1900547..bf91a26 100644
--- a/src/crypto/x509v3/tab_test.c
+++ b/src/crypto/x509v3/tab_test.cc
@@ -57,52 +57,22 @@
  *
  */
 
-/*
- * Simple program to check the ext_dat.h is correct and print out problems if
- * it is not.
- */
+#if !defined(BORINGSSL_SHARED_LIBRARY)
 
-#include <stdio.h>
+#include <gtest/gtest.h>
 
-#include <openssl/base.h>
-#include <openssl/crypto.h>
-#include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
-#if !defined(BORINGSSL_SHARED_LIBRARY)
-# include "ext_dat.h"
-#endif
+#include "../internal.h"
+#include "ext_dat.h"
 
-int main(void)
-{
-#if !defined(BORINGSSL_SHARED_LIBRARY)
-    unsigned i;
-    int prev = -1, bad = 0;
-    const X509V3_EXT_METHOD *const *tmp;
-    CRYPTO_library_init();
-    i = sizeof(standard_exts) / sizeof(X509V3_EXT_METHOD *);
-    if (i != STANDARD_EXTENSION_COUNT)
-        fprintf(stderr, "Extension number invalid expecting %d\n", i);
-    tmp = standard_exts;
-    for (i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++) {
-        if ((*tmp)->ext_nid < prev)
-            bad = 1;
-        prev = (*tmp)->ext_nid;
-
-    }
-    if (bad) {
-        tmp = standard_exts;
-        fprintf(stderr, "Extensions out of order!\n");
-        for (i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++)
-            printf("%d : %s\n", (*tmp)->ext_nid, OBJ_nid2sn((*tmp)->ext_nid));
-        return 1;
-    } else {
-        printf("PASS\n");
-        return 0;
-    }
-#else
-    /* TODO(davidben): Fix this test in the shared library build. */
-    printf("PASS\n");
-    return 0;
-#endif
+// Check ext_data.h is correct.
+TEST(X509V3Test, TabTest) {
+  EXPECT_EQ(OPENSSL_ARRAY_SIZE(standard_exts), STANDARD_EXTENSION_COUNT);
+  for (size_t i = 1; i < OPENSSL_ARRAY_SIZE(standard_exts); i++) {
+    SCOPED_TRACE(i);
+    EXPECT_LT(standard_exts[i-1]->ext_nid, standard_exts[i]->ext_nid);
+  }
 }
+
+#endif  // !BORINGSSL_SHARED_LIBRARY
diff --git a/src/crypto/x509v3/v3name_test.c b/src/crypto/x509v3/v3name_test.cc
similarity index 95%
rename from src/crypto/x509v3/v3name_test.c
rename to src/crypto/x509v3/v3name_test.cc
index 959b924..0736120 100644
--- a/src/crypto/x509v3/v3name_test.c
+++ b/src/crypto/x509v3/v3name_test.cc
@@ -57,6 +57,8 @@
 #include <stdarg.h>
 #include <string.h>
 
+#include <gtest/gtest.h>
+
 #include <openssl/crypto.h>
 #include <openssl/mem.h>
 #include <openssl/x509.h>
@@ -335,7 +337,7 @@
     while (*pname) {
         int samename = OPENSSL_strcasecmp(nameincert, *pname) == 0;
         size_t namelen = strlen(*pname);
-        char *name = malloc(namelen);
+        char *name = (char *)malloc(namelen);
         int match, ret;
         OPENSSL_memcpy(name, *pname, namelen);
 
@@ -383,31 +385,19 @@
     }
 }
 
-int main(void)
-{
-    CRYPTO_library_init();
-
+// TOOD(davidben): Convert this test to GTest more thoroughly.
+TEST(X509V3Test, NameTest) {
     const struct set_name_fn *pfn = name_fns;
     while (pfn->name) {
         const char *const *pname = names;
         while (*pname) {
-            X509 *crt = make_cert();
-            if (crt == NULL) {
-                fprintf(stderr, "make_cert failed\n");
-                return 1;
-            }
-            if (!pfn->fn(crt, *pname)) {
-                fprintf(stderr, "X509 name setting failed\n");
-                return 1;
-            }
-            run_cert(crt, *pname, pfn);
-            X509_free(crt);
+            bssl::UniquePtr<X509> crt(make_cert());
+            ASSERT_TRUE(crt);
+            ASSERT_TRUE(pfn->fn(crt.get(), *pname));
+            run_cert(crt.get(), *pname, pfn);
             ++pname;
         }
         ++pfn;
     }
-    if (errors == 0) {
-        printf("PASS\n");
-    }
-    return errors > 0 ? 1 : 0;
+    EXPECT_EQ(0, errors);
 }
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 5ebdade..0d2068f 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -938,6 +938,7 @@
 #if defined(__cplusplus)
 }  /* extern C */
 
+#if !defined(OPENSSL_NO_CXX)
 extern "C++" {
 
 namespace bssl {
@@ -946,9 +947,22 @@
 BORINGSSL_MAKE_DELETER(BN_CTX, BN_CTX_free)
 BORINGSSL_MAKE_DELETER(BN_MONT_CTX, BN_MONT_CTX_free)
 
+class BN_CTXScope {
+ public:
+  BN_CTXScope(BN_CTX *ctx) : ctx_(ctx) { BN_CTX_start(ctx_); }
+  ~BN_CTXScope() { BN_CTX_end(ctx_); }
+
+ private:
+  BN_CTX *ctx_;
+
+  BN_CTXScope(BN_CTXScope &) = delete;
+  BN_CTXScope &operator=(BN_CTXScope &) = delete;
+};
+
 }  // namespace bssl
 
 }  /* extern C++ */
+#endif
 
 #endif
 
diff --git a/src/include/openssl/pem.h b/src/include/openssl/pem.h
index ae6c23c..4868e12 100644
--- a/src/include/openssl/pem.h
+++ b/src/include/openssl/pem.h
@@ -125,7 +125,7 @@
 #define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \
 OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u)\
 { \
-return PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str,fp,(void **)x,cb,u); \
+return (type *)PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str,fp,(void **)x,cb,u); \
 } 
 
 #define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \
@@ -161,7 +161,7 @@
 #define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
 OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
 { \
-return PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
+return (type *)PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
 }
 
 #define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 59fbeb8..16aeaff 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -492,8 +492,7 @@
 
 /* SSL_ERROR_PENDING_CERTIFICATE indicates the operation failed because the
  * early callback indicated certificate lookup was incomplete. The caller may
- * retry the operation when lookup has completed. Note: when the operation is
- * retried, the early callback will not be called a second time.
+ * retry the operation when lookup has completed.
  *
  * See also |SSL_CTX_set_select_certificate_cb|. */
 #define SSL_ERROR_PENDING_CERTIFICATE 12
@@ -578,6 +577,8 @@
 #define DTLS1_2_VERSION 0xfefd
 
 #define TLS1_3_DRAFT_VERSION 0x7f12
+#define TLS1_3_EXPERIMENT_VERSION 0x7e01
+#define TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION 0x7a12
 
 /* SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to
  * |version|. If |version| is zero, the default minimum version is used. It
@@ -1874,12 +1875,17 @@
  * unset), the callback is not called.
  *
  * The callback is passed a reference to |session|. It returns one if it takes
- * ownership and zero otherwise.
+ * ownership (and then calls |SSL_SESSION_free| when done) and zero otherwise. A
+ * consumer which places |session| into an in-memory cache will likely return
+ * one, with the cache calling |SSL_SESSION_free|. A consumer which serializes
+ * |session| with |SSL_SESSION_to_bytes| may not need to retain |session| and
+ * will likely return zero. Returning one is equivalent to calling
+ * |SSL_SESSION_up_ref| and then returning zero.
  *
  * Note: For a client, the callback may be called on abbreviated handshakes if a
  * ticket is renewed. Further, it may not be called until some time after
  * |SSL_do_handshake| or |SSL_connect| completes if False Start is enabled. Thus
- * it's recommended to use this callback over checking |SSL_session_reused| on
+ * it's recommended to use this callback over calling |SSL_get_session| on
  * handshake completion. */
 OPENSSL_EXPORT void SSL_CTX_sess_set_new_cb(
     SSL_CTX *ctx, int (*new_session_cb)(SSL *ssl, SSL_SESSION *session));
@@ -3131,6 +3137,24 @@
  * performed by |ssl|. This includes the pending renegotiation, if any. */
 OPENSSL_EXPORT int SSL_total_renegotiations(const SSL *ssl);
 
+enum tls13_variant_t {
+  tls13_default = 0,
+  tls13_experiment = 1,
+  tls13_record_type_experiment = 2,
+};
+
+/* SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
+ * server, if |variant| is not |tls13_default|, all variants are enabled. On the
+ * client, only the configured variant is enabled. */
+OPENSSL_EXPORT void SSL_CTX_set_tls13_variant(SSL_CTX *ctx,
+                                              enum tls13_variant_t variant);
+
+/* SSL_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
+ * server, if |variant| is not |tls13_default|, all variants are enabled. On the
+ * client, only the configured variant is enabled. */
+OPENSSL_EXPORT void SSL_set_tls13_variant(SSL *ssl,
+                                          enum tls13_variant_t variant);
+
 /* SSL_MAX_CERT_LIST_DEFAULT is the default maximum length, in bytes, of a peer
  * certificate chain. */
 #define SSL_MAX_CERT_LIST_DEFAULT (1024 * 100)
@@ -4119,6 +4143,10 @@
    * and is 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;
+
   struct ssl_cipher_preference_list_st *cipher_list;
 
   X509_STORE *cert_store;
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index 98648c4..2b241ba 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -272,6 +272,7 @@
 #define SSL3_RT_ALERT 21
 #define SSL3_RT_HANDSHAKE 22
 #define SSL3_RT_APPLICATION_DATA 23
+#define SSL3_RT_PLAINTEXT_HANDSHAKE 24
 
 /* Pseudo content type for SSL/TLS header info */
 #define SSL3_RT_HEADER 0x100
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;
 }
 
diff --git a/src/tool/client.cc b/src/tool/client.cc
index 74a7124..f7a8259 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -124,6 +124,10 @@
         "-early-data", kOptionalArgument, "Allow early data",
     },
     {
+        "-tls13-variant", kOptionalArgument,
+        "Enable the specified experimental TLS 1.3 variant",
+    },
+    {
         "-ed25519", kBooleanArgument, "Advertise Ed25519 support",
     },
     {
@@ -459,6 +463,12 @@
     SSL_CTX_set_early_data_enabled(ctx.get(), 1);
   }
 
+  if (args_map.count("-tls13-variant") != 0) {
+    SSL_CTX_set_tls13_variant(ctx.get(),
+                              static_cast<enum tls13_variant_t>(
+                                  atoi(args_map["-tls13-variant"].c_str())));
+  }
+
   if (args_map.count("-ed25519") != 0) {
     SSL_CTX_set_ed25519_enabled(ctx.get(), 1);
   }
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 1d6d27c..3b125ad 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -68,6 +68,9 @@
         "-early-data", kBooleanArgument, "Allow early data",
     },
     {
+        "-tls13-variant", kBooleanArgument, "Enable TLS 1.3 variants",
+    },
+    {
         "", kOptionalArgument, "",
     },
 };
@@ -233,6 +236,11 @@
     SSL_CTX_set_early_data_enabled(ctx.get(), 1);
   }
 
+  // Enabling any TLS 1.3 variant on the server enables all of them.
+  if (args_map.count("-tls13-variant") != 0) {
+    SSL_CTX_set_tls13_variant(ctx.get(), tls13_experiment);
+  }
+
   Listener listener;
   if (!listener.Init(args_map["-accept"])) {
     return false;
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index b92a057..01d6fd0 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -1,8 +1,5 @@
 [
 	["crypto/crypto_test"],
-	["crypto/fipsmodule/example_mul"],
-	["crypto/x509v3/tab_test"],
-	["crypto/x509v3/v3name_test"],
 	["decrepit/decrepit_test"],
 	["ssl/ssl_test"]
 ]
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index 38d757b..ef340e6 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -164,14 +164,6 @@
       blueprint.write('    ],\n')
       blueprint.write('}\n\n')
 
-      blueprint.write('cc_defaults {\n')
-      blueprint.write('    name: "boringssl_tests_sources",\n')
-      blueprint.write('    srcs: [\n')
-      for f in sorted(files['test']):
-        blueprint.write('        "%s",\n' % f)
-      blueprint.write('    ],\n')
-      blueprint.write('}\n')
-
     # Legacy Android.mk format, only used by Trusty in new branches
     with open('sources.mk', 'w+') as makefile:
       makefile.write(self.header)
@@ -214,8 +206,6 @@
       self.PrintVariableSection(
           out, 'ssl_internal_headers', files['ssl_internal_headers'])
       self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
-      self.PrintVariableSection(out, 'ssl_c_sources', files['ssl_c'])
-      self.PrintVariableSection(out, 'ssl_cc_sources', files['ssl_cc'])
       self.PrintVariableSection(out, 'crypto_headers', files['crypto_headers'])
       self.PrintVariableSection(
           out, 'crypto_internal_headers', files['crypto_internal_headers'])
@@ -245,79 +235,6 @@
                                 files['crypto_test'])
       self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
 
-      out.write('def create_tests(copts, crypto, ssl):\n')
-      name_counts = {}
-      for test in files['tests']:
-        name = os.path.basename(test[0])
-        name_counts[name] = name_counts.get(name, 0) + 1
-
-      first = True
-      test_names = set()
-      for test in files['tests']:
-        name = os.path.basename(test[0])
-        if name_counts[name] > 1:
-          if '/' in test[-1]:
-            arg = test[-1].replace('crypto/cipher/test/','')  # boooring
-            arg = arg.replace('/', '_')
-            arg = os.path.splitext(arg)[0]  # remove .txt
-            arg = arg.replace('_tests', '')
-            name += '_' + arg
-          else:
-            name += '_' + test[-1].replace('-', '_')
-
-        if name in test_names:
-          raise ValueError("test name %s is not unique" % name)
-        test_names.add(name)
-
-        if not first:
-          out.write('\n')
-        first = False
-
-        for src in files['test']:
-          # For example, basename(src/crypto/fipsmodule/aes/aes_test.cc)
-          # startswith basename(crypto/fipsmodule/aes_test).
-          if os.path.basename(src).startswith(os.path.basename(test[0]) + '.'):
-            src = src
-            break
-        else:
-          raise ValueError("Can't find source for %s" % test[0])
-
-        out.write('  native.cc_test(\n')
-        out.write('      name = "%s",\n' % name)
-        out.write('      size = "small",\n')
-        out.write('      srcs = ["%s"] + test_support_sources,\n' %
-            PathOf(src))
-
-        data_files = []
-        if len(test) > 1:
-
-          out.write('      args = [\n')
-          for arg in test[1:]:
-            if '/' in arg:
-              out.write('          "$(location %s)",\n' %
-                  PathOf(os.path.join('src', arg)))
-              data_files.append('src/%s' % arg)
-            else:
-              out.write('          "%s",\n' % arg)
-          out.write('      ],\n')
-
-        out.write('      copts = copts,\n')
-
-        if len(data_files) > 0:
-          out.write('      data = [\n')
-          for filename in data_files:
-            out.write('          "%s",\n' % PathOf(filename))
-          out.write('      ],\n')
-
-        if 'ssl/' in test[0]:
-          out.write('      deps = [\n')
-          out.write('          crypto,\n')
-          out.write('          ssl,\n')
-          out.write('      ],\n')
-        else:
-          out.write('      deps = [crypto],\n')
-        out.write('  )\n')
-
 
 class GN(object):
 
@@ -371,36 +288,6 @@
       self.PrintVariableSection(out, 'crypto_test_sources',
                                 files['crypto_test'])
       self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
-      out.write('\n')
-
-      out.write('template("create_tests") {\n')
-
-      all_tests = []
-      for test in sorted(files['test']):
-        test_name = 'boringssl_%s' % os.path.splitext(os.path.basename(test))[0]
-        all_tests.append(test_name)
-
-        out.write('  executable("%s") {\n' % test_name)
-        out.write('    sources = [\n')
-        out.write('      "%s",\n' % test)
-        out.write('    ]\n')
-        out.write('    sources += test_support_sources\n')
-        out.write('    if (defined(invoker.configs_exclude)) {\n')
-        out.write('      configs -= invoker.configs_exclude\n')
-        out.write('    }\n')
-        out.write('    configs += invoker.configs\n')
-        out.write('    deps = invoker.deps + ')
-        out.write('[ "//build/config:exe_and_shlib_deps" ]\n')
-        out.write('  }\n')
-        out.write('\n')
-
-      out.write('  group(target_name) {\n')
-      out.write('    deps = [\n')
-      for test_name in sorted(all_tests):
-        out.write('      ":%s",\n' % test_name)
-      out.write('    ]\n')
-      out.write('  }\n')
-      out.write('}\n')
 
 
 class GYP(object):
@@ -464,7 +351,7 @@
   sources."""
   if is_dir:
     return dent != 'test'
-  return 'test.' not in dent and not dent.startswith('example_')
+  return 'test.' not in dent
 
 
 def OnlyTests(path, dent, is_dir):
@@ -472,7 +359,7 @@
   non-test sources."""
   if is_dir:
     return dent != 'test'
-  return '_test.' in dent or dent.startswith('example_')
+  return '_test.' in dent
 
 
 def AllFiles(path, dent, is_dir):
@@ -662,11 +549,6 @@
   return variables
 
 
-def IsGTest(path):
-  with open(path) as f:
-    return "#include <gtest/gtest.h>" in f.read()
-
-
 def main(platforms):
   cmake = ExtractVariablesFromCMakeFile(os.path.join('src', 'sources.cmake'))
   crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments)
@@ -695,18 +577,12 @@
         cwd='src',
         stdout=out)
 
-  test_c_files = []
-  crypto_test_files = [
+  crypto_test_files = FindCFiles(os.path.join('src', 'crypto'), OnlyTests)
+  crypto_test_files += [
       'crypto_test_data.cc',
       'src/crypto/test/file_test_gtest.cc',
       'src/crypto/test/gtest_main.cc',
   ]
-  # TODO(davidben): Remove this loop once all tests are converted.
-  for path in FindCFiles(os.path.join('src', 'crypto'), OnlyTests):
-    if IsGTest(path):
-      crypto_test_files.append(path)
-    else:
-      test_c_files.append(path)
 
   ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
   ssl_test_files.append('src/crypto/test/gtest_main.cc')
@@ -729,37 +605,6 @@
   crypto_internal_h_files = FindHeaderFiles(
       os.path.join('src', 'crypto'), NoTests)
 
-  with open('src/util/all_tests.json', 'r') as f:
-    tests = json.load(f)
-  # For now, GTest-based tests are specified manually.
-  tests = [test for test in tests if test[0] not in ['crypto/crypto_test',
-                                                     'decrepit/decrepit_test',
-                                                     'ssl/ssl_test']]
-  # The same test name can appear multiple times with different arguments.  So,
-  # make a set to get a list of unique test binaries.
-  test_names = set([test[0] for test in tests])
-  test_binaries = set(map(os.path.basename, test_names))
-  # Make sure the test basenames are unique.
-  if len(test_binaries) != len(set(test_names)):
-    raise ValueError('non-unique test basename')
-  # Ensure a 1:1 correspondence between test sources and tests.  This
-  # guarantees that the Bazel output includes everything in the JSON.
-  # Sometimes, a test's source isn't in the same directory as the test,
-  # which we handle by considering only the basename.
-  test_sources = set(map(os.path.basename, [
-      test.replace('.cc', '').replace('.c', '').replace(
-          'src/',
-          '')
-      for test in test_c_files]))
-  if test_binaries != test_sources:
-    print 'Test sources and configured tests do not match'
-    a = test_binaries.difference(test_sources)
-    if len(a) > 0:
-      print 'These tests are configured without sources: ' + str(a)
-    b = test_sources.difference(test_binaries)
-    if len(b) > 0:
-      print 'These test sources are not configured: ' + str(b)
-
   files = {
       'crypto': crypto_c_files,
       'crypto_headers': crypto_h_files,
@@ -768,17 +613,13 @@
       'fips_fragments': fips_fragments,
       'fuzz': fuzz_c_files,
       'ssl': ssl_source_files,
-      'ssl_c': [s for s in ssl_source_files if s.endswith('.c')],
-      'ssl_cc': [s for s in ssl_source_files if s.endswith('.cc')],
       'ssl_headers': ssl_h_files,
       'ssl_internal_headers': ssl_internal_h_files,
       'ssl_test': sorted(ssl_test_files),
       'tool': tool_c_files,
       'tool_headers': tool_h_files,
-      'test': test_c_files,
       'test_support': test_support_c_files,
       'test_support_headers': test_support_h_files,
-      'tests': tests,
   }
 
   asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).iteritems())