external/boringssl: Sync to f8de2af7e319f83ba88579fbf127ba5fafc26c71.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/73ffb74b9e36a93a3e593010a367a610105da9a1..f8de2af7e319f83ba88579fbf127ba5fafc26c71

Change-Id: Iab4fb4fde30c1f26ad2b98160abca366bdea1da4
Test: BoringSSL CTS Presubmits.
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 3266ea2..ae5d0b6 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -220,6 +220,7 @@
 
   asn1/asn1_test.cc
   base64/base64_test.cc
+  buf/buf_test.cc
   bio/bio_test.cc
   bytestring/bytestring_test.cc
   chacha/chacha_test.cc
diff --git a/src/crypto/buf/buf.c b/src/crypto/buf/buf.c
index 5addc79..146b1e0 100644
--- a/src/crypto/buf/buf.c
+++ b/src/crypto/buf/buf.c
@@ -86,7 +86,7 @@
   OPENSSL_free(buf);
 }
 
-static int buf_mem_reserve(BUF_MEM *buf, size_t cap, int clean) {
+int BUF_MEM_reserve(BUF_MEM *buf, size_t cap) {
   if (buf->max >= cap) {
     return 1;
   }
@@ -116,12 +116,8 @@
   return 1;
 }
 
-int BUF_MEM_reserve(BUF_MEM *buf, size_t cap) {
-  return buf_mem_reserve(buf, cap, 0 /* don't clear old buffer contents. */);
-}
-
-static size_t buf_mem_grow(BUF_MEM *buf, size_t len, int clean) {
-  if (!buf_mem_reserve(buf, len, clean)) {
+size_t BUF_MEM_grow(BUF_MEM *buf, size_t len) {
+  if (!BUF_MEM_reserve(buf, len)) {
     return 0;
   }
   if (buf->length < len) {
@@ -131,12 +127,22 @@
   return len;
 }
 
-size_t BUF_MEM_grow(BUF_MEM *buf, size_t len) {
-  return buf_mem_grow(buf, len, 0 /* don't clear old buffer contents. */);
+size_t BUF_MEM_grow_clean(BUF_MEM *buf, size_t len) {
+  return BUF_MEM_grow(buf, len);
 }
 
-size_t BUF_MEM_grow_clean(BUF_MEM *buf, size_t len) {
-  return buf_mem_grow(buf, len, 1 /* clear old buffer contents. */);
+int BUF_MEM_append(BUF_MEM *buf, const void *in, size_t len) {
+  size_t new_len = buf->length + len;
+  if (new_len < len) {
+    OPENSSL_PUT_ERROR(BUF, ERR_R_OVERFLOW);
+    return 0;
+  }
+  if (!BUF_MEM_reserve(buf, new_len)) {
+    return 0;
+  }
+  OPENSSL_memcpy(buf->data + buf->length, in, len);
+  buf->length = new_len;
+  return 1;
 }
 
 char *BUF_strdup(const char *str) {
diff --git a/src/crypto/buf/buf_test.cc b/src/crypto/buf/buf_test.cc
new file mode 100644
index 0000000..4be0394
--- /dev/null
+++ b/src/crypto/buf/buf_test.cc
@@ -0,0 +1,97 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/buf.h>
+
+#include <string.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+
+TEST(BufTest, Basic) {
+  bssl::UniquePtr<BUF_MEM> buf(BUF_MEM_new());
+  ASSERT_TRUE(buf);
+  EXPECT_EQ(0u, buf->length);
+
+  // Use BUF_MEM_reserve to increase buf->max.
+  ASSERT_TRUE(BUF_MEM_reserve(buf.get(), 200));
+  EXPECT_GE(buf->max, 200u);
+  EXPECT_EQ(0u, buf->length);
+
+  // BUF_MEM_reserve with a smaller cap is a no-op.
+  size_t old_max = buf->max;
+  ASSERT_TRUE(BUF_MEM_reserve(buf.get(), 100));
+  EXPECT_EQ(old_max, buf->max);
+  EXPECT_EQ(0u, buf->length);
+
+  // BUF_MEM_grow can increase the length without reallocating.
+  ASSERT_EQ(100u, BUF_MEM_grow(buf.get(), 100));
+  EXPECT_EQ(100u, buf->length);
+  EXPECT_EQ(old_max, buf->max);
+  memset(buf->data, 'A', buf->length);
+
+  // If BUF_MEM_reserve reallocates, it preserves the contents.
+  ASSERT_TRUE(BUF_MEM_reserve(buf.get(), old_max + 1));
+  ASSERT_GE(buf->max, old_max + 1);
+  EXPECT_EQ(100u, buf->length);
+  for (size_t i = 0; i < 100; i++) {
+    EXPECT_EQ('A', buf->data[i]);
+  }
+
+  // BUF_MEM_grow should zero everything beyond buf->length.
+  memset(buf->data, 'B', buf->max);
+  ASSERT_EQ(150u, BUF_MEM_grow(buf.get(), 150));
+  EXPECT_EQ(150u, buf->length);
+  for (size_t i = 0; i < 100; i++) {
+    EXPECT_EQ('B', buf->data[i]);
+  }
+  for (size_t i = 100; i < 150; i++) {
+    EXPECT_EQ(0, buf->data[i]);
+  }
+
+  // BUF_MEM_grow can rellocate if necessary.
+  size_t new_len = buf->max + 1;
+  ASSERT_EQ(new_len, BUF_MEM_grow(buf.get(), new_len));
+  EXPECT_GE(buf->max, new_len);
+  EXPECT_EQ(new_len, buf->length);
+  for (size_t i = 0; i < 100; i++) {
+    EXPECT_EQ('B', buf->data[i]);
+  }
+  for (size_t i = 100; i < new_len; i++) {
+    EXPECT_EQ(0, buf->data[i]);
+  }
+
+  // BUF_MEM_grow can shink.
+  ASSERT_EQ(50u, BUF_MEM_grow(buf.get(), 50));
+  EXPECT_EQ(50u, buf->length);
+  for (size_t i = 0; i < 50; i++) {
+    EXPECT_EQ('B', buf->data[i]);
+  }
+}
+
+TEST(BufTest, Append) {
+  bssl::UniquePtr<BUF_MEM> buf(BUF_MEM_new());
+  ASSERT_TRUE(buf);
+
+  ASSERT_TRUE(BUF_MEM_append(buf.get(), nullptr, 0));
+  ASSERT_TRUE(BUF_MEM_append(buf.get(), "hello ", 6));
+  ASSERT_TRUE(BUF_MEM_append(buf.get(), nullptr, 0));
+  ASSERT_TRUE(BUF_MEM_append(buf.get(), "world", 5));
+  std::string str(128, 'A');
+  ASSERT_TRUE(BUF_MEM_append(buf.get(), str.data(), str.size()));
+
+  EXPECT_EQ("hello world" + str, std::string(buf->data, buf->length));
+}
diff --git a/src/crypto/err/err.c b/src/crypto/err/err.c
index a620fc7..c7bff16 100644
--- a/src/crypto/err/err.c
+++ b/src/crypto/err/err.c
@@ -123,8 +123,39 @@
 #include <openssl/thread.h>
 
 #include "../internal.h"
+#include "./internal.h"
 
 
+struct err_error_st {
+  // file contains the filename where the error occurred.
+  const char *file;
+  // data contains a NUL-terminated string with optional data. It must be freed
+  // with |OPENSSL_free|.
+  char *data;
+  // packed contains the error library and reason, as packed by ERR_PACK.
+  uint32_t packed;
+  // line contains the line number where the error occurred.
+  uint16_t line;
+  // mark indicates a reversion point in the queue. See |ERR_pop_to_mark|.
+  unsigned mark : 1;
+};
+
+// ERR_STATE contains the per-thread, error queue.
+typedef struct err_state_st {
+  // errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring
+  // buffer.
+  struct err_error_st errors[ERR_NUM_ERRORS];
+  // top contains the index one past the most recent error. If |top| equals
+  // |bottom| then the queue is empty.
+  unsigned top;
+  // bottom contains the index of the last error in the queue.
+  unsigned bottom;
+
+  // to_free, if not NULL, contains a pointer owned by this structure that was
+  // previously a |data| pointer of one of the elements of |errors|.
+  void *to_free;
+} ERR_STATE;
+
 extern const uint32_t kOpenSSLReasonValues[];
 extern const size_t kOpenSSLReasonValuesLen;
 extern const char kOpenSSLReasonStringData[];
@@ -135,6 +166,16 @@
   OPENSSL_memset(error, 0, sizeof(struct err_error_st));
 }
 
+static void err_copy(struct err_error_st *dst, const struct err_error_st *src) {
+  err_clear(dst);
+  dst->file = src->file;
+  if (src->data != NULL) {
+    dst->data = OPENSSL_strdup(src->data);
+  }
+  dst->packed = src->packed;
+  dst->line = src->line;
+}
+
 // global_next_library contains the next custom library value to return.
 static int global_next_library = ERR_NUM_LIBS;
 
@@ -150,8 +191,7 @@
     return;
   }
 
-  unsigned i;
-  for (i = 0; i < ERR_NUM_ERRORS; i++) {
+  for (unsigned i = 0; i < ERR_NUM_ERRORS; i++) {
     err_clear(&state->errors[i]);
   }
   OPENSSL_free(state->to_free);
@@ -740,3 +780,68 @@
 void ERR_load_BIO_strings(void) {}
 
 void ERR_load_ERR_strings(void) {}
+
+struct err_save_state_st {
+  struct err_error_st *errors;
+  size_t num_errors;
+};
+
+void ERR_SAVE_STATE_free(ERR_SAVE_STATE *state) {
+  if (state == NULL) {
+    return;
+  }
+  for (size_t i = 0; i < state->num_errors; i++) {
+    err_clear(&state->errors[i]);
+  }
+  OPENSSL_free(state->errors);
+  OPENSSL_free(state);
+}
+
+ERR_SAVE_STATE *ERR_save_state(void) {
+  ERR_STATE *const state = err_get_state();
+  if (state == NULL || state->top == state->bottom) {
+    return NULL;
+  }
+
+  ERR_SAVE_STATE *ret = OPENSSL_malloc(sizeof(ERR_SAVE_STATE));
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  // Errors are stored in the range (bottom, top].
+  size_t num_errors = state->top >= state->bottom
+                          ? state->top - state->bottom
+                          : ERR_NUM_ERRORS + state->top - state->bottom;
+  assert(num_errors < ERR_NUM_ERRORS);
+  ret->errors = OPENSSL_malloc(num_errors * sizeof(struct err_error_st));
+  if (ret->errors == NULL) {
+    OPENSSL_free(ret);
+    return NULL;
+  }
+  OPENSSL_memset(ret->errors, 0, num_errors * sizeof(struct err_error_st));
+  ret->num_errors = num_errors;
+
+  for (size_t i = 0; i < num_errors; i++) {
+    size_t j = (state->bottom + i + 1) % ERR_NUM_ERRORS;
+    err_copy(&ret->errors[i], &state->errors[j]);
+  }
+  return ret;
+}
+
+void ERR_restore_state(const ERR_SAVE_STATE *state) {
+  if (state == NULL || state->num_errors == 0) {
+    ERR_clear_error();
+    return;
+  }
+
+  ERR_STATE *const dst = err_get_state();
+  if (dst == NULL) {
+    return;
+  }
+
+  for (size_t i = 0; i < state->num_errors; i++) {
+    err_copy(&dst->errors[i], &state->errors[i]);
+  }
+  dst->top = state->num_errors - 1;
+  dst->bottom = ERR_NUM_ERRORS - 1;
+}
diff --git a/src/crypto/err/err_test.cc b/src/crypto/err/err_test.cc
index 5d04ae2..489d248 100644
--- a/src/crypto/err/err_test.cc
+++ b/src/crypto/err/err_test.cc
@@ -21,6 +21,8 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
+#include "./internal.h"
+
 
 TEST(ErrTest, Overflow) {
   for (unsigned i = 0; i < ERR_NUM_ERRORS*2; i++) {
@@ -119,3 +121,94 @@
   EXPECT_EQ(ERR_LIB_USER, ERR_GET_LIB(error));
   EXPECT_EQ(ERR_R_INTERNAL_ERROR, ERR_GET_REASON(error));
 }
+
+TEST(ErrTest, SaveAndRestore) {
+  // Restoring no state clears the error queue, including error data.
+  ERR_put_error(1, 0 /* unused */, 1, "test1.c", 1);
+  ERR_put_error(2, 0 /* unused */, 2, "test2.c", 2);
+  ERR_add_error_data(1, "data1");
+  ERR_restore_state(nullptr);
+  EXPECT_EQ(0u, ERR_get_error());
+
+  // Add some entries to the error queue and save it.
+  ERR_put_error(1, 0 /* unused */, 1, "test1.c", 1);
+  ERR_add_error_data(1, "data1");
+  ERR_put_error(2, 0 /* unused */, 2, "test2.c", 2);
+  ERR_put_error(3, 0 /* unused */, 3, "test3.c", 3);
+  ERR_add_error_data(1, "data3");
+  bssl::UniquePtr<ERR_SAVE_STATE> saved(ERR_save_state());
+  ASSERT_TRUE(saved);
+
+  // The existing error queue entries still exist.
+  int line, flags;
+  const char *file, *data;
+  uint32_t packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+  EXPECT_EQ(ERR_GET_LIB(packed_error), 1);
+  EXPECT_EQ(ERR_GET_REASON(packed_error), 1);
+  EXPECT_STREQ("test1.c", file);
+  EXPECT_EQ(line, 1);
+  EXPECT_STREQ(data, "data1");
+  EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+  // The state may be restored, both over an empty and non-empty state.
+  for (unsigned i = 0; i < 2; i++) {
+    SCOPED_TRACE(i);
+    ERR_restore_state(saved.get());
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 1);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 1);
+    EXPECT_STREQ("test1.c", file);
+    EXPECT_EQ(line, 1);
+    EXPECT_STREQ(data, "data1");
+    EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 2);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 2);
+    EXPECT_STREQ("test2.c", file);
+    EXPECT_EQ(line, 2);
+    EXPECT_STREQ(data, "");  // No error data is reported as the empty string.
+    EXPECT_EQ(flags, 0);
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 3);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 3);
+    EXPECT_STREQ("test3.c", file);
+    EXPECT_EQ(line, 3);
+    EXPECT_STREQ(data, "data3");
+    EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+    // The error queue is now empty for the next iteration.
+    EXPECT_EQ(0u, ERR_get_error());
+  }
+
+  // Test a case where the error queue wraps around. The first set of errors
+  // will all be discarded, but result in wrapping the list around.
+  ERR_clear_error();
+  for (unsigned i = 0; i < ERR_NUM_ERRORS / 2; i++) {
+    ERR_put_error(0, 0 /* unused */, 0, "invalid", 0);
+  }
+  for (unsigned i = 1; i < ERR_NUM_ERRORS; i++) {
+    ERR_put_error(i, 0 /* unused */, i, "test", i);
+  }
+  saved.reset(ERR_save_state());
+
+  // The state may be restored, both over an empty and non-empty state. Pop one
+  // error off so the first iteration is tested to not be a no-op.
+  ERR_get_error();
+  for (int i = 0; i < 2; i++) {
+    SCOPED_TRACE(i);
+    ERR_restore_state(saved.get());
+    for (int j = 1; j < ERR_NUM_ERRORS; j++) {
+      SCOPED_TRACE(j);
+      packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+      EXPECT_EQ(ERR_GET_LIB(packed_error), j);
+      EXPECT_EQ(ERR_GET_REASON(packed_error), j);
+      EXPECT_STREQ("test", file);
+      EXPECT_EQ(line, j);
+    }
+    // The error queue is now empty for the next iteration.
+    EXPECT_EQ(0u, ERR_get_error());
+  }
+}
diff --git a/src/crypto/err/internal.h b/src/crypto/err/internal.h
new file mode 100644
index 0000000..3f2397c
--- /dev/null
+++ b/src/crypto/err/internal.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
+
+#include <openssl/err.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// Private error queue functions.
+
+// ERR_SAVE_STATE contains a saved representation of the error queue. It is
+// slightly more compact than |ERR_STATE| as the error queue will typically not
+// contain |ERR_NUM_ERRORS| entries.
+typedef struct err_save_state_st ERR_SAVE_STATE;
+
+// ERR_SAVE_STATE_free releases all memory associated with |state|.
+OPENSSL_EXPORT void ERR_SAVE_STATE_free(ERR_SAVE_STATE *state);
+
+// ERR_save_state returns a newly-allocated |ERR_SAVE_STATE| structure
+// containing the current state of the error queue or NULL on allocation
+// error. It should be released with |ERR_SAVE_STATE_free|.
+OPENSSL_EXPORT ERR_SAVE_STATE *ERR_save_state(void);
+
+// ERR_restore_state clears the error queue and replaces it with |state|.
+OPENSSL_EXPORT void ERR_restore_state(const ERR_SAVE_STATE *state);
+
+
+#if defined(__cplusplus)
+}  // extern C
+
+extern "C++" {
+
+namespace bssl {
+
+BORINGSSL_MAKE_DELETER(ERR_SAVE_STATE, ERR_SAVE_STATE_free)
+
+}  // namespace bssl
+
+}  // extern C++
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
diff --git a/src/include/openssl/buf.h b/src/include/openssl/buf.h
index d4c3e22..3f961b8 100644
--- a/src/include/openssl/buf.h
+++ b/src/include/openssl/buf.h
@@ -89,10 +89,14 @@
 // zeros. It returns the length of |buf|, or zero if there's an error.
 OPENSSL_EXPORT size_t BUF_MEM_grow(BUF_MEM *buf, size_t len);
 
-// BUF_MEM_grow_clean acts the same as |BUF_MEM_grow|, but clears the previous
-// contents of memory if reallocing.
+// BUF_MEM_grow_clean calls |BUF_MEM_grow|. BoringSSL always zeros memory
+// allocated memory on free.
 OPENSSL_EXPORT size_t BUF_MEM_grow_clean(BUF_MEM *buf, size_t len);
 
+// BUF_MEM_append appends |in| to |buf|. It returns one on success and zero on
+// error.
+OPENSSL_EXPORT int BUF_MEM_append(BUF_MEM *buf, const void *in, size_t len);
+
 // BUF_strdup returns an allocated, duplicate of |str|.
 OPENSSL_EXPORT char *BUF_strdup(const char *str);
 
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 66f6204..6d355b5 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -41,10 +41,16 @@
   size_t len;
 
 #if !defined(BORINGSSL_NO_CXX)
-  // Allow implicit conversions to bssl::Span<const uint8_t>.
+  // Allow implicit conversions to and from bssl::Span<const uint8_t>.
+  cbs_st(bssl::Span<const uint8_t> span)
+      : data(span.data()), len(span.size()) {}
   operator bssl::Span<const uint8_t>() const {
     return bssl::MakeConstSpan(data, len);
   }
+
+  // Defining any constructors requires we explicitly default the others.
+  cbs_st() = default;
+  cbs_st(const cbs_st &) = default;
 #endif
 };
 
diff --git a/src/include/openssl/chacha.h b/src/include/openssl/chacha.h
index 61deb8e..684fc5b 100644
--- a/src/include/openssl/chacha.h
+++ b/src/include/openssl/chacha.h
@@ -17,10 +17,14 @@
 
 #include <openssl/base.h>
 
-#ifdef  __cplusplus
+#if defined(__cplusplus)
 extern "C" {
 #endif
 
+// ChaCha20.
+//
+// ChaCha20 is a stream cipher. See https://tools.ietf.org/html/rfc7539.
+
 
 // CRYPTO_chacha_20 encrypts |in_len| bytes from |in| with the given key and
 // nonce and writes the result to |out|. If |in| and |out| alias, they must be
diff --git a/src/include/openssl/err.h b/src/include/openssl/err.h
index 5de3cbd..9a65ffb 100644
--- a/src/include/openssl/err.h
+++ b/src/include/openssl/err.h
@@ -436,39 +436,10 @@
 OPENSSL_EXPORT void ERR_add_error_dataf(const char *format, ...)
     OPENSSL_PRINTF_FORMAT_FUNC(1, 2);
 
-struct err_error_st {
-  // file contains the filename where the error occurred.
-  const char *file;
-  // data contains a NUL-terminated string with optional data. It must be freed
-  // with |OPENSSL_free|.
-  char *data;
-  // packed contains the error library and reason, as packed by ERR_PACK.
-  uint32_t packed;
-  // line contains the line number where the error occurred.
-  uint16_t line;
-  // mark indicates a reversion point in the queue. See |ERR_pop_to_mark|.
-  unsigned mark : 1;
-};
-
-// ERR_NUM_ERRORS is the limit of the number of errors in the queue.
+// ERR_NUM_ERRORS is one more than the limit of the number of errors in the
+// queue.
 #define ERR_NUM_ERRORS 16
 
-// err_state_st (aka |ERR_STATE|) contains the per-thread, error queue.
-typedef struct err_state_st {
-  // errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring
-  // buffer.
-  struct err_error_st errors[ERR_NUM_ERRORS];
-  // top contains the index one past the most recent error. If |top| equals
-  // |bottom| then the queue is empty.
-  unsigned top;
-  // bottom contains the index of the last error in the queue.
-  unsigned bottom;
-
-  // to_free, if not NULL, contains a pointer owned by this structure that was
-  // previously a |data| pointer of one of the elements of |errors|.
-  void *to_free;
-} ERR_STATE;
-
 #define ERR_PACK(lib, reason)                                              \
   (((((uint32_t)(lib)) & 0xff) << 24) | ((((uint32_t)(reason)) & 0xfff)))
 
diff --git a/src/include/openssl/span.h b/src/include/openssl/span.h
index 3109036..3a629f7 100644
--- a/src/include/openssl/span.h
+++ b/src/include/openssl/span.h
@@ -22,6 +22,7 @@
 extern "C++" {
 
 #include <algorithm>
+#include <cassert>
 #include <cstdlib>
 #include <type_traits>
 
@@ -134,6 +135,15 @@
   T *end() const { return data_ + size_; };
   const T *cend() const { return end(); };
 
+  T &front() const {
+    assert(size_ != 0);
+    return data_[0];
+  }
+  T &back() const {
+    assert(size_ != 0);
+    return data_[size_ - 1];
+  }
+
   T &operator[](size_t i) const { return data_[i]; }
   T &at(size_t i) const { return data_[i]; }
 
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 6b4bf81..a2d3ce7 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -146,7 +146,6 @@
 
 #include <openssl/bio.h>
 #include <openssl/buf.h>
-#include <openssl/lhash.h>
 #include <openssl/pem.h>
 #include <openssl/span.h>
 #include <openssl/ssl3.h>
@@ -158,6 +157,11 @@
 #include <sys/time.h>
 #endif
 
+// NGINX needs this #include. Consider revisiting this after NGINX 1.14.0 has
+// been out for a year or so (assuming that they fix it in that release.) See
+// https://boringssl-review.googlesource.com/c/boringssl/+/21664.
+#include <openssl/hmac.h>
+
 // Forward-declare struct timeval. On Windows, it is defined in winsock2.h and
 // Windows headers define too many macros to be included in public headers.
 // However, only a forward declaration is needed.
@@ -1042,8 +1046,8 @@
 // |type| parameter is one of the |SSL_FILETYPE_*| values and determines whether
 // the file's contents are read as PEM or DER.
 
-#define SSL_FILETYPE_ASN1 X509_FILETYPE_ASN1
-#define SSL_FILETYPE_PEM X509_FILETYPE_PEM
+#define SSL_FILETYPE_PEM 1
+#define SSL_FILETYPE_ASN1 2
 
 OPENSSL_EXPORT int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx,
                                                   const char *file,
@@ -1635,7 +1639,6 @@
 // established, an |SSL_SESSION| may be shared by multiple |SSL| objects on
 // different threads and must not be modified.
 
-DECLARE_LHASH_OF(SSL_SESSION)
 DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 
 // SSL_SESSION_new returns a newly-allocated blank |SSL_SESSION| or NULL on
@@ -2023,7 +2026,7 @@
 // 1) One can simply set the keys with |SSL_CTX_set_tlsext_ticket_keys|.
 // 2) One can configure an |EVP_CIPHER_CTX| and |HMAC_CTX| directly for
 //    encryption and authentication.
-// 3) One can configure an |SSL_TICKET_ENCRYPTION_METHOD| to have more control
+// 3) One can configure an |SSL_TICKET_AEAD_METHOD| to have more control
 //    and the option of asynchronous decryption.
 //
 // An attacker that compromises a server's session ticket key can impersonate
@@ -2100,8 +2103,8 @@
   ssl_ticket_aead_error,
 };
 
-// ssl_ticket_aead_method_st (aka |SSL_TICKET_ENCRYPTION_METHOD|) contains
-// methods for encrypting and decrypting session tickets.
+// ssl_ticket_aead_method_st (aka |SSL_TICKET_AEAD_METHOD|) contains methods
+// for encrypting and decrypting session tickets.
 struct ssl_ticket_aead_method_st {
   // max_overhead returns the maximum number of bytes of overhead that |seal|
   // may add.
@@ -4042,13 +4045,9 @@
 // This structures are exposed for historical reasons, but access to them is
 // deprecated.
 
-// TODO(davidben): Opaquify most or all of |SSL_CTX| and |SSL_SESSION| so these
-// forward declarations are not needed.
-typedef struct ssl_protocol_method_st SSL_PROTOCOL_METHOD;
+// TODO(davidben): Remove this forward declaration when |SSL_SESSION| is opaque.
 typedef struct ssl_x509_method_st SSL_X509_METHOD;
 
-DECLARE_STACK_OF(SSL_CUSTOM_EXTENSION)
-
 #define SSL_MAX_SSL_SESSION_ID_LENGTH 32
 #define SSL_MAX_SID_CTX_LENGTH 32
 #define SSL_MAX_MASTER_KEY_LENGTH 48
@@ -4177,319 +4176,6 @@
   unsigned is_server:1;
 };
 
-// ssl_cipher_preference_list_st contains a list of SSL_CIPHERs with
-// equal-preference groups. For TLS clients, the groups are moot because the
-// server picks the cipher and groups cannot be expressed on the wire. However,
-// for servers, the equal-preference groups allow the client's preferences to
-// be partially respected. (This only has an effect with
-// SSL_OP_CIPHER_SERVER_PREFERENCE).
-//
-// The equal-preference groups are expressed by grouping SSL_CIPHERs together.
-// All elements of a group have the same priority: no ordering is expressed
-// within a group.
-//
-// The values in |ciphers| are in one-to-one correspondence with
-// |in_group_flags|. (That is, sk_SSL_CIPHER_num(ciphers) is the number of
-// bytes in |in_group_flags|.) The bytes in |in_group_flags| are either 1, to
-// indicate that the corresponding SSL_CIPHER is not the last element of a
-// group, or 0 to indicate that it is.
-//
-// For example, if |in_group_flags| contains all zeros then that indicates a
-// traditional, fully-ordered preference. Every SSL_CIPHER is the last element
-// of the group (i.e. they are all in a one-element group).
-//
-// For a more complex example, consider:
-//   ciphers:        A  B  C  D  E  F
-//   in_group_flags: 1  1  0  0  1  0
-//
-// That would express the following, order:
-//
-//    A         E
-//    B -> D -> F
-//    C
-struct ssl_cipher_preference_list_st {
-  STACK_OF(SSL_CIPHER) *ciphers;
-  uint8_t *in_group_flags;
-};
-
-struct tlsext_ticket_key {
-  uint8_t name[SSL_TICKET_KEY_NAME_LEN];
-  uint8_t hmac_key[16];
-  uint8_t aes_key[16];
-  // next_rotation_tv_sec is the time (in seconds from the epoch) when the
-  // current key should be superseded by a new key, or the time when a previous
-  // key should be dropped. If zero, then the key should not be automatically
-  // rotated.
-  uint64_t next_rotation_tv_sec;
-};
-
-// ssl_ctx_st (aka |SSL_CTX|) contains configuration common to several SSL
-// 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;
-
-  // conf_max_version is the maximum acceptable protocol version configured by
-  // |SSL_CTX_set_max_proto_version|. Note this version is normalized in DTLS
-  // and is further constrainted by |SSL_OP_NO_*|.
-  uint16_t conf_max_version;
-
-  // conf_min_version is the minimum acceptable protocol version configured by
-  // |SSL_CTX_set_min_proto_version|. Note this version is normalized in DTLS
-  // and is further constrainted by |SSL_OP_NO_*|.
-  uint16_t conf_min_version;
-
-  // tls13_variant is the variant of TLS 1.3 we are using for this
-  // configuration.
-  enum tls13_variant_t tls13_variant;
-
-  struct ssl_cipher_preference_list_st *cipher_list;
-
-  X509_STORE *cert_store;
-  LHASH_OF(SSL_SESSION) *sessions;
-  // Most session-ids that will be cached, default is
-  // SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited.
-  unsigned long session_cache_size;
-  SSL_SESSION *session_cache_head;
-  SSL_SESSION *session_cache_tail;
-
-  // handshakes_since_cache_flush is the number of successful handshakes since
-  // the last cache flush.
-  int handshakes_since_cache_flush;
-
-  // This can have one of 2 values, ored together,
-  // SSL_SESS_CACHE_CLIENT,
-  // SSL_SESS_CACHE_SERVER,
-  // Default is SSL_SESSION_CACHE_SERVER, which means only
-  // SSL_accept which cache SSL_SESSIONS.
-  int session_cache_mode;
-
-  // session_timeout is the default lifetime for new sessions in TLS 1.2 and
-  // earlier, in seconds.
-  uint32_t session_timeout;
-
-  // session_psk_dhe_timeout is the default lifetime for new sessions in TLS
-  // 1.3, in seconds.
-  uint32_t session_psk_dhe_timeout;
-
-  // If this callback is not null, it will be called each time a session id is
-  // added to the cache.  If this function returns 1, it means that the
-  // callback will do a SSL_SESSION_free() when it has finished using it.
-  // Otherwise, on 0, it means the callback has finished with it. If
-  // remove_session_cb is not null, it will be called when a session-id is
-  // removed from the cache.  After the call, OpenSSL will SSL_SESSION_free()
-  // it.
-  int (*new_session_cb)(SSL *ssl, SSL_SESSION *sess);
-  void (*remove_session_cb)(SSL_CTX *ctx, SSL_SESSION *sess);
-  SSL_SESSION *(*get_session_cb)(SSL *ssl, const uint8_t *data, int len,
-                                 int *copy);
-  SSL_SESSION *(*get_session_cb_legacy)(SSL *ssl, uint8_t *data, int len,
-                                        int *copy);
-
-  CRYPTO_refcount_t references;
-
-  // if defined, these override the X509_verify_cert() calls
-  int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg);
-  void *app_verify_arg;
-
-  enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
-                                                     uint8_t *out_alert);
-
-  // Default password callback.
-  pem_password_cb *default_passwd_callback;
-
-  // Default password callback user data.
-  void *default_passwd_callback_userdata;
-
-  // get client cert callback
-  int (*client_cert_cb)(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey);
-
-  // get channel id callback
-  void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey);
-
-  CRYPTO_EX_DATA ex_data;
-
-  // custom_*_extensions stores any callback sets for custom extensions. Note
-  // that these pointers will be NULL if the stack would otherwise be empty.
-  STACK_OF(SSL_CUSTOM_EXTENSION) *client_custom_extensions;
-  STACK_OF(SSL_CUSTOM_EXTENSION) *server_custom_extensions;
-
-  // Default values used when no per-SSL value is defined follow
-
-  void (*info_callback)(const SSL *ssl, int type, int value);
-
-  // what we put in client cert requests
-  STACK_OF(CRYPTO_BUFFER) *client_CA;
-
-  // cached_x509_client_CA is a cache of parsed versions of the elements of
-  // |client_CA|.
-  STACK_OF(X509_NAME) *cached_x509_client_CA;
-
-
-  // Default values to use in SSL structures follow (these are copied by
-  // SSL_new)
-
-  uint32_t options;
-  uint32_t mode;
-  uint32_t max_cert_list;
-
-  struct cert_st *cert;
-
-  // callback that allows applications to peek at protocol messages
-  void (*msg_callback)(int write_p, int version, int content_type,
-                       const void *buf, size_t len, SSL *ssl, void *arg);
-  void *msg_callback_arg;
-
-  int verify_mode;
-  int (*default_verify_callback)(
-      int ok, X509_STORE_CTX *ctx);  // called 'verify_callback' in the SSL
-
-  X509_VERIFY_PARAM *param;
-
-  // select_certificate_cb is called before most ClientHello processing and
-  // before the decision whether to resume a session is made. See
-  // |ssl_select_cert_result_t| for details of the return values.
-  enum ssl_select_cert_result_t (*select_certificate_cb)(
-      const SSL_CLIENT_HELLO *);
-
-  // dos_protection_cb is called once the resumption decision for a ClientHello
-  // has been made. It returns one to continue the handshake or zero to
-  // abort.
-  int (*dos_protection_cb) (const SSL_CLIENT_HELLO *);
-
-  // Maximum amount of data to send in one fragment. actual record size can be
-  // more than this due to padding and MAC overheads.
-  uint16_t max_send_fragment;
-
-  // TLS extensions servername callback
-  int (*tlsext_servername_callback)(SSL *, int *, void *);
-  void *tlsext_servername_arg;
-
-  // RFC 4507 session ticket keys. |tlsext_ticket_key_current| may be NULL
-  // before the first handshake and |tlsext_ticket_key_prev| may be NULL at any
-  // time. Automatically generated ticket keys are rotated as needed at
-  // handshake time. Hence, all access must be synchronized through |lock|.
-  struct tlsext_ticket_key *tlsext_ticket_key_current;
-  struct tlsext_ticket_key *tlsext_ticket_key_prev;
-
-  // Callback to support customisation of ticket key setting
-  int (*tlsext_ticket_key_cb)(SSL *ssl, uint8_t *name, uint8_t *iv,
-                              EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc);
-
-  // Server-only: psk_identity_hint is the default identity hint to send in
-  // PSK-based key exchanges.
-  char *psk_identity_hint;
-
-  unsigned int (*psk_client_callback)(SSL *ssl, const char *hint,
-                                      char *identity,
-                                      unsigned int max_identity_len,
-                                      uint8_t *psk, unsigned int max_psk_len);
-  unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
-                                      uint8_t *psk, unsigned int max_psk_len);
-
-
-  // retain_only_sha256_of_client_certs is true if we should compute the SHA256
-  // hash of the peer's certificate and then discard it to save memory and
-  // session space. Only effective on the server side.
-  char retain_only_sha256_of_client_certs;
-
-  // Next protocol negotiation information
-  // (for experimental NPN extension).
-
-  // For a server, this contains a callback function by which the set of
-  // advertised protocols can be provided.
-  int (*next_protos_advertised_cb)(SSL *ssl, const uint8_t **out,
-                                   unsigned *out_len, void *arg);
-  void *next_protos_advertised_cb_arg;
-  // For a client, this contains a callback function that selects the
-  // next protocol from the list provided by the server.
-  int (*next_proto_select_cb)(SSL *ssl, uint8_t **out, uint8_t *out_len,
-                              const uint8_t *in, unsigned in_len, void *arg);
-  void *next_proto_select_cb_arg;
-
-  // ALPN information
-  // (we are in the process of transitioning from NPN to ALPN.)
-
-  // For a server, this contains a callback function that allows the
-  // server to select the protocol for the connection.
-  //   out: on successful return, this must point to the raw protocol
-  //        name (without the length prefix).
-  //   outlen: on successful return, this contains the length of |*out|.
-  //   in: points to the client's list of supported protocols in
-  //       wire-format.
-  //   inlen: the length of |in|.
-  int (*alpn_select_cb)(SSL *ssl, const uint8_t **out, uint8_t *out_len,
-                        const uint8_t *in, unsigned in_len, void *arg);
-  void *alpn_select_cb_arg;
-
-  // For a client, this contains the list of supported protocols in wire
-  // format.
-  uint8_t *alpn_client_proto_list;
-  unsigned alpn_client_proto_list_len;
-
-  // SRTP profiles we are willing to do from RFC 5764
-  STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
-
-  // Supported group values inherited by SSL structure
-  size_t supported_group_list_len;
-  uint16_t *supported_group_list;
-
-  // The client's Channel ID private key.
-  EVP_PKEY *tlsext_channel_id_private;
-
-  // keylog_callback, if not NULL, is the key logging callback. See
-  // |SSL_CTX_set_keylog_callback|.
-  void (*keylog_callback)(const SSL *ssl, const char *line);
-
-  // current_time_cb, if not NULL, is the function to use to get the current
-  // time. It sets |*out_clock| to the current time. The |ssl| argument is
-  // always NULL. See |SSL_CTX_set_current_time_cb|.
-  void (*current_time_cb)(const SSL *ssl, struct timeval *out_clock);
-
-  // pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
-  // memory.
-  CRYPTO_BUFFER_POOL *pool;
-
-  // ticket_aead_method contains function pointers for opening and sealing
-  // session tickets.
-  const SSL_TICKET_AEAD_METHOD *ticket_aead_method;
-
-  // verify_sigalgs, if not empty, is the set of signature algorithms
-  // accepted from the peer in decreasing order of preference.
-  uint16_t *verify_sigalgs;
-  size_t num_verify_sigalgs;
-
-  // quiet_shutdown is true if the connection should not send a close_notify on
-  // shutdown.
-  unsigned quiet_shutdown:1;
-
-  // ocsp_stapling_enabled is only used by client connections and indicates
-  // whether OCSP stapling will be requested.
-  unsigned ocsp_stapling_enabled:1;
-
-  // If true, a client will request certificate timestamps.
-  unsigned signed_cert_timestamps_enabled:1;
-
-  // tlsext_channel_id_enabled is one if Channel ID is enabled and zero
-  // otherwise. For a server, means that we'll accept Channel IDs from clients.
-  // For a client, means that we'll advertise support.
-  unsigned tlsext_channel_id_enabled:1;
-
-  // grease_enabled is one if draft-davidben-tls-grease-01 is enabled and zero
-  // otherwise.
-  unsigned grease_enabled:1;
-
-  // allow_unknown_alpn_protos is one if the client allows unsolicited ALPN
-  // protocols from the peer.
-  unsigned allow_unknown_alpn_protos:1;
-
-  // ed25519_enabled is one if Ed25519 is advertised in the handshake.
-  unsigned ed25519_enabled:1;
-};
-
 
 // Nodejs compatibility section (hidden).
 //
@@ -4644,14 +4330,12 @@
 BORINGSSL_MAKE_DELETER(SSL, SSL_free)
 BORINGSSL_MAKE_DELETER(SSL_CTX, SSL_CTX_free)
 BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
-BORINGSSL_MAKE_DELETER(tlsext_ticket_key, OPENSSL_free);
 
 enum class OpenRecordResult {
   kOK,
   kDiscard,
   kIncompleteRecord,
   kAlertCloseNotify,
-  kAlertFatal,
   kError,
 };
 
@@ -4665,9 +4349,8 @@
 // - kIncompleteRecord if |in| did not contain a complete record.
 // - kAlertCloseNotify if a record was successfully processed but is a
 //   close_notify alert.
-// - kAlertFatal if a record was successfully processed but is a fatal alert.
 // - kError if an error occurred or the record is invalid. |*out_alert| will be
-//   set to an alert to emit.
+//   set to an alert to emit, or zero if no alert should be emitted.
 OPENSSL_EXPORT OpenRecordResult OpenRecord(SSL *ssl, Span<uint8_t> *out,
                                            size_t *out_record_len,
                                            uint8_t *out_alert,
diff --git a/src/infra/config/cq.cfg b/src/infra/config/cq.cfg
index e63026c..c657250 100644
--- a/src/infra/config/cq.cfg
+++ b/src/infra/config/cq.cfg
@@ -53,10 +53,7 @@
       builders { name: "win64" }
       builders { name: "win64_rel" }
       builders { name: "win64_small" }
-      builders { name: "win32_vs2017_compile" }
-      builders { name: "win64_vs2017_compile" }
-      builders { name: "win32_clang_vs2017_compile" }
-      builders { name: "win64_clang_vs2017_compile" }
+
 
       builders { name: "linux_fips" }
       builders { name: "linux_fips_rel" }
diff --git a/src/ssl/custom_extensions.cc b/src/ssl/custom_extensions.cc
index c22f4fe..85b8a33 100644
--- a/src/ssl/custom_extensions.cc
+++ b/src/ssl/custom_extensions.cc
@@ -114,7 +114,7 @@
         break;
 
       default:
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
         OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
         ERR_add_error_dataf("extension %u", (unsigned) ext->value);
         return 0;
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index c2f6479..3a31a02 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -287,7 +287,7 @@
     if (frag->type != msg_hdr->type ||
         frag->msg_len != msg_hdr->msg_len) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return NULL;
     }
     return frag;
@@ -315,7 +315,7 @@
     case SSL3_RT_APPLICATION_DATA:
       // Unencrypted application data records are always illegal.
       if (ssl->s3->aead_read_ctx->is_null_cipher()) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
         return -1;
       }
@@ -330,21 +330,21 @@
       // We do not support renegotiation, so encrypted ChangeCipherSpec records
       // are illegal.
       if (!ssl->s3->aead_read_ctx->is_null_cipher()) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
         return -1;
       }
 
       if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
         return -1;
       }
 
       // Flag the ChangeCipherSpec for later.
       ssl->d1->has_change_cipher_spec = true;
       ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC,
-                          rr->data, rr->length);
+                          MakeSpan(rr->data, rr->length));
 
       rr->length = 0;
       ssl_read_buffer_discard(ssl);
@@ -355,7 +355,7 @@
       break;
 
     default:
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       return -1;
   }
@@ -369,7 +369,7 @@
     CBS body;
     if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return -1;
     }
 
@@ -380,14 +380,14 @@
         frag_off + frag_len > msg_len ||
         msg_len > ssl_max_handshake_message_len(ssl)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return -1;
     }
 
     // The encrypted epoch in DTLS has only one handshake message.
     if (ssl->d1->r_epoch == 1 && msg_hdr.seq != ssl->d1->handshake_read_seq) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return -1;
     }
 
@@ -433,8 +433,7 @@
   CBS_init(&out->raw, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
   out->is_v2_hello = false;
   if (!ssl->s3->has_message) {
-    ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, frag->data,
-                        frag->msg_len + DTLS1_HM_HEADER_LENGTH);
+    ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, out->raw);
     ssl->s3->has_message = true;
   }
   return true;
@@ -462,7 +461,11 @@
   }
 }
 
-int dtls_has_incoming_messages(const SSL *ssl) {
+bool dtls_has_unprocessed_handshake_data(const SSL *ssl) {
+  if (ssl->d1->has_change_cipher_spec) {
+    return true;
+  }
+
   size_t current = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
   for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
     // Skip the current message.
@@ -471,10 +474,10 @@
       continue;
     }
     if (ssl->d1->incoming_messages[i] != NULL) {
-      return 1;
+      return true;
     }
   }
-  return 0;
+  return false;
 }
 
 int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
@@ -575,7 +578,7 @@
     // TODO(svaldez): Move this up a layer to fix abstraction for SSLTranscript
     // on hs.
     if (ssl->s3->hs != NULL &&
-        !ssl->s3->hs->transcript.Update(data.data(), data.size())) {
+        !ssl->s3->hs->transcript.Update(data)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       return 0;
     }
@@ -673,7 +676,7 @@
     }
 
     ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
-                        kChangeCipherSpec, sizeof(kChangeCipherSpec));
+                        kChangeCipherSpec);
     return seal_success;
   }
 
@@ -716,7 +719,8 @@
     return seal_error;
   }
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len);
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE,
+                      MakeSpan(frag, frag_len));
 
   if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE,
                         out + prefix, frag_len, use_epoch)) {
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index a3095e1..5b1cce5 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -129,80 +129,55 @@
 namespace bssl {
 
 int dtls1_get_record(SSL *ssl) {
-again:
-  switch (ssl->s3->recv_shutdown) {
-    case ssl_shutdown_none:
-      break;
-    case ssl_shutdown_fatal_alert:
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
-      return -1;
-    case ssl_shutdown_close_notify:
-      return 0;
-  }
-
-  // Read a new packet if there is no unconsumed one.
-  if (ssl_read_buffer_len(ssl) == 0) {
-    int read_ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
-    if (read_ret < 0 && dtls1_is_timer_expired(ssl)) {
-      // Historically, timeouts were handled implicitly if the caller did not
-      // handle them.
-      //
-      // TODO(davidben): This was to support blocking sockets but affected
-      // non-blocking sockets. Can it be removed?
-      int timeout_ret = DTLSv1_handle_timeout(ssl);
-      if (timeout_ret <= 0) {
-        return timeout_ret;
+  for (;;) {
+    Span<uint8_t> body;
+    uint8_t type, alert;
+    size_t consumed;
+    enum ssl_open_record_t open_ret = dtls_open_record(
+        ssl, &type, &body, &consumed, &alert, ssl_read_buffer(ssl));
+    if (open_ret != ssl_open_record_partial) {
+      ssl_read_buffer_consume(ssl, consumed);
+    }
+    switch (open_ret) {
+      case ssl_open_record_partial: {
+        assert(ssl_read_buffer(ssl).empty());
+        int read_ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
+        if (read_ret <= 0) {
+          return read_ret;
+        }
+        continue;
       }
-      goto again;
-    }
-    if (read_ret <= 0) {
-      return read_ret;
-    }
-  }
-  assert(ssl_read_buffer_len(ssl) > 0);
 
-  CBS body;
-  uint8_t type, alert;
-  size_t consumed;
-  enum ssl_open_record_t open_ret =
-      dtls_open_record(ssl, &type, &body, &consumed, &alert,
-                       ssl_read_buffer(ssl), ssl_read_buffer_len(ssl));
-  ssl_read_buffer_consume(ssl, consumed);
-  switch (open_ret) {
-    case ssl_open_record_partial:
-      // Impossible in DTLS.
-      break;
+      case ssl_open_record_success: {
+        if (body.size() > 0xffff) {
+          OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+          return -1;
+        }
 
-    case ssl_open_record_success: {
-      if (CBS_len(&body) > 0xffff) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+        SSL3_RECORD *rr = &ssl->s3->rrec;
+        rr->type = type;
+        rr->length = static_cast<uint16_t>(body.size());
+        rr->data = body.data();
+        return 1;
+      }
+
+      case ssl_open_record_discard:
+        continue;
+
+      case ssl_open_record_close_notify:
+        return 0;
+
+      case ssl_open_record_error:
+        if (alert != 0) {
+          ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+        }
         return -1;
-      }
-
-      SSL3_RECORD *rr = &ssl->s3->rrec;
-      rr->type = type;
-      rr->length = (uint16_t)CBS_len(&body);
-      rr->data = (uint8_t *)CBS_data(&body);
-      return 1;
     }
 
-    case ssl_open_record_discard:
-      goto again;
-
-    case ssl_open_record_close_notify:
-      return 0;
-
-    case ssl_open_record_fatal_alert:
-      return -1;
-
-    case ssl_open_record_error:
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-      return -1;
+    assert(0);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
-
-  assert(0);
-  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return -1;
 }
 
 int dtls1_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf,
@@ -228,7 +203,7 @@
     struct hm_header_st msg_hdr;
     CBS_init(&cbs, rr->data, rr->length);
     if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
       return -1;
     }
@@ -255,7 +230,7 @@
   }
 
   if (rr->type != SSL3_RT_APPLICATION_DATA) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
     return -1;
   }
@@ -293,8 +268,8 @@
   // alerts also aren't delivered reliably, so we may even time out because the
   // peer never received our close_notify. Report to the caller that the channel
   // has fully shut down.
-  if (ssl->s3->recv_shutdown == ssl_shutdown_none) {
-    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+  if (ssl->s3->read_shutdown == ssl_shutdown_none) {
+    ssl->s3->read_shutdown = ssl_shutdown_close_notify;
   }
 }
 
@@ -369,8 +344,7 @@
     BIO_flush(ssl->wbio);
   }
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert,
-                      2);
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert);
 
   int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
   ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert);
diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc
index 050246d..daaec2d 100644
--- a/src/ssl/dtls_method.cc
+++ b/src/ssl/dtls_method.cc
@@ -83,10 +83,10 @@
 }
 
 static int dtls1_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
-  // Cipher changes are illegal when there are buffered incoming messages.
-  if (dtls_has_incoming_messages(ssl) || ssl->d1->has_change_cipher_spec) {
+  // Cipher changes are forbidden if the current epoch has leftover data.
+  if (dtls_has_unprocessed_handshake_data(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     return 0;
   }
 
diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc
index 5009f04..2bf1d42 100644
--- a/src/ssl/dtls_record.cc
+++ b/src/ssl/dtls_record.cc
@@ -174,14 +174,27 @@
   }
 }
 
-enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type,
+                                        Span<uint8_t> *out,
                                         size_t *out_consumed,
-                                        uint8_t *out_alert, uint8_t *in,
-                                        size_t in_len) {
+                                        uint8_t *out_alert, Span<uint8_t> in) {
   *out_consumed = 0;
+  switch (ssl->s3->read_shutdown) {
+    case ssl_shutdown_none:
+      break;
+    case ssl_shutdown_fatal_alert:
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+      *out_alert = 0;
+      return ssl_open_record_error;
+    case ssl_shutdown_close_notify:
+      return ssl_open_record_close_notify;
+  }
 
-  CBS cbs;
-  CBS_init(&cbs, in, in_len);
+  if (in.empty()) {
+    return ssl_open_record_partial;
+  }
+
+  CBS cbs = CBS(in);
 
   // Decode the record.
   uint8_t type;
@@ -194,7 +207,7 @@
       !CBS_get_u16_length_prefixed(&cbs, &body) ||
       CBS_len(&body) > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
     // The record header was incomplete or malformed. Drop the entire packet.
-    *out_consumed = in_len;
+    *out_consumed = in.size();
     return ssl_open_record_discard;
   }
 
@@ -209,12 +222,12 @@
 
   if (!version_ok) {
     // The record header was incomplete or malformed. Drop the entire packet.
-    *out_consumed = in_len;
+    *out_consumed = in.size();
     return ssl_open_record_discard;
   }
 
-  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in,
-                      DTLS1_RT_HEADER_LENGTH);
+  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER,
+                      in.subspan(0, DTLS1_RT_HEADER_LENGTH));
 
   uint16_t epoch = (((uint16_t)sequence[0]) << 8) | sequence[1];
   if (epoch != ssl->d1->r_epoch ||
@@ -223,14 +236,14 @@
     // |epoch| is the next epoch, the record could be buffered for later. For
     // simplicity, drop it and expect retransmit to handle it later; DTLS must
     // handle packet loss anyway.
-    *out_consumed = in_len - CBS_len(&cbs);
+    *out_consumed = in.size() - CBS_len(&cbs);
     return ssl_open_record_discard;
   }
 
-  // Decrypt the body in-place.
-  if (!ssl->s3->aead_read_ctx->Open(out, type, version, sequence,
-                                    (uint8_t *)CBS_data(&body),
-                                    CBS_len(&body))) {
+  // discard the body in-place.
+  if (!ssl->s3->aead_read_ctx->Open(
+          out, type, version, sequence,
+          MakeSpan(const_cast<uint8_t *>(CBS_data(&body)), CBS_len(&body)))) {
     // Bad packets are silently dropped in DTLS. See section 4.2.1 of RFC 6347.
     // Clear the error queue of any errors decryption may have added. Drop the
     // entire packet as it must not have come from the peer.
@@ -238,13 +251,13 @@
     // TODO(davidben): This doesn't distinguish malloc failures from encryption
     // failures.
     ERR_clear_error();
-    *out_consumed = in_len - CBS_len(&cbs);
+    *out_consumed = in.size() - CBS_len(&cbs);
     return ssl_open_record_discard;
   }
-  *out_consumed = in_len - CBS_len(&cbs);
+  *out_consumed = in.size() - CBS_len(&cbs);
 
   // Check the plaintext length.
-  if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) {
+  if (out->size() > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
@@ -256,7 +269,7 @@
   // useful if we also limit discarded packets.
 
   if (type == SSL3_RT_ALERT) {
-    return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out));
+    return ssl_process_alert(ssl, out_alert, *out);
   }
 
   ssl->s3->warning_alert_count = 0;
@@ -338,8 +351,8 @@
 
   *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len;
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out,
-                      DTLS1_RT_HEADER_LENGTH);
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER,
+                      MakeSpan(out, DTLS1_RT_HEADER_LENGTH));
 
   return 1;
 }
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index a03b140..80b64f3 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -162,7 +162,7 @@
 
 int ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) {
   if (msg.type != type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     ERR_add_error_dataf("got type %d, wanted type %d", msg.type, type);
     return 0;
@@ -194,7 +194,7 @@
     return kMaxMessageLen;
   }
 
-  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) < TLS1_3_VERSION) {
     // In TLS 1.2 and below, the largest acceptable post-handshake message is
     // a HelloRequest.
     return 0;
@@ -206,8 +206,7 @@
     return 1;
   }
 
-  // Clients must accept NewSessionTicket and CertificateRequest, so allow the
-  // default size.
+  // Clients must accept NewSessionTicket, so allow the default size.
   return kMaxMessageLen;
 }
 
@@ -217,7 +216,7 @@
     return true;
   }
 
-  return hs->transcript.Update(CBS_data(&msg.raw), CBS_len(&msg.raw));
+  return hs->transcript.Update(msg.raw);
 }
 
 int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
@@ -293,7 +292,7 @@
     if (sk_CRYPTO_BUFFER_num(prev_session->certs) !=
         sk_CRYPTO_BUFFER_num(hs->new_session->certs)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_verify_invalid;
     }
 
@@ -307,7 +306,7 @@
                          CRYPTO_BUFFER_data(new_cert),
                          CRYPTO_BUFFER_len(old_cert)) != 0) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
         return ssl_verify_invalid;
       }
     }
@@ -347,7 +346,7 @@
 
   if (ret == ssl_verify_invalid) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
   }
 
   return ret;
@@ -363,7 +362,7 @@
                              : ssl->s3->client_random[index];
   // The first four bytes of server_random are a timestamp prior to TLS 1.3, but
   // servers have no fields to GREASE until TLS 1.3.
-  assert(!ssl->server || ssl3_protocol_version(ssl) >= TLS1_3_VERSION);
+  assert(!ssl->server || ssl_protocol_version(ssl) >= TLS1_3_VERSION);
   // This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16.
   ret = (ret & 0xf0) | 0x0a;
   ret |= ret << 8;
@@ -395,7 +394,7 @@
   finished_ok = 1;
 #endif
   if (!finished_ok) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     return ssl_hs_error;
   }
@@ -421,13 +420,73 @@
   return ssl_hs_ok;
 }
 
+bool ssl_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;
+  if (!hs->transcript.GetFinishedMAC(finished, &finished_len, session,
+                                     ssl->server)) {
+    return 0;
+  }
+
+  // Log the master secret, if logging is enabled.
+  if (!ssl_log_secret(ssl, "CLIENT_RANDOM",
+                      session->master_key,
+                      session->master_key_length)) {
+    return 0;
+  }
+
+  // Copy the Finished so we can use it for renegotiation checks.
+  if (ssl->version != SSL3_VERSION) {
+    if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
+        finished_len > sizeof(ssl->s3->previous_server_finished)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return 0;
+    }
+
+    if (ssl->server) {
+      OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
+      ssl->s3->previous_server_finished_len = finished_len;
+    } else {
+      OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
+      ssl->s3->previous_client_finished_len = finished_len;
+    }
+  }
+
+  ScopedCBB cbb;
+  CBB body;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_FINISHED) ||
+      !CBB_add_bytes(&body, finished, finished_len) ||
+      !ssl_add_message_cbb(ssl, cbb.get())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  return 1;
+}
+
+bool ssl_output_cert_chain(SSL *ssl) {
+  ScopedCBB cbb;
+  CBB body;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
+      !ssl_add_cert_chain(ssl, &body) ||
+      !ssl_add_message_cbb(ssl, cbb.get())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  return true;
+}
+
 int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) {
   SSL *const ssl = hs->ssl;
   for (;;) {
     // Resolve the operation the handshake was waiting on.
     switch (hs->wait) {
       case ssl_hs_error:
-        OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+        ERR_restore_state(hs->error.get());
         return -1;
 
       case ssl_hs_flush: {
@@ -531,8 +590,7 @@
     // Run the state machine again.
     hs->wait = ssl->do_handshake(hs);
     if (hs->wait == ssl_hs_error) {
-      // Don't loop around to avoid a stray |SSL_R_SSL_HANDSHAKE_FAILURE| the
-      // first time around.
+      hs->error.reset(ERR_save_state());
       return -1;
     }
     if (hs->wait == ssl_hs_ok) {
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index b2d5384..5466b56 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -358,7 +358,7 @@
   SSL *const ssl = hs->ssl;
   if (msg.type != SSL3_MT_SERVER_HELLO &&
       msg.type != SSL3_MT_HELLO_RETRY_REQUEST) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return 0;
   }
@@ -366,7 +366,7 @@
   CBS server_hello = msg.body;
   if (!CBS_get_u16(&server_hello, out)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -382,7 +382,7 @@
       !CBS_skip(&server_hello, sid_length + 2 /* cipher_suite */ +
                 1 /* compression_method */)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -395,7 +395,7 @@
   if (!CBS_get_u16_length_prefixed(&server_hello, &extensions) ||
       CBS_len(&server_hello) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -410,14 +410,14 @@
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             1 /* ignore unknown */)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
   if (have_supported_versions &&
       (!CBS_get_u16(&supported_versions, out) ||
        CBS_len(&supported_versions) != 0)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -542,7 +542,7 @@
       CBS_len(&cookie) > sizeof(ssl->d1->cookie) ||
       CBS_len(&hello_verify_request) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -578,7 +578,7 @@
 
   if (!ssl_supports_version(hs, server_version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return ssl_hs_error;
   }
 
@@ -591,11 +591,11 @@
     ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
   } else if (server_version != ssl->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return ssl_hs_error;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     hs->state = state_tls13;
     return ssl_hs_ok;
   }
@@ -609,7 +609,7 @@
   // fallback described in draft-ietf-tls-tls13-18 appendix C.3.
   if (hs->early_data_offered) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_ON_EARLY_DATA);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return ssl_hs_error;
   }
 
@@ -627,7 +627,7 @@
       !CBS_get_u16(&server_hello, &cipher_suite) ||
       !CBS_get_u8(&server_hello, &compression_method)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -648,7 +648,7 @@
     // fill out.
     ssl_set_session(ssl, NULL);
     if (!ssl_get_new_session(hs, 0 /* client */)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     // Note: session_id could be empty.
@@ -661,7 +661,7 @@
   if (cipher == NULL) {
     // unknown cipher
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
@@ -669,30 +669,30 @@
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(ssl, &mask_a, &mask_k);
   if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
-      SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
+      SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
       !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
   if (ssl->session != NULL) {
     if (ssl->session->ssl_version != ssl->version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
     if (ssl->session->cipher != cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
     if (!ssl_session_is_context_valid(ssl, ssl->session)) {
       // This is actually a client application bug.
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
   } else {
@@ -702,9 +702,9 @@
 
   // Now that the cipher is known, initialize the handshake hash and hash the
   // ServerHello.
-  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), hs->new_cipher) ||
+  if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
       !ssl_hash_message(hs, msg)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -719,7 +719,7 @@
   // Only the NULL compression algorithm is supported.
   if (compression_method != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
@@ -733,7 +733,7 @@
   if (CBS_len(&server_hello) != 0) {
     // wrong packet length
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -744,7 +744,7 @@
     } else {
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION);
     }
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     return ssl_hs_error;
   }
 
@@ -792,7 +792,7 @@
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
   if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey, NULL, &body,
                             ssl->ctx->pool)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
   sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
@@ -802,14 +802,14 @@
       CBS_len(&body) != 0 ||
       !ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
   if (!ssl_check_leaf_certificate(
           hs, hs->peer_pubkey.get(),
           sk_CRYPTO_BUFFER_value(hs->new_session->certs, 0))) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
@@ -851,7 +851,7 @@
       CBS_len(&ocsp_response) == 0 ||
       CBS_len(&certificate_status) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -859,7 +859,7 @@
   hs->new_session->ocsp_response =
       CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool);
   if (hs->new_session->ocsp_response == nullptr) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -900,7 +900,7 @@
     // Some ciphers (pure PSK) have an optional ServerKeyExchange message.
     if (ssl_cipher_requires_server_key_exchange(hs->new_cipher)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return ssl_hs_error;
     }
 
@@ -922,21 +922,21 @@
     if (!CBS_get_u16_length_prefixed(&server_key_exchange,
                                      &psk_identity_hint)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
-    // Store PSK identity hint for later use, hint is used in
-    // ssl3_send_client_key_exchange.  Assume that the maximum length of a PSK
-    // identity hint can be as long as the maximum length of a PSK identity.
-    // Also do not allow NULL characters; identities are saved as C strings.
+    // Store the PSK identity hint for the ClientKeyExchange. Assume that the
+    // maximum length of a PSK identity hint can be as long as the maximum
+    // length of a PSK identity. Also do not allow NULL characters; identities
+    // are saved as C strings.
     //
     // TODO(davidben): Should invalid hints be ignored? It's a hint rather than
     // a specific identity.
     if (CBS_len(&psk_identity_hint) > PSK_MAX_IDENTITY_LEN ||
         CBS_contains_zero_byte(&psk_identity_hint)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
       return ssl_hs_error;
     }
 
@@ -949,7 +949,7 @@
     if (CBS_len(&psk_identity_hint) != 0 &&
         !CBS_strdup(&psk_identity_hint, &raw)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     hs->peer_psk_identity_hint.reset(raw);
@@ -965,7 +965,7 @@
         !CBS_get_u16(&server_key_exchange, &group_id) ||
         !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
     hs->new_session->group_id = group_id;
@@ -973,7 +973,7 @@
     // Ensure the group is consistent with preferences.
     if (!tls1_check_group_id(ssl, group_id)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
@@ -985,7 +985,7 @@
     }
   } else if (!(alg_k & SSL_kPSK)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     return ssl_hs_error;
   }
 
@@ -999,22 +999,22 @@
   // ServerKeyExchange should be signed by the server's public key.
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
     uint16_t signature_algorithm = 0;
-    if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+    if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
       if (!CBS_get_u16(&server_key_exchange, &signature_algorithm)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return ssl_hs_error;
       }
       uint8_t alert = SSL_AD_DECODE_ERROR;
       if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
         return ssl_hs_error;
       }
       hs->new_session->peer_signature_algorithm = signature_algorithm;
     } else if (!tls1_get_legacy_signature_algorithm(&signature_algorithm,
                                                     hs->peer_pubkey.get())) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
       return ssl_hs_error;
     }
 
@@ -1023,13 +1023,12 @@
     if (!CBS_get_u16_length_prefixed(&server_key_exchange, &signature) ||
         CBS_len(&server_key_exchange) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
     ScopedCBB transcript;
-    uint8_t *transcript_data;
-    size_t transcript_len;
+    Array<uint8_t> transcript_data;
     if (!CBB_init(transcript.get(),
                   2 * SSL3_RANDOM_SIZE + CBS_len(&parameter)) ||
         !CBB_add_bytes(transcript.get(), ssl->s3->client_random,
@@ -1038,25 +1037,22 @@
                        SSL3_RANDOM_SIZE) ||
         !CBB_add_bytes(transcript.get(), CBS_data(&parameter),
                        CBS_len(&parameter)) ||
-        !CBB_finish(transcript.get(), &transcript_data, &transcript_len)) {
+        !CBBFinishArray(transcript.get(), &transcript_data)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
 
-    int sig_ok = ssl_public_key_verify(
-        ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        hs->peer_pubkey.get(), transcript_data, transcript_len);
-    OPENSSL_free(transcript_data);
-
+    bool sig_ok = ssl_public_key_verify(ssl, signature, signature_algorithm,
+                                        hs->peer_pubkey.get(), transcript_data);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-    sig_ok = 1;
+    sig_ok = true;
     ERR_clear_error();
 #endif
     if (!sig_ok) {
       // bad signature
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
       return ssl_hs_error;
     }
   } else {
@@ -1065,7 +1061,7 @@
 
     if (CBS_len(&server_key_exchange) > 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_EXTRA_DATA_IN_MESSAGE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
   }
@@ -1104,21 +1100,21 @@
   // Get the certificate types.
   CBS body = msg.body, certificate_types;
   if (!CBS_get_u8_length_prefixed(&body, &certificate_types)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
 
   if (!hs->certificate_types.CopyFrom(certificate_types)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
     CBS supported_signature_algorithms;
     if (!CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) ||
         !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       return ssl_hs_error;
     }
@@ -1128,12 +1124,12 @@
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names =
       ssl_parse_client_CA_list(ssl, &alert, &body);
   if (!ca_names) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
   if (CBS_len(&body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
@@ -1161,7 +1157,7 @@
 
   // ServerHelloDone is empty.
   if (CBS_len(&msg.body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
@@ -1184,7 +1180,7 @@
   if (ssl->cert->cert_cb != NULL) {
     int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
     if (rv == 0) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
       return ssl_hs_error;
     }
@@ -1210,7 +1206,7 @@
   }
 
   if (!ssl_on_certificate_selected(hs) ||
-      !ssl3_output_cert_chain(ssl)) {
+      !ssl_output_cert_chain(ssl)) {
     return ssl_hs_error;
   }
 
@@ -1251,7 +1247,7 @@
                                  identity, sizeof(identity), psk, sizeof(psk));
     if (psk_len == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
       return ssl_hs_error;
     }
     assert(psk_len <= PSK_MAX_PSK_LEN);
@@ -1319,7 +1315,7 @@
     // Compute the premaster.
     uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!hs->key_share->Accept(&child, &pms, &alert, hs->peer_key)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
     if (!CBB_flush(&body)) {
@@ -1337,7 +1333,7 @@
     }
     OPENSSL_memset(pms.data(), 0, pms.size());
   } else {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return ssl_hs_error;
   }
@@ -1347,19 +1343,15 @@
   if (alg_a & SSL_aPSK) {
     ScopedCBB pms_cbb;
     CBB child;
-    uint8_t *new_pms;
-    size_t new_pms_len;
-
     if (!CBB_init(pms_cbb.get(), 2 + psk_len + 2 + pms.size()) ||
         !CBB_add_u16_length_prefixed(pms_cbb.get(), &child) ||
         !CBB_add_bytes(&child, pms.data(), pms.size()) ||
         !CBB_add_u16_length_prefixed(pms_cbb.get(), &child) ||
         !CBB_add_bytes(&child, psk, psk_len) ||
-        !CBB_finish(pms_cbb.get(), &new_pms, &new_pms_len)) {
+        !CBBFinishArray(pms_cbb.get(), &pms)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       return ssl_hs_error;
     }
-    pms.Reset(new_pms, new_pms_len);
   }
 
   // The message must be added to the finished hash before calculating the
@@ -1399,7 +1391,7 @@
   if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
     return ssl_hs_error;
   }
-  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
     // Write out the digest type in TLS 1.2.
     if (!CBB_add_u16(&body, signature_algorithm)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1418,7 +1410,7 @@
   size_t sig_len = max_sig_len;
   // 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) {
+  if (ssl_protocol_version(ssl) == SSL3_VERSION) {
     if (ssl->cert->key_method != NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
       return ssl_hs_error;
@@ -1438,9 +1430,9 @@
       return ssl_hs_error;
     }
   } else {
-    switch (ssl_private_key_sign(
-        hs, ptr, &sig_len, max_sig_len, signature_algorithm,
-        hs->transcript.buffer_data(), hs->transcript.buffer_len())) {
+    switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
+                                 signature_algorithm,
+                                 hs->transcript.buffer())) {
       case ssl_private_key_success:
         break;
       case ssl_private_key_failure:
@@ -1511,7 +1503,7 @@
     }
   }
 
-  if (!ssl3_send_finished(hs)) {
+  if (!ssl_send_finished(hs)) {
     return ssl_hs_error;
   }
 
@@ -1519,6 +1511,18 @@
   return ssl_hs_flush;
 }
 
+static bool can_false_start(const SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+
+  // False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN.
+  return !SSL_is_dtls(ssl) &&
+         SSL_version(ssl) == TLS1_2_VERSION &&
+         (ssl->s3->alpn_selected != NULL ||
+          ssl->s3->next_proto_negotiated != NULL) &&
+         hs->new_cipher->algorithm_mkey == SSL_kECDHE &&
+         hs->new_cipher->algorithm_mac == SSL_AEAD;
+}
+
 static enum ssl_hs_wait_t do_finish_flight(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (ssl->session != NULL) {
@@ -1536,7 +1540,7 @@
   hs->state = state_read_session_ticket;
 
   if ((SSL_get_mode(ssl) & SSL_MODE_ENABLE_FALSE_START) &&
-      ssl3_can_false_start(ssl) &&
+      can_false_start(hs) &&
       // No False Start on renegotiation (would complicate the state machine).
       !ssl->s3->initial_handshake_complete) {
     hs->in_false_start = true;
@@ -1570,7 +1574,7 @@
   if (!CBS_get_u32(&new_session_ticket, &tlsext_tick_lifetime_hint) ||
       !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
       CBS_len(&new_session_ticket) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 5507250..7b282d8 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -281,7 +281,7 @@
   // Handle FALLBACK_SCSV.
   if (ssl_client_cipher_list_contains_cipher(client_hello,
                                              SSL3_CK_FALLBACK_SCSV & 0xffff) &&
-      ssl3_protocol_version(ssl) < hs->max_version) {
+      ssl_protocol_version(ssl) < hs->max_version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
     *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
     return 0;
@@ -392,8 +392,8 @@
 
     size_t cipher_index;
     if (// Check if the cipher is supported for the current version.
-        SSL_CIPHER_get_min_version(c) <= ssl3_protocol_version(ssl) &&
-        ssl3_protocol_version(ssl) <= SSL_CIPHER_get_max_version(c) &&
+        SSL_CIPHER_get_min_version(c) <= ssl_protocol_version(ssl) &&
+        ssl_protocol_version(ssl) <= SSL_CIPHER_get_max_version(c) &&
         // Check the cipher is supported for the server configuration.
         (c->algorithm_mkey & mask_k) &&
         (c->algorithm_auth & mask_a) &&
@@ -444,7 +444,7 @@
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -457,7 +457,7 @@
       case ssl_select_cert_error:
         // Connection rejected.
         OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         return ssl_hs_error;
 
       default:
@@ -472,7 +472,7 @@
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!negotiate_version(hs, &alert, &client_hello)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -488,10 +488,10 @@
   // 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 &&
+      (ssl_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);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
@@ -518,7 +518,7 @@
     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);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     if (rv < 0) {
@@ -530,7 +530,7 @@
     return ssl_hs_error;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     // Jump to the TLS 1.3 state machine.
     hs->state = state_tls13;
     return ssl_hs_ok;
@@ -547,7 +547,7 @@
       ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
   if (hs->new_cipher == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     return ssl_hs_error;
   }
 
@@ -592,7 +592,7 @@
       // A ClientHello without EMS that attempts to resume a session with EMS
       // is fatal to the connection.
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
       return ssl_hs_error;
     }
 
@@ -626,7 +626,7 @@
       ssl->ctx->dos_protection_cb(&client_hello) == 0) {
     // Connection rejected for DOS reasons.
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -656,15 +656,15 @@
   // deferred. Complete it now.
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
   // Now that all parameters are known, initialize the handshake hash and hash
   // the ClientHello.
-  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), hs->new_cipher) ||
+  if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
       !ssl_hash_message(hs, msg)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -749,7 +749,7 @@
       return ssl_hs_error;
     }
 
-    if (!ssl3_output_cert_chain(ssl)) {
+    if (!ssl_output_cert_chain(ssl)) {
       return ssl_hs_error;
     }
 
@@ -800,7 +800,7 @@
       uint16_t group_id;
       if (!tls1_get_shared_group(hs, &group_id)) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         return ssl_hs_error;
        }
       hs->new_session->group_id = group_id;
@@ -849,7 +849,7 @@
   // Add a signature.
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
     if (!ssl_has_private_key(ssl)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
 
@@ -858,10 +858,10 @@
     if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
       return ssl_hs_error;
     }
-    if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+    if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
       if (!CBB_add_u16(&body, signature_algorithm)) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return ssl_hs_error;
       }
     }
@@ -876,8 +876,7 @@
 
     size_t sig_len;
     switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
-                                 signature_algorithm, hs->server_params.data(),
-                                 hs->server_params.size())) {
+                                 signature_algorithm, hs->server_params)) {
       case ssl_private_key_success:
         if (!CBB_did_write(&child, sig_len)) {
           return ssl_hs_error;
@@ -912,9 +911,9 @@
                                    SSL3_MT_CERTIFICATE_REQUEST) ||
         !CBB_add_u8_length_prefixed(&body, &cert_types) ||
         !CBB_add_u8(&cert_types, SSL3_CT_RSA_SIGN) ||
-        (ssl3_protocol_version(ssl) >= TLS1_VERSION &&
+        (ssl_protocol_version(ssl) >= TLS1_VERSION &&
          !CBB_add_u8(&cert_types, TLS_CT_ECDSA_SIGN)) ||
-        (ssl3_protocol_version(ssl) >= TLS1_2_VERSION &&
+        (ssl_protocol_version(ssl) >= TLS1_2_VERSION &&
          (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
           !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb))) ||
         !ssl_add_client_CA_list(ssl, &body) ||
@@ -955,7 +954,7 @@
       // certificate.
       if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         return ssl_hs_error;
       }
 
@@ -967,7 +966,7 @@
     }
 
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     return ssl_hs_error;
   }
 
@@ -983,7 +982,7 @@
                                 ? hs->new_session->peer_sha256
                                 : NULL,
                             &certificate_msg, ssl->ctx->pool)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
   sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
@@ -992,7 +991,7 @@
   if (CBS_len(&certificate_msg) != 0 ||
       !ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -1004,14 +1003,14 @@
     // Certificate message.
     if (ssl->version == SSL3_VERSION) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
       return ssl_hs_error;
     }
 
     if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
       // Fail for TLS only if we required a certificate
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
       return ssl_hs_error;
     }
 
@@ -1068,20 +1067,20 @@
     if (!CBS_get_u16_length_prefixed(&client_key_exchange, &psk_identity) ||
         ((alg_k & SSL_kPSK) && CBS_len(&client_key_exchange) != 0)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
     if (CBS_len(&psk_identity) > PSK_MAX_IDENTITY_LEN ||
         CBS_contains_zero_byte(&psk_identity)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
     if (!CBS_strdup(&psk_identity, &hs->new_session->psk_identity)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
   }
@@ -1095,7 +1094,7 @@
                                        &encrypted_premaster_secret) ||
           CBS_len(&client_key_exchange) != 0) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return ssl_hs_error;
       }
     } else {
@@ -1113,8 +1112,7 @@
     size_t decrypt_len;
     switch (ssl_private_key_decrypt(hs, decrypt_buf.data(), &decrypt_len,
                                     decrypt_buf.size(),
-                                    CBS_data(&encrypted_premaster_secret),
-                                    CBS_len(&encrypted_premaster_secret))) {
+                                    encrypted_premaster_secret)) {
       case ssl_private_key_success:
         break;
       case ssl_private_key_failure:
@@ -1125,7 +1123,7 @@
 
     if (decrypt_len != decrypt_buf.size()) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
       return ssl_hs_error;
     }
 
@@ -1140,7 +1138,7 @@
     // publicly invalid.
     if (decrypt_len < 11 + premaster_secret.size()) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
       return ssl_hs_error;
     }
 
@@ -1172,14 +1170,14 @@
     if (!CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key) ||
         CBS_len(&client_key_exchange) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
     // Compute the premaster.
     uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!hs->key_share->Finish(&premaster_secret, &alert, peer_key)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
 
@@ -1187,7 +1185,7 @@
     hs->key_share.reset();
   } else if (!(alg_k & SSL_kPSK)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     return ssl_hs_error;
   }
 
@@ -1196,7 +1194,7 @@
   if (alg_a & SSL_aPSK) {
     if (ssl->psk_server_callback == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
 
@@ -1206,12 +1204,12 @@
         ssl, hs->new_session->psk_identity, psk, sizeof(psk));
     if (psk_len > PSK_MAX_PSK_LEN) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     } else if (psk_len == 0) {
       // PSK related to the given identity not found.
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_PSK_IDENTITY);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_PSK_IDENTITY);
       return ssl_hs_error;
     }
 
@@ -1226,8 +1224,6 @@
 
     ScopedCBB new_premaster;
     CBB child;
-    uint8_t *new_data;
-    size_t new_len;
     if (!CBB_init(new_premaster.get(),
                   2 + psk_len + 2 + premaster_secret.size()) ||
         !CBB_add_u16_length_prefixed(new_premaster.get(), &child) ||
@@ -1235,12 +1231,10 @@
                        premaster_secret.size()) ||
         !CBB_add_u16_length_prefixed(new_premaster.get(), &child) ||
         !CBB_add_bytes(&child, psk, psk_len) ||
-        !CBB_finish(new_premaster.get(), &new_data, &new_len)) {
+        !CBBFinishArray(new_premaster.get(), &premaster_secret)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       return ssl_hs_error;
     }
-
-    premaster_secret.Reset(new_data, new_len);
   }
 
   if (!ssl_hash_message(hs, msg)) {
@@ -1285,22 +1279,22 @@
 
   // Determine the signature algorithm.
   uint16_t signature_algorithm = 0;
-  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
     if (!CBS_get_u16(&certificate_verify, &signature_algorithm)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
     uint8_t alert = SSL_AD_DECODE_ERROR;
     if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
     hs->new_session->peer_signature_algorithm = signature_algorithm;
   } else if (!tls1_get_legacy_signature_algorithm(&signature_algorithm,
                                                   hs->peer_pubkey.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
     return ssl_hs_error;
   }
 
@@ -1308,14 +1302,14 @@
   if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) ||
       CBS_len(&certificate_verify) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
-  int sig_ok;
+  bool sig_ok;
   // 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) {
+  if (ssl_protocol_version(ssl) == SSL3_VERSION) {
     uint8_t digest[EVP_MAX_MD_SIZE];
     size_t digest_len;
     if (!hs->transcript.GetSSL3CertVerifyHash(
@@ -1330,19 +1324,18 @@
              EVP_PKEY_verify(pctx.get(), CBS_data(&signature),
                              CBS_len(&signature), digest, digest_len);
   } else {
-    sig_ok = ssl_public_key_verify(
-        ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        hs->peer_pubkey.get(), hs->transcript.buffer_data(),
-        hs->transcript.buffer_len());
+    sig_ok =
+        ssl_public_key_verify(ssl, signature, signature_algorithm,
+                              hs->peer_pubkey.get(), hs->transcript.buffer());
   }
 
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  sig_ok = 1;
+  sig_ok = true;
   ERR_clear_error();
 #endif
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     return ssl_hs_error;
   }
 
@@ -1395,7 +1388,7 @@
       !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
       CBS_len(&next_protocol) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -1493,7 +1486,7 @@
 
   if (!ssl->method->add_change_cipher_spec(ssl) ||
       !tls1_change_cipher_state(hs, evp_aead_seal) ||
-      !ssl3_send_finished(hs)) {
+      !ssl_send_finished(hs)) {
     return ssl_hs_error;
   }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 4247425..edbf4eb 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -153,11 +153,13 @@
 
 #include <openssl/aead.h>
 #include <openssl/err.h>
+#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/ssl.h>
 #include <openssl/span.h>
 #include <openssl/stack.h>
 
+#include "../crypto/err/internal.h"
 #include "../crypto/internal.h"
 
 
@@ -171,11 +173,10 @@
 #endif
 
 
-typedef struct cert_st CERT;
-
 namespace bssl {
 
 struct SSL_HANDSHAKE;
+struct SSL_PROTOCOL_METHOD;
 
 // C++ utilities.
 
@@ -237,6 +238,14 @@
 #define PURE_VIRTUAL { abort(); }
 #endif
 
+// CONSTEXPR_ARRAY works around a VS 2015 bug where ranged for loops don't work
+// on constexpr arrays.
+#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1910
+#define CONSTEXPR_ARRAY const
+#else
+#define CONSTEXPR_ARRAY constexpr
+#endif
+
 // Array<T> is an owning array of elements of |T|.
 template <typename T>
 class Array {
@@ -379,9 +388,9 @@
 bool ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                            uint16_t *out_version, const CBS *peer_versions);
 
-// ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
+// ssl_protocol_version returns |ssl|'s protocol version. It is an error to
 // call this function before the version is determined.
-uint16_t ssl3_protocol_version(const SSL *ssl);
+uint16_t ssl_protocol_version(const SSL *ssl);
 
 // ssl_is_resumption_experiment returns whether the version corresponds to a
 // TLS 1.3 resumption experiment.
@@ -536,10 +545,10 @@
   // to call this function after the handshake buffer is released.
   bool InitHash(uint16_t version, const SSL_CIPHER *cipher);
 
-  const uint8_t *buffer_data() const {
-    return reinterpret_cast<const uint8_t *>(buffer_->data);
+  Span<const uint8_t> buffer() {
+    return MakeConstSpan(reinterpret_cast<const uint8_t *>(buffer_->data),
+                         buffer_->length);
   }
-  size_t buffer_len() const { return buffer_->length; }
 
   // FreeBuffer releases the handshake buffer. Subsequent calls to
   // |Update| will not update the handshake buffer.
@@ -554,7 +563,7 @@
 
   // Update adds |in| to the handshake buffer and handshake hash, whichever is
   // enabled. It returns true on success and false on failure.
-  bool Update(const uint8_t *in, size_t in_len);
+  bool Update(Span<const uint8_t> in);
 
   // GetHash writes the handshake hash to |out| which must have room for at
   // least |DigestLen| bytes. On success, it returns true and sets |*out_len| to
@@ -656,12 +665,11 @@
   bool SuffixLen(size_t *out_suffix_len, size_t in_len,
                  size_t extra_in_len) const;
 
-  // Open authenticates and decrypts |in_len| bytes from |in| in-place. On
-  // success, it sets |*out| to the plaintext in |in| and returns true.
-  // Otherwise, it returns false. The output will always be |ExplicitNonceLen|
-  // bytes ahead of |in|.
-  bool Open(CBS *out, uint8_t type, uint16_t record_version,
-            const uint8_t seqnum[8], uint8_t *in, size_t in_len);
+  // Open authenticates and decrypts |in| in-place. On success, it sets |*out|
+  // to the plaintext in |in| and returns true.  Otherwise, it returns
+  // false. The output will always be |ExplicitNonceLen| bytes ahead of |in|.
+  bool Open(Span<uint8_t> *out, uint8_t type, uint16_t record_version,
+            const uint8_t seqnum[8], Span<uint8_t> in);
 
   // Seal encrypts and authenticates |in_len| bytes from |in| and writes the
   // result to |out|. It returns true on success and false on error.
@@ -763,7 +771,6 @@
   ssl_open_record_discard,
   ssl_open_record_partial,
   ssl_open_record_close_notify,
-  ssl_open_record_fatal_alert,
   ssl_open_record_error,
 };
 
@@ -785,21 +792,22 @@
 // If a record was successfully processed but should be discarded, it returns
 // |ssl_open_record_discard|.
 //
-// If a record was successfully processed but is a close_notify or fatal alert,
-// it returns |ssl_open_record_close_notify| or |ssl_open_record_fatal_alert|.
+// If a record was successfully processed but is a close_notify, it returns
+// |ssl_open_record_close_notify|.
 //
-// On failure, it returns |ssl_open_record_error| and sets |*out_alert| to an
-// alert to emit.
-enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
-                                       size_t *out_consumed, uint8_t *out_alert,
-                                       uint8_t *in, size_t in_len);
+// On failure or fatal alert, it returns |ssl_open_record_error| and sets
+// |*out_alert| to an alert to emit, or zero if no alert should be emitted.
+enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type,
+                                       Span<uint8_t> *out, size_t *out_consumed,
+                                       uint8_t *out_alert, Span<uint8_t> in);
 
-// dtls_open_record implements |tls_open_record| for DTLS. It never returns
-// |ssl_open_record_partial| but otherwise behaves analogously.
-enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+// dtls_open_record implements |tls_open_record| for DTLS. It only returns
+// |ssl_open_record_partial| if |in| was empty and sets |*out_consumed| to
+// zero. The caller should read one packet and try again.
+enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type,
+                                        Span<uint8_t> *out,
                                         size_t *out_consumed,
-                                        uint8_t *out_alert, uint8_t *in,
-                                        size_t in_len);
+                                        uint8_t *out_alert, Span<uint8_t> in);
 
 // ssl_seal_align_prefix_len returns the length of the prefix before the start
 // of the bulk of the ciphertext when sealing a record with |ssl|. Callers may
@@ -853,7 +861,7 @@
 // |ssl_open_record_close_notify|, or |ssl_open_record_fatal_alert| as
 // appropriate.
 enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
-                                         const uint8_t *in, size_t in_len);
+                                         Span<const uint8_t> in);
 
 
 // Private key operations.
@@ -870,22 +878,24 @@
 
 enum ssl_private_key_result_t ssl_private_key_sign(
     SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
-    uint16_t sigalg, const uint8_t *in, size_t in_len);
+    uint16_t sigalg, Span<const uint8_t> in);
 
-enum ssl_private_key_result_t ssl_private_key_decrypt(
-    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
-    const uint8_t *in, size_t in_len);
+enum ssl_private_key_result_t ssl_private_key_decrypt(SSL_HANDSHAKE *hs,
+                                                      uint8_t *out,
+                                                      size_t *out_len,
+                                                      size_t max_out,
+                                                      Span<const uint8_t> in);
 
-// ssl_private_key_supports_signature_algorithm returns one if |hs|'s private
-// key supports |sigalg| and zero otherwise.
-int ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
+// ssl_private_key_supports_signature_algorithm returns whether |hs|'s private
+// key supports |sigalg|.
+bool ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
                                                  uint16_t sigalg);
 
 // ssl_public_key_verify verifies that the |signature| is valid for the public
 // key |pkey| and input |in|, using the signature algorithm |sigalg|.
-int ssl_public_key_verify(SSL *ssl, const uint8_t *signature,
-                          size_t signature_len, uint16_t sigalg, EVP_PKEY *pkey,
-                          const uint8_t *in, size_t in_len);
+bool ssl_public_key_verify(SSL *ssl, Span<const uint8_t> signature,
+                           uint16_t sigalg, EVP_PKEY *pkey,
+                           Span<const uint8_t> in);
 
 
 // Custom extensions
@@ -991,9 +1001,13 @@
 // dtls_clear_incoming_messages releases all buffered incoming messages.
 void dtls_clear_incoming_messages(SSL *ssl);
 
-// dtls_has_incoming_messages returns one if there are buffered incoming
-// messages ahead of the current message and zero otherwise.
-int dtls_has_incoming_messages(const SSL *ssl);
+// tls_has_unprocessed_handshake_data returns whether there is buffered
+// handshake data that has not been consumed by |get_message|.
+bool tls_has_unprocessed_handshake_data(const SSL *ssl);
+
+// dtls_has_unprocessed_handshake_data behaves like
+// |tls_has_unprocessed_handshake_data| for DTLS.
+bool dtls_has_unprocessed_handshake_data(const SSL *ssl);
 
 struct DTLS_OUTGOING_MESSAGE {
   uint8_t *data;
@@ -1013,16 +1027,13 @@
 
 // ssl_do_msg_callback calls |ssl|'s message callback, if set.
 void ssl_do_msg_callback(SSL *ssl, int is_write, int content_type,
-                         const void *buf, size_t len);
+                         Span<const uint8_t> in);
 
 
 // Transport buffers.
 
-// ssl_read_buffer returns a pointer to contents of the read buffer.
-uint8_t *ssl_read_buffer(SSL *ssl);
-
-// ssl_read_buffer_len returns the length of the read buffer.
-size_t ssl_read_buffer_len(const SSL *ssl);
+// ssl_read_buffer returns the current read buffer.
+Span<uint8_t> ssl_read_buffer(SSL *ssl);
 
 // ssl_read_buffer_extend_to extends the read buffer to the desired length. For
 // TLS, it reads to the end of the buffer until the buffer is |len| bytes
@@ -1292,6 +1303,9 @@
   // TLS 1.3.
   uint16_t retry_group = 0;
 
+  // error, if |wait| is |ssl_hs_error|, is the error the handshake failed on.
+  UniquePtr<ERR_SAVE_STATE> error;
+
   // key_share is the current key exchange instance.
   UniquePtr<SSLKeyShare> key_share;
 
@@ -1496,20 +1510,21 @@
 int tls13_add_finished(SSL_HANDSHAKE *hs);
 int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg);
 
-int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
-                                        Array<uint8_t> *out_secret,
-                                        uint8_t *out_alert, CBS *contents);
-int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
-                                        Array<uint8_t> *out_secret,
-                                        uint8_t *out_alert, CBS *contents);
-int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
+bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
+                                         Array<uint8_t> *out_secret,
+                                         uint8_t *out_alert, CBS *contents);
+bool ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
+                                         Array<uint8_t> *out_secret,
+                                         uint8_t *out_alert, CBS *contents);
+bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
 
-int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
-                                             uint8_t *out_alert, CBS *contents);
-int ssl_ext_pre_shared_key_parse_clienthello(
+bool ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
+                                              uint8_t *out_alert,
+                                              CBS *contents);
+bool ssl_ext_pre_shared_key_parse_clienthello(
     SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders,
     uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents);
-int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
+bool ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
 
 // ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
 // returns one iff it's valid.
@@ -1525,18 +1540,17 @@
 
 // tls13_get_cert_verify_signature_input generates the message to be signed for
 // TLS 1.3's CertificateVerify message. |cert_verify_context| determines the
-// type of signature. It sets |*out| and |*out_len| to a newly allocated buffer
-// containing the result. The caller must free it with |OPENSSL_free| to release
-// it. This function returns one on success and zero on failure.
-int tls13_get_cert_verify_signature_input(
-    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len,
+// type of signature. It sets |*out| to a newly allocated buffer containing the
+// result. This function returns true on success and false on failure.
+bool tls13_get_cert_verify_signature_input(
+    SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context);
 
 // ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
-// one on successful negotiation or if nothing was negotiated. It returns zero
+// true on successful negotiation or if nothing was negotiated. It returns false
 // and sets |*out_alert| to an alert on error.
-int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                       const SSL_CLIENT_HELLO *client_hello);
+bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                        const SSL_CLIENT_HELLO *client_hello);
 
 struct SSL_EXTENSION_TYPE {
   uint16_t type;
@@ -1556,6 +1570,10 @@
 // ssl_verify_peer_cert verifies the peer certificate for |hs|.
 enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs);
 
+enum ssl_hs_wait_t ssl_get_finished(SSL_HANDSHAKE *hs);
+bool ssl_send_finished(SSL_HANDSHAKE *hs);
+bool ssl_output_cert_chain(SSL *ssl);
+
 
 // SSLKEYLOGFILE functions.
 
@@ -1631,7 +1649,7 @@
 // From RFC4492, used in encoding the curve type in ECParameters
 #define NAMED_CURVE_TYPE 3
 
-struct SSLCertConfig {
+struct CERT {
   EVP_PKEY *privatekey;
 
   // chain contains the certificate chain, with the leaf at the beginning. The
@@ -1698,6 +1716,66 @@
   bool enable_early_data:1;
 };
 
+// |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS.
+struct SSL_PROTOCOL_METHOD {
+  // is_dtls is one if the protocol is DTLS and zero otherwise.
+  char is_dtls;
+  int (*ssl_new)(SSL *ssl);
+  void (*ssl_free)(SSL *ssl);
+  // get_message sets |*out| to the current handshake message and returns true
+  // if one has been received. It returns false if more input is needed.
+  bool (*get_message)(SSL *ssl, SSLMessage *out);
+  // read_message reads additional handshake data for |get_message|. On success,
+  // it returns one. Otherwise, it returns <= 0.
+  int (*read_message)(SSL *ssl);
+  // next_message is called to release the current handshake message.
+  void (*next_message)(SSL *ssl);
+  // read_app_data reads up to |len| bytes of application data into |buf|. On
+  // success, it returns the number of bytes read. Otherwise, it returns <= 0
+  // and sets |*out_got_handshake| to whether the failure was due to a
+  // post-handshake handshake message. If so, any handshake messages consumed
+  // may be read with |get_message|.
+  int (*read_app_data)(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len,
+                       int peek);
+  int (*read_change_cipher_spec)(SSL *ssl);
+  void (*read_close_notify)(SSL *ssl);
+  int (*write_app_data)(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
+                        int len);
+  int (*dispatch_alert)(SSL *ssl);
+  // supports_cipher returns one if |cipher| is supported by this protocol and
+  // zero otherwise.
+  int (*supports_cipher)(const SSL_CIPHER *cipher);
+  // init_message begins a new handshake message of type |type|. |cbb| is the
+  // root CBB to be passed into |finish_message|. |*body| is set to a child CBB
+  // the caller should write to. It returns one on success and zero on error.
+  int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+  // finish_message finishes a handshake message. It sets |*out_msg| to the
+  // serialized message. It returns one on success and zero on error.
+  int (*finish_message)(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
+  // add_message adds a handshake message to the pending flight. It returns one
+  // on success and zero on error.
+  int (*add_message)(SSL *ssl, Array<uint8_t> msg);
+  // add_change_cipher_spec adds a ChangeCipherSpec record to the pending
+  // flight. It returns one on success and zero on error.
+  int (*add_change_cipher_spec)(SSL *ssl);
+  // add_alert adds an alert to the pending flight. It returns one on success
+  // and zero on error.
+  int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc);
+  // flush_flight flushes the pending flight to the transport. It returns one on
+  // success and <= 0 on error.
+  int (*flush_flight)(SSL *ssl);
+  // on_handshake_complete is called when the handshake is complete.
+  void (*on_handshake_complete)(SSL *ssl);
+  // set_read_state sets |ssl|'s read cipher state to |aead_ctx|. It returns
+  // one on success and zero if changing the read state is forbidden at this
+  // point.
+  int (*set_read_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx);
+  // set_write_state sets |ssl|'s write cipher state to |aead_ctx|. It returns
+  // one on success and zero if changing the write state is forbidden at this
+  // point.
+  int (*set_write_state)(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx);
+};
+
 // ssl_crypto_x509_method provides the |SSL_X509_METHOD| functions using
 // crypto/x509.
 extern const SSL_X509_METHOD ssl_crypto_x509_method;
@@ -1706,6 +1784,327 @@
 // crypto/x509.
 extern const SSL_X509_METHOD ssl_noop_x509_method;
 
+// ssl_cipher_preference_list_st contains a list of SSL_CIPHERs with
+// equal-preference groups. For TLS clients, the groups are moot because the
+// server picks the cipher and groups cannot be expressed on the wire. However,
+// for servers, the equal-preference groups allow the client's preferences to
+// be partially respected. (This only has an effect with
+// SSL_OP_CIPHER_SERVER_PREFERENCE).
+//
+// The equal-preference groups are expressed by grouping SSL_CIPHERs together.
+// All elements of a group have the same priority: no ordering is expressed
+// within a group.
+//
+// The values in |ciphers| are in one-to-one correspondence with
+// |in_group_flags|. (That is, sk_SSL_CIPHER_num(ciphers) is the number of
+// bytes in |in_group_flags|.) The bytes in |in_group_flags| are either 1, to
+// indicate that the corresponding SSL_CIPHER is not the last element of a
+// group, or 0 to indicate that it is.
+//
+// For example, if |in_group_flags| contains all zeros then that indicates a
+// traditional, fully-ordered preference. Every SSL_CIPHER is the last element
+// of the group (i.e. they are all in a one-element group).
+//
+// For a more complex example, consider:
+//   ciphers:        A  B  C  D  E  F
+//   in_group_flags: 1  1  0  0  1  0
+//
+// That would express the following, order:
+//
+//    A         E
+//    B -> D -> F
+//    C
+struct ssl_cipher_preference_list_st {
+  STACK_OF(SSL_CIPHER) *ciphers;
+  uint8_t *in_group_flags;
+};
+
+struct tlsext_ticket_key {
+  static constexpr bool kAllowUniquePtr = true;
+
+  uint8_t name[SSL_TICKET_KEY_NAME_LEN];
+  uint8_t hmac_key[16];
+  uint8_t aes_key[16];
+  // next_rotation_tv_sec is the time (in seconds from the epoch) when the
+  // current key should be superseded by a new key, or the time when a previous
+  // key should be dropped. If zero, then the key should not be automatically
+  // rotated.
+  uint64_t next_rotation_tv_sec;
+};
+
+}  // namespace bssl
+
+DECLARE_LHASH_OF(SSL_SESSION)
+
+namespace bssl {
+
+// SSLContext backs the public |SSL_CTX| type. Due to compatibility constraints,
+// it is a base class for |ssl_ctx_st|.
+struct SSLContext {
+  const SSL_PROTOCOL_METHOD *method;
+  const SSL_X509_METHOD *x509_method;
+
+  // lock is used to protect various operations on this object.
+  CRYPTO_MUTEX lock;
+
+  // conf_max_version is the maximum acceptable protocol version configured by
+  // |SSL_CTX_set_max_proto_version|. Note this version is normalized in DTLS
+  // and is further constrainted by |SSL_OP_NO_*|.
+  uint16_t conf_max_version;
+
+  // conf_min_version is the minimum acceptable protocol version configured by
+  // |SSL_CTX_set_min_proto_version|. Note this version is normalized in DTLS
+  // and is further constrainted by |SSL_OP_NO_*|.
+  uint16_t conf_min_version;
+
+  // tls13_variant is the variant of TLS 1.3 we are using for this
+  // configuration.
+  enum tls13_variant_t tls13_variant;
+
+  struct ssl_cipher_preference_list_st *cipher_list;
+
+  X509_STORE *cert_store;
+  LHASH_OF(SSL_SESSION) *sessions;
+  // Most session-ids that will be cached, default is
+  // SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited.
+  unsigned long session_cache_size;
+  SSL_SESSION *session_cache_head;
+  SSL_SESSION *session_cache_tail;
+
+  // handshakes_since_cache_flush is the number of successful handshakes since
+  // the last cache flush.
+  int handshakes_since_cache_flush;
+
+  // This can have one of 2 values, ored together,
+  // SSL_SESS_CACHE_CLIENT,
+  // SSL_SESS_CACHE_SERVER,
+  // Default is SSL_SESSION_CACHE_SERVER, which means only
+  // SSL_accept which cache SSL_SESSIONS.
+  int session_cache_mode;
+
+  // session_timeout is the default lifetime for new sessions in TLS 1.2 and
+  // earlier, in seconds.
+  uint32_t session_timeout;
+
+  // session_psk_dhe_timeout is the default lifetime for new sessions in TLS
+  // 1.3, in seconds.
+  uint32_t session_psk_dhe_timeout;
+
+  // If this callback is not null, it will be called each time a session id is
+  // added to the cache.  If this function returns 1, it means that the
+  // callback will do a SSL_SESSION_free() when it has finished using it.
+  // Otherwise, on 0, it means the callback has finished with it. If
+  // remove_session_cb is not null, it will be called when a session-id is
+  // removed from the cache.  After the call, OpenSSL will SSL_SESSION_free()
+  // it.
+  int (*new_session_cb)(SSL *ssl, SSL_SESSION *sess);
+  void (*remove_session_cb)(SSL_CTX *ctx, SSL_SESSION *sess);
+  SSL_SESSION *(*get_session_cb)(SSL *ssl, const uint8_t *data, int len,
+                                 int *copy);
+  SSL_SESSION *(*get_session_cb_legacy)(SSL *ssl, uint8_t *data, int len,
+                                        int *copy);
+
+  CRYPTO_refcount_t references;
+
+  // if defined, these override the X509_verify_cert() calls
+  int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg);
+  void *app_verify_arg;
+
+  enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
+                                                     uint8_t *out_alert);
+
+  // Default password callback.
+  pem_password_cb *default_passwd_callback;
+
+  // Default password callback user data.
+  void *default_passwd_callback_userdata;
+
+  // get client cert callback
+  int (*client_cert_cb)(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey);
+
+  // get channel id callback
+  void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey);
+
+  CRYPTO_EX_DATA ex_data;
+
+  // custom_*_extensions stores any callback sets for custom extensions. Note
+  // that these pointers will be NULL if the stack would otherwise be empty.
+  STACK_OF(SSL_CUSTOM_EXTENSION) *client_custom_extensions;
+  STACK_OF(SSL_CUSTOM_EXTENSION) *server_custom_extensions;
+
+  // Default values used when no per-SSL value is defined follow
+
+  void (*info_callback)(const SSL *ssl, int type, int value);
+
+  // what we put in client cert requests
+  STACK_OF(CRYPTO_BUFFER) *client_CA;
+
+  // cached_x509_client_CA is a cache of parsed versions of the elements of
+  // |client_CA|.
+  STACK_OF(X509_NAME) *cached_x509_client_CA;
+
+
+  // Default values to use in SSL structures follow (these are copied by
+  // SSL_new)
+
+  uint32_t options;
+  uint32_t mode;
+  uint32_t max_cert_list;
+
+  CERT *cert;
+
+  // callback that allows applications to peek at protocol messages
+  void (*msg_callback)(int write_p, int version, int content_type,
+                       const void *buf, size_t len, SSL *ssl, void *arg);
+  void *msg_callback_arg;
+
+  int verify_mode;
+  int (*default_verify_callback)(
+      int ok, X509_STORE_CTX *ctx);  // called 'verify_callback' in the SSL
+
+  X509_VERIFY_PARAM *param;
+
+  // select_certificate_cb is called before most ClientHello processing and
+  // before the decision whether to resume a session is made. See
+  // |ssl_select_cert_result_t| for details of the return values.
+  enum ssl_select_cert_result_t (*select_certificate_cb)(
+      const SSL_CLIENT_HELLO *);
+
+  // dos_protection_cb is called once the resumption decision for a ClientHello
+  // has been made. It returns one to continue the handshake or zero to
+  // abort.
+  int (*dos_protection_cb) (const SSL_CLIENT_HELLO *);
+
+  // Maximum amount of data to send in one fragment. actual record size can be
+  // more than this due to padding and MAC overheads.
+  uint16_t max_send_fragment;
+
+  // TLS extensions servername callback
+  int (*tlsext_servername_callback)(SSL *, int *, void *);
+  void *tlsext_servername_arg;
+
+  // RFC 4507 session ticket keys. |tlsext_ticket_key_current| may be NULL
+  // before the first handshake and |tlsext_ticket_key_prev| may be NULL at any
+  // time. Automatically generated ticket keys are rotated as needed at
+  // handshake time. Hence, all access must be synchronized through |lock|.
+  struct tlsext_ticket_key *tlsext_ticket_key_current;
+  struct tlsext_ticket_key *tlsext_ticket_key_prev;
+
+  // Callback to support customisation of ticket key setting
+  int (*tlsext_ticket_key_cb)(SSL *ssl, uint8_t *name, uint8_t *iv,
+                              EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc);
+
+  // Server-only: psk_identity_hint is the default identity hint to send in
+  // PSK-based key exchanges.
+  char *psk_identity_hint;
+
+  unsigned int (*psk_client_callback)(SSL *ssl, const char *hint,
+                                      char *identity,
+                                      unsigned int max_identity_len,
+                                      uint8_t *psk, unsigned int max_psk_len);
+  unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
+                                      uint8_t *psk, unsigned int max_psk_len);
+
+
+  // Next protocol negotiation information
+  // (for experimental NPN extension).
+
+  // For a server, this contains a callback function by which the set of
+  // advertised protocols can be provided.
+  int (*next_protos_advertised_cb)(SSL *ssl, const uint8_t **out,
+                                   unsigned *out_len, void *arg);
+  void *next_protos_advertised_cb_arg;
+  // For a client, this contains a callback function that selects the
+  // next protocol from the list provided by the server.
+  int (*next_proto_select_cb)(SSL *ssl, uint8_t **out, uint8_t *out_len,
+                              const uint8_t *in, unsigned in_len, void *arg);
+  void *next_proto_select_cb_arg;
+
+  // ALPN information
+  // (we are in the process of transitioning from NPN to ALPN.)
+
+  // For a server, this contains a callback function that allows the
+  // server to select the protocol for the connection.
+  //   out: on successful return, this must point to the raw protocol
+  //        name (without the length prefix).
+  //   outlen: on successful return, this contains the length of |*out|.
+  //   in: points to the client's list of supported protocols in
+  //       wire-format.
+  //   inlen: the length of |in|.
+  int (*alpn_select_cb)(SSL *ssl, const uint8_t **out, uint8_t *out_len,
+                        const uint8_t *in, unsigned in_len, void *arg);
+  void *alpn_select_cb_arg;
+
+  // For a client, this contains the list of supported protocols in wire
+  // format.
+  uint8_t *alpn_client_proto_list;
+  unsigned alpn_client_proto_list_len;
+
+  // SRTP profiles we are willing to do from RFC 5764
+  STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
+
+  // Supported group values inherited by SSL structure
+  size_t supported_group_list_len;
+  uint16_t *supported_group_list;
+
+  // The client's Channel ID private key.
+  EVP_PKEY *tlsext_channel_id_private;
+
+  // keylog_callback, if not NULL, is the key logging callback. See
+  // |SSL_CTX_set_keylog_callback|.
+  void (*keylog_callback)(const SSL *ssl, const char *line);
+
+  // current_time_cb, if not NULL, is the function to use to get the current
+  // time. It sets |*out_clock| to the current time. The |ssl| argument is
+  // always NULL. See |SSL_CTX_set_current_time_cb|.
+  void (*current_time_cb)(const SSL *ssl, struct timeval *out_clock);
+
+  // pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
+  // memory.
+  CRYPTO_BUFFER_POOL *pool;
+
+  // ticket_aead_method contains function pointers for opening and sealing
+  // session tickets.
+  const SSL_TICKET_AEAD_METHOD *ticket_aead_method;
+
+  // verify_sigalgs, if not empty, is the set of signature algorithms
+  // accepted from the peer in decreasing order of preference.
+  uint16_t *verify_sigalgs;
+  size_t num_verify_sigalgs;
+
+  // retain_only_sha256_of_client_certs is true if we should compute the SHA256
+  // hash of the peer's certificate and then discard it to save memory and
+  // session space. Only effective on the server side.
+  bool retain_only_sha256_of_client_certs:1;
+
+  // quiet_shutdown is true if the connection should not send a close_notify on
+  // shutdown.
+  bool quiet_shutdown:1;
+
+  // ocsp_stapling_enabled is only used by client connections and indicates
+  // whether OCSP stapling will be requested.
+  bool ocsp_stapling_enabled:1;
+
+  // If true, a client will request certificate timestamps.
+  bool signed_cert_timestamps_enabled:1;
+
+  // tlsext_channel_id_enabled is one if Channel ID is enabled and zero
+  // otherwise. For a server, means that we'll accept Channel IDs from clients.
+  // For a client, means that we'll advertise support.
+  bool tlsext_channel_id_enabled:1;
+
+  // grease_enabled is one if draft-davidben-tls-grease-01 is enabled and zero
+  // otherwise.
+  bool grease_enabled:1;
+
+  // allow_unknown_alpn_protos is one if the client allows unsolicited ALPN
+  // protocols from the peer.
+  bool allow_unknown_alpn_protos:1;
+
+  // ed25519_enabled is one if Ed25519 is advertised in the handshake.
+  bool ed25519_enabled:1;
+};
+
 struct SSL3_RECORD {
   // type is the record type.
   uint8_t type;
@@ -1755,12 +2154,11 @@
   int wpend_ret;  // number of bytes submitted
   const uint8_t *wpend_buf;
 
-  // recv_shutdown is the shutdown state for the receive half of the
-  // connection.
-  enum ssl_shutdown_t recv_shutdown;
+  // read_shutdown is the shutdown state for the read half of the connection.
+  enum ssl_shutdown_t read_shutdown;
 
-  // recv_shutdown is the shutdown state for the send half of the connection.
-  enum ssl_shutdown_t send_shutdown;
+  // write_shutdown is the shutdown state for the write half of the connection.
+  enum ssl_shutdown_t write_shutdown;
 
   int alert_dispatch;
 
@@ -2149,31 +2547,31 @@
   // server is true iff the this SSL* is the server half. Note: before the SSL*
   // is initialized by either SSL_set_accept_state or SSL_set_connect_state,
   // the side is not determined. In this state, server is always false.
-  unsigned server:1;
+  bool server:1;
 
   // quiet_shutdown is true if the connection should not send a close_notify on
   // shutdown.
-  unsigned quiet_shutdown:1;
+  bool quiet_shutdown:1;
 
   // Enable signed certificate time stamps. Currently client only.
-  unsigned signed_cert_timestamps_enabled:1;
+  bool signed_cert_timestamps_enabled:1;
 
   // ocsp_stapling_enabled is only used by client connections and indicates
   // whether OCSP stapling will be requested.
-  unsigned ocsp_stapling_enabled:1;
+  bool ocsp_stapling_enabled:1;
 
   // tlsext_channel_id_enabled is copied from the |SSL_CTX|. For a server,
   // means that we'll accept Channel IDs from clients. For a client, means that
   // we'll advertise support.
-  unsigned tlsext_channel_id_enabled:1;
+  bool tlsext_channel_id_enabled:1;
 
   // retain_only_sha256_of_client_certs is true if we should compute the SHA256
   // hash of the peer's certificate and then discard it to save memory and
   // session space. Only effective on the server side.
-  unsigned retain_only_sha256_of_client_certs:1;
+  bool retain_only_sha256_of_client_certs:1;
 
   // early_data_accepted is true if early data was accepted by the server.
-  unsigned early_data_accepted:1;
+  bool early_data_accepted:1;
 };
 
 // From draft-ietf-tls-tls13-18, used in determining PSK modes.
@@ -2284,22 +2682,22 @@
 
 void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
 
-enum ssl_hs_wait_t ssl_get_finished(SSL_HANDSHAKE *hs);
-int ssl3_send_alert(SSL *ssl, int level, int desc);
+int ssl_send_alert(SSL *ssl, int level, int desc);
 bool ssl3_get_message(SSL *ssl, SSLMessage *out);
 int ssl3_read_message(SSL *ssl);
 void ssl3_next_message(SSL *ssl);
 
-int ssl3_send_finished(SSL_HANDSHAKE *hs);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len,
                        int peek);
 int ssl3_read_change_cipher_spec(SSL *ssl);
 void ssl3_read_close_notify(SSL *ssl);
-int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len);
+// ssl3_get_record reads a new input record. On success, it places it in
+// |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
+// more data is needed.
+int ssl3_get_record(SSL *ssl);
 int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
                         int len);
-int ssl3_output_cert_chain(SSL *ssl);
 
 int ssl3_new(SSL *ssl);
 void ssl3_free(SSL *ssl);
@@ -2447,10 +2845,6 @@
 // operation should be retried later.
 int ssl_do_channel_id_callback(SSL *ssl);
 
-// ssl3_can_false_start returns one if |ssl| is allowed to False Start and zero
-// otherwise.
-int ssl3_can_false_start(const SSL *ssl);
-
 // ssl_can_write returns one if |ssl| is allowed to write and zero otherwise.
 int ssl_can_write(const SSL *ssl);
 
@@ -2480,75 +2874,12 @@
   uint16_t version;
   // method is the underlying SSL_PROTOCOL_METHOD that initializes the
   // SSL_CTX.
-  const SSL_PROTOCOL_METHOD *method;
+  const bssl::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;
 };
 
-// ssl_protocol_method_st, aka |SSL_PROTOCOL_METHOD| abstracts between TLS and
-// DTLS.
-struct ssl_protocol_method_st {
-  // is_dtls is one if the protocol is DTLS and zero otherwise.
-  char is_dtls;
-  int (*ssl_new)(SSL *ssl);
-  void (*ssl_free)(SSL *ssl);
-  // get_message sets |*out| to the current handshake message and returns true
-  // if one has been received. It returns false if more input is needed.
-  bool (*get_message)(SSL *ssl, bssl::SSLMessage *out);
-  // read_message reads additional handshake data for |get_message|. On success,
-  // it returns one. Otherwise, it returns <= 0.
-  int (*read_message)(SSL *ssl);
-  // next_message is called to release the current handshake message.
-  void (*next_message)(SSL *ssl);
-  // read_app_data reads up to |len| bytes of application data into |buf|. On
-  // success, it returns the number of bytes read. Otherwise, it returns <= 0
-  // and sets |*out_got_handshake| to whether the failure was due to a
-  // post-handshake handshake message. If so, any handshake messages consumed
-  // may be read with |get_message|.
-  int (*read_app_data)(SSL *ssl, bool *out_got_handshake, uint8_t *buf, int len,
-                       int peek);
-  int (*read_change_cipher_spec)(SSL *ssl);
-  void (*read_close_notify)(SSL *ssl);
-  int (*write_app_data)(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
-                        int len);
-  int (*dispatch_alert)(SSL *ssl);
-  // supports_cipher returns one if |cipher| is supported by this protocol and
-  // zero otherwise.
-  int (*supports_cipher)(const SSL_CIPHER *cipher);
-  // init_message begins a new handshake message of type |type|. |cbb| is the
-  // root CBB to be passed into |finish_message|. |*body| is set to a child CBB
-  // the caller should write to. It returns one on success and zero on error.
-  int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-  // finish_message finishes a handshake message. It sets |*out_msg| to the
-  // serialized message. It returns one on success and zero on error.
-  int (*finish_message)(SSL *ssl, CBB *cbb, bssl::Array<uint8_t> *out_msg);
-  // add_message adds a handshake message to the pending flight. It returns one
-  // on success and zero on error.
-  int (*add_message)(SSL *ssl, bssl::Array<uint8_t> msg);
-  // add_change_cipher_spec adds a ChangeCipherSpec record to the pending
-  // flight. It returns one on success and zero on error.
-  int (*add_change_cipher_spec)(SSL *ssl);
-  // add_alert adds an alert to the pending flight. It returns one on success
-  // and zero on error.
-  int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc);
-  // flush_flight flushes the pending flight to the transport. It returns one on
-  // success and <= 0 on error.
-  int (*flush_flight)(SSL *ssl);
-  // on_handshake_complete is called when the handshake is complete.
-  void (*on_handshake_complete)(SSL *ssl);
-  // set_read_state sets |ssl|'s read cipher state to |aead_ctx|. It returns
-  // one on success and zero if changing the read state is forbidden at this
-  // point.
-  int (*set_read_state)(SSL *ssl,
-                        bssl::UniquePtr<bssl::SSLAEADContext> aead_ctx);
-  // set_write_state sets |ssl|'s write cipher state to |aead_ctx|. It returns
-  // one on success and zero if changing the write state is forbidden at this
-  // point.
-  int (*set_write_state)(SSL *ssl,
-                         bssl::UniquePtr<bssl::SSLAEADContext> aead_ctx);
-};
-
 struct ssl_x509_method_st {
   // check_client_CA_list returns one if |names| is a good list of X.509
   // distinguished names and zero otherwise. This is used to ensure that we can
@@ -2556,17 +2887,17 @@
   int (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names);
 
   // cert_clear frees and NULLs all X509 certificate-related state.
-  void (*cert_clear)(CERT *cert);
+  void (*cert_clear)(bssl::CERT *cert);
   // cert_free frees all X509-related state.
-  void (*cert_free)(CERT *cert);
+  void (*cert_free)(bssl::CERT *cert);
   // cert_flush_cached_chain drops any cached |X509|-based certificate chain
   // from |cert|.
   // cert_dup duplicates any needed fields from |cert| to |new_cert|.
-  void (*cert_dup)(CERT *new_cert, const CERT *cert);
-  void (*cert_flush_cached_chain)(CERT *cert);
+  void (*cert_dup)(bssl::CERT *new_cert, const bssl::CERT *cert);
+  void (*cert_flush_cached_chain)(bssl::CERT *cert);
   // cert_flush_cached_chain drops any cached |X509|-based leaf certificate
   // from |cert|.
-  void (*cert_flush_cached_leaf)(CERT *cert);
+  void (*cert_flush_cached_leaf)(bssl::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
@@ -2605,12 +2936,11 @@
   void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl);
 };
 
-// ssl_st backs the public |SSL| type. It subclasses the true type so that
-// SSLConnection may be a C++ type with methods and destructor without
-// polluting the global namespace.
+// The following types back public C-exposed types which must live in the global
+// namespace. We use subclassing so the implementations may be C++ types with
+// methods and destructor without polluting the global namespace.
+struct ssl_ctx_st : public bssl::SSLContext {};
 struct ssl_st : public bssl::SSLConnection {};
 
-struct cert_st : public bssl::SSLCertConfig {};
-
 
 #endif  // OPENSSL_HEADER_SSL_INTERNAL_H
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 65e2089..1c6882e 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -194,12 +194,11 @@
     }
   } while (!rest.empty());
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg.data(),
-                      msg.size());
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg);
   // TODO(svaldez): Move this up a layer to fix abstraction for SSLTranscript on
   // hs.
   if (ssl->s3->hs != NULL &&
-      !ssl->s3->hs->transcript.Update(msg.data(), msg.size())) {
+      !ssl->s3->hs->transcript.Update(msg)) {
     return 0;
   }
   return 1;
@@ -214,7 +213,7 @@
   }
 
   ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
-                      kChangeCipherSpec, sizeof(kChangeCipherSpec));
+                      kChangeCipherSpec);
   return 1;
 }
 
@@ -224,7 +223,7 @@
     return 0;
   }
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert, sizeof(alert));
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert);
   ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc);
   return 1;
 }
@@ -275,82 +274,6 @@
   return 1;
 }
 
-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;
-  if (!hs->transcript.GetFinishedMAC(finished, &finished_len, session,
-                                     ssl->server)) {
-    return 0;
-  }
-
-  // Log the master secret, if logging is enabled.
-  if (!ssl_log_secret(ssl, "CLIENT_RANDOM",
-                      session->master_key,
-                      session->master_key_length)) {
-    return 0;
-  }
-
-  // Copy the Finished so we can use it for renegotiation checks.
-  if (ssl->version != SSL3_VERSION) {
-    if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
-        finished_len > sizeof(ssl->s3->previous_server_finished)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-
-    if (ssl->server) {
-      OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
-      ssl->s3->previous_server_finished_len = finished_len;
-    } else {
-      OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
-      ssl->s3->previous_client_finished_len = finished_len;
-    }
-  }
-
-  ScopedCBB cbb;
-  CBB body;
-  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_FINISHED) ||
-      !CBB_add_bytes(&body, finished, finished_len) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  return 1;
-}
-
-int ssl3_output_cert_chain(SSL *ssl) {
-  ScopedCBB cbb;
-  CBB body;
-  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
-      !ssl_add_cert_chain(ssl, &body) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  return 1;
-}
-
-static int extend_handshake_buffer(SSL *ssl, size_t length) {
-  if (!BUF_MEM_reserve(ssl->init_buf, length)) {
-    return -1;
-  }
-  while (ssl->init_buf->length < length) {
-    int ret = ssl3_read_handshake_bytes(
-        ssl, (uint8_t *)ssl->init_buf->data + ssl->init_buf->length,
-        length - ssl->init_buf->length);
-    if (ret <= 0) {
-      return ret;
-    }
-    ssl->init_buf->length += (size_t)ret;
-  }
-  return 1;
-}
-
 static int read_v2_client_hello(SSL *ssl) {
   // Read the first 5 bytes, the size of the TLS record header. This is
   // sufficient to detect a V2ClientHello and ensures that we never read beyond
@@ -359,7 +282,7 @@
   if (ret <= 0) {
     return ret;
   }
-  const uint8_t *p = ssl_read_buffer(ssl);
+  const uint8_t *p = ssl_read_buffer(ssl).data();
 
   // Some dedicated error codes for protocol mixups should the application wish
   // to interpret them differently. (These do not overlap with ClientHello or
@@ -402,19 +325,16 @@
     return ret;
   }
 
-  CBS v2_client_hello;
-  CBS_init(&v2_client_hello, ssl_read_buffer(ssl) + 2, msg_length);
-
+  CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length));
   // The V2ClientHello without the length is incorporated into the handshake
   // hash. This is only ever called at the start of the handshake, so hs is
   // guaranteed to be non-NULL.
-  if (!ssl->s3->hs->transcript.Update(CBS_data(&v2_client_hello),
-                                      CBS_len(&v2_client_hello))) {
+  if (!ssl->s3->hs->transcript.Update(v2_client_hello)) {
     return -1;
   }
 
   ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */,
-                      CBS_data(&v2_client_hello), CBS_len(&v2_client_hello));
+                      v2_client_hello);
 
   uint8_t msg_type;
   uint16_t version, cipher_spec_length, session_id_length, challenge_length;
@@ -502,9 +422,8 @@
   return 1;
 }
 
-// TODO(davidben): Remove |out_bytes_needed| and inline into |ssl3_get_message|
-// when the entire record is copied into |init_buf|.
-static bool parse_message(SSL *ssl, SSLMessage *out, size_t *out_bytes_needed) {
+static bool parse_message(const SSL *ssl, SSLMessage *out,
+                          size_t *out_bytes_needed) {
   if (ssl->init_buf == NULL) {
     *out_bytes_needed = 4;
     return false;
@@ -528,19 +447,34 @@
   CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
            4 + len);
   out->is_v2_hello = ssl->s3->is_v2_hello;
+  return true;
+}
+
+bool ssl3_get_message(SSL *ssl, SSLMessage *out) {
+  size_t unused;
+  if (!parse_message(ssl, out, &unused)) {
+    return false;
+  }
   if (!ssl->s3->has_message) {
     if (!out->is_v2_hello) {
-      ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE,
-                          CBS_data(&out->raw), CBS_len(&out->raw));
+      ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, out->raw);
     }
     ssl->s3->has_message = true;
   }
   return true;
 }
 
-bool ssl3_get_message(SSL *ssl, SSLMessage *out) {
-  size_t unused;
-  return parse_message(ssl, out, &unused);
+bool tls_has_unprocessed_handshake_data(const SSL *ssl) {
+  size_t msg_len = 0;
+  if (ssl->s3->has_message) {
+    SSLMessage msg;
+    size_t unused;
+    if (parse_message(ssl, &msg, &unused)) {
+      msg_len = CBS_len(&msg.raw);
+    }
+  }
+
+  return ssl->init_buf != NULL && ssl->init_buf->length > msg_len;
 }
 
 int ssl3_read_message(SSL *ssl) {
@@ -553,7 +487,7 @@
 
   // Enforce the limit so the peer cannot force us to buffer 16MB.
   if (bytes_needed > 4 + ssl_max_handshake_message_len(ssl)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
     return -1;
   }
@@ -575,7 +509,40 @@
     return ret;
   }
 
-  return extend_handshake_buffer(ssl, bytes_needed);
+  SSL3_RECORD *rr = &ssl->s3->rrec;
+  // Get new packet if necessary.
+  if (rr->length == 0) {
+    int ret = ssl3_get_record(ssl);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  // WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the
+  // ServerHello and send the remaining encrypted application data records
+  // as-is. This manifests as an application data record when we expect
+  // handshake. Report a dedicated error code for this case.
+  if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA &&
+      ssl->s3->aead_read_ctx->is_null_cipher()) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+
+  if (rr->type != SSL3_RT_HANDSHAKE) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+
+  // Append the entire handshake record to the buffer.
+  if (!BUF_MEM_append(ssl->init_buf, rr->data, rr->length)) {
+    return -1;
+  }
+
+  rr->length = 0;
+  ssl_read_buffer_discard(ssl);
+  return 1;
 }
 
 void ssl3_next_message(SSL *ssl) {
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index f7470ae..71e1a08 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -126,69 +126,55 @@
 
 static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
 
-// ssl3_get_record reads a new input record. On success, it places it in
-// |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
-// more data is needed.
-static int ssl3_get_record(SSL *ssl) {
-again:
-  switch (ssl->s3->recv_shutdown) {
-    case ssl_shutdown_none:
-      break;
-    case ssl_shutdown_fatal_alert:
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
-      return -1;
-    case ssl_shutdown_close_notify:
-      return 0;
-  }
-
-  CBS body;
-  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,
-                      ssl_read_buffer(ssl), ssl_read_buffer_len(ssl));
-  if (open_ret != ssl_open_record_partial) {
-    ssl_read_buffer_consume(ssl, consumed);
-  }
-  switch (open_ret) {
-    case ssl_open_record_partial: {
-      int read_ret = ssl_read_buffer_extend_to(ssl, consumed);
-      if (read_ret <= 0) {
-        return read_ret;
-      }
-      goto again;
+int ssl3_get_record(SSL *ssl) {
+  for (;;) {
+    Span<uint8_t> body;
+    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, ssl_read_buffer(ssl));
+    if (open_ret != ssl_open_record_partial) {
+      ssl_read_buffer_consume(ssl, consumed);
     }
+    switch (open_ret) {
+      case ssl_open_record_partial: {
+        int read_ret = ssl_read_buffer_extend_to(ssl, consumed);
+        if (read_ret <= 0) {
+          return read_ret;
+        }
+        continue;
+      }
 
-    case ssl_open_record_success: {
-      if (CBS_len(&body) > 0xffff) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+      case ssl_open_record_success: {
+        if (body.size() > 0xffff) {
+          OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+          return -1;
+        }
+
+        SSL3_RECORD *rr = &ssl->s3->rrec;
+        rr->type = type;
+        rr->length = static_cast<uint16_t>(body.size());
+        rr->data = body.data();
+        return 1;
+      }
+
+      case ssl_open_record_discard:
+        continue;
+
+      case ssl_open_record_close_notify:
+        return 0;
+
+      case ssl_open_record_error:
+        if (alert != 0) {
+          ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+        }
         return -1;
-      }
-
-      SSL3_RECORD *rr = &ssl->s3->rrec;
-      rr->type = type;
-      rr->length = (uint16_t)CBS_len(&body);
-      rr->data = (uint8_t *)CBS_data(&body);
-      return 1;
     }
 
-    case ssl_open_record_discard:
-      goto again;
-
-    case ssl_open_record_close_notify:
-      return 0;
-
-    case ssl_open_record_fatal_alert:
-      return -1;
-
-    case ssl_open_record_error:
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
-      return -1;
+    assert(0);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
-
-  assert(0);
-  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return -1;
 }
 
 int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
@@ -396,15 +382,15 @@
       // by an alert.
       if (SSL_in_init(ssl)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
         return -1;
       }
 
       // Post-handshake data prior to TLS 1.3 is always renegotiation, which we
       // never accept as a server. Otherwise |ssl3_get_message| will send
       // |SSL_R_EXCESSIVE_MESSAGE_SIZE|.
-      if (ssl->server && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+      if (ssl->server && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
         OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
         return -1;
       }
@@ -421,7 +407,7 @@
     const int is_early_data_read = ssl->server &&
                                    ssl->s3->hs != NULL &&
                                    ssl->s3->hs->can_early_read &&
-                                   ssl3_protocol_version(ssl) >= TLS1_3_VERSION;
+                                   ssl_protocol_version(ssl) >= TLS1_3_VERSION;
 
     // Handle the end_of_early_data alert.
     if (rr->type == SSL3_RT_ALERT &&
@@ -440,14 +426,14 @@
 
     if (rr->type != SSL3_RT_APPLICATION_DATA) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return -1;
     }
 
     if (is_early_data_read) {
       if (rr->length > kMaxEarlyDataAccepted - ssl->s3->hs->early_data_read) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_READ_EARLY_DATA);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE);
         return -1;
       }
 
@@ -464,7 +450,6 @@
 
 int ssl3_read_change_cipher_spec(SSL *ssl) {
   SSL3_RECORD *rr = &ssl->s3->rrec;
-
   if (rr->length == 0) {
     int ret = ssl3_get_record(ssl);
     if (ret <= 0) {
@@ -472,20 +457,21 @@
     }
   }
 
-  if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+  if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC ||
+      tls_has_unprocessed_handshake_data(ssl)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
     return -1;
   }
 
   if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return -1;
   }
 
-  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data,
-                      rr->length);
+  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC,
+                      MakeSpan(rr->data, rr->length));
 
   rr->length = 0;
   ssl_read_buffer_discard(ssl);
@@ -499,55 +485,19 @@
   }
 }
 
-int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len) {
-  SSL3_RECORD *rr = &ssl->s3->rrec;
-
-  for (;;) {
-    // Get new packet if necessary.
-    if (rr->length == 0) {
-      int ret = ssl3_get_record(ssl);
-      if (ret <= 0) {
-        return ret;
-      }
-    }
-
-    // WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the
-    // ServerHello and send the remaining encrypted application data records
-    // as-is. This manifests as an application data record when we expect
-    // handshake. Report a dedicated error code for this case.
-    if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA &&
-        ssl->s3->aead_read_ctx->is_null_cipher()) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-      return -1;
-    }
-
-    if (rr->type != SSL3_RT_HANDSHAKE) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-      return -1;
-    }
-
-    if (rr->length != 0) {
-      return consume_record(ssl, buf, len, 0 /* consume data */);
-    }
-
-    // Discard empty records and loop again.
-  }
-}
-
-int ssl3_send_alert(SSL *ssl, int level, int desc) {
+int ssl_send_alert(SSL *ssl, int level, int desc) {
   // It is illegal to send an alert when we've already sent a closing one.
-  if (ssl->s3->send_shutdown != ssl_shutdown_none) {
+  if (ssl->s3->write_shutdown != ssl_shutdown_none) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
     return -1;
   }
 
   if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
-    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+    ssl->s3->write_shutdown = ssl_shutdown_close_notify;
   } else {
     assert(level == SSL3_AL_FATAL);
-    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
+    assert(desc != SSL_AD_CLOSE_NOTIFY);
+    ssl->s3->write_shutdown = ssl_shutdown_fatal_alert;
   }
 
   ssl->s3->alert_dispatch = 1;
@@ -575,8 +525,7 @@
     BIO_flush(ssl->wbio);
   }
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert,
-                      2);
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert);
 
   int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
   ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert);
diff --git a/src/ssl/ssl_aead_ctx.cc b/src/ssl/ssl_aead_ctx.cc
index 8856f74..775827c 100644
--- a/src/ssl/ssl_aead_ctx.cc
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -225,11 +225,12 @@
   return len;
 }
 
-bool SSLAEADContext::Open(CBS *out, uint8_t type, uint16_t record_version,
-                          const uint8_t seqnum[8], uint8_t *in, size_t in_len) {
+bool SSLAEADContext::Open(Span<uint8_t> *out, uint8_t type,
+                          uint16_t record_version, const uint8_t seqnum[8],
+                          Span<uint8_t> in) {
   if (is_null_cipher() || FUZZER_MODE) {
     // Handle the initial NULL cipher.
-    CBS_init(out, in, in_len);
+    *out = in;
     return true;
   }
 
@@ -238,12 +239,12 @@
   size_t plaintext_len = 0;
   if (!omit_length_in_ad_) {
     size_t overhead = MaxOverhead();
-    if (in_len < overhead) {
+    if (in.size() < overhead) {
       // Publicly invalid.
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH);
       return false;
     }
-    plaintext_len = in_len - overhead;
+    plaintext_len = in.size() - overhead;
   }
   uint8_t ad[13];
   size_t ad_len =
@@ -264,14 +265,13 @@
 
   // Add the variable nonce.
   if (variable_nonce_included_in_record_) {
-    if (in_len < variable_nonce_len_) {
+    if (in.size() < variable_nonce_len_) {
       // Publicly invalid.
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH);
       return false;
     }
-    OPENSSL_memcpy(nonce + nonce_len, in, variable_nonce_len_);
-    in += variable_nonce_len_;
-    in_len -= variable_nonce_len_;
+    OPENSSL_memcpy(nonce + nonce_len, in.data(), variable_nonce_len_);
+    in = in.subspan(variable_nonce_len_);
   } else {
     assert(variable_nonce_len_ == 8);
     OPENSSL_memcpy(nonce + nonce_len, seqnum, variable_nonce_len_);
@@ -288,11 +288,11 @@
 
   // Decrypt in-place.
   size_t len;
-  if (!EVP_AEAD_CTX_open(ctx_.get(), in, &len, in_len, nonce, nonce_len, in,
-                         in_len, ad, ad_len)) {
+  if (!EVP_AEAD_CTX_open(ctx_.get(), in.data(), &len, in.size(), nonce,
+                         nonce_len, in.data(), in.size(), ad, ad_len)) {
     return false;
   }
-  CBS_init(out, in, len);
+  *out = in.subspan(0, len);
   return true;
 }
 
diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc
index b424138..79f0cae 100644
--- a/src/ssl/ssl_buffer.cc
+++ b/src/ssl/ssl_buffer.cc
@@ -89,12 +89,9 @@
   OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
 }
 
-uint8_t *ssl_read_buffer(SSL *ssl) {
-  return ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset;
-}
-
-size_t ssl_read_buffer_len(const SSL *ssl) {
-  return ssl->s3->read_buffer.len;
+Span<uint8_t> ssl_read_buffer(SSL *ssl) {
+  return MakeSpan(ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset,
+                  ssl->s3->read_buffer.len);
 }
 
 static int dtls_read_buffer_next_packet(SSL *ssl) {
diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc
index 094d85a..cd586a0 100644
--- a/src/ssl/ssl_cert.cc
+++ b/src/ssl/ssl_cert.cc
@@ -599,7 +599,8 @@
       !CBS_get_optional_asn1(
           &tbs_cert, &outer_extensions, &has_extensions,
           CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3)) {
-    goto parse_err;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+    return 0;
   }
 
   if (!has_extensions) {
@@ -608,7 +609,8 @@
 
   CBS extensions;
   if (!CBS_get_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) {
-    goto parse_err;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+    return 0;
   }
 
   while (CBS_len(&extensions) > 0) {
@@ -619,7 +621,8 @@
          !CBS_get_asn1(&extension, NULL, CBS_ASN1_BOOLEAN)) ||
         !CBS_get_asn1(&extension, &contents, CBS_ASN1_OCTETSTRING) ||
         CBS_len(&extension) != 0) {
-      goto parse_err;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+      return 0;
     }
 
     static const uint8_t kKeyUsageOID[3] = {0x55, 0x1d, 0x0f};
@@ -632,13 +635,15 @@
     CBS bit_string;
     if (!CBS_get_asn1(&contents, &bit_string, CBS_ASN1_BITSTRING) ||
         CBS_len(&contents) != 0) {
-      goto parse_err;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+      return 0;
     }
 
     // This is the KeyUsage extension. See
     // https://tools.ietf.org/html/rfc5280#section-4.2.1.3
     if (!CBS_is_valid_asn1_bitstring(&bit_string)) {
-      goto parse_err;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+      return 0;
     }
 
     if (!CBS_asn1_bitstring_has_bit(&bit_string, 0)) {
@@ -651,10 +656,6 @@
 
   // No KeyUsage extension found.
   return 1;
-
-parse_err:
-  OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-  return 0;
 }
 
 UniquePtr<STACK_OF(CRYPTO_BUFFER)> ssl_parse_client_CA_list(SSL *ssl,
@@ -731,7 +732,7 @@
 int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
                                const CRYPTO_BUFFER *leaf) {
   SSL *const ssl = hs->ssl;
-  assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION);
+  assert(ssl_protocol_version(ssl) < TLS1_3_VERSION);
 
   // Check the certificate's type matches the cipher.
   if (!(hs->new_cipher->algorithm_auth & ssl_cipher_auth_mask_for_key(pkey))) {
diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc
index 3ceb180..52ff85d 100644
--- a/src/ssl/ssl_key_share.cc
+++ b/src/ssl/ssl_key_share.cc
@@ -168,7 +168,7 @@
   uint8_t private_key_[32];
 };
 
-const struct {
+CONSTEXPR_ARRAY struct {
   int nid;
   uint16_t group_id;
   const char name[8];
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index f62d155..c975337 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -317,19 +317,6 @@
   return 1;
 }
 
-int ssl3_can_false_start(const SSL *ssl) {
-  const SSL_CIPHER *const cipher = SSL_get_current_cipher(ssl);
-
-  // False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN.
-  return !SSL_is_dtls(ssl) &&
-      SSL_version(ssl) == TLS1_2_VERSION &&
-      (ssl->s3->alpn_selected != NULL ||
-       ssl->s3->next_proto_negotiated != NULL) &&
-      cipher != NULL &&
-      cipher->algorithm_mkey == SSL_kECDHE &&
-      cipher->algorithm_mac == SSL_AEAD;
-}
-
 void ssl_do_info_callback(const SSL *ssl, int type, int value) {
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   if (ssl->info_callback != NULL) {
@@ -344,7 +331,7 @@
 }
 
 void ssl_do_msg_callback(SSL *ssl, int is_write, int content_type,
-                         const void *buf, size_t len) {
+                         Span<const uint8_t> in) {
   if (ssl->msg_callback == NULL) {
     return;
   }
@@ -364,7 +351,7 @@
       version = SSL_version(ssl);
   }
 
-  ssl->msg_callback(is_write, version, content_type, buf, len, ssl,
+  ssl->msg_callback(is_write, version, content_type, in.data(), in.size(), ssl,
                     ssl->msg_callback_arg);
 }
 
@@ -744,12 +731,12 @@
 }
 
 void SSL_set_connect_state(SSL *ssl) {
-  ssl->server = 0;
+  ssl->server = false;
   ssl->do_handshake = ssl_client_handshake;
 }
 
 void SSL_set_accept_state(SSL *ssl) {
-  ssl->server = 1;
+  ssl->server = true;
   ssl->do_handshake = ssl_server_handshake;
 }
 
@@ -852,7 +839,7 @@
 }
 
 static int ssl_do_post_handshake(SSL *ssl, const SSLMessage &msg) {
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return tls13_post_handshake(ssl, msg);
   }
 
@@ -864,7 +851,7 @@
   }
 
   if (msg.type != SSL3_MT_HELLO_REQUEST || CBS_len(&msg.body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST);
     return 0;
   }
@@ -909,7 +896,7 @@
   return 1;
 
 no_renegotiation:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+  ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
   OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
   return 0;
 }
@@ -937,28 +924,27 @@
       }
     }
 
-    bool got_handshake = false;
-    int ret = ssl->method->read_app_data(ssl, &got_handshake, (uint8_t *)buf,
-                                         num, peek);
-    if (ret > 0 || !got_handshake) {
-      ssl->s3->key_update_count = 0;
-      return ret;
-    }
-
-    // If we received an interrupt in early read (the end_of_early_data alert),
-    // loop again for the handshake to process it.
-    if (SSL_in_init(ssl)) {
-      continue;
-    }
-
+    // Process any buffered post-handshake messages.
     SSLMessage msg;
-    while (ssl->method->get_message(ssl, &msg)) {
+    if (ssl->method->get_message(ssl, &msg)) {
       // Handle the post-handshake message and try again.
       if (!ssl_do_post_handshake(ssl, msg)) {
         return -1;
       }
       ssl->method->next_message(ssl);
+      continue;  // Loop again. We may have begun a new handshake.
     }
+
+    bool got_handshake = false;
+    int ret = ssl->method->read_app_data(ssl, &got_handshake, (uint8_t *)buf,
+                                         num, peek);
+    if (got_handshake) {
+      continue;  // Loop again to process the handshake data.
+    }
+    if (ret > 0) {
+      ssl->s3->key_update_count = 0;
+    }
+    return ret;
   }
 }
 
@@ -978,7 +964,7 @@
     return -1;
   }
 
-  if (ssl->s3->send_shutdown != ssl_shutdown_none) {
+  if (ssl->s3->write_shutdown != ssl_shutdown_none) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
     return -1;
   }
@@ -1021,8 +1007,8 @@
 
   if (ssl->quiet_shutdown) {
     // Do nothing if configured not to send a close_notify.
-    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
-    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+    ssl->s3->write_shutdown = ssl_shutdown_close_notify;
+    ssl->s3->read_shutdown = ssl_shutdown_close_notify;
     return 1;
   }
 
@@ -1030,9 +1016,9 @@
   // waits for a close_notify to come in. Perform exactly one action and return
   // whether or not it succeeds.
 
-  if (ssl->s3->send_shutdown != ssl_shutdown_close_notify) {
+  if (ssl->s3->write_shutdown != ssl_shutdown_close_notify) {
     // Send a close_notify.
-    if (ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
+    if (ssl_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
       return -1;
     }
   } else if (ssl->s3->alert_dispatch) {
@@ -1040,16 +1026,16 @@
     if (ssl->method->dispatch_alert(ssl) <= 0) {
       return -1;
     }
-  } else if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+  } else if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) {
     // Wait for the peer's close_notify.
     ssl->method->read_close_notify(ssl);
-    if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+    if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) {
       return -1;
     }
   }
 
   // Return 0 for unidirectional shutdown and 1 for bidirectional shutdown.
-  return ssl->s3->recv_shutdown == ssl_shutdown_close_notify;
+  return ssl->s3->read_shutdown == ssl_shutdown_close_notify;
 }
 
 int SSL_send_fatal_alert(SSL *ssl, uint8_t alert) {
@@ -1063,7 +1049,7 @@
     return ssl->method->dispatch_alert(ssl);
   }
 
-  return ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+  return ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
 }
 
 void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) {
@@ -1137,7 +1123,7 @@
   }
 
   if (ret_code == 0) {
-    if (ssl->s3->recv_shutdown == ssl_shutdown_close_notify) {
+    if (ssl->s3->read_shutdown == ssl_shutdown_close_notify) {
       return SSL_ERROR_ZERO_RETURN;
     }
     // An EOF was observed which violates the protocol, and the underlying
@@ -1272,8 +1258,8 @@
 
   // tls-unique is not defined for SSL 3.0 or TLS 1.3.
   if (!ssl->s3->initial_handshake_complete ||
-      ssl3_protocol_version(ssl) < TLS1_VERSION ||
-      ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      ssl_protocol_version(ssl) < TLS1_VERSION ||
+      ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 0;
   }
 
@@ -1411,8 +1397,8 @@
 
 size_t SSL_get_finished(const SSL *ssl, void *buf, size_t count) {
   if (!ssl->s3->initial_handshake_complete ||
-      ssl3_protocol_version(ssl) < TLS1_VERSION ||
-      ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      ssl_protocol_version(ssl) < TLS1_VERSION ||
+      ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 0;
   }
 
@@ -1427,8 +1413,8 @@
 
 size_t SSL_get_peer_finished(const SSL *ssl, void *buf, size_t count) {
   if (!ssl->s3->initial_handshake_complete ||
-      ssl3_protocol_version(ssl) < TLS1_VERSION ||
-      ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      ssl_protocol_version(ssl) < TLS1_VERSION ||
+      ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 0;
   }
 
@@ -1449,7 +1435,7 @@
   if (!ssl->s3->have_version) {
     return 0;
   }
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 1;
   }
 
@@ -1567,7 +1553,7 @@
   if (!ssl->s3->have_version) {
     return 0;
   }
-  return ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+  return ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
          ssl->s3->send_connection_binding;
 }
 
@@ -1795,19 +1781,19 @@
 }
 
 void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) {
-  ctx->signed_cert_timestamps_enabled = 1;
+  ctx->signed_cert_timestamps_enabled = true;
 }
 
 void SSL_enable_signed_cert_timestamps(SSL *ssl) {
-  ssl->signed_cert_timestamps_enabled = 1;
+  ssl->signed_cert_timestamps_enabled = true;
 }
 
 void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) {
-  ctx->ocsp_stapling_enabled = 1;
+  ctx->ocsp_stapling_enabled = true;
 }
 
 void SSL_enable_ocsp_stapling(SSL *ssl) {
-  ssl->ocsp_stapling_enabled = 1;
+  ssl->ocsp_stapling_enabled = true;
 }
 
 void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out,
@@ -2004,7 +1990,7 @@
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
   EVP_PKEY_up_ref(private_key);
   ctx->tlsext_channel_id_private = private_key;
-  ctx->tlsext_channel_id_enabled = 1;
+  ctx->tlsext_channel_id_enabled = true;
 
   return 1;
 }
@@ -2018,7 +2004,7 @@
   EVP_PKEY_free(ssl->tlsext_channel_id_private);
   EVP_PKEY_up_ref(private_key);
   ssl->tlsext_channel_id_private = private_key;
-  ssl->tlsext_channel_id_enabled = 1;
+  ssl->tlsext_channel_id_enabled = true;
 
   return 1;
 }
@@ -2091,24 +2077,24 @@
   assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl));
 
   if (mode & SSL_RECEIVED_SHUTDOWN &&
-      ssl->s3->recv_shutdown == ssl_shutdown_none) {
-    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+      ssl->s3->read_shutdown == ssl_shutdown_none) {
+    ssl->s3->read_shutdown = ssl_shutdown_close_notify;
   }
 
   if (mode & SSL_SENT_SHUTDOWN &&
-      ssl->s3->send_shutdown == ssl_shutdown_none) {
-    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+      ssl->s3->write_shutdown == ssl_shutdown_none) {
+    ssl->s3->write_shutdown = ssl_shutdown_close_notify;
   }
 }
 
 int SSL_get_shutdown(const SSL *ssl) {
   int ret = 0;
-  if (ssl->s3->recv_shutdown != ssl_shutdown_none) {
+  if (ssl->s3->read_shutdown != ssl_shutdown_none) {
     // Historically, OpenSSL set |SSL_RECEIVED_SHUTDOWN| on both close_notify
     // and fatal alert.
     ret |= SSL_RECEIVED_SHUTDOWN;
   }
-  if (ssl->s3->send_shutdown == ssl_shutdown_close_notify) {
+  if (ssl->s3->write_shutdown == ssl_shutdown_close_notify) {
     // Historically, OpenSSL set |SSL_SENT_SHUTDOWN| on only close_notify.
     ret |= SSL_SENT_SHUTDOWN;
   }
diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc
index d275561..b00613d 100644
--- a/src/ssl/ssl_privkey.cc
+++ b/src/ssl/ssl_privkey.cc
@@ -147,7 +147,7 @@
     return 0;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     // RSA keys may only be used with RSA-PSS.
     if (alg->pkey_type == EVP_PKEY_RSA && !alg->is_rsa_pss) {
       return 0;
@@ -222,16 +222,16 @@
 
 enum ssl_private_key_result_t ssl_private_key_sign(
     SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
-    uint16_t sigalg, const uint8_t *in, size_t in_len) {
+    uint16_t sigalg, Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
   if (ssl->cert->key_method != NULL) {
     enum ssl_private_key_result_t ret;
     if (hs->pending_private_key_op) {
       ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
     } else {
-      ret = (ssl->cert->key_method->sign != NULL
-                 ? ssl->cert->key_method->sign
-                 : legacy_sign)(ssl, out, out_len, max_out, sigalg, in, in_len);
+      ret = (ssl->cert->key_method->sign != NULL ? ssl->cert->key_method->sign
+                                                 : legacy_sign)(
+          ssl, out, out_len, max_out, sigalg, in.data(), in.size());
     }
     hs->pending_private_key_op = ret == ssl_private_key_retry;
     return ret;
@@ -240,31 +240,34 @@
   *out_len = max_out;
   ScopedEVP_MD_CTX ctx;
   if (!setup_ctx(ssl, ctx.get(), ssl->cert->privatekey, sigalg, 0 /* sign */) ||
-      !EVP_DigestSign(ctx.get(), out, out_len, in, in_len)) {
+      !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
     return ssl_private_key_failure;
   }
   return ssl_private_key_success;
 }
 
-int ssl_public_key_verify(SSL *ssl, const uint8_t *signature,
-                          size_t signature_len, uint16_t sigalg, EVP_PKEY *pkey,
-                          const uint8_t *in, size_t in_len) {
+bool ssl_public_key_verify(SSL *ssl, Span<const uint8_t> signature,
+                           uint16_t sigalg, EVP_PKEY *pkey,
+                           Span<const uint8_t> in) {
   ScopedEVP_MD_CTX ctx;
   return setup_ctx(ssl, ctx.get(), pkey, sigalg, 1 /* verify */) &&
-         EVP_DigestVerify(ctx.get(), signature, signature_len, in, in_len);
+         EVP_DigestVerify(ctx.get(), signature.data(), signature.size(),
+                          in.data(), in.size());
 }
 
-enum ssl_private_key_result_t ssl_private_key_decrypt(
-    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
-    const uint8_t *in, size_t in_len) {
+enum ssl_private_key_result_t ssl_private_key_decrypt(SSL_HANDSHAKE *hs,
+                                                      uint8_t *out,
+                                                      size_t *out_len,
+                                                      size_t max_out,
+                                                      Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
   if (ssl->cert->key_method != NULL) {
     enum ssl_private_key_result_t ret;
     if (hs->pending_private_key_op) {
       ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
     } else {
-      ret = ssl->cert->key_method->decrypt(ssl, out, out_len, max_out, in,
-                                           in_len);
+      ret = ssl->cert->key_method->decrypt(ssl, out, out_len, max_out,
+                                           in.data(), in.size());
     }
     hs->pending_private_key_op = ret == ssl_private_key_retry;
     return ret;
@@ -279,17 +282,18 @@
 
   // Decrypt with no padding. PKCS#1 padding will be removed as part of the
   // timing-sensitive code by the caller.
-  if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) {
+  if (!RSA_decrypt(rsa, out_len, out, max_out, in.data(), in.size(),
+                   RSA_NO_PADDING)) {
     return ssl_private_key_failure;
   }
   return ssl_private_key_success;
 }
 
-int ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
-                                                 uint16_t sigalg) {
+bool ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
+                                                  uint16_t sigalg) {
   SSL *const ssl = hs->ssl;
   if (!pkey_supports_algorithm(ssl, hs->local_pubkey.get(), sigalg)) {
-    return 0;
+    return false;
   }
 
   // Ensure the RSA key is large enough for the hash. RSASSA-PSS requires that
@@ -301,7 +305,7 @@
   const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
   if (alg->is_rsa_pss && (size_t)EVP_PKEY_size(hs->local_pubkey.get()) <
                              2 * EVP_MD_size(alg->digest_func()) + 2) {
-    return 0;
+    return false;
   }
 
   // Newer algorithms require message-based private keys.
@@ -309,10 +313,10 @@
   if (ssl->cert->key_method != NULL &&
       ssl->cert->key_method->sign == NULL &&
       !legacy_sign_digest_supported(alg)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 }  // namespace bssl
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index aff9c0f..ea3c53f 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -377,7 +377,7 @@
   ssl_get_current_time(ssl, &now);
   session->time = now.tv_sec;
 
-  uint16_t version = ssl3_protocol_version(ssl);
+  uint16_t version = ssl_protocol_version(ssl);
   if (version >= TLS1_3_VERSION) {
     // TLS 1.3 uses tickets as authenticators, so we are willing to use them for
     // longer.
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index f9cf83c..4da109d 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -3760,6 +3760,73 @@
   EXPECT_EQ(version(), SSL_SESSION_get_protocol_version(session.get()));
 }
 
+// Test that a handshake-level errors are sticky.
+TEST_P(SSLVersionTest, StickyErrorHandshake_ParseClientHello) {
+  UniquePtr<SSL_CTX> ctx = CreateContext();
+  ASSERT_TRUE(ctx);
+  UniquePtr<SSL> ssl(SSL_new(ctx.get()));
+  ASSERT_TRUE(ssl);
+  SSL_set_accept_state(ssl.get());
+
+  // Pass in an empty ClientHello.
+  if (is_dtls()) {
+    static const uint8_t kBadClientHello[] = {
+        0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    SSL_set0_rbio(ssl.get(),
+                  BIO_new_mem_buf(kBadClientHello, sizeof(kBadClientHello)));
+  } else {
+    static const uint8_t kBadClientHello[] = {0x16, 0x03, 0x01, 0x00, 0x04,
+                                              0x01, 0x00, 0x00, 0x00};
+    SSL_set0_rbio(ssl.get(),
+                  BIO_new_mem_buf(kBadClientHello, sizeof(kBadClientHello)));
+  }
+
+  SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem()));
+
+  // The handshake logic should reject the ClientHello.
+  int ret = SSL_do_handshake(ssl.get());
+  EXPECT_NE(1, ret);
+  EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), ret));
+  EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(ERR_peek_error()));
+  EXPECT_EQ(SSL_R_DECODE_ERROR, ERR_GET_REASON(ERR_peek_error()));
+  ERR_clear_error();
+
+  // If we drive the handshake again, the error is replayed.
+  ret = SSL_do_handshake(ssl.get());
+  EXPECT_NE(1, ret);
+  EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), ret));
+  EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(ERR_peek_error()));
+  EXPECT_EQ(SSL_R_DECODE_ERROR, ERR_GET_REASON(ERR_peek_error()));
+}
+
+TEST_P(SSLVersionTest, SSLPending) {
+  UniquePtr<SSL> ssl(SSL_new(client_ctx_.get()));
+  ASSERT_TRUE(ssl);
+  EXPECT_EQ(0, SSL_pending(ssl.get()));
+
+  ASSERT_TRUE(Connect());
+  EXPECT_EQ(0, SSL_pending(client_.get()));
+
+  ASSERT_EQ(5, SSL_write(server_.get(), "hello", 5));
+  ASSERT_EQ(5, SSL_write(server_.get(), "world", 5));
+  EXPECT_EQ(0, SSL_pending(client_.get()));
+
+  char buf[10];
+  ASSERT_EQ(1, SSL_peek(client_.get(), buf, 1));
+  EXPECT_EQ(5, SSL_pending(client_.get()));
+
+  ASSERT_EQ(1, SSL_read(client_.get(), buf, 1));
+  EXPECT_EQ(4, SSL_pending(client_.get()));
+
+  ASSERT_EQ(4, SSL_read(client_.get(), buf, 10));
+  EXPECT_EQ(0, SSL_pending(client_.get()));
+
+  ASSERT_EQ(2, SSL_read(client_.get(), buf, 2));
+  EXPECT_EQ(3, SSL_pending(client_.get()));
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
diff --git a/src/ssl/ssl_transcript.cc b/src/ssl/ssl_transcript.cc
index b9c1713..ab9822f 100644
--- a/src/ssl/ssl_transcript.cc
+++ b/src/ssl/ssl_transcript.cc
@@ -209,26 +209,19 @@
   return EVP_MD_CTX_md(hash_.get());
 }
 
-bool SSLTranscript::Update(const uint8_t *in, size_t in_len) {
+bool SSLTranscript::Update(Span<const uint8_t> in) {
   // Depending on the state of the handshake, either the handshake buffer may be
   // active, the rolling hash, or both.
-  if (buffer_) {
-    size_t new_len = buffer_->length + in_len;
-    if (new_len < in_len) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-      return false;
-    }
-    if (!BUF_MEM_grow(buffer_.get(), new_len)) {
-      return false;
-    }
-    OPENSSL_memcpy(buffer_->data + new_len - in_len, in, in_len);
+  if (buffer_ &&
+      !BUF_MEM_append(buffer_.get(), in.data(), in.size())) {
+    return false;
   }
 
   if (EVP_MD_CTX_md(hash_.get()) != NULL) {
-    EVP_DigestUpdate(hash_.get(), in, in_len);
+    EVP_DigestUpdate(hash_.get(), in.data(), in.size());
   }
   if (EVP_MD_CTX_md(md5_.get()) != NULL) {
-    EVP_DigestUpdate(md5_.get(), in, in_len);
+    EVP_DigestUpdate(md5_.get(), in.data(), in.size());
   }
 
   return true;
diff --git a/src/ssl/ssl_versions.cc b/src/ssl/ssl_versions.cc
index 56653b1..ccae6ef 100644
--- a/src/ssl/ssl_versions.cc
+++ b/src/ssl/ssl_versions.cc
@@ -287,7 +287,7 @@
   return ssl->version;
 }
 
-uint16_t ssl3_protocol_version(const SSL *ssl) {
+uint16_t ssl_protocol_version(const SSL *ssl) {
   assert(ssl->s3->have_version);
   uint16_t version;
   if (!ssl_protocol_version_from_wire(&version, ssl->version)) {
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index 85c368c..e1578bd 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -331,7 +331,7 @@
   size_t mac_secret_len, fixed_iv_len;
   if (session->cipher == NULL ||
       !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
-                               session->cipher, ssl3_protocol_version(ssl),
+                               session->cipher, ssl_protocol_version(ssl),
                                SSL_is_dtls(ssl))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
     return 0;
@@ -422,7 +422,7 @@
       return 0;
     }
   } else {
-    if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+    if (ssl_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,
@@ -455,7 +455,7 @@
 
 int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
   const SSL_SESSION *session = SSL_get_session(ssl);
-  if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+  if (ssl_protocol_version(ssl) == SSL3_VERSION) {
     return ssl3_prf(out, out_len, session->master_key,
                     session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
                     TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random,
@@ -482,7 +482,7 @@
     return 0;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return tls13_export_keying_material(ssl, out, out_len, label, label_len,
                                         context, context_len, use_context);
   }
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 9c4231d..5d85cb0 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -537,52 +537,52 @@
 // The add callbacks receive a |CBB| to which the extension can be appended but
 // the function is responsible for appending the type and length bytes too.
 //
-// All callbacks return one for success and zero for error. If a parse function
-// returns zero then a fatal alert with value |*out_alert| will be sent. If
-// |*out_alert| isn't set, then a |decode_error| alert will be sent.
+// All callbacks return true for success and false for error. If a parse
+// function returns zero then a fatal alert with value |*out_alert| will be
+// sent. If |*out_alert| isn't set, then a |decode_error| alert will be sent.
 struct tls_extension {
   uint16_t value;
   void (*init)(SSL_HANDSHAKE *hs);
 
-  int (*add_clienthello)(SSL_HANDSHAKE *hs, CBB *out);
-  int (*parse_serverhello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                           CBS *contents);
+  bool (*add_clienthello)(SSL_HANDSHAKE *hs, CBB *out);
+  bool (*parse_serverhello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                            CBS *contents);
 
-  int (*parse_clienthello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                           CBS *contents);
-  int (*add_serverhello)(SSL_HANDSHAKE *hs, CBB *out);
+  bool (*parse_clienthello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                            CBS *contents);
+  bool (*add_serverhello)(SSL_HANDSHAKE *hs, CBB *out);
 };
 
-static int forbid_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+static bool forbid_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                     CBS *contents) {
   if (contents != NULL) {
     // Servers MUST NOT send this extension.
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ignore_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+static bool ignore_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                     CBS *contents) {
   // This extension from the client is handled elsewhere.
-  return 1;
+  return true;
 }
 
-static int dont_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  return 1;
+static bool dont_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  return true;
 }
 
 // Server name indication (SNI).
 //
 // https://tools.ietf.org/html/rfc6066#section-3.
 
-static int ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->tlsext_hostname == NULL) {
-    return 1;
+    return true;
   }
 
   CBB contents, server_name_list, name;
@@ -594,24 +594,24 @@
       !CBB_add_bytes(&name, (const uint8_t *)ssl->tlsext_hostname,
                      strlen(ssl->tlsext_hostname)) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   // The server may acknowledge SNI with an empty extension. We check the syntax
   // but otherwise ignore this signal.
   return contents == NULL || CBS_len(contents) == 0;
 }
 
-static int ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS server_name_list, host_name;
@@ -628,7 +628,7 @@
       !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
       CBS_len(&server_name_list) != 0 ||
       CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   if (name_type != TLSEXT_NAMETYPE_host_name ||
@@ -636,31 +636,31 @@
       CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
       CBS_contains_zero_byte(&host_name)) {
     *out_alert = SSL_AD_UNRECOGNIZED_NAME;
-    return 0;
+    return false;
   }
 
   // Copy the hostname as a string.
   if (!CBS_strdup(&host_name, &ssl->s3->hostname)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
+    return false;
   }
 
   hs->should_ack_sni = true;
-  return 1;
+  return true;
 }
 
-static int ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   if (hs->ssl->s3->session_reused ||
       !hs->should_ack_sni) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_server_name) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -668,11 +668,11 @@
 //
 // https://tools.ietf.org/html/rfc5746
 
-static int ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // Renegotiation indication is not necessary in TLS 1.3.
   if (hs->min_version >= TLS1_3_VERSION) {
-    return 1;
+    return true;
   }
 
   assert(ssl->s3->initial_handshake_complete ==
@@ -685,18 +685,18 @@
       !CBB_add_bytes(&prev_finished, ssl->s3->previous_client_finished,
                      ssl->s3->previous_client_finished_len) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ri_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                    CBS *contents) {
+static bool ext_ri_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                     CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (contents != NULL && ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (contents != NULL && ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
   // Servers may not switch between omitting the extension and supporting it.
@@ -705,7 +705,7 @@
       (contents != NULL) != ssl->s3->send_connection_binding) {
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
-    return 0;
+    return false;
   }
 
   if (contents == NULL) {
@@ -716,7 +716,7 @@
     //
     // OpenSSL has |SSL_OP_LEGACY_SERVER_CONNECT| to control this, but in
     // practical terms every client sets it so it's just assumed here.
-    return 1;
+    return true;
   }
 
   const size_t expected_len = ssl->s3->previous_client_finished_len +
@@ -736,64 +736,64 @@
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
   // Check that the extension matches.
   if (CBS_len(&renegotiated_connection) != expected_len) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    return 0;
+    return false;
   }
 
   const uint8_t *d = CBS_data(&renegotiated_connection);
-  int ok = CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
-                         ssl->s3->previous_client_finished_len) == 0;
+  bool ok = CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
+                          ssl->s3->previous_client_finished_len) == 0;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  ok = 1;
+  ok = true;
 #endif
   if (!ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    return 0;
+    return false;
   }
   d += ssl->s3->previous_client_finished_len;
 
   ok = CRYPTO_memcmp(d, ssl->s3->previous_server_finished,
                      ssl->s3->previous_server_finished_len) == 0;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  ok = 1;
+  ok = true;
 #endif
   if (!ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    return 0;
+    return false;
   }
   ssl->s3->send_connection_binding = true;
 
-  return 1;
+  return true;
 }
 
-static int ext_ri_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                    CBS *contents) {
+static bool ext_ri_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                     CBS *contents) {
   SSL *const ssl = hs->ssl;
   // Renegotiation isn't supported as a server so this function should never be
   // called after the initial handshake.
   assert(!ssl->s3->initial_handshake_complete);
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 1;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return true;
   }
 
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS renegotiated_connection;
   if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR);
-    return 0;
+    return false;
   }
 
   // Check that the extension matches. We do not support renegotiation as a
@@ -801,31 +801,31 @@
   if (CBS_len(&renegotiated_connection) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    return 0;
+    return false;
   }
 
   ssl->s3->send_connection_binding = true;
 
-  return 1;
+  return true;
 }
 
-static int ext_ri_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ri_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // Renegotiation isn't supported as a server so this function should never be
   // called after the initial handshake.
   assert(!ssl->s3->initial_handshake_complete);
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 1;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) ||
       !CBB_add_u16(out, 1 /* length */) ||
       !CBB_add_u8(out, 0 /* empty renegotiation info */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -833,29 +833,29 @@
 //
 // https://tools.ietf.org/html/rfc7627
 
-static int ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   // Extended master secret is not necessary in TLS 1.3.
   if (hs->min_version >= TLS1_3_VERSION || hs->max_version <= SSL3_VERSION) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ems_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_ems_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   SSL *const ssl = hs->ssl;
 
   if (contents != NULL) {
-    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+    if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
         ssl->version == SSL3_VERSION ||
         CBS_len(contents) != 0) {
-      return 0;
+      return false;
     }
 
     hs->extended_master_secret = true;
@@ -867,43 +867,43 @@
           !!ssl->s3->established_session->extended_master_secret) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_EMS_MISMATCH);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ems_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
-  uint16_t version = ssl3_protocol_version(hs->ssl);
+static bool ext_ems_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
+  uint16_t version = ssl_protocol_version(hs->ssl);
   if (version >= TLS1_3_VERSION ||
       version == SSL3_VERSION) {
-    return 1;
+    return true;
   }
 
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   if (CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   hs->extended_master_secret = true;
-  return 1;
+  return true;
 }
 
-static int ext_ems_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ems_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   if (!hs->extended_master_secret) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -911,12 +911,12 @@
 //
 // https://tools.ietf.org/html/rfc5077
 
-static int ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // TLS 1.3 uses a different ticket extension.
   if (hs->min_version >= TLS1_3_VERSION ||
       SSL_get_options(ssl) & SSL_OP_NO_TICKET) {
-    return 1;
+    return true;
   }
 
   const uint8_t *ticket_data = NULL;
@@ -940,21 +940,21 @@
       !CBB_add_u16_length_prefixed(out, &ticket) ||
       !CBB_add_bytes(&ticket, ticket_data, ticket_len) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ticket_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                        CBS *contents) {
+static bool ext_ticket_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                         CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 0;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return false;
   }
 
   // If |SSL_OP_NO_TICKET| is set then no extension will have been sent and
@@ -963,16 +963,16 @@
   assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0);
 
   if (CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   hs->ticket_expected = true;
-  return 1;
+  return true;
 }
 
-static int ext_ticket_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ticket_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   if (!hs->ticket_expected) {
-    return 1;
+    return true;
   }
 
   // If |SSL_OP_NO_TICKET| is set, |ticket_expected| should never be true.
@@ -980,10 +980,10 @@
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -991,10 +991,10 @@
 //
 // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
 
-static int ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (hs->max_version < TLS1_2_VERSION) {
-    return 1;
+    return true;
   }
 
   CBB contents, sigalgs_cbb;
@@ -1003,17 +1003,17 @@
       !CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb) ||
       !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_sigalgs_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                         CBS *contents) {
+static bool ext_sigalgs_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                          CBS *contents) {
   hs->peer_sigalgs.Reset();
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS supported_signature_algorithms;
@@ -1021,10 +1021,10 @@
       CBS_len(contents) != 0 ||
       CBS_len(&supported_signature_algorithms) == 0 ||
       !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1032,10 +1032,10 @@
 //
 // https://tools.ietf.org/html/rfc6066#section-8
 
-static int ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (!ssl->ocsp_stapling_enabled) {
-    return 1;
+    return true;
   }
 
   CBB contents;
@@ -1045,28 +1045,28 @@
       !CBB_add_u16(&contents, 0 /* empty responder ID list */) ||
       !CBB_add_u16(&contents, 0 /* empty request extensions */) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ocsp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                      CBS *contents) {
+static bool ext_ocsp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                       CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   // TLS 1.3 OCSP responses are included in the Certificate extensions.
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 0;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return false;
   }
 
   // OCSP stapling is forbidden on non-certificate ciphers.
   if (CBS_len(contents) != 0 ||
       !ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    return 0;
+    return false;
   }
 
   // Note this does not check for resumption in TLS 1.2. Sending
@@ -1074,35 +1074,35 @@
   // specification does not say anything. Tolerate it but ignore it.
 
   hs->certificate_status_expected = true;
-  return 1;
+  return true;
 }
 
-static int ext_ocsp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                      CBS *contents) {
+static bool ext_ocsp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                       CBS *contents) {
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   uint8_t status_type;
   if (!CBS_get_u8(contents, &status_type)) {
-    return 0;
+    return false;
   }
 
   // We cannot decide whether OCSP stapling will occur yet because the correct
   // SSL_CTX might not have been selected.
   hs->ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp;
 
-  return 1;
+  return true;
 }
 
-static int ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
       !hs->ocsp_stapling_requested ||
       ssl->cert->ocsp_response == NULL ||
       ssl->s3->session_reused ||
       !ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    return 1;
+    return true;
   }
 
   hs->certificate_status_expected = true;
@@ -1116,31 +1116,31 @@
 //
 // https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html
 
-static int ext_npn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_npn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->s3->initial_handshake_complete ||
       ssl->ctx->next_proto_select_cb == NULL ||
       SSL_is_dtls(ssl)) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_npn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_npn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 0;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return false;
   }
 
   // If any of these are false then we should never have sent the NPN
@@ -1154,7 +1154,7 @@
     // NPN and ALPN may not be negotiated in the same connection.
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN);
-    return 0;
+    return false;
   }
 
   const uint8_t *const orig_contents = CBS_data(contents);
@@ -1164,7 +1164,7 @@
     CBS proto;
     if (!CBS_get_u8_length_prefixed(contents, &proto) ||
         CBS_len(&proto) == 0) {
-      return 0;
+      return false;
     }
   }
 
@@ -1174,7 +1174,7 @@
           ssl, &selected, &selected_len, orig_contents, orig_len,
           ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
+    return false;
   }
 
   OPENSSL_free(ssl->s3->next_proto_negotiated);
@@ -1182,43 +1182,43 @@
       (uint8_t *)BUF_memdup(selected, selected_len);
   if (ssl->s3->next_proto_negotiated == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
+    return false;
   }
 
   ssl->s3->next_proto_negotiated_len = selected_len;
   hs->next_proto_neg_seen = true;
 
-  return 1;
+  return true;
 }
 
-static int ext_npn_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_npn_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 1;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return true;
   }
 
   if (contents != NULL && CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   if (contents == NULL ||
       ssl->s3->initial_handshake_complete ||
       ssl->ctx->next_protos_advertised_cb == NULL ||
       SSL_is_dtls(ssl)) {
-    return 1;
+    return true;
   }
 
   hs->next_proto_neg_seen = true;
-  return 1;
+  return true;
 }
 
-static int ext_npn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_npn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // |next_proto_neg_seen| might have been cleared when an ALPN extension was
   // parsed.
   if (!hs->next_proto_neg_seen) {
-    return 1;
+    return true;
   }
 
   const uint8_t *npa;
@@ -1228,7 +1228,7 @@
           ssl, &npa, &npa_len, ssl->ctx->next_protos_advertised_cb_arg) !=
       SSL_TLSEXT_ERR_OK) {
     hs->next_proto_neg_seen = false;
-    return 1;
+    return true;
   }
 
   CBB contents;
@@ -1236,10 +1236,10 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_bytes(&contents, npa, npa_len) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1247,31 +1247,31 @@
 //
 // https://tools.ietf.org/html/rfc6962#section-3.3.1
 
-static int ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (!ssl->signed_cert_timestamps_enabled) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_sct_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_sct_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   // TLS 1.3 SCTs are included in the Certificate extensions.
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   // If this is false then we should never have sent the SCT extension in the
@@ -1280,7 +1280,7 @@
 
   if (!ssl_is_sct_list_valid(contents)) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   // Session resumption uses the original session information. The extension
@@ -1294,34 +1294,34 @@
         CRYPTO_BUFFER_new_from_CBS(contents, ssl->ctx->pool);
     if (hs->new_session->signed_cert_timestamp_list == nullptr) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
-      return 0;
+      return false;
     }
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_sct_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                     CBS *contents) {
+static bool ext_sct_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                      CBS *contents) {
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   if (CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   hs->scts_requested = true;
-  return 1;
+  return true;
 }
 
-static int ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // The extension shouldn't be sent when resuming sessions.
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
       ssl->s3->session_reused ||
       ssl->cert->signed_cert_timestamp_list == NULL) {
-    return 1;
+    return true;
   }
 
   CBB contents;
@@ -1339,11 +1339,11 @@
 //
 // https://tools.ietf.org/html/rfc7301
 
-static int ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->alpn_client_proto_list == NULL ||
       ssl->s3->initial_handshake_complete) {
-    return 1;
+    return true;
   }
 
   CBB contents, proto_list;
@@ -1353,17 +1353,17 @@
       !CBB_add_bytes(&proto_list, ssl->alpn_client_proto_list,
                      ssl->alpn_client_proto_list_len) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                      CBS *contents) {
+static bool ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                       CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   assert(!ssl->s3->initial_handshake_complete);
@@ -1373,7 +1373,7 @@
     // NPN and ALPN may not be negotiated in the same connection.
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN);
-    return 0;
+    return false;
   }
 
   // The extension data consists of a ProtocolNameList which must have
@@ -1385,12 +1385,12 @@
       // Empty protocol names are forbidden.
       CBS_len(&protocol_name) == 0 ||
       CBS_len(&protocol_name_list) != 0) {
-    return 0;
+    return false;
   }
 
   if (!ssl->ctx->allow_unknown_alpn_protos) {
     // Check that the protocol name is one of the ones we advertised.
-    int protocol_ok = 0;
+    bool protocol_ok = false;
     CBS client_protocol_name_list, client_protocol_name;
     CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list,
              ssl->alpn_client_proto_list_len);
@@ -1398,14 +1398,14 @@
       if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
                                       &client_protocol_name)) {
         *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
+        return false;
       }
 
       if (CBS_len(&client_protocol_name) == CBS_len(&protocol_name) &&
           OPENSSL_memcmp(CBS_data(&client_protocol_name),
                          CBS_data(&protocol_name),
                          CBS_len(&protocol_name)) == 0) {
-        protocol_ok = 1;
+        protocol_ok = true;
         break;
       }
     }
@@ -1413,21 +1413,21 @@
     if (!protocol_ok) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-      return 0;
+      return false;
     }
   }
 
   if (!CBS_stow(&protocol_name, &ssl->s3->alpn_selected,
                 &ssl->s3->alpn_selected_len)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                       const SSL_CLIENT_HELLO *client_hello) {
+bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                        const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
   CBS contents;
   if (ssl->ctx->alpn_select_cb == NULL ||
@@ -1435,7 +1435,7 @@
           client_hello, &contents,
           TLSEXT_TYPE_application_layer_protocol_negotiation)) {
     // Ignore ALPN if not configured or no extension was supplied.
-    return 1;
+    return true;
   }
 
   // ALPN takes precedence over NPN.
@@ -1447,7 +1447,7 @@
       CBS_len(&protocol_name_list) < 2) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   // Validate the protocol list.
@@ -1460,7 +1460,7 @@
         CBS_len(&protocol_name) == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
       *out_alert = SSL_AD_DECODE_ERROR;
-      return 0;
+      return false;
     }
   }
 
@@ -1474,18 +1474,18 @@
     ssl->s3->alpn_selected = (uint8_t *)BUF_memdup(selected, selected_len);
     if (ssl->s3->alpn_selected == NULL) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
-      return 0;
+      return false;
     }
     ssl->s3->alpn_selected_len = selected_len;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->s3->alpn_selected == NULL) {
-    return 1;
+    return true;
   }
 
   CBB contents, proto_list, proto;
@@ -1496,10 +1496,10 @@
       !CBB_add_bytes(&proto, ssl->s3->alpn_selected,
                      ssl->s3->alpn_selected_len) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1511,68 +1511,70 @@
   hs->ssl->s3->tlsext_channel_id_valid = false;
 }
 
-static int ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (!ssl->tlsext_channel_id_enabled ||
       SSL_is_dtls(ssl)) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_channel_id_parse_serverhello(SSL_HANDSHAKE *hs,
-                                            uint8_t *out_alert, CBS *contents) {
+static bool ext_channel_id_parse_serverhello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert,
+                                             CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   assert(!SSL_is_dtls(ssl));
   assert(ssl->tlsext_channel_id_enabled);
 
   if (CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   ssl->s3->tlsext_channel_id_valid = true;
-  return 1;
+  return true;
 }
 
-static int ext_channel_id_parse_clienthello(SSL_HANDSHAKE *hs,
-                                            uint8_t *out_alert, CBS *contents) {
+static bool ext_channel_id_parse_clienthello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert,
+                                             CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL ||
       !ssl->tlsext_channel_id_enabled ||
       SSL_is_dtls(ssl)) {
-    return 1;
+    return true;
   }
 
   if (CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   ssl->s3->tlsext_channel_id_valid = true;
-  return 1;
+  return true;
 }
 
-static int ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (!ssl->s3->tlsext_channel_id_valid) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) ||
       !CBB_add_u16(out, 0 /* length */)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1585,40 +1587,40 @@
   hs->ssl->srtp_profile = NULL;
 }
 
-static int ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
   if (profiles == NULL ||
       sk_SRTP_PROTECTION_PROFILE_num(profiles) == 0) {
-    return 1;
+    return true;
   }
 
   CBB contents, profile_ids;
   if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &profile_ids)) {
-    return 0;
+    return false;
   }
 
   for (const SRTP_PROTECTION_PROFILE *profile : profiles) {
     if (!CBB_add_u16(&profile_ids, profile->id)) {
-      return 0;
+      return false;
     }
   }
 
   if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                      CBS *contents) {
+static bool ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                       CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   // The extension consists of a u16-prefixed profile ID list containing a
@@ -1633,14 +1635,14 @@
       !CBS_get_u8_length_prefixed(contents, &srtp_mki) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
-    return 0;
+    return false;
   }
 
   if (CBS_len(&srtp_mki) != 0) {
     // Must be no MKI, since we never offer one.
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_MKI_VALUE);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
   STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
@@ -1650,20 +1652,20 @@
   for (const SRTP_PROTECTION_PROFILE *profile : profiles) {
     if (profile->id == profile_id) {
       ssl->srtp_profile = profile;
-      return 1;
+      return true;
     }
   }
 
   OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
   *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-  return 0;
+  return false;
 }
 
-static int ext_srtp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                      CBS *contents) {
+static bool ext_srtp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                       CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS profile_ids, srtp_mki;
@@ -1672,7 +1674,7 @@
       !CBS_get_u8_length_prefixed(contents, &srtp_mki) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
-    return 0;
+    return false;
   }
   // Discard the MKI value for now.
 
@@ -1687,23 +1689,23 @@
     while (CBS_len(&profile_ids_tmp) > 0) {
       uint16_t profile_id;
       if (!CBS_get_u16(&profile_ids_tmp, &profile_id)) {
-        return 0;
+        return false;
       }
 
       if (server_profile->id == profile_id) {
         ssl->srtp_profile = server_profile;
-        return 1;
+        return true;
       }
     }
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->srtp_profile == NULL) {
-    return 1;
+    return true;
   }
 
   CBB contents, profile_ids;
@@ -1713,10 +1715,10 @@
       !CBB_add_u16(&profile_ids, ssl->srtp_profile->id) ||
       !CBB_add_u8(&contents, 0 /* empty MKI */) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1724,42 +1726,42 @@
 //
 // https://tools.ietf.org/html/rfc4492#section-5.1.2
 
-static int ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) {
   CBB contents, formats;
   if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u8_length_prefixed(&contents, &formats) ||
       !CBB_add_u8(&formats, TLSEXT_ECPOINTFORMAT_uncompressed) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   // The point format extension is unneccessary in TLS 1.3.
   if (hs->min_version >= TLS1_3_VERSION) {
-    return 1;
+    return true;
   }
 
   return ext_ec_point_add_extension(hs, out);
 }
 
-static int ext_ec_point_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                                          CBS *contents) {
+static bool ext_ec_point_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                           CBS *contents) {
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
-  if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
-    return 0;
+  if (ssl_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
+    return false;
   }
 
   CBS ec_point_format_list;
   if (!CBS_get_u8_length_prefixed(contents, &ec_point_format_list) ||
       CBS_len(contents) != 0) {
-    return 0;
+    return false;
   }
 
   // Per RFC 4492, section 5.1.2, implementations MUST support the uncompressed
@@ -1768,33 +1770,33 @@
                      TLSEXT_ECPOINTFORMAT_uncompressed,
                      CBS_len(&ec_point_format_list)) == NULL) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_ec_point_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+static bool ext_ec_point_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                           CBS *contents) {
-  if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
-    return 1;
+  if (ssl_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
+    return true;
   }
 
   return ext_ec_point_parse_serverhello(hs, out_alert, contents);
 }
 
-static int ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    return 1;
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return true;
   }
 
   const uint32_t alg_k = hs->new_cipher->algorithm_mkey;
   const uint32_t alg_a = hs->new_cipher->algorithm_auth;
-  const int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA);
+  const bool using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA);
 
   if (!using_ecc) {
-    return 1;
+    return true;
   }
 
   return ext_ec_point_add_extension(hs, out);
@@ -1816,11 +1818,11 @@
   return 15 + ssl->session->tlsext_ticklen + binder_len;
 }
 
-static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
       ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION) {
-    return 1;
+    return true;
   }
 
   struct OPENSSL_timeval now;
@@ -1844,35 +1846,35 @@
       !CBB_add_u16_length_prefixed(&contents, &binders) ||
       !CBB_add_u8_length_prefixed(&binders, &binder) ||
       !CBB_add_bytes(&binder, zero_binder, binder_len)) {
-    return 0;
+    return false;
   }
 
   hs->needs_psk_binder = true;
   return CBB_flush(out);
 }
 
-int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
-                                             uint8_t *out_alert,
-                                             CBS *contents) {
+bool ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
+                                              uint8_t *out_alert,
+                                              CBS *contents) {
   uint16_t psk_id;
   if (!CBS_get_u16(contents, &psk_id) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   // We only advertise one PSK identity, so the only legal index is zero.
   if (psk_id != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
     *out_alert = SSL_AD_UNKNOWN_PSK_IDENTITY;
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int ssl_ext_pre_shared_key_parse_clienthello(
+bool ssl_ext_pre_shared_key_parse_clienthello(
     SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders,
     uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents) {
   // We only process the first PSK identity since we don't support pure PSK.
@@ -1885,7 +1887,7 @@
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   *out_binders = binders;
@@ -1899,7 +1901,7 @@
         !CBS_get_u32(&identities, &unused_obfuscated_ticket_age)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       *out_alert = SSL_AD_DECODE_ERROR;
-      return 0;
+      return false;
     }
 
     num_identities++;
@@ -1913,7 +1915,7 @@
     if (!CBS_get_u8_length_prefixed(&binders, &binder)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       *out_alert = SSL_AD_DECODE_ERROR;
-      return 0;
+      return false;
     }
 
     num_binders++;
@@ -1922,15 +1924,15 @@
   if (num_identities != num_binders) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_BINDER_COUNT_MISMATCH);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+bool ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   if (!hs->ssl->s3->session_reused) {
-    return 1;
+    return true;
   }
 
   CBB contents;
@@ -1939,10 +1941,10 @@
       // We only consider the first identity for resumption
       !CBB_add_u16(&contents, 0) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -1950,10 +1952,10 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.7
 
-static int ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs,
-                                                      CBB *out) {
+static bool ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs,
+                                                       CBB *out) {
   if (hs->max_version < TLS1_3_VERSION) {
-    return 1;
+    return true;
   }
 
   CBB contents, ke_modes;
@@ -1961,17 +1963,17 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u8_length_prefixed(&contents, &ke_modes) ||
       !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE)) {
-    return 0;
+    return false;
   }
 
   return CBB_flush(out);
 }
 
-static int ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
-                                                        uint8_t *out_alert,
-                                                        CBS *contents) {
+static bool ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
+                                                         uint8_t *out_alert,
+                                                         CBS *contents) {
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS ke_modes;
@@ -1979,14 +1981,14 @@
       CBS_len(&ke_modes) == 0 ||
       CBS_len(contents) != 0) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   // We only support tickets with PSK_DHE_KE.
   hs->accept_psk_mode = OPENSSL_memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE,
                                        CBS_len(&ke_modes)) != NULL;
 
-  return 1;
+  return true;
 }
 
 
@@ -1994,14 +1996,14 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8
 
-static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl->session == NULL ||
       ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION ||
       ssl->session->ticket_max_early_data == 0 ||
       hs->received_hello_retry_request ||
       !ssl->cert->enable_early_data) {
-    return 1;
+    return true;
   }
 
   hs->early_data_offered = true;
@@ -2009,63 +2011,63 @@
   if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
       !CBB_add_u16(out, 0) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
-                                            uint8_t *out_alert, CBS *contents) {
+static bool ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert, CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   if (CBS_len(contents) != 0) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   if (!ssl->s3->session_reused) {
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    return 0;
+    return false;
   }
 
-  ssl->early_data_accepted = 1;
-  return 1;
+  ssl->early_data_accepted = true;
+  return true;
 }
 
-static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
-                                            uint8_t *out_alert, CBS *contents) {
+static bool ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert, CBS *contents) {
   SSL *const ssl = hs->ssl;
   if (contents == NULL ||
-      ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-    return 1;
+      ssl_protocol_version(ssl) < TLS1_3_VERSION) {
+    return true;
   }
 
   if (CBS_len(contents) != 0) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   hs->early_data_offered = true;
-  return 1;
+  return true;
 }
 
-static int ext_early_data_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_early_data_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   if (!hs->ssl->early_data_accepted) {
-    return 1;
+    return true;
   }
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
       !CBB_add_u16(out, 0) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -2073,17 +2075,17 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.5
 
-static int ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (hs->max_version < TLS1_3_VERSION) {
-    return 1;
+    return true;
   }
 
   CBB contents, kse_bytes;
   if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &kse_bytes)) {
-    return 0;
+    return false;
   }
 
   uint16_t group_id = hs->retry_group;
@@ -2093,7 +2095,7 @@
     if (group_id == 0 &&
         !CBB_add_bytes(&kse_bytes, hs->key_share_bytes.data(),
                        hs->key_share_bytes.size())) {
-      return 0;
+      return false;
     }
     hs->key_share_bytes.Reset();
     if (group_id == 0) {
@@ -2106,14 +2108,14 @@
                       ssl_get_grease_value(ssl, ssl_grease_group)) ||
          !CBB_add_u16(&kse_bytes, 1 /* length */) ||
          !CBB_add_u8(&kse_bytes, 0 /* one byte key share */))) {
-      return 0;
+      return false;
     }
 
     // Predict the most preferred group.
     Span<const uint16_t> groups = tls1_get_grouplist(ssl);
     if (groups.empty()) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_GROUPS_SPECIFIED);
-      return 0;
+      return false;
     }
 
     group_id = groups[0];
@@ -2126,93 +2128,93 @@
       !CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) ||
       !hs->key_share->Offer(&key_exchange) ||
       !CBB_flush(&kse_bytes)) {
-    return 0;
+    return false;
   }
 
   // Save the contents of the extension to repeat it in the second ClientHello.
   if (!hs->received_hello_retry_request &&
       !hs->key_share_bytes.CopyFrom(
           MakeConstSpan(CBB_data(&kse_bytes), CBB_len(&kse_bytes)))) {
-    return 0;
+    return false;
   }
 
   return CBB_flush(out);
 }
 
-int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
-                                        Array<uint8_t> *out_secret,
-                                        uint8_t *out_alert, CBS *contents) {
+bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
+                                         Array<uint8_t> *out_secret,
+                                         uint8_t *out_alert, CBS *contents) {
   CBS peer_key;
   uint16_t group_id;
   if (!CBS_get_u16(contents, &group_id) ||
       !CBS_get_u16_length_prefixed(contents, &peer_key) ||
       CBS_len(contents) != 0) {
     *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
+    return false;
   }
 
   if (hs->key_share->GroupID() != group_id) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-    return 0;
+    return false;
   }
 
   if (!hs->key_share->Finish(out_secret, out_alert, peer_key)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
-    return 0;
+    return false;
   }
 
   hs->new_session->group_id = group_id;
   hs->key_share.reset();
-  return 1;
+  return true;
 }
 
-int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
-                                        Array<uint8_t> *out_secret,
-                                        uint8_t *out_alert, CBS *contents) {
+bool ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
+                                         Array<uint8_t> *out_secret,
+                                         uint8_t *out_alert, CBS *contents) {
   uint16_t group_id;
   CBS key_shares;
   if (!tls1_get_shared_group(hs, &group_id)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    return 0;
+    return false;
   }
 
   if (!CBS_get_u16_length_prefixed(contents, &key_shares) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   // Find the corresponding key share.
-  bool found = false;
   CBS peer_key;
+  CBS_init(&peer_key, NULL, 0);
   while (CBS_len(&key_shares) > 0) {
     uint16_t id;
     CBS peer_key_tmp;
     if (!CBS_get_u16(&key_shares, &id) ||
-        !CBS_get_u16_length_prefixed(&key_shares, &peer_key_tmp)) {
+        !CBS_get_u16_length_prefixed(&key_shares, &peer_key_tmp) ||
+        CBS_len(&peer_key_tmp) == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      return 0;
+      return false;
     }
 
     if (id == group_id) {
-      if (found) {
+      if (CBS_len(&peer_key) != 0) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_KEY_SHARE);
         *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-        return 0;
+        return false;
       }
 
-      found = true;
       peer_key = peer_key_tmp;
       // Continue parsing the structure to keep peers honest.
     }
   }
 
-  if (!found) {
+  if (CBS_len(&peer_key) == 0) {
     *out_found = false;
     out_secret->Reset();
-    return 1;
+    return true;
   }
 
   // Compute the DH secret.
@@ -2224,15 +2226,15 @@
       !key_share->Accept(public_key.get(), &secret, out_alert, peer_key) ||
       !CBBFinishArray(public_key.get(), &hs->ecdh_public_key)) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
+    return false;
   }
 
   *out_secret = std::move(secret);
   *out_found = true;
-  return 1;
+  return true;
 }
 
-int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   uint16_t group_id;
   CBB kse_bytes, public_key;
   if (!tls1_get_shared_group(hs, &group_id) ||
@@ -2243,13 +2245,13 @@
       !CBB_add_bytes(&public_key, hs->ecdh_public_key.data(),
                      hs->ecdh_public_key.size()) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
   hs->ecdh_public_key.Reset();
 
   hs->new_session->group_id = group_id;
-  return 1;
+  return true;
 }
 
 
@@ -2257,31 +2259,31 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.1
 
-static int ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (hs->max_version <= TLS1_2_VERSION) {
-    return 1;
+    return true;
   }
 
   CBB contents, versions;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u8_length_prefixed(&contents, &versions)) {
-    return 0;
+    return false;
   }
 
   // Add a fake version. See draft-davidben-tls-grease-01.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&versions, ssl_get_grease_value(ssl, ssl_grease_version))) {
-    return 0;
+    return false;
   }
 
   if (!ssl_add_supported_versions(hs, &versions) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -2289,9 +2291,9 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.2
 
-static int ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   if (hs->cookie.empty()) {
-    return 1;
+    return true;
   }
 
   CBB contents, cookie;
@@ -2300,12 +2302,12 @@
       !CBB_add_u16_length_prefixed(&contents, &cookie) ||
       !CBB_add_bytes(&cookie, hs->cookie.data(), hs->cookie.size()) ||
       !CBB_flush(out)) {
-    return 0;
+    return false;
   }
 
   // The cookie is no longer needed in memory.
   hs->cookie.Reset();
-  return 1;
+  return true;
 }
 
 
@@ -2314,37 +2316,37 @@
 // https://tools.ietf.org/html/rfc4492#section-5.1.2
 // https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.4
 
-static int ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   CBB contents, groups_bytes;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &groups_bytes)) {
-    return 0;
+    return false;
   }
 
   // Add a fake group. See draft-davidben-tls-grease-01.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&groups_bytes,
                    ssl_get_grease_value(ssl, ssl_grease_group))) {
-    return 0;
+    return false;
   }
 
   for (uint16_t group : tls1_get_grouplist(ssl)) {
     if (!CBB_add_u16(&groups_bytes, group)) {
-      return 0;
+      return false;
     }
   }
 
   return CBB_flush(out);
 }
 
-static int ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs,
-                                                  uint8_t *out_alert,
-                                                  CBS *contents) {
+static bool ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs,
+                                                   uint8_t *out_alert,
+                                                   CBS *contents) {
   // This extension is not expected to be echoed by servers in TLS 1.2, but some
   // BigIP servers send it nonetheless, so do not enforce this.
-  return 1;
+  return true;
 }
 
 static bool parse_u16_array(const CBS *cbs, Array<uint16_t> *out) {
@@ -2370,11 +2372,11 @@
   return 1;
 }
 
-static int ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs,
+static bool ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs,
                                                   uint8_t *out_alert,
-                                                  CBS *contents) {
+                                                   CBS *contents) {
   if (contents == NULL) {
-    return 1;
+    return true;
   }
 
   CBS supported_group_list;
@@ -2382,15 +2384,10 @@
       CBS_len(&supported_group_list) == 0 ||
       CBS_len(contents) != 0 ||
       !parse_u16_array(&supported_group_list, &hs->peer_supported_group_list)) {
-    return 0;
+    return false;
   }
 
-  return 1;
-}
-
-static int ext_supported_groups_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  // Servers don't echo this extension.
-  return 1;
+  return true;
 }
 
 
@@ -2543,7 +2540,7 @@
     ext_supported_groups_add_clienthello,
     ext_supported_groups_parse_serverhello,
     ext_supported_groups_parse_clienthello,
-    ext_supported_groups_add_serverhello,
+    dont_add_serverhello,
   },
 };
 
@@ -2710,7 +2707,7 @@
   }
 
   // Discard empty extensions blocks before TLS 1.3.
-  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION &&
+  if (ssl_protocol_version(ssl) < TLS1_3_VERSION &&
       CBB_len(&extensions) == 0) {
     CBB_discard_child(out);
   }
@@ -2812,7 +2809,7 @@
   SSL *const ssl = hs->ssl;
   int alert = SSL_AD_DECODE_ERROR;
   if (ssl_scan_clienthello_tlsext(hs, client_hello, &alert) <= 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
@@ -2828,7 +2825,7 @@
                                        int *out_alert) {
   SSL *const ssl = hs->ssl;
   // Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
-  if (CBS_len(cbs) == 0 && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+  if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
     return 1;
   }
 
@@ -2920,7 +2917,7 @@
 
   switch (ret) {
     case SSL_TLSEXT_ERR_ALERT_FATAL:
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, al);
       return -1;
 
     case SSL_TLSEXT_ERR_NOACK:
@@ -2936,7 +2933,7 @@
   SSL *const ssl = hs->ssl;
   int alert = SSL_AD_DECODE_ERROR;
   if (ssl_scan_serverhello_tlsext(hs, cbs, &alert) <= 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
@@ -3144,7 +3141,7 @@
 
 int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *in_sigalgs) {
   // Extension ignored for inappropriate versions
-  if (ssl3_protocol_version(hs->ssl) < TLS1_2_VERSION) {
+  if (ssl_protocol_version(hs->ssl) < TLS1_2_VERSION) {
     return 1;
   }
 
@@ -3170,7 +3167,7 @@
 
   // Before TLS 1.2, the signature algorithm isn't negotiated as part of the
   // handshake.
-  if (ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
+  if (ssl_protocol_version(ssl) < TLS1_2_VERSION) {
     if (!tls1_get_legacy_signature_algorithm(out, hs->local_pubkey.get())) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS);
       return 0;
@@ -3184,7 +3181,7 @@
   }
 
   Span<const uint16_t> peer_sigalgs = hs->peer_sigalgs;
-  if (peer_sigalgs.empty() && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+  if (peer_sigalgs.empty() && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
     // If the client didn't specify any signature_algorithms extension then
     // we can assume that it supports SHA1. See
     // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
@@ -3225,7 +3222,7 @@
       extension_type != TLSEXT_TYPE_channel_id ||
       CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -3271,7 +3268,7 @@
 #endif
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     ssl->s3->tlsext_channel_id_valid = false;
     return 0;
   }
@@ -3332,16 +3329,14 @@
 
 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(hs, &msg, &msg_len,
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+    Array<uint8_t> msg;
+    if (!tls13_get_cert_verify_signature_input(hs, &msg,
                                                ssl_cert_verify_channel_id)) {
       return 0;
     }
-    SHA256(msg, msg_len, out);
+    SHA256(msg.data(), msg.size(), out);
     *out_len = SHA256_DIGEST_LENGTH;
-    OPENSSL_free(msg);
     return 1;
   }
 
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 5bed962..eee1337 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -497,7 +497,11 @@
 	RSABadValueCorrupt
 	RSABadValueTooLong
 	RSABadValueTooShort
-	RSABadValueWrongVersion
+	RSABadValueWrongVersion1
+	RSABadValueWrongVersion2
+	RSABadValueWrongBlockType
+	RSABadValueWrongLeadingByte
+	RSABadValueNoZero
 	NumRSABadValues
 )
 
@@ -560,6 +564,10 @@
 	// NewSessionTicket message despite promising to in ServerHello.
 	SkipNewSessionTicket bool
 
+	// UseFirstSessionTicket causes the client to cache only the first session
+	// ticket received.
+	UseFirstSessionTicket bool
+
 	// SkipClientCertificate causes the client to skip the Certificate
 	// message.
 	SkipClientCertificate bool
@@ -948,10 +956,22 @@
 	// record size.
 	PackHandshakeFragments int
 
-	// PackHandshakeRecords, if true, causes handshake records in DTLS to be
-	// packed into individual packets, up to the specified packet size.
+	// PackHandshakeRecords, if non-zero, causes handshake and
+	// ChangeCipherSpec records in DTLS to be packed into individual
+	// packets, up to the specified packet size.
 	PackHandshakeRecords int
 
+	// PackAppDataWithHandshake, if true, extends PackHandshakeRecords to
+	// additionally include the first application data record sent after the
+	// final Finished message in a handshake. (If the final Finished message
+	// is sent by the peer, this option has no effect.) This requires that
+	// the runner rather than shim speak first in a given test.
+	PackAppDataWithHandshake bool
+
+	// SplitAndPackAppData, if true, causes application data in DTLS to be
+	// split into two records each and packed into one packet.
+	SplitAndPackAppData bool
+
 	// PackHandshakeFlight, if true, causes each handshake flight in TLS to
 	// be packed into records, up to the largest size record available.
 	PackHandshakeFlight bool
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index a80e3c8..0edbe5c 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -96,6 +96,7 @@
 	handMsg          []byte   // pending assembled handshake message
 	handMsgLen       int      // handshake message length, not including the header
 	pendingFragments [][]byte // pending outgoing handshake fragments.
+	pendingPacket    []byte   // pending outgoing packet.
 
 	keyUpdateRequested bool
 	seenOneByteRecord  bool
@@ -1400,10 +1401,6 @@
 	c.out.Lock()
 	defer c.out.Unlock()
 
-	// Flush any pending handshake data. PackHelloRequestWithFinished may
-	// have been set and the handshake not followed by Renegotiate.
-	c.flushHandshake()
-
 	if err := c.out.err; err != nil {
 		return 0, err
 	}
@@ -1486,7 +1483,10 @@
 	}
 
 	cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
-	c.config.ClientSessionCache.Put(cacheKey, session)
+	_, ok := c.config.ClientSessionCache.Get(cacheKey)
+	if !ok || !c.config.Bugs.UseFirstSessionTicket {
+		c.config.ClientSessionCache.Put(cacheKey, session)
+	}
 	return nil
 }
 
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index bd111e0..b97f829 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -129,25 +129,37 @@
 }
 
 func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
+	// Only handshake messages are fragmented.
 	if typ != recordTypeHandshake {
 		reorder := typ == recordTypeChangeCipherSpec && c.config.Bugs.ReorderChangeCipherSpec
 
-		// Flush pending handshake messages before writing a new record.
+		// Flush pending handshake messages before encrypting a new record.
 		if !reorder {
-			err = c.dtlsFlushHandshake()
+			err = c.dtlsPackHandshake()
 			if err != nil {
 				return
 			}
 		}
 
-		// Only handshake messages are fragmented.
-		n, err = c.dtlsWriteRawRecord(typ, data)
-		if err != nil {
-			return
+		if typ == recordTypeApplicationData && len(data) > 1 && c.config.Bugs.SplitAndPackAppData {
+			_, err = c.dtlsPackRecord(typ, data[:len(data)/2], false)
+			if err != nil {
+				return
+			}
+			_, err = c.dtlsPackRecord(typ, data[len(data)/2:], true)
+			if err != nil {
+				return
+			}
+			n = len(data)
+		} else {
+			n, err = c.dtlsPackRecord(typ, data, false)
+			if err != nil {
+				return
+			}
 		}
 
 		if reorder {
-			err = c.dtlsFlushHandshake()
+			err = c.dtlsPackHandshake()
 			if err != nil {
 				return
 			}
@@ -158,12 +170,19 @@
 			if err != nil {
 				return n, c.sendAlertLocked(alertLevelError, err.(alert))
 			}
+		} else {
+			// ChangeCipherSpec is part of the handshake and not
+			// flushed until dtlsFlushPacket.
+			err = c.dtlsFlushPacket()
+			if err != nil {
+				return
+			}
 		}
 		return
 	}
 
 	if c.out.cipher == nil && c.config.Bugs.StrayChangeCipherSpec {
-		_, err = c.dtlsWriteRawRecord(recordTypeChangeCipherSpec, []byte{1})
+		_, err = c.dtlsPackRecord(recordTypeChangeCipherSpec, []byte{1}, false)
 		if err != nil {
 			return
 		}
@@ -243,7 +262,9 @@
 	return
 }
 
-func (c *Conn) dtlsFlushHandshake() error {
+// dtlsPackHandshake packs the pending handshake flight into the pending
+// record. Callers should follow up with dtlsFlushPacket to write the packets.
+func (c *Conn) dtlsPackHandshake() error {
 	// This is a test-only DTLS implementation, so there is no need to
 	// retain |c.pendingFragments| for a future retransmit.
 	var fragments [][]byte
@@ -265,7 +286,6 @@
 	}
 
 	maxRecordLen := c.config.Bugs.PackHandshakeFragments
-	maxPacketLen := c.config.Bugs.PackHandshakeRecords
 
 	// Pack handshake fragments into records.
 	var records [][]byte
@@ -285,42 +305,39 @@
 		}
 	}
 
-	// Format them into packets.
-	var packets [][]byte
+	// Send the records.
 	for _, record := range records {
-		b, err := c.dtlsSealRecord(recordTypeHandshake, record)
+		_, err := c.dtlsPackRecord(recordTypeHandshake, record, false)
 		if err != nil {
 			return err
 		}
-
-		if i := len(packets) - 1; len(packets) > 0 && len(packets[i])+len(b.data) <= maxPacketLen {
-			packets[i] = append(packets[i], b.data...)
-		} else {
-			// The sealed record will be appended to and reused by
-			// |c.out|, so copy it.
-			packets = append(packets, append([]byte{}, b.data...))
-		}
-		c.out.freeBlock(b)
 	}
 
-	// Send all the packets.
-	for _, packet := range packets {
-		if _, err := c.conn.Write(packet); err != nil {
-			return err
-		}
-	}
 	return nil
 }
 
-// dtlsSealRecord seals a record into a block from |c.out|'s pool.
-func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) {
+func (c *Conn) dtlsFlushHandshake() error {
+	if err := c.dtlsPackHandshake(); err != nil {
+		return err
+	}
+	if err := c.dtlsFlushPacket(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// dtlsPackRecord packs a single record to the pending packet, flushing it
+// if necessary. The caller should call dtlsFlushPacket to flush the current
+// pending packet afterwards.
+func (c *Conn) dtlsPackRecord(typ recordType, data []byte, mustPack bool) (n int, err error) {
 	recordHeaderLen := dtlsRecordHeaderLen
 	maxLen := c.config.Bugs.MaxHandshakeRecordLength
 	if maxLen <= 0 {
 		maxLen = 1024
 	}
 
-	b = c.out.newBlock()
+	b := c.out.newBlock()
 
 	explicitIVLen := 0
 	explicitIVIsSeq := false
@@ -372,23 +389,30 @@
 	}
 	copy(b.data[recordHeaderLen+explicitIVLen:], data)
 	c.out.encrypt(b, explicitIVLen, typ)
+
+	// Flush the current pending packet if necessary.
+	if !mustPack && len(b.data)+len(c.pendingPacket) > c.config.Bugs.PackHandshakeRecords {
+		err = c.dtlsFlushPacket()
+		if err != nil {
+			c.out.freeBlock(b)
+			return
+		}
+	}
+
+	// Add the record to the pending packet.
+	c.pendingPacket = append(c.pendingPacket, b.data...)
+	c.out.freeBlock(b)
+	n = len(data)
 	return
 }
 
-func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) {
-	b, err := c.dtlsSealRecord(typ, data)
-	if err != nil {
-		return
+func (c *Conn) dtlsFlushPacket() error {
+	if len(c.pendingPacket) == 0 {
+		return nil
 	}
-
-	_, err = c.conn.Write(b.data)
-	if err != nil {
-		return
-	}
-	n = len(data)
-
-	c.out.freeBlock(b)
-	return
+	_, err := c.conn.Write(c.pendingPacket)
+	c.pendingPacket = nil
+	return err
 }
 
 func (c *Conn) dtlsDoReadHandshake() ([]byte, error) {
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 0c23793..ef6a464 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -1622,7 +1622,9 @@
 		}
 	}
 
-	c.flushHandshake()
+	if !isResume || !c.config.Bugs.PackAppDataWithHandshake {
+		c.flushHandshake()
+	}
 	return nil
 }
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index f67cc94..0ffb72c 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -80,7 +80,7 @@
 					return err
 				}
 			}
-			if err := hs.sendFinished(c.firstFinished[:]); err != nil {
+			if err := hs.sendFinished(c.firstFinished[:], isResume); err != nil {
 				return err
 			}
 			// Most retransmits are triggered by a timeout, but the final
@@ -120,7 +120,7 @@
 			if err := hs.sendSessionTicket(); err != nil {
 				return err
 			}
-			if err := hs.sendFinished(nil); err != nil {
+			if err := hs.sendFinished(nil, isResume); err != nil {
 				return err
 			}
 		}
@@ -1800,7 +1800,7 @@
 	return nil
 }
 
-func (hs *serverHandshakeState) sendFinished(out []byte) error {
+func (hs *serverHandshakeState) sendFinished(out []byte, isResume bool) error {
 	c := hs.c
 
 	finished := new(finishedMsg)
@@ -1845,8 +1845,8 @@
 		}
 	}
 
-	if !c.config.Bugs.PackHelloRequestWithFinished {
-		// Defer flushing until renegotiation.
+	if isResume || (!c.config.Bugs.PackHelloRequestWithFinished && !c.config.Bugs.PackAppDataWithHandshake) {
+		// Defer flushing until Renegotiate() or Write().
 		c.flushHandshake()
 	}
 
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index e33557b..5071985 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -136,12 +136,47 @@
 	return errors.New("tls: unexpected ServerKeyExchange")
 }
 
+func rsaSize(pub *rsa.PublicKey) int {
+	return (pub.N.BitLen() + 7) / 8
+}
+
+func rsaRawEncrypt(pub *rsa.PublicKey, msg []byte) ([]byte, error) {
+	k := rsaSize(pub)
+	if len(msg) != k {
+		return nil, errors.New("tls: bad padded RSA input")
+	}
+	m := new(big.Int).SetBytes(msg)
+	e := big.NewInt(int64(pub.E))
+	m.Exp(m, e, pub.N)
+	unpadded := m.Bytes()
+	ret := make([]byte, k)
+	copy(ret[len(ret)-len(unpadded):], unpadded)
+	return ret, nil
+}
+
+// nonZeroRandomBytes fills the given slice with non-zero random octets.
+func nonZeroRandomBytes(s []byte, rand io.Reader) {
+	if _, err := io.ReadFull(rand, s); err != nil {
+		panic(err)
+	}
+
+	for i := range s {
+		for s[i] == 0 {
+			if _, err := io.ReadFull(rand, s[i:i+1]); err != nil {
+				panic(err)
+			}
+		}
+	}
+}
+
 func (ka *rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
 	bad := config.Bugs.BadRSAClientKeyExchange
 	preMasterSecret := make([]byte, 48)
 	vers := clientHello.vers
-	if bad == RSABadValueWrongVersion {
+	if bad == RSABadValueWrongVersion1 {
 		vers ^= 1
+	} else if bad == RSABadValueWrongVersion2 {
+		vers ^= 0x100
 	}
 	preMasterSecret[0] = byte(vers >> 8)
 	preMasterSecret[1] = byte(vers)
@@ -152,13 +187,31 @@
 
 	sentPreMasterSecret := preMasterSecret
 	if bad == RSABadValueTooLong {
-		sentPreMasterSecret = make([]byte, len(sentPreMasterSecret)+1)
-		copy(sentPreMasterSecret, preMasterSecret)
+		sentPreMasterSecret = make([]byte, 1, len(sentPreMasterSecret)+1)
+		sentPreMasterSecret = append(sentPreMasterSecret, preMasterSecret...)
 	} else if bad == RSABadValueTooShort {
 		sentPreMasterSecret = sentPreMasterSecret[:len(sentPreMasterSecret)-1]
 	}
 
-	encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), sentPreMasterSecret)
+	// Pad for PKCS#1 v1.5.
+	padded := make([]byte, rsaSize(cert.PublicKey.(*rsa.PublicKey)))
+	padded[1] = 2
+	nonZeroRandomBytes(padded[2:len(padded)-len(sentPreMasterSecret)-1], config.rand())
+	copy(padded[len(padded)-len(sentPreMasterSecret):], sentPreMasterSecret)
+
+	if bad == RSABadValueWrongBlockType {
+		padded[1] = 3
+	} else if bad == RSABadValueWrongLeadingByte {
+		padded[0] = 1
+	} else if bad == RSABadValueNoZero {
+		for i := 2; i < len(padded); i++ {
+			if padded[i] == 0 {
+				padded[i]++
+			}
+		}
+	}
+
+	encrypted, err := rsaRawEncrypt(cert.PublicKey.(*rsa.PublicKey), padded)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 5918e6b..a57362d 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -837,6 +837,13 @@
 			if err != nil {
 				return err
 			}
+			if config.Bugs.SplitAndPackAppData {
+				m, err := tlsConn.Read(bufTmp[n:])
+				if err != nil {
+					return err
+				}
+				n += m
+			}
 			if n != len(buf) {
 				return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
 			}
@@ -2293,17 +2300,6 @@
 			flags: []string{"-async"},
 		},
 		{
-			protocol: dtls,
-			name:     "PackDTLSHandshake",
-			config: Config{
-				Bugs: ProtocolBugs{
-					MaxHandshakeRecordLength: 2,
-					PackHandshakeFragments:   20,
-					PackHandshakeRecords:     200,
-				},
-			},
-		},
-		{
 			name:             "SendEmptyRecords-Pass",
 			sendEmptyRecords: 32,
 		},
@@ -2495,7 +2491,7 @@
 				"-expect-total-renegotiations", "1",
 			},
 			shouldFail:    true,
-			expectedError: ":EXCESSIVE_MESSAGE_SIZE:",
+			expectedError: ":BAD_HELLO_REQUEST:",
 		},
 		{
 			name:        "BadHelloRequest-2",
@@ -2796,6 +2792,27 @@
 			shouldFail:         true,
 			expectedLocalError: "local error: record overflow",
 		},
+		{
+			// Test that DTLS can handle multiple application data
+			// records in a single packet.
+			protocol: dtls,
+			name:     "SplitAndPackAppData-DTLS",
+			config: Config{
+				Bugs: ProtocolBugs{
+					SplitAndPackAppData: true,
+				},
+			},
+		},
+		{
+			protocol: dtls,
+			name:     "SplitAndPackAppData-DTLS-Async",
+			config: Config{
+				Bugs: ProtocolBugs{
+					SplitAndPackAppData: true,
+				},
+			},
+			flags: []string{"-async"},
+		},
 	}
 	testCases = append(testCases, basicTests...)
 
@@ -3931,11 +3948,11 @@
 }
 
 type stateMachineTestConfig struct {
-	protocol            protocol
-	async               bool
-	splitHandshake      bool
-	packHandshakeFlight bool
-	implicitHandshake   bool
+	protocol          protocol
+	async             bool
+	splitHandshake    bool
+	packHandshake     bool
+	implicitHandshake bool
 }
 
 // Adds tests that try to cover the range of the handshake state machine, under
@@ -3958,13 +3975,11 @@
 				async:          async,
 				splitHandshake: true,
 			})
-			if protocol == tls {
-				addStateMachineCoverageTests(stateMachineTestConfig{
-					protocol:            protocol,
-					async:               async,
-					packHandshakeFlight: true,
-				})
-			}
+			addStateMachineCoverageTests(stateMachineTestConfig{
+				protocol:      protocol,
+				async:         async,
+				packHandshake: true,
+			})
 		}
 	}
 }
@@ -4603,7 +4618,7 @@
 			config: Config{
 				MaxVersion: VersionTLS12,
 				Bugs: ProtocolBugs{
-					PackHelloRequestWithFinished: config.packHandshakeFlight,
+					PackHelloRequestWithFinished: config.packHandshake,
 				},
 			},
 			sendHalfHelloRequest: true,
@@ -4913,9 +4928,16 @@
 				test.flags = append(test.flags, "-mtu", "256")
 			}
 		}
-		if config.packHandshakeFlight {
-			test.name += "-PackHandshakeFlight"
-			test.config.Bugs.PackHandshakeFlight = true
+		if config.packHandshake {
+			test.name += "-PackHandshake"
+			if config.protocol == dtls {
+				test.config.Bugs.MaxHandshakeRecordLength = 2
+				test.config.Bugs.PackHandshakeFragments = 20
+				test.config.Bugs.PackHandshakeRecords = 1500
+				test.config.Bugs.PackAppDataWithHandshake = true
+			} else {
+				test.config.Bugs.PackHandshakeFlight = true
+			}
 		}
 		if config.implicitHandshake {
 			test.name += "-ImplicitHandshake"
@@ -10871,6 +10893,27 @@
 			},
 		})
 
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-FirstTicket-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				MinVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					UseFirstSessionTicket:   true,
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				},
+			},
+			tls13Variant:  variant,
+			messageCount:  2,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+			},
+		})
 	}
 
 	testCases = append(testCases, testCase{
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index e0fefe3..5796bf4 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -37,57 +37,55 @@
 // without being able to return application data.
 static const uint8_t kMaxKeyUpdates = 32;
 
-int tls13_get_cert_verify_signature_input(
-    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len,
+bool tls13_get_cert_verify_signature_input(
+    SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context) {
   ScopedCBB cbb;
   if (!CBB_init(cbb.get(), 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   for (size_t i = 0; i < 64; i++) {
     if (!CBB_add_u8(cbb.get(), 0x20)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      return 0;
+      return false;
     }
   }
 
-  const uint8_t *context;
-  size_t context_len;
+  Span<const char> context;
   if (cert_verify_context == ssl_cert_verify_server) {
-    // Include the NUL byte.
     static const char kContext[] = "TLS 1.3, server CertificateVerify";
-    context = (const uint8_t *)kContext;
-    context_len = sizeof(kContext);
+    context = kContext;
   } else if (cert_verify_context == ssl_cert_verify_client) {
     static const char kContext[] = "TLS 1.3, client CertificateVerify";
-    context = (const uint8_t *)kContext;
-    context_len = sizeof(kContext);
+    context = kContext;
   } else if (cert_verify_context == ssl_cert_verify_channel_id) {
     static const char kContext[] = "TLS 1.3, Channel ID";
-    context = (const uint8_t *)kContext;
-    context_len = sizeof(kContext);
+    context = kContext;
   } else {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
-  if (!CBB_add_bytes(cbb.get(), context, context_len)) {
+  // Note |context| includes the NUL byte separator.
+  if (!CBB_add_bytes(cbb.get(),
+                     reinterpret_cast<const uint8_t *>(context.data()),
+                     context.size())) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
   if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
       !CBB_add_bytes(cbb.get(), context_hash, context_hash_len) ||
-      !CBB_finish(cbb.get(), out, out_len)) {
+      !CBBFinishArray(cbb.get(), out)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
@@ -98,14 +96,14 @@
       CBS_len(&context) != 0 ||
       !CBS_get_u24_length_prefixed(&body, &certificate_list) ||
       CBS_len(&body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return 0;
   }
 
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> certs(sk_CRYPTO_BUFFER_new_null());
   if (!certs) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
   }
@@ -118,7 +116,7 @@
     if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) ||
         !CBS_get_u16_length_prefixed(&certificate_list, &extensions) ||
         CBS_len(&certificate) == 0) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
       return 0;
     }
@@ -126,14 +124,14 @@
     if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) {
       pkey = ssl_cert_parse_pubkey(&certificate);
       if (!pkey) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
         return 0;
       }
       // TLS 1.3 always uses certificate keys for signing thus the correct
       // keyUsage is enforced.
       if (!ssl_cert_check_digital_signature_key_usage(&certificate)) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
         return 0;
       }
 
@@ -148,7 +146,7 @@
         CRYPTO_BUFFER_new_from_CBS(&certificate, ssl->ctx->pool));
     if (!buf ||
         !PushToStack(certs.get(), std::move(buf))) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       return 0;
     }
@@ -165,7 +163,7 @@
     if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                               OPENSSL_ARRAY_SIZE(ext_types),
                               0 /* reject unknown */)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return 0;
     }
 
@@ -174,7 +172,7 @@
     if (have_status_request) {
       if (ssl->server || !ssl->ocsp_stapling_enabled) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
         return 0;
       }
 
@@ -185,7 +183,7 @@
           !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) ||
           CBS_len(&ocsp_response) == 0 ||
           CBS_len(&status_request) != 0) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return 0;
       }
 
@@ -194,7 +192,7 @@
         hs->new_session->ocsp_response =
             CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool);
         if (hs->new_session->ocsp_response == nullptr) {
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+          ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
           return 0;
         }
       }
@@ -203,13 +201,13 @@
     if (have_sct) {
       if (ssl->server || !ssl->signed_cert_timestamps_enabled) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
         return 0;
       }
 
       if (!ssl_is_sct_list_valid(&sct)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return 0;
       }
 
@@ -218,7 +216,7 @@
         hs->new_session->signed_cert_timestamp_list =
             CRYPTO_BUFFER_new_from_CBS(&sct, ssl->ctx->pool);
         if (hs->new_session->signed_cert_timestamp_list == nullptr) {
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+          ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
           return 0;
         }
       }
@@ -238,14 +236,14 @@
 
   if (!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
   if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
     if (!allow_anonymous) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED);
       return 0;
     }
 
@@ -274,37 +272,34 @@
       !CBS_get_u16_length_prefixed(&body, &signature) ||
       CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
   hs->new_session->peer_signature_algorithm = signature_algorithm;
 
-  uint8_t *input = NULL;
-  size_t input_len;
+  Array<uint8_t> input;
   if (!tls13_get_cert_verify_signature_input(
-          hs, &input, &input_len,
+          hs, &input,
           ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return 0;
   }
-  UniquePtr<uint8_t> free_input(input);
 
-  int sig_ok = ssl_public_key_verify(ssl, CBS_data(&signature),
-                                     CBS_len(&signature), signature_algorithm,
-                                     hs->peer_pubkey.get(), input, input_len);
+  bool sig_ok = ssl_public_key_verify(ssl, signature, signature_algorithm,
+                                      hs->peer_pubkey.get(), input);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  sig_ok = 1;
+  sig_ok = true;
   ERR_clear_error();
 #endif
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     return 0;
   }
 
@@ -334,7 +329,7 @@
   finished_ok = 1;
 #endif
   if (!finished_ok) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     return 0;
   }
@@ -437,22 +432,20 @@
   size_t sig_len;
   if (!CBB_add_u16_length_prefixed(&body, &child) ||
       !CBB_reserve(&child, &sig, max_sig_len)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_private_key_failure;
   }
 
-  uint8_t *msg = NULL;
-  size_t msg_len;
+  Array<uint8_t> msg;
   if (!tls13_get_cert_verify_signature_input(
-          hs, &msg, &msg_len,
+          hs, &msg,
           ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_private_key_failure;
   }
-  UniquePtr<uint8_t> free_msg(msg);
 
   enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
-      hs, sig, &sig_len, max_sig_len, signature_algorithm, msg, msg_len);
+      hs, sig, &sig_len, max_sig_len, signature_algorithm, msg);
   if (sign_result != ssl_private_key_success) {
     return sign_result;
   }
@@ -471,7 +464,7 @@
   uint8_t verify_data[EVP_MAX_MD_SIZE];
 
   if (!tls13_finished_mac(hs, verify_data, &verify_data_len, ssl->server)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     return 0;
   }
@@ -495,7 +488,7 @@
       (key_update_request != SSL_KEY_UPDATE_NOT_REQUESTED &&
        key_update_request != SSL_KEY_UPDATE_REQUESTED)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
@@ -531,7 +524,7 @@
     ssl->s3->key_update_count++;
     if (ssl->s3->key_update_count > kMaxKeyUpdates) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_KEY_UPDATES);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return 0;
     }
 
@@ -544,7 +537,7 @@
     return tls13_process_new_session_ticket(ssl, msg);
   }
 
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+  ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
   OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
   return 0;
 }
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 7ed9fad..e75d976 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -70,7 +70,7 @@
       CBS_len(&extensions) == 0 ||
       CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -85,7 +85,7 @@
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             0 /* reject unknown */)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -95,7 +95,7 @@
         CBS_len(&cookie_value) == 0 ||
         CBS_len(&cookie) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
@@ -108,13 +108,13 @@
     uint16_t group_id;
     if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
     // The group must be supported.
     if (!tls1_check_group_id(ssl, group_id)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       return ssl_hs_error;
     }
@@ -122,7 +122,7 @@
     // Check that the HelloRetryRequest does not request the key share that
     // was provided in the initial ClientHello.
     if (hs->key_share->GroupID() == group_id) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       return ssl_hs_error;
     }
@@ -188,7 +188,7 @@
        (!CBS_get_u8(&body, &compression_method) || compression_method != 0)) ||
       !CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
@@ -197,7 +197,7 @@
                                   ? TLS1_2_VERSION
                                   : ssl->version;
   if (server_version != expected_version) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     return ssl_hs_error;
   }
@@ -209,15 +209,15 @@
   const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
   if (cipher == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
   // Check if the cipher is a TLS 1.3 cipher.
-  if (SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl)) {
+  if (SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
@@ -236,7 +236,7 @@
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             0 /* reject unknown */)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -244,7 +244,7 @@
   // TLS 1.3 version.
   if (have_supported_versions && !ssl_is_resumption_experiment(ssl->version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
     return ssl_hs_error;
   }
 
@@ -252,25 +252,25 @@
   if (have_pre_shared_key) {
     if (ssl->session == NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
       return ssl_hs_error;
     }
 
     if (!ssl_ext_pre_shared_key_parse_serverhello(hs, &alert,
                                                   &pre_shared_key)) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
 
     if (ssl->session->ssl_version != ssl->version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
     if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
@@ -278,7 +278,7 @@
       // This is actually a client application bug.
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
 
@@ -286,7 +286,7 @@
     // Only authentication information carries over in TLS 1.3.
     hs->new_session = SSL_SESSION_dup(ssl->session, SSL_SESSION_DUP_AUTH_ONLY);
     if (!hs->new_session) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     ssl_set_session(ssl, NULL);
@@ -295,7 +295,7 @@
     ssl_session_renew_timeout(ssl, hs->new_session.get(),
                               ssl->session_ctx->session_psk_dhe_timeout);
   } else if (!ssl_get_new_session(hs, 0)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -320,7 +320,7 @@
   if (!have_key_share) {
     // We do not support psk_ke and thus always require a key share.
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
     return ssl_hs_error;
   }
 
@@ -329,7 +329,7 @@
   alert = SSL_AD_DECODE_ERROR;
   if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &alert,
                                            &key_share)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -357,7 +357,7 @@
     // If not sending early data, set client traffic keys now so that alerts are
     // encrypted.
     if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
-         !ssl3_add_change_cipher_spec(ssl)) ||
+         !ssl->method->add_change_cipher_spec(ssl)) ||
         !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
                                hs->hash_len)) {
       return ssl_hs_error;
@@ -385,7 +385,7 @@
   }
   if (CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -394,7 +394,7 @@
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
         ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
     if (hs->new_session->early_alpn == NULL) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len;
@@ -452,7 +452,7 @@
       !CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) ||
       CBS_len(&supported_signature_algorithms) == 0 ||
       !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
@@ -461,7 +461,7 @@
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names =
       ssl_parse_client_CA_list(ssl, &alert, &body);
   if (!ca_names) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -469,7 +469,7 @@
   CBS extensions;
   if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
@@ -565,7 +565,7 @@
 
   if (hs->early_data_offered) {
     if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
-         !ssl3_add_change_cipher_spec(ssl)) ||
+         !ssl->method->add_change_cipher_spec(ssl)) ||
         !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
                                hs->hash_len)) {
       return ssl_hs_error;
@@ -589,7 +589,7 @@
   if (ssl->cert->cert_cb != NULL) {
     int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
     if (rv == 0) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
       return ssl_hs_error;
     }
@@ -790,7 +790,7 @@
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
       !CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return 0;
   }
@@ -813,14 +813,14 @@
   if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             OPENSSL_ARRAY_SIZE(ext_types),
                             1 /* ignore unknown */)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
   if (have_early_data_info && ssl->cert->enable_early_data) {
     if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
         CBS_len(&early_data_info) != 0) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       return 0;
     }
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 0a36aab..6c7e1da 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -47,7 +47,7 @@
 }
 
 int tls13_init_key_schedule(SSL_HANDSHAKE *hs) {
-  if (!init_key_schedule(hs, ssl3_protocol_version(hs->ssl), hs->new_cipher)) {
+  if (!init_key_schedule(hs, ssl_protocol_version(hs->ssl), hs->new_cipher)) {
     return 0;
   }
 
@@ -382,8 +382,8 @@
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
   if (!EVP_DigestInit_ex(ctx.get(), digest, NULL) ||
-      !EVP_DigestUpdate(ctx.get(), hs->transcript.buffer_data(),
-                        hs->transcript.buffer_len()) ||
+      !EVP_DigestUpdate(ctx.get(), hs->transcript.buffer().data(),
+                        hs->transcript.buffer().size()) ||
       !EVP_DigestUpdate(ctx.get(), msg, len - hash_len - 3) ||
       !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
     return 0;
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index ea1beae..09437a5 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -69,7 +69,7 @@
   if (!ssl_client_hello_get_extension(client_hello, &key_share,
                                       TLSEXT_TYPE_key_share)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
     return 0;
   }
 
@@ -78,7 +78,7 @@
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &dhe_secret,
                                            &alert, &key_share)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
@@ -114,7 +114,7 @@
            client_hello->cipher_suites_len);
 
   const int aes_is_fine = EVP_has_aes_hardware();
-  const uint16_t version = ssl3_protocol_version(ssl);
+  const uint16_t version = ssl_protocol_version(ssl);
 
   const SSL_CIPHER *best = NULL;
   while (CBS_len(&cipher_suites) > 0) {
@@ -165,6 +165,10 @@
     }
     hs->new_session->ticket_age_add_valid = 1;
 
+    if (ssl->cert->enable_early_data) {
+      hs->new_session->ticket_max_early_data = kMaxEarlyDataAccepted;
+    }
+
     ScopedCBB cbb;
     CBB body, ticket, extensions;
     if (!ssl->method->init_message(ssl, cbb.get(), &body,
@@ -178,8 +182,6 @@
     }
 
     if (ssl->cert->enable_early_data) {
-      hs->new_session->ticket_max_early_data = kMaxEarlyDataAccepted;
-
       CBB early_data_info;
       if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) ||
           !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
@@ -216,7 +218,7 @@
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -228,7 +230,7 @@
   hs->new_cipher = choose_tls13_cipher(ssl, &client_hello);
   if (hs->new_cipher == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     return ssl_hs_error;
   }
 
@@ -236,7 +238,7 @@
   // deferred. Complete it now.
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -347,7 +349,7 @@
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -358,7 +360,7 @@
     case ssl_ticket_aead_ignore_ticket:
       assert(!session);
       if (!ssl_get_new_session(hs, 1 /* server */)) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return ssl_hs_error;
       }
       break;
@@ -382,11 +384,11 @@
           ssl->s3->alpn_selected_len == session->early_alpn_len &&
           OPENSSL_memcmp(ssl->s3->alpn_selected, session->early_alpn,
                          ssl->s3->alpn_selected_len) == 0) {
-        ssl->early_data_accepted = 1;
+        ssl->early_data_accepted = true;
       }
 
       if (hs->new_session == NULL) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return ssl_hs_error;
       }
 
@@ -398,7 +400,7 @@
       break;
 
     case ssl_ticket_aead_error:
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
 
     case ssl_ticket_aead_retry:
@@ -414,7 +416,7 @@
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
         ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
     if (hs->new_session->early_alpn == NULL) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
     hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len;
@@ -424,7 +426,7 @@
       ssl->ctx->dos_protection_cb(&client_hello) == 0) {
     // Connection rejected for DOS reasons.
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
@@ -450,7 +452,7 @@
   bool need_retry;
   if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
     if (need_retry) {
-      ssl->early_data_accepted = 0;
+      ssl->early_data_accepted = false;
       ssl->s3->skip_early_data = true;
       ssl->method->next_message(ssl);
       hs->tls13_state = state_send_hello_retry_request;
@@ -497,7 +499,7 @@
   SSL_CLIENT_HELLO client_hello;
   if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -505,7 +507,7 @@
   if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
     if (need_retry) {
       // Only send one HelloRetryRequest.
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
     }
     return ssl_hs_error;
@@ -550,7 +552,7 @@
   }
 
   if (ssl_is_resumption_experiment(ssl->version) &&
-      !ssl3_add_change_cipher_spec(ssl)) {
+      !ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
   }
 
@@ -666,8 +668,9 @@
     assert(hs->hash_len <= 0xff);
     uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0,
                          static_cast<uint8_t>(hs->hash_len)};
-    if (!hs->transcript.Update(header, sizeof(header)) ||
-        !hs->transcript.Update(hs->expected_client_finished, hs->hash_len) ||
+    if (!hs->transcript.Update(header) ||
+        !hs->transcript.Update(
+            MakeConstSpan(hs->expected_client_finished, hs->hash_len)) ||
         !tls13_derive_resumption_secret(hs) ||
         !add_new_session_tickets(hs)) {
       return ssl_hs_error;
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 286bf03..b4d08a0 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -74,11 +74,10 @@
   assert(!ssl->s3->has_message);
 
   // During the handshake, |init_buf| is retained. Release if it there is no
-  // excess in it.
+  // excess in it. There may be excess left if there server sent Finished and
+  // HelloRequest in the same record.
   //
-  // TODO(davidben): The second check is always true but will not be once we
-  // switch to copying the entire handshake record. Replace this comment with an
-  // explanation when that happens and a TODO to reject it.
+  // TODO(davidben): SChannel does not support this. Reject this case.
   if (ssl->init_buf != NULL && ssl->init_buf->length == 0) {
     BUF_MEM_free(ssl->init_buf);
     ssl->init_buf = NULL;
@@ -86,10 +85,13 @@
 }
 
 static int ssl3_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
-  if (ssl->s3->rrec.length != 0) {
-    // There may not be unprocessed record data at a cipher change.
+  // Cipher changes are forbidden if the current epoch has leftover data.
+  //
+  // TODO(davidben): ssl->s3->rrec.length should be impossible now. Remove it
+  // once it is only used for application data.
+  if (ssl->s3->rrec.length != 0 || tls_has_unprocessed_handshake_data(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     return 0;
   }
 
diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc
index 5eeff3c..44a04d9 100644
--- a/src/ssl/tls_record.cc
+++ b/src/ssl/tls_record.cc
@@ -187,13 +187,22 @@
   return ret;
 }
 
-enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
-                                       size_t *out_consumed, uint8_t *out_alert,
-                                       uint8_t *in, size_t in_len) {
+enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type,
+                                       Span<uint8_t> *out, size_t *out_consumed,
+                                       uint8_t *out_alert, Span<uint8_t> in) {
   *out_consumed = 0;
+  switch (ssl->s3->read_shutdown) {
+    case ssl_shutdown_none:
+      break;
+    case ssl_shutdown_fatal_alert:
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+      *out_alert = 0;
+      return ssl_open_record_error;
+    case ssl_shutdown_close_notify:
+      return ssl_open_record_close_notify;
+  }
 
-  CBS cbs;
-  CBS_init(&cbs, in, in_len);
+  CBS cbs = CBS(in);
 
   // Decode the record header.
   uint8_t type;
@@ -234,10 +243,10 @@
     return ssl_open_record_partial;
   }
 
-  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in,
-                      SSL3_RT_HEADER_LENGTH);
+  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER,
+                      in.subspan(0, SSL3_RT_HEADER_LENGTH));
 
-  *out_consumed = in_len - CBS_len(&cbs);
+  *out_consumed = in.size() - CBS_len(&cbs);
 
   // Skip early data received when expecting a second ClientHello if we rejected
   // 0RTT.
@@ -248,9 +257,9 @@
   }
 
   // Decrypt the body in-place.
-  if (!ssl->s3->aead_read_ctx->Open(out, type, version, ssl->s3->read_sequence,
-                                    (uint8_t *)CBS_data(&body),
-                                    CBS_len(&body))) {
+  if (!ssl->s3->aead_read_ctx->Open(
+          out, type, version, ssl->s3->read_sequence,
+          MakeSpan(const_cast<uint8_t *>(CBS_data(&body)), CBS_len(&body)))) {
     if (ssl->s3->skip_early_data && !ssl->s3->aead_read_ctx->is_null_cipher()) {
       ERR_clear_error();
       goto skipped_data;
@@ -279,23 +288,25 @@
     }
 
     do {
-      if (!CBS_get_last_u8(out, &type)) {
+      if (out->empty()) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
         *out_alert = SSL_AD_DECRYPT_ERROR;
         return ssl_open_record_error;
       }
+      type = out->back();
+      *out = out->subspan(0, out->size() - 1);
     } while (type == 0);
   }
 
   // Check the plaintext length.
-  if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) {
+  if (out->size() > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
   }
 
   // Limit the number of consecutive empty records.
-  if (CBS_len(out) == 0) {
+  if (out->empty()) {
     ssl->s3->empty_record_count++;
     if (ssl->s3->empty_record_count > kMaxEmptyRecords) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_EMPTY_FRAGMENTS);
@@ -310,14 +321,14 @@
 
   if (type == SSL3_RT_ALERT) {
     // Return end_of_early_data alerts as-is for the caller to process.
-    if (CBS_len(out) == 2 &&
-        CBS_data(out)[0] == SSL3_AL_WARNING &&
-        CBS_data(out)[1] == TLS1_AD_END_OF_EARLY_DATA) {
+    if (out->size() == 2 &&
+        (*out)[0] == SSL3_AL_WARNING &&
+        (*out)[1] == TLS1_AD_END_OF_EARLY_DATA) {
       *out_type = type;
       return ssl_open_record_success;
     }
 
-    return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out));
+    return ssl_process_alert(ssl, out_alert, *out);
   }
 
   ssl->s3->warning_alert_count = 0;
@@ -390,8 +401,8 @@
     return 0;
   }
 
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out_prefix,
-                      SSL3_RT_HEADER_LENGTH);
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER,
+                      MakeSpan(out_prefix, SSL3_RT_HEADER_LENGTH));
   return 1;
 }
 
@@ -516,15 +527,15 @@
 }
 
 enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
-                                         const uint8_t *in, size_t in_len) {
+                                         Span<const uint8_t> in) {
   // Alerts records may not contain fragmented or multiple alerts.
-  if (in_len != 2) {
+  if (in.size() != 2) {
     *out_alert = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
     return ssl_open_record_error;
   }
 
-  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_ALERT, in, in_len);
+  ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_ALERT, in);
 
   const uint8_t alert_level = in[0];
   const uint8_t alert_descr = in[1];
@@ -534,13 +545,13 @@
 
   if (alert_level == SSL3_AL_WARNING) {
     if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-      ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+      ssl->s3->read_shutdown = ssl_shutdown_close_notify;
       return ssl_open_record_close_notify;
     }
 
     // Warning alerts do not exist in TLS 1.3.
     if (ssl->s3->have_version &&
-        ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+        ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
       *out_alert = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
       return ssl_open_record_error;
@@ -556,13 +567,14 @@
   }
 
   if (alert_level == SSL3_AL_FATAL) {
-    ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
+    ssl->s3->read_shutdown = ssl_shutdown_fatal_alert;
 
     char tmp[16];
     OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
     BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
     ERR_add_error_data(2, "SSL alert number ", tmp);
-    return ssl_open_record_fatal_alert;
+    *out_alert = 0;  // No alert to send back to the peer.
+    return ssl_open_record_error;
   }
 
   *out_alert = SSL_AD_ILLEGAL_PARAMETER;
@@ -577,16 +589,16 @@
   // and below.
   if (SSL_in_init(ssl) ||
       SSL_is_dtls(ssl) ||
-      ssl3_protocol_version(ssl) > TLS1_2_VERSION) {
+      ssl_protocol_version(ssl) > TLS1_2_VERSION) {
     assert(false);
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return OpenRecordResult::kError;
   }
 
-  CBS plaintext;
-  uint8_t type;
+  Span<uint8_t> plaintext;
+  uint8_t type = 0;
   const ssl_open_record_t result = tls_open_record(
-      ssl, &type, &plaintext, out_record_len, out_alert, in.data(), in.size());
+      ssl, &type, &plaintext, out_record_len, out_alert, in);
 
   switch (result) {
     case ssl_open_record_success:
@@ -594,8 +606,7 @@
         *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
         return OpenRecordResult::kError;
       }
-      *out = MakeSpan(
-          const_cast<uint8_t*>(CBS_data(&plaintext)), CBS_len(&plaintext));
+      *out = plaintext;
       return OpenRecordResult::kOK;
     case ssl_open_record_discard:
       return OpenRecordResult::kDiscard;
@@ -603,8 +614,6 @@
       return OpenRecordResult::kIncompleteRecord;
     case ssl_open_record_close_notify:
       return OpenRecordResult::kAlertCloseNotify;
-    case ssl_open_record_fatal_alert:
-      return OpenRecordResult::kAlertFatal;
     case ssl_open_record_error:
       return OpenRecordResult::kError;
   }
@@ -636,7 +645,7 @@
   // and below.
   if (SSL_in_init(ssl) ||
       SSL_is_dtls(ssl) ||
-      ssl3_protocol_version(ssl) > TLS1_2_VERSION) {
+      ssl_protocol_version(ssl) > TLS1_2_VERSION) {
     assert(false);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index ccaf75d..0d67e3d 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -14,21 +14,24 @@
 
 vars = {
   'chromium_git': 'https://chromium.googlesource.com',
+
+  'checkout_clang': False,
+  'checkout_fuzzer': False,
+  'checkout_sde': False,
 }
 
 deps = {
-  'boringssl/util/bot/gyp':
-    Var('chromium_git') + '/external/gyp.git' + '@' + 'eb296f67da078ec01f5e3a9ea9cdc6d26d680161',
-}
-
-deps_os = {
-  'android': {
-    'boringssl/util/bot/android_tools':
-      Var('chromium_git') + '/android_tools.git' + '@' + 'e9d4018e149d50172ed462a7c21137aa915940ec',
+  'boringssl/util/bot/android_tools': {
+    'url': Var('chromium_git') + '/android_tools.git' + '@' + 'ca9dc7245b888c75307f0619e4a39fb46a82de66',
+    'condition': 'checkout_android',
   },
-  'unix': {
-    'boringssl/util/bot/libFuzzer':
-      Var('chromium_git') + '/chromium/llvm-project/llvm/lib/Fuzzer.git' + '@' + '16f5f743c188c836d32cdaf349d5d3effb8a3518',
+
+  'boringssl/util/bot/gyp':
+    Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
+
+  'boringssl/util/bot/libFuzzer': {
+    'url': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' + '06fb50cc1f0197398c8a70658928a3b91912e68a',
+    'condition': 'checkout_fuzzer',
   },
 }
 
@@ -41,6 +44,7 @@
   {
     'name': 'cmake_linux64',
     'pattern': '.',
+    'condition': 'host_os == "linux"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
                 '--platform=linux*',
@@ -50,8 +54,19 @@
     ],
   },
   {
+    'name': 'cmake_linux64_extract',
+    'pattern': '.',
+    'condition': 'host_os == "linux"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-linux64.tar.gz',
+                'boringssl/util/bot/cmake-linux64/',
+    ],
+  },
+  {
     'name': 'cmake_mac',
     'pattern': '.',
+    'condition': 'host_os == "mac"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
                 '--platform=darwin',
@@ -61,8 +76,19 @@
     ],
   },
   {
+    'name': 'cmake_mac_extract',
+    'pattern': '.',
+    'condition': 'host_os == "mac"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-mac.tar.gz',
+                'boringssl/util/bot/cmake-mac/',
+    ],
+  },
+  {
     'name': 'cmake_win32',
     'pattern': '.',
+    'condition': 'host_os == "win"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
                 '--platform=win32',
@@ -72,8 +98,19 @@
     ],
   },
   {
+    'name': 'cmake_win32_extract',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-win32.zip',
+                'boringssl/util/bot/cmake-win32/',
+    ],
+  },
+  {
     'name': 'perl_win32',
     'pattern': '.',
+    'condition': 'host_os == "win"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
                 '--platform=win32',
@@ -83,8 +120,20 @@
     ],
   },
   {
+    'name': 'perl_win32_extract',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                '--no-prefix',
+                'boringssl/util/bot/perl-win32.zip',
+                'boringssl/util/bot/perl-win32/',
+    ],
+  },
+  {
     'name': 'yasm_win32',
     'pattern': '.',
+    'condition': 'host_os == "win"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
                 '--platform=win32',
@@ -96,6 +145,7 @@
   {
     'name': 'win_toolchain',
     'pattern': '.',
+    'condition': 'host_os == "win"',
     'action': [ 'python',
                 'boringssl/util/bot/vs_toolchain.py',
                 'update',
@@ -104,45 +154,29 @@
   {
     'name': 'clang',
     'pattern': '.',
+    'condition': 'checkout_clang',
     'action': [ 'python',
                 'boringssl/util/bot/update_clang.py',
     ],
   },
   {
-    'name': 'cmake_linux64_extract',
+    'name': 'sde_linux64',
     'pattern': '.',
-    'action': [ 'python',
-                'boringssl/util/bot/extract.py',
-                'boringssl/util/bot/cmake-linux64.tar.gz',
-                'boringssl/util/bot/cmake-linux64/',
+    'condition': 'checkout_sde and host_os == "linux"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--bucket', 'chrome-boringssl-sde',
+                '-s', 'boringssl/util/bot/sde-linux64.tar.bz2.sha1'
     ],
   },
   {
-    'name': 'cmake_mac_extract',
+    'name': 'sde_linux64_extract',
     'pattern': '.',
+    'condition': 'checkout_sde and host_os == "linux"',
     'action': [ 'python',
                 'boringssl/util/bot/extract.py',
-                'boringssl/util/bot/cmake-mac.tar.gz',
-                'boringssl/util/bot/cmake-mac/',
-    ],
-  },
-  {
-    'name': 'cmake_win32_extract',
-    'pattern': '.',
-    'action': [ 'python',
-                'boringssl/util/bot/extract.py',
-                'boringssl/util/bot/cmake-win32.zip',
-                'boringssl/util/bot/cmake-win32/',
-    ],
-  },
-  {
-    'name': 'perl_win32_extract',
-    'pattern': '.',
-    'action': [ 'python',
-                'boringssl/util/bot/extract.py',
-                '--no-prefix',
-                'boringssl/util/bot/perl-win32.zip',
-                'boringssl/util/bot/perl-win32/',
+                'boringssl/util/bot/sde-linux64.tar.bz2',
+                'boringssl/util/bot/sde-linux64/',
     ],
   },
 ]
diff --git a/src/util/bot/UPDATING b/src/util/bot/UPDATING
index fee8749..a9915ab 100644
--- a/src/util/bot/UPDATING
+++ b/src/util/bot/UPDATING
@@ -41,11 +41,11 @@
 
     The current revision is cmake-3.5.0-win32-x86.zip.
 
-perl-win32.zip: Update to the latest 32-bit prebuilt "PortableZip" edition of
+perl-win32.zip: Update to the latest 32-bit prebuilt "Portable" edition of
     Strawberry Perl, found at http://strawberryperl.com/releases.html. The
     download will be named strawberry-perl-VERSION-32bit-portable.zip.
 
-    The current revision is strawberry-perl-5.22.1.2-32bit-portable.zip.
+    The current revision is strawberry-perl-5.26.1.1-32bit-portable.zip.
 
 yasm-win32.exe: Update to the appropriate release of Yasm. Use the same version
     as Chromium, found at
@@ -62,4 +62,4 @@
 
     upload_to_google_storage.py -b chrome-boringssl-sde sde-linux64.tar.bz2
 
-The current revision is sde-external-8.5.0-2017-06-08-lin.tar.bz2.
+The current revision is sde-external-8.9.0-2017-08-06-lin.tar.bz2.
diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py
index 75bacf3..86335aa 100755
--- a/src/util/bot/go/bootstrap.py
+++ b/src/util/bot/go/bootstrap.py
@@ -45,7 +45,7 @@
 EXE_SFX = '.exe' if sys.platform == 'win32' else ''
 
 # Pinned version of Go toolset to download.
-TOOLSET_VERSION = 'go1.9'
+TOOLSET_VERSION = 'go1.9.1'
 
 # Platform dependent portion of a download URL. See http://golang.org/dl/.
 TOOLSET_VARIANTS = {
diff --git a/src/util/bot/perl-win32.zip.sha1 b/src/util/bot/perl-win32.zip.sha1
index 8874fbb..a185995 100644
--- a/src/util/bot/perl-win32.zip.sha1
+++ b/src/util/bot/perl-win32.zip.sha1
@@ -1 +1 @@
-3db2b4964d56f6e23cc48ced40a6cee05419a0af
\ No newline at end of file
+1245124da188c699fb41c4b5a60622bd0d363106
\ No newline at end of file
diff --git a/src/util/bot/sde-linux64.tar.bz2.sha1 b/src/util/bot/sde-linux64.tar.bz2.sha1
index 1598a95..cb35cf8 100644
--- a/src/util/bot/sde-linux64.tar.bz2.sha1
+++ b/src/util/bot/sde-linux64.tar.bz2.sha1
@@ -1 +1 @@
-2b13efc6fb26fcee614a8c4534b0ffa71c50a2a5
\ No newline at end of file
+dcf00ebdef4810ffd4ce0c8636dcf99d5ad760c9
\ No newline at end of file
diff --git a/src/util/bot/update_clang.py b/src/util/bot/update_clang.py
index 9595c0a..3164750 100644
--- a/src/util/bot/update_clang.py
+++ b/src/util/bot/update_clang.py
@@ -19,15 +19,15 @@
 # CLANG_REVISION and CLANG_SUB_REVISION determine the build of clang
 # to use. These should be synced with tools/clang/scripts/update.py in
 # Chromium.
-CLANG_REVISION = '310694'
-CLANG_SUB_REVISION=2
+CLANG_REVISION = '313786'
+CLANG_SUB_REVISION=1
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
 # Path constants. (All of these should be absolute paths.)
 THIS_DIR = os.path.abspath(os.path.dirname(__file__))
 LLVM_BUILD_DIR = os.path.join(THIS_DIR, 'llvm-build')
-STAMP_FILE = os.path.join(THIS_DIR, 'cr_build_revision')
+STAMP_FILE = os.path.join(LLVM_BUILD_DIR, 'cr_build_revision')
 
 # URL for pre-built binaries.
 CDS_URL = os.environ.get('CDS_CLANG_BUCKET_OVERRIDE',
diff --git a/src/util/bot/vs_toolchain.py b/src/util/bot/vs_toolchain.py
index cf7b5d0..64da79a 100644
--- a/src/util/bot/vs_toolchain.py
+++ b/src/util/bot/vs_toolchain.py
@@ -87,8 +87,8 @@
     # Update 3 final with 10.0.15063.468 SDK and no vctip.exe.
     return ['f53e4598951162bad6330f7a167486c7ae5db1e5']
   if env_version == '2017':
-    # VS 2017 Update 3.2 with 10.0.15063.468 SDK.
-    return ['9bc7ccbf9f4bd50d4a3bd185e8ca94ff1618de0b']
+    # VS 2017 Update 3.2 with 10.0.15063.468 SDK and patched setenv.cmd.
+    return ['a9e1098bba66d2acccc377d5ee81265910f29272']
   raise Exception('Unsupported VS version %s' % env_version)
 
 
diff --git a/src/util/doc.config b/src/util/doc.config
index f7e8baa..5bee1b5 100644
--- a/src/util/doc.config
+++ b/src/util/doc.config
@@ -23,6 +23,7 @@
     "Headers": [
       "include/openssl/aes.h",
       "include/openssl/bn.h",
+      "include/openssl/chacha.h",
       "include/openssl/cmac.h",
       "include/openssl/curve25519.h",
       "include/openssl/des.h",