external/boringssl: Sync to 040bc4944be97f5d4b44da176f6e801fc804a176. am: 5d625781eb am: dc2f6e4a42
am: 9ba2b26d7e

Change-Id: I881ab3af2870a4190aec09a5e84320dd34228dd0
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 14bbebc..f26983b 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-ab20cec1c1de815de8da6cc74c2503460efd6e1c
+040bc4944be97f5d4b44da176f6e801fc804a176
diff --git a/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S b/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
index 241d7d0..c7e673f 100644
--- a/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
+++ b/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
@@ -2,6 +2,9 @@
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
+
+chacha20_poly1305_constants:
+
 .align	64
 .chacha20_consts:
 .byte	'e','x','p','a','n','d',' ','3','2','-','b','y','t','e',' ','k'
diff --git a/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S b/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
index 4e5c0f3..03cd872 100644
--- a/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
+++ b/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
@@ -1,6 +1,9 @@
 #if defined(__x86_64__)
 .text	
 
+
+chacha20_poly1305_constants:
+
 .p2align	6
 .chacha20_consts:
 .byte	'e','x','p','a','n','d',' ','3','2','-','b','y','t','e',' ','k'
diff --git a/sources.bp b/sources.bp
index 99720a5..8e13f1a 100644
--- a/sources.bp
+++ b/sources.bp
@@ -408,7 +408,6 @@
         "src/ssl/handshake_client.c",
         "src/ssl/handshake_server.c",
         "src/ssl/s3_both.c",
-        "src/ssl/s3_enc.c",
         "src/ssl/s3_lib.c",
         "src/ssl/s3_pkt.c",
         "src/ssl/ssl_aead_ctx.c",
@@ -419,10 +418,11 @@
         "src/ssl/ssl_ecdh.c",
         "src/ssl/ssl_file.c",
         "src/ssl/ssl_lib.c",
-        "src/ssl/ssl_rsa.c",
-        "src/ssl/ssl_rsa_cc.cc",
+        "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_x509.c",
         "src/ssl/t1_enc.c",
         "src/ssl/t1_lib.c",
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 36224fc..bbc68d0 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -214,6 +214,7 @@
 
   dh/dh_test.cc
   dsa/dsa_test.cc
+  err/err_test.cc
 
   $<TARGET_OBJECTS:gtest_main>
   $<TARGET_OBJECTS:test_support>
diff --git a/src/crypto/bn/asm/rsaz-avx2.pl b/src/crypto/bn/asm/rsaz-avx2.pl
index 73db3c2..b8e830e 100755
--- a/src/crypto/bn/asm/rsaz-avx2.pl
+++ b/src/crypto/bn/asm/rsaz-avx2.pl
@@ -804,6 +804,7 @@
 	mov	%rbp, %rax
 ___
 $code.=<<___ if ($win64);
+.Lsqr_1024_in_tail:
 	movaps	-0xd8(%rax),%xmm6
 	movaps	-0xc8(%rax),%xmm7
 	movaps	-0xb8(%rax),%xmm8
@@ -1437,6 +1438,7 @@
 	mov	%rbp, %rax
 ___
 $code.=<<___ if ($win64);
+.Lmul_1024_in_tail:
 	movaps	-0xd8(%rax),%xmm6
 	movaps	-0xc8(%rax),%xmm7
 	movaps	-0xb8(%rax),%xmm8
@@ -1792,14 +1794,17 @@
 	cmp	%r10,%rbx		# context->Rip<prologue label
 	jb	.Lcommon_seh_tail
 
-	mov	152($context),%rax	# pull context->Rsp
-
 	mov	4(%r11),%r10d		# HandlerData[1]
 	lea	(%rsi,%r10),%r10	# epilogue label
 	cmp	%r10,%rbx		# context->Rip>=epilogue label
 	jae	.Lcommon_seh_tail
 
-	mov	160($context),%rax	# pull context->Rbp
+	mov	160($context),%rbp	# pull context->Rbp
+
+	mov	8(%r11),%r10d		# HandlerData[2]
+	lea	(%rsi,%r10),%r10	# "in tail" label
+	cmp	%r10,%rbx		# context->Rip>="in tail" label
+	cmovc	%rbp,%rax
 
 	mov	-48(%rax),%r15
 	mov	-40(%rax),%r14
@@ -1877,11 +1882,13 @@
 .LSEH_info_rsaz_1024_sqr_avx2:
 	.byte	9,0,0,0
 	.rva	rsaz_se_handler
-	.rva	.Lsqr_1024_body,.Lsqr_1024_epilogue
+	.rva	.Lsqr_1024_body,.Lsqr_1024_epilogue,.Lsqr_1024_in_tail
+	.long	0
 .LSEH_info_rsaz_1024_mul_avx2:
 	.byte	9,0,0,0
 	.rva	rsaz_se_handler
-	.rva	.Lmul_1024_body,.Lmul_1024_epilogue
+	.rva	.Lmul_1024_body,.Lmul_1024_epilogue,.Lmul_1024_in_tail
+	.long	0
 .LSEH_info_rsaz_1024_gather5:
 	.byte	0x01,0x36,0x17,0x0b
 	.byte	0x36,0xf8,0x09,0x00	# vmovaps 0x90(rsp),xmm15
diff --git a/src/crypto/bn/bn_test.cc b/src/crypto/bn/bn_test.cc
index a152cdf..c0af58d 100644
--- a/src/crypto/bn/bn_test.cc
+++ b/src/crypto/bn/bn_test.cc
@@ -1668,6 +1668,93 @@
   return true;
 }
 
+static bool TestBNPow2(BN_CTX *ctx) {
+  bssl::UniquePtr<BIGNUM>
+      power_of_two(BN_new()),
+      random(BN_new()),
+      expected(BN_new()),
+      actual(BN_new());
+
+  if (!power_of_two.get() ||
+      !random.get() ||
+      !expected.get() ||
+      !actual.get()) {
+    return false;
+  }
+
+  // Choose an exponent.
+  for (size_t e = 3; e < 512; e += 11) {
+    // Choose a bit length for our randoms.
+    for (int len = 3; len < 512; len += 23) {
+      // Set power_of_two = 2^e.
+      if (!BN_lshift(power_of_two.get(), BN_value_one(), (int) e)) {
+        fprintf(stderr, "Failed to shiftl.\n");
+        return false;
+      }
+
+      // Test BN_is_pow2 on power_of_two.
+      if (!BN_is_pow2(power_of_two.get())) {
+        fprintf(stderr, "BN_is_pow2 returned false for a power of two.\n");
+        hexdump(stderr, "Arg: ", power_of_two->d,
+                power_of_two->top * sizeof(BN_ULONG));
+        return false;
+      }
+
+      // Pick a large random value, ensuring it isn't a power of two.
+      if (!BN_rand(random.get(), len, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY)) {
+        fprintf(stderr, "Failed to generate random in TestBNPow2.\n");
+        return false;
+      }
+
+      // Test BN_is_pow2 on |r|.
+      if (BN_is_pow2(random.get())) {
+        fprintf(stderr, "BN_is_pow2 returned true for a non-power of two.\n");
+        hexdump(stderr, "Arg: ", random->d, random->top * sizeof(BN_ULONG));
+        return false;
+      }
+
+      // Test BN_mod_pow2 on |r|.
+      if (!BN_mod(expected.get(), random.get(), power_of_two.get(), ctx) ||
+          !BN_mod_pow2(actual.get(), random.get(), e) ||
+          BN_cmp(actual.get(), expected.get())) {
+        fprintf(stderr, "BN_mod_pow2 returned the wrong value:\n");
+        hexdump(stderr, "Expected: ", expected->d,
+                expected->top * sizeof(BN_ULONG));
+        hexdump(stderr, "Got:      ", actual->d,
+                actual->top * sizeof(BN_ULONG));
+        return false;
+      }
+
+      // Test BN_nnmod_pow2 on |r|.
+      if (!BN_nnmod(expected.get(), random.get(), power_of_two.get(), ctx) ||
+          !BN_nnmod_pow2(actual.get(), random.get(), e) ||
+          BN_cmp(actual.get(), expected.get())) {
+        fprintf(stderr, "BN_nnmod_pow2 failed on positive input:\n");
+        hexdump(stderr, "Expected: ", expected->d,
+                expected->top * sizeof(BN_ULONG));
+        hexdump(stderr, "Got:      ", actual->d,
+                actual->top * sizeof(BN_ULONG));
+        return false;
+      }
+
+      // Test BN_nnmod_pow2 on -|r|.
+      BN_set_negative(random.get(), 1);
+      if (!BN_nnmod(expected.get(), random.get(), power_of_two.get(), ctx) ||
+          !BN_nnmod_pow2(actual.get(), random.get(), e) ||
+          BN_cmp(actual.get(), expected.get())) {
+        fprintf(stderr, "BN_nnmod_pow2 failed on negative input:\n");
+        hexdump(stderr, "Expected: ", expected->d,
+                expected->top * sizeof(BN_ULONG));
+        hexdump(stderr, "Got:      ", actual->d,
+                actual->top * sizeof(BN_ULONG));
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
 int main(int argc, char *argv[]) {
   CRYPTO_library_init();
 
@@ -1695,7 +1782,8 @@
       !TestSmallPrime(ctx.get()) ||
       !TestCmpWord() ||
       !TestBN2Dec() ||
-      !TestBNSetGetU64()) {
+      !TestBNSetGetU64() ||
+      !TestBNPow2(ctx.get())) {
     return 1;
   }
 
diff --git a/src/crypto/bn/cmp.c b/src/crypto/bn/cmp.c
index 9cf33b4..71c0465 100644
--- a/src/crypto/bn/cmp.c
+++ b/src/crypto/bn/cmp.c
@@ -212,6 +212,20 @@
   return bn->top > 0 && (bn->d[0] & 1) == 1;
 }
 
+int BN_is_pow2(const BIGNUM *bn) {
+  if (bn->top == 0 || bn->neg) {
+    return 0;
+  }
+
+  for (int i = 0; i < bn->top - 1; i++) {
+    if (bn->d[i] != 0) {
+      return 0;
+    }
+  }
+
+  return 0 == (bn->d[bn->top-1] & (bn->d[bn->top-1] - 1));
+}
+
 int BN_equal_consttime(const BIGNUM *a, const BIGNUM *b) {
   if (a->top != b->top) {
     return 0;
diff --git a/src/crypto/bn/div.c b/src/crypto/bn/div.c
index 6e3df7d..de3fa1f 100644
--- a/src/crypto/bn/div.c
+++ b/src/crypto/bn/div.c
@@ -58,6 +58,7 @@
 
 #include <assert.h>
 #include <limits.h>
+
 #include <openssl/err.h>
 
 #include "internal.h"
@@ -646,3 +647,82 @@
   }
   return (BN_ULONG)ret;
 }
+
+int BN_mod_pow2(BIGNUM *r, const BIGNUM *a, size_t e) {
+  if (e == 0 || a->top == 0) {
+    BN_zero(r);
+    return 1;
+  }
+
+  size_t num_words = 1 + ((e - 1) / BN_BITS2);
+
+  /* If |a| definitely has less than |e| bits, just BN_copy. */
+  if ((size_t) a->top < num_words) {
+    return BN_copy(r, a) != NULL;
+  }
+
+  /* Otherwise, first make sure we have enough space in |r|.
+   * Note that this will fail if num_words > INT_MAX. */
+  if (bn_wexpand(r, num_words) == NULL) {
+    return 0;
+  }
+
+  /* Copy the content of |a| into |r|. */
+  OPENSSL_memcpy(r->d, a->d, num_words * sizeof(BN_ULONG));
+
+  /* If |e| isn't word-aligned, we have to mask off some of our bits. */
+  size_t top_word_exponent = e % (sizeof(BN_ULONG) * 8);
+  if (top_word_exponent != 0) {
+    r->d[num_words - 1] &= (((BN_ULONG) 1) << top_word_exponent) - 1;
+  }
+
+  /* Fill in the remaining fields of |r|. */
+  r->neg = a->neg;
+  r->top = (int) num_words;
+  bn_correct_top(r);
+  return 1;
+}
+
+int BN_nnmod_pow2(BIGNUM *r, const BIGNUM *a, size_t e) {
+  if (!BN_mod_pow2(r, a, e)) {
+    return 0;
+  }
+
+  /* If the returned value was non-negative, we're done. */
+  if (BN_is_zero(r) || !r->neg) {
+    return 1;
+  }
+
+  size_t num_words = 1 + (e - 1) / BN_BITS2;
+
+  /* Expand |r| to the size of our modulus. */
+  if (bn_wexpand(r, num_words) == NULL) {
+    return 0;
+  }
+
+  /* Clear the upper words of |r|. */
+  OPENSSL_memset(&r->d[r->top], 0, (num_words - r->top) * BN_BYTES);
+
+  /* Set parameters of |r|. */
+  r->neg = 0;
+  r->top = (int) num_words;
+
+  /* Now, invert every word. The idea here is that we want to compute 2^e-|x|,
+   * which is actually equivalent to the twos-complement representation of |x|
+   * in |e| bits, which is -x = ~x + 1. */
+  for (int i = 0; i < r->top; i++) {
+    r->d[i] = ~r->d[i];
+  }
+
+  /* If our exponent doesn't span the top word, we have to mask the rest. */
+  size_t top_word_exponent = e % BN_BITS2;
+  if (top_word_exponent != 0) {
+    r->d[r->top - 1] &= (((BN_ULONG) 1) << top_word_exponent) - 1;
+  }
+
+  /* Keep the correct_top invariant for BN_add. */
+  bn_correct_top(r);
+
+  /* Finally, add one, for the reason described above. */
+  return BN_add(r, r, BN_value_one());
+}
diff --git a/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl b/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
index 44590c2..857f1d5 100644
--- a/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
+++ b/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
@@ -39,6 +39,9 @@
 $code.=<<___;
 .text
 .extern OPENSSL_ia32cap_P
+
+chacha20_poly1305_constants:
+
 .align 64
 .chacha20_consts:
 .byte 'e','x','p','a','n','d',' ','3','2','-','b','y','t','e',' ','k'
diff --git a/src/crypto/dh/dh_test.cc b/src/crypto/dh/dh_test.cc
index 9cde679..e08664f 100644
--- a/src/crypto/dh/dh_test.cc
+++ b/src/crypto/dh/dh_test.cc
@@ -86,7 +86,6 @@
       !TestBadY() ||
       !TestASN1() ||
       !TestRFC3526()) {
-    ERR_print_errors_fp(stderr);
     ADD_FAILURE() << "Tests failed.";
   }
 }
diff --git a/src/crypto/dsa/dsa_test.cc b/src/crypto/dsa/dsa_test.cc
index d2cd33e..63b7803 100644
--- a/src/crypto/dsa/dsa_test.cc
+++ b/src/crypto/dsa/dsa_test.cc
@@ -312,7 +312,6 @@
       !TestVerify(fips_sig_extra, sizeof(fips_sig_extra), -1) ||
       !TestVerify(fips_sig_bad_length, sizeof(fips_sig_bad_length), -1) ||
       !TestVerify(fips_sig_bad_r, sizeof(fips_sig_bad_r), 0)) {
-    ERR_print_errors_fp(stderr);
     ADD_FAILURE() << "Tests failed";
   }
 }
diff --git a/src/crypto/err/CMakeLists.txt b/src/crypto/err/CMakeLists.txt
index a095b54..579a35b 100644
--- a/src/crypto/err/CMakeLists.txt
+++ b/src/crypto/err/CMakeLists.txt
@@ -37,14 +37,3 @@
   err.c
   err_data.c
 )
-
-add_executable(
-  err_test
-
-  err_test.cc
-
-  $<TARGET_OBJECTS:test_support>
-)
-
-target_link_libraries(err_test crypto)
-add_dependencies(all_tests err_test)
diff --git a/src/crypto/err/err_test.cc b/src/crypto/err/err_test.cc
index d691355..8e820b8 100644
--- a/src/crypto/err/err_test.cc
+++ b/src/crypto/err/err_test.cc
@@ -15,40 +15,34 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <gtest/gtest.h>
+
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
 
-static bool TestOverflow() {
+TEST(ErrTest, Overflow) {
   for (unsigned i = 0; i < ERR_NUM_ERRORS*2; i++) {
     ERR_put_error(1, 0 /* unused */, i+1, "test", 1);
   }
 
   for (unsigned i = 0; i < ERR_NUM_ERRORS - 1; i++) {
+    SCOPED_TRACE(i);
     uint32_t err = ERR_get_error();
     /* Errors are returned in order they were pushed, with the least recent ones
      * removed, up to |ERR_NUM_ERRORS - 1| errors. So the errors returned are
      * |ERR_NUM_ERRORS + 2| through |ERR_NUM_ERRORS * 2|, inclusive. */
-    if (err == 0 || ((unsigned)ERR_GET_REASON(err)) != i + ERR_NUM_ERRORS + 2) {
-      fprintf(stderr, "ERR_get_error failed at %u\n", i);
-      return false;
-    }
+    EXPECT_NE(0u, err);
+    EXPECT_EQ(static_cast<int>(i + ERR_NUM_ERRORS + 2), ERR_GET_REASON(err));
   }
 
-  if (ERR_get_error() != 0) {
-    fprintf(stderr, "ERR_get_error more than the expected number of values.\n");
-    return false;
-  }
-
-  return true;
+  EXPECT_EQ(0u, ERR_get_error());
 }
 
-static bool TestPutError() {
-  if (ERR_get_error() != 0) {
-    fprintf(stderr, "ERR_get_error returned value before an error was added.\n");
-    return false;
-  }
+TEST(ErrTest, PutError) {
+  ASSERT_EQ(0u, ERR_get_error())
+      << "ERR_get_error returned value before an error was added.";
 
   ERR_put_error(1, 0 /* unused */, 2, "test", 4);
   ERR_add_error_data(1, "testing");
@@ -60,45 +54,31 @@
                                &peeked_flags);
   uint32_t packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
 
-  if (peeked_packed_error != packed_error ||
-      peeked_file != file ||
-      peeked_data != data ||
-      peeked_flags != flags) {
-    fprintf(stderr, "Bad peeked error data returned.\n");
-    return false;
-  }
+  EXPECT_EQ(peeked_packed_error, packed_error);
+  EXPECT_EQ(peeked_file, file);
+  EXPECT_EQ(peeked_data, data);
+  EXPECT_EQ(peeked_flags, flags);
 
-  if (strcmp(file, "test") != 0 ||
-      line != 4 ||
-      (flags & ERR_FLAG_STRING) == 0 ||
-      ERR_GET_LIB(packed_error) != 1 ||
-      ERR_GET_REASON(packed_error) != 2 ||
-      strcmp(data, "testing") != 0) {
-    fprintf(stderr, "Bad error data returned.\n");
-    return false;
-  }
-
-  return true;
+  EXPECT_STREQ("test", file);
+  EXPECT_EQ(4, line);
+  EXPECT_TRUE(flags & ERR_FLAG_STRING);
+  EXPECT_EQ(1, ERR_GET_LIB(packed_error));
+  EXPECT_EQ(2, ERR_GET_REASON(packed_error));
+  EXPECT_STREQ("testing", data);
 }
 
-static bool TestClearError() {
-  if (ERR_get_error() != 0) {
-    fprintf(stderr, "ERR_get_error returned value before an error was added.\n");
-    return false;
-  }
+TEST(ErrTest, ClearError) {
+  ASSERT_EQ(0u, ERR_get_error())
+      << "ERR_get_error returned value before an error was added.";
 
   ERR_put_error(1, 0 /* unused */, 2, "test", 4);
   ERR_clear_error();
 
-  if (ERR_get_error() != 0) {
-    fprintf(stderr, "Error remained after clearing.\n");
-    return false;
-  }
-
-  return true;
+  // The error queue should be cleared.
+  EXPECT_EQ(0u, ERR_get_error());
 }
 
-static bool TestPrint() {
+TEST(ErrTest, Print) {
   ERR_put_error(1, 0 /* unused */, 2, "test", 4);
   ERR_add_error_data(1, "testing");
   uint32_t packed_error = ERR_get_error();
@@ -107,14 +87,14 @@
   for (size_t i = 0; i <= sizeof(buf); i++) {
     ERR_error_string_n(packed_error, buf, i);
   }
-
-  return true;
 }
 
-static bool TestRelease() {
+TEST(ErrTest, Release) {
   ERR_put_error(1, 0 /* unused */, 2, "test", 4);
   ERR_remove_thread_state(NULL);
-  return true;
+
+  // The error queue should be cleared.
+  EXPECT_EQ(0u, ERR_get_error());
 }
 
 static bool HasSuffix(const char *str, const char *suffix) {
@@ -126,7 +106,7 @@
   return strcmp(str + str_len - suffix_len, suffix) == 0;
 }
 
-static bool TestPutMacro() {
+TEST(ErrTest, PutMacro) {
   int expected_line = __LINE__ + 1;
   OPENSSL_PUT_ERROR(USER, ERR_R_INTERNAL_ERROR);
 
@@ -134,29 +114,8 @@
   const char *file;
   uint32_t error = ERR_get_error_line(&file, &line);
 
-  if (!HasSuffix(file, "err_test.cc") ||
-      line != expected_line ||
-      ERR_GET_LIB(error) != ERR_LIB_USER ||
-      ERR_GET_REASON(error) != ERR_R_INTERNAL_ERROR) {
-    fprintf(stderr, "Bad error data returned.\n");
-    return false;
-  }
-
-  return true;
-}
-
-int main() {
-  CRYPTO_library_init();
-
-  if (!TestOverflow() ||
-      !TestPutError() ||
-      !TestClearError() ||
-      !TestPrint() ||
-      !TestRelease() ||
-      !TestPutMacro()) {
-    return 1;
-  }
-
-  printf("PASS\n");
-  return 0;
+  EXPECT_PRED2(HasSuffix, file, "err_test.cc");
+  EXPECT_EQ(expected_line, line);
+  EXPECT_EQ(ERR_LIB_USER, ERR_GET_LIB(error));
+  EXPECT_EQ(ERR_R_INTERNAL_ERROR, ERR_GET_REASON(error));
 }
diff --git a/src/crypto/perlasm/x86_64-xlate.pl b/src/crypto/perlasm/x86_64-xlate.pl
index 074f185..16553f2 100755
--- a/src/crypto/perlasm/x86_64-xlate.pl
+++ b/src/crypto/perlasm/x86_64-xlate.pl
@@ -51,12 +51,7 @@
 # 7. Stick to explicit ip-relative addressing. If you have to use
 #    GOTPCREL addressing, stick to mov symbol@GOTPCREL(%rip),%r??.
 #    Both are recognized and translated to proper Win64 addressing
-#    modes. To support legacy code a synthetic directive, .picmeup,
-#    is implemented. It puts address of the *next* instruction into
-#    target register, e.g.:
-#
-#		.picmeup	%rax
-#		lea		.Label-.(%rax),%rax
+#    modes.
 #
 # 8. In order to provide for structured exception handling unified
 #    Win64 prologue copies %rsp value to %rax. For further details
@@ -125,7 +120,7 @@
 		$self->{sz} = "";
 	    } elsif ($self->{op} =~ /^p/ && $' !~ /^(ush|op|insrw)/) { # SSEn
 		$self->{sz} = "";
-	    } elsif ($self->{op} =~ /^v/) { # VEX
+	    } elsif ($self->{op} =~ /^[vk]/) { # VEX or k* such as kmov
 		$self->{sz} = "";
 	    } elsif ($self->{op} =~ /mov[dq]/ && $$line =~ /%xmm/) {
 		$self->{sz} = "";
@@ -218,18 +213,26 @@
     }
 }
 { package ea;		# pick up effective addresses: expr(%reg,%reg,scale)
+
+    my %szmap = (	b=>"BYTE$PTR",    w=>"WORD$PTR",
+			l=>"DWORD$PTR",   d=>"DWORD$PTR",
+			q=>"QWORD$PTR",   o=>"OWORD$PTR",
+			x=>"XMMWORD$PTR", y=>"YMMWORD$PTR",
+			z=>"ZMMWORD$PTR" ) if (!$gas);
+
     sub re {
 	my	($class, $line, $opcode) = @_;
 	my	$self = {};
 	my	$ret;
 
 	# optional * ----vvv--- appears in indirect jmp/call
-	if ($$line =~ /^(\*?)([^\(,]*)\(([%\w,]+)\)/) {
+	if ($$line =~ /^(\*?)([^\(,]*)\(([%\w,]+)\)((?:{[^}]+})*)/) {
 	    bless $self, $class;
 	    $self->{asterisk} = $1;
 	    $self->{label} = $2;
 	    ($self->{base},$self->{index},$self->{scale})=split(/,/,$3);
 	    $self->{scale} = 1 if (!defined($self->{scale}));
+	    $self->{opmask} = $4;
 	    $ret = $self;
 	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
 
@@ -270,6 +273,8 @@
 	    $self->{label} =~ s/\b([0-9]+)\b/$1>>0/eg;
 	}
 
+	# if base register is %rbp or %r13, see if it's possible to
+	# flip base and ingex registers [for better performance]
 	if (!$self->{label} && $self->{index} && $self->{scale}==1 &&
 	    $self->{base} =~ /(rbp|r13)/) {
 		$self->{base} = $self->{index}; $self->{index} = $1;
@@ -279,19 +284,16 @@
 	    $self->{label} =~ s/^___imp_/__imp__/   if ($flavour eq "mingw64");
 
 	    if (defined($self->{index})) {
-		sprintf "%s%s(%s,%%%s,%d)",$self->{asterisk},
-					$self->{label},
+		sprintf "%s%s(%s,%%%s,%d)%s",
+					$self->{asterisk},$self->{label},
 					$self->{base}?"%$self->{base}":"",
-					$self->{index},$self->{scale};
+					$self->{index},$self->{scale},
+					$self->{opmask};
 	    } else {
-		sprintf "%s%s(%%%s)",	$self->{asterisk},$self->{label},$self->{base};
+		sprintf "%s%s(%%%s)%s",	$self->{asterisk},$self->{label},
+					$self->{base},$self->{opmask};
 	    }
 	} else {
-	    my %szmap = (	b=>"BYTE$PTR",  w=>"WORD$PTR",
-			l=>"DWORD$PTR", d=>"DWORD$PTR",
-	    		q=>"QWORD$PTR", o=>"OWORD$PTR",
-			x=>"XMMWORD$PTR", y=>"YMMWORD$PTR", z=>"ZMMWORD$PTR" );
-
 	    $self->{label} =~ s/\./\$/g;
 	    $self->{label} =~ s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/ig;
 	    $self->{label} = "($self->{label})" if ($self->{label} =~ /[\*\+\-\/]/);
@@ -303,17 +305,20 @@
 	    ($mnemonic =~ /^vpbroadcast([qdwb])$/)	&& ($sz=$1)  ||
 	    ($mnemonic =~ /^v(?!perm)[a-z]+[fi]128$/)	&& ($sz="x");
 
+	    $self->{opmask}  =~ s/%(k[0-7])/$1/;
+
 	    if (defined($self->{index})) {
-		sprintf "%s[%s%s*%d%s]",$szmap{$sz},
+		sprintf "%s[%s%s*%d%s]%s",$szmap{$sz},
 					$self->{label}?"$self->{label}+":"",
 					$self->{index},$self->{scale},
-					$self->{base}?"+$self->{base}":"";
+					$self->{base}?"+$self->{base}":"",
+					$self->{opmask};
 	    } elsif ($self->{base} eq "rip") {
 		sprintf "%s[%s]",$szmap{$sz},$self->{label};
 	    } else {
-		sprintf "%s[%s%s]",$szmap{$sz},
+		sprintf "%s[%s%s]%s",	$szmap{$sz},
 					$self->{label}?"$self->{label}+":"",
-					$self->{base};
+					$self->{base},$self->{opmask};
 	    }
 	}
     }
@@ -325,10 +330,11 @@
 	my	$ret;
 
 	# optional * ----vvv--- appears in indirect jmp/call
-	if ($$line =~ /^(\*?)%(\w+)/) {
+	if ($$line =~ /^(\*?)%(\w+)((?:{[^}]+})*)/) {
 	    bless $self,$class;
 	    $self->{asterisk} = $1;
 	    $self->{value} = $2;
+	    $self->{opmask} = $3;
 	    $opcode->size($self->size());
 	    $ret = $self;
 	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
@@ -352,8 +358,11 @@
     }
     sub out {
     	my $self = shift;
-	if ($gas)	{ sprintf "%s%%%s",$self->{asterisk},$self->{value}; }
-	else		{ $self->{value}; }
+	if ($gas)	{ sprintf "%s%%%s%s",	$self->{asterisk},
+						$self->{value},
+						$self->{opmask}; }
+	else		{ $self->{opmask} =~ s/%(k[0-7])/$1/;
+			  $self->{value}.$self->{opmask}; }
     }
 }
 { package label;	# pick up labels, which end with :
@@ -377,9 +386,8 @@
 
 	if ($gas) {
 	    my $func = ($globals{$self->{value}} or $self->{value}) . ":";
-	    if ($win64	&&
-			$current_function->{name} eq $self->{value} &&
-			$current_function->{abi} eq "svr4") {
+	    if ($win64	&& $current_function->{name} eq $self->{value}
+			&& $current_function->{abi} eq "svr4") {
 		$func .= "\n";
 		$func .= "	movq	%rdi,8(%rsp)\n";
 		$func .= "	movq	%rsi,16(%rsp)\n";
@@ -458,15 +466,6 @@
 	my	$self = {};
 	my	$ret;
 	my	$dir;
-	my	%opcode =	# lea 2f-1f(%rip),%dst; 1: nop; 2:
-		(	"%rax"=>0x01058d48,	"%rcx"=>0x010d8d48,
-			"%rdx"=>0x01158d48,	"%rbx"=>0x011d8d48,
-			"%rsp"=>0x01258d48,	"%rbp"=>0x012d8d48,
-			"%rsi"=>0x01358d48,	"%rdi"=>0x013d8d48,
-			"%r8" =>0x01058d4c,	"%r9" =>0x010d8d4c,
-			"%r10"=>0x01158d4c,	"%r11"=>0x011d8d4c,
-			"%r12"=>0x01258d4c,	"%r13"=>0x012d8d4c,
-			"%r14"=>0x01358d4c,	"%r15"=>0x013d8d4c	);
 
 	if ($$line =~ /^\s*(\.\w+)/) {
 	    bless $self,$class;
@@ -476,12 +475,6 @@
 	    $$line = substr($$line,@+[0]); $$line =~ s/^\s+//;
 
 	    SWITCH: for ($dir) {
-		/\.picmeup/ && do { if ($$line =~ /(%r[\w]+)/i) {
-			    		$dir="\t.long";
-					$$line=sprintf "0x%x,0x90000000",$opcode{$1};
-				    }
-				    last;
-				  };
 		/\.global|\.globl|\.extern/
 			    && do { $globals{$$line} = $prefix . $$line;
 				    $$line = $globals{$$line} if ($prefix);
@@ -696,15 +689,6 @@
     }
 }
 
-sub rex {
- my $opcode=shift;
- my ($dst,$src,$rex)=@_;
-
-   $rex|=0x04 if($dst>=8);
-   $rex|=0x01 if($src>=8);
-   push @$opcode,($rex|0x40) if ($rex);
-}
-
 # Upon initial x86_64 introduction SSE>2 extensions were not introduced
 # yet. In order not to be bothered by tracing exact assembler versions,
 # but at the same time to provide a bare security minimum of AES-NI, we
@@ -715,6 +699,15 @@
 my %regrm = (	"%eax"=>0, "%ecx"=>1, "%edx"=>2, "%ebx"=>3,
 		"%esp"=>4, "%ebp"=>5, "%esi"=>6, "%edi"=>7	);
 
+sub rex {
+ my $opcode=shift;
+ my ($dst,$src,$rex)=@_;
+
+   $rex|=0x04 if($dst>=8);
+   $rex|=0x01 if($src>=8);
+   push @$opcode,($rex|0x40) if ($rex);
+}
+
 my $movq = sub {	# elderly gas can't handle inter-register movq
   my $arg = shift;
   my @opcode=(0x66);
@@ -838,6 +831,10 @@
     }
 };
 
+# Not all AVX-capable assemblers recognize AMD XOP extension. Since we
+# are using only two instructions hand-code them in order to be excused
+# from chasing assembler versions...
+
 sub rxb {
  my $opcode=shift;
  my ($dst,$src1,$src2,$rxb)=@_;
@@ -877,10 +874,15 @@
     }
 };
 
+# Intel Control-flow Enforcement Technology extension. All functions and
+# indirect branch targets will have to start with this instruction...
+
 my $endbranch = sub {
     (0xf3,0x0f,0x1e,0xfa);
 };
 
+########################################################################
+
 if ($nasm) {
     print <<___;
 default	rel
@@ -1077,6 +1079,7 @@
 #	movq	-16(%rcx),%rbx
 #	movq	-8(%rcx),%r15
 #	movq	%rcx,%rsp	# restore original rsp
+# magic_epilogue:
 #	ret
 # .size function,.-function
 #
@@ -1089,11 +1092,16 @@
 # EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
 #		CONTEXT *context,DISPATCHER_CONTEXT *disp)
 # {	ULONG64 *rsp = (ULONG64 *)context->Rax;
-#	if (context->Rip >= magic_point)
-#	{   rsp = ((ULONG64 **)context->Rsp)[0];
-#	    context->Rbp = rsp[-3];
-#	    context->Rbx = rsp[-2];
-#	    context->R15 = rsp[-1];
+#	ULONG64  rip = context->Rip;
+#
+#	if (rip >= magic_point)
+#	{   rsp = (ULONG64 *)context->Rsp;
+#	    if (rip < magic_epilogue)
+#	    {	rsp = (ULONG64 *)rsp[0];
+#		context->Rbp = rsp[-3];
+#		context->Rbx = rsp[-2];
+#		context->R15 = rsp[-1];
+#	    }
 #	}
 #	context->Rsp = (ULONG64)rsp;
 #	context->Rdi = rsp[1];
@@ -1185,13 +1193,12 @@
 # instruction and reflecting it in finer grade unwind logic in handler.
 # After all, isn't it why it's called *language-specific* handler...
 #
-# Attentive reader can notice that exceptions would be mishandled in
-# auto-generated "gear" epilogue. Well, exception effectively can't
-# occur there, because if memory area used by it was subject to
-# segmentation violation, then it would be raised upon call to the
-# function (and as already mentioned be accounted to caller, which is
-# not a problem). If you're still not comfortable, then define tail
-# "magic point" just prior ret instruction and have handler treat it...
+# SE handlers are also involved in unwinding stack when executable is
+# profiled or debugged. Profiling implies additional limitations that
+# are too subtle to discuss here. For now it's sufficient to say that
+# in order to simplify handlers one should either a) offload original
+# %rsp to stack (like discussed above); or b) if you have a register to
+# spare for frame pointer, choose volatile one.
 #
 # (*)	Note that we're talking about run-time, not debug-time. Lack of
 #	unwind information makes debugging hard on both Windows and
diff --git a/src/crypto/test/gtest_main.cc b/src/crypto/test/gtest_main.cc
index 50147bc..50970af 100644
--- a/src/crypto/test/gtest_main.cc
+++ b/src/crypto/test/gtest_main.cc
@@ -14,10 +14,35 @@
 
 #include <gtest/gtest.h>
 
+#include <stdio.h>
+
+#include <openssl/err.h>
 #include <openssl/crypto.h>
 
+namespace {
+
+class ErrorTestEventListener : public testing::EmptyTestEventListener {
+ public:
+  ErrorTestEventListener() {}
+  ~ErrorTestEventListener() override {}
+
+  void OnTestEnd(const testing::TestInfo &test_info) override {
+    // If the test failed, print any errors left in the error queue.
+    if (test_info.result()->Failed()) {
+      ERR_print_errors_fp(stdout);
+    }
+
+    // Clean up the error queue for the next run.
+    ERR_clear_error();
+  }
+};
+
+}  // namespace
+
 int main(int argc, char **argv) {
   CRYPTO_library_init();
   testing::InitGoogleTest(&argc, argv);
+  testing::UnitTest::GetInstance()->listeners().Append(
+      new ErrorTestEventListener);
   return RUN_ALL_TESTS();
 }
diff --git a/src/crypto/test/test_util.cc b/src/crypto/test/test_util.cc
index 928972a..493b124 100644
--- a/src/crypto/test/test_util.cc
+++ b/src/crypto/test/test_util.cc
@@ -12,11 +12,12 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
-#include <stdint.h>
-#include <stdio.h>
-
 #include "test_util.h"
 
+#include <ostream>
+
+#include "../internal.h"
+
 
 void hexdump(FILE *fp, const char *msg, const void *in, size_t len) {
   const uint8_t *data = reinterpret_cast<const uint8_t*>(in);
@@ -27,3 +28,13 @@
   }
   fputs("\n", fp);
 }
+
+std::ostream &operator<<(std::ostream &os, const Bytes &in) {
+  // Print a byte slice as hex.
+  static const char hex[] = "0123456789abcdef";
+  for (size_t i = 0; i < in.len; i++) {
+    os << hex[in.data[i] >> 4];
+    os << hex[in.data[i] & 0xf];
+  }
+  return os;
+}
diff --git a/src/crypto/test/test_util.h b/src/crypto/test/test_util.h
index 972e206..d834973 100644
--- a/src/crypto/test/test_util.h
+++ b/src/crypto/test/test_util.h
@@ -16,20 +16,38 @@
 #define OPENSSL_HEADER_CRYPTO_TEST_TEST_UTIL_H
 
 #include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include <iosfwd>
+
+#include "../internal.h"
 
 
-/* hexdump writes |msg| to |fp| followed by the hex encoding of |len| bytes
- * from |in|. */
+// hexdump writes |msg| to |fp| followed by the hex encoding of |len| bytes
+// from |in|.
 void hexdump(FILE *fp, const char *msg, const void *in, size_t len);
 
+// Bytes is a wrapper over a byte slice which may be compared for equality. This
+// allows it to be used in EXPECT_EQ macros.
+struct Bytes {
+  Bytes(const uint8_t *data_arg, size_t len_arg)
+      : data(data_arg), len(len_arg) {}
 
-#if defined(__cplusplus)
+  template <size_t N>
+  Bytes(const uint8_t (&array)[N]) : data(array), len(N) {}
+
+  const uint8_t *data;
+  size_t len;
+};
+
+inline bool operator==(const Bytes &a, const Bytes &b) {
+  return a.len == b.len && OPENSSL_memcmp(a.data, b.data, a.len) == 0;
 }
-#endif
+
+inline bool operator!=(const Bytes &a, const Bytes &b) { return !(a == b); }
+
+std::ostream &operator<<(std::ostream &os, const Bytes &in);
+
 
 #endif /* OPENSSL_HEADER_CRYPTO_TEST_TEST_UTIL_H */
diff --git a/src/crypto/x509/pkcs7_test.c b/src/crypto/x509/pkcs7_test.c
index 7bf4b81..f620b9b 100644
--- a/src/crypto/x509/pkcs7_test.c
+++ b/src/crypto/x509/pkcs7_test.c
@@ -23,7 +23,6 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
-#include "../test/test_util.h"
 
 
 /* kPKCS7NSS contains the certificate chain of mail.google.com, as saved by NSS
diff --git a/src/crypto/x509v3/v3_cpols.c b/src/crypto/x509v3/v3_cpols.c
index d67dcb0..7de5496 100644
--- a/src/crypto/x509v3/v3_cpols.c
+++ b/src/crypto/x509v3/v3_cpols.c
@@ -400,10 +400,10 @@
     return 1;
 
  merr:
+    ASN1_INTEGER_free(aint);
     OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
 
  err:
-    sk_ASN1_INTEGER_pop_free(nnums, ASN1_STRING_free);
     return 0;
 }
 
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 77f6196..a57c23a 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -476,6 +476,8 @@
 /* BN_is_odd returns one if |bn| is odd and zero otherwise. */
 OPENSSL_EXPORT int BN_is_odd(const BIGNUM *bn);
 
+/* BN_is_pow2 returns 1 if |a| is a power of two, and 0 otherwise. */
+OPENSSL_EXPORT int BN_is_pow2(const BIGNUM *a);
 
 /* Bitwise operations. */
 
@@ -519,6 +521,14 @@
 /* BN_mod_word returns |a| mod |w| or (BN_ULONG)-1 on error. */
 OPENSSL_EXPORT BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w);
 
+/* BN_mod_pow2 sets |r| = |a| mod 2^|e|. It returns 1 on success and
+ * 0 on error. */
+OPENSSL_EXPORT int BN_mod_pow2(BIGNUM *r, const BIGNUM *a, size_t e);
+
+/* BN_nnmod_pow2 sets |r| = |a| mod 2^|e| where |r| is always positive.
+ * It returns 1 on success and 0 on error. */
+OPENSSL_EXPORT int BN_nnmod_pow2(BIGNUM *r, const BIGNUM *a, size_t e);
+
 /* BN_mod is a helper macro that calls |BN_div| and discards the quotient. */
 #define BN_mod(rem, numerator, divisor, ctx) \
   BN_div(NULL, (rem), (numerator), (divisor), (ctx))
diff --git a/src/include/openssl/digest.h b/src/include/openssl/digest.h
index caf5861..87de3df 100644
--- a/src/include/openssl/digest.h
+++ b/src/include/openssl/digest.h
@@ -102,7 +102,8 @@
  * An EVP_MD_CTX represents the state of a specific digest operation in
  * progress. */
 
-/* EVP_MD_CTX_init initialises an, already allocated, |EVP_MD_CTX|. */
+/* EVP_MD_CTX_init initialises an, already allocated, |EVP_MD_CTX|. This is the
+ * same as setting the structure to zero. */
 OPENSSL_EXPORT void EVP_MD_CTX_init(EVP_MD_CTX *ctx);
 
 /* EVP_MD_CTX_create allocates and initialises a fresh |EVP_MD_CTX| and returns
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 263b272..497093d 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -904,6 +904,14 @@
                                                           const uint8_t *list,
                                                           size_t list_len);
 
+/* SSL_set_signed_cert_timestamp_list sets the list of signed certificate
+ * timestamps that is sent to clients that request is. The same format as the
+ * one used for |SSL_CTX_set_signed_cert_timestamp_list| applies. The caller
+ * retains ownership of |list|. */
+OPENSSL_EXPORT int SSL_set_signed_cert_timestamp_list(SSL *ctx,
+                                                      const uint8_t *list,
+                                                      size_t list_len);
+
 /* SSL_CTX_set_ocsp_response sets the OCSP response that is sent to clients
  * which request it. It returns one on success and zero on error. The caller
  * retains ownership of |response|. */
@@ -1562,9 +1570,9 @@
 DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 
 /* SSL_SESSION_new returns a newly-allocated blank |SSL_SESSION| or NULL on
- * error. This may be useful in writing tests but otherwise should not be
- * used outside the library. */
-OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_new(void);
+ * error. This may be useful when writing tests but should otherwise not be
+ * used. */
+OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_new(const SSL_CTX *ctx);
 
 /* SSL_SESSION_up_ref increments the reference count of |session| and returns
  * one. */
@@ -1589,8 +1597,8 @@
 
 /* SSL_SESSION_from_bytes parses |in_len| bytes from |in| as an SSL_SESSION. It
  * returns a newly-allocated |SSL_SESSION| on success or NULL on error. */
-OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in,
-                                                   size_t in_len);
+OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_from_bytes(
+    const uint8_t *in, size_t in_len, const SSL_CTX *ctx);
 
 /* SSL_SESSION_get_version returns a string describing the TLS version |session|
  * was established at. For example, "TLSv1.2" or "SSLv3". */
@@ -1753,20 +1761,6 @@
  * sessions created in |ctx|. */
 OPENSSL_EXPORT long SSL_CTX_get_timeout(const SSL_CTX *ctx);
 
-/* SSL_set_session_timeout sets the default lifetime, in seconds, of a TLS 1.2
- * (or earlier) session created in |ssl| to |timeout|, and returns the old
- * value.
- *
- * By default the value |SSL_DEFAULT_SESSION_TIMEOUT| is used, which can be
- * overridden at the context level by calling |SSL_CTX_set_timeout|.
- *
- * If |timeout| is zero the newly created session will not be resumable. */
-OPENSSL_EXPORT long SSL_set_session_timeout(SSL *ssl, long timeout);
-
-/* SSL_set_session_psk_dhe_timeout sets the lifetime, in seconds, of TLS 1.3
- * sessions created in |ssl| to |timeout|. */
-OPENSSL_EXPORT void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout);
-
 /* SSL_CTX_set_session_id_context sets |ctx|'s session ID context to |sid_ctx|.
  * It returns one on success and zero on error. The session ID context is an
  * application-defined opaque byte string. A session will not be used in a
@@ -3688,6 +3682,7 @@
  * deprecated. */
 
 typedef struct ssl_protocol_method_st SSL_PROTOCOL_METHOD;
+typedef struct ssl_x509_method_st SSL_X509_METHOD;
 
 struct ssl_cipher_st {
   /* name is the OpenSSL name for the cipher. */
@@ -3739,6 +3734,8 @@
    * certificate. */
   STACK_OF(CRYPTO_BUFFER) *certs;
 
+  const SSL_X509_METHOD *x509_method;
+
   /* x509_peer is the peer's certificate. */
   X509 *x509_peer;
 
@@ -3866,6 +3863,7 @@
  * connections. */
 struct ssl_ctx_st {
   const SSL_PROTOCOL_METHOD *method;
+  const SSL_X509_METHOD *x509_method;
 
   /* lock is used to protect various operations on this object. */
   CRYPTO_MUTEX lock;
@@ -4064,8 +4062,7 @@
   EVP_PKEY *tlsext_channel_id_private;
 
   /* Signed certificate timestamp list to be sent to the client, if requested */
-  uint8_t *signed_cert_timestamp_list;
-  size_t signed_cert_timestamp_list_length;
+  CRYPTO_BUFFER *signed_cert_timestamp_list;
 
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index 84051ff..6a03d1b 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -345,9 +345,6 @@
 #define SSL3_ST_SR_CLNT_HELLO_C (0x112 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CLNT_HELLO_D (0x113 | SSL_ST_ACCEPT)
 /* write to client */
-#define SSL3_ST_SW_HELLO_REQ_A (0x120 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_HELLO_REQ_B (0x121 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_HELLO_REQ_C (0x122 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_SRVR_HELLO_A (0x130 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_A (0x140 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_KEY_EXCH_A (0x150 | SSL_ST_ACCEPT)
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index afc3a39..5b5ea97 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -5,16 +5,15 @@
 
   bio_ssl.c
   custom_extensions.c
-  handshake_server.c
-  handshake_client.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_enc.c
   s3_lib.c
   s3_pkt.c
   ssl_aead_ctx.c
@@ -25,10 +24,11 @@
   ssl_ecdh.c
   ssl_file.c
   ssl_lib.c
-  ssl_rsa.c
-  ssl_rsa_cc.cc
+  ssl_privkey.c
+  ssl_privkey_cc.cc
   ssl_session.c
   ssl_stat.c
+  ssl_transcript.c
   ssl_x509.c
   t1_enc.c
   t1_lib.c
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 48a5c54..b864e42 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -549,7 +549,14 @@
   }
 
   if (!is_ccs) {
-    ssl3_update_handshake_hash(ssl, data, len);
+    /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT
+     * on hs. */
+    if (ssl->s3->hs != NULL &&
+        !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, data, len)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      OPENSSL_free(data);
+      return 0;
+    }
     ssl->d1->handshake_write_seq++;
   }
 
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 7a35b39..6084789 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -165,6 +165,7 @@
   static const SSL_METHOD kMethod = {
       0,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -175,6 +176,7 @@
   static const SSL_METHOD kMethod = {
       DTLS1_2_VERSION,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -183,6 +185,7 @@
   static const SSL_METHOD kMethod = {
       DTLS1_VERSION,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index 23a4cff..427213c 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -399,7 +399,7 @@
            * record the handshake hashes at this point in the session so that
            * any resumption of this session with ChannelID can sign those
            * hashes. */
-          ret = tls1_record_handshake_hashes_for_channel_id(ssl);
+          ret = tls1_record_handshake_hashes_for_channel_id(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -732,7 +732,7 @@
   /* Now that the length prefixes have been computed, fill in the placeholder
    * PSK binder. */
   if (hs->needs_psk_binder &&
-      !tls13_write_psk_binder(ssl, msg, len)) {
+      !tls13_write_psk_binder(hs, msg, len)) {
     OPENSSL_free(msg);
     goto err;
   }
@@ -748,7 +748,7 @@
   SSL *const ssl = hs->ssl;
   /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we
    * may send multiple ClientHellos if we receive HelloVerifyRequest. */
-  if (!ssl3_init_handshake_buffer(ssl)) {
+  if (!SSL_TRANSCRIPT_init(&hs->transcript)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -819,9 +819,6 @@
     return 1;
   }
 
-  /* The handshake transcript is reset on HelloVerifyRequst, so do not bother
-   * hashing it. */
-
   CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
@@ -897,8 +894,6 @@
   assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
   if (!ssl->s3->have_version) {
     ssl->version = server_wire_version;
-    ssl->s3->enc_method = ssl3_get_enc_method(server_version);
-    assert(ssl->s3->enc_method != NULL);
     /* At this point, the connection's version is known and ssl->version is
      * fixed. Begin enforcing the record-layer version. */
     ssl->s3->have_version = 1;
@@ -999,8 +994,9 @@
 
   /* Now that the cipher is known, initialize the handshake hash and hash the
    * ServerHello. */
-  if (!ssl3_init_handshake_hash(ssl) ||
-      !ssl_hash_current_message(ssl)) {
+  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl),
+                                c->algorithm_prf) ||
+      !ssl_hash_current_message(hs)) {
     goto f_err;
   }
 
@@ -1009,7 +1005,7 @@
    * buffer may be released. */
   if (ssl->session != NULL ||
       !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   }
 
   /* Only the NULL compression algorithm is supported. */
@@ -1061,14 +1057,14 @@
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
   CBS cbs;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs, CRYPTO_BUFFER_free);
   EVP_PKEY_free(hs->peer_pubkey);
   hs->peer_pubkey = NULL;
@@ -1081,7 +1077,7 @@
 
   if (sk_CRYPTO_BUFFER_num(ssl->s3->new_session->certs) == 0 ||
       CBS_len(&cbs) != 0 ||
-      !ssl_session_x509_cache_objects(ssl->s3->new_session)) {
+      !ssl->ctx->x509_method->session_cache_objects(ssl->s3->new_session)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return -1;
@@ -1115,7 +1111,7 @@
     return 1;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1177,7 +1173,7 @@
     return 1;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1403,12 +1399,12 @@
     ssl->s3->tmp.reuse_message = 1;
     /* If we get here we don't need the handshake buffer as we won't be doing
      * client auth. */
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
     return 1;
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_REQUEST) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1439,7 +1435,7 @@
     }
   }
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   STACK_OF(X509_NAME) *ca_sk = ssl_parse_client_CA_list(ssl, &alert, &cbs);
   if (ca_sk == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -1467,7 +1463,7 @@
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1499,7 +1495,7 @@
 
   if (!ssl_has_certificate(ssl)) {
     /* Without a client certificate, the handshake buffer may be released. */
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
 
     /* In SSL 3.0, the Certificate message is replaced with a warning alert. */
     if (ssl->version == SSL3_VERSION) {
@@ -1619,7 +1615,7 @@
     }
 
     /* Compute the premaster. */
-    uint8_t alert;
+    uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!SSL_ECDH_CTX_accept(&hs->ecdh_ctx, &child, &pms, &pms_len, &alert,
                              hs->peer_key, hs->peer_key_len)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -1680,9 +1676,8 @@
     goto err;
   }
 
-  ssl->s3->new_session->master_key_length =
-      tls1_generate_master_secret(ssl, ssl->s3->new_session->master_key, pms,
-                                  pms_len);
+  ssl->s3->new_session->master_key_length = tls1_generate_master_secret(
+      hs, ssl->s3->new_session->master_key, pms, pms_len);
   if (ssl->s3->new_session->master_key_length == 0) {
     goto err;
   }
@@ -1743,11 +1738,11 @@
         goto err;
       }
 
-      const EVP_MD *md;
       uint8_t digest[EVP_MAX_MD_SIZE];
       size_t digest_len;
-      if (!ssl3_cert_verify_hash(ssl, &md, digest, &digest_len,
-                                 signature_algorithm)) {
+      if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(
+              &hs->transcript, digest, &digest_len, ssl->s3->new_session,
+              signature_algorithm)) {
         goto err;
       }
 
@@ -1756,7 +1751,6 @@
       EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL);
       if (pctx == NULL ||
           !EVP_PKEY_sign_init(pctx) ||
-          !EVP_PKEY_CTX_set_signature_md(pctx, md) ||
           !EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len)) {
         EVP_PKEY_CTX_free(pctx);
         sign_result = ssl_private_key_failure;
@@ -1766,12 +1760,12 @@
     } else {
       sign_result = ssl_private_key_sign(
           ssl, ptr, &sig_len, max_sig_len, signature_algorithm,
-          (const uint8_t *)ssl->s3->handshake_buffer->data,
-          ssl->s3->handshake_buffer->length);
+          (const uint8_t *)hs->transcript.buffer->data,
+          hs->transcript.buffer->length);
     }
 
     /* The handshake buffer is no longer necessary. */
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   } else {
     assert(hs->state == SSL3_ST_CW_CERT_VRFY_B);
     sign_result = ssl_private_key_complete(ssl, ptr, &sig_len, max_sig_len);
@@ -1834,7 +1828,7 @@
 
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
-      !tls1_write_channel_id(ssl, &body) ||
+      !tls1_write_channel_id(hs, &body) ||
       !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
@@ -1852,7 +1846,7 @@
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_NEW_SESSION_TICKET) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index 00f451e..c352dd9 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -171,7 +171,9 @@
 #include "../crypto/internal.h"
 
 
-static int ssl3_get_client_hello(SSL_HANDSHAKE *hs);
+static int ssl3_process_client_hello(SSL_HANDSHAKE *hs);
+static int ssl3_select_certificate(SSL_HANDSHAKE *hs);
+static int ssl3_select_parameters(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs);
 static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs);
@@ -216,24 +218,37 @@
 
       case SSL_ST_ACCEPT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
-
-        if (!ssl3_init_handshake_buffer(ssl)) {
-          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          ret = -1;
-          goto end;
-        }
-
         hs->state = SSL3_ST_SR_CLNT_HELLO_A;
         break;
 
       case SSL3_ST_SR_CLNT_HELLO_A:
-      case SSL3_ST_SR_CLNT_HELLO_B:
-      case SSL3_ST_SR_CLNT_HELLO_C:
-      case SSL3_ST_SR_CLNT_HELLO_D:
-        ret = ssl3_get_client_hello(hs);
-        if (hs->state == SSL_ST_TLS13) {
-          break;
+        ret = ssl->method->ssl_get_message(ssl);
+        if (ret <= 0) {
+          goto end;
         }
+        hs->state = SSL3_ST_SR_CLNT_HELLO_B;
+        break;
+
+      case SSL3_ST_SR_CLNT_HELLO_B:
+        ret = ssl3_process_client_hello(hs);
+        if (ret <= 0) {
+          goto end;
+        }
+        hs->state = SSL3_ST_SR_CLNT_HELLO_C;
+        break;
+
+      case SSL3_ST_SR_CLNT_HELLO_C:
+        ret = ssl3_select_certificate(hs);
+        if (ret <= 0) {
+          goto end;
+        }
+        if (hs->state != SSL_ST_TLS13) {
+          hs->state = SSL3_ST_SR_CLNT_HELLO_D;
+        }
+        break;
+
+      case SSL3_ST_SR_CLNT_HELLO_D:
+        ret = ssl3_select_parameters(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -399,7 +414,7 @@
          * hashes in |ssl->s3->new_session| in case we need them to verify a
          * ChannelID signature on a resumption of this session in the future. */
         if (ssl->session == NULL && ssl->s3->tlsext_channel_id_valid) {
-          ret = tls1_record_handshake_hashes_for_channel_id(ssl);
+          ret = tls1_record_handshake_hashes_for_channel_id(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -468,10 +483,10 @@
          * now. */
         if (ssl->s3->new_session != NULL &&
             ssl->retain_only_sha256_of_client_certs) {
-          X509_free(ssl->s3->new_session->x509_peer);
-          ssl->s3->new_session->x509_peer = NULL;
-          sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
-          ssl->s3->new_session->x509_chain = NULL;
+          sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs,
+                                    CRYPTO_BUFFER_free);
+          ssl->s3->new_session->certs = NULL;
+          ssl->ctx->x509_method->session_clear(ssl->s3->new_session);
         }
 
         SSL_SESSION_free(ssl->s3->established_session);
@@ -632,8 +647,6 @@
 
   hs->client_version = client_hello->version;
   ssl->version = ssl->method->version_to_wire(version);
-  ssl->s3->enc_method = ssl3_get_enc_method(version);
-  assert(ssl->s3->enc_method != NULL);
 
   /* At this point, the connection's version is known and |ssl->version| is
    * fixed. Begin enforcing the record-layer version. */
@@ -807,125 +820,129 @@
   return ret;
 }
 
-static int ssl3_get_client_hello(SSL_HANDSHAKE *hs) {
+static int ssl3_process_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint8_t al = SSL_AD_INTERNAL_ERROR;
-  int ret = -1;
-  SSL_SESSION *session = NULL;
-
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_A) {
-    /* The first time around, read the ClientHello. */
-    int msg_ret = ssl->method->ssl_get_message(ssl);
-    if (msg_ret <= 0) {
-      return msg_ret;
-    }
-
-    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
-      return -1;
-    }
-
-    hs->state = SSL3_ST_SR_CLNT_HELLO_B;
+  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+    return -1;
   }
 
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
                              ssl->init_num)) {
-    al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return -1;
   }
 
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_B) {
-    /* Run the early callback. */
-    if (ssl->ctx->select_certificate_cb != NULL) {
-      switch (ssl->ctx->select_certificate_cb(&client_hello)) {
-        case 0:
-          ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
-          goto err;
+  /* Run the early callback. */
+  if (ssl->ctx->select_certificate_cb != NULL) {
+    switch (ssl->ctx->select_certificate_cb(&client_hello)) {
+      case 0:
+        ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
+        return -1;
 
-        case -1:
-          /* Connection rejected. */
-          al = SSL_AD_HANDSHAKE_FAILURE;
-          OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-          goto f_err;
+      case -1:
+        /* Connection rejected. */
+        OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+        return -1;
 
-        default:
-          /* fallthrough */;
-      }
+      default:
+        /* fallthrough */;
     }
+  }
 
-    if (!negotiate_version(hs, &al, &client_hello)) {
-      goto f_err;
-    }
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!negotiate_version(hs, &alert, &client_hello)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return -1;
+  }
 
-    /* Load the client random. */
-    if (client_hello.random_len != SSL3_RANDOM_SIZE) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  /* Load the client random. */
+  if (client_hello.random_len != SSL3_RANDOM_SIZE) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
+  }
+  OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
+                 client_hello.random_len);
+
+  /* Only null compression is supported. TLS 1.3 further requires the peer
+   * advertise no other compression. */
+  if (OPENSSL_memchr(client_hello.compression_methods, 0,
+                     client_hello.compression_methods_len) == NULL ||
+      (ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
+       client_hello.compression_methods_len != 1)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return -1;
+  }
+
+  /* TLS extensions. */
+  if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    return -1;
+  }
+
+  return 1;
+}
+
+static int ssl3_select_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  /* Call |cert_cb| to update server certificates if required. */
+  if (ssl->cert->cert_cb != NULL) {
+    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+    if (rv == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return -1;
     }
-    OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
-                   client_hello.random_len);
-
-    /* Only null compression is supported. TLS 1.3 further requires the peer
-     * advertise no other compression. */
-    if (OPENSSL_memchr(client_hello.compression_methods, 0,
-                       client_hello.compression_methods_len) == NULL ||
-        (ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
-         client_hello.compression_methods_len != 1)) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
-      goto f_err;
+    if (rv < 0) {
+      ssl->rwstate = SSL_X509_LOOKUP;
+      return -1;
     }
-
-    /* TLS extensions. */
-    if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-      goto err;
-    }
-
-    hs->state = SSL3_ST_SR_CLNT_HELLO_C;
   }
 
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_C) {
-    /* Call |cert_cb| to update server certificates if required. */
-    if (ssl->cert->cert_cb != NULL) {
-      int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
-      if (rv == 0) {
-        al = SSL_AD_INTERNAL_ERROR;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
-        goto f_err;
-      }
-      if (rv < 0) {
-        ssl->rwstate = SSL_X509_LOOKUP;
-        goto err;
-      }
-    }
-
-    if (!ssl_auto_chain_if_needed(ssl)) {
-      goto err;
-    }
-
-    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-      /* Jump to the TLS 1.3 state machine. */
-      hs->state = SSL_ST_TLS13;
-      hs->do_tls13_handshake = tls13_server_handshake;
-      return 1;
-    }
-
-    /* Negotiate the cipher suite. This must be done after |cert_cb| so the
-     * certificate is finalized. */
-    ssl->s3->tmp.new_cipher =
-        ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
-    if (ssl->s3->tmp.new_cipher == NULL) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
-      goto f_err;
-    }
-
-    hs->state = SSL3_ST_SR_CLNT_HELLO_D;
+  if (!ssl_auto_chain_if_needed(ssl)) {
+    return -1;
   }
 
-  assert(hs->state == SSL3_ST_SR_CLNT_HELLO_D);
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    /* Jump to the TLS 1.3 state machine. */
+    hs->state = SSL_ST_TLS13;
+    hs->do_tls13_handshake = tls13_server_handshake;
+    return 1;
+  }
+
+  SSL_CLIENT_HELLO client_hello;
+  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+                             ssl->init_num)) {
+    return -1;
+  }
+
+  /* Negotiate the cipher suite. This must be done after |cert_cb| so the
+   * certificate is finalized. */
+  ssl->s3->tmp.new_cipher =
+      ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
+  if (ssl->s3->tmp.new_cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    return -1;
+  }
+
+  return 1;
+}
+
+static int ssl3_select_parameters(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  uint8_t al = SSL_AD_INTERNAL_ERROR;
+  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)) {
+    return -1;
+  }
 
   /* Determine whether we are doing session resumption. */
   int tickets_supported = 0, renew_ticket = 0;
@@ -1027,14 +1044,15 @@
 
   /* Now that all parameters are known, initialize the handshake hash and hash
    * the ClientHello. */
-  if (!ssl3_init_handshake_hash(ssl) ||
-      !ssl_hash_current_message(ssl)) {
+  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl),
+                                ssl->s3->tmp.new_cipher->algorithm_prf) ||
+      !ssl_hash_current_message(hs)) {
     goto f_err;
   }
 
   /* Release the handshake buffer if client authentication isn't required. */
   if (!hs->cert_request) {
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   }
 
   ret = 1;
@@ -1431,7 +1449,7 @@
     return -1;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1441,7 +1459,7 @@
   sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs, CRYPTO_BUFFER_free);
   EVP_PKEY_free(hs->peer_pubkey);
   hs->peer_pubkey = NULL;
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   ssl->s3->new_session->certs =
       ssl_parse_cert_chain(&alert, &hs->peer_pubkey,
                            ssl->retain_only_sha256_of_client_certs
@@ -1454,7 +1472,7 @@
   }
 
   if (CBS_len(&certificate_msg) != 0 ||
-      !ssl_session_x509_cache_objects(ssl->s3->new_session)) {
+      !ssl->ctx->x509_method->session_cache_objects(ssl->s3->new_session)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return -1;
@@ -1462,7 +1480,7 @@
 
   if (sk_CRYPTO_BUFFER_num(ssl->s3->new_session->certs) == 0) {
     /* No client certificate so the handshake buffer may be discarded. */
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
 
     /* In SSL 3.0, sending no certificate is signaled by omitting the
      * Certificate message. */
@@ -1517,7 +1535,7 @@
     }
 
     if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE) ||
-        !ssl_hash_current_message(ssl)) {
+        !ssl_hash_current_message(hs)) {
       return -1;
     }
   }
@@ -1693,7 +1711,7 @@
     }
 
     /* Compute the premaster. */
-    uint8_t alert;
+    uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, &premaster_secret,
                              &premaster_secret_len, &alert, CBS_data(&peer_key),
                              CBS_len(&peer_key))) {
@@ -1745,9 +1763,9 @@
   }
 
   /* Compute the master secret */
-  ssl->s3->new_session->master_key_length = tls1_generate_master_secret(
-      ssl, ssl->s3->new_session->master_key, premaster_secret,
-      premaster_secret_len);
+  ssl->s3->new_session->master_key_length =
+      tls1_generate_master_secret(hs, ssl->s3->new_session->master_key,
+                                  premaster_secret, premaster_secret_len);
   if (ssl->s3->new_session->master_key_length == 0) {
     goto err;
   }
@@ -1779,7 +1797,7 @@
    * CertificateVerify is required if and only if there's a client certificate.
    * */
   if (hs->peer_pubkey == NULL) {
-    ssl3_free_handshake_buffer(ssl);
+    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
     return 1;
   }
 
@@ -1828,26 +1846,25 @@
   /* The SSL3 construction for CertificateVerify does not decompose into a
    * single final digest and signature, and must be special-cased. */
   if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
-    const EVP_MD *md;
     uint8_t digest[EVP_MAX_MD_SIZE];
     size_t digest_len;
-    if (!ssl3_cert_verify_hash(ssl, &md, digest, &digest_len,
-                               signature_algorithm)) {
+    if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest,
+                                              &digest_len, ssl->s3->new_session,
+                                              signature_algorithm)) {
       goto err;
     }
 
     EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(hs->peer_pubkey, NULL);
     sig_ok = pctx != NULL &&
              EVP_PKEY_verify_init(pctx) &&
-             EVP_PKEY_CTX_set_signature_md(pctx, md) &&
              EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature),
                              digest, digest_len);
     EVP_PKEY_CTX_free(pctx);
   } else {
     sig_ok = ssl_public_key_verify(
         ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        hs->peer_pubkey, (const uint8_t *)ssl->s3->handshake_buffer->data,
-        ssl->s3->handshake_buffer->length);
+        hs->peer_pubkey, (const uint8_t *)hs->transcript.buffer->data,
+        hs->transcript.buffer->length);
   }
 
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -1862,8 +1879,8 @@
 
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
-  ssl3_free_handshake_buffer(ssl);
-  if (!ssl_hash_current_message(ssl)) {
+  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+  if (!ssl_hash_current_message(hs)) {
     goto err;
   }
 
@@ -1885,7 +1902,7 @@
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_NEXT_PROTO) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -1916,8 +1933,8 @@
   }
 
   if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
-      !tls1_verify_channel_id(ssl) ||
-      !ssl_hash_current_message(ssl)) {
+      !tls1_verify_channel_id(hs) ||
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
   return 1;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index a90294e..5b93f47 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -220,10 +220,9 @@
                             const SSL_CIPHER *cipher, uint16_t version);
 
 /* ssl_get_handshake_digest returns the |EVP_MD| corresponding to
- * |algorithm_prf|. It returns SHA-1 for |SSL_HANDSHAKE_DEFAULT|. The caller is
- * responsible for maintaining the additional MD5 digest and switching to
- * SHA-256 in TLS 1.2. */
-const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf);
+ * |algorithm_prf| and the |version|. */
+const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf,
+                                       uint16_t version);
 
 /* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
  * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
@@ -260,6 +259,87 @@
 size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher);
 
 
+/* Transcript layer. */
+
+/* SSL_TRANSCRIPT maintains the handshake transcript as a combination of a
+ * buffer and running hash. */
+typedef struct ssl_transcript_st {
+  /* buffer, if non-NULL, contains the handshake transcript. */
+  BUF_MEM *buffer;
+  /* hash, if initialized with an |EVP_MD|, maintains the handshake hash. For
+   * TLS 1.1 and below, it is the SHA-1 half. */
+  EVP_MD_CTX hash;
+  /* md5, if initialized with an |EVP_MD|, maintains the MD5 half of the
+   * handshake hash for TLS 1.1 and below. */
+  EVP_MD_CTX md5;
+} SSL_TRANSCRIPT;
+
+/* SSL_TRANSCRIPT_init initializes the handshake transcript. If called on an
+ * existing transcript, it resets the transcript and hash. It returns one on
+ * success and zero on failure. */
+int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript);
+
+/* SSL_TRANSCRIPT_init_hash initializes the handshake hash based on the PRF and
+ * contents of the handshake transcript. Subsequent calls to
+ * |SSL_TRANSCRIPT_update| will update the rolling hash. It returns one on
+ * success and zero on failure. It is an error to call this function after the
+ * handshake buffer is released. */
+int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version,
+                             int algorithm_prf);
+
+/* SSL_TRANSCRIPT_cleanup cleans up the hash and transcript. */
+void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript);
+
+/* SSL_TRANSCRIPT_free_buffer releases the handshake buffer. Subsequent calls to
+ * |SSL_TRANSCRIPT_update| will not update the handshake buffer. */
+void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript);
+
+/* SSL_TRANSCRIPT_digest_len returns the length of the PRF hash. */
+size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript);
+
+/* SSL_TRANSCRIPT_md returns the PRF hash. For TLS 1.1 and below, this is
+ * |EVP_md5_sha1|. */
+const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript);
+
+/* SSL_TRANSCRIPT_update adds |in| to the handshake buffer and handshake hash,
+ * whichever is enabled. It returns one on success and zero on failure. */
+int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in,
+                          size_t in_len);
+
+/* SSL_TRANSCRIPT_get_hash writes the handshake hash to |out| which must have
+ * room for at least |SSL_TRANSCRIPT_digest_len| bytes. On success, it returns
+ * one and sets |*out_len| to the number of bytes written. Otherwise, it returns
+ * zero. */
+int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out,
+                            size_t *out_len);
+
+/* SSL_TRANSCRIPT_ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify
+ * hash into the bytes pointed to by |out| and writes the number of bytes to
+ * |*out_len|. |out| must have room for |EVP_MAX_MD_SIZE| bytes. It returns one
+ * on success and zero on failure. */
+int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript,
+                                         uint8_t *out, size_t *out_len,
+                                         const SSL_SESSION *session,
+                                         int signature_algorithm);
+
+/* SSL_TRANSCRIPT_finish_mac computes the MAC for the Finished message into the
+ * bytes pointed by |out| and writes the number of bytes to |*out_len|. |out|
+ * must have room for |EVP_MAX_MD_SIZE| bytes. It returns one on success and
+ * zero on failure. */
+int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out,
+                              size_t *out_len, const SSL_SESSION *session,
+                              int from_server, uint16_t version);
+
+/* tls1_prf computes the PRF function for |ssl|. It writes |out_len| bytes to
+ * |out|, using |secret| as the secret and |label| as the label. |seed1| and
+ * |seed2| are concatenated to form the seed parameter. It returns one on
+ * success and zero on failure. */
+int tls1_prf(const EVP_MD *digest, uint8_t *out, size_t out_len,
+             const uint8_t *secret, size_t secret_len, const char *label,
+             size_t label_len, const uint8_t *seed1, size_t seed1_len,
+             const uint8_t *seed2, size_t seed2_len);
+
+
 /* Encryption layer. */
 
 /* SSL_AEAD_CTX contains information about an AEAD that is being used to encrypt
@@ -525,35 +605,6 @@
 int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions);
 
 
-/* Handshake hash.
- *
- * The TLS handshake maintains a transcript of all handshake messages. At
- * various points in the protocol, this is either a handshake buffer, a rolling
- * hash (selected by cipher suite) or both. */
-
-/* ssl3_init_handshake_buffer initializes the handshake buffer and resets the
- * handshake hash. It returns one success and zero on failure. */
-int ssl3_init_handshake_buffer(SSL *ssl);
-
-/* ssl3_init_handshake_hash initializes the handshake hash based on the pending
- * cipher and the contents of the handshake buffer. Subsequent calls to
- * |ssl3_update_handshake_hash| will update the rolling hash. It returns one on
- * success and zero on failure. It is an error to call this function after the
- * handshake buffer is released. */
-int ssl3_init_handshake_hash(SSL *ssl);
-
-/* ssl3_free_handshake_buffer releases the handshake buffer. Subsequent calls
- * to |ssl3_update_handshake_hash| will not update the handshake buffer. */
-void ssl3_free_handshake_buffer(SSL *ssl);
-
-/* ssl3_free_handshake_hash releases the handshake hash. */
-void ssl3_free_handshake_hash(SSL *ssl);
-
-/* ssl3_update_handshake_hash adds |in| to the handshake buffer and handshake
- * hash, whichever is enabled. It returns one on success and zero on failure. */
-int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len);
-
-
 /* ECDH groups. */
 
 typedef struct ssl_ecdh_ctx_st SSL_ECDH_CTX;
@@ -753,12 +804,6 @@
  * configured and zero otherwise. */
 int ssl_has_certificate(const SSL *ssl);
 
-/* ssl_session_x509_cache_objects fills out |sess->x509_peer| and
- * |sess->x509_chain| from |sess->certs| and erases
- * |sess->x509_chain_without_leaf|. It returns one on success or zero on
- * error. */
-int ssl_session_x509_cache_objects(SSL_SESSION *sess);
-
 /* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
  * by a TLS Certificate message. On success, it returns a newly-allocated
  * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns NULL and sets
@@ -827,12 +872,6 @@
 int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
                                size_t len);
 
-/* tls13_get_context_hash writes Hash(Handshake Context) to |out| which must
- * have room for at least |EVP_MAX_MD_SIZE| bytes. On success, it returns one
- * and sets |*out_len| to the number of bytes written. Otherwise, it returns
- * zero. */
-int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len);
-
 /* tls13_set_traffic_key sets the read or write traffic keys to
  * |traffic_secret|. It returns one on success and zero on error. */
 int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
@@ -872,12 +911,13 @@
 /* tls13_write_psk_binder calculates the PSK binder value and replaces the last
  * bytes of |msg| with the resulting value. It returns 1 on success, and 0 on
  * failure. */
-int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len);
+int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len);
 
 /* tls13_verify_psk_binder verifies that the handshake transcript, truncated
  * up to the binders has a valid signature using the value of |session|'s
  * resumption secret. It returns 1 on success, and 0 on failure. */
-int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session, CBS *binders);
+int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
+                            CBS *binders);
 
 
 /* Handshake functions. */
@@ -951,6 +991,9 @@
   /* ecdh_ctx is the current ECDH instance. */
   SSL_ECDH_CTX ecdh_ctx;
 
+  /* transcript is the current handshake transcript. */
+  SSL_TRANSCRIPT transcript;
+
   /* cookie is the value of the cookie received from the server, if any. */
   uint8_t *cookie;
   size_t cookie_len;
@@ -1132,7 +1175,7 @@
  * containing the result. The caller must free it with |OPENSSL_free| to release
  * it. This function returns one on success and zero on failure. */
 int tls13_get_cert_verify_signature_input(
-    SSL *ssl, uint8_t **out, size_t *out_len,
+    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len,
     enum ssl_cert_verify_context_t cert_verify_context);
 
 /* ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
@@ -1256,6 +1299,10 @@
    * operations. */
   const SSL_PRIVATE_KEY_METHOD *key_method;
 
+  /* x509_method contains pointers to functions that might deal with |X509|
+   * compatibility, or might be a no-op, depending on the application. */
+  const SSL_X509_METHOD *x509_method;
+
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
@@ -1287,6 +1334,9 @@
   /* method is the underlying SSL_PROTOCOL_METHOD that initializes the
    * SSL_CTX. */
   const SSL_PROTOCOL_METHOD *method;
+  /* x509_method contains pointers to functions that might deal with |X509|
+   * compatibility, or might be a no-op, depending on the application. */
+  const SSL_X509_METHOD *x509_method;
 };
 
 /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
@@ -1368,19 +1418,34 @@
   int (*set_write_state)(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
 };
 
-/* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
- * of a mess of functions, but hell, think of it as an opaque structure. */
-typedef struct ssl3_enc_method {
-  /* prf computes the PRF function for |ssl|. It writes |out_len| bytes to
-   * |out|, using |secret| as the secret and |label| as the label. |seed1| and
-   * |seed2| are concatenated to form the seed parameter. It returns one on
-   * success and zero on failure. */
-  int (*prf)(const SSL *ssl, uint8_t *out, size_t out_len,
-             const uint8_t *secret, size_t secret_len, const char *label,
-             size_t label_len, const uint8_t *seed1, size_t seed1_len,
-             const uint8_t *seed2, size_t seed2_len);
-  int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out);
-} SSL3_ENC_METHOD;
+struct ssl_x509_method_st {
+  /* cert_clear frees and NULLs all X509-related state. */
+  void (*cert_clear)(CERT *cert);
+  /* cert_flush_cached_chain drops any cached |X509|-based certificate chain
+   * from |cert|. */
+  void (*cert_flush_cached_chain)(CERT *cert);
+  /* cert_flush_cached_chain drops any cached |X509|-based leaf certificate
+   * from |cert|. */
+  void (*cert_flush_cached_leaf)(CERT *cert);
+
+  /* session_cache_objects fills out |sess->x509_peer| and |sess->x509_chain|
+   * from |sess->certs| and erases |sess->x509_chain_without_leaf|. It returns
+   * one on success or zero on error. */
+  int (*session_cache_objects)(SSL_SESSION *session);
+  /* session_dup duplicates any needed fields from |session| to |new_session|.
+   * It returns one on success or zero on error. */
+  int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
+  /* session_clear frees any X509-related state from |session|. */
+  void (*session_clear)(SSL_SESSION *session);
+};
+
+/* ssl_noop_x509_method is implements the |ssl_x509_method_st| functions by
+ * doing nothing. */
+extern const struct ssl_x509_method_st ssl_noop_x509_method;
+
+/* ssl_crypto_x509_method provides the |ssl_x509_method_st| functions using
+ * crypto/x509. */
+extern const struct ssl_x509_method_st ssl_crypto_x509_method;
 
 typedef struct ssl3_record_st {
   /* type is the record type. */
@@ -1431,15 +1496,6 @@
   int wpend_ret; /* number of bytes submitted */
   const uint8_t *wpend_buf;
 
-  /* handshake_buffer, if non-NULL, contains the handshake transcript. */
-  BUF_MEM *handshake_buffer;
-  /* handshake_hash, if initialized with an |EVP_MD|, maintains the handshake
-   * hash. For TLS 1.1 and below, it is the SHA-1 half. */
-  EVP_MD_CTX handshake_hash;
-  /* handshake_md5, if initialized with an |EVP_MD|, maintains the MD5 half of
-   * the handshake hash for TLS 1.1 and below. */
-  EVP_MD_CTX handshake_md5;
-
   /* recv_shutdown is the shutdown state for the receive half of the
    * connection. */
   enum ssl_shutdown_t recv_shutdown;
@@ -1516,10 +1572,6 @@
   /* aead_write_ctx is the current write cipher state. */
   SSL_AEAD_CTX *aead_write_ctx;
 
-  /* enc_method is the method table corresponding to the current protocol
-   * version. */
-  const SSL3_ENC_METHOD *enc_method;
-
   /* hs is the handshake state for the current handshake or NULL if there isn't
    * one. */
   SSL_HANDSHAKE *hs;
@@ -1836,21 +1888,13 @@
    * session space. Only effective on the server side. */
   unsigned retain_only_sha256_of_client_certs:1;
 
-  /* session_timeout is the default lifetime in seconds of the session
-   * created in this connection at TLS 1.2 and earlier. */
-  long session_timeout;
-
-  /* session_psk_dhe_timeout is the default lifetime in seconds of sessions
-   * created in this connection at TLS 1.3. */
-  long session_psk_dhe_timeout;
+  /* Signed certificate timestamp list to be sent to the client, if requested */
+  CRYPTO_BUFFER *signed_cert_timestamp_list;
 
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
 };
 
-extern const SSL3_ENC_METHOD TLSv1_enc_data;
-extern const SSL3_ENC_METHOD SSLv3_enc_data;
-
 /* From draft-ietf-tls-tls13-18, used in determining PSK modes. */
 #define SSL_PSK_KE     0x0
 #define SSL_PSK_DHE_KE 0x1
@@ -1860,13 +1904,12 @@
 #define SSL_KEY_UPDATE_NOT_REQUESTED 0
 #define SSL_KEY_UPDATE_REQUESTED 1
 
-CERT *ssl_cert_new(void);
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method);
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509);
-void ssl_cert_flush_cached_x509_leaf(CERT *cert);
-int ssl_cert_cache_leaf_cert(CERT *cert);
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
+int ssl_is_key_type_supported(int key_type);
 /* ssl_compare_public_and_private_key returns one if |pubkey| is the public
  * counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
  * message on the error queue. */
@@ -1876,6 +1919,15 @@
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
 int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
 
+/* ssl_session_new returns a newly-allocated blank |SSL_SESSION| or NULL on
+ * error. */
+SSL_SESSION *ssl_session_new(const SSL_X509_METHOD *x509_method);
+
+/* SSL_SESSION_parse parses an |SSL_SESSION| from |cbs| and advances |cbs| over
+ * the parsed data. */
+SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method,
+                               CRYPTO_BUFFER_POOL *pool);
+
 /* ssl_session_is_context_valid returns one if |session|'s session ID context
  * matches the one set on |ssl| and zero otherwise. */
 int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session);
@@ -1888,6 +1940,11 @@
  * zero otherwise. */
 int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session);
 
+/* SSL_SESSION_get_digest returns the digest used in |session|. If the digest is
+ * invalid, it returns NULL. */
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
+                                     const SSL *ssl);
+
 void ssl_set_session(SSL *ssl, SSL_SESSION *session);
 
 enum ssl_session_result_t {
@@ -1936,7 +1993,7 @@
     const SSL *ssl);
 
 int ssl_verify_cert_chain(SSL *ssl, long *out_verify_result,
-                          STACK_OF(X509) * cert_chain);
+                          STACK_OF(X509) *cert_chain);
 void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
 
 int ssl_verify_alarm_type(long type);
@@ -1947,13 +2004,6 @@
 void ssl3_get_current_message(const SSL *ssl, CBS *out);
 void ssl3_release_current_message(SSL *ssl, int free_buffer);
 
-/* ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify hash into the
- * bytes pointed to by |out| and writes the number of bytes to |*out_len|. |out|
- * must have room for |EVP_MAX_MD_SIZE| bytes. It sets |*out_md| to the hash
- * function used. It returns one on success and zero on failure. */
-int ssl3_cert_verify_hash(SSL *ssl, const EVP_MD **out_md, uint8_t *out,
-                          size_t *out_len, uint16_t signature_algorithm);
-
 int ssl3_send_finished(SSL_HANDSHAKE *hs);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
@@ -1990,7 +2040,7 @@
 
 /* ssl_hash_current_message incorporates the current handshake message into the
  * handshake hash. It returns one on success and zero on allocation failure. */
-int ssl_hash_current_message(SSL *ssl);
+int ssl_hash_current_message(SSL_HANDSHAKE *hs);
 
 /* dtls1_get_record reads a new input record. On success, it places it in
  * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
@@ -2034,9 +2084,8 @@
 int dtls1_dispatch_alert(SSL *ssl);
 
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which);
-int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
-int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
-                                size_t premaster_len);
+int tls1_generate_master_secret(SSL_HANDSHAKE *hs, uint8_t *out,
+                                const uint8_t *premaster, size_t premaster_len);
 
 /* tls1_get_grouplist sets |*out_group_ids| and |*out_group_ids_len| to the
  * locally-configured group preference list. */
@@ -2092,19 +2141,19 @@
 /* tls1_verify_channel_id processes the current message as a Channel ID message,
  * and verifies the signature. If the key is valid, it saves the Channel ID and
  * returns one. Otherwise, it returns zero. */
-int tls1_verify_channel_id(SSL *ssl);
+int tls1_verify_channel_id(SSL_HANDSHAKE *hs);
 
 /* tls1_write_channel_id generates a Channel ID message and puts the output in
  * |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling.
  * This function returns one on success and zero on error. */
-int tls1_write_channel_id(SSL *ssl, CBB *cbb);
+int tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb);
 
 /* tls1_channel_id_hash computes the hash to be signed by Channel ID and writes
  * it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns
  * one on success and zero on failure. */
-int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len);
+int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len);
 
-int tls1_record_handshake_hashes_for_channel_id(SSL *ssl);
+int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs);
 
 /* ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if
  * necessary. It returns one on success and zero on fatal error. Note that, on
@@ -2116,10 +2165,6 @@
  * otherwise. */
 int ssl3_can_false_start(const SSL *ssl);
 
-/* ssl3_get_enc_method returns the SSL3_ENC_METHOD corresponding to
- * |version|. */
-const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version);
-
 /* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
  * minimum and maximum enabled protocol versions, respectively. */
 int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
@@ -2129,8 +2174,6 @@
  * call this function before the version is determined. */
 uint16_t ssl3_protocol_version(const SSL *ssl);
 
-uint32_t ssl_get_algorithm_prf(const SSL *ssl);
-
 void ssl_get_current_time(const SSL *ssl, struct timeval *out_clock);
 
 /* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 8d2657f..d3f9421 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -141,6 +141,10 @@
   hs->ssl = ssl;
   hs->wait = ssl_hs_ok;
   hs->state = SSL_ST_INIT;
+  if (!SSL_TRANSCRIPT_init(&hs->transcript)) {
+    ssl_handshake_free(hs);
+    return NULL;
+  }
   return hs;
 }
 
@@ -159,6 +163,7 @@
   OPENSSL_cleanse(hs->server_traffic_secret_0,
                   sizeof(hs->server_traffic_secret_0));
   SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
+  SSL_TRANSCRIPT_cleanup(&hs->transcript);
   OPENSSL_free(hs->cookie);
   OPENSSL_free(hs->key_share_bytes);
   OPENSSL_free(hs->public_key);
@@ -264,7 +269,12 @@
   } while (added < len);
 
   ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg, len);
-  ssl3_update_handshake_hash(ssl, msg, len);
+  /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT
+   * on hs. */
+  if (ssl->s3->hs != NULL &&
+      !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, msg, len)) {
+    goto err;
+  }
   ret = 1;
 
 err:
@@ -353,17 +363,20 @@
 
 int ssl3_send_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  const SSL_SESSION *session = SSL_get_session(ssl);
+
   uint8_t finished[EVP_MAX_MD_SIZE];
-  size_t finished_len =
-      ssl->s3->enc_method->final_finish_mac(ssl, ssl->server, finished);
-  if (finished_len == 0) {
+  size_t finished_len;
+  if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len,
+                                 session, ssl->server,
+                                 ssl3_protocol_version(ssl))) {
     return 0;
   }
 
   /* Log the master secret, if logging is enabled. */
   if (!ssl_log_secret(ssl, "CLIENT_RANDOM",
-                      SSL_get_session(ssl)->master_key,
-                      SSL_get_session(ssl)->master_key_length)) {
+                      session->master_key,
+                      session->master_key_length)) {
     return 0;
   }
 
@@ -409,10 +422,11 @@
 
   /* Snapshot the finished hash before incorporating the new message. */
   uint8_t finished[EVP_MAX_MD_SIZE];
-  size_t finished_len =
-      ssl->s3->enc_method->final_finish_mac(ssl, !ssl->server, finished);
-  if (finished_len == 0 ||
-      !ssl_hash_current_message(ssl)) {
+  size_t finished_len;
+  if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len,
+                                 SSL_get_session(ssl), !ssl->server,
+                                 ssl3_protocol_version(ssl)) ||
+      !ssl_hash_current_message(hs)) {
     return -1;
   }
 
@@ -482,7 +496,7 @@
   if (ssl->server) {
     /* The largest acceptable post-handshake message for a server is a
      * KeyUpdate. We will never initiate post-handshake auth. */
-    return 0;
+    return 1;
   }
 
   /* Clients must accept NewSessionTicket and CertificateRequest, so allow the
@@ -561,9 +575,11 @@
   CBS_init(&v2_client_hello, ssl_read_buffer(ssl) + 2, msg_length);
 
   /* The V2ClientHello without the length is incorporated into the handshake
-   * hash. */
-  if (!ssl3_update_handshake_hash(ssl, CBS_data(&v2_client_hello),
-                                  CBS_len(&v2_client_hello))) {
+   * hash. This is only ever called at the start of the handshake, so hs is
+   * guaranteed to be non-NULL. */
+  if (!SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript,
+                             CBS_data(&v2_client_hello),
+                             CBS_len(&v2_client_hello))) {
     return -1;
   }
 
@@ -734,15 +750,15 @@
   CBS_init(out, (uint8_t *)ssl->init_buf->data, ssl->init_buf->length);
 }
 
-int ssl_hash_current_message(SSL *ssl) {
+int ssl_hash_current_message(SSL_HANDSHAKE *hs) {
   /* V2ClientHellos are hashed implicitly. */
-  if (ssl->s3->is_v2_hello) {
+  if (hs->ssl->s3->is_v2_hello) {
     return 1;
   }
 
   CBS cbs;
-  ssl->method->get_current_message(ssl, &cbs);
-  return ssl3_update_handshake_hash(ssl, CBS_data(&cbs), CBS_len(&cbs));
+  hs->ssl->method->get_current_message(hs->ssl, &cbs);
+  return SSL_TRANSCRIPT_update(&hs->transcript, CBS_data(&cbs), CBS_len(&cbs));
 }
 
 void ssl3_release_current_message(SSL *ssl, int free_buffer) {
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 3f44629..1c723cd 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -178,9 +178,6 @@
     return 0;
   }
 
-  EVP_MD_CTX_init(&s3->handshake_hash);
-  EVP_MD_CTX_init(&s3->handshake_md5);
-
   ssl->s3 = s3;
 
   /* Set the version to the highest supported version.
@@ -202,8 +199,6 @@
 
   SSL_SESSION_free(ssl->s3->new_session);
   SSL_SESSION_free(ssl->s3->established_session);
-  ssl3_free_handshake_buffer(ssl);
-  ssl3_free_handshake_hash(ssl);
   ssl_handshake_free(ssl->s3->hs);
   OPENSSL_free(ssl->s3->next_proto_negotiated);
   OPENSSL_free(ssl->s3->alpn_selected);
@@ -224,14 +219,3 @@
 
   return ssl->ctx->cipher_list;
 }
-
-/* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
- * handshake macs if required. */
-uint32_t ssl_get_algorithm_prf(const SSL *ssl) {
-  uint32_t algorithm_prf = ssl->s3->tmp.new_cipher->algorithm_prf;
-  if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT &&
-      ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
-    return SSL_HANDSHAKE_MAC_SHA256;
-  }
-  return algorithm_prf;
-}
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 5d5b7e8..2f919ca 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -140,7 +140,7 @@
   }
 
   CBS body;
-  uint8_t type, alert;
+  uint8_t type, alert = SSL_AD_DECODE_ERROR;
   size_t consumed;
   enum ssl_open_record_t open_ret =
       tls_open_record(ssl, &type, &body, &consumed, &alert,
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index 4c1ee89..3582864 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -575,8 +575,9 @@
   return 1;
 }
 
-static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
-  SSL_SESSION *ret = SSL_SESSION_new();
+SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method,
+                               CRYPTO_BUFFER_POOL *pool) {
+  SSL_SESSION *ret = ssl_session_new(x509_method);
   if (ret == NULL) {
     goto err;
   }
@@ -738,7 +739,7 @@
 
     if (has_peer) {
       /* TODO(agl): this should use the |SSL_CTX|'s pool. */
-      CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, NULL);
+      CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, pool);
       if (buffer == NULL ||
           !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) {
         CRYPTO_BUFFER_free(buffer);
@@ -756,7 +757,7 @@
       }
 
       /* TODO(agl): this should use the |SSL_CTX|'s pool. */
-      CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, NULL);
+      CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, pool);
       if (buffer == NULL ||
           !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) {
         CRYPTO_BUFFER_free(buffer);
@@ -766,7 +767,7 @@
     }
   }
 
-  if (!ssl_session_x509_cache_objects(ret)) {
+  if (!x509_method->session_cache_objects(ret)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
   }
@@ -811,10 +812,11 @@
   return NULL;
 }
 
-SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len) {
+SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len,
+                                    const SSL_CTX *ctx) {
   CBS cbs;
   CBS_init(&cbs, in, in_len);
-  SSL_SESSION *ret = SSL_SESSION_parse(&cbs);
+  SSL_SESSION *ret = SSL_SESSION_parse(&cbs, ctx->x509_method, ctx->pool);
   if (ret == NULL) {
     return NULL;
   }
@@ -825,25 +827,3 @@
   }
   return ret;
 }
-
-SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) {
-  if (length < 0) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return NULL;
-  }
-
-  CBS cbs;
-  CBS_init(&cbs, *pp, length);
-
-  SSL_SESSION *ret = SSL_SESSION_parse(&cbs);
-  if (ret == NULL) {
-    return NULL;
-  }
-
-  if (a) {
-    SSL_SESSION_free(*a);
-    *a = ret;
-  }
-  *pp = CBS_data(&cbs);
-  return ret;
-}
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 6452620..4177a48 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -141,13 +141,14 @@
   return 0;
 }
 
-CERT *ssl_cert_new(void) {
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method) {
   CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(CERT));
+  ret->x509_method = x509_method;
 
   return ret;
 }
@@ -174,6 +175,7 @@
   }
 
   ret->key_method = cert->key_method;
+  ret->x509_method = cert->x509_method;
 
   if (cert->dh_tmp != NULL) {
     ret->dh_tmp = DHparams_dup(cert->dh_tmp);
@@ -208,27 +210,13 @@
   return NULL;
 }
 
-void ssl_cert_flush_cached_x509_leaf(CERT *cert) {
-  X509_free(cert->x509_leaf);
-  cert->x509_leaf = NULL;
-}
-
-static void ssl_cert_flush_cached_x509_chain(CERT *cert) {
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = NULL;
-}
-
 /* Free up and clear all certificates and chains */
 void ssl_cert_clear_certs(CERT *cert) {
   if (cert == NULL) {
     return;
   }
 
-  ssl_cert_flush_cached_x509_leaf(cert);
-  ssl_cert_flush_cached_x509_chain(cert);
-
-  X509_free(cert->x509_stash);
-  cert->x509_stash = NULL;
+  cert->x509_method->cert_clear(cert);
 
   sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
   cert->chain = NULL;
@@ -251,157 +239,99 @@
   OPENSSL_free(c);
 }
 
-/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
-static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
-  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
-  if (chain == NULL) {
-    return NULL;
-  }
-
-  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
-    sk_CRYPTO_BUFFER_free(chain);
-    return NULL;
-  }
-
-  return chain;
-}
-
-/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
- * contents of |x509|. */
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
-  uint8_t *buf = NULL;
-  int cert_len = i2d_X509(x509, &buf);
-  if (cert_len <= 0) {
-    return 0;
-  }
-
-  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
-  OPENSSL_free(buf);
-
-  return buffer;
-}
-
-/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
- * forms of elements of |chain|. It returns one on success or zero on error, in
- * which case no change to |cert->chain| is made. It preverses the existing
- * leaf from |cert->chain|, if any. */
-static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
-  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
-
-  if (cert->chain != NULL) {
-    new_chain = sk_CRYPTO_BUFFER_new_null();
-    if (new_chain == NULL) {
-      return 0;
-    }
-
-    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
-    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
-      goto err;
-    }
-    /* |leaf| might be NULL if it's a “leafless” chain. */
-    if (leaf != NULL) {
-      CRYPTO_BUFFER_up_ref(leaf);
-    }
-  }
-
-  for (size_t i = 0; i < sk_X509_num(chain); i++) {
-    if (new_chain == NULL) {
-      new_chain = new_leafless_chain();
-      if (new_chain == NULL) {
-        goto err;
-      }
-    }
-
-    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
-    if (buffer == NULL ||
-        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
-      CRYPTO_BUFFER_free(buffer);
-      goto err;
-    }
-  }
-
-  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
-  cert->chain = new_chain;
-
-  return 1;
-
-err:
-  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
-  return 0;
-}
-
-static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
-  if (!ssl_cert_set_chain(cert, chain)) {
-    return 0;
-  }
-
-  sk_X509_pop_free(chain, X509_free);
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
-  if (!ssl_cert_set_chain(cert, chain)) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  if (cert->chain != NULL) {
-    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
-      CRYPTO_BUFFER_free(buffer);
-      return 0;
-    }
-
-    return 1;
-  }
-
-  cert->chain = new_leafless_chain();
-  if (cert->chain == NULL ||
-      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
-    CRYPTO_BUFFER_free(buffer);
-    sk_CRYPTO_BUFFER_free(cert->chain);
-    cert->chain = NULL;
-    return 0;
-  }
-
-  return 1;
-}
-
-static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_append_cert(cert, x509)) {
-    return 0;
-  }
-
-  X509_free(cert->x509_stash);
-  cert->x509_stash = x509;
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_append_cert(cert, x509)) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
 static void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg),
                                  void *arg) {
   c->cert_cb = cb;
   c->cert_cb_arg = arg;
 }
 
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+  CBS cert_cbs;
+  CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
+  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
+  if (pubkey == NULL) {
+    return 0;
+  }
+
+  if (!ssl_is_key_type_supported(pubkey->type)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+    EVP_PKEY_free(pubkey);
+    return 0;
+  }
+
+  /* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
+   * certificates, so sanity-check the key usage extension. */
+  if (pubkey->type == EVP_PKEY_EC &&
+      !ssl_cert_check_digital_signature_key_usage(&cert_cbs)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+    EVP_PKEY_free(pubkey);
+    return 0;
+  }
+
+  if (cert->privatekey != NULL) {
+    /* Sanity-check that the private key and the certificate match, unless the
+     * key is opaque (in case of, say, a smartcard). */
+    if (!EVP_PKEY_is_opaque(cert->privatekey) &&
+        !ssl_compare_public_and_private_key(pubkey, cert->privatekey)) {
+      /* don't fail for a cert/key mismatch, just free current private key
+       * (when switching to a different cert & key, first this function should
+       * be used, then ssl_set_pkey */
+      EVP_PKEY_free(cert->privatekey);
+      cert->privatekey = NULL;
+      /* clear error queue */
+      ERR_clear_error();
+    }
+  }
+
+  EVP_PKEY_free(pubkey);
+
+  cert->x509_method->cert_flush_cached_leaf(cert);
+
+  if (cert->chain != NULL) {
+    CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
+    sk_CRYPTO_BUFFER_set(cert->chain, 0, buffer);
+    CRYPTO_BUFFER_up_ref(buffer);
+    return 1;
+  }
+
+  cert->chain = sk_CRYPTO_BUFFER_new_null();
+  if (cert->chain == NULL) {
+    return 0;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
+    return 0;
+  }
+  CRYPTO_BUFFER_up_ref(buffer);
+
+  return 1;
+}
+
+int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len,
+                                 const uint8_t *der) {
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(ctx->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
+int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(ssl->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
 int ssl_verify_cert_chain(SSL *ssl, long *out_verify_result,
                           STACK_OF(X509) *cert_chain) {
   if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) {
@@ -590,6 +520,7 @@
     if (sk_CRYPTO_BUFFER_num(ret) == 0) {
       *out_pubkey = ssl_cert_parse_pubkey(&certificate);
       if (*out_pubkey == NULL) {
+        *out_alert = SSL_AD_DECODE_ERROR;
         goto err;
       }
 
@@ -652,49 +583,6 @@
   return 0;
 }
 
-int ssl_auto_chain_if_needed(SSL *ssl) {
-  /* Only build a chain if there are no intermediates configured and the feature
-   * isn't disabled. */
-  if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
-      !ssl_has_certificate(ssl) ||
-      ssl->cert->chain == NULL ||
-      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
-    return 1;
-  }
-
-  X509 *leaf =
-      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
-  if (!leaf) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
-    return 0;
-  }
-
-  X509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
-    X509_free(leaf);
-    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
-    return 0;
-  }
-
-  /* Attempt to build a chain, ignoring the result. */
-  X509_verify_cert(&ctx);
-  X509_free(leaf);
-  ERR_clear_error();
-
-  /* Remove the leaf from the generated chain. */
-  X509_free(sk_X509_shift(ctx.chain));
-
-  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
-  X509_STORE_CTX_cleanup(&ctx);
-  if (!ok) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(ssl->cert);
-
-  return 1;
-}
-
 /* ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and
  * positions |*out_tbs_cert| to cover the TBSCertificate, starting at the
  * subjectPublicKeyInfo. */
@@ -986,54 +874,6 @@
   return set_cert_store(&ssl->cert->verify_store, store, 1);
 }
 
-int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  return ssl_cert_set0_chain(ctx->cert, chain);
-}
-
-int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  return ssl_cert_set1_chain(ctx->cert, chain);
-}
-
-int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
-  return ssl_cert_set0_chain(ssl->cert, chain);
-}
-
-int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
-  return ssl_cert_set1_chain(ssl->cert, chain);
-}
-
-int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return ssl_cert_add0_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return ssl_cert_add1_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return SSL_CTX_add0_chain_cert(ctx, x509);
-}
-
-int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
-  return ssl_cert_add0_chain_cert(ssl->cert, x509);
-}
-
-int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
-  return ssl_cert_add1_chain_cert(ssl->cert, x509);
-}
-
-int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
-  return SSL_CTX_set0_chain(ctx, NULL);
-}
-
-int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
-  return SSL_CTX_clear_chain_certs(ctx);
-}
-
-int SSL_clear_chain_certs(SSL *ssl) {
-  return SSL_set0_chain(ssl, NULL);
-}
-
 void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
                          void *arg) {
   ssl_cert_set_cert_cb(ctx->cert, cb, arg);
@@ -1043,80 +883,6 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
-/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
- * first element of |cert->chain|. */
-int ssl_cert_cache_leaf_cert(CERT *cert) {
-  if (cert->x509_leaf != NULL ||
-      cert->chain == NULL) {
-    return 1;
-  }
-
-  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
-  if (!leaf) {
-    return 1;
-  }
-
-  cert->x509_leaf = X509_parse_from_buffer(leaf);
-  return cert->x509_leaf != NULL;
-}
-
-/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
- * |cert->chain|. */
-static int ssl_cert_cache_chain_certs(CERT *cert) {
-  if (cert->x509_chain != NULL ||
-      cert->chain == NULL ||
-      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
-    return 1;
-  }
-
-  STACK_OF(X509) *chain = sk_X509_new_null();
-  if (chain == NULL) {
-    return 0;
-  }
-
-  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
-    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
-    X509 *x509 = X509_parse_from_buffer(buffer);
-    if (x509 == NULL ||
-        !sk_X509_push(chain, x509)) {
-      X509_free(x509);
-      goto err;
-    }
-  }
-
-  cert->x509_chain = chain;
-  return 1;
-
-err:
-  sk_X509_pop_free(chain, X509_free);
-  return 0;
-}
-
-int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
-  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
-    *out_chain = NULL;
-    return 0;
-  }
-
-  *out_chain = ctx->cert->x509_chain;
-  return 1;
-}
-
-int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
-                                  STACK_OF(X509) **out_chain) {
-  return SSL_CTX_get0_chain_certs(ctx, out_chain);
-}
-
-int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
-  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
-    *out_chain = NULL;
-    return 0;
-  }
-
-  *out_chain = ssl->cert->x509_chain;
-  return 1;
-}
-
 int ssl_check_leaf_certificate(SSL *ssl, EVP_PKEY *pkey,
                                const CRYPTO_BUFFER *leaf) {
   assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION);
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 480304f..8f1ad73 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -787,10 +787,11 @@
   return 1;
 }
 
-const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf) {
+const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf,
+                                       uint16_t version) {
   switch (algorithm_prf) {
     case SSL_HANDSHAKE_MAC_DEFAULT:
-      return EVP_sha1();
+      return version >= TLS1_2_VERSION ? EVP_sha256() : EVP_md5_sha1();
     case SSL_HANDSHAKE_MAC_SHA256:
       return EVP_sha256();
     case SSL_HANDSHAKE_MAC_SHA384:
diff --git a/src/ssl/ssl_file.c b/src/ssl/ssl_file.c
index e1ebaa6..59351a3 100644
--- a/src/ssl/ssl_file.c
+++ b/src/ssl/ssl_file.c
@@ -573,14 +573,3 @@
 void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data) {
   ctx->default_passwd_callback_userdata = data;
 }
-
-SSL_SESSION *d2i_SSL_SESSION_bio(BIO *bio, SSL_SESSION **out) {
-  return ASN1_d2i_bio_of(SSL_SESSION, SSL_SESSION_new, d2i_SSL_SESSION, bio,
-                         out);
-}
-
-int i2d_SSL_SESSION_bio(BIO *bio, const SSL_SESSION *session) {
-  return ASN1_i2d_bio_of(SSL_SESSION, i2d_SSL_SESSION, bio, session);
-}
-
-IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 851c81f..c946b77 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -248,6 +248,7 @@
   OPENSSL_memset(ret, 0, sizeof(SSL_CTX));
 
   ret->method = method->method;
+  ret->x509_method = method->x509_method;
 
   CRYPTO_MUTEX_init(&ret->lock);
 
@@ -261,7 +262,7 @@
 
   ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
   ret->verify_mode = SSL_VERIFY_NONE;
-  ret->cert = ssl_cert_new();
+  ret->cert = ssl_cert_new(method->x509_method);
   if (ret->cert == NULL) {
     goto err;
   }
@@ -362,8 +363,8 @@
   OPENSSL_free(ctx->psk_identity_hint);
   OPENSSL_free(ctx->supported_group_list);
   OPENSSL_free(ctx->alpn_client_proto_list);
+  CRYPTO_BUFFER_free(ctx->signed_cert_timestamp_list);
   CRYPTO_BUFFER_free(ctx->ocsp_response);
-  OPENSSL_free(ctx->signed_cert_timestamp_list);
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
 
   OPENSSL_free(ctx);
@@ -471,8 +472,11 @@
   ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
   ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
 
-  ssl->session_timeout = ctx->session_timeout;
-  ssl->session_psk_dhe_timeout = ctx->session_psk_dhe_timeout;
+  /* If the context has an SCT list, use it. */
+  if (ctx->signed_cert_timestamp_list != NULL) {
+    CRYPTO_BUFFER_up_ref(ctx->signed_cert_timestamp_list);
+    ssl->signed_cert_timestamp_list = ctx->signed_cert_timestamp_list;
+  }
 
   /* If the context has an OCSP response, use it. */
   if (ctx->ocsp_response != NULL) {
@@ -518,6 +522,7 @@
   OPENSSL_free(ssl->psk_identity_hint);
   sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free);
   sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
+  CRYPTO_BUFFER_free(ssl->signed_cert_timestamp_list);
   CRYPTO_BUFFER_free(ssl->ocsp_response);
 
   if (ssl->method != NULL) {
@@ -1628,8 +1633,27 @@
     return 0;
   }
 
-  return CBS_stow(&sct_list, &ctx->signed_cert_timestamp_list,
-                  &ctx->signed_cert_timestamp_list_length);
+  CRYPTO_BUFFER_free(ctx->signed_cert_timestamp_list);
+  ctx->signed_cert_timestamp_list = CRYPTO_BUFFER_new(CBS_data(&sct_list),
+                                                      CBS_len(&sct_list),
+                                                      NULL);
+  return ctx->signed_cert_timestamp_list != NULL;
+}
+
+int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list,
+                                       size_t list_len) {
+  CBS sct_list;
+  CBS_init(&sct_list, list, list_len);
+  if (!ssl_is_sct_list_valid(&sct_list)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST);
+    return 0;
+  }
+
+  CRYPTO_BUFFER_free(ssl->signed_cert_timestamp_list);
+  ssl->signed_cert_timestamp_list = CRYPTO_BUFFER_new(CBS_data(&sct_list),
+                                                      CBS_len(&sct_list),
+                                                      NULL);
+  return ssl->signed_cert_timestamp_list != NULL;
 }
 
 int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
@@ -2035,6 +2059,12 @@
     return ssl->ctx;
   }
 
+  /* One cannot change the X.509 callbacks during a connection. */
+  if (ssl->ctx->x509_method != ctx->x509_method) {
+    assert(0);
+    return NULL;
+  }
+
   if (ctx == NULL) {
     ctx = ssl->initial_ctx;
   }
@@ -2353,22 +2383,6 @@
       cipher->algorithm_mac == SSL_AEAD;
 }
 
-const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) {
-  switch (version) {
-    case SSL3_VERSION:
-      return &SSLv3_enc_data;
-
-    case TLS1_VERSION:
-    case TLS1_1_VERSION:
-    case TLS1_2_VERSION:
-    case TLS1_3_VERSION:
-      return &TLSv1_enc_data;
-
-    default:
-      return NULL;
-  }
-}
-
 const struct {
   uint16_t version;
   uint32_t flag;
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_privkey.c
similarity index 86%
rename from src/ssl/ssl_rsa.c
rename to src/ssl/ssl_privkey.c
index 6ad2b71..7962247 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_privkey.c
@@ -70,38 +70,30 @@
 #include "internal.h"
 
 
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
-static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey);
-
-static int is_key_type_supported(int key_type) {
+int ssl_is_key_type_supported(int key_type) {
   return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
 }
 
-int SSL_use_certificate(SSL *ssl, X509 *x) {
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
+  if (!ssl_is_key_type_supported(pkey->type)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
     return 0;
   }
 
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
-  if (buffer == NULL) {
+  if (cert->chain != NULL &&
+      sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL &&
+      /* Sanity-check that the private key and the certificate match, unless
+       * the key is opaque (in case of, say, a smartcard). */
+      !EVP_PKEY_is_opaque(pkey) &&
+      !ssl_cert_check_private_key(cert, pkey)) {
     return 0;
   }
 
-  const int ok = ssl_set_cert(ssl->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
-}
+  EVP_PKEY_free(cert->privatekey);
+  EVP_PKEY_up_ref(pkey);
+  cert->privatekey = pkey;
 
-int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
-  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  const int ok = ssl_set_cert(ssl->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
+  return 1;
 }
 
 int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) {
@@ -128,28 +120,6 @@
   return ret;
 }
 
-static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
-  if (!is_key_type_supported(pkey->type)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    return 0;
-  }
-
-  if (cert->chain != NULL &&
-      sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL &&
-      /* Sanity-check that the private key and the certificate match, unless
-       * the key is opaque (in case of, say, a smartcard). */
-      !EVP_PKEY_is_opaque(pkey) &&
-      !ssl_cert_check_private_key(cert, pkey)) {
-    return 0;
-  }
-
-  EVP_PKEY_free(cert->privatekey);
-  EVP_PKEY_up_ref(pkey);
-  cert->privatekey = pkey;
-
-  return 1;
-}
-
 int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
@@ -179,98 +149,6 @@
   return ret;
 }
 
-int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  const int ok = ssl_set_cert(ctx->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
-}
-
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
-  CBS cert_cbs;
-  CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
-  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
-  if (pubkey == NULL) {
-    return 0;
-  }
-
-  if (!is_key_type_supported(pubkey->type)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    EVP_PKEY_free(pubkey);
-    return 0;
-  }
-
-  /* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
-   * certificates, so sanity-check the key usage extension. */
-  if (pubkey->type == EVP_PKEY_EC &&
-      !ssl_cert_check_digital_signature_key_usage(&cert_cbs)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    EVP_PKEY_free(pubkey);
-    return 0;
-  }
-
-  if (cert->privatekey != NULL) {
-    /* Sanity-check that the private key and the certificate match, unless the
-     * key is opaque (in case of, say, a smartcard). */
-    if (!EVP_PKEY_is_opaque(cert->privatekey) &&
-        !ssl_compare_public_and_private_key(pubkey, cert->privatekey)) {
-      /* don't fail for a cert/key mismatch, just free current private key
-       * (when switching to a different cert & key, first this function should
-       * be used, then ssl_set_pkey */
-      EVP_PKEY_free(cert->privatekey);
-      cert->privatekey = NULL;
-      /* clear error queue */
-      ERR_clear_error();
-    }
-  }
-
-  EVP_PKEY_free(pubkey);
-
-  ssl_cert_flush_cached_x509_leaf(cert);
-
-  if (cert->chain != NULL) {
-    CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
-    sk_CRYPTO_BUFFER_set(cert->chain, 0, buffer);
-    CRYPTO_BUFFER_up_ref(buffer);
-    return 1;
-  }
-
-  cert->chain = sk_CRYPTO_BUFFER_new_null();
-  if (cert->chain == NULL) {
-    return 0;
-  }
-
-  if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
-    sk_CRYPTO_BUFFER_free(cert->chain);
-    cert->chain = NULL;
-    return 0;
-  }
-  CRYPTO_BUFFER_up_ref(buffer);
-
-  return 1;
-}
-
-int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len,
-                                 const uint8_t *der) {
-  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  const int ok = ssl_set_cert(ctx->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
-}
-
 int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) {
   int ret;
   EVP_PKEY *pkey;
diff --git a/src/ssl/ssl_rsa_cc.cc b/src/ssl/ssl_privkey_cc.cc
similarity index 100%
rename from src/ssl/ssl_rsa_cc.cc
rename to src/ssl/ssl_privkey_cc.cc
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 7cddbdf..b71b994 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -160,7 +160,7 @@
 static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session);
 static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock);
 
-SSL_SESSION *SSL_SESSION_new(void) {
+SSL_SESSION *ssl_session_new(const SSL_X509_METHOD *x509_method) {
   SSL_SESSION *session = OPENSSL_malloc(sizeof(SSL_SESSION));
   if (session == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -168,6 +168,7 @@
   }
   OPENSSL_memset(session, 0, sizeof(SSL_SESSION));
 
+  session->x509_method = x509_method;
   session->verify_result = X509_V_ERR_INVALID_CALL;
   session->references = 1;
   session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
@@ -177,8 +178,12 @@
   return session;
 }
 
+SSL_SESSION *SSL_SESSION_new(const SSL_CTX *ctx) {
+  return ssl_session_new(ctx->x509_method);
+}
+
 SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int dup_flags) {
-  SSL_SESSION *new_session = SSL_SESSION_new();
+  SSL_SESSION *new_session = ssl_session_new(session->x509_method);
   if (new_session == NULL) {
     goto err;
   }
@@ -214,16 +219,11 @@
       CRYPTO_BUFFER_up_ref(buffer);
     }
   }
-  if (session->x509_peer != NULL) {
-    X509_up_ref(session->x509_peer);
-    new_session->x509_peer = session->x509_peer;
+
+  if (!session->x509_method->session_dup(new_session, session)) {
+    goto err;
   }
-  if (session->x509_chain != NULL) {
-    new_session->x509_chain = X509_chain_up_ref(session->x509_chain);
-    if (new_session->x509_chain == NULL) {
-      goto err;
-    }
-  }
+
   new_session->verify_result = session->verify_result;
 
   new_session->ocsp_response_length = session->ocsp_response_length;
@@ -367,9 +367,7 @@
   OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
   OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
   sk_CRYPTO_BUFFER_pop_free(session->certs, CRYPTO_BUFFER_free);
-  X509_free(session->x509_peer);
-  sk_X509_pop_free(session->x509_chain, X509_free);
-  sk_X509_pop_free(session->x509_chain_without_leaf, X509_free);
+  session->x509_method->session_clear(session);
   OPENSSL_free(session->tlsext_hostname);
   OPENSSL_free(session->tlsext_tick);
   OPENSSL_free(session->tlsext_signed_cert_timestamp_list);
@@ -494,6 +492,16 @@
   return CRYPTO_get_ex_data(&session->ex_data, idx);
 }
 
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
+                                     const SSL *ssl) {
+  uint16_t version;
+  if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
+    return NULL;
+  }
+
+  return ssl_get_handshake_digest(session->cipher->algorithm_prf, version);
+}
+
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
   SSL *const ssl = hs->ssl;
   if (ssl->mode & SSL_MODE_NO_SESSION_CREATION) {
@@ -501,7 +509,7 @@
     return 0;
   }
 
-  SSL_SESSION *session = SSL_SESSION_new();
+  SSL_SESSION *session = ssl_session_new(ssl->ctx->x509_method);
   if (session == NULL) {
     return 0;
   }
@@ -518,13 +526,13 @@
   if (version >= TLS1_3_VERSION) {
     /* TLS 1.3 uses tickets as authenticators, so we are willing to use them for
      * longer. */
-    session->timeout = ssl->session_psk_dhe_timeout;
+    session->timeout = ssl->initial_ctx->session_psk_dhe_timeout;
     session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
   } else {
     /* TLS 1.2 resumption does not incorporate new key material, so we use a
      * much shorter timeout. */
-    session->timeout = ssl->session_timeout;
-    session->auth_timeout = ssl->session_timeout;
+    session->timeout = ssl->initial_ctx->session_timeout;
+    session->auth_timeout = ssl->initial_ctx->session_timeout;
   }
 
   if (is_server) {
@@ -563,53 +571,6 @@
   return 0;
 }
 
-int ssl_session_x509_cache_objects(SSL_SESSION *sess) {
-  STACK_OF(X509) *chain = NULL;
-  const size_t num_certs = sk_CRYPTO_BUFFER_num(sess->certs);
-
-  if (num_certs > 0) {
-    chain = sk_X509_new_null();
-    if (chain == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-  }
-
-  X509 *leaf = NULL;
-  for (size_t i = 0; i < num_certs; i++) {
-    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;
-    }
-    if (!sk_X509_push(chain, x509)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      X509_free(x509);
-      goto err;
-    }
-    if (i == 0) {
-      leaf = x509;
-    }
-  }
-
-  sk_X509_pop_free(sess->x509_chain, X509_free);
-  sess->x509_chain = chain;
-  sk_X509_pop_free(sess->x509_chain_without_leaf, X509_free);
-  sess->x509_chain_without_leaf = NULL;
-
-  X509_free(sess->x509_peer);
-  if (leaf != NULL) {
-    X509_up_ref(leaf);
-  }
-  sess->x509_peer = leaf;
-
-  return 1;
-
-err:
-  sk_X509_pop_free(chain, X509_free);
-  return 0;
-}
-
 int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
   int ret = 0;
 
@@ -743,7 +704,8 @@
          /* If the session contains a client certificate (either the full
           * certificate or just the hash) then require that the form of the
           * certificate matches the current configuration. */
-         ((session->x509_peer == NULL && !session->peer_sha256_valid) ||
+         ((sk_CRYPTO_BUFFER_num(session->certs) == 0 &&
+           !session->peer_sha256_valid) ||
           session->peer_sha256_valid ==
               ssl->retain_only_sha256_of_client_certs);
 }
@@ -983,16 +945,6 @@
   ctx->session_psk_dhe_timeout = timeout;
 }
 
-long SSL_set_session_timeout(SSL *ssl, long timeout) {
-  long old_timeout = ssl->session_timeout;
-  ssl->session_timeout = timeout;
-  return old_timeout;
-}
-
-void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout) {
-  ssl->session_psk_dhe_timeout = timeout;
-}
-
 typedef struct timeout_param_st {
   SSL_CTX *ctx;
   long time;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index 09a43d1..479288a 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -163,15 +163,6 @@
     case SSL3_ST_SR_CLNT_HELLO_C:
       return "SSLv3 read client hello C";
 
-    case SSL3_ST_SW_HELLO_REQ_A:
-      return "SSLv3 write hello request A";
-
-    case SSL3_ST_SW_HELLO_REQ_B:
-      return "SSLv3 write hello request B";
-
-    case SSL3_ST_SW_HELLO_REQ_C:
-      return "SSLv3 write hello request C";
-
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "SSLv3 write server hello A";
 
@@ -273,15 +264,6 @@
     case SSL3_ST_CR_FINISHED_A:
       return "3RFINA";
 
-    case SSL3_ST_SW_HELLO_REQ_A:
-      return "3WHR_A";
-
-    case SSL3_ST_SW_HELLO_REQ_B:
-      return "3WHR_B";
-
-    case SSL3_ST_SW_HELLO_REQ_C:
-      return "3WHR_C";
-
     case SSL3_ST_SR_CLNT_HELLO_A:
       return "3RCH_A";
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index aa265c8..dfab976 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -634,7 +634,12 @@
   }
 
   // Verify the SSL_SESSION decodes.
-  bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(input.data(), input.size()));
+  bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+  if (!ssl_ctx) {
+    return false;
+  }
+  bssl::UniquePtr<SSL_SESSION> session(
+      SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
   if (!session) {
     fprintf(stderr, "SSL_SESSION_from_bytes failed\n");
     return false;
@@ -703,7 +708,12 @@
   }
 
   // Verify that the SSL_SESSION fails to decode.
-  bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(input.data(), input.size()));
+  bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+  if (!ssl_ctx) {
+    return false;
+  }
+  bssl::UniquePtr<SSL_SESSION> session(
+      SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
   if (session) {
     fprintf(stderr, "SSL_SESSION_from_bytes unexpectedly succeeded\n");
     return false;
@@ -795,8 +805,13 @@
   if (!DecodeBase64(&der, kOpenSSLSession)) {
     return nullptr;
   }
+
+  bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+  if (!ssl_ctx) {
+    return nullptr;
+  }
   bssl::UniquePtr<SSL_SESSION> session(
-      SSL_SESSION_from_bytes(der.data(), der.size()));
+      SSL_SESSION_from_bytes(der.data(), der.size(), ssl_ctx.get()));
   if (!session) {
     return nullptr;
   }
@@ -935,24 +950,18 @@
 
 // Test that |SSL_get_client_CA_list| echoes back the configured parameter even
 // before configuring as a server.
-static bool TestClientCAList() {
+TEST(SSLTest, ClientCAList) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-  if (!ctx) {
-    return false;
-  }
+  ASSERT_TRUE(ctx);
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
-  if (!ssl) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
 
   STACK_OF(X509_NAME) *stack = sk_X509_NAME_new_null();
-  if (stack == nullptr) {
-    return false;
-  }
+  ASSERT_TRUE(stack);
   // |SSL_set_client_CA_list| takes ownership.
   SSL_set_client_CA_list(ssl.get(), stack);
 
-  return SSL_get_client_CA_list(ssl.get()) == stack;
+  EXPECT_EQ(stack, SSL_get_client_CA_list(ssl.get()));
 }
 
 static void AppendSession(SSL_SESSION *session, void *arg) {
@@ -995,7 +1004,11 @@
 }
 
 static bssl::UniquePtr<SSL_SESSION> CreateTestSession(uint32_t number) {
-  bssl::UniquePtr<SSL_SESSION> ret(SSL_SESSION_new());
+  bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+  if (!ssl_ctx) {
+    return nullptr;
+  }
+  bssl::UniquePtr<SSL_SESSION> ret(SSL_SESSION_new(ssl_ctx.get()));
   if (!ret) {
     return nullptr;
   }
@@ -1456,163 +1469,128 @@
   return true;
 }
 
-static bool TestSessionDuplication() {
+TEST(SSLTest, SessionDuplication) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-  if (!client_ctx || !server_ctx) {
-    return false;
-  }
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
 
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  if (!cert || !key ||
-      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
-    return false;
-  }
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
 
   bssl::UniquePtr<SSL> client, server;
-  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
-                              server_ctx.get(), nullptr /* no session */)) {
-    return false;
-  }
+  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get(),
+                                     nullptr /* no session */));
 
   SSL_SESSION *session0 = SSL_get_session(client.get());
-  bssl::UniquePtr<SSL_SESSION> session1(SSL_SESSION_dup(session0, SSL_SESSION_DUP_ALL));
-  if (!session1) {
-    return false;
-  }
+  bssl::UniquePtr<SSL_SESSION> session1(
+      SSL_SESSION_dup(session0, SSL_SESSION_DUP_ALL));
+  ASSERT_TRUE(session1);
 
   session1->not_resumable = 0;
 
   uint8_t *s0_bytes, *s1_bytes;
   size_t s0_len, s1_len;
 
-  if (!SSL_SESSION_to_bytes(session0, &s0_bytes, &s0_len)) {
-    return false;
-  }
+  ASSERT_TRUE(SSL_SESSION_to_bytes(session0, &s0_bytes, &s0_len));
   bssl::UniquePtr<uint8_t> free_s0(s0_bytes);
 
-  if (!SSL_SESSION_to_bytes(session1.get(), &s1_bytes, &s1_len)) {
-    return false;
-  }
+  ASSERT_TRUE(SSL_SESSION_to_bytes(session1.get(), &s1_bytes, &s1_len));
   bssl::UniquePtr<uint8_t> free_s1(s1_bytes);
 
-  return s0_len == s1_len && OPENSSL_memcmp(s0_bytes, s1_bytes, s0_len) == 0;
+  EXPECT_EQ(Bytes(s0_bytes, s0_len), Bytes(s1_bytes, s1_len));
 }
 
-static bool ExpectFDs(const SSL *ssl, int rfd, int wfd) {
-  if (SSL_get_rfd(ssl) != rfd || SSL_get_wfd(ssl) != wfd) {
-    fprintf(stderr, "Got fds %d and %d, wanted %d and %d.\n", SSL_get_rfd(ssl),
-            SSL_get_wfd(ssl), rfd, wfd);
-    return false;
-  }
+static void ExpectFDs(const SSL *ssl, int rfd, int wfd) {
+  EXPECT_EQ(rfd, SSL_get_rfd(ssl));
+  EXPECT_EQ(wfd, SSL_get_wfd(ssl));
 
   // The wrapper BIOs are always equal when fds are equal, even if set
   // individually.
-  if (rfd == wfd && SSL_get_rbio(ssl) != SSL_get_wbio(ssl)) {
-    fprintf(stderr, "rbio and wbio did not match.\n");
-    return false;
+  if (rfd == wfd) {
+    EXPECT_EQ(SSL_get_rbio(ssl), SSL_get_wbio(ssl));
   }
-
-  return true;
 }
 
-static bool TestSetFD() {
+TEST(SSLTest, SetFD) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-  if (!ctx) {
-    return false;
-  }
+  ASSERT_TRUE(ctx);
 
   // Test setting different read and write FDs.
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_rfd(ssl.get(), 1) ||
-      !SSL_set_wfd(ssl.get(), 2) ||
-      !ExpectFDs(ssl.get(), 1, 2)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_wfd(ssl.get(), 2));
+  ExpectFDs(ssl.get(), 1, 2);
 
   // Test setting the same FD.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_fd(ssl.get(), 1) ||
-      !ExpectFDs(ssl.get(), 1, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_fd(ssl.get(), 1));
+  ExpectFDs(ssl.get(), 1, 1);
 
   // Test setting the same FD one side at a time.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_rfd(ssl.get(), 1) ||
-      !SSL_set_wfd(ssl.get(), 1) ||
-      !ExpectFDs(ssl.get(), 1, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1));
+  ExpectFDs(ssl.get(), 1, 1);
 
   // Test setting the same FD in the other order.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_wfd(ssl.get(), 1) ||
-      !SSL_set_rfd(ssl.get(), 1) ||
-      !ExpectFDs(ssl.get(), 1, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1));
+  ExpectFDs(ssl.get(), 1, 1);
 
   // Test changing the read FD partway through.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_fd(ssl.get(), 1) ||
-      !SSL_set_rfd(ssl.get(), 2) ||
-      !ExpectFDs(ssl.get(), 2, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_fd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_rfd(ssl.get(), 2));
+  ExpectFDs(ssl.get(), 2, 1);
 
   // Test changing the write FD partway through.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_fd(ssl.get(), 1) ||
-      !SSL_set_wfd(ssl.get(), 2) ||
-      !ExpectFDs(ssl.get(), 1, 2)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_fd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_wfd(ssl.get(), 2));
+  ExpectFDs(ssl.get(), 1, 2);
 
   // Test a no-op change to the read FD partway through.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_fd(ssl.get(), 1) ||
-      !SSL_set_rfd(ssl.get(), 1) ||
-      !ExpectFDs(ssl.get(), 1, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_fd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1));
+  ExpectFDs(ssl.get(), 1, 1);
 
   // Test a no-op change to the write FD partway through.
   ssl.reset(SSL_new(ctx.get()));
-  if (!ssl ||
-      !SSL_set_fd(ssl.get(), 1) ||
-      !SSL_set_wfd(ssl.get(), 1) ||
-      !ExpectFDs(ssl.get(), 1, 1)) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  EXPECT_TRUE(SSL_set_fd(ssl.get(), 1));
+  EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1));
+  ExpectFDs(ssl.get(), 1, 1);
 
   // ASan builds will implicitly test that the internal |BIO| reference-counting
   // is correct.
-
-  return true;
 }
 
-static bool TestSetBIO() {
+TEST(SSLTest, SetBIO) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-  if (!ctx) {
-    return false;
-  }
+  ASSERT_TRUE(ctx);
 
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
   bssl::UniquePtr<BIO> bio1(BIO_new(BIO_s_mem())), bio2(BIO_new(BIO_s_mem())),
       bio3(BIO_new(BIO_s_mem()));
-  if (!ssl || !bio1 || !bio2 || !bio3) {
-    return false;
-  }
+  ASSERT_TRUE(ssl);
+  ASSERT_TRUE(bio1);
+  ASSERT_TRUE(bio2);
+  ASSERT_TRUE(bio3);
 
   // SSL_set_bio takes one reference when the parameters are the same.
   BIO_up_ref(bio1.get());
@@ -1655,7 +1633,6 @@
 
   // ASAN builds will implicitly test that the internal |BIO| reference-counting
   // is correct.
-  return true;
 }
 
 static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { return 1; }
@@ -2207,8 +2184,12 @@
   len = static_cast<size_t>(len1 + len2);
 #endif
 
+  bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+  if (!ssl_ctx) {
+    return false;
+  }
   bssl::UniquePtr<SSL_SESSION> server_session(
-      SSL_SESSION_from_bytes(plaintext.get(), len));
+      SSL_SESSION_from_bytes(plaintext.get(), len, ssl_ctx.get()));
   if (!server_session) {
     return false;
   }
@@ -2410,151 +2391,6 @@
   return true;
 }
 
-static int SetSessionTimeoutCallback(SSL *ssl, void *arg) {
-  long timeout = *(long *) arg;
-  SSL_set_session_timeout(ssl, timeout);
-  return 1;
-}
-
-static int SetSessionTimeoutCallbackTLS13(SSL *ssl, void *arg) {
-  long timeout = *(long *) arg;
-  SSL_set_session_psk_dhe_timeout(ssl, timeout);
-  return 1;
-}
-
-static bool TestSessionTimeoutCertCallback(bool is_dtls,
-                                           const SSL_METHOD *method,
-                                           uint16_t version) {
-  if (version == TLS1_3_VERSION) {
-    // |SSL_set_session_timeout| only applies to TLS 1.2 style resumption.
-    return true;
-  }
-
-  static const int kStartTime = 1000;
-  g_current_time.tv_sec = kStartTime;
-
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  if (!cert || !key) {
-    return false;
-  }
-
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
-  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
-  if (!server_ctx || !client_ctx ||
-      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
-      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
-      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
-      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
-      !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
-    return false;
-  }
-
-  SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
-  SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
-
-  SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
-
-  long timeout = 25;
-  if (version == TLS1_3_VERSION) {
-    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallbackTLS13,
-                        &timeout);
-  } else {
-    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
-  }
-
-  bssl::UniquePtr<SSL_SESSION> session =
-      CreateClientSession(client_ctx.get(), server_ctx.get());
-  if (!session) {
-    fprintf(stderr, "Error getting session.\n");
-    return false;
-  }
-
-  // Advance the clock just behind the timeout.
-  g_current_time.tv_sec += timeout - 1;
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
-                           true /* expect session reused */)) {
-    fprintf(stderr, "Error resuming session.\n");
-    return false;
-  }
-
-  // Advance the clock one more second.
-  g_current_time.tv_sec++;
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
-                           false /* expect session not reused */)) {
-    fprintf(stderr, "Error resuming session.\n");
-    return false;
-  }
-
-  // Set session timeout to 0 to disable resumption.
-  timeout = 0;
-  g_current_time.tv_sec = kStartTime;
-
-  bssl::UniquePtr<SSL_SESSION> not_resumable_session =
-      CreateClientSession(client_ctx.get(), server_ctx.get());
-  if (!not_resumable_session) {
-    fprintf(stderr, "Error getting session.\n");
-    return false;
-  }
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                           not_resumable_session.get(),
-                           false /* expect session not reused */)) {
-    fprintf(stderr, "Error resuming session with timeout of 0.\n");
-    return false;
-  }
-
-  // Set both context and connection (via callback) default session timeout.
-  // The connection one is the one that ends up being used.
-  timeout = 25;
-  g_current_time.tv_sec = kStartTime;
-
-  if (version == TLS1_3_VERSION) {
-    SSL_CTX_set_session_psk_dhe_timeout(server_ctx.get(), timeout - 10);
-  } else {
-    SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
-  }
-
-  bssl::UniquePtr<SSL_SESSION> ctx_and_cb_session =
-      CreateClientSession(client_ctx.get(), server_ctx.get());
-  if (!ctx_and_cb_session) {
-    fprintf(stderr, "Error getting session.\n");
-    return false;
-  }
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                           ctx_and_cb_session.get(),
-                           true /* expect session reused */)) {
-    fprintf(stderr, "Error resuming session with timeout of 0.\n");
-    return false;
-  }
-
-  // Advance the clock just behind the timeout.
-  g_current_time.tv_sec += timeout - 1;
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                           ctx_and_cb_session.get(),
-                           true /* expect session reused */)) {
-    fprintf(stderr, "Error resuming session.\n");
-    return false;
-  }
-
-  // Advance the clock one more second.
-  g_current_time.tv_sec++;
-
-  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                           ctx_and_cb_session.get(),
-                           false /* expect session not reused */)) {
-    fprintf(stderr, "Error resuming session.\n");
-    return false;
-  }
-
-  return true;
-}
-
 static int SwitchContext(SSL *ssl, int *out_alert, void *arg) {
   SSL_CTX *ctx = reinterpret_cast<SSL_CTX*>(arg);
   SSL_set_SSL_CTX(ssl, ctx);
@@ -2622,121 +2458,81 @@
   return true;
 }
 
-static int SetMaxVersion(const SSL_CLIENT_HELLO *client_hello) {
-  if (!SSL_set_max_proto_version(client_hello->ssl, TLS1_2_VERSION)) {
-    return -1;
-  }
-
-  return 1;
-}
-
-// TestEarlyCallbackVersionSwitch tests that the early callback can swap the
-// maximum version.
-static bool TestEarlyCallbackVersionSwitch() {
+// Test that the early callback can swap the maximum version.
+TEST(SSLTest, EarlyCallbackVersionSwitch) {
   bssl::UniquePtr<X509> cert = GetTestCertificate();
   bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  if (!cert || !key || !server_ctx || !client_ctx ||
-      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
-      !SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION) ||
-      !SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION)) {
-    return false;
-  }
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(server_ctx);
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
+  ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
+  ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
 
-  SSL_CTX_set_select_certificate_cb(server_ctx.get(), SetMaxVersion);
+  SSL_CTX_set_select_certificate_cb(
+      server_ctx.get(), [](const SSL_CLIENT_HELLO *client_hello) -> int {
+        if (!SSL_set_max_proto_version(client_hello->ssl, TLS1_2_VERSION)) {
+          return -1;
+        }
+
+        return 1;
+      });
 
   bssl::UniquePtr<SSL> client, server;
-  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
-                              server_ctx.get(), nullptr)) {
-    return false;
-  }
-
-  if (SSL_version(client.get()) != TLS1_2_VERSION) {
-    fprintf(stderr, "Early callback failed to switch the maximum version.\n");
-    return false;
-  }
-
-  return true;
+  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get(), nullptr));
+  EXPECT_EQ(TLS1_2_VERSION, SSL_version(client.get()));
 }
 
-static bool TestSetVersion() {
+TEST(SSLTest, SetVersion) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
-  if (!ctx) {
-    return false;
-  }
+  ASSERT_TRUE(ctx);
 
-  if (!SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION) ||
-      !SSL_CTX_set_max_proto_version(ctx.get(), TLS1_1_VERSION) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), TLS1_1_VERSION)) {
-    fprintf(stderr, "Could not set valid TLS version.\n");
-    return false;
-  }
+  // Set valid TLS versions.
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_1_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_1_VERSION));
 
-  if (SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION) ||
-      SSL_CTX_set_max_proto_version(ctx.get(), 0x0200) ||
-      SSL_CTX_set_max_proto_version(ctx.get(), 0x1234) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), 0x0200) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), 0x1234)) {
-    fprintf(stderr, "Unexpectedly set invalid TLS version.\n");
-    return false;
-  }
+  // Invalid TLS versions are rejected.
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION));
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x0200));
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x1234));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x0200));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x1234));
 
-  if (!SSL_CTX_set_max_proto_version(ctx.get(), 0) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), 0)) {
-    fprintf(stderr, "Could not set default TLS version.\n");
-    return false;
-  }
-
-  if (ctx->min_version != SSL3_VERSION ||
-      ctx->max_version != TLS1_2_VERSION) {
-    fprintf(stderr, "Default TLS versions were incorrect (%04x and %04x).\n",
-            ctx->min_version, ctx->max_version);
-    return false;
-  }
+  // Zero is the default version.
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0));
+  EXPECT_EQ(TLS1_2_VERSION, ctx->max_version);
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0));
+  EXPECT_EQ(SSL3_VERSION, ctx->min_version);
 
   ctx.reset(SSL_CTX_new(DTLS_method()));
-  if (!ctx) {
-    return false;
-  }
+  ASSERT_TRUE(ctx);
 
-  if (!SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION) ||
-      !SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_2_VERSION) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_2_VERSION)) {
-    fprintf(stderr, "Could not set valid DTLS version.\n");
-    return false;
-  }
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_2_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION));
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_2_VERSION));
 
-  if (SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION) ||
-      SSL_CTX_set_max_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */) ||
-      SSL_CTX_set_max_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */) ||
-      SSL_CTX_set_max_proto_version(ctx.get(), 0x1234) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */) ||
-      SSL_CTX_set_min_proto_version(ctx.get(), 0x1234)) {
-    fprintf(stderr, "Unexpectedly set invalid DTLS version.\n");
-    return false;
-  }
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION));
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */));
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */));
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x1234));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */));
+  EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x1234));
 
-  if (!SSL_CTX_set_max_proto_version(ctx.get(), 0) ||
-      !SSL_CTX_set_min_proto_version(ctx.get(), 0)) {
-    fprintf(stderr, "Could not set default DTLS version.\n");
-    return false;
-  }
-
-  if (ctx->min_version != TLS1_1_VERSION ||
-      ctx->max_version != TLS1_2_VERSION) {
-    fprintf(stderr, "Default DTLS versions were incorrect (%04x and %04x).\n",
-            ctx->min_version, ctx->max_version);
-    return false;
-  }
-
-  return true;
+  EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0));
+  EXPECT_EQ(TLS1_2_VERSION, ctx->max_version);
+  EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0));
+  EXPECT_EQ(TLS1_1_VERSION, ctx->min_version);
 }
 
 static const char *GetVersionName(uint16_t version) {
@@ -3279,10 +3075,8 @@
   bssl::UniquePtr<uint8_t> free_der3(der3);
 
   // They must also encode identically.
-  ASSERT_EQ(der2_len, der_len);
-  EXPECT_EQ(0, OPENSSL_memcmp(der, der2, static_cast<size_t>(der_len)));
-  ASSERT_EQ(der3_len, der_len);
-  EXPECT_EQ(0, OPENSSL_memcmp(der, der3, static_cast<size_t>(der_len)));
+  EXPECT_EQ(Bytes(der, der_len), Bytes(der2, der2_len));
+  EXPECT_EQ(Bytes(der, der_len), Bytes(der3, der3_len));
 }
 
 // TODO(davidben): Convert this file to GTest properly.
@@ -3313,28 +3107,20 @@
       // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
       // will be a PSK binder after the padding extension.
       !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT_VERSION) ||
-      !TestClientCAList() ||
       !TestInternalSessionCache() ||
       !ForEachVersion(TestSequenceNumber) ||
       !ForEachVersion(TestOneSidedShutdown) ||
-      !TestSessionDuplication() ||
-      !TestSetFD() ||
-      !TestSetBIO() ||
       !ForEachVersion(TestGetPeerCertificate) ||
       !ForEachVersion(TestRetainOnlySHA256OfCerts) ||
       !TestClientHello() ||
       !ForEachVersion(TestSessionIDContext) ||
       !ForEachVersion(TestSessionTimeout) ||
-      !ForEachVersion(TestSessionTimeoutCertCallback) ||
       !ForEachVersion(TestSNICallback) ||
-      !TestEarlyCallbackVersionSwitch() ||
-      !TestSetVersion() ||
       !ForEachVersion(TestVersion) ||
       !ForEachVersion(TestALPNCipherAvailable) ||
       !ForEachVersion(TestSSLClearSessionResumption) ||
       !ForEachVersion(TestAutoChain) ||
       !ForEachVersion(TestSSLWriteRetry)) {
-    ERR_print_errors_fp(stderr);
     ADD_FAILURE() << "Tests failed";
   }
 }
diff --git a/src/ssl/s3_enc.c b/src/ssl/ssl_transcript.c
similarity index 64%
rename from src/ssl/s3_enc.c
rename to src/ssl/ssl_transcript.c
index bf82e08..9cc3777 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/ssl_transcript.c
@@ -138,84 +138,22 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/buf.h>
+#include <openssl/digest.h>
 #include <openssl/err.h>
-#include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/md5.h>
 #include <openssl/nid.h>
+#include <openssl/sha.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
 
 
-static int ssl3_prf(const SSL *ssl, uint8_t *out, size_t out_len,
-                    const uint8_t *secret, size_t secret_len, const char *label,
-                    size_t label_len, const uint8_t *seed1, size_t seed1_len,
-                    const uint8_t *seed2, size_t seed2_len) {
-  EVP_MD_CTX md5;
-  EVP_MD_CTX sha1;
-  uint8_t buf[16], smd[SHA_DIGEST_LENGTH];
-  uint8_t c = 'A';
-  size_t i, j, k;
-
-  k = 0;
-  EVP_MD_CTX_init(&md5);
-  EVP_MD_CTX_init(&sha1);
-  for (i = 0; i < out_len; i += MD5_DIGEST_LENGTH) {
-    k++;
-    if (k > sizeof(buf)) {
-      /* bug: 'buf' is too small for this ciphersuite */
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-
-    for (j = 0; j < k; j++) {
-      buf[j] = c;
-    }
-    c++;
-    if (!EVP_DigestInit_ex(&sha1, EVP_sha1(), NULL)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
-      return 0;
-    }
-    EVP_DigestUpdate(&sha1, buf, k);
-    EVP_DigestUpdate(&sha1, secret, secret_len);
-    /* |label| is ignored for SSLv3. */
-    if (seed1_len) {
-      EVP_DigestUpdate(&sha1, seed1, seed1_len);
-    }
-    if (seed2_len) {
-      EVP_DigestUpdate(&sha1, seed2, seed2_len);
-    }
-    EVP_DigestFinal_ex(&sha1, smd, NULL);
-
-    if (!EVP_DigestInit_ex(&md5, EVP_md5(), NULL)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
-      return 0;
-    }
-    EVP_DigestUpdate(&md5, secret, secret_len);
-    EVP_DigestUpdate(&md5, smd, SHA_DIGEST_LENGTH);
-    if (i + MD5_DIGEST_LENGTH > out_len) {
-      EVP_DigestFinal_ex(&md5, smd, NULL);
-      OPENSSL_memcpy(out, smd, out_len - i);
-    } else {
-      EVP_DigestFinal_ex(&md5, out, NULL);
-    }
-
-    out += MD5_DIGEST_LENGTH;
-  }
-
-  OPENSSL_cleanse(smd, SHA_DIGEST_LENGTH);
-  EVP_MD_CTX_cleanup(&md5);
-  EVP_MD_CTX_cleanup(&sha1);
-
-  return 1;
-}
-
-int ssl3_init_handshake_buffer(SSL *ssl) {
-  ssl3_free_handshake_buffer(ssl);
-  ssl3_free_handshake_hash(ssl);
-  ssl->s3->handshake_buffer = BUF_MEM_new();
-  return ssl->s3->handshake_buffer != NULL;
+int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript) {
+  SSL_TRANSCRIPT_cleanup(transcript);
+  transcript->buffer = BUF_MEM_new();
+  return transcript->buffer != NULL;
 }
 
 /* init_digest_with_data calls |EVP_DigestInit_ex| on |ctx| with |md| and then
@@ -229,78 +167,113 @@
   return 1;
 }
 
-int ssl3_init_handshake_hash(SSL *ssl) {
-  ssl3_free_handshake_hash(ssl);
+int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version,
+                             int algorithm_prf) {
+  const EVP_MD *md = ssl_get_handshake_digest(algorithm_prf, version);
 
-  uint32_t algorithm_prf = ssl_get_algorithm_prf(ssl);
-  if (!init_digest_with_data(&ssl->s3->handshake_hash,
-                             ssl_get_handshake_digest(algorithm_prf),
-                             ssl->s3->handshake_buffer)) {
-    return 0;
+  /* To support SSL 3.0's Finished and CertificateVerify constructions,
+   * EVP_md5_sha1() is split into MD5 and SHA-1 halves. When SSL 3.0 is removed,
+   * we can simplify this. */
+  if (md == EVP_md5_sha1()) {
+    if (!init_digest_with_data(&transcript->md5, EVP_md5(),
+                               transcript->buffer)) {
+      return 0;
+    }
+    md = EVP_sha1();
   }
 
-  if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT &&
-      !init_digest_with_data(&ssl->s3->handshake_md5, EVP_md5(),
-                             ssl->s3->handshake_buffer)) {
+  if (!init_digest_with_data(&transcript->hash, md, transcript->buffer)) {
     return 0;
   }
 
   return 1;
 }
 
-void ssl3_free_handshake_hash(SSL *ssl) {
-  EVP_MD_CTX_cleanup(&ssl->s3->handshake_hash);
-  EVP_MD_CTX_cleanup(&ssl->s3->handshake_md5);
+void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript) {
+  SSL_TRANSCRIPT_free_buffer(transcript);
+  EVP_MD_CTX_cleanup(&transcript->hash);
+  EVP_MD_CTX_cleanup(&transcript->md5);
 }
 
-void ssl3_free_handshake_buffer(SSL *ssl) {
-  BUF_MEM_free(ssl->s3->handshake_buffer);
-  ssl->s3->handshake_buffer = NULL;
+void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript) {
+  BUF_MEM_free(transcript->buffer);
+  transcript->buffer = NULL;
 }
 
-int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len) {
+size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript) {
+  return EVP_MD_size(SSL_TRANSCRIPT_md(transcript));
+}
+
+const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript) {
+  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
+    return EVP_md5_sha1();
+  }
+  return EVP_MD_CTX_md(&transcript->hash);
+}
+
+int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in,
+                          size_t in_len) {
   /* Depending on the state of the handshake, either the handshake buffer may be
    * active, the rolling hash, or both. */
-
-  if (ssl->s3->handshake_buffer != NULL) {
-    size_t new_len = ssl->s3->handshake_buffer->length + in_len;
+  if (transcript->buffer != NULL) {
+    size_t new_len = transcript->buffer->length + in_len;
     if (new_len < in_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
       return 0;
     }
-    if (!BUF_MEM_grow(ssl->s3->handshake_buffer, new_len)) {
+    if (!BUF_MEM_grow(transcript->buffer, new_len)) {
       return 0;
     }
-    OPENSSL_memcpy(ssl->s3->handshake_buffer->data + new_len - in_len, in,
-                   in_len);
+    OPENSSL_memcpy(transcript->buffer->data + new_len - in_len, in, in_len);
   }
 
-  if (EVP_MD_CTX_md(&ssl->s3->handshake_hash) != NULL) {
-    EVP_DigestUpdate(&ssl->s3->handshake_hash, in, in_len);
+  if (EVP_MD_CTX_md(&transcript->hash) != NULL) {
+    EVP_DigestUpdate(&transcript->hash, in, in_len);
   }
-  if (EVP_MD_CTX_md(&ssl->s3->handshake_md5) != NULL) {
-    EVP_DigestUpdate(&ssl->s3->handshake_md5, in, in_len);
+  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
+    EVP_DigestUpdate(&transcript->md5, in, in_len);
   }
+
   return 1;
 }
 
-static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
-                              size_t sender_len, uint8_t *p) {
-  unsigned int ret;
+int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out,
+                            size_t *out_len) {
+  int ret = 0;
+  EVP_MD_CTX ctx;
+  EVP_MD_CTX_init(&ctx);
+  unsigned md5_len = 0;
+  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
+    if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->md5) ||
+        !EVP_DigestFinal_ex(&ctx, out, &md5_len)) {
+      goto err;
+    }
+  }
+
+  unsigned len;
+  if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->hash) ||
+      !EVP_DigestFinal_ex(&ctx, out + md5_len, &len)) {
+    goto err;
+  }
+
+  *out_len = md5_len + len;
+  ret = 1;
+
+err:
+  EVP_MD_CTX_cleanup(&ctx);
+  return ret;
+}
+
+static int ssl3_handshake_mac(SSL_TRANSCRIPT *transcript,
+                              const SSL_SESSION *session,
+                              const EVP_MD_CTX *ctx_template,
+                              const char *sender, size_t sender_len,
+                              uint8_t *p, size_t *out_len) {
+  unsigned int len;
   size_t npad, n;
   unsigned int i;
   uint8_t md_buf[EVP_MAX_MD_SIZE];
   EVP_MD_CTX ctx;
-  const EVP_MD_CTX *ctx_template;
-
-  if (md_nid == NID_md5) {
-    ctx_template = &ssl->s3->handshake_md5;
-  } else if (md_nid == EVP_MD_CTX_type(&ssl->s3->handshake_hash)) {
-    ctx_template = &ssl->s3->handshake_hash;
-  } else {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST);
-    return 0;
-  }
 
   EVP_MD_CTX_init(&ctx);
   if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) {
@@ -325,11 +298,6 @@
 
   n = EVP_MD_CTX_size(&ctx);
 
-  SSL_SESSION *session = ssl->session;
-  if (ssl->s3->new_session != NULL) {
-    session = ssl->s3->new_session;
-  }
-
   npad = (48 / n) * n;
   if (sender != NULL) {
     EVP_DigestUpdate(&ctx, sender, sender_len);
@@ -346,61 +314,92 @@
   EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length);
   EVP_DigestUpdate(&ctx, kPad2, npad);
   EVP_DigestUpdate(&ctx, md_buf, i);
-  EVP_DigestFinal_ex(&ctx, p, &ret);
+  EVP_DigestFinal_ex(&ctx, p, &len);
 
   EVP_MD_CTX_cleanup(&ctx);
 
-  return ret;
+  *out_len = len;
+  return 1;
 }
 
-static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
-  const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
-                                   : SSL3_MD_CLIENT_FINISHED_CONST;
-  const size_t sender_len = 4;
-  int ret, sha1len;
-  ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out);
-  if (ret == 0) {
-    return 0;
-  }
-
-  out += ret;
-
-  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out);
-  if (sha1len == 0) {
-    return 0;
-  }
-
-  ret += sha1len;
-  return ret;
-}
-
-int ssl3_cert_verify_hash(SSL *ssl, const EVP_MD **out_md, uint8_t *out,
-                          size_t *out_len, uint16_t signature_algorithm) {
-  assert(ssl3_protocol_version(ssl) == SSL3_VERSION);
-
-  if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
-    if (ssl3_handshake_mac(ssl, NID_md5, NULL, 0, out) == 0 ||
-        ssl3_handshake_mac(ssl, NID_sha1, NULL, 0,
-                           out + MD5_DIGEST_LENGTH) == 0) {
-      return 0;
-    }
-    *out_md = EVP_md5_sha1();
-    *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH;
-  } else if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
-    if (ssl3_handshake_mac(ssl, NID_sha1, NULL, 0, out) == 0) {
-      return 0;
-    }
-    *out_md = EVP_sha1();
-    *out_len = SHA_DIGEST_LENGTH;
-  } else {
+int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript,
+                                         uint8_t *out, size_t *out_len,
+                                         const SSL_SESSION *session,
+                                         int signature_algorithm) {
+  if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  return 1;
+  if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
+    size_t md5_len, len;
+    if (!ssl3_handshake_mac(transcript, session, &transcript->md5, NULL, 0, out,
+                            &md5_len) ||
+        !ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0,
+                            out + md5_len, &len)) {
+      return 0;
+    }
+    *out_len = md5_len + len;
+    return 1;
+  }
+
+  if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
+    return ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0,
+                              out, out_len);
+  }
+
+  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  return 0;
 }
 
-const SSL3_ENC_METHOD SSLv3_enc_data = {
-    ssl3_prf,
-    ssl3_final_finish_mac,
-};
+int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out,
+                              size_t *out_len, const SSL_SESSION *session,
+                              int from_server, uint16_t version) {
+  if (version == SSL3_VERSION) {
+    if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
+    }
+
+    const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
+                                     : SSL3_MD_CLIENT_FINISHED_CONST;
+    const size_t sender_len = 4;
+    size_t md5_len, len;
+    if (!ssl3_handshake_mac(transcript, session, &transcript->md5, sender,
+                            sender_len, out, &md5_len) ||
+        !ssl3_handshake_mac(transcript, session, &transcript->hash, sender,
+                            sender_len, out + md5_len, &len)) {
+      return 0;
+    }
+
+    *out_len = md5_len + len;
+    return 1;
+  }
+
+  /* At this point, the handshake should have released the handshake buffer on
+   * its own. */
+  assert(transcript->buffer == NULL);
+
+  const char *label = TLS_MD_CLIENT_FINISH_CONST;
+  size_t label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
+  if (from_server) {
+    label = TLS_MD_SERVER_FINISH_CONST;
+    label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
+  }
+
+  uint8_t digests[EVP_MAX_MD_SIZE];
+  size_t digests_len;
+  if (!SSL_TRANSCRIPT_get_hash(transcript, digests, &digests_len)) {
+    return 0;
+  }
+
+  static const size_t kFinishedLen = 12;
+  if (!tls1_prf(SSL_TRANSCRIPT_md(transcript), out, kFinishedLen,
+                session->master_key, session->master_key_length, label,
+                label_len, digests, digests_len, NULL, 0)) {
+    return 0;
+  }
+
+  *out_len = kFinishedLen;
+  return 1;
+}
diff --git a/src/ssl/ssl_x509.c b/src/ssl/ssl_x509.c
index 5d78deb..2955c21 100644
--- a/src/ssl/ssl_x509.c
+++ b/src/ssl/ssl_x509.c
@@ -140,8 +140,15 @@
 
 #include <openssl/ssl.h>
 
+#include <assert.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
 #include <openssl/stack.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/x509_vfy.h>
 
 #include "internal.h"
@@ -281,23 +288,6 @@
   X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 }
 
-static X509 *ssl_cert_get0_leaf(CERT *cert) {
-  if (cert->x509_leaf == NULL &&
-      !ssl_cert_cache_leaf_cert(cert)) {
-    return NULL;
-  }
-
-  return cert->x509_leaf;
-}
-
-X509 *SSL_get_certificate(const SSL *ssl) {
-  return ssl_cert_get0_leaf(ssl->cert);
-}
-
-X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  return ssl_cert_get0_leaf(ctx->cert);
-}
-
 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
   return X509_STORE_set_default_paths(ctx->cert_store);
 }
@@ -329,3 +319,497 @@
   X509_STORE_free(ctx->cert_store);
   ctx->cert_store = store;
 }
+
+static void ssl_crypto_x509_flush_cached_leaf(CERT *cert) {
+  X509_free(cert->x509_leaf);
+  cert->x509_leaf = NULL;
+}
+
+static void ssl_crypto_x509_flush_cached_chain(CERT *cert) {
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = NULL;
+}
+
+static void ssl_crypto_x509_clear(CERT *cert) {
+  ssl_crypto_x509_flush_cached_leaf(cert);
+  ssl_crypto_x509_flush_cached_chain(cert);
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = NULL;
+}
+
+static int ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) {
+  STACK_OF(X509) *chain = NULL;
+  const size_t num_certs = sk_CRYPTO_BUFFER_num(sess->certs);
+
+  if (num_certs > 0) {
+    chain = sk_X509_new_null();
+    if (chain == NULL) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
+
+  X509 *leaf = NULL;
+  for (size_t i = 0; i < num_certs; i++) {
+    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;
+    }
+    if (!sk_X509_push(chain, x509)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      X509_free(x509);
+      goto err;
+    }
+    if (i == 0) {
+      leaf = x509;
+    }
+  }
+
+  sk_X509_pop_free(sess->x509_chain, X509_free);
+  sess->x509_chain = chain;
+  sk_X509_pop_free(sess->x509_chain_without_leaf, X509_free);
+  sess->x509_chain_without_leaf = NULL;
+
+  X509_free(sess->x509_peer);
+  if (leaf != NULL) {
+    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,
+                                       const SSL_SESSION *session) {
+  if (session->x509_peer != NULL) {
+    X509_up_ref(session->x509_peer);
+    new_session->x509_peer = session->x509_peer;
+  }
+  if (session->x509_chain != NULL) {
+    new_session->x509_chain = X509_chain_up_ref(session->x509_chain);
+    if (new_session->x509_chain == NULL) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+static void ssl_crypto_x509_session_clear(SSL_SESSION *session) {
+  X509_free(session->x509_peer);
+  session->x509_peer = NULL;
+  sk_X509_pop_free(session->x509_chain, X509_free);
+  session->x509_chain = NULL;
+  sk_X509_pop_free(session->x509_chain_without_leaf, X509_free);
+  session->x509_chain_without_leaf = NULL;
+}
+
+const SSL_X509_METHOD ssl_crypto_x509_method = {
+  ssl_crypto_x509_clear,
+  ssl_crypto_x509_flush_cached_chain,
+  ssl_crypto_x509_flush_cached_leaf,
+  ssl_crypto_x509_session_cache_objects,
+  ssl_crypto_x509_session_dup,
+  ssl_crypto_x509_session_clear,
+};
+
+/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
+ * contents of |x509|. */
+static CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
+  uint8_t *buf = NULL;
+  int cert_len = i2d_X509(x509, &buf);
+  if (cert_len <= 0) {
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
+  OPENSSL_free(buf);
+
+  return buffer;
+}
+
+static int ssl_use_certificate(CERT *cert, X509 *x) {
+  if (x == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
+int SSL_use_certificate(SSL *ssl, X509 *x) {
+  return ssl_use_certificate(ssl->cert, x);
+}
+
+int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
+  return ssl_use_certificate(ctx->cert, x);
+}
+
+/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
+ * first element of |cert->chain|. */
+static int ssl_cert_cache_leaf_cert(CERT *cert) {
+  assert(cert->x509_method);
+
+  if (cert->x509_leaf != NULL ||
+      cert->chain == NULL) {
+    return 1;
+  }
+
+  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+  if (!leaf) {
+    return 1;
+  }
+
+  cert->x509_leaf = X509_parse_from_buffer(leaf);
+  return cert->x509_leaf != NULL;
+}
+
+static X509 *ssl_cert_get0_leaf(CERT *cert) {
+  if (cert->x509_leaf == NULL &&
+      !ssl_cert_cache_leaf_cert(cert)) {
+    return NULL;
+  }
+
+  return cert->x509_leaf;
+}
+
+X509 *SSL_get_certificate(const SSL *ssl) {
+  return ssl_cert_get0_leaf(ssl->cert);
+}
+
+X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
+  return ssl_cert_get0_leaf(ctx->cert);
+}
+
+/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
+static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
+  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
+  if (chain == NULL) {
+    return NULL;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
+    sk_CRYPTO_BUFFER_free(chain);
+    return NULL;
+  }
+
+  return chain;
+}
+
+/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
+ * forms of elements of |chain|. It returns one on success or zero on error, in
+ * which case no change to |cert->chain| is made. It preverses the existing
+ * leaf from |cert->chain|, if any. */
+static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
+
+  if (cert->chain != NULL) {
+    new_chain = sk_CRYPTO_BUFFER_new_null();
+    if (new_chain == NULL) {
+      return 0;
+    }
+
+    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
+      goto err;
+    }
+    /* |leaf| might be NULL if it's a “leafless” chain. */
+    if (leaf != NULL) {
+      CRYPTO_BUFFER_up_ref(leaf);
+    }
+  }
+
+  for (size_t i = 0; i < sk_X509_num(chain); i++) {
+    if (new_chain == NULL) {
+      new_chain = new_leafless_chain();
+      if (new_chain == NULL) {
+        goto err;
+      }
+    }
+
+    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      goto err;
+    }
+  }
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = new_chain;
+
+  return 1;
+
+err:
+  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
+  return 0;
+}
+
+static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  sk_X509_pop_free(chain, X509_free);
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
+  assert(cert->x509_method);
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  if (cert->chain != NULL) {
+    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      return 0;
+    }
+
+    return 1;
+  }
+
+  cert->chain = new_leafless_chain();
+  if (cert->chain == NULL ||
+      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    CRYPTO_BUFFER_free(buffer);
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
+  if (!ssl_cert_append_cert(cert, x509)) {
+    return 0;
+  }
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = x509;
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
+  if (!ssl_cert_append_cert(cert, x509)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+  return ssl_cert_set0_chain(ctx->cert, chain);
+}
+
+int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+  return ssl_cert_set1_chain(ctx->cert, chain);
+}
+
+int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
+  return ssl_cert_set0_chain(ssl->cert, chain);
+}
+
+int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
+  return ssl_cert_set1_chain(ssl->cert, chain);
+}
+
+int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return ssl_cert_add0_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return ssl_cert_add1_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return SSL_CTX_add0_chain_cert(ctx, x509);
+}
+
+int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
+  return ssl_cert_add0_chain_cert(ssl->cert, x509);
+}
+
+int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
+  return ssl_cert_add1_chain_cert(ssl->cert, x509);
+}
+
+int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
+  return SSL_CTX_set0_chain(ctx, NULL);
+}
+
+int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
+  return SSL_CTX_clear_chain_certs(ctx);
+}
+
+int SSL_clear_chain_certs(SSL *ssl) {
+  return SSL_set0_chain(ssl, NULL);
+}
+
+int ssl_auto_chain_if_needed(SSL *ssl) {
+  /* Only build a chain if there are no intermediates configured and the feature
+   * isn't disabled. */
+  if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
+      !ssl_has_certificate(ssl) ||
+      ssl->cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
+    return 1;
+  }
+
+  X509 *leaf =
+      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
+  if (!leaf) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
+  X509_STORE_CTX ctx;
+  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
+    X509_free(leaf);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
+  /* Attempt to build a chain, ignoring the result. */
+  X509_verify_cert(&ctx);
+  X509_free(leaf);
+  ERR_clear_error();
+
+  /* Remove the leaf from the generated chain. */
+  X509_free(sk_X509_shift(ctx.chain));
+
+  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
+  X509_STORE_CTX_cleanup(&ctx);
+  if (!ok) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(ssl->cert);
+
+  return 1;
+}
+
+/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
+ * |cert->chain|. */
+static int ssl_cert_cache_chain_certs(CERT *cert) {
+  assert(cert->x509_method);
+
+  if (cert->x509_chain != NULL ||
+      cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
+    return 1;
+  }
+
+  STACK_OF(X509) *chain = sk_X509_new_null();
+  if (chain == NULL) {
+    return 0;
+  }
+
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
+    X509 *x509 = X509_parse_from_buffer(buffer);
+    if (x509 == NULL ||
+        !sk_X509_push(chain, x509)) {
+      X509_free(x509);
+      goto err;
+    }
+  }
+
+  cert->x509_chain = chain;
+  return 1;
+
+err:
+  sk_X509_pop_free(chain, X509_free);
+  return 0;
+}
+
+int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
+  *out_chain = ctx->cert->x509_chain;
+  return 1;
+}
+
+int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
+                                  STACK_OF(X509) **out_chain) {
+  return SSL_CTX_get0_chain_certs(ctx, out_chain);
+}
+
+int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
+  *out_chain = ssl->cert->x509_chain;
+  return 1;
+}
+
+static SSL_SESSION *ssl_session_new_with_crypto_x509(void) {
+  return ssl_session_new(&ssl_crypto_x509_method);
+}
+
+SSL_SESSION *d2i_SSL_SESSION_bio(BIO *bio, SSL_SESSION **out) {
+  return ASN1_d2i_bio_of(SSL_SESSION, ssl_session_new_with_crypto_x509,
+                         d2i_SSL_SESSION, bio, out);
+}
+
+int i2d_SSL_SESSION_bio(BIO *bio, const SSL_SESSION *session) {
+  return ASN1_i2d_bio_of(SSL_SESSION, i2d_SSL_SESSION, bio, session);
+}
+
+IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
+
+SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) {
+  if (length < 0) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return NULL;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, *pp, length);
+
+  SSL_SESSION *ret = SSL_SESSION_parse(&cbs, &ssl_crypto_x509_method,
+                                       NULL /* no buffer pool */);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  if (a) {
+    SSL_SESSION_free(*a);
+    *a = ret;
+  }
+  *pp = CBS_data(&cbs);
+  return ret;
+}
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 5e5c348..d01992e 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -224,18 +224,17 @@
   return ret;
 }
 
-static int tls1_prf(const SSL *ssl, uint8_t *out, size_t out_len,
-                    const uint8_t *secret, size_t secret_len, const char *label,
-                    size_t label_len, const uint8_t *seed1, size_t seed1_len,
-                    const uint8_t *seed2, size_t seed2_len) {
+int tls1_prf(const EVP_MD *digest, uint8_t *out, size_t out_len,
+             const uint8_t *secret, size_t secret_len, const char *label,
+             size_t label_len, const uint8_t *seed1, size_t seed1_len,
+             const uint8_t *seed2, size_t seed2_len) {
   if (out_len == 0) {
     return 1;
   }
 
   OPENSSL_memset(out, 0, out_len);
 
-  uint32_t algorithm_prf = ssl_get_algorithm_prf(ssl);
-  if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) {
+  if (digest == EVP_md5_sha1()) {
     /* If using the MD5/SHA1 PRF, |secret| is partitioned between SHA-1 and
      * MD5, MD5 first. */
     size_t secret_half = secret_len - (secret_len / 2);
@@ -248,17 +247,82 @@
     /* Note that, if |secret_len| is odd, the two halves share a byte. */
     secret = secret + (secret_len - secret_half);
     secret_len = secret_half;
+
+    digest = EVP_sha1();
   }
 
-  if (!tls1_P_hash(out, out_len, ssl_get_handshake_digest(algorithm_prf),
-                   secret, secret_len, (const uint8_t *)label, label_len,
-                   seed1, seed1_len, seed2, seed2_len)) {
+  if (!tls1_P_hash(out, out_len, digest, secret, secret_len,
+                   (const uint8_t *)label, label_len, seed1, seed1_len, seed2,
+                   seed2_len)) {
     return 0;
   }
 
   return 1;
 }
 
+static int ssl3_prf(uint8_t *out, size_t out_len, const uint8_t *secret,
+                    size_t secret_len, const char *label, size_t label_len,
+                    const uint8_t *seed1, size_t seed1_len,
+                    const uint8_t *seed2, size_t seed2_len) {
+  EVP_MD_CTX md5;
+  EVP_MD_CTX sha1;
+  uint8_t buf[16], smd[SHA_DIGEST_LENGTH];
+  uint8_t c = 'A';
+  size_t i, j, k;
+
+  k = 0;
+  EVP_MD_CTX_init(&md5);
+  EVP_MD_CTX_init(&sha1);
+  for (i = 0; i < out_len; i += MD5_DIGEST_LENGTH) {
+    k++;
+    if (k > sizeof(buf)) {
+      /* bug: 'buf' is too small for this ciphersuite */
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
+    }
+
+    for (j = 0; j < k; j++) {
+      buf[j] = c;
+    }
+    c++;
+    if (!EVP_DigestInit_ex(&sha1, EVP_sha1(), NULL)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
+      return 0;
+    }
+    EVP_DigestUpdate(&sha1, buf, k);
+    EVP_DigestUpdate(&sha1, secret, secret_len);
+    /* |label| is ignored for SSLv3. */
+    if (seed1_len) {
+      EVP_DigestUpdate(&sha1, seed1, seed1_len);
+    }
+    if (seed2_len) {
+      EVP_DigestUpdate(&sha1, seed2, seed2_len);
+    }
+    EVP_DigestFinal_ex(&sha1, smd, NULL);
+
+    if (!EVP_DigestInit_ex(&md5, EVP_md5(), NULL)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
+      return 0;
+    }
+    EVP_DigestUpdate(&md5, secret, secret_len);
+    EVP_DigestUpdate(&md5, smd, SHA_DIGEST_LENGTH);
+    if (i + MD5_DIGEST_LENGTH > out_len) {
+      EVP_DigestFinal_ex(&md5, smd, NULL);
+      OPENSSL_memcpy(out, smd, out_len - i);
+    } else {
+      EVP_DigestFinal_ex(&md5, out, NULL);
+    }
+
+    out += MD5_DIGEST_LENGTH;
+  }
+
+  OPENSSL_cleanse(smd, SHA_DIGEST_LENGTH);
+  EVP_MD_CTX_cleanup(&md5);
+  EVP_MD_CTX_cleanup(&sha1);
+
+  return 1;
+}
+
 static int tls1_setup_key_block(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (hs->key_block_len != 0) {
@@ -385,112 +449,58 @@
 }
 
 int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
-  return ssl->s3->enc_method->prf(
-      ssl, out, out_len, SSL_get_session(ssl)->master_key,
-      SSL_get_session(ssl)->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
-      TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random, SSL3_RANDOM_SIZE,
-      ssl->s3->client_random, SSL3_RANDOM_SIZE);
-}
-
-static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len,
-                         size_t max_out) {
-  int ret = 0;
-  EVP_MD_CTX ctx_copy;
-  EVP_MD_CTX_init(&ctx_copy);
-
-  if (EVP_MD_CTX_size(ctx) > max_out) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-    goto err;
-  }
-  unsigned len;
-  if (!EVP_MD_CTX_copy_ex(&ctx_copy, ctx) ||
-      !EVP_DigestFinal_ex(&ctx_copy, out, &len)) {
-    goto err;
-  }
-  assert(len == EVP_MD_CTX_size(ctx));
-
-  *out_len = len;
-  ret = 1;
-
-err:
-  EVP_MD_CTX_cleanup(&ctx_copy);
-  return ret;
-}
-
-/* tls1_handshake_digest calculates the current handshake hash and writes it to
- * |out|, which has space for |out_len| bytes. It returns the number of bytes
- * written or -1 in the event of an error. This function works on a copy of the
- * underlying digests so can be called multiple times and prior to the final
- * update etc. */
-int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len) {
-  size_t md5_len = 0;
-  if (EVP_MD_CTX_md(&ssl->s3->handshake_md5) != NULL &&
-      !append_digest(&ssl->s3->handshake_md5, out, &md5_len, out_len)) {
-    return -1;
+  if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+    return ssl3_prf(out, out_len, SSL_get_session(ssl)->master_key,
+                    SSL_get_session(ssl)->master_key_length,
+                    TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE,
+                    ssl->s3->server_random, SSL3_RANDOM_SIZE,
+                    ssl->s3->client_random, SSL3_RANDOM_SIZE);
   }
 
-  size_t len;
-  if (!append_digest(&ssl->s3->handshake_hash, out + md5_len, &len,
-                     out_len - md5_len)) {
-    return -1;
-  }
-
-  return (int)(md5_len + len);
-}
-
-static int tls1_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
-  /* At this point, the handshake should have released the handshake buffer on
-   * its own. */
-  assert(ssl->s3->handshake_buffer == NULL);
-
-  const char *label = TLS_MD_CLIENT_FINISH_CONST;
-  size_t label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
-  if (from_server) {
-    label = TLS_MD_SERVER_FINISH_CONST;
-    label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
-  }
-
-  uint8_t buf[EVP_MAX_MD_SIZE];
-  int digests_len = tls1_handshake_digest(ssl, buf, sizeof(buf));
-  if (digests_len < 0) {
+  const EVP_MD *digest = ssl_get_handshake_digest(
+      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
-
-  static const size_t kFinishedLen = 12;
-  if (!ssl->s3->enc_method->prf(ssl, out, kFinishedLen,
-                                SSL_get_session(ssl)->master_key,
-                                SSL_get_session(ssl)->master_key_length, label,
-                                label_len, buf, digests_len, NULL, 0)) {
-    return 0;
-  }
-
-  return (int)kFinishedLen;
+  return tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key,
+                  SSL_get_session(ssl)->master_key_length,
+                  TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE,
+                  ssl->s3->server_random, SSL3_RANDOM_SIZE,
+                  ssl->s3->client_random, SSL3_RANDOM_SIZE);
 }
 
-int tls1_generate_master_secret(SSL *ssl, uint8_t *out,
+int tls1_generate_master_secret(SSL_HANDSHAKE *hs, uint8_t *out,
                                 const uint8_t *premaster,
                                 size_t premaster_len) {
+  const SSL *ssl = hs->ssl;
   if (ssl->s3->tmp.extended_master_secret) {
     uint8_t digests[EVP_MAX_MD_SIZE];
-    int digests_len = tls1_handshake_digest(ssl, digests, sizeof(digests));
-    if (digests_len == -1) {
-      return 0;
-    }
-
-    if (!ssl->s3->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                                  premaster_len,
-                                  TLS_MD_EXTENDED_MASTER_SECRET_CONST,
-                                  TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE,
-                                  digests, digests_len, NULL, 0)) {
+    size_t digests_len;
+    if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, digests, &digests_len) ||
+        !tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out,
+                  SSL3_MASTER_SECRET_SIZE, premaster, premaster_len,
+                  TLS_MD_EXTENDED_MASTER_SECRET_CONST,
+                  TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests,
+                  digests_len, NULL, 0)) {
       return 0;
     }
   } else {
-    if (!ssl->s3->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                                  premaster_len, TLS_MD_MASTER_SECRET_CONST,
-                                  TLS_MD_MASTER_SECRET_CONST_SIZE,
-                                  ssl->s3->client_random, SSL3_RANDOM_SIZE,
-                                  ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
-      return 0;
+    if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+      if (!ssl3_prf(out, SSL3_MASTER_SECRET_SIZE, premaster, premaster_len,
+                    TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE,
+                    ssl->s3->client_random, SSL3_RANDOM_SIZE,
+                    ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
+        return 0;
+      }
+    } else {
+      if (!tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out,
+                    SSL3_MASTER_SECRET_SIZE, premaster, premaster_len,
+                    TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE,
+                    ssl->s3->client_random, SSL3_RANDOM_SIZE,
+                    ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
+        return 0;
+      }
     }
   }
 
@@ -538,16 +548,15 @@
     OPENSSL_memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len);
   }
 
-  int ret =
-      ssl->s3->enc_method->prf(ssl, out, out_len,
-                               SSL_get_session(ssl)->master_key,
-                               SSL_get_session(ssl)->master_key_length, label,
-                               label_len, seed, seed_len, NULL, 0);
+  const EVP_MD *digest = ssl_get_handshake_digest(
+      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+  int ret = tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key,
+                     SSL_get_session(ssl)->master_key_length, label, label_len,
+                     seed, seed_len, NULL, 0);
   OPENSSL_free(seed);
   return ret;
 }
-
-const SSL3_ENC_METHOD TLSv1_enc_data = {
-    tls1_prf,
-    tls1_final_finish_mac,
-};
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 8091787..7723ccd 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -1371,15 +1371,16 @@
   /* The extension shouldn't be sent when resuming sessions. */
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
       ssl->s3->session_reused ||
-      ssl->ctx->signed_cert_timestamp_list_length == 0) {
+      ssl->signed_cert_timestamp_list == NULL) {
     return 1;
   }
 
   CBB contents;
   return CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) &&
          CBB_add_u16_length_prefixed(out, &contents) &&
-         CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
-                       ssl->ctx->signed_cert_timestamp_list_length) &&
+         CBB_add_bytes(&contents,
+                       CRYPTO_BUFFER_data(ssl->signed_cert_timestamp_list),
+                       CRYPTO_BUFFER_len(ssl->signed_cert_timestamp_list)) &&
          CBB_flush(out);
 }
 
@@ -1882,8 +1883,12 @@
     return 0;
   }
 
-  const EVP_MD *digest =
-      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
   size_t binder_len = EVP_MD_size(digest);
   return 15 + ssl->session->tlsext_ticklen + binder_len;
 }
@@ -1911,8 +1916,13 @@
   /* Fill in a placeholder zero binder of the appropriate length. It will be
    * computed and filled in later after length prefixes are computed. */
   uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
-  const EVP_MD *digest =
-      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+
+  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
   size_t binder_len = EVP_MD_size(digest);
 
   CBB contents, identity, ticket, binders, binder;
@@ -2961,7 +2971,7 @@
 int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
                                  const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
-  int alert = -1;
+  int alert = SSL_AD_DECODE_ERROR;
   if (ssl_scan_clienthello_tlsext(hs, client_hello, &alert) <= 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
@@ -3084,7 +3094,7 @@
 
 int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
   SSL *const ssl = hs->ssl;
-  int alert = -1;
+  int alert = SSL_AD_DECODE_ERROR;
   if (ssl_scan_serverhello_tlsext(hs, cbs, &alert) <= 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
@@ -3203,7 +3213,8 @@
 #endif
 
   /* Decode the session. */
-  SSL_SESSION *session = SSL_SESSION_from_bytes(plaintext, plaintext_len);
+  SSL_SESSION *session =
+      SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx);
   if (session == NULL) {
     ERR_clear_error(); /* Don't leave an error on the queue. */
     goto done;
@@ -3324,7 +3335,8 @@
   return 0;
 }
 
-int tls1_verify_channel_id(SSL *ssl) {
+int tls1_verify_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = 0;
   uint16_t extension_type;
   CBS extension, channel_id;
@@ -3383,7 +3395,7 @@
 
   uint8_t digest[EVP_MAX_MD_SIZE];
   size_t digest_len;
-  if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+  if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
     goto err;
   }
 
@@ -3412,10 +3424,11 @@
   return ret;
 }
 
-int tls1_write_channel_id(SSL *ssl, CBB *cbb) {
+int tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) {
+  SSL *const ssl = hs->ssl;
   uint8_t digest[EVP_MAX_MD_SIZE];
   size_t digest_len;
-  if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+  if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
     return 0;
   }
 
@@ -3461,11 +3474,12 @@
   return ret;
 }
 
-int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len) {
+  SSL *const ssl = hs->ssl;
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     uint8_t *msg;
     size_t msg_len;
-    if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+    if (!tls13_get_cert_verify_signature_input(hs, &msg, &msg_len,
                                                ssl_cert_verify_channel_id)) {
       return 0;
     }
@@ -3492,13 +3506,12 @@
                   ssl->session->original_handshake_hash_len);
   }
 
-  uint8_t handshake_hash[EVP_MAX_MD_SIZE];
-  int handshake_hash_len = tls1_handshake_digest(ssl, handshake_hash,
-                                                 sizeof(handshake_hash));
-  if (handshake_hash_len < 0) {
+  uint8_t hs_hash[EVP_MAX_MD_SIZE];
+  size_t hs_hash_len;
+  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, hs_hash, &hs_hash_len)) {
     return 0;
   }
-  SHA256_Update(&ctx, handshake_hash, (size_t)handshake_hash_len);
+  SHA256_Update(&ctx, hs_hash, (size_t)hs_hash_len);
   SHA256_Final(out, &ctx);
   *out_len = SHA256_DIGEST_LENGTH;
   return 1;
@@ -3507,8 +3520,8 @@
 /* tls1_record_handshake_hashes_for_channel_id records the current handshake
  * hashes in |ssl->s3->new_session| so that Channel ID resumptions can sign that
  * data. */
-int tls1_record_handshake_hashes_for_channel_id(SSL *ssl) {
-  int digest_len;
+int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* This function should never be called for a resumed session because the
    * handshake hashes that we wish to record are for the original, full
    * handshake. */
@@ -3516,15 +3529,18 @@
     return -1;
   }
 
-  digest_len =
-      tls1_handshake_digest(
-          ssl, ssl->s3->new_session->original_handshake_hash,
-          sizeof(ssl->s3->new_session->original_handshake_hash));
-  if (digest_len < 0) {
+  OPENSSL_COMPILE_ASSERT(
+      sizeof(ssl->s3->new_session->original_handshake_hash) == EVP_MAX_MD_SIZE,
+      original_handshake_hash_is_too_small);
+
+  size_t digest_len;
+  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript,
+                               ssl->s3->new_session->original_handshake_hash,
+                               &digest_len)) {
     return -1;
   }
 
-  assert(sizeof(ssl->s3->new_session->original_handshake_hash) < 256);
+  OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE <= 0xff, max_md_size_is_too_large);
   ssl->s3->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 66a71a0..381f4c2 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1477,7 +1477,7 @@
   }
 
   if (expected_sha256_client_cert &&
-      SSL_get_session(ssl)->x509_peer != nullptr) {
+      SSL_get_session(ssl)->certs != nullptr) {
     fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
             is_resume);
     return false;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 668f78c..2e7b053 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -668,9 +668,16 @@
 	// ticket before sending it in a resume handshake.
 	FilterTicket func([]byte) ([]byte, error)
 
-	// OversizedSessionId causes the session id that is sent with a ticket
-	// resumption attempt to be too large (33 bytes).
-	OversizedSessionId bool
+	// TicketSessionIDLength, if non-zero, is the length of the session ID
+	// to send with a ticket resumption offer.
+	TicketSessionIDLength int
+
+	// EmptyTicketSessionID, if true, causes the client to send an empty
+	// session ID with a ticket resumption offer. For simplicity, this will
+	// also cause the client to interpret a ServerHello with empty session
+	// ID as a resumption. (A client which sends empty session ID is
+	// normally expected to look ahead for ChangeCipherSpec.)
+	EmptyTicketSessionID bool
 
 	// ExpectNoTLS12Session, if true, causes the server to fail the
 	// connection if either a session ID or TLS 1.2 ticket is offered.
@@ -1291,6 +1298,10 @@
 	// SendTicketLifetime, if non-zero, is the ticket lifetime to send in
 	// NewSessionTicket messages.
 	SendTicketLifetime time.Duration
+
+	// SendServerNameAck, if true, causes the server to acknowledge the SNI
+	// extension.
+	SendServerNameAck bool
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index afc1958..162302e 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -289,8 +289,11 @@
 				// server accepted the ticket and is resuming a session
 				// (see RFC 5077).
 				sessionIdLen := 16
-				if c.config.Bugs.OversizedSessionId {
-					sessionIdLen = 33
+				if c.config.Bugs.TicketSessionIDLength != 0 {
+					sessionIdLen = c.config.Bugs.TicketSessionIDLength
+				}
+				if c.config.Bugs.EmptyTicketSessionID {
+					sessionIdLen = 0
 				}
 				hello.sessionId = make([]byte, sessionIdLen)
 				if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil {
@@ -860,6 +863,10 @@
 	hs.finishedHash.addEntropy(zeroSecret)
 	clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel)
 	serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel)
+	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
+
+	// Switch to application data keys on read. In particular, any alerts
+	// from the client certificate are read over these keys.
 	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
 	// If we're expecting 0.5-RTT messages from the server, read them
@@ -966,7 +973,6 @@
 	// Switch to application data keys.
 	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
 
-	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 	return nil
 }
@@ -1346,6 +1352,10 @@
 func (hs *clientHandshakeState) serverResumedSession() bool {
 	// If the server responded with the same sessionId then it means the
 	// sessionTicket is being used to resume a TLS session.
+	//
+	// Note that, if hs.hello.sessionId is a non-nil empty array, this will
+	// accept an empty session ID from the server as resumption. See
+	// EmptyTicketSessionID.
 	return hs.session != nil && hs.hello.sessionId != nil &&
 		bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
 }
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 1141797..4ecb3cb 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -1082,6 +1082,7 @@
 	hasEarlyData            bool
 	keyShare                keyShareEntry
 	supportedPoints         []uint8
+	serverNameAck           bool
 }
 
 func (m *serverExtensions) marshal(extensions *byteBuilder) {
@@ -1187,6 +1188,10 @@
 		extensions.addU16(extensionEarlyData)
 		extensions.addBytes([]byte{0, 0})
 	}
+	if m.serverNameAck {
+		extensions.addU16(extensionServerName)
+		extensions.addU16(0) // zero length
+	}
 }
 
 func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
@@ -1281,7 +1286,7 @@
 			if length != 0 {
 				return false
 			}
-			// Ignore this extension from the server.
+			m.serverNameAck = true
 		case extensionSupportedPoints:
 			// supported_points is illegal in TLS 1.3.
 			if version >= VersionTLS13 {
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index d8ab1a5..8aa1587 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -1244,6 +1244,8 @@
 		return errors.New("tls: no GREASE extension found")
 	}
 
+	serverExtensions.serverNameAck = c.config.Bugs.SendServerNameAck
+
 	return nil
 }
 
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 8c9278e..d6e984a 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2290,7 +2290,16 @@
 			expectedError: ":WRONG_VERSION_NUMBER:",
 		},
 		{
-			name: "KeyUpdate",
+			name: "KeyUpdate-Client",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			sendKeyUpdates:   1,
+			keyUpdateRequest: keyUpdateNotRequested,
+		},
+		{
+			testType: serverTest,
+			name:     "KeyUpdate-Server",
 			config: Config{
 				MaxVersion: VersionTLS13,
 			},
@@ -2514,11 +2523,6 @@
 		// NULL ciphers must be explicitly enabled.
 		flags = append(flags, "-cipher", "DEFAULT:NULL-SHA")
 	}
-	if hasComponent(suite.name, "ECDHE-PSK") && hasComponent(suite.name, "GCM") {
-		// ECDHE_PSK AES_GCM ciphers must be explicitly enabled
-		// for now.
-		flags = append(flags, "-cipher", suite.name)
-	}
 
 	var shouldServerFail, shouldClientFail bool
 	if hasComponent(suite.name, "ECDHE") && ver.version == VersionSSL30 {
@@ -2558,6 +2562,13 @@
 		sendCipherSuite = suite.id
 	}
 
+	// For cipher suites and versions where exporters are defined, verify
+	// that they interoperate.
+	var exportKeyingMaterial int
+	if ver.version > VersionSSL30 {
+		exportKeyingMaterial = 1024
+	}
+
 	testCases = append(testCases, testCase{
 		testType: serverTest,
 		protocol: protocol,
@@ -2573,12 +2584,13 @@
 				AdvertiseAllConfiguredCiphers: true,
 			},
 		},
-		certFile:      certFile,
-		keyFile:       keyFile,
-		flags:         flags,
-		resumeSession: true,
-		shouldFail:    shouldServerFail,
-		expectedError: expectedServerError,
+		certFile:             certFile,
+		keyFile:              keyFile,
+		flags:                flags,
+		resumeSession:        true,
+		shouldFail:           shouldServerFail,
+		expectedError:        expectedServerError,
+		exportKeyingMaterial: exportKeyingMaterial,
 	})
 
 	testCases = append(testCases, testCase{
@@ -2597,10 +2609,11 @@
 				SendCipherSuite:             sendCipherSuite,
 			},
 		},
-		flags:         flags,
-		resumeSession: true,
-		shouldFail:    shouldClientFail,
-		expectedError: expectedClientError,
+		flags:                flags,
+		resumeSession:        true,
+		shouldFail:           shouldClientFail,
+		expectedError:        expectedClientError,
+		exportKeyingMaterial: exportKeyingMaterial,
 	})
 
 	if shouldClientFail {
@@ -4751,6 +4764,31 @@
 			expectedLocalError: "tls: unexpected server name",
 		})
 		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "TolerateServerNameAck-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					SendServerNameAck: true,
+				},
+			},
+			flags:         []string{"-host-name", "example.com"},
+			resumeSession: true,
+		})
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "UnsolicitedServerNameAck-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				Bugs: ProtocolBugs{
+					SendServerNameAck: true,
+				},
+			},
+			shouldFail:         true,
+			expectedError:      ":UNEXPECTED_EXTENSION:",
+			expectedLocalError: "remote error: unsupported extension",
+		})
+		testCases = append(testCases, testCase{
 			testType: serverTest,
 			name:     "ServerNameExtensionServer-" + ver.name,
 			config: Config{
@@ -5015,19 +5053,53 @@
 			},
 		})
 
-		// Resume with an oversized session id.
+		// Resume with various lengths of ticket session id.
 		if ver.version < VersionTLS13 {
 			testCases = append(testCases, testCase{
 				testType: serverTest,
-				name:     "OversizedSessionId-" + ver.name,
+				name:     "TicketSessionIDLength-0-" + ver.name,
 				config: Config{
 					MaxVersion: ver.version,
 					Bugs: ProtocolBugs{
-						OversizedSessionId: true,
+						EmptyTicketSessionID: true,
+					},
+				},
+				resumeSession: true,
+			})
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "TicketSessionIDLength-16-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					Bugs: ProtocolBugs{
+						TicketSessionIDLength: 16,
+					},
+				},
+				resumeSession: true,
+			})
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "TicketSessionIDLength-32-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					Bugs: ProtocolBugs{
+						TicketSessionIDLength: 32,
+					},
+				},
+				resumeSession: true,
+			})
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "TicketSessionIDLength-33-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					Bugs: ProtocolBugs{
+						TicketSessionIDLength: 33,
 					},
 				},
 				resumeSession: true,
 				shouldFail:    true,
+				// The maximum session ID length is 32.
 				expectedError: ":DECODE_ERROR:",
 			})
 		}
@@ -8520,7 +8592,7 @@
 		flags: []string{
 			"-resumption-delay", "21",
 		},
-		resumeSession: true,
+		resumeSession:        true,
 		expectResumeRejected: true,
 	})
 }
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index a49ee14..19dd555 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -102,7 +102,7 @@
 }
 
 int tls13_get_cert_verify_signature_input(
-    SSL *ssl, uint8_t **out, size_t *out_len,
+    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len,
     enum ssl_cert_verify_context_t cert_verify_context) {
   CBB cbb;
   if (!CBB_init(&cbb, 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) {
@@ -140,7 +140,8 @@
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
+  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
+                               &context_hash_len) ||
       !CBB_add_bytes(&cbb, context_hash, context_hash_len) ||
       !CBB_finish(&cbb, out, out_len)) {
     goto err;
@@ -232,7 +233,7 @@
         {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct},
     };
 
-    uint8_t alert;
+    uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                               OPENSSL_ARRAY_SIZE(ext_types),
                               0 /* reject unknown */)) {
@@ -306,7 +307,7 @@
   ssl->s3->new_session->certs = certs;
   certs = NULL;
 
-  if (!ssl_session_x509_cache_objects(ssl->s3->new_session)) {
+  if (!ssl->ctx->x509_method->session_cache_objects(ssl->s3->new_session)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     goto err;
@@ -372,7 +373,7 @@
   ssl->s3->new_session->peer_signature_algorithm = signature_algorithm;
 
   if (!tls13_get_cert_verify_signature_input(
-          ssl, &msg, &msg_len,
+          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;
@@ -451,13 +452,13 @@
     goto err;
   }
 
-  if (hs->scts_requested &&
-      ssl->ctx->signed_cert_timestamp_list_length != 0) {
+  if (hs->scts_requested && ssl->signed_cert_timestamp_list != NULL) {
     CBB contents;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
         !CBB_add_u16_length_prefixed(&extensions, &contents) ||
-        !CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
-                       ssl->ctx->signed_cert_timestamp_list_length) ||
+        !CBB_add_bytes(&contents,
+                       CRYPTO_BUFFER_data(ssl->signed_cert_timestamp_list),
+                       CRYPTO_BUFFER_len(ssl->signed_cert_timestamp_list)) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto err;
@@ -536,7 +537,7 @@
   enum ssl_private_key_result_t sign_result;
   if (is_first_run) {
     if (!tls13_get_cert_verify_signature_input(
-            ssl, &msg, &msg_len,
+            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;
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 74d3646..50f7e5a 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -74,7 +74,7 @@
       {TLSEXT_TYPE_cookie, &have_cookie, &cookie},
   };
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             0 /* reject unknown */)) {
@@ -135,7 +135,7 @@
     hs->retry_group = group_id;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -207,7 +207,7 @@
       {TLSEXT_TYPE_short_header, &have_short_header, &short_header},
   };
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             0 /* reject unknown */)) {
@@ -261,7 +261,7 @@
 
     /* Resumption incorporates fresh key material, so refresh the timeout. */
     ssl_session_renew_timeout(ssl, ssl->s3->new_session,
-                              ssl->session_psk_dhe_timeout);
+                              ssl->initial_ctx->session_psk_dhe_timeout);
   } else if (!ssl_get_new_session(hs, 0)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
@@ -271,8 +271,6 @@
   ssl->s3->tmp.new_cipher = cipher;
 
   /* The PRF hash is now known. Set up the key schedule. */
-  size_t hash_len =
-      EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
   if (!tls13_init_key_schedule(hs)) {
     return ssl_hs_error;
   }
@@ -283,7 +281,7 @@
                                     ssl->s3->new_session->master_key_length)) {
       return ssl_hs_error;
     }
-  } else if (!tls13_advance_key_schedule(hs, kZeroes, hash_len)) {
+  } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) {
     return ssl_hs_error;
   }
 
@@ -297,6 +295,7 @@
   /* Resolve ECDHE and incorporate it into the secret. */
   uint8_t *dhe_secret;
   size_t dhe_secret_len;
+  alert = SSL_AD_DECODE_ERROR;
   if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &dhe_secret_len,
                                            &alert, &key_share)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -326,7 +325,7 @@
     ssl->s3->short_header = 1;
   }
 
-  if (!ssl_hash_current_message(ssl) ||
+  if (!ssl_hash_current_message(hs) ||
       !tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
                              hs->hash_len) ||
@@ -357,7 +356,7 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -392,7 +391,7 @@
     return ssl_hs_error;
   }
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   STACK_OF(X509_NAME) *ca_sk = ssl_parse_client_CA_list(ssl, &alert, &cbs);
   if (ca_sk == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -413,7 +412,7 @@
   sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
   hs->ca_names = ca_sk;
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -425,7 +424,7 @@
   SSL *const ssl = hs->ssl;
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, 0 /* certificate required */) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -438,7 +437,7 @@
   SSL *const ssl = hs->ssl;
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -450,7 +449,7 @@
   SSL *const ssl = hs->ssl;
   if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
-      !ssl_hash_current_message(ssl) ||
+      !ssl_hash_current_message(hs) ||
       /* Update the secret to the master secret and derive traffic keys. */
       !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
       !tls13_derive_application_secrets(hs)) {
@@ -535,7 +534,7 @@
 
     CBB cbb, body;
     if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
-        !tls1_write_channel_id(ssl, &body) ||
+        !tls1_write_channel_id(hs, &body) ||
         !ssl_add_message_cbb(ssl, &cbb)) {
       CBB_cleanup(&cbb);
       return ssl_hs_error;
@@ -616,9 +615,8 @@
 
 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);
+  SSL_SESSION *session = SSL_SESSION_dup(ssl->s3->established_session,
+                                         SSL_SESSION_INCLUDE_NONAUTH);
   if (session == NULL) {
     return 0;
   }
@@ -662,7 +660,7 @@
        &early_data_info},
   };
 
-  uint8_t alert;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             1 /* ignore unknown */)) {
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index ea9dce8..4d140e3 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -29,28 +29,25 @@
 
 
 int tls13_init_key_schedule(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(hs->ssl),
+                                hs->ssl->s3->tmp.new_cipher->algorithm_prf)) {
+    return 0;
+  }
 
-  hs->hash_len = EVP_MD_size(digest);
+
+  hs->hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript);
 
   /* Initialize the secret to the zero key. */
   OPENSSL_memset(hs->secret, 0, hs->hash_len);
 
-  /* Initialize the rolling hashes and release the handshake buffer. */
-  if (!ssl3_init_handshake_hash(ssl)) {
-    return 0;
-  }
-  ssl3_free_handshake_buffer(ssl);
+  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   return 1;
 }
 
 int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
                                size_t len) {
-  const EVP_MD *digest =
-      ssl_get_handshake_digest(ssl_get_algorithm_prf(hs->ssl));
-
-  return HKDF_extract(hs->secret, &hs->hash_len, digest, in, len, hs->secret,
+  return HKDF_extract(hs->secret, &hs->hash_len,
+                      SSL_TRANSCRIPT_md(&hs->transcript), in, len, hs->secret,
                       hs->hash_len);
 }
 
@@ -83,35 +80,21 @@
   return ret;
 }
 
-int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
-  EVP_MD_CTX ctx;
-  EVP_MD_CTX_init(&ctx);
-  unsigned handshake_len = 0;
-  int ok = EVP_MD_CTX_copy_ex(&ctx, &ssl->s3->handshake_hash) &&
-           EVP_DigestFinal_ex(&ctx, out, &handshake_len);
-  EVP_MD_CTX_cleanup(&ctx);
-  if (ok) {
-    *out_len = handshake_len;
-  }
-  return ok;
-}
-
 /* derive_secret derives a secret of length |len| and writes the result in |out|
  * with the given label and the current base secret and most recently-saved
  * handshake context. It returns one on success and zero on error. */
 static int derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len,
                          const uint8_t *label, size_t label_len) {
-  SSL *const ssl = hs->ssl;
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
-
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len)) {
+  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
+                               &context_hash_len)) {
     return 0;
   }
 
-  return hkdf_expand_label(out, digest, hs->secret, hs->hash_len, label,
-                           label_len, context_hash, context_hash_len, len);
+  return hkdf_expand_label(out, SSL_TRANSCRIPT_md(&hs->transcript), hs->secret,
+                           hs->hash_len, label, label_len, context_hash,
+                           context_hash_len, len);
 }
 
 int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
@@ -124,7 +107,6 @@
 
   /* Look up cipher suite properties. */
   const EVP_AEAD *aead;
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
   size_t discard;
   if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard,
                                SSL_get_session(ssl)->cipher,
@@ -132,6 +114,9 @@
     return 0;
   }
 
+  const EVP_MD *digest = ssl_get_handshake_digest(
+      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
+
   /* Derive the key. */
   size_t key_len = EVP_AEAD_key_length(aead);
   uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
@@ -226,7 +211,8 @@
     "application traffic secret";
 
 int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+  const EVP_MD *digest = ssl_get_handshake_digest(
+      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
 
   uint8_t *secret;
   size_t secret_len;
@@ -287,7 +273,6 @@
 int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
                        int is_server) {
   SSL *const ssl = hs->ssl;
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
   const uint8_t *traffic_secret;
   if (is_server == ssl->server) {
@@ -298,9 +283,11 @@
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
-      !tls13_verify_data(digest, out, out_len, traffic_secret, hs->hash_len,
-                         context_hash, context_hash_len)) {
+  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
+                               &context_hash_len) ||
+      !tls13_verify_data(SSL_TRANSCRIPT_md(&hs->transcript), out, out_len,
+                         traffic_secret, hs->hash_len, context_hash,
+                         context_hash_len)) {
     return 0;
   }
   return 1;
@@ -310,7 +297,8 @@
                                  const char *label, size_t label_len,
                                  const uint8_t *context, size_t context_len,
                                  int use_context) {
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+  const EVP_MD *digest = ssl_get_handshake_digest(
+      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
 
   const uint8_t *hash = NULL;
   size_t hash_len = 0;
@@ -325,8 +313,8 @@
 
 static const char kTLS13LabelPSKBinder[] = "resumption psk binder key";
 
-static int tls13_psk_binder(SSL *ssl, uint8_t *out, const EVP_MD *digest,
-                            uint8_t *psk, size_t psk_len, uint8_t *context,
+static int tls13_psk_binder(uint8_t *out, const EVP_MD *digest, uint8_t *psk,
+                            size_t psk_len, uint8_t *context,
                             size_t context_len, size_t hash_len) {
   uint8_t binder_context[EVP_MAX_MD_SIZE];
   unsigned binder_context_len;
@@ -355,9 +343,13 @@
   return 1;
 }
 
-int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len) {
-  const EVP_MD *digest =
-      ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
+  SSL *const ssl = hs->ssl;
+  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
+  if (digest == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
   size_t hash_len = EVP_MD_size(digest);
 
   if (len < hash_len + 3) {
@@ -370,8 +362,8 @@
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
   if (!EVP_DigestInit_ex(&ctx, digest, NULL) ||
-      !EVP_DigestUpdate(&ctx, ssl->s3->handshake_buffer->data,
-                        ssl->s3->handshake_buffer->length) ||
+      !EVP_DigestUpdate(&ctx, hs->transcript.buffer->data,
+                        hs->transcript.buffer->length) ||
       !EVP_DigestUpdate(&ctx, msg, len - hash_len - 3) ||
       !EVP_DigestFinal_ex(&ctx, context, &context_len)) {
     EVP_MD_CTX_cleanup(&ctx);
@@ -381,9 +373,9 @@
   EVP_MD_CTX_cleanup(&ctx);
 
   uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
-  if (!tls13_psk_binder(ssl, verify_data, digest, ssl->session->master_key,
-                        ssl->session->master_key_length, context,
-                        context_len, hash_len)) {
+  if (!tls13_psk_binder(verify_data, digest, ssl->session->master_key,
+                        ssl->session->master_key_length, context, context_len,
+                        hash_len)) {
     return 0;
   }
 
@@ -391,16 +383,14 @@
   return 1;
 }
 
-int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session,
+int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
                             CBS *binders) {
-  const EVP_MD *digest =
-      ssl_get_handshake_digest(session->cipher->algorithm_prf);
-  size_t hash_len = EVP_MD_size(digest);
+  size_t hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript);
 
   /* Get the full ClientHello, including message header. It must be large enough
    * to exclude the binders. */
   CBS message;
-  ssl->method->get_current_message(ssl, &message);
+  hs->ssl->method->get_current_message(hs->ssl, &message);
   if (CBS_len(&message) < CBS_len(binders) + 2) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
@@ -411,23 +401,25 @@
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
   if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2,
-                  context, &context_len, digest, NULL)) {
+                  context, &context_len, SSL_TRANSCRIPT_md(&hs->transcript),
+                  NULL)) {
     return 0;
   }
 
   uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
   CBS binder;
-  if (!tls13_psk_binder(ssl, verify_data, digest, session->master_key,
-                        session->master_key_length, context, context_len,
-                        hash_len) ||
+  if (!tls13_psk_binder(verify_data, SSL_TRANSCRIPT_md(&hs->transcript),
+                        session->master_key, session->master_key_length,
+                        context, context_len, hash_len) ||
       /* We only consider the first PSK, so compare against the first binder. */
       !CBS_get_u8_length_prefixed(binders, &binder)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  int binder_ok = CBS_len(&binder) == hash_len &&
-                  CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
+  int binder_ok =
+      CBS_len(&binder) == hash_len &&
+      CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   binder_ok = 1;
 #endif
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 319fe4c..0278b50 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -157,6 +157,14 @@
     return ssl_hs_error;
   }
 
+  /* The PRF hash is now known. Set up the key schedule and hash the
+   * ClientHello. */
+  if (!tls13_init_key_schedule(hs) ||
+      !ssl_hash_current_message(hs)) {
+    return ssl_hs_error;
+  }
+
+
   /* Decode the ticket if we agree on a PSK key exchange mode. */
   uint8_t alert = SSL_AD_DECODE_ERROR;
   SSL_SESSION *session = NULL;
@@ -207,7 +215,7 @@
     }
   } else {
     /* Check the PSK binder. */
-    if (!tls13_verify_psk_binder(ssl, session, &binders)) {
+    if (!tls13_verify_psk_binder(hs, session, &binders)) {
       SSL_SESSION_free(session);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
       return ssl_hs_error;
@@ -224,7 +232,7 @@
 
     /* Resumption incorporates fresh key material, so refresh the timeout. */
     ssl_session_renew_timeout(ssl, ssl->s3->new_session,
-                              ssl->session_psk_dhe_timeout);
+                              ssl->initial_ctx->session_psk_dhe_timeout);
   }
 
   if (ssl->ctx->dos_protection_cb != NULL &&
@@ -237,27 +245,19 @@
 
   /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
    * deferred. Complete it now. */
+  alert = SSL_AD_DECODE_ERROR;
   if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  /* The PRF hash is now known. Set up the key schedule and hash the
-   * ClientHello. */
-  size_t hash_len =
-      EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
-  if (!tls13_init_key_schedule(hs) ||
-      !ssl_hash_current_message(ssl)) {
-    return ssl_hs_error;
-  }
-
   /* Incorporate the PSK into the running secret. */
   if (ssl->s3->session_reused) {
     if (!tls13_advance_key_schedule(hs, ssl->s3->new_session->master_key,
                                     ssl->s3->new_session->master_key_length)) {
       return ssl_hs_error;
     }
-  } else if (!tls13_advance_key_schedule(hs, kZeroes, hash_len)) {
+  } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) {
     return ssl_hs_error;
   }
 
@@ -322,7 +322,7 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_hash_current_message(ssl)) {
+  if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -484,7 +484,7 @@
 
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, allow_anonymous) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -495,7 +495,7 @@
 static enum ssl_hs_wait_t do_process_client_certificate_verify(
     SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (ssl->s3->new_session->x509_peer == NULL) {
+  if (sk_CRYPTO_BUFFER_num(ssl->s3->new_session->certs) == 0) {
     /* Skip this state. */
     hs->tls13_state = state_process_channel_id;
     return ssl_hs_ok;
@@ -503,7 +503,7 @@
 
   if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
-      !ssl_hash_current_message(ssl)) {
+      !ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -512,15 +512,14 @@
 }
 
 static enum ssl_hs_wait_t do_process_channel_id(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->s3->tlsext_channel_id_valid) {
+  if (!hs->ssl->s3->tlsext_channel_id_valid) {
     hs->tls13_state = state_process_client_finished;
     return ssl_hs_ok;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
-      !tls1_verify_channel_id(ssl) ||
-      !ssl_hash_current_message(ssl)) {
+  if (!ssl_check_message_type(hs->ssl, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(hs) ||
+      !ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
@@ -532,7 +531,7 @@
   SSL *const ssl = hs->ssl;
   if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
-      !ssl_hash_current_message(ssl) ||
+      !ssl_hash_current_message(hs) ||
       /* evp_aead_seal keys have already been switched. */
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
                              hs->hash_len) ||
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 70683e4..eaad2ca 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -161,6 +161,7 @@
   static const SSL_METHOD kMethod = {
       0,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -175,6 +176,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_2_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -183,6 +185,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_1_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -191,6 +194,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -199,6 +203,7 @@
   static const SSL_METHOD kMethod = {
       SSL3_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -252,3 +257,24 @@
 const SSL_METHOD *TLS_client_method(void) {
   return TLS_method();
 }
+
+static void ssl_noop_x509_clear(CERT *cert) {}
+static void ssl_noop_x509_flush_cached_leaf(CERT *cert) {}
+static void ssl_noop_x509_flush_cached_chain(CERT *cert) {}
+static int ssl_noop_x509_session_cache_objects(SSL_SESSION *sess) {
+  return 1;
+}
+static int ssl_noop_x509_session_dup(SSL_SESSION *new_session,
+                                       const SSL_SESSION *session) {
+  return 1;
+}
+static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
+
+const SSL_X509_METHOD ssl_noop_x509_method = {
+  ssl_noop_x509_clear,
+  ssl_noop_x509_flush_cached_chain,
+  ssl_noop_x509_flush_cached_leaf,
+  ssl_noop_x509_session_cache_objects,
+  ssl_noop_x509_session_dup,
+  ssl_noop_x509_session_clear,
+};
diff --git a/src/tool/client.cc b/src/tool/client.cc
index 0301a81..8e0ef2d 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -282,16 +282,16 @@
     return false;
   }
 
-  if (args_map.count("-max-version") != 0) {
-    uint16_t version;
-    if (!VersionFromString(&version, args_map["-max-version"])) {
-      fprintf(stderr, "Unknown protocol version: '%s'\n",
-              args_map["-max-version"].c_str());
-      return false;
-    }
-    if (!SSL_CTX_set_max_proto_version(ctx.get(), version)) {
-      return false;
-    }
+  uint16_t max_version = TLS1_3_VERSION;
+  if (args_map.count("-max-version") != 0 &&
+      !VersionFromString(&max_version, args_map["-max-version"])) {
+    fprintf(stderr, "Unknown protocol version: '%s'\n",
+            args_map["-max-version"].c_str());
+    return false;
+  }
+
+  if (!SSL_CTX_set_max_proto_version(ctx.get(), max_version)) {
+    return false;
   }
 
   if (args_map.count("-min-version") != 0) {
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 94abbbd..f203df1 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -193,16 +193,16 @@
     return false;
   }
 
-  if (args_map.count("-max-version") != 0) {
-    uint16_t version;
-    if (!VersionFromString(&version, args_map["-max-version"])) {
-      fprintf(stderr, "Unknown protocol version: '%s'\n",
-              args_map["-max-version"].c_str());
-      return false;
-    }
-    if (!SSL_CTX_set_max_proto_version(ctx.get(), version)) {
-      return false;
-    }
+  uint16_t max_version = TLS1_3_VERSION;
+  if (args_map.count("-max-version") != 0 &&
+      !VersionFromString(&max_version, args_map["-max-version"])) {
+    fprintf(stderr, "Unknown protocol version: '%s'\n",
+            args_map["-max-version"].c_str());
+    return false;
+  }
+
+  if (!SSL_CTX_set_max_proto_version(ctx.get(), max_version)) {
+    return false;
   }
 
   if (args_map.count("-min-version") != 0) {
diff --git a/src/util/all_tests.go b/src/util/all_tests.go
index d4bb802..3cf2320 100644
--- a/src/util/all_tests.go
+++ b/src/util/all_tests.go
@@ -186,10 +186,9 @@
 	} else {
 		cmd = exec.Command(prog, args...)
 	}
-	var stdoutBuf bytes.Buffer
-	var stderrBuf bytes.Buffer
-	cmd.Stdout = &stdoutBuf
-	cmd.Stderr = &stderrBuf
+	var outBuf bytes.Buffer
+	cmd.Stdout = &outBuf
+	cmd.Stderr = &outBuf
 	if mallocNumToFail >= 0 {
 		cmd.Env = os.Environ()
 		cmd.Env = append(cmd.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10))
@@ -208,13 +207,12 @@
 				return false, errMoreMallocs
 			}
 		}
-		fmt.Print(string(stderrBuf.Bytes()))
+		fmt.Print(string(outBuf.Bytes()))
 		return false, err
 	}
-	fmt.Print(string(stderrBuf.Bytes()))
 
 	// Account for Windows line-endings.
-	stdout := bytes.Replace(stdoutBuf.Bytes(), []byte("\r\n"), []byte("\n"), -1)
+	stdout := bytes.Replace(outBuf.Bytes(), []byte("\r\n"), []byte("\n"), -1)
 
 	if bytes.HasSuffix(stdout, []byte("PASS\n")) &&
 		(len(stdout) == 5 || stdout[len(stdout)-6] == '\n') {
@@ -228,6 +226,7 @@
 		return true, nil
 	}
 
+	fmt.Print(string(outBuf.Bytes()))
 	return false, nil
 }
 
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index 4589ee7..76637b2 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -40,7 +40,6 @@
 	["crypto/ecdsa/ecdsa_sign_test", "crypto/ecdsa/ecdsa_sign_tests.txt"],
 	["crypto/ecdsa/ecdsa_test"],
 	["crypto/ecdsa/ecdsa_verify_test", "crypto/ecdsa/ecdsa_verify_tests.txt"],
-	["crypto/err/err_test"],
 	["crypto/evp/evp_extra_test"],
 	["crypto/evp/evp_test", "crypto/evp/evp_tests.txt"],
 	["crypto/evp/pbkdf_test"],