external/boringssl: Sync to 7c5728649affe20e2952b11a0aeaf0e7b114aad9.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/68f37b7a3f451aa1ca8c93669c024d01f6270ae8..7c5728649affe20e2952b11a0aeaf0e7b114aad9

This also removes sha256-armv4.S from libcrypto_sources_no_clang; clang
can assemble it now. The other files still need to be there though.

Note this pulls in a fix to a wpa_supplicant regression introduced in
c895d6b1c580258e72e1ed3fcc86d38970ded9e1.

Test: make checkbuild
Test: cts-tradefed run cts -m CtsLibcoreTestCases -a arm64-v8a
Test: cts-tradefed run cts -m CtsLibcoreOkHttpTestCases -a arm64-v8a

Change-Id: Ife1d9ea1c87a0b7b1814b8e3590d6f1eaf721629
diff --git a/src/.gitignore b/src/.gitignore
index a2e3ed2..9f396f6 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -4,3 +4,18 @@
 *.swo
 doc/*.html
 doc/doc.css
+
+util/bot/android_tools
+util/bot/cmake-linux64
+util/bot/cmake-linux64.tar.gz
+util/bot/cmake-mac
+util/bot/cmake-mac.tar.gz
+util/bot/cmake-win32
+util/bot/cmake-win32.zip
+util/bot/golang
+util/bot/gyp
+util/bot/llvm-build
+util/bot/perl-win32
+util/bot/perl-win32.zip
+util/bot/win_toolchain.json
+util/bot/yasm-win32.exe
diff --git a/src/FUZZING.md b/src/FUZZING.md
index f004c27..77c50c1 100644
--- a/src/FUZZING.md
+++ b/src/FUZZING.md
@@ -37,6 +37,7 @@
 | `pkcs8`       | 2048            |
 | `privkey`     | 2048            |
 | `server`      | 4096            |
+| `session`     | 8192            |
 | `spki`        | 1024            |
 | `read_pem`    | 512             |
 | `ssl_ctx_api` | 256             |
diff --git a/src/crypto/bio/bio_test.cc b/src/crypto/bio/bio_test.cc
index 01b93f6..4ae6c6e 100644
--- a/src/crypto/bio/bio_test.cc
+++ b/src/crypto/bio/bio_test.cc
@@ -135,152 +135,6 @@
   return true;
 }
 
-
-// BioReadZeroCopyWrapper is a wrapper around the zero-copy APIs to make
-// testing easier.
-static size_t BioReadZeroCopyWrapper(BIO *bio, uint8_t *data, size_t len) {
-  uint8_t *read_buf;
-  size_t read_buf_offset;
-  size_t available_bytes;
-  size_t len_read = 0;
-
-  do {
-    if (!BIO_zero_copy_get_read_buf(bio, &read_buf, &read_buf_offset,
-                                    &available_bytes)) {
-      return 0;
-    }
-
-    available_bytes = std::min(available_bytes, len - len_read);
-    memmove(data + len_read, read_buf + read_buf_offset, available_bytes);
-
-    BIO_zero_copy_get_read_buf_done(bio, available_bytes);
-
-    len_read += available_bytes;
-  } while (len - len_read > 0 && available_bytes > 0);
-
-  return len_read;
-}
-
-// BioWriteZeroCopyWrapper is a wrapper around the zero-copy APIs to make
-// testing easier.
-static size_t BioWriteZeroCopyWrapper(BIO *bio, const uint8_t *data,
-                                      size_t len) {
-  uint8_t *write_buf;
-  size_t write_buf_offset;
-  size_t available_bytes;
-  size_t len_written = 0;
-
-  do {
-    if (!BIO_zero_copy_get_write_buf(bio, &write_buf, &write_buf_offset,
-                                     &available_bytes)) {
-      return 0;
-    }
-
-    available_bytes = std::min(available_bytes, len - len_written);
-    memmove(write_buf + write_buf_offset, data + len_written, available_bytes);
-
-    BIO_zero_copy_get_write_buf_done(bio, available_bytes);
-
-    len_written += available_bytes;
-  } while (len - len_written > 0 && available_bytes > 0);
-
-  return len_written;
-}
-
-static bool TestZeroCopyBioPairs() {
-  // Test read and write, especially triggering the ring buffer wrap-around.
-  uint8_t bio1_application_send_buffer[1024];
-  uint8_t bio2_application_recv_buffer[1024];
-
-  const size_t kLengths[] = {254, 255, 256, 257, 510, 511, 512, 513};
-
-  // These trigger ring buffer wrap around.
-  const size_t kPartialLengths[] = {0, 1, 2, 3, 128, 255, 256, 257, 511, 512};
-
-  static const size_t kBufferSize = 512;
-
-  srand(1);
-  for (size_t i = 0; i < sizeof(bio1_application_send_buffer); i++) {
-    bio1_application_send_buffer[i] = rand() & 255;
-  }
-
-  // Transfer bytes from bio1_application_send_buffer to
-  // bio2_application_recv_buffer in various ways.
-  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kLengths); i++) {
-    for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(kPartialLengths); j++) {
-      size_t total_write = 0;
-      size_t total_read = 0;
-
-      BIO *bio1, *bio2;
-      if (!BIO_new_bio_pair(&bio1, kBufferSize, &bio2, kBufferSize)) {
-        return false;
-      }
-      bssl::UniquePtr<BIO> bio1_scoper(bio1);
-      bssl::UniquePtr<BIO> bio2_scoper(bio2);
-
-      total_write += BioWriteZeroCopyWrapper(
-          bio1, bio1_application_send_buffer, kLengths[i]);
-
-      // This tests interleaved read/write calls. Do a read between zero copy
-      // write calls.
-      uint8_t *write_buf;
-      size_t write_buf_offset;
-      size_t available_bytes;
-      if (!BIO_zero_copy_get_write_buf(bio1, &write_buf, &write_buf_offset,
-                                       &available_bytes)) {
-        return false;
-      }
-
-      // Free kPartialLengths[j] bytes in the beginning of bio1 write buffer.
-      // This enables ring buffer wrap around for the next write.
-      total_read += BIO_read(bio2, bio2_application_recv_buffer + total_read,
-                             kPartialLengths[j]);
-
-      size_t interleaved_write_len = std::min(kPartialLengths[j],
-                                              available_bytes);
-
-      // Write the data for the interleaved write call. If the buffer becomes
-      // empty after a read, the write offset is normally set to 0. Check that
-      // this does not happen for interleaved read/write and that
-      // |write_buf_offset| is still valid.
-      memcpy(write_buf + write_buf_offset,
-             bio1_application_send_buffer + total_write, interleaved_write_len);
-      if (BIO_zero_copy_get_write_buf_done(bio1, interleaved_write_len)) {
-        total_write += interleaved_write_len;
-      }
-
-      // Do another write in case |write_buf_offset| was wrapped.
-      total_write += BioWriteZeroCopyWrapper(
-          bio1, bio1_application_send_buffer + total_write,
-          kPartialLengths[j] - interleaved_write_len);
-
-      // Drain the rest.
-      size_t bytes_left = BIO_pending(bio2);
-      total_read += BioReadZeroCopyWrapper(
-          bio2, bio2_application_recv_buffer + total_read, bytes_left);
-
-      if (total_read != total_write) {
-        fprintf(stderr, "Lengths not equal in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return false;
-      }
-      if (total_read > kLengths[i] + kPartialLengths[j]) {
-        fprintf(stderr, "Bad lengths in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return false;
-      }
-      if (memcmp(bio1_application_send_buffer, bio2_application_recv_buffer,
-                 total_read) != 0) {
-        fprintf(stderr, "Buffers not equal in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return false;
-      }
-    }
-  }
-
-  return true;
-}
-
 static bool TestPrintf() {
   // Test a short output, a very long one, and various sizes around
   // 256 (the size of the buffer) to ensure edge cases are correct.
@@ -409,7 +263,137 @@
   return true;
 }
 
-int main(void) {
+static bool TestPair() {
+  // Run through the tests twice, swapping |bio1| and |bio2|, for symmetry.
+  for (int i = 0; i < 2; i++) {
+    BIO *bio1, *bio2;
+    if (!BIO_new_bio_pair(&bio1, 10, &bio2, 10)) {
+      return false;
+    }
+    bssl::UniquePtr<BIO> free_bio1(bio1), free_bio2(bio2);
+
+    if (i == 1) {
+      std::swap(bio1, bio2);
+    }
+
+    // Check initial states.
+    if (BIO_ctrl_get_write_guarantee(bio1) != 10 ||
+        BIO_ctrl_get_read_request(bio1) != 0) {
+      return false;
+    }
+
+    // Data written in one end may be read out the other.
+    char buf[20];
+    if (BIO_write(bio1, "12345", 5) != 5 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 5 ||
+        BIO_read(bio2, buf, sizeof(buf)) != 5 ||
+        memcmp(buf, "12345", 5) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 10) {
+      return false;
+    }
+
+    // Attempting to write more than 10 bytes will write partially.
+    if (BIO_write(bio1, "1234567890___", 13) != 10 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 0 ||
+        BIO_write(bio1, "z", 1) != -1 ||
+        !BIO_should_write(bio1) ||
+        BIO_read(bio2, buf, sizeof(buf)) != 10 ||
+        memcmp(buf, "1234567890", 10) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 10) {
+      return false;
+    }
+
+    // Unsuccessful reads update the read request.
+    if (BIO_read(bio2, buf, 5) != -1 ||
+        !BIO_should_read(bio2) ||
+        BIO_ctrl_get_read_request(bio1) != 5) {
+      return false;
+    }
+
+    // The read request is clamped to the size of the buffer.
+    if (BIO_read(bio2, buf, 20) != -1 ||
+        !BIO_should_read(bio2) ||
+        BIO_ctrl_get_read_request(bio1) != 10) {
+      return false;
+    }
+
+    // Data may be written and read in chunks.
+    if (BIO_write(bio1, "12345", 5) != 5 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 5 ||
+        BIO_write(bio1, "67890___", 8) != 5 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 0 ||
+        BIO_read(bio2, buf, 3) != 3 ||
+        memcmp(buf, "123", 3) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 3 ||
+        BIO_read(bio2, buf, sizeof(buf)) != 7 ||
+        memcmp(buf, "4567890", 7) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 10) {
+      return false;
+    }
+
+    // Successful reads reset the read request.
+    if (BIO_ctrl_get_read_request(bio1) != 0) {
+      return false;
+    }
+
+    // Test writes and reads starting in the middle of the ring buffer and
+    // wrapping to front.
+    if (BIO_write(bio1, "abcdefgh", 8) != 8 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 2 ||
+        BIO_read(bio2, buf, 3) != 3 ||
+        memcmp(buf, "abc", 3) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 5 ||
+        BIO_write(bio1, "ijklm___", 8) != 5 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 0 ||
+        BIO_read(bio2, buf, sizeof(buf)) != 10 ||
+        memcmp(buf, "defghijklm", 10) != 0 ||
+        BIO_ctrl_get_write_guarantee(bio1) != 10) {
+      return false;
+    }
+
+    // Data may flow from both ends in parallel.
+    if (BIO_write(bio1, "12345", 5) != 5 ||
+        BIO_write(bio2, "67890", 5) != 5 ||
+        BIO_read(bio2, buf, sizeof(buf)) != 5 ||
+        memcmp(buf, "12345", 5) != 0 ||
+        BIO_read(bio1, buf, sizeof(buf)) != 5 ||
+        memcmp(buf, "67890", 5) != 0) {
+      return false;
+    }
+
+    // Closing the write end causes an EOF on the read half, after draining.
+    if (BIO_write(bio1, "12345", 5) != 5 ||
+        !BIO_shutdown_wr(bio1) ||
+        BIO_read(bio2, buf, sizeof(buf)) != 5 ||
+        memcmp(buf, "12345", 5) != 0 ||
+        BIO_read(bio2, buf, sizeof(buf)) != 0) {
+      return false;
+    }
+
+    // A closed write end may not be written to.
+    if (BIO_ctrl_get_write_guarantee(bio1) != 0 ||
+        BIO_write(bio1, "_____", 5) != -1) {
+      return false;
+    }
+
+    uint32_t err = ERR_get_error();
+    if (ERR_GET_LIB(err) != ERR_LIB_BIO ||
+        ERR_GET_REASON(err) != BIO_R_BROKEN_PIPE) {
+      return false;
+    }
+
+    // The other end is still functional.
+    if (BIO_write(bio2, "12345", 5) != 5 ||
+        BIO_read(bio1, buf, sizeof(buf)) != 5 ||
+        memcmp(buf, "12345", 5) != 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+int main() {
   CRYPTO_library_init();
 
 #if defined(OPENSSL_WINDOWS)
@@ -429,8 +413,8 @@
 
   if (!TestSocketConnect() ||
       !TestPrintf() ||
-      !TestZeroCopyBioPairs() ||
-      !TestASN1()) {
+      !TestASN1() ||
+      !TestPair()) {
     return 1;
   }
 
diff --git a/src/crypto/bio/pair.c b/src/crypto/bio/pair.c
index 2da2d20..df36343 100644
--- a/src/crypto/bio/pair.c
+++ b/src/crypto/bio/pair.c
@@ -72,12 +72,6 @@
   size_t offset; /* valid iff buf != NULL; 0 if len == 0 */
   size_t size;
   uint8_t *buf; /* "size" elements (if != NULL) */
-  char buf_externally_allocated; /* true iff buf was externally allocated. */
-
-  char zero_copy_read_lock;  /* true iff a zero copy read operation
-                              * is in progress. */
-  char zero_copy_write_lock; /* true iff a zero copy write operation
-                              * is in progress. */
 
   size_t request; /* valid iff peer != NULL; 0 if len != 0,
                    * otherwise set by peer to number of bytes
@@ -145,263 +139,12 @@
     bio_destroy_pair(bio);
   }
 
-  if (!b->buf_externally_allocated) {
-    OPENSSL_free(b->buf);
-  }
-
+  OPENSSL_free(b->buf);
   OPENSSL_free(b);
 
   return 1;
 }
 
-static size_t bio_zero_copy_get_read_buf(struct bio_bio_st* peer_b,
-                                         uint8_t** out_read_buf,
-                                         size_t* out_buf_offset) {
-  size_t max_available;
-  if (peer_b->len > peer_b->size - peer_b->offset) {
-    /* Only the first half of the ring buffer can be read. */
-    max_available = peer_b->size - peer_b->offset;
-  } else {
-    max_available = peer_b->len;
-  }
-
-  *out_read_buf = peer_b->buf;
-  *out_buf_offset = peer_b->offset;
-  return max_available;
-}
-
-int BIO_zero_copy_get_read_buf(BIO* bio, uint8_t** out_read_buf,
-                               size_t* out_buf_offset,
-                               size_t* out_available_bytes) {
-  struct bio_bio_st* b;
-  struct bio_bio_st* peer_b;
-  size_t max_available;
-  *out_available_bytes = 0;
-
-  BIO_clear_retry_flags(bio);
-
-  if (!bio->init) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
-    return 0;
-  }
-
-  b = bio->ptr;
-
-  if (!b || !b->peer) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  peer_b = b->peer->ptr;
-  if (!peer_b || !peer_b->peer || peer_b->peer->ptr != b) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  if (peer_b->zero_copy_read_lock) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  peer_b->request = 0;  /* Is not used by zero-copy API. */
-
-  max_available =
-      bio_zero_copy_get_read_buf(peer_b, out_read_buf, out_buf_offset);
-
-  assert(peer_b->buf != NULL);
-  if (max_available > 0) {
-    peer_b->zero_copy_read_lock = 1;
-  }
-
-  *out_available_bytes = max_available;
-  return 1;
-}
-
-int BIO_zero_copy_get_read_buf_done(BIO* bio, size_t bytes_read) {
-  struct bio_bio_st* b;
-  struct bio_bio_st* peer_b;
-  size_t max_available;
-  size_t dummy_read_offset;
-  uint8_t* dummy_read_buf;
-
-  assert(BIO_get_retry_flags(bio) == 0);
-
-  if (!bio->init) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
-    return 0;
-  }
-
-  b = bio->ptr;
-
-  if (!b || !b->peer) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  peer_b = b->peer->ptr;
-  if (!peer_b || !peer_b->peer || peer_b->peer->ptr != b) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  if (!peer_b->zero_copy_read_lock) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  max_available =
-      bio_zero_copy_get_read_buf(peer_b, &dummy_read_buf, &dummy_read_offset);
-  if (bytes_read > max_available) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  assert(peer_b->len >= bytes_read);
-  peer_b->len -= bytes_read;
-  assert(peer_b->offset + bytes_read <= peer_b->size);
-
-  /* Move read offset. If zero_copy_write_lock == 1 we must advance the
-   * offset even if buffer becomes empty, to make sure
-   * write_offset = (offset + len) mod size does not change. */
-  if (peer_b->offset + bytes_read == peer_b->size ||
-      (!peer_b->zero_copy_write_lock && peer_b->len == 0)) {
-    peer_b->offset = 0;
-  } else {
-    peer_b->offset += bytes_read;
-  }
-
-  bio->num_read += bytes_read;
-  peer_b->zero_copy_read_lock = 0;
-  return 1;
-}
-
-static size_t bio_zero_copy_get_write_buf(struct bio_bio_st* b,
-                                          uint8_t** out_write_buf,
-                                          size_t* out_buf_offset) {
-  size_t write_offset;
-  size_t max_available;
-
-  assert(b->len <= b->size);
-
-  write_offset = b->offset + b->len;
-
-  if (write_offset >= b->size) {
-    /* Only the first half of the ring buffer can be written to. */
-    write_offset -= b->size;
-    /* write up to the start of the ring buffer. */
-    max_available = b->offset - write_offset;
-  } else {
-    /* write up to the end the buffer. */
-    max_available = b->size - write_offset;
-  }
-
-  *out_write_buf = b->buf;
-  *out_buf_offset = write_offset;
-  return max_available;
-}
-
-int BIO_zero_copy_get_write_buf(BIO* bio, uint8_t** out_write_buf,
-                                size_t* out_buf_offset,
-                                size_t* out_available_bytes) {
-  struct bio_bio_st* b;
-  struct bio_bio_st* peer_b;
-  size_t max_available;
-
-  *out_available_bytes = 0;
-  BIO_clear_retry_flags(bio);
-
-  if (!bio->init) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
-    return 0;
-  }
-
-  b = bio->ptr;
-
-  if (!b || !b->buf || !b->peer) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-  peer_b = b->peer->ptr;
-  if (!peer_b || !peer_b->peer || peer_b->peer->ptr != b) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  assert(b->buf != NULL);
-
-  if (b->zero_copy_write_lock) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  b->request = 0;
-  if (b->closed) {
-    /* Bio is already closed. */
-    OPENSSL_PUT_ERROR(BIO, BIO_R_BROKEN_PIPE);
-    return 0;
-  }
-
-  max_available = bio_zero_copy_get_write_buf(b, out_write_buf, out_buf_offset);
-
-  if (max_available > 0) {
-    b->zero_copy_write_lock = 1;
-  }
-
-  *out_available_bytes = max_available;
-  return 1;
-}
-
-int BIO_zero_copy_get_write_buf_done(BIO* bio, size_t bytes_written) {
-  struct bio_bio_st* b;
-  struct bio_bio_st* peer_b;
-
-  size_t rest;
-  size_t dummy_write_offset;
-  uint8_t* dummy_write_buf;
-
-  if (!bio->init) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
-    return 0;
-  }
-
-  b = bio->ptr;
-
-  if (!b || !b->buf || !b->peer) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-  peer_b = b->peer->ptr;
-  if (!peer_b || !peer_b->peer || peer_b->peer->ptr != b) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
-    return 0;
-  }
-
-  b->request = 0;
-  if (b->closed) {
-    /* BIO is already closed. */
-    OPENSSL_PUT_ERROR(BIO, BIO_R_BROKEN_PIPE);
-    return 0;
-  }
-
-  if (!b->zero_copy_write_lock) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  rest = bio_zero_copy_get_write_buf(b, &dummy_write_buf, &dummy_write_offset);
-
-  if (bytes_written > rest) {
-    OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
-    return 0;
-  }
-
-  bio->num_write += bytes_written;
-  /* Move write offset. */
-  b->len += bytes_written;
-  b->zero_copy_write_lock = 0;
-  return 1;
-}
-
 static int bio_read(BIO *bio, char *buf, int size_) {
   size_t size = size_;
   size_t rest;
@@ -422,7 +165,7 @@
 
   peer_b->request = 0; /* will be set in "retry_read" situation */
 
-  if (buf == NULL || size == 0 || peer_b->zero_copy_read_lock) {
+  if (buf == NULL || size == 0) {
     return 0;
   }
 
@@ -467,10 +210,7 @@
     memcpy(buf, peer_b->buf + peer_b->offset, chunk);
 
     peer_b->len -= chunk;
-    /* If zero_copy_write_lock == 1 we must advance the offset even if buffer
-     * becomes empty, to make sure write_offset = (offset + len) % size
-     * does not change. */
-    if (peer_b->len || peer_b->zero_copy_write_lock) {
+    if (peer_b->len) {
       peer_b->offset += chunk;
       assert(peer_b->offset <= peer_b->size);
       if (peer_b->offset == peer_b->size) {
@@ -504,10 +244,6 @@
   assert(b->peer != NULL);
   assert(b->buf != NULL);
 
-  if (b->zero_copy_write_lock) {
-    return 0;
-  }
-
   b->request = 0;
   if (b->closed) {
     /* we already closed */
@@ -564,9 +300,8 @@
   return num;
 }
 
-static int bio_make_pair(BIO* bio1, BIO* bio2,
-                         size_t writebuf1_len, uint8_t* ext_writebuf1,
-                         size_t writebuf2_len, uint8_t* ext_writebuf2) {
+static int bio_make_pair(BIO *bio1, BIO *bio2, size_t writebuf1_len,
+                         size_t writebuf2_len) {
   struct bio_bio_st *b1, *b2;
 
   assert(bio1 != NULL);
@@ -580,23 +315,14 @@
     return 0;
   }
 
-  assert(b1->buf_externally_allocated == 0);
-  assert(b2->buf_externally_allocated == 0);
-
   if (b1->buf == NULL) {
     if (writebuf1_len) {
       b1->size = writebuf1_len;
     }
-    if (!ext_writebuf1) {
-      b1->buf_externally_allocated = 0;
-      b1->buf = OPENSSL_malloc(b1->size);
-      if (b1->buf == NULL) {
-        OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
-        return 0;
-      }
-    } else {
-      b1->buf = ext_writebuf1;
-      b1->buf_externally_allocated = 1;
+    b1->buf = OPENSSL_malloc(b1->size);
+    if (b1->buf == NULL) {
+      OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
+      return 0;
     }
     b1->len = 0;
     b1->offset = 0;
@@ -606,16 +332,10 @@
     if (writebuf2_len) {
       b2->size = writebuf2_len;
     }
-    if (!ext_writebuf2) {
-      b2->buf_externally_allocated = 0;
-      b2->buf = OPENSSL_malloc(b2->size);
-      if (b2->buf == NULL) {
-        OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
-        return 0;
-      }
-    } else {
-      b2->buf = ext_writebuf2;
-      b2->buf_externally_allocated = 1;
+    b2->buf = OPENSSL_malloc(b2->size);
+    if (b2->buf == NULL) {
+      OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
+      return 0;
     }
     b2->len = 0;
     b2->offset = 0;
@@ -624,13 +344,9 @@
   b1->peer = bio2;
   b1->closed = 0;
   b1->request = 0;
-  b1->zero_copy_read_lock = 0;
-  b1->zero_copy_write_lock = 0;
   b2->peer = bio1;
   b2->closed = 0;
   b2->request = 0;
-  b2->zero_copy_read_lock = 0;
-  b2->zero_copy_write_lock = 0;
 
   bio1->init = 1;
   bio2->init = 1;
@@ -744,50 +460,22 @@
 
 static const BIO_METHOD *bio_s_bio(void) { return &methods_biop; }
 
-int BIO_new_bio_pair(BIO** bio1_p, size_t writebuf1,
-                     BIO** bio2_p, size_t writebuf2) {
-  return BIO_new_bio_pair_external_buf(bio1_p, writebuf1, NULL, bio2_p,
-                                       writebuf2, NULL);
-}
-
-int BIO_new_bio_pair_external_buf(BIO** bio1_p, size_t writebuf1_len,
-                                  uint8_t* ext_writebuf1,
-                                  BIO** bio2_p, size_t writebuf2_len,
-                                  uint8_t* ext_writebuf2) {
-  BIO *bio1 = NULL, *bio2 = NULL;
-  int ret = 0;
-
-  /* External buffers must have sizes greater than 0. */
-  if ((ext_writebuf1 && !writebuf1_len) || (ext_writebuf2 && !writebuf2_len)) {
-    goto err;
-  }
-
-  bio1 = BIO_new(bio_s_bio());
-  if (bio1 == NULL) {
-    goto err;
-  }
-  bio2 = BIO_new(bio_s_bio());
-  if (bio2 == NULL) {
-    goto err;
-  }
-
-  if (!bio_make_pair(bio1, bio2, writebuf1_len, ext_writebuf1, writebuf2_len,
-                     ext_writebuf2)) {
-    goto err;
-  }
-  ret = 1;
-
-err:
-  if (ret == 0) {
+int BIO_new_bio_pair(BIO** bio1_p, size_t writebuf1_len,
+                     BIO** bio2_p, size_t writebuf2_len) {
+  BIO *bio1 = BIO_new(bio_s_bio());
+  BIO *bio2 = BIO_new(bio_s_bio());
+  if (bio1 == NULL || bio2 == NULL ||
+      !bio_make_pair(bio1, bio2, writebuf1_len, writebuf2_len)) {
     BIO_free(bio1);
-    bio1 = NULL;
     BIO_free(bio2);
-    bio2 = NULL;
+    *bio1_p = NULL;
+    *bio2_p = NULL;
+    return 0;
   }
 
   *bio1_p = bio1;
   *bio2_p = bio2;
-  return ret;
+  return 1;
 }
 
 size_t BIO_ctrl_get_read_request(BIO *bio) {
diff --git a/src/crypto/bn/bn_test.cc b/src/crypto/bn/bn_test.cc
index 672d83f..044af5f 100644
--- a/src/crypto/bn/bn_test.cc
+++ b/src/crypto/bn/bn_test.cc
@@ -568,21 +568,25 @@
   bssl::UniquePtr<BIGNUM> a = GetBIGNUM(t, "A");
   bssl::UniquePtr<BIGNUM> p = GetBIGNUM(t, "P");
   bssl::UniquePtr<BIGNUM> mod_sqrt = GetBIGNUM(t, "ModSqrt");
-  if (!a || !p || !mod_sqrt) {
+  bssl::UniquePtr<BIGNUM> mod_sqrt2(BN_new());
+  if (!a || !p || !mod_sqrt || !mod_sqrt2 ||
+      // There are two possible answers.
+      !BN_sub(mod_sqrt2.get(), p.get(), mod_sqrt.get())) {
     return false;
   }
 
+  // -0 is 0, not P.
+  if (BN_is_zero(mod_sqrt.get())) {
+    BN_zero(mod_sqrt2.get());
+  }
+
   bssl::UniquePtr<BIGNUM> ret(BN_new());
-  bssl::UniquePtr<BIGNUM> ret2(BN_new());
   if (!ret ||
-      !ret2 ||
-      !BN_mod_sqrt(ret.get(), a.get(), p.get(), ctx) ||
-      // There are two possible answers.
-      !BN_sub(ret2.get(), p.get(), ret.get())) {
+      !BN_mod_sqrt(ret.get(), a.get(), p.get(), ctx)) {
     return false;
   }
 
-  if (BN_cmp(ret2.get(), mod_sqrt.get()) != 0 &&
+  if (BN_cmp(ret.get(), mod_sqrt2.get()) != 0 &&
       !ExpectBIGNUMsEqual(t, "sqrt(A) (mod P)", mod_sqrt.get(), ret.get())) {
     return false;
   }
@@ -590,6 +594,29 @@
   return true;
 }
 
+static bool TestNotModSquare(FileTest *t, BN_CTX *ctx) {
+  bssl::UniquePtr<BIGNUM> not_mod_square = GetBIGNUM(t, "NotModSquare");
+  bssl::UniquePtr<BIGNUM> p = GetBIGNUM(t, "P");
+  bssl::UniquePtr<BIGNUM> ret(BN_new());
+  if (!not_mod_square || !p || !ret) {
+    return false;
+  }
+
+  if (BN_mod_sqrt(ret.get(), not_mod_square.get(), p.get(), ctx)) {
+    t->PrintLine("BN_mod_sqrt unexpectedly succeeded.");
+    return false;
+  }
+
+  uint32_t err = ERR_peek_error();
+  if (ERR_GET_LIB(err) == ERR_LIB_BN &&
+      ERR_GET_REASON(err) == BN_R_NOT_A_SQUARE) {
+    ERR_clear_error();
+    return true;
+  }
+
+  return false;
+}
+
 static bool TestModInv(FileTest *t, BN_CTX *ctx) {
   bssl::UniquePtr<BIGNUM> a = GetBIGNUM(t, "A");
   bssl::UniquePtr<BIGNUM> m = GetBIGNUM(t, "M");
@@ -634,6 +661,7 @@
     {"ModExp", TestModExp},
     {"Exp", TestExp},
     {"ModSqrt", TestModSqrt},
+    {"NotModSquare", TestNotModSquare},
     {"ModInv", TestModInv},
 };
 
diff --git a/src/crypto/bn/bn_tests.txt b/src/crypto/bn/bn_tests.txt
index 692a642..ec89b8e 100644
--- a/src/crypto/bn/bn_tests.txt
+++ b/src/crypto/bn/bn_tests.txt
@@ -10737,6 +10737,28 @@
 A = 2eee37cf06228a387788188e650bc6d8a2ff402931443f69156a29155eca07dcb45f3aac238d92943c0c25c896098716baa433f25bd696a142f5a69d5d937e81
 P = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f
 
+
+# NotModSquare tests.
+#
+# These test vectors are such that NotModSquare is not a square modulo P.
+
+NotModSquare = 03
+P = 07
+
+NotModSquare = 05
+P = 07
+
+NotModSquare = 06
+P = 07
+
+NotModSquare = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951e
+P = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f
+
+
+# ModInv tests.
+#
+# These test vectors satisfy ModInv * A = 1 (mod M) and 0 <= ModInv < M.
+
 ModInv = 00
 A = 00
 M = 01
diff --git a/src/crypto/bn/sqrt.c b/src/crypto/bn/sqrt.c
index e3a7b9a..fb962a9 100644
--- a/src/crypto/bn/sqrt.c
+++ b/src/crypto/bn/sqrt.c
@@ -456,7 +456,9 @@
   }
 
   /* We estimate that the square root of an n-bit number is 2^{n/2}. */
-  BN_lshift(estimate, BN_value_one(), BN_num_bits(in)/2);
+  if (!BN_lshift(estimate, BN_value_one(), BN_num_bits(in)/2)) {
+    goto err;
+  }
 
   /* This is Newton's method for finding a root of the equation |estimate|^2 -
    * |in| = 0. */
diff --git a/src/crypto/bytestring/bytestring_test.cc b/src/crypto/bytestring/bytestring_test.cc
index 563c6b0..6ce071a 100644
--- a/src/crypto/bytestring/bytestring_test.cc
+++ b/src/crypto/bytestring/bytestring_test.cc
@@ -28,7 +28,6 @@
 #include "internal.h"
 #include "../internal.h"
 
-namespace bssl {
 
 static bool TestSkip() {
   static const uint8_t kData[] = {1, 2, 3};
@@ -317,7 +316,7 @@
 }
 
 static bool TestCBBFixed() {
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   uint8_t buf[1];
   uint8_t *out_buf;
   size_t out_size;
@@ -401,7 +400,7 @@
 }
 
 static bool TestCBBDiscardChild() {
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   CBB contents, inner_contents, inner_inner_contents;
 
   if (!CBB_init(cbb.get(), 0) ||
@@ -804,7 +803,7 @@
   uint8_t buf[10];
   uint8_t *ptr;
   size_t len;
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   if (!CBB_init_fixed(cbb.get(), buf, sizeof(buf)) ||
       // Too large.
       CBB_reserve(cbb.get(), &ptr, 11)) {
@@ -827,7 +826,7 @@
 
 static bool TestStickyError() {
   // Write an input that exceeds the limit for its length prefix.
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   CBB child;
   static const uint8_t kZeros[256] = {0};
   if (!CBB_init(cbb.get(), 0) ||
@@ -890,7 +889,7 @@
   return true;
 }
 
-static int Main() {
+int main() {
   CRYPTO_library_init();
 
   if (!TestSkip() ||
@@ -918,9 +917,3 @@
   printf("PASS\n");
   return 0;
 }
-
-}  // namespace bssl
-
-int main() {
-  return bssl::Main();
-}
diff --git a/src/crypto/cipher/aead_test.cc b/src/crypto/cipher/aead_test.cc
index b33a36d..313f041 100644
--- a/src/crypto/cipher/aead_test.cc
+++ b/src/crypto/cipher/aead_test.cc
@@ -23,7 +23,15 @@
 
 #include "../test/file_test.h"
 
-namespace bssl {
+
+#if defined(OPENSSL_SMALL)
+const EVP_AEAD* EVP_aead_aes_128_gcm_siv(void) {
+  return nullptr;
+}
+const EVP_AEAD* EVP_aead_aes_256_gcm_siv(void) {
+  return nullptr;
+}
+#endif
 
 // This program tests an AEAD against a series of test vectors from a file,
 // using the FileTest format. As an example, here's a valid test case:
@@ -48,7 +56,7 @@
     return false;
   }
 
-  ScopedEVP_AEAD_CTX ctx;
+  bssl::ScopedEVP_AEAD_CTX ctx;
   if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.data(), key.size(),
                                         tag.size(), evp_aead_seal)) {
     t->PrintLine("Failed to init AEAD.");
@@ -198,7 +206,7 @@
   const size_t max_overhead = EVP_AEAD_max_overhead(aead);
 
   std::vector<uint8_t> key(key_len, 'a');
-  ScopedEVP_AEAD_CTX ctx;
+  bssl::ScopedEVP_AEAD_CTX ctx;
   if (!EVP_AEAD_CTX_init(ctx.get(), aead, key.data(), key_len,
                          EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr)) {
     return false;
@@ -302,6 +310,8 @@
 static const struct KnownAEAD kAEADs[] = {
   { "aes-128-gcm", EVP_aead_aes_128_gcm, false },
   { "aes-256-gcm", EVP_aead_aes_256_gcm, false },
+  { "aes-128-gcm-siv", EVP_aead_aes_128_gcm_siv, false },
+  { "aes-256-gcm-siv", EVP_aead_aes_256_gcm_siv, false },
   { "chacha20-poly1305", EVP_aead_chacha20_poly1305, false },
   { "chacha20-poly1305-old", EVP_aead_chacha20_poly1305_old, false },
   { "aes-128-cbc-sha1-tls", EVP_aead_aes_128_cbc_sha1_tls, true },
@@ -321,7 +331,7 @@
   { "", NULL, false },
 };
 
-static int Main(int argc, char **argv) {
+int main(int argc, char **argv) {
   CRYPTO_library_init();
 
   if (argc != 3) {
@@ -342,6 +352,11 @@
   }
 
   const EVP_AEAD *const aead = known_aead->func();
+  if (aead == NULL) {
+    // AEAD is not compiled in this configuration.
+    printf("PASS\n");
+    return 0;
+  }
 
   if (!TestCleanupAfterInitFailure(aead)) {
     return 1;
@@ -354,9 +369,3 @@
 
   return FileTestMain(TestAEAD, const_cast<EVP_AEAD*>(aead), argv[2]);
 }
-
-}  // namespace bssl
-
-int main(int argc, char **argv) {
-  return bssl::Main(argc, argv);
-}
diff --git a/src/crypto/cipher/cipher_test.cc b/src/crypto/cipher/cipher_test.cc
index cb42fc5..09802c2 100644
--- a/src/crypto/cipher/cipher_test.cc
+++ b/src/crypto/cipher/cipher_test.cc
@@ -63,7 +63,6 @@
 
 #include "../test/file_test.h"
 
-namespace bssl {
 
 static const EVP_CIPHER *GetCipher(const std::string &name) {
   if (name == "DES-CBC") {
@@ -127,7 +126,7 @@
 
   bool is_aead = EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE;
 
-  ScopedEVP_CIPHER_CTX ctx;
+  bssl::ScopedEVP_CIPHER_CTX ctx;
   if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr,
                          encrypt ? 1 : 0)) {
     return false;
@@ -284,7 +283,7 @@
   return true;
 }
 
-static int Main(int argc, char **argv) {
+int main(int argc, char **argv) {
   CRYPTO_library_init();
 
   if (argc != 2) {
@@ -294,9 +293,3 @@
 
   return FileTestMain(TestCipher, nullptr, argv[1]);
 }
-
-}  // namespace bssl
-
-int main(int argc, char **argv) {
-  return bssl::Main(argc, argv);
-}
diff --git a/src/crypto/cipher/e_aes.c b/src/crypto/cipher/e_aes.c
index 9225d6a..f99022f 100644
--- a/src/crypto/cipher/e_aes.c
+++ b/src/crypto/cipher/e_aes.c
@@ -1446,6 +1446,305 @@
   return &aead_aes_256_ctr_hmac_sha256;
 }
 
+#if !defined(OPENSSL_SMALL)
+
+#define EVP_AEAD_AES_GCM_SIV_TAG_LEN 16
+
+struct aead_aes_gcm_siv_ctx {
+  union {
+    double align;
+    AES_KEY ks;
+  } ks;
+  block128_f kgk_block;
+  unsigned is_256:1;
+};
+
+static int aead_aes_gcm_siv_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
+                                 size_t key_len, size_t tag_len) {
+  const size_t key_bits = key_len * 8;
+
+  if (key_bits != 128 && key_bits != 256) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH);
+    return 0; /* EVP_AEAD_CTX_init should catch this. */
+  }
+
+  if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) {
+    tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN;
+  }
+
+  if (tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE);
+    return 0;
+  }
+
+  struct aead_aes_gcm_siv_ctx *gcm_siv_ctx =
+      OPENSSL_malloc(sizeof(struct aead_aes_gcm_siv_ctx));
+  if (gcm_siv_ctx == NULL) {
+    return 0;
+  }
+  memset(gcm_siv_ctx, 0, sizeof(struct aead_aes_gcm_siv_ctx));
+
+  if (aesni_capable()) {
+    aesni_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks);
+    gcm_siv_ctx->kgk_block = (block128_f)aesni_encrypt;
+  } else if (hwaes_capable()) {
+    aes_hw_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks);
+    gcm_siv_ctx->kgk_block = (block128_f)aes_hw_encrypt;
+  } else if (vpaes_capable()) {
+    vpaes_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks);
+    gcm_siv_ctx->kgk_block = (block128_f)vpaes_encrypt;
+  } else {
+    AES_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks);
+    gcm_siv_ctx->kgk_block = (block128_f)AES_encrypt;
+  }
+
+  gcm_siv_ctx->is_256 = (key_len == 32);
+  ctx->aead_state = gcm_siv_ctx;
+
+  return 1;
+}
+
+static void aead_aes_gcm_siv_cleanup(EVP_AEAD_CTX *ctx) {
+  struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state;
+  OPENSSL_cleanse(gcm_siv_ctx, sizeof(struct aead_aes_gcm_siv_ctx));
+  OPENSSL_free(gcm_siv_ctx);
+}
+
+/* gcm_siv_crypt encrypts (or decrypts—it's the same thing) |in_len| bytes from
+ * |in| to |out|, using the block function |enc_block| with |key| in counter
+ * mode, starting at |initial_counter|. This differs from the traditional
+ * counter mode code in that the counter is handled little-endian, only the
+ * first four bytes are used and the GCM-SIV tweak to the final byte is
+ * applied. The |in| and |out| pointers may be equal but otherwise must not
+ * alias. */
+static void gcm_siv_crypt(uint8_t *out, const uint8_t *in, size_t in_len,
+                          const uint8_t initial_counter[AES_BLOCK_SIZE],
+                          block128_f enc_block, const AES_KEY *key) {
+  union {
+    uint32_t w[4];
+    uint8_t c[16];
+  } counter;
+
+  memcpy(counter.c, initial_counter, AES_BLOCK_SIZE);
+  counter.c[15] |= 0x80;
+
+  for (size_t done = 0; done < in_len;) {
+    uint8_t keystream[AES_BLOCK_SIZE];
+    enc_block(counter.c, keystream, key);
+    counter.w[0]++;
+
+    size_t todo = AES_BLOCK_SIZE;
+    if (in_len - done < todo) {
+      todo = in_len - done;
+    }
+
+    for (size_t i = 0; i < todo; i++) {
+      out[done + i] = keystream[i] ^ in[done + i];
+    }
+
+    done += todo;
+  }
+}
+
+/* gcm_siv_polyval evaluates POLYVAL at |auth_key| on the given plaintext and
+ * AD. The result is written to |out_tag|. */
+static void gcm_siv_polyval(uint8_t out_tag[16], const uint8_t *in,
+                            size_t in_len, const uint8_t *ad, size_t ad_len,
+                            const uint8_t auth_key[16]) {
+  struct polyval_ctx polyval_ctx;
+  CRYPTO_POLYVAL_init(&polyval_ctx, auth_key);
+
+  CRYPTO_POLYVAL_update_blocks(&polyval_ctx, ad, ad_len & ~15);
+
+  uint8_t scratch[16];
+  if (ad_len & 15) {
+    memset(scratch, 0, sizeof(scratch));
+    memcpy(scratch, &ad[ad_len & ~15], ad_len & 15);
+    CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch));
+  }
+
+  CRYPTO_POLYVAL_update_blocks(&polyval_ctx, in, in_len & ~15);
+  if (in_len & 15) {
+    memset(scratch, 0, sizeof(scratch));
+    memcpy(scratch, &in[in_len & ~15], in_len & 15);
+    CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch));
+  }
+
+  union {
+    uint8_t c[16];
+    struct {
+      uint64_t ad;
+      uint64_t in;
+    } bitlens;
+  } length_block;
+
+  length_block.bitlens.ad = ad_len * 8;
+  length_block.bitlens.in = in_len * 8;
+  CRYPTO_POLYVAL_update_blocks(&polyval_ctx, length_block.c,
+                               sizeof(length_block));
+
+  CRYPTO_POLYVAL_finish(&polyval_ctx, out_tag);
+  out_tag[15] &= 0x7f;
+}
+
+/* gcm_siv_record_keys contains the keys used for a specific GCM-SIV record. */
+struct gcm_siv_record_keys {
+  uint8_t auth_key[16];
+  union {
+    double align;
+    AES_KEY ks;
+  } enc_key;
+  block128_f enc_block;
+};
+
+/* gcm_siv_keys calculates the keys for a specific GCM-SIV record with the
+ * given nonce and writes them to |*out_keys|. */
+static void gcm_siv_keys(
+    const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx,
+    struct gcm_siv_record_keys *out_keys,
+    const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_TAG_LEN]) {
+  const AES_KEY *const key = &gcm_siv_ctx->ks.ks;
+  gcm_siv_ctx->kgk_block(nonce, out_keys->auth_key, key);
+
+  if (gcm_siv_ctx->is_256) {
+    uint8_t record_enc_key[32];
+    gcm_siv_ctx->kgk_block(out_keys->auth_key, record_enc_key + 16, key);
+    gcm_siv_ctx->kgk_block(record_enc_key + 16, record_enc_key, key);
+    aes_ctr_set_key(&out_keys->enc_key.ks, NULL, &out_keys->enc_block,
+                    record_enc_key, sizeof(record_enc_key));
+  } else {
+    uint8_t record_enc_key[16];
+    gcm_siv_ctx->kgk_block(out_keys->auth_key, record_enc_key, key);
+    aes_ctr_set_key(&out_keys->enc_key.ks, NULL, &out_keys->enc_block,
+                    record_enc_key, sizeof(record_enc_key));
+  }
+}
+
+static int aead_aes_gcm_siv_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
+                                 size_t *out_len, size_t max_out_len,
+                                 const uint8_t *nonce, size_t nonce_len,
+                                 const uint8_t *in, size_t in_len,
+                                 const uint8_t *ad, size_t ad_len) {
+  const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state;
+  const uint64_t in_len_64 = in_len;
+  const uint64_t ad_len_64 = ad_len;
+
+  if (in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN < in_len ||
+      in_len_64 > (UINT64_C(1) << 36) ||
+      ad_len_64 >= (UINT64_C(1) << 61)) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
+    return 0;
+  }
+
+  if (max_out_len < in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (nonce_len != AES_BLOCK_SIZE) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
+    return 0;
+  }
+
+  struct gcm_siv_record_keys keys;
+  gcm_siv_keys(gcm_siv_ctx, &keys, nonce);
+
+  uint8_t tag[16];
+  gcm_siv_polyval(tag, in, in_len, ad, ad_len, keys.auth_key);
+  keys.enc_block(tag, tag, &keys.enc_key.ks);
+
+  gcm_siv_crypt(out, in, in_len, tag, keys.enc_block, &keys.enc_key.ks);
+
+  memcpy(&out[in_len], tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN);
+  *out_len = in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN;
+
+  return 1;
+}
+
+static int aead_aes_gcm_siv_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
+                                 size_t *out_len, size_t max_out_len,
+                                 const uint8_t *nonce, size_t nonce_len,
+                                 const uint8_t *in, size_t in_len,
+                                 const uint8_t *ad, size_t ad_len) {
+  const uint64_t ad_len_64 = ad_len;
+  if (ad_len_64 >= (UINT64_C(1) << 61)) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
+    return 0;
+  }
+
+  const uint64_t in_len_64 = in_len;
+  if (in_len < EVP_AEAD_AES_GCM_SIV_TAG_LEN ||
+      in_len_64 > (UINT64_C(1) << 36) + AES_BLOCK_SIZE) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
+    return 0;
+  }
+
+  const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state;
+  const size_t plaintext_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN;
+
+  if (max_out_len < plaintext_len) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  struct gcm_siv_record_keys keys;
+  gcm_siv_keys(gcm_siv_ctx, &keys, nonce);
+
+  gcm_siv_crypt(out, in, plaintext_len, &in[plaintext_len], keys.enc_block,
+                &keys.enc_key.ks);
+
+  uint8_t expected_tag[EVP_AEAD_AES_GCM_SIV_TAG_LEN];
+  gcm_siv_polyval(expected_tag, out, plaintext_len, ad, ad_len, keys.auth_key);
+  keys.enc_block(expected_tag, expected_tag, &keys.enc_key.ks);
+
+  if (CRYPTO_memcmp(expected_tag, &in[plaintext_len], sizeof(expected_tag)) !=
+      0) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
+    return 0;
+  }
+
+  *out_len = plaintext_len;
+  return 1;
+}
+
+static const EVP_AEAD aead_aes_128_gcm_siv = {
+    16,                           /* key length */
+    AES_BLOCK_SIZE,               /* nonce length */
+    EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */
+    EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */
+
+    aead_aes_gcm_siv_init,
+    NULL /* init_with_direction */,
+    aead_aes_gcm_siv_cleanup,
+    aead_aes_gcm_siv_seal,
+    aead_aes_gcm_siv_open,
+    NULL /* get_iv */,
+};
+
+static const EVP_AEAD aead_aes_256_gcm_siv = {
+    32,                           /* key length */
+    AES_BLOCK_SIZE,               /* nonce length */
+    EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */
+    EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */
+
+    aead_aes_gcm_siv_init,
+    NULL /* init_with_direction */,
+    aead_aes_gcm_siv_cleanup,
+    aead_aes_gcm_siv_seal,
+    aead_aes_gcm_siv_open,
+    NULL /* get_iv */,
+};
+
+const EVP_AEAD *EVP_aead_aes_128_gcm_siv(void) {
+  return &aead_aes_128_gcm_siv;
+}
+
+const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void) {
+  return &aead_aes_256_gcm_siv;
+}
+
+#endif  /* !OPENSSL_SMALL */
+
 int EVP_has_aes_hardware(void) {
 #if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
   return aesni_capable() && crypto_gcm_clmul_enabled();
diff --git a/src/crypto/cipher/test/aes_128_gcm_siv_tests.txt b/src/crypto/cipher/test/aes_128_gcm_siv_tests.txt
new file mode 100644
index 0000000..a929b59
--- /dev/null
+++ b/src/crypto/cipher/test/aes_128_gcm_siv_tests.txt
@@ -0,0 +1,236 @@
+# This is the example from
+# https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02#section-8
+
+KEY: ee8e1ed9ff2540ae8f2ba9f50bc2f27c
+NONCE: 752abad3e0afb5f434dc4310f71f3d21
+IN: "Hello world"
+AD: "example"
+CT: 810649724764545b3625ff
+TAG: 010a10f4942710781d2948ac0192572f
+
+# Test vectors from
+# https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02#appendix-B
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 
+AD: 
+CT: 
+TAG: cb52de357fad226ae428d0ed5a575496
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000
+AD: 
+CT: 7e139f58002d68ee
+TAG: 715835541f2136f03b6dc80ae0a8ac46
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000
+AD: 
+CT: 4a87f0cd26e5d5086e90da02
+TAG: 4dff905e48d512e9c34ae8f3be66ec43
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000
+AD: 
+CT: 048ca58c46d2368ce00132389f40b511
+TAG: 971da9aa385283522c4f67a9aedb37e5
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000000000000000000002000000000000000000000000000000
+AD: 
+CT: e1cf1cf545d2743ec005b26bd2c836ac1a4233d646c195ffa401f28063127baa
+TAG: 1071338b8c2930d3ec4c17cecbefa4b4
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000
+AD: 
+CT: 2e7e6881a02d57b877794b2fbfbfef5484f1cf74f4ad53a751b2582c0e698466bd9a49dcab53806d8e31d864c4632d00
+TAG: 04b1b8a9c1630ff028b14d2e57bca429
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 
+CT: 0ac5be860726209d9218de3e9d533743e1efe1595bc58f93f00e9bb9a7558dc1e1b14a9c0d49eb5064c7efa79842f9c7cfdd77614709f0b545d3227498e774d5
+TAG: 860b73a1ed8a5b9acd925c3f3f49c5c5
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000
+AD: 01
+CT: 4919e29e9890e452
+TAG: 1433a5c0284c911163888dbd128e6874
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000
+AD: 01
+CT: db55d6da719fe0473538294e
+TAG: 5a8ab948ccd205a70c78e8fdf954693b
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000
+AD: 01
+CT: aea3c54272abc1b58ed34a536743f4da
+TAG: da10d98bfe23784cfdfd0af97b6d5b78
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000000000000000000003000000000000000000000000000000
+AD: 01
+CT: aa694c0cfe148100cb5c6e27a77a7ff7b4233d6af251d9faa3d84f7c0d1113f1
+TAG: 778c5b68356a1a6a6f3c14a8f96c35ca
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 01
+CT: 9ac909928bcde79c2afa885df9c035c85a9eab136f6f6ea11034456bd306ea3c5dd542f706fffe538b5f139fa9dc622e
+TAG: 26c0c0d146d38787ca0fcbc3f911577a
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000
+AD: 01
+CT: c56be9d61ecf6a31a6289cddc9b91aaf84cdb53a3913b825d6eb5e157906dfb0a308c6b0b095d6fd1a5b761ca7fa0e39ca92f38ae206eec844c0c4ab0c1c165e
+TAG: a60986309b99431a35dd8c5ebeef8375
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000
+AD: 010000000000000000000000
+CT: 47995b96
+TAG: 16b668094202cadde992e0c16205793c
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0300000000000000000000000000000004000000
+AD: 010000000000000000000000000000000200
+CT: 8fe25de75089e9f849150e57ab7f7810981cd319
+TAG: 89ca91ebc560709432fe9496746404cc
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 030000000000000000000000000000000400
+AD: 0100000000000000000000000000000002000000
+CT: b26d43ae158316ac37f41579ccf1d461274e
+TAG: 13b7c01d08dd6969d51d1bf0fbbdc4d2
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 
+AD: 
+CT: 
+TAG: cb52de357fad226ae428d0ed5a575496
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000
+AD: 
+CT: 7e139f58002d68ee
+TAG: 715835541f2136f03b6dc80ae0a8ac46
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000
+AD: 
+CT: 4a87f0cd26e5d5086e90da02
+TAG: 4dff905e48d512e9c34ae8f3be66ec43
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000
+AD: 
+CT: 048ca58c46d2368ce00132389f40b511
+TAG: 971da9aa385283522c4f67a9aedb37e5
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000000000000000000002000000000000000000000000000000
+AD: 
+CT: e1cf1cf545d2743ec005b26bd2c836ac1a4233d646c195ffa401f28063127baa
+TAG: 1071338b8c2930d3ec4c17cecbefa4b4
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000
+AD: 
+CT: 2e7e6881a02d57b877794b2fbfbfef5484f1cf74f4ad53a751b2582c0e698466bd9a49dcab53806d8e31d864c4632d00
+TAG: 04b1b8a9c1630ff028b14d2e57bca429
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 
+CT: 0ac5be860726209d9218de3e9d533743e1efe1595bc58f93f00e9bb9a7558dc1e1b14a9c0d49eb5064c7efa79842f9c7cfdd77614709f0b545d3227498e774d5
+TAG: 860b73a1ed8a5b9acd925c3f3f49c5c5
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000
+AD: 01
+CT: 4919e29e9890e452
+TAG: 1433a5c0284c911163888dbd128e6874
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000
+AD: 01
+CT: db55d6da719fe0473538294e
+TAG: 5a8ab948ccd205a70c78e8fdf954693b
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000
+AD: 01
+CT: aea3c54272abc1b58ed34a536743f4da
+TAG: da10d98bfe23784cfdfd0af97b6d5b78
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000000000000000000003000000000000000000000000000000
+AD: 01
+CT: aa694c0cfe148100cb5c6e27a77a7ff7b4233d6af251d9faa3d84f7c0d1113f1
+TAG: 778c5b68356a1a6a6f3c14a8f96c35ca
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 01
+CT: 9ac909928bcde79c2afa885df9c035c85a9eab136f6f6ea11034456bd306ea3c5dd542f706fffe538b5f139fa9dc622e
+TAG: 26c0c0d146d38787ca0fcbc3f911577a
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000
+AD: 01
+CT: c56be9d61ecf6a31a6289cddc9b91aaf84cdb53a3913b825d6eb5e157906dfb0a308c6b0b095d6fd1a5b761ca7fa0e39ca92f38ae206eec844c0c4ab0c1c165e
+TAG: a60986309b99431a35dd8c5ebeef8375
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000
+AD: 010000000000000000000000
+CT: 47995b96
+TAG: 16b668094202cadde992e0c16205793c
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0300000000000000000000000000000004000000
+AD: 010000000000000000000000000000000200
+CT: 8fe25de75089e9f849150e57ab7f7810981cd319
+TAG: 89ca91ebc560709432fe9496746404cc
+
+KEY: 01000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 030000000000000000000000000000000400
+AD: 0100000000000000000000000000000002000000
+CT: b26d43ae158316ac37f41579ccf1d461274e
+TAG: 13b7c01d08dd6969d51d1bf0fbbdc4d2
diff --git a/src/crypto/cipher/test/aes_256_gcm_siv_tests.txt b/src/crypto/cipher/test/aes_256_gcm_siv_tests.txt
new file mode 100644
index 0000000..cd38e23
--- /dev/null
+++ b/src/crypto/cipher/test/aes_256_gcm_siv_tests.txt
@@ -0,0 +1,226 @@
+# Test vectors from
+# https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02#appendix-B
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 
+AD: 
+CT: 
+TAG: eb7ccf36eeff369241379c87cc08e4f0
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000
+AD: 
+CT: ab3f382a6f0fb4c3
+TAG: a0a69e07b73281f5cdfd034f646cfa08
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000
+AD: 
+CT: be8d81f033ca23b953da2197
+TAG: cdf3ba70da9c7cbd45f5140ba0cca9f1
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000
+AD: 
+CT: 46e05b7116dbe27aaeffe99892194072
+TAG: be19d78991c62130cf97f628c37c3eaa
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000000000000000000002000000000000000000000000000000
+AD: 
+CT: 23ddbe9ef342b03003f56d6b4a2e8aff035c7d7cfd705e1ab4502904254bb67a
+TAG: 16c5944034050657af7c0fec7efbc40f
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000
+AD: 
+CT: b104c8945f280e75b52c05c45a63d1872c7f0552b1501968d9913d71207d0433f978f1a3eecdf782016b77e8c9d3ff53
+TAG: abedb4841c20f3b05e61e0fd1fcaf3d0
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 
+CT: e3f2bd14f4c80c9cea4c90c81f0e4d7eedb87eb19a7c0cf5a5a95cd3e441a71083b1191d115e9a9ff008b93feeb5a86d012a3e0adb89de2d1e3225479022292f
+TAG: 3ced67f5e03bb476a738c1343926dc19
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000
+AD: 01
+CT: 4dca2c16c3b0413c
+TAG: ac9b952c76a6f8b5df315f88126daa1c
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000
+AD: 01
+CT: ee0ca9068b5b85dfe115a660
+TAG: 756d6155927271077d790a05390ecb71
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000
+AD: 01
+CT: 590edb785c0cb89d19f031fa7e7d4f91
+TAG: ac2c8f711c86dbecc8c7b663c5fbc1ea
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000000000000000000003000000000000000000000000000000
+AD: 01
+CT: dcf2024f5f98d463b82a8673c47dd82159748cac8bcc7c76b8cfa26029cb333c
+TAG: a9b406643e190e602fb104fbb842a1ac
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 01
+CT: 79216506b1ddadfe16366e4ec886d10dc9400b995259f74c0091f9b5a6add5680a612130f6c31ab833aa76d9b2be86de
+TAG: 3ddfe9ad2c350980942638d3f954ac6d
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000
+AD: 01
+CT: 9535eb67240c49f30a0de5a90670813fa615e71fcb4c522ca79d9a33459a22f8c6a56d650bf0b15eecdd706e7689cf6510a281724613fea76b5366b40574b1b9
+TAG: abcb59ee31d25ee8889b70d7c36f9a41
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000
+AD: 010000000000000000000000
+CT: 9611baa0
+TAG: 53daf2bc5916f7a6750f2432068dabee
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0300000000000000000000000000000004000000
+AD: 010000000000000000000000000000000200
+CT: 78e3a1b54daa6547f775f30c38a45e887aea5c87
+TAG: f65187d8c28adba364d659b627b16431
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 030000000000000000000000000000000400
+AD: 0100000000000000000000000000000002000000
+CT: c6d3d28704bf20067d62e1a3872d40dda44b
+TAG: 6ac0135a4379dbc67967ff55fd4d1f2f
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 
+AD: 
+CT: 
+TAG: eb7ccf36eeff369241379c87cc08e4f0
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000
+AD: 
+CT: ab3f382a6f0fb4c3
+TAG: a0a69e07b73281f5cdfd034f646cfa08
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000
+AD: 
+CT: be8d81f033ca23b953da2197
+TAG: cdf3ba70da9c7cbd45f5140ba0cca9f1
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000
+AD: 
+CT: 46e05b7116dbe27aaeffe99892194072
+TAG: be19d78991c62130cf97f628c37c3eaa
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0100000000000000000000000000000002000000000000000000000000000000
+AD: 
+CT: 23ddbe9ef342b03003f56d6b4a2e8aff035c7d7cfd705e1ab4502904254bb67a
+TAG: 16c5944034050657af7c0fec7efbc40f
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000
+AD: 
+CT: b104c8945f280e75b52c05c45a63d1872c7f0552b1501968d9913d71207d0433f978f1a3eecdf782016b77e8c9d3ff53
+TAG: abedb4841c20f3b05e61e0fd1fcaf3d0
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 
+CT: e3f2bd14f4c80c9cea4c90c81f0e4d7eedb87eb19a7c0cf5a5a95cd3e441a71083b1191d115e9a9ff008b93feeb5a86d012a3e0adb89de2d1e3225479022292f
+TAG: 3ced67f5e03bb476a738c1343926dc19
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000
+AD: 01
+CT: 4dca2c16c3b0413c
+TAG: ac9b952c76a6f8b5df315f88126daa1c
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000
+AD: 01
+CT: ee0ca9068b5b85dfe115a660
+TAG: 756d6155927271077d790a05390ecb71
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000
+AD: 01
+CT: 590edb785c0cb89d19f031fa7e7d4f91
+TAG: ac2c8f711c86dbecc8c7b663c5fbc1ea
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0200000000000000000000000000000003000000000000000000000000000000
+AD: 01
+CT: dcf2024f5f98d463b82a8673c47dd82159748cac8bcc7c76b8cfa26029cb333c
+TAG: a9b406643e190e602fb104fbb842a1ac
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000
+AD: 01
+CT: 79216506b1ddadfe16366e4ec886d10dc9400b995259f74c0091f9b5a6add5680a612130f6c31ab833aa76d9b2be86de
+TAG: 3ddfe9ad2c350980942638d3f954ac6d
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000
+AD: 01
+CT: 9535eb67240c49f30a0de5a90670813fa615e71fcb4c522ca79d9a33459a22f8c6a56d650bf0b15eecdd706e7689cf6510a281724613fea76b5366b40574b1b9
+TAG: abcb59ee31d25ee8889b70d7c36f9a41
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 02000000
+AD: 010000000000000000000000
+CT: 9611baa0
+TAG: 53daf2bc5916f7a6750f2432068dabee
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 0300000000000000000000000000000004000000
+AD: 010000000000000000000000000000000200
+CT: 78e3a1b54daa6547f775f30c38a45e887aea5c87
+TAG: f65187d8c28adba364d659b627b16431
+
+KEY: 0100000000000000000000000000000000000000000000000000000000000000
+NONCE: 03000000000000000000000000000000
+IN: 030000000000000000000000000000000400
+AD: 0100000000000000000000000000000002000000
+CT: c6d3d28704bf20067d62e1a3872d40dda44b
+TAG: 6ac0135a4379dbc67967ff55fd4d1f2f
diff --git a/src/crypto/cipher/tls_cbc.c b/src/crypto/cipher/tls_cbc.c
index dd6ab8c..eb56604 100644
--- a/src/crypto/cipher/tls_cbc.c
+++ b/src/crypto/cipher/tls_cbc.c
@@ -133,107 +133,32 @@
   /* mac_end is the index of |in| just after the end of the MAC. */
   unsigned mac_end = in_len;
   unsigned mac_start = mac_end - md_size;
-  /* scan_start contains the number of bytes that we can ignore because
-   * the MAC's position can only vary by 255 bytes. */
-  unsigned scan_start = 0;
-  unsigned i, j;
-  unsigned rotate_offset;
 
   assert(orig_len >= in_len);
   assert(in_len >= md_size);
   assert(md_size <= EVP_MAX_MD_SIZE);
 
+  /* scan_start contains the number of bytes that we can ignore because
+   * the MAC's position can only vary by 255 bytes. */
+  unsigned scan_start = 0;
   /* This information is public so it's safe to branch based on it. */
   if (orig_len > md_size + 255 + 1) {
     scan_start = orig_len - (md_size + 255 + 1);
   }
 
-  /* Ideally the next statement would be:
-   *
-   *   rotate_offset = (mac_start - scan_start) % md_size;
-   *
-   * However, division is not a constant-time operation (at least on Intel
-   * chips). Thus we enumerate the possible values of md_size and handle each
-   * separately. The value of |md_size| is public information (it's determined
-   * by the cipher suite in the ServerHello) so our timing can vary based on
-   * its value. */
-
-  rotate_offset = mac_start - scan_start;
-  /* rotate_offset can be, at most, 255 (bytes of padding) + 1 (padding length)
-   * + md_size = 256 + 48 (since SHA-384 is the largest hash) = 304. */
-  assert(rotate_offset <= 304);
-
-  /* Below is an SMT-LIB2 verification that the Barrett reductions below are
-   * correct within this range:
-   *
-   * (define-fun barrett (
-   *     (x (_ BitVec 32))
-   *     (mul (_ BitVec 32))
-   *     (shift (_ BitVec 32))
-   *     (divisor (_ BitVec 32)) ) (_ BitVec 32)
-   *   (let ((q (bvsub x (bvmul divisor (bvlshr (bvmul x mul) shift))) ))
-   *     (ite (bvuge q divisor)
-   *       (bvsub q divisor)
-   *       q)))
-   *
-   * (declare-fun x () (_ BitVec 32))
-   *
-   * (assert (or
-   *   (let (
-   *     (divisor (_ bv20 32))
-   *     (mul (_ bv25 32))
-   *     (shift (_ bv9 32))
-   *     (limit (_ bv853 32)))
-   *
-   *     (and (bvule x limit) (not (= (bvurem x divisor)
-   *                                  (barrett x mul shift divisor)))))
-   *
-   *   (let (
-   *     (divisor (_ bv48 32))
-   *     (mul (_ bv10 32))
-   *     (shift (_ bv9 32))
-   *     (limit (_ bv768 32)))
-   *
-   *     (and (bvule x limit) (not (= (bvurem x divisor)
-   *                                  (barrett x mul shift divisor)))))
-   * ))
-   *
-   * (check-sat)
-   * (get-model)
-   */
-
-  if (md_size == 16) {
-    rotate_offset &= 15;
-  } else if (md_size == 20) {
-    /* 1/20 is approximated as 25/512 and then Barrett reduction is used.
-     * Analytically, this is correct for 0 <= rotate_offset <= 853. */
-    unsigned q = (rotate_offset * 25) >> 9;
-    rotate_offset -= q * 20;
-    rotate_offset -=
-        constant_time_select(constant_time_ge(rotate_offset, 20), 20, 0);
-  } else if (md_size == 32) {
-    rotate_offset &= 31;
-  } else if (md_size == 48) {
-    /* 1/48 is approximated as 10/512 and then Barrett reduction is used.
-     * Analytically, this is correct for 0 <= rotate_offset <= 768. */
-    unsigned q = (rotate_offset * 10) >> 9;
-    rotate_offset -= q * 48;
-    rotate_offset -=
-        constant_time_select(constant_time_ge(rotate_offset, 48), 48, 0);
-  } else {
-    /* This should be impossible therefore this path doesn't run in constant
-     * time. */
-    assert(0);
-    rotate_offset = rotate_offset % md_size;
-  }
-
+  unsigned rotate_offset = 0;
+  uint8_t mac_started = 0;
   memset(rotated_mac, 0, md_size);
-  for (i = scan_start, j = 0; i < orig_len; i++) {
-    uint8_t mac_started = constant_time_ge_8(i, mac_start);
+  for (unsigned i = scan_start, j = 0; i < orig_len; i++, j++) {
+    if (j >= md_size) {
+      j -= md_size;
+    }
+    unsigned is_mac_start = constant_time_eq(i, mac_start);
+    mac_started |= is_mac_start;
     uint8_t mac_ended = constant_time_ge_8(i, mac_end);
-    uint8_t b = in[i];
-    rotated_mac[j++] |= b & mac_started & ~mac_ended;
-    j &= constant_time_lt(j, md_size);
+    rotated_mac[j] |= in[i] & mac_started & ~mac_ended;
+    /* Save the offset that |mac_start| is mapped to. */
+    rotate_offset |= j & is_mac_start;
   }
 
   /* Now rotate the MAC. We rotate in log(md_size) steps, one for each bit
@@ -243,7 +168,7 @@
     /* Rotate by |offset| iff the corresponding bit is set in
      * |rotate_offset|, placing the result in |rotated_mac_tmp|. */
     const uint8_t skip_rotate = (rotate_offset & 1) - 1;
-    for (i = 0, j = offset; i < md_size; i++, j++) {
+    for (unsigned i = 0, j = offset; i < md_size; i++, j++) {
       if (j >= md_size) {
         j -= md_size;
       }
diff --git a/src/crypto/dh/dh_test.cc b/src/crypto/dh/dh_test.cc
index 9a3d780..99bb945 100644
--- a/src/crypto/dh/dh_test.cc
+++ b/src/crypto/dh/dh_test.cc
@@ -68,7 +68,6 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-namespace bssl {
 
 static bool RunBasicTests();
 static bool RunRFC5114Tests();
@@ -76,7 +75,7 @@
 static bool TestASN1();
 static bool TestRFC3526();
 
-static int Main() {
+int main() {
   CRYPTO_library_init();
 
   if (!RunBasicTests() ||
@@ -568,7 +567,7 @@
     return false;
   }
 
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   uint8_t *der;
   size_t der_len;
   if (!CBB_init(cbb.get(), 0) ||
@@ -661,9 +660,3 @@
 
   return true;
 }
-
-}  // namespace bssl
-
-int main() {
-  return bssl::Main();
-}
diff --git a/src/crypto/digest/digest_test.cc b/src/crypto/digest/digest_test.cc
index ecf0308..0d3f16e 100644
--- a/src/crypto/digest/digest_test.cc
+++ b/src/crypto/digest/digest_test.cc
@@ -28,8 +28,6 @@
 #include "../internal.h"
 
 
-namespace bssl {
-
 struct MD {
   // name is the name of the digest.
   const char* name;
@@ -161,7 +159,7 @@
 }
 
 static int TestDigest(const TestVector *test) {
-  ScopedEVP_MD_CTX ctx;
+  bssl::ScopedEVP_MD_CTX ctx;
 
   // Test the input provided.
   if (!EVP_DigestInit_ex(ctx.get(), test->md.func(), NULL)) {
@@ -246,7 +244,7 @@
   return true;
 }
 
-static int Main() {
+int main() {
   CRYPTO_library_init();
 
   for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kTestVectors); i++) {
@@ -263,9 +261,3 @@
   printf("PASS\n");
   return 0;
 }
-
-}  // namespace bssl
-
-int main() {
-  return bssl::Main();
-}
diff --git a/src/crypto/ec/oct.c b/src/crypto/ec/oct.c
index bf1957c..4e8272d 100644
--- a/src/crypto/ec/oct.c
+++ b/src/crypto/ec/oct.c
@@ -284,7 +284,7 @@
                                              EC_POINT *point, const BIGNUM *x,
                                              int y_bit, BN_CTX *ctx) {
   if (BN_is_negative(x) || BN_cmp(x, &group->field) >= 0) {
-    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSION_BIT);
+    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
     return 0;
   }
 
@@ -381,19 +381,7 @@
 
   if (y_bit != BN_is_odd(y)) {
     if (BN_is_zero(y)) {
-      int kron;
-
-      kron = BN_kronecker(x, &group->field, ctx);
-      if (kron == -2) {
-        goto err;
-      }
-
-      if (kron == 1) {
-        OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSION_BIT);
-      } else {
-        /* BN_mod_sqrt() should have cought this error (not a square) */
-        OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
-      }
+      OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSION_BIT);
       goto err;
     }
     if (!BN_usub(y, &group->field, y)) {
diff --git a/src/crypto/ec/wnaf.c b/src/crypto/ec/wnaf.c
index ba2257c..1594354 100644
--- a/src/crypto/ec/wnaf.c
+++ b/src/crypto/ec/wnaf.c
@@ -90,10 +90,10 @@
  * with the exception that the most significant digit may be only
  * w-1 zeros away from that next non-zero digit.
  */
-static signed char *compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len) {
+static int8_t *compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len) {
   int window_val;
   int ok = 0;
-  signed char *r = NULL;
+  int8_t *r = NULL;
   int sign = 1;
   int bit, next_bit, mask;
   size_t len = 0, j;
@@ -109,9 +109,8 @@
     return r;
   }
 
-  if (w <= 0 || w > 7) /* 'signed char' can represent integers with absolute
-                          values less than 2^7 */
-  {
+  /* 'int8_t' can represent integers with absolute values less than 2^7. */
+  if (w <= 0 || w > 7) {
     OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
     goto err;
   }
@@ -129,20 +128,18 @@
   }
 
   len = BN_num_bits(scalar);
-  r = OPENSSL_malloc(
-      len +
-      1); /* modified wNAF may be one digit longer than binary representation
-           * (*ret_len will be set to the actual length, i.e. at most
-           * BN_num_bits(scalar) + 1) */
+  /* The modified wNAF may be one digit longer than binary representation
+   * (*ret_len will be set to the actual length, i.e. at most
+   * BN_num_bits(scalar) + 1). */
+  r = OPENSSL_malloc(len + 1);
   if (r == NULL) {
     OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
     goto err;
   }
   window_val = scalar->d[0] & mask;
   j = 0;
-  while ((window_val != 0) ||
-         (j + w + 1 < len)) /* if j+w+1 >= len, window_val will not increase */
-  {
+  /* If j+w+1 >= len, window_val will not increase. */
+  while (window_val != 0 || j + w + 1 < len) {
     int digit = 0;
 
     /* 0 <= window_val <= 2^(w+1) */
@@ -174,9 +171,8 @@
 
       window_val -= digit;
 
-      /* now window_val is 0 or 2^(w+1) in standard wNAF generation;
-       * for modified window NAFs, it may also be 2^w
-       */
+      /* Now window_val is 0 or 2^(w+1) in standard wNAF generation;
+       * for modified window NAFs, it may also be 2^w. */
       if (window_val != 0 && window_val != next_bit && window_val != bit) {
         OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
         goto err;
@@ -217,12 +213,29 @@
  *       sometimes smaller windows will give better performance
  *       (thus the boundaries should be increased)
  */
-#define EC_window_bits_for_scalar_size(b)                                      \
-  ((size_t)((b) >= 2000 ? 6 : (b) >= 800 ? 5 : (b) >= 300                      \
-                                                   ? 4                         \
-                                                   : (b) >= 70 ? 3 : (b) >= 20 \
-                                                                         ? 2   \
-                                                                         : 1))
+static size_t window_bits_for_scalar_size(size_t b) {
+  if (b >= 2000) {
+    return 6;
+  }
+
+  if (b >= 800) {
+    return 5;
+  }
+
+  if (b >= 300) {
+    return 4;
+  }
+
+  if (b >= 70) {
+    return 3;
+  }
+
+  if (b >= 20) {
+    return 2;
+  }
+
+  return 1;
+}
 
 int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
                 const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx) {
@@ -235,7 +248,7 @@
   int r_is_inverted = 0;
   int r_is_at_infinity = 1;
   size_t *wsize = NULL;      /* individual window sizes */
-  signed char **wNAF = NULL; /* individual wNAFs */
+  int8_t **wNAF = NULL; /* individual wNAFs */
   size_t *wNAF_len = NULL;
   size_t max_len = 0;
   size_t num_val;
@@ -294,7 +307,7 @@
     size_t bits;
 
     bits = i < num ? BN_num_bits(scalars[i]) : BN_num_bits(g_scalar);
-    wsize[i] = EC_window_bits_for_scalar_size(bits);
+    wsize[i] = window_bits_for_scalar_size(bits);
     num_val += (size_t)1 << (wsize[i] - 1);
     wNAF[i + 1] = NULL; /* make sure we always have a pivot */
     wNAF[i] =
@@ -364,7 +377,7 @@
     }
   }
 
-#if 1 /* optional; EC_window_bits_for_scalar_size assumes we do this step */
+#if 1 /* optional; window_bits_for_scalar_size assumes we do this step */
   if (!EC_POINTs_make_affine(group, num_val, val, ctx)) {
     goto err;
   }
@@ -429,7 +442,7 @@
   OPENSSL_free(wsize);
   OPENSSL_free(wNAF_len);
   if (wNAF != NULL) {
-    signed char **w;
+    int8_t **w;
 
     for (w = wNAF; *w != NULL; w++) {
       OPENSSL_free(*w);
diff --git a/src/crypto/err/ssl.errordata b/src/crypto/err/ssl.errordata
index b50f9ab..e9b2066 100644
--- a/src/crypto/err/ssl.errordata
+++ b/src/crypto/err/ssl.errordata
@@ -109,6 +109,7 @@
 SSL,193,PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE
 SSL,267,PRE_SHARED_KEY_MUST_BE_LAST
 SSL,194,PROTOCOL_IS_SHUTDOWN
+SSL,271,PSK_IDENTITY_BINDER_COUNT_MISMATCH
 SSL,195,PSK_IDENTITY_NOT_FOUND
 SSL,196,PSK_NO_CLIENT_CB
 SSL,197,PSK_NO_SERVER_CB
@@ -170,6 +171,7 @@
 SSL,219,TOO_MANY_EMPTY_FRAGMENTS
 SSL,260,TOO_MANY_KEY_UPDATES
 SSL,220,TOO_MANY_WARNING_ALERTS
+SSL,270,TOO_MUCH_SKIPPED_EARLY_DATA
 SSL,221,UNABLE_TO_FIND_ECDH_PARAMETERS
 SSL,222,UNEXPECTED_EXTENSION
 SSL,223,UNEXPECTED_MESSAGE
diff --git a/src/crypto/evp/evp_extra_test.cc b/src/crypto/evp/evp_extra_test.cc
index 755fa83..4d41760 100644
--- a/src/crypto/evp/evp_extra_test.cc
+++ b/src/crypto/evp/evp_extra_test.cc
@@ -27,7 +27,6 @@
 #include <openssl/pkcs8.h>
 #include <openssl/rsa.h>
 
-namespace bssl {
 
 // kExampleRSAKeyDER is an RSA private key in ASN.1, DER format. Of course, you
 // should never use this key anywhere but in an example.
@@ -371,7 +370,7 @@
 
 static bool TestEVP_DigestSignInit(void) {
   bssl::UniquePtr<EVP_PKEY> pkey = LoadExampleRSAKey();
-  ScopedEVP_MD_CTX md_ctx;
+  bssl::ScopedEVP_MD_CTX md_ctx;
   if (!pkey ||
       !EVP_DigestSignInit(md_ctx.get(), NULL, EVP_sha256(), NULL, pkey.get()) ||
       !EVP_DigestSignUpdate(md_ctx.get(), kMsg, sizeof(kMsg))) {
@@ -409,7 +408,7 @@
 
 static bool TestEVP_DigestVerifyInit(void) {
   bssl::UniquePtr<EVP_PKEY> pkey = LoadExampleRSAKey();
-  ScopedEVP_MD_CTX md_ctx;
+  bssl::ScopedEVP_MD_CTX md_ctx;
   if (!pkey ||
       !EVP_DigestVerifyInit(md_ctx.get(), NULL, EVP_sha256(), NULL,
                             pkey.get()) ||
@@ -591,7 +590,7 @@
   if (!empty) {
     return false;
   }
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   if (EVP_marshal_public_key(cbb.get(), empty.get())) {
     fprintf(stderr, "Marshalled empty public key.\n");
     return false;
@@ -670,7 +669,7 @@
   return true;
 }
 
-static int Main(void) {
+int main() {
   CRYPTO_library_init();
 
   if (!TestEVP_DigestSignInit()) {
@@ -718,9 +717,3 @@
   printf("PASS\n");
   return 0;
 }
-
-}  // namespace bssl
-
-int main() {
-  return bssl::Main();
-}
diff --git a/src/crypto/evp/evp_test.cc b/src/crypto/evp/evp_test.cc
index 68b869a..bfaa38a 100644
--- a/src/crypto/evp/evp_test.cc
+++ b/src/crypto/evp/evp_test.cc
@@ -75,7 +75,6 @@
 
 #include "../test/file_test.h"
 
-namespace bssl {
 
 // evp_test dispatches between multiple test types. PrivateKey tests take a key
 // name parameter and single block, decode it as a PEM private key, and save it
@@ -141,7 +140,7 @@
   }
 
   // The key must re-encode correctly.
-  ScopedCBB cbb;
+  bssl::ScopedCBB cbb;
   uint8_t *der;
   size_t der_len;
   if (!CBB_init(cbb.get(), 0) ||
@@ -253,7 +252,7 @@
   return true;
 }
 
-static int Main(int argc, char *argv[]) {
+int main(int argc, char *argv[]) {
   CRYPTO_library_init();
   if (argc != 2) {
     fprintf(stderr, "%s <test file.txt>\n", argv[0]);
@@ -263,9 +262,3 @@
   KeyMap map;
   return FileTestMain(TestEVP, &map, argv[1]);
 }
-
-}  // namespace bssl
-
-int main(int argc, char *argv[]) {
-  return bssl::Main(argc, argv);
-}
diff --git a/src/crypto/hmac/hmac_test.cc b/src/crypto/hmac/hmac_test.cc
index 60a9581..7b216e2 100644
--- a/src/crypto/hmac/hmac_test.cc
+++ b/src/crypto/hmac/hmac_test.cc
@@ -67,7 +67,6 @@
 
 #include "../test/file_test.h"
 
-namespace bssl {
 
 static const EVP_MD *GetDigest(const std::string &name) {
   if (name == "MD5") {
@@ -117,7 +116,7 @@
   }
 
   // Test using HMAC_CTX.
-  ScopedHMAC_CTX ctx;
+  bssl::ScopedHMAC_CTX ctx;
   if (!HMAC_Init_ex(ctx.get(), key.data(), key.size(), digest, nullptr) ||
       !HMAC_Update(ctx.get(), input.data(), input.size()) ||
       !HMAC_Final(ctx.get(), mac.get(), &mac_len) ||
@@ -158,7 +157,7 @@
   return true;
 }
 
-static int Main(int argc, char *argv[]) {
+int main(int argc, char *argv[]) {
   CRYPTO_library_init();
 
   if (argc != 2) {
@@ -168,9 +167,3 @@
 
   return FileTestMain(TestHMAC, nullptr, argv[1]);
 }
-
-}  // namespace bssl
-
-int main(int argc, char **argv) {
-  return bssl::Main(argc, argv);
-}
diff --git a/src/crypto/modes/CMakeLists.txt b/src/crypto/modes/CMakeLists.txt
index 17faa15..dc9e504 100644
--- a/src/crypto/modes/CMakeLists.txt
+++ b/src/crypto/modes/CMakeLists.txt
@@ -48,10 +48,11 @@
   OBJECT
 
   cbc.c
-  ctr.c
-  ofb.c
   cfb.c
+  ctr.c
   gcm.c
+  ofb.c
+  polyval.c
 
   ${MODES_ARCH_SOURCES}
 )
diff --git a/src/crypto/modes/cbc.c b/src/crypto/modes/cbc.c
index e41f2b4..6e9fe24 100644
--- a/src/crypto/modes/cbc.c
+++ b/src/crypto/modes/cbc.c
@@ -52,10 +52,6 @@
 #include "internal.h"
 
 
-#ifndef STRICT_ALIGNMENT
-#  define STRICT_ALIGNMENT 0
-#endif
-
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                            const void *key, uint8_t ivec[16],
                            block128_f block) {
diff --git a/src/crypto/modes/gcm.c b/src/crypto/modes/gcm.c
index eb63aa0..3b793e8 100644
--- a/src/crypto/modes/gcm.c
+++ b/src/crypto/modes/gcm.c
@@ -65,14 +65,6 @@
 #define GHASH_ASM
 #endif
 
-#if defined(BSWAP4) && STRICT_ALIGNMENT == 1
-/* redefine, because alignment is ensured */
-#undef GETU32
-#define GETU32(p) BSWAP4(*(const uint32_t *)(p))
-#undef PUTU32
-#define PUTU32(p, v) *(uint32_t *)(p) = BSWAP4(v)
-#endif
-
 #define PACK(s) ((size_t)(s) << (sizeof(size_t) * 8 - 16))
 #define REDUCE1BIT(V)                                                 \
   do {                                                                \
@@ -121,27 +113,10 @@
   Htable[15].hi = V.hi ^ Htable[7].hi, Htable[15].lo = V.lo ^ Htable[7].lo;
 
 #if defined(GHASH_ASM) && defined(OPENSSL_ARM)
-  /* ARM assembler expects specific dword order in Htable. */
-  {
-    int j;
-    const union {
-      long one;
-      char little;
-    } is_endian = {1};
-
-    if (is_endian.little) {
-      for (j = 0; j < 16; ++j) {
-        V = Htable[j];
-        Htable[j].hi = V.lo;
-        Htable[j].lo = V.hi;
-      }
-    } else {
-      for (j = 0; j < 16; ++j) {
-        V = Htable[j];
-        Htable[j].hi = V.lo << 32 | V.lo >> 32;
-        Htable[j].lo = V.hi << 32 | V.hi >> 32;
-      }
-    }
+  for (int j = 0; j < 16; ++j) {
+    V = Htable[j];
+    Htable[j].hi = V.lo;
+    Htable[j].lo = V.hi;
   }
 #endif
 }
@@ -157,10 +132,6 @@
   u128 Z;
   int cnt = 15;
   size_t rem, nlo, nhi;
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
 
   nlo = ((const uint8_t *)Xi)[15];
   nhi = nlo >> 4;
@@ -203,26 +174,8 @@
     Z.lo ^= Htable[nlo].lo;
   }
 
-  if (is_endian.little) {
-#ifdef BSWAP8
-    Xi[0] = BSWAP8(Z.hi);
-    Xi[1] = BSWAP8(Z.lo);
-#else
-    uint8_t *p = (uint8_t *)Xi;
-    uint32_t v;
-    v = (uint32_t)(Z.hi >> 32);
-    PUTU32(p, v);
-    v = (uint32_t)(Z.hi);
-    PUTU32(p + 4, v);
-    v = (uint32_t)(Z.lo >> 32);
-    PUTU32(p + 8, v);
-    v = (uint32_t)(Z.lo);
-    PUTU32(p + 12, v);
-#endif
-  } else {
-    Xi[0] = Z.hi;
-    Xi[1] = Z.lo;
-  }
+  Xi[0] = CRYPTO_bswap8(Z.hi);
+  Xi[1] = CRYPTO_bswap8(Z.lo);
 }
 
 /* Streamed gcm_mult_4bit, see CRYPTO_gcm128_[en|de]crypt for
@@ -230,15 +183,11 @@
  * performance improvement, at least not on x86[_64]. It's here
  * mostly as reference and a placeholder for possible future
  * non-trivial optimization[s]... */
-static void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                           size_t len) {
+static void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16],
+                           const uint8_t *inp, size_t len) {
   u128 Z;
   int cnt;
   size_t rem, nlo, nhi;
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
 
   do {
     cnt = 15;
@@ -285,26 +234,8 @@
       Z.lo ^= Htable[nlo].lo;
     }
 
-    if (is_endian.little) {
-#ifdef BSWAP8
-      Xi[0] = BSWAP8(Z.hi);
-      Xi[1] = BSWAP8(Z.lo);
-#else
-      uint8_t *p = (uint8_t *)Xi;
-      uint32_t v;
-      v = (uint32_t)(Z.hi >> 32);
-      PUTU32(p, v);
-      v = (uint32_t)(Z.hi);
-      PUTU32(p + 4, v);
-      v = (uint32_t)(Z.lo >> 32);
-      PUTU32(p + 8, v);
-      v = (uint32_t)(Z.lo);
-      PUTU32(p + 12, v);
-#endif
-    } else {
-      Xi[0] = Z.hi;
-      Xi[1] = Z.lo;
-    }
+    Xi[0] = CRYPTO_bswap8(Z.hi);
+    Xi[1] = CRYPTO_bswap8(Z.lo);
   } while (inp += 16, len -= 16);
 }
 #else /* GHASH_ASM */
@@ -425,96 +356,88 @@
 #endif
 #endif
 
-void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, const void *key,
-                        block128_f block) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
+void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
+                       u128 out_table[16], const uint8_t *gcm_key) {
+  union {
+    uint64_t u[2];
+    uint8_t c[16];
+  } H;
 
-  memset(ctx, 0, sizeof(*ctx));
-  ctx->block = block;
+  memcpy(H.c, gcm_key, 16);
 
-  (*block)(ctx->H.c, ctx->H.c, key);
-
-  if (is_endian.little) {
-/* H is stored in host byte order */
-#ifdef BSWAP8
-    ctx->H.u[0] = BSWAP8(ctx->H.u[0]);
-    ctx->H.u[1] = BSWAP8(ctx->H.u[1]);
-#else
-    uint8_t *p = ctx->H.c;
-    uint64_t hi, lo;
-    hi = (uint64_t)GETU32(p) << 32 | GETU32(p + 4);
-    lo = (uint64_t)GETU32(p + 8) << 32 | GETU32(p + 12);
-    ctx->H.u[0] = hi;
-    ctx->H.u[1] = lo;
-#endif
-  }
+  /* H is stored in host byte order */
+  H.u[0] = CRYPTO_bswap8(H.u[0]);
+  H.u[1] = CRYPTO_bswap8(H.u[1]);
 
 #if defined(GHASH_ASM_X86_OR_64)
   if (crypto_gcm_clmul_enabled()) {
     if (((OPENSSL_ia32cap_P[1] >> 22) & 0x41) == 0x41) { /* AVX+MOVBE */
-      gcm_init_avx(ctx->Htable, ctx->H.u);
-      ctx->gmult = gcm_gmult_avx;
-      ctx->ghash = gcm_ghash_avx;
-    } else {
-      gcm_init_clmul(ctx->Htable, ctx->H.u);
-      ctx->gmult = gcm_gmult_clmul;
-      ctx->ghash = gcm_ghash_clmul;
+      gcm_init_avx(out_table, H.u);
+      *out_mult = gcm_gmult_avx;
+      *out_hash = gcm_ghash_avx;
+      return;
     }
+
+    gcm_init_clmul(out_table, H.u);
+    *out_mult = gcm_gmult_clmul;
+    *out_hash = gcm_ghash_clmul;
     return;
   }
-  gcm_init_4bit(ctx->Htable, ctx->H.u);
 #if defined(GHASH_ASM_X86) /* x86 only */
   if (OPENSSL_ia32cap_P[0] & (1 << 25)) { /* check SSE bit */
-    ctx->gmult = gcm_gmult_4bit_mmx;
-    ctx->ghash = gcm_ghash_4bit_mmx;
-  } else {
-    ctx->gmult = gcm_gmult_4bit_x86;
-    ctx->ghash = gcm_ghash_4bit_x86;
+    gcm_init_4bit(out_table, H.u);
+    *out_mult = gcm_gmult_4bit_mmx;
+    *out_hash = gcm_ghash_4bit_mmx;
+    return;
   }
-#else
-  ctx->gmult = gcm_gmult_4bit;
-  ctx->ghash = gcm_ghash_4bit;
 #endif
 #elif defined(GHASH_ASM_ARM)
   if (pmull_capable()) {
-    gcm_init_v8(ctx->Htable, ctx->H.u);
-    ctx->gmult = gcm_gmult_v8;
-    ctx->ghash = gcm_ghash_v8;
-  } else if (neon_capable()) {
-    gcm_init_neon(ctx->Htable,ctx->H.u);
-    ctx->gmult = gcm_gmult_neon;
-    ctx->ghash = gcm_ghash_neon;
-  } else {
-    gcm_init_4bit(ctx->Htable, ctx->H.u);
-    ctx->gmult = gcm_gmult_4bit;
-    ctx->ghash = gcm_ghash_4bit;
+    gcm_init_v8(out_table, H.u);
+    *out_mult = gcm_gmult_v8;
+    *out_hash = gcm_ghash_v8;
+    return;
+  }
+
+  if (neon_capable()) {
+    gcm_init_neon(out_table, H.u);
+    *out_mult = gcm_gmult_neon;
+    *out_hash = gcm_ghash_neon;
+    return;
   }
 #elif defined(GHASH_ASM_PPC64LE)
   if (CRYPTO_is_PPC64LE_vcrypto_capable()) {
-    gcm_init_p8(ctx->Htable, ctx->H.u);
-    ctx->gmult = gcm_gmult_p8;
-    ctx->ghash = gcm_ghash_p8;
-  } else {
-    gcm_init_4bit(ctx->Htable, ctx->H.u);
-    ctx->gmult = gcm_gmult_4bit;
-    ctx->ghash = gcm_ghash_4bit;
+    gcm_init_p8(out_table, H.u);
+    *out_mult = gcm_gmult_p8;
+    *out_hash = gcm_ghash_p8;
+    return;
   }
-#else
-  gcm_init_4bit(ctx->Htable, ctx->H.u);
-  ctx->gmult = gcm_gmult_4bit;
-  ctx->ghash = gcm_ghash_4bit;
 #endif
+
+  gcm_init_4bit(out_table, H.u);
+#if defined(GHASH_ASM_X86)
+  *out_mult = gcm_gmult_4bit_x86;
+  *out_hash = gcm_ghash_4bit_x86;
+#else
+  *out_mult = gcm_gmult_4bit;
+  *out_hash = gcm_ghash_4bit;
+#endif
+}
+
+void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, const void *aes_key,
+                        block128_f block) {
+  memset(ctx, 0, sizeof(*ctx));
+  ctx->block = block;
+
+  uint8_t gcm_key[16];
+  memset(gcm_key, 0, sizeof(gcm_key));
+  (*block)(gcm_key, gcm_key, aes_key);
+
+  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, ctx->Htable, gcm_key);
 }
 
 void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
                          const uint8_t *iv, size_t len) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   unsigned int ctr;
 #ifdef GCM_FUNCREF_4BIT
   void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
@@ -551,39 +474,15 @@
       GCM_MUL(ctx, Yi);
     }
     len0 <<= 3;
-    if (is_endian.little) {
-#ifdef BSWAP8
-      ctx->Yi.u[1] ^= BSWAP8(len0);
-#else
-      ctx->Yi.c[8] ^= (uint8_t)(len0 >> 56);
-      ctx->Yi.c[9] ^= (uint8_t)(len0 >> 48);
-      ctx->Yi.c[10] ^= (uint8_t)(len0 >> 40);
-      ctx->Yi.c[11] ^= (uint8_t)(len0 >> 32);
-      ctx->Yi.c[12] ^= (uint8_t)(len0 >> 24);
-      ctx->Yi.c[13] ^= (uint8_t)(len0 >> 16);
-      ctx->Yi.c[14] ^= (uint8_t)(len0 >> 8);
-      ctx->Yi.c[15] ^= (uint8_t)(len0);
-#endif
-    } else {
-      ctx->Yi.u[1] ^= len0;
-    }
+    ctx->Yi.u[1] ^= CRYPTO_bswap8(len0);
 
     GCM_MUL(ctx, Yi);
-
-    if (is_endian.little) {
-      ctr = GETU32(ctx->Yi.c + 12);
-    } else {
-      ctr = ctx->Yi.d[3];
-    }
+    ctr = GETU32_aligned(ctx->Yi.c + 12);
   }
 
   (*ctx->block)(ctx->Yi.c, ctx->EK0.c, key);
   ++ctr;
-  if (is_endian.little) {
-    PUTU32(ctx->Yi.c + 12, ctr);
-  } else {
-    ctx->Yi.d[3] = ctr;
-  }
+  PUTU32_aligned(ctx->Yi.c + 12, ctr);
 }
 
 int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx, const uint8_t *aad, size_t len) {
@@ -656,10 +555,6 @@
 int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
                           const unsigned char *in, unsigned char *out,
                           size_t len) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
   block128_f block = ctx->block;
@@ -684,11 +579,7 @@
     ctx->ares = 0;
   }
 
-  if (is_endian.little) {
-    ctr = GETU32(ctx->Yi.c + 12);
-  } else {
-    ctr = ctx->Yi.d[3];
-  }
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
 
   n = ctx->mres;
   if (n) {
@@ -709,11 +600,7 @@
       if (n == 0) {
         (*block)(ctx->Yi.c, ctx->EKi.c, key);
         ++ctr;
-        if (is_endian.little) {
-          PUTU32(ctx->Yi.c + 12, ctr);
-        } else {
-          ctx->Yi.d[3] = ctr;
-        }
+        PUTU32_aligned(ctx->Yi.c + 12, ctr);
       }
       ctx->Xi.c[n] ^= out[i] = in[i] ^ ctx->EKi.c[n];
       n = (n + 1) % 16;
@@ -735,11 +622,7 @@
 
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
-      if (is_endian.little) {
-        PUTU32(ctx->Yi.c + 12, ctr);
-      } else {
-        ctx->Yi.d[3] = ctr;
-      }
+      PUTU32_aligned(ctx->Yi.c + 12, ctr);
       for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
         out_t[i] = in_t[i] ^ ctx->EKi.t[i];
       }
@@ -758,11 +641,7 @@
 
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
-      if (is_endian.little) {
-        PUTU32(ctx->Yi.c + 12, ctr);
-      } else {
-        ctx->Yi.d[3] = ctr;
-      }
+      PUTU32_aligned(ctx->Yi.c + 12, ctr);
       for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
         out_t[i] = in_t[i] ^ ctx->EKi.t[i];
       }
@@ -779,11 +658,7 @@
 
     (*block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
       ctx->Xi.t[i] ^= out_t[i] = in_t[i] ^ ctx->EKi.t[i];
     }
@@ -796,11 +671,7 @@
   if (len) {
     (*block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     while (len--) {
       ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
       ++n;
@@ -814,10 +685,6 @@
 int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
                           const unsigned char *in, unsigned char *out,
                           size_t len) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
   block128_f block = ctx->block;
@@ -842,11 +709,7 @@
     ctx->ares = 0;
   }
 
-  if (is_endian.little) {
-    ctr = GETU32(ctx->Yi.c + 12);
-  } else {
-    ctr = ctx->Yi.d[3];
-  }
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
 
   n = ctx->mres;
   if (n) {
@@ -870,11 +733,7 @@
       if (n == 0) {
         (*block)(ctx->Yi.c, ctx->EKi.c, key);
         ++ctr;
-        if (is_endian.little) {
-          PUTU32(ctx->Yi.c + 12, ctr);
-        } else {
-          ctx->Yi.d[3] = ctr;
-        }
+        PUTU32_aligned(ctx->Yi.c + 12, ctr);
       }
       c = in[i];
       out[i] = c ^ ctx->EKi.c[n];
@@ -899,11 +758,7 @@
 
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
-      if (is_endian.little) {
-        PUTU32(ctx->Yi.c + 12, ctr);
-      } else {
-        ctx->Yi.d[3] = ctr;
-      }
+      PUTU32_aligned(ctx->Yi.c + 12, ctr);
       for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
         out_t[i] = in_t[i] ^ ctx->EKi.t[i];
       }
@@ -922,11 +777,7 @@
 
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
-      if (is_endian.little) {
-        PUTU32(ctx->Yi.c + 12, ctr);
-      } else {
-        ctx->Yi.d[3] = ctr;
-      }
+      PUTU32_aligned(ctx->Yi.c + 12, ctr);
       for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
         out_t[i] = in_t[i] ^ ctx->EKi.t[i];
       }
@@ -942,11 +793,7 @@
 
     (*block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
       size_t c = in_t[i];
       out_t[i] = c ^ ctx->EKi.t[i];
@@ -961,11 +808,7 @@
   if (len) {
     (*block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     while (len--) {
       uint8_t c = in[n];
       ctx->Xi.c[n] ^= c;
@@ -981,10 +824,6 @@
 int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
 #ifdef GCM_FUNCREF_4BIT
@@ -1034,21 +873,13 @@
   }
 #endif
 
-  if (is_endian.little) {
-    ctr = GETU32(ctx->Yi.c + 12);
-  } else {
-    ctr = ctx->Yi.d[3];
-  }
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
 
 #if defined(GHASH)
   while (len >= GHASH_CHUNK) {
     (*stream)(in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
     ctr += GHASH_CHUNK / 16;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     GHASH(ctx, out, GHASH_CHUNK);
     out += GHASH_CHUNK;
     in += GHASH_CHUNK;
@@ -1061,11 +892,7 @@
 
     (*stream)(in, out, j, key, ctx->Yi.c);
     ctr += (unsigned int)j;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     in += i;
     len -= i;
 #if defined(GHASH)
@@ -1084,11 +911,7 @@
   if (len) {
     (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     while (len--) {
       ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
       ++n;
@@ -1102,10 +925,6 @@
 int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
 #ifdef GCM_FUNCREF_4BIT
@@ -1157,22 +976,14 @@
   }
 #endif
 
-  if (is_endian.little) {
-    ctr = GETU32(ctx->Yi.c + 12);
-  } else {
-    ctr = ctx->Yi.d[3];
-  }
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
 
 #if defined(GHASH)
   while (len >= GHASH_CHUNK) {
     GHASH(ctx, in, GHASH_CHUNK);
     (*stream)(in, out, GHASH_CHUNK / 16, key, ctx->Yi.c);
     ctr += GHASH_CHUNK / 16;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     out += GHASH_CHUNK;
     in += GHASH_CHUNK;
     len -= GHASH_CHUNK;
@@ -1198,11 +1009,7 @@
 #endif
     (*stream)(in, out, j, key, ctx->Yi.c);
     ctr += (unsigned int)j;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     out += i;
     in += i;
     len -= i;
@@ -1210,11 +1017,7 @@
   if (len) {
     (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
-    if (is_endian.little) {
-      PUTU32(ctx->Yi.c + 12, ctr);
-    } else {
-      ctx->Yi.d[3] = ctr;
-    }
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
     while (len--) {
       uint8_t c = in[n];
       ctx->Xi.c[n] ^= c;
@@ -1228,10 +1031,6 @@
 }
 
 int CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx, const uint8_t *tag, size_t len) {
-  const union {
-    long one;
-    char little;
-  } is_endian = {1};
   uint64_t alen = ctx->len.u[0] << 3;
   uint64_t clen = ctx->len.u[1] << 3;
 #ifdef GCM_FUNCREF_4BIT
@@ -1242,20 +1041,8 @@
     GCM_MUL(ctx, Xi);
   }
 
-  if (is_endian.little) {
-#ifdef BSWAP8
-    alen = BSWAP8(alen);
-    clen = BSWAP8(clen);
-#else
-    uint8_t *p = ctx->len.c;
-
-    ctx->len.u[0] = alen;
-    ctx->len.u[1] = clen;
-
-    alen = (uint64_t)GETU32(p) << 32 | GETU32(p + 4);
-    clen = (uint64_t)GETU32(p + 8) << 32 | GETU32(p + 12);
-#endif
-  }
+  alen = CRYPTO_bswap8(alen);
+  clen = CRYPTO_bswap8(clen);
 
   ctx->Xi.u[0] ^= alen;
   ctx->Xi.u[1] ^= clen;
diff --git a/src/crypto/modes/gcm_test.cc b/src/crypto/modes/gcm_test.cc
index 51d966e..8baf20e 100644
--- a/src/crypto/modes/gcm_test.cc
+++ b/src/crypto/modes/gcm_test.cc
@@ -46,6 +46,13 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  * ==================================================================== */
 
+/* Per C99, various stdint.h and inttypes.h macros (the latter used by
+ * internal.h) are unavailable in C++ unless some macros are defined. C++11
+ * overruled this decision, but older Android NDKs still require it. */
+#if !defined(__STDC_CONSTANT_MACROS)
+#define __STDC_CONSTANT_MACROS
+#endif
+
 #include <stdio.h>
 #include <string.h>
 
@@ -388,12 +395,22 @@
   return ret;
 }
 
+static bool TestByteSwap() {
+  return CRYPTO_bswap4(0x01020304) == 0x04030201 &&
+         CRYPTO_bswap8(UINT64_C(0x0102030405060708)) ==
+             UINT64_C(0x0807060504030201);
+}
+
 int main(void) {
   int ret = 0;
   unsigned i;
 
   CRYPTO_library_init();
 
+  if (!TestByteSwap()) {
+    ret = 1;
+  }
+
   for (i = 0; i < sizeof(test_cases) / sizeof(struct test_case); i++) {
     if (!run_test_case(i, &test_cases[i])) {
       ret = 1;
diff --git a/src/crypto/modes/internal.h b/src/crypto/modes/internal.h
index 430d040..a53da04 100644
--- a/src/crypto/modes/internal.h
+++ b/src/crypto/modes/internal.h
@@ -51,6 +51,8 @@
 
 #include <openssl/base.h>
 
+#include <string.h>
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -64,90 +66,58 @@
 #define STRICT_ALIGNMENT 0
 #endif
 
-#if !defined(PEDANTIC) && !defined(OPENSSL_NO_ASM)
 #if defined(__GNUC__) && __GNUC__ >= 2
-#if defined(OPENSSL_X86_64)
-#define BSWAP8(x)                 \
-  ({                              \
-    uint64_t ret = (x);           \
-    asm("bswapq %0" : "+r"(ret)); \
-    ret;                          \
-  })
-#define BSWAP4(x)                 \
-  ({                              \
-    uint32_t ret = (x);           \
-    asm("bswapl %0" : "+r"(ret)); \
-    ret;                          \
-  })
-#elif defined(OPENSSL_X86)
-#define BSWAP8(x)                                     \
-  ({                                                  \
-    uint32_t lo = (uint64_t)(x) >> 32, hi = (x);      \
-    asm("bswapl %0; bswapl %1" : "+r"(hi), "+r"(lo)); \
-    (uint64_t) hi << 32 | lo;                         \
-  })
-#define BSWAP4(x)                 \
-  ({                              \
-    uint32_t ret = (x);           \
-    asm("bswapl %0" : "+r"(ret)); \
-    ret;                          \
-  })
-#elif defined(OPENSSL_AARCH64)
-#define BSWAP8(x)                          \
-  ({                                       \
-    uint64_t ret;                          \
-    asm("rev %0,%1" : "=r"(ret) : "r"(x)); \
-    ret;                                   \
-  })
-#define BSWAP4(x)                            \
-  ({                                         \
-    uint32_t ret;                            \
-    asm("rev %w0,%w1" : "=r"(ret) : "r"(x)); \
-    ret;                                     \
-  })
-#elif defined(OPENSSL_ARM) && !defined(STRICT_ALIGNMENT)
-#define BSWAP8(x)                                     \
-  ({                                                  \
-    uint32_t lo = (uint64_t)(x) >> 32, hi = (x);      \
-    asm("rev %0,%0; rev %1,%1" : "+r"(hi), "+r"(lo)); \
-    (uint64_t) hi << 32 | lo;                         \
-  })
-#define BSWAP4(x)                                      \
-  ({                                                   \
-    uint32_t ret;                                      \
-    asm("rev %0,%1" : "=r"(ret) : "r"((uint32_t)(x))); \
-    ret;                                               \
-  })
-#endif
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return __builtin_bswap32(x);
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return __builtin_bswap64(x);
+}
 #elif defined(_MSC_VER)
-#if _MSC_VER >= 1300
 OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <intrin.h>
 OPENSSL_MSVC_PRAGMA(warning(pop))
 #pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
-#define BSWAP8(x) _byteswap_uint64((uint64_t)(x))
-#define BSWAP4(x) _byteswap_ulong((uint32_t)(x))
-#elif defined(OPENSSL_X86)
-__inline uint32_t _bswap4(uint32_t val) {
-  _asm mov eax, val
-  _asm bswap eax
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return _byteswap_ulong(x);
 }
-#define BSWAP4(x) _bswap4(x)
-#endif
-#endif
-#endif
 
-#if defined(BSWAP4) && !defined(STRICT_ALIGNMENT)
-#define GETU32(p) BSWAP4(*(const uint32_t *)(p))
-#define PUTU32(p, v) *(uint32_t *)(p) = BSWAP4(v)
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return _byteswap_uint64(x);
+}
 #else
-#define GETU32(p) \
-  ((uint32_t)(p)[0] << 24 | (uint32_t)(p)[1] << 16 | (uint32_t)(p)[2] << 8 | (uint32_t)(p)[3])
-#define PUTU32(p, v)                                   \
-  ((p)[0] = (uint8_t)((v) >> 24), (p)[1] = (uint8_t)((v) >> 16), \
-   (p)[2] = (uint8_t)((v) >> 8), (p)[3] = (uint8_t)(v))
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  x = (x >> 16) | (x << 16);
+  x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
+  return x;
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return CRYPTO_bswap4(x >> 32) | (((uint64_t)CRYPTO_bswap4(x)) << 32);
+}
 #endif
 
+static inline uint32_t GETU32(const void *in) {
+  uint32_t v;
+  memcpy(&v, in, sizeof(v));
+  return CRYPTO_bswap4(v);
+}
+
+static inline void PUTU32(void *out, uint32_t v) {
+  v = CRYPTO_bswap4(v);
+  memcpy(out, &v, sizeof(v));
+}
+
+static inline uint32_t GETU32_aligned(const void *in) {
+  const char *alias = (const char *) in;
+  return CRYPTO_bswap4(*((const uint32_t *) alias));
+}
+
+static inline void PUTU32_aligned(void *in, uint32_t v) {
+  char *alias = (char *) in;
+  *((uint32_t *) alias) = CRYPTO_bswap4(v);
+}
 
 /* block128_f is the type of a 128-bit, block cipher. */
 typedef void (*block128_f)(const uint8_t in[16], uint8_t out[16],
@@ -156,6 +126,16 @@
 /* GCM definitions */
 typedef struct { uint64_t hi,lo; } u128;
 
+/* gmult_func multiplies |Xi| by the GCM key and writes the result back to
+ * |Xi|. */
+typedef void (*gmult_func)(uint64_t Xi[2], const u128 Htable[16]);
+
+/* ghash_func repeatedly multiplies |Xi| by the GCM key and adds in blocks from
+ * |inp|. The result is written back to |Xi| and the |len| argument must be a
+ * multiple of 16. */
+typedef void (*ghash_func)(uint64_t Xi[2], const u128 Htable[16],
+                           const uint8_t *inp, size_t len);
+
 /* This differs from upstream's |gcm128_context| in that it does not have the
  * |key| pointer, in order to make it |memcpy|-friendly. Rather the key is
  * passed into each call that needs it. */
@@ -166,14 +146,11 @@
     uint32_t d[4];
     uint8_t c[16];
     size_t t[16 / sizeof(size_t)];
-  } Yi, EKi, EK0, len, Xi, H;
+  } Yi, EKi, EK0, len, Xi;
 
-  /* Relative position of Xi, H and pre-computed Htable is used in some
-   * assembler modules, i.e. don't change the order! */
   u128 Htable[16];
-  void (*gmult)(uint64_t Xi[2], const u128 Htable[16]);
-  void (*ghash)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                size_t len);
+  gmult_func gmult;
+  ghash_func ghash;
 
   unsigned int mres, ares;
   block128_f block;
@@ -212,6 +189,12 @@
                                  uint8_t ecount_buf[16], unsigned *num,
                                  ctr128_f ctr);
 
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
+void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
+                                const void *key, const uint8_t *ivec);
+#endif
+
 
 /* GCM.
  *
@@ -222,6 +205,12 @@
 
 typedef struct gcm128_context GCM128_CONTEXT;
 
+/* CRYPTO_ghash_init writes a precomputed table of powers of |gcm_key| to
+ * |out_table| and sets |*out_mult| and |*out_hash| to (potentially hardware
+ * accelerated) functions for performing operations in the GHASH field. */
+void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
+                       u128 out_table[16], const uint8_t *gcm_key);
+
 /* CRYPTO_gcm128_init initialises |ctx| to use |block| (typically AES) with
  * the given key. */
 OPENSSL_EXPORT void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, const void *key,
@@ -345,11 +334,36 @@
                                    block128_f block);
 
 
-#if !defined(OPENSSL_NO_ASM) && \
-    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
-void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
-                                const void *key, const uint8_t *ivec);
-#endif
+/* POLYVAL.
+ *
+ * POLYVAL is a polynomial authenticator that operates over a field very
+ * similar to the one that GHASH uses. See
+ * https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02#section-3. */
+
+typedef union {
+  uint64_t u[2];
+  uint8_t c[16];
+} polyval_block;
+
+struct polyval_ctx {
+  polyval_block S;
+  u128 Htable[16];
+  gmult_func gmult;
+  ghash_func ghash;
+};
+
+/* CRYPTO_POLYVAL_init initialises |ctx| using |key|. */
+void CRYPTO_POLYVAL_init(struct polyval_ctx *ctx, const uint8_t key[16]);
+
+/* CRYPTO_POLYVAL_update_blocks updates the accumulator in |ctx| given the
+ * blocks from |in|. Only a whole number of blocks can be processed so |in_len|
+ * must be a multiple of 16. */
+void CRYPTO_POLYVAL_update_blocks(struct polyval_ctx *ctx, const uint8_t *in,
+                                  size_t in_len);
+
+/* CRYPTO_POLYVAL_finish writes the accumulator from |ctx| to |out|. */
+void CRYPTO_POLYVAL_finish(const struct polyval_ctx *ctx, uint8_t out[16]);
+
 
 #if defined(__cplusplus)
 } /* extern C */
diff --git a/src/crypto/modes/polyval.c b/src/crypto/modes/polyval.c
new file mode 100644
index 0000000..c5121a1
--- /dev/null
+++ b/src/crypto/modes/polyval.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/base.h>
+
+#if !defined(OPENSSL_SMALL)
+
+#include <assert.h>
+#include <string.h>
+
+#include "internal.h"
+#include "../internal.h"
+
+
+/* byte_reverse reverses the order of the bytes in |b->c|. */
+static void byte_reverse(polyval_block *b) {
+  const uint64_t t = CRYPTO_bswap8(b->u[0]);
+  b->u[0] = CRYPTO_bswap8(b->u[1]);
+  b->u[1] = t;
+}
+
+/* reverse_and_mulX_ghash interprets the bytes |b->c| as a reversed element of
+ * the GHASH field, multiplies that by 'x' and serialises the result back into
+ * |b|, but with GHASH's backwards bit ordering. */
+static void reverse_and_mulX_ghash(polyval_block *b) {
+  uint64_t hi = b->u[0];
+  uint64_t lo = b->u[1];
+  const unsigned carry = constant_time_eq(hi & 1, 1);
+  hi >>= 1;
+  hi |= lo << 63;
+  lo >>= 1;
+  lo ^= ((uint64_t) constant_time_select(carry, 0xe1, 0)) << 56;
+
+  b->u[0] = CRYPTO_bswap8(lo);
+  b->u[1] = CRYPTO_bswap8(hi);
+}
+
+/* POLYVAL(H, X_1, ..., X_n) =
+ * ByteReverse(GHASH(mulX_GHASH(ByteReverse(H)), ByteReverse(X_1), ...,
+ * ByteReverse(X_n))).
+ *
+ * See https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02#appendix-A. */
+
+void CRYPTO_POLYVAL_init(struct polyval_ctx *ctx, const uint8_t key[16]) {
+  polyval_block H;
+  memcpy(H.c, key, 16);
+  reverse_and_mulX_ghash(&H);
+
+  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, ctx->Htable, H.c);
+  memset(&ctx->S, 0, sizeof(ctx->S));
+}
+
+void CRYPTO_POLYVAL_update_blocks(struct polyval_ctx *ctx, const uint8_t *in,
+                                  size_t in_len) {
+  assert((in_len & 15) == 0);
+  polyval_block reversed[32];
+
+  while (in_len > 0) {
+    size_t todo = in_len;
+    if (todo > sizeof(reversed)) {
+      todo = sizeof(reversed);
+    }
+    memcpy(reversed, in, todo);
+    in_len -= todo;
+
+    size_t blocks = todo / sizeof(polyval_block);
+    for (size_t i = 0; i < blocks; i++) {
+      byte_reverse(&reversed[i]);
+    }
+
+    ctx->ghash(ctx->S.u, ctx->Htable, (const uint8_t *) reversed, todo);
+  }
+}
+
+void CRYPTO_POLYVAL_finish(const struct polyval_ctx *ctx, uint8_t out[16]) {
+  polyval_block S = ctx->S;
+  byte_reverse(&S);
+  memcpy(out, &S.c, sizeof(polyval_block));
+}
+
+
+#endif  /* !OPENSSL_SMALL */
diff --git a/src/crypto/rand/urandom.c b/src/crypto/rand/urandom.c
index 2572625..17d194c 100644
--- a/src/crypto/rand/urandom.c
+++ b/src/crypto/rand/urandom.c
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -87,12 +88,16 @@
 /* requested_lock is used to protect the |*_requested| variables. */
 static struct CRYPTO_STATIC_MUTEX requested_lock = CRYPTO_STATIC_MUTEX_INIT;
 
-/* urandom_fd_requested is set by |RAND_set_urandom_fd|.  It's protected by
+/* The following constants are magic values of |urandom_fd|. */
+static const int kUnset = -2;
+static const int kHaveGetrandom = -3;
+
+/* urandom_fd_requested is set by |RAND_set_urandom_fd|. It's protected by
  * |requested_lock|. */
-static int urandom_fd_requested = -2;
+static int urandom_fd_requested = -2 /* kUnset */;
 
 /* urandom_fd is a file descriptor to /dev/urandom. It's protected by |once|. */
-static int urandom_fd = -2;
+static int urandom_fd = -2 /* kUnset */;
 
 /* urandom_buffering_requested is set by |RAND_enable_fork_unsafe_buffering|.
  * It's protected by |requested_lock|. */
@@ -115,12 +120,31 @@
   CRYPTO_STATIC_MUTEX_unlock_read(&requested_lock);
 
 #if defined(USE_SYS_getrandom)
-  /* Initial test of getrandom to find any unexpected behavior. */
   uint8_t dummy;
-  syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK);
-#endif
+  long getrandom_ret =
+      syscall(SYS_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK);
 
-  if (fd == -2) {
+  if (getrandom_ret == 1) {
+    urandom_fd = kHaveGetrandom;
+    return;
+  } else if (getrandom_ret == -1 && errno == EAGAIN) {
+    fprintf(stderr,
+            "getrandom indicates that the entropy pool has not been "
+            "initialized. Rather than continue with poor entropy, this process "
+            "will block until entropy is available.\n");
+    do {
+      getrandom_ret =
+          syscall(SYS_getrandom, &dummy, sizeof(dummy), 0 /* no flags */);
+    } while (getrandom_ret == -1 && errno == EINTR);
+
+    if (getrandom_ret == 1) {
+      urandom_fd = kHaveGetrandom;
+      return;
+    }
+  }
+#endif  /* USE_SYS_getrandom */
+
+  if (fd == kUnset) {
     do {
       fd = open("/dev/urandom", O_RDONLY);
     } while (fd == -1 && errno == EINTR);
@@ -156,7 +180,9 @@
   CRYPTO_STATIC_MUTEX_unlock_write(&requested_lock);
 
   CRYPTO_once(&once, init_once);
-  if (urandom_fd != fd) {
+  if (urandom_fd == kHaveGetrandom) {
+    close(fd);
+  } else if (urandom_fd != fd) {
     abort();  // Already initialized.
   }
 }
@@ -168,7 +194,7 @@
       abort();
     }
   } else {
-    fd = -2;
+    fd = kUnset;
   }
 
   CRYPTO_STATIC_MUTEX_lock_write(&requested_lock);
@@ -177,7 +203,15 @@
   CRYPTO_STATIC_MUTEX_unlock_write(&requested_lock);
 
   CRYPTO_once(&once, init_once);
-  if (urandom_buffering != 1 || (fd >= 0 && urandom_fd != fd)) {
+  if (urandom_buffering != 1) {
+    abort();  // Already initialized
+  }
+
+  if (urandom_fd == kHaveGetrandom) {
+    if (fd >= 0) {
+      close(fd);
+    }
+  } else if (urandom_fd != fd) {
     abort();  // Already initialized.
   }
 }
@@ -209,9 +243,19 @@
   ssize_t r;
 
   while (len > 0) {
-    do {
-      r = read(urandom_fd, out, len);
-    } while (r == -1 && errno == EINTR);
+    if (urandom_fd == kHaveGetrandom) {
+#if defined(USE_SYS_getrandom)
+      do {
+        r = syscall(SYS_getrandom, out, len, 0 /* no flags */);
+      } while (r == -1 && errno == EINTR);
+#else
+      abort();
+#endif
+    } else {
+      do {
+        r = read(urandom_fd, out, len);
+      } while (r == -1 && errno == EINTR);
+    }
 
     if (r <= 0) {
       return 0;
diff --git a/src/crypto/sha/asm/sha256-armv4.pl b/src/crypto/sha/asm/sha256-armv4.pl
index e1be226..bac7ce8 100644
--- a/src/crypto/sha/asm/sha256-armv4.pl
+++ b/src/crypto/sha/asm/sha256-armv4.pl
@@ -1,4 +1,11 @@
-#!/usr/bin/env perl
+#! /usr/bin/env perl
+# Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
 
 # ====================================================================
 # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
@@ -175,16 +182,11 @@
 #endif
 
 .text
-#if __ARM_ARCH__<7
-.code	32
-#else
+#if defined(__thumb2__)
 .syntax unified
-# if defined(__thumb2__) && !defined(__APPLE__)
-#  define adrl adr
 .thumb
-# else
+#else
 .code   32
-# endif
 #endif
 
 .type	K256,%object
@@ -218,10 +220,10 @@
 .type	sha256_block_data_order,%function
 sha256_block_data_order:
 .Lsha256_block_data_order:
-#if __ARM_ARCH__<7
+#if __ARM_ARCH__<7 && !defined(__thumb2__)
 	sub	r3,pc,#8		@ sha256_block_data_order
 #else
-	adr	r3,sha256_block_data_order
+	adr	r3,.Lsha256_block_data_order
 #endif
 #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
 	ldr	r12,.LOPENSSL_armcap
@@ -473,13 +475,14 @@
 
 .global	sha256_block_data_order_neon
 .type	sha256_block_data_order_neon,%function
-.align	4
+.align	5
+.skip	16
 sha256_block_data_order_neon:
 .LNEON:
 	stmdb	sp!,{r4-r12,lr}
 
 	sub	$H,sp,#16*4+16
-	adrl	$Ktbl,K256
+	adr	$Ktbl,K256
 	bic	$H,$H,#15		@ align for 128-bit stores
 	mov	$t2,sp
 	mov	sp,$H			@ alloca
@@ -599,7 +602,7 @@
 $code.=<<___;
 #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
 
-# if defined(__thumb2__) && !defined(__APPLE__)
+# if defined(__thumb2__)
 #  define INST(a,b,c,d)	.byte	c,d|0xc,a,b
 # else
 #  define INST(a,b,c,d)	.byte	a,b,c,d
@@ -610,16 +613,11 @@
 sha256_block_data_order_armv8:
 .LARMv8:
 	vld1.32	{$ABCD,$EFGH},[$ctx]
-# ifdef	__APPLE__
 	sub	$Ktbl,$Ktbl,#256+32
-# elif	defined(__thumb2__)
-	adr	$Ktbl,.LARMv8
-	sub	$Ktbl,$Ktbl,#.LARMv8-K256
-# else
-	adrl	$Ktbl,K256
-# endif
 	add	$len,$inp,$len,lsl#6	@ len to point at the end of inp
+	b	.Loop_v8
 
+.align	4
 .Loop_v8:
 	vld1.8		{@MSG[0]-@MSG[1]},[$inp]!
 	vld1.8		{@MSG[2]-@MSG[3]},[$inp]!
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index c39d98d..0c25754 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -25,7 +25,6 @@
 #include <openssl/pool.h>
 #include <openssl/x509.h>
 
-namespace bssl {
 
 static const char kCrossSigningRootPEM[] =
     "-----BEGIN CERTIFICATE-----\n"
@@ -724,7 +723,7 @@
   }
 
   // Test PKCS#1 v1.5.
-  ScopedEVP_MD_CTX md_ctx;
+  bssl::ScopedEVP_MD_CTX md_ctx;
   if (!EVP_DigestSignInit(md_ctx.get(), NULL, EVP_sha256(), NULL, pkey.get()) ||
       !SignatureRoundTrips(md_ctx.get(), pkey.get())) {
     fprintf(stderr, "RSA PKCS#1 with SHA-256 failed\n");
@@ -941,7 +940,51 @@
   return true;
 }
 
-static int Main() {
+static bool TestFailedParseFromBuffer() {
+  static const uint8_t kNonsense[] = {1, 2, 3, 4, 5};
+
+  bssl::UniquePtr<CRYPTO_BUFFER> buf(
+      CRYPTO_BUFFER_new(kNonsense, sizeof(kNonsense), nullptr));
+  if (!buf) {
+    return false;
+  }
+
+  bssl::UniquePtr<X509> cert(X509_parse_from_buffer(buf.get()));
+  if (cert) {
+    fprintf(stderr, "Nonsense somehow parsed.\n");
+    return false;
+  }
+  ERR_clear_error();
+
+  // Test a buffer with trailing data.
+  size_t data_len;
+  bssl::UniquePtr<uint8_t> data;
+  if (!PEMToDER(&data, &data_len, kRootCAPEM)) {
+    return false;
+  }
+
+  std::unique_ptr<uint8_t[]> data_with_trailing_byte(new uint8_t[data_len + 1]);
+  memcpy(data_with_trailing_byte.get(), data.get(), data_len);
+  data_with_trailing_byte[data_len] = 0;
+
+  bssl::UniquePtr<CRYPTO_BUFFER> buf_with_trailing_byte(
+      CRYPTO_BUFFER_new(data_with_trailing_byte.get(), data_len + 1, nullptr));
+  if (!buf_with_trailing_byte) {
+    return false;
+  }
+
+  bssl::UniquePtr<X509> root(
+      X509_parse_from_buffer(buf_with_trailing_byte.get()));
+  if (root) {
+    fprintf(stderr, "Parsed buffer with trailing byte.\n");
+    return false;
+  }
+  ERR_clear_error();
+
+  return true;
+}
+
+int main() {
   CRYPTO_library_init();
 
   if (!TestVerify() ||
@@ -952,16 +995,11 @@
       !TestFromBuffer() ||
       !TestFromBufferTrailingData() ||
       !TestFromBufferModified() ||
-      !TestFromBufferReused()) {
+      !TestFromBufferReused() ||
+      !TestFailedParseFromBuffer()) {
     return 1;
   }
 
   printf("PASS\n");
   return 0;
 }
-
-}  // namespace bssl
-
-int main() {
-  return bssl::Main();
-}
diff --git a/src/crypto/x509/x_x509.c b/src/crypto/x509/x_x509.c
index 845d4b2..d3cd5b0 100644
--- a/src/crypto/x509/x_x509.c
+++ b/src/crypto/x509/x_x509.c
@@ -106,6 +106,7 @@
         ret->crldp = NULL;
         ret->buf = NULL;
         CRYPTO_new_ex_data(&ret->ex_data);
+        CRYPTO_MUTEX_init(&ret->lock);
         break;
 
     case ASN1_OP_D2I_PRE:
@@ -120,6 +121,7 @@
         break;
 
     case ASN1_OP_FREE_POST:
+        CRYPTO_MUTEX_cleanup(&ret->lock);
         CRYPTO_free_ex_data(&g_ex_data_class, ret, &ret->ex_data);
         X509_CERT_AUX_free(ret->aux);
         ASN1_OCTET_STRING_free(ret->skid);
@@ -129,9 +131,7 @@
         GENERAL_NAMES_free(ret->altname);
         NAME_CONSTRAINTS_free(ret->nc);
         CRYPTO_BUFFER_free(ret->buf);
-
-        if (ret->name != NULL)
-            OPENSSL_free(ret->name);
+        OPENSSL_free(ret->name);
         break;
 
     }
@@ -162,8 +162,8 @@
   X509 *x509p = x509;
   X509 *ret = d2i_X509(&x509p, &inp, CRYPTO_BUFFER_len(buf));
   if (ret == NULL ||
-      (inp - CRYPTO_BUFFER_data(buf)) != (ptrdiff_t) CRYPTO_BUFFER_len(buf)) {
-    X509_free(x509);
+      inp - CRYPTO_BUFFER_data(buf) != (ptrdiff_t)CRYPTO_BUFFER_len(buf)) {
+    X509_free(x509p);
     return NULL;
   }
   assert(x509p == x509);
diff --git a/src/crypto/x509v3/v3_purp.c b/src/crypto/x509v3/v3_purp.c
index 9152444..324de85 100644
--- a/src/crypto/x509v3/v3_purp.c
+++ b/src/crypto/x509v3/v3_purp.c
@@ -146,9 +146,7 @@
 {
     int idx;
     const X509_PURPOSE *pt;
-    if (!(x->ex_flags & EXFLAG_SET)) {
-        x509v3_cache_extensions(x);
-    }
+    x509v3_cache_extensions(x);
     if (id == -1)
         return 1;
     idx = X509_PURPOSE_get_by_id(id);
@@ -407,16 +405,6 @@
         setup_dp(x, sk_DIST_POINT_value(x->crldp, i));
 }
 
-/*
- * g_x509_cache_extensions_lock is used to protect against concurrent calls
- * to |x509v3_cache_extensions|. Ideally this would be done with a
- * |CRYPTO_once_t| in the |X509| structure, but |CRYPTO_once_t| isn't public.
- * Note: it's not entirely clear whether this lock is needed. Not all paths to
- * this function took a lock in OpenSSL.
- */
-static struct CRYPTO_STATIC_MUTEX g_x509_cache_extensions_lock =
-    CRYPTO_STATIC_MUTEX_INIT;
-
 static void x509v3_cache_extensions(X509 *x)
 {
     BASIC_CONSTRAINTS *bs;
@@ -428,10 +416,17 @@
     size_t i;
     int j;
 
-    CRYPTO_STATIC_MUTEX_lock_write(&g_x509_cache_extensions_lock);
+    CRYPTO_MUTEX_lock_read(&x->lock);
+    const int is_set = x->ex_flags & EXFLAG_SET;
+    CRYPTO_MUTEX_unlock_read(&x->lock);
 
+    if (is_set) {
+        return;
+    }
+
+    CRYPTO_MUTEX_lock_write(&x->lock);
     if (x->ex_flags & EXFLAG_SET) {
-        CRYPTO_STATIC_MUTEX_unlock_write(&g_x509_cache_extensions_lock);
+        CRYPTO_MUTEX_unlock_write(&x->lock);
         return;
     }
 
@@ -564,7 +559,7 @@
     }
     x->ex_flags |= EXFLAG_SET;
 
-    CRYPTO_STATIC_MUTEX_unlock_write(&g_x509_cache_extensions_lock);
+    CRYPTO_MUTEX_unlock_write(&x->lock);
 }
 
 /*
@@ -604,10 +599,7 @@
 
 int X509_check_ca(X509 *x)
 {
-    if (!(x->ex_flags & EXFLAG_SET)) {
-        x509v3_cache_extensions(x);
-    }
-
+    x509v3_cache_extensions(x);
     return check_ca(x);
 }
 
diff --git a/src/include/openssl/aead.h b/src/include/openssl/aead.h
index fff0e49..eaa2b8f 100644
--- a/src/include/openssl/aead.h
+++ b/src/include/openssl/aead.h
@@ -114,6 +114,14 @@
  * authentication. See |EVP_aead_aes_128_ctr_hmac_sha256| for details. */
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_ctr_hmac_sha256(void);
 
+/* EVP_aead_aes_128_gcm_siv is AES-128 in GCM-SIV mode. See
+ * https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02 */
+OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_gcm_siv(void);
+
+/* EVP_aead_aes_256_gcm_siv is AES-256 in GCM-SIV mode. See
+ * https://tools.ietf.org/html/draft-irtf-cfrg-gcmsiv-02 */
+OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void);
+
 /* EVP_has_aes_hardware returns one if we enable hardware support for fast and
  * constant-time AES-GCM. */
 OPENSSL_EXPORT int EVP_has_aes_hardware(void);
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 3c8b97d..58a4747 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -616,18 +616,6 @@
 OPENSSL_EXPORT int BIO_new_bio_pair(BIO **out1, size_t writebuf1, BIO **out2,
                                     size_t writebuf2);
 
-/* BIO_new_bio_pair_external_buf is the same as |BIO_new_bio_pair| with the
- * difference that the caller keeps ownership of the write buffers
- * |ext_writebuf1_len| and |ext_writebuf2_len|. This is useful when using zero
- * copy API for read and write operations, in cases where the buffers need to
- * outlive the BIO pairs. It returns one on success and zero on error. */
-OPENSSL_EXPORT int BIO_new_bio_pair_external_buf(BIO** bio1_p,
-                                                 size_t writebuf1_len,
-                                                 uint8_t* ext_writebuf1,
-                                                 BIO** bio2_p,
-                                                 size_t writebuf2_len,
-                                                 uint8_t* ext_writebuf2);
-
 /* BIO_ctrl_get_read_request returns the number of bytes that the other side of
  * |bio| tried (unsuccessfully) to read. */
 OPENSSL_EXPORT size_t BIO_ctrl_get_read_request(BIO *bio);
@@ -643,63 +631,6 @@
 OPENSSL_EXPORT int BIO_shutdown_wr(BIO *bio);
 
 
-/* Zero copy versions of BIO_read and BIO_write for BIO pairs. */
-
-/* BIO_zero_copy_get_read_buf initiates a zero copy read operation.
- * |out_read_buf| is set to the internal read buffer, and |out_buf_offset| is
- * set to the current read position of |out_read_buf|. The number of bytes
- * available for read from |out_read_buf| + |out_buf_offset| is returned in
- * |out_available_bytes|. Note that this function might report fewer bytes
- * available than |BIO_pending|, if the internal ring buffer is wrapped. It
- * returns one on success. In case of error it returns zero and pushes to the
- * error stack.
- *
- * The zero copy read operation is completed by calling
- * |BIO_zero_copy_get_read_buf_done|. Neither |BIO_zero_copy_get_read_buf| nor
- * any other I/O read operation may be called while a zero copy read operation
- * is active. */
-OPENSSL_EXPORT int BIO_zero_copy_get_read_buf(BIO* bio,
-                                              uint8_t** out_read_buf,
-                                              size_t* out_buf_offset,
-                                              size_t* out_available_bytes);
-
-/* BIO_zero_copy_get_read_buf_done must be called after reading from a BIO using
- * |BIO_zero_copy_get_read_buf| to finish the read operation. The |bytes_read|
- * argument is the number of bytes read.
- *
- * It returns one on success. In case of error it returns zero and pushes to the
- * error stack. */
-OPENSSL_EXPORT int BIO_zero_copy_get_read_buf_done(BIO* bio, size_t bytes_read);
-
-/* BIO_zero_copy_get_write_buf initiates a zero copy write operation.
- * |out_write_buf| is set to to the internal write buffer, and |out_buf_offset|
- * is set to the current write position of |out_write_buf|.
- * The number of bytes available for write from |out_write_buf| +
- * |out_buf_offset| is returned in |out_available_bytes|. Note that this
- * function might report fewer bytes available than
- * |BIO_ctrl_get_write_guarantee|, if the internal buffer is wrapped. It returns
- * one on success. In case of error it returns zero and pushes to the error
- * stack.
- *
- * The zero copy write operation is completed by calling
- * |BIO_zero_copy_get_write_buf_done|. Neither |BIO_zero_copy_get_write_buf|
- * nor any other I/O write operation may be called while a zero copy write
- * operation is active. */
-OPENSSL_EXPORT int BIO_zero_copy_get_write_buf(BIO* bio,
-                                               uint8_t** out_write_buf,
-                                               size_t* out_buf_offset,
-                                               size_t* out_available_bytes);
-
-/* BIO_zero_copy_get_write_buf_done must be called after writing to a BIO using
- * |BIO_zero_copy_get_write_buf| to finish the write operation. The
- * |bytes_written| argument gives the number of bytes written.
- *
- * It returns one on success. In case of error it returns zero and pushes to the
- * error stack. */
-OPENSSL_EXPORT int BIO_zero_copy_get_write_buf_done(BIO* bio,
-                                                    size_t bytes_written);
-
-
 /* BIO_NOCLOSE and |BIO_CLOSE| can be used as symbolic arguments when a "close
  * flag" is passed to a BIO function. */
 #define BIO_NOCLOSE 0
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 764d8c5..8deb278 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -575,8 +575,10 @@
 OPENSSL_EXPORT int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a,
                                         const BIGNUM *m);
 
-/* BN_mod_sqrt returns a |BIGNUM|, r, such that r^2 == a (mod p). |p| must be a
- * prime. */
+/* BN_mod_sqrt returns a newly-allocated |BIGNUM|, r, such that
+ * r^2 == a (mod p). |p| must be a prime. It returns NULL on error or if |a| is
+ * not a square mod |p|. In the latter case, it will add |BN_R_NOT_A_SQUARE| to
+ * the error queue. */
 OPENSSL_EXPORT BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p,
                                    BN_CTX *ctx);
 
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index f5b9f9d..c29040a 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -608,11 +608,6 @@
  * client's. */
 #define SSL_OP_CIPHER_SERVER_PREFERENCE 0x00400000L
 
-/* SSL_OP_DISABLE_NPN configures an individual |SSL| to not advertise NPN,
- * despite |SSL_CTX_set_next_proto_select_cb| being configured on the
- * |SSL_CTX|. */
-#define SSL_OP_DISABLE_NPN 0x00800000L
-
 /* The following flags toggle individual protocol versions. This is deprecated.
  * Use |SSL_CTX_set_min_proto_version| and |SSL_CTX_set_max_proto_version|
  * instead. */
@@ -1620,7 +1615,7 @@
  * should not be used. */
 OPENSSL_EXPORT int SSL_SESSION_set1_id_context(SSL_SESSION *session,
                                                const uint8_t *sid_ctx,
-                                               unsigned sid_ctx_len);
+                                               size_t sid_ctx_len);
 
 
 /* Session caching.
@@ -1721,6 +1716,15 @@
  * |ctx|. */
 OPENSSL_EXPORT long SSL_CTX_get_timeout(const SSL_CTX *ctx);
 
+/* SSL_set_session_timeout sets the default lifetime, in seconds, of the
+ * session created in |ssl| to |timeout|, and returns the old value.
+ *
+ * By default the value |SSL_DEFAULT_SESSION_TIMEOUT| is used, which can be
+ * overridden at the context level by calling |SSL_CTX_set_timeout|.
+ *
+ * If |timeout| is zero the newly created session will not be resumable. */
+OPENSSL_EXPORT long SSL_set_session_timeout(SSL *ssl, long timeout);
+
 /* SSL_CTX_set_session_id_context sets |ctx|'s session ID context to |sid_ctx|.
  * It returns one on success and zero on error. The session ID context is an
  * application-defined opaque byte string. A session will not be used in a
@@ -1734,13 +1738,13 @@
  * relevant if a server requires client auth. */
 OPENSSL_EXPORT int SSL_CTX_set_session_id_context(SSL_CTX *ctx,
                                                   const uint8_t *sid_ctx,
-                                                  unsigned sid_ctx_len);
+                                                  size_t sid_ctx_len);
 
 /* SSL_set_session_id_context sets |ssl|'s session ID context to |sid_ctx|. It
  * returns one on success and zero on error. See also
  * |SSL_CTX_set_session_id_context|. */
 OPENSSL_EXPORT int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx,
-                                              unsigned sid_ctx_len);
+                                              size_t sid_ctx_len);
 
 /* SSL_SESSION_CACHE_MAX_SIZE_DEFAULT is the default maximum size of a session
  * cache. */
@@ -2502,15 +2506,14 @@
  *
  * See draft-balfanz-tls-channelid-01. */
 
-/* SSL_CTX_enable_tls_channel_id either configures a TLS server to accept TLS
- * Channel IDs from clients, or configures a client to send TLS Channel IDs to
- * a server. It returns one. */
-OPENSSL_EXPORT int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx);
+/* SSL_CTX_set_tls_channel_id_enabled configures whether connections associated
+ * with |ctx| should enable Channel ID. */
+OPENSSL_EXPORT void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx,
+                                                       int enabled);
 
-/* SSL_enable_tls_channel_id either configures a TLS server to accept TLS
- * Channel IDs from clients, or configures a client to send TLS Channel IDs to
- * server. It returns one. */
-OPENSSL_EXPORT int SSL_enable_tls_channel_id(SSL *ssl);
+/* SSL_set_tls_channel_id_enabled configures whether |ssl| should enable Channel
+ * ID. */
+OPENSSL_EXPORT void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled);
 
 /* SSL_CTX_set1_tls_channel_id configures a TLS client to send a TLS Channel ID
  * to compatible servers. |private_key| must be a P-256 EC key. It returns one
@@ -2942,10 +2945,11 @@
 OPENSSL_EXPORT int SSL_set_max_send_fragment(SSL *ssl,
                                              size_t max_send_fragment);
 
-/* ssl_early_callback_ctx is passed to certain callbacks that are called very
- * early on during the server handshake. At this point, much of the SSL* hasn't
- * been filled out and only the ClientHello can be depended on. */
-struct ssl_early_callback_ctx {
+/* ssl_early_callback_ctx (aka |SSL_CLIENT_HELLO|) is passed to certain
+ * callbacks that are called very early on during the server handshake. At this
+ * point, much of the SSL* hasn't been filled out and only the ClientHello can
+ * be depended on. */
+typedef struct ssl_early_callback_ctx {
   SSL *ssl;
   const uint8_t *client_hello;
   size_t client_hello_len;
@@ -2960,15 +2964,15 @@
   size_t compression_methods_len;
   const uint8_t *extensions;
   size_t extensions_len;
-};
+} SSL_CLIENT_HELLO;
 
-/* SSL_early_callback_ctx_extension_get searches the extensions in |ctx| for an
- * extension of the given type. If not found, it returns zero. Otherwise it
- * sets |out_data| to point to the extension contents (not including the type
- * and length bytes), sets |out_len| to the length of the extension contents
- * and returns one. */
+/* SSL_early_callback_ctx_extension_get searches the extensions in
+ * |client_hello| for an extension of the given type. If not found, it returns
+ * zero. Otherwise it sets |out_data| to point to the extension contents (not
+ * including the type and length bytes), sets |out_len| to the length of the
+ * extension contents and returns one. */
 OPENSSL_EXPORT int SSL_early_callback_ctx_extension_get(
-    const struct ssl_early_callback_ctx *ctx, uint16_t extension_type,
+    const SSL_CLIENT_HELLO *client_hello, uint16_t extension_type,
     const uint8_t **out_data, size_t *out_len);
 
 /* SSL_CTX_set_select_certificate_cb sets a callback that is called before most
@@ -2978,19 +2982,19 @@
  * pause the handshake to perform an asynchronous operation. If paused,
  * |SSL_get_error| will return |SSL_ERROR_PENDING_CERTIFICATE|.
  *
- * Note: The |ssl_early_callback_ctx| is only valid for the duration of the
- * callback and is not valid while the handshake is paused. Further, unlike with
- * most callbacks, when the handshake loop is resumed, it will not call the
- * callback a second time. The caller must finish reconfiguring the connection
- * before resuming the handshake. */
+ * Note: The |SSL_CLIENT_HELLO| is only valid for the duration of the callback
+ * and is not valid while the handshake is paused. Further, unlike with most
+ * callbacks, when the handshake loop is resumed, it will not call the callback
+ * a second time. The caller must finish reconfiguring the connection before
+ * resuming the handshake. */
 OPENSSL_EXPORT void SSL_CTX_set_select_certificate_cb(
-    SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *));
+    SSL_CTX *ctx, int (*cb)(const SSL_CLIENT_HELLO *));
 
 /* SSL_CTX_set_dos_protection_cb sets a callback that is called once the
  * resumption decision for a ClientHello has been made. It can return one to
  * allow the handshake to continue or zero to cause the handshake to abort. */
 OPENSSL_EXPORT void SSL_CTX_set_dos_protection_cb(
-    SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *));
+    SSL_CTX *ctx, int (*cb)(const SSL_CLIENT_HELLO *));
 
 /* SSL_ST_* are possible values for |SSL_state| and the bitmasks that make them
  * up. */
@@ -3146,11 +3150,6 @@
 /* SSL_library_init calls |CRYPTO_library_init| and returns one. */
 OPENSSL_EXPORT int SSL_library_init(void);
 
-/* SSL_set_reject_peer_renegotiations calls |SSL_set_renegotiate_mode| with
- * |ssl_never_renegotiate| if |reject| is one and |ssl_renegotiate_freely| if
- * zero. */
-OPENSSL_EXPORT void SSL_set_reject_peer_renegotiations(SSL *ssl, int reject);
-
 /* SSL_CIPHER_description writes a description of |cipher| into |buf| and
  * returns |buf|. If |buf| is NULL, it returns a newly allocated string, to be
  * freed with |OPENSSL_free|, or NULL on error.
@@ -3627,6 +3626,12 @@
 /* SSL_set_max_version calls |SSL_set_max_proto_version|. */
 OPENSSL_EXPORT int SSL_set_max_version(SSL *ssl, uint16_t version);
 
+/* SSL_CTX_enable_tls_channel_id calls |SSL_CTX_set_tls_channel_id_enabled|. */
+OPENSSL_EXPORT int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx);
+
+/* SSL_enable_tls_channel_id calls |SSL_set_tls_channel_id_enabled|. */
+OPENSSL_EXPORT int SSL_enable_tls_channel_id(SSL *ssl);
+
 
 /* Private structures.
  *
@@ -3679,7 +3684,7 @@
   /* this is used to determine whether the session is being reused in
    * the appropriate context. It is up to the application to set this,
    * via SSL_new */
-  unsigned int sid_ctx_length;
+  uint8_t sid_ctx_length;
   uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH];
 
   char *psk_identity;
@@ -3730,7 +3735,7 @@
    * SHA-2, depending on TLS version) for the original, full handshake that
    * created a session. This is used by Channel IDs during resumption. */
   uint8_t original_handshake_hash[EVP_MAX_MD_SIZE];
-  unsigned original_handshake_hash_len;
+  uint8_t original_handshake_hash_len;
 
   uint32_t tlsext_tick_lifetime_hint; /* Session lifetime hint in seconds */
 
@@ -3903,7 +3908,7 @@
   void *msg_callback_arg;
 
   int verify_mode;
-  unsigned int sid_ctx_length;
+  uint8_t sid_ctx_length;
   uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH];
   int (*default_verify_callback)(
       int ok, X509_STORE_CTX *ctx); /* called 'verify_callback' in the SSL */
@@ -3916,12 +3921,12 @@
    * with an error and cause SSL_get_error to return
    * SSL_ERROR_PENDING_CERTIFICATE. Note: when the handshake loop is resumed, it
    * will not call the callback a second time. */
-  int (*select_certificate_cb)(const struct ssl_early_callback_ctx *);
+  int (*select_certificate_cb)(const SSL_CLIENT_HELLO *);
 
   /* dos_protection_cb is called once the resumption decision for a ClientHello
    * has been made. It returns one to continue the handshake or zero to
    * abort. */
-  int (*dos_protection_cb) (const struct ssl_early_callback_ctx *);
+  int (*dos_protection_cb) (const SSL_CLIENT_HELLO *);
 
   /* Maximum amount of data to send in one fragment. actual record size can be
    * more than this due to padding and MAC overheads. */
@@ -4042,6 +4047,8 @@
   int freelist_max_len;
 };
 
+typedef struct ssl_handshake_st SSL_HANDSHAKE;
+
 struct ssl_st {
   /* method is the method table corresponding to the current protocol (DTLS or
    * TLS). */
@@ -4080,7 +4087,7 @@
    * with a better mechanism. */
   BIO *bbio;
 
-  int (*handshake_func)(SSL *);
+  int (*handshake_func)(SSL_HANDSHAKE *hs);
 
   BUF_MEM *init_buf; /* buffer used during init */
 
@@ -4123,7 +4130,7 @@
 
   /* the session_id_context is used to ensure sessions are only reused
    * in the appropriate context */
-  unsigned int sid_ctx_length;
+  uint8_t sid_ctx_length;
   uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH];
 
   /* session is the configured session to be offered by the client. This session
@@ -4157,8 +4164,6 @@
   uint32_t options; /* protocol behaviour */
   uint32_t mode;    /* API behaviour */
   uint32_t max_cert_list;
-  int client_version; /* what was passed, used for
-                       * SSLv3/TLS rollback check */
   char *tlsext_hostname;
   size_t supported_group_list_len;
   uint16_t *supported_group_list; /* our list */
@@ -4215,6 +4220,10 @@
 
   /* TODO(agl): remove once node.js not longer references this. */
   int tlsext_status_type;
+
+  /* session_timeout is the default lifetime in seconds of the session
+   * created in this connection. */
+  long session_timeout;
 };
 
 
@@ -4267,7 +4276,6 @@
 #define SSL_CTRL_NEED_TMP_RSA doesnt_exist
 #define SSL_CTRL_OPTIONS doesnt_exist
 #define SSL_CTRL_SESS_NUMBER doesnt_exist
-#define SSL_CTRL_SET_CHANNEL_ID doesnt_exist
 #define SSL_CTRL_SET_CURVES doesnt_exist
 #define SSL_CTRL_SET_CURVES_LIST doesnt_exist
 #define SSL_CTRL_SET_MAX_CERT_LIST doesnt_exist
@@ -4299,7 +4307,6 @@
 #define SSL_CTX_clear_chain_certs SSL_CTX_clear_chain_certs
 #define SSL_CTX_clear_mode SSL_CTX_clear_mode
 #define SSL_CTX_clear_options SSL_CTX_clear_options
-#define SSL_CTX_enable_tls_channel_id SSL_CTX_enable_tls_channel_id
 #define SSL_CTX_get0_chain_certs SSL_CTX_get0_chain_certs
 #define SSL_CTX_get_extra_chain_certs SSL_CTX_get_extra_chain_certs
 #define SSL_CTX_get_max_cert_list SSL_CTX_get_max_cert_list
@@ -4315,7 +4322,6 @@
 #define SSL_CTX_set0_chain SSL_CTX_set0_chain
 #define SSL_CTX_set1_chain SSL_CTX_set1_chain
 #define SSL_CTX_set1_curves SSL_CTX_set1_curves
-#define SSL_CTX_set1_tls_channel_id SSL_CTX_set1_tls_channel_id
 #define SSL_CTX_set_max_cert_list SSL_CTX_set_max_cert_list
 #define SSL_CTX_set_max_send_fragment SSL_CTX_set_max_send_fragment
 #define SSL_CTX_set_mode SSL_CTX_set_mode
@@ -4336,7 +4342,6 @@
 #define SSL_clear_chain_certs SSL_clear_chain_certs
 #define SSL_clear_mode SSL_clear_mode
 #define SSL_clear_options SSL_clear_options
-#define SSL_enable_tls_channel_id SSL_enable_tls_channel_id
 #define SSL_get0_certificate_types SSL_get0_certificate_types
 #define SSL_get0_chain_certs SSL_get0_chain_certs
 #define SSL_get_max_cert_list SSL_get_max_cert_list
@@ -4344,14 +4349,12 @@
 #define SSL_get_options SSL_get_options
 #define SSL_get_secure_renegotiation_support \
     SSL_get_secure_renegotiation_support
-#define SSL_get_tls_channel_id SSL_get_tls_channel_id
 #define SSL_need_tmp_RSA SSL_need_tmp_RSA
 #define SSL_num_renegotiations SSL_num_renegotiations
 #define SSL_session_reused SSL_session_reused
 #define SSL_set0_chain SSL_set0_chain
 #define SSL_set1_chain SSL_set1_chain
 #define SSL_set1_curves SSL_set1_curves
-#define SSL_set1_tls_channel_id SSL_set1_tls_channel_id
 #define SSL_set_max_cert_list SSL_set_max_cert_list
 #define SSL_set_max_send_fragment SSL_set_max_send_fragment
 #define SSL_set_mode SSL_set_mode
@@ -4552,6 +4555,8 @@
 #define SSL_R_PRE_SHARED_KEY_MUST_BE_LAST 267
 #define SSL_R_OLD_SESSION_PRF_HASH_MISMATCH 268
 #define SSL_R_INVALID_SCT_LIST 269
+#define SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA 270
+#define SSL_R_PSK_IDENTITY_BINDER_COUNT_MISMATCH 271
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/src/include/openssl/x509.h b/src/include/openssl/x509.h
index 8f2e1c3..ffd0fbc 100644
--- a/src/include/openssl/x509.h
+++ b/src/include/openssl/x509.h
@@ -263,6 +263,7 @@
 	unsigned char sha1_hash[SHA_DIGEST_LENGTH];
 	X509_CERT_AUX *aux;
 	CRYPTO_BUFFER *buf;
+	CRYPTO_MUTEX lock;
 	} /* X509 */;
 
 DECLARE_STACK_OF(X509)
diff --git a/src/ssl/custom_extensions.c b/src/ssl/custom_extensions.c
index 46b5efb..10fbfc8 100644
--- a/src/ssl/custom_extensions.c
+++ b/src/ssl/custom_extensions.c
@@ -58,7 +58,8 @@
   return 1;
 }
 
-static int custom_ext_add_hello(SSL *ssl, CBB *extensions) {
+static int custom_ext_add_hello(SSL_HANDSHAKE *hs, CBB *extensions) {
+  SSL *const ssl = hs->ssl;
   STACK_OF(SSL_CUSTOM_EXTENSION) *stack = ssl->ctx->client_custom_extensions;
   if (ssl->server) {
     stack = ssl->ctx->server_custom_extensions;
@@ -72,7 +73,7 @@
     const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i);
 
     if (ssl->server &&
-        !(ssl->s3->hs->custom_extensions.received & (1u << i))) {
+        !(hs->custom_extensions.received & (1u << i))) {
       /* Servers cannot echo extensions that the client didn't send. */
       continue;
     }
@@ -102,8 +103,8 @@
         }
 
         if (!ssl->server) {
-          assert((ssl->s3->hs->custom_extensions.sent & (1u << i)) == 0);
-          ssl->s3->hs->custom_extensions.sent |= (1u << i);
+          assert((hs->custom_extensions.sent & (1u << i)) == 0);
+          hs->custom_extensions.sent |= (1u << i);
         }
         break;
 
@@ -121,12 +122,13 @@
   return 1;
 }
 
-int custom_ext_add_clienthello(SSL *ssl, CBB *extensions) {
-  return custom_ext_add_hello(ssl, extensions);
+int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions) {
+  return custom_ext_add_hello(hs, extensions);
 }
 
-int custom_ext_parse_serverhello(SSL *ssl, int *out_alert, uint16_t value,
-                                 const CBS *extension) {
+int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert,
+                                 uint16_t value, const CBS *extension) {
+  SSL *const ssl = hs->ssl;
   unsigned index;
   const SSL_CUSTOM_EXTENSION *ext =
       custom_ext_find(ssl->ctx->client_custom_extensions, &index, value);
@@ -134,7 +136,7 @@
   if (/* Unknown extensions are not allowed in a ServerHello. */
       ext == NULL ||
       /* Also, if we didn't send the extension, that's also unacceptable. */
-      !(ssl->s3->hs->custom_extensions.sent & (1u << index))) {
+      !(hs->custom_extensions.sent & (1u << index))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     ERR_add_error_dataf("extension %u", (unsigned)value);
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
@@ -152,8 +154,9 @@
   return 1;
 }
 
-int custom_ext_parse_clienthello(SSL *ssl, int *out_alert, uint16_t value,
-                                 const CBS *extension) {
+int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert,
+                                 uint16_t value, const CBS *extension) {
+  SSL *const ssl = hs->ssl;
   unsigned index;
   const SSL_CUSTOM_EXTENSION *ext =
       custom_ext_find(ssl->ctx->server_custom_extensions, &index, value);
@@ -162,8 +165,8 @@
     return 1;
   }
 
-  assert((ssl->s3->hs->custom_extensions.received & (1u << index)) == 0);
-  ssl->s3->hs->custom_extensions.received |= (1u << index);
+  assert((hs->custom_extensions.received & (1u << index)) == 0);
+  hs->custom_extensions.received |= (1u << index);
 
   if (ext->parse_callback &&
       !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
@@ -176,8 +179,8 @@
   return 1;
 }
 
-int custom_ext_add_serverhello(SSL *ssl, CBB *extensions) {
-  return custom_ext_add_hello(ssl, extensions);
+int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions) {
+  return custom_ext_add_hello(hs, extensions);
 }
 
 /* MAX_NUM_CUSTOM_EXTENSIONS is the maximum number of custom extensions that
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index a63b8c9..cafb4c2 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -113,10 +113,6 @@
   ssl->d1 = NULL;
 }
 
-int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
-  return cipher->algorithm_enc != SSL_eNULL;
-}
-
 void DTLSv1_set_initial_timeout_duration(SSL *ssl, unsigned int duration_ms) {
   ssl->initial_timeout_duration_ms = duration_ms;
 }
@@ -260,11 +256,3 @@
   dtls1_start_timer(ssl);
   return dtls1_retransmit_outgoing_messages(ssl);
 }
-
-void dtls1_expect_flight(SSL *ssl) {
-  dtls1_start_timer(ssl);
-}
-
-void dtls1_received_flight(SSL *ssl) {
-  dtls1_stop_timer(ssl);
-}
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 8e92cc9..89b5491 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -94,6 +94,14 @@
   return 0;
 }
 
+static int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
+  return cipher->algorithm_enc != SSL_eNULL;
+}
+
+static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); }
+
+static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); }
+
 static int dtls1_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
   /* Cipher changes are illegal when there are buffered incoming messages. */
   if (dtls_has_incoming_messages(ssl)) {
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index 70d8d96..4a58af9 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -170,23 +170,24 @@
 #include "internal.h"
 
 
-static int ssl3_send_client_hello(SSL *ssl);
-static int dtls1_get_hello_verify(SSL *ssl);
-static int ssl3_get_server_hello(SSL *ssl);
-static int ssl3_get_server_certificate(SSL *ssl);
-static int ssl3_get_cert_status(SSL *ssl);
-static int ssl3_verify_server_cert(SSL *ssl);
-static int ssl3_get_server_key_exchange(SSL *ssl);
-static int ssl3_get_certificate_request(SSL *ssl);
-static int ssl3_get_server_hello_done(SSL *ssl);
-static int ssl3_send_client_certificate(SSL *ssl);
-static int ssl3_send_client_key_exchange(SSL *ssl);
-static int ssl3_send_cert_verify(SSL *ssl);
-static int ssl3_send_next_proto(SSL *ssl);
-static int ssl3_send_channel_id(SSL *ssl);
-static int ssl3_get_new_session_ticket(SSL *ssl);
+static int ssl3_send_client_hello(SSL_HANDSHAKE *hs);
+static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs);
+static int ssl3_get_server_hello(SSL_HANDSHAKE *hs);
+static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs);
+static int ssl3_get_cert_status(SSL_HANDSHAKE *hs);
+static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs);
+static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs);
+static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs);
+static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs);
+static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs);
+static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs);
+static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs);
+static int ssl3_send_next_proto(SSL_HANDSHAKE *hs);
+static int ssl3_send_channel_id(SSL_HANDSHAKE *hs);
+static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs);
 
-int ssl3_connect(SSL *ssl) {
+int ssl3_connect(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = -1;
   int state, skip = 0;
 
@@ -205,12 +206,6 @@
       case SSL_ST_CONNECT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        ssl->s3->hs = ssl_handshake_new(tls13_client_handshake);
-        if (ssl->s3->hs == NULL) {
-          ret = -1;
-          goto end;
-        }
-
         if (!ssl_init_wbio_buffer(ssl)) {
           ret = -1;
           goto end;
@@ -221,7 +216,7 @@
 
       case SSL3_ST_CW_CLNT_HELLO_A:
       case SSL3_ST_CW_CLNT_HELLO_B:
-        ret = ssl3_send_client_hello(ssl);
+        ret = ssl3_send_client_hello(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -236,7 +231,7 @@
 
       case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
         assert(SSL_is_dtls(ssl));
-        ret = dtls1_get_hello_verify(ssl);
+        ret = dtls1_get_hello_verify(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -249,7 +244,7 @@
         break;
 
       case SSL3_ST_CR_SRVR_HELLO_A:
-        ret = ssl3_get_server_hello(ssl);
+        ret = ssl3_get_server_hello(hs);
         if (ssl->state == SSL_ST_TLS13) {
           break;
         }
@@ -266,7 +261,7 @@
 
       case SSL3_ST_CR_CERT_A:
         if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ret = ssl3_get_server_certificate(ssl);
+          ret = ssl3_get_server_certificate(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -277,8 +272,8 @@
         break;
 
       case SSL3_ST_CR_CERT_STATUS_A:
-        if (ssl->s3->hs->certificate_status_expected) {
-          ret = ssl3_get_cert_status(ssl);
+        if (hs->certificate_status_expected) {
+          ret = ssl3_get_cert_status(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -290,7 +285,7 @@
 
       case SSL3_ST_VERIFY_SERVER_CERT:
         if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ret = ssl3_verify_server_cert(ssl);
+          ret = ssl3_verify_server_cert(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -301,7 +296,7 @@
         break;
 
       case SSL3_ST_CR_KEY_EXCH_A:
-        ret = ssl3_get_server_key_exchange(ssl);
+        ret = ssl3_get_server_key_exchange(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -310,7 +305,7 @@
 
       case SSL3_ST_CR_CERT_REQ_A:
         if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ret = ssl3_get_certificate_request(ssl);
+          ret = ssl3_get_certificate_request(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -321,7 +316,7 @@
         break;
 
       case SSL3_ST_CR_SRVR_DONE_A:
-        ret = ssl3_get_server_hello_done(ssl);
+        ret = ssl3_get_server_hello_done(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -332,8 +327,8 @@
       case SSL3_ST_CW_CERT_A:
       case SSL3_ST_CW_CERT_B:
       case SSL3_ST_CW_CERT_C:
-        if (ssl->s3->hs->cert_request) {
-          ret = ssl3_send_client_certificate(ssl);
+        if (hs->cert_request) {
+          ret = ssl3_send_client_certificate(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -345,7 +340,7 @@
 
       case SSL3_ST_CW_KEY_EXCH_A:
       case SSL3_ST_CW_KEY_EXCH_B:
-        ret = ssl3_send_client_key_exchange(ssl);
+        ret = ssl3_send_client_key_exchange(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -355,8 +350,8 @@
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
       case SSL3_ST_CW_CERT_VRFY_C:
-        if (ssl->s3->hs->cert_request) {
-          ret = ssl3_send_cert_verify(ssl);
+        if (hs->cert_request) {
+          ret = ssl3_send_cert_verify(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -374,7 +369,7 @@
 
         ssl->state = SSL3_ST_CW_NEXT_PROTO_A;
 
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -383,8 +378,8 @@
 
       case SSL3_ST_CW_NEXT_PROTO_A:
       case SSL3_ST_CW_NEXT_PROTO_B:
-        if (ssl->s3->hs->next_proto_neg_seen) {
-          ret = ssl3_send_next_proto(ssl);
+        if (hs->next_proto_neg_seen) {
+          ret = ssl3_send_next_proto(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -397,7 +392,7 @@
       case SSL3_ST_CW_CHANNEL_ID_A:
       case SSL3_ST_CW_CHANNEL_ID_B:
         if (ssl->s3->tlsext_channel_id_valid) {
-          ret = ssl3_send_channel_id(ssl);
+          ret = ssl3_send_channel_id(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -409,7 +404,7 @@
 
       case SSL3_ST_CW_FINISHED_A:
       case SSL3_ST_CW_FINISHED_B:
-        ret = ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A,
+        ret = ssl3_send_finished(hs, SSL3_ST_CW_FINISHED_A,
                                  SSL3_ST_CW_FINISHED_B);
         if (ret <= 0) {
           goto end;
@@ -441,15 +436,15 @@
 
       case SSL3_ST_FALSE_START:
         ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
-        ssl->s3->hs->in_false_start = 1;
+        hs->in_false_start = 1;
 
         ssl_free_wbio_buffer(ssl);
         ret = 1;
         goto end;
 
       case SSL3_ST_CR_SESSION_TICKET_A:
-        if (ssl->s3->hs->ticket_expected) {
-          ret = ssl3_get_new_session_ticket(ssl);
+        if (hs->ticket_expected) {
+          ret = ssl3_get_new_session_ticket(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -465,7 +460,7 @@
           goto end;
         }
 
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
+        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
           ret = -1;
           goto end;
         }
@@ -473,7 +468,7 @@
         break;
 
       case SSL3_ST_CR_FINISHED_A:
-        ret = ssl3_get_finished(ssl);
+        ret = ssl3_get_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -499,7 +494,7 @@
         break;
 
       case SSL_ST_TLS13:
-        ret = tls13_handshake(ssl);
+        ret = tls13_handshake(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -540,12 +535,9 @@
         ssl->s3->initial_handshake_complete = 1;
         if (is_initial_handshake) {
           /* Renegotiations do not participate in session resumption. */
-          ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
+          ssl_update_cache(hs, SSL_SESS_CACHE_CLIENT);
         }
 
-        ssl_handshake_free(ssl->s3->hs);
-        ssl->s3->hs = NULL;
-
         ret = 1;
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         goto end;
@@ -650,7 +642,7 @@
 
   /* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
    * added. */
-  if (ssl->client_version == SSL3_VERSION &&
+  if (max_version == SSL3_VERSION &&
       !ssl->s3->initial_handshake_complete) {
     if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) {
       return 0;
@@ -666,7 +658,8 @@
   return CBB_flush(out);
 }
 
-int ssl_write_client_hello(SSL *ssl) {
+int ssl_write_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -682,7 +675,7 @@
                     !ssl->s3->initial_handshake_complete;
 
   CBB child;
-  if (!CBB_add_u16(&body, ssl->client_version) ||
+  if (!CBB_add_u16(&body, hs->client_version) ||
       !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
       !CBB_add_u8_length_prefixed(&body, &child) ||
       (has_session &&
@@ -703,7 +696,7 @@
   if (!ssl_write_client_cipher_list(ssl, &body, min_version, max_version) ||
       !CBB_add_u8(&body, 1 /* one compression method */) ||
       !CBB_add_u8(&body, 0 /* null compression */) ||
-      !ssl_add_clienthello_tlsext(ssl, &body, header_len + CBB_len(&body))) {
+      !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) {
     goto err;
   }
 
@@ -715,7 +708,7 @@
 
   /* Now that the length prefixes have been computed, fill in the placeholder
    * PSK binder. */
-  if (ssl->s3->hs->needs_psk_binder &&
+  if (hs->needs_psk_binder &&
       !tls13_write_psk_binder(ssl, msg, len)) {
     OPENSSL_free(msg);
     goto err;
@@ -728,7 +721,8 @@
   return 0;
 }
 
-static int ssl3_send_client_hello(SSL *ssl) {
+static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_CLNT_HELLO_B) {
     return ssl->method->write_message(ssl);
   }
@@ -745,18 +739,18 @@
     return -1;
   }
 
+  uint16_t max_wire_version = ssl->method->version_to_wire(max_version);
   assert(ssl->state == SSL3_ST_CW_CLNT_HELLO_A);
   if (!ssl->s3->have_version) {
-    ssl->version = ssl->method->version_to_wire(max_version);
-    /* Only set |ssl->client_version| on the initial handshake. Renegotiations,
-     * although locked to a version, reuse the value. When using the plain RSA
-     * key exchange, the ClientHello version is checked in the premaster secret.
-     * Some servers fail when this value changes. */
-    ssl->client_version = ssl->version;
+    ssl->version = max_wire_version;
+  }
 
-    if (max_version >= TLS1_3_VERSION) {
-      ssl->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
-    }
+  /* Always advertise the ClientHello version from the original maximum version,
+   * even on renegotiation. The static RSA key exchange uses this field, and
+   * some servers fail when it changes across handshakes. */
+  hs->client_version = max_wire_version;
+  if (max_version >= TLS1_3_VERSION) {
+    hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
   }
 
   /* If the configured session has expired or was created at a disabled
@@ -781,7 +775,7 @@
     return -1;
   }
 
-  if (!ssl_write_client_hello(ssl)) {
+  if (!ssl_write_client_hello(hs)) {
     return -1;
   }
 
@@ -789,7 +783,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int dtls1_get_hello_verify(SSL *ssl) {
+static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int al;
   CBS hello_verify_request, cookie;
   uint16_t server_version;
@@ -831,7 +826,8 @@
   return -1;
 }
 
-static int ssl3_get_server_hello(SSL *ssl) {
+static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   CERT *ct = ssl->cert;
   int al = SSL_AD_INTERNAL_ERROR;
   CBS server_hello, server_random, session_id;
@@ -894,10 +890,11 @@
 
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     ssl->state = SSL_ST_TLS13;
+    hs->do_tls13_handshake = tls13_client_handshake;
     return 1;
   }
 
-  ssl_clear_tls13_state(ssl);
+  ssl_clear_tls13_state(hs);
 
   if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
@@ -930,7 +927,7 @@
     /* The session wasn't resumed. Create a fresh SSL_SESSION to
      * fill out. */
     ssl_set_session(ssl, NULL);
-    if (!ssl_get_new_session(ssl, 0 /* client */)) {
+    if (!ssl_get_new_session(hs, 0 /* client */)) {
       goto f_err;
     }
     /* Note: session_id could be empty. */
@@ -1001,7 +998,7 @@
   }
 
   /* TLS extensions */
-  if (!ssl_parse_serverhello_tlsext(ssl, &server_hello)) {
+  if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     goto err;
   }
@@ -1034,7 +1031,8 @@
   return -1;
 }
 
-static int ssl3_get_server_certificate(SSL *ssl) {
+static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret =
       ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE, ssl_hash_message);
   if (ret <= 0) {
@@ -1078,7 +1076,8 @@
   return -1;
 }
 
-static int ssl3_get_cert_status(SSL *ssl) {
+static int ssl3_get_cert_status(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int al;
   CBS certificate_status, ocsp_response;
   uint8_t status_type;
@@ -1119,7 +1118,8 @@
   return -1;
 }
 
-static int ssl3_verify_server_cert(SSL *ssl) {
+static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!ssl_verify_cert_chain(ssl, &ssl->s3->new_session->verify_result,
                              ssl->s3->new_session->x509_chain)) {
     return -1;
@@ -1128,7 +1128,8 @@
   return 1;
 }
 
-static int ssl3_get_server_key_exchange(SSL *ssl) {
+static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int al;
   EVP_PKEY *pkey = NULL;
   DH *dh = NULL;
@@ -1191,7 +1192,7 @@
      * empty hint. Having different capabilities is odd, so we interpret empty
      * and missing as identical. */
     if (CBS_len(&psk_identity_hint) != 0 &&
-        !CBS_strdup(&psk_identity_hint, &ssl->s3->hs->peer_psk_identity_hint)) {
+        !CBS_strdup(&psk_identity_hint, &hs->peer_psk_identity_hint)) {
       al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto f_err;
@@ -1234,11 +1235,11 @@
       goto err;
     }
 
-    SSL_ECDH_CTX_init_for_dhe(&ssl->s3->hs->ecdh_ctx, dh);
+    SSL_ECDH_CTX_init_for_dhe(&hs->ecdh_ctx, dh);
     dh = NULL;
 
     /* Save the peer public key for later. */
-    if (!CBS_stow(&dh_Ys, &ssl->s3->hs->peer_key, &ssl->s3->hs->peer_key_len)) {
+    if (!CBS_stow(&dh_Ys, &hs->peer_key, &hs->peer_key_len)) {
       goto err;
     }
   } else if (alg_k & SSL_kECDHE) {
@@ -1264,12 +1265,12 @@
     }
 
     /* Initialize ECDH and save the peer public key for later. */
-    if (!SSL_ECDH_CTX_init(&ssl->s3->hs->ecdh_ctx, group_id) ||
-        !CBS_stow(&point, &ssl->s3->hs->peer_key, &ssl->s3->hs->peer_key_len)) {
+    if (!SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) ||
+        !CBS_stow(&point, &hs->peer_key, &hs->peer_key_len)) {
       goto err;
     }
   } else if (alg_k & SSL_kCECPQ1) {
-    SSL_ECDH_CTX_init_for_cecpq1(&ssl->s3->hs->ecdh_ctx);
+    SSL_ECDH_CTX_init_for_cecpq1(&hs->ecdh_ctx);
     CBS key;
     if (!CBS_get_u16_length_prefixed(&server_key_exchange, &key)) {
       al = SSL_AD_DECODE_ERROR;
@@ -1277,7 +1278,7 @@
       goto f_err;
     }
 
-    if (!CBS_stow(&key, &ssl->s3->hs->peer_key, &ssl->s3->hs->peer_key_len)) {
+    if (!CBS_stow(&key, &hs->peer_key, &hs->peer_key_len)) {
       goto err;
     }
   } else if (!(alg_k & SSL_kPSK)) {
@@ -1382,7 +1383,8 @@
   return -1;
 }
 
-static int ssl3_get_certificate_request(SSL *ssl) {
+static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int msg_ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
   if (msg_ret <= 0) {
     return msg_ret;
@@ -1413,8 +1415,8 @@
     return -1;
   }
 
-  if (!CBS_stow(&certificate_types, &ssl->s3->hs->certificate_types,
-                &ssl->s3->hs->num_certificate_types)) {
+  if (!CBS_stow(&certificate_types, &hs->certificate_types,
+                &hs->num_certificate_types)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
   }
@@ -1422,7 +1424,7 @@
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     CBS supported_signature_algorithms;
     if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
-        !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
+        !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       return -1;
@@ -1443,13 +1445,14 @@
     return -1;
   }
 
-  ssl->s3->hs->cert_request = 1;
-  sk_X509_NAME_pop_free(ssl->s3->hs->ca_names, X509_NAME_free);
-  ssl->s3->hs->ca_names = ca_sk;
+  hs->cert_request = 1;
+  sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
+  hs->ca_names = ca_sk;
   return 1;
 }
 
-static int ssl3_get_server_hello_done(SSL *ssl) {
+static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO_DONE,
                                          ssl_hash_message);
   if (ret <= 0) {
@@ -1466,7 +1469,8 @@
   return 1;
 }
 
-static int ssl3_send_client_certificate(SSL *ssl) {
+static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_CERT_A) {
     /* Call cert_cb to update the certificate. */
     if (ssl->cert->cert_cb) {
@@ -1496,7 +1500,7 @@
     }
 
     if (!ssl_has_certificate(ssl)) {
-      ssl->s3->hs->cert_request = 0;
+      hs->cert_request = 0;
       /* Without a client certificate, the handshake buffer may be released. */
       ssl3_free_handshake_buffer(ssl);
 
@@ -1520,7 +1524,8 @@
 OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
                        SIZE_T_IS_SMALLER_THAN_UNSIGNED);
 
-static int ssl3_send_client_key_exchange(SSL *ssl) {
+static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_KEY_EXCH_B) {
     return ssl->method->write_message(ssl);
   }
@@ -1548,9 +1553,9 @@
 
     char identity[PSK_MAX_IDENTITY_LEN + 1];
     memset(identity, 0, sizeof(identity));
-    psk_len = ssl->psk_client_callback(
-        ssl, ssl->s3->hs->peer_psk_identity_hint, identity, sizeof(identity),
-        psk, sizeof(psk));
+    psk_len =
+        ssl->psk_client_callback(ssl, hs->peer_psk_identity_hint, identity,
+                                 sizeof(identity), psk, sizeof(psk));
     if (psk_len == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -1598,8 +1603,8 @@
 
     EVP_PKEY_free(pkey);
 
-    pms[0] = ssl->client_version >> 8;
-    pms[1] = ssl->client_version & 0xff;
+    pms[0] = hs->client_version >> 8;
+    pms[1] = hs->client_version & 0xff;
     if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) {
       goto err;
     }
@@ -1627,15 +1632,14 @@
   } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
     /* Generate a keypair and serialize the public half. */
     CBB child;
-    if (!SSL_ECDH_CTX_add_key(&ssl->s3->hs->ecdh_ctx, &body, &child)) {
+    if (!SSL_ECDH_CTX_add_key(&hs->ecdh_ctx, &body, &child)) {
       goto err;
     }
 
     /* Compute the premaster. */
     uint8_t alert;
-    if (!SSL_ECDH_CTX_accept(&ssl->s3->hs->ecdh_ctx, &child, &pms, &pms_len,
-                             &alert, ssl->s3->hs->peer_key,
-                             ssl->s3->hs->peer_key_len)) {
+    if (!SSL_ECDH_CTX_accept(&hs->ecdh_ctx, &child, &pms, &pms_len, &alert,
+                             hs->peer_key, hs->peer_key_len)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
       goto err;
     }
@@ -1644,10 +1648,10 @@
     }
 
     /* The key exchange state may now be discarded. */
-    SSL_ECDH_CTX_cleanup(&ssl->s3->hs->ecdh_ctx);
-    OPENSSL_free(ssl->s3->hs->peer_key);
-    ssl->s3->hs->peer_key = NULL;
-    ssl->s3->hs->peer_key_len = 0;
+    SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
+    OPENSSL_free(hs->peer_key);
+    hs->peer_key = NULL;
+    hs->peer_key_len = 0;
   } else if (alg_k & SSL_kPSK) {
     /* For plain PSK, other_secret is a block of 0s with the same length as
      * the pre-shared key. */
@@ -1717,7 +1721,8 @@
   return -1;
 }
 
-static int ssl3_send_cert_verify(SSL *ssl) {
+static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) {
     return ssl->method->write_message(ssl);
   }
@@ -1731,7 +1736,7 @@
   }
 
   uint16_t signature_algorithm;
-  if (!tls1_choose_signature_algorithm(ssl, &signature_algorithm)) {
+  if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
     goto err;
   }
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
@@ -1819,7 +1824,8 @@
   return -1;
 }
 
-static int ssl3_send_next_proto(SSL *ssl) {
+static int ssl3_send_next_proto(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) {
     return ssl->method->write_message(ssl);
   }
@@ -1846,7 +1852,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_send_channel_id(SSL *ssl) {
+static int ssl3_send_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_CW_CHANNEL_ID_B) {
     return ssl->method->write_message(ssl);
   }
@@ -1875,7 +1882,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_get_new_session_ticket(SSL *ssl) {
+static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
                                          ssl_hash_message);
   if (ret <= 0) {
@@ -1897,7 +1905,7 @@
     /* RFC 5077 allows a server to change its mind and send no ticket after
      * negotiating the extension. The value of |ticket_expected| is checked in
      * |ssl_update_cache| so is cleared here to avoid an unnecessary update. */
-    ssl->s3->hs->ticket_expected = 0;
+    hs->ticket_expected = 0;
     return 1;
   }
 
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index 3b66ab7..c114074 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -171,21 +171,22 @@
 #include "../crypto/internal.h"
 
 
-static int ssl3_get_client_hello(SSL *ssl);
-static int ssl3_send_server_hello(SSL *ssl);
-static int ssl3_send_server_certificate(SSL *ssl);
-static int ssl3_send_certificate_status(SSL *ssl);
-static int ssl3_send_server_key_exchange(SSL *ssl);
-static int ssl3_send_certificate_request(SSL *ssl);
-static int ssl3_send_server_hello_done(SSL *ssl);
-static int ssl3_get_client_certificate(SSL *ssl);
-static int ssl3_get_client_key_exchange(SSL *ssl);
-static int ssl3_get_cert_verify(SSL *ssl);
-static int ssl3_get_next_proto(SSL *ssl);
-static int ssl3_get_channel_id(SSL *ssl);
-static int ssl3_send_new_session_ticket(SSL *ssl);
+static int ssl3_get_client_hello(SSL_HANDSHAKE *hs);
+static int ssl3_send_server_hello(SSL_HANDSHAKE *hs);
+static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs);
+static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs);
+static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs);
+static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs);
+static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs);
+static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs);
+static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs);
+static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs);
+static int ssl3_get_next_proto(SSL_HANDSHAKE *hs);
+static int ssl3_get_channel_id(SSL_HANDSHAKE *hs);
+static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs);
 
-int ssl3_accept(SSL *ssl) {
+int ssl3_accept(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint32_t alg_a;
   int ret = -1;
   int state, skip = 0;
@@ -205,12 +206,6 @@
       case SSL_ST_ACCEPT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        ssl->s3->hs = ssl_handshake_new(tls13_server_handshake);
-        if (ssl->s3->hs == NULL) {
-          ret = -1;
-          goto end;
-        }
-
         /* Enable a write buffer. This groups handshake messages within a flight
          * into a single write. */
         if (!ssl_init_wbio_buffer(ssl)) {
@@ -232,7 +227,7 @@
       case SSL3_ST_SR_CLNT_HELLO_C:
       case SSL3_ST_SR_CLNT_HELLO_D:
       case SSL3_ST_SR_CLNT_HELLO_E:
-        ret = ssl3_get_client_hello(ssl);
+        ret = ssl3_get_client_hello(hs);
         if (ssl->state == SSL_ST_TLS13) {
           break;
         }
@@ -245,7 +240,7 @@
 
       case SSL3_ST_SW_SRVR_HELLO_A:
       case SSL3_ST_SW_SRVR_HELLO_B:
-        ret = ssl3_send_server_hello(ssl);
+        ret = ssl3_send_server_hello(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -259,7 +254,7 @@
       case SSL3_ST_SW_CERT_A:
       case SSL3_ST_SW_CERT_B:
         if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ret = ssl3_send_server_certificate(ssl);
+          ret = ssl3_send_server_certificate(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -271,8 +266,8 @@
 
       case SSL3_ST_SW_CERT_STATUS_A:
       case SSL3_ST_SW_CERT_STATUS_B:
-        if (ssl->s3->hs->certificate_status_expected) {
-          ret = ssl3_send_certificate_status(ssl);
+        if (hs->certificate_status_expected) {
+          ret = ssl3_send_certificate_status(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -290,7 +285,7 @@
         /* PSK ciphers send ServerKeyExchange if there is an identity hint. */
         if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher) ||
             ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
-          ret = ssl3_send_server_key_exchange(ssl);
+          ret = ssl3_send_server_key_exchange(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -303,8 +298,8 @@
 
       case SSL3_ST_SW_CERT_REQ_A:
       case SSL3_ST_SW_CERT_REQ_B:
-        if (ssl->s3->hs->cert_request) {
-          ret = ssl3_send_certificate_request(ssl);
+        if (hs->cert_request) {
+          ret = ssl3_send_certificate_request(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -316,7 +311,7 @@
 
       case SSL3_ST_SW_SRVR_DONE_A:
       case SSL3_ST_SW_SRVR_DONE_B:
-        ret = ssl3_send_server_hello_done(ssl);
+        ret = ssl3_send_server_hello_done(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -325,8 +320,8 @@
         break;
 
       case SSL3_ST_SR_CERT_A:
-        if (ssl->s3->hs->cert_request) {
-          ret = ssl3_get_client_certificate(ssl);
+        if (hs->cert_request) {
+          ret = ssl3_get_client_certificate(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -336,7 +331,7 @@
 
       case SSL3_ST_SR_KEY_EXCH_A:
       case SSL3_ST_SR_KEY_EXCH_B:
-        ret = ssl3_get_client_key_exchange(ssl);
+        ret = ssl3_get_client_key_exchange(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -344,7 +339,7 @@
         break;
 
       case SSL3_ST_SR_CERT_VRFY_A:
-        ret = ssl3_get_cert_verify(ssl);
+        ret = ssl3_get_cert_verify(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -358,7 +353,7 @@
           goto end;
         }
 
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_READ)) {
+        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_READ)) {
           ret = -1;
           goto end;
         }
@@ -367,8 +362,8 @@
         break;
 
       case SSL3_ST_SR_NEXT_PROTO_A:
-        if (ssl->s3->hs->next_proto_neg_seen) {
-          ret = ssl3_get_next_proto(ssl);
+        if (hs->next_proto_neg_seen) {
+          ret = ssl3_get_next_proto(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -380,7 +375,7 @@
 
       case SSL3_ST_SR_CHANNEL_ID_A:
         if (ssl->s3->tlsext_channel_id_valid) {
-          ret = ssl3_get_channel_id(ssl);
+          ret = ssl3_get_channel_id(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -391,7 +386,7 @@
         break;
 
       case SSL3_ST_SR_FINISHED_A:
-        ret = ssl3_get_finished(ssl);
+        ret = ssl3_get_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -416,8 +411,8 @@
 
       case SSL3_ST_SW_SESSION_TICKET_A:
       case SSL3_ST_SW_SESSION_TICKET_B:
-        if (ssl->s3->hs->ticket_expected) {
-          ret = ssl3_send_new_session_ticket(ssl);
+        if (hs->ticket_expected) {
+          ret = ssl3_send_new_session_ticket(hs);
           if (ret <= 0) {
             goto end;
           }
@@ -434,7 +429,7 @@
         }
         ssl->state = SSL3_ST_SW_FINISHED_A;
 
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -442,7 +437,7 @@
 
       case SSL3_ST_SW_FINISHED_A:
       case SSL3_ST_SW_FINISHED_B:
-        ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
+        ret = ssl3_send_finished(hs, SSL3_ST_SW_FINISHED_A,
                                  SSL3_ST_SW_FINISHED_B);
         if (ret <= 0) {
           goto end;
@@ -469,7 +464,7 @@
         break;
 
       case SSL_ST_TLS13:
-        ret = tls13_handshake(ssl);
+        ret = tls13_handshake(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -503,10 +498,7 @@
         ssl_free_wbio_buffer(ssl);
 
         ssl->s3->initial_handshake_complete = 1;
-        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
-
-        ssl_handshake_free(ssl->s3->hs);
-        ssl->s3->hs = NULL;
+        ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
 
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         ret = 1;
@@ -532,8 +524,8 @@
   return ret;
 }
 
-int ssl_client_cipher_list_contains_cipher(
-    const struct ssl_early_callback_ctx *client_hello, uint16_t id) {
+int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
+                                           uint16_t id) {
   CBS cipher_suites;
   CBS_init(&cipher_suites, client_hello->cipher_suites,
            client_hello->cipher_suites_len);
@@ -552,9 +544,9 @@
   return 0;
 }
 
-static int negotiate_version(
-    SSL *ssl, uint8_t *out_alert,
-    const struct ssl_early_callback_ctx *client_hello) {
+static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                             const SSL_CLIENT_HELLO *client_hello) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     *out_alert = SSL_AD_PROTOCOL_VERSION;
@@ -564,8 +556,8 @@
   uint16_t version = 0;
   /* Check supported_versions extension if it is present. */
   CBS supported_versions;
-  if (ssl_early_callback_get_extension(client_hello, &supported_versions,
-                                       TLSEXT_TYPE_supported_versions)) {
+  if (ssl_client_hello_get_extension(client_hello, &supported_versions,
+                                     TLSEXT_TYPE_supported_versions)) {
     CBS versions;
     if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) ||
         CBS_len(&supported_versions) != 0 ||
@@ -644,7 +636,7 @@
     return 0;
   }
 
-  ssl->client_version = client_hello->version;
+  hs->client_version = client_hello->version;
   ssl->version = ssl->method->version_to_wire(version);
   ssl->s3->enc_method = ssl3_get_enc_method(version);
   assert(ssl->s3->enc_method != NULL);
@@ -661,7 +653,171 @@
   return 0;
 }
 
-static int ssl3_get_client_hello(SSL *ssl) {
+static STACK_OF(SSL_CIPHER) *
+    ssl_parse_client_cipher_list(const SSL_CLIENT_HELLO *client_hello) {
+  CBS cipher_suites;
+  CBS_init(&cipher_suites, client_hello->cipher_suites,
+           client_hello->cipher_suites_len);
+
+  STACK_OF(SSL_CIPHER) *sk = sk_SSL_CIPHER_new_null();
+  if (sk == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t cipher_suite;
+
+    if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
+      goto err;
+    }
+
+    const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
+    if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
+
+  return sk;
+
+err:
+  sk_SSL_CIPHER_free(sk);
+  return NULL;
+}
+
+/* ssl_get_compatible_server_ciphers determines the key exchange and
+ * authentication cipher suite masks compatible with the server configuration
+ * and current ClientHello parameters of |hs|. It sets |*out_mask_k| to the key
+ * exchange mask and |*out_mask_a| to the authentication mask. */
+static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
+                                              uint32_t *out_mask_k,
+                                              uint32_t *out_mask_a) {
+  SSL *const ssl = hs->ssl;
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    *out_mask_k = SSL_kGENERIC;
+    *out_mask_a = SSL_aGENERIC;
+    return;
+  }
+
+  uint32_t mask_k = 0;
+  uint32_t mask_a = 0;
+
+  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
+    int type = ssl_private_key_type(ssl);
+    if (type == NID_rsaEncryption) {
+      mask_k |= SSL_kRSA;
+      mask_a |= SSL_aRSA;
+    } else if (ssl_is_ecdsa_key_type(type)) {
+      mask_a |= SSL_aECDSA;
+    }
+  }
+
+  if (ssl->cert->dh_tmp != NULL || ssl->cert->dh_tmp_cb != NULL) {
+    mask_k |= SSL_kDHE;
+  }
+
+  /* Check for a shared group to consider ECDHE ciphers. */
+  uint16_t unused;
+  if (tls1_get_shared_group(hs, &unused)) {
+    mask_k |= SSL_kECDHE;
+  }
+
+  /* CECPQ1 ciphers are always acceptable if supported by both sides. */
+  mask_k |= SSL_kCECPQ1;
+
+  /* PSK requires a server callback. */
+  if (ssl->psk_server_callback != NULL) {
+    mask_k |= SSL_kPSK;
+    mask_a |= SSL_aPSK;
+  }
+
+  *out_mask_k = mask_k;
+  *out_mask_a = mask_a;
+}
+
+static const SSL_CIPHER *ssl3_choose_cipher(
+    SSL_HANDSHAKE *hs, const SSL_CLIENT_HELLO *client_hello,
+    const struct ssl_cipher_preference_list_st *server_pref) {
+  SSL *const ssl = hs->ssl;
+  const SSL_CIPHER *c, *ret = NULL;
+  STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;
+  int ok;
+  size_t cipher_index;
+  uint32_t alg_k, alg_a, mask_k, mask_a;
+  /* in_group_flags will either be NULL, or will point to an array of bytes
+   * which indicate equal-preference groups in the |prio| stack. See the
+   * comment about |in_group_flags| in the |ssl_cipher_preference_list_st|
+   * struct. */
+  const uint8_t *in_group_flags;
+  /* group_min contains the minimal index so far found in a group, or -1 if no
+   * such value exists yet. */
+  int group_min = -1;
+
+  STACK_OF(SSL_CIPHER) *clnt = ssl_parse_client_cipher_list(client_hello);
+  if (clnt == NULL) {
+    return NULL;
+  }
+
+  if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
+    prio = srvr;
+    in_group_flags = server_pref->in_group_flags;
+    allow = clnt;
+  } else {
+    prio = clnt;
+    in_group_flags = NULL;
+    allow = srvr;
+  }
+
+  ssl_get_compatible_server_ciphers(hs, &mask_k, &mask_a);
+
+  for (size_t i = 0; i < sk_SSL_CIPHER_num(prio); i++) {
+    c = sk_SSL_CIPHER_value(prio, i);
+
+    ok = 1;
+
+    /* Check the TLS version. */
+    if (SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
+        SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) {
+      ok = 0;
+    }
+
+    alg_k = c->algorithm_mkey;
+    alg_a = c->algorithm_auth;
+
+    ok = ok && (alg_k & mask_k) && (alg_a & mask_a);
+
+    if (ok && sk_SSL_CIPHER_find(allow, &cipher_index, c)) {
+      if (in_group_flags != NULL && in_group_flags[i] == 1) {
+        /* This element of |prio| is in a group. Update the minimum index found
+         * so far and continue looking. */
+        if (group_min == -1 || (size_t)group_min > cipher_index) {
+          group_min = cipher_index;
+        }
+      } else {
+        if (group_min != -1 && (size_t)group_min < cipher_index) {
+          cipher_index = group_min;
+        }
+        ret = sk_SSL_CIPHER_value(allow, cipher_index);
+        break;
+      }
+    }
+
+    if (in_group_flags != NULL && in_group_flags[i] == 0 && group_min != -1) {
+      /* We are about to leave a group, but we found a match in it, so that's
+       * our answer. */
+      ret = sk_SSL_CIPHER_value(allow, group_min);
+      break;
+    }
+  }
+
+  sk_SSL_CIPHER_free(clnt);
+  return ret;
+}
+
+static int ssl3_get_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint8_t al = SSL_AD_INTERNAL_ERROR;
   int ret = -1;
   SSL_SESSION *session = NULL;
@@ -677,9 +833,9 @@
     ssl->state = SSL3_ST_SR_CLNT_HELLO_B;
   }
 
-  struct ssl_early_callback_ctx client_hello;
-  if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
-                               ssl->init_num)) {
+  SSL_CLIENT_HELLO client_hello;
+  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+                             ssl->init_num)) {
     al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     goto f_err;
@@ -711,12 +867,13 @@
 
   /* Negotiate the protocol version if we have not done so yet. */
   if (!ssl->s3->have_version) {
-    if (!negotiate_version(ssl, &al, &client_hello)) {
+    if (!negotiate_version(hs, &al, &client_hello)) {
       goto f_err;
     }
 
     if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
       ssl->state = SSL_ST_TLS13;
+      hs->do_tls13_handshake = tls13_server_handshake;
       return 1;
     }
   }
@@ -739,7 +896,7 @@
     }
 
     /* TLS extensions. */
-    if (!ssl_parse_clienthello_tlsext(ssl, &client_hello)) {
+    if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
       goto err;
     }
@@ -765,7 +922,7 @@
     /* Negotiate the cipher suite. This must be done after |cert_cb| so the
      * certificate is finalized. */
     ssl->s3->tmp.new_cipher =
-        ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
+        ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
     if (ssl->s3->tmp.new_cipher == NULL) {
       al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
@@ -812,14 +969,14 @@
 
   if (session != NULL) {
     /* Use the old session. */
-    ssl->s3->hs->ticket_expected = renew_ticket;
+    hs->ticket_expected = renew_ticket;
     ssl->session = session;
     session = NULL;
     ssl->s3->session_reused = 1;
   } else {
-    ssl->s3->hs->ticket_expected = tickets_supported;
+    hs->ticket_expected = tickets_supported;
     ssl_set_session(ssl, NULL);
-    if (!ssl_get_new_session(ssl, 1 /* server */)) {
+    if (!ssl_get_new_session(hs, 1 /* server */)) {
       goto err;
     }
 
@@ -841,8 +998,8 @@
     ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
 
     /* On new sessions, stash the SNI value in the session. */
-    if (ssl->s3->hs->hostname != NULL) {
-      ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+    if (hs->hostname != NULL) {
+      ssl->s3->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
       if (ssl->s3->new_session->tlsext_hostname == NULL) {
         al = SSL_AD_INTERNAL_ERROR;
         goto f_err;
@@ -850,18 +1007,18 @@
     }
 
     /* Determine whether to request a client certificate. */
-    ssl->s3->hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+    hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
     /* Only request a certificate if Channel ID isn't negotiated. */
     if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
         ssl->s3->tlsext_channel_id_valid) {
-      ssl->s3->hs->cert_request = 0;
+      hs->cert_request = 0;
     }
     /* CertificateRequest may only be sent in certificate-based ciphers. */
     if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-      ssl->s3->hs->cert_request = 0;
+      hs->cert_request = 0;
     }
 
-    if (!ssl->s3->hs->cert_request) {
+    if (!hs->cert_request) {
       /* OpenSSL returns X509_V_OK when no certificates are requested. This is
        * classed by them as a bug, but it's assumed by at least NGINX. */
       ssl->s3->new_session->verify_result = X509_V_OK;
@@ -870,7 +1027,7 @@
 
   /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
    * deferred. Complete it now. */
-  if (!ssl_negotiate_alpn(ssl, &al, &client_hello)) {
+  if (!ssl_negotiate_alpn(hs, &al, &client_hello)) {
     goto f_err;
   }
 
@@ -880,7 +1037,7 @@
   }
 
   /* Release the handshake buffer if client authentication isn't required. */
-  if (!ssl->s3->hs->cert_request) {
+  if (!hs->cert_request) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -896,7 +1053,8 @@
   return ret;
 }
 
-static int ssl3_send_server_hello(SSL *ssl) {
+static int ssl3_send_server_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_SRVR_HELLO_B) {
     return ssl->method->write_message(ssl);
   }
@@ -945,7 +1103,7 @@
                      session->session_id_length) ||
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u8(&body, 0 /* no compression */) ||
-      !ssl_add_serverhello_tlsext(ssl, &body) ||
+      !ssl_add_serverhello_tlsext(hs, &body) ||
       !ssl_complete_message(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
@@ -956,7 +1114,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_send_server_certificate(SSL *ssl) {
+static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_CERT_B) {
     return ssl->method->write_message(ssl);
   }
@@ -973,7 +1132,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_send_certificate_status(SSL *ssl) {
+static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_CERT_STATUS_B) {
     return ssl->method->write_message(ssl);
   }
@@ -995,7 +1155,8 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_send_server_key_exchange(SSL *ssl) {
+static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) {
     return ssl->method->write_message(ssl);
   }
@@ -1043,19 +1204,19 @@
         goto err;
       }
 
-      SSL_ECDH_CTX_init_for_dhe(&ssl->s3->hs->ecdh_ctx, dh);
+      SSL_ECDH_CTX_init_for_dhe(&hs->ecdh_ctx, dh);
       if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
           !BN_bn2cbb_padded(&child, BN_num_bytes(params->p), params->p) ||
           !CBB_add_u16_length_prefixed(&cbb, &child) ||
           !BN_bn2cbb_padded(&child, BN_num_bytes(params->g), params->g) ||
           !CBB_add_u16_length_prefixed(&cbb, &child) ||
-          !SSL_ECDH_CTX_offer(&ssl->s3->hs->ecdh_ctx, &child)) {
+          !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) {
         goto err;
       }
     } else if (alg_k & SSL_kECDHE) {
       /* Determine the group to use. */
       uint16_t group_id;
-      if (!tls1_get_shared_group(ssl, &group_id)) {
+      if (!tls1_get_shared_group(hs, &group_id)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
@@ -1063,25 +1224,24 @@
       ssl->s3->new_session->key_exchange_info = group_id;
 
       /* Set up ECDH, generate a key, and emit the public half. */
-      if (!SSL_ECDH_CTX_init(&ssl->s3->hs->ecdh_ctx, group_id) ||
+      if (!SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) ||
           !CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
           !CBB_add_u16(&cbb, group_id) ||
           !CBB_add_u8_length_prefixed(&cbb, &child) ||
-          !SSL_ECDH_CTX_offer(&ssl->s3->hs->ecdh_ctx, &child)) {
+          !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) {
         goto err;
       }
     } else if (alg_k & SSL_kCECPQ1) {
-      SSL_ECDH_CTX_init_for_cecpq1(&ssl->s3->hs->ecdh_ctx);
+      SSL_ECDH_CTX_init_for_cecpq1(&hs->ecdh_ctx);
       if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
-          !SSL_ECDH_CTX_offer(&ssl->s3->hs->ecdh_ctx, &child)) {
+          !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) {
         goto err;
       }
     } else {
       assert(alg_k & SSL_kPSK);
     }
 
-    if (!CBB_finish(&cbb, &ssl->s3->hs->server_params,
-                    &ssl->s3->hs->server_params_len)) {
+    if (!CBB_finish(&cbb, &hs->server_params, &hs->server_params_len)) {
       goto err;
     }
   }
@@ -1090,8 +1250,7 @@
   CBB body;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_SERVER_KEY_EXCHANGE) ||
-      !CBB_add_bytes(&body, ssl->s3->hs->server_params,
-                     ssl->s3->hs->server_params_len)) {
+      !CBB_add_bytes(&body, hs->server_params, hs->server_params_len)) {
     goto err;
   }
 
@@ -1104,7 +1263,7 @@
 
     /* Determine the signature algorithm. */
     uint16_t signature_algorithm;
-    if (!tls1_choose_signature_algorithm(ssl, &signature_algorithm)) {
+    if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
       goto err;
     }
     if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
@@ -1130,11 +1289,13 @@
       uint8_t *transcript_data;
       size_t transcript_len;
       if (!CBB_init(&transcript,
-                    2*SSL3_RANDOM_SIZE + ssl->s3->hs->server_params_len) ||
-          !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
-          !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
-          !CBB_add_bytes(&transcript, ssl->s3->hs->server_params,
-                         ssl->s3->hs->server_params_len) ||
+                    2 * SSL3_RANDOM_SIZE + hs->server_params_len) ||
+          !CBB_add_bytes(&transcript, ssl->s3->client_random,
+                         SSL3_RANDOM_SIZE) ||
+          !CBB_add_bytes(&transcript, ssl->s3->server_random,
+                         SSL3_RANDOM_SIZE) ||
+          !CBB_add_bytes(&transcript, hs->server_params,
+                         hs->server_params_len) ||
           !CBB_finish(&transcript, &transcript_data, &transcript_len)) {
         CBB_cleanup(&transcript);
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1170,9 +1331,9 @@
     goto err;
   }
 
-  OPENSSL_free(ssl->s3->hs->server_params);
-  ssl->s3->hs->server_params = NULL;
-  ssl->s3->hs->server_params_len = 0;
+  OPENSSL_free(hs->server_params);
+  hs->server_params = NULL;
+  hs->server_params_len = 0;
 
   ssl->state = SSL3_ST_SW_KEY_EXCH_C;
   return ssl->method->write_message(ssl);
@@ -1220,7 +1381,8 @@
   return 1;
 }
 
-static int ssl3_send_certificate_request(SSL *ssl) {
+static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_CERT_REQ_B) {
     return ssl->method->write_message(ssl);
   }
@@ -1261,7 +1423,8 @@
   return -1;
 }
 
-static int ssl3_send_server_hello_done(SSL *ssl) {
+static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_SRVR_DONE_B) {
     return ssl->method->write_message(ssl);
   }
@@ -1278,8 +1441,9 @@
   return ssl->method->write_message(ssl);
 }
 
-static int ssl3_get_client_certificate(SSL *ssl) {
-  assert(ssl->s3->hs->cert_request);
+static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  assert(hs->cert_request);
 
   int msg_ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
   if (msg_ret <= 0) {
@@ -1377,7 +1541,8 @@
   return -1;
 }
 
-static int ssl3_get_client_key_exchange(SSL *ssl) {
+static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int al;
   CBS client_key_exchange;
   uint32_t alg_k;
@@ -1544,9 +1709,9 @@
     /* The premaster secret must begin with |client_version|. This too must be
      * checked in constant time (http://eprint.iacr.org/2003/052/). */
     good &= constant_time_eq_8(decrypt_buf[padding_len],
-                               (unsigned)(ssl->client_version >> 8));
+                               (unsigned)(hs->client_version >> 8));
     good &= constant_time_eq_8(decrypt_buf[padding_len + 1],
-                               (unsigned)(ssl->client_version & 0xff));
+                               (unsigned)(hs->client_version & 0xff));
 
     /* Select, in constant time, either the decrypted premaster or the random
      * premaster based on |good|. */
@@ -1560,8 +1725,7 @@
   } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
     /* Parse the ClientKeyExchange. */
     CBS peer_key;
-    if (!SSL_ECDH_CTX_get_key(&ssl->s3->hs->ecdh_ctx, &client_key_exchange,
-                              &peer_key) ||
+    if (!SSL_ECDH_CTX_get_key(&hs->ecdh_ctx, &client_key_exchange, &peer_key) ||
         CBS_len(&client_key_exchange) != 0) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -1570,7 +1734,7 @@
 
     /* Compute the premaster. */
     uint8_t alert;
-    if (!SSL_ECDH_CTX_finish(&ssl->s3->hs->ecdh_ctx, &premaster_secret,
+    if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, &premaster_secret,
                              &premaster_secret_len, &alert, CBS_data(&peer_key),
                              CBS_len(&peer_key))) {
       al = alert;
@@ -1578,7 +1742,7 @@
     }
 
     /* The key exchange state may now be discarded. */
-    SSL_ECDH_CTX_cleanup(&ssl->s3->hs->ecdh_ctx);
+    SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
   } else if (alg_k & SSL_kPSK) {
     /* For plain PSK, other_secret is a block of 0s with the same length as the
      * pre-shared key. */
@@ -1646,7 +1810,8 @@
   return -1;
 }
 
-static int ssl3_get_cert_verify(SSL *ssl) {
+static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int al, ret = 0;
   CBS certificate_verify, signature;
   X509 *peer = ssl->s3->new_session->x509_peer;
@@ -1762,7 +1927,8 @@
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
  * sets the next_proto member in s if found */
-static int ssl3_get_next_proto(SSL *ssl) {
+static int ssl3_get_next_proto(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret =
       ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message);
   if (ret <= 0) {
@@ -1788,7 +1954,8 @@
 }
 
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
-static int ssl3_get_channel_id(SSL *ssl) {
+static int ssl3_get_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID,
                                              ssl_dont_hash_message);
   if (msg_ret <= 0) {
@@ -1802,7 +1969,8 @@
   return 1;
 }
 
-static int ssl3_send_new_session_ticket(SSL *ssl) {
+static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == SSL3_ST_SW_SESSION_TICKET_B) {
     return ssl->method->write_message(ssl);
   }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 1d78ec1..995adcf 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -509,12 +509,12 @@
 
 void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension);
 
-int custom_ext_add_clienthello(SSL *ssl, CBB *extensions);
-int custom_ext_parse_serverhello(SSL *ssl, int *out_alert, uint16_t value,
-                                 const CBS *extension);
-int custom_ext_parse_clienthello(SSL *ssl, int *out_alert, uint16_t value,
-                                 const CBS *extension);
-int custom_ext_add_serverhello(SSL *ssl, CBB *extensions);
+int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions);
+int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert,
+                                 uint16_t value, const CBS *extension);
+int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert,
+                                 uint16_t value, const CBS *extension);
+int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions);
 
 
 /* Handshake hash.
@@ -798,11 +798,12 @@
 /* tls13_init_key_schedule initializes the handshake hash and key derivation
  * state. The cipher suite and PRF hash must have been selected at this point.
  * It returns one on success and zero on error. */
-int tls13_init_key_schedule(SSL *ssl);
+int tls13_init_key_schedule(SSL_HANDSHAKE *hs);
 
 /* tls13_advance_key_schedule incorporates |in| into the key schedule with
  * HKDF-Extract. It returns one on success and zero on error. */
-int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len);
+int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
+                               size_t len);
 
 /* tls13_get_context_hash writes Hash(Handshake Context) to |out| which must
  * have room for at least |EVP_MAX_MD_SIZE| bytes. On success, it returns one
@@ -810,13 +811,6 @@
  * zero. */
 int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len);
 
-enum tls_record_type_t {
-  type_early_handshake,
-  type_early_data,
-  type_handshake,
-  type_data,
-};
-
 /* tls13_set_traffic_key sets the read or write traffic keys to
  * |traffic_secret|. It returns one on success and zero on error. */
 int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
@@ -826,7 +820,7 @@
 /* tls13_set_handshake_traffic derives the handshake traffic secret and
  * switches both read and write traffic to it. It returns one on success and
  * zero on error. */
-int tls13_set_handshake_traffic(SSL *ssl);
+int tls13_set_handshake_traffic(SSL_HANDSHAKE *hs);
 
 /* tls13_rotate_traffic_key derives the next read or write traffic secret. It
  * returns one on success and zero on error. */
@@ -835,10 +829,10 @@
 /* tls13_derive_application_secrets derives the initial application data traffic
  * and exporter secrets based on the handshake transcripts and |master_secret|.
  * It returns one on success and zero on error. */
-int tls13_derive_application_secrets(SSL *ssl);
+int tls13_derive_application_secrets(SSL_HANDSHAKE *hs);
 
 /* tls13_derive_resumption_secret derives the |resumption_secret|. */
-int tls13_derive_resumption_secret(SSL *ssl);
+int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs);
 
 /* tls13_export_keying_material provides an exporter interface to use the
  * |exporter_secret|. */
@@ -851,7 +845,8 @@
  * the integrity of the Finished message, and stores the result in |out| and
  * length in |out_len|. |is_server| is 1 if this is for the Server Finished and
  * 0 for the Client Finished. */
-int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server);
+int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out,
+                       size_t *out_len, int is_server);
 
 /* tls13_write_psk_binder calculates the PSK binder value and replaces the last
  * bytes of |msg| with the resulting value. It returns 1 on success, and 0 on
@@ -878,15 +873,18 @@
   ssl_hs_private_key_operation,
 };
 
-typedef struct ssl_handshake_st {
-  /* wait contains the operation |do_handshake| is currently blocking on or
-   * |ssl_hs_ok| if none. */
-  enum ssl_hs_wait_t wait;
+struct ssl_handshake_st {
+  /* ssl is a non-owning pointer to the parent |SSL| object. */
+  SSL *ssl;
 
-  /* do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
-   * Otherwise, it returns a value corresponding to what operation is needed to
-   * progress. */
-  enum ssl_hs_wait_t (*do_handshake)(SSL *ssl);
+  /* do_tls13_handshake runs the TLS 1.3 handshake. On completion, it returns
+   * |ssl_hs_ok|. Otherwise, it returns a value corresponding to what operation
+   * is needed to progress. */
+  enum ssl_hs_wait_t (*do_tls13_handshake)(SSL_HANDSHAKE *hs);
+
+  /* wait contains the operation |do_tls13_handshake| is currently blocking on
+   * or |ssl_hs_ok| if none. */
+  enum ssl_hs_wait_t wait;
 
   int state;
 
@@ -916,26 +914,13 @@
     uint16_t received;
   } custom_extensions;
 
-  /* ecdh_ctx is the current ECDH instance. */
-  SSL_ECDH_CTX ecdh_ctx;
-
-  /* scts_requested is one if the SCT extension is in the ClientHello. */
-  unsigned scts_requested:1;
-
-  /* needs_psk_binder if the ClientHello has a placeholder PSK binder to be
-   * filled in. */
-  unsigned needs_psk_binder:1;
-
-  unsigned received_hello_retry_request:1;
-
-  /* accept_psk_mode stores whether the client's PSK mode is compatible with our
-   * preferences. */
-  unsigned accept_psk_mode:1;
-
   /* retry_group is the group ID selected by the server in HelloRetryRequest in
    * TLS 1.3. */
   uint16_t retry_group;
 
+  /* ecdh_ctx is the current ECDH instance. */
+  SSL_ECDH_CTX ecdh_ctx;
+
   /* cookie is the value of the cookie received from the server, if any. */
   uint8_t *cookie;
   size_t cookie_len;
@@ -972,10 +957,43 @@
   uint8_t *server_params;
   size_t server_params_len;
 
+  /* peer_psk_identity_hint, on the client, is the psk_identity_hint sent by the
+   * server when using a TLS 1.2 PSK key exchange. */
+  char *peer_psk_identity_hint;
+
+  /* ca_names, on the client, contains the list of CAs received in a
+   * CertificateRequest message. */
+  STACK_OF(X509_NAME) *ca_names;
+
+  /* certificate_types, on the client, contains the set of certificate types
+   * received in a CertificateRequest message. */
+  uint8_t *certificate_types;
+  size_t num_certificate_types;
+
+  /* hostname, on the server, is the value of the SNI extension. */
+  char *hostname;
+
+  /* key_block is the record-layer key block for TLS 1.2 and earlier. */
+  uint8_t *key_block;
+  uint8_t key_block_len;
+
   /* session_tickets_sent, in TLS 1.3, is the number of tickets the server has
    * sent. */
   uint8_t session_tickets_sent;
 
+  /* scts_requested is one if the SCT extension is in the ClientHello. */
+  unsigned scts_requested:1;
+
+  /* needs_psk_binder if the ClientHello has a placeholder PSK binder to be
+   * filled in. */
+  unsigned needs_psk_binder:1;
+
+  unsigned received_hello_retry_request:1;
+
+  /* accept_psk_mode stores whether the client's PSK mode is compatible with our
+   * preferences. */
+  unsigned accept_psk_mode:1;
+
   /* cert_request is one if a client certificate was requested and zero
    * otherwise. */
   unsigned cert_request:1;
@@ -1003,40 +1021,23 @@
    * or received. */
   unsigned ticket_expected:1;
 
-  /* peer_psk_identity_hint, on the client, is the psk_identity_hint sent by the
-   * server when using a TLS 1.2 PSK key exchange. */
-  char *peer_psk_identity_hint;
+  /* client_version is the value sent or received in the ClientHello version. */
+  uint16_t client_version;
+} /* SSL_HANDSHAKE */;
 
-  /* ca_names, on the client, contains the list of CAs received in a
-   * CertificateRequest message. */
-  STACK_OF(X509_NAME) *ca_names;
-
-  /* certificate_types, on the client, contains the set of certificate types
-   * received in a CertificateRequest message. */
-  uint8_t *certificate_types;
-  size_t num_certificate_types;
-
-  /* key_block is the record-layer key block for TLS 1.2 and earlier. */
-  uint8_t *key_block;
-  uint8_t key_block_len;
-
-  /* hostname, on the server, is the value of the SNI extension. */
-  char *hostname;
-} SSL_HANDSHAKE;
-
-SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
+SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl);
 
 /* ssl_handshake_free releases all memory associated with |hs|. */
 void ssl_handshake_free(SSL_HANDSHAKE *hs);
 
 /* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <=
  * 0 on error. */
-int tls13_handshake(SSL *ssl);
+int tls13_handshake(SSL_HANDSHAKE *hs);
 
-/* The following are implementations of |do_handshake| for the client and
+/* The following are implementations of |do_tls13_handshake| for the client and
  * server. */
-enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl);
-enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl);
+enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs);
+enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs);
 
 /* tls13_post_handshake processes a post-handshake message. It returns one on
  * success and zero on failure. */
@@ -1048,40 +1049,40 @@
 
 int tls13_process_certificate(SSL *ssl, int allow_anonymous);
 int tls13_process_certificate_verify(SSL *ssl);
-int tls13_process_finished(SSL *ssl);
+int tls13_process_finished(SSL_HANDSHAKE *hs);
 
-int tls13_prepare_certificate(SSL *ssl);
+int tls13_prepare_certificate(SSL_HANDSHAKE *hs);
 enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL *ssl, int is_first_run);
-int tls13_prepare_finished(SSL *ssl);
+    SSL_HANDSHAKE *hs, int is_first_run);
+int tls13_prepare_finished(SSL_HANDSHAKE *hs);
 int tls13_process_new_session_ticket(SSL *ssl);
 
-int ssl_ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
+int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret,
                                         size_t *out_secret_len,
                                         uint8_t *out_alert, CBS *contents);
-int ssl_ext_key_share_parse_clienthello(SSL *ssl, int *out_found,
+int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, int *out_found,
                                         uint8_t **out_secret,
                                         size_t *out_secret_len,
                                         uint8_t *out_alert, CBS *contents);
-int ssl_ext_key_share_add_serverhello(SSL *ssl, CBB *out);
+int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
 
-int ssl_ext_pre_shared_key_parse_serverhello(SSL *ssl, uint8_t *out_alert,
-                                             CBS *contents);
-int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
+int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert, CBS *contents);
+int ssl_ext_pre_shared_key_parse_clienthello(SSL_HANDSHAKE *hs,
                                              SSL_SESSION **out_session,
                                              CBS *out_binders,
                                              uint8_t *out_alert, CBS *contents);
-int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out);
+int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
 
 /* ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
  * returns one iff it's valid. */
 int ssl_is_sct_list_valid(const CBS *contents);
 
-int ssl_write_client_hello(SSL *ssl);
+int ssl_write_client_hello(SSL_HANDSHAKE *hs);
 
 /* ssl_clear_tls13_state releases client state only needed for TLS 1.3. It
  * should be called once the version is known to be TLS 1.2 or earlier. */
-void ssl_clear_tls13_state(SSL *ssl);
+void ssl_clear_tls13_state(SSL_HANDSHAKE *hs);
 
 enum ssl_cert_verify_context_t {
   ssl_cert_verify_server,
@@ -1101,8 +1102,8 @@
 /* ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
  * one on successful negotiation or if nothing was negotiated. It returns zero
  * and sets |*out_alert| to an alert on error. */
-int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
-                       const struct ssl_early_callback_ctx *client_hello);
+int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                       const SSL_CLIENT_HELLO *client_hello);
 
 typedef struct {
   uint16_t type;
@@ -1139,17 +1140,14 @@
 
 /* ClientHello functions. */
 
-int ssl_early_callback_init(SSL *ssl, struct ssl_early_callback_ctx *ctx,
-                            const uint8_t *in, size_t in_len);
+int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in,
+                          size_t in_len);
 
-int ssl_early_callback_get_extension(const struct ssl_early_callback_ctx *ctx,
-                                     CBS *out, uint16_t extension_type);
+int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
+                                   CBS *out, uint16_t extension_type);
 
-STACK_OF(SSL_CIPHER) *
-    ssl_parse_client_cipher_list(const struct ssl_early_callback_ctx *ctx);
-
-int ssl_client_cipher_list_contains_cipher(
-    const struct ssl_early_callback_ctx *client_hello, uint16_t id);
+int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
+                                           uint16_t id);
 
 
 /* GREASE. */
@@ -1173,13 +1171,14 @@
 /* Signature algorithms. */
 
 /* tls1_parse_peer_sigalgs parses |sigalgs| as the list of peer signature
- * algorithms and them on |ssl|. It returns one on success and zero on error. */
-int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *sigalgs);
+ * algorithms and saves them on |hs|. It returns one on success and zero on
+ * error. */
+int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *sigalgs);
 
 /* tls1_choose_signature_algorithm sets |*out| to a signature algorithm for use
- * with |ssl|'s private key based on the peer's preferences and the algorithms
+ * with |hs|'s private key based on the peer's preferences and the algorithms
  * supported. It returns one on success and zero on error. */
-int tls1_choose_signature_algorithm(SSL *ssl, uint16_t *out);
+int tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out);
 
 /* tls12_get_verify_sigalgs sets |*out| to the signature algorithms acceptable
  * for the peer signature and returns the length of the list. */
@@ -1381,18 +1380,6 @@
   uint8_t server_random[SSL3_RANDOM_SIZE];
   uint8_t client_random[SSL3_RANDOM_SIZE];
 
-  /* have_version is true if the connection's final version is known. Otherwise
-   * the version has not been negotiated yet. */
-  unsigned have_version:1;
-
-  /* v2_hello_done is true if the peer's V2ClientHello, if any, has been handled
-   * and future messages should use the record layer. */
-  unsigned v2_hello_done:1;
-
-  /* initial_handshake_complete is true if the initial handshake has
-   * completed. */
-  unsigned initial_handshake_complete:1;
-
   /* read_buffer holds data from the transport to be processed. */
   SSL3_BUFFER read_buffer;
   /* write_buffer holds data to be written to the transport. */
@@ -1424,10 +1411,13 @@
   enum ssl_shutdown_t send_shutdown;
 
   int alert_dispatch;
-  uint8_t send_alert[2];
 
   int total_renegotiations;
 
+  /* early_data_skipped is the amount of early data that has been skipped by the
+   * record layer. */
+  uint16_t early_data_skipped;
+
   /* empty_record_count is the number of consecutive empty records received. */
   uint8_t empty_record_count;
 
@@ -1438,6 +1428,39 @@
   /* key_update_count is the number of consecutive KeyUpdates received. */
   uint8_t key_update_count;
 
+  /* skip_early_data instructs the record layer to skip unexpected early data
+   * messages when 0RTT is rejected. */
+  unsigned skip_early_data:1;
+
+  /* have_version is true if the connection's final version is known. Otherwise
+   * the version has not been negotiated yet. */
+  unsigned have_version:1;
+
+  /* v2_hello_done is true if the peer's V2ClientHello, if any, has been handled
+   * and future messages should use the record layer. */
+  unsigned v2_hello_done:1;
+
+  /* initial_handshake_complete is true if the initial handshake has
+   * completed. */
+  unsigned initial_handshake_complete:1;
+
+  /* session_reused indicates whether a session was resumed. */
+  unsigned session_reused:1;
+
+  unsigned send_connection_binding:1;
+
+  /* In a client, this means that the server supported Channel ID and that a
+   * Channel ID was sent. In a server it means that we echoed support for
+   * Channel IDs and that tlsext_channel_id will be valid after the
+   * handshake. */
+  unsigned tlsext_channel_id_valid:1;
+
+  uint8_t send_alert[2];
+
+  /* pending_message is the current outgoing handshake message. */
+  uint8_t *pending_message;
+  uint32_t pending_message_len;
+
   /* aead_read_ctx is the current read cipher state. */
   SSL_AEAD_CTX *aead_read_ctx;
 
@@ -1448,31 +1471,33 @@
    * version. */
   const SSL3_ENC_METHOD *enc_method;
 
-  /* pending_message is the current outgoing handshake message. */
-  uint8_t *pending_message;
-  uint32_t pending_message_len;
-
   /* hs is the handshake state for the current handshake or NULL if there isn't
    * one. */
   SSL_HANDSHAKE *hs;
 
   uint8_t write_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t write_traffic_secret_len;
   uint8_t read_traffic_secret[EVP_MAX_MD_SIZE];
-  uint8_t read_traffic_secret_len;
   uint8_t exporter_secret[EVP_MAX_MD_SIZE];
+  uint8_t write_traffic_secret_len;
+  uint8_t read_traffic_secret_len;
   uint8_t exporter_secret_len;
 
+  /* 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 {
-    int message_type;
-
     /* used to hold the new cipher we are going to use */
     const SSL_CIPHER *new_cipher;
 
+    int message_type;
+
     /* used when SSL_ST_FLUSH_DATA is entered */
     int next_state;
 
@@ -1504,16 +1529,6 @@
    * immutable. */
   SSL_SESSION *established_session;
 
-  /* session_reused indicates whether a session was resumed. */
-  unsigned session_reused:1;
-
-  /* Connection binding to prevent renegotiation attacks */
-  uint8_t previous_client_finished[12];
-  uint8_t previous_client_finished_len;
-  uint8_t previous_server_finished[12];
-  uint8_t previous_server_finished_len;
-  int send_connection_binding;
-
   /* Next protocol negotiation. For the client, this is the protocol that we
    * sent in NextProtocol and is set when handling ServerHello extensions.
    *
@@ -1532,11 +1547,6 @@
   uint8_t *alpn_selected;
   size_t alpn_selected_len;
 
-  /* In a client, this means that the server supported Channel ID and that a
-   * Channel ID was sent. In a server it means that we echoed support for
-   * Channel IDs and that tlsext_channel_id will be valid after the
-   * handshake. */
-  char tlsext_channel_id_valid;
   /* 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
@@ -1642,7 +1652,7 @@
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
-int ssl_get_new_session(SSL *ssl, int is_server);
+int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
 int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
 
 /* ssl_session_is_context_valid returns one if |session|'s session ID context
@@ -1665,14 +1675,14 @@
   ssl_session_retry,
 };
 
-/* ssl_get_prev_session looks up the previous session based on |ctx|. On
- * success, it sets |*out_session| to the session or NULL if none was found. If
- * the session could not be looked up synchronously, it returns
+/* ssl_get_prev_session looks up the previous session based on |client_hello|.
+ * On success, it sets |*out_session| to the session or NULL if none was found.
+ * If the session could not be looked up synchronously, it returns
  * |ssl_session_retry| and should be called again. Otherwise, it returns
  * |ssl_session_error|.  */
 enum ssl_session_result_t ssl_get_prev_session(
     SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
-    int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx);
+    int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello);
 
 /* The following flags determine which parts of the session are duplicated. */
 #define SSL_SESSION_DUP_AUTH_ONLY 0x0
@@ -1699,27 +1709,13 @@
 const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(
     const SSL *ssl);
 
-int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain);
-int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain);
-int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509);
-int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509);
-void ssl_cert_set_cert_cb(CERT *cert,
-                          int (*cb)(SSL *ssl, void *arg), void *arg);
-
 int ssl_verify_cert_chain(SSL *ssl, long *out_verify_result,
                           STACK_OF(X509) * cert_chain);
-void ssl_update_cache(SSL *ssl, int mode);
-
-/* ssl_get_compatible_server_ciphers determines the key exchange and
- * authentication cipher suite masks compatible with the server configuration
- * and current ClientHello parameters of |ssl|. It sets |*out_mask_k| to the key
- * exchange mask and |*out_mask_a| to the authentication mask. */
-void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
-                                       uint32_t *out_mask_a);
+void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
 
 int ssl_verify_alarm_type(long type);
 
-int ssl3_get_finished(SSL *ssl);
+int ssl3_get_finished(SSL_HANDSHAKE *hs);
 int ssl3_send_change_cipher_spec(SSL *ssl);
 int ssl3_send_alert(SSL *ssl, int level, int desc);
 int ssl3_get_message(SSL *ssl, int msg_type,
@@ -1734,8 +1730,7 @@
 int ssl3_cert_verify_hash(SSL *ssl, const EVP_MD **out_md, uint8_t *out,
                           size_t *out_len, uint16_t signature_algorithm);
 
-int ssl3_send_finished(SSL *ssl, int a, int b);
-int ssl3_supports_cipher(const SSL_CIPHER *cipher);
+int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
@@ -1746,23 +1741,16 @@
 int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
 int ssl3_output_cert_chain(SSL *ssl);
 
-const SSL_CIPHER *ssl3_choose_cipher(
-    SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
-    const struct ssl_cipher_preference_list_st *srvr);
-
 int ssl3_new(SSL *ssl);
 void ssl3_free(SSL *ssl);
-int ssl3_accept(SSL *ssl);
-int ssl3_connect(SSL *ssl);
+int ssl3_accept(SSL_HANDSHAKE *hs);
+int ssl3_connect(SSL_HANDSHAKE *hs);
 
 int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
 int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
 int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len);
 int ssl3_write_message(SSL *ssl);
 
-void ssl3_expect_flight(SSL *ssl);
-void ssl3_received_flight(SSL *ssl);
-
 int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
 int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
                          size_t *out_len);
@@ -1802,10 +1790,7 @@
                          CBS *out_body);
 int dtls1_check_timeout_num(SSL *ssl);
 int dtls1_handshake_write(SSL *ssl);
-void dtls1_expect_flight(SSL *ssl);
-void dtls1_received_flight(SSL *ssl);
 
-int dtls1_supports_cipher(const SSL_CIPHER *cipher);
 void dtls1_start_timer(SSL *ssl);
 void dtls1_stop_timer(SSL *ssl);
 int dtls1_is_timer_expired(SSL *ssl);
@@ -1829,7 +1814,7 @@
 int ssl_init_wbio_buffer(SSL *ssl);
 void ssl_free_wbio_buffer(SSL *ssl);
 
-int tls1_change_cipher_state(SSL *ssl, int which);
+int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which);
 int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
                                 size_t premaster_len);
@@ -1846,7 +1831,7 @@
 /* tls1_get_shared_group sets |*out_group_id| to the first preferred shared
  * group between client and server preferences and returns one. If none may be
  * found, it returns zero. */
-int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_id);
+int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id);
 
 /* tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves|
  * into a newly allocated array of TLS group IDs. On success, the function
@@ -1866,12 +1851,12 @@
  * returns one on success and zero on failure. The |header_len| argument is the
  * length of the ClientHello written so far and is used to compute the padding
  * length. (It does not include the record header.) */
-int ssl_add_clienthello_tlsext(SSL *ssl, CBB *out, size_t header_len);
+int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len);
 
-int ssl_add_serverhello_tlsext(SSL *ssl, CBB *out);
-int ssl_parse_clienthello_tlsext(
-    SSL *ssl, const struct ssl_early_callback_ctx *client_hello);
-int ssl_parse_serverhello_tlsext(SSL *ssl, CBS *cbs);
+int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
+int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
+                                 const SSL_CLIENT_HELLO *client_hello);
+int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
 
 #define tlsext_tick_md EVP_sha256
 
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index d872020..d775cca 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -130,14 +130,14 @@
 #include "internal.h"
 
 
-SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl)) {
+SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) {
   SSL_HANDSHAKE *hs = OPENSSL_malloc(sizeof(SSL_HANDSHAKE));
   if (hs == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
   memset(hs, 0, sizeof(SSL_HANDSHAKE));
-  hs->do_handshake = do_handshake;
+  hs->ssl = ssl;
   hs->wait = ssl_hs_ok;
   return hs;
 }
@@ -260,7 +260,8 @@
   return 1;
 }
 
-int ssl3_send_finished(SSL *ssl, int a, int b) {
+int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b) {
+  SSL *const ssl = hs->ssl;
   if (ssl->state == b) {
     return ssl->method->write_message(ssl);
   }
@@ -309,7 +310,8 @@
   return ssl->method->write_message(ssl);
 }
 
-int ssl3_get_finished(SSL *ssl) {
+int ssl3_get_finished(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_FINISHED,
                                          ssl_dont_hash_message);
   if (ret <= 0) {
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 901b8af..1aad8e6 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -162,14 +162,6 @@
 #include "internal.h"
 
 
-int ssl3_supports_cipher(const SSL_CIPHER *cipher) {
-  return 1;
-}
-
-void ssl3_expect_flight(SSL *ssl) {}
-
-void ssl3_received_flight(SSL *ssl) {}
-
 int ssl3_new(SSL *ssl) {
   SSL3_STATE *s3;
 
@@ -240,84 +232,6 @@
   return NULL;
 }
 
-const SSL_CIPHER *ssl3_choose_cipher(
-    SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
-    const struct ssl_cipher_preference_list_st *server_pref) {
-  const SSL_CIPHER *c, *ret = NULL;
-  STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;
-  int ok;
-  size_t cipher_index;
-  uint32_t alg_k, alg_a, mask_k, mask_a;
-  /* in_group_flags will either be NULL, or will point to an array of bytes
-   * which indicate equal-preference groups in the |prio| stack. See the
-   * comment about |in_group_flags| in the |ssl_cipher_preference_list_st|
-   * struct. */
-  const uint8_t *in_group_flags;
-  /* group_min contains the minimal index so far found in a group, or -1 if no
-   * such value exists yet. */
-  int group_min = -1;
-
-  STACK_OF(SSL_CIPHER) *clnt = ssl_parse_client_cipher_list(client_hello);
-  if (clnt == NULL) {
-    return NULL;
-  }
-
-  if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
-    prio = srvr;
-    in_group_flags = server_pref->in_group_flags;
-    allow = clnt;
-  } else {
-    prio = clnt;
-    in_group_flags = NULL;
-    allow = srvr;
-  }
-
-  ssl_get_compatible_server_ciphers(ssl, &mask_k, &mask_a);
-
-  for (size_t i = 0; i < sk_SSL_CIPHER_num(prio); i++) {
-    c = sk_SSL_CIPHER_value(prio, i);
-
-    ok = 1;
-
-    /* Check the TLS version. */
-    if (SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
-        SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) {
-      ok = 0;
-    }
-
-    alg_k = c->algorithm_mkey;
-    alg_a = c->algorithm_auth;
-
-    ok = ok && (alg_k & mask_k) && (alg_a & mask_a);
-
-    if (ok && sk_SSL_CIPHER_find(allow, &cipher_index, c)) {
-      if (in_group_flags != NULL && in_group_flags[i] == 1) {
-        /* This element of |prio| is in a group. Update the minimum index found
-         * so far and continue looking. */
-        if (group_min == -1 || (size_t)group_min > cipher_index) {
-          group_min = cipher_index;
-        }
-      } else {
-        if (group_min != -1 && (size_t)group_min < cipher_index) {
-          cipher_index = group_min;
-        }
-        ret = sk_SSL_CIPHER_value(allow, cipher_index);
-        break;
-      }
-    }
-
-    if (in_group_flags != NULL && in_group_flags[i] == 0 && group_min != -1) {
-      /* We are about to leave a group, but we found a match in it, so that's
-       * our answer. */
-      ret = sk_SSL_CIPHER_value(allow, group_min);
-      break;
-    }
-  }
-
-  sk_SSL_CIPHER_free(clnt);
-  return ret;
-}
-
 /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
  * handshake macs if required. */
 uint32_t ssl_get_algorithm_prf(const SSL *ssl) {
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index 2a7b7d4..aab6052 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -473,7 +473,7 @@
 /* SSL_SESSION_parse_bounded_octet_string parses an optional ASN.1 OCTET STRING
  * explicitly tagged with |tag| of size at most |max_out|. */
 static int SSL_SESSION_parse_bounded_octet_string(
-    CBS *cbs, uint8_t *out, unsigned *out_len, unsigned max_out, unsigned tag) {
+    CBS *cbs, uint8_t *out, uint8_t *out_len, uint8_t max_out, unsigned tag) {
   CBS value;
   if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag) ||
       CBS_len(&value) > max_out) {
@@ -481,7 +481,7 @@
     return 0;
   }
   memcpy(out, CBS_data(&value), CBS_len(&value));
-  *out_len = (unsigned)CBS_len(&value);
+  *out_len = (uint8_t)CBS_len(&value);
   return 1;
 }
 
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 7f6cf63..c0bdb5c 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -244,13 +244,13 @@
   OPENSSL_free(c);
 }
 
-int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
+static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
   sk_X509_pop_free(cert->x509_chain, X509_free);
   cert->x509_chain = chain;
   return 1;
 }
 
-int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
+static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
   STACK_OF(X509) *dchain;
   if (chain == NULL) {
     return ssl_cert_set0_chain(cert, NULL);
@@ -269,7 +269,7 @@
   return 1;
 }
 
-int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
+static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
   if (cert->x509_chain == NULL) {
     cert->x509_chain = sk_X509_new_null();
   }
@@ -280,7 +280,7 @@
   return 1;
 }
 
-int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
+static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
   if (!ssl_cert_add0_chain_cert(cert, x509)) {
     return 0;
   }
@@ -289,7 +289,8 @@
   return 1;
 }
 
-void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg) {
+static void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg),
+                                 void *arg) {
   c->cert_cb = cb;
   c->cert_cb_arg = arg;
 }
@@ -775,6 +776,15 @@
   return SSL_set0_chain(ssl, NULL);
 }
 
+void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
+                         void *arg) {
+  ssl_cert_set_cert_cb(ctx->cert, cb, arg);
+}
+
+void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) {
+  ssl_cert_set_cert_cb(ssl->cert, cb, arg);
+}
+
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
   *out_chain = ctx->cert->x509_chain;
   return 1;
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 70a39ea..b0c0906 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -477,6 +477,13 @@
       ssl->ctx->signed_cert_timestamps_enabled;
   ssl->ocsp_stapling_enabled = ssl->ctx->ocsp_stapling_enabled;
 
+  ssl->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+
+  /* If the context has a default timeout, use it over the default. */
+  if (ctx->session_timeout != 0) {
+    ssl->session_timeout = ctx->session_timeout;
+  }
+
   return ssl;
 
 err:
@@ -617,7 +624,28 @@
     return 1;
   }
 
-  return ssl->handshake_func(ssl);
+  /* Set up a new handshake if necessary. */
+  if (ssl->state == SSL_ST_INIT && ssl->s3->hs == NULL) {
+    ssl->s3->hs = ssl_handshake_new(ssl);
+    if (ssl->s3->hs == NULL) {
+      return -1;
+    }
+  }
+
+  /* Run the handshake. */
+  assert(ssl->s3->hs != NULL);
+  int ret = ssl->handshake_func(ssl->s3->hs);
+  if (ret <= 0) {
+    return ret;
+  }
+
+  /* Destroy the handshake object if the handshake has completely finished. */
+  if (!SSL_in_init(ssl)) {
+    ssl_handshake_free(ssl->s3->hs);
+    ssl->s3->hs = NULL;
+  }
+
+  return 1;
 }
 
 int SSL_connect(SSL *ssl) {
@@ -1106,24 +1134,28 @@
 }
 
 int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx,
-                                   unsigned sid_ctx_len) {
+                                   size_t sid_ctx_len) {
   if (sid_ctx_len > sizeof(ctx->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
     return 0;
   }
-  ctx->sid_ctx_length = sid_ctx_len;
+
+  assert(sizeof(ctx->sid_ctx) < 256);
+  ctx->sid_ctx_length = (uint8_t)sid_ctx_len;
   memcpy(ctx->sid_ctx, sid_ctx, sid_ctx_len);
 
   return 1;
 }
 
 int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx,
-                               unsigned sid_ctx_len) {
-  if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) {
+                               size_t sid_ctx_len) {
+  if (sid_ctx_len > sizeof(ssl->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
     return 0;
   }
-  ssl->sid_ctx_length = sid_ctx_len;
+
+  assert(sizeof(ssl->sid_ctx) < 256);
+  ssl->sid_ctx_length = (uint8_t)sid_ctx_len;
   memcpy(ssl->sid_ctx, sid_ctx, sid_ctx_len);
 
   return 1;
@@ -1664,39 +1696,6 @@
   return 1;
 }
 
-STACK_OF(SSL_CIPHER) *
-    ssl_parse_client_cipher_list(const struct ssl_early_callback_ctx *ctx) {
-  CBS cipher_suites;
-  CBS_init(&cipher_suites, ctx->cipher_suites, ctx->cipher_suites_len);
-
-  STACK_OF(SSL_CIPHER) *sk = sk_SSL_CIPHER_new_null();
-  if (sk == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
-  while (CBS_len(&cipher_suites) > 0) {
-    uint16_t cipher_suite;
-
-    if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
-      goto err;
-    }
-
-    const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
-    if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-  }
-
-  return sk;
-
-err:
-  sk_SSL_CIPHER_free(sk);
-  return NULL;
-}
-
 const char *SSL_get_servername(const SSL *ssl, const int type) {
   if (type != TLSEXT_NAMETYPE_host_name) {
     return NULL;
@@ -1942,13 +1941,21 @@
 }
 
 
+void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx, int enabled) {
+  ctx->tlsext_channel_id_enabled = !!enabled;
+}
+
 int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
-  ctx->tlsext_channel_id_enabled = 1;
+  SSL_CTX_set_tls_channel_id_enabled(ctx, 1);
   return 1;
 }
 
+void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled) {
+  ssl->tlsext_channel_id_enabled = !!enabled;
+}
+
 int SSL_enable_tls_channel_id(SSL *ssl) {
-  ssl->tlsext_channel_id_enabled = 1;
+  SSL_set_tls_channel_id_enabled(ssl, 1);
   return 1;
 }
 
@@ -2013,15 +2020,6 @@
   X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 }
 
-void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
-                         void *arg) {
-  ssl_cert_set_cert_cb(ctx->cert, cb, arg);
-}
-
-void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) {
-  ssl_cert_set_cert_cb(ssl->cert, cb, arg);
-}
-
 size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
   if (ssl->server || ssl->s3->hs == NULL) {
     *out_types = NULL;
@@ -2031,51 +2029,8 @@
   return ssl->s3->hs->num_certificate_types;
 }
 
-void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
-                                       uint32_t *out_mask_a) {
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    *out_mask_k = SSL_kGENERIC;
-    *out_mask_a = SSL_aGENERIC;
-    return;
-  }
-
-  uint32_t mask_k = 0;
-  uint32_t mask_a = 0;
-
-  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
-    int type = ssl_private_key_type(ssl);
-    if (type == NID_rsaEncryption) {
-      mask_k |= SSL_kRSA;
-      mask_a |= SSL_aRSA;
-    } else if (ssl_is_ecdsa_key_type(type)) {
-      mask_a |= SSL_aECDSA;
-    }
-  }
-
-  if (ssl->cert->dh_tmp != NULL || ssl->cert->dh_tmp_cb != NULL) {
-    mask_k |= SSL_kDHE;
-  }
-
-  /* Check for a shared group to consider ECDHE ciphers. */
-  uint16_t unused;
-  if (tls1_get_shared_group(ssl, &unused)) {
-    mask_k |= SSL_kECDHE;
-  }
-
-  /* CECPQ1 ciphers are always acceptable if supported by both sides. */
-  mask_k |= SSL_kCECPQ1;
-
-  /* PSK requires a server callback. */
-  if (ssl->psk_server_callback != NULL) {
-    mask_k |= SSL_kPSK;
-    mask_a |= SSL_aPSK;
-  }
-
-  *out_mask_k = mask_k;
-  *out_mask_a = mask_a;
-}
-
-void ssl_update_cache(SSL *ssl, int mode) {
+void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
+  SSL *const ssl = hs->ssl;
   SSL_CTX *ctx = ssl->initial_ctx;
   /* Never cache sessions with empty session IDs. */
   if (ssl->s3->established_session->session_id_length == 0 ||
@@ -2091,7 +2046,7 @@
    * decides to renew the ticket. Once the handshake is completed, it should be
    * inserted into the cache. */
   if (ssl->s3->established_session != ssl->session ||
-      (!ssl->server && ssl->s3->hs->ticket_expected)) {
+      (!ssl->server && hs->ticket_expected)) {
     if (use_internal_cache) {
       SSL_CTX_add_session(ctx, ssl->s3->established_session);
     }
@@ -2813,13 +2768,13 @@
 
 int SSL_is_dtls(const SSL *ssl) { return ssl->method->is_dtls; }
 
-void SSL_CTX_set_select_certificate_cb(
-    SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *)) {
+void SSL_CTX_set_select_certificate_cb(SSL_CTX *ctx,
+                                       int (*cb)(const SSL_CLIENT_HELLO *)) {
   ctx->select_certificate_cb = cb;
 }
 
-void SSL_CTX_set_dos_protection_cb(
-    SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *)) {
+void SSL_CTX_set_dos_protection_cb(SSL_CTX *ctx,
+                                   int (*cb)(const SSL_CLIENT_HELLO *)) {
   ctx->dos_protection_cb = cb;
 }
 
@@ -2827,11 +2782,6 @@
   ssl->renegotiate_mode = mode;
 }
 
-void SSL_set_reject_peer_renegotiations(SSL *ssl, int reject) {
-  SSL_set_renegotiate_mode(
-      ssl, reject ? ssl_renegotiate_never : ssl_renegotiate_freely);
-}
-
 int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv,
                 const uint8_t **out_write_iv, size_t *out_iv_len) {
   if (ssl->s3->aead_read_ctx == NULL || ssl->s3->aead_write_ctx == NULL) {
@@ -2922,9 +2872,13 @@
 }
 
 int SSL_clear(SSL *ssl) {
-  if (ssl->method == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_METHOD_SPECIFIED);
-    return 0;
+  /* 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;
+  if (!ssl->server && ssl->s3->established_session != NULL) {
+    session = ssl->s3->established_session;
+    SSL_SESSION_up_ref(session);
   }
 
   /* TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and
@@ -2952,6 +2906,7 @@
 
   ssl->method->ssl_free(ssl);
   if (!ssl->method->ssl_new(ssl)) {
+    SSL_SESSION_free(session);
     return 0;
   }
 
@@ -2959,7 +2914,10 @@
     ssl->d1->mtu = mtu;
   }
 
-  ssl->client_version = ssl->version;
+  if (session != NULL) {
+    SSL_set_session(ssl, session);
+    SSL_SESSION_free(session);
+  }
 
   return 1;
 }
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index a6b669d..67e809d 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -392,13 +392,14 @@
 }
 
 int SSL_SESSION_set1_id_context(SSL_SESSION *session, const uint8_t *sid_ctx,
-                                unsigned sid_ctx_len) {
-  if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) {
+                                size_t sid_ctx_len) {
+  if (sid_ctx_len > sizeof(session->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
     return 0;
   }
 
-  session->sid_ctx_length = sid_ctx_len;
+  assert(sizeof(session->sid_ctx) < 256);
+  session->sid_ctx_length = (uint8_t)sid_ctx_len;
   memcpy(session->sid_ctx, sid_ctx, sid_ctx_len);
 
   return 1;
@@ -449,7 +450,8 @@
   return CRYPTO_get_ex_data(&session->ex_data, idx);
 }
 
-int ssl_get_new_session(SSL *ssl, int is_server) {
+int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
+  SSL *const ssl = hs->ssl;
   if (ssl->mode & SSL_MODE_NO_SESSION_CREATION) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_MAY_NOT_BE_CREATED);
     return 0;
@@ -465,15 +467,12 @@
   ssl_get_current_time(ssl, &now);
   session->time = now.tv_sec;
 
-  /* If the context has a default timeout, use it over the default. */
-  if (ssl->initial_ctx->session_timeout != 0) {
-    session->timeout = ssl->initial_ctx->session_timeout;
-  }
+  session->timeout = ssl->session_timeout;
 
   session->ssl_version = ssl->version;
 
   if (is_server) {
-    if (ssl->s3->hs->ticket_expected) {
+    if (hs->ticket_expected) {
       /* Don't set session IDs for sessions resumed with tickets. This will keep
        * them out of the session cache. */
       session->session_id_length = 0;
@@ -724,7 +723,7 @@
 
 enum ssl_session_result_t ssl_get_prev_session(
     SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
-    int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx) {
+    int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello) {
   /* This is used only by servers. */
   assert(ssl->server);
   SSL_SESSION *session = NULL;
@@ -736,17 +735,18 @@
   const int tickets_supported =
       !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) &&
       ssl->version > SSL3_VERSION &&
-      SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket,
-                                           &ticket, &ticket_len);
+      SSL_early_callback_ctx_extension_get(
+          client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
   if (tickets_supported && ticket_len > 0) {
     if (!tls_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
-                            ctx->session_id, ctx->session_id_len)) {
+                            client_hello->session_id,
+                            client_hello->session_id_len)) {
       return ssl_session_error;
     }
   } else {
     /* The client didn't send a ticket, so the session ID is a real ID. */
     enum ssl_session_result_t lookup_ret = ssl_lookup_session(
-        ssl, &session, ctx->session_id, ctx->session_id_len);
+        ssl, &session, client_hello->session_id, client_hello->session_id_len);
     if (lookup_ret != ssl_session_success) {
       return lookup_ret;
     }
@@ -875,6 +875,12 @@
   return ctx->session_timeout;
 }
 
+long SSL_set_session_timeout(SSL *ssl, long timeout) {
+  long old_timeout = ssl->session_timeout;
+  ssl->session_timeout = timeout;
+  return old_timeout;
+}
+
 typedef struct timeout_param_st {
   SSL_CTX *ctx;
   long time;
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index ae9d5ca..9bdaad2 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -1246,7 +1246,37 @@
       PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
 }
 
-static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client, bssl::UniquePtr<SSL> *out_server,
+static bool CompleteHandshakes(SSL *client, SSL *server) {
+  // Drive both their handshakes to completion.
+  for (;;) {
+    int client_ret = SSL_do_handshake(client);
+    int client_err = SSL_get_error(client, client_ret);
+    if (client_err != SSL_ERROR_NONE &&
+        client_err != SSL_ERROR_WANT_READ &&
+        client_err != SSL_ERROR_WANT_WRITE) {
+      fprintf(stderr, "Client error: %d\n", client_err);
+      return false;
+    }
+
+    int server_ret = SSL_do_handshake(server);
+    int server_err = SSL_get_error(server, server_ret);
+    if (server_err != SSL_ERROR_NONE &&
+        server_err != SSL_ERROR_WANT_READ &&
+        server_err != SSL_ERROR_WANT_WRITE) {
+      fprintf(stderr, "Server error: %d\n", server_err);
+      return false;
+    }
+
+    if (client_ret == 1 && server_ret == 1) {
+      break;
+    }
+  }
+
+  return true;
+}
+
+static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
+                                   bssl::UniquePtr<SSL> *out_server,
                                    SSL_CTX *client_ctx, SSL_CTX *server_ctx,
                                    SSL_SESSION *session) {
   bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
@@ -1266,29 +1296,8 @@
   SSL_set_bio(client.get(), bio1, bio1);
   SSL_set_bio(server.get(), bio2, bio2);
 
-  // Drive both their handshakes to completion.
-  for (;;) {
-    int client_ret = SSL_do_handshake(client.get());
-    int client_err = SSL_get_error(client.get(), client_ret);
-    if (client_err != SSL_ERROR_NONE &&
-        client_err != SSL_ERROR_WANT_READ &&
-        client_err != SSL_ERROR_WANT_WRITE) {
-      fprintf(stderr, "Client error: %d\n", client_err);
-      return false;
-    }
-
-    int server_ret = SSL_do_handshake(server.get());
-    int server_err = SSL_get_error(server.get(), server_ret);
-    if (server_err != SSL_ERROR_NONE &&
-        server_err != SSL_ERROR_WANT_READ &&
-        server_err != SSL_ERROR_WANT_WRITE) {
-      fprintf(stderr, "Server error: %d\n", server_err);
-      return false;
-    }
-
-    if (client_ret == 1 && server_ret == 1) {
-      break;
-    }
+  if (!CompleteHandshakes(client.get(), server.get())) {
+    return false;
   }
 
   *out_client = std::move(client);
@@ -2030,11 +2039,11 @@
   return SSL_TLSEXT_ERR_OK;
 }
 
-static int SwitchSessionIDContextEarly(
-    const struct ssl_early_callback_ctx *ctx) {
+static int SwitchSessionIDContextEarly(const SSL_CLIENT_HELLO *client_hello) {
   static const uint8_t kContext[] = {3};
 
-  if (!SSL_set_session_id_context(ctx->ssl, kContext, sizeof(kContext))) {
+  if (!SSL_set_session_id_context(client_hello->ssl, kContext,
+                                  sizeof(kContext))) {
     return -1;
   }
 
@@ -2319,6 +2328,131 @@
   return true;
 }
 
+static int SetSessionTimeoutCallback(SSL *ssl, void *arg) {
+  long timeout = *(long *) arg;
+  SSL_set_session_timeout(ssl, timeout);
+  return 1;
+}
+
+static bool TestSessionTimeoutCertCallback(bool is_dtls,
+                                           const SSL_METHOD *method,
+                                           uint16_t version) {
+  static const int kStartTime = 1000;
+  g_current_time.tv_sec = kStartTime;
+
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  if (!cert || !key) {
+    return false;
+  }
+
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  if (!server_ctx || !client_ctx ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
+    return false;
+  }
+
+  SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
+
+  SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
+
+  long timeout = 25;
+  SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  if (!session) {
+    fprintf(stderr, "Error getting session.\n");
+    return false;
+  }
+
+  // Advance the clock just behind the timeout.
+  g_current_time.tv_sec += timeout - 1;
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           true /* expect session reused */)) {
+    fprintf(stderr, "Error resuming session.\n");
+    return false;
+  }
+
+  // Advance the clock one more second.
+  g_current_time.tv_sec++;
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr, "Error resuming session.\n");
+    return false;
+  }
+
+  // Set session timeout to 0 to disable resumption.
+  timeout = 0;
+  g_current_time.tv_sec = kStartTime;
+
+  bssl::UniquePtr<SSL_SESSION> not_resumable_session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  if (!not_resumable_session) {
+    fprintf(stderr, "Error getting session.\n");
+    return false;
+  }
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                           not_resumable_session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr, "Error resuming session with timeout of 0.\n");
+    return false;
+  }
+
+  // Set both context and connection (via callback) default session timeout.
+  // The connection one is the one that ends up being used.
+  timeout = 25;
+  g_current_time.tv_sec = kStartTime;
+
+  SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+
+  bssl::UniquePtr<SSL_SESSION> ctx_and_cb_session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  if (!ctx_and_cb_session) {
+    fprintf(stderr, "Error getting session.\n");
+    return false;
+  }
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                           ctx_and_cb_session.get(),
+                           true /* expect session reused */)) {
+    fprintf(stderr, "Error resuming session with timeout of 0.\n");
+    return false;
+  }
+
+  // Advance the clock just behind the timeout.
+  g_current_time.tv_sec += timeout - 1;
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                           ctx_and_cb_session.get(),
+                           true /* expect session reused */)) {
+    fprintf(stderr, "Error resuming session.\n");
+    return false;
+  }
+
+  // Advance the clock one more second.
+  g_current_time.tv_sec++;
+
+  if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                           ctx_and_cb_session.get(),
+                           false /* expect session not reused */)) {
+    fprintf(stderr, "Error resuming session.\n");
+    return false;
+  }
+
+  return true;
+}
+
 static int SwitchContext(SSL *ssl, int *out_alert, void *arg) {
   SSL_CTX *ctx = reinterpret_cast<SSL_CTX*>(arg);
   SSL_set_SSL_CTX(ssl, ctx);
@@ -2386,8 +2520,8 @@
   return true;
 }
 
-static int SetMaxVersion(const struct ssl_early_callback_ctx *ctx) {
-  if (!SSL_set_max_proto_version(ctx->ssl, TLS1_2_VERSION)) {
+static int SetMaxVersion(const SSL_CLIENT_HELLO *client_hello) {
+  if (!SSL_set_max_proto_version(client_hello->ssl, TLS1_2_VERSION)) {
     return -1;
   }
 
@@ -2594,6 +2728,65 @@
   return true;
 }
 
+static bool TestSSLClearSessionResumption(bool is_dtls,
+                                          const SSL_METHOD *method,
+                                          uint16_t version) {
+  // Skip this for TLS 1.3. TLS 1.3's ticket mechanism is incompatible with this
+  // API pattern.
+  if (version == TLS1_3_VERSION) {
+    return true;
+  }
+
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+  if (!cert || !key || !server_ctx || !client_ctx ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+      !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
+    return false;
+  }
+
+  // Connect a client and a server.
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get(), nullptr /* no session */)) {
+    return false;
+  }
+
+  if (SSL_session_reused(client.get()) ||
+      SSL_session_reused(server.get())) {
+    fprintf(stderr, "Session unexpectedly reused.\n");
+    return false;
+  }
+
+  // Reset everything.
+  if (!SSL_clear(client.get()) ||
+      !SSL_clear(server.get())) {
+    fprintf(stderr, "SSL_clear failed.\n");
+    return false;
+  }
+
+  // Attempt to connect a second time.
+  if (!CompleteHandshakes(client.get(), server.get())) {
+    fprintf(stderr, "Could not reuse SSL objects.\n");
+    return false;
+  }
+
+  // |SSL_clear| should implicitly offer the previous session to the server.
+  if (!SSL_session_reused(client.get()) ||
+      !SSL_session_reused(server.get())) {
+    fprintf(stderr, "Session was not reused in second try.\n");
+    return false;
+  }
+
+  return true;
+}
+
 static bool ForEachVersion(bool (*test_func)(bool is_dtls,
                                              const SSL_METHOD *method,
                                              uint16_t version)) {
@@ -2664,11 +2857,13 @@
       !TestClientHello() ||
       !ForEachVersion(TestSessionIDContext) ||
       !ForEachVersion(TestSessionTimeout) ||
+      !ForEachVersion(TestSessionTimeoutCertCallback) ||
       !ForEachVersion(TestSNICallback) ||
       !TestEarlyCallbackVersionSwitch() ||
       !TestSetVersion() ||
       !ForEachVersion(TestVersion) ||
-      !ForEachVersion(TestALPNCipherAvailable)) {
+      !ForEachVersion(TestALPNCipherAvailable) ||
+      !ForEachVersion(TestSSLClearSessionResumption)) {
     ERR_print_errors_fp(stderr);
     return 1;
   }
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 4c7d3ee..70907e1 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -258,8 +258,9 @@
   return 1;
 }
 
-static int tls1_setup_key_block(SSL *ssl) {
-  if (ssl->s3->hs->key_block_len != 0) {
+static int tls1_setup_key_block(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (hs->key_block_len != 0) {
     return 1;
   }
 
@@ -310,14 +311,15 @@
   }
 
   assert(key_block_len < 256);
-  ssl->s3->hs->key_block_len = (uint8_t)key_block_len;
-  ssl->s3->hs->key_block = keyblock;
+  hs->key_block_len = (uint8_t)key_block_len;
+  hs->key_block = keyblock;
   return 1;
 }
 
-int tls1_change_cipher_state(SSL *ssl, int which) {
+int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which) {
+  SSL *const ssl = hs->ssl;
   /* Ensure the key block is set up. */
-  if (!tls1_setup_key_block(ssl)) {
+  if (!tls1_setup_key_block(hs)) {
     return 0;
   }
 
@@ -333,9 +335,9 @@
   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 == ssl->s3->hs->key_block_len);
+  assert((mac_secret_len + key_len + iv_len) * 2 == hs->key_block_len);
 
-  const uint8_t *key_data = ssl->s3->hs->key_block;
+  const uint8_t *key_data = hs->key_block;
   const uint8_t *client_write_mac_secret = key_data;
   key_data += mac_secret_len;
   const uint8_t *server_write_mac_secret = key_data;
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 421232f..3530ff5 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -127,8 +127,8 @@
 #include "../crypto/internal.h"
 
 
-static int ssl_check_clienthello_tlsext(SSL *ssl);
-static int ssl_check_serverhello_tlsext(SSL *ssl);
+static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs);
+static int ssl_check_serverhello_tlsext(SSL_HANDSHAKE *hs);
 
 static int compare_uint16_t(const void *p1, const void *p2) {
   uint16_t u1 = *((const uint16_t *)p1);
@@ -203,29 +203,29 @@
   return ret;
 }
 
-int ssl_early_callback_init(SSL *ssl, struct ssl_early_callback_ctx *ctx,
-                            const uint8_t *in, size_t in_len) {
-  memset(ctx, 0, sizeof(*ctx));
-  ctx->ssl = ssl;
-  ctx->client_hello = in;
-  ctx->client_hello_len = in_len;
+int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in,
+                          size_t in_len) {
+  memset(out, 0, sizeof(*out));
+  out->ssl = ssl;
+  out->client_hello = in;
+  out->client_hello_len = in_len;
 
   CBS client_hello, random, session_id;
-  CBS_init(&client_hello, ctx->client_hello, ctx->client_hello_len);
-  if (!CBS_get_u16(&client_hello, &ctx->version) ||
+  CBS_init(&client_hello, out->client_hello, out->client_hello_len);
+  if (!CBS_get_u16(&client_hello, &out->version) ||
       !CBS_get_bytes(&client_hello, &random, SSL3_RANDOM_SIZE) ||
       !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
       CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
     return 0;
   }
 
-  ctx->random = CBS_data(&random);
-  ctx->random_len = CBS_len(&random);
-  ctx->session_id = CBS_data(&session_id);
-  ctx->session_id_len = CBS_len(&session_id);
+  out->random = CBS_data(&random);
+  out->random_len = CBS_len(&random);
+  out->session_id = CBS_data(&session_id);
+  out->session_id_len = CBS_len(&session_id);
 
   /* Skip past DTLS cookie */
-  if (SSL_is_dtls(ctx->ssl)) {
+  if (SSL_is_dtls(out->ssl)) {
     CBS cookie;
     if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
         CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
@@ -241,16 +241,16 @@
     return 0;
   }
 
-  ctx->cipher_suites = CBS_data(&cipher_suites);
-  ctx->cipher_suites_len = CBS_len(&cipher_suites);
-  ctx->compression_methods = CBS_data(&compression_methods);
-  ctx->compression_methods_len = CBS_len(&compression_methods);
+  out->cipher_suites = CBS_data(&cipher_suites);
+  out->cipher_suites_len = CBS_len(&cipher_suites);
+  out->compression_methods = CBS_data(&compression_methods);
+  out->compression_methods_len = CBS_len(&compression_methods);
 
   /* If the ClientHello ends here then it's valid, but doesn't have any
    * extensions. (E.g. SSLv3.) */
   if (CBS_len(&client_hello) == 0) {
-    ctx->extensions = NULL;
-    ctx->extensions_len = 0;
+    out->extensions = NULL;
+    out->extensions_len = 0;
     return 1;
   }
 
@@ -262,16 +262,16 @@
     return 0;
   }
 
-  ctx->extensions = CBS_data(&extensions);
-  ctx->extensions_len = CBS_len(&extensions);
+  out->extensions = CBS_data(&extensions);
+  out->extensions_len = CBS_len(&extensions);
 
   return 1;
 }
 
-int ssl_early_callback_get_extension(const struct ssl_early_callback_ctx *ctx,
-                                     CBS *out, uint16_t extension_type) {
+int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
+                                   CBS *out, uint16_t extension_type) {
   CBS extensions;
-  CBS_init(&extensions, ctx->extensions, ctx->extensions_len);
+  CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
   while (CBS_len(&extensions) != 0) {
     /* Decode the next extension. */
     uint16_t type;
@@ -290,11 +290,12 @@
   return 0;
 }
 
-int SSL_early_callback_ctx_extension_get(
-    const struct ssl_early_callback_ctx *ctx, uint16_t extension_type,
-    const uint8_t **out_data, size_t *out_len) {
+int SSL_early_callback_ctx_extension_get(const SSL_CLIENT_HELLO *client_hello,
+                                         uint16_t extension_type,
+                                         const uint8_t **out_data,
+                                         size_t *out_len) {
   CBS cbs;
-  if (!ssl_early_callback_get_extension(ctx, &cbs, extension_type)) {
+  if (!ssl_client_hello_get_extension(client_hello, &cbs, extension_type)) {
     return 0;
   }
 
@@ -322,7 +323,8 @@
   }
 }
 
-int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_id) {
+int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) {
+  SSL *const ssl = hs->ssl;
   assert(ssl->server);
 
   const uint16_t *groups, *pref, *supp;
@@ -341,11 +343,11 @@
   if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
     pref = groups;
     pref_len = groups_len;
-    supp = ssl->s3->hs->peer_supported_group_list;
-    supp_len = ssl->s3->hs->peer_supported_group_list_len;
+    supp = hs->peer_supported_group_list;
+    supp_len = hs->peer_supported_group_list_len;
   } else {
-    pref = ssl->s3->hs->peer_supported_group_list;
-    pref_len = ssl->s3->hs->peer_supported_group_list_len;
+    pref = hs->peer_supported_group_list;
+    pref_len = hs->peer_supported_group_list_len;
     supp = groups;
     supp_len = groups_len;
   }
@@ -597,16 +599,19 @@
  * |*out_alert| isn't set, then a |decode_error| alert will be sent. */
 struct tls_extension {
   uint16_t value;
-  void (*init)(SSL *ssl);
+  void (*init)(SSL_HANDSHAKE *hs);
 
-  int (*add_clienthello)(SSL *ssl, CBB *out);
-  int (*parse_serverhello)(SSL *ssl, uint8_t *out_alert, CBS *contents);
+  int (*add_clienthello)(SSL_HANDSHAKE *hs, CBB *out);
+  int (*parse_serverhello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                           CBS *contents);
 
-  int (*parse_clienthello)(SSL *ssl, uint8_t *out_alert, CBS *contents);
-  int (*add_serverhello)(SSL *ssl, CBB *out);
+  int (*parse_clienthello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                           CBS *contents);
+  int (*add_serverhello)(SSL_HANDSHAKE *hs, CBB *out);
 };
 
-static int forbid_parse_serverhello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int forbid_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                    CBS *contents) {
   if (contents != NULL) {
     /* Servers MUST NOT send this extension. */
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
@@ -617,12 +622,13 @@
   return 1;
 }
 
-static int ignore_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int ignore_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                                    CBS *contents) {
   /* This extension from the client is handled elsewhere. */
   return 1;
 }
 
-static int dont_add_serverhello(SSL *ssl, CBB *out) {
+static int dont_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   return 1;
 }
 
@@ -630,7 +636,8 @@
  *
  * https://tools.ietf.org/html/rfc6066#section-3. */
 
-static int ext_sni_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl->tlsext_hostname == NULL) {
     return 1;
   }
@@ -650,8 +657,9 @@
   return 1;
 }
 
-static int ext_sni_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -674,7 +682,7 @@
   return 1;
 }
 
-static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
   if (contents == NULL) {
     return 1;
@@ -706,18 +714,18 @@
   }
 
   /* Copy the hostname as a string. */
-  if (!CBS_strdup(&host_name, &ssl->s3->hs->hostname)) {
+  if (!CBS_strdup(&host_name, &hs->hostname)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
   }
 
-  ssl->s3->hs->should_ack_sni = 1;
+  hs->should_ack_sni = 1;
   return 1;
 }
 
-static int ext_sni_add_serverhello(SSL *ssl, CBB *out) {
-  if (ssl->s3->session_reused ||
-      !ssl->s3->hs->should_ack_sni) {
+static int ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  if (hs->ssl->s3->session_reused ||
+      !hs->should_ack_sni) {
     return 1;
   }
 
@@ -734,7 +742,8 @@
  *
  * https://tools.ietf.org/html/rfc5746 */
 
-static int ext_ri_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -761,8 +770,9 @@
   return 1;
 }
 
-static int ext_ri_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_ri_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                     CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents != NULL && ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 0;
   }
@@ -834,8 +844,9 @@
   return 1;
 }
 
-static int ext_ri_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_ri_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                     CBS *contents) {
+  SSL *const ssl = hs->ssl;
   /* Renegotiation isn't supported as a server so this function should never be
    * called after the initial handshake. */
   assert(!ssl->s3->initial_handshake_complete);
@@ -868,7 +879,8 @@
   return 1;
 }
 
-static int ext_ri_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_ri_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   /* Renegotiation isn't supported as a server so this function should never be
    * called after the initial handshake. */
   assert(!ssl->s3->initial_handshake_complete);
@@ -891,9 +903,9 @@
  *
  * https://tools.ietf.org/html/rfc7627 */
 
-static int ext_ems_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+  if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) {
     return 0;
   }
 
@@ -910,8 +922,9 @@
   return 1;
 }
 
-static int ext_ems_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_ems_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
+  SSL *const ssl = hs->ssl;
   /* Whether EMS is negotiated may not change on renegotation. */
   if (ssl->s3->initial_handshake_complete) {
     if ((contents != NULL) != ssl->s3->tmp.extended_master_secret) {
@@ -940,10 +953,12 @@
   return 1;
 }
 
-static int ext_ems_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_ems_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
-      ssl->version == SSL3_VERSION) {
+  SSL *const ssl = hs->ssl;
+  uint16_t version = ssl3_protocol_version(ssl);
+  if (version >= TLS1_3_VERSION ||
+      version == SSL3_VERSION) {
     return 1;
   }
 
@@ -959,8 +974,8 @@
   return 1;
 }
 
-static int ext_ems_add_serverhello(SSL *ssl, CBB *out) {
-  if (!ssl->s3->tmp.extended_master_secret) {
+static int ext_ems_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  if (!hs->ssl->s3->tmp.extended_master_secret) {
     return 1;
   }
 
@@ -977,7 +992,8 @@
  *
  * https://tools.ietf.org/html/rfc5077 */
 
-static int ext_ticket_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -1019,8 +1035,9 @@
   return 1;
 }
 
-static int ext_ticket_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_ticket_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                         CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1038,17 +1055,17 @@
     return 0;
   }
 
-  ssl->s3->hs->ticket_expected = 1;
+  hs->ticket_expected = 1;
   return 1;
 }
 
-static int ext_ticket_add_serverhello(SSL *ssl, CBB *out) {
-  if (!ssl->s3->hs->ticket_expected) {
+static int ext_ticket_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  if (!hs->ticket_expected) {
     return 1;
   }
 
   /* If |SSL_OP_NO_TICKET| is set, |ticket_expected| should never be true. */
-  assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0);
+  assert((SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) == 0);
 
   if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) ||
       !CBB_add_u16(out, 0 /* length */)) {
@@ -1063,7 +1080,8 @@
  *
  * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 
-static int ext_sigalgs_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -1096,11 +1114,11 @@
   return 1;
 }
 
-static int ext_sigalgs_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_sigalgs_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                          CBS *contents) {
-  OPENSSL_free(ssl->s3->hs->peer_sigalgs);
-  ssl->s3->hs->peer_sigalgs = NULL;
-  ssl->s3->hs->num_peer_sigalgs = 0;
+  OPENSSL_free(hs->peer_sigalgs);
+  hs->peer_sigalgs = NULL;
+  hs->num_peer_sigalgs = 0;
 
   if (contents == NULL) {
     return 1;
@@ -1110,7 +1128,7 @@
   if (!CBS_get_u16_length_prefixed(contents, &supported_signature_algorithms) ||
       CBS_len(contents) != 0 ||
       CBS_len(&supported_signature_algorithms) == 0 ||
-      !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
+      !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
     return 0;
   }
 
@@ -1122,11 +1140,12 @@
  *
  * https://tools.ietf.org/html/rfc6066#section-8 */
 
-static void ext_ocsp_init(SSL *ssl) {
-  ssl->tlsext_status_type = -1;
+static void ext_ocsp_init(SSL_HANDSHAKE *hs) {
+  hs->ssl->tlsext_status_type = -1;
 }
 
-static int ext_ocsp_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->ocsp_stapling_enabled) {
     return 1;
   }
@@ -1145,8 +1164,9 @@
   return 1;
 }
 
-static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_ocsp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1166,11 +1186,11 @@
    * status_request here does not make sense, but OpenSSL does so and the
    * specification does not say anything. Tolerate it but ignore it. */
 
-  ssl->s3->hs->certificate_status_expected = 1;
+  hs->certificate_status_expected = 1;
   return 1;
 }
 
-static int ext_ocsp_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_ocsp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
   if (contents == NULL) {
     return 1;
@@ -1183,21 +1203,22 @@
 
   /* We cannot decide whether OCSP stapling will occur yet because the correct
    * SSL_CTX might not have been selected. */
-  ssl->s3->hs->ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp;
+  hs->ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp;
 
   return 1;
 }
 
-static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
-      !ssl->s3->hs->ocsp_stapling_requested ||
+      !hs->ocsp_stapling_requested ||
       ssl->ctx->ocsp_response_length == 0 ||
       ssl->s3->session_reused ||
       !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     return 1;
   }
 
-  ssl->s3->hs->certificate_status_expected = 1;
+  hs->certificate_status_expected = 1;
 
   return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
          CBB_add_u16(out, 0 /* length */);
@@ -1208,10 +1229,10 @@
  *
  * https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html */
 
-static int ext_npn_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_npn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl->s3->initial_handshake_complete ||
       ssl->ctx->next_proto_select_cb == NULL ||
-      (ssl->options & SSL_OP_DISABLE_NPN) ||
       SSL_is_dtls(ssl)) {
     return 1;
   }
@@ -1224,8 +1245,9 @@
   return 1;
 }
 
-static int ext_npn_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_npn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1240,7 +1262,6 @@
   assert(!ssl->s3->initial_handshake_complete);
   assert(!SSL_is_dtls(ssl));
   assert(ssl->ctx->next_proto_select_cb != NULL);
-  assert(!(ssl->options & SSL_OP_DISABLE_NPN));
 
   if (ssl->s3->alpn_selected != NULL) {
     /* NPN and ALPN may not be negotiated in the same connection. */
@@ -1277,13 +1298,14 @@
   }
 
   ssl->s3->next_proto_negotiated_len = selected_len;
-  ssl->s3->hs->next_proto_neg_seen = 1;
+  hs->next_proto_neg_seen = 1;
 
   return 1;
 }
 
-static int ext_npn_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_npn_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 1;
   }
@@ -1299,14 +1321,15 @@
     return 1;
   }
 
-  ssl->s3->hs->next_proto_neg_seen = 1;
+  hs->next_proto_neg_seen = 1;
   return 1;
 }
 
-static int ext_npn_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_npn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   /* |next_proto_neg_seen| might have been cleared when an ALPN extension was
    * parsed. */
-  if (!ssl->s3->hs->next_proto_neg_seen) {
+  if (!hs->next_proto_neg_seen) {
     return 1;
   }
 
@@ -1316,7 +1339,7 @@
   if (ssl->ctx->next_protos_advertised_cb(
           ssl, &npa, &npa_len, ssl->ctx->next_protos_advertised_cb_arg) !=
       SSL_TLSEXT_ERR_OK) {
-    ssl->s3->hs->next_proto_neg_seen = 0;
+    hs->next_proto_neg_seen = 0;
     return 1;
   }
 
@@ -1336,7 +1359,8 @@
  *
  * https://tools.ietf.org/html/rfc6962#section-3.3.1 */
 
-static int ext_sct_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->signed_cert_timestamps_enabled) {
     return 1;
   }
@@ -1349,8 +1373,9 @@
   return 1;
 }
 
-static int ext_sct_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_sct_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1387,7 +1412,7 @@
   return 1;
 }
 
-static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_sct_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                      CBS *contents) {
   if (contents == NULL) {
     return 1;
@@ -1397,11 +1422,12 @@
     return 0;
   }
 
-  ssl->s3->hs->scts_requested = 1;
+  hs->scts_requested = 1;
   return 1;
 }
 
-static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   /* The extension shouldn't be sent when resuming sessions. */
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
       ssl->s3->session_reused ||
@@ -1422,7 +1448,8 @@
  *
  * https://tools.ietf.org/html/rfc7301 */
 
-static int ext_alpn_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl->alpn_client_proto_list == NULL ||
       ssl->s3->initial_handshake_complete) {
     return 1;
@@ -1441,8 +1468,9 @@
   return 1;
 }
 
-static int ext_alpn_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1450,7 +1478,7 @@
   assert(!ssl->s3->initial_handshake_complete);
   assert(ssl->alpn_client_proto_list != NULL);
 
-  if (ssl->s3->hs->next_proto_neg_seen) {
+  if (hs->next_proto_neg_seen) {
     /* 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);
@@ -1504,11 +1532,12 @@
   return 1;
 }
 
-int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
-                       const struct ssl_early_callback_ctx *client_hello) {
+int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                       const SSL_CLIENT_HELLO *client_hello) {
+  SSL *const ssl = hs->ssl;
   CBS contents;
   if (ssl->ctx->alpn_select_cb == NULL ||
-      !ssl_early_callback_get_extension(
+      !ssl_client_hello_get_extension(
           client_hello, &contents,
           TLSEXT_TYPE_application_layer_protocol_negotiation)) {
     /* Ignore ALPN if not configured or no extension was supplied. */
@@ -1516,7 +1545,7 @@
   }
 
   /* ALPN takes precedence over NPN. */
-  ssl->s3->hs->next_proto_neg_seen = 0;
+  hs->next_proto_neg_seen = 0;
 
   CBS protocol_name_list;
   if (!CBS_get_u16_length_prefixed(&contents, &protocol_name_list) ||
@@ -1559,7 +1588,8 @@
   return 1;
 }
 
-static int ext_alpn_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl->s3->alpn_selected == NULL) {
     return 1;
   }
@@ -1583,11 +1613,12 @@
  *
  * https://tools.ietf.org/html/draft-balfanz-tls-channelid-01 */
 
-static void ext_channel_id_init(SSL *ssl) {
-  ssl->s3->tlsext_channel_id_valid = 0;
+static void ext_channel_id_init(SSL_HANDSHAKE *hs) {
+  hs->ssl->s3->tlsext_channel_id_valid = 0;
 }
 
-static int ext_channel_id_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->tlsext_channel_id_enabled ||
       SSL_is_dtls(ssl)) {
     return 1;
@@ -1601,8 +1632,9 @@
   return 1;
 }
 
-static int ext_channel_id_parse_serverhello(SSL *ssl, uint8_t *out_alert,
-                                            CBS *contents) {
+static int ext_channel_id_parse_serverhello(SSL_HANDSHAKE *hs,
+                                            uint8_t *out_alert, CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1618,8 +1650,9 @@
   return 1;
 }
 
-static int ext_channel_id_parse_clienthello(SSL *ssl, uint8_t *out_alert,
-                                            CBS *contents) {
+static int ext_channel_id_parse_clienthello(SSL_HANDSHAKE *hs,
+                                            uint8_t *out_alert, CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL ||
       !ssl->tlsext_channel_id_enabled ||
       SSL_is_dtls(ssl)) {
@@ -1634,7 +1667,8 @@
   return 1;
 }
 
-static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->s3->tlsext_channel_id_valid) {
     return 1;
   }
@@ -1653,11 +1687,12 @@
  * https://tools.ietf.org/html/rfc5764 */
 
 
-static void ext_srtp_init(SSL *ssl) {
-  ssl->srtp_profile = NULL;
+static void ext_srtp_init(SSL_HANDSHAKE *hs) {
+  hs->ssl->srtp_profile = NULL;
 }
 
-static int ext_srtp_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
   if (profiles == NULL) {
     return 1;
@@ -1689,8 +1724,9 @@
   return 1;
 }
 
-static int ext_srtp_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1736,8 +1772,9 @@
   return 0;
 }
 
-static int ext_srtp_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_srtp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return 1;
   }
@@ -1779,7 +1816,8 @@
   return 1;
 }
 
-static int ext_srtp_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl->srtp_profile == NULL) {
     return 1;
   }
@@ -1802,7 +1840,7 @@
  *
  * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
 
-static int ext_ec_point_add_extension(SSL *ssl, CBB *out) {
+static int ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) {
   CBB contents, formats;
   if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -1815,9 +1853,9 @@
   return 1;
 }
 
-static int ext_ec_point_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+  if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) {
     return 0;
   }
 
@@ -1826,16 +1864,16 @@
     return 1;
   }
 
-  return ext_ec_point_add_extension(ssl, out);
+  return ext_ec_point_add_extension(hs, out);
 }
 
-static int ext_ec_point_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_ec_point_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                           CBS *contents) {
   if (contents == NULL) {
     return 1;
   }
 
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
     return 0;
   }
 
@@ -1856,16 +1894,17 @@
   return 1;
 }
 
-static int ext_ec_point_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_ec_point_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                           CBS *contents) {
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
     return 1;
   }
 
-  return ext_ec_point_parse_serverhello(ssl, out_alert, contents);
+  return ext_ec_point_parse_serverhello(hs, out_alert, contents);
 }
 
-static int ext_ec_point_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     return 1;
   }
@@ -1878,7 +1917,7 @@
     return 1;
   }
 
-  return ext_ec_point_add_extension(ssl, out);
+  return ext_ec_point_add_extension(hs, out);
 }
 
 
@@ -1886,7 +1925,8 @@
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.6 */
 
-static size_t ext_pre_shared_key_clienthello_length(SSL *ssl) {
+static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -1906,7 +1946,8 @@
   return 15 + ssl->session->tlsext_ticklen + binder_len;
 }
 
-static int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -1946,11 +1987,12 @@
     return 0;
   }
 
-  ssl->s3->hs->needs_psk_binder = 1;
+  hs->needs_psk_binder = 1;
   return CBB_flush(out);
 }
 
-int ssl_ext_pre_shared_key_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
+                                             uint8_t *out_alert,
                                              CBS *contents) {
   uint16_t psk_id;
   if (!CBS_get_u16(contents, &psk_id) ||
@@ -1970,18 +2012,20 @@
   return 1;
 }
 
-int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
+int ssl_ext_pre_shared_key_parse_clienthello(SSL_HANDSHAKE *hs,
                                              SSL_SESSION **out_session,
                                              CBS *out_binders,
                                              uint8_t *out_alert,
                                              CBS *contents) {
+  SSL *const ssl = hs->ssl;
   /* We only process the first PSK identity since we don't support pure PSK. */
   uint32_t obfuscated_ticket_age;
-  CBS identity, ticket, binders;
-  if (!CBS_get_u16_length_prefixed(contents, &identity) ||
-      !CBS_get_u16_length_prefixed(&identity, &ticket) ||
-      !CBS_get_u32(&identity, &obfuscated_ticket_age) ||
+  CBS identities, ticket, binders;
+  if (!CBS_get_u16_length_prefixed(contents, &identities) ||
+      !CBS_get_u16_length_prefixed(&identities, &ticket) ||
+      !CBS_get_u32(&identities, &obfuscated_ticket_age) ||
       !CBS_get_u16_length_prefixed(contents, &binders) ||
+      CBS_len(&binders) == 0 ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     *out_alert = SSL_AD_DECODE_ERROR;
@@ -1990,11 +2034,38 @@
 
   *out_binders = binders;
 
-  /* The PSK identity must have a corresponding binder. */
-  CBS binder;
-  if (!CBS_get_u8_length_prefixed(&binders, &binder)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
+  /* Check the syntax of the remaining identities, but do not process them. */
+  size_t num_identities = 1;
+  while (CBS_len(&identities) != 0) {
+    CBS unused_ticket;
+    uint32_t unused_obfuscated_ticket_age;
+    if (!CBS_get_u16_length_prefixed(&identities, &unused_ticket) ||
+        !CBS_get_u32(&identities, &unused_obfuscated_ticket_age)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return 0;
+    }
+
+    num_identities++;
+  }
+
+  /* Check the syntax of the binders. The value will be checked later if
+   * resuming. */
+  size_t num_binders = 0;
+  while (CBS_len(&binders) != 0) {
+    CBS binder;
+    if (!CBS_get_u8_length_prefixed(&binders, &binder)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return 0;
+    }
+
+    num_binders++;
+  }
+
+  if (num_identities != num_binders) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_BINDER_COUNT_MISMATCH);
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     return 0;
   }
 
@@ -2013,8 +2084,8 @@
   return 1;
 }
 
-int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out) {
-  if (!ssl->s3->session_reused) {
+int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  if (!hs->ssl->s3->session_reused) {
     return 1;
   }
 
@@ -2034,7 +2105,10 @@
 /* Pre-Shared Key Exchange Modes
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.7 */
-static int ext_psk_key_exchange_modes_add_clienthello(SSL *ssl, CBB *out) {
+
+static int ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs,
+                                                      CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -2055,7 +2129,7 @@
   return CBB_flush(out);
 }
 
-static int ext_psk_key_exchange_modes_parse_clienthello(SSL *ssl,
+static int ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
                                                         uint8_t *out_alert,
                                                         CBS *contents) {
   if (contents == NULL) {
@@ -2071,18 +2145,49 @@
   }
 
   /* We only support tickets with PSK_DHE_KE. */
-  ssl->s3->hs->accept_psk_mode =
+  hs->accept_psk_mode =
       memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL;
 
   return 1;
 }
 
 
+/* Early Data Indication
+ *
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8 */
+
+static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  /* TODO(svaldez): Support 0RTT. */
+  return 1;
+}
+
+static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
+                                            uint8_t *out_alert, CBS *contents) {
+  SSL *const ssl = hs->ssl;
+  if (contents == NULL) {
+    return 1;
+  }
+
+  if (CBS_len(contents) != 0) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  /* Since we don't currently accept 0-RTT, we have to skip past any early data
+   * the client might have sent. */
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    ssl->s3->skip_early_data = 1;
+  }
+  return 1;
+}
+
+
 /* Key Share
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.5 */
 
-static int ext_key_share_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -2099,18 +2204,18 @@
     return 0;
   }
 
-  uint16_t group_id = ssl->s3->hs->retry_group;
-  if (ssl->s3->hs->received_hello_retry_request) {
+  uint16_t group_id = hs->retry_group;
+  if (hs->received_hello_retry_request) {
     /* We received a HelloRetryRequest without a new curve, so there is no new
      * share to append. Leave |ecdh_ctx| as-is. */
     if (group_id == 0 &&
-        !CBB_add_bytes(&kse_bytes, ssl->s3->hs->key_share_bytes,
-                       ssl->s3->hs->key_share_bytes_len)) {
+        !CBB_add_bytes(&kse_bytes, hs->key_share_bytes,
+                       hs->key_share_bytes_len)) {
       return 0;
     }
-    OPENSSL_free(ssl->s3->hs->key_share_bytes);
-    ssl->s3->hs->key_share_bytes = NULL;
-    ssl->s3->hs->key_share_bytes_len = 0;
+    OPENSSL_free(hs->key_share_bytes);
+    hs->key_share_bytes = NULL;
+    hs->key_share_bytes_len = 0;
     if (group_id == 0) {
       return CBB_flush(out);
     }
@@ -2139,19 +2244,18 @@
   CBB key_exchange;
   if (!CBB_add_u16(&kse_bytes, group_id) ||
       !CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) ||
-      !SSL_ECDH_CTX_init(&ssl->s3->hs->ecdh_ctx, group_id) ||
-      !SSL_ECDH_CTX_offer(&ssl->s3->hs->ecdh_ctx, &key_exchange) ||
+      !SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) ||
+      !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &key_exchange) ||
       !CBB_flush(&kse_bytes)) {
     return 0;
   }
 
-  if (!ssl->s3->hs->received_hello_retry_request) {
+  if (!hs->received_hello_retry_request) {
     /* Save the contents of the extension to repeat it in the second
      * ClientHello. */
-    ssl->s3->hs->key_share_bytes_len = CBB_len(&kse_bytes);
-    ssl->s3->hs->key_share_bytes = BUF_memdup(CBB_data(&kse_bytes),
-                                              CBB_len(&kse_bytes));
-    if (ssl->s3->hs->key_share_bytes == NULL) {
+    hs->key_share_bytes_len = CBB_len(&kse_bytes);
+    hs->key_share_bytes = BUF_memdup(CBB_data(&kse_bytes), CBB_len(&kse_bytes));
+    if (hs->key_share_bytes == NULL) {
       return 0;
     }
   }
@@ -2159,9 +2263,10 @@
   return CBB_flush(out);
 }
 
-int ssl_ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
+int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret,
                                         size_t *out_secret_len,
                                         uint8_t *out_alert, CBS *contents) {
+  SSL *const ssl = hs->ssl;
   CBS peer_key;
   uint16_t group_id;
   if (!CBS_get_u16(contents, &group_id) ||
@@ -2171,31 +2276,30 @@
     return 0;
   }
 
-  if (SSL_ECDH_CTX_get_id(&ssl->s3->hs->ecdh_ctx) != group_id) {
+  if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) != group_id) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
     return 0;
   }
 
-  if (!SSL_ECDH_CTX_finish(&ssl->s3->hs->ecdh_ctx, out_secret, out_secret_len,
-                           out_alert, CBS_data(&peer_key),
-                           CBS_len(&peer_key))) {
+  if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, out_secret, out_secret_len, out_alert,
+                           CBS_data(&peer_key), CBS_len(&peer_key))) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
   }
 
   ssl->s3->new_session->key_exchange_info = group_id;
-  SSL_ECDH_CTX_cleanup(&ssl->s3->hs->ecdh_ctx);
+  SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
   return 1;
 }
 
-int ssl_ext_key_share_parse_clienthello(SSL *ssl, int *out_found,
+int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, int *out_found,
                                         uint8_t **out_secret,
                                         size_t *out_secret_len,
                                         uint8_t *out_alert, CBS *contents) {
   uint16_t group_id;
   CBS key_shares;
-  if (!tls1_get_shared_group(ssl, &group_id)) {
+  if (!tls1_get_shared_group(hs, &group_id)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
     return 0;
@@ -2247,11 +2351,9 @@
   CBB public_key;
   if (!CBB_init(&public_key, 32) ||
       !SSL_ECDH_CTX_init(&group, group_id) ||
-      !SSL_ECDH_CTX_accept(&group, &public_key, &secret, &secret_len,
-                           out_alert, CBS_data(&peer_key),
-                           CBS_len(&peer_key)) ||
-      !CBB_finish(&public_key, &ssl->s3->hs->public_key,
-                  &ssl->s3->hs->public_key_len)) {
+      !SSL_ECDH_CTX_accept(&group, &public_key, &secret, &secret_len, out_alert,
+                           CBS_data(&peer_key), CBS_len(&peer_key)) ||
+      !CBB_finish(&public_key, &hs->public_key, &hs->public_key_len)) {
     OPENSSL_free(secret);
     SSL_ECDH_CTX_cleanup(&group);
     CBB_cleanup(&public_key);
@@ -2267,23 +2369,23 @@
   return 1;
 }
 
-int ssl_ext_key_share_add_serverhello(SSL *ssl, CBB *out) {
+int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t group_id;
   CBB kse_bytes, public_key;
-  if (!tls1_get_shared_group(ssl, &group_id) ||
+  if (!tls1_get_shared_group(hs, &group_id) ||
       !CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16_length_prefixed(out, &kse_bytes) ||
       !CBB_add_u16(&kse_bytes, group_id) ||
       !CBB_add_u16_length_prefixed(&kse_bytes, &public_key) ||
-      !CBB_add_bytes(&public_key, ssl->s3->hs->public_key,
-                     ssl->s3->hs->public_key_len) ||
+      !CBB_add_bytes(&public_key, hs->public_key, hs->public_key_len) ||
       !CBB_flush(out)) {
     return 0;
   }
 
-  OPENSSL_free(ssl->s3->hs->public_key);
-  ssl->s3->hs->public_key = NULL;
-  ssl->s3->hs->public_key_len = 0;
+  OPENSSL_free(hs->public_key);
+  hs->public_key = NULL;
+  hs->public_key_len = 0;
 
   ssl->s3->new_session->key_exchange_info = group_id;
   return 1;
@@ -2294,7 +2396,8 @@
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.1 */
 
-static int ext_supported_versions_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     return 0;
@@ -2335,8 +2438,8 @@
  *
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.2 */
 
-static int ext_cookie_add_clienthello(SSL *ssl, CBB *out) {
-  if (ssl->s3->hs->cookie == NULL) {
+static int ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  if (hs->cookie == NULL) {
     return 1;
   }
 
@@ -2344,15 +2447,15 @@
   if (!CBB_add_u16(out, TLSEXT_TYPE_cookie) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &cookie) ||
-      !CBB_add_bytes(&cookie, ssl->s3->hs->cookie, ssl->s3->hs->cookie_len) ||
+      !CBB_add_bytes(&cookie, hs->cookie, hs->cookie_len) ||
       !CBB_flush(out)) {
     return 0;
   }
 
   /* The cookie is no longer needed in memory. */
-  OPENSSL_free(ssl->s3->hs->cookie);
-  ssl->s3->hs->cookie = NULL;
-  ssl->s3->hs->cookie_len = 0;
+  OPENSSL_free(hs->cookie);
+  hs->cookie = NULL;
+  hs->cookie_len = 0;
   return 1;
 }
 
@@ -2362,7 +2465,8 @@
  * https://tools.ietf.org/html/rfc4492#section-5.1.2
  * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.4 */
 
-static int ext_supported_groups_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   CBB contents, groups_bytes;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -2390,14 +2494,16 @@
   return CBB_flush(out);
 }
 
-static int ext_supported_groups_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+static int ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs,
+                                                  uint8_t *out_alert,
                                                   CBS *contents) {
   /* This extension is not expected to be echoed by servers in TLS 1.2, but some
    * BigIP servers send it nonetheless, so do not enforce this. */
   return 1;
 }
 
-static int ext_supported_groups_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+static int ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs,
+                                                  uint8_t *out_alert,
                                                   CBS *contents) {
   if (contents == NULL) {
     return 1;
@@ -2411,9 +2517,9 @@
     return 0;
   }
 
-  ssl->s3->hs->peer_supported_group_list = OPENSSL_malloc(
-      CBS_len(&supported_group_list));
-  if (ssl->s3->hs->peer_supported_group_list == NULL) {
+  hs->peer_supported_group_list =
+      OPENSSL_malloc(CBS_len(&supported_group_list));
+  if (hs->peer_supported_group_list == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
   }
@@ -2421,24 +2527,24 @@
   const size_t num_groups = CBS_len(&supported_group_list) / 2;
   for (size_t i = 0; i < num_groups; i++) {
     if (!CBS_get_u16(&supported_group_list,
-                     &ssl->s3->hs->peer_supported_group_list[i])) {
+                     &hs->peer_supported_group_list[i])) {
       goto err;
     }
   }
 
   assert(CBS_len(&supported_group_list) == 0);
-  ssl->s3->hs->peer_supported_group_list_len = num_groups;
+  hs->peer_supported_group_list_len = num_groups;
 
   return 1;
 
 err:
-  OPENSSL_free(ssl->s3->hs->peer_supported_group_list);
-  ssl->s3->hs->peer_supported_group_list = NULL;
+  OPENSSL_free(hs->peer_supported_group_list);
+  hs->peer_supported_group_list = NULL;
   *out_alert = SSL_AD_INTERNAL_ERROR;
   return 0;
 }
 
-static int ext_supported_groups_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   /* Servers don't echo this extension. */
   return 1;
 }
@@ -2561,6 +2667,14 @@
     dont_add_serverhello,
   },
   {
+    TLSEXT_TYPE_early_data,
+    NULL,
+    ext_early_data_add_clienthello,
+    forbid_parse_serverhello,
+    ext_early_data_parse_clienthello,
+    dont_add_serverhello,
+  },
+  {
     TLSEXT_TYPE_supported_versions,
     NULL,
     ext_supported_versions_add_clienthello,
@@ -2617,9 +2731,10 @@
          tls_extension_find(&index, extension_value) != NULL;
 }
 
-int ssl_add_clienthello_tlsext(SSL *ssl, CBB *out, size_t header_len) {
-  /* don't add extensions for SSLv3 unless doing secure renegotiation */
-  if (ssl->client_version == SSL3_VERSION &&
+int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len) {
+  SSL *const ssl = hs->ssl;
+  /* Don't add extensions for SSLv3 unless doing secure renegotiation. */
+  if (hs->client_version == SSL3_VERSION &&
       !ssl->s3->send_connection_binding) {
     return 1;
   }
@@ -2629,12 +2744,12 @@
     goto err;
   }
 
-  ssl->s3->hs->extensions.sent = 0;
-  ssl->s3->hs->custom_extensions.sent = 0;
+  hs->extensions.sent = 0;
+  hs->custom_extensions.sent = 0;
 
   for (size_t i = 0; i < kNumExtensions; i++) {
     if (kExtensions[i].init != NULL) {
-      kExtensions[i].init(ssl);
+      kExtensions[i].init(hs);
     }
   }
 
@@ -2650,18 +2765,18 @@
 
   for (size_t i = 0; i < kNumExtensions; i++) {
     const size_t len_before = CBB_len(&extensions);
-    if (!kExtensions[i].add_clienthello(ssl, &extensions)) {
+    if (!kExtensions[i].add_clienthello(hs, &extensions)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       goto err;
     }
 
     if (CBB_len(&extensions) != len_before) {
-      ssl->s3->hs->extensions.sent |= (1u << i);
+      hs->extensions.sent |= (1u << i);
     }
   }
 
-  if (!custom_ext_add_clienthello(ssl, &extensions)) {
+  if (!custom_ext_add_clienthello(hs, &extensions)) {
     goto err;
   }
 
@@ -2684,7 +2799,7 @@
   }
 
   if (!SSL_is_dtls(ssl)) {
-    size_t psk_extension_len = ext_pre_shared_key_clienthello_length(ssl);
+    size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs);
     header_len += 2 + CBB_len(&extensions) + psk_extension_len;
     if (header_len > 0xff && header_len < 0x200) {
       /* Add padding to workaround bugs in F5 terminators. See RFC 7685.
@@ -2714,7 +2829,7 @@
   }
 
   /* The PSK extension must be last, including after the padding. */
-  if (!ext_pre_shared_key_add_clienthello(ssl, &extensions)) {
+  if (!ext_pre_shared_key_add_clienthello(hs, &extensions)) {
     goto err;
   }
 
@@ -2730,27 +2845,27 @@
   return 0;
 }
 
-int ssl_add_serverhello_tlsext(SSL *ssl, CBB *out) {
+int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   CBB extensions;
   if (!CBB_add_u16_length_prefixed(out, &extensions)) {
     goto err;
   }
 
-  unsigned i;
-  for (i = 0; i < kNumExtensions; i++) {
-    if (!(ssl->s3->hs->extensions.received & (1u << i))) {
+  for (unsigned i = 0; i < kNumExtensions; i++) {
+    if (!(hs->extensions.received & (1u << i))) {
       /* Don't send extensions that were not received. */
       continue;
     }
 
-    if (!kExtensions[i].add_serverhello(ssl, &extensions)) {
+    if (!kExtensions[i].add_serverhello(hs, &extensions)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       goto err;
     }
   }
 
-  if (!custom_ext_add_serverhello(ssl, &extensions)) {
+  if (!custom_ext_add_serverhello(hs, &extensions)) {
     goto err;
   }
 
@@ -2767,17 +2882,18 @@
   return 0;
 }
 
-static int ssl_scan_clienthello_tlsext(
-    SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
-    int *out_alert) {
+static int ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs,
+                                       const SSL_CLIENT_HELLO *client_hello,
+                                       int *out_alert) {
+  SSL *const ssl = hs->ssl;
   for (size_t i = 0; i < kNumExtensions; i++) {
     if (kExtensions[i].init != NULL) {
-      kExtensions[i].init(ssl);
+      kExtensions[i].init(hs);
     }
   }
 
-  ssl->s3->hs->extensions.received = 0;
-  ssl->s3->hs->custom_extensions.received = 0;
+  hs->extensions.received = 0;
+  hs->custom_extensions.received = 0;
 
   CBS extensions;
   CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
@@ -2803,16 +2919,16 @@
         tls_extension_find(&ext_index, type);
 
     if (ext == NULL) {
-      if (!custom_ext_parse_clienthello(ssl, out_alert, type, &extension)) {
+      if (!custom_ext_parse_clienthello(hs, out_alert, type, &extension)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
         return 0;
       }
       continue;
     }
 
-    ssl->s3->hs->extensions.received |= (1u << ext_index);
+    hs->extensions.received |= (1u << ext_index);
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ext->parse_clienthello(ssl, &alert, &extension)) {
+    if (!ext->parse_clienthello(hs, &alert, &extension)) {
       *out_alert = alert;
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)type);
@@ -2821,7 +2937,7 @@
   }
 
   for (size_t i = 0; i < kNumExtensions; i++) {
-    if (ssl->s3->hs->extensions.received & (1u << i)) {
+    if (hs->extensions.received & (1u << i)) {
       continue;
     }
 
@@ -2835,13 +2951,13 @@
       CBS_init(&fake_contents, kFakeRenegotiateExtension,
                sizeof(kFakeRenegotiateExtension));
       contents = &fake_contents;
-      ssl->s3->hs->extensions.received |= (1u << i);
+      hs->extensions.received |= (1u << i);
     }
 
     /* Extension wasn't observed so call the callback with a NULL
      * parameter. */
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!kExtensions[i].parse_clienthello(ssl, &alert, contents)) {
+    if (!kExtensions[i].parse_clienthello(hs, &alert, contents)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       *out_alert = alert;
@@ -2852,15 +2968,16 @@
   return 1;
 }
 
-int ssl_parse_clienthello_tlsext(
-    SSL *ssl, const struct ssl_early_callback_ctx *client_hello) {
+int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
+                                 const SSL_CLIENT_HELLO *client_hello) {
+  SSL *const ssl = hs->ssl;
   int alert = -1;
-  if (ssl_scan_clienthello_tlsext(ssl, client_hello, &alert) <= 0) {
+  if (ssl_scan_clienthello_tlsext(hs, client_hello, &alert) <= 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
-  if (ssl_check_clienthello_tlsext(ssl) <= 0) {
+  if (ssl_check_clienthello_tlsext(hs) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_TLSEXT);
     return 0;
   }
@@ -2870,7 +2987,9 @@
 
 OPENSSL_COMPILE_ASSERT(kNumExtensions <= sizeof(uint32_t) * 8, too_many_bits);
 
-static int ssl_scan_serverhello_tlsext(SSL *ssl, CBS *cbs, int *out_alert) {
+static int ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
+                                       int *out_alert) {
+  SSL *const ssl = hs->ssl;
   /* Before TLS 1.3, ServerHello extensions blocks may be omitted if empty. */
   if (CBS_len(cbs) == 0 && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
     return 1;
@@ -2901,13 +3020,13 @@
         tls_extension_find(&ext_index, type);
 
     if (ext == NULL) {
-      if (!custom_ext_parse_serverhello(ssl, out_alert, type, &extension)) {
+      if (!custom_ext_parse_serverhello(hs, out_alert, type, &extension)) {
         return 0;
       }
       continue;
     }
 
-    if (!(ssl->s3->hs->extensions.sent & (1u << ext_index)) &&
+    if (!(hs->extensions.sent & (1u << ext_index)) &&
         type != TLSEXT_TYPE_renegotiate) {
       /* If the extension was never sent then it is illegal, except for the
        * renegotiation extension which, in SSL 3.0, is signaled via SCSV. */
@@ -2920,7 +3039,7 @@
     received |= (1u << ext_index);
 
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ext->parse_serverhello(ssl, &alert, &extension)) {
+    if (!ext->parse_serverhello(hs, &alert, &extension)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)type);
       *out_alert = alert;
@@ -2933,7 +3052,7 @@
       /* Extension wasn't observed so call the callback with a NULL
        * parameter. */
       uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!kExtensions[i].parse_serverhello(ssl, &alert, NULL)) {
+      if (!kExtensions[i].parse_serverhello(hs, &alert, NULL)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
         ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
         *out_alert = alert;
@@ -2945,7 +3064,8 @@
   return 1;
 }
 
-static int ssl_check_clienthello_tlsext(SSL *ssl) {
+static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = SSL_TLSEXT_ERR_NOACK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
@@ -2967,7 +3087,7 @@
       return 1;
 
     case SSL_TLSEXT_ERR_NOACK:
-      ssl->s3->hs->should_ack_sni = 0;
+      hs->should_ack_sni = 0;
       return 1;
 
     default:
@@ -2975,7 +3095,8 @@
   }
 }
 
-static int ssl_check_serverhello_tlsext(SSL *ssl) {
+static int ssl_check_serverhello_tlsext(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   int ret = SSL_TLSEXT_ERR_OK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
@@ -3001,14 +3122,15 @@
   }
 }
 
-int ssl_parse_serverhello_tlsext(SSL *ssl, CBS *cbs) {
+int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
+  SSL *const ssl = hs->ssl;
   int alert = -1;
-  if (ssl_scan_serverhello_tlsext(ssl, cbs, &alert) <= 0) {
+  if (ssl_scan_serverhello_tlsext(hs, cbs, &alert) <= 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
-  if (ssl_check_serverhello_tlsext(ssl) <= 0) {
+  if (ssl_check_serverhello_tlsext(hs) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SERVERHELLO_TLSEXT);
     return 0;
   }
@@ -3146,13 +3268,12 @@
   return ret;
 }
 
-int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *in_sigalgs) {
+int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *in_sigalgs) {
   /* Extension ignored for inappropriate versions */
-  if (ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
+  if (ssl3_protocol_version(hs->ssl) < TLS1_2_VERSION) {
     return 1;
   }
 
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
   OPENSSL_free(hs->peer_sigalgs);
   hs->peer_sigalgs = NULL;
   hs->num_peer_sigalgs = 0;
@@ -3188,9 +3309,9 @@
   return 1;
 }
 
-int tls1_choose_signature_algorithm(SSL *ssl, uint16_t *out) {
+int tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) {
+  SSL *const ssl = hs->ssl;
   CERT *cert = ssl->cert;
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
 
   /* Before TLS 1.2, the signature algorithm isn't negotiated as part of the
    * handshake. It is fixed at MD5-SHA1 for RSA and SHA1 for ECDSA. */
@@ -3448,7 +3569,8 @@
     return -1;
   }
 
-  ssl->s3->new_session->original_handshake_hash_len = digest_len;
+  assert(sizeof(ssl->s3->new_session->original_handshake_hash) < 256);
+  ssl->s3->new_session->original_handshake_hash_len = (uint8_t)digest_len;
 
   return 1;
 }
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 97860c4..b7f9c9d 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -65,7 +65,6 @@
 #include "packeted_bio.h"
 #include "test_config.h"
 
-namespace bssl {
 
 #if !defined(OPENSSL_WINDOWS)
 static int closesocket(int sock) {
@@ -246,7 +245,7 @@
       return ssl_private_key_failure;
   }
 
-  ScopedEVP_MD_CTX ctx;
+  bssl::ScopedEVP_MD_CTX ctx;
   EVP_PKEY_CTX *pctx;
   if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr,
                           test_state->private_key.get())) {
@@ -438,9 +437,9 @@
   return true;
 }
 
-static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
-  const TestConfig *config = GetTestConfig(ctx->ssl);
-  GetTestState(ctx->ssl)->early_callback_called = true;
+static int SelectCertificateCallback(const SSL_CLIENT_HELLO *client_hello) {
+  const TestConfig *config = GetTestConfig(client_hello->ssl);
+  GetTestState(client_hello->ssl)->early_callback_called = true;
 
   if (!config->expected_server_name.empty()) {
     const uint8_t *extension_data;
@@ -448,9 +447,9 @@
     CBS extension, server_name_list, host_name;
     uint8_t name_type;
 
-    if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
-                                              &extension_data,
-                                              &extension_len)) {
+    if (!SSL_early_callback_ctx_extension_get(
+            client_hello, TLSEXT_TYPE_server_name, &extension_data,
+            &extension_len)) {
       fprintf(stderr, "Could not find server_name extension.\n");
       return -1;
     }
@@ -483,7 +482,7 @@
       // Install the certificate asynchronously.
       return 0;
     }
-    if (!InstallCertificate(ctx->ssl)) {
+    if (!InstallCertificate(client_hello->ssl)) {
       return -1;
     }
   }
@@ -697,8 +696,8 @@
   }
 }
 
-static int DDoSCallback(const struct ssl_early_callback_ctx *early_context) {
-  const TestConfig *config = GetTestConfig(early_context->ssl);
+static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) {
+  const TestConfig *config = GetTestConfig(client_hello->ssl);
   static int callback_num = 0;
 
   callback_num++;
@@ -981,7 +980,7 @@
     SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
   }
 
-  SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
+  SSL_CTX_set_tls_channel_id_enabled(ssl_ctx.get(), 1);
   SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
 
   SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
@@ -1520,10 +1519,10 @@
   }
   if (!config->expected_channel_id.empty() ||
       config->enable_channel_id) {
-    SSL_enable_tls_channel_id(ssl.get());
+    SSL_set_tls_channel_id_enabled(ssl.get(), 1);
   }
   if (!config->send_channel_id.empty()) {
-    SSL_enable_tls_channel_id(ssl.get());
+    SSL_set_tls_channel_id_enabled(ssl.get(), 1);
     if (!config->async) {
       // The async case will be supplied by |ChannelIdCallback|.
       bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id);
@@ -1589,9 +1588,6 @@
   if (!config->check_close_notify) {
     SSL_set_quiet_shutdown(ssl.get(), 1);
   }
-  if (config->disable_npn) {
-    SSL_set_options(ssl.get(), SSL_OP_DISABLE_NPN);
-  }
   if (config->p384_only) {
     int nid = NID_secp384r1;
     if (!SSL_set1_curves(ssl.get(), &nid, 1)) {
@@ -1873,7 +1869,7 @@
   ~StderrDelimiter() { fprintf(stderr, "--- DONE ---\n"); }
 };
 
-static int Main(int argc, char **argv) {
+int main(int argc, char **argv) {
   // To distinguish ASan's output from ours, add a trailing message to stderr.
   // Anything following this line will be considered an error.
   StderrDelimiter delimiter;
@@ -1941,9 +1937,3 @@
 
   return 0;
 }
-
-}  // namespace bssl
-
-int main(int argc, char **argv) {
-  return bssl::Main(argc, argv);
-}
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index a6496fd..9fbf5dc 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -1109,6 +1109,25 @@
 	// copies of each KeyShareEntry.
 	DuplicateKeyShares bool
 
+	// SendEarlyAlert, if true, sends a fatal alert after the ClientHello.
+	SendEarlyAlert bool
+
+	// SendEarlyDataLength, if non-zero, is the amount of early data to send after
+	// the ClientHello.
+	SendEarlyDataLength int
+
+	// OmitEarlyDataExtension, if true, causes the early data extension to
+	// be omitted in the ClientHello.
+	OmitEarlyDataExtension bool
+
+	// SendEarlyDataOnSecondClientHello, if true, causes the TLS 1.3 client to
+	// send early data after the second ClientHello.
+	SendEarlyDataOnSecondClientHello bool
+
+	// InterleaveEarlyData, if true, causes the TLS 1.3 client to send early
+	// data interleaved with the second ClientHello and the client Finished.
+	InterleaveEarlyData bool
+
 	// EmptyEncryptedExtensions, if true, causes the TLS 1.3 server to
 	// emit an empty EncryptedExtensions block.
 	EmptyEncryptedExtensions bool
@@ -1198,6 +1217,10 @@
 	// SendNoPSKBinder, if true, causes the client to send no PSK binders.
 	SendNoPSKBinder bool
 
+	// SendExtraPSKBinder, if true, causes the client to send an extra PSK
+	// binder.
+	SendExtraPSKBinder bool
+
 	// PSKBinderFirst, if true, causes the client to send the PSK Binder
 	// extension as the first extension instead of the last extension.
 	PSKBinderFirst bool
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 39c2785..80a5b06 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1768,3 +1768,16 @@
 	c.out.doKeyUpdate(c, true)
 	return nil
 }
+
+func (c *Conn) sendFakeEarlyData(len int) error {
+	// Assemble a fake early data record. This does not use writeRecord
+	// because the record layer may be using different keys at this point.
+	payload := make([]byte, 5+len)
+	payload[0] = byte(recordTypeApplicationData)
+	payload[1] = 3
+	payload[2] = 1
+	payload[3] = byte(len >> 8)
+	payload[4] = byte(len)
+	_, err := c.conn.Write(payload)
+	return err
+}
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index d074778..7fa7ea2 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -319,6 +319,10 @@
 		hello.cipherSuites = c.config.Bugs.SendCipherSuites
 	}
 
+	if c.config.Bugs.SendEarlyDataLength > 0 && !c.config.Bugs.OmitEarlyDataExtension {
+		hello.hasEarlyData = true
+	}
+
 	var helloBytes []byte
 	if c.config.Bugs.SendV2ClientHello {
 		// Test that the peer left-pads random.
@@ -356,6 +360,12 @@
 	if err := c.simulatePacketLoss(nil); err != nil {
 		return err
 	}
+	if c.config.Bugs.SendEarlyAlert {
+		c.sendAlert(alertHandshakeFailure)
+	}
+	if c.config.Bugs.SendEarlyDataLength > 0 {
+		c.sendFakeEarlyData(c.config.Bugs.SendEarlyDataLength)
+	}
 	msg, err := c.readHandshake()
 	if err != nil {
 		return err
@@ -452,16 +462,28 @@
 			hello.hasKeyShares = false
 		}
 
-		hello.hasEarlyData = false
+		hello.hasEarlyData = c.config.Bugs.SendEarlyDataOnSecondClientHello
 		hello.raw = nil
 
 		if len(hello.pskIdentities) > 0 {
 			generatePSKBinders(hello, pskCipherSuite, session.masterSecret, append(helloBytes, helloRetryRequest.marshal()...), c.config)
 		}
 		secondHelloBytes = hello.marshal()
-		c.writeRecord(recordTypeHandshake, secondHelloBytes)
+
+		if c.config.Bugs.InterleaveEarlyData {
+			c.sendFakeEarlyData(4)
+			c.writeRecord(recordTypeHandshake, secondHelloBytes[:16])
+			c.sendFakeEarlyData(4)
+			c.writeRecord(recordTypeHandshake, secondHelloBytes[16:])
+		} else {
+			c.writeRecord(recordTypeHandshake, secondHelloBytes)
+		}
 		c.flushHandshake()
 
+		if c.config.Bugs.SendEarlyDataOnSecondClientHello {
+			c.sendFakeEarlyData(4)
+		}
+
 		msg, err = c.readHandshake()
 		if err != nil {
 			return err
@@ -622,7 +644,6 @@
 	//
 	// TODO(davidben): This will need to be handled slightly earlier once
 	// 0-RTT is implemented.
-	var psk []byte
 	if hs.serverHello.hasPSKIdentity {
 		// We send at most one PSK identity.
 		if hs.session == nil || hs.serverHello.pskIdentity != 0 {
@@ -634,21 +655,18 @@
 			c.sendAlert(alertHandshakeFailure)
 			return errors.New("tls: server resumed an invalid session for the cipher suite")
 		}
-		psk = hs.session.masterSecret
+		hs.finishedHash.addEntropy(hs.session.masterSecret)
 		c.didResume = true
 	} else {
-		psk = zeroSecret
+		hs.finishedHash.addEntropy(zeroSecret)
 	}
 
-	earlySecret := hs.finishedHash.extractKey(zeroSecret, psk)
-
 	if !hs.serverHello.hasKeyShare {
 		c.sendAlert(alertUnsupportedExtension)
 		return errors.New("tls: server omitted KeyShare on resumption.")
 	}
 
 	// Resolve ECDHE and compute the handshake secret.
-	var ecdheSecret []byte
 	if !c.config.Bugs.MissingKeyShare && !c.config.Bugs.SecondClientHelloMissingKeyShare {
 		curve, ok := hs.keyShares[hs.serverHello.keyShare.group]
 		if !ok {
@@ -657,22 +675,19 @@
 		}
 		c.curveID = hs.serverHello.keyShare.group
 
-		var err error
-		ecdheSecret, err = curve.finish(hs.serverHello.keyShare.keyExchange)
+		ecdheSecret, err := curve.finish(hs.serverHello.keyShare.keyExchange)
 		if err != nil {
 			return err
 		}
+		hs.finishedHash.addEntropy(ecdheSecret)
 	} else {
-		ecdheSecret = zeroSecret
+		hs.finishedHash.addEntropy(zeroSecret)
 	}
 
-	// Compute the handshake secret.
-	handshakeSecret := hs.finishedHash.extractKey(earlySecret, ecdheSecret)
-
 	// Switch to handshake traffic keys.
-	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
+	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
-	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
+	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
 	c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
 
 	msg, err := c.readHandshake()
@@ -800,9 +815,9 @@
 
 	// The various secrets do not incorporate the client's final leg, so
 	// derive them now before updating the handshake context.
-	masterSecret := hs.finishedHash.extractKey(handshakeSecret, zeroSecret)
-	clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
-	serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
+	hs.finishedHash.addEntropy(zeroSecret)
+	clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel)
+	serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -871,6 +886,12 @@
 	if c.config.Bugs.PartialClientFinishedWithClientHello {
 		// The first byte has already been sent.
 		c.writeRecord(recordTypeHandshake, finished.marshal()[1:])
+	} else if c.config.Bugs.InterleaveEarlyData {
+		finishedBytes := finished.marshal()
+		c.sendFakeEarlyData(4)
+		c.writeRecord(recordTypeHandshake, finishedBytes[:1])
+		c.sendFakeEarlyData(4)
+		c.writeRecord(recordTypeHandshake, finishedBytes[1:])
 	} else {
 		c.writeRecord(recordTypeHandshake, finished.marshal())
 	}
@@ -883,8 +904,8 @@
 	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
 	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
-	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
-	c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
+	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
+	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 	return nil
 }
 
@@ -1648,11 +1669,16 @@
 		binderLen--
 	}
 
+	numBinders := 1
+	if config.Bugs.SendExtraPSKBinder {
+		numBinders++
+	}
+
 	// Fill hello.pskBinders with appropriate length arrays of zeros so the
 	// length prefixes are correct when computing the binder over the truncated
 	// ClientHello message.
-	hello.pskBinders = make([][]byte, len(hello.pskIdentities))
-	for i := range hello.pskIdentities {
+	hello.pskBinders = make([][]byte, numBinders)
+	for i := range hello.pskBinders {
 		hello.pskBinders[i] = make([]byte, binderLen)
 	}
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 67950ba..57566c5 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -501,15 +501,12 @@
 	}
 
 	// Resolve PSK and compute the early secret.
-	var psk []byte
 	if hs.sessionState != nil {
-		psk = hs.sessionState.masterSecret
+		hs.finishedHash.addEntropy(hs.sessionState.masterSecret)
 	} else {
-		psk = hs.finishedHash.zeroSecret()
+		hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret())
 	}
 
-	earlySecret := hs.finishedHash.extractKey(hs.finishedHash.zeroSecret(), psk)
-
 	hs.hello.hasKeyShare = true
 	if hs.sessionState != nil && config.Bugs.NegotiatePSKResumption {
 		hs.hello.hasKeyShare = false
@@ -647,7 +644,6 @@
 	}
 
 	// Resolve ECDHE and compute the handshake secret.
-	var ecdheSecret []byte
 	if hs.hello.hasKeyShare {
 		// Once a curve has been selected and a key share identified,
 		// the server needs to generate a public value and send it in
@@ -672,13 +668,12 @@
 			peerKey = selectedKeyShare.keyExchange
 		}
 
-		var publicKey []byte
-		var err error
-		publicKey, ecdheSecret, err = curve.accept(config.rand(), peerKey)
+		publicKey, ecdheSecret, err := curve.accept(config.rand(), peerKey)
 		if err != nil {
 			c.sendAlert(alertHandshakeFailure)
 			return err
 		}
+		hs.finishedHash.addEntropy(ecdheSecret)
 		hs.hello.hasKeyShare = true
 
 		curveID := selectedCurve
@@ -702,7 +697,7 @@
 			}
 		}
 	} else {
-		ecdheSecret = hs.finishedHash.zeroSecret()
+		hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret())
 	}
 
 	// Send unencrypted ServerHello.
@@ -718,13 +713,10 @@
 	}
 	c.flushHandshake()
 
-	// Compute the handshake secret.
-	handshakeSecret := hs.finishedHash.extractKey(earlySecret, ecdheSecret)
-
 	// Switch to handshake traffic keys.
-	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
+	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
 	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
-	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
+	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
 	// Send EncryptedExtensions.
@@ -842,10 +834,10 @@
 
 	// The various secrets do not incorporate the client's final leg, so
 	// derive them now before updating the handshake context.
-	masterSecret := hs.finishedHash.extractKey(handshakeSecret, hs.finishedHash.zeroSecret())
-	clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
-	serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
-	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
+	hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret())
+	clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel)
+	serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel)
+	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
@@ -956,7 +948,7 @@
 	c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
 
 	c.cipherSuite = hs.suite
-	c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
+	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 
 	// TODO(davidben): Allow configuring the number of tickets sent for
 	// testing.
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index c311e99..cfc383d 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -129,14 +129,8 @@
 		return prf30
 	case VersionTLS10, VersionTLS11:
 		return prf10
-	// TODO(nharper): VersionTLS13 is in the case statement below only to
-	// support Fake TLS 1.3. Real TLS 1.3 should never call this function.
-	// Once we no longer support Fake TLS 1.3, the VersionTLS13 should be
-	// removed from this case statement.
-	case VersionTLS12, VersionTLS13:
-		if version == VersionTLS12 {
-			return prf12(suite.hash().New)
-		}
+	case VersionTLS12:
+		return prf12(suite.hash().New)
 	}
 	panic("unknown version")
 }
@@ -197,6 +191,8 @@
 
 		if version == VersionTLS12 {
 			ret.prf = prf12(ret.hash.New)
+		} else {
+			ret.secret = make([]byte, ret.hash.Size())
 		}
 	} else {
 		ret.hash = crypto.MD5SHA1
@@ -232,6 +228,9 @@
 
 	version uint16
 	prf     func(result, secret, label, seed []byte)
+
+	// secret, in TLS 1.3, is the running input secret.
+	secret []byte
 }
 
 func (h *finishedHash) Write(msg []byte) (n int, err error) {
@@ -370,10 +369,9 @@
 	return make([]byte, h.hash.Size())
 }
 
-// extractKey combines two secrets together with HKDF-Expand in the TLS 1.3 key
-// derivation schedule.
-func (h *finishedHash) extractKey(salt, ikm []byte) []byte {
-	return hkdfExtract(h.hash.New, salt, ikm)
+// addEntropy incorporates ikm into the running TLS 1.3 secret with HKDF-Expand.
+func (h *finishedHash) addEntropy(ikm []byte) {
+	h.secret = hkdfExtract(h.hash.New, h.secret, ikm)
 }
 
 // hkdfExpandLabel implements TLS 1.3's HKDF-Expand-Label function, as defined
@@ -420,8 +418,8 @@
 
 // deriveSecret implements TLS 1.3's Derive-Secret function, as defined in
 // section 7.1 of draft ietf-tls-tls13-16.
-func (h *finishedHash) deriveSecret(secret, label []byte) []byte {
-	return hkdfExpandLabel(h.hash, secret, label, h.appendContextHashes(nil), h.hash.Size())
+func (h *finishedHash) deriveSecret(label []byte) []byte {
+	return hkdfExpandLabel(h.hash, h.secret, label, h.appendContextHashes(nil), h.hash.Size())
 }
 
 // The following are context strings for CertificateVerify in TLS 1.3.
@@ -472,8 +470,8 @@
 
 func computePSKBinder(psk, label []byte, cipherSuite *cipherSuite, transcript, truncatedHello []byte) []byte {
 	finishedHash := newFinishedHash(VersionTLS13, cipherSuite)
-	earlySecret := finishedHash.extractKey(finishedHash.zeroSecret(), psk)
-	binderKey := finishedHash.deriveSecret(earlySecret, label)
+	finishedHash.addEntropy(psk)
+	binderKey := finishedHash.deriveSecret(label)
 	finishedHash.Write(transcript)
 	finishedHash.Write(truncatedHello)
 	return finishedHash.clientSum(binderKey)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 683f07c..15895d6 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -835,6 +835,18 @@
 		panic("expectResumeRejected without resumeSession in " + test.name)
 	}
 
+	for _, ver := range tlsVersions {
+		if !strings.Contains("-"+test.name+"-", "-"+ver.name+"-") {
+			continue
+		}
+
+		if test.config.MaxVersion != 0 || test.config.MinVersion != 0 || test.expectedVersion != 0 {
+			continue
+		}
+
+		panic(fmt.Sprintf("The name of test %q suggests that it's version specific, but min/max version in the Config is %x/%x. One of them should probably be %x", test.name, test.config.MinVersion, test.config.MaxVersion, ver.version))
+	}
+
 	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
 	if err != nil {
 		panic(err)
@@ -4973,20 +4985,6 @@
 				shouldFail:    true,
 				expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
 			})
-
-			// Test that NPN can be disabled with SSL_OP_DISABLE_NPN.
-			testCases = append(testCases, testCase{
-				name: "DisableNPN-" + ver.name,
-				config: Config{
-					MaxVersion: ver.version,
-					NextProtos: []string{"foo"},
-				},
-				flags: []string{
-					"-select-next-proto", "foo",
-					"-disable-npn",
-				},
-				expectNoNextProto: true,
-			})
 		}
 
 		// Test ticket behavior.
@@ -5568,7 +5566,7 @@
 			"-signed-cert-timestamps",
 			base64.StdEncoding.EncodeToString([]byte{0, 0}),
 		},
-		shouldFail: true,
+		shouldFail:    true,
 		expectedError: ":INVALID_SCT_LIST:",
 	})
 }
@@ -5979,6 +5977,36 @@
 
 	testCases = append(testCases, testCase{
 		testType:      serverTest,
+		name:          "Resume-Server-ExtraPSKBinder",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendExtraPSKBinder: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: illegal parameter",
+		expectedError:      ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-ExtraIdentityNoBinder",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				ExtraPSKIdentity: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: illegal parameter",
+		expectedError:      ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
 		name:          "Resume-Server-InvalidPSKBinder",
 		resumeSession: true,
 		config: Config{
@@ -9033,6 +9061,144 @@
 	})
 
 	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 4,
+			},
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-OmitEarlyDataExtension",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength:    4,
+				OmitEarlyDataExtension: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-TooMuchData",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 16384 + 1,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-Interleaved",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 4,
+				InterleaveEarlyData: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-EarlyDataInTLS12",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 4,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_RECORD:",
+		flags:         []string{"-max-version", strconv.Itoa(VersionTLS12)},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-HRR",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 4,
+			},
+			DefaultCurves: []CurveID{},
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-HRR-Interleaved",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 4,
+				InterleaveEarlyData: true,
+			},
+			DefaultCurves: []CurveID{},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_RECORD:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-HRR-TooMuchData",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataLength: 16384 + 1,
+			},
+			DefaultCurves: []CurveID{},
+		},
+		shouldFail:    true,
+		expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+	})
+
+	// Test that skipping early data looking for cleartext correctly
+	// processes an alert record.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-HRR-FatalAlert",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyAlert:      true,
+				SendEarlyDataLength: 4,
+			},
+			DefaultCurves: []CurveID{},
+		},
+		shouldFail:    true,
+		expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipEarlyData-SecondClientHelloEarlyData",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendEarlyDataOnSecondClientHello: true,
+			},
+			DefaultCurves: []CurveID{},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: bad record MAC",
+	})
+
+	testCases = append(testCases, testCase{
 		testType: clientTest,
 		name:     "EmptyEncryptedExtensions",
 		config: Config{
@@ -9364,7 +9530,8 @@
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
-				ExtraPSKIdentity: true,
+				ExtraPSKIdentity:   true,
+				SendExtraPSKBinder: true,
 			},
 		},
 		resumeSession: true,
@@ -9559,6 +9726,8 @@
 			testType: clientTest,
 			name:     "SendReceiveIntermediate-Client-" + ver.name,
 			config: Config{
+				MinVersion:   ver.version,
+				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaChainCertificate},
 				ClientAuth:   RequireAnyClientCert,
 			},
@@ -9574,6 +9743,8 @@
 			testType: serverTest,
 			name:     "SendReceiveIntermediate-Server-" + ver.name,
 			config: Config{
+				MinVersion:   ver.version,
+				MaxVersion:   ver.version,
 				Certificates: []Certificate{rsaChainCertificate},
 			},
 			expectPeerCertificate: &rsaChainCertificate,
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 940e676..9b9e20c 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -97,7 +97,6 @@
   { "-renegotiate-once", &TestConfig::renegotiate_once },
   { "-renegotiate-freely", &TestConfig::renegotiate_freely },
   { "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
-  { "-disable-npn", &TestConfig::disable_npn },
   { "-p384-only", &TestConfig::p384_only },
   { "-enable-all-curves", &TestConfig::enable_all_curves },
   { "-use-sparse-dh-prime", &TestConfig::use_sparse_dh_prime },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index ed1a47b..76cd5fb 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -102,7 +102,6 @@
   bool renegotiate_once = false;
   bool renegotiate_freely = false;
   bool renegotiate_ignore = false;
-  bool disable_npn = false;
   int expect_peer_signature_algorithm = 0;
   bool p384_only = false;
   bool enable_all_curves = false;
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index 17f7161..ea3eb77 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -34,9 +34,8 @@
  * without being able to return application data. */
 static const uint8_t kMaxKeyUpdates = 32;
 
-int tls13_handshake(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+int tls13_handshake(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   for (;;) {
     /* Resolve the operation the handshake was waiting on. */
     switch (hs->wait) {
@@ -95,7 +94,7 @@
     }
 
     /* Run the state machine again. */
-    hs->wait = hs->do_handshake(ssl);
+    hs->wait = hs->do_tls13_handshake(hs);
     if (hs->wait == ssl_hs_error) {
       /* Don't loop around to avoid a stray |SSL_R_SSL_HANDSHAKE_FAILURE| the
        * first time around. */
@@ -402,10 +401,11 @@
   return 1;
 }
 
-int tls13_process_finished(SSL *ssl) {
+int tls13_process_finished(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
   size_t verify_data_len;
-  if (!tls13_finished_mac(ssl, verify_data, &verify_data_len, !ssl->server)) {
+  if (!tls13_finished_mac(hs, verify_data, &verify_data_len, !ssl->server)) {
     return 0;
   }
 
@@ -424,7 +424,8 @@
   return 1;
 }
 
-int tls13_prepare_certificate(SSL *ssl) {
+int tls13_prepare_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   CBB cbb, body, certificate_list;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
       /* The request context is always empty in the handshake. */
@@ -451,7 +452,7 @@
     goto err;
   }
 
-  if (ssl->s3->hs->scts_requested &&
+  if (hs->scts_requested &&
       ssl->ctx->signed_cert_timestamp_list_length != 0) {
     CBB contents;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
@@ -464,7 +465,7 @@
     }
   }
 
-  if (ssl->s3->hs->ocsp_stapling_requested &&
+  if (hs->ocsp_stapling_requested &&
       ssl->ctx->ocsp_response_length != 0) {
     CBB contents, ocsp_response;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) ||
@@ -501,7 +502,8 @@
 }
 
 enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL *ssl, int is_first_run) {
+    SSL_HANDSHAKE *hs, int is_first_run) {
+  SSL *const ssl = hs->ssl;
   enum ssl_private_key_result_t ret = ssl_private_key_failure;
   uint8_t *msg = NULL;
   size_t msg_len;
@@ -509,7 +511,7 @@
   CBB_zero(&cbb);
 
   uint16_t signature_algorithm;
-  if (!tls1_choose_signature_algorithm(ssl, &signature_algorithm)) {
+  if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
     goto err;
   }
   if (!ssl->method->init_message(ssl, &cbb, &body,
@@ -562,11 +564,12 @@
   return ret;
 }
 
-int tls13_prepare_finished(SSL *ssl) {
+int tls13_prepare_finished(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   size_t verify_data_len;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
 
-  if (!tls13_finished_mac(ssl, verify_data, &verify_data_len, ssl->server)) {
+  if (!tls13_finished_mac(hs, verify_data, &verify_data_len, ssl->server)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     return 0;
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 4a30ce3..7a09fb1 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -48,8 +48,10 @@
   state_done,
 };
 
-static enum ssl_hs_wait_t do_process_hello_retry_request(SSL *ssl,
-                                                         SSL_HANDSHAKE *hs) {
+static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+
+static enum ssl_hs_wait_t do_process_hello_retry_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
     hs->state = state_process_server_hello;
     return ssl_hs_ok;
@@ -140,9 +142,8 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_send_second_client_hello(SSL *ssl,
-                                                      SSL_HANDSHAKE *hs) {
-  if (!ssl_write_client_hello(ssl)) {
+static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) {
+  if (!ssl_write_client_hello(hs)) {
     return ssl_hs_error;
   }
 
@@ -150,13 +151,13 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_flush_second_client_hello(SSL *ssl,
-                                                       SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_flush_second_client_hello(SSL_HANDSHAKE *hs) {
   hs->state = state_process_server_hello;
   return ssl_hs_flush_and_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_server_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
     return ssl_hs_error;
   }
@@ -229,7 +230,7 @@
       return ssl_hs_error;
     }
 
-    if (!ssl_ext_pre_shared_key_parse_serverhello(ssl, &alert,
+    if (!ssl_ext_pre_shared_key_parse_serverhello(hs, &alert,
                                                   &pre_shared_key)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
@@ -264,7 +265,7 @@
       return ssl_hs_error;
     }
     ssl_set_session(ssl, NULL);
-  } else if (!ssl_get_new_session(ssl, 0)) {
+  } else if (!ssl_get_new_session(hs, 0)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
@@ -275,33 +276,30 @@
   /* The PRF hash is now known. Set up the key schedule. */
   size_t hash_len =
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
-
-  /* Derive resumption material. */
-  uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
-  if (ssl->s3->session_reused) {
-    if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
-      return ssl_hs_error;
-    }
-    memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
+  if (!tls13_init_key_schedule(hs)) {
+    return ssl_hs_error;
   }
 
-  /* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
-   * into the running secret. */
-  if (!tls13_init_key_schedule(ssl) ||
-      !tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
+  /* Incorporate the PSK into the running secret. */
+  if (ssl->s3->session_reused) {
+    if (!tls13_advance_key_schedule(hs, ssl->s3->new_session->master_key,
+                                    ssl->s3->new_session->master_key_length)) {
+      return ssl_hs_error;
+    }
+  } else if (!tls13_advance_key_schedule(hs, kZeroes, hash_len)) {
     return ssl_hs_error;
   }
 
   /* Resolve ECDHE and incorporate it into the secret. */
   uint8_t *dhe_secret;
   size_t dhe_secret_len;
-  if (!ssl_ext_key_share_parse_serverhello(ssl, &dhe_secret, &dhe_secret_len,
+  if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &dhe_secret_len,
                                            &alert, &key_share)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  if (!tls13_advance_key_schedule(ssl, dhe_secret, dhe_secret_len)) {
+  if (!tls13_advance_key_schedule(hs, dhe_secret, dhe_secret_len)) {
     OPENSSL_free(dhe_secret);
     return ssl_hs_error;
   }
@@ -314,7 +312,7 @@
     return ssl_hs_error;
   }
 
-  if (!tls13_set_handshake_traffic(ssl)) {
+  if (!tls13_set_handshake_traffic(hs)) {
     return ssl_hs_error;
   }
 
@@ -322,15 +320,15 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL *ssl,
-                                                          SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
     return ssl_hs_error;
   }
 
   CBS cbs;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!ssl_parse_serverhello_tlsext(ssl, &cbs)) {
+  if (!ssl_parse_serverhello_tlsext(hs, &cbs)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
@@ -348,8 +346,8 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_certificate_request(SSL *ssl,
-                                                         SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_certificate_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* CertificateRequest may only be sent in non-resumption handshakes. */
   if (ssl->s3->session_reused) {
     hs->state = state_process_server_finished;
@@ -369,7 +367,7 @@
       CBS_len(&context) != 0 ||
       !CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
       CBS_len(&supported_signature_algorithms) == 0 ||
-      !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
+      !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
@@ -392,9 +390,9 @@
     return ssl_hs_error;
   }
 
-  ssl->s3->hs->cert_request = 1;
-  sk_X509_NAME_pop_free(ssl->s3->hs->ca_names, X509_NAME_free);
-  ssl->s3->hs->ca_names = ca_sk;
+  hs->cert_request = 1;
+  sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
+  hs->ca_names = ca_sk;
 
   if (!ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -404,8 +402,8 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_server_certificate(SSL *ssl,
-                                                        SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_server_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(ssl, 0 /* certificate required */) ||
       !ssl_hash_current_message(ssl)) {
@@ -417,7 +415,8 @@
 }
 
 static enum ssl_hs_wait_t do_process_server_certificate_verify(
-    SSL *ssl, SSL_HANDSHAKE *hs) {
+    SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(ssl) ||
       !ssl_hash_current_message(ssl)) {
@@ -428,15 +427,14 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_server_finished(SSL *ssl,
-                                                     SSL_HANDSHAKE *hs) {
-  static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+static enum ssl_hs_wait_t do_process_server_finished(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
-      !tls13_process_finished(ssl) ||
+      !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* Update the secret to the master secret and derive traffic keys. */
-      !tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
-      !tls13_derive_application_secrets(ssl)) {
+      !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
+      !tls13_derive_application_secrets(hs)) {
     return ssl_hs_error;
   }
 
@@ -445,9 +443,10 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_certificate_callback(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_certificate_callback(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* The peer didn't request a certificate. */
-  if (!ssl->s3->hs->cert_request) {
+  if (!hs->cert_request) {
     hs->state = state_send_channel_id;
     return ssl_hs_ok;
   }
@@ -470,8 +469,8 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_send_client_certificate(SSL *ssl,
-                                                     SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_client_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* Call client_cert_cb to update the certificate. */
   int should_retry;
   if (!ssl_do_client_cert_cb(ssl, &should_retry)) {
@@ -482,7 +481,7 @@
     return ssl_hs_error;
   }
 
-  if (!tls13_prepare_certificate(ssl)) {
+  if (!tls13_prepare_certificate(hs)) {
     return ssl_hs_error;
   }
 
@@ -490,16 +489,16 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL *ssl,
-                                                            SSL_HANDSHAKE *hs,
+static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs,
                                                             int is_first_run) {
+  SSL *const ssl = hs->ssl;
   /* Don't send CertificateVerify if there is no certificate. */
   if (!ssl_has_certificate(ssl)) {
     hs->state = state_send_channel_id;
     return ssl_hs_ok;
   }
 
-  switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
+  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
       hs->state = state_send_channel_id;
       return ssl_hs_write_message;
@@ -516,7 +515,8 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->s3->tlsext_channel_id_valid) {
     hs->state = state_send_client_finished;
     return ssl_hs_ok;
@@ -542,8 +542,8 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_send_client_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(ssl)) {
+static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) {
+  if (!tls13_prepare_finished(hs)) {
     return ssl_hs_error;
   }
 
@@ -551,12 +551,13 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0,
                              hs->hash_len) ||
-      !tls13_derive_resumption_secret(ssl)) {
+      !tls13_derive_resumption_secret(hs)) {
     return ssl_hs_error;
   }
 
@@ -564,60 +565,58 @@
   return ssl_hs_flush;
 }
 
-enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs) {
   while (hs->state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
     enum client_hs_state_t state = hs->state;
     switch (state) {
       case state_process_hello_retry_request:
-        ret = do_process_hello_retry_request(ssl, hs);
+        ret = do_process_hello_retry_request(hs);
         break;
       case state_send_second_client_hello:
-        ret = do_send_second_client_hello(ssl, hs);
+        ret = do_send_second_client_hello(hs);
         break;
       case state_flush_second_client_hello:
-        ret = do_flush_second_client_hello(ssl, hs);
+        ret = do_flush_second_client_hello(hs);
         break;
       case state_process_server_hello:
-        ret = do_process_server_hello(ssl, hs);
+        ret = do_process_server_hello(hs);
         break;
       case state_process_encrypted_extensions:
-        ret = do_process_encrypted_extensions(ssl, hs);
+        ret = do_process_encrypted_extensions(hs);
         break;
       case state_process_certificate_request:
-        ret = do_process_certificate_request(ssl, hs);
+        ret = do_process_certificate_request(hs);
         break;
       case state_process_server_certificate:
-        ret = do_process_server_certificate(ssl, hs);
+        ret = do_process_server_certificate(hs);
         break;
       case state_process_server_certificate_verify:
-        ret = do_process_server_certificate_verify(ssl, hs);
+        ret = do_process_server_certificate_verify(hs);
         break;
       case state_process_server_finished:
-        ret = do_process_server_finished(ssl, hs);
+        ret = do_process_server_finished(hs);
         break;
       case state_certificate_callback:
-        ret = do_certificate_callback(ssl, hs);
+        ret = do_certificate_callback(hs);
         break;
       case state_send_client_certificate:
-        ret = do_send_client_certificate(ssl, hs);
+        ret = do_send_client_certificate(hs);
         break;
       case state_send_client_certificate_verify:
-        ret = do_send_client_certificate_verify(ssl, hs, 1 /* first run */);
+        ret = do_send_client_certificate_verify(hs, 1 /* first run */);
         break;
       case state_complete_client_certificate_verify:
-        ret = do_send_client_certificate_verify(ssl, hs, 0 /* complete */);
+        ret = do_send_client_certificate_verify(hs, 0 /* complete */);
         break;
       case state_send_channel_id:
-        ret = do_send_channel_id(ssl, hs);
+        ret = do_send_channel_id(hs);
         break;
       case state_send_client_finished:
-        ret = do_send_client_finished(ssl, hs);
+        ret = do_send_client_finished(hs);
         break;
       case state_flush:
-        ret = do_flush(ssl, hs);
+        ret = do_flush(hs);
         break;
       case state_done:
         ret = ssl_hs_ok;
@@ -669,10 +668,10 @@
   return 1;
 }
 
-void ssl_clear_tls13_state(SSL *ssl) {
-  SSL_ECDH_CTX_cleanup(&ssl->s3->hs->ecdh_ctx);
+void ssl_clear_tls13_state(SSL_HANDSHAKE *hs) {
+  SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
 
-  OPENSSL_free(ssl->s3->hs->key_share_bytes);
-  ssl->s3->hs->key_share_bytes = NULL;
-  ssl->s3->hs->key_share_bytes_len = 0;
+  OPENSSL_free(hs->key_share_bytes);
+  hs->key_share_bytes = NULL;
+  hs->key_share_bytes_len = 0;
 }
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index d87d8a6..4fca65b 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -27,8 +27,8 @@
 #include "internal.h"
 
 
-int tls13_init_key_schedule(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+int tls13_init_key_schedule(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
   hs->hash_len = EVP_MD_size(digest);
@@ -44,9 +44,10 @@
   return 1;
 }
 
-int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
+                               size_t len) {
+  const EVP_MD *digest =
+      ssl_get_handshake_digest(ssl_get_algorithm_prf(hs->ssl));
 
   return HKDF_extract(hs->secret, &hs->hash_len, digest, in, len, hs->secret,
                       hs->hash_len);
@@ -97,9 +98,9 @@
 /* derive_secret derives a secret of length |len| and writes the result in |out|
  * with the given label and the current base secret and most recently-saved
  * handshake context. It returns one on success and zero on error. */
-static int derive_secret(SSL *ssl, uint8_t *out, size_t len,
+static int derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len,
                          const uint8_t *label, size_t label_len) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  SSL *const ssl = hs->ssl;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
@@ -184,17 +185,16 @@
 static const char kTLS13LabelServerApplicationTraffic[] =
     "server application traffic secret";
 
-int tls13_set_handshake_traffic(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+int tls13_set_handshake_traffic(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   uint8_t client_traffic_secret[EVP_MAX_MD_SIZE];
   uint8_t server_traffic_secret[EVP_MAX_MD_SIZE];
-  if (!derive_secret(ssl, client_traffic_secret, hs->hash_len,
+  if (!derive_secret(hs, client_traffic_secret, hs->hash_len,
                      (const uint8_t *)kTLS13LabelClientHandshakeTraffic,
                      strlen(kTLS13LabelClientHandshakeTraffic)) ||
       !ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
                       client_traffic_secret, hs->hash_len) ||
-      !derive_secret(ssl, server_traffic_secret, hs->hash_len,
+      !derive_secret(hs, server_traffic_secret, hs->hash_len,
                      (const uint8_t *)kTLS13LabelServerHandshakeTraffic,
                      strlen(kTLS13LabelServerHandshakeTraffic)) ||
       !ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET",
@@ -222,21 +222,20 @@
 
 static const char kTLS13LabelExporter[] = "exporter master secret";
 
-int tls13_derive_application_secrets(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+int tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   ssl->s3->exporter_secret_len = hs->hash_len;
-  return derive_secret(ssl, hs->client_traffic_secret_0, hs->hash_len,
+  return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len,
                        (const uint8_t *)kTLS13LabelClientApplicationTraffic,
                        strlen(kTLS13LabelClientApplicationTraffic)) &&
          ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0",
                         hs->client_traffic_secret_0, hs->hash_len) &&
-         derive_secret(ssl, hs->server_traffic_secret_0, hs->hash_len,
+         derive_secret(hs, hs->server_traffic_secret_0, hs->hash_len,
                        (const uint8_t *)kTLS13LabelServerApplicationTraffic,
                        strlen(kTLS13LabelServerApplicationTraffic)) &&
          ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
                         hs->server_traffic_secret_0, hs->hash_len) &&
-         derive_secret(ssl, ssl->s3->exporter_secret, hs->hash_len,
+         derive_secret(hs, ssl->s3->exporter_secret, hs->hash_len,
                        (const uint8_t *)kTLS13LabelExporter,
                        strlen(kTLS13LabelExporter));
 }
@@ -269,9 +268,15 @@
 
 static const char kTLS13LabelResumption[] = "resumption master secret";
 
-int tls13_derive_resumption_secret(SSL *ssl) {
-  ssl->s3->new_session->master_key_length = ssl->s3->hs->hash_len;
-  return derive_secret(ssl, ssl->s3->new_session->master_key,
+int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (ssl->s3->hs->hash_len > SSL_MAX_MASTER_KEY_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  ssl->s3->new_session->master_key_length = hs->hash_len;
+  return derive_secret(hs, ssl->s3->new_session->master_key,
                        ssl->s3->new_session->master_key_length,
                        (const uint8_t *)kTLS13LabelResumption,
                        strlen(kTLS13LabelResumption));
@@ -297,8 +302,9 @@
   return 1;
 }
 
-int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
+int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
+                       int is_server) {
+  SSL *const ssl = hs->ssl;
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
   const uint8_t *traffic_secret;
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 83ef679..626d2ee 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -53,14 +53,15 @@
 
 static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
 
-static int resolve_ecdhe_secret(SSL *ssl, int *out_need_retry,
-                                struct ssl_early_callback_ctx *early_ctx) {
+static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, int *out_need_retry,
+                                SSL_CLIENT_HELLO *client_hello) {
+  SSL *const ssl = hs->ssl;
   *out_need_retry = 0;
 
   /* We only support connections that include an ECDHE key exchange. */
   CBS key_share;
-  if (!ssl_early_callback_get_extension(early_ctx, &key_share,
-                                        TLSEXT_TYPE_key_share)) {
+  if (!ssl_client_hello_get_extension(client_hello, &key_share,
+                                      TLSEXT_TYPE_key_share)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
     return 0;
@@ -70,7 +71,7 @@
   uint8_t *dhe_secret;
   size_t dhe_secret_len;
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_ext_key_share_parse_clienthello(ssl, &found_key_share, &dhe_secret,
+  if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &dhe_secret,
                                            &dhe_secret_len, &alert,
                                            &key_share)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -82,19 +83,20 @@
     return 0;
   }
 
-  int ok = tls13_advance_key_schedule(ssl, dhe_secret, dhe_secret_len);
+  int ok = tls13_advance_key_schedule(hs, dhe_secret, dhe_secret_len);
   OPENSSL_free(dhe_secret);
   return ok;
 }
 
-static enum ssl_hs_wait_t do_process_client_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
     return ssl_hs_error;
   }
 
-  struct ssl_early_callback_ctx client_hello;
-  if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
-                               ssl->init_num)) {
+  SSL_CLIENT_HELLO client_hello;
+  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+                             ssl->init_num)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -118,7 +120,7 @@
   }
 
   /* TLS extensions. */
-  if (!ssl_parse_clienthello_tlsext(ssl, &client_hello)) {
+  if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
@@ -128,7 +130,7 @@
 }
 
 static const SSL_CIPHER *choose_tls13_cipher(
-    const SSL *ssl, const struct ssl_early_callback_ctx *client_hello) {
+    const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) {
   if (client_hello->cipher_suites_len % 2 != 0) {
     return NULL;
   }
@@ -173,7 +175,8 @@
   return best;
 }
 
-static enum ssl_hs_wait_t do_select_parameters(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* Call |cert_cb| to update server certificates if required. */
   if (ssl->cert->cert_cb != NULL) {
     int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -188,9 +191,9 @@
     }
   }
 
-  struct ssl_early_callback_ctx client_hello;
-  if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
-                               ssl->init_num)) {
+  SSL_CLIENT_HELLO client_hello;
+  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+                             ssl->init_num)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -209,8 +212,8 @@
   SSL_SESSION *session = NULL;
   CBS pre_shared_key, binders;
   if (hs->accept_psk_mode &&
-      ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
-                                       TLSEXT_TYPE_pre_shared_key)) {
+      ssl_client_hello_get_extension(&client_hello, &pre_shared_key,
+                                     TLSEXT_TYPE_pre_shared_key)) {
     /* Verify that the pre_shared_key extension is the last extension in
      * ClientHello. */
     if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) !=
@@ -220,7 +223,7 @@
       return ssl_hs_error;
     }
 
-    if (!ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &binders,
+    if (!ssl_ext_pre_shared_key_parse_clienthello(hs, &session, &binders,
                                                   &alert, &pre_shared_key)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
@@ -236,7 +239,7 @@
   /* Set up the new session, either using the original one as a template or
    * creating a fresh one. */
   if (session == NULL) {
-    if (!ssl_get_new_session(ssl, 1 /* server */)) {
+    if (!ssl_get_new_session(hs, 1 /* server */)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
@@ -244,8 +247,8 @@
     ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
 
     /* On new sessions, stash the SNI value in the session. */
-    if (ssl->s3->hs->hostname != NULL) {
-      ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+    if (hs->hostname != NULL) {
+      ssl->s3->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
       if (ssl->s3->new_session->tlsext_hostname == NULL) {
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return ssl_hs_error;
@@ -279,28 +282,25 @@
 
   /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
    * deferred. Complete it now. */
-  if (!ssl_negotiate_alpn(ssl, &alert, &client_hello)) {
+  if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  /* The PRF hash is now known. */
+  /* The PRF hash is now known. Set up the key schedule. */
   size_t hash_len =
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
-
-  /* Derive resumption material. */
-  uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
-  if (ssl->s3->session_reused) {
-    if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
-      return ssl_hs_error;
-    }
-    memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
+  if (!tls13_init_key_schedule(hs)) {
+    return ssl_hs_error;
   }
 
-  /* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
-   * into the running secret. */
-  if (!tls13_init_key_schedule(ssl) ||
-      !tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
+  /* Incorporate the PSK into the running secret. */
+  if (ssl->s3->session_reused) {
+    if (!tls13_advance_key_schedule(hs, ssl->s3->new_session->master_key,
+                                    ssl->s3->new_session->master_key_length)) {
+      return ssl_hs_error;
+    }
+  } else if (!tls13_advance_key_schedule(hs, kZeroes, hash_len)) {
     return ssl_hs_error;
   }
 
@@ -308,7 +308,7 @@
 
   /* Resolve ECDHE and incorporate it into the secret. */
   int need_retry;
-  if (!resolve_ecdhe_secret(ssl, &need_retry, &client_hello)) {
+  if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
     if (need_retry) {
       hs->state = state_send_hello_retry_request;
       return ssl_hs_ok;
@@ -320,14 +320,14 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_send_hello_retry_request(SSL *ssl,
-                                                      SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   CBB cbb, body, extensions;
   uint16_t group_id;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_HELLO_RETRY_REQUEST) ||
       !CBB_add_u16(&body, ssl->version) ||
-      !tls1_get_shared_group(ssl, &group_id) ||
+      !tls1_get_shared_group(hs, &group_id) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
@@ -341,28 +341,27 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_flush_hello_retry_request(SSL *ssl,
-                                                       SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_flush_hello_retry_request(SSL_HANDSHAKE *hs) {
   hs->state = state_process_second_client_hello;
   return ssl_hs_flush_and_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_second_client_hello(SSL *ssl,
-                                                      SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_second_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
     return ssl_hs_error;
   }
 
-  struct ssl_early_callback_ctx client_hello;
-  if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
-                               ssl->init_num)) {
+  SSL_CLIENT_HELLO client_hello;
+  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+                             ssl->init_num)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
   int need_retry;
-  if (!resolve_ecdhe_secret(ssl, &need_retry, &client_hello)) {
+  if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
     if (need_retry) {
       /* Only send one HelloRetryRequest. */
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -380,7 +379,8 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_send_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   CBB cbb, body, extensions;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
       !CBB_add_u16(&body, ssl->version) ||
@@ -388,8 +388,8 @@
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
-      !ssl_ext_pre_shared_key_add_serverhello(ssl, &extensions) ||
-      !ssl_ext_key_share_add_serverhello(ssl, &extensions) ||
+      !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
+      !ssl_ext_key_share_add_serverhello(hs, &extensions) ||
       !ssl_complete_message(ssl, &cbb)) {
     goto err;
   }
@@ -402,16 +402,16 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL *ssl,
-                                                       SSL_HANDSHAKE *hs) {
-  if (!tls13_set_handshake_traffic(ssl)) {
+static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!tls13_set_handshake_traffic(hs)) {
     return ssl_hs_error;
   }
 
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_ENCRYPTED_EXTENSIONS) ||
-      !ssl_add_serverhello_tlsext(ssl, &body) ||
+      !ssl_add_serverhello_tlsext(hs, &body) ||
       !ssl_complete_message(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return ssl_hs_error;
@@ -421,16 +421,16 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_send_certificate_request(SSL *ssl,
-                                                      SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_certificate_request(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* Determine whether to request a client certificate. */
-  ssl->s3->hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+  hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
   /* CertificateRequest may only be sent in non-resumption handshakes. */
   if (ssl->s3->session_reused) {
-    ssl->s3->hs->cert_request = 0;
+    hs->cert_request = 0;
   }
 
-  if (!ssl->s3->hs->cert_request) {
+  if (!hs->cert_request) {
     /* Skip this state. */
     hs->state = state_send_server_certificate;
     return ssl_hs_ok;
@@ -469,8 +469,8 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_server_certificate(SSL *ssl,
-                                                     SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->s3->session_reused) {
     hs->state = state_send_server_finished;
     return ssl_hs_ok;
@@ -481,7 +481,7 @@
     return ssl_hs_error;
   }
 
-  if (!tls13_prepare_certificate(ssl)) {
+  if (!tls13_prepare_certificate(hs)) {
     return ssl_hs_error;
   }
 
@@ -489,10 +489,9 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL *ssl,
-                                                            SSL_HANDSHAKE *hs,
+static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs,
                                                             int is_first_run) {
-  switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
+  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
       hs->state = state_send_server_finished;
       return ssl_hs_write_message;
@@ -509,8 +508,8 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_server_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(ssl)) {
+static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
+  if (!tls13_prepare_finished(hs)) {
     return ssl_hs_error;
   }
 
@@ -518,10 +517,11 @@
   return ssl_hs_write_message;
 }
 
-static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* Update the secret to the master secret and derive traffic keys. */
-  if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
-      !tls13_derive_application_secrets(ssl) ||
+  if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
+      !tls13_derive_application_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
                              hs->hash_len)) {
     return ssl_hs_error;
@@ -531,9 +531,9 @@
   return ssl_hs_flush_and_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_client_certificate(SSL *ssl,
-                                                        SSL_HANDSHAKE *hs) {
-  if (!ssl->s3->hs->cert_request) {
+static enum ssl_hs_wait_t do_process_client_certificate(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!hs->cert_request) {
     /* OpenSSL returns X509_V_OK when no certificates are requested. This is
      * classed by them as a bug, but it's assumed by at least NGINX. */
     ssl->s3->new_session->verify_result = X509_V_OK;
@@ -563,7 +563,8 @@
 }
 
 static enum ssl_hs_wait_t do_process_client_certificate_verify(
-    SSL *ssl, SSL_HANDSHAKE *hs) {
+    SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (ssl->s3->new_session->x509_peer == NULL) {
     /* Skip this state. */
     hs->state = state_process_channel_id;
@@ -580,7 +581,8 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!ssl->s3->tlsext_channel_id_valid) {
     hs->state = state_process_client_finished;
     return ssl_hs_ok;
@@ -596,15 +598,15 @@
   return ssl_hs_read_message;
 }
 
-static enum ssl_hs_wait_t do_process_client_finished(SSL *ssl,
-                                                     SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_process_client_finished(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
-      !tls13_process_finished(ssl) ||
+      !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* evp_aead_seal keys have already been switched. */
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
                              hs->hash_len) ||
-      !tls13_derive_resumption_secret(ssl)) {
+      !tls13_derive_resumption_secret(hs)) {
     return ssl_hs_error;
   }
 
@@ -621,8 +623,8 @@
  * client makes several connections before getting a renewal. */
 static const int kNumTickets = 2;
 
-static enum ssl_hs_wait_t do_send_new_session_ticket(SSL *ssl,
-                                                     SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_send_new_session_ticket(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
   /* If the client doesn't accept resumption with PSK_DHE_KE, don't send a
    * session ticket. */
   if (!hs->accept_psk_mode) {
@@ -674,75 +676,72 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL *ssl,
-                                                       SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL_HANDSHAKE *hs) {
   hs->state = state_done;
   return ssl_hs_flush;
 }
 
-enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl) {
-  SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
   while (hs->state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
     enum server_hs_state_t state = hs->state;
     switch (state) {
       case state_process_client_hello:
-        ret = do_process_client_hello(ssl, hs);
+        ret = do_process_client_hello(hs);
         break;
       case state_select_parameters:
-        ret = do_select_parameters(ssl, hs);
+        ret = do_select_parameters(hs);
         break;
       case state_send_hello_retry_request:
-        ret = do_send_hello_retry_request(ssl, hs);
+        ret = do_send_hello_retry_request(hs);
         break;
       case state_flush_hello_retry_request:
-        ret = do_flush_hello_retry_request(ssl, hs);
+        ret = do_flush_hello_retry_request(hs);
         break;
       case state_process_second_client_hello:
-        ret = do_process_second_client_hello(ssl, hs);
+        ret = do_process_second_client_hello(hs);
         break;
       case state_send_server_hello:
-        ret = do_send_server_hello(ssl, hs);
+        ret = do_send_server_hello(hs);
         break;
       case state_send_encrypted_extensions:
-        ret = do_send_encrypted_extensions(ssl, hs);
+        ret = do_send_encrypted_extensions(hs);
         break;
       case state_send_certificate_request:
-        ret = do_send_certificate_request(ssl, hs);
+        ret = do_send_certificate_request(hs);
         break;
       case state_send_server_certificate:
-        ret = do_send_server_certificate(ssl, hs);
+        ret = do_send_server_certificate(hs);
         break;
       case state_send_server_certificate_verify:
-        ret = do_send_server_certificate_verify(ssl, hs, 1 /* first run */);
+        ret = do_send_server_certificate_verify(hs, 1 /* first run */);
       break;
       case state_complete_server_certificate_verify:
-        ret = do_send_server_certificate_verify(ssl, hs, 0 /* complete */);
+        ret = do_send_server_certificate_verify(hs, 0 /* complete */);
       break;
       case state_send_server_finished:
-        ret = do_send_server_finished(ssl, hs);
+        ret = do_send_server_finished(hs);
         break;
       case state_flush:
-        ret = do_flush(ssl, hs);
+        ret = do_flush(hs);
         break;
       case state_process_client_certificate:
-        ret = do_process_client_certificate(ssl, hs);
+        ret = do_process_client_certificate(hs);
         break;
       case state_process_client_certificate_verify:
-        ret = do_process_client_certificate_verify(ssl, hs);
+        ret = do_process_client_certificate_verify(hs);
         break;
       case state_process_channel_id:
-        ret = do_process_channel_id(ssl, hs);
+        ret = do_process_channel_id(hs);
         break;
       case state_process_client_finished:
-        ret = do_process_client_finished(ssl, hs);
+        ret = do_process_client_finished(hs);
         break;
       case state_send_new_session_ticket:
-        ret = do_send_new_session_ticket(ssl, hs);
+        ret = do_send_new_session_ticket(hs);
         break;
       case state_flush_new_session_tickets:
-        ret = do_flush_new_session_tickets(ssl, hs);
+        ret = do_flush_new_session_tickets(hs);
         break;
       case state_done:
         ret = ssl_hs_ok;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index ce42904..9effb36 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -97,6 +97,12 @@
   return 0;
 }
 
+static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
+
+static void ssl3_expect_flight(SSL *ssl) {}
+
+static void ssl3_received_flight(SSL *ssl) {}
+
 static int ssl3_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
   if (ssl->s3->rrec.length != 0) {
     /* There may not be unprocessed record data at a cipher change. */
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 5931922..c52909c 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -125,6 +125,12 @@
  * forever. */
 static const uint8_t kMaxEmptyRecords = 32;
 
+/* kMaxEarlyDataSkipped is the maximum amount of data processed when skipping
+ * over early data. Without this limit an attacker could send records at a
+ * faster rate than we can process and cause trial decryption to loop
+ * forever. */
+static const size_t kMaxEarlyDataSkipped = 16384;
+
 /* kMaxWarningAlerts is the number of consecutive warning alerts that will be
  * processed. */
 static const uint8_t kMaxWarningAlerts = 4;
@@ -246,15 +252,32 @@
   ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in,
                       SSL3_RT_HEADER_LENGTH);
 
+  *out_consumed = in_len - CBS_len(&cbs);
+
+  /* Skip early data received when expecting a second ClientHello if we rejected
+   * 0RTT. */
+  if (ssl->s3->skip_early_data &&
+      ssl->s3->aead_read_ctx == NULL &&
+      type == SSL3_RT_APPLICATION_DATA) {
+    goto skipped_data;
+  }
+
   /* Decrypt the body in-place. */
   if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, type, version,
                          ssl->s3->read_sequence, (uint8_t *)CBS_data(&body),
                          CBS_len(&body))) {
+    if (ssl->s3->skip_early_data &&
+        ssl->s3->aead_read_ctx != NULL) {
+      ERR_clear_error();
+      goto skipped_data;
+    }
+
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
     *out_alert = SSL_AD_BAD_RECORD_MAC;
     return ssl_open_record_error;
   }
-  *out_consumed = in_len - CBS_len(&cbs);
+
+  ssl->s3->skip_early_data = 0;
 
   if (!ssl_record_sequence_update(ssl->s3->read_sequence, 8)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -310,6 +333,20 @@
 
   *out_type = type;
   return ssl_open_record_success;
+
+skipped_data:
+  ssl->s3->early_data_skipped += *out_consumed;
+  if (ssl->s3->early_data_skipped < *out_consumed) {
+    ssl->s3->early_data_skipped = kMaxEarlyDataSkipped + 1;
+  }
+
+  if (ssl->s3->early_data_skipped > kMaxEarlyDataSkipped) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA);
+    *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+    return ssl_open_record_error;
+  }
+
+  return ssl_open_record_discard;
 }
 
 static int do_seal_record(SSL *ssl, uint8_t *out, size_t *out_len,
diff --git a/src/tool/server.cc b/src/tool/server.cc
index d0213e9..a049422 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -15,6 +15,7 @@
 #include <openssl/base.h>
 
 #include <openssl/err.h>
+#include <openssl/rand.h>
 #include <openssl/ssl.h>
 
 #include "internal.h"
@@ -40,7 +41,9 @@
     },
     {
       "-key", kOptionalArgument,
-      "Private-key file to use (default is server.pem)",
+      "PEM-encoded file containing the private key, leaf certificate and "
+      "optional certificate chain. A self-signed certificate is generated "
+      "at runtime if this argument is not provided.",
     },
     {
       "-ocsp-response", kOptionalArgument,
@@ -91,6 +94,49 @@
   return ret;
 }
 
+static bssl::UniquePtr<EVP_PKEY> MakeKeyPairForSelfSignedCert() {
+  bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  if (!ec_key || !EC_KEY_generate_key(ec_key.get())) {
+    fprintf(stderr, "Failed to generate key pair.\n");
+    return nullptr;
+  }
+  bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+  if (!evp_pkey || !EVP_PKEY_assign_EC_KEY(evp_pkey.get(), ec_key.release())) {
+    fprintf(stderr, "Failed to assign key pair.\n");
+    return nullptr;
+  }
+  return evp_pkey;
+}
+
+static bssl::UniquePtr<X509> MakeSelfSignedCert(EVP_PKEY *evp_pkey,
+                                                const int valid_days) {
+  bssl::UniquePtr<X509> x509(X509_new());
+  uint32_t serial;
+  RAND_bytes(reinterpret_cast<uint8_t*>(&serial), sizeof(serial));
+  ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), serial >> 1);
+  X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+  X509_gmtime_adj(X509_get_notAfter(x509.get()), 60 * 60 * 24 * valid_days);
+
+  X509_NAME* subject = X509_get_subject_name(x509.get());
+  X509_NAME_add_entry_by_txt(subject, "C", MBSTRING_ASC,
+                             reinterpret_cast<const uint8_t *>("US"), -1, -1,
+                             0);
+  X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_ASC,
+                             reinterpret_cast<const uint8_t *>("BoringSSL"), -1,
+                             -1, 0);
+  X509_set_issuer_name(x509.get(), subject);
+
+  if (!X509_set_pubkey(x509.get(), evp_pkey)) {
+    fprintf(stderr, "Failed to set public key.\n");
+    return nullptr;
+  }
+  if (!X509_sign(x509.get(), evp_pkey, EVP_sha256())) {
+    fprintf(stderr, "Failed to sign certificate.\n");
+    return nullptr;
+  }
+  return x509;
+}
+
 bool Server(const std::vector<std::string> &args) {
   if (!InitSocketLibrary()) {
     return false;
@@ -107,17 +153,34 @@
   SSL_CTX_set_options(ctx.get(), SSL_OP_NO_SSLv3);
 
   // Server authentication is required.
-  std::string key_file = "server.pem";
   if (args_map.count("-key") != 0) {
-    key_file = args_map["-key"];
-  }
-  if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_file.c_str(), SSL_FILETYPE_PEM)) {
-    fprintf(stderr, "Failed to load private key: %s\n", key_file.c_str());
-    return false;
-  }
-  if (!SSL_CTX_use_certificate_chain_file(ctx.get(), key_file.c_str())) {
-    fprintf(stderr, "Failed to load cert chain: %s\n", key_file.c_str());
-    return false;
+    std::string key_file = args_map["-key"];
+    if (!SSL_CTX_use_PrivateKey_file(ctx.get(), key_file.c_str(), SSL_FILETYPE_PEM)) {
+      fprintf(stderr, "Failed to load private key: %s\n", key_file.c_str());
+      return false;
+    }
+    if (!SSL_CTX_use_certificate_chain_file(ctx.get(), key_file.c_str())) {
+      fprintf(stderr, "Failed to load cert chain: %s\n", key_file.c_str());
+      return false;
+    }
+  } else {
+    bssl::UniquePtr<EVP_PKEY> evp_pkey = MakeKeyPairForSelfSignedCert();
+    if (!evp_pkey) {
+      return false;
+    }
+    bssl::UniquePtr<X509> cert =
+        MakeSelfSignedCert(evp_pkey.get(), 365 /* valid_days */);
+    if (!cert) {
+      return false;
+    }
+    if (!SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get())) {
+      fprintf(stderr, "Failed to set private key.\n");
+      return false;
+    }
+    if (!SSL_CTX_use_certificate(ctx.get(), cert.get())) {
+      fprintf(stderr, "Failed to set certificate.\n");
+      return false;
+    }
   }
 
   if (args_map.count("-cipher") != 0 &&
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index f16a9eb..5fd1058 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -654,6 +654,12 @@
                  kLegacyADLen, selected) ||
       !SpeedAEAD(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1",
                  kLegacyADLen, selected) ||
+#if !defined(OPENSSL_SMALL)
+      !SpeedAEAD(EVP_aead_aes_128_gcm_siv(), "AES-128-GCM-SIV", kTLSADLen,
+                 selected) ||
+      !SpeedAEAD(EVP_aead_aes_256_gcm_siv(), "AES-256-GCM-SIV", kTLSADLen,
+                 selected) ||
+#endif
       !SpeedHash(EVP_sha1(), "SHA-1", selected) ||
       !SpeedHash(EVP_sha256(), "SHA-256", selected) ||
       !SpeedHash(EVP_sha512(), "SHA-512", selected) ||
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index d2e39ce..5ffa5e8 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -8,6 +8,8 @@
 	["crypto/chacha/chacha_test"],
 	["crypto/cipher/aead_test", "aes-128-gcm", "crypto/cipher/test/aes_128_gcm_tests.txt"],
 	["crypto/cipher/aead_test", "aes-256-gcm", "crypto/cipher/test/aes_256_gcm_tests.txt"],
+	["crypto/cipher/aead_test", "aes-128-gcm-siv", "crypto/cipher/test/aes_128_gcm_siv_tests.txt"],
+	["crypto/cipher/aead_test", "aes-256-gcm-siv", "crypto/cipher/test/aes_256_gcm_siv_tests.txt"],
 	["crypto/cipher/aead_test", "chacha20-poly1305", "crypto/cipher/test/chacha20_poly1305_tests.txt"],
 	["crypto/cipher/aead_test", "chacha20-poly1305-old", "crypto/cipher/test/chacha20_poly1305_old_tests.txt"],
 	["crypto/cipher/aead_test", "aes-128-cbc-sha1-tls", "crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt"],
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index 0452d55..5036c0c 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -104,8 +104,6 @@
                 'boringssl/util/bot/update_clang.py',
     ],
   },
-  # TODO(davidben): Only extract archives when they've changed. Extracting perl
-  # on Windows is a significant part of the cycle time.
   {
     'name': 'cmake_linux64_extract',
     'pattern': '.',
diff --git a/src/util/bot/extract.py b/src/util/bot/extract.py
index 77603c0..e36ce99 100644
--- a/src/util/bot/extract.py
+++ b/src/util/bot/extract.py
@@ -15,6 +15,7 @@
 """Extracts archives."""
 
 
+import hashlib
 import optparse
 import os
 import os.path
@@ -78,6 +79,22 @@
     # Skip archives that weren't downloaded.
     return 0
 
+  with open(archive) as f:
+    sha256 = hashlib.sha256()
+    while True:
+      chunk = f.read(1024 * 1024)
+      if not chunk:
+        break
+      sha256.update(chunk)
+    digest = sha256.hexdigest()
+
+  stamp_path = os.path.join(output, ".boringssl_archive_digest")
+  if os.path.exists(stamp_path):
+    with open(stamp_path) as f:
+      if f.read().strip() == digest:
+        print "Already up-to-date."
+        return 0
+
   if archive.endswith('.zip'):
     entries = IterateZip(archive)
   elif archive.endswith('.tar.gz'):
@@ -129,9 +146,10 @@
   finally:
     entries.close()
 
-  if num_extracted % 100 == 0:
-    print "Done. Extracted %d files." % (num_extracted,)
+  with open(stamp_path, 'w') as f:
+    f.write(digest)
 
+  print "Done. Extracted %d files." % (num_extracted,)
   return 0