external/boringssl: Sync to b2ff2623a88a65fd4db42d3820f3d8c64e8ab180.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/6d50f475e319de153a43e1dba5a1beca95948c63..b2ff2623a88a65fd4db42d3820f3d8c64e8ab180

Change-Id: I649281e093369d99e863b4882a2ff6a5ad8a64d1
Test: ATP's cts/libcore/gce-net (go/gce-net)
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 7102769..afc3a39 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_library(
   ssl
 
+  bio_ssl.c
   custom_extensions.c
   handshake_server.c
   handshake_client.c
diff --git a/src/ssl/bio_ssl.c b/src/ssl/bio_ssl.c
new file mode 100644
index 0000000..ad8f5d8
--- /dev/null
+++ b/src/ssl/bio_ssl.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1995-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
+ */
+
+#include <openssl/ssl.h>
+
+#include <openssl/bio.h>
+
+
+static int ssl_read(BIO *bio, char *out, int outl) {
+  SSL *ssl = bio->ptr;
+  if (ssl == NULL) {
+    return 0;
+  }
+
+  BIO_clear_retry_flags(bio);
+
+  const int ret = SSL_read(ssl, out, outl);
+
+  switch (SSL_get_error(ssl, ret)) {
+    case SSL_ERROR_WANT_READ:
+      BIO_set_retry_read(bio);
+      break;
+
+    case SSL_ERROR_WANT_WRITE:
+      BIO_set_retry_write(bio);
+      break;
+
+    case SSL_ERROR_WANT_ACCEPT:
+      BIO_set_retry_special(bio);
+      bio->retry_reason = BIO_RR_ACCEPT;
+      break;
+
+    case SSL_ERROR_WANT_CONNECT:
+      BIO_set_retry_special(bio);
+      bio->retry_reason = BIO_RR_CONNECT;
+      break;
+
+    case SSL_ERROR_NONE:
+    case SSL_ERROR_SYSCALL:
+    case SSL_ERROR_SSL:
+    case SSL_ERROR_ZERO_RETURN:
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static int ssl_write(BIO *bio, const char *out, int outl) {
+  SSL *ssl = bio->ptr;
+  if (ssl == NULL) {
+    return 0;
+  }
+
+  BIO_clear_retry_flags(bio);
+
+  const int ret = SSL_write(ssl, out, outl);
+
+  switch (SSL_get_error(ssl, ret)) {
+    case SSL_ERROR_WANT_WRITE:
+      BIO_set_retry_write(bio);
+      break;
+
+    case SSL_ERROR_WANT_READ:
+      BIO_set_retry_read(bio);
+      break;
+
+    case SSL_ERROR_WANT_CONNECT:
+      BIO_set_retry_special(bio);
+      bio->retry_reason = BIO_RR_CONNECT;
+      break;
+
+    case SSL_ERROR_NONE:
+    case SSL_ERROR_SYSCALL:
+    case SSL_ERROR_SSL:
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static long ssl_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+  SSL *ssl = bio->ptr;
+  if (ssl == NULL && cmd != BIO_C_SET_SSL) {
+    return 0;
+  }
+
+  switch (cmd) {
+    case BIO_C_SET_SSL:
+      bio->shutdown = num;
+      bio->ptr = ptr;
+      bio->init = 1;
+      return 1;
+
+    case BIO_CTRL_GET_CLOSE:
+      return bio->shutdown;
+
+    case BIO_CTRL_SET_CLOSE:
+      bio->shutdown = num;
+      return 1;
+
+    case BIO_CTRL_WPENDING:
+      return BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr);
+
+    case BIO_CTRL_PENDING:
+      return SSL_pending(ssl);
+
+    case BIO_CTRL_FLUSH: {
+      BIO_clear_retry_flags(bio);
+      long ret = BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr);
+      BIO_copy_next_retry(bio);
+      return ret;
+    }
+
+    case BIO_CTRL_PUSH:
+    case BIO_CTRL_POP:
+    case BIO_CTRL_DUP:
+      return -1;
+
+    default:
+      return BIO_ctrl(SSL_get_rbio(ssl), cmd, num, ptr);
+  }
+}
+
+static int ssl_new(BIO *bio) {
+  return 1;
+}
+
+static int ssl_free(BIO *bio) {
+  SSL *ssl = bio->ptr;
+
+  if (ssl == NULL) {
+    return 1;
+  }
+
+  SSL_shutdown(ssl);
+  if (bio->shutdown) {
+    SSL_free(ssl);
+  }
+
+  return 1;
+}
+
+static long ssl_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+  SSL *ssl = bio->ptr;
+  if (ssl == NULL) {
+    return 0;
+  }
+
+  switch (cmd) {
+    case BIO_CTRL_SET_CALLBACK:
+      return -1;
+
+    default:
+      return BIO_callback_ctrl(SSL_get_rbio(ssl), cmd, fp);
+  }
+}
+
+static const BIO_METHOD ssl_method = {
+    BIO_TYPE_SSL, "SSL",    ssl_write, ssl_read, NULL,
+    NULL,         ssl_ctrl, ssl_new,   ssl_free, ssl_callback_ctrl,
+};
+
+const BIO_METHOD *BIO_f_ssl(void) { return &ssl_method; }
+
+long BIO_set_ssl(BIO *bio, SSL *ssl, int take_owership) {
+  return BIO_ctrl(bio, BIO_C_SET_SSL, take_owership, ssl);
+}
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index d3e4a92..48a5c54 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -122,6 +122,7 @@
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/type_check.h>
 #include <openssl/x509.h>
 
 #include "../crypto/internal.h"
@@ -311,9 +312,9 @@
   }
 
   /* Cross-epoch records are discarded, but we may receive out-of-order
-   * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec
-   * before the appropriate point in the handshake. Those must be silently
-   * discarded.
+   * application data between ChangeCipherSpec and Finished or a
+   * ChangeCipherSpec before the appropriate point in the handshake. Those must
+   * be silently discarded.
    *
    * However, only allow the out-of-order records in the correct epoch.
    * Application data must come in the encrypted epoch, and ChangeCipherSpec in
@@ -384,8 +385,8 @@
     assert(msg_len > 0);
 
     /* Copy the body into the fragment. */
-    OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, CBS_data(&body),
-           CBS_len(&body));
+    OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off,
+                   CBS_data(&body), CBS_len(&body));
     dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
   }
 
@@ -394,17 +395,11 @@
   return 1;
 }
 
-int dtls1_get_message(SSL *ssl, int msg_type,
-                      enum ssl_hash_message_t hash_message) {
+int dtls1_get_message(SSL *ssl) {
   if (ssl->s3->tmp.reuse_message) {
-    /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
-     * ssl_dont_hash_message would have to have been applied to the previous
-     * call. */
-    assert(hash_message == ssl_hash_message);
+    /* There must be a current message. */
     assert(ssl->init_msg != NULL);
-
     ssl->s3->tmp.reuse_message = 0;
-    hash_message = ssl_dont_hash_message;
   } else {
     dtls1_release_current_message(ssl, 0 /* don't free buffer */);
   }
@@ -429,15 +424,6 @@
   ssl->init_msg = frag->data + DTLS1_HM_HEADER_LENGTH;
   ssl->init_num = frag->msg_len;
 
-  if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    return -1;
-  }
-  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
-    return -1;
-  }
-
   ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, frag->data,
                       ssl->init_num + DTLS1_HM_HEADER_LENGTH);
   return 1;
@@ -507,219 +493,14 @@
 
 /* Sending handshake messages. */
 
-static void dtls1_update_mtu(SSL *ssl) {
-  /* TODO(davidben): What is this code doing and do we need it? */
-  if (ssl->d1->mtu < dtls1_min_mtu() &&
-      !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
-    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
-    if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
-      ssl->d1->mtu = (unsigned)mtu;
-    } else {
-      ssl->d1->mtu = kDefaultMTU;
-      BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
-    }
-  }
-
-  /* The MTU should be above the minimum now. */
-  assert(ssl->d1->mtu >= dtls1_min_mtu());
-}
-
-/* dtls1_max_record_size returns the maximum record body length that may be
- * written without exceeding the MTU. It accounts for any buffering installed on
- * the write BIO. If no record may be written, it returns zero. */
-static size_t dtls1_max_record_size(const SSL *ssl) {
-  size_t ret = ssl->d1->mtu;
-
-  size_t overhead = SSL_max_seal_overhead(ssl);
-  if (ret <= overhead) {
-    return 0;
-  }
-  ret -= overhead;
-
-  size_t pending = BIO_wpending(ssl->wbio);
-  if (ret <= pending) {
-    return 0;
-  }
-  ret -= pending;
-
-  return ret;
-}
-
-static int dtls1_write_change_cipher_spec(SSL *ssl,
-                                          enum dtls1_use_epoch_t use_epoch) {
-  dtls1_update_mtu(ssl);
-
-  /* During the handshake, wbio is buffered to pack messages together. Flush the
-   * buffer if the ChangeCipherSpec would not fit in a packet. */
-  if (dtls1_max_record_size(ssl) == 0) {
-    int ret = BIO_flush(ssl->wbio);
-    if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
-      return ret;
-    }
-  }
-
-  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
-  int ret =
-      dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                         sizeof(kChangeCipherSpec), use_epoch);
-  if (ret <= 0) {
-    return ret;
-  }
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
-                      kChangeCipherSpec, sizeof(kChangeCipherSpec));
-  return 1;
-}
-
-/* dtls1_do_handshake_write writes handshake message |in| using the given epoch,
- * starting |offset| bytes into the message body. It returns one on success. On
- * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body
- * that were successfully written. This may be used to retry the write
- * later. |in| must be a reassembled handshake message with the full DTLS
- * handshake header. */
-static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset,
-                                    const uint8_t *in, size_t offset,
-                                    size_t len,
-                                    enum dtls1_use_epoch_t use_epoch) {
-  dtls1_update_mtu(ssl);
-
-  int ret = -1;
-  CBB cbb;
-  CBB_zero(&cbb);
-  /* Allocate a temporary buffer to hold the message fragments to avoid
-   * clobbering the message. */
-  uint8_t *buf = OPENSSL_malloc(ssl->d1->mtu);
-  if (buf == NULL) {
-    goto err;
-  }
-
-  /* Although it may be sent as multiple fragments, a DTLS message must be sent
-   * serialized as a single fragment for purposes of |ssl_do_msg_callback| and
-   * the handshake hash. */
-  CBS cbs, body;
-  struct hm_header_st hdr;
-  CBS_init(&cbs, in, len);
-  if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
-      hdr.frag_off != 0 ||
-      hdr.frag_len != CBS_len(&body) ||
-      hdr.msg_len != CBS_len(&body) ||
-      !CBS_skip(&body, offset) ||
-      CBS_len(&cbs) != 0) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    goto err;
-  }
-
-  do {
-    /* During the handshake, wbio is buffered to pack messages together. Flush
-     * the buffer if there isn't enough room to make progress. */
-    if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
-      int flush_ret = BIO_flush(ssl->wbio);
-      if (flush_ret <= 0) {
-        ssl->rwstate = SSL_WRITING;
-        ret = flush_ret;
-        goto err;
-      }
-      assert(BIO_wpending(ssl->wbio) == 0);
-    }
-
-    size_t todo = dtls1_max_record_size(ssl);
-    if (todo < DTLS1_HM_HEADER_LENGTH + 1) {
-      /* To make forward progress, the MTU must, at minimum, fit the handshake
-       * header and one byte of handshake body. */
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
-      goto err;
-    }
-    todo -= DTLS1_HM_HEADER_LENGTH;
-
-    if (todo > CBS_len(&body)) {
-      todo = CBS_len(&body);
-    }
-    if (todo >= (1u << 24)) {
-      todo = (1u << 24) - 1;
-    }
-
-    size_t buf_len;
-    if (!CBB_init_fixed(&cbb, buf, ssl->d1->mtu) ||
-        !CBB_add_u8(&cbb, hdr.type) ||
-        !CBB_add_u24(&cbb, hdr.msg_len) ||
-        !CBB_add_u16(&cbb, hdr.seq) ||
-        !CBB_add_u24(&cbb, offset) ||
-        !CBB_add_u24(&cbb, todo) ||
-        !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
-        !CBB_finish(&cbb, NULL, &buf_len)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-
-    int write_ret =
-        dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, buf_len, use_epoch);
-    if (write_ret <= 0) {
-      ret = write_ret;
-      goto err;
-    }
-
-    if (!CBS_skip(&body, todo)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-    offset += todo;
-  } while (CBS_len(&body) != 0);
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, in, len);
-
-  ret = 1;
-
-err:
-  *out_offset = offset;
-  CBB_cleanup(&cbb);
-  OPENSSL_free(buf);
-  return ret;
-}
-
 void dtls_clear_outgoing_messages(SSL *ssl) {
   for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
     OPENSSL_free(ssl->d1->outgoing_messages[i].data);
     ssl->d1->outgoing_messages[i].data = NULL;
   }
   ssl->d1->outgoing_messages_len = 0;
-}
-
-/* dtls1_add_change_cipher_spec adds a ChangeCipherSpec to the current
- * handshake flight. */
-static int dtls1_add_change_cipher_spec(SSL *ssl) {
-  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
-  msg->data = NULL;
-  msg->len = 0;
-  msg->epoch = ssl->d1->w_epoch;
-  msg->is_ccs = 1;
-
-  ssl->d1->outgoing_messages_len++;
-  return 1;
-}
-
-static int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) {
-  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    OPENSSL_free(data);
-    return 0;
-  }
-
-  DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
-  msg->data = data;
-  msg->len = len;
-  msg->epoch = ssl->d1->w_epoch;
-  msg->is_ccs = 0;
-
-  ssl->d1->outgoing_messages_len++;
-  return 1;
+  ssl->d1->outgoing_written = 0;
+  ssl->d1->outgoing_offset = 0;
 }
 
 int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
@@ -752,36 +533,91 @@
   return 1;
 }
 
-int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
-  ssl3_update_handshake_hash(ssl, msg, len);
-
-  ssl->d1->handshake_write_seq++;
-  ssl->init_off = 0;
-  return dtls1_add_message(ssl, msg, len);
-}
-
-int dtls1_write_message(SSL *ssl) {
-  if (ssl->d1->outgoing_messages_len == 0) {
+/* add_outgoing adds a new handshake message or ChangeCipherSpec to the current
+ * outgoing flight. It returns one on success and zero on error. In both cases,
+ * it takes ownership of |data| and releases it with |OPENSSL_free| when
+ * done. */
+static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) {
+  OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT <
+                             (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
+                         outgoing_messages_len_is_too_small);
+  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
+    assert(0);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
+    OPENSSL_free(data);
+    return 0;
   }
 
-  const DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1];
-  if (msg->is_ccs) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
+  if (!is_ccs) {
+    ssl3_update_handshake_hash(ssl, data, len);
+    ssl->d1->handshake_write_seq++;
   }
 
-  size_t offset = ssl->init_off;
-  int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
-                                     dtls1_use_current_epoch);
-  ssl->init_off = offset;
-  return ret;
+  DTLS_OUTGOING_MESSAGE *msg =
+      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
+  msg->data = data;
+  msg->len = len;
+  msg->epoch = ssl->d1->w_epoch;
+  msg->is_ccs = is_ccs;
+
+  ssl->d1->outgoing_messages_len++;
+  return 1;
 }
 
-static int dtls1_retransmit_message(SSL *ssl,
-                                    const DTLS_OUTGOING_MESSAGE *msg) {
+int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) {
+  return add_outgoing(ssl, 0 /* handshake */, data, len);
+}
+
+int dtls1_add_change_cipher_spec(SSL *ssl) {
+  return add_outgoing(ssl, 1 /* ChangeCipherSpec */, NULL, 0);
+}
+
+int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
+  /* The |add_alert| path is only used for warning alerts for now, which DTLS
+   * never sends. This will be implemented later once closure alerts are
+   * converted. */
+  assert(0);
+  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  return 0;
+}
+
+/* dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above
+ * the minimum. */
+static void dtls1_update_mtu(SSL *ssl) {
+  /* TODO(davidben): No consumer implements |BIO_CTRL_DGRAM_SET_MTU| and the
+   * only |BIO_CTRL_DGRAM_QUERY_MTU| implementation could use
+   * |SSL_set_mtu|. Does this need to be so complex?  */
+  if (ssl->d1->mtu < dtls1_min_mtu() &&
+      !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
+    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+    if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
+      ssl->d1->mtu = (unsigned)mtu;
+    } else {
+      ssl->d1->mtu = kDefaultMTU;
+      BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+    }
+  }
+
+  /* The MTU should be above the minimum now. */
+  assert(ssl->d1->mtu >= dtls1_min_mtu());
+}
+
+enum seal_result_t {
+  seal_error,
+  seal_no_progress,
+  seal_partial,
+  seal_success,
+};
+
+/* seal_next_message seals |msg|, which must be the next message, to |out|. If
+ * progress was made, it returns |seal_partial| or |seal_success| and sets
+ * |*out_len| to the number of bytes written. */
+static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out,
+                                            size_t *out_len, size_t max_out,
+                                            const DTLS_OUTGOING_MESSAGE *msg) {
+  assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+  assert(msg == &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]);
+
   /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
    * (negotiated cipher) exist. */
   assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1);
@@ -790,56 +626,181 @@
   if (ssl->d1->w_epoch == 1 && msg->epoch == 0) {
     use_epoch = dtls1_use_previous_epoch;
   }
+  size_t overhead = dtls_max_seal_overhead(ssl, use_epoch);
+  size_t prefix = dtls_seal_prefix_len(ssl, use_epoch);
 
-  /* TODO(davidben): This cannot handle non-blocking writes. */
-  int ret;
   if (msg->is_ccs) {
-    ret = dtls1_write_change_cipher_spec(ssl, use_epoch);
-  } else {
-    size_t offset = 0;
-    ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
-                                   use_epoch);
+    /* Check there is room for the ChangeCipherSpec. */
+    static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+    if (max_out < sizeof(kChangeCipherSpec) + overhead) {
+      return seal_no_progress;
+    }
+
+    if (!dtls_seal_record(ssl, out, out_len, max_out,
+                          SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                          sizeof(kChangeCipherSpec), use_epoch)) {
+      return seal_error;
+    }
+
+    ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
+                        kChangeCipherSpec, sizeof(kChangeCipherSpec));
+    return seal_success;
   }
 
-  return ret;
+  /* DTLS messages are serialized as a single fragment in |msg|. */
+  CBS cbs, body;
+  struct hm_header_st hdr;
+  CBS_init(&cbs, msg->data, msg->len);
+  if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
+      hdr.frag_off != 0 ||
+      hdr.frag_len != CBS_len(&body) ||
+      hdr.msg_len != CBS_len(&body) ||
+      !CBS_skip(&body, ssl->d1->outgoing_offset) ||
+      CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return seal_error;
+  }
+
+  /* Determine how much progress can be made. */
+  if (max_out < DTLS1_HM_HEADER_LENGTH + 1 + overhead || max_out < prefix) {
+    return seal_no_progress;
+  }
+  size_t todo = CBS_len(&body);
+  if (todo > max_out - DTLS1_HM_HEADER_LENGTH - overhead) {
+    todo = max_out - DTLS1_HM_HEADER_LENGTH - overhead;
+  }
+
+  /* Assemble a fragment, to be sealed in-place. */
+  CBB cbb;
+  uint8_t *frag = out + prefix;
+  size_t max_frag = max_out - prefix, frag_len;
+  if (!CBB_init_fixed(&cbb, frag, max_frag) ||
+      !CBB_add_u8(&cbb, hdr.type) ||
+      !CBB_add_u24(&cbb, hdr.msg_len) ||
+      !CBB_add_u16(&cbb, hdr.seq) ||
+      !CBB_add_u24(&cbb, ssl->d1->outgoing_offset) ||
+      !CBB_add_u24(&cbb, todo) ||
+      !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
+      !CBB_finish(&cbb, NULL, &frag_len)) {
+    CBB_cleanup(&cbb);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return seal_error;
+  }
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len);
+
+  if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE,
+                        out + prefix, frag_len, use_epoch)) {
+    return seal_error;
+  }
+
+  if (todo == CBS_len(&body)) {
+    /* The next message is complete. */
+    ssl->d1->outgoing_offset = 0;
+    return seal_success;
+  }
+
+  ssl->d1->outgoing_offset += todo;
+  return seal_partial;
 }
 
-int dtls1_retransmit_outgoing_messages(SSL *ssl) {
-  /* Ensure we are packing handshake messages. */
-  const int was_buffered = ssl_is_wbio_buffered(ssl);
-  assert(was_buffered == SSL_in_init(ssl));
-  if (!was_buffered && !ssl_init_wbio_buffer(ssl)) {
-    return -1;
+/* seal_next_packet writes as much of the next flight as possible to |out| and
+ * advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as
+ * appropriate. */
+static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len,
+                            size_t max_out) {
+  int made_progress = 0;
+  size_t total = 0;
+  assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+  for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len;
+       ssl->d1->outgoing_written++) {
+    const DTLS_OUTGOING_MESSAGE *msg =
+        &ssl->d1->outgoing_messages[ssl->d1->outgoing_written];
+    size_t len;
+    enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg);
+    switch (ret) {
+      case seal_error:
+        return 0;
+
+      case seal_no_progress:
+        goto packet_full;
+
+      case seal_partial:
+      case seal_success:
+        out += len;
+        max_out -= len;
+        total += len;
+        made_progress = 1;
+
+        if (ret == seal_partial) {
+          goto packet_full;
+        }
+        break;
+    }
   }
-  assert(ssl_is_wbio_buffered(ssl));
+
+packet_full:
+  /* The MTU was too small to make any progress. */
+  if (!made_progress) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
+    return 0;
+  }
+
+  *out_len = total;
+  return 1;
+}
+
+int dtls1_flush_flight(SSL *ssl) {
+  dtls1_update_mtu(ssl);
 
   int ret = -1;
-  for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
-    if (dtls1_retransmit_message(ssl, &ssl->d1->outgoing_messages[i]) <= 0) {
+  uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu);
+  if (packet == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) {
+    uint8_t old_written = ssl->d1->outgoing_written;
+    uint32_t old_offset = ssl->d1->outgoing_offset;
+
+    size_t packet_len;
+    if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) {
+      goto err;
+    }
+
+    int bio_ret = BIO_write(ssl->wbio, packet, packet_len);
+    if (bio_ret <= 0) {
+      /* Retry this packet the next time around. */
+      ssl->d1->outgoing_written = old_written;
+      ssl->d1->outgoing_offset = old_offset;
+      ssl->rwstate = SSL_WRITING;
+      ret = bio_ret;
       goto err;
     }
   }
 
-  ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
+  if (BIO_flush(ssl->wbio) <= 0) {
     ssl->rwstate = SSL_WRITING;
     goto err;
   }
 
+  ret = 1;
+
 err:
-  if (!was_buffered) {
-    ssl_free_wbio_buffer(ssl);
-  }
+  OPENSSL_free(packet);
   return ret;
 }
 
-int dtls1_send_change_cipher_spec(SSL *ssl) {
-  int ret = dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
-  if (ret <= 0) {
-    return ret;
-  }
-  dtls1_add_change_cipher_spec(ssl);
-  return 1;
+int dtls1_retransmit_outgoing_messages(SSL *ssl) {
+  /* Rewind to the start of the flight and write it again.
+   *
+   * TODO(davidben): This does not allow retransmits to be resumed on
+   * non-blocking write. */
+  ssl->d1->outgoing_written = 0;
+  ssl->d1->outgoing_offset = 0;
+
+  return dtls1_flush_flight(ssl);
 }
 
 unsigned int dtls1_min_mtu(void) {
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index c6950d5..27b2763 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -331,7 +331,7 @@
   }
 }
 
-int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) {
+int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
   assert(!SSL_in_init(ssl));
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
@@ -348,7 +348,7 @@
     return 0;
   }
 
-  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf_, (size_t)len,
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
     return ret;
@@ -364,15 +364,6 @@
    * |ssl_write_buffer_flush|. */
   assert(!ssl_write_buffer_is_pending(ssl));
 
-  /* If we have an alert to send, lets send it */
-  if (ssl->s3->alert_dispatch) {
-    int ret = ssl->method->dispatch_alert(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
-    /* if it went, fall through and send more stuff */
-  }
-
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
@@ -397,13 +388,12 @@
 }
 
 int dtls1_dispatch_alert(SSL *ssl) {
-  ssl->s3->alert_dispatch = 0;
   int ret = dtls1_write_record(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
-    ssl->s3->alert_dispatch = 1;
     return ret;
   }
+  ssl->s3->alert_dispatch = 0;
 
   /* If the alert is fatal, flush the BIO now. */
   if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 702b3c0..7a35b39 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -99,14 +99,6 @@
   return cipher->algorithm_enc != SSL_eNULL;
 }
 
-static int dtls1_flush_flight(SSL *ssl) {
-  int ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
-  }
-  return ret;
-}
-
 static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); }
 
 static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); }
@@ -159,9 +151,9 @@
     dtls1_supports_cipher,
     dtls1_init_message,
     dtls1_finish_message,
-    dtls1_queue_message,
-    dtls1_write_message,
-    dtls1_send_change_cipher_spec,
+    dtls1_add_message,
+    dtls1_add_change_cipher_spec,
+    dtls1_add_alert,
     dtls1_flush_flight,
     dtls1_expect_flight,
     dtls1_received_flight,
diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.c
index 35c08b0..879706d 100644
--- a/src/ssl/dtls_record.c
+++ b/src/ssl/dtls_record.c
@@ -249,16 +249,27 @@
   return ssl_open_record_success;
 }
 
-size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
-  const SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx;
+static const SSL_AEAD_CTX *get_write_aead(const SSL *ssl,
+                                          enum dtls1_use_epoch_t use_epoch) {
   if (use_epoch == dtls1_use_previous_epoch) {
     /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
      * (negotiated cipher) exist. */
     assert(ssl->d1->w_epoch == 1);
-    aead = NULL;
+    return NULL;
   }
 
-  return DTLS1_RT_HEADER_LENGTH + SSL_AEAD_CTX_explicit_nonce_len(aead);
+  return ssl->s3->aead_write_ctx;
+}
+
+size_t dtls_max_seal_overhead(const SSL *ssl,
+                              enum dtls1_use_epoch_t use_epoch) {
+  return DTLS1_RT_HEADER_LENGTH +
+         SSL_AEAD_CTX_max_overhead(get_write_aead(ssl, use_epoch));
+}
+
+size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
+  return DTLS1_RT_HEADER_LENGTH +
+         SSL_AEAD_CTX_explicit_nonce_len(get_write_aead(ssl, use_epoch));
 }
 
 int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index 3389d6c..23a4cff 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -206,17 +206,10 @@
 
       case SSL_ST_CONNECT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
-
-        if (!ssl_init_wbio_buffer(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
         hs->state = SSL3_ST_CW_CLNT_HELLO_A;
         break;
 
       case SSL3_ST_CW_CLNT_HELLO_A:
-      case SSL3_ST_CW_CLNT_HELLO_B:
         ret = ssl3_send_client_hello(hs);
         if (ret <= 0) {
           goto end;
@@ -326,7 +319,6 @@
         break;
 
       case SSL3_ST_CW_CERT_A:
-      case SSL3_ST_CW_CERT_B:
         if (hs->cert_request) {
           ret = ssl3_send_client_certificate(hs);
           if (ret <= 0) {
@@ -339,7 +331,6 @@
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
-      case SSL3_ST_CW_KEY_EXCH_B:
         ret = ssl3_send_client_key_exchange(hs);
         if (ret <= 0) {
           goto end;
@@ -349,7 +340,6 @@
 
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
-      case SSL3_ST_CW_CERT_VRFY_C:
         if (hs->cert_request && ssl_has_certificate(ssl)) {
           ret = ssl3_send_cert_verify(hs);
           if (ret <= 0) {
@@ -362,22 +352,16 @@
         break;
 
       case SSL3_ST_CW_CHANGE:
-        ret = ssl->method->send_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        hs->state = SSL3_ST_CW_NEXT_PROTO_A;
-
-        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        if (!ssl->method->add_change_cipher_spec(ssl) ||
+            !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
 
+        hs->state = SSL3_ST_CW_NEXT_PROTO_A;
         break;
 
       case SSL3_ST_CW_NEXT_PROTO_A:
-      case SSL3_ST_CW_NEXT_PROTO_B:
         if (hs->next_proto_neg_seen) {
           ret = ssl3_send_next_proto(hs);
           if (ret <= 0) {
@@ -390,7 +374,6 @@
         break;
 
       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(hs);
           if (ret <= 0) {
@@ -403,9 +386,7 @@
         break;
 
       case SSL3_ST_CW_FINISHED_A:
-      case SSL3_ST_CW_FINISHED_B:
-        ret = ssl3_send_finished(hs, SSL3_ST_CW_FINISHED_A,
-                                 SSL3_ST_CW_FINISHED_B);
+        ret = ssl3_send_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -437,8 +418,6 @@
       case SSL3_ST_FALSE_START:
         hs->state = SSL3_ST_CR_SESSION_TICKET_A;
         hs->in_false_start = 1;
-
-        ssl_free_wbio_buffer(ssl);
         ret = 1;
         goto end;
 
@@ -527,9 +506,6 @@
           ssl->s3->new_session = NULL;
         }
 
-        /* Remove write buffering now. */
-        ssl_free_wbio_buffer(ssl);
-
         const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
         ssl->s3->initial_handshake_complete = 1;
         if (is_initial_handshake) {
@@ -578,11 +554,59 @@
   return ret;
 }
 
+/* ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of
+ * disabled algorithms. */
+static void ssl_get_client_disabled(SSL *ssl, uint32_t *out_mask_a,
+                                    uint32_t *out_mask_k) {
+  int have_rsa = 0, have_ecdsa = 0;
+  *out_mask_a = 0;
+  *out_mask_k = 0;
+
+  /* Now go through all signature algorithms seeing if we support any for RSA or
+   * ECDSA. Do this for all versions not just TLS 1.2. */
+  const uint16_t *sigalgs;
+  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
+  for (size_t i = 0; i < num_sigalgs; i++) {
+    switch (sigalgs[i]) {
+      case SSL_SIGN_RSA_PSS_SHA512:
+      case SSL_SIGN_RSA_PSS_SHA384:
+      case SSL_SIGN_RSA_PSS_SHA256:
+      case SSL_SIGN_RSA_PKCS1_SHA512:
+      case SSL_SIGN_RSA_PKCS1_SHA384:
+      case SSL_SIGN_RSA_PKCS1_SHA256:
+      case SSL_SIGN_RSA_PKCS1_SHA1:
+        have_rsa = 1;
+        break;
+
+      case SSL_SIGN_ECDSA_SECP521R1_SHA512:
+      case SSL_SIGN_ECDSA_SECP384R1_SHA384:
+      case SSL_SIGN_ECDSA_SECP256R1_SHA256:
+      case SSL_SIGN_ECDSA_SHA1:
+        have_ecdsa = 1;
+        break;
+    }
+  }
+
+  /* Disable auth if we don't include any appropriate signature algorithms. */
+  if (!have_rsa) {
+    *out_mask_a |= SSL_aRSA;
+  }
+  if (!have_ecdsa) {
+    *out_mask_a |= SSL_aECDSA;
+  }
+
+  /* PSK requires a client callback. */
+  if (ssl->psk_client_callback == NULL) {
+    *out_mask_a |= SSL_aPSK;
+    *out_mask_k |= SSL_kPSK;
+  }
+}
+
 static int ssl_write_client_cipher_list(SSL *ssl, CBB *out,
                                         uint16_t min_version,
                                         uint16_t max_version) {
-  /* Prepare disabled cipher masks. */
-  ssl_set_client_disabled(ssl);
+  uint32_t mask_a, mask_k;
+  ssl_get_client_disabled(ssl, &mask_a, &mask_k);
 
   CBB child;
   if (!CBB_add_u16_length_prefixed(out, &child)) {
@@ -618,8 +642,8 @@
     for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
       const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
       /* Skip disabled ciphers */
-      if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
-          (cipher->algorithm_auth & ssl->cert->mask_a)) {
+      if ((cipher->algorithm_mkey & mask_k) ||
+          (cipher->algorithm_auth & mask_a)) {
         continue;
       }
       if (SSL_CIPHER_get_min_version(cipher) > max_version ||
@@ -713,7 +737,7 @@
     goto err;
   }
 
-  return ssl->method->queue_message(ssl, msg, len);
+  return ssl->method->add_message(ssl, msg, len);
 
  err:
   CBB_cleanup(&cbb);
@@ -722,10 +746,6 @@
 
 static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CLNT_HELLO_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we
    * may send multiple ClientHellos if we receive HelloVerifyRequest. */
   if (!ssl3_init_handshake_buffer(ssl)) {
@@ -779,8 +799,7 @@
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_CLNT_HELLO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs) {
@@ -789,7 +808,7 @@
   CBS hello_verify_request, cookie;
   uint16_t server_version;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -800,8 +819,10 @@
     return 1;
   }
 
-  CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
+  /* The handshake transcript is reset on HelloVerifyRequst, so do not bother
+   * hashing it. */
 
+  CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
       CBS_len(&hello_verify_request) != 0) {
@@ -828,13 +849,12 @@
 
 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;
   uint16_t server_wire_version, cipher_suite;
   uint8_t compression_method;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     uint32_t err = ERR_peek_error();
     if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
@@ -896,9 +916,7 @@
 
   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);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
     return -1;
   }
 
@@ -945,7 +963,9 @@
   }
 
   /* The cipher must be allowed in the selected version and enabled. */
-  if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
+  uint32_t mask_a, mask_k;
+  ssl_get_client_disabled(ssl, &mask_a, &mask_k);
+  if ((c->algorithm_mkey & mask_k) || (c->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) ||
       !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) {
@@ -977,8 +997,10 @@
   }
   ssl->s3->tmp.new_cipher = c;
 
-  /* Now that the cipher is known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(ssl)) {
+  /* Now that the cipher is known, initialize the handshake hash and hash the
+   * ServerHello. */
+  if (!ssl3_init_handshake_hash(ssl) ||
+      !ssl_hash_current_message(ssl)) {
     goto f_err;
   }
 
@@ -1033,12 +1055,16 @@
 
 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);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS cbs;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
 
@@ -1077,7 +1103,7 @@
   CBS certificate_status, ocsp_response;
   uint8_t status_type;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -1089,6 +1115,10 @@
     return 1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS_init(&certificate_status, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
@@ -1130,7 +1160,7 @@
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -1147,6 +1177,10 @@
     return 1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   /* Retain a copy of the original CBS to compute the signature over. */
   CBS server_key_exchange;
   CBS_init(&server_key_exchange, ssl->init_msg, ssl->init_num);
@@ -1360,7 +1394,7 @@
 
 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);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
@@ -1373,9 +1407,8 @@
     return 1;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_REQUEST) ||
+      !ssl_hash_current_message(ssl)) {
     return -1;
   }
 
@@ -1428,12 +1461,16 @@
 
 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);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO_DONE) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   /* ServerHelloDone is empty. */
   if (ssl->init_num > 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -1446,11 +1483,6 @@
 
 static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CERT_B) {
-    return ssl->method->write_message(ssl);
-  }
-  assert(hs->state == SSL3_ST_CW_CERT_A);
-
   /* Call cert_cb to update the certificate. */
   if (ssl->cert->cert_cb) {
     int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -1469,9 +1501,12 @@
     /* Without a client certificate, the handshake buffer may be released. */
     ssl3_free_handshake_buffer(ssl);
 
+    /* In SSL 3.0, the Certificate message is replaced with a warning alert. */
     if (ssl->version == SSL3_VERSION) {
-      /* In SSL 3.0, send no certificate by skipping both messages. */
-      ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
+      if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING,
+                                  SSL_AD_NO_CERTIFICATE)) {
+        return -1;
+      }
       return 1;
     }
   }
@@ -1480,8 +1515,7 @@
       !ssl3_output_cert_chain(ssl)) {
     return -1;
   }
-  hs->state = SSL3_ST_CW_CERT_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
@@ -1489,11 +1523,6 @@
 
 static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_KEY_EXCH_B) {
-    return ssl->method->write_message(ssl);
-  }
-  assert(hs->state == SSL3_ST_CW_KEY_EXCH_A);
-
   uint8_t *pms = NULL;
   size_t pms_len = 0;
   CBB cbb, body;
@@ -1647,10 +1676,9 @@
 
   /* The message must be added to the finished hash before calculating the
    * master secret. */
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
-  hs->state = SSL3_ST_CW_KEY_EXCH_B;
 
   ssl->s3->new_session->master_key_length =
       tls1_generate_master_secret(ssl, ssl->s3->new_session->master_key, pms,
@@ -1663,7 +1691,7 @@
   OPENSSL_cleanse(pms, pms_len);
   OPENSSL_free(pms);
 
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1676,10 +1704,6 @@
 
 static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CERT_VRFY_C) {
-    return ssl->method->write_message(ssl);
-  }
-
   assert(ssl_has_private_key(ssl));
 
   CBB cbb, body, child;
@@ -1765,12 +1789,11 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->state = SSL3_ST_CW_CERT_VRFY_C;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1779,12 +1802,6 @@
 
 static int ssl3_send_next_proto(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_NEXT_PROTO_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_CW_NEXT_PROTO_A);
-
   static const uint8_t kZero[32] = {0};
   size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
 
@@ -1795,24 +1812,17 @@
                      ssl->s3->next_proto_negotiated_len) ||
       !CBB_add_u8_length_prefixed(&body, &child) ||
       !CBB_add_bytes(&child, kZero, padding_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_NEXT_PROTO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CHANNEL_ID_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_CW_CHANNEL_ID_A);
-
   if (!ssl_do_channel_id_callback(ssl)) {
     return -1;
   }
@@ -1825,24 +1835,27 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
       !tls1_write_channel_id(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_CHANNEL_ID_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 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);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_NEW_SESSION_TICKET) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS new_session_ticket, ticket;
   uint32_t tlsext_tick_lifetime_hint;
   CBS_init(&new_session_ticket, ssl->init_msg, ssl->init_num);
@@ -1877,7 +1890,7 @@
   }
 
   /* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */
-  ssl_session_refresh_time(ssl, session);
+  ssl_session_rebase_time(ssl, session);
 
   if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index a4396f4..7b66cf2 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -185,6 +185,17 @@
 static int ssl3_get_channel_id(SSL_HANDSHAKE *hs);
 static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs);
 
+static struct CRYPTO_STATIC_MUTEX g_v2clienthello_lock =
+    CRYPTO_STATIC_MUTEX_INIT;
+static uint64_t g_v2clienthello_count = 0;
+
+uint64_t SSL_get_v2clienthello_count(void) {
+  CRYPTO_STATIC_MUTEX_lock_read(&g_v2clienthello_lock);
+  uint64_t ret = g_v2clienthello_count;
+  CRYPTO_STATIC_MUTEX_unlock_read(&g_v2clienthello_lock);
+  return ret;
+}
+
 int ssl3_accept(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint32_t alg_a;
@@ -206,13 +217,6 @@
       case SSL_ST_ACCEPT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        /* Enable a write buffer. This groups handshake messages within a flight
-         * into a single write. */
-        if (!ssl_init_wbio_buffer(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
         if (!ssl3_init_handshake_buffer(ssl)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
           ret = -1;
@@ -226,7 +230,6 @@
       case SSL3_ST_SR_CLNT_HELLO_B:
       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(hs);
         if (hs->state == SSL_ST_TLS13) {
           break;
@@ -239,7 +242,6 @@
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
-      case SSL3_ST_SW_SRVR_HELLO_B:
         ret = ssl3_send_server_hello(hs);
         if (ret <= 0) {
           goto end;
@@ -252,7 +254,6 @@
         break;
 
       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(hs);
           if (ret <= 0) {
@@ -265,7 +266,6 @@
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
-      case SSL3_ST_SW_CERT_STATUS_B:
         if (hs->certificate_status_expected) {
           ret = ssl3_send_certificate_status(hs);
           if (ret <= 0) {
@@ -279,7 +279,6 @@
 
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
-      case SSL3_ST_SW_KEY_EXCH_C:
         alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
         /* PSK ciphers send ServerKeyExchange if there is an identity hint. */
@@ -297,7 +296,6 @@
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
-      case SSL3_ST_SW_CERT_REQ_B:
         if (hs->cert_request) {
           ret = ssl3_send_certificate_request(hs);
           if (ret <= 0) {
@@ -310,7 +308,6 @@
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
-      case SSL3_ST_SW_SRVR_DONE_B:
         ret = ssl3_send_server_hello_done(hs);
         if (ret <= 0) {
           goto end;
@@ -410,7 +407,6 @@
         break;
 
       case SSL3_ST_SW_SESSION_TICKET_A:
-      case SSL3_ST_SW_SESSION_TICKET_B:
         if (hs->ticket_expected) {
           ret = ssl3_send_new_session_ticket(hs);
           if (ret <= 0) {
@@ -423,22 +419,17 @@
         break;
 
       case SSL3_ST_SW_CHANGE:
-        ret = ssl->method->send_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        hs->state = SSL3_ST_SW_FINISHED_A;
-
-        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!ssl->method->add_change_cipher_spec(ssl) ||
+            !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
+
+        hs->state = SSL3_ST_SW_FINISHED_A;
         break;
 
       case SSL3_ST_SW_FINISHED_A:
-      case SSL3_ST_SW_FINISHED_B:
-        ret = ssl3_send_finished(hs, SSL3_ST_SW_FINISHED_A,
-                                 SSL3_ST_SW_FINISHED_B);
+        ret = ssl3_send_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -493,8 +484,11 @@
           ssl->s3->new_session = NULL;
         }
 
-        /* remove buffering on output */
-        ssl_free_wbio_buffer(ssl);
+        if (hs->v2_clienthello) {
+          CRYPTO_STATIC_MUTEX_lock_write(&g_v2clienthello_lock);
+          g_v2clienthello_count++;
+          CRYPTO_STATIC_MUTEX_unlock_write(&g_v2clienthello_lock);
+        }
 
         ssl->s3->initial_handshake_complete = 1;
         ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
@@ -546,6 +540,7 @@
 static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                              const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
+  assert(!ssl->s3->have_version);
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     *out_alert = SSL_AD_PROTOCOL_VERSION;
@@ -703,7 +698,7 @@
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
-  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
+  if (ssl_has_certificate(ssl)) {
     int type = ssl_private_key_type(ssl);
     if (type == NID_rsaEncryption) {
       mask_k |= SSL_kRSA;
@@ -820,12 +815,15 @@
 
   if (hs->state == SSL3_ST_SR_CLNT_HELLO_A) {
     /* The first time around, read the ClientHello. */
-    int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_HELLO,
-                                               ssl_hash_message);
+    int msg_ret = ssl->method->ssl_get_message(ssl);
     if (msg_ret <= 0) {
       return msg_ret;
     }
 
+    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+      return -1;
+    }
+
     hs->state = SSL3_ST_SR_CLNT_HELLO_B;
   }
 
@@ -855,23 +853,11 @@
           /* fallthrough */;
       }
     }
-    hs->state = SSL3_ST_SR_CLNT_HELLO_C;
-  }
 
-  /* Negotiate the protocol version if we have not done so yet. */
-  if (!ssl->s3->have_version) {
     if (!negotiate_version(hs, &al, &client_hello)) {
       goto f_err;
     }
 
-    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-      hs->state = SSL_ST_TLS13;
-      hs->do_tls13_handshake = tls13_server_handshake;
-      return 1;
-    }
-  }
-
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_C) {
     /* Load the client random. */
     if (client_hello.random_len != SSL3_RANDOM_SIZE) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -880,11 +866,14 @@
     OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
                    client_hello.random_len);
 
-    /* Only null compression is supported. */
+    /* Only null compression is supported. TLS 1.3 further requires the peer
+     * advertise no other compression. */
     if (OPENSSL_memchr(client_hello.compression_methods, 0,
-                       client_hello.compression_methods_len) == NULL) {
+                       client_hello.compression_methods_len) == NULL ||
+        (ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
+         client_hello.compression_methods_len != 1)) {
       al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMPRESSION_SPECIFIED);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
       goto f_err;
     }
 
@@ -894,10 +883,10 @@
       goto err;
     }
 
-    hs->state = SSL3_ST_SR_CLNT_HELLO_D;
+    hs->state = SSL3_ST_SR_CLNT_HELLO_C;
   }
 
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_D) {
+  if (hs->state == SSL3_ST_SR_CLNT_HELLO_C) {
     /* Call |cert_cb| to update server certificates if required. */
     if (ssl->cert->cert_cb != NULL) {
       int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -916,6 +905,13 @@
       goto err;
     }
 
+    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      /* Jump to the TLS 1.3 state machine. */
+      hs->state = SSL_ST_TLS13;
+      hs->do_tls13_handshake = tls13_server_handshake;
+      return 1;
+    }
+
     /* Negotiate the cipher suite. This must be done after |cert_cb| so the
      * certificate is finalized. */
     ssl->s3->tmp.new_cipher =
@@ -926,10 +922,10 @@
       goto f_err;
     }
 
-    hs->state = SSL3_ST_SR_CLNT_HELLO_E;
+    hs->state = SSL3_ST_SR_CLNT_HELLO_D;
   }
 
-  assert(hs->state == SSL3_ST_SR_CLNT_HELLO_E);
+  assert(hs->state == SSL3_ST_SR_CLNT_HELLO_D);
 
   /* Determine whether we are doing session resumption. */
   int tickets_supported = 0, renew_ticket = 0;
@@ -1028,8 +1024,10 @@
     goto f_err;
   }
 
-  /* Now that all parameters are known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(ssl)) {
+  /* Now that all parameters are known, initialize the handshake hash and hash
+   * the ClientHello. */
+  if (!ssl3_init_handshake_hash(ssl) ||
+      !ssl_hash_current_message(ssl)) {
     goto f_err;
   }
 
@@ -1052,11 +1050,6 @@
 
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SRVR_HELLO_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_SW_SRVR_HELLO_A);
 
   /* We only accept ChannelIDs on connections with ECDHE in order to avoid a
    * known attack while we fix ChannelID itself. */
@@ -1101,40 +1094,30 @@
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u8(&body, 0 /* no compression */) ||
       !ssl_add_serverhello_tlsext(hs, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SRVR_HELLO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   if (!ssl_has_certificate(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return 0;
+    return -1;
   }
 
   if (!ssl3_output_cert_chain(ssl)) {
-    return 0;
+    return -1;
   }
-  hs->state = SSL3_ST_SW_CERT_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_STATUS_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body, ocsp_response;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_CERTIFICATE_STATUS) ||
@@ -1142,22 +1125,17 @@
       !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
       !CBB_add_bytes(&ocsp_response, CRYPTO_BUFFER_data(ssl->ocsp_response),
                      CRYPTO_BUFFER_len(ssl->ocsp_response)) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_CERT_STATUS_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_KEY_EXCH_C) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, child;
   CBB_zero(&cbb);
 
@@ -1317,7 +1295,7 @@
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -1325,8 +1303,7 @@
   hs->server_params = NULL;
   hs->server_params_len = 0;
 
-  hs->state = SSL3_ST_SW_KEY_EXCH_C;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1373,10 +1350,6 @@
 
 static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_REQ_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body, cert_types, sigalgs_cbb;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_CERTIFICATE_REQUEST) ||
@@ -1400,12 +1373,11 @@
   }
 
   if (!ssl_add_client_CA_list(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->state = SSL3_ST_SW_CERT_REQ_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1415,27 +1387,22 @@
 
 static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SRVR_DONE_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SRVR_DONE_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 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);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
@@ -1463,6 +1430,10 @@
     return -1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS certificate_msg;
   CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
 
@@ -1539,11 +1510,15 @@
   uint8_t psk[PSK_MAX_PSK_LEN];
 
   if (hs->state == SSL3_ST_SR_KEY_EXCH_A) {
-    int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE,
-                                           ssl_hash_message);
+    int ret = ssl->method->ssl_get_message(ssl);
     if (ret <= 0) {
       return ret;
     }
+
+    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE) ||
+        !ssl_hash_current_message(ssl)) {
+      return -1;
+    }
   }
 
   CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num);
@@ -1807,12 +1782,15 @@
     return 1;
   }
 
-  int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE_VERIFY,
-                                             ssl_dont_hash_message);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY)) {
+    return -1;
+  }
+
   CBS_init(&certificate_verify, ssl->init_msg, ssl->init_num);
 
   /* Determine the digest type if needbe. */
@@ -1900,12 +1878,16 @@
  * sets the next_proto member in s if found */
 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);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_NEXT_PROTO) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS next_protocol, selected_protocol, padding;
   CBS_init(&next_protocol, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
@@ -1927,13 +1909,13 @@
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
 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);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
 
-  if (!tls1_verify_channel_id(ssl) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(ssl) ||
       !ssl_hash_current_message(ssl)) {
     return -1;
   }
@@ -1942,15 +1924,11 @@
 
 static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SESSION_TICKET_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   const SSL_SESSION *session;
   SSL_SESSION *session_copy = NULL;
   if (ssl->session == NULL) {
     /* Fix the timeout to measure from the ticket issuance time. */
-    ssl_session_refresh_time(ssl, ssl->s3->new_session);
+    ssl_session_rebase_time(ssl, ssl->s3->new_session);
     session = ssl->s3->new_session;
   } else {
     /* We are renewing an existing session. Duplicate the session to adjust the
@@ -1960,7 +1938,7 @@
       return -1;
     }
 
-    ssl_session_refresh_time(ssl, session_copy);
+    ssl_session_rebase_time(ssl, session_copy);
     session = session_copy;
   }
 
@@ -1970,7 +1948,7 @@
       CBB_add_u32(&body, session->timeout) &&
       CBB_add_u16_length_prefixed(&body, &ticket) &&
       ssl_encrypt_ticket(ssl, &ticket, session) &&
-      ssl_complete_message(ssl, &cbb);
+      ssl_add_message_cbb(ssl, &cbb);
 
   SSL_SESSION_free(session_copy);
   CBB_cleanup(&cbb);
@@ -1979,6 +1957,5 @@
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SESSION_TICKET_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index a320e72..544259c 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -273,22 +273,22 @@
   uint8_t fixed_nonce_len, variable_nonce_len;
   /* variable_nonce_included_in_record is non-zero if the variable nonce
    * for a record is included as a prefix before the ciphertext. */
-  char variable_nonce_included_in_record;
+  unsigned variable_nonce_included_in_record : 1;
   /* random_variable_nonce is non-zero if the variable nonce is
    * randomly generated, rather than derived from the sequence
    * number. */
-  char random_variable_nonce;
+  unsigned random_variable_nonce : 1;
   /* omit_length_in_ad is non-zero if the length should be omitted in the
    * AEAD's ad parameter. */
-  char omit_length_in_ad;
+  unsigned omit_length_in_ad : 1;
   /* omit_version_in_ad is non-zero if the version should be omitted
    * in the AEAD's ad parameter. */
-  char omit_version_in_ad;
+  unsigned omit_version_in_ad : 1;
   /* omit_ad is non-zero if the AEAD's ad parameter should be omitted. */
-  char omit_ad;
+  unsigned omit_ad : 1;
   /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
    * variable nonce rather than prepended. */
-  char xor_fixed_nonce;
+  unsigned xor_fixed_nonce : 1;
 } SSL_AEAD_CTX;
 
 /* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
@@ -433,6 +433,10 @@
   dtls1_use_current_epoch,
 };
 
+/* dtls_max_seal_overhead returns the maximum overhead, in bytes, of sealing a
+ * record. */
+size_t dtls_max_seal_overhead(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
+
 /* dtls_seal_prefix_len returns the number of bytes of prefix to reserve in
  * front of the plaintext when sealing a record in-place. */
 size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
@@ -769,10 +773,6 @@
                                               CBS *cbs,
                                               CRYPTO_BUFFER_POOL *pool);
 
-/* ssl_add_cert_to_cbb adds |x509| to |cbb|. It returns one on success and zero
- * on error. */
-int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509);
-
 /* ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used
  * by a TLS Certificate message. If there is no certificate chain, it emits an
  * empty certificate list. It returns one on success and zero on error. */
@@ -884,7 +884,6 @@
   ssl_hs_error,
   ssl_hs_ok,
   ssl_hs_read_message,
-  ssl_hs_write_message,
   ssl_hs_flush,
   ssl_hs_flush_and_read_message,
   ssl_hs_x509_lookup,
@@ -1053,6 +1052,9 @@
    * or received. */
   unsigned ticket_expected:1;
 
+  /* v2_clienthello is one if we received a V2ClientHello. */
+  unsigned v2_clienthello:1;
+
   /* client_version is the value sent or received in the ClientHello version. */
   uint16_t client_version;
 } /* SSL_HANDSHAKE */;
@@ -1062,6 +1064,10 @@
 /* ssl_handshake_free releases all memory associated with |hs|. */
 void ssl_handshake_free(SSL_HANDSHAKE *hs);
 
+/* ssl_check_message_type checks if the current message has type |type|. If so
+ * it returns one. Otherwise, it sends an alert and returns zero. */
+int ssl_check_message_type(SSL *ssl, int type);
+
 /* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <=
  * 0 on error. */
 int tls13_handshake(SSL_HANDSHAKE *hs);
@@ -1075,18 +1081,14 @@
  * success and zero on failure. */
 int tls13_post_handshake(SSL *ssl);
 
-/* tls13_check_message_type checks if the current message has type |type|. If so
- * it returns one. Otherwise, it sends an alert and returns zero. */
-int tls13_check_message_type(SSL *ssl, int type);
-
 int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous);
 int tls13_process_certificate_verify(SSL_HANDSHAKE *hs);
 int tls13_process_finished(SSL_HANDSHAKE *hs);
 
-int tls13_prepare_certificate(SSL_HANDSHAKE *hs);
-enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL_HANDSHAKE *hs, int is_first_run);
-int tls13_prepare_finished(SSL_HANDSHAKE *hs);
+int tls13_add_certificate(SSL_HANDSHAKE *hs);
+enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs,
+                                                           int is_first_run);
+int tls13_add_finished(SSL_HANDSHAKE *hs);
 int tls13_process_new_session_ticket(SSL *ssl);
 
 int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret,
@@ -1222,28 +1224,36 @@
 /* From RFC4492, used in encoding the curve type in ECParameters */
 #define NAMED_CURVE_TYPE 3
 
-enum ssl_hash_message_t {
-  ssl_dont_hash_message,
-  ssl_hash_message,
-};
-
 typedef struct cert_st {
   EVP_PKEY *privatekey;
-  X509 *x509_leaf;
+
+  /* chain contains the certificate chain, with the leaf at the beginning. The
+   * first element of |chain| may be NULL to indicate that the leaf certificate
+   * has not yet been set.
+   *   If |chain| != NULL -> len(chain) >= 1
+   *   If |chain[0]| == NULL -> len(chain) >= 2.
+   *   |chain[1..]| != NULL */
+  STACK_OF(CRYPTO_BUFFER) *chain;
+
+  /* x509_chain may contain a parsed copy of |chain[1..]|. This is only used as
+   * a cache in order to implement “get0” functions that return a non-owning
+   * pointer to the certificate chain. */
   STACK_OF(X509) *x509_chain;
 
+  /* x509_leaf may contain a parsed copy of the first element of |chain|. This
+   * is only used as a cache in order to implement “get0” functions that return
+   * a non-owning pointer to the certificate chain. */
+  X509 *x509_leaf;
+
+  /* x509_stash contains the last |X509| object append to the chain. This is a
+   * workaround for some third-party code that continue to use an |X509| object
+   * even after passing ownership with an “add0” function. */
+  X509 *x509_stash;
+
   /* key_method, if non-NULL, is a set of callbacks to call for private key
    * operations. */
   const SSL_PRIVATE_KEY_METHOD *key_method;
 
-  /* For clients the following masks are of *disabled* key and auth algorithms
-   * based on the current configuration.
-   *
-   * TODO(davidben): Remove these. They get checked twice: when sending the
-   * ClientHello and when processing the ServerHello. */
-  uint32_t mask_k;
-  uint32_t mask_a;
-
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
@@ -1294,12 +1304,10 @@
   uint16_t (*version_to_wire)(uint16_t version);
   int (*ssl_new)(SSL *ssl);
   void (*ssl_free)(SSL *ssl);
-  /* ssl_get_message reads the next handshake message. If |msg_type| is not -1,
-   * the message must have the specified type. On success, it returns one and
-   * sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and |ssl->init_num|.
-   * Otherwise, it returns <= 0. */
-  int (*ssl_get_message)(SSL *ssl, int msg_type,
-                         enum ssl_hash_message_t hash_message);
+  /* ssl_get_message reads the next handshake message. On success, it returns
+   * one and sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and
+   * |ssl->init_num|. Otherwise, it returns <= 0. */
+  int (*ssl_get_message)(SSL *ssl);
   /* get_current_message sets |*out| to the current handshake message. This
    * includes the protocol-specific message header. */
   void (*get_current_message)(const SSL *ssl, CBS *out);
@@ -1311,11 +1319,11 @@
    * and sets |*out_got_handshake| to whether the failure was due to a
    * post-handshake handshake message. If so, it fills in the current message as
    * in |ssl_get_message|. */
-  int (*read_app_data)(SSL *ssl, int *out_got_handshake,  uint8_t *buf, int len,
+  int (*read_app_data)(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
   int (*read_change_cipher_spec)(SSL *ssl);
   void (*read_close_notify)(SSL *ssl);
-  int (*write_app_data)(SSL *ssl, const void *buf_, int len);
+  int (*write_app_data)(SSL *ssl, const uint8_t *buf, int len);
   int (*dispatch_alert)(SSL *ssl);
   /* supports_cipher returns one if |cipher| is supported by this protocol and
    * zero otherwise. */
@@ -1329,16 +1337,17 @@
    * release it with |OPENSSL_free| when done. It returns one on success and
    * zero on error. */
   int (*finish_message)(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
-  /* queue_message queues a handshake message and prepares it to be written. It
-   * takes ownership of |msg| and releases it with |OPENSSL_free| when done. It
-   * returns one on success and zero on error. */
-  int (*queue_message)(SSL *ssl, uint8_t *msg, size_t len);
-  /* write_message writes the next message to the transport. It returns one on
-   * success and <= 0 on error. */
-  int (*write_message)(SSL *ssl);
-  /* send_change_cipher_spec sends a ChangeCipherSpec message. */
-  int (*send_change_cipher_spec)(SSL *ssl);
-  /* flush_flight flushes the current flight to the transport. It returns one on
+  /* add_message adds a handshake message to the pending flight. It returns one
+   * on success and zero on error. In either case, it takes ownership of |msg|
+   * and releases it with |OPENSSL_free| when done. */
+  int (*add_message)(SSL *ssl, uint8_t *msg, size_t len);
+  /* add_change_cipher_spec adds a ChangeCipherSpec record to the pending
+   * flight. It returns one on success and zero on error. */
+  int (*add_change_cipher_spec)(SSL *ssl);
+  /* add_alert adds an alert to the pending flight. It returns one on success
+   * and zero on error. */
+  int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc);
+  /* flush_flight flushes the pending flight to the transport. It returns one on
    * success and <= 0 on error. */
   int (*flush_flight)(SSL *ssl);
   /* expect_flight is called when the handshake expects a flight of messages from
@@ -1466,6 +1475,10 @@
    * and future messages should use the record layer. */
   unsigned v2_hello_done:1;
 
+  /* is_v2_hello is true if the current handshake message was derived from a
+   * V2ClientHello rather than received from the peer directly. */
+  unsigned is_v2_hello:1;
+
   /* initial_handshake_complete is true if the initial handshake has
    * completed. */
   unsigned initial_handshake_complete:1;
@@ -1487,9 +1500,13 @@
 
   uint8_t send_alert[2];
 
-  /* pending_message is the current outgoing handshake message. */
-  uint8_t *pending_message;
-  uint32_t pending_message_len;
+  /* pending_flight is the pending outgoing flight. This is used to flush each
+   * handshake flight in a single write. */
+  BUF_MEM *pending_flight;
+
+  /* pending_flight_offset is the number of bytes of |pending_flight| which have
+   * been successfully written. */
+  uint32_t pending_flight_offset;
 
   /* aead_read_ctx is the current read cipher state. */
   SSL_AEAD_CTX *aead_read_ctx;
@@ -1645,6 +1662,13 @@
   DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT];
   uint8_t outgoing_messages_len;
 
+  /* outgoing_written is the number of outgoing messages that have been
+   * written. */
+  uint8_t outgoing_written;
+  /* outgoing_offset is the number of bytes of the next outgoing message have
+   * been written. */
+  uint32_t outgoing_offset;
+
   unsigned int mtu; /* max DTLS packet size */
 
   /* num_timeouts is the number of times the retransmit timer has fired since
@@ -1675,6 +1699,15 @@
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
+CRYPTO_BUFFER *x509_to_buffer(X509 *x509);
+void ssl_cert_flush_cached_x509_leaf(CERT *cert);
+int ssl_cert_cache_leaf_cert(CERT *cert);
+/* ssl_compare_public_and_private_key returns one if |pubkey| is the public
+ * counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
+ * message on the error queue. */
+int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+                                       const EVP_PKEY *privkey);
+int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
 int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
 
@@ -1720,9 +1753,14 @@
 OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
                                             int dup_flags);
 
-/* ssl_session_refresh_time updates |session|'s start time to the current time,
+/* ssl_session_rebase_time updates |session|'s start time to the current time,
  * adjusting the timeout so the expiration time is unchanged. */
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session);
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session);
+
+/* ssl_session_renew_timeout calls |ssl_session_rebase_time| and renews
+ * |session|'s timeout to |timeout| (measured from the current time). The
+ * renewal is clamped to the session's auth_timeout. */
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout);
 
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list);
@@ -1739,10 +1777,8 @@
 int ssl_verify_alarm_type(long type);
 
 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,
-                     enum ssl_hash_message_t hash_message);
+int ssl3_get_message(SSL *ssl);
 void ssl3_get_current_message(const SSL *ssl, CBS *out);
 void ssl3_release_current_message(SSL *ssl, int free_buffer);
 
@@ -1753,15 +1789,14 @@
 int ssl3_cert_verify_hash(SSL *ssl, const EVP_MD **out_md, uint8_t *out,
                           size_t *out_len, uint16_t signature_algorithm);
 
-int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b);
+int ssl3_send_finished(SSL_HANDSHAKE *hs);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
 int ssl3_read_change_cipher_spec(SSL *ssl);
 void ssl3_read_close_notify(SSL *ssl);
 int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len);
-int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
-int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
+int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len);
 int ssl3_output_cert_chain(SSL *ssl);
 
 int ssl3_new(SSL *ssl);
@@ -1771,18 +1806,22 @@
 
 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);
+int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len);
+int ssl3_add_change_cipher_spec(SSL *ssl);
+int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
+int ssl3_flush_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);
-int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len);
-int dtls1_write_message(SSL *ssl);
+int dtls1_add_message(SSL *ssl, uint8_t *msg, size_t len);
+int dtls1_add_change_cipher_spec(SSL *ssl);
+int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
+int dtls1_flush_flight(SSL *ssl);
 
-/* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to
- * queue the message for writing. */
-int ssl_complete_message(SSL *ssl, CBB *cbb);
+/* ssl_add_message_cbb finishes the handshake message in |cbb| and adds it to
+ * the pending flight. It returns one on success and zero on error. */
+int ssl_add_message_cbb(SSL *ssl, CBB *cbb);
 
 /* ssl_hash_current_message incorporates the current handshake message into the
  * handshake hash. It returns one on success and zero on allocation failure. */
@@ -1798,14 +1837,13 @@
 int dtls1_read_change_cipher_spec(SSL *ssl);
 void dtls1_read_close_notify(SSL *ssl);
 
-int dtls1_write_app_data(SSL *ssl, const void *buf, int len);
+int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len);
 
 /* dtls1_write_record sends a record. It returns one on success and <= 0 on
  * error. */
 int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
                        enum dtls1_use_epoch_t use_epoch);
 
-int dtls1_send_change_cipher_spec(SSL *ssl);
 int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
 int dtls1_retransmit_outgoing_messages(SSL *ssl);
 void dtls1_clear_record_buffer(SSL *ssl);
@@ -1825,18 +1863,11 @@
 int dtls1_connect(SSL *ssl);
 void dtls1_free(SSL *ssl);
 
-int dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message);
+int dtls1_get_message(SSL *ssl);
 void dtls1_get_current_message(const SSL *ssl, CBS *out);
 void dtls1_release_current_message(SSL *ssl, int free_buffer);
 int dtls1_dispatch_alert(SSL *ssl);
 
-/* ssl_is_wbio_buffered returns one if |ssl|'s write BIO is buffered and zero
- * otherwise. */
-int ssl_is_wbio_buffered(const SSL *ssl);
-
-int ssl_init_wbio_buffer(SSL *ssl);
-void ssl_free_wbio_buffer(SSL *ssl);
-
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which);
 int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
@@ -1935,8 +1966,6 @@
 
 uint32_t ssl_get_algorithm_prf(const SSL *ssl);
 
-void ssl_set_client_disabled(SSL *ssl);
-
 void ssl_get_current_time(const SSL *ssl, struct timeval *out_clock);
 
 /* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 492884f..8d2657f 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -180,33 +180,56 @@
   OPENSSL_free(hs);
 }
 
-/* ssl3_do_write sends |ssl->init_buf| in records of type 'type'
- * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns 1 on success
- * and <= 0 on error. */
-static int ssl3_do_write(SSL *ssl, int type, const uint8_t *data, size_t len) {
-  int ret = ssl3_write_bytes(ssl, type, data, len);
-  if (ret <= 0) {
-    return ret;
+int ssl_check_message_type(SSL *ssl, int type) {
+  if (ssl->s3->tmp.message_type != type) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    ERR_add_error_dataf("got type %d, wanted type %d",
+                        ssl->s3->tmp.message_type, type);
+    return 0;
   }
 
-  /* ssl3_write_bytes writes the data in its entirety. */
-  assert((size_t)ret == len);
-  ssl_do_msg_callback(ssl, 1 /* write */, type, data, len);
+  return 1;
+}
+
+static int add_record_to_flight(SSL *ssl, uint8_t type, const uint8_t *in,
+                                size_t in_len) {
+  /* We'll never add a flight while in the process of writing it out. */
+  assert(ssl->s3->pending_flight_offset == 0);
+
+  if (ssl->s3->pending_flight == NULL) {
+    ssl->s3->pending_flight = BUF_MEM_new();
+    if (ssl->s3->pending_flight == NULL) {
+      return 0;
+    }
+  }
+
+  size_t max_out = in_len + SSL_max_seal_overhead(ssl);
+  size_t new_cap = ssl->s3->pending_flight->length + max_out;
+  if (max_out < in_len || new_cap < max_out) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+    return 0;
+  }
+
+  size_t len;
+  if (!BUF_MEM_reserve(ssl->s3->pending_flight, new_cap) ||
+      !tls_seal_record(ssl, (uint8_t *)ssl->s3->pending_flight->data +
+                                ssl->s3->pending_flight->length,
+                       &len, max_out, type, in, in_len)) {
+    return 0;
+  }
+
+  ssl->s3->pending_flight->length += len;
   return 1;
 }
 
 int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
-  CBB_zero(cbb);
-  if (ssl->s3->pending_message != NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
   /* Pick a modest size hint to save most of the |realloc| calls. */
   if (!CBB_init(cbb, 64) ||
       !CBB_add_u8(cbb, type) ||
       !CBB_add_u24_length_prefixed(cbb, body)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    CBB_cleanup(cbb);
     return 0;
   }
 
@@ -223,56 +246,113 @@
   return 1;
 }
 
-int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
-  if (ssl->s3->pending_message != NULL ||
-      len > 0xffffffffu) {
-    OPENSSL_free(msg);
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len) {
+  /* Add the message to the current flight, splitting into several records if
+   * needed. */
+  int ret = 0;
+  size_t added = 0;
+  do {
+    size_t todo = len - added;
+    if (todo > ssl->max_send_fragment) {
+      todo = ssl->max_send_fragment;
+    }
+
+    if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) {
+      goto err;
+    }
+    added += todo;
+  } while (added < len);
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg, len);
+  ssl3_update_handshake_hash(ssl, msg, len);
+  ret = 1;
+
+err:
+  OPENSSL_free(msg);
+  return ret;
+}
+
+int ssl3_add_change_cipher_spec(SSL *ssl) {
+  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+
+  if (!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                            sizeof(kChangeCipherSpec))) {
     return 0;
   }
 
-  ssl3_update_handshake_hash(ssl, msg, len);
-
-  ssl->s3->pending_message = msg;
-  ssl->s3->pending_message_len = (uint32_t)len;
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
+                      kChangeCipherSpec, sizeof(kChangeCipherSpec));
   return 1;
 }
 
-int ssl_complete_message(SSL *ssl, CBB *cbb) {
+int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
+  uint8_t alert[2] = {level, desc};
+  if (!add_record_to_flight(ssl, SSL3_RT_ALERT, alert, sizeof(alert))) {
+    return 0;
+  }
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert, sizeof(alert));
+  ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc);
+  return 1;
+}
+
+int ssl_add_message_cbb(SSL *ssl, CBB *cbb) {
   uint8_t *msg;
   size_t len;
   if (!ssl->method->finish_message(ssl, cbb, &msg, &len) ||
-      !ssl->method->queue_message(ssl, msg, len)) {
+      !ssl->method->add_message(ssl, msg, len)) {
     return 0;
   }
 
   return 1;
 }
 
-int ssl3_write_message(SSL *ssl) {
-  if (ssl->s3->pending_message == NULL) {
+int ssl3_flush_flight(SSL *ssl) {
+  if (ssl->s3->pending_flight == NULL) {
+    return 1;
+  }
+
+  if (ssl->s3->pending_flight->length > 0xffffffff ||
+      ssl->s3->pending_flight->length > INT_MAX) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return -1;
   }
 
-  int ret = ssl3_do_write(ssl, SSL3_RT_HANDSHAKE, ssl->s3->pending_message,
-                          ssl->s3->pending_message_len);
-  if (ret <= 0) {
-    return ret;
+  /* The handshake flight buffer is mutually exclusive with application data.
+   *
+   * TODO(davidben): This will not be true when closure alerts use this. */
+  if (ssl_write_buffer_is_pending(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
 
-  OPENSSL_free(ssl->s3->pending_message);
-  ssl->s3->pending_message = NULL;
-  ssl->s3->pending_message_len = 0;
+  /* Write the pending flight. */
+  while (ssl->s3->pending_flight_offset < ssl->s3->pending_flight->length) {
+    int ret = BIO_write(
+        ssl->wbio,
+        ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
+        ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset);
+    if (ret <= 0) {
+      ssl->rwstate = SSL_WRITING;
+      return ret;
+    }
+
+    ssl->s3->pending_flight_offset += ret;
+  }
+
+  if (BIO_flush(ssl->wbio) <= 0) {
+    ssl->rwstate = SSL_WRITING;
+    return -1;
+  }
+
+  BUF_MEM_free(ssl->s3->pending_flight);
+  ssl->s3->pending_flight = NULL;
+  ssl->s3->pending_flight_offset = 0;
   return 1;
 }
 
-int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b) {
+int ssl3_send_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == b) {
-    return ssl->method->write_message(ssl);
-  }
-
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len =
       ssl->s3->enc_method->final_finish_mac(ssl, ssl->server, finished);
@@ -307,24 +387,26 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, finished, finished_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = b;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 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);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED)) {
+    return -1;
+  }
+
   /* Snapshot the finished hash before incorporating the new message. */
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len =
@@ -365,18 +447,11 @@
   return 1;
 }
 
-int ssl3_send_change_cipher_spec(SSL *ssl) {
-  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
-
-  return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                       sizeof(kChangeCipherSpec));
-}
-
 int ssl3_output_cert_chain(SSL *ssl) {
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
       !ssl_add_cert_chain(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return 0;
@@ -431,7 +506,7 @@
   return 1;
 }
 
-static int read_v2_client_hello(SSL *ssl, int *out_is_v2_client_hello) {
+static int read_v2_client_hello(SSL *ssl) {
   /* Read the first 5 bytes, the size of the TLS record header. This is
    * sufficient to detect a V2ClientHello and ensures that we never read beyond
    * the first record. */
@@ -459,7 +534,6 @@
   if ((p[0] & 0x80) == 0 || p[2] != SSL2_MT_CLIENT_HELLO ||
       p[3] != SSL3_VERSION_MAJOR) {
     /* Not a V2ClientHello. */
-    *out_is_v2_client_hello = 0;
     return 1;
   }
 
@@ -581,12 +655,13 @@
   ssl_read_buffer_consume(ssl, 2 + msg_length);
   ssl_read_buffer_discard(ssl);
 
-  *out_is_v2_client_hello = 1;
+  ssl->s3->is_v2_hello = 1;
+  /* This is the first message, so hs must be non-NULL. */
+  ssl->s3->hs->v2_clienthello = 1;
   return 1;
 }
 
-int ssl3_get_message(SSL *ssl, int msg_type,
-                     enum ssl_hash_message_t hash_message) {
+int ssl3_get_message(SSL *ssl) {
 again:
   /* Re-create the handshake buffer if needed. */
   if (ssl->init_buf == NULL) {
@@ -598,28 +673,17 @@
 
   if (ssl->server && !ssl->s3->v2_hello_done) {
     /* Bypass the record layer for the first message to handle V2ClientHello. */
-    assert(hash_message == ssl_hash_message);
-    int is_v2_client_hello = 0;
-    int ret = read_v2_client_hello(ssl, &is_v2_client_hello);
+    int ret = read_v2_client_hello(ssl);
     if (ret <= 0) {
       return ret;
     }
-    if (is_v2_client_hello) {
-      /* V2ClientHello is hashed separately. */
-      hash_message = ssl_dont_hash_message;
-    }
     ssl->s3->v2_hello_done = 1;
   }
 
   if (ssl->s3->tmp.reuse_message) {
-    /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
-     * ssl_dont_hash_message would have to have been applied to the previous
-     * call. */
-    assert(hash_message == ssl_hash_message);
+    /* There must be a current message. */
     assert(ssl->init_msg != NULL);
-
     ssl->s3->tmp.reuse_message = 0;
-    hash_message = ssl_dont_hash_message;
   } else {
     ssl3_release_current_message(ssl, 0 /* don't free buffer */);
   }
@@ -663,17 +727,6 @@
     goto again;
   }
 
-  if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    return -1;
-  }
-
-  /* Feed this message into MAC computation. */
-  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
-    return -1;
-  }
-
   return 1;
 }
 
@@ -682,6 +735,11 @@
 }
 
 int ssl_hash_current_message(SSL *ssl) {
+  /* V2ClientHellos are hashed implicitly. */
+  if (ssl->s3->is_v2_hello) {
+    return 1;
+  }
+
   CBS cbs;
   ssl->method->get_current_message(ssl, &cbs);
   return ssl3_update_handshake_hash(ssl, CBS_data(&cbs), CBS_len(&cbs));
@@ -696,6 +754,7 @@
     ssl->init_msg = NULL;
     ssl->init_num = 0;
     ssl->init_buf->length = 0;
+    ssl->s3->is_v2_hello = 0;
   }
 
   if (free_buffer) {
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 7039418..3f44629 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -209,7 +209,7 @@
   OPENSSL_free(ssl->s3->alpn_selected);
   SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
   SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
-  OPENSSL_free(ssl->s3->pending_message);
+  BUF_MEM_free(ssl->s3->pending_flight);
 
   OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3);
   OPENSSL_free(ssl->s3);
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 9bd9f1f..5d5b7e8 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -188,16 +188,9 @@
   return -1;
 }
 
-int ssl3_write_app_data(SSL *ssl, const void *buf, int len) {
+int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
   assert(!SSL_in_init(ssl) || SSL_in_false_start(ssl));
 
-  return ssl3_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len);
-}
-
-/* Call this to write data in records of type |type|. It will return <= 0 if
- * not all data has been sent or non-blocking IO. */
-int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, int len) {
-  const uint8_t *buf = buf_;
   unsigned tot, n, nw;
 
   assert(ssl->s3->wnum <= INT_MAX);
@@ -216,7 +209,7 @@
     return -1;
   }
 
-  n = (len - tot);
+  n = len - tot;
   for (;;) {
     /* max contains the maximum number of bytes that we can put into a
      * record. */
@@ -227,14 +220,13 @@
       nw = n;
     }
 
-    int ret = do_ssl3_write(ssl, type, &buf[tot], nw);
+    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
       return ret;
     }
 
-    if (ret == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
-                          (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
+    if (ret == (int)n || (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)) {
       return tot + ret;
     }
 
@@ -267,13 +259,12 @@
     return ssl3_write_pending(ssl, type, buf, len);
   }
 
-  /* If we have an alert to send, lets send it */
-  if (ssl->s3->alert_dispatch) {
-    int ret = ssl->method->dispatch_alert(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
-    /* if it went, fall through and send more stuff */
+  /* The handshake flight buffer is mutually exclusive with application data.
+   *
+   * TODO(davidben): This will not be true when closure alerts use this. */
+  if (ssl->s3->pending_flight != NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
@@ -364,7 +355,7 @@
       }
 
       /* Parse post-handshake handshake messages. */
-      int ret = ssl3_get_message(ssl, -1, ssl_dont_hash_message);
+      int ret = ssl3_get_message(ssl);
       if (ret <= 0) {
         return ret;
       }
@@ -456,10 +447,11 @@
     return -1;
   }
 
-  if (level == SSL3_AL_FATAL) {
-    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
-  } else if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
+  if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
     ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+  } else {
+    assert(level == SSL3_AL_FATAL);
+    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
   }
 
   ssl->s3->alert_dispatch = 1;
@@ -476,12 +468,11 @@
 }
 
 int ssl3_dispatch_alert(SSL *ssl) {
-  ssl->s3->alert_dispatch = 0;
   int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
   if (ret <= 0) {
-    ssl->s3->alert_dispatch = 1;
     return ret;
   }
+  ssl->s3->alert_dispatch = 0;
 
   /* If the alert is fatal, flush the BIO now. */
   if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index c9dfdcc..4c1ee89 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -129,10 +129,11 @@
  *     isServer                [22] BOOLEAN DEFAULT TRUE,
  *     peerSignatureAlgorithm  [23] INTEGER OPTIONAL,
  *     ticketMaxEarlyData      [24] INTEGER OPTIONAL,
+ *     authTimeout             [25] INTEGER OPTIONAL, -- defaults to timeout
  * }
  *
  * Note: historically this serialization has included other optional
- * fields. Their presense is currently treated as a parse error:
+ * fields. Their presence is currently treated as a parse error:
  *
  *     keyArg                  [0] IMPLICIT OCTET STRING OPTIONAL,
  *     pskIdentityHint         [7] OCTET STRING OPTIONAL,
@@ -183,6 +184,8 @@
     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23;
 static const int kTicketMaxEarlyDataTag =
     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24;
+static const int kAuthTimeoutTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25;
 
 static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
                                      size_t *out_len, int for_ticket) {
@@ -402,6 +405,13 @@
     goto err;
   }
 
+  if (in->timeout != in->auth_timeout &&
+      (!CBB_add_asn1(&session, &child, kAuthTimeoutTag) ||
+       !CBB_add_asn1_uint64(&child, in->auth_timeout))) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
   if (!CBB_finish(&cbb, out_data, out_len)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
@@ -787,6 +797,8 @@
                              kPeerSignatureAlgorithmTag, 0) ||
       !SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data,
                              kTicketMaxEarlyDataTag, 0) ||
+      !SSL_SESSION_parse_long(&session, &ret->auth_timeout, kAuthTimeoutTag,
+                              ret->timeout) ||
       CBS_len(&session) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 66edf37..6452620 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -152,6 +152,11 @@
   return ret;
 }
 
+static CRYPTO_BUFFER *buffer_up_ref(CRYPTO_BUFFER *buffer) {
+  CRYPTO_BUFFER_up_ref(buffer);
+  return buffer;
+}
+
 CERT *ssl_cert_dup(CERT *cert) {
   CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
@@ -160,29 +165,16 @@
   }
   OPENSSL_memset(ret, 0, sizeof(CERT));
 
-  if (cert->x509_leaf != NULL) {
-    X509_up_ref(cert->x509_leaf);
-    ret->x509_leaf = cert->x509_leaf;
-  }
+  ret->chain = sk_CRYPTO_BUFFER_deep_copy(cert->chain, buffer_up_ref,
+                                          CRYPTO_BUFFER_free);
 
   if (cert->privatekey != NULL) {
     EVP_PKEY_up_ref(cert->privatekey);
     ret->privatekey = cert->privatekey;
   }
 
-  if (cert->x509_chain) {
-    ret->x509_chain = X509_chain_up_ref(cert->x509_chain);
-    if (!ret->x509_chain) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-  }
-
   ret->key_method = cert->key_method;
 
-  ret->mask_k = cert->mask_k;
-  ret->mask_a = cert->mask_a;
-
   if (cert->dh_tmp != NULL) {
     ret->dh_tmp = DHparams_dup(cert->dh_tmp);
     if (ret->dh_tmp == NULL) {
@@ -216,18 +208,32 @@
   return NULL;
 }
 
+void ssl_cert_flush_cached_x509_leaf(CERT *cert) {
+  X509_free(cert->x509_leaf);
+  cert->x509_leaf = NULL;
+}
+
+static void ssl_cert_flush_cached_x509_chain(CERT *cert) {
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = NULL;
+}
+
 /* Free up and clear all certificates and chains */
 void ssl_cert_clear_certs(CERT *cert) {
   if (cert == NULL) {
     return;
   }
 
-  X509_free(cert->x509_leaf);
-  cert->x509_leaf = NULL;
+  ssl_cert_flush_cached_x509_leaf(cert);
+  ssl_cert_flush_cached_x509_chain(cert);
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = NULL;
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = NULL;
   EVP_PKEY_free(cert->privatekey);
   cert->privatekey = NULL;
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = NULL;
   cert->key_method = NULL;
 }
 
@@ -245,25 +251,125 @@
   OPENSSL_free(c);
 }
 
+/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
+static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
+  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
+  if (chain == NULL) {
+    return NULL;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
+    sk_CRYPTO_BUFFER_free(chain);
+    return NULL;
+  }
+
+  return chain;
+}
+
+/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
+ * contents of |x509|. */
+CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
+  uint8_t *buf = NULL;
+  int cert_len = i2d_X509(x509, &buf);
+  if (cert_len <= 0) {
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
+  OPENSSL_free(buf);
+
+  return buffer;
+}
+
+/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
+ * forms of elements of |chain|. It returns one on success or zero on error, in
+ * which case no change to |cert->chain| is made. It preverses the existing
+ * leaf from |cert->chain|, if any. */
+static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
+
+  if (cert->chain != NULL) {
+    new_chain = sk_CRYPTO_BUFFER_new_null();
+    if (new_chain == NULL) {
+      return 0;
+    }
+
+    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
+      goto err;
+    }
+    /* |leaf| might be NULL if it's a “leafless” chain. */
+    if (leaf != NULL) {
+      CRYPTO_BUFFER_up_ref(leaf);
+    }
+  }
+
+  for (size_t i = 0; i < sk_X509_num(chain); i++) {
+    if (new_chain == NULL) {
+      new_chain = new_leafless_chain();
+      if (new_chain == NULL) {
+        goto err;
+      }
+    }
+
+    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      goto err;
+    }
+  }
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = new_chain;
+
+  return 1;
+
+err:
+  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
+  return 0;
+}
+
 static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = chain;
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  sk_X509_pop_free(chain, X509_free);
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
 static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
-  STACK_OF(X509) *dchain;
-  if (chain == NULL) {
-    return ssl_cert_set0_chain(cert, NULL);
-  }
-
-  dchain = X509_chain_up_ref(chain);
-  if (dchain == NULL) {
+  if (!ssl_cert_set_chain(cert, chain)) {
     return 0;
   }
 
-  if (!ssl_cert_set0_chain(cert, dchain)) {
-    sk_X509_pop_free(dchain, X509_free);
+  ssl_cert_flush_cached_x509_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  if (cert->chain != NULL) {
+    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      return 0;
+    }
+
+    return 1;
+  }
+
+  cert->chain = new_leafless_chain();
+  if (cert->chain == NULL ||
+      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    CRYPTO_BUFFER_free(buffer);
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
     return 0;
   }
 
@@ -271,22 +377,22 @@
 }
 
 static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
-  if (cert->x509_chain == NULL) {
-    cert->x509_chain = sk_X509_new_null();
-  }
-  if (cert->x509_chain == NULL || !sk_X509_push(cert->x509_chain, x509)) {
+  if (!ssl_cert_append_cert(cert, x509)) {
     return 0;
   }
 
+  X509_free(cert->x509_stash);
+  cert->x509_stash = x509;
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
 static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_add0_chain_cert(cert, x509)) {
+  if (!ssl_cert_append_cert(cert, x509)) {
     return 0;
   }
 
-  X509_up_ref(x509);
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
@@ -446,7 +552,9 @@
 }
 
 int ssl_has_certificate(const SSL *ssl) {
-  return ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl);
+  return ssl->cert->chain != NULL &&
+         sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0) != NULL &&
+         ssl_has_private_key(ssl);
 }
 
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
@@ -515,49 +623,33 @@
   return NULL;
 }
 
-int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509) {
-  int len = i2d_X509(x509, NULL);
-  if (len < 0) {
-    return 0;
-  }
-  uint8_t *buf;
-  if (!CBB_add_space(cbb, &buf, len)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-  if (buf != NULL && i2d_X509(x509, &buf) < 0) {
-    return 0;
-  }
-  return 1;
-}
-
-static int ssl_add_cert_with_length(CBB *cbb, X509 *x509) {
-  CBB child;
-  return CBB_add_u24_length_prefixed(cbb, &child) &&
-         ssl_add_cert_to_cbb(&child, x509) &&
-         CBB_flush(cbb);
-}
-
 int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
   if (!ssl_has_certificate(ssl)) {
     return CBB_add_u24(cbb, 0);
   }
 
-  CBB child;
-  if (!CBB_add_u24_length_prefixed(cbb, &child) ||
-      !ssl_add_cert_with_length(&child, ssl->cert->x509_leaf)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+  CBB certs;
+  if (!CBB_add_u24_length_prefixed(cbb, &certs)) {
+    goto err;
   }
 
-  STACK_OF(X509) *chain = ssl->cert->x509_chain;
-  for (size_t i = 0; i < sk_X509_num(chain); i++) {
-    if (!ssl_add_cert_with_length(&child, sk_X509_value(chain, i))) {
-      return 0;
+  STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain;
+  for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(chain, i);
+    CBB child;
+    if (!CBB_add_u24_length_prefixed(&certs, &child) ||
+        !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
+                       CRYPTO_BUFFER_len(buffer)) ||
+        !CBB_flush(&certs)) {
+      goto err;
     }
   }
 
   return CBB_flush(cbb);
+
+err:
+  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  return 0;
 }
 
 int ssl_auto_chain_if_needed(SSL *ssl) {
@@ -565,30 +657,41 @@
    * isn't disabled. */
   if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
       !ssl_has_certificate(ssl) ||
-      ssl->cert->x509_chain != NULL) {
+      ssl->cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
     return 1;
   }
 
+  X509 *leaf =
+      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
+  if (!leaf) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
   X509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, ssl->cert->x509_leaf,
-                           NULL)) {
+  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
+    X509_free(leaf);
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
 
   /* Attempt to build a chain, ignoring the result. */
   X509_verify_cert(&ctx);
+  X509_free(leaf);
   ERR_clear_error();
 
-  /* Configure the intermediates from any partial chain we managed to build. */
-  for (size_t i = 1; i < sk_X509_num(ctx.chain); i++) {
-    if (!SSL_add1_chain_cert(ssl, sk_X509_value(ctx.chain, i))) {
-      X509_STORE_CTX_cleanup(&ctx);
-      return 0;
-    }
+  /* Remove the leaf from the generated chain. */
+  X509_free(sk_X509_shift(ctx.chain));
+
+  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
+  X509_STORE_CTX_cleanup(&ctx);
+  if (!ok) {
+    return 0;
   }
 
-  X509_STORE_CTX_cleanup(&ctx);
+  ssl_cert_flush_cached_x509_chain(ssl->cert);
+
   return 1;
 }
 
@@ -647,6 +750,55 @@
   return EVP_parse_public_key(&tbs_cert);
 }
 
+int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+                                       const EVP_PKEY *privkey) {
+  int ret = 0;
+
+  switch (EVP_PKEY_cmp(pubkey, privkey)) {
+    case 1:
+      ret = 1;
+      break;
+    case 0:
+      OPENSSL_PUT_ERROR(X509, X509_R_KEY_VALUES_MISMATCH);
+      break;
+    case -1:
+      OPENSSL_PUT_ERROR(X509, X509_R_KEY_TYPE_MISMATCH);
+      break;
+    case -2:
+      OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
+    default:
+      assert(0);
+      break;
+  }
+
+  return ret;
+}
+
+int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey) {
+  if (privkey == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
+    return 0;
+  }
+
+  if (cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_value(cert->chain, 0) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
+    return 0;
+  }
+
+  CBS cert_cbs;
+  CRYPTO_BUFFER_init_CBS(sk_CRYPTO_BUFFER_value(cert->chain, 0), &cert_cbs);
+  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
+  if (!pubkey) {
+    OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
+    return 0;
+  }
+
+  const int ok = ssl_compare_public_and_private_key(pubkey, privkey);
+  EVP_PKEY_free(pubkey);
+  return ok;
+}
+
 int ssl_cert_check_digital_signature_key_usage(const CBS *in) {
   CBS buf = *in;
 
@@ -891,7 +1043,61 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
+/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
+ * first element of |cert->chain|. */
+int ssl_cert_cache_leaf_cert(CERT *cert) {
+  if (cert->x509_leaf != NULL ||
+      cert->chain == NULL) {
+    return 1;
+  }
+
+  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+  if (!leaf) {
+    return 1;
+  }
+
+  cert->x509_leaf = X509_parse_from_buffer(leaf);
+  return cert->x509_leaf != NULL;
+}
+
+/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
+ * |cert->chain|. */
+static int ssl_cert_cache_chain_certs(CERT *cert) {
+  if (cert->x509_chain != NULL ||
+      cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
+    return 1;
+  }
+
+  STACK_OF(X509) *chain = sk_X509_new_null();
+  if (chain == NULL) {
+    return 0;
+  }
+
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
+    X509 *x509 = X509_parse_from_buffer(buffer);
+    if (x509 == NULL ||
+        !sk_X509_push(chain, x509)) {
+      X509_free(x509);
+      goto err;
+    }
+  }
+
+  cert->x509_chain = chain;
+  return 1;
+
+err:
+  sk_X509_pop_free(chain, X509_free);
+  return 0;
+}
+
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
   *out_chain = ctx->cert->x509_chain;
   return 1;
 }
@@ -902,6 +1108,11 @@
 }
 
 int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
   *out_chain = ssl->cert->x509_chain;
   return 1;
 }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index ba1ee8f..851c81f 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -254,8 +254,8 @@
   ret->session_cache_mode = SSL_SESS_CACHE_SERVER;
   ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
 
-  /* We take the system default */
   ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  ret->session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT;
 
   ret->references = 1;
 
@@ -425,22 +425,21 @@
   ssl->initial_ctx = ctx;
 
   if (ctx->supported_group_list) {
-    ssl->supported_group_list =
-        BUF_memdup(ctx->supported_group_list,
-                   ctx->supported_group_list_len * 2);
+    ssl->supported_group_list = BUF_memdup(ctx->supported_group_list,
+                                           ctx->supported_group_list_len * 2);
     if (!ssl->supported_group_list) {
       goto err;
     }
     ssl->supported_group_list_len = ctx->supported_group_list_len;
   }
 
-  if (ssl->ctx->alpn_client_proto_list) {
-    ssl->alpn_client_proto_list = BUF_memdup(
-        ssl->ctx->alpn_client_proto_list, ssl->ctx->alpn_client_proto_list_len);
+  if (ctx->alpn_client_proto_list) {
+    ssl->alpn_client_proto_list = BUF_memdup(ctx->alpn_client_proto_list,
+                                             ctx->alpn_client_proto_list_len);
     if (ssl->alpn_client_proto_list == NULL) {
       goto err;
     }
-    ssl->alpn_client_proto_list_len = ssl->ctx->alpn_client_proto_list_len;
+    ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
   }
 
   ssl->method = ctx->method;
@@ -469,16 +468,11 @@
     ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
   }
 
-  ssl->signed_cert_timestamps_enabled =
-      ssl->ctx->signed_cert_timestamps_enabled;
-  ssl->ocsp_stapling_enabled = ssl->ctx->ocsp_stapling_enabled;
+  ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
+  ssl->ocsp_stapling_enabled = 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;
-  }
+  ssl->session_timeout = ctx->session_timeout;
+  ssl->session_psk_dhe_timeout = ctx->session_psk_dhe_timeout;
 
   /* If the context has an OCSP response, use it. */
   if (ctx->ocsp_response != NULL) {
@@ -504,9 +498,6 @@
 
   CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
 
-  ssl_free_wbio_buffer(ssl);
-  assert(ssl->bbio == NULL);
-
   BIO_free_all(ssl->rbio);
   BIO_free_all(ssl->wbio);
 
@@ -553,18 +544,8 @@
 }
 
 void SSL_set0_wbio(SSL *ssl, BIO *wbio) {
-  /* If the output buffering BIO is still in place, remove it. */
-  if (ssl->bbio != NULL) {
-    ssl->wbio = BIO_pop(ssl->wbio);
-  }
-
   BIO_free_all(ssl->wbio);
   ssl->wbio = wbio;
-
-  /* Re-attach |bbio| to the new |wbio|. */
-  if (ssl->bbio != NULL) {
-    ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
-  }
 }
 
 void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
@@ -603,14 +584,7 @@
 
 BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; }
 
-BIO *SSL_get_wbio(const SSL *ssl) {
-  if (ssl->bbio != NULL) {
-    /* If |bbio| is active, the true caller-configured BIO is its |next_bio|. */
-    assert(ssl->bbio == ssl->wbio);
-    return ssl->bbio->next_bio;
-  }
-  return ssl->wbio;
-}
+BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
 
 void ssl_reset_error_state(SSL *ssl) {
   /* Functions which use |SSL_get_error| must reset I/O and error state on
@@ -1292,34 +1266,12 @@
 
 /* Fix this so it checks all the valid key/cert options */
 int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
-  if (ctx->cert->privatekey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
-    return 0;
-  }
-
-  X509 *x509 = ctx->cert->x509_leaf;
-  if (x509 == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
-  }
-
-  return X509_check_private_key(x509, ctx->cert->privatekey);
+  return ssl_cert_check_private_key(ctx->cert, ctx->cert->privatekey);
 }
 
 /* Fix this function so that it takes an optional type parameter */
 int SSL_check_private_key(const SSL *ssl) {
-  if (ssl->cert->privatekey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
-    return 0;
-  }
-
-  X509 *x509 = ssl->cert->x509_leaf;
-  if (x509 == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
-  }
-
-  return X509_check_private_key(x509, ssl->cert->privatekey);
+  return ssl_cert_check_private_key(ssl->cert, ssl->cert->privatekey);
 }
 
 long SSL_get_default_timeout(const SSL *ssl) {
@@ -2023,41 +1975,6 @@
 
 int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
 
-int ssl_is_wbio_buffered(const SSL *ssl) {
-  return ssl->bbio != NULL;
-}
-
-int ssl_init_wbio_buffer(SSL *ssl) {
-  if (ssl->bbio != NULL) {
-    /* Already buffered. */
-    assert(ssl->bbio == ssl->wbio);
-    return 1;
-  }
-
-  BIO *bbio = BIO_new(BIO_f_buffer());
-  if (bbio == NULL ||
-      !BIO_set_read_buffer_size(bbio, 1)) {
-    BIO_free(bbio);
-    return 0;
-  }
-
-  ssl->bbio = bbio;
-  ssl->wbio = BIO_push(bbio, ssl->wbio);
-  return 1;
-}
-
-void ssl_free_wbio_buffer(SSL *ssl) {
-  if (ssl->bbio == NULL) {
-    return;
-  }
-
-  assert(ssl->bbio == ssl->wbio);
-
-  ssl->wbio = BIO_pop(ssl->wbio);
-  BIO_free(ssl->bbio);
-  ssl->bbio = NULL;
-}
-
 void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
   ctx->quiet_shutdown = (mode != 0);
 }
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 34d1f86..6ad2b71 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -70,8 +70,8 @@
 #include "internal.h"
 
 
-static int ssl_set_cert(CERT *c, X509 *x509);
-static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey);
+static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
+static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey);
 
 static int is_key_type_supported(int key_type) {
   return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
@@ -82,26 +82,26 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  return ssl_set_cert(ssl->cert, x);
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(ssl->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
-  if (der_len > LONG_MAX) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
     return 0;
   }
 
-  const uint8_t *p = der;
-  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
-  if (x509 == NULL || p != der + der_len) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-    X509_free(x509);
-    return 0;
-  }
-
-  int ret = SSL_use_certificate(ssl, x509);
-  X509_free(x509);
-  return ret;
+  const int ok = ssl_set_cert(ssl->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) {
@@ -128,41 +128,35 @@
   return ret;
 }
 
-static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey) {
+static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
   if (!is_key_type_supported(pkey->type)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
     return 0;
   }
 
-  X509 *x509_leaf = c->x509_leaf;
-  if (x509_leaf != NULL) {
-    /* Sanity-check that the private key and the certificate match, unless the
-     * key is opaque (in case of, say, a smartcard). */
-    if (!EVP_PKEY_is_opaque(pkey) &&
-        !X509_check_private_key(x509_leaf, pkey)) {
-      X509_free(c->x509_leaf);
-      c->x509_leaf = NULL;
-      return 0;
-    }
+  if (cert->chain != NULL &&
+      sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL &&
+      /* Sanity-check that the private key and the certificate match, unless
+       * the key is opaque (in case of, say, a smartcard). */
+      !EVP_PKEY_is_opaque(pkey) &&
+      !ssl_cert_check_private_key(cert, pkey)) {
+    return 0;
   }
 
-  EVP_PKEY_free(c->privatekey);
+  EVP_PKEY_free(cert->privatekey);
   EVP_PKEY_up_ref(pkey);
-  c->privatekey = pkey;
+  cert->privatekey = pkey;
 
   return 1;
 }
 
 int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
-  int ret;
-
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
-  ret = ssl_set_pkey(ssl->cert, pkey);
-  return ret;
+  return ssl_set_pkey(ssl->cert, pkey);
 }
 
 int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der,
@@ -191,77 +185,90 @@
     return 0;
   }
 
-  return ssl_set_cert(ctx->cert, x);
-}
-
-static int ssl_set_cert(CERT *c, X509 *x) {
-  EVP_PKEY *pkey = X509_get_pubkey(x);
-  if (pkey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_X509_LIB);
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
     return 0;
   }
 
-  if (!is_key_type_supported(pkey->type)) {
+  const int ok = ssl_set_cert(ctx->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
+static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+  CBS cert_cbs;
+  CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
+  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
+  if (pubkey == NULL) {
+    return 0;
+  }
+
+  if (!is_key_type_supported(pubkey->type)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    EVP_PKEY_free(pkey);
+    EVP_PKEY_free(pubkey);
     return 0;
   }
 
   /* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
    * certificates, so sanity-check the key usage extension. */
-  if (pkey->type == EVP_PKEY_EC) {
-    /* This call populates extension flags (ex_flags). */
-    X509_check_purpose(x, -1, 0);
-    if ((x->ex_flags & EXFLAG_KUSAGE) &&
-        !(x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-      EVP_PKEY_free(pkey);
-      return 0;
-    }
+  if (pubkey->type == EVP_PKEY_EC &&
+      !ssl_cert_check_digital_signature_key_usage(&cert_cbs)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+    EVP_PKEY_free(pubkey);
+    return 0;
   }
 
-  if (c->privatekey != NULL) {
+  if (cert->privatekey != NULL) {
     /* Sanity-check that the private key and the certificate match, unless the
      * key is opaque (in case of, say, a smartcard). */
-    if (!EVP_PKEY_is_opaque(c->privatekey) &&
-        !X509_check_private_key(x, c->privatekey)) {
+    if (!EVP_PKEY_is_opaque(cert->privatekey) &&
+        !ssl_compare_public_and_private_key(pubkey, cert->privatekey)) {
       /* don't fail for a cert/key mismatch, just free current private key
        * (when switching to a different cert & key, first this function should
        * be used, then ssl_set_pkey */
-      EVP_PKEY_free(c->privatekey);
-      c->privatekey = NULL;
+      EVP_PKEY_free(cert->privatekey);
+      cert->privatekey = NULL;
       /* clear error queue */
       ERR_clear_error();
     }
   }
 
-  EVP_PKEY_free(pkey);
+  EVP_PKEY_free(pubkey);
 
-  X509_free(c->x509_leaf);
-  X509_up_ref(x);
-  c->x509_leaf = x;
+  ssl_cert_flush_cached_x509_leaf(cert);
+
+  if (cert->chain != NULL) {
+    CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
+    sk_CRYPTO_BUFFER_set(cert->chain, 0, buffer);
+    CRYPTO_BUFFER_up_ref(buffer);
+    return 1;
+  }
+
+  cert->chain = sk_CRYPTO_BUFFER_new_null();
+  if (cert->chain == NULL) {
+    return 0;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
+    return 0;
+  }
+  CRYPTO_BUFFER_up_ref(buffer);
 
   return 1;
 }
 
 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len,
                                  const uint8_t *der) {
-  if (der_len > LONG_MAX) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
     return 0;
   }
 
-  const uint8_t *p = der;
-  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
-  if (x509 == NULL || p != der + der_len) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-    X509_free(x509);
-    return 0;
-  }
-
-  int ret = SSL_CTX_use_certificate(ctx, x509);
-  X509_free(x509);
-  return ret;
+  const int ok = ssl_set_cert(ctx->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) {
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 7adef1a..5dffc70 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -171,6 +171,7 @@
   session->verify_result = X509_V_ERR_INVALID_CALL;
   session->references = 1;
   session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  session->auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   session->time = (long)time(NULL);
   CRYPTO_new_ex_data(&session->ex_data);
   return session;
@@ -259,6 +260,7 @@
   new_session->peer_signature_algorithm = session->peer_signature_algorithm;
 
   new_session->timeout = session->timeout;
+  new_session->auth_timeout = session->auth_timeout;
   new_session->time = session->time;
 
   /* Copy non-authentication connection properties. */
@@ -303,7 +305,7 @@
   return 0;
 }
 
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session) {
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session) {
   struct timeval now;
   ssl_get_current_time(ssl, &now);
 
@@ -314,11 +316,12 @@
       now.tv_sec < 0) {
     session->time = now.tv_sec;
     session->timeout = 0;
+    session->auth_timeout = 0;
     return;
   }
 
-  /* Adjust the session time and timeout. If the session has already expired,
-   * clamp the timeout at zero. */
+  /* Adjust the session time and timeouts. If the session has already expired,
+   * clamp the timeouts at zero. */
   long delta = now.tv_sec - session->time;
   session->time = now.tv_sec;
   if (session->timeout < delta) {
@@ -326,6 +329,26 @@
   } else {
     session->timeout -= delta;
   }
+  if (session->auth_timeout < delta) {
+    session->auth_timeout = 0;
+  } else {
+    session->auth_timeout -= delta;
+  }
+}
+
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout) {
+  /* Rebase the timestamp relative to the current time so |timeout| is measured
+   * correctly. */
+  ssl_session_rebase_time(ssl, session);
+
+  if (session->timeout > timeout) {
+    return;
+  }
+
+  session->timeout = timeout;
+  if (session->timeout > session->auth_timeout) {
+    session->timeout = session->auth_timeout;
+  }
 }
 
 int SSL_SESSION_up_ref(SSL_SESSION *session) {
@@ -408,6 +431,7 @@
   }
 
   session->timeout = timeout;
+  session->auth_timeout = timeout;
   return 1;
 }
 
@@ -490,10 +514,21 @@
   ssl_get_current_time(ssl, &now);
   session->time = now.tv_sec;
 
-  session->timeout = ssl->session_timeout;
+  uint16_t version = ssl3_protocol_version(ssl);
+  if (version >= TLS1_3_VERSION) {
+    /* TLS 1.3 uses tickets as authenticators, so we are willing to use them for
+     * longer. */
+    session->timeout = ssl->session_psk_dhe_timeout;
+    session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
+  } else {
+    /* TLS 1.2 resumption does not incorporate new key material, so we use a
+     * much shorter timeout. */
+    session->timeout = ssl->session_timeout;
+    session->auth_timeout = ssl->session_timeout;
+  }
 
   if (is_server) {
-    if (hs->ticket_expected) {
+    if (hs->ticket_expected || version >= TLS1_3_VERSION) {
       /* Don't set session IDs for sessions resumed with tickets. This will keep
        * them out of the session cache. */
       session->session_id_length = 0;
@@ -934,6 +969,11 @@
     return 0;
   }
 
+  /* Historically, zero was treated as |SSL_DEFAULT_SESSION_TIMEOUT|. */
+  if (timeout == 0) {
+    timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  }
+
   long old_timeout = ctx->session_timeout;
   ctx->session_timeout = timeout;
   return old_timeout;
@@ -947,12 +987,20 @@
   return ctx->session_timeout;
 }
 
+void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx, long timeout) {
+  ctx->session_psk_dhe_timeout = timeout;
+}
+
 long SSL_set_session_timeout(SSL *ssl, long timeout) {
   long old_timeout = ssl->session_timeout;
   ssl->session_timeout = timeout;
   return old_timeout;
 }
 
+void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout) {
+  ssl->session_psk_dhe_timeout = timeout;
+}
+
 typedef struct timeout_param_st {
   SSL_CTX *ctx;
   long time;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index d657788..09a43d1 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -104,9 +104,6 @@
     case SSL3_ST_CW_CLNT_HELLO_A:
       return "SSLv3 write client hello A";
 
-    case SSL3_ST_CW_CLNT_HELLO_B:
-      return "SSLv3 write client hello B";
-
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "SSLv3 read server hello A";
 
@@ -128,15 +125,9 @@
     case SSL3_ST_CW_CERT_A:
       return "SSLv3 write client certificate A";
 
-    case SSL3_ST_CW_CERT_B:
-      return "SSLv3 write client certificate B";
-
     case SSL3_ST_CW_KEY_EXCH_A:
       return "SSLv3 write client key exchange A";
 
-    case SSL3_ST_CW_KEY_EXCH_B:
-      return "SSLv3 write client key exchange B";
-
     case SSL3_ST_CW_CERT_VRFY_A:
       return "SSLv3 write certificate verify A";
 
@@ -151,10 +142,6 @@
     case SSL3_ST_SW_FINISHED_A:
       return "SSLv3 write finished A";
 
-    case SSL3_ST_CW_FINISHED_B:
-    case SSL3_ST_SW_FINISHED_B:
-      return "SSLv3 write finished B";
-
     case SSL3_ST_CR_CHANGE:
     case SSL3_ST_SR_CHANGE:
       return "SSLv3 read change cipher spec";
@@ -188,39 +175,21 @@
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "SSLv3 write server hello A";
 
-    case SSL3_ST_SW_SRVR_HELLO_B:
-      return "SSLv3 write server hello B";
-
     case SSL3_ST_SW_CERT_A:
       return "SSLv3 write certificate A";
 
-    case SSL3_ST_SW_CERT_B:
-      return "SSLv3 write certificate B";
-
     case SSL3_ST_SW_KEY_EXCH_A:
       return "SSLv3 write key exchange A";
 
-    case SSL3_ST_SW_KEY_EXCH_B:
-      return "SSLv3 write key exchange B";
-
     case SSL3_ST_SW_CERT_REQ_A:
       return "SSLv3 write certificate request A";
 
-    case SSL3_ST_SW_CERT_REQ_B:
-      return "SSLv3 write certificate request B";
-
     case SSL3_ST_SW_SESSION_TICKET_A:
       return "SSLv3 write session ticket A";
 
-    case SSL3_ST_SW_SESSION_TICKET_B:
-      return "SSLv3 write session ticket B";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "SSLv3 write server done A";
 
-    case SSL3_ST_SW_SRVR_DONE_B:
-      return "SSLv3 write server done B";
-
     case SSL3_ST_SR_CERT_A:
       return "SSLv3 read client certificate A";
 
@@ -261,9 +230,6 @@
     case SSL3_ST_CW_CLNT_HELLO_A:
       return "3WCH_A";
 
-    case SSL3_ST_CW_CLNT_HELLO_B:
-      return "3WCH_B";
-
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "3RSH_A";
 
@@ -282,15 +248,9 @@
     case SSL3_ST_CW_CERT_A:
       return "3WCC_A";
 
-    case SSL3_ST_CW_CERT_B:
-      return "3WCC_B";
-
     case SSL3_ST_CW_KEY_EXCH_A:
       return "3WCKEA";
 
-    case SSL3_ST_CW_KEY_EXCH_B:
-      return "3WCKEB";
-
     case SSL3_ST_CW_CERT_VRFY_A:
       return "3WCV_A";
 
@@ -305,10 +265,6 @@
     case SSL3_ST_CW_FINISHED_A:
       return "3WFINA";
 
-    case SSL3_ST_SW_FINISHED_B:
-    case SSL3_ST_CW_FINISHED_B:
-      return "3WFINB";
-
     case SSL3_ST_CR_CHANGE:
     case SSL3_ST_SR_CHANGE:
       return "3RCCS_";
@@ -338,15 +294,9 @@
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "3WSH_A";
 
-    case SSL3_ST_SW_SRVR_HELLO_B:
-      return "3WSH_B";
-
     case SSL3_ST_SW_CERT_A:
       return "3WSC_A";
 
-    case SSL3_ST_SW_CERT_B:
-      return "3WSC_B";
-
     case SSL3_ST_SW_KEY_EXCH_A:
       return "3WSKEA";
 
@@ -356,15 +306,9 @@
     case SSL3_ST_SW_CERT_REQ_A:
       return "3WCR_A";
 
-    case SSL3_ST_SW_CERT_REQ_B:
-      return "3WCR_B";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "3WSD_A";
 
-    case SSL3_ST_SW_SRVR_DONE_B:
-      return "3WSD_B";
-
     case SSL3_ST_SR_CERT_A:
       return "3RCC_A";
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 22a9b7b..aa265c8 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -2154,6 +2154,11 @@
   *out_clock = g_current_time;
 }
 
+static void FrozenTimeCallback(const SSL *ssl, timeval *out_clock) {
+  out_clock->tv_sec = 1000;
+  out_clock->tv_usec = 0;
+}
+
 static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
                                EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
                                int encrypt) {
@@ -2221,9 +2226,15 @@
   }
 
   for (bool server_test : std::vector<bool>{false, true}) {
-    static const int kStartTime = 1000;
+    static const time_t kStartTime = 1000;
     g_current_time.tv_sec = kStartTime;
 
+    // We are willing to use a longer lifetime for TLS 1.3 sessions as
+    // resumptions still perform ECDHE.
+    const time_t timeout = version == TLS1_3_VERSION
+                               ? SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT
+                               : SSL_DEFAULT_SESSION_TIMEOUT;
+
     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 ||
@@ -2239,11 +2250,14 @@
     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);
 
-    // Both client and server must enforce session timeouts.
+    // Both client and server must enforce session timeouts. We configure the
+    // other side with a frozen clock so it never expires tickets.
     if (server_test) {
+      SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback);
       SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
     } else {
       SSL_CTX_set_current_time_cb(client_ctx.get(), CurrentTimeCallback);
+      SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback);
     }
 
     // Configure a ticket callback which renews tickets.
@@ -2257,7 +2271,7 @@
     }
 
     // Advance the clock just behind the timeout.
-    g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT - 1;
+    g_current_time.tv_sec += timeout - 1;
 
     if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
                              true /* expect session reused */)) {
@@ -2289,7 +2303,8 @@
     }
 
     // Renew the session 10 seconds before expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 10;
+    time_t new_start_time = kStartTime + timeout - 10;
+    g_current_time.tv_sec = new_start_time;
     bssl::UniquePtr<SSL_SESSION> new_session =
         ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), session.get());
     if (!new_session) {
@@ -2319,23 +2334,76 @@
       return false;
     }
 
-    // The new session is usable just before the old expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 1;
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                             new_session.get(),
-                             true /* expect session reused */)) {
-      fprintf(stderr, "Error resuming renewed session.\n");
-      return false;
-    }
+    if (version == TLS1_3_VERSION) {
+      // Renewal incorporates fresh key material in TLS 1.3, so we extend the
+      // lifetime TLS 1.3.
+      g_current_time.tv_sec = new_start_time + timeout - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
 
-    // Renewal does not extend the lifetime, so it is not usable beyond the
-    // old expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT + 1;
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                             new_session.get(),
-                             false /* expect session not reused */)) {
-      fprintf(stderr, "Renewed session's lifetime is too long.\n");
-      return false;
+      // The new session expires after the new timeout.
+      g_current_time.tv_sec = new_start_time + timeout + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session ot reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
+
+      // Renew the session until it begins just past the auth timeout.
+      time_t auth_end_time = kStartTime + SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
+      while (new_start_time < auth_end_time - 1000) {
+        // Get as close as possible to target start time.
+        new_start_time =
+            std::min(auth_end_time - 1000, new_start_time + timeout - 1);
+        g_current_time.tv_sec = new_start_time;
+        new_session = ExpectSessionRenewed(client_ctx.get(), server_ctx.get(),
+                                           new_session.get());
+        if (!new_session) {
+          fprintf(stderr, "Error renewing session.\n");
+          return false;
+        }
+      }
+
+      // Now the session's lifetime is bound by the auth timeout.
+      g_current_time.tv_sec = auth_end_time - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
+
+      g_current_time.tv_sec = auth_end_time + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session ot reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
+    } else {
+      // The new session is usable just before the old expiration.
+      g_current_time.tv_sec = kStartTime + timeout - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
+
+      // Renewal does not extend the lifetime, so it is not usable beyond the
+      // old expiration.
+      g_current_time.tv_sec = kStartTime + timeout + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session not reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
     }
   }
 
@@ -2348,9 +2416,20 @@
   return 1;
 }
 
+static int SetSessionTimeoutCallbackTLS13(SSL *ssl, void *arg) {
+  long timeout = *(long *) arg;
+  SSL_set_session_psk_dhe_timeout(ssl, timeout);
+  return 1;
+}
+
 static bool TestSessionTimeoutCertCallback(bool is_dtls,
                                            const SSL_METHOD *method,
                                            uint16_t version) {
+  if (version == TLS1_3_VERSION) {
+    // |SSL_set_session_timeout| only applies to TLS 1.2 style resumption.
+    return true;
+  }
+
   static const int kStartTime = 1000;
   g_current_time.tv_sec = kStartTime;
 
@@ -2378,7 +2457,12 @@
   SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
 
   long timeout = 25;
-  SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+  if (version == TLS1_3_VERSION) {
+    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallbackTLS13,
+                        &timeout);
+  } else {
+    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+  }
 
   bssl::UniquePtr<SSL_SESSION> session =
       CreateClientSession(client_ctx.get(), server_ctx.get());
@@ -2428,7 +2512,11 @@
   timeout = 25;
   g_current_time.tv_sec = kStartTime;
 
-  SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+  if (version == TLS1_3_VERSION) {
+    SSL_CTX_set_session_psk_dhe_timeout(server_ctx.get(), timeout - 10);
+  } else {
+    SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+  }
 
   bssl::UniquePtr<SSL_SESSION> ctx_and_cb_session =
       CreateClientSession(client_ctx.get(), server_ctx.get());
@@ -3142,6 +3230,61 @@
   return true;
 }
 
+TEST(SSLTest, AddChainCertHack) {
+  // Ensure that we don't accidently break the hack that we have in place to
+  // keep curl and serf happy when they use an |X509| even after transfering
+  // ownership.
+
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  X509 *cert = GetTestCertificate().release();
+  ASSERT_TRUE(cert);
+  SSL_CTX_add0_chain_cert(ctx.get(), cert);
+
+  // This should not trigger a use-after-free.
+  X509_cmp(cert, cert);
+}
+
+TEST(SSLTest, GetCertificate) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
+  bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
+  ASSERT_TRUE(ssl);
+
+  X509 *cert2 = SSL_CTX_get0_certificate(ctx.get());
+  ASSERT_TRUE(cert2);
+  X509 *cert3 = SSL_get_certificate(ssl.get());
+  ASSERT_TRUE(cert3);
+
+  // The old and new certificates must be identical.
+  EXPECT_EQ(0, X509_cmp(cert.get(), cert2));
+  EXPECT_EQ(0, X509_cmp(cert.get(), cert3));
+
+  uint8_t *der = nullptr;
+  long der_len = i2d_X509(cert.get(), &der);
+  ASSERT_LT(0, der_len);
+  bssl::UniquePtr<uint8_t> free_der(der);
+
+  uint8_t *der2 = nullptr;
+  long der2_len = i2d_X509(cert2, &der2);
+  ASSERT_LT(0, der2_len);
+  bssl::UniquePtr<uint8_t> free_der2(der2);
+
+  uint8_t *der3 = nullptr;
+  long der3_len = i2d_X509(cert3, &der3);
+  ASSERT_LT(0, der3_len);
+  bssl::UniquePtr<uint8_t> free_der3(der3);
+
+  // They must also encode identically.
+  ASSERT_EQ(der2_len, der_len);
+  EXPECT_EQ(0, OPENSSL_memcmp(der, der2, static_cast<size_t>(der_len)));
+  ASSERT_EQ(der3_len, der_len);
+  EXPECT_EQ(0, OPENSSL_memcmp(der, der3, static_cast<size_t>(der_len)));
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestCipherRules() ||
diff --git a/src/ssl/ssl_x509.c b/src/ssl/ssl_x509.c
index 3fc62b7..5d78deb 100644
--- a/src/ssl/ssl_x509.c
+++ b/src/ssl/ssl_x509.c
@@ -281,20 +281,21 @@
   X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 }
 
-X509 *SSL_get_certificate(const SSL *ssl) {
-  if (ssl->cert != NULL) {
-    return ssl->cert->x509_leaf;
+static X509 *ssl_cert_get0_leaf(CERT *cert) {
+  if (cert->x509_leaf == NULL &&
+      !ssl_cert_cache_leaf_cert(cert)) {
+    return NULL;
   }
 
-  return NULL;
+  return cert->x509_leaf;
+}
+
+X509 *SSL_get_certificate(const SSL *ssl) {
+  return ssl_cert_get0_leaf(ssl->cert);
 }
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  if (ctx->cert != NULL) {
-    return ctx->cert->x509_leaf;
-  }
-
-  return NULL;
+  return ssl_cert_get0_leaf(ctx->cert);
 }
 
 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index ec5dce0..6fdef45 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -527,56 +527,6 @@
   return 0;
 }
 
-/* Get a mask of disabled algorithms: an algorithm is disabled if it isn't
- * supported or doesn't appear in supported signature algorithms. Unlike
- * ssl_cipher_get_disabled this applies to a specific session and not global
- * settings. */
-void ssl_set_client_disabled(SSL *ssl) {
-  CERT *c = ssl->cert;
-  int have_rsa = 0, have_ecdsa = 0;
-  c->mask_a = 0;
-  c->mask_k = 0;
-
-  /* Now go through all signature algorithms seeing if we support any for RSA or
-   * ECDSA. Do this for all versions not just TLS 1.2. */
-  const uint16_t *sigalgs;
-  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
-  for (size_t i = 0; i < num_sigalgs; i++) {
-    switch (sigalgs[i]) {
-      case SSL_SIGN_RSA_PSS_SHA512:
-      case SSL_SIGN_RSA_PSS_SHA384:
-      case SSL_SIGN_RSA_PSS_SHA256:
-      case SSL_SIGN_RSA_PKCS1_SHA512:
-      case SSL_SIGN_RSA_PKCS1_SHA384:
-      case SSL_SIGN_RSA_PKCS1_SHA256:
-      case SSL_SIGN_RSA_PKCS1_SHA1:
-        have_rsa = 1;
-        break;
-
-      case SSL_SIGN_ECDSA_SECP521R1_SHA512:
-      case SSL_SIGN_ECDSA_SECP384R1_SHA384:
-      case SSL_SIGN_ECDSA_SECP256R1_SHA256:
-      case SSL_SIGN_ECDSA_SHA1:
-        have_ecdsa = 1;
-        break;
-    }
-  }
-
-  /* Disable auth if we don't include any appropriate signature algorithms. */
-  if (!have_rsa) {
-    c->mask_a |= SSL_aRSA;
-  }
-  if (!have_ecdsa) {
-    c->mask_a |= SSL_aECDSA;
-  }
-
-  /* with PSK there must be client callback set */
-  if (!ssl->psk_client_callback) {
-    c->mask_a |= SSL_aPSK;
-    c->mask_k |= SSL_kPSK;
-  }
-}
-
 /* tls_extension represents a TLS extension that is handled internally. The
  * |init| function is called for each handshake, before any other functions of
  * the extension. Then the add and parse callbacks are called as needed.
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 12f0b46..66a71a0 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1254,6 +1254,17 @@
     }
   }
 
+  if (!is_resume) {
+    if (config->expect_session_id && !GetTestState(ssl)->got_new_session) {
+      fprintf(stderr, "session was not cached on the server.\n");
+      return false;
+    }
+    if (config->expect_no_session_id && GetTestState(ssl)->got_new_session) {
+      fprintf(stderr, "session was unexpectedly cached on the server.\n");
+      return false;
+    }
+  }
+
   if (config->is_server && !GetTestState(ssl)->early_callback_called) {
     fprintf(stderr, "early callback not called\n");
     return false;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 3afd73f..92f3e15 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -1132,6 +1132,19 @@
 	// data interleaved with the second ClientHello and the client Finished.
 	InterleaveEarlyData bool
 
+	// SendHalfRTTData causes a TLS 1.3 server to send the provided
+	// data in application data records before reading the client's
+	// Finished message.
+	SendHalfRTTData [][]byte
+
+	// ExpectHalfRTTData causes a TLS 1.3 client to read application
+	// data after reading the server's Finished message and before
+	// sending any other handshake messages. It checks that the
+	// application data it reads matches what is provided in
+	// ExpectHalfRTTData and errors if the number of records or their
+	// content do not match.
+	ExpectHalfRTTData [][]byte
+
 	// EmptyEncryptedExtensions, if true, causes the TLS 1.3 server to
 	// emit an empty EncryptedExtensions block.
 	EmptyEncryptedExtensions bool
@@ -1257,6 +1270,10 @@
 	// MaxReceivePlaintext, if non-zero, is the maximum plaintext record
 	// length accepted from the peer.
 	MaxReceivePlaintext int
+
+	// SendTicketLifetime, if non-zero, is the ticket lifetime to send in
+	// NewSessionTicket messages.
+	SendTicketLifetime time.Duration
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 3cbd496..0cc0b38 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -38,6 +38,7 @@
 	haveVers             bool       // version has been negotiated
 	config               *Config    // configuration passed to constructor
 	handshakeComplete    bool
+	skipEarlyData        bool
 	didResume            bool // whether this connection was a session resumption
 	extendedMasterSecret bool // whether this session used an extended master secret
 	cipherSuite          *cipherSuite
@@ -726,6 +727,7 @@
 }
 
 func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
+RestartReadRecord:
 	if c.isDTLS {
 		return c.dtlsDoReadRecord(want)
 	}
@@ -829,10 +831,24 @@
 	// Process message.
 	b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
 	ok, off, encTyp, alertValue := c.in.decrypt(b)
+
+	// Handle skipping over early data.
+	if !ok && c.skipEarlyData {
+		goto RestartReadRecord
+	}
+
+	// If the server is expecting a second ClientHello (in response to
+	// a HelloRetryRequest) and the client sends early data, there
+	// won't be a decryption failure but it still needs to be skipped.
+	if c.in.cipher == nil && typ == recordTypeApplicationData && c.skipEarlyData {
+		goto RestartReadRecord
+	}
+
 	if !ok {
 		return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue))
 	}
 	b.off = off
+	c.skipEarlyData = false
 
 	if c.vers >= VersionTLS13 && c.in.cipher != nil {
 		if typ != recordTypeApplicationData {
@@ -860,7 +876,7 @@
 			return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete"))
 		}
 	case recordTypeApplicationData:
-		if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart {
+		if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart && len(c.config.Bugs.ExpectHalfRTTData) == 0 {
 			c.sendAlert(alertInternalError)
 			return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
 		}
@@ -1779,6 +1795,10 @@
 		ticketAgeAdd:           ticketAgeAdd,
 	}
 
+	if c.config.Bugs.SendTicketLifetime != 0 {
+		m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+	}
+
 	state := sessionState{
 		vers:               c.vers,
 		cipherSuite:        c.cipherSuite.id,
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 507ea40..02daa78 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -835,6 +835,20 @@
 	hs.finishedHash.addEntropy(zeroSecret)
 	clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel)
 	serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
+
+	// If we're expecting 0.5-RTT messages from the server, read them
+	// now.
+	for _, expectedMsg := range c.config.Bugs.ExpectHalfRTTData {
+		if err := c.readRecord(recordTypeApplicationData); err != nil {
+			return err
+		}
+		if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) {
+			return errors.New("ExpectHalfRTTData: did not get expected message")
+		}
+		c.in.freeBlock(c.input)
+		c.input = nil
+	}
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -919,7 +933,6 @@
 
 	// Switch to application data keys.
 	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
 	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 1116d6c..38925e9 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -509,6 +509,12 @@
 		}
 	}
 
+	// Decide whether or not to accept early data.
+	if hs.clientHello.hasEarlyData {
+		// For now, we'll reject and skip early data.
+		c.skipEarlyData = true
+	}
+
 	// Resolve PSK and compute the early secret.
 	if hs.sessionState != nil {
 		hs.finishedHash.addEntropy(hs.sessionState.masterSecret)
@@ -852,6 +858,13 @@
 	// from the client certificate are sent over these keys.
 	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
+	// Send 0.5-RTT messages.
+	for _, halfRTTMsg := range config.Bugs.SendHalfRTTData {
+		if _, err := c.writeRecord(recordTypeApplicationData, halfRTTMsg); err != nil {
+			return err
+		}
+	}
+
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
 	if config.ClientAuth >= RequestClientCert {
@@ -1668,6 +1681,9 @@
 	}
 
 	m := new(newSessionTicketMsg)
+	if c.config.Bugs.SendTicketLifetime != 0 {
+		m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+	}
 
 	if !c.config.Bugs.SendEmptySessionTicket {
 		var err error
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 54bfca5..4e08200 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2349,7 +2349,7 @@
 				},
 			},
 			shouldFail:         true,
-			expectedError:      ":NO_COMPRESSION_SPECIFIED:",
+			expectedError:      ":INVALID_COMPRESSION_LIST:",
 			expectedLocalError: "remote error: illegal parameter",
 		},
 		{
@@ -3417,6 +3417,7 @@
 			},
 		},
 		resumeSession: true,
+		flags:         []string{"-expect-no-session-id"},
 	})
 	tests = append(tests, testCase{
 		testType: serverTest,
@@ -3426,6 +3427,7 @@
 			SessionTicketsDisabled: true,
 		},
 		resumeSession: true,
+		flags:         []string{"-expect-session-id"},
 	})
 	tests = append(tests, testCase{
 		testType: serverTest,
@@ -3467,6 +3469,9 @@
 			},
 			resumeSession:        true,
 			resumeRenewedSession: true,
+			// TLS 1.3 uses tickets, so the session should not be
+			// cached statefully.
+			flags: []string{"-expect-no-session-id"},
 		})
 
 		tests = append(tests, testCase{
@@ -8443,10 +8448,23 @@
 			},
 		},
 		flags: []string{
+			"-enable-early-data",
 			"-expect-early-data-info",
 		},
 	})
 
+	// Test that 0-RTT tickets are ignored in clients unless opted in.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-SendTicketEarlyDataInfo-Disabled",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketEarlyDataInfo: 16384,
+			},
+		},
+	})
+
 	testCases = append(testCases, testCase{
 		testType: clientTest,
 		name:     "TLS13-DuplicateTicketEarlyDataInfo",
@@ -8475,6 +8493,40 @@
 			"-enable-early-data",
 		},
 	})
+
+	// Test that, in TLS 1.3, the server-offered NewSessionTicket lifetime
+	// is honored.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-HonorServerSessionTicketLifetime",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketLifetime: 20 * time.Second,
+			},
+		},
+		flags: []string{
+			"-resumption-delay", "19",
+		},
+		resumeSession: true,
+	})
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-HonorServerSessionTicketLifetime-2",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketLifetime: 20 * time.Second,
+				// The client should not offer the expired session.
+				ExpectNoTLS13PSK: true,
+			},
+		},
+		flags: []string{
+			"-resumption-delay", "21",
+		},
+		resumeSession: true,
+		expectResumeRejected: true,
+	})
 }
 
 func addChangeCipherSpecTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 5bc9544..b729f69 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -123,6 +123,8 @@
     &TestConfig::expect_secure_renegotiation },
   { "-expect-no-secure-renegotiation",
     &TestConfig::expect_no_secure_renegotiation },
+  { "-expect-session-id", &TestConfig::expect_session_id },
+  { "-expect-no-session-id", &TestConfig::expect_no_session_id },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 7cf0a6f..7122856 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -131,6 +131,8 @@
   bool expect_no_secure_renegotiation = false;
   int max_send_fragment = 0;
   int read_size = 0;
+  bool expect_session_id = false;
+  bool expect_no_session_id = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index 1425665..a49ee14 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -58,15 +58,7 @@
       }
 
       case ssl_hs_read_message: {
-        int ret = ssl->method->ssl_get_message(ssl, -1, ssl_dont_hash_message);
-        if (ret <= 0) {
-          return ret;
-        }
-        break;
-      }
-
-      case ssl_hs_write_message: {
-        int ret = ssl->method->write_message(ssl);
+        int ret = ssl->method->ssl_get_message(ssl);
         if (ret <= 0) {
           return ret;
         }
@@ -406,18 +398,6 @@
   return ret;
 }
 
-int tls13_check_message_type(SSL *ssl, int type) {
-  if (ssl->s3->tmp.message_type != type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ERR_add_error_dataf("got type %d, wanted type %d",
-                        ssl->s3->tmp.message_type, type);
-    return 0;
-  }
-
-  return 1;
-}
-
 int tls13_process_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
@@ -441,7 +421,7 @@
   return 1;
 }
 
-int tls13_prepare_certificate(SSL_HANDSHAKE *hs) {
+int tls13_add_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   CBB cbb, body, certificate_list;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
@@ -453,7 +433,7 @@
   }
 
   if (!ssl_has_certificate(ssl)) {
-    if (!ssl_complete_message(ssl, &cbb)) {
+    if (!ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
 
@@ -461,9 +441,11 @@
   }
 
   CERT *cert = ssl->cert;
+  CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
   CBB leaf, extensions;
   if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
-      !ssl_add_cert_to_cbb(&leaf, cert->x509_leaf) ||
+      !CBB_add_bytes(&leaf, CRYPTO_BUFFER_data(leaf_buf),
+                     CRYPTO_BUFFER_len(leaf_buf)) ||
       !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     goto err;
@@ -497,17 +479,19 @@
     }
   }
 
-  for (size_t i = 0; i < sk_X509_num(cert->x509_chain); i++) {
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain, i);
     CBB child;
     if (!CBB_add_u24_length_prefixed(&certificate_list, &child) ||
-        !ssl_add_cert_to_cbb(&child, sk_X509_value(cert->x509_chain, i)) ||
+        !CBB_add_bytes(&child, CRYPTO_BUFFER_data(cert_buf),
+                       CRYPTO_BUFFER_len(cert_buf)) ||
         !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto err;
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -518,8 +502,8 @@
   return 0;
 }
 
-enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL_HANDSHAKE *hs, int is_first_run) {
+enum ssl_private_key_result_t tls13_add_certificate_verify(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;
@@ -569,7 +553,7 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -581,7 +565,7 @@
   return ret;
 }
 
-int tls13_prepare_finished(SSL_HANDSHAKE *hs) {
+int tls13_add_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   size_t verify_data_len;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
@@ -595,7 +579,7 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, verify_data, verify_data_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return 0;
   }
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 6f2bb21..74d3646 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -15,6 +15,7 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/bytestring.h>
@@ -31,7 +32,6 @@
 enum client_hs_state_t {
   state_process_hello_retry_request = 0,
   state_send_second_client_hello,
-  state_flush_second_client_hello,
   state_process_server_hello,
   state_process_encrypted_extensions,
   state_process_certificate_request,
@@ -41,9 +41,7 @@
   state_send_client_certificate,
   state_send_client_certificate_verify,
   state_complete_client_certificate_verify,
-  state_send_channel_id,
-  state_send_client_finished,
-  state_flush,
+  state_complete_second_flight,
   state_done,
 };
 
@@ -137,6 +135,10 @@
     hs->retry_group = group_id;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
   hs->received_hello_retry_request = 1;
   hs->tls13_state = state_send_second_client_hello;
   return ssl_hs_ok;
@@ -147,18 +149,13 @@
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_flush_second_client_hello;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush_second_client_hello(SSL_HANDSHAKE *hs) {
   hs->tls13_state = state_process_server_hello;
   return ssl_hs_flush_and_read_message;
 }
 
 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)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
     return ssl_hs_error;
   }
 
@@ -261,6 +258,10 @@
       return ssl_hs_error;
     }
     ssl_set_session(ssl, NULL);
+
+    /* Resumption incorporates fresh key material, so refresh the timeout. */
+    ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+                              ssl->session_psk_dhe_timeout);
   } else if (!ssl_get_new_session(hs, 0)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
@@ -325,14 +326,8 @@
     ssl->s3->short_header = 1;
   }
 
-  /* If there was no HelloRetryRequest, the version negotiation logic has
-   * already hashed the message. */
-  if (hs->received_hello_retry_request &&
-      !ssl_hash_current_message(ssl)) {
-    return ssl_hs_error;
-  }
-
-  if (!tls13_derive_handshake_secrets(hs) ||
+  if (!ssl_hash_current_message(ssl) ||
+      !tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
@@ -346,7 +341,7 @@
 
 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)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
     return ssl_hs_error;
   }
 
@@ -428,7 +423,7 @@
 
 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) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, 0 /* certificate required */) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -441,7 +436,7 @@
 static enum ssl_hs_wait_t do_process_server_certificate_verify(
     SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -453,7 +448,7 @@
 
 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) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* Update the secret to the master secret and derive traffic keys. */
@@ -471,7 +466,7 @@
   SSL *const ssl = hs->ssl;
   /* The peer didn't request a certificate. */
   if (!hs->cert_request) {
-    hs->tls13_state = state_send_channel_id;
+    hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
 
@@ -490,12 +485,12 @@
   }
 
   if (!ssl_auto_chain_if_needed(ssl) ||
-      !tls13_prepare_certificate(hs)) {
+      !tls13_add_certificate(hs)) {
     return ssl_hs_error;
   }
 
   hs->tls13_state = state_send_client_certificate_verify;
-  return ssl_hs_write_message;
+  return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs,
@@ -503,14 +498,14 @@
   SSL *const ssl = hs->ssl;
   /* Don't send CertificateVerify if there is no certificate. */
   if (!ssl_has_certificate(ssl)) {
-    hs->tls13_state = state_send_channel_id;
+    hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
 
-  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
+  switch (tls13_add_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
-      hs->tls13_state = state_send_channel_id;
-      return ssl_hs_write_message;
+      hs->tls13_state = state_complete_second_flight;
+      return ssl_hs_ok;
 
     case ssl_private_key_retry:
       hs->tls13_state = state_complete_client_certificate_verify;
@@ -524,44 +519,35 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_channel_id(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl->s3->tlsext_channel_id_valid) {
-    hs->tls13_state = state_send_client_finished;
-    return ssl_hs_ok;
+
+  /* Send a Channel ID assertion if necessary. */
+  if (ssl->s3->tlsext_channel_id_valid) {
+    if (!ssl_do_channel_id_callback(ssl)) {
+      hs->tls13_state = state_complete_second_flight;
+      return ssl_hs_error;
+    }
+
+    if (ssl->tlsext_channel_id_private == NULL) {
+      return ssl_hs_channel_id_lookup;
+    }
+
+    CBB cbb, body;
+    if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+        !tls1_write_channel_id(ssl, &body) ||
+        !ssl_add_message_cbb(ssl, &cbb)) {
+      CBB_cleanup(&cbb);
+      return ssl_hs_error;
+    }
   }
 
-  if (!ssl_do_channel_id_callback(ssl)) {
+  /* Send a Finished message. */
+  if (!tls13_add_finished(hs)) {
     return ssl_hs_error;
   }
 
-  if (ssl->tlsext_channel_id_private == NULL) {
-    return ssl_hs_channel_id_lookup;
-  }
-
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
-      !tls1_write_channel_id(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_send_client_finished;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_flush;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+  /* Derive the final keys and enable them. */
   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,
@@ -585,9 +571,6 @@
       case state_send_second_client_hello:
         ret = do_send_second_client_hello(hs);
         break;
-      case state_flush_second_client_hello:
-        ret = do_flush_second_client_hello(hs);
-        break;
       case state_process_server_hello:
         ret = do_process_server_hello(hs);
         break;
@@ -615,14 +598,8 @@
       case state_complete_client_certificate_verify:
         ret = do_send_client_certificate_verify(hs, 0 /* complete */);
         break;
-      case state_send_channel_id:
-        ret = do_send_channel_id(hs);
-        break;
-      case state_send_client_finished:
-        ret = do_send_client_finished(hs);
-        break;
-      case state_flush:
-        ret = do_flush(hs);
+      case state_complete_second_flight:
+        ret = do_complete_second_flight(hs);
         break;
       case state_done:
         ret = ssl_hs_ok;
@@ -646,11 +623,12 @@
     return 0;
   }
 
-  ssl_session_refresh_time(ssl, session);
+  ssl_session_rebase_time(ssl, session);
 
+  uint32_t server_timeout;
   CBS cbs, ticket, extensions;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
+  if (!CBS_get_u32(&cbs, &server_timeout) ||
       !CBS_get_u32(&cbs, &session->ticket_age_add) ||
       !CBS_get_u16_length_prefixed(&cbs, &ticket) ||
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
@@ -661,6 +639,21 @@
     goto err;
   }
 
+  /* Cap the renewable lifetime by the server advertised value. This avoids
+   * wasting bandwidth on 0-RTT when we know the server will reject it.
+   *
+   * TODO(davidben): This dance where we're not sure if long or uint32_t is
+   * bigger is silly. session->timeout should not be a long to begin with.
+   * https://crbug.com/boringssl/155. */
+#if LONG_MAX < 0xffffffff
+  if (server_timeout > LONG_MAX) {
+    server_timeout = LONG_MAX;
+  }
+#endif
+  if (session->timeout > (long)server_timeout) {
+    session->timeout = (long)server_timeout;
+  }
+
   /* Parse out the extensions. */
   int have_early_data_info = 0;
   CBS early_data_info;
@@ -677,7 +670,7 @@
     goto err;
   }
 
-  if (have_early_data_info) {
+  if (have_early_data_info && ssl->ctx->enable_early_data) {
     if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
         CBS_len(&early_data_info) != 0) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 750e47f..db5fb24 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -35,25 +35,18 @@
 static const size_t kMaxEarlyDataAccepted = 14336;
 
 enum server_hs_state_t {
-  state_process_client_hello = 0,
-  state_select_parameters,
+  state_select_parameters = 0,
   state_send_hello_retry_request,
-  state_flush_hello_retry_request,
   state_process_second_client_hello,
   state_send_server_hello,
-  state_send_encrypted_extensions,
-  state_send_certificate_request,
-  state_send_server_certificate,
   state_send_server_certificate_verify,
   state_complete_server_certificate_verify,
   state_send_server_finished,
-  state_flush,
   state_process_client_certificate,
   state_process_client_certificate_verify,
   state_process_channel_id,
   state_process_client_finished,
   state_send_new_session_ticket,
-  state_flush_new_session_tickets,
   state_done,
 };
 
@@ -94,54 +87,6 @@
   return ok;
 }
 
-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;
-  }
-
-  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;
-  }
-
-  assert(ssl->s3->have_version);
-
-  /* Load the client random. */
-  if (client_hello.random_len != SSL3_RANDOM_SIZE) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return ssl_hs_error;
-  }
-  OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
-                 client_hello.random_len);
-
-  /* TLS 1.3 requires the peer only advertise the null compression. */
-  if (client_hello.compression_methods_len != 1 ||
-      client_hello.compression_methods[0] != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
-  /* TLS extensions. */
-  if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-    return ssl_hs_error;
-  }
-
-  /* The short record header extension is incompatible with early data. */
-  if (ssl->s3->skip_early_data && ssl->s3->short_header) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_select_parameters;
-  return ssl_hs_ok;
-}
-
 static const SSL_CIPHER *choose_tls13_cipher(
     const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) {
   if (client_hello->cipher_suites_len % 2 != 0) {
@@ -190,21 +135,9 @@
 
 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);
-    if (rv == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return ssl_hs_error;
-    }
-    if (rv < 0) {
-      hs->tls13_state = state_select_parameters;
-      return ssl_hs_x509_lookup;
-    }
-  }
-
-  if (!ssl_auto_chain_if_needed(ssl)) {
+  /* The short record header extension is incompatible with early data. */
+  if (ssl->s3->skip_early_data && ssl->s3->short_header) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     return ssl_hs_error;
   }
 
@@ -287,6 +220,10 @@
     }
     ssl->s3->session_reused = 1;
     SSL_SESSION_free(session);
+
+    /* Resumption incorporates fresh key material, so refresh the timeout. */
+    ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+                              ssl->session_psk_dhe_timeout);
   }
 
   if (ssl->ctx->dos_protection_cb != NULL &&
@@ -304,10 +241,12 @@
     return ssl_hs_error;
   }
 
-  /* The PRF hash is now known. Set up the key schedule. */
+  /* The PRF hash is now known. Set up the key schedule and hash the
+   * ClientHello. */
   size_t hash_len =
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
-  if (!tls13_init_key_schedule(hs)) {
+  if (!tls13_init_key_schedule(hs) ||
+      !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -349,23 +288,18 @@
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
       !CBB_add_u16(&extensions, group_id) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_flush_hello_retry_request;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush_hello_retry_request(SSL_HANDSHAKE *hs) {
   hs->tls13_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_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
     return ssl_hs_error;
   }
 
@@ -398,6 +332,8 @@
 
 static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+
+  /* Send a ServerHello. */
   CBB cbb, body, extensions;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
       !CBB_add_u16(&body, ssl->version) ||
@@ -417,43 +353,27 @@
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->tls13_state = state_send_encrypted_extensions;
-  return ssl_hs_write_message;
-
-err:
-  CBB_cleanup(&cbb);
-  return ssl_hs_error;
-}
-
-static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+  /* Derive and enable the handshake traffic secrets. */
   if (!tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret,
                              hs->hash_len)) {
-    return ssl_hs_error;
+    goto err;
   }
 
-  CBB cbb, body;
+  /* Send EncryptedExtensions. */
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_ENCRYPTED_EXTENSIONS) ||
       !ssl_add_serverhello_tlsext(hs, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
-    return ssl_hs_error;
+      !ssl_add_message_cbb(ssl, &cbb)) {
+    goto err;
   }
 
-  hs->tls13_state = state_send_certificate_request;
-  return ssl_hs_write_message;
-}
-
-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. */
   hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
   /* CertificateRequest may only be sent in non-resumption handshakes. */
@@ -461,71 +381,63 @@
     hs->cert_request = 0;
   }
 
-  if (!hs->cert_request) {
-    /* Skip this state. */
-    hs->tls13_state = state_send_server_certificate;
-    return ssl_hs_ok;
-  }
+  /* Send a CertificateRequest, if necessary. */
+  if (hs->cert_request) {
+    CBB sigalgs_cbb;
+    if (!ssl->method->init_message(ssl, &cbb, &body,
+                                   SSL3_MT_CERTIFICATE_REQUEST) ||
+        !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
+      goto err;
+    }
 
-  CBB cbb, body, sigalgs_cbb;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_CERTIFICATE_REQUEST) ||
-      !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
-    goto err;
-  }
+    const uint16_t *sigalgs;
+    size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
+    if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
+      goto err;
+    }
 
-  const uint16_t *sigalgs;
-  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
-  if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
-    goto err;
-  }
+    for (size_t i = 0; i < num_sigalgs; i++) {
+      if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
+        goto err;
+      }
+    }
 
-  for (size_t i = 0; i < num_sigalgs; i++) {
-    if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
+    if (!ssl_add_client_CA_list(ssl, &body) ||
+        !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
+        !ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
   }
 
-  if (!ssl_add_client_CA_list(ssl, &body) ||
-      !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    goto err;
+  /* Send the server Certificate message, if necessary. */
+  if (!ssl->s3->session_reused) {
+    if (!ssl_has_certificate(ssl)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
+      goto err;
+    }
+
+    if (!tls13_add_certificate(hs)) {
+      goto err;
+    }
+
+    hs->tls13_state = state_send_server_certificate_verify;
+    return ssl_hs_ok;
   }
 
-  hs->tls13_state = state_send_server_certificate;
-  return ssl_hs_write_message;
+  hs->tls13_state = state_send_server_finished;
+  return ssl_hs_ok;
 
 err:
   CBB_cleanup(&cbb);
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (ssl->s3->session_reused) {
-    hs->tls13_state = state_send_server_finished;
-    return ssl_hs_ok;
-  }
-
-  if (!ssl_has_certificate(ssl)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return ssl_hs_error;
-  }
-
-  if (!tls13_prepare_certificate(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_send_server_certificate_verify;
-  return ssl_hs_write_message;
-}
-
 static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs,
                                                             int is_first_run) {
-  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
+  switch (tls13_add_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
       hs->tls13_state = state_send_server_finished;
-      return ssl_hs_write_message;
+      return ssl_hs_ok;
 
     case ssl_private_key_retry:
       hs->tls13_state = state_complete_server_certificate_verify;
@@ -540,18 +452,10 @@
 }
 
 static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_flush;
-  return ssl_hs_write_message;
-}
-
-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(hs, kZeroes, hs->hash_len) ||
+  if (!tls13_add_finished(hs) ||
+      /* Update the secret to the master secret and derive traffic keys. */
+      !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
       !tls13_derive_application_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
                              hs->hash_len)) {
@@ -577,7 +481,7 @@
   const int allow_anonymous =
       (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, allow_anonymous) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -596,7 +500,7 @@
     return ssl_hs_ok;
   }
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -613,7 +517,7 @@
     return ssl_hs_ok;
   }
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
       !tls1_verify_channel_id(ssl) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -625,7 +529,7 @@
 
 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) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* evp_aead_seal keys have already been switched. */
@@ -637,18 +541,18 @@
 
   ssl->method->received_flight(ssl);
 
-  /* Refresh the session timestamp so that it is measured from ticket
+  /* Rebase the session timestamp so that it is measured from ticket
    * issuance. */
-  ssl_session_refresh_time(ssl, ssl->s3->new_session);
+  ssl_session_rebase_time(ssl, ssl->s3->new_session);
   hs->tls13_state = state_send_new_session_ticket;
   return ssl_hs_ok;
 }
 
-/* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
- * 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_HANDSHAKE *hs) {
+  /* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
+   * client makes several connections before getting a renewal. */
+  static const int kNumTickets = 2;
+
   SSL *const ssl = hs->ssl;
   /* If the client doesn't accept resumption with PSK_DHE_KE, don't send a
    * session ticket. */
@@ -658,95 +562,75 @@
   }
 
   SSL_SESSION *session = ssl->s3->new_session;
-  if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
-    goto err;
-  }
+  CBB cbb;
+  CBB_zero(&cbb);
 
-  CBB cbb, body, ticket, extensions;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_NEW_SESSION_TICKET) ||
-      !CBB_add_u32(&body, session->timeout) ||
-      !CBB_add_u32(&body, session->ticket_age_add) ||
-      !CBB_add_u16_length_prefixed(&body, &ticket) ||
-      !ssl_encrypt_ticket(ssl, &ticket, session) ||
-      !CBB_add_u16_length_prefixed(&body, &extensions)) {
-    goto err;
-  }
+  for (int i = 0; i < kNumTickets; i++) {
+    if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
+      goto err;
+    }
 
-  if (ssl->ctx->enable_early_data) {
-    session->ticket_max_early_data = kMaxEarlyDataAccepted;
+    CBB body, ticket, extensions;
+    if (!ssl->method->init_message(ssl, &cbb, &body,
+                                   SSL3_MT_NEW_SESSION_TICKET) ||
+        !CBB_add_u32(&body, session->timeout) ||
+        !CBB_add_u32(&body, session->ticket_age_add) ||
+        !CBB_add_u16_length_prefixed(&body, &ticket) ||
+        !ssl_encrypt_ticket(ssl, &ticket, session) ||
+        !CBB_add_u16_length_prefixed(&body, &extensions)) {
+      goto err;
+    }
 
-    CBB early_data_info;
-    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) ||
-        !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
-        !CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
-        !CBB_flush(&extensions)) {
+    if (ssl->ctx->enable_early_data) {
+      session->ticket_max_early_data = kMaxEarlyDataAccepted;
+
+      CBB early_data_info;
+      if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) ||
+          !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
+          !CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
+          !CBB_flush(&extensions)) {
+        goto err;
+      }
+    }
+
+    /* Add a fake extension. See draft-davidben-tls-grease-01. */
+    if (!CBB_add_u16(&extensions,
+                     ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
+        !CBB_add_u16(&extensions, 0 /* empty */)) {
+      goto err;
+    }
+
+    if (!ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
   }
 
-  /* Add a fake extension. See draft-davidben-tls-grease-01. */
-  if (!CBB_add_u16(&extensions,
-                   ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
-      !CBB_add_u16(&extensions, 0 /* empty */)) {
-    goto err;
-  }
-
-  if (!ssl_complete_message(ssl, &cbb)) {
-    goto err;
-  }
-
   hs->session_tickets_sent++;
-  if (hs->session_tickets_sent >= kNumTickets) {
-    hs->tls13_state = state_flush_new_session_tickets;
-  } else {
-    hs->tls13_state = state_send_new_session_ticket;
-  }
-
-  return ssl_hs_write_message;
+  hs->tls13_state = state_done;
+  return ssl_hs_flush;
 
 err:
   CBB_cleanup(&cbb);
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL_HANDSHAKE *hs) {
-  hs->tls13_state = state_done;
-  return ssl_hs_flush;
-}
-
 enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
   while (hs->tls13_state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
     enum server_hs_state_t state = hs->tls13_state;
     switch (state) {
-      case state_process_client_hello:
-        ret = do_process_client_hello(hs);
-        break;
       case state_select_parameters:
         ret = do_select_parameters(hs);
         break;
       case state_send_hello_retry_request:
         ret = do_send_hello_retry_request(hs);
         break;
-      case state_flush_hello_retry_request:
-        ret = do_flush_hello_retry_request(hs);
-        break;
       case state_process_second_client_hello:
         ret = do_process_second_client_hello(hs);
         break;
       case state_send_server_hello:
         ret = do_send_server_hello(hs);
         break;
-      case state_send_encrypted_extensions:
-        ret = do_send_encrypted_extensions(hs);
-        break;
-      case state_send_certificate_request:
-        ret = do_send_certificate_request(hs);
-        break;
-      case state_send_server_certificate:
-        ret = do_send_server_certificate(hs);
-        break;
       case state_send_server_certificate_verify:
         ret = do_send_server_certificate_verify(hs, 1 /* first run */);
       break;
@@ -756,9 +640,6 @@
       case state_send_server_finished:
         ret = do_send_server_finished(hs);
         break;
-      case state_flush:
-        ret = do_flush(hs);
-        break;
       case state_process_client_certificate:
         ret = do_process_client_certificate(hs);
         break;
@@ -774,9 +655,6 @@
       case state_send_new_session_ticket:
         ret = do_send_new_session_ticket(hs);
         break;
-      case state_flush_new_session_tickets:
-        ret = do_flush_new_session_tickets(hs);
-        break;
       case state_done:
         ret = ssl_hs_ok;
         break;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index a6584c1..70683e4 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -100,14 +100,6 @@
 
 static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
 
-static int ssl3_flush_flight(SSL *ssl) {
-  int ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
-  }
-  return ret;
-}
-
 static void ssl3_expect_flight(SSL *ssl) {}
 
 static void ssl3_received_flight(SSL *ssl) {}
@@ -155,9 +147,9 @@
     ssl3_supports_cipher,
     ssl3_init_message,
     ssl3_finish_message,
-    ssl3_queue_message,
-    ssl3_write_message,
-    ssl3_send_change_cipher_spec,
+    ssl3_add_message,
+    ssl3_add_change_cipher_spec,
+    ssl3_add_alert,
     ssl3_flush_flight,
     ssl3_expect_flight,
     ssl3_received_flight,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 362b0c2..6ff79c4 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -205,20 +205,19 @@
 }
 
 size_t SSL_max_seal_overhead(const SSL *ssl) {
-  size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   if (SSL_is_dtls(ssl)) {
-    ret += DTLS1_RT_HEADER_LENGTH;
-  } else if (ssl_uses_short_header(ssl, evp_aead_seal)) {
-    ret += 2;
-  } else {
-    ret += SSL3_RT_HEADER_LENGTH;
+    return dtls_max_seal_overhead(ssl, dtls1_use_current_epoch);
   }
+
+  size_t ret =
+      ssl_uses_short_header(ssl, evp_aead_seal) ? 2 : SSL3_RT_HEADER_LENGTH;
+  ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   /* TLS 1.3 needs an extra byte for the encrypted record type. */
   if (ssl->s3->have_version &&
       ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
     ret += 1;
   }
-  if (!SSL_is_dtls(ssl) && ssl_needs_record_splitting(ssl)) {
+  if (ssl_needs_record_splitting(ssl)) {
     ret *= 2;
   }
   return ret;