external/boringssl: Sync to ba94746eb2b4b59a0eb72047e4ca2d2d54454c87.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/7f8c553d7f4db0a6ce727f2986d41bf8fe8ec4bf..ba94746eb2b4b59a0eb72047e4ca2d2d54454c87

Test: BoringSSL CTS Presubmits
Change-Id: I5283ca8ec80f4abbc2543fece2ecf2b33240c6e4
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1814b13..1f2f6ba 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -72,6 +72,9 @@
 
   if(NOT MSVC)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    if(APPLE)
+      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+    endif()
     if(NOT BORINGSSL_ALLOW_CXX_RUNTIME)
       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
     endif()
@@ -307,7 +310,7 @@
   set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -march=${CMAKE_SYSTEM_PROCESSOR}")
 endif()
 
-if (${ARCH} STREQUAL "x86" AND APPLE)
+if (${ARCH} STREQUAL "x86" AND APPLE AND ${CMAKE_VERSION} VERSION_LESS "3.0")
   # With CMake 2.8.x, ${CMAKE_SYSTEM_PROCESSOR} evalutes to i386 on OS X,
   # but clang defaults to 64-bit builds on OS X unless otherwise told.
   # Set ARCH to x86_64 so clang and CMake agree. This is fixed in CMake 3.
diff --git a/src/crypto/asn1/a_enum.c b/src/crypto/asn1/a_enum.c
index cc46905..4a77971 100644
--- a/src/crypto/asn1/a_enum.c
+++ b/src/crypto/asn1/a_enum.c
@@ -56,6 +56,7 @@
 
 #include <openssl/asn1.h>
 
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/err.h>
@@ -110,7 +111,6 @@
 long ASN1_ENUMERATED_get(ASN1_ENUMERATED *a)
 {
     int neg = 0, i;
-    long r = 0;
 
     if (a == NULL)
         return (0L);
@@ -120,20 +120,31 @@
     else if (i != V_ASN1_ENUMERATED)
         return -1;
 
-    if (a->length > (int)sizeof(long)) {
-        /* hmm... a bit ugly */
-        return (0xffffffffL);
-    }
-    if (a->data == NULL)
-        return 0;
+    OPENSSL_COMPILE_ASSERT(sizeof(uint64_t) >= sizeof(long),
+                           long_larger_than_uint64_t);
 
-    for (i = 0; i < a->length; i++) {
-        r <<= 8;
-        r |= (unsigned char)a->data[i];
+    if (a->length > (int)sizeof(uint64_t)) {
+        /* hmm... a bit ugly */
+        return -1;
     }
+
+    uint64_t r64 = 0;
+    if (a->data != NULL) {
+      for (i = 0; i < a->length; i++) {
+          r64 <<= 8;
+          r64 |= (unsigned char)a->data[i];
+      }
+
+      if (r64 > LONG_MAX) {
+          return -1;
+      }
+    }
+
+    long r = (long) r64;
     if (neg)
         r = -r;
-    return (r);
+
+    return r;
 }
 
 ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(BIGNUM *bn, ASN1_ENUMERATED *ai)
diff --git a/src/crypto/asn1/a_int.c b/src/crypto/asn1/a_int.c
index 617ba96..8a4edd6 100644
--- a/src/crypto/asn1/a_int.c
+++ b/src/crypto/asn1/a_int.c
@@ -57,6 +57,7 @@
 #include <openssl/asn1.h>
 
 #include <string.h>
+#include <limits.h>
 
 #include <openssl/err.h>
 #include <openssl/mem.h>
@@ -385,7 +386,6 @@
 long ASN1_INTEGER_get(const ASN1_INTEGER *a)
 {
     int neg = 0, i;
-    long r = 0;
 
     if (a == NULL)
         return (0L);
@@ -395,20 +395,31 @@
     else if (i != V_ASN1_INTEGER)
         return -1;
 
-    if (a->length > (int)sizeof(long)) {
+    OPENSSL_COMPILE_ASSERT(sizeof(uint64_t) >= sizeof(long),
+                           long_larger_than_uint64_t);
+
+    if (a->length > (int)sizeof(uint64_t)) {
         /* hmm... a bit ugly, return all ones */
         return -1;
     }
-    if (a->data == NULL)
-        return 0;
 
-    for (i = 0; i < a->length; i++) {
-        r <<= 8;
-        r |= (unsigned char)a->data[i];
+    uint64_t r64 = 0;
+    if (a->data != NULL) {
+      for (i = 0; i < a->length; i++) {
+          r64 <<= 8;
+          r64 |= (unsigned char)a->data[i];
+      }
+
+      if (r64 > LONG_MAX) {
+          return -1;
+      }
     }
+
+    long r = (long) r64;
     if (neg)
         r = -r;
-    return (r);
+
+    return r;
 }
 
 ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai)
diff --git a/src/crypto/asn1/asn1_locl.h b/src/crypto/asn1/asn1_locl.h
index fef00ac..10a832c 100644
--- a/src/crypto/asn1/asn1_locl.h
+++ b/src/crypto/asn1/asn1_locl.h
@@ -90,6 +90,9 @@
 int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d);
 int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d);
 
+void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it,
+                            int combine);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/src/crypto/asn1/tasn_fre.c b/src/crypto/asn1/tasn_fre.c
index 609cb9f..eabc0fb 100644
--- a/src/crypto/asn1/tasn_fre.c
+++ b/src/crypto/asn1/tasn_fre.c
@@ -59,8 +59,7 @@
 #include <openssl/asn1t.h>
 #include <openssl/mem.h>
 
-static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it,
-                                   int combine);
+#include "asn1_locl.h"
 
 /* Free up an ASN1 structure */
 
@@ -74,8 +73,7 @@
     asn1_item_combine_free(pval, it, 0);
 }
 
-static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it,
-                                   int combine)
+void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int combine)
 {
     const ASN1_TEMPLATE *tt = NULL, *seqtt;
     const ASN1_EXTERN_FUNCS *ef;
diff --git a/src/crypto/asn1/tasn_new.c b/src/crypto/asn1/tasn_new.c
index 10cf954..5db38be 100644
--- a/src/crypto/asn1/tasn_new.c
+++ b/src/crypto/asn1/tasn_new.c
@@ -63,6 +63,7 @@
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 
+#include "asn1_locl.h"
 #include "../internal.h"
 
 
@@ -201,7 +202,7 @@
     return 1;
 
  memerr2:
-    ASN1_item_ex_free(pval, it);
+    asn1_item_combine_free(pval, it, combine);
  memerr:
     OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
 #ifdef CRYPTO_MDEBUG
@@ -211,7 +212,7 @@
     return 0;
 
  auxerr2:
-    ASN1_item_ex_free(pval, it);
+    asn1_item_combine_free(pval, it, combine);
  auxerr:
     OPENSSL_PUT_ERROR(ASN1, ASN1_R_AUX_ERROR);
 #ifdef CRYPTO_MDEBUG
diff --git a/src/crypto/bn_extra/bn_asn1.c b/src/crypto/bn_extra/bn_asn1.c
index 8b939da..0d96573 100644
--- a/src/crypto/bn_extra/bn_asn1.c
+++ b/src/crypto/bn_extra/bn_asn1.c
@@ -42,22 +42,6 @@
   return BN_bin2bn(CBS_data(&child), CBS_len(&child), ret) != NULL;
 }
 
-int BN_parse_asn1_unsigned_buggy(CBS *cbs, BIGNUM *ret) {
-  CBS child;
-  if (!CBS_get_asn1(cbs, &child, CBS_ASN1_INTEGER) ||
-      CBS_len(&child) == 0) {
-    OPENSSL_PUT_ERROR(BN, BN_R_BAD_ENCODING);
-    return 0;
-  }
-
-  // This function intentionally does not reject negative numbers or non-minimal
-  // encodings. Estonian IDs issued between September 2014 to September 2015 are
-  // broken. See https://crbug.com/532048 and https://crbug.com/534766.
-  //
-  // TODO(davidben): Remove this code and callers in March 2016.
-  return BN_bin2bn(CBS_data(&child), CBS_len(&child), ret) != NULL;
-}
-
 int BN_marshal_asn1(CBB *cbb, const BIGNUM *bn) {
   // Negative numbers are unsupported.
   if (BN_is_negative(bn)) {
diff --git a/src/crypto/cpu-intel.c b/src/crypto/cpu-intel.c
index 127fa57..1ac280c 100644
--- a/src/crypto/cpu-intel.c
+++ b/src/crypto/cpu-intel.c
@@ -164,7 +164,7 @@
     uint32_t num_extended_ids = eax;
     if (num_extended_ids >= 0x80000001) {
       OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 0x80000001);
-      if (ecx & (1 << 11)) {
+      if (ecx & (1u << 11)) {
         has_amd_xop = 1;
       }
     }
@@ -193,68 +193,68 @@
   OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 1);
 
   // Adjust the hyper-threading bit.
-  if (edx & (1 << 28)) {
+  if (edx & (1u << 28)) {
     uint32_t num_logical_cores = (ebx >> 16) & 0xff;
     if (cores_per_cache == 1 || num_logical_cores <= 1) {
-      edx &= ~(1 << 28);
+      edx &= ~(1u << 28);
     }
   }
 
   // Reserved bit #20 was historically repurposed to control the in-memory
   // representation of RC4 state. Always set it to zero.
-  edx &= ~(1 << 20);
+  edx &= ~(1u << 20);
 
   // Reserved bit #30 is repurposed to signal an Intel CPU.
   if (is_intel) {
-    edx |= (1 << 30);
+    edx |= (1u << 30);
 
     // Clear the XSAVE bit on Knights Landing to mimic Silvermont. This enables
     // some Silvermont-specific codepaths which perform better. See OpenSSL
     // commit 64d92d74985ebb3d0be58a9718f9e080a14a8e7f.
     if ((eax & 0x0fff0ff0) == 0x00050670 /* Knights Landing */ ||
         (eax & 0x0fff0ff0) == 0x00080650 /* Knights Mill (per SDE) */) {
-      ecx &= ~(1 << 26);
+      ecx &= ~(1u << 26);
     }
   } else {
-    edx &= ~(1 << 30);
+    edx &= ~(1u << 30);
   }
 
   // The SDBG bit is repurposed to denote AMD XOP support.
   if (has_amd_xop) {
-    ecx |= (1 << 11);
+    ecx |= (1u << 11);
   } else {
-    ecx &= ~(1 << 11);
+    ecx &= ~(1u << 11);
   }
 
   uint64_t xcr0 = 0;
-  if (ecx & (1 << 27)) {
+  if (ecx & (1u << 27)) {
     // XCR0 may only be queried if the OSXSAVE bit is set.
     xcr0 = OPENSSL_xgetbv(0);
   }
   // See Intel manual, volume 1, section 14.3.
   if ((xcr0 & 6) != 6) {
     // YMM registers cannot be used.
-    ecx &= ~(1 << 28);  // AVX
-    ecx &= ~(1 << 12);  // FMA
-    ecx &= ~(1 << 11);  // AMD XOP
+    ecx &= ~(1u << 28);  // AVX
+    ecx &= ~(1u << 12);  // FMA
+    ecx &= ~(1u << 11);  // AMD XOP
     // Clear AVX2 and AVX512* bits.
     //
     // TODO(davidben): Should bits 17 and 26-28 also be cleared? Upstream
     // doesn't clear those.
     extended_features &=
-        ~((1 << 5) | (1 << 16) | (1 << 21) | (1 << 30) | (1 << 31));
+        ~((1u << 5) | (1u << 16) | (1u << 21) | (1u << 30) | (1u << 31));
   }
   // See Intel manual, volume 1, section 15.2.
   if ((xcr0 & 0xe6) != 0xe6) {
     // Clear AVX512F. Note we don't touch other AVX512 extensions because they
     // can be used with YMM.
-    extended_features &= ~(1 << 16);
+    extended_features &= ~(1u << 16);
   }
 
   // Disable ADX instructions on Knights Landing. See OpenSSL commit
   // 64d92d74985ebb3d0be58a9718f9e080a14a8e7f.
-  if ((ecx & (1 << 26)) == 0) {
-    extended_features &= ~(1 << 19);
+  if ((ecx & (1u << 26)) == 0) {
+    extended_features &= ~(1u << 19);
   }
 
   OPENSSL_ia32cap_P[0] = edx;
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index 390cd8b..9f4639f 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -40,6 +40,18 @@
                                OPENSSL_ARM || OPENSSL_AARCH64) */
 
 
+// Our assembly does not use the GOT to reference symbols, which means
+// references to visible symbols will often require a TEXTREL. This is
+// undesirable, so all assembly-referenced symbols should be hidden. CPU
+// capabilities are the only such symbols defined in C. Explicitly hide them,
+// rather than rely on being built with -fvisibility=hidden.
+#if defined(OPENSSL_WINDOWS)
+#define HIDDEN
+#else
+#define HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+
 // The capability variables are defined in this file in order to work around a
 // linker bug. When linking with a .a, if no symbols in a .o are referenced
 // then the .o is discarded, even if it has constructor functions.
@@ -57,11 +69,11 @@
 // archive, linking on OS X will fail to resolve common symbols. By
 // initialising it to zero, it becomes a "data symbol", which isn't so
 // affected.
-uint32_t OPENSSL_ia32cap_P[4] = {0};
+HIDDEN uint32_t OPENSSL_ia32cap_P[4] = {0};
 
 #elif defined(OPENSSL_PPC64LE)
 
-unsigned long OPENSSL_ppc64le_hwcap2 = 0;
+HIDDEN unsigned long OPENSSL_ppc64le_hwcap2 = 0;
 
 #elif defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)
 
@@ -69,7 +81,7 @@
 
 #if defined(OPENSSL_STATIC_ARMCAP)
 
-uint32_t OPENSSL_armcap_P =
+HIDDEN uint32_t OPENSSL_armcap_P =
 #if defined(OPENSSL_STATIC_ARMCAP_NEON) || defined(__ARM_NEON__)
     ARMV7_NEON |
 #endif
@@ -88,7 +100,7 @@
     0;
 
 #else
-uint32_t OPENSSL_armcap_P = 0;
+HIDDEN uint32_t OPENSSL_armcap_P = 0;
 #endif
 
 #endif
diff --git a/src/crypto/ecdh/ecdh_test.cc b/src/crypto/ecdh/ecdh_test.cc
index b311445..6d48408 100644
--- a/src/crypto/ecdh/ecdh_test.cc
+++ b/src/crypto/ecdh/ecdh_test.cc
@@ -14,6 +14,7 @@
 
 #include <stdio.h>
 
+#include <utility>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -23,6 +24,7 @@
 #include <openssl/ec.h>
 #include <openssl/ec_key.h>
 #include <openssl/ecdh.h>
+#include <openssl/err.h>
 #include <openssl/nid.h>
 
 #include "../test/file_test.h"
@@ -112,3 +114,98 @@
               Bytes(actual_z.data(), static_cast<size_t>(ret)));
   });
 }
+
+// MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
+// with the wrong generator.)
+static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
+  static const uint8_t kP[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  };
+  static const uint8_t kA[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
+  };
+  static const uint8_t kB[] = {
+      0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
+      0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
+      0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
+  };
+  static const uint8_t kX[] = {
+      0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
+      0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
+      0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
+  };
+  static const uint8_t kY[] = {
+      0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
+      0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
+      0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
+  };
+  static const uint8_t kOrder[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
+      0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
+  };
+  bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
+  bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
+  bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
+  bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
+  bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
+  bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
+  bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
+  if (!ctx || !p || !a || !b || !x || !y || !order) {
+    return nullptr;
+  }
+  bssl::UniquePtr<EC_GROUP> group(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  if (!group) {
+    return nullptr;
+  }
+  bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
+  if (!generator ||
+      !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
+                                           x.get(), y.get(), ctx.get()) ||
+      !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
+                              BN_value_one())) {
+    return nullptr;
+  }
+  return group;
+}
+
+TEST(ECDHTest, GroupMismatch) {
+  const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
+  std::vector<EC_builtin_curve> curves(num_curves);
+  EC_get_builtin_curves(curves.data(), num_curves);
+
+  // Instantiate all the built-in curves.
+  std::vector<bssl::UniquePtr<EC_GROUP>> groups;
+  for (const auto &curve : curves) {
+    groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
+    ASSERT_TRUE(groups.back());
+  }
+
+  // Also create some arbitrary group. (This is P-256 with the wrong generator.)
+  groups.push_back(MakeCustomGroup());
+  ASSERT_TRUE(groups.back());
+
+  for (const auto &a : groups) {
+    for (const auto &b : groups) {
+      if (a.get() == b.get()) {
+        continue;
+      }
+
+      bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+      ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
+      ASSERT_TRUE(EC_KEY_generate_key(key.get()));
+
+      // ECDH across the groups should not work.
+      char out[64];
+      const EC_POINT *peer = EC_GROUP_get0_generator(b.get());
+      EXPECT_EQ(-1,
+                ECDH_compute_key(out, sizeof(out), peer, key.get(), nullptr));
+      ERR_clear_error();
+    }
+  }
+}
diff --git a/src/crypto/evp/p_rsa_asn1.c b/src/crypto/evp/p_rsa_asn1.c
index 5157a89..a188538 100644
--- a/src/crypto/evp/p_rsa_asn1.c
+++ b/src/crypto/evp/p_rsa_asn1.c
@@ -63,19 +63,9 @@
 #include <openssl/rsa.h>
 
 #include "../fipsmodule/rsa/internal.h"
-#include "../internal.h"
 #include "internal.h"
 
 
-static struct CRYPTO_STATIC_MUTEX g_buggy_lock = CRYPTO_STATIC_MUTEX_INIT;
-static int g_buggy = 0;
-
-void EVP_set_buggy_rsa_parser(int buggy) {
-  CRYPTO_STATIC_MUTEX_lock_write(&g_buggy_lock);
-  g_buggy = buggy;
-  CRYPTO_STATIC_MUTEX_unlock_write(&g_buggy_lock);
-}
-
 static int rsa_pub_encode(CBB *out, const EVP_PKEY *key) {
   // See RFC 3279, section 2.3.1.
   CBB spki, algorithm, oid, null, key_bitstring;
@@ -96,11 +86,6 @@
 }
 
 static int rsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
-  int buggy;
-  CRYPTO_STATIC_MUTEX_lock_read(&g_buggy_lock);
-  buggy = g_buggy;
-  CRYPTO_STATIC_MUTEX_unlock_read(&g_buggy_lock);
-
   // See RFC 3279, section 2.3.1.
 
   // The parameters must be NULL.
@@ -112,13 +97,7 @@
     return 0;
   }
 
-  // Estonian IDs issued between September 2014 to September 2015 are
-  // broken. See https://crbug.com/532048 and https://crbug.com/534766.
-  //
-  // TODO(davidben): Switch this to the strict version in March 2016 or when
-  // Chromium can force client certificates down a different codepath, whichever
-  // comes first.
-  RSA *rsa = buggy ? RSA_parse_public_key_buggy(key) : RSA_parse_public_key(key);
+  RSA *rsa = RSA_parse_public_key(key);
   if (rsa == NULL || CBS_len(key) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     RSA_free(rsa);
diff --git a/src/crypto/ex_data.c b/src/crypto/ex_data.c
index af7e7e2..71d60a5 100644
--- a/src/crypto/ex_data.c
+++ b/src/crypto/ex_data.c
@@ -113,7 +113,6 @@
 
 #include <openssl/crypto.h>
 #include <openssl/err.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/stack.h>
 #include <openssl/thread.h>
diff --git a/src/crypto/fipsmodule/bn/add.c b/src/crypto/fipsmodule/bn/add.c
index bbe275e..201c526 100644
--- a/src/crypto/fipsmodule/bn/add.c
+++ b/src/crypto/fipsmodule/bn/add.c
@@ -133,7 +133,7 @@
     while (dif) {
       dif--;
       t1 = *(ap++);
-      t2 = (t1 + 1) & BN_MASK2;
+      t2 = t1 + 1;
       *(rp++) = t2;
       if (t2) {
         carry = 0;
@@ -162,8 +162,6 @@
   BN_ULONG l;
   int i;
 
-  w &= BN_MASK2;
-
   // degenerate case: w is zero
   if (!w) {
     return 1;
@@ -185,7 +183,7 @@
   }
 
   for (i = 0; w != 0 && i < a->top; i++) {
-    a->d[i] = l = (a->d[i] + w) & BN_MASK2;
+    a->d[i] = l = a->d[i] + w;
     w = (w > l) ? 1 : 0;
   }
 
@@ -285,12 +283,12 @@
     t2 = *(bp++);
     if (carry) {
       carry = (t1 <= t2);
-      t1 = (t1 - t2 - 1) & BN_MASK2;
+      t1 -= t2 + 1;
     } else {
       carry = (t1 < t2);
-      t1 = (t1 - t2) & BN_MASK2;
+      t1 -= t2;
     }
-    *(rp++) = t1 & BN_MASK2;
+    *(rp++) = t1;
   }
 
   if (carry)  // subtracted
@@ -303,7 +301,7 @@
     while (dif) {
       dif--;
       t1 = *(ap++);
-      t2 = (t1 - 1) & BN_MASK2;
+      t2 = t1 - 1;
       *(rp++) = t2;
       if (t1) {
         break;
@@ -325,8 +323,6 @@
 int BN_sub_word(BIGNUM *a, BN_ULONG w) {
   int i;
 
-  w &= BN_MASK2;
-
   // degenerate case: w is zero
   if (!w) {
     return 1;
@@ -361,7 +357,7 @@
       a->d[i] -= w;
       break;
     } else {
-      a->d[i] = (a->d[i] - w) & BN_MASK2;
+      a->d[i] -= w;
       i++;
       w = 1;
     }
diff --git a/src/crypto/fipsmodule/bn/bn_test.cc b/src/crypto/fipsmodule/bn/bn_test.cc
index fe03e5f..975264e 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -778,27 +778,27 @@
 TEST_F(BNTest, Dec2BN) {
   bssl::UniquePtr<BIGNUM> bn;
   int ret = DecimalToBIGNUM(&bn, "0");
-  EXPECT_EQ(1, ret);
+  ASSERT_EQ(1, ret);
   EXPECT_TRUE(BN_is_zero(bn.get()));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = DecimalToBIGNUM(&bn, "256");
-  EXPECT_EQ(3, ret);
+  ASSERT_EQ(3, ret);
   EXPECT_TRUE(BN_is_word(bn.get(), 256));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = DecimalToBIGNUM(&bn, "-42");
-  EXPECT_EQ(3, ret);
+  ASSERT_EQ(3, ret);
   EXPECT_TRUE(BN_abs_is_word(bn.get(), 42));
   EXPECT_TRUE(BN_is_negative(bn.get()));
 
   ret = DecimalToBIGNUM(&bn, "-0");
-  EXPECT_EQ(2, ret);
+  ASSERT_EQ(2, ret);
   EXPECT_TRUE(BN_is_zero(bn.get()));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = DecimalToBIGNUM(&bn, "42trailing garbage is ignored");
-  EXPECT_EQ(2, ret);
+  ASSERT_EQ(2, ret);
   EXPECT_TRUE(BN_abs_is_word(bn.get(), 42));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 }
@@ -806,27 +806,27 @@
 TEST_F(BNTest, Hex2BN) {
   bssl::UniquePtr<BIGNUM> bn;
   int ret = HexToBIGNUM(&bn, "0");
-  EXPECT_EQ(1, ret);
+  ASSERT_EQ(1, ret);
   EXPECT_TRUE(BN_is_zero(bn.get()));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = HexToBIGNUM(&bn, "256");
-  EXPECT_EQ(3, ret);
+  ASSERT_EQ(3, ret);
   EXPECT_TRUE(BN_is_word(bn.get(), 0x256));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = HexToBIGNUM(&bn, "-42");
-  EXPECT_EQ(3, ret);
+  ASSERT_EQ(3, ret);
   EXPECT_TRUE(BN_abs_is_word(bn.get(), 0x42));
   EXPECT_TRUE(BN_is_negative(bn.get()));
 
   ret = HexToBIGNUM(&bn, "-0");
-  EXPECT_EQ(2, ret);
+  ASSERT_EQ(2, ret);
   EXPECT_TRUE(BN_is_zero(bn.get()));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 
   ret = HexToBIGNUM(&bn, "abctrailing garbage is ignored");
-  EXPECT_EQ(3, ret);
+  ASSERT_EQ(3, ret);
   EXPECT_TRUE(BN_is_word(bn.get(), 0xabc));
   EXPECT_FALSE(BN_is_negative(bn.get()));
 }
@@ -1000,16 +1000,11 @@
     {"\x03\x01\x00", 3},
     // Empty contents.
     {"\x02\x00", 2},
-};
-
-// kASN1BuggyTests contains incorrect encodings and the corresponding, expected
-// results of |BN_parse_asn1_unsigned_buggy| given that input.
-static const ASN1Test kASN1BuggyTests[] = {
     // Negative numbers.
-    {"128", "\x02\x01\x80", 3},
-    {"255", "\x02\x01\xff", 3},
+    {"\x02\x01\x80", 3},
+    {"\x02\x01\xff", 3},
     // Unnecessary leading zeros.
-    {"1", "\x02\x02\x00\x01", 4},
+    {"\x02\x02\x00\x01", 4},
 };
 
 TEST_F(BNTest, ASN1) {
@@ -1036,12 +1031,6 @@
     ASSERT_TRUE(CBB_finish(cbb.get(), &der, &der_len));
     bssl::UniquePtr<uint8_t> delete_der(der);
     EXPECT_EQ(Bytes(test.der, test.der_len), Bytes(der, der_len));
-
-    // |BN_parse_asn1_unsigned_buggy| parses all valid input.
-    CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
-    ASSERT_TRUE(BN_parse_asn1_unsigned_buggy(&cbs, bn2.get()));
-    EXPECT_EQ(0u, CBS_len(&cbs));
-    EXPECT_BIGNUMS_EQUAL("decode ASN.1 buggy", bn.get(), bn2.get());
   }
 
   for (const ASN1InvalidTest &test : kASN1InvalidTests) {
@@ -1053,35 +1042,6 @@
     EXPECT_FALSE(BN_parse_asn1_unsigned(&cbs, bn.get()))
         << "Parsed invalid input.";
     ERR_clear_error();
-
-    // All tests in kASN1InvalidTests are also rejected by
-    // |BN_parse_asn1_unsigned_buggy|.
-    CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
-    EXPECT_FALSE(BN_parse_asn1_unsigned_buggy(&cbs, bn.get()))
-        << "Parsed invalid input.";
-    ERR_clear_error();
-  }
-
-  for (const ASN1Test &test : kASN1BuggyTests) {
-    SCOPED_TRACE(Bytes(test.der, test.der_len));
-
-    // These broken encodings are rejected by |BN_parse_asn1_unsigned|.
-    bssl::UniquePtr<BIGNUM> bn(BN_new());
-    ASSERT_TRUE(bn);
-    CBS cbs;
-    CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
-    EXPECT_FALSE(BN_parse_asn1_unsigned(&cbs, bn.get()))
-        << "Parsed invalid input.";
-    ERR_clear_error();
-
-    // However |BN_parse_asn1_unsigned_buggy| accepts them.
-    bssl::UniquePtr<BIGNUM> bn2 = ASCIIToBIGNUM(test.value_ascii);
-    ASSERT_TRUE(bn2);
-
-    CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
-    ASSERT_TRUE(BN_parse_asn1_unsigned_buggy(&cbs, bn.get()));
-    EXPECT_EQ(0u, CBS_len(&cbs));
-    EXPECT_BIGNUMS_EQUAL("decode ASN.1 buggy", bn2.get(), bn.get());
   }
 
   // Serializing negative numbers is not supported.
diff --git a/src/crypto/fipsmodule/bn/bn_test_to_fuzzer.go b/src/crypto/fipsmodule/bn/bn_test_to_fuzzer.go
new file mode 100644
index 0000000..d1ee734
--- /dev/null
+++ b/src/crypto/fipsmodule/bn/bn_test_to_fuzzer.go
@@ -0,0 +1,234 @@
+// 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.
+
+package main
+
+import (
+	"bufio"
+	"crypto/sha1"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"math/big"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+type test struct {
+	LineNumber int
+	Type       string
+	Values     map[string]*big.Int
+}
+
+type testScanner struct {
+	scanner *bufio.Scanner
+	lineNo  int
+	err     error
+	test    test
+}
+
+func newTestScanner(r io.Reader) *testScanner {
+	return &testScanner{scanner: bufio.NewScanner(r)}
+}
+
+func (s *testScanner) scanLine() bool {
+	if !s.scanner.Scan() {
+		return false
+	}
+	s.lineNo++
+	return true
+}
+
+func (s *testScanner) addAttribute(line string) (key string, ok bool) {
+	fields := strings.SplitN(line, "=", 2)
+	if len(fields) != 2 {
+		s.setError(errors.New("invalid syntax"))
+		return "", false
+	}
+
+	key = strings.TrimSpace(fields[0])
+	value := strings.TrimSpace(fields[1])
+
+	valueInt, ok := new(big.Int).SetString(value, 16)
+	if !ok {
+		s.setError(fmt.Errorf("could not parse %q", value))
+		return "", false
+	}
+	if _, dup := s.test.Values[key]; dup {
+		s.setError(fmt.Errorf("duplicate key %q", key))
+		return "", false
+	}
+	s.test.Values[key] = valueInt
+	return key, true
+}
+
+func (s *testScanner) Scan() bool {
+	s.test = test{
+		Values: make(map[string]*big.Int),
+	}
+
+	// Scan until the first attribute.
+	for {
+		if !s.scanLine() {
+			return false
+		}
+		if len(s.scanner.Text()) != 0 && s.scanner.Text()[0] != '#' {
+			break
+		}
+	}
+
+	var ok bool
+	s.test.Type, ok = s.addAttribute(s.scanner.Text())
+	if !ok {
+		return false
+	}
+	s.test.LineNumber = s.lineNo
+
+	for s.scanLine() {
+		if len(s.scanner.Text()) == 0 {
+			break
+		}
+
+		if s.scanner.Text()[0] == '#' {
+			continue
+		}
+
+		if _, ok := s.addAttribute(s.scanner.Text()); !ok {
+			return false
+		}
+	}
+	return s.scanner.Err() == nil
+}
+
+func (s *testScanner) Test() test {
+	return s.test
+}
+
+func (s *testScanner) Err() error {
+	if s.err != nil {
+		return s.err
+	}
+	return s.scanner.Err()
+}
+
+func (s *testScanner) setError(err error) {
+	s.err = fmt.Errorf("line %d: %s", s.lineNo, err)
+}
+
+func checkKeys(t test, keys ...string) bool {
+	var foundErrors bool
+
+	for _, k := range keys {
+		if _, ok := t.Values[k]; !ok {
+			fmt.Fprintf(os.Stderr, "Line %d: missing key %q.\n", t.LineNumber, k)
+			foundErrors = true
+		}
+	}
+
+	for k, _ := range t.Values {
+		var found bool
+		for _, k2 := range keys {
+			if k == k2 {
+				found = true
+				break
+			}
+		}
+		if !found {
+			fmt.Fprintf(os.Stderr, "Line %d: unexpected key %q.\n", t.LineNumber, k)
+			foundErrors = true
+		}
+	}
+
+	return !foundErrors
+}
+
+func appendLengthPrefixed(ret, b []byte) []byte {
+	ret = append(ret, byte(len(b)>>8), byte(len(b)))
+	ret = append(ret, b...)
+	return ret
+}
+
+func appendUnsigned(ret []byte, n *big.Int) []byte {
+	b := n.Bytes()
+	if n.Sign() == 0 {
+		b = []byte{0}
+	}
+	return appendLengthPrefixed(ret, b)
+}
+
+func appendSigned(ret []byte, n *big.Int) []byte {
+	var sign byte
+	if n.Sign() < 0 {
+		sign = 1
+	}
+	b := []byte{sign}
+	b = append(b, n.Bytes()...)
+	if n.Sign() == 0 {
+		b = append(b, 0)
+	}
+	return appendLengthPrefixed(ret, b)
+}
+
+func main() {
+	if len(os.Args) != 3 {
+		fmt.Fprintf(os.Stderr, "Usage: %s TESTS FUZZ_DIR\n", os.Args[0])
+		os.Exit(1)
+	}
+
+	in, err := os.Open(os.Args[1])
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error opening %s: %s.\n", os.Args[0], err)
+		os.Exit(1)
+	}
+	defer in.Close()
+
+	fuzzerDir := os.Args[2]
+
+	scanner := newTestScanner(in)
+	for scanner.Scan() {
+		var fuzzer string
+		var b []byte
+		test := scanner.Test()
+		switch test.Type {
+		case "Quotient":
+			if checkKeys(test, "A", "B", "Quotient", "Remainder") {
+				fuzzer = "bn_div"
+				b = appendSigned(b, test.Values["A"])
+				b = appendSigned(b, test.Values["B"])
+			}
+		case "ModExp":
+			if checkKeys(test, "A", "E", "M", "ModExp") {
+				fuzzer = "bn_mod_exp"
+				b = appendSigned(b, test.Values["A"])
+				b = appendUnsigned(b, test.Values["E"])
+				b = appendUnsigned(b, test.Values["M"])
+			}
+		}
+
+		if len(fuzzer) != 0 {
+			hash := sha1.Sum(b)
+			path := filepath.Join(fuzzerDir, fuzzer + "_corpus", hex.EncodeToString(hash[:]))
+			if err := ioutil.WriteFile(path, b, 0666); err != nil {
+				fmt.Fprintf(os.Stderr, "Error writing to %s: %s.\n", path, err)
+				os.Exit(1)
+			}
+		}
+	}
+	if scanner.Err() != nil {
+		fmt.Fprintf(os.Stderr, "Error reading tests: %s.\n", scanner.Err())
+	}
+}
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 1bcff50..0b8b9b9 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -128,7 +128,7 @@
     }
 
     ret = q << BN_BITS4;
-    h = ((h << BN_BITS4) | (l >> BN_BITS4)) & BN_MASK2;
+    h = (h << BN_BITS4) | (l >> BN_BITS4);
     l = (l & BN_MASK2l) << BN_BITS4;
   }
 
@@ -569,8 +569,6 @@
   BN_ULONG ret = 0;
   int i, j;
 
-  w &= BN_MASK2;
-
   if (!w) {
     // actually this an error (division by zero)
     return (BN_ULONG) - 1;
@@ -592,7 +590,7 @@
     BN_ULONG d;
     BN_ULONG unused_rem;
     bn_div_rem_words(&d, &unused_rem, ret, l, w);
-    ret = (l - ((d * w) & BN_MASK2)) & BN_MASK2;
+    ret = l - (d * w);
     a->d[i] = d;
   }
 
@@ -634,7 +632,6 @@
   }
 #endif
 
-  w &= BN_MASK2;
   for (i = a->top - 1; i >= 0; i--) {
 #ifndef BN_ULLONG
     ret = ((ret << BN_BITS4) | ((a->d[i] >> BN_BITS4) & BN_MASK2l)) % w;
diff --git a/src/crypto/fipsmodule/bn/exponentiation.c b/src/crypto/fipsmodule/bn/exponentiation.c
index f4e028b..b6b7fa9 100644
--- a/src/crypto/fipsmodule/bn/exponentiation.c
+++ b/src/crypto/fipsmodule/bn/exponentiation.c
@@ -655,9 +655,9 @@
       goto err;
     }
     // 2^(top*BN_BITS2) - m
-    r->d[0] = (0 - m->d[0]) & BN_MASK2;
+    r->d[0] = 0 - m->d[0];
     for (i = 1; i < j; i++) {
-      r->d[i] = (~m->d[i]) & BN_MASK2;
+      r->d[i] = ~m->d[i];
     }
     r->top = j;
     // Upper words will be zero if the corresponding words of 'm'
@@ -963,9 +963,9 @@
 // by Shay Gueron's suggestion
   if (m->d[top - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
     // 2^(top*BN_BITS2) - m
-    tmp.d[0] = (0 - m->d[0]) & BN_MASK2;
+    tmp.d[0] = 0 - m->d[0];
     for (i = 1; i < top; i++) {
-      tmp.d[i] = (~m->d[i]) & BN_MASK2;
+      tmp.d[i] = ~m->d[i];
     }
     tmp.top = top;
   } else if (!BN_to_montgomery(&tmp, BN_value_one(), mont, ctx)) {
diff --git a/src/crypto/fipsmodule/bn/generic.c b/src/crypto/fipsmodule/bn/generic.c
index b70080f..11c377c 100644
--- a/src/crypto/fipsmodule/bn/generic.c
+++ b/src/crypto/fipsmodule/bn/generic.c
@@ -209,21 +209,21 @@
 
   assert(n >= 0);
   if (n <= 0) {
-    return (BN_ULONG)0;
+    return 0;
   }
 
   while (n & ~3) {
     ll += (BN_ULLONG)a[0] + b[0];
-    r[0] = (BN_ULONG)ll & BN_MASK2;
+    r[0] = (BN_ULONG)ll;
     ll >>= BN_BITS2;
     ll += (BN_ULLONG)a[1] + b[1];
-    r[1] = (BN_ULONG)ll & BN_MASK2;
+    r[1] = (BN_ULONG)ll;
     ll >>= BN_BITS2;
     ll += (BN_ULLONG)a[2] + b[2];
-    r[2] = (BN_ULONG)ll & BN_MASK2;
+    r[2] = (BN_ULONG)ll;
     ll >>= BN_BITS2;
     ll += (BN_ULLONG)a[3] + b[3];
-    r[3] = (BN_ULONG)ll & BN_MASK2;
+    r[3] = (BN_ULONG)ll;
     ll >>= BN_BITS2;
     a += 4;
     b += 4;
@@ -232,7 +232,7 @@
   }
   while (n) {
     ll += (BN_ULLONG)a[0] + b[0];
-    r[0] = (BN_ULONG)ll & BN_MASK2;
+    r[0] = (BN_ULONG)ll;
     ll >>= BN_BITS2;
     a++;
     b++;
@@ -256,27 +256,27 @@
   c = 0;
   while (n & ~3) {
     t = a[0];
-    t = (t + c) & BN_MASK2;
+    t += c;
     c = (t < c);
-    l = (t + b[0]) & BN_MASK2;
+    l = t + b[0];
     c += (l < t);
     r[0] = l;
     t = a[1];
-    t = (t + c) & BN_MASK2;
+    t += c;
     c = (t < c);
-    l = (t + b[1]) & BN_MASK2;
+    l = t + b[1];
     c += (l < t);
     r[1] = l;
     t = a[2];
-    t = (t + c) & BN_MASK2;
+    t += c;
     c = (t < c);
-    l = (t + b[2]) & BN_MASK2;
+    l = t + b[2];
     c += (l < t);
     r[2] = l;
     t = a[3];
-    t = (t + c) & BN_MASK2;
+    t += c;
     c = (t < c);
-    l = (t + b[3]) & BN_MASK2;
+    l = t + b[3];
     c += (l < t);
     r[3] = l;
     a += 4;
@@ -286,9 +286,9 @@
   }
   while (n) {
     t = a[0];
-    t = (t + c) & BN_MASK2;
+    t += c;
     c = (t < c);
-    l = (t + b[0]) & BN_MASK2;
+    l = t + b[0];
     c += (l < t);
     r[0] = l;
     a++;
@@ -314,25 +314,25 @@
   while (n & ~3) {
     t1 = a[0];
     t2 = b[0];
-    r[0] = (t1 - t2 - c) & BN_MASK2;
+    r[0] = t1 - t2 - c;
     if (t1 != t2) {
       c = (t1 < t2);
     }
     t1 = a[1];
     t2 = b[1];
-    r[1] = (t1 - t2 - c) & BN_MASK2;
+    r[1] = t1 - t2 - c;
     if (t1 != t2) {
       c = (t1 < t2);
     }
     t1 = a[2];
     t2 = b[2];
-    r[2] = (t1 - t2 - c) & BN_MASK2;
+    r[2] = t1 - t2 - c;
     if (t1 != t2) {
       c = (t1 < t2);
     }
     t1 = a[3];
     t2 = b[3];
-    r[3] = (t1 - t2 - c) & BN_MASK2;
+    r[3] = t1 - t2 - c;
     if (t1 != t2) {
       c = (t1 < t2);
     }
@@ -344,7 +344,7 @@
   while (n) {
     t1 = a[0];
     t2 = b[0];
-    r[0] = (t1 - t2 - c) & BN_MASK2;
+    r[0] = t1 - t2 - c;
     if (t1 != t2) {
       c = (t1 < t2);
     }
@@ -372,7 +372,7 @@
     t += (c0); /* no carry */           \
     (c0) = (BN_ULONG)Lw(t);             \
     hi = (BN_ULONG)Hw(t);               \
-    (c1) = ((c1) + (hi)) & BN_MASK2;    \
+    (c1) += (hi);                       \
     if ((c1) < hi) {                    \
       (c2)++;                           \
     }                                   \
@@ -385,14 +385,14 @@
     BN_ULLONG tt = t + (c0); /* no carry */ \
     (c0) = (BN_ULONG)Lw(tt);                \
     hi = (BN_ULONG)Hw(tt);                  \
-    (c1) = ((c1) + hi) & BN_MASK2;          \
+    (c1) += hi;                             \
     if ((c1) < hi) {                        \
       (c2)++;                               \
     }                                       \
     t += (c0); /* no carry */               \
     (c0) = (BN_ULONG)Lw(t);                 \
     hi = (BN_ULONG)Hw(t);                   \
-    (c1) = ((c1) + hi) & BN_MASK2;          \
+    (c1) += hi;                             \
     if ((c1) < hi) {                        \
       (c2)++;                               \
     }                                       \
@@ -405,7 +405,7 @@
     t += (c0); /* no carry */                 \
     (c0) = (BN_ULONG)Lw(t);                   \
     hi = (BN_ULONG)Hw(t);                     \
-    (c1) = ((c1) + hi) & BN_MASK2;            \
+    (c1) += hi;                               \
     if ((c1) < hi) {                          \
       (c2)++;                                 \
     }                                         \
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index ecd7d6c..b1e0b0d 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -191,8 +191,8 @@
   }
 
 #if defined(BN_ULLONG)
-#define Lw(t) (((BN_ULONG)(t))&BN_MASK2)
-#define Hw(t) (((BN_ULONG)((t)>>BN_BITS2))&BN_MASK2)
+#define Lw(t) ((BN_ULONG)(t))
+#define Hw(t) ((BN_ULONG)((t) >> BN_BITS2))
 #endif
 
 // bn_correct_top decrements |bn->top| until |bn->d[top-1]| is non-zero or
diff --git a/src/crypto/fipsmodule/bn/montgomery.c b/src/crypto/fipsmodule/bn/montgomery.c
index 8024e27..5219187 100644
--- a/src/crypto/fipsmodule/bn/montgomery.c
+++ b/src/crypto/fipsmodule/bn/montgomery.c
@@ -290,8 +290,8 @@
   n0 = mont->n0[0];
 
   for (carry = 0, i = 0; i < nl; i++, rp++) {
-    v = bn_mul_add_words(rp, np, nl, (rp[0] * n0) & BN_MASK2);
-    v = (v + carry + rp[nl]) & BN_MASK2;
+    v = bn_mul_add_words(rp, np, nl, rp[0] * n0);
+    v += carry + rp[nl];
     carry |= (v != rp[nl]);
     carry &= (v <= rp[nl]);
     rp[nl] = v;
diff --git a/src/crypto/fipsmodule/bn/mul.c b/src/crypto/fipsmodule/bn/mul.c
index a4e27f2..b6f3ff1 100644
--- a/src/crypto/fipsmodule/bn/mul.c
+++ b/src/crypto/fipsmodule/bn/mul.c
@@ -141,7 +141,7 @@
   if (dl < 0) {
     for (;;) {
       t = b[0];
-      r[0] = (0 - t - c) & BN_MASK2;
+      r[0] = 0 - t - c;
       if (t != 0) {
         c = 1;
       }
@@ -150,7 +150,7 @@
       }
 
       t = b[1];
-      r[1] = (0 - t - c) & BN_MASK2;
+      r[1] = 0 - t - c;
       if (t != 0) {
         c = 1;
       }
@@ -159,7 +159,7 @@
       }
 
       t = b[2];
-      r[2] = (0 - t - c) & BN_MASK2;
+      r[2] = 0 - t - c;
       if (t != 0) {
         c = 1;
       }
@@ -168,7 +168,7 @@
       }
 
       t = b[3];
-      r[3] = (0 - t - c) & BN_MASK2;
+      r[3] = 0 - t - c;
       if (t != 0) {
         c = 1;
       }
@@ -183,7 +183,7 @@
     int save_dl = dl;
     while (c) {
       t = a[0];
-      r[0] = (t - c) & BN_MASK2;
+      r[0] = t - c;
       if (t != 0) {
         c = 0;
       }
@@ -192,7 +192,7 @@
       }
 
       t = a[1];
-      r[1] = (t - c) & BN_MASK2;
+      r[1] = t - c;
       if (t != 0) {
         c = 0;
       }
@@ -201,7 +201,7 @@
       }
 
       t = a[2];
-      r[2] = (t - c) & BN_MASK2;
+      r[2] = t - c;
       if (t != 0) {
         c = 0;
       }
@@ -210,7 +210,7 @@
       }
 
       t = a[3];
-      r[3] = (t - c) & BN_MASK2;
+      r[3] = t - c;
       if (t != 0) {
         c = 0;
       }
@@ -407,7 +407,7 @@
   if (c1) {
     p = &(r[n + n2]);
     lo = *p;
-    ln = (lo + c1) & BN_MASK2;
+    ln = lo + c1;
     *p = ln;
 
     // The overflow will stop before we over write
@@ -416,7 +416,7 @@
       do {
         p++;
         lo = *p;
-        ln = (lo + 1) & BN_MASK2;
+        ln = lo + 1;
         *p = ln;
       } while (ln == 0);
     }
@@ -544,7 +544,7 @@
   if (c1) {
     p = &(r[n + n2]);
     lo = *p;
-    ln = (lo + c1) & BN_MASK2;
+    ln = lo + c1;
     *p = ln;
 
     // The overflow will stop before we over write
@@ -553,7 +553,7 @@
       do {
         p++;
         lo = *p;
-        ln = (lo + 1) & BN_MASK2;
+        ln = lo + 1;
         *p = ln;
       } while (ln == 0);
     }
@@ -757,7 +757,7 @@
   if (c1) {
     p = &(r[n + n2]);
     lo = *p;
-    ln = (lo + c1) & BN_MASK2;
+    ln = lo + c1;
     *p = ln;
 
     // The overflow will stop before we over write
@@ -766,7 +766,7 @@
       do {
         p++;
         lo = *p;
-        ln = (lo + 1) & BN_MASK2;
+        ln = lo + 1;
         *p = ln;
       } while (ln == 0);
     }
@@ -774,9 +774,6 @@
 }
 
 int BN_mul_word(BIGNUM *bn, BN_ULONG w) {
-  BN_ULONG ll;
-
-  w &= BN_MASK2;
   if (!bn->top) {
     return 1;
   }
@@ -786,7 +783,7 @@
     return 1;
   }
 
-  ll = bn_mul_words(bn->d, bn->d, bn->top, w);
+  BN_ULONG ll = bn_mul_words(bn->d, bn->d, bn->top, w);
   if (ll) {
     if (!bn_wexpand(bn, bn->top + 1)) {
       return 0;
diff --git a/src/crypto/fipsmodule/bn/shift.c b/src/crypto/fipsmodule/bn/shift.c
index d3fcf39..64afa78 100644
--- a/src/crypto/fipsmodule/bn/shift.c
+++ b/src/crypto/fipsmodule/bn/shift.c
@@ -90,8 +90,8 @@
   } else {
     for (i = a->top - 1; i >= 0; i--) {
       l = f[i];
-      t[nw + i + 1] |= (l >> rb) & BN_MASK2;
-      t[nw + i] = (l << lb) & BN_MASK2;
+      t[nw + i + 1] |= l >> rb;
+      t[nw + i] = l << lb;
     }
   }
   OPENSSL_memset(t, 0, nw * sizeof(t[0]));
@@ -121,7 +121,7 @@
   c = 0;
   for (i = 0; i < a->top; i++) {
     t = *(ap++);
-    *(rp++) = ((t << 1) | c) & BN_MASK2;
+    *(rp++) = (t << 1) | c;
     c = (t & BN_TBIT) ? 1 : 0;
   }
   if (c) {
@@ -173,11 +173,12 @@
   } else {
     l = *(f++);
     for (i = j - 1; i != 0; i--) {
-      tmp = (l >> rb) & BN_MASK2;
+      tmp = l >> rb;
       l = *(f++);
-      *(t++) = (tmp | (l << lb)) & BN_MASK2;
+      *(t++) = tmp | (l << lb);
     }
-    if ((l = (l >> rb) & BN_MASK2)) {
+    l >>= rb;
+    if (l) {
       *(t) = l;
     }
   }
@@ -214,7 +215,7 @@
   }
   while (i > 0) {
     t = ap[--i];
-    rp[i] = ((t >> 1) & BN_MASK2) | c;
+    rp[i] = (t >> 1) | c;
     c = (t & 1) ? BN_TBIT : 0;
   }
   r->top = j;
@@ -227,19 +228,17 @@
 }
 
 int BN_set_bit(BIGNUM *a, int n) {
-  int i, j, k;
-
   if (n < 0) {
     return 0;
   }
 
-  i = n / BN_BITS2;
-  j = n % BN_BITS2;
+  int i = n / BN_BITS2;
+  int j = n % BN_BITS2;
   if (a->top <= i) {
     if (!bn_wexpand(a, i + 1)) {
       return 0;
     }
-    for (k = a->top; k < i + 1; k++) {
+    for (int k = a->top; k < i + 1; k++) {
       a->d[k] = 0;
     }
     a->top = i + 1;
@@ -269,13 +268,11 @@
 }
 
 int BN_is_bit_set(const BIGNUM *a, int n) {
-  int i, j;
-
   if (n < 0) {
     return 0;
   }
-  i = n / BN_BITS2;
-  j = n % BN_BITS2;
+  int i = n / BN_BITS2;
+  int j = n % BN_BITS2;
   if (a->top <= i) {
     return 0;
   }
@@ -284,14 +281,12 @@
 }
 
 int BN_mask_bits(BIGNUM *a, int n) {
-  int b, w;
-
   if (n < 0) {
     return 0;
   }
 
-  w = n / BN_BITS2;
-  b = n % BN_BITS2;
+  int w = n / BN_BITS2;
+  int b = n % BN_BITS2;
   if (w >= a->top) {
     return 0;
   }
diff --git a/src/crypto/fipsmodule/ec/ec.c b/src/crypto/fipsmodule/ec/ec.c
index a39ca59..600aedc 100644
--- a/src/crypto/fipsmodule/ec/ec.c
+++ b/src/crypto/fipsmodule/ec/ec.c
@@ -80,6 +80,8 @@
 #include "../delocate.h"
 
 
+static void ec_point_free(EC_POINT *point, int free_group);
+
 static const uint8_t kP224Params[6 * 28] = {
     // p
     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -354,6 +356,7 @@
   }
   OPENSSL_memset(ret, 0, sizeof(EC_GROUP));
 
+  ret->references = 1;
   ret->meth = meth;
   BN_init(&ret->order);
 
@@ -365,6 +368,19 @@
   return ret;
 }
 
+static void ec_group_set0_generator(EC_GROUP *group, EC_POINT *generator) {
+  assert(group->generator == NULL);
+  assert(group == generator->group);
+
+  // Avoid a reference cycle. |group->generator| does not maintain an owning
+  // pointer to |group|.
+  group->generator = generator;
+  int is_zero = CRYPTO_refcount_dec_and_test_zero(&group->references);
+
+  assert(!is_zero);
+  (void)is_zero;
+}
+
 EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a,
                                  const BIGNUM *b, BN_CTX *ctx) {
   EC_GROUP *ret = ec_group_new(EC_GFp_mont_method());
@@ -372,9 +388,10 @@
     return NULL;
   }
 
-  if (ret->meth->group_set_curve == 0) {
+  if (ret->meth->group_set_curve == NULL) {
     OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
+    EC_GROUP_free(ret);
+    return NULL;
   }
   if (!ret->meth->group_set_curve(ret, p, a, b, ctx)) {
     EC_GROUP_free(ret);
@@ -385,9 +402,13 @@
 
 int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
                            const BIGNUM *order, const BIGNUM *cofactor) {
-  if (group->curve_name != NID_undef || group->generator != NULL) {
+  if (group->curve_name != NID_undef || group->generator != NULL ||
+      generator->group != group) {
     // |EC_GROUP_set_generator| may only be used with |EC_GROUP|s returned by
     // |EC_GROUP_new_curve_GFp| and may only used once on each group.
+    // Additionally, |generator| must been created from
+    // |EC_GROUP_new_curve_GFp|, not a copy, so that
+    // |generator->group->generator| is set correctly.
     return 0;
   }
 
@@ -397,10 +418,16 @@
     return 0;
   }
 
-  group->generator = EC_POINT_new(group);
-  return group->generator != NULL &&
-         EC_POINT_copy(group->generator, generator) &&
-         BN_copy(&group->order, order);
+  EC_POINT *copy = EC_POINT_new(group);
+  if (copy == NULL ||
+      !EC_POINT_copy(copy, generator) ||
+      !BN_copy(&group->order, order)) {
+    EC_POINT_free(copy);
+    return 0;
+  }
+
+  ec_group_set0_generator(group, copy);
+  return 1;
 }
 
 static EC_GROUP *ec_group_new_from_data(unsigned built_in_index) {
@@ -459,7 +486,7 @@
     group->order_mont = monts[built_in_index];
   }
 
-  group->generator = P;
+  ec_group_set0_generator(group, P);
   P = NULL;
   ok = 1;
 
@@ -500,15 +527,16 @@
 }
 
 void EC_GROUP_free(EC_GROUP *group) {
-  if (!group) {
+  if (group == NULL ||
+      !CRYPTO_refcount_dec_and_test_zero(&group->references)) {
     return;
   }
 
-  if (group->meth->group_finish != 0) {
+  if (group->meth->group_finish != NULL) {
     group->meth->group_finish(group);
   }
 
-  EC_POINT_free(group->generator);
+  ec_point_free(group->generator, 0 /* don't free group */);
   BN_free(&group->order);
 
   OPENSSL_free(group);
@@ -523,42 +551,37 @@
     return NULL;
   }
 
-  if (a->meth->group_copy == NULL) {
-    OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return NULL;
-  }
-
-  EC_GROUP *ret = ec_group_new(a->meth);
-  if (ret == NULL) {
-    return NULL;
-  }
-
-  ret->order_mont = a->order_mont;
-  ret->curve_name = a->curve_name;
-
-  if (a->generator != NULL) {
-    ret->generator = EC_POINT_dup(a->generator, ret);
-    if (ret->generator == NULL) {
-      goto err;
-    }
-  }
-
-  if (!BN_copy(&ret->order, &a->order) ||
-      !ret->meth->group_copy(ret, a)) {
-    goto err;
-  }
-
-  return ret;
-
-err:
-  EC_GROUP_free(ret);
-  return NULL;
+  // Groups are logically immutable (but for |EC_GROUP_set_generator| which must
+  // be called early on), so we simply take a reference.
+  EC_GROUP *group = (EC_GROUP *)a;
+  CRYPTO_refcount_inc(&group->references);
+  return group;
 }
 
 int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ignored) {
-  return a->curve_name == NID_undef ||
-         b->curve_name == NID_undef ||
-         a->curve_name != b->curve_name;
+  // Note this function returns 0 if equal and non-zero otherwise.
+  if (a == b) {
+    return 0;
+  }
+  if (a->curve_name != b->curve_name) {
+    return 1;
+  }
+  if (a->curve_name != NID_undef) {
+    // Built-in curves may be compared by curve name alone.
+    return 0;
+  }
+
+  // |a| and |b| are both custom curves. We compare the entire curve
+  // structure. If |a| or |b| is incomplete (due to legacy OpenSSL mistakes,
+  // custom curve construction is sadly done in two parts) but otherwise not the
+  // same object, we consider them always unequal.
+  return a->generator == NULL ||
+         b->generator == NULL ||
+         BN_cmp(&a->order, &b->order) != 0 ||
+         BN_cmp(&a->field, &b->field) != 0 ||
+         BN_cmp(&a->a, &b->a) != 0 ||
+         BN_cmp(&a->b, &b->b) != 0 ||
+         ec_GFp_simple_cmp(a, a->generator, b->generator, NULL) != 0;
 }
 
 const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group) {
@@ -608,9 +631,9 @@
     return NULL;
   }
 
-  ret->meth = group->meth;
-
-  if (!ec_GFp_simple_point_init(ret)) {
+  ret->group = EC_GROUP_dup(group);
+  if (ret->group == NULL ||
+      !ec_GFp_simple_point_init(ret)) {
     OPENSSL_free(ret);
     return NULL;
   }
@@ -618,28 +641,25 @@
   return ret;
 }
 
-void EC_POINT_free(EC_POINT *point) {
+static void ec_point_free(EC_POINT *point, int free_group) {
   if (!point) {
     return;
   }
-
   ec_GFp_simple_point_finish(point);
-
-  OPENSSL_free(point);
-}
-
-void EC_POINT_clear_free(EC_POINT *point) {
-  if (!point) {
-    return;
+  if (free_group) {
+    EC_GROUP_free(point->group);
   }
-
-  ec_GFp_simple_point_clear_finish(point);
-
   OPENSSL_free(point);
 }
 
+void EC_POINT_free(EC_POINT *point) {
+  ec_point_free(point, 1 /* free group */);
+}
+
+void EC_POINT_clear_free(EC_POINT *point) { EC_POINT_free(point); }
+
 int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src) {
-  if (dest->meth != src->meth) {
+  if (EC_GROUP_cmp(dest->group, src->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -665,7 +685,7 @@
 }
 
 int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -673,7 +693,7 @@
 }
 
 int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -682,7 +702,7 @@
 
 int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
                          BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -691,7 +711,8 @@
 
 int EC_POINT_cmp(const EC_GROUP *group, const EC_POINT *a, const EC_POINT *b,
                  BN_CTX *ctx) {
-  if ((group->meth != a->meth) || (a->meth != b->meth)) {
+  if (EC_GROUP_cmp(group, a->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, b->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return -1;
   }
@@ -699,7 +720,7 @@
 }
 
 int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -709,7 +730,7 @@
 int EC_POINTs_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[],
                           BN_CTX *ctx) {
   for (size_t i = 0; i < num; i++) {
-    if (group->meth != points[i]->meth) {
+    if (EC_GROUP_cmp(group, points[i]->group, NULL) != 0) {
       OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
       return 0;
     }
@@ -724,7 +745,7 @@
     OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -734,7 +755,7 @@
 int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
                                         const BIGNUM *x, const BIGNUM *y,
                                         BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -752,8 +773,9 @@
 
 int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
                  const EC_POINT *b, BN_CTX *ctx) {
-  if ((group->meth != r->meth) || (r->meth != a->meth) ||
-      (a->meth != b->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, a->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, b->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -763,7 +785,8 @@
 
 int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
                  BN_CTX *ctx) {
-  if ((group->meth != r->meth) || (r->meth != a->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, a->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -772,7 +795,7 @@
 
 
 int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx) {
-  if (group->meth != a->meth) {
+  if (EC_GROUP_cmp(group, a->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -790,8 +813,8 @@
     return 0;
   }
 
-  if (group->meth != r->meth ||
-      (p != NULL && group->meth != p->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      (p != NULL && EC_GROUP_cmp(group, p->group, NULL) != 0)) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -803,7 +826,7 @@
                                              EC_POINT *point, const BIGNUM *x,
                                              const BIGNUM *y, const BIGNUM *z,
                                              BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
diff --git a/src/crypto/fipsmodule/ec/ec_montgomery.c b/src/crypto/fipsmodule/ec/ec_montgomery.c
index c5f240b..6670b84 100644
--- a/src/crypto/fipsmodule/ec/ec_montgomery.c
+++ b/src/crypto/fipsmodule/ec/ec_montgomery.c
@@ -90,32 +90,6 @@
   ec_GFp_simple_group_finish(group);
 }
 
-int ec_GFp_mont_group_copy(EC_GROUP *dest, const EC_GROUP *src) {
-  BN_MONT_CTX_free(dest->mont);
-  dest->mont = NULL;
-
-  if (!ec_GFp_simple_group_copy(dest, src)) {
-    return 0;
-  }
-
-  if (src->mont != NULL) {
-    dest->mont = BN_MONT_CTX_new();
-    if (dest->mont == NULL) {
-      return 0;
-    }
-    if (!BN_MONT_CTX_copy(dest->mont, src->mont)) {
-      goto err;
-    }
-  }
-
-  return 1;
-
-err:
-  BN_MONT_CTX_free(dest->mont);
-  dest->mont = NULL;
-  return 0;
-}
-
 int ec_GFp_mont_group_set_curve(EC_GROUP *group, const BIGNUM *p,
                                 const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
   BN_CTX *new_ctx = NULL;
@@ -293,7 +267,6 @@
 DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_mont_method) {
   out->group_init = ec_GFp_mont_group_init;
   out->group_finish = ec_GFp_mont_group_finish;
-  out->group_copy = ec_GFp_mont_group_copy;
   out->group_set_curve = ec_GFp_mont_group_set_curve;
   out->point_get_affine_coordinates = ec_GFp_mont_point_get_affine_coordinates;
   out->mul = ec_wNAF_mul /* XXX: Not constant time. */;
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 48b60ee..0ee7378 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -276,6 +276,33 @@
 
   // The key must be valid according to the new group too.
   EXPECT_TRUE(EC_KEY_check_key(key2.get()));
+
+  // Make a second instance of |group|.
+  bssl::UniquePtr<EC_GROUP> group2(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  ASSERT_TRUE(group2);
+  bssl::UniquePtr<EC_POINT> generator2(EC_POINT_new(group2.get()));
+  ASSERT_TRUE(generator2);
+  ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+      group2.get(), generator2.get(), gx.get(), gy.get(), ctx.get()));
+  ASSERT_TRUE(EC_GROUP_set_generator(group2.get(), generator2.get(),
+                                     order.get(), BN_value_one()));
+
+  EXPECT_EQ(0, EC_GROUP_cmp(group.get(), group.get(), NULL));
+  EXPECT_EQ(0, EC_GROUP_cmp(group2.get(), group.get(), NULL));
+
+  // group3 uses the wrong generator.
+  bssl::UniquePtr<EC_GROUP> group3(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  ASSERT_TRUE(group3);
+  bssl::UniquePtr<EC_POINT> generator3(EC_POINT_new(group3.get()));
+  ASSERT_TRUE(generator3);
+  ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+      group3.get(), generator3.get(), x.get(), y.get(), ctx.get()));
+  ASSERT_TRUE(EC_GROUP_set_generator(group3.get(), generator3.get(),
+                                     order.get(), BN_value_one()));
+
+  EXPECT_NE(0, EC_GROUP_cmp(group.get(), group3.get(), NULL));
 }
 
 class ECCurveTest : public testing::TestWithParam<EC_builtin_curve> {};
diff --git a/src/crypto/fipsmodule/ec/internal.h b/src/crypto/fipsmodule/ec/internal.h
index 39c9349..f065744 100644
--- a/src/crypto/fipsmodule/ec/internal.h
+++ b/src/crypto/fipsmodule/ec/internal.h
@@ -82,7 +82,6 @@
 struct ec_method_st {
   int (*group_init)(EC_GROUP *);
   void (*group_finish)(EC_GROUP *);
-  int (*group_copy)(EC_GROUP *, const EC_GROUP *);
   int (*group_set_curve)(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
                          const BIGNUM *b, BN_CTX *);
   int (*point_get_affine_coordinates)(const EC_GROUP *, const EC_POINT *,
@@ -114,6 +113,8 @@
 struct ec_group_st {
   const EC_METHOD *meth;
 
+  // Unlike all other |EC_POINT|s, |generator| does not own |generator->group|
+  // to avoid a reference cycle.
   EC_POINT *generator;
   BIGNUM order;
 
@@ -130,13 +131,17 @@
 
   int a_is_minus3;  // enable optimized point arithmetics for special case
 
+  CRYPTO_refcount_t references;
+
   BN_MONT_CTX *mont;  // Montgomery structure.
 
   BIGNUM one;  // The value one.
 } /* EC_GROUP */;
 
 struct ec_point_st {
-  const EC_METHOD *meth;
+  // group is an owning reference to |group|, unless this is
+  // |group->generator|.
+  EC_GROUP *group;
 
   BIGNUM X;
   BIGNUM Y;
@@ -145,7 +150,6 @@
 } /* EC_POINT */;
 
 EC_GROUP *ec_group_new(const EC_METHOD *meth);
-int ec_group_copy(EC_GROUP *dest, const EC_GROUP *src);
 
 // ec_group_get_order_mont returns a Montgomery context for operations modulo
 // |group|'s order. It may return NULL in the case that |group| is not a
@@ -158,7 +162,6 @@
 // method functions in simple.c
 int ec_GFp_simple_group_init(EC_GROUP *);
 void ec_GFp_simple_group_finish(EC_GROUP *);
-int ec_GFp_simple_group_copy(EC_GROUP *, const EC_GROUP *);
 int ec_GFp_simple_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
                                   const BIGNUM *b, BN_CTX *);
 int ec_GFp_simple_group_get_curve(const EC_GROUP *, BIGNUM *p, BIGNUM *a,
@@ -166,7 +169,6 @@
 unsigned ec_GFp_simple_group_get_degree(const EC_GROUP *);
 int ec_GFp_simple_point_init(EC_POINT *);
 void ec_GFp_simple_point_finish(EC_POINT *);
-void ec_GFp_simple_point_clear_finish(EC_POINT *);
 int ec_GFp_simple_point_copy(EC_POINT *, const EC_POINT *);
 int ec_GFp_simple_point_set_to_infinity(const EC_GROUP *, EC_POINT *);
 int ec_GFp_simple_set_Jprojective_coordinates_GFp(const EC_GROUP *, EC_POINT *,
@@ -205,7 +207,6 @@
 int ec_GFp_mont_group_set_curve(EC_GROUP *, const BIGNUM *p, const BIGNUM *a,
                                 const BIGNUM *b, BN_CTX *);
 void ec_GFp_mont_group_finish(EC_GROUP *);
-int ec_GFp_mont_group_copy(EC_GROUP *, const EC_GROUP *);
 int ec_GFp_mont_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
                           const BIGNUM *b, BN_CTX *);
 int ec_GFp_mont_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
diff --git a/src/crypto/fipsmodule/ec/oct.c b/src/crypto/fipsmodule/ec/oct.c
index cf51e4b..7d62395 100644
--- a/src/crypto/fipsmodule/ec/oct.c
+++ b/src/crypto/fipsmodule/ec/oct.c
@@ -251,7 +251,7 @@
 
 int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
                        const uint8_t *buf, size_t len, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -261,7 +261,7 @@
 size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
                           point_conversion_form_t form, uint8_t *buf,
                           size_t len, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -396,7 +396,7 @@
 int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
                                             EC_POINT *point, const BIGNUM *x,
                                             int y_bit, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
diff --git a/src/crypto/fipsmodule/ec/p224-64.c b/src/crypto/fipsmodule/ec/p224-64.c
index ec5a93d..26a94b9 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -1151,7 +1151,6 @@
 DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp224_method) {
   out->group_init = ec_GFp_simple_group_init;
   out->group_finish = ec_GFp_simple_group_finish;
-  out->group_copy = ec_GFp_simple_group_copy;
   out->group_set_curve = ec_GFp_simple_group_set_curve;
   out->point_get_affine_coordinates =
       ec_GFp_nistp224_point_get_affine_coordinates;
diff --git a/src/crypto/fipsmodule/ec/p256-64.c b/src/crypto/fipsmodule/ec/p256-64.c
index f7d1ff1..04ae56b 100644
--- a/src/crypto/fipsmodule/ec/p256-64.c
+++ b/src/crypto/fipsmodule/ec/p256-64.c
@@ -1694,7 +1694,6 @@
 DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp256_method) {
   out->group_init = ec_GFp_simple_group_init;
   out->group_finish = ec_GFp_simple_group_finish;
-  out->group_copy = ec_GFp_simple_group_copy;
   out->group_set_curve = ec_GFp_simple_group_set_curve;
   out->point_get_affine_coordinates =
       ec_GFp_nistp256_point_get_affine_coordinates;
diff --git a/src/crypto/fipsmodule/ec/p256-x86_64.c b/src/crypto/fipsmodule/ec/p256-x86_64.c
index 8b51677..0004add 100644
--- a/src/crypto/fipsmodule/ec/p256-x86_64.c
+++ b/src/crypto/fipsmodule/ec/p256-x86_64.c
@@ -547,7 +547,6 @@
 DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistz256_method) {
   out->group_init = ec_GFp_mont_group_init;
   out->group_finish = ec_GFp_mont_group_finish;
-  out->group_copy = ec_GFp_mont_group_copy;
   out->group_set_curve = ec_GFp_mont_group_set_curve;
   out->point_get_affine_coordinates = ecp_nistz256_get_affine;
   out->mul = ecp_nistz256_points_mul;
diff --git a/src/crypto/fipsmodule/ec/simple.c b/src/crypto/fipsmodule/ec/simple.c
index 75c06da..f7faa45 100644
--- a/src/crypto/fipsmodule/ec/simple.c
+++ b/src/crypto/fipsmodule/ec/simple.c
@@ -104,18 +104,6 @@
   BN_free(&group->one);
 }
 
-int ec_GFp_simple_group_copy(EC_GROUP *dest, const EC_GROUP *src) {
-  if (!BN_copy(&dest->field, &src->field) ||
-      !BN_copy(&dest->a, &src->a) ||
-      !BN_copy(&dest->b, &src->b) ||
-      !BN_copy(&dest->one, &src->one)) {
-    return 0;
-  }
-
-  dest->a_is_minus3 = src->a_is_minus3;
-  return 1;
-}
-
 int ec_GFp_simple_group_set_curve(EC_GROUP *group, const BIGNUM *p,
                                   const BIGNUM *a, const BIGNUM *b,
                                   BN_CTX *ctx) {
@@ -249,12 +237,6 @@
   BN_free(&point->Z);
 }
 
-void ec_GFp_simple_point_clear_finish(EC_POINT *point) {
-  BN_clear_free(&point->X);
-  BN_clear_free(&point->Y);
-  BN_clear_free(&point->Z);
-}
-
 int ec_GFp_simple_point_copy(EC_POINT *dest, const EC_POINT *src) {
   if (!BN_copy(&dest->X, &src->X) ||
       !BN_copy(&dest->Y, &src->Y) ||
@@ -814,11 +796,11 @@
   const BIGNUM *tmp1_, *tmp2_;
   int ret = -1;
 
-  if (EC_POINT_is_at_infinity(group, a)) {
-    return EC_POINT_is_at_infinity(group, b) ? 0 : 1;
+  if (ec_GFp_simple_is_at_infinity(group, a)) {
+    return ec_GFp_simple_is_at_infinity(group, b) ? 0 : 1;
   }
 
-  if (EC_POINT_is_at_infinity(group, b)) {
+  if (ec_GFp_simple_is_at_infinity(group, b)) {
     return 1;
   }
 
diff --git a/src/crypto/fipsmodule/ec/wnaf.c b/src/crypto/fipsmodule/ec/wnaf.c
index 0e3ee13..842a8fb 100644
--- a/src/crypto/fipsmodule/ec/wnaf.c
+++ b/src/crypto/fipsmodule/ec/wnaf.c
@@ -446,7 +446,7 @@
   }
   if (val != NULL) {
     for (i = 0; i < num_val; i++) {
-      EC_POINT_clear_free(val[i]);
+      EC_POINT_free(val[i]);
     }
 
     OPENSSL_free(val);
diff --git a/src/crypto/lhash/lhash.c b/src/crypto/lhash/lhash.c
index 5054529..7bfd289 100644
--- a/src/crypto/lhash/lhash.c
+++ b/src/crypto/lhash/lhash.c
@@ -73,6 +73,25 @@
 static const size_t kMaxAverageChainLength = 2;
 static const size_t kMinAverageChainLength = 1;
 
+struct lhash_st {
+  // num_items contains the total number of items in the hash table.
+  size_t num_items;
+  // buckets is an array of |num_buckets| pointers. Each points to the head of
+  // a chain of LHASH_ITEM objects that have the same hash value, mod
+  // |num_buckets|.
+  LHASH_ITEM **buckets;
+  // num_buckets contains the length of |buckets|. This value is always >=
+  // kMinNumBuckets.
+  size_t num_buckets;
+  // callback_depth contains the current depth of |lh_doall| or |lh_doall_arg|
+  // calls. If non-zero then this suppresses resizing of the |buckets| array,
+  // which would otherwise disrupt the iteration.
+  unsigned callback_depth;
+
+  lhash_cmp_func comp;
+  lhash_hash_func hash;
+};
+
 _LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp) {
   _LHASH *ret = OPENSSL_malloc(sizeof(_LHASH));
   if (ret == NULL) {
diff --git a/src/crypto/pem/pem_lib.c b/src/crypto/pem/pem_lib.c
index afa39d7..8f89096 100644
--- a/src/crypto/pem/pem_lib.c
+++ b/src/crypto/pem/pem_lib.c
@@ -764,13 +764,13 @@
 
 int PEM_def_callback(char *buf, int size, int rwflag, void *userdata)
 {
-    if (!buf || !userdata) {
+    if (!buf || !userdata || size < 0) {
         return 0;
     }
     size_t len = strlen((char *)userdata);
     if (len >= (size_t)size) {
         return 0;
     }
-    strcpy(buf, (char *)userdata);
+    BUF_strlcpy(buf, userdata, (size_t)size);
     return len;
 }
diff --git a/src/crypto/rsa_extra/rsa_asn1.c b/src/crypto/rsa_extra/rsa_asn1.c
index 23c91bd..3cc6a9c 100644
--- a/src/crypto/rsa_extra/rsa_asn1.c
+++ b/src/crypto/rsa_extra/rsa_asn1.c
@@ -69,22 +69,15 @@
 #include "../internal.h"
 
 
-static int parse_integer_buggy(CBS *cbs, BIGNUM **out, int buggy) {
+static int parse_integer(CBS *cbs, BIGNUM **out) {
   assert(*out == NULL);
   *out = BN_new();
   if (*out == NULL) {
     return 0;
   }
-  if (buggy) {
-    return BN_parse_asn1_unsigned_buggy(cbs, *out);
-  }
   return BN_parse_asn1_unsigned(cbs, *out);
 }
 
-static int parse_integer(CBS *cbs, BIGNUM **out) {
-  return parse_integer_buggy(cbs, out, 0 /* not buggy */);
-}
-
 static int marshal_integer(CBB *cbb, BIGNUM *bn) {
   if (bn == NULL) {
     // An RSA object may be missing some components.
@@ -94,14 +87,14 @@
   return BN_marshal_asn1(cbb, bn);
 }
 
-static RSA *parse_public_key(CBS *cbs, int buggy) {
+RSA *RSA_parse_public_key(CBS *cbs) {
   RSA *ret = RSA_new();
   if (ret == NULL) {
     return NULL;
   }
   CBS child;
   if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) ||
-      !parse_integer_buggy(&child, &ret->n, buggy) ||
+      !parse_integer(&child, &ret->n) ||
       !parse_integer(&child, &ret->e) ||
       CBS_len(&child) != 0) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
@@ -119,18 +112,6 @@
   return ret;
 }
 
-RSA *RSA_parse_public_key(CBS *cbs) {
-  return parse_public_key(cbs, 0 /* not buggy */);
-}
-
-RSA *RSA_parse_public_key_buggy(CBS *cbs) {
-  // Estonian IDs issued between September 2014 to September 2015 are
-  // broken. See https://crbug.com/532048 and https://crbug.com/534766.
-  //
-  // TODO(davidben): Remove this code and callers in March 2016.
-  return parse_public_key(cbs, 1 /* buggy */);
-}
-
 RSA *RSA_public_key_from_bytes(const uint8_t *in, size_t in_len) {
   CBS cbs;
   CBS_init(&cbs, in, in_len);
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index 1cc71ea..23e8c67 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -622,13 +622,6 @@
                                       sizeof(kEstonianRSAKey)));
   EXPECT_FALSE(rsa);
   ERR_clear_error();
-
-  // But |RSA_parse_public_key_buggy| will accept it.
-  CBS cbs;
-  CBS_init(&cbs, kEstonianRSAKey, sizeof(kEstonianRSAKey));
-  rsa.reset(RSA_parse_public_key_buggy(&cbs));
-  EXPECT_TRUE(rsa);
-  EXPECT_EQ(0u, CBS_len(&cbs));
 }
 
 TEST(RSATest, BadExponent) {
diff --git a/src/crypto/x509/by_dir.c b/src/crypto/x509/by_dir.c
index d72f9ec..635b851 100644
--- a/src/crypto/x509/by_dir.c
+++ b/src/crypto/x509/by_dir.c
@@ -61,7 +61,6 @@
 
 #include <openssl/buf.h>
 #include <openssl/err.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/thread.h>
 #include <openssl/x509.h>
@@ -235,8 +234,7 @@
                 by_dir_entry_free(ent);
                 return 0;
             }
-            strncpy(ent->dir, ss, len);
-            ent->dir[len] = '\0';
+            BUF_strlcpy(ent->dir, ss, len + 1);
             if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent)) {
                 by_dir_entry_free(ent);
                 return 0;
diff --git a/src/crypto/x509/by_file.c b/src/crypto/x509/by_file.c
index 2bec572..555cb85 100644
--- a/src/crypto/x509/by_file.c
+++ b/src/crypto/x509/by_file.c
@@ -59,7 +59,6 @@
 
 #include <openssl/buf.h>
 #include <openssl/err.h>
-#include <openssl/lhash.h>
 #include <openssl/pem.h>
 #include <openssl/thread.h>
 
diff --git a/src/crypto/x509/x509_lu.c b/src/crypto/x509/x509_lu.c
index 1014a05..1a841db 100644
--- a/src/crypto/x509/x509_lu.c
+++ b/src/crypto/x509/x509_lu.c
@@ -58,7 +58,6 @@
 #include <string.h>
 
 #include <openssl/err.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/thread.h>
 #include <openssl/x509.h>
diff --git a/src/crypto/x509/x509_obj.c b/src/crypto/x509/x509_obj.c
index 33eafc4..65b1bfb 100644
--- a/src/crypto/x509/x509_obj.c
+++ b/src/crypto/x509/x509_obj.c
@@ -59,7 +59,6 @@
 
 #include <openssl/buf.h>
 #include <openssl/err.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/x509.h>
@@ -102,8 +101,7 @@
             buf = b->data;
             OPENSSL_free(b);
         }
-        strncpy(buf, "NO X509_NAME", len);
-        buf[len - 1] = '\0';
+        BUF_strlcpy(buf, "NO X509_NAME", len);
         return buf;
     }
 
diff --git a/src/crypto/x509/x509_txt.c b/src/crypto/x509/x509_txt.c
index 17e6cdb..753e720 100644
--- a/src/crypto/x509/x509_txt.c
+++ b/src/crypto/x509/x509_txt.c
@@ -54,13 +54,7 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include <openssl/asn1.h>
-#include <openssl/buf.h>
-#include <openssl/cipher.h>
-#include <openssl/evp.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/x509.h>
 
 const char *X509_verify_cert_error_string(long n)
diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c
index 8e258fe..aff2ee9 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -61,7 +61,6 @@
 #include <openssl/buf.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/thread.h>
diff --git a/src/crypto/x509/x509_vpm.c b/src/crypto/x509/x509_vpm.c
index 2317214..d0f8f79 100644
--- a/src/crypto/x509/x509_vpm.c
+++ b/src/crypto/x509/x509_vpm.c
@@ -57,7 +57,6 @@
 #include <string.h>
 
 #include <openssl/buf.h>
-#include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/stack.h>
diff --git a/src/crypto/x509v3/v3_alt.c b/src/crypto/x509v3/v3_alt.c
index 6280651..b78a410 100644
--- a/src/crypto/x509v3/v3_alt.c
+++ b/src/crypto/x509v3/v3_alt.c
@@ -166,9 +166,9 @@
             for (i = 0; i < 8; i++) {
                 BIO_snprintf(htmp, sizeof htmp, "%X", p[0] << 8 | p[1]);
                 p += 2;
-                strcat(oline, htmp);
+                BUF_strlcat(oline, htmp, sizeof(oline));
                 if (i != 7)
-                    strcat(oline, ":");
+                    BUF_strlcat(oline, ":", sizeof(oline));
             }
         } else {
             if (!X509V3_add_value("IP Address", "<invalid>", &ret))
@@ -588,8 +588,7 @@
     objtmp = OPENSSL_malloc(objlen + 1);
     if (objtmp == NULL)
         return 0;
-    strncpy(objtmp, value, objlen);
-    objtmp[objlen] = 0;
+    BUF_strlcpy(objtmp, value, objlen + 1);
     gen->d.otherName->type_id = OBJ_txt2obj(objtmp, 0);
     OPENSSL_free(objtmp);
     if (!gen->d.otherName->type_id)
diff --git a/src/crypto/x509v3/v3_genn.c b/src/crypto/x509v3/v3_genn.c
index 2331cd4..8c92687 100644
--- a/src/crypto/x509v3/v3_genn.c
+++ b/src/crypto/x509v3/v3_genn.c
@@ -231,6 +231,7 @@
     oth = OTHERNAME_new();
     if (!oth)
         return 0;
+    ASN1_TYPE_free(oth->value);
     oth->type_id = oid;
     oth->value = value;
     GENERAL_NAME_set0_value(gen, GEN_OTHERNAME, oth);
diff --git a/src/crypto/x509v3/v3_info.c b/src/crypto/x509v3/v3_info.c
index 40f451f..ff96489 100644
--- a/src/crypto/x509v3/v3_info.c
+++ b/src/crypto/x509v3/v3_info.c
@@ -192,8 +192,7 @@
             OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
             goto err;
         }
-        strncpy(objtmp, cnf->name, objlen);
-        objtmp[objlen] = 0;
+        BUF_strlcpy(objtmp, cnf->name, objlen + 1);
         acc->method = OBJ_txt2obj(objtmp, 0);
         if (!acc->method) {
             OPENSSL_PUT_ERROR(X509V3, X509V3_R_BAD_OBJECT);
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 52333ac..9960b75 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -317,10 +317,6 @@
 // the result to |ret|. It returns one on success and zero on failure.
 OPENSSL_EXPORT int BN_parse_asn1_unsigned(CBS *cbs, BIGNUM *ret);
 
-// BN_parse_asn1_unsigned_buggy acts like |BN_parse_asn1_unsigned| but tolerates
-// some invalid encodings. Do not use this function.
-OPENSSL_EXPORT int BN_parse_asn1_unsigned_buggy(CBS *cbs, BIGNUM *ret);
-
 // BN_marshal_asn1 marshals |bn| as a non-negative DER INTEGER and appends the
 // result to |cbb|. It returns one on success and zero on failure.
 OPENSSL_EXPORT int BN_marshal_asn1(CBB *cbb, const BIGNUM *bn);
diff --git a/src/include/openssl/ec.h b/src/include/openssl/ec.h
index 4a08a9b..dee41b7 100644
--- a/src/include/openssl/ec.h
+++ b/src/include/openssl/ec.h
@@ -162,10 +162,6 @@
 // EC_POINT_free frees |point| and the data that it points to.
 OPENSSL_EXPORT void EC_POINT_free(EC_POINT *point);
 
-// EC_POINT_clear_free clears the data that |point| points to, frees it and
-// then frees |point| itself.
-OPENSSL_EXPORT void EC_POINT_clear_free(EC_POINT *point);
-
 // EC_POINT_copy sets |*dest| equal to |*src|. It returns one on success and
 // zero otherwise.
 OPENSSL_EXPORT int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src);
@@ -306,7 +302,7 @@
 // EC_GROUP_set_generator sets the generator for |group| to |generator|, which
 // must have the given order and cofactor. It may only be used with |EC_GROUP|
 // objects returned by |EC_GROUP_new_curve_GFp| and may only be used once on
-// each group.
+// each group. |generator| must have been created using |group|.
 OPENSSL_EXPORT int EC_GROUP_set_generator(EC_GROUP *group,
                                           const EC_POINT *generator,
                                           const BIGNUM *order,
@@ -350,6 +346,9 @@
 OPENSSL_EXPORT size_t EC_get_builtin_curves(EC_builtin_curve *out_curves,
                                             size_t max_num_curves);
 
+// EC_POINT_clear_free calls |EC_POINT_free|.
+OPENSSL_EXPORT void EC_POINT_clear_free(EC_POINT *point);
+
 // Old code expects to get EC_KEY from ec.h.
 #include <openssl/ec_key.h>
 
diff --git a/src/include/openssl/evp.h b/src/include/openssl/evp.h
index c57d7c7..f5eb200 100644
--- a/src/include/openssl/evp.h
+++ b/src/include/openssl/evp.h
@@ -225,10 +225,6 @@
 // success and zero on error.
 OPENSSL_EXPORT int EVP_marshal_private_key(CBB *cbb, const EVP_PKEY *key);
 
-// EVP_set_buggy_rsa_parser configures whether |RSA_parse_public_key_buggy| is
-// used by |EVP_parse_public_key|. By default, it is not used.
-OPENSSL_EXPORT void EVP_set_buggy_rsa_parser(int buggy);
-
 
 // Signing
 
diff --git a/src/include/openssl/lhash.h b/src/include/openssl/lhash.h
index 7525c08..1ceeb69 100644
--- a/src/include/openssl/lhash.h
+++ b/src/include/openssl/lhash.h
@@ -125,24 +125,7 @@
 // uint32_t.
 typedef uint32_t (*lhash_hash_func)(const void *a);
 
-typedef struct lhash_st {
-  // num_items contains the total number of items in the hash table.
-  size_t num_items;
-  // buckets is an array of |num_buckets| pointers. Each points to the head of
-  // a chain of LHASH_ITEM objects that have the same hash value, mod
-  // |num_buckets|.
-  LHASH_ITEM **buckets;
-  // num_buckets contains the length of |buckets|. This value is always >=
-  // kMinNumBuckets.
-  size_t num_buckets;
-  // callback_depth contains the current depth of |lh_doall| or |lh_doall_arg|
-  // calls. If non-zero then this suppresses resizing of the |buckets| array,
-  // which would otherwise disrupt the iteration.
-  unsigned callback_depth;
-
-  lhash_cmp_func comp;
-  lhash_hash_func hash;
-} _LHASH;
+typedef struct lhash_st _LHASH;
 
 // lh_new returns a new, empty hash table or NULL on error.
 OPENSSL_EXPORT _LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp);
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 3f45318..74268cf 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -428,10 +428,6 @@
 // error.
 OPENSSL_EXPORT RSA *RSA_parse_public_key(CBS *cbs);
 
-// RSA_parse_public_key_buggy behaves like |RSA_parse_public_key|, but it
-// tolerates some invalid encodings. Do not use this function.
-OPENSSL_EXPORT RSA *RSA_parse_public_key_buggy(CBS *cbs);
-
 // RSA_public_key_from_bytes parses |in| as a DER-encoded RSAPublicKey structure
 // (RFC 3447). It returns a newly-allocated |RSA| or NULL on error.
 OPENSSL_EXPORT RSA *RSA_public_key_from_bytes(const uint8_t *in, size_t in_len);
diff --git a/src/include/openssl/x509_vfy.h b/src/include/openssl/x509_vfy.h
index 991cbda..208a380 100644
--- a/src/include/openssl/x509_vfy.h
+++ b/src/include/openssl/x509_vfy.h
@@ -64,8 +64,6 @@
 #ifndef HEADER_X509_VFY_H
 #define HEADER_X509_VFY_H
 
-#include <openssl/bio.h>
-#include <openssl/lhash.h>
 #include <openssl/thread.h>
 
 #ifdef  __cplusplus
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index dd56bbc..abd52c0 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -57,6 +57,7 @@
 
 #include <openssl/bio.h>
 #include <openssl/conf.h>
+#include <openssl/lhash.h>
 #include <openssl/x509.h>
 
 #ifdef __cplusplus
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index 798deb0..c219f5a 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -144,23 +144,18 @@
 
 // Receiving handshake messages.
 
-static void dtls1_hm_fragment_free(hm_fragment *frag) {
-  if (frag == NULL) {
-    return;
-  }
-  OPENSSL_free(frag->data);
-  OPENSSL_free(frag->reassembly);
-  OPENSSL_free(frag);
+hm_fragment::~hm_fragment() {
+  OPENSSL_free(data);
+  OPENSSL_free(reassembly);
 }
 
-static hm_fragment *dtls1_hm_fragment_new(const struct hm_header_st *msg_hdr) {
+static UniquePtr<hm_fragment> dtls1_hm_fragment_new(
+    const struct hm_header_st *msg_hdr) {
   ScopedCBB cbb;
-  hm_fragment *frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment));
-  if (frag == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return NULL;
+  UniquePtr<hm_fragment> frag = MakeUnique<hm_fragment>();
+  if (!frag) {
+    return nullptr;
   }
-  OPENSSL_memset(frag, 0, sizeof(hm_fragment));
   frag->type = msg_hdr->type;
   frag->seq = msg_hdr->seq;
   frag->msg_len = msg_hdr->msg_len;
@@ -170,7 +165,7 @@
       (uint8_t *)OPENSSL_malloc(DTLS1_HM_HEADER_LENGTH + msg_hdr->msg_len);
   if (frag->data == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+    return nullptr;
   }
 
   if (!CBB_init_fixed(cbb.get(), frag->data, DTLS1_HM_HEADER_LENGTH) ||
@@ -181,7 +176,7 @@
       !CBB_add_u24(cbb.get(), msg_hdr->msg_len) ||
       !CBB_finish(cbb.get(), NULL, NULL)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+    return nullptr;
   }
 
   // If the handshake message is empty, |frag->reassembly| is NULL.
@@ -189,22 +184,18 @@
     // Initialize reassembly bitmask.
     if (msg_hdr->msg_len + 7 < msg_hdr->msg_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-      goto err;
+      return nullptr;
     }
     size_t bitmask_len = (msg_hdr->msg_len + 7) / 8;
     frag->reassembly = (uint8_t *)OPENSSL_malloc(bitmask_len);
     if (frag->reassembly == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
+      return nullptr;
     }
     OPENSSL_memset(frag->reassembly, 0, bitmask_len);
   }
 
   return frag;
-
-err:
-  dtls1_hm_fragment_free(frag);
-  return NULL;
 }
 
 // bit_range returns a |uint8_t| with bits |start|, inclusive, to |end|,
@@ -262,8 +253,8 @@
 // dtls1_is_current_message_complete returns whether the current handshake
 // message is complete.
 static bool dtls1_is_current_message_complete(const SSL *ssl) {
-  hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
-                                                 SSL_MAX_HANDSHAKE_FLIGHT];
+  size_t idx = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   return frag != NULL && frag->reassembly == NULL;
 }
 
@@ -280,7 +271,7 @@
   }
 
   size_t idx = msg_hdr->seq % SSL_MAX_HANDSHAKE_FLIGHT;
-  hm_fragment *frag = ssl->d1->incoming_messages[idx];
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   if (frag != NULL) {
     assert(frag->seq == msg_hdr->seq);
     // The new fragment must be compatible with the previous fragments from this
@@ -295,13 +286,12 @@
   }
 
   // This is the first fragment from this message.
-  frag = dtls1_hm_fragment_new(msg_hdr);
-  if (frag == NULL) {
+  ssl->d1->incoming_messages[idx] = dtls1_hm_fragment_new(msg_hdr);
+  if (!ssl->d1->incoming_messages[idx]) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return NULL;
   }
-  ssl->d1->incoming_messages[idx] = frag;
-  return frag;
+  return ssl->d1->incoming_messages[idx].get();
 }
 
 ssl_open_record_t dtls1_open_handshake(SSL *ssl, size_t *out_consumed,
@@ -420,8 +410,8 @@
     return false;
   }
 
-  hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
-                                                 SSL_MAX_HANDSHAKE_FLIGHT];
+  size_t idx = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
+  hm_fragment *frag = ssl->d1->incoming_messages[idx].get();
   out->type = frag->type;
   CBS_init(&out->body, frag->data + DTLS1_HM_HEADER_LENGTH, frag->msg_len);
   CBS_init(&out->raw, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
@@ -437,8 +427,7 @@
   assert(ssl->s3->has_message);
   assert(dtls1_is_current_message_complete(ssl));
   size_t index = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
-  dtls1_hm_fragment_free(ssl->d1->incoming_messages[index]);
-  ssl->d1->incoming_messages[index] = NULL;
+  ssl->d1->incoming_messages[index].reset();
   ssl->d1->handshake_read_seq++;
   ssl->s3->has_message = false;
   // If we previously sent a flight, mark it as having a reply, so
@@ -448,13 +437,6 @@
   }
 }
 
-void dtls_clear_incoming_messages(SSL *ssl) {
-  for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
-    dtls1_hm_fragment_free(ssl->d1->incoming_messages[i]);
-    ssl->d1->incoming_messages[i] = NULL;
-  }
-}
-
 bool dtls_has_unprocessed_handshake_data(const SSL *ssl) {
   if (ssl->d1->has_change_cipher_spec) {
     return true;
@@ -467,7 +449,7 @@
       assert(dtls1_is_current_message_complete(ssl));
       continue;
     }
-    if (ssl->d1->incoming_messages[i] != NULL) {
+    if (ssl->d1->incoming_messages[i] != nullptr) {
       return true;
     }
   }
@@ -510,10 +492,14 @@
 
 // Sending handshake messages.
 
+void DTLS_OUTGOING_MESSAGE::Clear() {
+  OPENSSL_free(data);
+  data = nullptr;
+}
+
 void dtls_clear_outgoing_messages(SSL *ssl) {
   for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
-    OPENSSL_free(ssl->d1->outgoing_messages[i].data);
-    ssl->d1->outgoing_messages[i].data = NULL;
+    ssl->d1->outgoing_messages[i].Clear();
   }
   ssl->d1->outgoing_messages_len = 0;
   ssl->d1->outgoing_written = 0;
@@ -552,7 +538,7 @@
 
 // add_outgoing adds a new handshake message or ChangeCipherSpec to the current
 // outgoing flight. It returns true on success and false on error.
-static bool add_outgoing(SSL *ssl, int is_ccs, Array<uint8_t> data) {
+static bool add_outgoing(SSL *ssl, bool is_ccs, Array<uint8_t> data) {
   if (ssl->d1->outgoing_messages_complete) {
     // If we've begun writing a new flight, we received the peer flight. Discard
     // the timer and the our flight.
@@ -594,11 +580,11 @@
 }
 
 bool dtls1_add_message(SSL *ssl, Array<uint8_t> data) {
-  return add_outgoing(ssl, 0 /* handshake */, std::move(data));
+  return add_outgoing(ssl, false /* handshake */, std::move(data));
 }
 
 bool dtls1_add_change_cipher_spec(SSL *ssl) {
-  return add_outgoing(ssl, 1 /* ChangeCipherSpec */, Array<uint8_t>());
+  return add_outgoing(ssl, true /* ChangeCipherSpec */, Array<uint8_t>());
 }
 
 bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
@@ -808,14 +794,14 @@
       // Retry this packet the next time around.
       ssl->d1->outgoing_written = old_written;
       ssl->d1->outgoing_offset = old_offset;
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       ret = bio_ret;
       goto err;
     }
   }
 
   if (BIO_flush(ssl->wbio) <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     goto err;
   }
 
diff --git a/src/ssl/d1_lib.cc b/src/ssl/d1_lib.cc
index 38bc5be..eff06ee 100644
--- a/src/ssl/d1_lib.cc
+++ b/src/ssl/d1_lib.cc
@@ -78,18 +78,24 @@
 // before failing the DTLS handshake.
 #define DTLS1_MAX_TIMEOUTS                     12
 
+DTLS1_STATE::DTLS1_STATE()
+    : has_change_cipher_spec(false),
+      outgoing_messages_complete(false),
+      flight_has_reply(false) {}
+
+DTLS1_STATE::~DTLS1_STATE() {}
+
 bool dtls1_new(SSL *ssl) {
   if (!ssl3_new(ssl)) {
     return false;
   }
-  DTLS1_STATE *d1 = (DTLS1_STATE *)OPENSSL_malloc(sizeof *d1);
-  if (d1 == NULL) {
+  UniquePtr<DTLS1_STATE> d1 = MakeUnique<DTLS1_STATE>();
+  if (!d1) {
     ssl3_free(ssl);
     return false;
   }
-  OPENSSL_memset(d1, 0, sizeof *d1);
 
-  ssl->d1 = d1;
+  ssl->d1 = d1.release();
 
   // Set the version to the highest supported version.
   //
@@ -103,15 +109,11 @@
 void dtls1_free(SSL *ssl) {
   ssl3_free(ssl);
 
-  if (ssl == NULL || ssl->d1 == NULL) {
+  if (ssl == NULL) {
     return;
   }
 
-  dtls_clear_incoming_messages(ssl);
-  dtls_clear_outgoing_messages(ssl);
-  Delete(ssl->d1->last_aead_write_ctx);
-
-  OPENSSL_free(ssl->d1);
+  Delete(ssl->d1);
   ssl->d1 = NULL;
 }
 
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index c6be93d..d29a5c2 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -187,8 +187,8 @@
   return ssl_open_record_success;
 }
 
-int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake,
-                         const uint8_t *buf, int len) {
+int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
+                         int len) {
   assert(!SSL_in_init(ssl));
   *out_needs_handshake = false;
 
@@ -211,7 +211,7 @@
     return 0;
   }
 
-  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len,
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, in, (size_t)len,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
     return ret;
@@ -219,29 +219,29 @@
   return len;
 }
 
-int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
+int dtls1_write_record(SSL *ssl, int type, const uint8_t *in, size_t len,
                        enum dtls1_use_epoch_t use_epoch) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
   // There should never be a pending write buffer in DTLS. One can't write half
   // a datagram, so the write buffer is always dropped in
   // |ssl_write_buffer_flush|.
-  assert(!ssl_write_buffer_is_pending(ssl));
+  assert(buf->empty());
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  size_t max_out = len + SSL_max_seal_overhead(ssl);
-  uint8_t *out;
   size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out) ||
-      !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len,
-                        use_epoch)) {
-    ssl_write_buffer_clear(ssl);
+  if (!buf->EnsureCap(ssl_seal_align_prefix_len(ssl),
+                      len + SSL_max_seal_overhead(ssl)) ||
+      !dtls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                        buf->remaining().size(), type, in, len, use_epoch)) {
+    buf->Clear();
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   int ret = ssl_write_buffer_flush(ssl);
   if (ret <= 0) {
diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc
index fab19be..91a955f 100644
--- a/src/ssl/dtls_method.cc
+++ b/src/ssl/dtls_method.cc
@@ -94,8 +94,7 @@
   OPENSSL_memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap));
   OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
 
-  Delete(ssl->s3->aead_read_ctx);
-  ssl->s3->aead_read_ctx = aead_ctx.release();
+  ssl->s3->aead_read_ctx = std::move(aead_ctx);
   return true;
 }
 
@@ -106,9 +105,8 @@
                  sizeof(ssl->s3->write_sequence));
   OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
 
-  Delete(ssl->d1->last_aead_write_ctx);
-  ssl->d1->last_aead_write_ctx = ssl->s3->aead_write_ctx;
-  ssl->s3->aead_write_ctx = aead_ctx.release();
+  ssl->d1->last_aead_write_ctx = std::move(ssl->s3->aead_write_ctx);
+  ssl->s3->aead_write_ctx = std::move(aead_ctx);
   return true;
 }
 
diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc
index a746640..5e795fa 100644
--- a/src/ssl/dtls_record.cc
+++ b/src/ssl/dtls_record.cc
@@ -275,10 +275,10 @@
                                             enum dtls1_use_epoch_t use_epoch) {
   if (use_epoch == dtls1_use_previous_epoch) {
     assert(ssl->d1->w_epoch >= 1);
-    return ssl->d1->last_aead_write_ctx;
+    return ssl->d1->last_aead_write_ctx.get();
   }
 
-  return ssl->s3->aead_write_ctx;
+  return ssl->s3->aead_write_ctx.get();
 }
 
 size_t dtls_max_seal_overhead(const SSL *ssl,
@@ -303,12 +303,12 @@
 
   // Determine the parameters for the current epoch.
   uint16_t epoch = ssl->d1->w_epoch;
-  SSLAEADContext *aead = ssl->s3->aead_write_ctx;
+  SSLAEADContext *aead = ssl->s3->aead_write_ctx.get();
   uint8_t *seq = ssl->s3->write_sequence;
   if (use_epoch == dtls1_use_previous_epoch) {
     assert(ssl->d1->w_epoch >= 1);
     epoch = ssl->d1->w_epoch - 1;
-    aead = ssl->d1->last_aead_write_ctx;
+    aead = ssl->d1->last_aead_write_ctx.get();
     seq = ssl->d1->last_write_sequence;
   }
 
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 8531ca4..ed11484 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -149,17 +149,15 @@
   ssl->ctx->x509_method->hs_flush_cached_ca_names(this);
 }
 
-SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) {
+UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl) {
   UniquePtr<SSL_HANDSHAKE> hs = MakeUnique<SSL_HANDSHAKE>(ssl);
   if (!hs ||
       !hs->transcript.Init()) {
     return nullptr;
   }
-  return hs.release();
+  return hs;
 }
 
-void ssl_handshake_free(SSL_HANDSHAKE *hs) { Delete(hs); }
-
 bool ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) {
   if (msg.type != type) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
@@ -282,7 +280,7 @@
 
 enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  const SSL_SESSION *prev_session = ssl->s3->established_session;
+  const SSL_SESSION *prev_session = ssl->s3->established_session.get();
   if (prev_session != NULL) {
     // If renegotiating, the server must not change the server certificate. See
     // https://mitls.org/pages/attacks/3SHAKE. We never resume on renegotiation,
@@ -505,10 +503,10 @@
         ssl_open_record_t ret;
         if (hs->wait == ssl_hs_read_change_cipher_spec) {
           ret = ssl_open_change_cipher_spec(ssl, &consumed, &alert,
-                                            ssl_read_buffer(ssl));
+                                            ssl->s3->read_buffer.span());
         } else {
-          ret =
-              ssl_open_handshake(ssl, &consumed, &alert, ssl_read_buffer(ssl));
+          ret = ssl_open_handshake(ssl, &consumed, &alert,
+                                   ssl->s3->read_buffer.span());
         }
         if (ret == ssl_open_record_error &&
             hs->wait == ssl_hs_read_server_hello) {
@@ -533,7 +531,7 @@
         if (retry) {
           continue;
         }
-        ssl_read_buffer_discard(ssl);
+        ssl->s3->read_buffer.DiscardConsumed();
         break;
       }
 
@@ -548,42 +546,42 @@
       }
 
       case ssl_hs_certificate_selection_pending:
-        ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
+        ssl->s3->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_x509_lookup:
-        ssl->rwstate = SSL_X509_LOOKUP;
+        ssl->s3->rwstate = SSL_X509_LOOKUP;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_channel_id_lookup:
-        ssl->rwstate = SSL_CHANNEL_ID_LOOKUP;
+        ssl->s3->rwstate = SSL_CHANNEL_ID_LOOKUP;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_private_key_operation:
-        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        ssl->s3->rwstate = SSL_PRIVATE_KEY_OPERATION;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_pending_session:
-        ssl->rwstate = SSL_PENDING_SESSION;
+        ssl->s3->rwstate = SSL_PENDING_SESSION;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_pending_ticket:
-        ssl->rwstate = SSL_PENDING_TICKET;
+        ssl->s3->rwstate = SSL_PENDING_TICKET;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_certificate_verify:
-        ssl->rwstate = SSL_CERTIFICATE_VERIFY;
+        ssl->s3->rwstate = SSL_CERTIFICATE_VERIFY;
         hs->wait = ssl_hs_ok;
         return -1;
 
       case ssl_hs_early_data_rejected:
-        ssl->rwstate = SSL_EARLY_DATA_REJECTED;
+        ssl->s3->rwstate = SSL_EARLY_DATA_REJECTED;
         // Cause |SSL_write| to start failing immediately.
         hs->can_early_write = false;
         return -1;
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 5466b56..ff8ebd8 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -1476,14 +1476,15 @@
 
   if (hs->next_proto_neg_seen) {
     static const uint8_t kZero[32] = {0};
-    size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
+    size_t padding_len =
+        32 - ((ssl->s3->next_proto_negotiated.size() + 2) % 32);
 
     ScopedCBB cbb;
     CBB body, child;
     if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_NEXT_PROTO) ||
         !CBB_add_u8_length_prefixed(&body, &child) ||
-        !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated,
-                       ssl->s3->next_proto_negotiated_len) ||
+        !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated.data(),
+                       ssl->s3->next_proto_negotiated.size()) ||
         !CBB_add_u8_length_prefixed(&body, &child) ||
         !CBB_add_bytes(&child, kZero, padding_len) ||
         !ssl_add_message_cbb(ssl, cbb.get())) {
@@ -1517,8 +1518,8 @@
   // 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) &&
+         (!ssl->s3->alpn_selected.empty() ||
+          !ssl->s3->next_proto_negotiated.empty()) &&
          hs->new_cipher->algorithm_mkey == SSL_kECDHE &&
          hs->new_cipher->algorithm_mac == SSL_AEAD;
 }
@@ -1664,18 +1665,16 @@
 
   ssl->method->on_handshake_complete(ssl);
 
-  SSL_SESSION_free(ssl->s3->established_session);
   if (ssl->session != NULL) {
     SSL_SESSION_up_ref(ssl->session);
-    ssl->s3->established_session = ssl->session;
+    ssl->s3->established_session.reset(ssl->session);
   } else {
     // We make a copy of the session in order to maintain the immutability
     // of the new established_session due to False Start. The caller may
     // have taken a reference to the temporary session.
     ssl->s3->established_session =
-        SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_DUP_ALL)
-        .release();
-    if (ssl->s3->established_session == NULL) {
+        SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_DUP_ALL);
+    if (!ssl->s3->established_session) {
       return ssl_hs_error;
     }
     // Renegotiations do not participate in session resumption.
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 7b282d8..d346875 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -1392,8 +1392,7 @@
     return ssl_hs_error;
   }
 
-  if (!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
-                &ssl->s3->next_proto_negotiated_len)) {
+  if (!ssl->s3->next_proto_negotiated.CopyFrom(selected_protocol)) {
     return ssl_hs_error;
   }
 
@@ -1510,12 +1509,11 @@
     ssl->ctx->x509_method->session_clear(hs->new_session.get());
   }
 
-  SSL_SESSION_free(ssl->s3->established_session);
   if (ssl->session != NULL) {
     SSL_SESSION_up_ref(ssl->session);
-    ssl->s3->established_session = ssl->session;
+    ssl->s3->established_session.reset(ssl->session);
   } else {
-    ssl->s3->established_session = hs->new_session.release();
+    ssl->s3->established_session = std::move(hs->new_session);
     ssl->s3->established_session->not_resumable = 0;
   }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 174445a..7e1801a 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -746,10 +746,10 @@
 struct DTLS1_BITMAP {
   // map is a bit mask of the last 64 sequence numbers. Bit
   // |1<<i| corresponds to |max_seq_num - i|.
-  uint64_t map;
+  uint64_t map = 0;
   // max_seq_num is the largest sequence number seen so far as a 64-bit
   // integer.
-  uint64_t max_seq_num;
+  uint64_t max_seq_num = 0;
 };
 
 
@@ -998,9 +998,6 @@
 // in a handshake message for |ssl|.
 size_t ssl_max_handshake_message_len(const SSL *ssl);
 
-// dtls_clear_incoming_messages releases all buffered incoming messages.
-void dtls_clear_incoming_messages(SSL *ssl);
-
 // tls_can_accept_handshake_data returns whether |ssl| is able to accept more
 // data into handshake buffer.
 bool tls_can_accept_handshake_data(const SSL *ssl, uint8_t *out_alert);
@@ -1014,10 +1011,17 @@
 bool dtls_has_unprocessed_handshake_data(const SSL *ssl);
 
 struct DTLS_OUTGOING_MESSAGE {
-  uint8_t *data;
-  uint32_t len;
-  uint16_t epoch;
-  char is_ccs;
+  DTLS_OUTGOING_MESSAGE() {}
+  DTLS_OUTGOING_MESSAGE(const DTLS_OUTGOING_MESSAGE &) = delete;
+  DTLS_OUTGOING_MESSAGE &operator=(const DTLS_OUTGOING_MESSAGE &) = delete;
+  ~DTLS_OUTGOING_MESSAGE() { Clear(); }
+
+  void Clear();
+
+  uint8_t *data = nullptr;
+  uint32_t len = 0;
+  uint16_t epoch = 0;
+  bool is_ccs = false;
 };
 
 // dtls_clear_outgoing_messages releases all buffered outgoing messages.
@@ -1036,8 +1040,57 @@
 
 // Transport buffers.
 
-// ssl_read_buffer returns the current read buffer.
-Span<uint8_t> ssl_read_buffer(SSL *ssl);
+class SSLBuffer {
+ public:
+  SSLBuffer() {}
+  ~SSLBuffer() { Clear(); }
+
+  SSLBuffer(const SSLBuffer &) = delete;
+  SSLBuffer &operator=(const SSLBuffer &) = delete;
+
+  uint8_t *data() { return buf_ + offset_; }
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+  size_t cap() const { return cap_; }
+
+  Span<uint8_t> span() { return MakeSpan(data(), size()); }
+
+  Span<uint8_t> remaining() {
+    return MakeSpan(data() + size(), cap() - size());
+  }
+
+  // Clear releases the buffer.
+  void Clear();
+
+  // EnsureCap ensures the buffer has capacity at least |new_cap|, aligned such
+  // that data written after |header_len| is aligned to a
+  // |SSL3_ALIGN_PAYLOAD|-byte boundary. It returns true on success and false
+  // on error.
+  bool EnsureCap(size_t header_len, size_t new_cap);
+
+  // DidWrite extends the buffer by |len|. The caller must have filled in to
+  // this point.
+  void DidWrite(size_t len);
+
+  // Consume consumes |len| bytes from the front of the buffer.  The memory
+  // consumed will remain valid until the next call to |DiscardConsumed| or
+  // |Clear|.
+  void Consume(size_t len);
+
+  // DiscardConsumed discards the consumed bytes from the buffer. If the buffer
+  // is now empty, it releases memory used by it.
+  void DiscardConsumed();
+
+ private:
+  // buf_ is the memory allocated for this buffer.
+  uint8_t *buf_ = nullptr;
+  // offset_ is the offset into |buf_| which the buffer contents start at.
+  uint16_t offset_ = 0;
+  // size_ is the size of the buffer contents from |buf_| + |offset_|.
+  uint16_t size_ = 0;
+  // cap_ is how much memory beyond |buf_| + |offset_| is available.
+  uint16_t cap_ = 0;
+};
 
 // 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
@@ -1048,49 +1101,18 @@
 // non-empty.
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len);
 
-// ssl_read_buffer_consume consumes |len| bytes from the read buffer. It
-// advances the data pointer and decrements the length. The memory consumed will
-// remain valid until the next call to |ssl_read_buffer_extend| or it is
-// discarded with |ssl_read_buffer_discard|.
-void ssl_read_buffer_consume(SSL *ssl, size_t len);
-
-// ssl_read_buffer_discard discards the consumed bytes from the read buffer. If
-// the buffer is now empty, it releases memory used by it.
-void ssl_read_buffer_discard(SSL *ssl);
-
-// ssl_read_buffer_clear releases all memory associated with the read buffer and
-// zero-initializes it.
-void ssl_read_buffer_clear(SSL *ssl);
-
-// ssl_handle_open_record handles the result of passing |ssl_read_buffer| to a
-// record-processing function. If |ret| is a success or if the caller should
-// retry, it returns one and sets |*out_retry|. Otherwise, it returns <= 0.
+// ssl_handle_open_record handles the result of passing |ssl->s3->read_buffer|
+// to a record-processing function. If |ret| is a success or if the caller
+// should retry, it returns one and sets |*out_retry|. Otherwise, it returns <=
+// 0.
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert);
 
-// ssl_write_buffer_is_pending returns one if the write buffer has pending data
-// and zero if is empty.
-int ssl_write_buffer_is_pending(const SSL *ssl);
-
-// ssl_write_buffer_init initializes the write buffer. On success, it sets
-// |*out_ptr| to the start of the write buffer with space for up to |max_len|
-// bytes. It returns one on success and zero on failure. Call
-// |ssl_write_buffer_set_len| to complete initialization.
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len);
-
-// ssl_write_buffer_set_len is called after |ssl_write_buffer_init| to complete
-// initialization after |len| bytes are written to the buffer.
-void ssl_write_buffer_set_len(SSL *ssl, size_t len);
-
 // ssl_write_buffer_flush flushes the write buffer to the transport. It returns
 // one on success and <= 0 on error. For DTLS, whether or not the write
 // succeeds, the write buffer will be cleared.
 int ssl_write_buffer_flush(SSL *ssl);
 
-// ssl_write_buffer_clear releases all memory associated with the write buffer
-// and zero-initializes it.
-void ssl_write_buffer_clear(SSL *ssl);
-
 
 // Certificate functions.
 
@@ -1468,10 +1490,7 @@
   uint16_t early_data_written = 0;
 };
 
-SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl);
-
-// ssl_handshake_free releases all memory associated with |hs|.
-void ssl_handshake_free(SSL_HANDSHAKE *hs);
+UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl);
 
 // ssl_check_message_type checks if |msg| has type |type|. If so it returns
 // one. Otherwise, it sends an alert and returns zero.
@@ -1556,6 +1575,10 @@
     SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context);
 
+// ssl_is_alpn_protocol_allowed returns whether |protocol| is a valid server
+// selection for |ssl|'s client preferences.
+bool ssl_is_alpn_protocol_allowed(const SSL *ssl, Span<const uint8_t> protocol);
+
 // ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
 // true on successful negotiation or if nothing was negotiated. It returns false
 // and sets |*out_alert| to an alert on error.
@@ -2134,17 +2157,6 @@
   bool ed25519_enabled:1;
 };
 
-struct SSL3_BUFFER {
-  // buf is the memory allocated for this buffer.
-  uint8_t *buf;
-  // offset is the offset into |buf| which the buffer contents start at.
-  uint16_t offset;
-  // len is the length of the buffer contents from |buf| + |offset|.
-  uint16_t len;
-  // cap is how much memory beyond |buf| + |offset| is available.
-  uint16_t cap;
-};
-
 // An ssl_shutdown_t describes the shutdown state of one end of the connection,
 // whether it is alive or has been shutdown via close_notify or fatal alert.
 enum ssl_shutdown_t {
@@ -2154,55 +2166,65 @@
 };
 
 struct SSL3_STATE {
-  uint8_t read_sequence[8];
-  uint8_t write_sequence[8];
+  static constexpr bool kAllowUniquePtr = true;
 
-  uint8_t server_random[SSL3_RANDOM_SIZE];
-  uint8_t client_random[SSL3_RANDOM_SIZE];
+  SSL3_STATE();
+  ~SSL3_STATE();
+
+  uint8_t read_sequence[8] = {0};
+  uint8_t write_sequence[8] = {0};
+
+  uint8_t server_random[SSL3_RANDOM_SIZE] = {0};
+  uint8_t client_random[SSL3_RANDOM_SIZE] = {0};
 
   // read_buffer holds data from the transport to be processed.
-  SSL3_BUFFER read_buffer;
+  SSLBuffer read_buffer;
   // write_buffer holds data to be written to the transport.
-  SSL3_BUFFER write_buffer;
+  SSLBuffer write_buffer;
 
   // pending_app_data is the unconsumed application data. It points into
   // |read_buffer|.
   Span<uint8_t> pending_app_data;
 
   // partial write - check the numbers match
-  unsigned int wnum;  // number of bytes sent so far
-  int wpend_tot;      // number bytes written
-  int wpend_type;
-  int wpend_ret;  // number of bytes submitted
-  const uint8_t *wpend_buf;
+  unsigned int wnum = 0;  // number of bytes sent so far
+  int wpend_tot = 0;      // number bytes written
+  int wpend_type = 0;
+  int wpend_ret = 0;  // number of bytes submitted
+  const uint8_t *wpend_buf = nullptr;
 
   // read_shutdown is the shutdown state for the read half of the connection.
-  enum ssl_shutdown_t read_shutdown;
+  enum ssl_shutdown_t read_shutdown = ssl_shutdown_none;
 
   // write_shutdown is the shutdown state for the write half of the connection.
-  enum ssl_shutdown_t write_shutdown;
+  enum ssl_shutdown_t write_shutdown = ssl_shutdown_none;
 
   // read_error, if |read_shutdown| is |ssl_shutdown_error|, is the error for
   // the receive half of the connection.
-  ERR_SAVE_STATE *read_error;
+  UniquePtr<ERR_SAVE_STATE> read_error;
 
-  int alert_dispatch;
+  int alert_dispatch = 0;
 
-  int total_renegotiations;
+  int total_renegotiations = 0;
+
+  // This holds a variable that indicates what we were doing when a 0 or -1 is
+  // returned.  This is needed for non-blocking IO so we know what request
+  // needs re-doing when in SSL_accept or SSL_connect
+  int rwstate = SSL_NOTHING;
 
   // early_data_skipped is the amount of early data that has been skipped by the
   // record layer.
-  uint16_t early_data_skipped;
+  uint16_t early_data_skipped = 0;
 
   // empty_record_count is the number of consecutive empty records received.
-  uint8_t empty_record_count;
+  uint8_t empty_record_count = 0;
 
   // warning_alert_count is the number of consecutive warning alerts
   // received.
-  uint8_t warning_alert_count;
+  uint8_t warning_alert_count = 0;
 
   // key_update_count is the number of consecutive KeyUpdates received.
-  uint8_t key_update_count;
+  uint8_t key_update_count = 0;
 
   // skip_early_data instructs the record layer to skip unexpected early data
   // messages when 0RTT is rejected.
@@ -2246,56 +2268,49 @@
   // wpend_pending is true if we have a pending write outstanding.
   bool wpend_pending:1;
 
-  uint8_t send_alert[2];
+  uint8_t send_alert[2] = {0};
+
+  // hs_buf is the buffer of handshake data to process.
+  UniquePtr<BUF_MEM> hs_buf;
 
   // pending_flight is the pending outgoing flight. This is used to flush each
   // handshake flight in a single write. |write_buffer| must be written out
   // before this data.
-  BUF_MEM *pending_flight;
+  UniquePtr<BUF_MEM> pending_flight;
 
   // pending_flight_offset is the number of bytes of |pending_flight| which have
   // been successfully written.
-  uint32_t pending_flight_offset;
+  uint32_t pending_flight_offset = 0;
 
   // aead_read_ctx is the current read cipher state.
-  SSLAEADContext *aead_read_ctx;
+  UniquePtr<SSLAEADContext> aead_read_ctx;
 
   // aead_write_ctx is the current write cipher state.
-  SSLAEADContext *aead_write_ctx;
+  UniquePtr<SSLAEADContext> aead_write_ctx;
 
   // hs is the handshake state for the current handshake or NULL if there isn't
   // one.
-  SSL_HANDSHAKE *hs;
+  UniquePtr<SSL_HANDSHAKE> hs;
 
-  uint8_t write_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t read_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t exporter_secret[EVP_MAX_MD_SIZE];
-  uint8_t early_exporter_secret[EVP_MAX_MD_SIZE];
-  uint8_t write_traffic_secret_len;
-  uint8_t read_traffic_secret_len;
-  uint8_t exporter_secret_len;
-  uint8_t early_exporter_secret_len;
+  uint8_t write_traffic_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t read_traffic_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t exporter_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t early_exporter_secret[EVP_MAX_MD_SIZE] = {0};
+  uint8_t write_traffic_secret_len = 0;
+  uint8_t read_traffic_secret_len = 0;
+  uint8_t exporter_secret_len = 0;
+  uint8_t early_exporter_secret_len = 0;
 
   // Connection binding to prevent renegotiation attacks
-  uint8_t previous_client_finished[12];
-  uint8_t previous_client_finished_len;
-  uint8_t previous_server_finished_len;
-  uint8_t previous_server_finished[12];
-
-  // State pertaining to the pending handshake.
-  //
-  // TODO(davidben): Move everything not needed after the handshake completes to
-  // |hs| and remove this.
-  struct {
-    uint8_t new_mac_secret_len;
-    uint8_t new_key_len;
-    uint8_t new_fixed_iv_len;
-  } tmp;
+  uint8_t previous_client_finished[12] = {0};
+  uint8_t previous_client_finished_len = 0;
+  uint8_t previous_server_finished_len = 0;
+  uint8_t previous_server_finished[12] = {0};
 
   // established_session is the session established by the connection. This
   // session is only filled upon the completion of the handshake and is
   // immutable.
-  SSL_SESSION *established_session;
+  UniquePtr<SSL_SESSION> established_session;
 
   // Next protocol negotiation. For the client, this is the protocol that we
   // sent in NextProtocol and is set when handling ServerHello extensions.
@@ -2303,8 +2318,7 @@
   // For a server, this is the client's selected_protocol from NextProtocol and
   // is set when handling the NextProtocol message, before the Finished
   // message.
-  uint8_t *next_proto_negotiated;
-  size_t next_proto_negotiated_len;
+  Array<uint8_t> next_proto_negotiated;
 
   // ALPN information
   // (we are in the process of transitioning from NPN to ALPN.)
@@ -2312,22 +2326,21 @@
   // In a server these point to the selected ALPN protocol after the
   // ClientHello has been processed. In a client these contain the protocol
   // that the server selected once the ServerHello has been processed.
-  uint8_t *alpn_selected;
-  size_t alpn_selected_len;
+  Array<uint8_t> alpn_selected;
 
   // hostname, on the server, is the value of the SNI extension.
-  char *hostname;
+  UniquePtr<char> hostname;
 
   // For a server:
   //     If |tlsext_channel_id_valid| is true, then this contains the
   //     verified Channel ID from the client: a P256 point, (x,y), where
   //     each are big-endian values.
-  uint8_t tlsext_channel_id[64];
+  uint8_t tlsext_channel_id[64] = {0};
 
   // ticket_age_skew is the difference, in seconds, between the client-sent
   // ticket age and the server-computed value in TLS 1.3 server connections
   // which resumed a session.
-  int32_t ticket_age_skew;
+  int32_t ticket_age_skew = 0;
 };
 
 // lengths of messages
@@ -2351,18 +2364,26 @@
 
 // An hm_fragment is an incoming DTLS message, possibly not yet assembled.
 struct hm_fragment {
+  static constexpr bool kAllowUniquePtr = true;
+
+  hm_fragment() {}
+  hm_fragment(const hm_fragment &) = delete;
+  hm_fragment &operator=(const hm_fragment &) = delete;
+
+  ~hm_fragment();
+
   // type is the type of the message.
-  uint8_t type;
+  uint8_t type = 0;
   // seq is the sequence number of this message.
-  uint16_t seq;
+  uint16_t seq = 0;
   // msg_len is the length of the message body.
-  uint32_t msg_len;
+  uint32_t msg_len = 0;
   // data is a pointer to the message, including message header. It has length
   // |DTLS1_HM_HEADER_LENGTH| + |msg_len|.
-  uint8_t *data;
+  uint8_t *data = nullptr;
   // reassembly is a bitmask of |msg_len| bits corresponding to which parts of
   // the message have been received. It is NULL if the message is complete.
-  uint8_t *reassembly;
+  uint8_t *reassembly = nullptr;
 };
 
 struct OPENSSL_timeval {
@@ -2371,6 +2392,11 @@
 };
 
 struct DTLS1_STATE {
+  static constexpr bool kAllowUniquePtr = true;
+
+  DTLS1_STATE();
+  ~DTLS1_STATE();
+
   // has_change_cipher_spec is true if we have received a ChangeCipherSpec from
   // the peer in this epoch.
   bool has_change_cipher_spec:1;
@@ -2385,54 +2411,54 @@
   // peer sent the final flight.
   bool flight_has_reply:1;
 
-  uint8_t cookie[DTLS1_COOKIE_LENGTH];
-  size_t cookie_len;
+  uint8_t cookie[DTLS1_COOKIE_LENGTH] = {0};
+  size_t cookie_len = 0;
 
   // The current data and handshake epoch.  This is initially undefined, and
   // starts at zero once the initial handshake is completed.
-  uint16_t r_epoch;
-  uint16_t w_epoch;
+  uint16_t r_epoch = 0;
+  uint16_t w_epoch = 0;
 
   // records being received in the current epoch
   DTLS1_BITMAP bitmap;
 
-  uint16_t handshake_write_seq;
-  uint16_t handshake_read_seq;
+  uint16_t handshake_write_seq = 0;
+  uint16_t handshake_read_seq = 0;
 
   // save last sequence number for retransmissions
-  uint8_t last_write_sequence[8];
-  SSLAEADContext *last_aead_write_ctx;
+  uint8_t last_write_sequence[8] = {0};
+  UniquePtr<SSLAEADContext> last_aead_write_ctx;
 
   // incoming_messages is a ring buffer of incoming handshake messages that have
   // yet to be processed. The front of the ring buffer is message number
   // |handshake_read_seq|, at position |handshake_read_seq| %
   // |SSL_MAX_HANDSHAKE_FLIGHT|.
-  hm_fragment *incoming_messages[SSL_MAX_HANDSHAKE_FLIGHT];
+  UniquePtr<hm_fragment> incoming_messages[SSL_MAX_HANDSHAKE_FLIGHT];
 
   // outgoing_messages is the queue of outgoing messages from the last handshake
   // flight.
   DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT];
-  uint8_t outgoing_messages_len;
+  uint8_t outgoing_messages_len = 0;
 
   // outgoing_written is the number of outgoing messages that have been
   // written.
-  uint8_t outgoing_written;
+  uint8_t outgoing_written = 0;
   // outgoing_offset is the number of bytes of the next outgoing message have
   // been written.
-  uint32_t outgoing_offset;
+  uint32_t outgoing_offset = 0;
 
-  unsigned int mtu;  // max DTLS packet size
+  unsigned mtu = 0;  // max DTLS packet size
 
   // num_timeouts is the number of times the retransmit timer has fired since
   // the last time it was reset.
-  unsigned int num_timeouts;
+  unsigned num_timeouts = 0;
 
   // Indicates when the last handshake msg or heartbeat sent will
   // timeout.
-  struct OPENSSL_timeval next_timeout;
+  struct OPENSSL_timeval next_timeout = {0, 0};
 
   // timeout_duration_ms is the timeout duration in milliseconds.
-  unsigned timeout_duration_ms;
+  unsigned timeout_duration_ms = 0;
 };
 
 // SSLConnection backs the public |SSL| type. Due to compatibility constraints,
@@ -2472,8 +2498,6 @@
   // progress.
   enum ssl_hs_wait_t (*do_handshake)(SSL_HANDSHAKE *hs);
 
-  BUF_MEM *init_buf;  // buffer used during init
-
   SSL3_STATE *s3;   // SSLv3 variables
   DTLS1_STATE *d1;  // DTLSv1 variables
 
@@ -2493,11 +2517,6 @@
   // This is used to hold the server certificate used
   CERT *cert;
 
-  // This holds a variable that indicates what we were doing when a 0 or -1 is
-  // returned.  This is needed for non-blocking IO so we know what request
-  // needs re-doing when in SSL_accept or SSL_connect
-  int rwstate;
-
   // initial_timeout_duration_ms is the default DTLS timeout duration in
   // milliseconds. It's used to initialize the timer any time it's restarted.
   unsigned initial_timeout_duration_ms;
@@ -2601,7 +2620,6 @@
 };
 
 // From draft-ietf-tls-tls13-18, used in determining PSK modes.
-#define SSL_PSK_KE     0x0
 #define SSL_PSK_DHE_KE 0x1
 
 // From draft-ietf-tls-tls13-16, used in determining whether to respond with a
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 1459085..ede4ba7 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -137,9 +137,9 @@
   // We'll never add a flight while in the process of writing it out.
   assert(ssl->s3->pending_flight_offset == 0);
 
-  if (ssl->s3->pending_flight == NULL) {
-    ssl->s3->pending_flight = BUF_MEM_new();
-    if (ssl->s3->pending_flight == NULL) {
+  if (ssl->s3->pending_flight == nullptr) {
+    ssl->s3->pending_flight.reset(BUF_MEM_new());
+    if (ssl->s3->pending_flight == nullptr) {
       return false;
     }
   }
@@ -152,7 +152,7 @@
   }
 
   size_t len;
-  if (!BUF_MEM_reserve(ssl->s3->pending_flight, new_cap) ||
+  if (!BUF_MEM_reserve(ssl->s3->pending_flight.get(), new_cap) ||
       !tls_seal_record(ssl,
                        (uint8_t *)ssl->s3->pending_flight->data +
                            ssl->s3->pending_flight->length,
@@ -229,7 +229,7 @@
 }
 
 int ssl3_flush_flight(SSL *ssl) {
-  if (ssl->s3->pending_flight == NULL) {
+  if (ssl->s3->pending_flight == nullptr) {
     return 1;
   }
 
@@ -246,10 +246,10 @@
 
   // If there is pending data in the write buffer, it must be flushed out before
   // any new data in pending_flight.
-  if (ssl_write_buffer_is_pending(ssl)) {
+  if (!ssl->s3->write_buffer.empty()) {
     int ret = ssl_write_buffer_flush(ssl);
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
   }
@@ -261,7 +261,7 @@
         ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
         ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset);
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
 
@@ -269,12 +269,11 @@
   }
 
   if (BIO_flush(ssl->wbio) <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     return -1;
   }
 
-  BUF_MEM_free(ssl->s3->pending_flight);
-  ssl->s3->pending_flight = NULL;
+  ssl->s3->pending_flight.reset();
   ssl->s3->pending_flight_offset = 0;
   return 1;
 }
@@ -303,7 +302,7 @@
     return ssl_open_record_partial;
   }
 
-  CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length));
+  CBS v2_client_hello = CBS(ssl->s3->read_buffer.span().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.
@@ -352,9 +351,9 @@
                                1 /* compression length */ + 1 /* compression */;
   ScopedCBB client_hello;
   CBB hello_body, cipher_suites;
-  if (!BUF_MEM_reserve(ssl->init_buf, max_v3_client_hello) ||
-      !CBB_init_fixed(client_hello.get(), (uint8_t *)ssl->init_buf->data,
-                      ssl->init_buf->max) ||
+  if (!BUF_MEM_reserve(ssl->s3->hs_buf.get(), max_v3_client_hello) ||
+      !CBB_init_fixed(client_hello.get(), (uint8_t *)ssl->s3->hs_buf->data,
+                      ssl->s3->hs_buf->max) ||
       !CBB_add_u8(client_hello.get(), SSL3_MT_CLIENT_HELLO) ||
       !CBB_add_u24_length_prefixed(client_hello.get(), &hello_body) ||
       !CBB_add_u16(&hello_body, version) ||
@@ -387,7 +386,7 @@
   // Add the null compression scheme and finish.
   if (!CBB_add_u8(&hello_body, 1) ||
       !CBB_add_u8(&hello_body, 0) ||
-      !CBB_finish(client_hello.get(), NULL, &ssl->init_buf->length)) {
+      !CBB_finish(client_hello.get(), NULL, &ssl->s3->hs_buf->length)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return ssl_open_record_error;
   }
@@ -399,15 +398,15 @@
 
 static bool parse_message(const SSL *ssl, SSLMessage *out,
                           size_t *out_bytes_needed) {
-  if (ssl->init_buf == NULL) {
+  if (!ssl->s3->hs_buf) {
     *out_bytes_needed = 4;
     return false;
   }
 
   CBS cbs;
   uint32_t len;
-  CBS_init(&cbs, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
-           ssl->init_buf->length);
+  CBS_init(&cbs, reinterpret_cast<const uint8_t *>(ssl->s3->hs_buf->data),
+           ssl->s3->hs_buf->length);
   if (!CBS_get_u8(&cbs, &out->type) ||
       !CBS_get_u24(&cbs, &len)) {
     *out_bytes_needed = 4;
@@ -419,7 +418,7 @@
     return false;
   }
 
-  CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
+  CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->s3->hs_buf->data),
            4 + len);
   out->is_v2_hello = ssl->s3->is_v2_hello;
   return true;
@@ -469,16 +468,16 @@
     }
   }
 
-  return ssl->init_buf != NULL && ssl->init_buf->length > msg_len;
+  return ssl->s3->hs_buf && ssl->s3->hs_buf->length > msg_len;
 }
 
 ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed,
                                       uint8_t *out_alert, Span<uint8_t> in) {
   *out_consumed = 0;
   // Re-create the handshake buffer if needed.
-  if (ssl->init_buf == NULL) {
-    ssl->init_buf = BUF_MEM_new();
-    if (ssl->init_buf == NULL) {
+  if (!ssl->s3->hs_buf) {
+    ssl->s3->hs_buf.reset(BUF_MEM_new());
+    if (!ssl->s3->hs_buf) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return ssl_open_record_error;
     }
@@ -552,7 +551,7 @@
   }
 
   // Append the entire handshake record to the buffer.
-  if (!BUF_MEM_append(ssl->init_buf, body.data(), body.size())) {
+  if (!BUF_MEM_append(ssl->s3->hs_buf.get(), body.data(), body.size())) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return ssl_open_record_error;
   }
@@ -563,23 +562,23 @@
 void ssl3_next_message(SSL *ssl) {
   SSLMessage msg;
   if (!ssl3_get_message(ssl, &msg) ||
-      ssl->init_buf == NULL ||
-      ssl->init_buf->length < CBS_len(&msg.raw)) {
+      !ssl->s3->hs_buf ||
+      ssl->s3->hs_buf->length < CBS_len(&msg.raw)) {
     assert(0);
     return;
   }
 
-  OPENSSL_memmove(ssl->init_buf->data, ssl->init_buf->data + CBS_len(&msg.raw),
-                  ssl->init_buf->length - CBS_len(&msg.raw));
-  ssl->init_buf->length -= CBS_len(&msg.raw);
+  OPENSSL_memmove(ssl->s3->hs_buf->data,
+                  ssl->s3->hs_buf->data + CBS_len(&msg.raw),
+                  ssl->s3->hs_buf->length - CBS_len(&msg.raw));
+  ssl->s3->hs_buf->length -= CBS_len(&msg.raw);
   ssl->s3->is_v2_hello = false;
   ssl->s3->has_message = false;
 
   // Post-handshake messages are rare, so release the buffer after every
   // message. During the handshake, |on_handshake_complete| will release it.
-  if (!SSL_in_init(ssl) && ssl->init_buf->length == 0) {
-    BUF_MEM_free(ssl->init_buf);
-    ssl->init_buf = NULL;
+  if (!SSL_in_init(ssl) && ssl->s3->hs_buf->length == 0) {
+    ssl->s3->hs_buf.reset();
   }
 }
 
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index f3f99fa..b925cd7 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -164,30 +164,35 @@
 
 namespace bssl {
 
+SSL3_STATE::SSL3_STATE()
+    : skip_early_data(false),
+      have_version(false),
+      v2_hello_done(false),
+      is_v2_hello(false),
+      has_message(false),
+      initial_handshake_complete(false),
+      session_reused(false),
+      send_connection_binding(false),
+      tlsext_channel_id_valid(false),
+      key_update_pending(false),
+      wpend_pending(false) {}
+
+SSL3_STATE::~SSL3_STATE() {}
+
 bool ssl3_new(SSL *ssl) {
-  UniquePtr<SSLAEADContext> aead_read_ctx =
-      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
-  UniquePtr<SSLAEADContext> aead_write_ctx =
-      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
-  if (!aead_read_ctx || !aead_write_ctx) {
+  UniquePtr<SSL3_STATE> s3 = MakeUnique<SSL3_STATE>();
+  if (!s3) {
     return false;
   }
 
-  SSL3_STATE *s3 = (SSL3_STATE *)OPENSSL_malloc(sizeof *s3);
-  if (s3 == NULL) {
-    return false;
-  }
-  OPENSSL_memset(s3, 0, sizeof *s3);
-
+  s3->aead_read_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
+  s3->aead_write_ctx = SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
   s3->hs = ssl_handshake_new(ssl);
-  if (s3->hs == NULL) {
-    OPENSSL_free(s3);
+  if (!s3->aead_read_ctx || !s3->aead_write_ctx || !s3->hs) {
     return false;
   }
 
-  s3->aead_read_ctx = aead_read_ctx.release();
-  s3->aead_write_ctx = aead_write_ctx.release();
-  ssl->s3 = s3;
+  ssl->s3 = s3.release();
 
   // Set the version to the highest supported version.
   //
@@ -203,20 +208,7 @@
     return;
   }
 
-  ssl_read_buffer_clear(ssl);
-  ssl_write_buffer_clear(ssl);
-
-  ERR_SAVE_STATE_free(ssl->s3->read_error);
-  SSL_SESSION_free(ssl->s3->established_session);
-  ssl_handshake_free(ssl->s3->hs);
-  OPENSSL_free(ssl->s3->next_proto_negotiated);
-  OPENSSL_free(ssl->s3->alpn_selected);
-  OPENSSL_free(ssl->s3->hostname);
-  Delete(ssl->s3->aead_read_ctx);
-  Delete(ssl->s3->aead_write_ctx);
-  BUF_MEM_free(ssl->s3->pending_flight);
-
-  OPENSSL_free(ssl->s3);
+  Delete(ssl->s3);
   ssl->s3 = NULL;
 }
 
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index 7966e6f..285abb3 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -124,9 +124,9 @@
 
 namespace bssl {
 
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len);
 
-int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
+int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
                         int len) {
   assert(ssl_can_write(ssl));
   assert(!ssl->s3->aead_write_ctx->is_null_cipher());
@@ -180,7 +180,7 @@
       nw = n;
     }
 
-    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
+    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &in[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
       return ret;
@@ -199,11 +199,11 @@
   }
 }
 
-static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
+static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *in,
                               unsigned int len) {
   if (ssl->s3->wpend_tot > (int)len ||
       (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) &&
-       ssl->s3->wpend_buf != buf) ||
+       ssl->s3->wpend_buf != in) ||
       ssl->s3->wpend_type != type) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
     return -1;
@@ -218,13 +218,14 @@
 }
 
 // do_ssl3_write writes an SSL record of the given type.
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len) {
   // If there is still data from the previous record, flush it.
   if (ssl->s3->wpend_pending) {
-    return ssl3_write_pending(ssl, type, buf, len);
+    return ssl3_write_pending(ssl, type, in, len);
   }
 
-  if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (len > SSL3_RT_MAX_PLAIN_LENGTH || buf->size() > 0) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -234,7 +235,7 @@
   }
 
   size_t flight_len = 0;
-  if (ssl->s3->pending_flight != NULL) {
+  if (ssl->s3->pending_flight != nullptr) {
     flight_len =
         ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset;
   }
@@ -246,9 +247,7 @@
   }
   max_out += flight_len;
 
-  uint8_t *out;
-  size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out)) {
+  if (!buf->EnsureCap(flight_len + ssl_seal_align_prefix_len(ssl), max_out)) {
     return -1;
   }
 
@@ -256,20 +255,22 @@
   // acknowledgment or 0-RTT key change messages. |pending_flight| must be clear
   // when data is added to |write_buffer| or it will be written in the wrong
   // order.
-  if (ssl->s3->pending_flight != NULL) {
+  if (ssl->s3->pending_flight != nullptr) {
     OPENSSL_memcpy(
-        out, ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
+        buf->remaining().data(),
+        ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
         flight_len);
-    BUF_MEM_free(ssl->s3->pending_flight);
-    ssl->s3->pending_flight = NULL;
+    ssl->s3->pending_flight.reset();
     ssl->s3->pending_flight_offset = 0;
+    buf->DidWrite(flight_len);
   }
 
-  if (!tls_seal_record(ssl, out + flight_len, &ciphertext_len,
-                       max_out - flight_len, type, buf, len)) {
+  size_t ciphertext_len;
+  if (!tls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                       buf->remaining().size(), type, in, len)) {
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, flight_len + ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   // Now that we've made progress on the connection, uncork KeyUpdate
   // acknowledgments.
@@ -278,13 +279,13 @@
   // Memorize arguments so that ssl3_write_pending can detect bad write retries
   // later.
   ssl->s3->wpend_tot = len;
-  ssl->s3->wpend_buf = buf;
+  ssl->s3->wpend_buf = in;
   ssl->s3->wpend_type = type;
   ssl->s3->wpend_ret = len;
   ssl->s3->wpend_pending = true;
 
   // We now just need to write the buffer.
-  return ssl3_write_pending(ssl, type, buf, len);
+  return ssl3_write_pending(ssl, type, in, len);
 }
 
 ssl_open_record_t ssl3_open_app_data(SSL *ssl, Span<uint8_t> *out,
@@ -320,11 +321,11 @@
       return ssl_open_record_error;
     }
 
-    if (ssl->init_buf == NULL) {
-      ssl->init_buf = BUF_MEM_new();
+    if (!ssl->s3->hs_buf) {
+      ssl->s3->hs_buf.reset(BUF_MEM_new());
     }
-    if (ssl->init_buf == NULL ||
-        !BUF_MEM_append(ssl->init_buf, body.data(), body.size())) {
+    if (!ssl->s3->hs_buf ||
+        !BUF_MEM_append(ssl->s3->hs_buf.get(), body.data(), body.size())) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return ssl_open_record_error;
     }
@@ -408,7 +409,7 @@
   ssl->s3->alert_dispatch = 1;
   ssl->s3->send_alert[0] = level;
   ssl->s3->send_alert[1] = desc;
-  if (!ssl_write_buffer_is_pending(ssl)) {
+  if (ssl->s3->write_buffer.empty()) {
     // Nothing is being written out, so the alert may be dispatched
     // immediately.
     return ssl->method->dispatch_alert(ssl);
diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc
index 412df73..da1de93 100644
--- a/src/ssl/ssl_buffer.cc
+++ b/src/ssl/ssl_buffer.cc
@@ -36,17 +36,22 @@
 static_assert((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
               "SSL3_ALIGN_PAYLOAD must be a power of 2");
 
-// ensure_buffer ensures |buf| has capacity at least |cap|, aligned such that
-// data written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
-// boundary. It returns one on success and zero on error.
-static int ensure_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) {
-  if (cap > 0xffff) {
+void SSLBuffer::Clear() {
+  free(buf_);  // Allocated with malloc().
+  buf_ = nullptr;
+  offset_ = 0;
+  size_ = 0;
+  cap_ = 0;
+}
+
+bool SSLBuffer::EnsureCap(size_t header_len, size_t new_cap) {
+  if (new_cap > 0xffff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
-  if (buf->cap >= cap) {
-    return 1;
+  if (cap_ >= new_cap) {
+    return true;
   }
 
   // Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
@@ -54,88 +59,88 @@
   // Since this buffer gets allocated quite frequently and doesn't contain any
   // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
   // avoid zeroing on free.
-  uint8_t *new_buf = (uint8_t *)malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+  uint8_t *new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   // Offset the buffer such that the record body is aligned.
   size_t new_offset =
       (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
 
-  if (buf->buf != NULL) {
-    OPENSSL_memcpy(new_buf + new_offset, buf->buf + buf->offset, buf->len);
-    free(buf->buf);  // Allocated with malloc().
+  if (buf_ != NULL) {
+    OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
+    free(buf_);  // Allocated with malloc().
   }
 
-  buf->buf = new_buf;
-  buf->offset = new_offset;
-  buf->cap = cap;
-  return 1;
+  buf_ = new_buf;
+  offset_ = new_offset;
+  cap_ = new_cap;
+  return true;
 }
 
-static void consume_buffer(SSL3_BUFFER *buf, size_t len) {
-  if (len > buf->len) {
+void SSLBuffer::DidWrite(size_t new_size) {
+  if (new_size > cap() - size()) {
     abort();
   }
-  buf->offset += (uint16_t)len;
-  buf->len -= (uint16_t)len;
-  buf->cap -= (uint16_t)len;
+  size_ += new_size;
 }
 
-static void clear_buffer(SSL3_BUFFER *buf) {
-  free(buf->buf);  // Allocated with malloc().
-  OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
+void SSLBuffer::Consume(size_t len) {
+  if (len > size_) {
+    abort();
+  }
+  offset_ += (uint16_t)len;
+  size_ -= (uint16_t)len;
+  cap_ -= (uint16_t)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);
+void SSLBuffer::DiscardConsumed() {
+  if (size_ == 0) {
+    Clear();
+  }
 }
 
 static int dtls_read_buffer_next_packet(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (buf->len > 0) {
+  if (!buf->empty()) {
     // It is an error to call |dtls_read_buffer_extend| when the read buffer is
     // not empty.
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  // Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int.
-  int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap);
+  // Read a single packet from |ssl->rbio|. |buf->cap()| must fit in an int.
+  int ret = BIO_read(ssl->rbio, buf->data(), static_cast<int>(buf->cap()));
   if (ret <= 0) {
-    ssl->rwstate = SSL_READING;
+    ssl->s3->rwstate = SSL_READING;
     return ret;
   }
-  // |BIO_read| was bound by |buf->cap|, so this cannot overflow.
-  buf->len = (uint16_t)ret;
+  buf->DidWrite(static_cast<size_t>(ret));
   return 1;
 }
 
 static int tls_read_buffer_extend_to(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (len > buf->cap) {
+  if (len > buf->cap()) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return -1;
   }
 
   // Read until the target length is reached.
-  while (buf->len < len) {
+  while (buf->size() < len) {
     // The amount of data to read is bounded by |buf->cap|, which must fit in an
     // int.
-    int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len,
-                       (int)(len - buf->len));
+    int ret = BIO_read(ssl->rbio, buf->data() + buf->size(),
+                       static_cast<int>(len - buf->size()));
     if (ret <= 0) {
-      ssl->rwstate = SSL_READING;
+      ssl->s3->rwstate = SSL_READING;
       return ret;
     }
-    // |BIO_read| was bound by |buf->cap - buf->len|, so this cannot
-    // overflow.
-    buf->len += (uint16_t)ret;
+    buf->DidWrite(static_cast<size_t>(ret));
   }
 
   return 1;
@@ -143,7 +148,7 @@
 
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len) {
   // |ssl_read_buffer_extend_to| implicitly discards any consumed data.
-  ssl_read_buffer_discard(ssl);
+  ssl->s3->read_buffer.DiscardConsumed();
 
   if (SSL_is_dtls(ssl)) {
     static_assert(
@@ -154,7 +159,7 @@
     len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
   }
 
-  if (!ensure_buffer(&ssl->s3->read_buffer, ssl_record_prefix_len(ssl), len)) {
+  if (!ssl->s3->read_buffer.EnsureCap(ssl_record_prefix_len(ssl), len)) {
     return -1;
   }
 
@@ -174,43 +179,20 @@
   if (ret <= 0) {
     // If the buffer was empty originally and remained empty after attempting to
     // extend it, release the buffer until the next attempt.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }
 
-void ssl_read_buffer_consume(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
-
-  consume_buffer(buf, len);
-
-  // The TLS stack never reads beyond the current record, so there will never be
-  // unconsumed data. If read-ahead is ever reimplemented,
-  // |ssl_read_buffer_discard| will require a |memcpy| to shift the excess back
-  // to the front of the buffer, to ensure there is enough space for the next
-  // record.
-  assert(SSL_is_dtls(ssl) || len == 0 || buf->len == 0);
-}
-
-void ssl_read_buffer_discard(SSL *ssl) {
-  if (ssl->s3->read_buffer.len == 0) {
-    ssl_read_buffer_clear(ssl);
-  }
-}
-
-void ssl_read_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->read_buffer);
-}
-
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert) {
   *out_retry = false;
   if (ret != ssl_open_record_partial) {
-    ssl_read_buffer_consume(ssl, consumed);
+    ssl->s3->read_buffer.Consume(consumed);
   }
   if (ret != ssl_open_record_success) {
     // Nothing was returned to the caller, so discard anything marked consumed.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   switch (ret) {
     case ssl_open_record_success:
@@ -243,10 +225,6 @@
 }
 
 
-int ssl_write_buffer_is_pending(const SSL *ssl) {
-  return ssl->s3->write_buffer.len > 0;
-}
-
 static_assert(SSL3_RT_HEADER_LENGTH * 2 +
                       SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
                       SSL3_RT_MAX_PLAIN_LENGTH <=
@@ -258,61 +236,37 @@
                   0xffff,
               "maximum DTLS write buffer is too large");
 
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (buf->buf != NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  if (!ensure_buffer(buf, ssl_seal_align_prefix_len(ssl), max_len)) {
-    return 0;
-  }
-  *out_ptr = buf->buf + buf->offset;
-  return 1;
-}
-
-void ssl_write_buffer_set_len(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (len > buf->cap) {
-    abort();
-  }
-  buf->len = len;
-}
-
 static int tls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
+  SSLBuffer *buf = &ssl->s3->write_buffer;
 
-  while (buf->len > 0) {
-    int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  while (!buf->empty()) {
+    int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
     if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
+      ssl->s3->rwstate = SSL_WRITING;
       return ret;
     }
-    consume_buffer(buf, (size_t)ret);
+    buf->Consume(static_cast<size_t>(ret));
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
 static int dtls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-  if (buf->len == 0) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (buf->empty()) {
     return 1;
   }
 
-  int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
   if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
+    ssl->s3->rwstate = SSL_WRITING;
     // If the write failed, drop the write buffer anyway. Datagram transports
     // can't write half a packet, so the caller is expected to retry from the
     // top.
-    ssl_write_buffer_clear(ssl);
+    buf->Clear();
     return ret;
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
@@ -329,8 +283,4 @@
   }
 }
 
-void ssl_write_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->write_buffer);
-}
-
 }  // namespace bssl
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index e3f8a88..2fc6ffd 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -201,20 +201,19 @@
 void ssl_reset_error_state(SSL *ssl) {
   // Functions which use |SSL_get_error| must reset I/O and error state on
   // entry.
-  ssl->rwstate = SSL_NOTHING;
+  ssl->s3->rwstate = SSL_NOTHING;
   ERR_clear_error();
   ERR_clear_system_error();
 }
 
 void ssl_set_read_error(SSL* ssl) {
   ssl->s3->read_shutdown = ssl_shutdown_error;
-  ERR_SAVE_STATE_free(ssl->s3->read_error);
-  ssl->s3->read_error = ERR_save_state();
+  ssl->s3->read_error.reset(ERR_save_state());
 }
 
 static bool check_read_error(const SSL *ssl) {
   if (ssl->s3->read_shutdown == ssl_shutdown_error) {
-    ERR_restore_state(ssl->s3->read_error);
+    ERR_restore_state(ssl->s3->read_error.get());
     return false;
   }
   return true;
@@ -300,16 +299,16 @@
   // A client may see new sessions on abbreviated handshakes if the server
   // decides to renew the ticket. Once the handshake is completed, it should be
   // inserted into the cache.
-  if (ssl->s3->established_session != ssl->session ||
+  if (ssl->s3->established_session.get() != ssl->session ||
       (!ssl->server && hs->ticket_expected)) {
     if (use_internal_cache) {
-      SSL_CTX_add_session(ctx, ssl->s3->established_session);
+      SSL_CTX_add_session(ctx, ssl->s3->established_session.get());
     }
     if (ctx->new_session_cb != NULL) {
-      SSL_SESSION_up_ref(ssl->s3->established_session);
-      if (!ctx->new_session_cb(ssl, ssl->s3->established_session)) {
+      SSL_SESSION_up_ref(ssl->s3->established_session.get());
+      if (!ctx->new_session_cb(ssl, ssl->s3->established_session.get())) {
         // |new_session_cb|'s return value signals whether it took ownership.
-        SSL_SESSION_free(ssl->s3->established_session);
+        SSL_SESSION_free(ssl->s3->established_session.get());
       }
     }
   }
@@ -717,8 +716,6 @@
     goto err;
   }
 
-  ssl->rwstate = SSL_NOTHING;
-
   CRYPTO_new_ex_data(&ssl->ex_data);
 
   ssl->psk_identity_hint = NULL;
@@ -763,8 +760,6 @@
   BIO_free_all(ssl->rbio);
   BIO_free_all(ssl->wbio);
 
-  BUF_MEM_free(ssl->init_buf);
-
   // add extra stuff
   ssl_cipher_preference_list_free(ssl->cipher_list);
 
@@ -860,7 +855,7 @@
   }
 
   // Run the handshake.
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
 
   bool early_return = false;
   int ret = ssl_run_handshake(hs, &early_return);
@@ -872,8 +867,7 @@
 
   // Destroy the handshake object if the handshake has completely finished.
   if (!early_return) {
-    ssl_handshake_free(ssl->s3->hs);
-    ssl->s3->hs = NULL;
+    ssl->s3->hs.reset();
   }
 
   return 1;
@@ -937,18 +931,18 @@
   // protocol, namely in HTTPS, just before reading the HTTP response. Require
   // the record-layer be idle and avoid complexities of sending a handshake
   // record while an application_data record is being written.
-  if (ssl_write_buffer_is_pending(ssl) ||
+  if (!ssl->s3->write_buffer.empty() ||
       ssl->s3->write_shutdown != ssl_shutdown_none) {
     goto no_renegotiation;
   }
 
   // Begin a new handshake.
-  if (ssl->s3->hs != NULL) {
+  if (ssl->s3->hs != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
   ssl->s3->hs = ssl_handshake_new(ssl);
-  if (ssl->s3->hs == NULL) {
+  if (ssl->s3->hs == nullptr) {
     return 0;
   }
 
@@ -1004,7 +998,7 @@
     uint8_t alert = SSL_AD_DECODE_ERROR;
     size_t consumed = 0;
     auto ret = ssl_open_app_data(ssl, &ssl->s3->pending_app_data, &consumed,
-                                 &alert, ssl_read_buffer(ssl));
+                                 &alert, ssl->s3->read_buffer.span());
     bool retry;
     int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert);
     if (bio_ret <= 0) {
@@ -1029,7 +1023,7 @@
   ssl->s3->pending_app_data =
       ssl->s3->pending_app_data.subspan(static_cast<size_t>(ret));
   if (ssl->s3->pending_app_data.empty()) {
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }
@@ -1125,7 +1119,7 @@
       // time out because the peer never received our close_notify. Report to
       // the caller that the channel has fully shut down.
       if (ssl->s3->read_shutdown == ssl_shutdown_error) {
-        ERR_restore_state(ssl->s3->read_error);
+        ERR_restore_state(ssl->s3->read_error.get());
         return -1;
       }
       ssl->s3->read_shutdown = ssl_shutdown_close_notify;
@@ -1190,7 +1184,7 @@
 }
 
 void SSL_reset_early_data_reject(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs == NULL ||
       hs->wait != ssl_hs_early_data_rejected) {
     abort();
@@ -1242,7 +1236,7 @@
     return SSL_ERROR_SYSCALL;
   }
 
-  switch (ssl->rwstate) {
+  switch (ssl->s3->rwstate) {
     case SSL_PENDING_SESSION:
       return SSL_ERROR_PENDING_SESSION;
 
@@ -1863,7 +1857,7 @@
     return ssl->tlsext_hostname;
   }
 
-  return ssl->s3->hostname;
+  return ssl->s3->hostname.get();
 }
 
 int SSL_get_servername_type(const SSL *ssl) {
@@ -1996,8 +1990,8 @@
 
 void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data,
                                     unsigned *out_len) {
-  *out_data = ssl->s3->next_proto_negotiated;
-  *out_len = ssl->s3->next_proto_negotiated_len;
+  *out_data = ssl->s3->next_proto_negotiated.data();
+  *out_len = ssl->s3->next_proto_negotiated.size();
 }
 
 void SSL_CTX_set_next_protos_advertised_cb(
@@ -2054,8 +2048,8 @@
     *out_data = ssl->s3->hs->early_session->early_alpn;
     *out_len = ssl->s3->hs->early_session->early_alpn_len;
   } else {
-    *out_data = ssl->s3->alpn_selected;
-    *out_len = ssl->s3->alpn_selected_len;
+    *out_data = ssl->s3->alpn_selected.data();
+    *out_len = ssl->s3->alpn_selected.size();
   }
 }
 
@@ -2296,7 +2290,7 @@
   return CRYPTO_get_ex_data(&ctx->ex_data, idx);
 }
 
-int SSL_want(const SSL *ssl) { return ssl->rwstate; }
+int SSL_want(const SSL *ssl) { return ssl->s3->rwstate; }
 
 void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx,
                                   RSA *(*cb)(SSL *ssl, int is_export,
@@ -2434,7 +2428,7 @@
   // This returns false once all the handshake state has been finalized, to
   // allow callbacks and getters based on SSL_in_init to return the correct
   // values.
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   return hs != nullptr && !hs->handshake_finalized;
 }
 
@@ -2547,7 +2541,7 @@
 }
 
 const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs == NULL) {
     return NULL;
   }
@@ -2574,23 +2568,12 @@
   // In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
   // established session to be offered the next time around. wpa_supplicant
   // depends on this behavior, so emulate it.
-  SSL_SESSION *session = NULL;
+  UniquePtr<SSL_SESSION> session;
   if (!ssl->server && ssl->s3->established_session != NULL) {
-    session = ssl->s3->established_session;
-    SSL_SESSION_up_ref(session);
+    session.reset(ssl->s3->established_session.get());
+    SSL_SESSION_up_ref(session.get());
   }
 
-  // TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and
-  // |SSL_clear| because it is per-connection state rather than configuration
-  // state. Per-connection state should be on |ssl->s3| and |ssl->d1| so it is
-  // naturally reset at the right points between |SSL_new|, |SSL_clear|, and
-  // |ssl3_new|.
-
-  ssl->rwstate = SSL_NOTHING;
-
-  BUF_MEM_free(ssl->init_buf);
-  ssl->init_buf = NULL;
-
   // The ssl->d1->mtu is simultaneously configuration (preserved across
   // clear) and connection-specific state (gets reset).
   //
@@ -2602,7 +2585,6 @@
 
   ssl->method->ssl_free(ssl);
   if (!ssl->method->ssl_new(ssl)) {
-    SSL_SESSION_free(session);
     return 0;
   }
 
@@ -2610,9 +2592,8 @@
     ssl->d1->mtu = mtu;
   }
 
-  if (session != NULL) {
-    SSL_set_session(ssl, session);
-    SSL_SESSION_free(session);
+  if (session != nullptr) {
+    SSL_set_session(ssl, session.get());
   }
 
   return 1;
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index ea3c53f..34e7b31 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -999,9 +999,9 @@
   // we return the intermediate session, either |session| (for resumption) or
   // |new_session| if doing a full handshake.
   if (!SSL_in_init(ssl)) {
-    return ssl->s3->established_session;
+    return ssl->s3->established_session.get();
   }
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs->early_session) {
     return hs->early_session.get();
   }
diff --git a/src/ssl/ssl_stat.cc b/src/ssl/ssl_stat.cc
index 37e0324..01153e9 100644
--- a/src/ssl/ssl_stat.cc
+++ b/src/ssl/ssl_stat.cc
@@ -89,12 +89,12 @@
 
 
 const char *SSL_state_string_long(const SSL *ssl) {
-  if (ssl->s3->hs == NULL) {
+  if (ssl->s3->hs == nullptr) {
     return "SSL negotiation finished successfully";
   }
 
-  return ssl->server ? ssl_server_handshake_state(ssl->s3->hs)
-                     : ssl_client_handshake_state(ssl->s3->hs);
+  return ssl->server ? ssl_server_handshake_state(ssl->s3->hs.get())
+                     : ssl_client_handshake_state(ssl->s3->hs.get());
 }
 
 const char *SSL_state_string(const SSL *ssl) {
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 5e9cde1..ab50a57 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -75,10 +75,7 @@
     {TLS1_VERSION, VersionParam::is_tls, "TLS1"},
     {TLS1_1_VERSION, VersionParam::is_tls, "TLS1_1"},
     {TLS1_2_VERSION, VersionParam::is_tls, "TLS1_2"},
-// TLS 1.3 requires RSA-PSS, which is disabled for Android system builds.
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {TLS1_3_VERSION, VersionParam::is_tls, "TLS1_3"},
-#endif
     {DTLS1_VERSION, VersionParam::is_dtls, "DTLS1"},
     {DTLS1_2_VERSION, VersionParam::is_dtls, "DTLS1_2"},
 };
@@ -1974,8 +1971,6 @@
       0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00,
       0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
       0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}},
-  // This test assumes RSA-PSS, which is disabled for Android system builds.
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {TLS1_2_VERSION,
      {0x16, 0x03, 0x01, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x8a, 0x03, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1990,7 +1985,6 @@
       0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00,
       0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00,
       0x17, 0x00, 0x18}},
-#endif
     // TODO(davidben): Add a change detector for TLS 1.3 once the spec and our
     // implementation has settled enough that it won't change.
   };
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index e1578bd..1298a10 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -316,68 +316,65 @@
   return 1;
 }
 
-static int tls1_setup_key_block(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (!hs->key_block.empty()) {
-    return 1;
-  }
-
-  SSL_SESSION *session = ssl->session;
-  if (hs->new_session) {
-    session = hs->new_session.get();
-  }
-
+static bool get_key_block_lengths(const SSL *ssl, size_t *out_mac_secret_len,
+                                  size_t *out_key_len, size_t *out_iv_len,
+                                  const SSL_CIPHER *cipher) {
   const EVP_AEAD *aead = NULL;
-  size_t mac_secret_len, fixed_iv_len;
-  if (session->cipher == NULL ||
-      !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
-                               session->cipher, ssl_protocol_version(ssl),
-                               SSL_is_dtls(ssl))) {
+  if (!ssl_cipher_get_evp_aead(&aead, out_mac_secret_len, out_iv_len, cipher,
+                               ssl_protocol_version(ssl), SSL_is_dtls(ssl))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
-    return 0;
+    return false;
   }
-  size_t key_len = EVP_AEAD_key_length(aead);
-  if (mac_secret_len > 0) {
+
+  *out_key_len = EVP_AEAD_key_length(aead);
+  if (*out_mac_secret_len > 0) {
     // For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
     // key length reported by |EVP_AEAD_key_length| will include the MAC key
     // bytes and initial implicit IV.
-    if (key_len < mac_secret_len + fixed_iv_len) {
+    if (*out_key_len < *out_mac_secret_len + *out_iv_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
-    key_len -= mac_secret_len + fixed_iv_len;
+    *out_key_len -= *out_mac_secret_len + *out_iv_len;
   }
 
-  assert(mac_secret_len < 256);
-  assert(key_len < 256);
-  assert(fixed_iv_len < 256);
+  return true;
+}
 
-  ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
-  ssl->s3->tmp.new_key_len = (uint8_t)key_len;
-  ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+static bool setup_key_block(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!hs->key_block.empty()) {
+    return true;
+  }
 
+  size_t mac_secret_len, key_len, fixed_iv_len;
   Array<uint8_t> key_block;
-  if (!key_block.Init(SSL_get_key_block_len(ssl)) ||
+  if (!get_key_block_lengths(ssl, &mac_secret_len, &key_len, &fixed_iv_len,
+                             hs->new_cipher) ||
+      !key_block.Init(2 * (mac_secret_len + key_len + fixed_iv_len)) ||
       !SSL_generate_key_block(ssl, key_block.data(), key_block.size())) {
-    return 0;
+    return false;
   }
 
   hs->key_block = std::move(key_block);
-  return 1;
+  return true;
 }
 
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs,
                              evp_aead_direction_t direction) {
   SSL *const ssl = hs->ssl;
   // Ensure the key block is set up.
-  if (!tls1_setup_key_block(hs)) {
+  size_t mac_secret_len, key_len, iv_len;
+  if (!setup_key_block(hs) ||
+      !get_key_block_lengths(ssl, &mac_secret_len, &key_len, &iv_len,
+                             hs->new_cipher)) {
     return 0;
   }
 
-  size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
-  size_t key_len = ssl->s3->tmp.new_key_len;
-  size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
-  assert((mac_secret_len + key_len + iv_len) * 2 == hs->key_block.size());
+  if ((mac_secret_len + key_len + iv_len) * 2 != hs->key_block.size()) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
 
   Span<const uint8_t> key_block = hs->key_block;
   Span<const uint8_t> mac_secret, key, iv;
@@ -448,9 +445,14 @@
 using namespace bssl;
 
 size_t SSL_get_key_block_len(const SSL *ssl) {
-  return 2 * ((size_t)ssl->s3->tmp.new_mac_secret_len +
-              (size_t)ssl->s3->tmp.new_key_len +
-              (size_t)ssl->s3->tmp.new_fixed_iv_len);
+  size_t mac_secret_len, key_len, fixed_iv_len;
+  if (!get_key_block_lengths(ssl, &mac_secret_len, &key_len, &fixed_iv_len,
+                             SSL_get_current_cipher(ssl))) {
+    ERR_clear_error();
+    return 0;
+  }
+
+  return 2 * (mac_secret_len + key_len + fixed_iv_len);
 }
 
 int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 5d85cb0..31b51c9 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -418,25 +418,15 @@
     // List our preferred algorithms first.
     SSL_SIGN_ED25519,
     SSL_SIGN_ECDSA_SECP256R1_SHA256,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA256,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA256,
 
     // Larger hashes are acceptable.
     SSL_SIGN_ECDSA_SECP384R1_SHA384,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA384,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA384,
 
-    // TODO(davidben): Remove this.
-#if defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_SIGN_ECDSA_SECP521R1_SHA512,
-#endif
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA512,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA512,
 
     // For now, SHA-1 is still accepted but least preferable.
@@ -454,24 +444,18 @@
     // List our preferred algorithms first.
     SSL_SIGN_ED25519,
     SSL_SIGN_ECDSA_SECP256R1_SHA256,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA256,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA256,
 
     // If needed, sign larger hashes.
     //
     // TODO(davidben): Determine which of these may be pruned.
     SSL_SIGN_ECDSA_SECP384R1_SHA384,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA384,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA384,
 
     SSL_SIGN_ECDSA_SECP521R1_SHA512,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     SSL_SIGN_RSA_PSS_SHA512,
-#endif
     SSL_SIGN_RSA_PKCS1_SHA512,
 
     // If the peer supports nothing else, sign with SHA-1.
@@ -640,10 +624,12 @@
   }
 
   // Copy the hostname as a string.
-  if (!CBS_strdup(&host_name, &ssl->s3->hostname)) {
+  char *raw = nullptr;
+  if (!CBS_strdup(&host_name, &raw)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
+  ssl->s3->hostname.reset(raw);
 
   hs->should_ack_sni = true;
   return true;
@@ -862,7 +848,7 @@
   }
 
   // Whether EMS is negotiated may not change on renegotiation.
-  if (ssl->s3->established_session != NULL &&
+  if (ssl->s3->established_session != nullptr &&
       hs->extended_master_secret !=
           !!ssl->s3->established_session->extended_master_secret) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_EMS_MISMATCH);
@@ -1150,7 +1136,7 @@
   assert(!SSL_is_dtls(ssl));
   assert(ssl->ctx->next_proto_select_cb != NULL);
 
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     // 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);
@@ -1172,22 +1158,14 @@
   uint8_t selected_len;
   if (ssl->ctx->next_proto_select_cb(
           ssl, &selected, &selected_len, orig_contents, orig_len,
-          ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) {
+          ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK ||
+      !ssl->s3->next_proto_negotiated.CopyFrom(
+          MakeConstSpan(selected, selected_len))) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
 
-  OPENSSL_free(ssl->s3->next_proto_negotiated);
-  ssl->s3->next_proto_negotiated =
-      (uint8_t *)BUF_memdup(selected, selected_len);
-  if (ssl->s3->next_proto_negotiated == NULL) {
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    return false;
-  }
-
-  ssl->s3->next_proto_negotiated_len = selected_len;
   hs->next_proto_neg_seen = true;
-
   return true;
 }
 
@@ -1388,37 +1366,13 @@
     return false;
   }
 
-  if (!ssl->ctx->allow_unknown_alpn_protos) {
-    // Check that the protocol name is one of the ones we advertised.
-    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);
-    while (CBS_len(&client_protocol_name_list) > 0) {
-      if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
-                                      &client_protocol_name)) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        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 = true;
-        break;
-      }
-    }
-
-    if (!protocol_ok) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
-      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-      return false;
-    }
+  if (!ssl_is_alpn_protocol_allowed(ssl, protocol_name)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    return false;
   }
 
-  if (!CBS_stow(&protocol_name, &ssl->s3->alpn_selected,
-                &ssl->s3->alpn_selected_len)) {
+  if (!ssl->s3->alpn_selected.CopyFrom(protocol_name)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
@@ -1426,6 +1380,34 @@
   return true;
 }
 
+bool ssl_is_alpn_protocol_allowed(const SSL *ssl,
+                                  Span<const uint8_t> protocol) {
+  if (ssl->alpn_client_proto_list == nullptr) {
+    return false;
+  }
+
+  if (ssl->ctx->allow_unknown_alpn_protos) {
+    return true;
+  }
+
+  // Check that the protocol name is one of the ones we advertised.
+  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);
+  while (CBS_len(&client_protocol_name_list) > 0) {
+    if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
+                                    &client_protocol_name)) {
+      return false;
+    }
+
+    if (client_protocol_name == protocol) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
@@ -1470,13 +1452,11 @@
           ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
           CBS_len(&protocol_name_list),
           ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
-    OPENSSL_free(ssl->s3->alpn_selected);
-    ssl->s3->alpn_selected = (uint8_t *)BUF_memdup(selected, selected_len);
-    if (ssl->s3->alpn_selected == NULL) {
+    if (!ssl->s3->alpn_selected.CopyFrom(
+            MakeConstSpan(selected, selected_len))) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       return false;
     }
-    ssl->s3->alpn_selected_len = selected_len;
   }
 
   return true;
@@ -1484,7 +1464,7 @@
 
 static bool ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->s3->alpn_selected == NULL) {
+  if (ssl->s3->alpn_selected.empty()) {
     return true;
   }
 
@@ -1493,8 +1473,8 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list) ||
       !CBB_add_u8_length_prefixed(&proto_list, &proto) ||
-      !CBB_add_bytes(&proto, ssl->s3->alpn_selected,
-                     ssl->s3->alpn_selected_len) ||
+      !CBB_add_bytes(&proto, ssl->s3->alpn_selected.data(),
+                     ssl->s3->alpn_selected.size()) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -1998,11 +1978,19 @@
 
 static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->session == NULL ||
+  if (!ssl->cert->enable_early_data ||
+      // Session must be 0-RTT capable.
+      ssl->session == NULL ||
       ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION ||
       ssl->session->ticket_max_early_data == 0 ||
+      // The second ClientHello never offers early data.
       hs->received_hello_retry_request ||
-      !ssl->cert->enable_early_data) {
+      // In case ALPN preferences changed since this session was established,
+      // avoid reporting a confusing value in |SSL_get0_alpn_selected|.
+      (ssl->session->early_alpn_len != 0 &&
+       !ssl_is_alpn_protocol_allowed(
+           ssl, MakeConstSpan(ssl->session->early_alpn,
+                              ssl->session->early_alpn_len)))) {
     return true;
   }
 
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 1d7b996..d439cb5 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1101,7 +1101,6 @@
 // Connect returns a new socket connected to localhost on |port| or -1 on
 // error.
 static int Connect(uint16_t port) {
-  time_t start_time = time(nullptr);
   for (int af : { AF_INET6, AF_INET }) {
     int sock = socket(af, SOCK_STREAM, 0);
     if (sock == -1) {
@@ -1148,11 +1147,6 @@
   }
 
   PrintSocketError("connect");
-  // TODO(davidben): Remove this logging when https://crbug.com/boringssl/199 is
-  // resolved.
-  fprintf(stderr, "start_time = %lld, end_time = %lld\n",
-          static_cast<long long>(start_time),
-          static_cast<long long>(time(nullptr)));
   return -1;
 }
 
@@ -2201,6 +2195,12 @@
       return false;
     }
 
+    if (is_resume && !is_retry && !config->is_server &&
+        config->expect_no_offer_early_data && SSL_in_early_data(ssl)) {
+      fprintf(stderr, "Client unexpectedly offered early data.\n");
+      return false;
+    }
+
     if (config->handshake_twice) {
       do {
         ret = SSL_do_handshake(ssl);
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 0edbe5c..71b52f2 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -244,14 +244,6 @@
 	hc.incEpoch()
 }
 
-func (hc *halfConn) doKeyUpdate(c *Conn, isOutgoing bool) {
-	side := serverWrite
-	if c.isClient == isOutgoing {
-		side = clientWrite
-	}
-	hc.useTrafficSecret(hc.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
-}
-
 // incSeq increments the sequence number.
 func (hc *halfConn) incSeq(isOutgoing bool) {
 	limit := 0
@@ -737,6 +729,26 @@
 	return b, bb
 }
 
+func (c *Conn) useInTrafficSecret(version uint16, suite *cipherSuite, secret []byte) error {
+	if c.hand.Len() != 0 {
+		return c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+	}
+	side := serverWrite
+	if !c.isClient {
+		side = clientWrite
+	}
+	c.in.useTrafficSecret(version, suite, secret, side)
+	return nil
+}
+
+func (c *Conn) useOutTrafficSecret(version uint16, suite *cipherSuite, secret []byte) {
+	side := serverWrite
+	if c.isClient {
+		side = clientWrite
+	}
+	c.out.useTrafficSecret(version, suite, secret, side)
+}
+
 func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
 RestartReadRecord:
 	if c.isDTLS {
@@ -940,8 +952,11 @@
 			break
 		}
 		if !isResumptionExperiment(c.wireVersion) {
-			err := c.in.changeCipherSpec(c.config)
-			if err != nil {
+			if c.hand.Len() != 0 {
+				c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+				break
+			}
+			if err := c.in.changeCipherSpec(c.config); err != nil {
 				c.in.setErrorLocked(c.sendAlert(err.(alert)))
 			}
 		}
@@ -1522,7 +1537,9 @@
 		if c.config.Bugs.RejectUnsolicitedKeyUpdate {
 			return errors.New("tls: unexpected KeyUpdate message")
 		}
-		c.in.doKeyUpdate(c, false)
+		if err := c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret)); err != nil {
+			return err
+		}
 		if keyUpdate.keyUpdateRequest == keyUpdateRequested {
 			c.keyUpdateRequested = true
 		}
@@ -1554,8 +1571,7 @@
 		return errors.New("tls: received invalid KeyUpdate message")
 	}
 
-	c.in.doKeyUpdate(c, false)
-	return nil
+	return c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret))
 }
 
 func (c *Conn) Renegotiate() error {
@@ -1885,7 +1901,7 @@
 	if err := c.flushHandshake(); err != nil {
 		return err
 	}
-	c.out.doKeyUpdate(c, true)
+	c.useOutTrafficSecret(c.out.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.out.trafficSecret))
 	return nil
 }
 
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index ef6a464..30105a5 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -411,7 +411,7 @@
 		finishedHash.addEntropy(session.masterSecret)
 		finishedHash.Write(helloBytes)
 		earlyTrafficSecret := finishedHash.deriveSecret(earlyTrafficLabel)
-		c.out.useTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret, clientWrite)
+		c.useOutTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret)
 		for _, earlyData := range c.config.Bugs.SendEarlyData {
 			if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil {
 				return err
@@ -754,7 +754,9 @@
 	// traffic key.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret); err != nil {
+		return err
+	}
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -888,7 +890,9 @@
 
 	// Switch to application data keys on read. In particular, any alerts
 	// from the client certificate are read over these keys.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret); err != nil {
+		return err
+	}
 
 	// If we're expecting 0.5-RTT messages from the server, read them
 	// now.
@@ -934,7 +938,7 @@
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -1020,7 +1024,7 @@
 	c.flushHandshake()
 
 	// Switch to application data keys.
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret)
 
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 	return nil
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 0ffb72c..f50772e 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -648,8 +648,7 @@
 		// AcceptAnyBinder is set. See https://crbug.com/115.
 		if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
 			binderToVerify := newClientHello.pskBinders[pskIndex]
-			err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...))
-			if err != nil {
+			if err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...)); err != nil {
 				return err
 			}
 		}
@@ -664,7 +663,9 @@
 		}
 		if encryptedExtensions.extensions.hasEarlyData {
 			earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyTrafficLabel)
-			c.in.useTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret, clientWrite)
+			if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil {
+				return err
+			}
 
 			for _, expectedMsg := range config.Bugs.ExpectEarlyData {
 				if err := c.readRecord(recordTypeApplicationData); err != nil {
@@ -761,7 +762,7 @@
 
 	// Switch to handshake traffic keys.
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret)
 	// Derive handshake traffic read key, but don't switch yet.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 
@@ -902,7 +903,7 @@
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
-	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
+	c.useOutTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret)
 
 	// Send 0.5-RTT messages.
 	for _, halfRTTMsg := range config.Bugs.SendHalfRTTData {
@@ -928,7 +929,9 @@
 	}
 
 	// Switch input stream to handshake traffic keys.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret); err != nil {
+		return err
+	}
 
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
@@ -1032,7 +1035,9 @@
 	hs.writeClientHash(clientFinished.marshal())
 
 	// Switch to application data keys on read.
-	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
+	if err := c.useInTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret); err != nil {
+		return err
+	}
 
 	c.cipherSuite = hs.suite
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index e666404..d34eaa6 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -901,28 +901,20 @@
 // exit first.
 func acceptOrWait(listener *net.TCPListener, waitChan chan error) (net.Conn, error) {
 	type connOrError struct {
-		conn               net.Conn
-		err                error
-		startTime, endTime time.Time
+		conn net.Conn
+		err  error
 	}
 	connChan := make(chan connOrError, 1)
 	go func() {
-		startTime := time.Now()
 		if !*useGDB {
 			listener.SetDeadline(time.Now().Add(*idleTimeout))
 		}
 		conn, err := listener.Accept()
-		endTime := time.Now()
-		connChan <- connOrError{conn, err, startTime, endTime}
+		connChan <- connOrError{conn, err}
 		close(connChan)
 	}()
 	select {
 	case result := <-connChan:
-		if result.err != nil {
-			// TODO(davidben): Remove this logging when
-			// https://crbug.com/boringssl/199 is resolved.
-			fmt.Fprintf(os.Stderr, "acceptOrWait failed, startTime=%v, endTime=%v\n", result.startTime, result.endTime)
-		}
 		return result.conn, result.err
 	case childErr := <-waitChan:
 		waitChan <- childErr
@@ -2761,11 +2753,12 @@
 			},
 		},
 		{
-			// Test the server so there is a large certificate as
-			// well as application data.
+			// Test the TLS 1.2 server so there is a large
+			// unencrypted certificate as well as application data.
 			testType: serverTest,
-			name:     "MaxSendFragment",
+			name:     "MaxSendFragment-TLS12",
 			config: Config{
+				MaxVersion: VersionTLS12,
 				Bugs: ProtocolBugs{
 					MaxReceivePlaintext: 512,
 				},
@@ -2777,11 +2770,50 @@
 			},
 		},
 		{
-			// Test the server so there is a large certificate as
-			// well as application data.
+			// Test the TLS 1.2 server so there is a large
+			// unencrypted certificate as well as application data.
 			testType: serverTest,
-			name:     "MaxSendFragment-TooLarge",
+			name:     "MaxSendFragment-TLS12-TooLarge",
 			config: Config{
+				MaxVersion: VersionTLS12,
+				Bugs: ProtocolBugs{
+					// Ensure that some of the records are
+					// 512.
+					MaxReceivePlaintext: 511,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+			shouldFail:         true,
+			expectedLocalError: "local error: record overflow",
+		},
+		{
+			// Test the TLS 1.3 server so there is a large encrypted
+			// certificate as well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					MaxReceivePlaintext: 512,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+		},
+		{
+			// Test the TLS 1.3 server so there is a large encrypted
+			// certificate as well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment-TLS13-TooLarge",
+			config: Config{
+				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
 					// Ensure that some of the records are
 					// 512.
@@ -11726,6 +11758,28 @@
 		expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
 	})
 
+	// Test that the client does not offer early data if it is incompatible
+	// with ALPN preferences.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-EarlyData-ALPNPreferenceChanged",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			NextProtos:       []string{"foo", "bar"},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-no-offer-early-data",
+			"-on-initial-advertise-alpn", "\x03foo",
+			"-on-resume-advertise-alpn", "\x03bar",
+			"-on-initial-expect-alpn", "foo",
+			"-on-resume-expect-alpn", "bar",
+		},
+	})
+
 	// Test that the server correctly rejects 0-RTT when the previous
 	// session did not allow early data on resumption.
 	testCases = append(testCases, testCase{
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 6df8d2a..3354851 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -121,6 +121,7 @@
   { "-expect-no-session-id", &TestConfig::expect_no_session_id },
   { "-expect-accept-early-data", &TestConfig::expect_accept_early_data },
   { "-expect-reject-early-data", &TestConfig::expect_reject_early_data },
+  { "-expect-no-offer-early-data", &TestConfig::expect_no_offer_early_data },
   { "-no-op-extra-handshake", &TestConfig::no_op_extra_handshake },
   { "-handshake-twice", &TestConfig::handshake_twice },
   { "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9af64bc..b96d9e5 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -90,6 +90,7 @@
   bool expect_early_data_info = false;
   bool expect_accept_early_data = false;
   bool expect_reject_early_data = false;
+  bool expect_no_offer_early_data = false;
   bool use_ticket_callback = false;
   bool renew_ticket = false;
   bool enable_early_data = false;
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index a03c581..b6307ee 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -390,21 +390,21 @@
   }
 
   // Store the negotiated ALPN in the session.
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
-        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+        ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
     if (hs->new_session->early_alpn == NULL) {
       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;
+    hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
   }
 
   if (ssl->early_data_accepted) {
     if (hs->early_session->cipher != hs->new_session->cipher ||
-        hs->early_session->early_alpn_len != ssl->s3->alpn_selected_len ||
-        OPENSSL_memcmp(hs->early_session->early_alpn, ssl->s3->alpn_selected,
-                       ssl->s3->alpn_selected_len) != 0) {
+        MakeConstSpan(hs->early_session->early_alpn,
+                      hs->early_session->early_alpn_len) !=
+            ssl->s3->alpn_selected) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
       return ssl_hs_error;
     }
@@ -781,8 +781,8 @@
     return 1;
   }
 
-  UniquePtr<SSL_SESSION> session(SSL_SESSION_dup(ssl->s3->established_session,
-                                                 SSL_SESSION_INCLUDE_NONAUTH));
+  UniquePtr<SSL_SESSION> session = SSL_SESSION_dup(
+      ssl->s3->established_session.get(), SSL_SESSION_INCLUDE_NONAUTH);
   if (!session) {
     return 0;
   }
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 09437a5..89c9d46 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -371,8 +371,8 @@
       hs->new_session =
           SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
 
-      if (// Early data must be acceptable for this ticket.
-          ssl->cert->enable_early_data &&
+      if (ssl->cert->enable_early_data &&
+          // Early data must be acceptable for this ticket.
           session->ticket_max_early_data != 0 &&
           // The client must have offered early data.
           hs->early_data_offered &&
@@ -381,9 +381,8 @@
           // Custom extensions is incompatible with 0-RTT.
           hs->custom_extensions.received == 0 &&
           // The negotiated ALPN must match the one in the ticket.
-          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->s3->alpn_selected ==
+              MakeConstSpan(session->early_alpn, session->early_alpn_len)) {
         ssl->early_data_accepted = true;
       }
 
@@ -412,14 +411,14 @@
   hs->new_session->cipher = hs->new_cipher;
 
   // Store the initial negotiated ALPN in the session.
-  if (ssl->s3->alpn_selected != NULL) {
+  if (!ssl->s3->alpn_selected.empty()) {
     hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
-        ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
+        ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
     if (hs->new_session->early_alpn == NULL) {
       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;
+    hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
   }
 
   if (ssl->ctx->dos_protection_cb != NULL &&
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 94b9e20..c5b0178 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -73,14 +73,13 @@
   // The handshake should have released its final message.
   assert(!ssl->s3->has_message);
 
-  // During the handshake, |init_buf| is retained. Release if it there is no
+  // During the handshake, |hs_buf| is retained. Release if it there is no
   // excess in it. There may be excess left if there server sent Finished and
   // HelloRequest in the same record.
   //
   // 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;
+  if (ssl->s3->hs_buf && ssl->s3->hs_buf->length == 0) {
+    ssl->s3->hs_buf.reset();
   }
 }
 
@@ -93,17 +92,13 @@
   }
 
   OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
-
-  Delete(ssl->s3->aead_read_ctx);
-  ssl->s3->aead_read_ctx = aead_ctx.release();
+  ssl->s3->aead_read_ctx = std::move(aead_ctx);
   return true;
 }
 
 static bool ssl3_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
   OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
-
-  Delete(ssl->s3->aead_write_ctx);
-  ssl->s3->aead_write_ctx = aead_ctx.release();
+  ssl->s3->aead_write_ctx = std::move(aead_ctx);
   return true;
 }
 
diff --git a/src/util/BUILD.toplevel b/src/util/BUILD.toplevel
index af7bd30..6e569ec 100644
--- a/src/util/BUILD.toplevel
+++ b/src/util/BUILD.toplevel
@@ -46,6 +46,11 @@
     values = {"cpu": "x64_windows"},
 )
 
+config_setting(
+    name = "android",
+    values = {"crosstool_top": "//external:android/crosstool"}
+)
+
 posix_copts = [
     # Assembler option --noexecstack adds .note.GNU-stack to each object to
     # ensure that binaries can be built with non-executable stack.
@@ -120,6 +125,9 @@
     includes = ["src/include"],
     linkopts = select({
         ":mac_x86_64": [],
+        # Android supports pthreads, but does not provide a libpthread
+        # to link against.
+        ":android": [],
         "//conditions:default": ["-lpthread"],
     }),
     visibility = ["//visibility:public"],