external/boringssl: bump revision.

This change bumps the BoringSSL revision to the current tip-of-tree.

Change-Id: I91d5bf467e16e8d86cb19a4de873985f524e5faa
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 91bd5ea..9cc6de4 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -22,8 +22,7 @@
   ssl_algs.c
   ssl_asn1.c
   ssl_cert.c
-  ssl_ciph.c
-  ssl_error.c
+  ssl_cipher.c
   ssl_lib.c
   ssl_rsa.c
   ssl_sess.c
@@ -39,7 +38,7 @@
 add_executable(
   ssl_test
 
-  ssl_test.c
+  ssl_test.cc
 )
 
 target_link_libraries(ssl_test ssl crypto)
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 5edc93f..662f518 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -124,44 +124,8 @@
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
-#define RSMBLY_BITMASK_SIZE(msg_len) (((msg_len) + 7) / 8)
-
-#define RSMBLY_BITMASK_MARK(bitmask, start, end)                     \
-  {                                                                  \
-    if ((end) - (start) <= 8) {                                      \
-      long ii;                                                       \
-      for (ii = (start); ii < (end); ii++)                           \
-        bitmask[((ii) >> 3)] |= (1 << ((ii)&7));                     \
-    } else {                                                         \
-      long ii;                                                       \
-      bitmask[((start) >> 3)] |= bitmask_start_values[((start)&7)];  \
-      for (ii = (((start) >> 3) + 1); ii < ((((end)-1)) >> 3); ii++) \
-        bitmask[ii] = 0xff;                                          \
-      bitmask[(((end)-1) >> 3)] |= bitmask_end_values[((end)&7)];    \
-    }                                                                \
-  }
-
-#define RSMBLY_BITMASK_IS_COMPLETE(bitmask, msg_len, is_complete)           \
-  {                                                                         \
-    long ii;                                                                \
-    assert((msg_len) > 0);                                                  \
-    is_complete = 1;                                                        \
-    if (bitmask[(((msg_len)-1) >> 3)] != bitmask_end_values[((msg_len)&7)]) \
-      is_complete = 0;                                                      \
-    if (is_complete)                                                        \
-      for (ii = (((msg_len)-1) >> 3) - 1; ii >= 0; ii--)                    \
-        if (bitmask[ii] != 0xff) {                                          \
-          is_complete = 0;                                                  \
-          break;                                                            \
-        }                                                                   \
-  }
-
-static const uint8_t bitmask_start_values[] = {0xff, 0xfe, 0xfc, 0xf8,
-                                               0xf0, 0xe0, 0xc0, 0x80};
-static const uint8_t bitmask_end_values[] = {0xff, 0x01, 0x03, 0x07,
-                                             0x0f, 0x1f, 0x3f, 0x7f};
 
 /* TODO(davidben): 28 comes from the size of IP + UDP header. Is this reasonable
  * for these values? Notably, why is kMinMTU a function of the transport
@@ -175,25 +139,30 @@
  * the underlying BIO supplies one. */
 static const unsigned int kDefaultMTU = 1500 - 28;
 
+/* kMaxHandshakeBuffer is the maximum number of handshake messages ahead of the
+ * current one to buffer. */
+static const unsigned int kHandshakeBufferSize = 10;
+
 static void dtls1_fix_message_header(SSL *s, unsigned long frag_off,
                                      unsigned long frag_len);
 static unsigned char *dtls1_write_message_header(SSL *s, unsigned char *p);
-static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok);
 
 static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len,
                                           int reassembly) {
   hm_fragment *frag = NULL;
-  unsigned char *buf = NULL;
-  unsigned char *bitmask = NULL;
+  uint8_t *buf = NULL;
+  uint8_t *bitmask = NULL;
 
   frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment));
   if (frag == NULL) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
 
   if (frag_len) {
-    buf = (unsigned char *)OPENSSL_malloc(frag_len);
+    buf = (uint8_t *)OPENSSL_malloc(frag_len);
     if (buf == NULL) {
+      OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE);
       OPENSSL_free(frag);
       return NULL;
     }
@@ -203,16 +172,22 @@
   frag->fragment = buf;
 
   /* Initialize reassembly bitmask if necessary */
-  if (reassembly) {
-    bitmask = (unsigned char *)OPENSSL_malloc(RSMBLY_BITMASK_SIZE(frag_len));
+  if (reassembly && frag_len > 0) {
+    if (frag_len + 7 < frag_len) {
+      OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_OVERFLOW);
+      return NULL;
+    }
+    size_t bitmask_len = (frag_len + 7) / 8;
+    bitmask = (uint8_t *)OPENSSL_malloc(bitmask_len);
     if (bitmask == NULL) {
+      OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE);
       if (buf != NULL) {
         OPENSSL_free(buf);
       }
       OPENSSL_free(frag);
       return NULL;
     }
-    memset(bitmask, 0, RSMBLY_BITMASK_SIZE(frag_len));
+    memset(bitmask, 0, bitmask_len);
   }
 
   frag->reassembly = bitmask;
@@ -221,23 +196,65 @@
 }
 
 void dtls1_hm_fragment_free(hm_fragment *frag) {
-  if (frag->msg_header.is_ccs) {
-    /* TODO(davidben): Simplify aead_write_ctx ownership, probably by just
-     * forbidding DTLS renego. */
-    SSL_AEAD_CTX *aead_write_ctx =
-        frag->msg_header.saved_retransmit_state.aead_write_ctx;
-    if (aead_write_ctx) {
-      EVP_AEAD_CTX_cleanup(&aead_write_ctx->ctx);
-      OPENSSL_free(aead_write_ctx);
+  if (frag == NULL) {
+    return;
+  }
+  OPENSSL_free(frag->fragment);
+  OPENSSL_free(frag->reassembly);
+  OPENSSL_free(frag);
+}
+
+#if !defined(inline)
+#define inline __inline
+#endif
+
+/* bit_range returns a |uint8_t| with bits |start|, inclusive, to |end|,
+ * exclusive, set. */
+static inline uint8_t bit_range(size_t start, size_t end) {
+  return (uint8_t)(~((1u << start) - 1) & ((1u << end) - 1));
+}
+
+/* dtls1_hm_fragment_mark marks bytes |start|, inclusive, to |end|, exclusive,
+ * as received in |frag|. If |frag| becomes complete, it clears
+ * |frag->reassembly|. The range must be within the bounds of |frag|'s message
+ * and |frag->reassembly| must not be NULL. */
+static void dtls1_hm_fragment_mark(hm_fragment *frag, size_t start,
+                                   size_t end) {
+  size_t i;
+  size_t msg_len = frag->msg_header.msg_len;
+
+  if (frag->reassembly == NULL || start > end || end > msg_len) {
+    assert(0);
+    return;
+  }
+  /* A zero-length message will never have a pending reassembly. */
+  assert(msg_len > 0);
+
+  if ((start >> 3) == (end >> 3)) {
+    frag->reassembly[start >> 3] |= bit_range(start & 7, end & 7);
+  } else {
+    frag->reassembly[start >> 3] |= bit_range(start & 7, 8);
+    for (i = (start >> 3) + 1; i < (end >> 3); i++) {
+      frag->reassembly[i] = 0xff;
+    }
+    if ((end & 7) != 0) {
+      frag->reassembly[end >> 3] |= bit_range(0, end & 7);
     }
   }
-  if (frag->fragment) {
-    OPENSSL_free(frag->fragment);
+
+  /* Check if the fragment is complete. */
+  for (i = 0; i < (msg_len >> 3); i++) {
+    if (frag->reassembly[i] != 0xff) {
+      return;
+    }
   }
-  if (frag->reassembly) {
-    OPENSSL_free(frag->reassembly);
+  if ((msg_len & 7) != 0 &&
+      frag->reassembly[msg_len >> 3] != bit_range(0, msg_len & 7)) {
+    return;
   }
-  OPENSSL_free(frag);
+
+  OPENSSL_free(frag->reassembly);
+  frag->reassembly = NULL;
 }
 
 /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or
@@ -368,26 +385,189 @@
   return 0;
 }
 
+/* dtls1_is_next_message_complete returns one if the next handshake message is
+ * complete and zero otherwise. */
+static int dtls1_is_next_message_complete(SSL *s) {
+  pitem *item = pqueue_peek(s->d1->buffered_messages);
+  if (item == NULL) {
+    return 0;
+  }
 
-/* Obtain handshake message of message type 'mt' (any if mt == -1), maximum
- * acceptable body length 'max'. Read an entire handshake message. Handshake
- * messages arrive in fragments. */
-long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max,
-                       int hash_message, int *ok) {
-  int i, al;
-  struct hm_header_st *msg_hdr;
-  uint8_t *p;
-  unsigned long msg_len;
+  hm_fragment *frag = (hm_fragment *)item->data;
+  assert(s->d1->handshake_read_seq <= frag->msg_header.seq);
+
+  return s->d1->handshake_read_seq == frag->msg_header.seq &&
+      frag->reassembly == NULL;
+}
+
+/* dtls1_discard_fragment_body discards a handshake fragment body of length
+ * |frag_len|. It returns one on success and zero on error.
+ *
+ * TODO(davidben): This function will go away when ssl_read_bytes is gone from
+ * the DTLS side. */
+static int dtls1_discard_fragment_body(SSL *s, size_t frag_len) {
+  uint8_t discard[256];
+  while (frag_len > 0) {
+    size_t chunk = frag_len < sizeof(discard) ? frag_len : sizeof(discard);
+    int ret = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, discard, chunk,
+                                        0);
+    if (ret != chunk) {
+      return 0;
+    }
+    frag_len -= chunk;
+  }
+  return 1;
+}
+
+/* dtls1_get_buffered_message returns the buffered message corresponding to
+ * |msg_hdr|. If none exists, it creates a new one and inserts it in the
+ * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It
+ * returns NULL on failure. The caller does not take ownership of the result. */
+static hm_fragment *dtls1_get_buffered_message(
+    SSL *s, const struct hm_header_st *msg_hdr) {
+  uint8_t seq64be[8];
+  memset(seq64be, 0, sizeof(seq64be));
+  seq64be[6] = (uint8_t)(msg_hdr->seq >> 8);
+  seq64be[7] = (uint8_t)msg_hdr->seq;
+  pitem *item = pqueue_find(s->d1->buffered_messages, seq64be);
+
+  hm_fragment *frag;
+  if (item == NULL) {
+    /* This is the first fragment from this message. */
+    frag = dtls1_hm_fragment_new(msg_hdr->msg_len,
+                                 1 /* reassembly buffer needed */);
+    if (frag == NULL) {
+      return NULL;
+    }
+    memcpy(&frag->msg_header, msg_hdr, sizeof(*msg_hdr));
+    item = pitem_new(seq64be, frag);
+    if (item == NULL) {
+      dtls1_hm_fragment_free(frag);
+      return NULL;
+    }
+    item = pqueue_insert(s->d1->buffered_messages, item);
+    /* |pqueue_insert| fails iff a duplicate item is inserted, but |item| cannot
+     * be a duplicate. */
+    assert(item != NULL);
+  } else {
+    frag = item->data;
+    assert(frag->msg_header.seq == msg_hdr->seq);
+    if (frag->msg_header.type != msg_hdr->type ||
+        frag->msg_header.msg_len != msg_hdr->msg_len) {
+      /* The new fragment must be compatible with the previous fragments from
+       * this message. */
+      OPENSSL_PUT_ERROR(SSL, dtls1_get_buffered_message,
+                        SSL_R_FRAGMENT_MISMATCH);
+      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      return NULL;
+    }
+  }
+  return frag;
+}
+
+/* dtls1_max_handshake_message_len returns the maximum number of bytes
+ * permitted in a DTLS handshake message for |s|. The minimum is 16KB, but may
+ * be greater if the maximum certificate list size requires it. */
+static size_t dtls1_max_handshake_message_len(const SSL *s) {
+  size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
+  if (max_len < s->max_cert_list) {
+    return s->max_cert_list;
+  }
+  return max_len;
+}
+
+/* dtls1_process_fragment reads a handshake fragment and processes it. It
+ * returns one if a fragment was successfully processed and 0 or -1 on error. */
+static int dtls1_process_fragment(SSL *s) {
+  /* Read handshake message header.
+   *
+   * TODO(davidben): ssl_read_bytes allows splitting the fragment header and
+   * body across two records. Change this interface to consume the fragment in
+   * one pass. */
+  uint8_t header[DTLS1_HM_HEADER_LENGTH];
+  int ret = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, header,
+                                      DTLS1_HM_HEADER_LENGTH, 0);
+  if (ret <= 0) {
+    return ret;
+  }
+  if (ret != DTLS1_HM_HEADER_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE);
+    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+
+  /* Parse the message fragment header. */
+  struct hm_header_st msg_hdr;
+  dtls1_get_message_header(header, &msg_hdr);
+
+  const size_t frag_off = msg_hdr.frag_off;
+  const size_t frag_len = msg_hdr.frag_len;
+  const size_t msg_len = msg_hdr.msg_len;
+  if (frag_off > msg_len || frag_off + frag_len < frag_off ||
+      frag_off + frag_len > msg_len ||
+      msg_len > dtls1_max_handshake_message_len(s)) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment,
+                      SSL_R_EXCESSIVE_MESSAGE_SIZE);
+    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return -1;
+  }
+
+  if (msg_hdr.seq < s->d1->handshake_read_seq ||
+      msg_hdr.seq > (unsigned)s->d1->handshake_read_seq +
+                    kHandshakeBufferSize) {
+    /* Ignore fragments from the past, or ones too far in the future. */
+    if (!dtls1_discard_fragment_body(s, frag_len)) {
+      return -1;
+    }
+    return 1;
+  }
+
+  hm_fragment *frag = dtls1_get_buffered_message(s, &msg_hdr);
+  if (frag == NULL) {
+    return -1;
+  }
+  assert(frag->msg_header.msg_len == msg_len);
+
+  if (frag->reassembly == NULL) {
+    /* The message is already assembled. */
+    if (!dtls1_discard_fragment_body(s, frag_len)) {
+      return -1;
+    }
+    return 1;
+  }
+  assert(msg_len > 0);
+
+  /* Read the body of the fragment. */
+  ret = s->method->ssl_read_bytes(
+      s, SSL3_RT_HANDSHAKE, frag->fragment + frag_off, frag_len, 0);
+  if (ret != frag_len) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE);
+    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+  dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
+
+  return 1;
+}
+
+/* dtls1_get_message reads a handshake message of message type |msg_type| (any
+ * if |msg_type| == -1), maximum acceptable body length |max|. Read an entire
+ * handshake message. Handshake messages arrive in fragments. */
+long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max,
+                       enum ssl_hash_message_t hash_message, int *ok) {
+  pitem *item = NULL;
+  hm_fragment *frag = NULL;
+  int al;
 
   /* s3->tmp is used to store messages that are unexpected, caused
    * by the absence of an optional handshake message */
   if (s->s3->tmp.reuse_message) {
-    /* A SSL_GET_MESSAGE_DONT_HASH_MESSAGE call cannot be combined
-     * with reuse_message; the SSL_GET_MESSAGE_DONT_HASH_MESSAGE
-     * would have to have been applied to the previous call. */
-    assert(hash_message != SSL_GET_MESSAGE_DONT_HASH_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);
     s->s3->tmp.reuse_message = 0;
-    if (mt >= 0 && s->s3->tmp.message_type != mt) {
+    if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
       al = SSL_AD_UNEXPECTED_MESSAGE;
       OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE);
       goto f_err;
@@ -398,484 +578,86 @@
     return s->init_num;
   }
 
-  msg_hdr = &s->d1->r_msg_hdr;
-  memset(msg_hdr, 0x00, sizeof(struct hm_header_st));
-
-again:
-  i = dtls1_get_message_fragment(s, stn, max, ok);
-  if (i == DTLS1_HM_BAD_FRAGMENT ||
-      i == DTLS1_HM_FRAGMENT_RETRY) {
-    /* bad fragment received */
-    goto again;
-  } else if (i <= 0 && !*ok) {
-    return i;
+  /* Process fragments until one is found. */
+  while (!dtls1_is_next_message_complete(s)) {
+    int ret = dtls1_process_fragment(s);
+    if (ret <= 0) {
+      *ok = 0;
+      return ret;
+    }
   }
 
-  p = (uint8_t *)s->init_buf->data;
-  msg_len = msg_hdr->msg_len;
+  /* Read out the next complete handshake message. */
+  item = pqueue_pop(s->d1->buffered_messages);
+  assert(item != NULL);
+  frag = (hm_fragment *)item->data;
+  assert(s->d1->handshake_read_seq == frag->msg_header.seq);
+  assert(frag->reassembly == NULL);
 
-  /* reconstruct message header */
-  *(p++) = msg_hdr->type;
-  l2n3(msg_len, p);
-  s2n(msg_hdr->seq, p);
-  l2n3(0, p);
-  l2n3(msg_len, p);
-  p -= DTLS1_HM_HEADER_LENGTH;
-  msg_len += DTLS1_HM_HEADER_LENGTH;
-
-  s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-
-  if (hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE) {
-    ssl3_hash_current_message(s);
-  }
-  if (s->msg_callback) {
-    s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, msg_len, s,
-                    s->msg_callback_arg);
+  if (frag->msg_header.msg_len > (size_t)max) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+    goto err;
   }
 
-  memset(msg_hdr, 0x00, sizeof(struct hm_header_st));
+  CBB cbb;
+  if (!BUF_MEM_grow(s->init_buf,
+                    (size_t)frag->msg_header.msg_len +
+                    DTLS1_HM_HEADER_LENGTH) ||
+      !CBB_init_fixed(&cbb, (uint8_t *)s->init_buf->data, s->init_buf->max)) {
+    OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  /* Reconstruct the assembled message. */
+  size_t len;
+  if (!CBB_add_u8(&cbb, frag->msg_header.type) ||
+      !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
+      !CBB_add_u16(&cbb, frag->msg_header.seq) ||
+      !CBB_add_u24(&cbb, 0 /* frag_off */) ||
+      !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
+      !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) ||
+      !CBB_finish(&cbb, NULL, &len)) {
+    CBB_cleanup(&cbb);
+    OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+  assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
 
   s->d1->handshake_read_seq++;
 
+  /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
+   * |ssl_get_message| API. */
+  s->s3->tmp.message_type = frag->msg_header.type;
+  s->s3->tmp.message_size = frag->msg_header.msg_len;
+  s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+  s->init_num = frag->msg_header.msg_len;
+
+  if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
+    al = SSL_AD_UNEXPECTED_MESSAGE;
+    OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE);
+    goto f_err;
+  }
+  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) {
+    goto err;
+  }
+  if (s->msg_callback) {
+    s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
+                    s->init_num + DTLS1_HM_HEADER_LENGTH, s,
+                    s->msg_callback_arg);
+  }
+
+  pitem_free(item);
+  dtls1_hm_fragment_free(frag);
+
+  s->state = stn;
+  *ok = 1;
   return s->init_num;
 
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
-  *ok = 0;
-  return -1;
-}
-
-static int dtls1_preprocess_fragment(SSL *s, struct hm_header_st *msg_hdr,
-                                     int max) {
-  size_t frag_off, frag_len, msg_len;
-
-  msg_len = msg_hdr->msg_len;
-  frag_off = msg_hdr->frag_off;
-  frag_len = msg_hdr->frag_len;
-
-  /* sanity checking */
-  if ((frag_off + frag_len) > msg_len) {
-    OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment,
-                      SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    return SSL_AD_ILLEGAL_PARAMETER;
-  }
-
-  if ((frag_off + frag_len) > (unsigned long)max) {
-    OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment,
-                      SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    return SSL_AD_ILLEGAL_PARAMETER;
-  }
-
-  if (s->d1->r_msg_hdr.frag_off == 0) {
-    /* first fragment */
-    /* msg_len is limited to 2^24, but is effectively checked
-     * against max above */
-    if (!BUF_MEM_grow_clean(s->init_buf, msg_len + DTLS1_HM_HEADER_LENGTH)) {
-      OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, ERR_R_BUF_LIB);
-      return SSL_AD_INTERNAL_ERROR;
-    }
-
-    s->s3->tmp.message_size = msg_len;
-    s->d1->r_msg_hdr.msg_len = msg_len;
-    s->s3->tmp.message_type = msg_hdr->type;
-    s->d1->r_msg_hdr.type = msg_hdr->type;
-    s->d1->r_msg_hdr.seq = msg_hdr->seq;
-  } else if (msg_len != s->d1->r_msg_hdr.msg_len) {
-    /* They must be playing with us! BTW, failure to enforce
-     * upper limit would open possibility for buffer overrun. */
-    OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment,
-                      SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    return SSL_AD_ILLEGAL_PARAMETER;
-  }
-
-  return 0; /* no error */
-}
-
-
-static int dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok) {
-  /* (0) check whether the desired fragment is available
-   * if so:
-   * (1) copy over the fragment to s->init_buf->data[]
-   * (2) update s->init_num */
-  pitem *item;
-  hm_fragment *frag;
-  int al;
-  unsigned long frag_len;
-
-  *ok = 0;
-  item = pqueue_peek(s->d1->buffered_messages);
-  if (item == NULL) {
-    return 0;
-  }
-
-  frag = (hm_fragment *)item->data;
-
-  /* Don't return if reassembly still in progress */
-  if (frag->reassembly != NULL) {
-    return 0;
-  }
-
-  if (s->d1->handshake_read_seq != frag->msg_header.seq) {
-    return 0;
-  }
-
-  frag_len = frag->msg_header.frag_len;
-  pqueue_pop(s->d1->buffered_messages);
-
-  al = dtls1_preprocess_fragment(s, &frag->msg_header, max);
-
-  if (al == 0) {
-    /* no alert */
-    uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-    memcpy(&p[frag->msg_header.frag_off], frag->fragment,
-           frag->msg_header.frag_len);
-  }
-
-  dtls1_hm_fragment_free(frag);
+err:
   pitem_free(item);
-
-  if (al == 0) {
-    *ok = 1;
-    return frag_len;
-  }
-
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
-  s->init_num = 0;
-  *ok = 0;
-  return -1;
-}
-
-/* dtls1_max_handshake_message_len returns the maximum number of bytes
- * permitted in a DTLS handshake message for |s|. The minimum is 16KB, but may
- * be greater if the maximum certificate list size requires it. */
-static unsigned long dtls1_max_handshake_message_len(const SSL *s) {
-  unsigned long max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
-  if (max_len < (unsigned long)s->max_cert_list) {
-    return s->max_cert_list;
-  }
-  return max_len;
-}
-
-static int dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr,
-                                     int *ok) {
-  hm_fragment *frag = NULL;
-  pitem *item = NULL;
-  int i = -1, is_complete;
-  uint8_t seq64be[8];
-  unsigned long frag_len = msg_hdr->frag_len;
-
-  if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len ||
-      msg_hdr->msg_len > dtls1_max_handshake_message_len(s)) {
-    goto err;
-  }
-
-  if (frag_len == 0) {
-    return DTLS1_HM_FRAGMENT_RETRY;
-  }
-
-  /* Try to find item in queue */
-  memset(seq64be, 0, sizeof(seq64be));
-  seq64be[6] = (uint8_t)(msg_hdr->seq >> 8);
-  seq64be[7] = (uint8_t)msg_hdr->seq;
-  item = pqueue_find(s->d1->buffered_messages, seq64be);
-
-  if (item == NULL) {
-    frag = dtls1_hm_fragment_new(msg_hdr->msg_len, 1);
-    if (frag == NULL) {
-      goto err;
-    }
-    memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr));
-    frag->msg_header.frag_len = frag->msg_header.msg_len;
-    frag->msg_header.frag_off = 0;
-  } else {
-    frag = (hm_fragment *)item->data;
-    if (frag->msg_header.msg_len != msg_hdr->msg_len) {
-      item = NULL;
-      frag = NULL;
-      goto err;
-    }
-  }
-
-  /* If message is already reassembled, this must be a
-   * retransmit and can be dropped. In this case item != NULL and so frag
-   * does not need to be freed. */
-  if (frag->reassembly == NULL) {
-    uint8_t devnull[256];
-
-    assert(item != NULL);
-    while (frag_len) {
-      i = s->method->ssl_read_bytes(
-          s, SSL3_RT_HANDSHAKE, devnull,
-          frag_len > sizeof(devnull) ? sizeof(devnull) : frag_len, 0);
-      if (i <= 0) {
-        goto err;
-      }
-      frag_len -= i;
-    }
-    return DTLS1_HM_FRAGMENT_RETRY;
-  }
-
-  /* read the body of the fragment (header has already been read */
-  i = s->method->ssl_read_bytes(
-      s, SSL3_RT_HANDSHAKE, frag->fragment + msg_hdr->frag_off, frag_len, 0);
-  if ((unsigned long)i != frag_len) {
-    i = -1;
-  }
-  if (i <= 0) {
-    goto err;
-  }
-
-  RSMBLY_BITMASK_MARK(frag->reassembly, (long)msg_hdr->frag_off,
-                      (long)(msg_hdr->frag_off + frag_len));
-
-  RSMBLY_BITMASK_IS_COMPLETE(frag->reassembly, (long)msg_hdr->msg_len,
-                             is_complete);
-
-  if (is_complete) {
-    OPENSSL_free(frag->reassembly);
-    frag->reassembly = NULL;
-  }
-
-  if (item == NULL) {
-    item = pitem_new(seq64be, frag);
-    if (item == NULL) {
-      i = -1;
-      goto err;
-    }
-
-    item = pqueue_insert(s->d1->buffered_messages, item);
-    /* pqueue_insert fails iff a duplicate item is inserted.
-     * However, |item| cannot be a duplicate. If it were,
-     * |pqueue_find|, above, would have returned it and control
-     * would never have reached this branch. */
-    assert(item != NULL);
-  }
-
-  return DTLS1_HM_FRAGMENT_RETRY;
-
-err:
-  if (frag != NULL && item == NULL) {
-    dtls1_hm_fragment_free(frag);
-  }
-  *ok = 0;
-  return i;
-}
-
-static int dtls1_process_out_of_seq_message(SSL *s,
-                                            const struct hm_header_st *msg_hdr,
-                                            int *ok) {
-  int i = -1;
-  hm_fragment *frag = NULL;
-  pitem *item = NULL;
-  uint8_t seq64be[8];
-  unsigned long frag_len = msg_hdr->frag_len;
-
-  if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len) {
-    goto err;
-  }
-
-  /* Try to find item in queue, to prevent duplicate entries */
-  memset(seq64be, 0, sizeof(seq64be));
-  seq64be[6] = (uint8_t)(msg_hdr->seq >> 8);
-  seq64be[7] = (uint8_t)msg_hdr->seq;
-  item = pqueue_find(s->d1->buffered_messages, seq64be);
-
-  /* If we already have an entry and this one is a fragment,
-   * don't discard it and rather try to reassemble it. */
-  if (item != NULL && frag_len != msg_hdr->msg_len) {
-    item = NULL;
-  }
-
-  /* Discard the message if sequence number was already there, is
-   * too far in the future, already in the queue or if we received
-   * a FINISHED before the SERVER_HELLO, which then must be a stale
-   * retransmit. */
-  if (msg_hdr->seq <= s->d1->handshake_read_seq ||
-      msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL ||
-      (s->d1->handshake_read_seq == 0 && msg_hdr->type == SSL3_MT_FINISHED)) {
-    uint8_t devnull[256];
-
-    while (frag_len) {
-      i = s->method->ssl_read_bytes(
-          s, SSL3_RT_HANDSHAKE, devnull,
-          frag_len > sizeof(devnull) ? sizeof(devnull) : frag_len, 0);
-      if (i <= 0) {
-        goto err;
-      }
-      frag_len -= i;
-    }
-  } else {
-    if (frag_len != msg_hdr->msg_len) {
-      return dtls1_reassemble_fragment(s, msg_hdr, ok);
-    }
-
-    if (frag_len > dtls1_max_handshake_message_len(s)) {
-      goto err;
-    }
-
-    frag = dtls1_hm_fragment_new(frag_len, 0);
-    if (frag == NULL) {
-      goto err;
-    }
-
-    memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr));
-
-    if (frag_len) {
-      /* read the body of the fragment (header has already been read */
-      i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, frag->fragment,
-                                    frag_len, 0);
-      if ((unsigned long)i != frag_len) {
-        i = -1;
-      }
-      if (i <= 0) {
-        goto err;
-      }
-    }
-
-    item = pitem_new(seq64be, frag);
-    if (item == NULL) {
-      goto err;
-    }
-
-    item = pqueue_insert(s->d1->buffered_messages, item);
-    /* pqueue_insert fails iff a duplicate item is inserted.
-     * However, |item| cannot be a duplicate. If it were,
-     * |pqueue_find|, above, would have returned it. Then, either
-     * |frag_len| != |msg_hdr->msg_len| in which case |item| is set
-     * to NULL and it will have been processed with
-     * |dtls1_reassemble_fragment|, above, or the record will have
-     * been discarded. */
-    assert(item != NULL);
-  }
-
-  return DTLS1_HM_FRAGMENT_RETRY;
-
-err:
-  if (frag != NULL && item == NULL) {
-    dtls1_hm_fragment_free(frag);
-  }
-  *ok = 0;
-  return i;
-}
-
-
-static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok) {
-  uint8_t wire[DTLS1_HM_HEADER_LENGTH];
-  unsigned long len, frag_off, frag_len;
-  int i, al;
-  struct hm_header_st msg_hdr;
-
-redo:
-  /* see if we have the required fragment already */
-  if ((frag_len = dtls1_retrieve_buffered_fragment(s, max, ok)) || *ok) {
-    if (*ok) {
-      s->init_num = frag_len;
-    }
-    return frag_len;
-  }
-
-  /* read handshake message header */
-  i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, wire,
-                                DTLS1_HM_HEADER_LENGTH, 0);
-  if (i <= 0) {
-    /* nbio, or an error */
-    s->rwstate = SSL_READING;
-    *ok = 0;
-    return i;
-  }
-
-  /* Handshake fails if message header is incomplete */
-  if (i != DTLS1_HM_HEADER_LENGTH) {
-    al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment,
-                      SSL_R_UNEXPECTED_MESSAGE);
-    goto f_err;
-  }
-
-  /* parse the message fragment header */
-  dtls1_get_message_header(wire, &msg_hdr);
-
-  /* if this is a future (or stale) message it gets buffered
-   * (or dropped)--no further processing at this time. */
-  if (msg_hdr.seq != s->d1->handshake_read_seq) {
-    return dtls1_process_out_of_seq_message(s, &msg_hdr, ok);
-  }
-
-  len = msg_hdr.msg_len;
-  frag_off = msg_hdr.frag_off;
-  frag_len = msg_hdr.frag_len;
-
-  if (frag_len && frag_len < len) {
-    return dtls1_reassemble_fragment(s, &msg_hdr, ok);
-  }
-
-  if (!s->server && s->d1->r_msg_hdr.frag_off == 0 &&
-      wire[0] == SSL3_MT_HELLO_REQUEST) {
-    /* The server may always send 'Hello Request' messages --
-     * we are doing a handshake anyway now, so ignore them
-     * if their format is correct. Does not count for
-     * 'Finished' MAC. */
-    if (wire[1] == 0 && wire[2] == 0 && wire[3] == 0) {
-      if (s->msg_callback) {
-        s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, wire,
-                        DTLS1_HM_HEADER_LENGTH, s, s->msg_callback_arg);
-      }
-
-      s->init_num = 0;
-      goto redo;
-    } else {
-      /* Incorrectly formated Hello request */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment,
-                        SSL_R_UNEXPECTED_MESSAGE);
-      goto f_err;
-    }
-  }
-
-  if ((al = dtls1_preprocess_fragment(s, &msg_hdr, max))) {
-    goto f_err;
-  }
-
-  /* XDTLS:  ressurect this when restart is in place */
-  s->state = stn;
-
-  if (frag_len > 0) {
-    uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-
-    i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &p[frag_off], frag_len,
-                                  0);
-    /* XDTLS:  fix this--message fragments cannot span multiple packets */
-    if (i <= 0) {
-      s->rwstate = SSL_READING;
-      *ok = 0;
-      return i;
-    }
-  } else {
-    i = 0;
-  }
-
-  /* XDTLS:  an incorrectly formatted fragment should cause the
-   * handshake to fail */
-  if (i != (int)frag_len) {
-    al = SSL3_AD_ILLEGAL_PARAMETER;
-    OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment,
-                      SSL3_AD_ILLEGAL_PARAMETER);
-    goto f_err;
-  }
-
-  *ok = 1;
-
-  /* Note that s->init_num is *not* used as current offset in
-   * s->init_buf->data, but as a counter summing up fragments'
-   * lengths: as soon as they sum up to handshake packet
-   * length, we assume we have got all the fragments. */
-  s->init_num = frag_len;
-  return frag_len;
-
-f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
-  s->init_num = 0;
-
+  dtls1_hm_fragment_free(frag);
   *ok = 0;
   return -1;
 }
@@ -913,7 +695,7 @@
 
 int dtls1_read_failed(SSL *s, int code) {
   if (code > 0) {
-    fprintf(stderr, "invalid state reached %s:%d", __FILE__, __LINE__);
+    assert(0);
     return 1;
   }
 
@@ -929,7 +711,7 @@
     return code;
   }
 
-  return dtls1_handle_timeout(s);
+  return DTLSv1_handle_timeout(s);
 }
 
 int dtls1_get_queue_priority(unsigned short seq, int is_ccs) {
@@ -944,23 +726,82 @@
   return seq * 2 - is_ccs;
 }
 
+static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) {
+  int ret;
+  /* XDTLS: for now assuming that read/writes are blocking */
+  unsigned long header_length;
+  uint8_t save_write_sequence[8];
+
+  /* assert(s->init_num == 0);
+     assert(s->init_off == 0); */
+
+  if (frag->msg_header.is_ccs) {
+    header_length = DTLS1_CCS_HEADER_LENGTH;
+  } else {
+    header_length = DTLS1_HM_HEADER_LENGTH;
+  }
+
+  memcpy(s->init_buf->data, frag->fragment,
+         frag->msg_header.msg_len + header_length);
+  s->init_num = frag->msg_header.msg_len + header_length;
+
+  dtls1_set_message_header(s, frag->msg_header.type,
+                           frag->msg_header.msg_len, frag->msg_header.seq,
+                           0, frag->msg_header.frag_len);
+
+  /* Save current state. */
+  SSL_AEAD_CTX *aead_write_ctx = s->aead_write_ctx;
+  uint16_t epoch = s->d1->w_epoch;
+
+  /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
+   * (negotiated cipher) exist. */
+  assert(epoch == 0 || epoch == 1);
+  assert(frag->msg_header.epoch <= epoch);
+  const int fragment_from_previous_epoch = (epoch == 1 &&
+                                            frag->msg_header.epoch == 0);
+  if (fragment_from_previous_epoch) {
+    /* Rewind to the previous epoch.
+     *
+     * TODO(davidben): Instead of swapping out connection-global state, this
+     * logic should pass a "use previous epoch" parameter down to lower-level
+     * functions. */
+    s->d1->w_epoch = frag->msg_header.epoch;
+    s->aead_write_ctx = NULL;
+    memcpy(save_write_sequence, s->s3->write_sequence,
+           sizeof(s->s3->write_sequence));
+    memcpy(s->s3->write_sequence, s->d1->last_write_sequence,
+           sizeof(s->s3->write_sequence));
+  } else {
+    /* Otherwise the messages must be from the same epoch. */
+    assert(frag->msg_header.epoch == epoch);
+  }
+
+  ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC
+                                                  : SSL3_RT_HANDSHAKE);
+
+  if (fragment_from_previous_epoch) {
+    /* Restore the current epoch. */
+    s->aead_write_ctx = aead_write_ctx;
+    s->d1->w_epoch = epoch;
+    memcpy(s->d1->last_write_sequence, s->s3->write_sequence,
+           sizeof(s->s3->write_sequence));
+    memcpy(s->s3->write_sequence, save_write_sequence,
+           sizeof(s->s3->write_sequence));
+  }
+
+  (void)BIO_flush(SSL_get_wbio(s));
+  return ret;
+}
+
+
 int dtls1_retransmit_buffered_messages(SSL *s) {
   pqueue sent = s->d1->sent_messages;
-  piterator iter;
+  piterator iter = pqueue_iterator(sent);
   pitem *item;
-  hm_fragment *frag;
-  int found = 0;
-
-  iter = pqueue_iterator(sent);
 
   for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) {
-    frag = (hm_fragment *)item->data;
-    if (dtls1_retransmit_message(
-            s, (unsigned short)dtls1_get_queue_priority(
-                   frag->msg_header.seq, frag->msg_header.is_ccs),
-            0, &found) <= 0 &&
-        found) {
-      fprintf(stderr, "dtls1_retransmit_message() failed\n");
+    hm_fragment *frag = (hm_fragment *)item->data;
+    if (dtls1_retransmit_message(s, frag) <= 0) {
       return -1;
     }
   }
@@ -998,11 +839,7 @@
   frag->msg_header.frag_off = 0;
   frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len;
   frag->msg_header.is_ccs = is_ccs;
-
-  /* save current state*/
-  frag->msg_header.saved_retransmit_state.aead_write_ctx = s->aead_write_ctx;
-  frag->msg_header.saved_retransmit_state.session = s->session;
-  frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch;
+  frag->msg_header.epoch = s->d1->w_epoch;
 
   memset(seq64be, 0, sizeof(seq64be));
   seq64be[6] = (uint8_t)(
@@ -1021,85 +858,6 @@
   return 1;
 }
 
-int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off,
-                             int *found) {
-  int ret;
-  /* XDTLS: for now assuming that read/writes are blocking */
-  pitem *item;
-  hm_fragment *frag;
-  unsigned long header_length;
-  uint8_t seq64be[8];
-  struct dtls1_retransmit_state saved_state;
-  uint8_t save_write_sequence[8];
-
-  /* assert(s->init_num == 0);
-     assert(s->init_off == 0); */
-
-  /* XDTLS:  the requested message ought to be found, otherwise error */
-  memset(seq64be, 0, sizeof(seq64be));
-  seq64be[6] = (uint8_t)(seq >> 8);
-  seq64be[7] = (uint8_t)seq;
-
-  item = pqueue_find(s->d1->sent_messages, seq64be);
-  if (item == NULL) {
-    fprintf(stderr, "retransmit:  message %d non-existant\n", seq);
-    *found = 0;
-    return 0;
-  }
-
-  *found = 1;
-  frag = (hm_fragment *)item->data;
-
-  if (frag->msg_header.is_ccs) {
-    header_length = DTLS1_CCS_HEADER_LENGTH;
-  } else {
-    header_length = DTLS1_HM_HEADER_LENGTH;
-  }
-
-  memcpy(s->init_buf->data, frag->fragment,
-         frag->msg_header.msg_len + header_length);
-  s->init_num = frag->msg_header.msg_len + header_length;
-
-  dtls1_set_message_header(s, frag->msg_header.type,
-                           frag->msg_header.msg_len, frag->msg_header.seq,
-                           0, frag->msg_header.frag_len);
-
-  /* save current state */
-  saved_state.aead_write_ctx = s->aead_write_ctx;
-  saved_state.session = s->session;
-  saved_state.epoch = s->d1->w_epoch;
-
-  /* restore state in which the message was originally sent */
-  s->aead_write_ctx = frag->msg_header.saved_retransmit_state.aead_write_ctx;
-  s->session = frag->msg_header.saved_retransmit_state.session;
-  s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch;
-
-  if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) {
-    memcpy(save_write_sequence, s->s3->write_sequence,
-           sizeof(s->s3->write_sequence));
-    memcpy(s->s3->write_sequence, s->d1->last_write_sequence,
-           sizeof(s->s3->write_sequence));
-  }
-
-  ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC
-                                                  : SSL3_RT_HANDSHAKE);
-
-  /* restore current state */
-  s->aead_write_ctx = saved_state.aead_write_ctx;
-  s->session = saved_state.session;
-  s->d1->w_epoch = saved_state.epoch;
-
-  if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) {
-    memcpy(s->d1->last_write_sequence, s->s3->write_sequence,
-           sizeof(s->s3->write_sequence));
-    memcpy(s->s3->write_sequence, save_write_sequence,
-           sizeof(s->s3->write_sequence));
-  }
-
-  (void)BIO_flush(SSL_get_wbio(s));
-  return ret;
-}
-
 /* call this function when the buffered messages are no longer needed */
 void dtls1_clear_record_buffer(SSL *s) {
   pitem *item;
@@ -1160,12 +918,6 @@
   n2l3(data, msg_hdr->frag_len);
 }
 
-void dtls1_get_ccs_header(uint8_t *data, struct ccs_header_st *ccs_hdr) {
-  memset(ccs_hdr, 0x00, sizeof(struct ccs_header_st));
-
-  ccs_hdr->type = *(data++);
-}
-
 int dtls1_shutdown(SSL *s) {
   int ret;
   ret = ssl3_shutdown(s);
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index 3f9e814..1827a67 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -114,17 +114,19 @@
 
 #include <assert.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/bn.h>
 #include <openssl/buf.h>
 #include <openssl/dh.h>
 #include <openssl/evp.h>
+#include <openssl/err.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 static int dtls1_get_hello_verify(SSL *s);
 
@@ -156,7 +158,6 @@
       case SSL_ST_RENEGOTIATE:
         s->renegotiate = 1;
         s->state = SSL_ST_CONNECT;
-        s->ctx->stats.sess_connect_renegotiate++;
       /* break */
       case SSL_ST_CONNECT:
       case SSL_ST_BEFORE | SSL_ST_CONNECT:
@@ -175,8 +176,7 @@
           buf = NULL;
         }
 
-        if (!ssl3_setup_buffers(s) ||
-            !ssl_init_wbio_buffer(s, 0)) {
+        if (!ssl_init_wbio_buffer(s, 0)) {
           ret = -1;
           goto end;
         }
@@ -184,7 +184,6 @@
         /* don't push the buffering BIO quite yet */
 
         s->state = SSL3_ST_CW_CLNT_HELLO_A;
-        s->ctx->stats.sess_connect++;
         s->init_num = 0;
         s->d1->send_cookie = 0;
         s->hit = 0;
@@ -458,12 +457,6 @@
       case SSL3_ST_CW_FLUSH:
         s->rwstate = SSL_WRITING;
         if (BIO_flush(s->wbio) <= 0) {
-          /* If the write error was fatal, stop trying */
-          if (!BIO_should_retry(s->wbio)) {
-            s->rwstate = SSL_NOTHING;
-            s->state = s->s3->tmp.next_state;
-          }
-
           ret = -1;
           goto end;
         }
@@ -483,15 +476,12 @@
         s->new_session = 0;
 
         ssl_update_cache(s, SSL_SESS_CACHE_CLIENT);
-        if (s->hit) {
-          s->ctx->stats.sess_hit++;
-        }
 
         ret = 1;
-        s->ctx->stats.sess_connect_good++;
 
-        if (cb != NULL)
+        if (cb != NULL) {
           cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+        }
 
         /* done with handshaking */
         s->d1->handshake_read_seq = 0;
@@ -519,9 +509,7 @@
 end:
   s->in_handshake--;
 
-  if (buf != NULL) {
-    BUF_MEM_free(buf);
-  }
+  BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(s, SSL_CB_CONNECT_EXIT, ret);
   }
@@ -538,7 +526,7 @@
       s, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B,
       -1,
       /* Use the same maximum size as ssl3_get_server_hello. */
-      20000, SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+      20000, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -556,7 +544,7 @@
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
       CBS_len(&hello_verify_request) != 0) {
     al = SSL_AD_DECODE_ERROR;
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, SSL_R_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, dtls1_get_hello_verify, SSL_R_DECODE_ERROR);
     goto f_err;
   }
 
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index 8244cb9..e53156f 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -58,6 +58,7 @@
 
 #include <limits.h>
 #include <stdio.h>
+#include <string.h>
 
 #if defined(OPENSSL_WINDOWS)
 #include <sys/timeb.h>
@@ -70,51 +71,17 @@
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
-static void get_current_time(OPENSSL_timeval *t);
-static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft);
-static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
-static int dtls1_handshake_write(SSL *s);
+/* DTLS1_MTU_TIMEOUTS is the maximum number of timeouts to expire
+ * before starting to decrease the MTU. */
+#define DTLS1_MTU_TIMEOUTS                     2
 
-const SSL3_ENC_METHOD DTLSv1_enc_data = {
-  tls1_enc,
-  tls1_prf,
-  tls1_setup_key_block,
-  tls1_generate_master_secret,
-  tls1_change_cipher_state,
-  tls1_final_finish_mac,
-  TLS1_FINISH_MAC_LENGTH,
-  tls1_cert_verify_mac,
-  TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
-  TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
-  tls1_alert_code,
-  tls1_export_keying_material,
-  SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV,
-  DTLS1_HM_HEADER_LENGTH,
-  dtls1_set_handshake_header,
-  dtls1_handshake_write,
-};
+/* DTLS1_MAX_TIMEOUTS is the maximum number of timeouts to expire
+ * before failing the DTLS handshake. */
+#define DTLS1_MAX_TIMEOUTS                     12
 
-const SSL3_ENC_METHOD DTLSv1_2_enc_data = {
-  tls1_enc,
-  tls1_prf,
-  tls1_setup_key_block,
-  tls1_generate_master_secret,
-  tls1_change_cipher_state,
-  tls1_final_finish_mac,
-  TLS1_FINISH_MAC_LENGTH,
-  tls1_cert_verify_mac,
-  TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
-  TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
-  tls1_alert_code,
-  tls1_export_keying_material,
-  SSL_ENC_FLAG_DTLS | SSL_ENC_FLAG_EXPLICIT_IV | SSL_ENC_FLAG_SIGALGS |
-      SSL_ENC_FLAG_SHA256_PRF | SSL_ENC_FLAG_TLS1_2_CIPHERS,
-  DTLS1_HM_HEADER_LENGTH,
-  dtls1_set_handshake_header,
-  dtls1_handshake_write,
-};
+static void get_current_time(const SSL *ssl, struct timeval *out_clock);
 
 int dtls1_new(SSL *s) {
   DTLS1_STATE *d1;
@@ -129,30 +96,12 @@
   }
   memset(d1, 0, sizeof *d1);
 
-  d1->unprocessed_rcds.q = pqueue_new();
-  d1->processed_rcds.q = pqueue_new();
   d1->buffered_messages = pqueue_new();
   d1->sent_messages = pqueue_new();
-  d1->buffered_app_data.q = pqueue_new();
 
-  if (!d1->unprocessed_rcds.q || !d1->processed_rcds.q ||
-      !d1->buffered_messages || !d1->sent_messages ||
-      !d1->buffered_app_data.q) {
-    if (d1->unprocessed_rcds.q) {
-      pqueue_free(d1->unprocessed_rcds.q);
-    }
-    if (d1->processed_rcds.q) {
-      pqueue_free(d1->processed_rcds.q);
-    }
-    if (d1->buffered_messages) {
-      pqueue_free(d1->buffered_messages);
-    }
-    if (d1->sent_messages) {
-      pqueue_free(d1->sent_messages);
-    }
-    if (d1->buffered_app_data.q) {
-      pqueue_free(d1->buffered_app_data.q);
-    }
+  if (!d1->buffered_messages || !d1->sent_messages) {
+    pqueue_free(d1->buffered_messages);
+    pqueue_free(d1->sent_messages);
     OPENSSL_free(d1);
     ssl3_free(s);
     return 0;
@@ -172,25 +121,6 @@
 static void dtls1_clear_queues(SSL *s) {
   pitem *item = NULL;
   hm_fragment *frag = NULL;
-  DTLS1_RECORD_DATA *rdata;
-
-  while ((item = pqueue_pop(s->d1->unprocessed_rcds.q)) != NULL) {
-    rdata = (DTLS1_RECORD_DATA *)item->data;
-    if (rdata->rbuf.buf) {
-      OPENSSL_free(rdata->rbuf.buf);
-    }
-    OPENSSL_free(item->data);
-    pitem_free(item);
-  }
-
-  while ((item = pqueue_pop(s->d1->processed_rcds.q)) != NULL) {
-    rdata = (DTLS1_RECORD_DATA *)item->data;
-    if (rdata->rbuf.buf) {
-      OPENSSL_free(rdata->rbuf.buf);
-    }
-    OPENSSL_free(item->data);
-    pitem_free(item);
-  }
 
   while ((item = pqueue_pop(s->d1->buffered_messages)) != NULL) {
     frag = (hm_fragment *)item->data;
@@ -203,15 +133,6 @@
     dtls1_hm_fragment_free(frag);
     pitem_free(item);
   }
-
-  while ((item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL) {
-    rdata = (DTLS1_RECORD_DATA *)item->data;
-    if (rdata->rbuf.buf) {
-      OPENSSL_free(rdata->rbuf.buf);
-    }
-    OPENSSL_free(item->data);
-    pitem_free(item);
-  }
 }
 
 void dtls1_free(SSL *s) {
@@ -223,40 +144,15 @@
 
   dtls1_clear_queues(s);
 
-  pqueue_free(s->d1->unprocessed_rcds.q);
-  pqueue_free(s->d1->processed_rcds.q);
   pqueue_free(s->d1->buffered_messages);
   pqueue_free(s->d1->sent_messages);
-  pqueue_free(s->d1->buffered_app_data.q);
 
   OPENSSL_free(s->d1);
   s->d1 = NULL;
 }
 
-long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg) {
-  int ret = 0;
-
-  switch (cmd) {
-    case DTLS_CTRL_GET_TIMEOUT:
-      if (dtls1_get_timeout(s, (OPENSSL_timeval *)parg) != NULL) {
-        ret = 1;
-      }
-      break;
-
-    case DTLS_CTRL_HANDLE_TIMEOUT:
-      ret = dtls1_handle_timeout(s);
-      break;
-
-    default:
-      ret = ssl3_ctrl(s, cmd, larg, parg);
-      break;
-  }
-
-  return ret;
-}
-
-const SSL_CIPHER *dtls1_get_cipher(unsigned int u) {
-  const SSL_CIPHER *ciph = ssl3_get_cipher(u);
+const SSL_CIPHER *dtls1_get_cipher(size_t i) {
+  const SSL_CIPHER *ciph = ssl3_get_cipher(i);
   /* DTLS does not support stream ciphers. */
   if (ciph == NULL || ciph->algorithm_enc == SSL_RC4) {
     return NULL;
@@ -272,7 +168,7 @@
   }
 
   /* Set timeout to current time */
-  get_current_time(&s->d1->next_timeout);
+  get_current_time(s, &s->d1->next_timeout);
 
   /* Add duration to current time */
   s->d1->next_timeout.tv_sec += s->d1->timeout_duration;
@@ -280,48 +176,51 @@
            &s->d1->next_timeout);
 }
 
-static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft) {
-  OPENSSL_timeval timenow;
+int DTLSv1_get_timeout(const SSL *ssl, struct timeval *out) {
+  if (!SSL_IS_DTLS(ssl)) {
+    return 0;
+  }
 
   /* If no timeout is set, just return NULL */
-  if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) {
-    return NULL;
+  if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) {
+    return 0;
   }
 
   /* Get current time */
-  get_current_time(&timenow);
+  struct timeval timenow;
+  get_current_time(ssl, &timenow);
 
   /* If timer already expired, set remaining time to 0 */
-  if (s->d1->next_timeout.tv_sec < timenow.tv_sec ||
-      (s->d1->next_timeout.tv_sec == timenow.tv_sec &&
-       s->d1->next_timeout.tv_usec <= timenow.tv_usec)) {
-    memset(timeleft, 0, sizeof(OPENSSL_timeval));
-    return timeleft;
+  if (ssl->d1->next_timeout.tv_sec < timenow.tv_sec ||
+      (ssl->d1->next_timeout.tv_sec == timenow.tv_sec &&
+       ssl->d1->next_timeout.tv_usec <= timenow.tv_usec)) {
+    memset(out, 0, sizeof(struct timeval));
+    return 1;
   }
 
   /* Calculate time left until timer expires */
-  memcpy(timeleft, &s->d1->next_timeout, sizeof(OPENSSL_timeval));
-  timeleft->tv_sec -= timenow.tv_sec;
-  timeleft->tv_usec -= timenow.tv_usec;
-  if (timeleft->tv_usec < 0) {
-    timeleft->tv_sec--;
-    timeleft->tv_usec += 1000000;
+  memcpy(out, &ssl->d1->next_timeout, sizeof(struct timeval));
+  out->tv_sec -= timenow.tv_sec;
+  out->tv_usec -= timenow.tv_usec;
+  if (out->tv_usec < 0) {
+    out->tv_sec--;
+    out->tv_usec += 1000000;
   }
 
   /* If remaining time is less than 15 ms, set it to 0 to prevent issues
    * because of small devergences with socket timeouts. */
-  if (timeleft->tv_sec == 0 && timeleft->tv_usec < 15000) {
-    memset(timeleft, 0, sizeof(OPENSSL_timeval));
+  if (out->tv_sec == 0 && out->tv_usec < 15000) {
+    memset(out, 0, sizeof(struct timeval));
   }
 
-  return timeleft;
+  return 1;
 }
 
 int dtls1_is_timer_expired(SSL *s) {
-  OPENSSL_timeval timeleft;
+  struct timeval timeleft;
 
   /* Get time left until timeout, return false if no timer running */
-  if (dtls1_get_timeout(s, &timeleft) == NULL) {
+  if (!DTLSv1_get_timeout(s, &timeleft)) {
     return 0;
   }
 
@@ -344,8 +243,8 @@
 
 void dtls1_stop_timer(SSL *s) {
   /* Reset everything */
-  memset(&(s->d1->timeout), 0, sizeof(struct dtls1_timeout_st));
-  memset(&s->d1->next_timeout, 0, sizeof(OPENSSL_timeval));
+  s->d1->num_timeouts = 0;
+  memset(&s->d1->next_timeout, 0, sizeof(struct timeval));
   s->d1->timeout_duration = 1;
   BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
            &s->d1->next_timeout);
@@ -354,10 +253,10 @@
 }
 
 int dtls1_check_timeout_num(SSL *s) {
-  s->d1->timeout.num_alerts++;
+  s->d1->num_timeouts++;
 
   /* Reduce MTU after 2 unsuccessful retransmissions */
-  if (s->d1->timeout.num_alerts > 2 &&
+  if (s->d1->num_timeouts > DTLS1_MTU_TIMEOUTS &&
       !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) {
     long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0,
                         NULL);
@@ -366,7 +265,7 @@
     }
   }
 
-  if (s->d1->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT) {
+  if (s->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) {
     /* fail the connection, enough alerts have been sent */
     OPENSSL_PUT_ERROR(SSL, dtls1_check_timeout_num, SSL_R_READ_TIMEOUT_EXPIRED);
     return -1;
@@ -375,39 +274,43 @@
   return 0;
 }
 
-int dtls1_handle_timeout(SSL *s) {
-  /* if no timer is expired, don't do anything */
-  if (!dtls1_is_timer_expired(s)) {
-    return 0;
-  }
-
-  dtls1_double_timeout(s);
-
-  if (dtls1_check_timeout_num(s) < 0) {
+int DTLSv1_handle_timeout(SSL *ssl) {
+  if (!SSL_IS_DTLS(ssl)) {
     return -1;
   }
 
-  s->d1->timeout.read_timeouts++;
-  if (s->d1->timeout.read_timeouts > DTLS1_TMO_READ_COUNT) {
-    s->d1->timeout.read_timeouts = 1;
+  /* if no timer is expired, don't do anything */
+  if (!dtls1_is_timer_expired(ssl)) {
+    return 0;
   }
 
-  dtls1_start_timer(s);
-  return dtls1_retransmit_buffered_messages(s);
+  dtls1_double_timeout(ssl);
+
+  if (dtls1_check_timeout_num(ssl) < 0) {
+    return -1;
+  }
+
+  dtls1_start_timer(ssl);
+  return dtls1_retransmit_buffered_messages(ssl);
 }
 
-static void get_current_time(OPENSSL_timeval *t) {
+static void get_current_time(const SSL *ssl, struct timeval *out_clock) {
+  if (ssl->ctx->current_time_cb != NULL) {
+    ssl->ctx->current_time_cb(ssl, out_clock);
+    return;
+  }
+
 #if defined(OPENSSL_WINDOWS)
   struct _timeb time;
   _ftime(&time);
-  t->tv_sec = time.time;
-  t->tv_usec = time.millitm * 1000;
+  out_clock->tv_sec = time.time;
+  out_clock->tv_usec = time.millitm * 1000;
 #else
-  gettimeofday(t, NULL);
+  gettimeofday(out_clock, NULL);
 #endif
 }
 
-static void dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) {
+int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) {
   uint8_t *message = (uint8_t *)s->init_buf->data;
   const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
   uint8_t serialised_header[DTLS1_HM_HEADER_LENGTH];
@@ -430,10 +333,10 @@
   s2n(msg_hdr->seq, p);
   l2n3(0, p);
   l2n3(msg_hdr->msg_len, p);
-  ssl3_finish_mac(s, serialised_header, sizeof(serialised_header));
-  ssl3_finish_mac(s, message + DTLS1_HM_HEADER_LENGTH, len);
+  return ssl3_finish_mac(s, serialised_header, sizeof(serialised_header)) &&
+         ssl3_finish_mac(s, message + DTLS1_HM_HEADER_LENGTH, len);
 }
 
-static int dtls1_handshake_write(SSL *s) {
+int dtls1_handshake_write(SSL *s) {
   return dtls1_do_write(s, SSL3_RT_HANDSHAKE);
 }
diff --git a/src/ssl/d1_meth.c b/src/ssl/d1_meth.c
index a894222..a11fbdd 100644
--- a/src/ssl/d1_meth.c
+++ b/src/ssl/d1_meth.c
@@ -55,32 +55,33 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 static const SSL_PROTOCOL_METHOD DTLS_protocol_method = {
-  dtls1_new,
-  dtls1_free,
-  dtls1_accept,
-  dtls1_connect,
-  ssl3_read,
-  ssl3_peek,
-  ssl3_write,
-  dtls1_shutdown,
-  ssl3_renegotiate,
-  ssl3_renegotiate_check,
-  dtls1_get_message,
-  dtls1_read_bytes,
-  dtls1_write_app_data_bytes,
-  dtls1_dispatch_alert,
-  dtls1_ctrl,
-  ssl3_ctx_ctrl,
-  ssl3_pending,
-  ssl3_num_ciphers,
-  dtls1_get_cipher,
-  ssl_undefined_void_function,
-  ssl3_callback_ctrl,
-  ssl3_ctx_callback_ctrl,
+    1 /* is_dtls */,
+    dtls1_new,
+    dtls1_free,
+    dtls1_accept,
+    dtls1_connect,
+    ssl3_read,
+    ssl3_peek,
+    ssl3_write,
+    dtls1_shutdown,
+    ssl3_renegotiate,
+    ssl3_renegotiate_check,
+    dtls1_get_message,
+    dtls1_read_bytes,
+    dtls1_write_app_data_bytes,
+    dtls1_dispatch_alert,
+    ssl3_ctrl,
+    ssl3_ctx_ctrl,
+    ssl3_pending,
+    ssl3_num_ciphers,
+    dtls1_get_cipher,
+    DTLS1_HM_HEADER_LENGTH,
+    dtls1_set_handshake_header,
+    dtls1_handshake_write,
 };
 
 const SSL_METHOD *DTLS_method(void) {
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index a77ad4e..9e056ac 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -109,9 +109,9 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include <stdio.h>
-#include <errno.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <openssl/buf.h>
 #include <openssl/mem.h>
@@ -119,7 +119,7 @@
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 /* mod 128 saturating subtract of two 64-bit values in big-endian order */
@@ -181,152 +181,12 @@
   }
 }
 
-static int have_handshake_fragment(SSL *s, int type, uint8_t *buf, int len,
-                                   int peek);
 static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap);
 static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap);
-static DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr,
-                                      unsigned int *is_next_epoch);
-static int dtls1_buffer_record(SSL *s, record_pqueue *q,
-                               uint8_t *priority);
 static int dtls1_process_record(SSL *s);
 static int do_dtls1_write(SSL *s, int type, const uint8_t *buf,
                           unsigned int len);
 
-/* copy buffered record into SSL structure */
-static int dtls1_copy_record(SSL *s, pitem *item) {
-  DTLS1_RECORD_DATA *rdata;
-
-  rdata = (DTLS1_RECORD_DATA *)item->data;
-
-  if (s->s3->rbuf.buf != NULL) {
-    OPENSSL_free(s->s3->rbuf.buf);
-  }
-
-  s->packet = rdata->packet;
-  s->packet_length = rdata->packet_length;
-  memcpy(&(s->s3->rbuf), &(rdata->rbuf), sizeof(SSL3_BUFFER));
-  memcpy(&(s->s3->rrec), &(rdata->rrec), sizeof(SSL3_RECORD));
-
-  /* Set proper sequence number for mac calculation */
-  memcpy(&(s->s3->read_sequence[2]), &(rdata->packet[5]), 6);
-
-  return 1;
-}
-
-static int dtls1_buffer_record(SSL *s, record_pqueue *queue,
-                               uint8_t *priority) {
-  DTLS1_RECORD_DATA *rdata;
-  pitem *item;
-
-  /* Limit the size of the queue to prevent DOS attacks */
-  if (pqueue_size(queue->q) >= 100) {
-    return 0;
-  }
-
-  rdata = OPENSSL_malloc(sizeof(DTLS1_RECORD_DATA));
-  item = pitem_new(priority, rdata);
-  if (rdata == NULL || item == NULL) {
-    if (rdata != NULL) {
-      OPENSSL_free(rdata);
-    }
-    if (item != NULL) {
-      pitem_free(item);
-    }
-
-    OPENSSL_PUT_ERROR(SSL, dtls1_buffer_record, ERR_R_INTERNAL_ERROR);
-    return -1;
-  }
-
-  rdata->packet = s->packet;
-  rdata->packet_length = s->packet_length;
-  memcpy(&(rdata->rbuf), &(s->s3->rbuf), sizeof(SSL3_BUFFER));
-  memcpy(&(rdata->rrec), &(s->s3->rrec), sizeof(SSL3_RECORD));
-
-  item->data = rdata;
-
-  s->packet = NULL;
-  s->packet_length = 0;
-  memset(&(s->s3->rbuf), 0, sizeof(SSL3_BUFFER));
-  memset(&(s->s3->rrec), 0, sizeof(SSL3_RECORD));
-
-  if (!ssl3_setup_buffers(s)) {
-    goto internal_error;
-  }
-
-  /* insert should not fail, since duplicates are dropped */
-  if (pqueue_insert(queue->q, item) == NULL) {
-    goto internal_error;
-  }
-
-  return 1;
-
-internal_error:
-  OPENSSL_PUT_ERROR(SSL, dtls1_buffer_record, ERR_R_INTERNAL_ERROR);
-  if (rdata->rbuf.buf != NULL) {
-    OPENSSL_free(rdata->rbuf.buf);
-  }
-  OPENSSL_free(rdata);
-  pitem_free(item);
-  return -1;
-}
-
-static int dtls1_retrieve_buffered_record(SSL *s, record_pqueue *queue) {
-  pitem *item;
-
-  item = pqueue_pop(queue->q);
-  if (item) {
-    dtls1_copy_record(s, item);
-
-    OPENSSL_free(item->data);
-    pitem_free(item);
-
-    return 1;
-  }
-
-  return 0;
-}
-
-/* retrieve a buffered record that belongs to the new epoch, i.e., not
- * processed yet */
-#define dtls1_get_unprocessed_record(s) \
-  dtls1_retrieve_buffered_record((s), &((s)->d1->unprocessed_rcds))
-
-/* retrieve a buffered record that belongs to the current epoch, i.e.,
- * processed */
-#define dtls1_get_processed_record(s) \
-  dtls1_retrieve_buffered_record((s), &((s)->d1->processed_rcds))
-
-static int dtls1_process_buffered_records(SSL *s) {
-  pitem *item;
-
-  item = pqueue_peek(s->d1->unprocessed_rcds.q);
-  if (item) {
-    /* Check if epoch is current. */
-    if (s->d1->unprocessed_rcds.epoch != s->d1->r_epoch) {
-      return 1; /* Nothing to do. */
-    }
-
-    /* Process all the records. */
-    while (pqueue_peek(s->d1->unprocessed_rcds.q)) {
-      dtls1_get_unprocessed_record(s);
-      if (!dtls1_process_record(s)) {
-        return 0;
-      }
-      if (dtls1_buffer_record(s, &(s->d1->processed_rcds),
-                              s->s3->rrec.seq_num) < 0) {
-        return -1;
-      }
-    }
-  }
-
-  /* sync epoch numbers once all the unprocessed records have been processed */
-  s->d1->processed_rcds.epoch = s->d1->r_epoch;
-  s->d1->unprocessed_rcds.epoch = s->d1->r_epoch + 1;
-
-  return 1;
-}
-
 static int dtls1_process_record(SSL *s) {
   int al;
   SSL3_RECORD *rr;
@@ -405,28 +265,15 @@
   SSL3_RECORD *rr;
   unsigned char *p = NULL;
   unsigned short version;
-  DTLS1_BITMAP *bitmap;
-  unsigned int is_next_epoch;
 
   rr = &(s->s3->rrec);
 
-  /* The epoch may have changed. If so, process all the pending records. This
-   * is a non-blocking operation. */
-  if (dtls1_process_buffered_records(s) < 0) {
-    return -1;
-  }
-
-  /* If we're renegotiating, then there may be buffered records. */
-  if (dtls1_get_processed_record(s)) {
-    return 1;
-  }
-
   /* get something from the wire */
 again:
   /* check if we have the header */
   if ((s->rstate != SSL_ST_READ_BODY) ||
       (s->packet_length < DTLS1_RT_HEADER_LENGTH)) {
-    n = ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, s->s3->rbuf.len, 0);
+    n = ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, 0);
     /* read timeout is handled by dtls1_read_bytes */
     if (n <= 0) {
       return n; /* error or non-blocking */
@@ -498,7 +345,7 @@
   if (rr->length > s->packet_length - DTLS1_RT_HEADER_LENGTH) {
     /* now s->packet_length == DTLS1_RT_HEADER_LENGTH */
     i = rr->length;
-    n = ssl3_read_n(s, i, i, 1);
+    n = ssl3_read_n(s, i, 1);
     if (n <= 0) {
       return n; /* error or non-blocking io */
     }
@@ -515,16 +362,17 @@
   }
   s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */
 
-  /* match epochs.  NULL means the packet is dropped on the floor */
-  bitmap = dtls1_get_bitmap(s, rr, &is_next_epoch);
-  if (bitmap == NULL) {
+  if (rr->epoch != s->d1->r_epoch) {
+    /* This record is from the wrong epoch. If it is the next epoch, it could be
+     * buffered. For simplicity, drop it and expect retransmit to handle it
+     * later; DTLS is supposed to handle packet loss. */
     rr->length = 0;
-    s->packet_length = 0; /* dump this record */
-    goto again;           /* get another record */
+    s->packet_length = 0;
+    goto again;
   }
 
   /* Check whether this is a repeat, or aged record. */
-  if (!dtls1_record_replay_check(s, bitmap)) {
+  if (!dtls1_record_replay_check(s, &s->d1->bitmap)) {
     rr->length = 0;
     s->packet_length = 0; /* dump this record */
     goto again;           /* get another record */
@@ -535,28 +383,12 @@
     goto again;
   }
 
-  /* If this record is from the next epoch (either HM or ALERT),
-   * and a handshake is currently in progress, buffer it since it
-   * cannot be processed at this time.
-   */
-  if (is_next_epoch) {
-    if (SSL_in_init(s) || s->in_handshake) {
-      if (dtls1_buffer_record(s, &(s->d1->unprocessed_rcds), rr->seq_num) < 0) {
-        return -1;
-      }
-      dtls1_record_bitmap_update(s, bitmap); /* Mark receipt of record. */
-    }
-    rr->length = 0;
-    s->packet_length = 0;
-    goto again;
-  }
-
   if (!dtls1_process_record(s)) {
     rr->length = 0;
     s->packet_length = 0; /* dump this record */
     goto again;           /* get another record */
   }
-  dtls1_record_bitmap_update(s, bitmap); /* Mark receipt of record. */
+  dtls1_record_bitmap_update(s, &s->d1->bitmap); /* Mark receipt of record. */
 
   return 1;
 }
@@ -589,15 +421,11 @@
  *             none of our business
  */
 int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) {
-  int al, i, j, ret;
+  int al, i, ret;
   unsigned int n;
   SSL3_RECORD *rr;
   void (*cb)(const SSL *ssl, int type2, int val) = NULL;
 
-  if (s->s3->rbuf.buf == NULL && !ssl3_setup_buffers(s)) {
-      return -1;
-  }
-
   /* XXX: check what the second '&& type' is about */
   if ((type && (type != SSL3_RT_APPLICATION_DATA) &&
        (type != SSL3_RT_HANDSHAKE) && type) ||
@@ -606,14 +434,6 @@
     return -1;
   }
 
-  /* check whether there's a handshake message (client hello?) waiting */
-  ret = have_handshake_fragment(s, type, buf, len, peek);
-  if (ret) {
-    return ret;
-  }
-
-  /* Now s->d1->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
-
   if (!s->in_handshake && SSL_in_init(s)) {
     /* type == SSL3_RT_APPLICATION_DATA */
     i = s->handshake_func(s);
@@ -635,23 +455,8 @@
    * s->s3->rrec.length   - number of bytes. */
   rr = &s->s3->rrec;
 
-  /* We are not handshaking and have no data yet,
-   * so process data buffered during the last handshake
-   * in advance, if any.
-   */
-  if (s->state == SSL_ST_OK && rr->length == 0) {
-    pitem *item;
-    item = pqueue_pop(s->d1->buffered_app_data.q);
-    if (item) {
-      dtls1_copy_record(s, item);
-
-      OPENSSL_free(item->data);
-      pitem_free(item);
-    }
-  }
-
   /* Check for timeout */
-  if (dtls1_handle_timeout(s) > 0) {
+  if (DTLSv1_handle_timeout(s) > 0) {
     goto start;
   }
 
@@ -673,14 +478,11 @@
 
   /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by
    * ssl3_get_finished. */
-  if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE) {
-    /* We now have application data between CCS and Finished. Most likely the
-     * packets were reordered on their way, so buffer the application data for
-     * later processing rather than dropping the connection. */
-    if (dtls1_buffer_record(s, &(s->d1->buffered_app_data), rr->seq_num) < 0) {
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR);
-      return -1;
-    }
+  if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE &&
+      rr->type != SSL3_RT_ALERT) {
+    /* We now have an unexpected record between CCS and Finished. Most likely
+     * the packets were reordered on their way. DTLS is unreliable, so drop the
+     * packet and expect the peer to retransmit. */
     rr->length = 0;
     goto start;
   }
@@ -729,118 +531,25 @@
     return n;
   }
 
-  /* If we get here, then type != rr->type; if we have a handshake message,
-   * then it was unexpected (Hello Request or Client Hello). */
+  /* If we get here, then type != rr->type. */
 
-  /* In case of record types for which we have 'fragment' storage, fill that so
-   * that we can process the data at a fixed place. */
-  {
-    unsigned int k, dest_maxlen = 0;
-    uint8_t *dest = NULL;
-    unsigned int *dest_len = NULL;
-
-    if (rr->type == SSL3_RT_HANDSHAKE) {
-      dest_maxlen = sizeof s->d1->handshake_fragment;
-      dest = s->d1->handshake_fragment;
-      dest_len = &s->d1->handshake_fragment_len;
-    } else if (rr->type == SSL3_RT_ALERT) {
-      dest_maxlen = sizeof(s->d1->alert_fragment);
-      dest = s->d1->alert_fragment;
-      dest_len = &s->d1->alert_fragment_len;
-    }
-    /* else it's a CCS message, or application data or wrong */
-    else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) {
-      /* Application data while renegotiating is allowed. Try again reading. */
-      if (rr->type == SSL3_RT_APPLICATION_DATA) {
-        BIO *bio;
-        s->s3->in_read_app_data = 2;
-        bio = SSL_get_rbio(s);
-        s->rwstate = SSL_READING;
-        BIO_clear_retry_flags(bio);
-        BIO_set_retry_read(bio);
-        return -1;
-      }
-
-      /* Not certain if this is the right error handling */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD);
-      goto f_err;
-    }
-
-    if (dest_maxlen > 0) {
-      /* XDTLS:  In a pathalogical case, the Client Hello
-       *  may be fragmented--don't always expect dest_maxlen bytes */
-      if (rr->length < dest_maxlen) {
-        s->rstate = SSL_ST_READ_HEADER;
-        rr->length = 0;
-        goto start;
-      }
-
-      /* now move 'n' bytes: */
-      for (k = 0; k < dest_maxlen; k++) {
-        dest[k] = rr->data[rr->off++];
-        rr->length--;
-      }
-      *dest_len = dest_maxlen;
-    }
-  }
-
-  /* s->d1->handshake_fragment_len == 12  iff  rr->type == SSL3_RT_HANDSHAKE;
-   * s->d1->alert_fragment_len == 7      iff  rr->type == SSL3_RT_ALERT.
-   * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */
-
-  /* If we are a client, check for an incoming 'Hello Request': */
-  if (!s->server && s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH &&
-      s->d1->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST &&
-      s->session != NULL && s->session->cipher != NULL) {
-    s->d1->handshake_fragment_len = 0;
-
-    if ((s->d1->handshake_fragment[1] != 0) ||
-        (s->d1->handshake_fragment[2] != 0) ||
-        (s->d1->handshake_fragment[3] != 0)) {
+  /* If an alert record, process one alert out of the record. Note that we allow
+   * a single record to contain multiple alerts. */
+  if (rr->type == SSL3_RT_ALERT) {
+    /* Alerts may not be fragmented. */
+    if (rr->length < 2) {
       al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_HELLO_REQUEST);
+      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_ALERT);
       goto f_err;
     }
 
-    /* no need to check sequence number on HELLO REQUEST messages */
-
     if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
-                      s->d1->handshake_fragment, 4, s, s->msg_callback_arg);
-    }
-
-    if (SSL_is_init_finished(s) && !s->s3->renegotiate) {
-      s->d1->handshake_read_seq++;
-      s->new_session = 1;
-      ssl3_renegotiate(s);
-      if (ssl3_renegotiate_check(s)) {
-        i = s->handshake_func(s);
-        if (i < 0) {
-          return i;
-        }
-        if (i == 0) {
-          OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE);
-          return -1;
-        }
-      }
-    }
-
-    /* we either finished a handshake or ignored the request, now try again to
-     * obtain the (application) data we were asked for */
-    goto start;
-  }
-
-  if (s->d1->alert_fragment_len >= DTLS1_AL_HEADER_LENGTH) {
-    int alert_level = s->d1->alert_fragment[0];
-    int alert_descr = s->d1->alert_fragment[1];
-
-    s->d1->alert_fragment_len = 0;
-
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_ALERT, s->d1->alert_fragment, 2, s,
+      s->msg_callback(0, s->version, SSL3_RT_ALERT, &rr->data[rr->off], 2, s,
                       s->msg_callback_arg);
     }
+    const uint8_t alert_level = rr->data[rr->off++];
+    const uint8_t alert_descr = rr->data[rr->off++];
+    rr->length -= 2;
 
     if (s->info_callback != NULL) {
       cb = s->info_callback;
@@ -849,17 +558,17 @@
     }
 
     if (cb != NULL) {
-      j = (alert_level << 8) | alert_descr;
-      cb(s, SSL_CB_READ_ALERT, j);
+      uint16_t alert = (alert_level << 8) | alert_descr;
+      cb(s, SSL_CB_READ_ALERT, alert);
     }
 
-    if (alert_level == 1) { /* warning */
+    if (alert_level == SSL3_AL_WARNING) {
       s->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
         s->shutdown |= SSL_RECEIVED_SHUTDOWN;
         return 0;
       }
-    } else if (alert_level == 2) { /* fatal */
+    } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
       s->rwstate = SSL_NOTHING;
@@ -888,16 +597,9 @@
   }
 
   if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) {
-    struct ccs_header_st ccs_hdr;
-    unsigned int ccs_hdr_len = DTLS1_CCS_HEADER_LENGTH;
-
-    dtls1_get_ccs_header(rr->data, &ccs_hdr);
-
-    /* 'Change Cipher Spec' is just a single byte, so we know
-     * exactly what the record payload has to look like */
-    /* XDTLS: check that epoch is consistent */
-    if ((rr->length != ccs_hdr_len) || (rr->off != 0) ||
-        (rr->data[0] != SSL3_MT_CCS)) {
+    /* 'Change Cipher Spec' is just a single byte, so we know exactly what the
+     * record payload has to look like */
+    if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) {
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC);
       goto f_err;
@@ -930,91 +632,41 @@
     goto start;
   }
 
-  /* Unexpected handshake message (Client Hello, or protocol violation) */
-  if ((s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) &&
-      !s->in_handshake) {
+  /* Unexpected handshake message. It may be a retransmitted Finished (the only
+   * post-CCS message). Otherwise, it's a pre-CCS handshake message from an
+   * unsupported renegotiation attempt. */
+  if (rr->type == SSL3_RT_HANDSHAKE && !s->in_handshake) {
+    if (rr->length < DTLS1_HM_HEADER_LENGTH) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_HANDSHAKE_RECORD);
+      goto f_err;
+    }
     struct hm_header_st msg_hdr;
+    dtls1_get_message_header(&rr->data[rr->off], &msg_hdr);
 
-    /* this may just be a stale retransmit */
-    dtls1_get_message_header(rr->data, &msg_hdr);
-    if (rr->epoch != s->d1->r_epoch) {
-      rr->length = 0;
-      goto start;
-    }
-
-    /* If we are server, we may have a repeated FINISHED of the client here,
-     * then retransmit our CCS and FINISHED. */
+    /* Ignore a stray Finished from the previous handshake. */
     if (msg_hdr.type == SSL3_MT_FINISHED) {
-      if (dtls1_check_timeout_num(s) < 0) {
-        return -1;
+      if (msg_hdr.frag_off == 0) {
+        /* Retransmit our last flight of messages. If the peer sends the second
+         * Finished, they may not have received ours. Only do this for the
+         * first fragment, in case the Finished was fragmented. */
+        if (dtls1_check_timeout_num(s) < 0) {
+          return -1;
+        }
+
+        dtls1_retransmit_buffered_messages(s);
       }
 
-      dtls1_retransmit_buffered_messages(s);
       rr->length = 0;
       goto start;
     }
-
-    if ((s->state & SSL_ST_MASK) == SSL_ST_OK) {
-      s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT;
-      s->renegotiate = 1;
-      s->new_session = 1;
-    }
-    i = s->handshake_func(s);
-    if (i < 0) {
-      return i;
-    }
-    if (i == 0) {
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-
-    goto start;
   }
 
-  switch (rr->type) {
-    default:
-      /* TLS just ignores unknown message types */
-      if (s->version == TLS1_VERSION) {
-        rr->length = 0;
-        goto start;
-      }
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD);
-      goto f_err;
+  /* We already handled these. */
+  assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT);
 
-    case SSL3_RT_CHANGE_CIPHER_SPEC:
-    case SSL3_RT_ALERT:
-    case SSL3_RT_HANDSHAKE:
-      /* we already handled all of these, with the possible exception of
-       * SSL3_RT_HANDSHAKE when s->in_handshake is set, but that should not
-       * happen when type != rr->type */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR);
-      goto f_err;
-
-    case SSL3_RT_APPLICATION_DATA:
-      /* At this point, we were expecting handshake data, but have application
-       * data. If the library was running inside ssl3_read() (i.e.
-       * in_read_app_data is set) and it makes sense to read application data
-       * at this point (session renegotiation not yet started), we will indulge
-       * it. */
-      if (s->s3->in_read_app_data && (s->s3->total_renegotiations != 0) &&
-          (((s->state & SSL_ST_CONNECT) &&
-            (s->state >= SSL3_ST_CW_CLNT_HELLO_A) &&
-            (s->state <= SSL3_ST_CR_SRVR_HELLO_A)) ||
-           ((s->state & SSL_ST_ACCEPT) &&
-            (s->state <= SSL3_ST_SW_HELLO_REQ_A) &&
-            (s->state >= SSL3_ST_SR_CLNT_HELLO_A)))) {
-        s->s3->in_read_app_data = 2;
-        return -1;
-      } else {
-        al = SSL_AD_UNEXPECTED_MESSAGE;
-        OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD);
-        goto f_err;
-      }
-  }
-
-  /* not reached */
+  al = SSL_AD_UNEXPECTED_MESSAGE;
+  OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD);
 
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -1047,35 +699,6 @@
   return i;
 }
 
-
-/* this only happens when a client hello is received and a handshake is
- * started. */
-static int have_handshake_fragment(SSL *s, int type, uint8_t *buf,
-                                   int len, int peek) {
-  if (type == SSL3_RT_HANDSHAKE && s->d1->handshake_fragment_len > 0) {
-    /* (partially) satisfy request from storage */
-    uint8_t *src = s->d1->handshake_fragment;
-    uint8_t *dst = buf;
-    unsigned int k, n;
-
-    /* peek == 0 */
-    n = 0;
-    while (len > 0 && s->d1->handshake_fragment_len > 0) {
-      *dst++ = *src++;
-      len--;
-      s->d1->handshake_fragment_len--;
-      n++;
-    }
-    /* move any remaining fragment bytes: */
-    for (k = 0; k < s->d1->handshake_fragment_len; k++) {
-      s->d1->handshake_fragment[k] = *src++;
-    }
-    return n;
-  }
-
-  return 0;
-}
-
 /* 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 dtls1_write_bytes(SSL *s, int type, const void *buf, int len) {
@@ -1096,12 +719,9 @@
   SSL3_RECORD *wr;
   SSL3_BUFFER *wb;
 
-  /* first check if there is a SSL3_BUFFER still being written
-   * out.  This will happen with non blocking IO */
-  if (s->s3->wbuf.left != 0) {
-    assert(0); /* XDTLS:  want to see if we ever get here */
-    return ssl3_write_pending(s, type, buf, len);
-  }
+  /* ssl3_write_pending drops the write if |BIO_write| fails in DTLS, so there
+   * is never pending data. */
+  assert(s->s3->wbuf.left == 0);
 
   /* If we have an alert to send, lets send it */
   if (s->s3->alert_dispatch) {
@@ -1119,6 +739,9 @@
   wr = &(s->s3->wrec);
   wb = &(s->s3->wbuf);
 
+  if (wb->buf == NULL && !ssl3_setup_write_buffer(s)) {
+    return -1;
+  }
   p = wb->buf + prefix_len;
 
   /* write the header */
@@ -1147,19 +770,14 @@
     eivlen = s->aead_write_ctx->variable_nonce_len;
   }
 
-  /* lets setup the record stuff. */
-  wr->data = p + eivlen; /* make room for IV in case of CBC */
-  wr->length = (int)len;
-  wr->input = (unsigned char *)buf;
-
-  /* we now 'read' from wr->input, wr->length bytes into wr->data */
-  memcpy(wr->data, wr->input, wr->length);
-  wr->input = wr->data;
-
-  /* this is true regardless of mac size */
+  /* Assemble the input for |s->enc_method->enc|. The input is the plaintext
+   * with |eivlen| bytes of space prepended for the explicit nonce. */
   wr->input = p;
+  wr->length = eivlen + len;
+  memcpy(p + eivlen, buf, len);
+
+  /* Encrypt in-place, so the output also goes into |p|. */
   wr->data = p;
-  wr->length += eivlen;
 
   if (!s->enc_method->enc(s, 1)) {
     goto err;
@@ -1182,7 +800,9 @@
   wr->type = type; /* not needed but helps for debugging */
   wr->length += DTLS1_RT_HEADER_LENGTH;
 
-  ssl3_record_sequence_update(&(s->s3->write_sequence[0]));
+  if (!ssl3_record_sequence_update(&s->s3->write_sequence[2], 6)) {
+    goto err;
+  }
 
   /* now let's set up wb */
   wb->left = prefix_len + wr->length;
@@ -1285,23 +905,6 @@
   return i;
 }
 
-static DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr,
-                                      unsigned int *is_next_epoch) {
-  *is_next_epoch = 0;
-
-  /* In current epoch, accept HM, CCS, DATA, & ALERT */
-  if (rr->epoch == s->d1->r_epoch) {
-    return &s->d1->bitmap;
-  } else if (rr->epoch == (unsigned long)(s->d1->r_epoch + 1) &&
-             (rr->type == SSL3_RT_HANDSHAKE || rr->type == SSL3_RT_ALERT)) {
-    /* Only HM and ALERT messages can be from the next epoch */
-    *is_next_epoch = 1;
-    return &s->d1->next_bitmap;
-  }
-
-  return NULL;
-}
-
 void dtls1_reset_seq_numbers(SSL *s, int rw) {
   uint8_t *seq;
   unsigned int seq_bytes = sizeof(s->s3->read_sequence);
@@ -1309,8 +912,7 @@
   if (rw & SSL3_CC_READ) {
     seq = s->s3->read_sequence;
     s->d1->r_epoch++;
-    memcpy(&(s->d1->bitmap), &(s->d1->next_bitmap), sizeof(DTLS1_BITMAP));
-    memset(&(s->d1->next_bitmap), 0x00, sizeof(DTLS1_BITMAP));
+    memset(&s->d1->bitmap, 0, sizeof(DTLS1_BITMAP));
   } else {
     seq = s->s3->write_sequence;
     memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3->write_sequence));
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index b85ff9b..5928fc8 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -115,12 +115,13 @@
 */
 
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/bytestring.h>
-#include <openssl/obj.h>
 #include <openssl/err.h>
+#include <openssl/obj.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 #include <openssl/srtp.h>
 
 
@@ -169,7 +170,7 @@
 }
 
 static int ssl_ctx_make_profiles(const char *profiles_string,
-                                 STACK_OF(SRTP_PROTECTION_PROFILE) * *out) {
+                                 STACK_OF(SRTP_PROTECTION_PROFILE) **out) {
   STACK_OF(SRTP_PROTECTION_PROFILE) *profiles;
 
   const char *col;
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
index 5bce98e..e314910 100644
--- a/src/ssl/d1_srvr.c
+++ b/src/ssl/d1_srvr.c
@@ -118,21 +118,20 @@
 #include <openssl/bn.h>
 #include <openssl/buf.h>
 #include <openssl/dh.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/md5.h>
 #include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
-static int dtls1_send_hello_verify_request(SSL *s);
-
 int dtls1_accept(SSL *s) {
   BUF_MEM *buf = NULL;
   void (*cb)(const SSL *ssl, int type, int val) = NULL;
-  unsigned long alg_a;
+  uint32_t alg_a;
   int ret = -1;
   int new_state, state, skip = 0;
 
@@ -180,11 +179,6 @@
           buf = NULL;
         }
 
-        if (!ssl3_setup_buffers(s)) {
-          ret = -1;
-          goto end;
-        }
-
         s->init_num = 0;
 
         if (s->state != SSL_ST_RENEGOTIATE) {
@@ -200,11 +194,9 @@
           }
 
           s->state = SSL3_ST_SR_CLNT_HELLO_A;
-          s->ctx->stats.sess_accept++;
         } else {
           /* s->state == SSL_ST_RENEGOTIATE, * we will just send a
            * HelloRequest */
-          s->ctx->stats.sess_accept_renegotiate++;
           s->state = SSL3_ST_SW_HELLO_REQ_A;
         }
 
@@ -244,33 +236,10 @@
           goto end;
         }
         dtls1_stop_timer(s);
-
-        if (ret == 1 && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) {
-          s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A;
-        } else {
-          s->state = SSL3_ST_SW_SRVR_HELLO_A;
-        }
-
+        s->state = SSL3_ST_SW_SRVR_HELLO_A;
         s->init_num = 0;
         break;
 
-      case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A:
-      case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B:
-        ret = dtls1_send_hello_verify_request(s);
-        if (ret <= 0) {
-          goto end;
-        }
-        s->state = SSL3_ST_SW_FLUSH;
-        s->s3->tmp.next_state = SSL3_ST_SR_CLNT_HELLO_A;
-
-        /* HelloVerifyRequest resets Finished MAC */
-        if (!ssl3_init_finished_mac(s)) {
-          OPENSSL_PUT_ERROR(SSL, dtls1_accept, ERR_R_INTERNAL_ERROR);
-          ret = -1;
-          goto end;
-        }
-        break;
-
       case SSL3_ST_SW_SRVR_HELLO_A:
       case SSL3_ST_SW_SRVR_HELLO_B:
         s->renegotiate = 2;
@@ -347,13 +316,6 @@
              * don't request cert during re-negotiation: */
             ((s->session->peer != NULL) &&
              (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) ||
-            /* never request cert in anonymous ciphersuites
-             * (see section "Certificate request" in SSL 3 drafts
-             * and in RFC 2246): */
-            ((s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
-             /* ... except when the application insists on verification
-              * (against the specs, but s3_clnt.c accepts this for SSL 3) */
-             !(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) ||
             /* With normal PSK Certificates and
              * Certificate Requests are omitted */
             (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) {
@@ -368,12 +330,7 @@
           if (ret <= 0) {
             goto end;
           }
-#ifndef NETSCAPE_HANG_BUG
           s->state = SSL3_ST_SW_SRVR_DONE_A;
-#else
-          s->state = SSL3_ST_SW_FLUSH;
-          s->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
-#endif
           s->init_num = 0;
         }
         break;
@@ -393,12 +350,6 @@
       case SSL3_ST_SW_FLUSH:
         s->rwstate = SSL_WRITING;
         if (BIO_flush(s->wbio) <= 0) {
-          /* If the write error was fatal, stop trying */
-          if (!BIO_should_retry(s->wbio)) {
-            s->rwstate = SSL_NOTHING;
-            s->state = s->s3->tmp.next_state;
-          }
-
           ret = -1;
           goto end;
         }
@@ -527,8 +478,6 @@
 
           ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
 
-          s->ctx->stats.sess_accept_good++;
-
           if (cb != NULL) {
             cb(s, SSL_CB_HANDSHAKE_DONE, 1);
           }
@@ -562,44 +511,9 @@
 
 end:
   s->in_handshake--;
-  if (buf != NULL) {
-    BUF_MEM_free(buf);
-  }
+  BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(s, SSL_CB_ACCEPT_EXIT, ret);
   }
   return ret;
 }
-
-int dtls1_send_hello_verify_request(SSL *s) {
-  uint8_t *msg, *p;
-
-  if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) {
-    msg = p = ssl_handshake_start(s);
-    /* Always use DTLS 1.0 version: see RFC 6347 */
-    *(p++) = DTLS1_VERSION >> 8;
-    *(p++) = DTLS1_VERSION & 0xFF;
-
-    /* Inform the callback how much space is in the
-     * cookie's buffer. */
-    s->d1->cookie_len = sizeof(s->d1->cookie);
-
-    if (s->ctx->app_gen_cookie_cb == NULL ||
-        s->ctx->app_gen_cookie_cb(s, s->d1->cookie, &(s->d1->cookie_len)) ==
-            0) {
-      OPENSSL_PUT_ERROR(SSL, dtls1_send_hello_verify_request,
-                        ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-
-    *(p++) = (uint8_t)s->d1->cookie_len;
-    memcpy(p, s->d1->cookie, s->d1->cookie_len);
-    p += s->d1->cookie_len;
-
-    ssl_set_handshake_header(s, DTLS1_MT_HELLO_VERIFY_REQUEST, p - msg);
-    s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B;
-  }
-
-  /* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */
-  return ssl_do_write(s);
-}
diff --git a/src/ssl/ssl_locl.h b/src/ssl/internal.h
similarity index 79%
rename from src/ssl/ssl_locl.h
rename to src/ssl/internal.h
index a0c323c..3bd749d 100644
--- a/src/ssl/ssl_locl.h
+++ b/src/ssl/internal.h
@@ -139,25 +139,161 @@
  * OTHERWISE.
  */
 
-#ifndef HEADER_SSL_LOCL_H
-#define HEADER_SSL_LOCL_H
+#ifndef OPENSSL_HEADER_SSL_INTERNAL_H
+#define OPENSSL_HEADER_SSL_INTERNAL_H
 
 #include <openssl/base.h>
 
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
 #include <openssl/aead.h>
-#include <openssl/bio.h>
-#include <openssl/buf.h>
-#include <openssl/dsa.h>
-#include <openssl/err.h>
-#include <openssl/rsa.h>
+#include <openssl/pqueue.h>
 #include <openssl/ssl.h>
 #include <openssl/stack.h>
 
+#if defined(OPENSSL_WINDOWS)
+/* Windows defines struct timeval in winsock2.h. */
+#pragma warning(push, 3)
+#include <winsock2.h>
+#pragma warning(pop)
+#else
+#include <sys/types.h>
+#endif
+
+
+/* Cipher suites. */
+
+/* Bits for |algorithm_mkey| (key exchange algorithm). */
+#define SSL_kRSA 0x00000001L
+#define SSL_kDHE 0x00000002L
+#define SSL_kECDHE 0x00000004L
+/* SSL_kPSK is only set for plain PSK, not ECDHE_PSK. */
+#define SSL_kPSK 0x00000008L
+
+/* Bits for |algorithm_auth| (server authentication). */
+#define SSL_aRSA 0x00000001L
+#define SSL_aECDSA 0x00000002L
+/* SSL_aPSK is set for both PSK and ECDHE_PSK. */
+#define SSL_aPSK 0x00000004L
+
+/* Bits for |algorithm_enc| (symmetric encryption). */
+#define SSL_3DES 0x00000001L
+#define SSL_RC4 0x00000002L
+#define SSL_AES128 0x00000004L
+#define SSL_AES256 0x00000008L
+#define SSL_AES128GCM 0x00000010L
+#define SSL_AES256GCM 0x00000020L
+#define SSL_CHACHA20POLY1305 0x00000040L
+
+#define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
+
+/* Bits for |algorithm_mac| (symmetric authentication). */
+#define SSL_MD5 0x00000001L
+#define SSL_SHA1 0x00000002L
+#define SSL_SHA256 0x00000004L
+#define SSL_SHA384 0x00000008L
+/* SSL_AEAD is set for all AEADs. */
+#define SSL_AEAD 0x00000010L
+
+/* Bits for |algorithm_ssl| (protocol version). These denote the first protocol
+ * version which introduced the cipher.
+ *
+ * TODO(davidben): These are extremely confusing, both in code and in
+ * cipher rules. Try to remove them. */
+#define SSL_SSLV3 0x00000002L
+#define SSL_TLSV1 SSL_SSLV3
+#define SSL_TLSV1_2 0x00000004L
+
+/* Bits for |algorithm2| (handshake digests and other extra flags). */
+
+#define SSL_HANDSHAKE_MAC_MD5 0x10
+#define SSL_HANDSHAKE_MAC_SHA 0x20
+#define SSL_HANDSHAKE_MAC_SHA256 0x40
+#define SSL_HANDSHAKE_MAC_SHA384 0x80
+#define SSL_HANDSHAKE_MAC_DEFAULT \
+  (SSL_HANDSHAKE_MAC_MD5 | SSL_HANDSHAKE_MAC_SHA)
+
+/* SSL_MAX_DIGEST is the number of digest types which exist. When adding a new
+ * one, update the table in ssl_cipher.c. */
+#define SSL_MAX_DIGEST 4
+
+#define TLS1_PRF_DGST_MASK (0xff << TLS1_PRF_DGST_SHIFT)
+
+#define TLS1_PRF_DGST_SHIFT 10
+#define TLS1_PRF_MD5 (SSL_HANDSHAKE_MAC_MD5 << TLS1_PRF_DGST_SHIFT)
+#define TLS1_PRF_SHA1 (SSL_HANDSHAKE_MAC_SHA << TLS1_PRF_DGST_SHIFT)
+#define TLS1_PRF_SHA256 (SSL_HANDSHAKE_MAC_SHA256 << TLS1_PRF_DGST_SHIFT)
+#define TLS1_PRF_SHA384 (SSL_HANDSHAKE_MAC_SHA384 << TLS1_PRF_DGST_SHIFT)
+#define TLS1_PRF (TLS1_PRF_MD5 | TLS1_PRF_SHA1)
+
+/* SSL_CIPHER_ALGORITHM2_AEAD is a flag in SSL_CIPHER.algorithm2 which
+ * indicates that the cipher is implemented via an EVP_AEAD. */
+#define SSL_CIPHER_ALGORITHM2_AEAD (1 << 23)
+
+/* SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD is a flag in
+ * SSL_CIPHER.algorithm2 which indicates that the variable part of the nonce is
+ * included as a prefix of the record. (AES-GCM, for example, does with with an
+ * 8-byte variable nonce.) */
+#define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22)
+
+/* Bits for |algo_strength|, cipher strength information. */
+#define SSL_MEDIUM 0x00000001L
+#define SSL_HIGH 0x00000002L
+#define SSL_FIPS 0x00000004L
+
+/* ssl_cipher_get_evp_aead sets |*out_aead| to point to the correct EVP_AEAD
+ * object for |cipher| protocol version |version|. It sets |*out_mac_secret_len|
+ * and |*out_fixed_iv_len| to the MAC key length and fixed IV length,
+ * respectively. The MAC key length is zero except for legacy block and stream
+ * ciphers. It returns 1 on success and 0 on error. */
+int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
+                            size_t *out_mac_secret_len,
+                            size_t *out_fixed_iv_len,
+                            const SSL_CIPHER *cipher, uint16_t version);
+
+/* ssl_get_handshake_digest looks up the |i|th handshake digest type and sets
+ * |*out_mask| to the |SSL_HANDSHAKE_MAC_*| mask and |*out_md| to the
+ * |EVP_MD|. It returns one on successs and zero if |i| >= |SSL_MAX_DIGEST|. */
+int ssl_get_handshake_digest(uint32_t *out_mask, const EVP_MD **out_md,
+                             size_t i);
+
+/* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
+ * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
+ * |ssl_cipher_preference_list_st| containing the result.
+ * |*out_cipher_list_by_id| is set to a list of selected ciphers sorted by
+ * id. It returns |(*out_cipher_list)->ciphers| on success and NULL on
+ * failure. */
+STACK_OF(SSL_CIPHER) *
+ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
+                       struct ssl_cipher_preference_list_st **out_cipher_list,
+                       STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
+                       const char *rule_str);
+
+/* SSL_PKEY_* denote certificate types. */
+#define SSL_PKEY_RSA_ENC 0
+#define SSL_PKEY_RSA_SIGN 1
+#define SSL_PKEY_ECC 2
+#define SSL_PKEY_NUM 3
+
+/* ssl_cipher_get_cert_index returns the |SSL_PKEY_*| value corresponding to the
+ * certificate type of |cipher| or -1 if there is none. */
+int ssl_cipher_get_cert_index(const SSL_CIPHER *cipher);
+
+/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server
+ * public key in the key exchange, sent in a server Certificate message.
+ * Otherwise it returns 0. */
+int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
+
+/* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a
+ * ServerKeyExchange message. Otherwise it returns 0.
+ *
+ * Unlike ssl_cipher_has_server_public_key, some ciphers take optional
+ * ServerKeyExchanges. PSK and RSA_PSK only use the ServerKeyExchange to
+ * communicate a psk_identity_hint, so it is optional. */
+int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher);
+
+
+/* Underdocumented functions.
+ *
+ * Functions below here haven't been touched up and may be underdocumented. */
 
 #define c2l(c, l)                                                            \
   (l = ((unsigned long)(*((c)++))), l |= (((unsigned long)(*((c)++))) << 8), \
@@ -261,110 +397,10 @@
 
 /* LOCAL STUFF */
 
-#define SSL_DECRYPT 0
-#define SSL_ENCRYPT 1
-
-#define TWO_BYTE_BIT 0x80
-#define SEC_ESC_BIT 0x40
-#define TWO_BYTE_MASK 0x7fff
-#define THREE_BYTE_MASK 0x3fff
-
-#define INC32(a) ((a) = ((a) + 1) & 0xffffffffL)
-#define DEC32(a) ((a) = ((a)-1) & 0xffffffffL)
-#define MAX_MAC_SIZE 20 /* up from 16 for SSLv3 */
-
-/* Define the Bitmasks for SSL_CIPHER.algorithms.
- *
- * This bits are used packed as dense as possible. If new methods/ciphers etc
- * will be added, the bits a likely to change, so this information is for
- * internal library use only, even though SSL_CIPHER.algorithms can be publicly
- * accessed. Use the according functions for cipher management instead.
- *
- * The bit mask handling in the selection and sorting scheme in
- * ssl_create_cipher_list() has only limited capabilities, reflecting that the
- * different entities within are mutually exclusive:
- * ONLY ONE BIT PER MASK CAN BE SET AT A TIME. */
-
-/* Bits for algorithm_mkey (key exchange algorithm) */
-#define SSL_kRSA 0x00000001L   /* RSA key exchange */
-#define SSL_kEDH 0x00000002L   /* tmp DH key no DH cert */
-#define SSL_kEECDH 0x00000004L /* ephemeral ECDH */
-#define SSL_kPSK 0x00000008L   /* PSK */
-
-/* Bits for algorithm_auth (server authentication) */
-#define SSL_aRSA 0x00000001L   /* RSA auth */
-#define SSL_aNULL 0x00000002L  /* no auth (i.e. use ADH or AECDH) */
-#define SSL_aECDSA 0x00000004L /* ECDSA auth*/
-#define SSL_aPSK 0x00000008L   /* PSK auth */
-
-/* Bits for algorithm_enc (symmetric encryption) */
-#define SSL_3DES 0x00000001L
-#define SSL_RC4 0x00000002L
-#define SSL_AES128 0x00000004L
-#define SSL_AES256 0x00000008L
-#define SSL_AES128GCM 0x00000010L
-#define SSL_AES256GCM 0x00000020L
-#define SSL_CHACHA20POLY1305 0x00000040L
-
-#define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
-
-/* Bits for algorithm_mac (symmetric authentication) */
-
-#define SSL_MD5 0x00000001L
-#define SSL_SHA1 0x00000002L
-#define SSL_SHA256 0x00000004L
-#define SSL_SHA384 0x00000008L
-/* Not a real MAC, just an indication it is part of cipher */
-#define SSL_AEAD 0x00000010L
-
-/* Bits for algorithm_ssl (protocol version) */
-#define SSL_SSLV3 0x00000002L
-#define SSL_TLSV1 SSL_SSLV3 /* for now */
-#define SSL_TLSV1_2 0x00000004L
-
-/* Bits for algorithm2 (handshake digests and other extra flags) */
-
-#define SSL_HANDSHAKE_MAC_MD5 0x10
-#define SSL_HANDSHAKE_MAC_SHA 0x20
-#define SSL_HANDSHAKE_MAC_SHA256 0x40
-#define SSL_HANDSHAKE_MAC_SHA384 0x80
-#define SSL_HANDSHAKE_MAC_DEFAULT \
-  (SSL_HANDSHAKE_MAC_MD5 | SSL_HANDSHAKE_MAC_SHA)
-
-/* When adding new digest in the ssl_ciph.c and increment SSM_MD_NUM_IDX
- * make sure to update this constant too */
-#define SSL_MAX_DIGEST 4
-
-#define TLS1_PRF_DGST_MASK (0xff << TLS1_PRF_DGST_SHIFT)
-
-#define TLS1_PRF_DGST_SHIFT 10
-#define TLS1_PRF_MD5 (SSL_HANDSHAKE_MAC_MD5 << TLS1_PRF_DGST_SHIFT)
-#define TLS1_PRF_SHA1 (SSL_HANDSHAKE_MAC_SHA << TLS1_PRF_DGST_SHIFT)
-#define TLS1_PRF_SHA256 (SSL_HANDSHAKE_MAC_SHA256 << TLS1_PRF_DGST_SHIFT)
-#define TLS1_PRF_SHA384 (SSL_HANDSHAKE_MAC_SHA384 << TLS1_PRF_DGST_SHIFT)
-#define TLS1_PRF (TLS1_PRF_MD5 | TLS1_PRF_SHA1)
-
 #define TLSEXT_CHANNEL_ID_SIZE 128
 
-/* SSL_CIPHER_ALGORITHM2_AEAD is a flag in SSL_CIPHER.algorithm2 which
- * indicates that the cipher is implemented via an EVP_AEAD. */
-#define SSL_CIPHER_ALGORITHM2_AEAD (1 << 23)
-
-/* SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD is a flag in
- * SSL_CIPHER.algorithm2 which indicates that the variable part of the nonce is
- * included as a prefix of the record. (AES-GCM, for example, does with with an
- * 8-byte variable nonce.) */
-#define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22)
-
-/* Cipher strength information. */
-#define SSL_MEDIUM 0x00000001L
-#define SSL_HIGH 0x00000002L
-#define SSL_FIPS 0x00000004L
-
-/* we have used 000001ff - 23 bits left to go */
-
 /* Check if an SSL structure is using DTLS */
-#define SSL_IS_DTLS(s) (s->enc_method->enc_flags & SSL_ENC_FLAG_DTLS)
+#define SSL_IS_DTLS(s) (s->method->is_dtls)
 /* See if we need explicit IV */
 #define SSL_USE_EXPLICIT_IV(s) \
   (s->enc_method->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV)
@@ -381,36 +417,30 @@
   ((SSL_IS_DTLS(s) && s->client_version <= DTLS1_2_VERSION) || \
    (!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION))
 
-/* Mostly for SSLv3 */
-#define SSL_PKEY_RSA_ENC 0
-#define SSL_PKEY_RSA_SIGN 1
-#define SSL_PKEY_ECC 2
-#define SSL_PKEY_NUM 3
-
 /* SSL_kRSA <- RSA_ENC | (RSA_TMP & RSA_SIGN) |
  * 	    <- (EXPORT & (RSA_ENC | RSA_TMP) & RSA_SIGN)
  * SSL_kDH  <- DH_ENC & (RSA_ENC | RSA_SIGN | DSA_SIGN)
- * SSL_kEDH <- RSA_ENC | RSA_SIGN | DSA_SIGN
+ * SSL_kDHE <- RSA_ENC | RSA_SIGN | DSA_SIGN
  * SSL_aRSA <- RSA_ENC | RSA_SIGN
  * SSL_aDSS <- DSA_SIGN */
 
 #define PENDING_SESSION -10000
-#define CERTIFICATE_SELECTION_PENDING -10001
 
 /* From RFC4492, used in encoding the curve type in ECParameters */
 #define EXPLICIT_PRIME_CURVE_TYPE 1
 #define EXPLICIT_CHAR2_CURVE_TYPE 2
 #define NAMED_CURVE_TYPE 3
 
-/* Values for the |hash_message| parameter of |s->method->ssl_get_message|. */
-#define SSL_GET_MESSAGE_DONT_HASH_MESSAGE 0
-#define SSL_GET_MESSAGE_HASH_MESSAGE 1
+enum ssl_hash_message_t {
+  ssl_dont_hash_message,
+  ssl_hash_message,
+};
 
 typedef struct cert_pkey_st {
   X509 *x509;
   EVP_PKEY *privatekey;
   /* Chain for this certificate */
-  STACK_OF(X509) * chain;
+  STACK_OF(X509) *chain;
 } CERT_PKEY;
 
 typedef struct cert_st {
@@ -428,19 +458,20 @@
    * round-about way of checking the server's cipher was one of the advertised
    * ones. (Currently it checks the masks and then the list of ciphers prior to
    * applying the masks in ClientHello.) */
-  unsigned long mask_k;
-  unsigned long mask_a;
-  unsigned long mask_ssl;
+  uint32_t mask_k;
+  uint32_t mask_a;
+  uint32_t mask_ssl;
 
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
-  EC_KEY *ecdh_tmp;
-  /* Callback for generating ephemeral ECDH keys */
+
+  /* ecdh_nid, if not |NID_undef|, is the NID of the curve to use for ephemeral
+   * ECDH keys. If unset, |ecdh_tmp_cb| is consulted. */
+  int ecdh_nid;
+  /* ecdh_tmp_cb is a callback for selecting the curve to use for ephemeral ECDH
+   * keys. If NULL, a curve is selected automatically. See
+   * |SSL_CTX_set_tmp_ecdh_callback|. */
   EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize);
-  /* Select ECDH parameters automatically */
-  int ecdh_tmp_auto;
-  /* Flags related to certificates */
-  unsigned int cert_flags;
   CERT_PKEY pkeys[SSL_PKEY_NUM];
 
   /* Server-only: client_certificate_types is list of certificate types to
@@ -488,14 +519,10 @@
    * If NULL the parent SSL_CTX store is used instead. */
   X509_STORE *chain_store;
   X509_STORE *verify_store;
-
-  /* Raw values of the cipher list from a client */
-  uint8_t *ciphers_raw;
-  size_t ciphers_rawlen;
 } CERT;
 
 typedef struct sess_cert_st {
-  STACK_OF(X509) * cert_chain; /* as received from peer (not for SSL2) */
+  STACK_OF(X509) *cert_chain; /* as received from peer (not for SSL2) */
 
   /* The 'peer_...' members are used only by clients. */
   int peer_cert_type;
@@ -535,6 +562,8 @@
 
 /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
 struct ssl_protocol_method_st {
+  /* is_dtls is one if the protocol is DTLS and zero otherwise. */
+  char is_dtls;
   int (*ssl_new)(SSL *s);
   void (*ssl_free)(SSL *s);
   int (*ssl_accept)(SSL *s);
@@ -546,18 +575,22 @@
   int (*ssl_renegotiate)(SSL *s);
   int (*ssl_renegotiate_check)(SSL *s);
   long (*ssl_get_message)(SSL *s, int header_state, int body_state,
-                          int msg_type, long max, int hash_message, int *ok);
+                          int msg_type, long max,
+                          enum ssl_hash_message_t hash_message, int *ok);
   int (*ssl_read_bytes)(SSL *s, int type, uint8_t *buf, int len, int peek);
   int (*ssl_write_bytes)(SSL *s, int type, const void *buf_, int len);
   int (*ssl_dispatch_alert)(SSL *s);
   long (*ssl_ctrl)(SSL *s, int cmd, long larg, void *parg);
   long (*ssl_ctx_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
   int (*ssl_pending)(const SSL *s);
-  int (*num_ciphers)(void);
-  const SSL_CIPHER *(*get_cipher)(unsigned ncipher);
-  int (*ssl_version)(void);
-  long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)(void));
-  long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void));
+  size_t (*num_ciphers)(void);
+  const SSL_CIPHER *(*get_cipher)(size_t i);
+  /* Handshake header length */
+  unsigned int hhlen;
+  /* Set the handshake header */
+  int (*set_handshake_header)(SSL *s, int type, unsigned long len);
+  /* Write out handshake message */
+  int (*do_write)(SSL *s);
 };
 
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
@@ -570,7 +603,6 @@
   int (*generate_master_secret)(SSL *, uint8_t *, const uint8_t *, size_t);
   int (*change_cipher_state)(SSL *, int);
   int (*final_finish_mac)(SSL *, const char *, int, uint8_t *);
-  int finish_mac_length;
   int (*cert_verify_mac)(SSL *, int, uint8_t *);
   const char *client_finished_label;
   int client_finished_label_len;
@@ -581,20 +613,14 @@
                                 const uint8_t *, size_t, int use_context);
   /* Various flags indicating protocol version requirements */
   unsigned int enc_flags;
-  /* Handshake header length */
-  unsigned int hhlen;
-  /* Set the handshake header */
-  void (*set_handshake_header)(SSL *s, int type, unsigned long len);
-  /* Write out handshake message */
-  int (*do_write)(SSL *s);
 };
 
-#define SSL_HM_HEADER_LENGTH(s) s->enc_method->hhlen
+#define SSL_HM_HEADER_LENGTH(s) s->method->hhlen
 #define ssl_handshake_start(s) \
-  (((uint8_t *)s->init_buf->data) + s->enc_method->hhlen)
+  (((uint8_t *)s->init_buf->data) + s->method->hhlen)
 #define ssl_set_handshake_header(s, htype, len) \
-  s->enc_method->set_handshake_header(s, htype, len)
-#define ssl_do_write(s) s->enc_method->do_write(s)
+  s->method->set_handshake_header(s, htype, len)
+#define ssl_do_write(s) s->method->do_write(s)
 
 /* Values for enc_flags */
 
@@ -604,11 +630,9 @@
 #define SSL_ENC_FLAG_SIGALGS 0x2
 /* Uses SHA256 default PRF */
 #define SSL_ENC_FLAG_SHA256_PRF 0x4
-/* Is DTLS */
-#define SSL_ENC_FLAG_DTLS 0x8
 /* Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2:
  * may apply to others in future. */
-#define SSL_ENC_FLAG_TLS1_2_CIPHERS 0x10
+#define SSL_ENC_FLAG_TLS1_2_CIPHERS 0x8
 
 /* ssl_aead_ctx_st contains information about an AEAD that is being used to
  * encrypt an SSL connection. */
@@ -633,20 +657,117 @@
   char omit_version_in_ad;
 };
 
+/* lengths of messages */
+#define DTLS1_COOKIE_LENGTH 256
+
+#define DTLS1_RT_HEADER_LENGTH 13
+
+#define DTLS1_HM_HEADER_LENGTH 12
+
+#define DTLS1_CCS_HEADER_LENGTH 1
+
+#define DTLS1_AL_HEADER_LENGTH 2
+
+typedef struct dtls1_bitmap_st {
+  /* map is a bit mask of the last 64 sequence numbers. Bit
+   * |1<<i| corresponds to |max_seq_num - i|. */
+  uint64_t map;
+  /* max_seq_num is the largest sequence number seen so far. It
+   * is a 64-bit value in big-endian encoding. */
+  uint8_t max_seq_num[8];
+} DTLS1_BITMAP;
+
+/* TODO(davidben): This structure is used for both incoming messages and
+ * outgoing messages. |is_ccs| and |epoch| are only used in the latter and
+ * should be moved elsewhere. */
+struct hm_header_st {
+  uint8_t type;
+  uint32_t msg_len;
+  uint16_t seq;
+  uint32_t frag_off;
+  uint32_t frag_len;
+  int is_ccs;
+  /* epoch, for buffered outgoing messages, is the epoch the message was
+   * originally sent in. */
+  uint16_t epoch;
+};
+
+/* TODO(davidben): This structure is used for both incoming messages and
+ * outgoing messages. |fragment| and |reassembly| are only used in the former
+ * and should be moved elsewhere. */
+typedef struct hm_fragment_st {
+  struct hm_header_st msg_header;
+  uint8_t *fragment;
+  uint8_t *reassembly;
+} hm_fragment;
+
+typedef struct dtls1_state_st {
+  /* send_cookie is true if we are resending the ClientHello
+   * with a cookie from a HelloVerifyRequest. */
+  unsigned int send_cookie;
+
+  uint8_t cookie[DTLS1_COOKIE_LENGTH];
+  size_t cookie_len;
+
+  /* The current data and handshake epoch.  This is initially undefined, and
+   * starts at zero once the initial handshake is completed. */
+  uint16_t r_epoch;
+  uint16_t w_epoch;
+
+  /* records being received in the current epoch */
+  DTLS1_BITMAP bitmap;
+
+  /* handshake message numbers */
+  uint16_t handshake_write_seq;
+  uint16_t next_handshake_write_seq;
+
+  uint16_t handshake_read_seq;
+
+  /* save last sequence number for retransmissions */
+  uint8_t last_write_sequence[8];
+
+  /* buffered_messages is a priority queue of incoming handshake messages that
+   * have yet to be processed.
+   *
+   * TODO(davidben): This data structure may as well be a ring buffer of fixed
+   * size. */
+  pqueue buffered_messages;
+
+  /* send_messages is a priority queue of outgoing handshake messages sent in
+   * the most recent handshake flight.
+   *
+   * TODO(davidben): This data structure may as well be a STACK_OF(T). */
+  pqueue sent_messages;
+
+  unsigned int mtu; /* max DTLS packet size */
+
+  struct hm_header_st w_msg_hdr;
+
+  /* num_timeouts is the number of times the retransmit timer has fired since
+   * the last time it was reset. */
+  unsigned int num_timeouts;
+
+  /* Indicates when the last handshake msg or heartbeat sent will
+   * timeout. */
+  struct timeval next_timeout;
+
+  /* Timeout duration */
+  unsigned short timeout_duration;
+
+  unsigned int change_cipher_spec_ok;
+} DTLS1_STATE;
+
 extern const SSL_CIPHER ssl3_ciphers[];
 
 extern const SSL3_ENC_METHOD TLSv1_enc_data;
 extern const SSL3_ENC_METHOD TLSv1_1_enc_data;
 extern const SSL3_ENC_METHOD TLSv1_2_enc_data;
 extern const SSL3_ENC_METHOD SSLv3_enc_data;
-extern const SSL3_ENC_METHOD DTLSv1_enc_data;
-extern const SSL3_ENC_METHOD DTLSv1_2_enc_data;
 
 void ssl_clear_cipher_ctx(SSL *s);
 int ssl_clear_bad_session(SSL *s);
 CERT *ssl_cert_new(void);
 CERT *ssl_cert_dup(CERT *cert);
-int ssl_cert_inst(CERT **o);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
 SESS_CERT *ssl_sess_cert_new(void);
@@ -655,50 +776,27 @@
 int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx);
 int ssl_cipher_id_cmp(const void *in_a, const void *in_b);
 int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **ap, const SSL_CIPHER **bp);
-STACK_OF(SSL_CIPHER) * ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
-int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) * sk, uint8_t *p);
-STACK_OF(SSL_CIPHER) *
-    ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *meth,
-                           struct ssl_cipher_preference_list_st **pref,
-                           STACK_OF(SSL_CIPHER) * *sorted, const char *rule_str,
-                           CERT *c);
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
+int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p);
 struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup(
     struct ssl_cipher_preference_list_st *cipher_list);
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list);
 struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers(
-    STACK_OF(SSL_CIPHER) * ciphers);
+    STACK_OF(SSL_CIPHER) *ciphers);
 struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s);
 
-/* ssl_cipher_get_evp_aead sets |*out_aead| to point to the correct EVP_AEAD
-* object for |cipher| protocol version |version|. It sets |*out_mac_secret_len|
-* and |*out_fixed_iv_len| to the MAC key length and fixed IV length,
-* respectively. The MAC key length is zero except for legacy block and stream
-* ciphers. It returns 1 on success and 0 on error. */
-int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
-                            size_t *out_mac_secret_len,
-                            size_t *out_fixed_iv_len,
-                            const SSL_CIPHER *cipher, uint16_t version);
-
-int ssl_get_handshake_digest(size_t i, long *mask, const EVP_MD **md);
-int ssl_cipher_get_cert_index(const SSL_CIPHER *c);
-int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
-int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher);
-
-int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) * chain);
-int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) * chain);
+int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain);
+int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain);
 int ssl_cert_add0_chain_cert(CERT *c, X509 *x);
 int ssl_cert_add1_chain_cert(CERT *c, X509 *x);
 int ssl_cert_select_current(CERT *c, X509 *x);
 void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg);
 
-int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) * sk);
+int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk);
 int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l);
 int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags);
 int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref);
-int ssl_undefined_function(SSL *s);
-int ssl_undefined_void_function(void);
-int ssl_undefined_const_function(const SSL *s);
 CERT_PKEY *ssl_get_server_send_pkey(const SSL *s);
 EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *c);
 int ssl_cert_type(EVP_PKEY *pkey);
@@ -707,10 +805,10 @@
  * authentication cipher suite masks compatible with the server configuration
  * and current ClientHello parameters of |s|. It sets |*out_mask_k| to the key
  * exchange mask and |*out_mask_a| to the authentication mask. */
-void ssl_get_compatible_server_ciphers(SSL *s, unsigned long *out_mask_k,
-                                       unsigned long *out_mask_a);
+void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k,
+                                       uint32_t *out_mask_a);
 
-STACK_OF(SSL_CIPHER) * ssl_get_ciphers_by_id(SSL *s);
+STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *s);
 int ssl_verify_alarm_type(long type);
 int ssl_fill_hello_random(SSL *s, int server, uint8_t *field, size_t len);
 
@@ -731,11 +829,11 @@
 int ssl3_send_alert(SSL *s, int level, int desc);
 int ssl3_get_req_cert_type(SSL *s, uint8_t *p);
 long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type,
-                      long max, int hash_message, int *ok);
+                      long max, enum ssl_hash_message_t hash_message, int *ok);
 
-/* ssl3_hash_current_message incorporates the current handshake message into
- * the handshake hash. */
-void ssl3_hash_current_message(SSL *s);
+/* ssl3_hash_current_message incorporates the current handshake message into the
+ * handshake hash. It returns one on success and zero on allocation failure. */
+int ssl3_hash_current_message(SSL *s);
 
 /* ssl3_cert_verify_hash writes the CertificateVerify hash into the bytes
  * pointed to by |out| and writes the number of bytes to |*out_len|. |out| must
@@ -747,8 +845,8 @@
                           const EVP_MD **out_md, EVP_PKEY *pkey);
 
 int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen);
-int ssl3_num_ciphers(void);
-const SSL_CIPHER *ssl3_get_cipher(unsigned int u);
+size_t ssl3_num_ciphers(void);
+const SSL_CIPHER *ssl3_get_cipher(size_t i);
 int ssl3_renegotiate(SSL *ssl);
 int ssl3_renegotiate_check(SSL *ssl);
 int ssl3_dispatch_alert(SSL *s);
@@ -757,13 +855,12 @@
 int ssl3_write_bytes(SSL *s, int type, const void *buf, int len);
 int ssl3_final_finish_mac(SSL *s, const char *sender, int slen, uint8_t *p);
 int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p);
-void ssl3_finish_mac(SSL *s, const uint8_t *buf, int len);
+int ssl3_finish_mac(SSL *s, const uint8_t *buf, int len);
 void ssl3_free_digest_list(SSL *s);
-unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk);
+int ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk);
 const SSL_CIPHER *ssl3_choose_cipher(
-    SSL *ssl, STACK_OF(SSL_CIPHER) * clnt,
+    SSL *ssl, STACK_OF(SSL_CIPHER) *clnt,
     struct ssl_cipher_preference_list_st *srvr);
-int ssl3_setup_buffers(SSL *s);
 int ssl3_setup_read_buffer(SSL *s);
 int ssl3_setup_write_buffer(SSL *s);
 int ssl3_release_read_buffer(SSL *s);
@@ -785,18 +882,19 @@
 int ssl3_shutdown(SSL *s);
 long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg);
 long ssl3_ctx_ctrl(SSL_CTX *s, int cmd, long larg, void *parg);
-long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void));
-long ssl3_ctx_callback_ctrl(SSL_CTX *s, int cmd, void (*fp)(void));
 int ssl3_pending(const SSL *s);
 
-void ssl3_record_sequence_update(uint8_t *seq);
+/* ssl3_record_sequence_update increments the sequence number in |seq|. It
+ * returns one on success and zero on wraparound. */
+int ssl3_record_sequence_update(uint8_t *seq, size_t seq_len);
+
 int ssl3_do_change_cipher_spec(SSL *ssl);
 
-void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
+int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
 int ssl3_handshake_write(SSL *s);
 
 int dtls1_do_write(SSL *s, int type);
-int ssl3_read_n(SSL *s, int n, int max, int extend);
+int ssl3_read_n(SSL *s, int n, int extend);
 int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek);
 int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len);
 void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len,
@@ -810,17 +908,16 @@
 int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen);
 int dtls1_read_failed(SSL *s, int code);
 int dtls1_buffer_message(SSL *s, int ccs);
-int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off,
-                             int *found);
 int dtls1_get_queue_priority(unsigned short seq, int is_ccs);
 int dtls1_retransmit_buffered_messages(SSL *s);
 void dtls1_clear_record_buffer(SSL *s);
 void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr);
-void dtls1_get_ccs_header(uint8_t *data, struct ccs_header_st *ccs_hdr);
 void dtls1_reset_seq_numbers(SSL *s, int rw);
 int dtls1_check_timeout_num(SSL *s);
-int dtls1_handle_timeout(SSL *s);
-const SSL_CIPHER *dtls1_get_cipher(unsigned int u);
+int dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
+int dtls1_handshake_write(SSL *s);
+
+const SSL_CIPHER *dtls1_get_cipher(size_t i);
 void dtls1_start_timer(SSL *s);
 void dtls1_stop_timer(SSL *s);
 int dtls1_is_timer_expired(SSL *s);
@@ -866,11 +963,10 @@
 int dtls1_accept(SSL *s);
 int dtls1_connect(SSL *s);
 void dtls1_free(SSL *s);
-long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg);
 int dtls1_shutdown(SSL *s);
 
 long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max,
-                       int hash_message, int *ok);
+                       enum ssl_hash_message_t hash_message, int *ok);
 int dtls1_get_record(SSL *s);
 int dtls1_dispatch_alert(SSL *s);
 
@@ -895,9 +991,10 @@
 int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *p);
 int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster,
                                 size_t premaster_len);
-int tls1_export_keying_material(SSL *s, uint8_t *out, size_t olen,
-                                const char *label, size_t llen,
-                                const uint8_t *p, size_t plen, int use_context);
+int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len,
+                                const char *label, size_t label_len,
+                                const uint8_t *context, size_t context_len,
+                                int use_context);
 int tls1_alert_code(int code);
 int ssl3_alert_code(int code);
 int ssl_ok(SSL *s);
@@ -975,7 +1072,9 @@
                               size_t client_random_len, const uint8_t *master,
                               size_t master_len);
 
-int ssl3_can_cutthrough(const SSL *s);
+/* ssl3_can_false_start returns one if |s| is allowed to False Start and zero
+ * otherwise. */
+int ssl3_can_false_start(const SSL *s);
 
 /* ssl3_get_enc_method returns the SSL3_ENC_METHOD corresponding to
  * |version|. */
@@ -1015,7 +1114,7 @@
 int ssl_add_clienthello_renegotiate_ext(SSL *s, uint8_t *p, int *len,
                                         int maxlen);
 int ssl_parse_clienthello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert);
-long ssl_get_algorithm2(SSL *s);
+uint32_t ssl_get_algorithm2(SSL *s);
 int tls1_process_sigalgs(SSL *s, const CBS *sigalgs);
 
 /* tls1_choose_signing_digest returns a digest for use with |pkey| based on the
@@ -1032,4 +1131,4 @@
 int ssl_add_serverhello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen);
 int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert);
 
-#endif
+#endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/src/ssl/pqueue/pqueue.c b/src/ssl/pqueue/pqueue.c
index ecaa139..14bd9b6 100644
--- a/src/ssl/pqueue/pqueue.c
+++ b/src/ssl/pqueue/pqueue.c
@@ -56,6 +56,7 @@
 
 #include <openssl/pqueue.h>
 
+#include <assert.h>
 #include <string.h>
 
 #include <openssl/mem.h>
@@ -104,6 +105,8 @@
     return;
   }
 
+  /* The queue must be empty. */
+  assert(pq->items == NULL);
   OPENSSL_free(pq);
 }
 
diff --git a/src/ssl/pqueue/pqueue_test.c b/src/ssl/pqueue/pqueue_test.c
index c4b4b9d..cb688f7 100644
--- a/src/ssl/pqueue/pqueue_test.c
+++ b/src/ssl/pqueue/pqueue_test.c
@@ -19,6 +19,17 @@
 #include <openssl/ssl.h>
 
 
+static void clear_and_free_queue(pqueue q) {
+  for (;;) {
+    pitem *item = pqueue_pop(q);
+    if (item == NULL) {
+      break;
+    }
+    pitem_free(item);
+  }
+  pqueue_free(q);
+}
+
 static int trivial(void) {
   pqueue q = pqueue_new();
   if (q == NULL) {
@@ -37,7 +48,7 @@
     return 0;
   }
   pitem_free(item);
-  pqueue_free(q);
+  clear_and_free_queue(q);
   return 1;
 }
 
@@ -101,7 +112,7 @@
     }
     curr = next;
   }
-  pqueue_free(q);
+  clear_and_free_queue(q);
   return 1;
 }
 
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index a34d221..b78f6d3 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -116,6 +116,7 @@
 #include <string.h>
 
 #include <openssl/buf.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/md5.h>
@@ -124,7 +125,7 @@
 #include <openssl/sha.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 /* ssl3_do_write sends |s->init_buf| in records of type 'type'
@@ -173,8 +174,7 @@
       return 0;
     }
 
-    /* Copy the finished so we can use it for
-     * renegotiation checks */
+    /* Copy the finished so we can use it for renegotiation checks */
     if (s->server) {
       assert(n <= EVP_MAX_MD_SIZE);
       memcpy(s->s3->previous_server_finished, s->s3->tmp.finish_md, n);
@@ -185,7 +185,9 @@
       s->s3->previous_client_finished_len = n;
     }
 
-    ssl_set_handshake_header(s, SSL3_MT_FINISHED, n);
+    if (!ssl_set_handshake_header(s, SSL3_MT_FINISHED, n)) {
+      return 0;
+    }
     s->state = b;
   }
 
@@ -224,7 +226,7 @@
 
   message_len =
       s->method->ssl_get_message(s, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE,
-                                 SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok);
+                                 ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return message_len;
@@ -232,7 +234,9 @@
 
   /* Snapshot the finished hash before incorporating the new message. */
   ssl3_take_mac(s);
-  ssl3_hash_current_message(s);
+  if (!ssl3_hash_current_message(s)) {
+    goto err;
+  }
 
   /* If this occurs, we have missed a message.
    * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */
@@ -273,6 +277,7 @@
 
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
+err:
   return 0;
 }
 
@@ -296,11 +301,17 @@
   return ssl3_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC);
 }
 
-unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) {
+int ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) {
   uint8_t *p;
   unsigned long l = 3 + SSL_HM_HEADER_LENGTH(s);
 
-  if (!ssl_add_cert_chain(s, cpk, &l)) {
+  if (cpk == NULL) {
+    /* TLSv1 sends a chain with nothing in it, instead of an alert. */
+    if (!BUF_MEM_grow_clean(s->init_buf, l)) {
+      OPENSSL_PUT_ERROR(SSL, ssl3_output_cert_chain, ERR_R_BUF_LIB);
+      return 0;
+    }
+  } else if (!ssl_add_cert_chain(s, cpk, &l)) {
     return 0;
   }
 
@@ -308,25 +319,24 @@
   p = ssl_handshake_start(s);
   l2n3(l, p);
   l += 3;
-  ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, l);
-  return l + SSL_HM_HEADER_LENGTH(s);
+  return ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, l);
 }
 
 /* Obtain handshake message of message type |msg_type| (any if |msg_type| == -1),
  * maximum acceptable body length |max|. The first four bytes (msg_type and
  * length) are read in state |header_state|, the body is read in state |body_state|. */
 long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type,
-                      long max, int hash_message, int *ok) {
+                      long max, enum ssl_hash_message_t hash_message, int *ok) {
   uint8_t *p;
   unsigned long l;
   long n;
   int al;
 
   if (s->s3->tmp.reuse_message) {
-    /* A SSL_GET_MESSAGE_DONT_HASH_MESSAGE call cannot be combined with
-     * reuse_message; the SSL_GET_MESSAGE_DONT_HASH_MESSAGE would have to have
-     * been applied to the previous call. */
-    assert(hash_message != SSL_GET_MESSAGE_DONT_HASH_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);
     s->s3->tmp.reuse_message = 0;
     if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
       al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -350,7 +360,6 @@
         int bytes_read = s->method->ssl_read_bytes(
             s, SSL3_RT_HANDSHAKE, &p[s->init_num], 4 - s->init_num, 0);
         if (bytes_read <= 0) {
-          s->rwstate = SSL_READING;
           *ok = 0;
           return bytes_read;
         }
@@ -416,8 +425,8 @@
   }
 
   /* Feed this message into MAC computation. */
-  if (hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE) {
-    ssl3_hash_current_message(s);
+  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) {
+    goto err;
   }
   if (s->msg_callback) {
     s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
@@ -434,11 +443,12 @@
   return -1;
 }
 
-void ssl3_hash_current_message(SSL *s) {
+int ssl3_hash_current_message(SSL *s) {
   /* The handshake header (different size between DTLS and TLS) is included in
    * the hash. */
   size_t header_len = s->init_msg - (uint8_t *)s->init_buf->data;
-  ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num + header_len);
+  return ssl3_finish_mac(s, (uint8_t *)s->init_buf->data,
+                         s->init_num + header_len);
 }
 
 /* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that
@@ -586,8 +596,7 @@
 #endif
 
   if (s->s3->rbuf.buf == NULL) {
-    len = SSL3_RT_MAX_PLAIN_LENGTH + SSL3_RT_MAX_ENCRYPTED_OVERHEAD +
-          headerlen + align;
+    len = SSL3_RT_MAX_ENCRYPTED_LENGTH + headerlen + align;
     if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) {
       s->s3->init_extra = 1;
       len += SSL3_RT_MAX_EXTRA;
@@ -645,28 +654,16 @@
   return 0;
 }
 
-
-int ssl3_setup_buffers(SSL *s) {
-  if (!ssl3_setup_read_buffer(s) ||
-      !ssl3_setup_write_buffer(s)) {
-    return 0;
-  }
-  return 1;
-}
-
 int ssl3_release_write_buffer(SSL *s) {
-  if (s->s3->wbuf.buf != NULL) {
-    OPENSSL_free(s->s3->wbuf.buf);
-    s->s3->wbuf.buf = NULL;
-  }
+  OPENSSL_free(s->s3->wbuf.buf);
+  s->s3->wbuf.buf = NULL;
   return 1;
 }
 
 int ssl3_release_read_buffer(SSL *s) {
-  if (s->s3->rbuf.buf != NULL) {
-    OPENSSL_free(s->s3->rbuf.buf);
-    s->s3->rbuf.buf = NULL;
-  }
+  OPENSSL_free(s->s3->rbuf.buf);
+  s->s3->rbuf.buf = NULL;
+  s->packet = NULL;
   return 1;
 }
 
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 231cc65..d01acae 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -150,20 +150,21 @@
 
 #include <assert.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/buf.h>
 #include <openssl/bytestring.h>
 #include <openssl/rand.h>
 #include <openssl/obj.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/md5.h>
 #include <openssl/dh.h>
 #include <openssl/bn.h>
-#include <openssl/engine.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 #include "../crypto/dh/internal.h"
 
 
@@ -195,12 +196,21 @@
       case SSL_ST_RENEGOTIATE:
         s->renegotiate = 1;
         s->state = SSL_ST_CONNECT;
-        s->ctx->stats.sess_connect_renegotiate++;
         /* fallthrough */
       case SSL_ST_CONNECT:
       case SSL_ST_BEFORE | SSL_ST_CONNECT:
-        if (cb != NULL)
+        if (cb != NULL) {
           cb(s, SSL_CB_HANDSHAKE_START, 1);
+        }
+
+        if ((s->version >> 8) != 3) {
+          /* TODO(davidben): Some consumers clear |s->version| to break the
+           * handshake in a callback. Remove this when they're using proper
+           * APIs. */
+          OPENSSL_PUT_ERROR(SSL, ssl3_connect, ERR_R_INTERNAL_ERROR);
+          ret = -1;
+          goto end;
+        }
 
         if (s->init_buf == NULL) {
           buf = BUF_MEM_new();
@@ -214,8 +224,7 @@
           buf = NULL;
         }
 
-        if (!ssl3_setup_buffers(s) ||
-            !ssl_init_wbio_buffer(s, 0)) {
+        if (!ssl_init_wbio_buffer(s, 0)) {
           ret = -1;
           goto end;
         }
@@ -229,7 +238,6 @@
         }
 
         s->state = SSL3_ST_CW_CLNT_HELLO_A;
-        s->ctx->stats.sess_connect++;
         s->init_num = 0;
         break;
 
@@ -389,12 +397,8 @@
         s->init_num = 0;
 
         s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->enc_method->setup_key_block(s)) {
-          ret = -1;
-          goto end;
-        }
-
-        if (!s->enc_method->change_cipher_state(
+        if (!s->enc_method->setup_key_block(s) ||
+            !s->enc_method->change_cipher_state(
                 s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
@@ -445,15 +449,16 @@
            * hashes. */
           if (s->s3->tlsext_channel_id_new) {
             ret = tls1_record_handshake_hashes_for_channel_id(s);
-            if (ret <= 0)
+            if (ret <= 0) {
               goto end;
+            }
           }
-          if ((SSL_get_mode(s) & SSL_MODE_HANDSHAKE_CUTTHROUGH) &&
-              ssl3_can_cutthrough(s) &&
-              /* no cutthrough on renegotiation (would complicate the state
-               * machine) */
+          if ((SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START) &&
+              ssl3_can_false_start(s) &&
+              /* No False Start on renegotiation (would complicate the state
+               * machine). */
               s->s3->previous_server_finished_len == 0) {
-            s->s3->tmp.next_state = SSL3_ST_CUTTHROUGH_COMPLETE;
+            s->s3->tmp.next_state = SSL3_ST_FALSE_START;
           } else {
             /* Allow NewSessionTicket if ticket expected */
             if (s->tlsext_ticket_expected) {
@@ -522,13 +527,14 @@
         s->state = s->s3->tmp.next_state;
         break;
 
-      case SSL3_ST_CUTTHROUGH_COMPLETE:
+      case SSL3_ST_FALSE_START:
         /* Allow NewSessionTicket if ticket expected */
         if (s->tlsext_ticket_expected) {
           s->state = SSL3_ST_CR_SESSION_TICKET_A;
         } else {
           s->state = SSL3_ST_CR_CHANGE;
         }
+        s->s3->tmp.in_false_start = 1;
 
         ssl_free_wbio_buffer(s);
         ret = 1;
@@ -538,10 +544,8 @@
         /* clean a few things up */
         ssl3_cleanup_key_block(s);
 
-        if (s->init_buf != NULL) {
-          BUF_MEM_free(s->init_buf);
-          s->init_buf = NULL;
-        }
+        BUF_MEM_free(s->init_buf);
+        s->init_buf = NULL;
 
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(s);
@@ -549,15 +553,12 @@
         s->init_num = 0;
         s->renegotiate = 0;
         s->new_session = 0;
+        s->s3->tmp.in_false_start = 0;
 
         ssl_update_cache(s, SSL_SESS_CACHE_CLIENT);
-        if (s->hit) {
-          s->ctx->stats.sess_hit++;
-        }
 
         ret = 1;
         /* s->server=0; */
-        s->ctx->stats.sess_connect_good++;
 
         if (cb != NULL) {
           cb(s, SSL_CB_HANDSHAKE_DONE, 1);
@@ -584,9 +585,7 @@
 
 end:
   s->in_handshake--;
-  if (buf != NULL) {
-    BUF_MEM_free(buf);
-  }
+  BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(s, SSL_CB_CONNECT_EXIT, ret);
   }
@@ -625,8 +624,9 @@
 
     /* If resending the ClientHello in DTLS after a HelloVerifyRequest, don't
      * renegerate the client_random. The random must be reused. */
-    if (!SSL_IS_DTLS(s) || !s->d1->send_cookie) {
-      ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random));
+    if ((!SSL_IS_DTLS(s) || !s->d1->send_cookie) &&
+        !ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random))) {
+      goto err;
     }
 
     /* Do the message type and length last. Note: the final argument to
@@ -720,7 +720,9 @@
     }
 
     l = p - d;
-    ssl_set_handshake_header(s, SSL3_MT_CLIENT_HELLO, l);
+    if (!ssl_set_handshake_header(s, SSL3_MT_CLIENT_HELLO, l)) {
+      goto err;
+    }
     s->state = SSL3_ST_CW_CLNT_HELLO_B;
   }
 
@@ -732,7 +734,7 @@
 }
 
 int ssl3_get_server_hello(SSL *s) {
-  STACK_OF(SSL_CIPHER) * sk;
+  STACK_OF(SSL_CIPHER) *sk;
   const SSL_CIPHER *c;
   CERT *ct = s->cert;
   int al = SSL_AD_INTERNAL_ERROR, ok;
@@ -740,12 +742,12 @@
   CBS server_hello, server_random, session_id;
   uint16_t server_version, cipher_suite;
   uint8_t compression_method;
-  unsigned long mask_ssl;
+  uint32_t mask_ssl;
 
   n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_HELLO_A,
                                  SSL3_ST_CR_SRVR_HELLO_B, SSL3_MT_SERVER_HELLO,
                                  20000, /* ?? */
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     uint32_t err = ERR_peek_error();
@@ -858,24 +860,22 @@
     goto f_err;
   }
 
-  if (s->hit && s->session->cipher != c) {
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello,
-                      SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
-    goto f_err;
+  if (s->hit) {
+    if (s->session->cipher != c) {
+      al = SSL_AD_ILLEGAL_PARAMETER;
+      OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello,
+                        SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
+      goto f_err;
+    }
+    if (s->session->ssl_version != s->version) {
+      al = SSL_AD_ILLEGAL_PARAMETER;
+      OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello,
+                        SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
+      goto f_err;
+    }
   }
   s->s3->tmp.new_cipher = c;
 
-  /* Most clients also require that the negotiated version match the session's
-   * version if resuming. However OpenSSL has historically not had the
-   * corresponding logic on the server, so this may not be compatible,
-   * depending on other factors. (Whether the ClientHello version is clamped to
-   * the session's version and whether the session cache is keyed on IP
-   * address.)
-   *
-   * TODO(davidben): See if we can still enforce this? Perhaps for the future
-   * TLS 1.3 and forward if this is fixed upstream. */
-
   /* Don't digest cached records if no sigalgs: we may need them for client
    * authentication. */
   if (!SSL_USE_SIGALGS(s) &&
@@ -924,8 +924,8 @@
   const uint8_t *data;
 
   n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B,
-                                 SSL3_MT_CERTIFICATE, s->max_cert_list,
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 SSL3_MT_CERTIFICATE, (long)s->max_cert_list,
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -988,9 +988,7 @@
     goto err;
   }
 
-  if (s->session->sess_cert) {
-    ssl_sess_cert_free(s->session->sess_cert);
-  }
+  ssl_sess_cert_free(s->session->sess_cert);
   s->session->sess_cert = sc;
 
   sc->cert_chain = sk;
@@ -1028,17 +1026,11 @@
     goto f_err;
   }
   sc->peer_cert_type = i;
-  /* Why would the following ever happen? We just created sc a couple of lines
-   * ago. */
-  if (sc->peer_pkeys[i].x509 != NULL) {
-    X509_free(sc->peer_pkeys[i].x509);
-  }
+  X509_free(sc->peer_pkeys[i].x509);
   sc->peer_pkeys[i].x509 = X509_up_ref(x);
   sc->peer_key = &(sc->peer_pkeys[i]);
 
-  if (s->session->peer != NULL) {
-    X509_free(s->session->peer);
-  }
+  X509_free(s->session->peer);
   s->session->peer = X509_up_ref(x);
 
   s->session->verify_result = s->verify_result;
@@ -1075,7 +1067,7 @@
    * ServerKeyExchange message may be skipped */
   n = s->method->ssl_get_message(s, SSL3_ST_CR_KEY_EXCH_A,
                                  SSL3_ST_CR_KEY_EXCH_B, -1, s->max_cert_list,
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
   if (!ok) {
     return n;
   }
@@ -1096,14 +1088,15 @@
        * initialized |sess_cert|. */
       if (s->session->sess_cert == NULL) {
         s->session->sess_cert = ssl_sess_cert_new();
+        if (s->session->sess_cert == NULL) {
+          return -1;
+        }
       }
 
       /* TODO(davidben): This should be reset in one place with the rest of the
        * handshake state. */
-      if (s->s3->tmp.peer_psk_identity_hint) {
-        OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
-        s->s3->tmp.peer_psk_identity_hint = NULL;
-      }
+      OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
+      s->s3->tmp.peer_psk_identity_hint = NULL;
     }
     s->s3->tmp.reuse_message = 1;
     return 1;
@@ -1114,16 +1107,15 @@
   server_key_exchange_orig = server_key_exchange;
 
   if (s->session->sess_cert != NULL) {
-    if (s->session->sess_cert->peer_dh_tmp) {
-      DH_free(s->session->sess_cert->peer_dh_tmp);
-      s->session->sess_cert->peer_dh_tmp = NULL;
-    }
-    if (s->session->sess_cert->peer_ecdh_tmp) {
-      EC_KEY_free(s->session->sess_cert->peer_ecdh_tmp);
-      s->session->sess_cert->peer_ecdh_tmp = NULL;
-    }
+    DH_free(s->session->sess_cert->peer_dh_tmp);
+    s->session->sess_cert->peer_dh_tmp = NULL;
+    EC_KEY_free(s->session->sess_cert->peer_ecdh_tmp);
+    s->session->sess_cert->peer_ecdh_tmp = NULL;
   } else {
     s->session->sess_cert = ssl_sess_cert_new();
+    if (s->session->sess_cert == NULL) {
+      return -1;
+    }
   }
 
   alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
@@ -1165,7 +1157,7 @@
     }
   }
 
-  if (alg_k & SSL_kEDH) {
+  if (alg_k & SSL_kDHE) {
     CBS dh_p, dh_g, dh_Ys;
 
     if (!CBS_get_u16_length_prefixed(&server_key_exchange, &dh_p) ||
@@ -1207,10 +1199,9 @@
 
     s->session->sess_cert->peer_dh_tmp = dh;
     dh = NULL;
-  } else if (alg_k & SSL_kEECDH) {
+  } else if (alg_k & SSL_kECDHE) {
     uint16_t curve_id;
     int curve_nid = 0;
-    EC_GROUP *ngroup;
     const EC_GROUP *group;
     CBS point;
 
@@ -1231,21 +1222,13 @@
       goto f_err;
     }
 
-    ecdh = EC_KEY_new();
+    ecdh = EC_KEY_new_by_curve_name(curve_nid);
     if (ecdh == NULL) {
       OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange,
-                        ERR_R_MALLOC_FAILURE);
+                        ERR_R_EC_LIB);
       goto err;
     }
 
-    ngroup = EC_GROUP_new_by_curve_name(curve_nid);
-    if (ngroup == NULL ||
-        EC_KEY_set_group(ecdh, ngroup) == 0) {
-      OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_EC_LIB);
-      goto err;
-    }
-    EC_GROUP_free(ngroup);
-
     group = EC_KEY_get0_group(ecdh);
 
     /* Next, get the encoded ECPoint */
@@ -1362,17 +1345,11 @@
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
 err:
   EVP_PKEY_free(pkey);
-  if (rsa != NULL) {
-    RSA_free(rsa);
-  }
-  if (dh != NULL) {
-    DH_free(dh);
-  }
+  RSA_free(rsa);
+  DH_free(dh);
   BN_CTX_free(bn_ctx);
   EC_POINT_free(srvr_ecpoint);
-  if (ecdh != NULL) {
-    EC_KEY_free(ecdh);
-  }
+  EC_KEY_free(ecdh);
   EVP_MD_CTX_cleanup(&md_ctx);
   return -1;
 }
@@ -1393,7 +1370,7 @@
 
   n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_REQ_A,
                                  SSL3_ST_CR_CERT_REQ_B, -1, s->max_cert_list,
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1419,15 +1396,6 @@
     goto err;
   }
 
-  /* TLS does not like anon-DH with client cert */
-  if (s->version > SSL3_VERSION &&
-      (s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request,
-                      SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER);
-    goto err;
-  }
-
   CBS_init(&cbs, s->init_msg, n);
 
   ca_sk = sk_X509_NAME_new(ca_dn_cmp);
@@ -1493,7 +1461,7 @@
 
     if (!CBS_skip(&distinguished_name, data - CBS_data(&distinguished_name))) {
       ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_INTERNAL_ERROR);
+      OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_INTERNAL_ERROR);
       goto err;
     }
 
@@ -1513,29 +1481,25 @@
 
   /* we should setup a certificate to return.... */
   s->s3->tmp.cert_req = 1;
-  if (s->s3->tmp.ca_names != NULL) {
-    sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
-  }
+  sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
   s->s3->tmp.ca_names = ca_sk;
   ca_sk = NULL;
 
   ret = 1;
 
 err:
-  if (ca_sk != NULL) {
-    sk_X509_NAME_pop_free(ca_sk, X509_NAME_free);
-  }
+  sk_X509_NAME_pop_free(ca_sk, X509_NAME_free);
   return ret;
 }
 
 int ssl3_get_new_session_ticket(SSL *s) {
-  int ok, al, ret = 0;
+  int ok, al;
   long n;
   CBS new_session_ticket, ticket;
 
   n = s->method->ssl_get_message(
       s, SSL3_ST_CR_SESSION_TICKET_A, SSL3_ST_CR_SESSION_TICKET_B,
-      SSL3_MT_NEWSESSION_TICKET, 16384, SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+      SSL3_MT_NEWSESSION_TICKET, 16384, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1558,21 +1522,15 @@
     goto err;
   }
 
-  /* There are two ways to detect a resumed ticket sesion. One is to set an
-   * appropriate session ID and then the server must return a match in
-   * ServerHello. This allows the normal client session ID matching to work and
-   * we know much earlier that the ticket has been accepted.
-   *
-   * The other way is to set zero length session ID when the ticket is
-   * presented and rely on the handshake to determine session resumption.
-   *
-   * We choose the former approach because this fits in with assumptions
-   * elsewhere in OpenSSL. The session ID is set to the SHA256 (or SHA1 is
-   * SHA256 is disabled) hash of the ticket. */
-  EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), s->session->session_id,
-             &s->session->session_id_length, EVP_sha256(), NULL);
-  ret = 1;
-  return ret;
+  /* Generate a session ID for this session based on the session ticket. We use
+   * the session ID mechanism for detecting ticket resumption. This also fits in
+   * with assumptions elsewhere in OpenSSL.*/
+  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), s->session->session_id,
+                  &s->session->session_id_length, EVP_sha256(), NULL)) {
+    goto err;
+  }
+
+  return 1;
 
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -1588,12 +1546,19 @@
 
   n = s->method->ssl_get_message(
       s, SSL3_ST_CR_CERT_STATUS_A, SSL3_ST_CR_CERT_STATUS_B,
-      SSL3_MT_CERTIFICATE_STATUS, 16384, SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+      -1, 16384, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
   }
 
+  if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
+    /* A server may send status_request in ServerHello and then change
+     * its mind about sending CertificateStatus. */
+    s->s3->tmp.reuse_message = 1;
+    return 1;
+  }
+
   CBS_init(&certificate_status, s->init_msg, n);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
@@ -1625,7 +1590,7 @@
   n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_DONE_A,
                                  SSL3_ST_CR_SRVR_DONE_B, SSL3_MT_SERVER_DONE,
                                  30, /* should be very small, like 0 :-) */
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1645,8 +1610,8 @@
 int ssl3_send_client_key_exchange(SSL *s) {
   uint8_t *p;
   int n = 0;
-  unsigned long alg_k;
-  unsigned long alg_a;
+  uint32_t alg_k;
+  uint32_t alg_a;
   uint8_t *q;
   EVP_PKEY *pkey = NULL;
   EC_KEY *clnt_ecdh = NULL;
@@ -1699,10 +1664,7 @@
         goto err;
       }
 
-      if (s->session->psk_identity != NULL) {
-        OPENSSL_free(s->session->psk_identity);
-      }
-
+      OPENSSL_free(s->session->psk_identity);
       s->session->psk_identity = BUF_strdup(identity);
       if (s->session->psk_identity == NULL) {
         OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange,
@@ -1744,9 +1706,7 @@
           pkey->pkey.rsa == NULL) {
         OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange,
                           ERR_R_INTERNAL_ERROR);
-        if (pkey != NULL) {
-          EVP_PKEY_free(pkey);
-        }
+        EVP_PKEY_free(pkey);
         goto err;
       }
 
@@ -1785,7 +1745,7 @@
       if (s->version > SSL3_VERSION) {
         s2n(enc_pms_len, q);
       }
-    } else if (alg_k & SSL_kEDH) {
+    } else if (alg_k & SSL_kDHE) {
       DH *dh_srvr, *dh_clnt;
       SESS_CERT *scert = s->session->sess_cert;
       int dh_len;
@@ -1841,7 +1801,7 @@
       n += 2 + pub_len;
 
       DH_free(dh_clnt);
-    } else if (alg_k & SSL_kEECDH) {
+    } else if (alg_k & SSL_kECDHE) {
       const EC_GROUP *srvr_group = NULL;
       EC_KEY *tkey;
       int field_size = 0, ecdh_len;
@@ -1993,7 +1953,9 @@
 
     /* The message must be added to the finished hash before calculating the
      * master secret. */
-    ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n);
+    if (!ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n)) {
+      goto err;
+    }
     s->state = SSL3_ST_CW_KEY_EXCH_B;
 
     s->session->master_key_length = s->enc_method->generate_master_secret(
@@ -2007,16 +1969,12 @@
   }
 
   /* SSL3_ST_CW_KEY_EXCH_B */
-  return s->enc_method->do_write(s);
+  return s->method->do_write(s);
 
 err:
   BN_CTX_free(bn_ctx);
-  if (encodedPoint != NULL) {
-    OPENSSL_free(encodedPoint);
-  }
-  if (clnt_ecdh != NULL) {
-    EC_KEY_free(clnt_ecdh);
-  }
+  OPENSSL_free(encodedPoint);
+  EC_KEY_free(clnt_ecdh);
   EVP_PKEY_free(srvr_pub_pkey);
   if (pms) {
     OPENSSL_cleanse(pms, pms_len);
@@ -2089,7 +2047,9 @@
     s2n(signature_length, p);
     n += signature_length + 2;
 
-    ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n);
+    if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n)) {
+      goto err;
+    }
     s->state = SSL3_ST_CW_CERT_VRFY_B;
   }
 
@@ -2156,12 +2116,8 @@
                         SSL_R_BAD_DATA_RETURNED_BY_CALLBACK);
     }
 
-    if (x509 != NULL) {
-      X509_free(x509);
-    }
-    if (pkey != NULL) {
-      EVP_PKEY_free(pkey);
-    }
+    X509_free(x509);
+    EVP_PKEY_free(pkey);
     if (i && !ssl3_has_client_certificate(s)) {
       i = 0;
     }
@@ -2180,8 +2136,10 @@
   }
 
   if (s->state == SSL3_ST_CW_CERT_C) {
-    s->state = SSL3_ST_CW_CERT_D;
-    ssl3_output_cert_chain(s, (s->s3->tmp.cert_req == 2) ? NULL : s->cert->key);
+    CERT_PKEY *cert_pkey = (s->s3->tmp.cert_req == 2) ? NULL : s->cert->key;
+    if (!ssl3_output_cert_chain(s, cert_pkey)) {
+      return -1;
+    }
   }
 
   /* SSL3_ST_CW_CERT_D */
@@ -2246,7 +2204,7 @@
     goto f_err;
   }
 
-  if ((alg_k & SSL_kEDH) &&
+  if ((alg_k & SSL_kDHE) &&
       !(has_bits(i, EVP_PK_DH | EVP_PKT_EXCH) || dh != NULL)) {
     OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_DH_KEY);
     goto f_err;
@@ -2276,7 +2234,9 @@
     memset(p, 0, padding_len);
     p += padding_len;
 
-    ssl_set_handshake_header(s, SSL3_MT_NEXT_PROTO, p - d);
+    if (!ssl_set_handshake_header(s, SSL3_MT_NEXT_PROTO, p - d)) {
+      return -1;
+    }
     s->state = SSL3_ST_CW_NEXT_PROTO_B;
   }
 
@@ -2387,23 +2347,19 @@
     goto err;
   }
 
-  ssl_set_handshake_header(s, SSL3_MT_ENCRYPTED_EXTENSIONS,
-                           2 + 2 + TLSEXT_CHANNEL_ID_SIZE);
+  if (!ssl_set_handshake_header(s, SSL3_MT_ENCRYPTED_EXTENSIONS,
+                                2 + 2 + TLSEXT_CHANNEL_ID_SIZE)) {
+    goto err;
+  }
   s->state = SSL3_ST_CW_CHANNEL_ID_B;
 
   ret = ssl_do_write(s);
 
 err:
   EVP_MD_CTX_cleanup(&md_ctx);
-  if (public_key) {
-    OPENSSL_free(public_key);
-  }
-  if (der_sig) {
-    OPENSSL_free(der_sig);
-  }
-  if (sig) {
-    ECDSA_SIG_free(sig);
-  }
+  OPENSSL_free(public_key);
+  OPENSSL_free(der_sig);
+  ECDSA_SIG_free(sig);
 
   return ret;
 }
diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c
index 562cb84..fbe68da 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/s3_enc.c
@@ -133,8 +133,9 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <openssl/err.h>
 #include <openssl/evp.h>
@@ -142,7 +143,7 @@
 #include <openssl/md5.h>
 #include <openssl/obj.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 static const uint8_t ssl3_pad_1[48] = {
@@ -235,12 +236,8 @@
 }
 
 int ssl3_init_finished_mac(SSL *s) {
-  if (s->s3->handshake_buffer) {
-    BIO_free(s->s3->handshake_buffer);
-  }
-  if (s->s3->handshake_dgst) {
-    ssl3_free_digest_list(s);
-  }
+  BIO_free(s->s3->handshake_buffer);
+  ssl3_free_digest_list(s);
   s->s3->handshake_buffer = BIO_new(BIO_s_mem());
   if (s->s3->handshake_buffer == NULL) {
     return 0;
@@ -264,12 +261,11 @@
   s->s3->handshake_dgst = NULL;
 }
 
-void ssl3_finish_mac(SSL *s, const uint8_t *buf, int len) {
+int ssl3_finish_mac(SSL *s, const uint8_t *buf, int len) {
   int i;
 
   if (s->s3->handshake_buffer) {
-    BIO_write(s->s3->handshake_buffer, (void *)buf, len);
-    return;
+    return BIO_write(s->s3->handshake_buffer, (void *)buf, len) >= 0;
   }
 
   for (i = 0; i < SSL_MAX_DIGEST; i++) {
@@ -277,12 +273,13 @@
       EVP_DigestUpdate(s->s3->handshake_dgst[i], buf, len);
     }
   }
+  return 1;
 }
 
 int ssl3_digest_cached_records(
     SSL *s, enum should_free_handshake_buffer_t should_free_handshake_buffer) {
   int i;
-  long mask;
+  uint32_t mask;
   const EVP_MD *md;
   const uint8_t *hdata;
   size_t hdatalen;
@@ -303,7 +300,7 @@
   }
 
   /* Loop through bits of algorithm2 field and create MD_CTX-es */
-  for (i = 0; ssl_get_handshake_digest(i, &mask, &md); i++) {
+  for (i = 0; ssl_get_handshake_digest(&mask, &md, i); i++) {
     if ((mask & ssl_get_algorithm2(s)) && md) {
       s->s3->handshake_dgst[i] = EVP_MD_CTX_create();
       if (s->s3->handshake_dgst[i] == NULL) {
@@ -383,7 +380,7 @@
   EVP_MD_CTX_init(&ctx);
   if (!EVP_MD_CTX_copy_ex(&ctx, d)) {
     EVP_MD_CTX_cleanup(&ctx);
-    OPENSSL_PUT_ERROR(SSL, ssl3_generate_key_block, ERR_LIB_EVP);
+    OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, ERR_LIB_EVP);
     return 0;
   }
 
@@ -402,7 +399,7 @@
 
   if (!EVP_DigestInit_ex(&ctx, EVP_MD_CTX_md(&ctx), NULL)) {
     EVP_MD_CTX_cleanup(&ctx);
-    OPENSSL_PUT_ERROR(SSL, ssl3_generate_key_block, ERR_LIB_EVP);
+    OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, ERR_LIB_EVP);
     return 0;
   }
   EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length);
@@ -415,15 +412,16 @@
   return ret;
 }
 
-void ssl3_record_sequence_update(uint8_t *seq) {
-  int i;
-
-  for (i = 7; i >= 0; i--) {
+int ssl3_record_sequence_update(uint8_t *seq, size_t seq_len) {
+  size_t i;
+  for (i = seq_len - 1; i < seq_len; i--) {
     ++seq[i];
     if (seq[i] != 0) {
-      break;
+      return 1;
     }
   }
+  OPENSSL_PUT_ERROR(SSL, ssl3_record_sequence_update, ERR_R_OVERFLOW);
+  return 0;
 }
 
 int ssl3_alert_code(int code) {
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index e0ccedc..674277f 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -148,14 +148,16 @@
 
 #include <assert.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/buf.h>
 #include <openssl/dh.h>
+#include <openssl/err.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 #define SSL3_NUM_CIPHERS (sizeof(ssl3_ciphers) / sizeof(SSL_CIPHER))
@@ -165,77 +167,53 @@
     /* The RSA ciphers */
     /* Cipher 04 */
     {
-     1, SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA,
+     SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA,
      SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 05 */
     {
-     1, SSL3_TXT_RSA_RC4_128_SHA, SSL3_CK_RSA_RC4_128_SHA, SSL_kRSA, SSL_aRSA,
+     SSL3_TXT_RSA_RC4_128_SHA, SSL3_CK_RSA_RC4_128_SHA, SSL_kRSA, SSL_aRSA,
      SSL_RC4, SSL_SHA1, SSL_SSLV3, SSL_MEDIUM,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 0A */
     {
-     1, SSL3_TXT_RSA_DES_192_CBC3_SHA, SSL3_CK_RSA_DES_192_CBC3_SHA, SSL_kRSA,
+     SSL3_TXT_RSA_DES_192_CBC3_SHA, SSL3_CK_RSA_DES_192_CBC3_SHA, SSL_kRSA,
      SSL_aRSA, SSL_3DES, SSL_SHA1, SSL_SSLV3, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 112, 168,
     },
 
 
-    /* The Ephemeral DH ciphers */
-
-    /* Cipher 18 */
-    {
-     1, SSL3_TXT_ADH_RC4_128_MD5, SSL3_CK_ADH_RC4_128_MD5, SSL_kEDH, SSL_aNULL,
-     SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
-    },
-
-
     /* New AES ciphersuites */
 
     /* Cipher 2F */
     {
-     1, TLS1_TXT_RSA_WITH_AES_128_SHA, TLS1_CK_RSA_WITH_AES_128_SHA, SSL_kRSA,
+     TLS1_TXT_RSA_WITH_AES_128_SHA, TLS1_CK_RSA_WITH_AES_128_SHA, SSL_kRSA,
      SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 33 */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA, TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
-     SSL_kEDH, SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
-    },
-
-    /* Cipher 34 */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_128_SHA, TLS1_CK_ADH_WITH_AES_128_SHA, SSL_kEDH,
-     SSL_aNULL, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
+     TLS1_TXT_DHE_RSA_WITH_AES_128_SHA, TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
+     SSL_kDHE, SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 35 */
     {
-     1, TLS1_TXT_RSA_WITH_AES_256_SHA, TLS1_CK_RSA_WITH_AES_256_SHA, SSL_kRSA,
+     TLS1_TXT_RSA_WITH_AES_256_SHA, TLS1_CK_RSA_WITH_AES_256_SHA, SSL_kRSA,
      SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
     },
 
     /* Cipher 39 */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA, TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
-     SSL_kEDH, SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
-    },
-
-    /* Cipher 3A */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_256_SHA, TLS1_CK_ADH_WITH_AES_256_SHA, SSL_kEDH,
-     SSL_aNULL, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
+     TLS1_TXT_DHE_RSA_WITH_AES_256_SHA, TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
+     SSL_kDHE, SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
     },
 
@@ -244,65 +222,51 @@
 
     /* Cipher 3C */
     {
-     1, TLS1_TXT_RSA_WITH_AES_128_SHA256, TLS1_CK_RSA_WITH_AES_128_SHA256,
+     TLS1_TXT_RSA_WITH_AES_128_SHA256, TLS1_CK_RSA_WITH_AES_128_SHA256,
      SSL_kRSA, SSL_aRSA, SSL_AES128, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher 3D */
     {
-     1, TLS1_TXT_RSA_WITH_AES_256_SHA256, TLS1_CK_RSA_WITH_AES_256_SHA256,
+     TLS1_TXT_RSA_WITH_AES_256_SHA256, TLS1_CK_RSA_WITH_AES_256_SHA256,
      SSL_kRSA, SSL_aRSA, SSL_AES256, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 256, 256,
     },
 
     /* Cipher 67 */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128,
+     TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256,
+     TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES128,
      SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher 6B */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES256,
+     TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256,
+     TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES256,
      SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
-    },
-
-    /* Cipher 6C */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_128_SHA256, TLS1_CK_ADH_WITH_AES_128_SHA256,
-     SSL_kEDH, SSL_aNULL, SSL_AES128, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
-    },
-
-    /* Cipher 6D */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_256_SHA256, TLS1_CK_ADH_WITH_AES_256_SHA256,
-     SSL_kEDH, SSL_aNULL, SSL_AES256, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 256, 256,
     },
 
     /* Cipher 8A */
     {
-     1, TLS1_TXT_PSK_WITH_RC4_128_SHA, TLS1_CK_PSK_WITH_RC4_128_SHA, SSL_kPSK,
+     TLS1_TXT_PSK_WITH_RC4_128_SHA, TLS1_CK_PSK_WITH_RC4_128_SHA, SSL_kPSK,
      SSL_aPSK, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 8C */
     {
-     1, TLS1_TXT_PSK_WITH_AES_128_CBC_SHA, TLS1_CK_PSK_WITH_AES_128_CBC_SHA,
+     TLS1_TXT_PSK_WITH_AES_128_CBC_SHA, TLS1_CK_PSK_WITH_AES_128_CBC_SHA,
      SSL_kPSK, SSL_aPSK, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher 8D */
     {
-     1, TLS1_TXT_PSK_WITH_AES_256_CBC_SHA, TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
+     TLS1_TXT_PSK_WITH_AES_256_CBC_SHA, TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
      SSL_kPSK, SSL_aPSK, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
     },
@@ -312,7 +276,7 @@
 
     /* Cipher 9C */
     {
-     1, TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256,
+     TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256,
      TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, SSL_kRSA, SSL_aRSA, SSL_AES128GCM,
      SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
@@ -322,7 +286,7 @@
 
     /* Cipher 9D */
     {
-     1, TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384,
+     TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384,
      TLS1_CK_RSA_WITH_AES_256_GCM_SHA384, SSL_kRSA, SSL_aRSA, SSL_AES256GCM,
      SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
@@ -332,8 +296,8 @@
 
     /* Cipher 9E */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128GCM,
+     TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256,
+     TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES128GCM,
      SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -342,28 +306,8 @@
 
     /* Cipher 9F */
     {
-     1, TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aRSA, SSL_AES256GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
-         SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
-     256, 256,
-    },
-
-    /* Cipher A6 */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ADH_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aNULL, SSL_AES128GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
-         SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
-     128, 128,
-    },
-
-    /* Cipher A7 */
-    {
-     1, TLS1_TXT_ADH_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_ADH_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aNULL, SSL_AES256GCM,
+     TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kDHE, SSL_aRSA, SSL_AES256GCM,
      SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -372,70 +316,47 @@
 
     /* Cipher C007 */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_kEECDH, SSL_aECDSA, SSL_RC4,
+     TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
+     TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_kECDHE, SSL_aECDSA, SSL_RC4,
      SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128,
      128,
     },
 
     /* Cipher C009 */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_kECDHE, SSL_aECDSA,
      SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher C00A */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_kECDHE, SSL_aECDSA,
      SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
     },
 
     /* Cipher C011 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA, TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA,
-     SSL_kEECDH, SSL_aRSA, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
+     TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA, TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA,
+     SSL_kECDHE, SSL_aRSA, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher C013 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aRSA, SSL_AES128,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_kECDHE, SSL_aRSA, SSL_AES128,
      SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
     },
 
     /* Cipher C014 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aRSA, SSL_AES256,
-     SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
-    },
-
-    /* Cipher C016 */
-    {
-     1, TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA, TLS1_CK_ECDH_anon_WITH_RC4_128_SHA,
-     SSL_kEECDH, SSL_aNULL, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
-    },
-
-    /* Cipher C018 */
-    {
-     1, TLS1_TXT_ECDH_anon_WITH_AES_128_CBC_SHA,
-     TLS1_CK_ECDH_anon_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aNULL, SSL_AES128,
-     SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
-    },
-
-    /* Cipher C019 */
-    {
-     1, TLS1_TXT_ECDH_anon_WITH_AES_256_CBC_SHA,
-     TLS1_CK_ECDH_anon_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aNULL, SSL_AES256,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_kECDHE, SSL_aRSA, SSL_AES256,
      SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
     },
@@ -445,32 +366,32 @@
 
     /* Cipher C023 */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, SSL_kECDHE, SSL_aECDSA,
      SSL_AES128, SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher C024 */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, SSL_kECDHE, SSL_aECDSA,
      SSL_AES256, SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384, 256, 256,
     },
 
     /* Cipher C027 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, SSL_kEECDH, SSL_aRSA, SSL_AES128,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, SSL_kECDHE, SSL_aRSA, SSL_AES128,
      SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher C028 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, SSL_kEECDH, SSL_aRSA, SSL_AES256,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, SSL_kECDHE, SSL_aRSA, SSL_AES256,
      SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384, 256, 256,
     },
@@ -480,8 +401,8 @@
 
     /* Cipher C02B */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aECDSA,
      SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -490,8 +411,8 @@
 
     /* Cipher C02C */
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aECDSA,
      SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -500,8 +421,8 @@
 
     /* Cipher C02F */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aRSA,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aRSA,
      SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -510,8 +431,8 @@
 
     /* Cipher C030 */
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aRSA,
+     TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aRSA,
      SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
      SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -523,8 +444,8 @@
 
     /* Cipher CAFE */
     {
-     1, TLS1_TXT_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aPSK,
+     TLS1_TXT_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+     TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aPSK,
      SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD |
          SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD,
@@ -532,24 +453,24 @@
     },
 
     {
-     1, TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305,
-     TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aRSA,
+     TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+     TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, SSL_kECDHE, SSL_aRSA,
      SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
      256, 0,
     },
 
     {
-     1, TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
-     TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aECDSA,
+     TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+     TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, SSL_kECDHE, SSL_aECDSA,
      SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
      256, 0,
     },
 
     {
-     1, TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305,
-     TLS1_CK_DHE_RSA_CHACHA20_POLY1305, SSL_kEDH, SSL_aRSA,
+     TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305,
+     TLS1_CK_DHE_RSA_CHACHA20_POLY1305, SSL_kDHE, SSL_aRSA,
      SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
      SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD,
      256, 0,
@@ -563,27 +484,22 @@
     tls1_generate_master_secret,
     tls1_change_cipher_state,
     ssl3_final_finish_mac,
-    MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH,
     ssl3_cert_verify_mac,
     SSL3_MD_CLIENT_FINISHED_CONST, 4,
     SSL3_MD_SERVER_FINISHED_CONST, 4,
     ssl3_alert_code,
-    (int (*)(SSL *, uint8_t *, size_t, const char *, size_t, const uint8_t *,
-             size_t, int use_context)) ssl_undefined_function,
+    tls1_export_keying_material,
     0,
-    SSL3_HM_HEADER_LENGTH,
-    ssl3_set_handshake_header,
-    ssl3_handshake_write,
 };
 
-int ssl3_num_ciphers(void) { return SSL3_NUM_CIPHERS; }
+size_t ssl3_num_ciphers(void) { return SSL3_NUM_CIPHERS; }
 
-const SSL_CIPHER *ssl3_get_cipher(unsigned int u) {
-  if (u >= SSL3_NUM_CIPHERS) {
+const SSL_CIPHER *ssl3_get_cipher(size_t i) {
+  if (i >= SSL3_NUM_CIPHERS) {
     return NULL;
   }
 
-  return &ssl3_ciphers[SSL3_NUM_CIPHERS - 1 - u];
+  return &ssl3_ciphers[SSL3_NUM_CIPHERS - 1 - i];
 }
 
 int ssl3_pending(const SSL *s) {
@@ -595,7 +511,7 @@
                                                         : 0;
 }
 
-void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) {
+int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) {
   uint8_t *p = (uint8_t *)s->init_buf->data;
   *(p++) = htype;
   l2n3(len, p);
@@ -603,7 +519,7 @@
   s->init_off = 0;
 
   /* Add the message to the handshake hash. */
-  ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num);
+  return ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num);
 }
 
 int ssl3_handshake_write(SSL *s) { return ssl3_do_write(s, SSL3_RT_HANDSHAKE); }
@@ -637,47 +553,21 @@
     return;
   }
 
-  if (s->s3->sniff_buffer != NULL) {
-    BUF_MEM_free(s->s3->sniff_buffer);
-  }
+  BUF_MEM_free(s->s3->sniff_buffer);
   ssl3_cleanup_key_block(s);
-  if (s->s3->rbuf.buf != NULL) {
-    ssl3_release_read_buffer(s);
-  }
-  if (s->s3->wbuf.buf != NULL) {
-    ssl3_release_write_buffer(s);
-  }
-  if (s->s3->tmp.dh != NULL) {
-    DH_free(s->s3->tmp.dh);
-  }
-  if (s->s3->tmp.ecdh != NULL) {
-    EC_KEY_free(s->s3->tmp.ecdh);
-  }
+  ssl3_release_read_buffer(s);
+  ssl3_release_write_buffer(s);
+  DH_free(s->s3->tmp.dh);
+  EC_KEY_free(s->s3->tmp.ecdh);
 
-  if (s->s3->tmp.ca_names != NULL) {
-    sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
-  }
-  if (s->s3->tmp.certificate_types != NULL) {
-    OPENSSL_free(s->s3->tmp.certificate_types);
-  }
-  if (s->s3->tmp.peer_ecpointformatlist) {
-    OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
-  }
-  if (s->s3->tmp.peer_ellipticcurvelist) {
-    OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-  }
-  if (s->s3->tmp.peer_psk_identity_hint) {
-    OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
-  }
-  if (s->s3->handshake_buffer) {
-    BIO_free(s->s3->handshake_buffer);
-  }
-  if (s->s3->handshake_dgst) {
-    ssl3_free_digest_list(s);
-  }
-  if (s->s3->alpn_selected) {
-    OPENSSL_free(s->s3->alpn_selected);
-  }
+  sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
+  OPENSSL_free(s->s3->tmp.certificate_types);
+  OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
+  OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
+  OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
+  BIO_free(s->s3->handshake_buffer);
+  ssl3_free_digest_list(s);
+  OPENSSL_free(s->s3->alpn_selected);
 
   OPENSSL_cleanse(s->s3, sizeof *s->s3);
   OPENSSL_free(s->s3);
@@ -686,145 +576,139 @@
 
 static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len);
 
+int SSL_session_reused(const SSL *ssl) {
+  return ssl->hit;
+}
+
+int SSL_total_renegotiations(const SSL *ssl) {
+  return ssl->s3->total_renegotiations;
+}
+
+int SSL_num_renegotiations(const SSL *ssl) {
+  return SSL_total_renegotiations(ssl);
+}
+
+int SSL_CTX_need_tmp_RSA(const SSL_CTX *ctx) {
+  return 0;
+}
+
+int SSL_need_rsa(const SSL *ssl) {
+  return 0;
+}
+
+int SSL_CTX_set_tmp_rsa(SSL_CTX *ctx, const RSA *rsa) {
+  return 1;
+}
+
+int SSL_set_tmp_rsa(SSL *ssl, const RSA *rsa) {
+  return 1;
+}
+
+int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) {
+  DH_free(ctx->cert->dh_tmp);
+  ctx->cert->dh_tmp = DHparams_dup(dh);
+  if (ctx->cert->dh_tmp == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_tmp_dh, ERR_R_DH_LIB);
+    return 0;
+  }
+  return 1;
+}
+
+int SSL_set_tmp_dh(SSL *ssl, const DH *dh) {
+  DH_free(ssl->cert->dh_tmp);
+  ssl->cert->dh_tmp = DHparams_dup(dh);
+  if (ssl->cert->dh_tmp == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_set_tmp_dh, ERR_R_DH_LIB);
+    return 0;
+  }
+  return 1;
+}
+
+int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) {
+  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_tmp_ecdh, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  ctx->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return 1;
+}
+
+int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) {
+  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_set_tmp_ecdh, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  ssl->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return 1;
+}
+
+int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
+  ctx->tlsext_channel_id_enabled = 1;
+  return 1;
+}
+
+int SSL_enable_tls_channel_id(SSL *ssl) {
+  ssl->tlsext_channel_id_enabled = 1;
+  return 1;
+}
+
+int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) {
+  ctx->tlsext_channel_id_enabled = 1;
+  if (EVP_PKEY_id(private_key) != EVP_PKEY_EC ||
+      EVP_PKEY_bits(private_key) != 256) {
+    OPENSSL_PUT_ERROR(SSL, SSL_CTX_set1_tls_channel_id,
+                      SSL_R_CHANNEL_ID_NOT_P256);
+    return 0;
+  }
+  EVP_PKEY_free(ctx->tlsext_channel_id_private);
+  ctx->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
+  return 1;
+}
+
+int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
+  ssl->tlsext_channel_id_enabled = 1;
+  if (EVP_PKEY_id(private_key) != EVP_PKEY_EC ||
+      EVP_PKEY_bits(private_key) != 256) {
+    OPENSSL_PUT_ERROR(SSL, SSL_set1_tls_channel_id, SSL_R_CHANNEL_ID_NOT_P256);
+    return 0;
+  }
+  EVP_PKEY_free(ssl->tlsext_channel_id_private);
+  ssl->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
+  return 1;
+}
+
+size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) {
+  if (!ssl->s3->tlsext_channel_id_valid) {
+    return 0;
+  }
+  memcpy(out, ssl->s3->tlsext_channel_id, (max_out < 64) ? max_out : 64);
+  return 64;
+}
+
+int SSL_set_tlsext_host_name(SSL *ssl, const char *name) {
+  OPENSSL_free(ssl->tlsext_hostname);
+  ssl->tlsext_hostname = NULL;
+
+  if (name == NULL) {
+    return 1;
+  }
+  if (strlen(name) > TLSEXT_MAXLEN_host_name) {
+    OPENSSL_PUT_ERROR(SSL, SSL_set_tlsext_host_name,
+                      SSL_R_SSL3_EXT_INVALID_SERVERNAME);
+    return 0;
+  }
+  ssl->tlsext_hostname = BUF_strdup(name);
+  if (ssl->tlsext_hostname == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_set_tlsext_host_name, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  return 1;
+}
+
 long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) {
   int ret = 0;
 
-  if (cmd == SSL_CTRL_SET_TMP_RSA || cmd == SSL_CTRL_SET_TMP_RSA_CB ||
-      cmd == SSL_CTRL_SET_TMP_DH || cmd == SSL_CTRL_SET_TMP_DH_CB) {
-    if (!ssl_cert_inst(&s->cert)) {
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_MALLOC_FAILURE);
-      return 0;
-    }
-  }
-
   switch (cmd) {
-    case SSL_CTRL_GET_SESSION_REUSED:
-      ret = s->hit;
-      break;
-
-    case SSL_CTRL_GET_CLIENT_CERT_REQUEST:
-      break;
-
-    case SSL_CTRL_GET_NUM_RENEGOTIATIONS:
-      ret = s->s3->num_renegotiations;
-      break;
-
-    case SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS:
-      ret = s->s3->num_renegotiations;
-      s->s3->num_renegotiations = 0;
-      break;
-
-    case SSL_CTRL_GET_TOTAL_RENEGOTIATIONS:
-      ret = s->s3->total_renegotiations;
-      break;
-
-    case SSL_CTRL_GET_FLAGS:
-      ret = (int)(s->s3->flags);
-      break;
-
-    case SSL_CTRL_NEED_TMP_RSA:
-      /* Temporary RSA keys are never used. */
-      ret = 0;
-      break;
-
-    case SSL_CTRL_SET_TMP_RSA:
-      /* Temporary RSA keys are never used. */
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      break;
-
-    case SSL_CTRL_SET_TMP_RSA_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return ret;
-
-    case SSL_CTRL_SET_TMP_DH: {
-      DH *dh = (DH *)parg;
-      if (dh == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_PASSED_NULL_PARAMETER);
-        return ret;
-      }
-      dh = DHparams_dup(dh);
-      if (dh == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_DH_LIB);
-        return ret;
-      }
-      if (!(s->options & SSL_OP_SINGLE_DH_USE) && !DH_generate_key(dh)) {
-        DH_free(dh);
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_DH_LIB);
-        return ret;
-      }
-      if (s->cert->dh_tmp != NULL) {
-        DH_free(s->cert->dh_tmp);
-      }
-      s->cert->dh_tmp = dh;
-      ret = 1;
-      break;
-    }
-
-    case SSL_CTRL_SET_TMP_DH_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return ret;
-
-    case SSL_CTRL_SET_TMP_ECDH: {
-      EC_KEY *ecdh = NULL;
-
-      if (parg == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_PASSED_NULL_PARAMETER);
-        return ret;
-      }
-      if (!EC_KEY_up_ref((EC_KEY *)parg)) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB);
-        return ret;
-      }
-      ecdh = (EC_KEY *)parg;
-      if (!(s->options & SSL_OP_SINGLE_ECDH_USE) && !EC_KEY_generate_key(ecdh)) {
-        EC_KEY_free(ecdh);
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB);
-        return ret;
-      }
-      if (s->cert->ecdh_tmp != NULL) {
-        EC_KEY_free(s->cert->ecdh_tmp);
-      }
-      s->cert->ecdh_tmp = ecdh;
-      ret = 1;
-      break;
-    }
-
-    case SSL_CTRL_SET_TMP_ECDH_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return ret;
-
-    case SSL_CTRL_SET_TLSEXT_HOSTNAME:
-      if (larg == TLSEXT_NAMETYPE_host_name) {
-        if (s->tlsext_hostname != NULL) {
-          OPENSSL_free(s->tlsext_hostname);
-        }
-        s->tlsext_hostname = NULL;
-
-        ret = 1;
-        if (parg == NULL) {
-          break;
-        }
-        if (strlen((char *)parg) > TLSEXT_MAXLEN_host_name) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
-          return 0;
-        }
-        s->tlsext_hostname = BUF_strdup((char *) parg);
-        if (s->tlsext_hostname == NULL) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_INTERNAL_ERROR);
-          return 0;
-        }
-      } else {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl,
-                          SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE);
-        return 0;
-      }
-      break;
-
-    case SSL_CTRL_SET_TLSEXT_DEBUG_ARG:
-      s->tlsext_debug_arg = parg;
-      ret = 1;
-      break;
-
     case SSL_CTRL_CHAIN:
       if (larg) {
         return ssl_cert_set1_chain(s->cert, (STACK_OF(X509) *)parg);
@@ -870,10 +754,6 @@
       return tls1_set_curves(&s->tlsext_ellipticcurvelist,
                              &s->tlsext_ellipticcurvelist_length, parg, larg);
 
-    case SSL_CTRL_SET_ECDH_AUTO:
-      s->cert->ecdh_tmp_auto = larg;
-      return 1;
-
     case SSL_CTRL_SET_SIGALGS:
       return tls1_set_sigalgs(s->cert, parg, larg, 0);
 
@@ -943,65 +823,6 @@
       return (int)s->s3->tmp.peer_ecpointformatlist_length;
     }
 
-    case SSL_CTRL_CHANNEL_ID:
-      s->tlsext_channel_id_enabled = 1;
-      ret = 1;
-      break;
-
-    case SSL_CTRL_SET_CHANNEL_ID:
-      s->tlsext_channel_id_enabled = 1;
-      if (EVP_PKEY_bits(parg) != 256) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, SSL_R_CHANNEL_ID_NOT_P256);
-        break;
-      }
-      if (s->tlsext_channel_id_private) {
-        EVP_PKEY_free(s->tlsext_channel_id_private);
-      }
-      s->tlsext_channel_id_private = EVP_PKEY_dup((EVP_PKEY *)parg);
-      ret = 1;
-      break;
-
-    case SSL_CTRL_GET_CHANNEL_ID:
-      if (!s->s3->tlsext_channel_id_valid) {
-        break;
-      }
-      memcpy(parg, s->s3->tlsext_channel_id, larg < 64 ? larg : 64);
-      return 64;
-
-    default:
-      break;
-  }
-
-  return ret;
-}
-
-long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void)) {
-  int ret = 0;
-
-  if ((cmd == SSL_CTRL_SET_TMP_RSA_CB || cmd == SSL_CTRL_SET_TMP_DH_CB) &&
-      !ssl_cert_inst(&s->cert)) {
-    OPENSSL_PUT_ERROR(SSL, ssl3_callback_ctrl, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
-  switch (cmd) {
-    case SSL_CTRL_SET_TMP_RSA_CB:
-      /* Ignore the callback; temporary RSA keys are never used. */
-      break;
-
-    case SSL_CTRL_SET_TMP_DH_CB:
-      s->cert->dh_tmp_cb = (DH * (*)(SSL *, int, int))fp;
-      break;
-
-    case SSL_CTRL_SET_TMP_ECDH_CB:
-      s->cert->ecdh_tmp_cb = (EC_KEY * (*)(SSL *, int, int))fp;
-      break;
-
-    case SSL_CTRL_SET_TLSEXT_DEBUG_CB:
-      s->tlsext_debug_cb =
-          (void (*)(SSL *, int, int, uint8_t *, int, void *))fp;
-      break;
-
     default:
       break;
   }
@@ -1010,82 +831,7 @@
 }
 
 long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) {
-  CERT *cert;
-
-  cert = ctx->cert;
-
   switch (cmd) {
-    case SSL_CTRL_NEED_TMP_RSA:
-      /* Temporary RSA keys are never used. */
-      return 0;
-
-    case SSL_CTRL_SET_TMP_RSA:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return 0;
-
-    case SSL_CTRL_SET_TMP_RSA_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return 0;
-
-    case SSL_CTRL_SET_TMP_DH: {
-      DH *new = NULL, *dh;
-
-      dh = (DH *)parg;
-      new = DHparams_dup(dh);
-      if (new == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_DH_LIB);
-        return 0;
-      }
-      if (!(ctx->options & SSL_OP_SINGLE_DH_USE) && !DH_generate_key(new)) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_DH_LIB);
-        DH_free(new);
-        return 0;
-      }
-      if (cert->dh_tmp != NULL) {
-        DH_free(cert->dh_tmp);
-      }
-      cert->dh_tmp = new;
-      return 1;
-    }
-
-    case SSL_CTRL_SET_TMP_DH_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return 0;
-
-    case SSL_CTRL_SET_TMP_ECDH: {
-      EC_KEY *ecdh = NULL;
-
-      if (parg == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB);
-        return 0;
-      }
-      ecdh = EC_KEY_dup((EC_KEY *)parg);
-      if (ecdh == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_EC_LIB);
-        return 0;
-      }
-      if (!(ctx->options & SSL_OP_SINGLE_ECDH_USE) &&
-          !EC_KEY_generate_key(ecdh)) {
-        EC_KEY_free(ecdh);
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB);
-        return 0;
-      }
-
-      if (cert->ecdh_tmp != NULL) {
-        EC_KEY_free(cert->ecdh_tmp);
-      }
-      cert->ecdh_tmp = ecdh;
-      return 1;
-    }
-
-    case SSL_CTRL_SET_TMP_ECDH_CB:
-      OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-      return 0;
-
-    case SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG:
-      ctx->tlsext_servername_arg = parg;
-      break;
-
     case SSL_CTRL_SET_TLSEXT_TICKET_KEYS:
     case SSL_CTRL_GET_TLSEXT_TICKET_KEYS: {
       uint8_t *keys = parg;
@@ -1108,19 +854,10 @@
       return 1;
     }
 
-    case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG:
-      ctx->tlsext_status_arg = parg;
-      return 1;
-      break;
-
     case SSL_CTRL_SET_CURVES:
       return tls1_set_curves(&ctx->tlsext_ellipticcurvelist,
                              &ctx->tlsext_ellipticcurvelist_length, parg, larg);
 
-    case SSL_CTRL_SET_ECDH_AUTO:
-      ctx->cert->ecdh_tmp_auto = larg;
-      return 1;
-
     case SSL_CTRL_SET_SIGALGS:
       return tls1_set_sigalgs(ctx->cert, parg, larg, 0);
 
@@ -1158,10 +895,8 @@
       break;
 
     case SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS:
-      if (ctx->extra_certs) {
-        sk_X509_pop_free(ctx->extra_certs, X509_free);
-        ctx->extra_certs = NULL;
-      }
+      sk_X509_pop_free(ctx->extra_certs, X509_free);
+      ctx->extra_certs = NULL;
       break;
 
     case SSL_CTRL_CHAIN:
@@ -1185,22 +920,6 @@
     case SSL_CTRL_SELECT_CURRENT_CERT:
       return ssl_cert_select_current(ctx->cert, (X509 *)parg);
 
-    case SSL_CTRL_CHANNEL_ID:
-      ctx->tlsext_channel_id_enabled = 1;
-      return 1;
-
-    case SSL_CTRL_SET_CHANNEL_ID:
-      ctx->tlsext_channel_id_enabled = 1;
-      if (EVP_PKEY_bits(parg) != 256) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, SSL_R_CHANNEL_ID_NOT_P256);
-        break;
-      }
-      if (ctx->tlsext_channel_id_private) {
-        EVP_PKEY_free(ctx->tlsext_channel_id_private);
-      }
-      ctx->tlsext_channel_id_private = EVP_PKEY_dup((EVP_PKEY *)parg);
-      break;
-
     default:
       return 0;
   }
@@ -1208,41 +927,22 @@
   return 1;
 }
 
-long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) {
-  CERT *cert;
+int SSL_CTX_set_tlsext_servername_callback(
+    SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) {
+  ctx->tlsext_servername_callback = callback;
+  return 1;
+}
 
-  cert = ctx->cert;
+int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) {
+  ctx->tlsext_servername_arg = arg;
+  return 1;
+}
 
-  switch (cmd) {
-    case SSL_CTRL_SET_TMP_RSA_CB:
-      /* Ignore the callback; temporary RSA keys are never used. */
-      break;
-
-    case SSL_CTRL_SET_TMP_DH_CB:
-      cert->dh_tmp_cb = (DH * (*)(SSL *, int, int))fp;
-      break;
-
-    case SSL_CTRL_SET_TMP_ECDH_CB:
-      cert->ecdh_tmp_cb = (EC_KEY * (*)(SSL *, int, int))fp;
-      break;
-
-    case SSL_CTRL_SET_TLSEXT_SERVERNAME_CB:
-      ctx->tlsext_servername_callback = (int (*)(SSL *, int *, void *))fp;
-      break;
-
-    case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB:
-      ctx->tlsext_status_cb = (int (*)(SSL *, void *))fp;
-      break;
-
-    case SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB:
-      ctx->tlsext_ticket_key_cb = (int (
-          *)(SSL *, uint8_t *, uint8_t *, EVP_CIPHER_CTX *, HMAC_CTX *, int))fp;
-      break;
-
-    default:
-      return 0;
-  }
-
+int SSL_CTX_set_tlsext_ticket_key_cb(
+    SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv,
+                                  EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
+                                  int encrypt)) {
+  ctx->tlsext_ticket_key_cb = callback;
   return 1;
 }
 
@@ -1261,7 +961,7 @@
 
 /* ssl3_get_cipher_by_value returns the cipher value of |c|. */
 uint16_t ssl3_get_cipher_value(const SSL_CIPHER *c) {
-  unsigned long id = c->id;
+  uint32_t id = c->id;
   /* All ciphers are SSLv3 now. */
   assert((id & 0xff000000) == 0x03000000);
   return id & 0xffff;
@@ -1285,14 +985,14 @@
 }
 
 const SSL_CIPHER *ssl3_choose_cipher(
-    SSL *s, STACK_OF(SSL_CIPHER) * clnt,
+    SSL *s, STACK_OF(SSL_CIPHER) *clnt,
     struct ssl_cipher_preference_list_st *server_pref) {
   const SSL_CIPHER *c, *ret = NULL;
   STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;
   size_t i;
   int ok;
   size_t cipher_index;
-  unsigned long alg_k, alg_a, mask_k, mask_a;
+  uint32_t alg_k, alg_a, mask_k, mask_a;
   /* in_group_flags will either be NULL, or will point to an array of bytes
    * which indicate equal-preference groups in the |prio| stack. See the
    * comment about |in_group_flags| in the |ssl_cipher_preference_list_st|
@@ -1389,7 +1089,7 @@
   }
 
   /* ECDSA certs can be used with RSA cipher suites as well so we don't need to
-   * check for SSL_kECDH or SSL_kEECDH. */
+   * check for SSL_kECDH or SSL_kECDHE. */
   if (s->version >= TLS1_VERSION && have_ecdsa_sign) {
       p[ret++] = TLS_CT_ECDSA_SIGN;
   }
@@ -1398,12 +1098,10 @@
 }
 
 static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len) {
-  if (c->client_certificate_types) {
-    OPENSSL_free(c->client_certificate_types);
-    c->client_certificate_types = NULL;
-  }
-
+  OPENSSL_free(c->client_certificate_types);
+  c->client_certificate_types = NULL;
   c->num_client_certificate_types = 0;
+
   if (!p || !len) {
     return 1;
   }
@@ -1474,29 +1172,12 @@
 }
 
 static int ssl3_read_internal(SSL *s, void *buf, int len, int peek) {
-  int ret;
-
   ERR_clear_system_error();
   if (s->s3->renegotiate) {
     ssl3_renegotiate_check(s);
   }
-  s->s3->in_read_app_data = 1;
-  ret = s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len, peek);
-  if (ret == -1 && s->s3->in_read_app_data == 2) {
-    /* ssl3_read_bytes decided to call s->handshake_func, which called
-     * ssl3_read_bytes to read handshake data. However, ssl3_read_bytes
-     * actually found application data and thinks that application data makes
-     * sense here; so disable handshake processing and try to read application
-     * data again. */
-    s->in_handshake++;
-    ret =
-        s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len, peek);
-    s->in_handshake--;
-  } else {
-    s->s3->in_read_app_data = 0;
-  }
 
-  return ret;
+  return s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
 int ssl3_read(SSL *s, void *buf, int len) {
@@ -1523,7 +1204,6 @@
      * need to go to SSL_ST_ACCEPT. */
     s->state = SSL_ST_RENEGOTIATE;
     s->s3->renegotiate = 0;
-    s->s3->num_renegotiations++;
     s->s3->total_renegotiations++;
     return 1;
   }
@@ -1533,9 +1213,9 @@
 
 /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
  * handshake macs if required. */
-long ssl_get_algorithm2(SSL *s) {
-  static const unsigned long kMask = SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF;
-  long alg2 = s->s3->tmp.new_cipher->algorithm2;
+uint32_t ssl_get_algorithm2(SSL *s) {
+  static const uint32_t kMask = SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF;
+  uint32_t alg2 = s->s3->tmp.new_cipher->algorithm2;
   if (s->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF &&
       (alg2 & kMask) == kMask) {
     return SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256;
diff --git a/src/ssl/s3_meth.c b/src/ssl/s3_meth.c
index 5a25d7b..28b9051 100644
--- a/src/ssl/s3_meth.c
+++ b/src/ssl/s3_meth.c
@@ -54,10 +54,11 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 static const SSL_PROTOCOL_METHOD TLS_protocol_method = {
+    0 /* is_dtls */,
     ssl3_new,
     ssl3_free,
     ssl3_accept,
@@ -77,9 +78,9 @@
     ssl3_pending,
     ssl3_num_ciphers,
     ssl3_get_cipher,
-    ssl_undefined_void_function,
-    ssl3_callback_ctrl,
-    ssl3_ctx_callback_ctrl,
+    SSL3_HM_HEADER_LENGTH,
+    ssl3_set_handshake_header,
+    ssl3_handshake_write,
 };
 
 const SSL_METHOD *TLS_method(void) {
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 3a42c3a..c42d000 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -107,9 +107,9 @@
  * Hudson (tjh@cryptsoft.com). */
 
 #include <assert.h>
-#include <errno.h>
 #include <limits.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/buf.h>
 #include <openssl/err.h>
@@ -117,23 +117,23 @@
 #include <openssl/mem.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len,
-                         char fragment, char is_fragment);
+                         char fragment);
 static int ssl3_get_record(SSL *s);
 
-int ssl3_read_n(SSL *s, int n, int max, int extend) {
+int ssl3_read_n(SSL *s, int n, int extend) {
   /* If |extend| is 0, obtain new n-byte packet;
    * if |extend| is 1, increase packet by another n bytes.
    *
    * The packet will be in the sub-array of |s->s3->rbuf.buf| specified by
-   * |s->packet| and |s->packet_length|. (If |s->read_ahead| is set, |max|
-   * bytes may be stored in |rbuf| (plus |s->packet_length| bytes if |extend|
-   * is one.) */
+   * |s->packet| and |s->packet_length|. (If |s->read_ahead| is set and |extend|
+   * is 0, additional bytes may be read into |rbuf|, up to the size of the
+   * buffer.) */
   int i, len, left;
-  long align = 0;
+  uintptr_t align = 0;
   uint8_t *pkt;
   SSL3_BUFFER *rb;
 
@@ -148,8 +148,8 @@
 
   left = rb->left;
 
-  align = (long)rb->buf + SSL3_RT_HEADER_LENGTH;
-  align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
+  align = (uintptr_t)rb->buf + SSL3_RT_HEADER_LENGTH;
+  align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1);
 
   if (!extend) {
     /* start with empty packet ... */
@@ -201,22 +201,14 @@
     rb->offset = len + align;
   }
 
-  assert(n <= (int)(rb->len - rb->offset));
   if (n > (int)(rb->len - rb->offset)) {
     OPENSSL_PUT_ERROR(SSL, ssl3_read_n, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  if (!s->read_ahead) {
-    /* ignore max parameter */
-    max = n;
-  } else {
-    if (max < n) {
-      max = n;
-    }
-    if (max > (int)(rb->len - rb->offset)) {
-      max = rb->len - rb->offset;
-    }
+  int max = n;
+  if (s->read_ahead && !extend) {
+    max = rb->len - rb->offset;
   }
 
   while (left < n) {
@@ -233,8 +225,7 @@
 
     if (i <= 0) {
       rb->left = left;
-      if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s) &&
-          len + left == 0) {
+      if (len + left == 0) {
         ssl3_release_read_buffer(s);
       }
       return i;
@@ -281,29 +272,21 @@
 
   rr = &s->s3->rrec;
 
-  if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) {
-    extra = SSL3_RT_MAX_EXTRA;
-  } else {
-    extra = 0;
-  }
-
-  if (extra && !s->s3->init_extra) {
-    /* An application error: SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER set after
-     * ssl3_setup_buffers() was done */
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_record, ERR_R_INTERNAL_ERROR);
-    return -1;
-  }
-
 again:
   /* check if we have the header */
   if (s->rstate != SSL_ST_READ_BODY ||
       s->packet_length < SSL3_RT_HEADER_LENGTH) {
-    n = ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, s->s3->rbuf.len, 0);
+    n = ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, 0);
     if (n <= 0) {
       return n; /* error or non-blocking */
     }
     s->rstate = SSL_ST_READ_BODY;
 
+    /* Some bytes were read, so the read buffer must be existant and
+     * |s->s3->init_extra| is defined. */
+    assert(s->s3->rbuf.buf != NULL);
+    extra = s->s3->init_extra ? SSL3_RT_MAX_EXTRA : 0;
+
     p = s->packet;
     if (s->msg_callback) {
       s->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, s, s->msg_callback_arg);
@@ -318,10 +301,6 @@
 
     if (s->s3->have_version && version != s->version) {
       OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_WRONG_VERSION_NUMBER);
-      if ((s->version & 0xFF00) == (version & 0xFF00)) {
-        /* Send back error using their minor version number. */
-        s->version = (unsigned short)version;
-      }
       al = SSL_AD_PROTOCOL_VERSION;
       goto f_err;
     }
@@ -331,13 +310,18 @@
       goto err;
     }
 
-    if (rr->length > s->s3->rbuf.len - SSL3_RT_HEADER_LENGTH) {
+    if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) {
       al = SSL_AD_RECORD_OVERFLOW;
-      OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_PACKET_LENGTH_TOO_LONG);
+      OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
       goto f_err;
     }
 
     /* now s->rstate == SSL_ST_READ_BODY */
+  } else {
+    /* |packet_length| is non-zero and |s->rstate| is |SSL_ST_READ_BODY|. The
+     * read buffer must be existant and |s->s3->init_extra| is defined. */
+    assert(s->s3->rbuf.buf != NULL);
+    extra = s->s3->init_extra ? SSL3_RT_MAX_EXTRA : 0;
   }
 
   /* s->rstate == SSL_ST_READ_BODY, get and decode the data */
@@ -345,7 +329,7 @@
   if (rr->length > s->packet_length - SSL3_RT_HEADER_LENGTH) {
     /* now s->packet_length == SSL3_RT_HEADER_LENGTH */
     i = rr->length;
-    n = ssl3_read_n(s, i, i, 1);
+    n = ssl3_read_n(s, i, 1);
     if (n <= 0) {
       /* Error or non-blocking IO. Now |n| == |rr->length|, and
        * |s->packet_length| == |SSL3_RT_HEADER_LENGTH| + |rr->length|. */
@@ -367,13 +351,6 @@
   /* We now have - encrypted [ MAC [ compressed [ plain ] ] ]
    * rr->length bytes of encrypted compressed stuff. */
 
-  /* check is not needed I believe */
-  if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) {
-    al = SSL_AD_RECORD_OVERFLOW;
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
-    goto f_err;
-  }
-
   /* decrypt in place in 'rr->input' */
   rr->data = rr->input;
 
@@ -431,7 +408,7 @@
   tot = s->s3->wnum;
   s->s3->wnum = 0;
 
-  if (SSL_in_init(s) && !s->in_handshake) {
+  if (!s->in_handshake && SSL_in_init(s) && !SSL_in_false_start(s)) {
     i = s->handshake_func(s);
     if (i < 0) {
       return i;
@@ -454,6 +431,7 @@
     return -1;
   }
 
+  int record_split_done = 0;
   n = (len - tot);
   for (;;) {
     /* max contains the maximum number of bytes that we can put into a
@@ -462,34 +440,27 @@
     /* fragment is true if do_ssl3_write should send the first byte in its own
      * record in order to randomise a CBC IV. */
     int fragment = 0;
-
-    if (n > 1 && s->s3->need_record_splitting &&
-        type == SSL3_RT_APPLICATION_DATA && !s->s3->record_split_done) {
+    if (!record_split_done && s->s3->need_record_splitting &&
+        type == SSL3_RT_APPLICATION_DATA) {
+      /* Only the the first record per write call needs to be split. The
+       * remaining plaintext was determined before the IV was randomized. */
       fragment = 1;
-      /* record_split_done records that the splitting has been done in case we
-       * hit an SSL_WANT_WRITE condition. In that case, we don't need to do the
-       * split again. */
-      s->s3->record_split_done = 1;
+      record_split_done = 1;
     }
-
     if (n > max) {
       nw = max;
     } else {
       nw = n;
     }
 
-    i = do_ssl3_write(s, type, &(buf[tot]), nw, fragment, 0);
+    i = do_ssl3_write(s, type, &buf[tot], nw, fragment);
     if (i <= 0) {
       s->s3->wnum = tot;
-      s->s3->record_split_done = 0;
       return i;
     }
 
     if (i == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
                         (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
-      /* next chunk of data should get another prepended, one-byte fragment in
-       * ciphersuites with known-IV weakness. */
-      s->s3->record_split_done = 0;
       return tot + i;
     }
 
@@ -498,20 +469,92 @@
   }
 }
 
+/* ssl3_seal_record seals a new record of type |type| and plaintext |in| and
+ * writes it to |out|. At most |max_out| bytes will be written. It returns one
+ * on success and zero on error. On success, |s->s3->wrec| is updated to include
+ * the new record. */
+static int ssl3_seal_record(SSL *s, uint8_t *out, size_t *out_len,
+                            size_t max_out, uint8_t type, const uint8_t *in,
+                            size_t in_len) {
+  if (max_out < SSL3_RT_HEADER_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, SSL_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  out[0] = type;
+
+  /* Some servers hang if initial ClientHello is larger than 256 bytes and
+   * record version number > TLS 1.0. */
+  if (!s->s3->have_version && s->version > SSL3_VERSION) {
+    out[1] = TLS1_VERSION >> 8;
+    out[2] = TLS1_VERSION & 0xff;
+  } else {
+    out[1] = s->version >> 8;
+    out[2] = s->version & 0xff;
+  }
+
+  size_t explicit_nonce_len = 0;
+  if (s->aead_write_ctx != NULL &&
+      s->aead_write_ctx->variable_nonce_included_in_record) {
+    explicit_nonce_len = s->aead_write_ctx->variable_nonce_len;
+  }
+  size_t max_overhead = 0;
+  if (s->aead_write_ctx != NULL) {
+    max_overhead = s->aead_write_ctx->tag_len;
+  }
+
+  /* Assemble the input for |s->enc_method->enc|. The input is the plaintext
+   * with |explicit_nonce_len| bytes of space prepended for the explicit
+   * nonce. The input is copied into |out| and then encrypted in-place to take
+   * advantage of alignment.
+   *
+   * TODO(davidben): |tls1_enc| should accept its inputs and outputs directly
+   * rather than looking up in |wrec| and friends. The |max_overhead| bounds
+   * check would also be unnecessary if |max_out| were passed down. */
+  SSL3_RECORD *wr = &s->s3->wrec;
+  size_t plaintext_len = in_len + explicit_nonce_len;
+  if (plaintext_len < in_len || plaintext_len > INT_MAX ||
+      plaintext_len + max_overhead < plaintext_len) {
+    OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, ERR_R_OVERFLOW);
+    return 0;
+  }
+  if (max_out - SSL3_RT_HEADER_LENGTH < plaintext_len + max_overhead) {
+    OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, SSL_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+  wr->type = type;
+  wr->input = out + SSL3_RT_HEADER_LENGTH;
+  wr->data = wr->input;
+  wr->length = plaintext_len;
+  memcpy(wr->input + explicit_nonce_len, in, in_len);
+
+  if (!s->enc_method->enc(s, 1)) {
+    return 0;
+  }
+
+  /* |wr->length| has now been set to the ciphertext length. */
+  if (wr->length >= 1 << 16) {
+    OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, ERR_R_OVERFLOW);
+    return 0;
+  }
+  out[3] = wr->length >> 8;
+  out[4] = wr->length & 0xff;
+  *out_len = SSL3_RT_HEADER_LENGTH + (size_t)wr->length;
+
+ if (s->msg_callback) {
+   s->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, SSL3_RT_HEADER_LENGTH,
+                   s, s->msg_callback_arg);
+ }
+
+  return 1;
+}
+
 /* do_ssl3_write writes an SSL record of the given type. If |fragment| is 1
  * then it splits the record into a one byte record and a record with the rest
- * of the data in order to randomise a CBC IV. If |is_fragment| is true then
- * this call resulted from do_ssl3_write calling itself in order to create that
- * one byte fragment. */
+ * of the data in order to randomise a CBC IV. */
 static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len,
-                         char fragment, char is_fragment) {
-  uint8_t *p, *plen;
-  int i;
-  int prefix_len = 0;
-  int eivlen = 0;
-  long align = 0;
-  SSL3_RECORD *wr;
-  SSL3_BUFFER *wb = &(s->s3->wbuf);
+                         char fragment) {
+  SSL3_BUFFER *wb = &s->s3->wbuf;
 
   /* first check if there is a SSL3_BUFFER still being written out. This will
    * happen with non blocking IO */
@@ -521,9 +564,9 @@
 
   /* If we have an alert to send, lets send it */
   if (s->s3->alert_dispatch) {
-    i = s->method->ssl_dispatch_alert(s);
-    if (i <= 0) {
-      return i;
+    int ret = s->method->ssl_dispatch_alert(s);
+    if (ret <= 0) {
+      return ret;
     }
     /* if it went, fall through and send more stuff */
   }
@@ -535,122 +578,62 @@
   if (len == 0) {
     return 0;
   }
+  if (len == 1) {
+    /* No sense in fragmenting a one-byte record. */
+    fragment = 0;
+  }
 
-  wr = &s->s3->wrec;
-
+  /* Align the output so the ciphertext is aligned to |SSL3_ALIGN_PAYLOAD|. */
+  uintptr_t align;
   if (fragment) {
-    /* countermeasure against known-IV weakness in CBC ciphersuites (see
-     * http://www.openssl.org/~bodo/tls-cbc.txt) */
-    prefix_len = do_ssl3_write(s, type, buf, 1 /* length */, 0 /* fragment */,
-                               1 /* is_fragment */);
-    if (prefix_len <= 0) {
-      goto err;
-    }
-
-    if (prefix_len >
-        (SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD)) {
-      /* insufficient space */
-      OPENSSL_PUT_ERROR(SSL, do_ssl3_write, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-  }
-
-  if (is_fragment) {
-    /* The extra fragment would be couple of cipher blocks, and that will be a
-     * multiple of SSL3_ALIGN_PAYLOAD. So, if we want to align the real
-     * payload, we can just pretend that we have two headers and a byte. */
-    align = (long)wb->buf + 2 * SSL3_RT_HEADER_LENGTH + 1;
-    align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
-    p = wb->buf + align;
-    wb->offset = align;
-  } else if (prefix_len) {
-    p = wb->buf + wb->offset + prefix_len;
+    /* Only CBC-mode ciphers require fragmenting. CBC-mode ciphertext is a
+     * multiple of the block size which we may assume is aligned. Thus we only
+     * need to account for a second copy of the record header. */
+    align = (uintptr_t)wb->buf + 2 * SSL3_RT_HEADER_LENGTH;
   } else {
-    align = (long)wb->buf + SSL3_RT_HEADER_LENGTH;
-    align = (-align) & (SSL3_ALIGN_PAYLOAD - 1);
-    p = wb->buf + align;
-    wb->offset = align;
+    align = (uintptr_t)wb->buf + SSL3_RT_HEADER_LENGTH;
+  }
+  align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1);
+  uint8_t *out = wb->buf + align;
+  wb->offset = align;
+  size_t max_out = wb->len - wb->offset;
+
+  const uint8_t *orig_buf = buf;
+  unsigned int orig_len = len;
+  size_t fragment_len = 0;
+  if (fragment) {
+    /* Write the first byte in its own record as a countermeasure against
+     * known-IV weaknesses in CBC ciphersuites. (See
+     * http://www.openssl.org/~bodo/tls-cbc.txt.) */
+    if (!ssl3_seal_record(s, out, &fragment_len, max_out, type, buf, 1)) {
+      return -1;
+    }
+    out += fragment_len;
+    max_out -= fragment_len;
+    buf++;
+    len--;
   }
 
-  /* write the header */
-
-  *(p++) = type & 0xff;
-  wr->type = type;
-
-  /* Some servers hang if initial ClientHello is larger than 256 bytes and
-   * record version number > TLS 1.0. */
-  if (!s->s3->have_version && s->version > SSL3_VERSION) {
-    *(p++) = TLS1_VERSION >> 8;
-    *(p++) = TLS1_VERSION & 0xff;
-  } else {
-    *(p++) = s->version >> 8;
-    *(p++) = s->version & 0xff;
+  assert((((uintptr_t)out + SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1))
+         == 0);
+  size_t ciphertext_len;
+  if (!ssl3_seal_record(s, out, &ciphertext_len, max_out, type, buf, len)) {
+    return -1;
   }
-
-  /* field where we are to write out packet length */
-  plen = p;
-  p += 2;
-
-  /* Leave room for the variable nonce for AEADs which specify it explicitly. */
-  if (s->aead_write_ctx != NULL &&
-      s->aead_write_ctx->variable_nonce_included_in_record) {
-    eivlen = s->aead_write_ctx->variable_nonce_len;
-  }
-
-  /* lets setup the record stuff. */
-  wr->data = p + eivlen;
-  wr->length = (int)(len - (fragment != 0));
-  wr->input = (uint8_t *)buf + (fragment != 0);
-
-  /* we now 'read' from wr->input, wr->length bytes into wr->data */
-
-  memcpy(wr->data, wr->input, wr->length);
-  wr->input = wr->data;
-
-  /* we should still have the output to wr->data and the input from wr->input.
-   * Length should be wr->length. wr->data still points in the wb->buf */
-
-  wr->input = p;
-  wr->data = p;
-  wr->length += eivlen;
-
-  if (!s->enc_method->enc(s, 1)) {
-    goto err;
-  }
-
-  /* record length after mac and block padding */
-  s2n(wr->length, plen);
-
-  if (s->msg_callback) {
-    s->msg_callback(1, 0, SSL3_RT_HEADER, plen - 5, 5, s, s->msg_callback_arg);
-  }
-
-  /* we should now have wr->data pointing to the encrypted data, which is
-   * wr->length long. */
-  wr->type = type; /* not needed but helps for debugging */
-  wr->length += SSL3_RT_HEADER_LENGTH;
-
-  if (is_fragment) {
-    /* we are in a recursive call; just return the length, don't write out
-     * anything. */
-    return wr->length;
-  }
+  ciphertext_len += fragment_len;
 
   /* now let's set up wb */
-  wb->left = prefix_len + wr->length;
+  wb->left = ciphertext_len;
 
   /* memorize arguments so that ssl3_write_pending can detect bad write retries
    * later */
-  s->s3->wpend_tot = len;
-  s->s3->wpend_buf = buf;
+  s->s3->wpend_tot = orig_len;
+  s->s3->wpend_buf = orig_buf;
   s->s3->wpend_type = type;
-  s->s3->wpend_ret = len;
+  s->s3->wpend_ret = orig_len;
 
   /* we now just need to write the buffer */
-  return ssl3_write_pending(s, type, buf, len);
-
-err:
-  return -1;
+  return ssl3_write_pending(s, type, orig_buf, orig_len);
 }
 
 /* if s->s3->wbuf.left != 0, we need to call this */
@@ -679,19 +662,19 @@
     if (i == wb->left) {
       wb->left = 0;
       wb->offset += i;
-      if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s)) {
-        ssl3_release_write_buffer(s);
-      }
+      ssl3_release_write_buffer(s);
       s->rwstate = SSL_NOTHING;
       return s->s3->wpend_ret;
     } else if (i <= 0) {
       if (SSL_IS_DTLS(s)) {
-        /* For DTLS, just drop it. That's kind of the whole
-           point in using a datagram service */
+        /* For DTLS, just drop it. That's kind of the whole point in
+         * using a datagram service */
         wb->left = 0;
       }
       return i;
     }
+    /* TODO(davidben): This codepath is used in DTLS, but the write
+     * payload may not split across packets. */
     wb->offset += i;
     wb->left -= i;
   }
@@ -741,15 +724,10 @@
  *             none of our business
  */
 int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek) {
-  int al, i, j, ret;
+  int al, i, ret;
   unsigned int n;
   SSL3_RECORD *rr;
   void (*cb)(const SSL *ssl, int type2, int val) = NULL;
-  uint8_t alert_buffer[2];
-
-  if (s->s3->rbuf.buf == NULL && !ssl3_setup_read_buffer(s)) {
-    return -1;
-  }
 
   if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) ||
       (peek && type != SSL3_RT_APPLICATION_DATA)) {
@@ -780,8 +758,13 @@
 
   /* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
 
-  if (!s->in_handshake && SSL_in_init(s)) {
-    /* type == SSL3_RT_APPLICATION_DATA */
+  /* This may require multiple iterations. False Start will cause
+   * |s->handshake_func| to signal success one step early, but the handshake
+   * must be completely finished before other modes are accepted.
+   *
+   * TODO(davidben): Move this check up to a higher level. */
+  while (!s->in_handshake && SSL_in_init(s)) {
+    assert(type == SSL3_RT_APPLICATION_DATA);
     i = s->handshake_func(s);
     if (i < 0) {
       return i;
@@ -811,9 +794,10 @@
 
   /* we now have a packet which can be read and processed */
 
-  if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
-                                 * reset by ssl3_get_finished */
-      && rr->type != SSL3_RT_HANDSHAKE) {
+  /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by
+   * ssl3_get_finished. */
+  if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE &&
+      rr->type != SSL3_RT_ALERT) {
     al = SSL_AD_UNEXPECTED_MESSAGE;
     OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes,
                       SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
@@ -866,7 +850,7 @@
       if (rr->length == 0) {
         s->rstate = SSL_ST_READ_HEADER;
         rr->off = 0;
-        if (s->mode & SSL_MODE_RELEASE_BUFFERS && s->s3->rbuf.left == 0) {
+        if (s->s3->rbuf.left == 0) {
           ssl3_release_read_buffer(s);
         }
       }
@@ -883,6 +867,14 @@
    * that we can process the data at a fixed place. */
 
   if (rr->type == SSL3_RT_HANDSHAKE) {
+    /* If peer renegotiations are disabled, all out-of-order handshake records
+     * are fatal. */
+    if (s->reject_peer_renegotiations) {
+      al = SSL_AD_NO_RENEGOTIATION;
+      OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION);
+      goto f_err;
+    }
+
     const size_t size = sizeof(s->s3->handshake_fragment);
     const size_t avail = size - s->s3->handshake_fragment_len;
     const size_t todo = (rr->length < avail) ? rr->length : avail;
@@ -894,17 +886,6 @@
     if (s->s3->handshake_fragment_len < size) {
       goto start; /* fragment was too small */
     }
-  } else if (rr->type == SSL3_RT_ALERT) {
-    /* Note that this will still allow multiple alerts to be processed in the
-     * same record */
-    if (rr->length < sizeof(alert_buffer)) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_ALERT);
-      goto f_err;
-    }
-    memcpy(alert_buffer, &rr->data[rr->off], sizeof(alert_buffer));
-    rr->off += sizeof(alert_buffer);
-    rr->length -= sizeof(alert_buffer);
   }
 
   /* s->s3->handshake_fragment_len == 4  iff  rr->type == SSL3_RT_HANDSHAKE;
@@ -947,14 +928,23 @@
     goto start;
   }
 
+  /* If an alert record, process one alert out of the record. Note that we allow
+   * a single record to contain multiple alerts. */
   if (rr->type == SSL3_RT_ALERT) {
-    const uint8_t alert_level = alert_buffer[0];
-    const uint8_t alert_descr = alert_buffer[1];
+    /* Alerts may not be fragmented. */
+    if (rr->length < 2) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_ALERT);
+      goto f_err;
+    }
 
     if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_ALERT, alert_buffer, 2, s,
+      s->msg_callback(0, s->version, SSL3_RT_ALERT, &rr->data[rr->off], 2, s,
                       s->msg_callback_arg);
     }
+    const uint8_t alert_level = rr->data[rr->off++];
+    const uint8_t alert_descr = rr->data[rr->off++];
+    rr->length -= 2;
 
     if (s->info_callback != NULL) {
       cb = s->info_callback;
@@ -963,12 +953,11 @@
     }
 
     if (cb != NULL) {
-      j = (alert_level << 8) | alert_descr;
-      cb(s, SSL_CB_READ_ALERT, j);
+      uint16_t alert = (alert_level << 8) | alert_descr;
+      cb(s, SSL_CB_READ_ALERT, alert);
     }
 
-    if (alert_level == 1) {
-      /* warning */
+    if (alert_level == SSL3_AL_WARNING) {
       s->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
         s->shutdown |= SSL_RECEIVED_SHUTDOWN;
@@ -987,8 +976,7 @@
         OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION);
         goto f_err;
       }
-    } else if (alert_level == 2) {
-      /* fatal */
+    } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
       s->rwstate = SSL_NOTHING;
@@ -1074,50 +1062,12 @@
     goto start;
   }
 
-  switch (rr->type) {
-    default:
-      /* TLS up to v1.1 just ignores unknown message types. TLS v1.2 gives an
-       * unexpected message alert. */
-      if (s->version >= TLS1_VERSION && s->version <= TLS1_1_VERSION) {
-        rr->length = 0;
-        goto start;
-      }
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD);
-      goto f_err;
+  /* We already handled these. */
+  assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT &&
+         rr->type != SSL3_RT_HANDSHAKE);
 
-    case SSL3_RT_CHANGE_CIPHER_SPEC:
-    case SSL3_RT_ALERT:
-    case SSL3_RT_HANDSHAKE:
-      /* we already handled all of these, with the possible exception of
-       * SSL3_RT_HANDSHAKE when s->in_handshake is set, but that should not
-       * happen when type != rr->type */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR);
-      goto f_err;
-
-    case SSL3_RT_APPLICATION_DATA:
-      /* At this point we were expecting handshake data but have application
-       * data. If the library was running inside ssl3_read() (i.e.
-       * |in_read_app_data| is set) and it makes sense to read application data
-       * at this point (session renegotiation not yet started), we will indulge
-       * it. */
-      if (s->s3->in_read_app_data && s->s3->total_renegotiations != 0 &&
-          (((s->state & SSL_ST_CONNECT) &&
-            s->state >= SSL3_ST_CW_CLNT_HELLO_A &&
-            s->state <= SSL3_ST_CR_SRVR_HELLO_A) ||
-           ((s->state & SSL_ST_ACCEPT) &&
-            s->state <= SSL3_ST_SW_HELLO_REQ_A &&
-            s->state >= SSL3_ST_SR_CLNT_HELLO_A))) {
-        s->s3->in_read_app_data = 2;
-        return -1;
-      } else {
-        al = SSL_AD_UNEXPECTED_MESSAGE;
-        OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD);
-        goto f_err;
-      }
-  }
-  /* not reached */
+  al = SSL_AD_UNEXPECTED_MESSAGE;
+  OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD);
 
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -1189,7 +1139,7 @@
   void (*cb)(const SSL *ssl, int type, int val) = NULL;
 
   s->s3->alert_dispatch = 0;
-  i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2, 0, 0);
+  i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2, 0);
   if (i <= 0) {
     s->s3->alert_dispatch = 1;
   } else {
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index b346d14..3cc3032 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -146,8 +146,6 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#define NETSCAPE_HANG_BUG
-
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
@@ -159,6 +157,7 @@
 #include <openssl/dh.h>
 #include <openssl/ec.h>
 #include <openssl/ecdsa.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 #include <openssl/md5.h>
@@ -168,7 +167,7 @@
 #include <openssl/sha.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 #include "../crypto/internal.h"
 #include "../crypto/dh/internal.h"
 
@@ -179,7 +178,7 @@
 
 int ssl3_accept(SSL *s) {
   BUF_MEM *buf = NULL;
-  unsigned long alg_a;
+  uint32_t alg_a;
   void (*cb)(const SSL *ssl, int type, int val) = NULL;
   int ret = -1;
   int new_state, state, skip = 0;
@@ -228,11 +227,6 @@
         }
         s->init_num = 0;
 
-        if (!ssl3_setup_buffers(s)) {
-          ret = -1;
-          goto end;
-        }
-
         if (!s->s3->send_connection_binding &&
             !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) {
           /* Server attempting to renegotiate with client that doesn't support
@@ -244,7 +238,6 @@
           goto end;
         }
 
-        s->ctx->stats.sess_accept_renegotiate++;
         s->state = SSL3_ST_SW_HELLO_REQ_A;
         break;
 
@@ -278,6 +271,15 @@
           cb(s, SSL_CB_HANDSHAKE_START, 1);
         }
 
+        if ((s->version >> 8) != 3) {
+          /* TODO(davidben): Some consumers clear |s->version| to break the
+           * handshake in a callback. Remove this when they're using proper
+           * APIs. */
+          OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR);
+          ret = -1;
+          goto end;
+        }
+
         if (s->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
@@ -289,6 +291,13 @@
         }
         s->init_num = 0;
 
+        /* Enable a write buffer. This groups handshake messages within a flight
+         * into a single write. */
+        if (!ssl_init_wbio_buffer(s, 1)) {
+          ret = -1;
+          goto end;
+        }
+
         if (!ssl3_init_finished_mac(s)) {
           OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR);
           ret = -1;
@@ -296,22 +305,10 @@
         }
 
         if (!s->s3->have_version) {
-          /* This is the initial handshake. The record layer has not been
-           * initialized yet. Sniff for a V2ClientHello before reading a
-           * ClientHello normally. */
-          assert(s->s3->rbuf.buf == NULL);
-          assert(s->s3->wbuf.buf == NULL);
           s->state = SSL3_ST_SR_INITIAL_BYTES;
         } else {
-          /* Enable a write buffer. This groups handshake messages within a
-           * flight into a single write. */
-          if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) {
-            ret = -1;
-            goto end;
-          }
           s->state = SSL3_ST_SR_CLNT_HELLO_A;
         }
-        s->ctx->stats.sess_accept++;
         break;
 
       case SSL3_ST_SR_INITIAL_BYTES:
@@ -337,14 +334,6 @@
       case SSL3_ST_SR_CLNT_HELLO_D:
         s->shutdown = 0;
         ret = ssl3_get_client_hello(s);
-        if (ret == PENDING_SESSION) {
-          s->rwstate = SSL_PENDING_SESSION;
-          goto end;
-        }
-        if (ret == CERTIFICATE_SELECTION_PENDING) {
-          s->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
-          goto end;
-        }
         if (ret <= 0) {
           goto end;
         }
@@ -404,8 +393,9 @@
         if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) ||
             ((alg_a & SSL_aPSK) && s->psk_identity_hint)) {
           ret = ssl3_send_server_key_exchange(s);
-          if (ret <= 0)
+          if (ret <= 0) {
             goto end;
+          }
         } else {
           skip = 1;
         }
@@ -425,13 +415,6 @@
              * don't request cert during re-negotiation: */
             ((s->session->peer != NULL) &&
              (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) ||
-            /* never request cert in anonymous ciphersuites
-             * (see section "Certificate request" in SSL 3 drafts
-             * and in RFC 2246): */
-            ((s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
-             /* ... except when the application insists on verification
-              * (against the specs, but s3_clnt.c accepts this for SSL 3) */
-             !(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) ||
             /* With normal PSK Certificates and
              * Certificate Requests are omitted */
             (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) {
@@ -449,14 +432,7 @@
           if (ret <= 0) {
             goto end;
           }
-#ifndef NETSCAPE_HANG_BUG
           s->state = SSL3_ST_SW_SRVR_DONE_A;
-#else
-          /* ServerHelloDone was already sent in the
-           * previous record. */
-          s->state = SSL3_ST_SW_FLUSH;
-          s->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
-#endif
           s->init_num = 0;
         }
         break;
@@ -659,7 +635,7 @@
 
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
-        if (s->session->peer && s->ctx->retain_only_sha256_of_client_certs) {
+        if (s->ctx->retain_only_sha256_of_client_certs) {
           X509_free(s->session->peer);
           s->session->peer = NULL;
         }
@@ -671,8 +647,6 @@
 
           ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
 
-          s->ctx->stats.sess_accept_good++;
-
           if (cb != NULL) {
             cb(s, SSL_CB_HANDSHAKE_DONE, 1);
           }
@@ -698,9 +672,7 @@
 
 end:
   s->in_handshake--;
-  if (buf != NULL) {
-    BUF_MEM_free(buf);
-  }
+  BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(s, SSL_CB_ACCEPT_EXIT, ret);
   }
@@ -770,10 +742,12 @@
       p[5] == SSL3_MT_CLIENT_HELLO) {
     /* This is a ClientHello. Initialize the record layer with the already
      * consumed data and continue the handshake. */
-    if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) {
+    if (!ssl3_setup_read_buffer(s)) {
       return -1;
     }
     assert(s->rstate == SSL_ST_READ_HEADER);
+    /* There cannot have already been data in the record layer. */
+    assert(s->s3->rbuf.left == 0);
     memcpy(s->s3->rbuf.buf, p, s->s3->sniff_buffer_len);
     s->s3->rbuf.offset = 0;
     s->s3->rbuf.left = s->s3->sniff_buffer_len;
@@ -829,7 +803,10 @@
 
   /* The V2ClientHello without the length is incorporated into the Finished
    * hash. */
-  ssl3_finish_mac(s, CBS_data(&v2_client_hello), CBS_len(&v2_client_hello));
+  if (!ssl3_finish_mac(s, CBS_data(&v2_client_hello),
+                       CBS_len(&v2_client_hello))) {
+    return -1;
+  }
   if (s->msg_callback) {
     s->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello),
                     CBS_len(&v2_client_hello), s, s->msg_callback_arg);
@@ -913,11 +890,6 @@
   /* The handshake message header is 4 bytes. */
   s->s3->tmp.message_size = len - 4;
 
-  /* Initialize the record layer. */
-  if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) {
-    return -1;
-  }
-
   /* Drop the sniff buffer. */
   BUF_MEM_free(s->s3->sniff_buffer);
   s->s3->sniff_buffer = NULL;
@@ -928,7 +900,9 @@
 
 int ssl3_send_hello_request(SSL *s) {
   if (s->state == SSL3_ST_SW_HELLO_REQ_A) {
-    ssl_set_handshake_header(s, SSL3_MT_HELLO_REQUEST, 0);
+    if (!ssl_set_handshake_header(s, SSL3_MT_HELLO_REQUEST, 0)) {
+      return -1;
+    }
     s->state = SSL3_ST_SW_HELLO_REQ_B;
   }
 
@@ -956,31 +930,12 @@
       n = s->method->ssl_get_message(
           s, SSL3_ST_SR_CLNT_HELLO_A, SSL3_ST_SR_CLNT_HELLO_B,
           SSL3_MT_CLIENT_HELLO, SSL3_RT_MAX_PLAIN_LENGTH,
-          SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+          ssl_hash_message, &ok);
 
       if (!ok) {
         return n;
       }
 
-      /* If we require cookies and this ClientHello doesn't contain one, just
-       * return since we do not want to allocate any memory yet. So check
-       * cookie length... */
-      if (SSL_IS_DTLS(s) && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) {
-        uint8_t cookie_length;
-
-        CBS_init(&client_hello, s->init_msg, n);
-        if (!CBS_skip(&client_hello, 2 + SSL3_RANDOM_SIZE) ||
-            !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
-            !CBS_get_u8(&client_hello, &cookie_length)) {
-          al = SSL_AD_DECODE_ERROR;
-          OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR);
-          goto f_err;
-        }
-
-        if (cookie_length == 0) {
-          return 1;
-        }
-      }
       s->state = SSL3_ST_SR_CLNT_HELLO_C;
       /* fallthrough */
     case SSL3_ST_SR_CLNT_HELLO_C:
@@ -1006,7 +961,8 @@
         s->state = SSL3_ST_SR_CLNT_HELLO_D;
         switch (s->ctx->select_certificate_cb(&early_ctx)) {
           case 0:
-            return CERTIFICATE_SELECTION_PENDING;
+            s->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
+            goto err;
 
           case -1:
             /* Connection rejected. */
@@ -1053,27 +1009,6 @@
       OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR);
       goto f_err;
     }
-
-    /* Verify the cookie if appropriate option is set. */
-    if ((SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) && CBS_len(&cookie) > 0) {
-      if (s->ctx->app_verify_cookie_cb != NULL) {
-        if (s->ctx->app_verify_cookie_cb(s, CBS_data(&cookie),
-                                         CBS_len(&cookie)) == 0) {
-          al = SSL_AD_HANDSHAKE_FAILURE;
-          OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_COOKIE_MISMATCH);
-          goto f_err;
-        }
-        /* else cookie verification succeeded */
-      } else if (!CBS_mem_equal(&cookie, s->d1->cookie, s->d1->cookie_len)) {
-        /* default verification */
-        al = SSL_AD_HANDSHAKE_FAILURE;
-        OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_COOKIE_MISMATCH);
-        goto f_err;
-      }
-      /* Set to -2 so if successful we return 2 and don't send
-       * HelloVerifyRequest. */
-      ret = -2;
-    }
   }
 
   if (!s->s3->have_version) {
@@ -1118,7 +1053,7 @@
   } else {
     i = ssl_get_prev_session(s, &early_ctx);
     if (i == PENDING_SESSION) {
-      ret = PENDING_SESSION;
+      s->rwstate = SSL_PENDING_SESSION;
       goto err;
     } else if (i == -1) {
       goto err;
@@ -1136,7 +1071,16 @@
     }
   }
 
+  if (s->ctx->dos_protection_cb != NULL && s->ctx->dos_protection_cb(&early_ctx) == 0) {
+    /* Connection rejected for DOS reasons. */
+    al = SSL_AD_ACCESS_DENIED;
+    OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_CONNECTION_REJECTED);
+    goto f_err;
+  }
+
   if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) ||
+      CBS_len(&cipher_suites) == 0 ||
+      CBS_len(&cipher_suites) % 2 != 0 ||
       !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) ||
       CBS_len(&compression_methods) == 0) {
     al = SSL_AD_DECODE_ERROR;
@@ -1144,28 +1088,16 @@
     goto f_err;
   }
 
-  /* TODO(davidben): Per spec, cipher_suites can never be empty (specified at
-   * the ClientHello structure level). This logic allows it to be empty if
-   * resuming a session. Can we always require non-empty? If a client sends
-   * empty cipher_suites because it's resuming a session, it could always fail
-   * to resume a session, so it's unlikely to actually work. */
-  if (CBS_len(&cipher_suites) == 0 && CBS_len(&session_id) != 0) {
-    /* We need a cipher if we are not resuming a session. */
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_CIPHERS_SPECIFIED);
-    goto f_err;
-  }
-
   ciphers = ssl_bytes_to_cipher_list(s, &cipher_suites);
   if (ciphers == NULL) {
     goto err;
   }
 
   /* If it is a hit, check that the cipher is in the list. */
-  if (s->hit && CBS_len(&cipher_suites) > 0) {
+  if (s->hit) {
     size_t j;
     int found_cipher = 0;
-    unsigned long id = s->session->cipher->id;
+    uint32_t id = s->session->cipher->id;
 
     for (j = 0; j < sk_SSL_CIPHER_num(ciphers); j++) {
       c = sk_SSL_CIPHER_value(ciphers, j);
@@ -1269,9 +1201,7 @@
   }
 
 err:
-  if (ciphers != NULL) {
-    sk_SSL_CIPHER_free(ciphers);
-  }
+  sk_SSL_CIPHER_free(ciphers);
   return ret;
 }
 
@@ -1285,7 +1215,7 @@
     /* We only accept ChannelIDs on connections with ECDHE in order to avoid a
      * known attack while we fix ChannelID itself. */
     if (s->s3->tlsext_channel_id_valid &&
-        (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kEECDH) == 0) {
+        (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kECDHE) == 0) {
       s->s3->tlsext_channel_id_valid = 0;
     }
 
@@ -1354,7 +1284,9 @@
 
     /* do the header */
     l = (p - d);
-    ssl_set_handshake_header(s, SSL3_MT_SERVER_HELLO, l);
+    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_HELLO, l)) {
+      return -1;
+    }
     s->state = SSL3_ST_SW_SRVR_HELLO_B;
   }
 
@@ -1364,7 +1296,9 @@
 
 int ssl3_send_server_done(SSL *s) {
   if (s->state == SSL3_ST_SW_SRVR_DONE_A) {
-    ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0);
+    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0)) {
+      return -1;
+    }
     s->state = SSL3_ST_SW_SRVR_DONE_B;
   }
 
@@ -1374,7 +1308,7 @@
 
 int ssl3_send_server_key_exchange(SSL *s) {
   DH *dh = NULL, *dhp;
-  EC_KEY *ecdh = NULL, *ecdhp;
+  EC_KEY *ecdh = NULL;
   uint8_t *encodedPoint = NULL;
   int encodedlen = 0;
   uint16_t curve_id = 0;
@@ -1384,8 +1318,8 @@
   EVP_PKEY *pkey;
   uint8_t *p, *d;
   int al, i;
-  unsigned long alg_k;
-  unsigned long alg_a;
+  uint32_t alg_k;
+  uint32_t alg_a;
   int n;
   CERT *cert;
   BIGNUM *r[4];
@@ -1414,7 +1348,7 @@
       n += 2 + psk_identity_hint_len;
     }
 
-    if (alg_k & SSL_kEDH) {
+    if (alg_k & SSL_kDHE) {
       dhp = cert->dh_tmp;
       if (dhp == NULL && s->cert->dh_tmp_cb != NULL) {
         dhp = s->cert->dh_tmp_cb(s, 0, 1024);
@@ -1431,46 +1365,37 @@
                           ERR_R_INTERNAL_ERROR);
         goto err;
       }
-
       dh = DHparams_dup(dhp);
       if (dh == NULL) {
         OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB);
         goto err;
       }
-
       s->s3->tmp.dh = dh;
-      if (dhp->pub_key == NULL || dhp->priv_key == NULL ||
-          (s->options & SSL_OP_SINGLE_DH_USE)) {
-        if (!DH_generate_key(dh)) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB);
-          goto err;
-        }
-      } else {
-        dh->pub_key = BN_dup(dhp->pub_key);
-        dh->priv_key = BN_dup(dhp->priv_key);
-        if (dh->pub_key == NULL || dh->priv_key == NULL) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB);
-          goto err;
-        }
+
+      if (!DH_generate_key(dh)) {
+        OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB);
+        goto err;
       }
 
       r[0] = dh->p;
       r[1] = dh->g;
       r[2] = dh->pub_key;
-    } else if (alg_k & SSL_kEECDH) {
-      const EC_GROUP *group;
-
-      ecdhp = cert->ecdh_tmp;
-      if (s->cert->ecdh_tmp_auto) {
-        /* Get NID of appropriate shared curve */
-        int nid = tls1_get_shared_curve(s);
-        if (nid != NID_undef) {
-          ecdhp = EC_KEY_new_by_curve_name(nid);
+    } else if (alg_k & SSL_kECDHE) {
+      /* Determine the curve to use. */
+      int nid = NID_undef;
+      if (cert->ecdh_nid != NID_undef) {
+        nid = cert->ecdh_nid;
+      } else if (cert->ecdh_tmp_cb != NULL) {
+        /* Note: |ecdh_tmp_cb| does NOT pass ownership of the result
+         * to the caller. */
+        EC_KEY *template = s->cert->ecdh_tmp_cb(s, 0, 1024);
+        if (template != NULL && EC_KEY_get0_group(template) != NULL) {
+          nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(template));
         }
-      } else if (ecdhp == NULL && s->cert->ecdh_tmp_cb) {
-        ecdhp = s->cert->ecdh_tmp_cb(s, 0, 1024);
+      } else {
+        nid = tls1_get_shared_curve(s);
       }
-      if (ecdhp == NULL) {
+      if (nid == NID_undef) {
         al = SSL_AD_HANDSHAKE_FAILURE;
         OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange,
                           SSL_R_MISSING_TMP_ECDH_KEY);
@@ -1482,42 +1407,19 @@
                           ERR_R_INTERNAL_ERROR);
         goto err;
       }
-
-      /* Duplicate the ECDH structure. */
-      if (ecdhp == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
+      ecdh = EC_KEY_new_by_curve_name(nid);
+      if (ecdh == NULL) {
         goto err;
       }
-
-      if (s->cert->ecdh_tmp_auto) {
-        ecdh = ecdhp;
-      } else {
-        ecdh = EC_KEY_dup(ecdhp);
-        if (ecdh == NULL) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
-          goto err;
-        }
-      }
-
       s->s3->tmp.ecdh = ecdh;
-      if (EC_KEY_get0_public_key(ecdh) == NULL ||
-          EC_KEY_get0_private_key(ecdh) == NULL ||
-          (s->options & SSL_OP_SINGLE_ECDH_USE)) {
-        if (!EC_KEY_generate_key(ecdh)) {
-          OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
-          goto err;
-        }
-      }
 
-      group = EC_KEY_get0_group(ecdh);
-      if (group == NULL ||
-          EC_KEY_get0_public_key(ecdh) == NULL ||
-          EC_KEY_get0_private_key(ecdh) == NULL) {
+      if (!EC_KEY_generate_key(ecdh)) {
         OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB);
         goto err;
       }
 
       /* We only support ephemeral ECDH keys over named (not generic) curves. */
+      const EC_GROUP *group = EC_KEY_get0_group(ecdh);
       if (!tls1_ec_nid2curve_id(&curve_id, EC_GROUP_get_curve_name(group))) {
         OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange,
                           SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
@@ -1597,7 +1499,7 @@
       p += nr[i];
     }
 
-    /* Note: ECDHE PSK ciphersuites use SSL_kEECDH and SSL_aPSK. When one of
+    /* Note: ECDHE PSK ciphersuites use SSL_kECDHE and SSL_aPSK. When one of
      * them is used, the server key exchange record needs to have both the
      * psk_identity_hint and the ServerECDHParams. */
     if (alg_a & SSL_aPSK) {
@@ -1609,7 +1511,7 @@
       }
     }
 
-    if (alg_k & SSL_kEECDH) {
+    if (alg_k & SSL_kECDHE) {
       /* We only support named (not generic) curves. In this situation, the
        * serverKeyExchange message has:
        * [1 byte CurveType], [2 byte CurveName]
@@ -1667,7 +1569,9 @@
       }
     }
 
-    ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n);
+    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n)) {
+      goto err;
+    }
   }
 
   s->state = SSL3_ST_SW_KEY_EXCH_B;
@@ -1677,9 +1581,7 @@
 f_err:
   ssl3_send_alert(s, SSL3_AL_FATAL, al);
 err:
-  if (encodedPoint != NULL) {
-    OPENSSL_free(encodedPoint);
-  }
+  OPENSSL_free(encodedPoint);
   BN_CTX_free(bn_ctx);
   EVP_MD_CTX_cleanup(&md_ctx);
   return -1;
@@ -1740,27 +1642,9 @@
     p = ssl_handshake_start(s) + off;
     s2n(nl, p);
 
-    ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_REQUEST, n);
-
-#ifdef NETSCAPE_HANG_BUG
-    if (!SSL_IS_DTLS(s)) {
-      /* Prepare a ServerHelloDone in the same record. This is to workaround a
-       * hang in Netscape. */
-      if (!BUF_MEM_grow_clean(buf, s->init_num + 4)) {
-        OPENSSL_PUT_ERROR(SSL, ssl3_send_certificate_request, ERR_R_BUF_LIB);
-        goto err;
-      }
-      p = (uint8_t *)s->init_buf->data + s->init_num;
-      /* do the header */
-      *(p++) = SSL3_MT_SERVER_DONE;
-      *(p++) = 0;
-      *(p++) = 0;
-      *(p++) = 0;
-      s->init_num += 4;
-      ssl3_finish_mac(s, p - 4, 4);
+    if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_REQUEST, n)) {
+      goto err;
     }
-#endif
-
     s->state = SSL3_ST_SW_CERT_REQ_B;
   }
 
@@ -1775,8 +1659,8 @@
   int al, ok;
   long n;
   CBS client_key_exchange;
-  unsigned long alg_k;
-  unsigned long alg_a;
+  uint32_t alg_k;
+  uint32_t alg_a;
   uint8_t *premaster_secret = NULL;
   size_t premaster_secret_len = 0;
   RSA *rsa = NULL;
@@ -1795,7 +1679,7 @@
   n = s->method->ssl_get_message(s, SSL3_ST_SR_KEY_EXCH_A,
                                  SSL3_ST_SR_KEY_EXCH_B,
                                  SSL3_MT_CLIENT_KEY_EXCHANGE, 2048, /* ??? */
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1983,7 +1867,7 @@
     }
 
     premaster_secret_len = sizeof(rand_premaster_secret);
-  } else if (alg_k & SSL_kEDH) {
+  } else if (alg_k & SSL_kDHE) {
     CBS dh_Yc;
     int dh_len;
 
@@ -2014,6 +1898,7 @@
     if (premaster_secret == NULL) {
       OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange,
                         ERR_R_MALLOC_FAILURE);
+      BN_clear_free(pub);
       goto err;
     }
 
@@ -2030,7 +1915,7 @@
     pub = NULL;
 
     premaster_secret_len = dh_len;
-  } else if (alg_k & SSL_kEECDH) {
+  } else if (alg_k & SSL_kECDHE) {
     int field_size = 0, ecdh_len;
     const EC_KEY *tkey;
     const EC_GROUP *group;
@@ -2191,14 +2076,10 @@
     }
     OPENSSL_free(premaster_secret);
   }
-  if (decrypt_buf) {
-    OPENSSL_free(decrypt_buf);
-  }
+  OPENSSL_free(decrypt_buf);
   EVP_PKEY_free(clnt_pub_pkey);
   EC_POINT_free(clnt_ecpoint);
-  if (srvr_ecdh != NULL) {
-    EC_KEY_free(srvr_ecdh);
-  }
+  EC_KEY_free(srvr_ecdh);
   BN_CTX_free(bn_ctx);
 
   return -1;
@@ -2229,7 +2110,7 @@
   n = s->method->ssl_get_message(
       s, SSL3_ST_SR_CERT_VRFY_A, SSL3_ST_SR_CERT_VRFY_B,
       SSL3_MT_CERTIFICATE_VERIFY, SSL3_RT_MAX_PLAIN_LENGTH,
-      SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok);
+      ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -2237,6 +2118,9 @@
 
   /* Filter out unsupported certificate types. */
   pkey = X509_get_pubkey(peer);
+  if (pkey == NULL) {
+    goto err;
+  }
   if (!(X509_certificate_type(peer, pkey) & EVP_PKT_SIGN) ||
       (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_EC)) {
     al = SSL_AD_UNSUPPORTED_CERTIFICATE;
@@ -2264,7 +2148,9 @@
       !ssl3_digest_cached_records(s, free_handshake_buffer)) {
     goto err;
   }
-  ssl3_hash_current_message(s);
+  if (!ssl3_hash_current_message(s)) {
+    goto err;
+  }
 
   /* Parse and verify the signature. */
   if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) ||
@@ -2311,8 +2197,7 @@
   int is_first_certificate = 1;
 
   n = s->method->ssl_get_message(s, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B, -1,
-                                 s->max_cert_list, SSL_GET_MESSAGE_HASH_MESSAGE,
-                                 &ok);
+                                 (long)s->max_cert_list, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -2433,11 +2318,7 @@
     }
   }
 
-  if (s->session->peer != NULL) {
-    /* This should not be needed */
-    X509_free(s->session->peer);
-  }
-
+  X509_free(s->session->peer);
   s->session->peer = sk_X509_shift(sk);
   s->session->verify_result = s->verify_result;
 
@@ -2450,9 +2331,7 @@
       goto err;
     }
   }
-  if (s->session->sess_cert->cert_chain != NULL) {
-    sk_X509_pop_free(s->session->sess_cert->cert_chain, X509_free);
-  }
+  sk_X509_pop_free(s->session->sess_cert->cert_chain, X509_free);
   s->session->sess_cert->cert_chain = sk;
   /* Inconsistency alert: cert_chain does *not* include the peer's own
    * certificate, while we do include it in s3_clnt.c */
@@ -2467,12 +2346,8 @@
   }
 
 err:
-  if (x != NULL) {
-    X509_free(x);
-  }
-  if (sk != NULL) {
-    sk_X509_pop_free(sk, X509_free);
-  }
+  X509_free(x);
+  sk_X509_pop_free(sk, X509_free);
   return ret;
 }
 
@@ -2487,7 +2362,9 @@
       return 0;
     }
 
-    ssl3_output_cert_chain(s, cpk);
+    if (!ssl3_output_cert_chain(s, cpk)) {
+      return 0;
+    }
     s->state = SSL3_ST_SW_CERT_B;
   }
 
@@ -2497,14 +2374,19 @@
 
 /* send a new session ticket (not necessarily for a new session) */
 int ssl3_send_new_session_ticket(SSL *s) {
+  int ret = -1;
+  uint8_t *session = NULL;
+  size_t session_len;
+  EVP_CIPHER_CTX ctx;
+  HMAC_CTX hctx;
+
+  EVP_CIPHER_CTX_init(&ctx);
+  HMAC_CTX_init(&hctx);
+
   if (s->state == SSL3_ST_SW_SESSION_TICKET_A) {
-    uint8_t *session;
-    size_t session_len;
     uint8_t *p, *macstart;
     int len;
     unsigned int hlen;
-    EVP_CIPHER_CTX ctx;
-    HMAC_CTX hctx;
     SSL_CTX *tctx = s->initial_ctx;
     uint8_t iv[EVP_MAX_IV_LENGTH];
     uint8_t key_name[16];
@@ -2515,7 +2397,7 @@
 
     /* Serialize the SSL_SESSION to be encoded into the ticket. */
     if (!SSL_SESSION_to_bytes_for_ticket(s->session, &session, &session_len)) {
-      return -1;
+      goto err;
     }
 
     /* If the session is too long, emit a dummy value rather than abort the
@@ -2525,6 +2407,7 @@
       const size_t placeholder_len = strlen(kTicketPlaceholder);
 
       OPENSSL_free(session);
+      session = NULL;
 
       p = ssl_handshake_start(s);
       /* Emit ticket_lifetime_hint. */
@@ -2535,7 +2418,9 @@
       p += placeholder_len;
 
       len = p - ssl_handshake_start(s);
-      ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len);
+      if (!ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len)) {
+        goto err;
+      }
       s->state = SSL3_ST_SW_SESSION_TICKET_B;
       return ssl_do_write(s);
     }
@@ -2545,20 +2430,15 @@
      * max_ticket_overhead + * session_length */
     if (!BUF_MEM_grow(s->init_buf, SSL_HM_HEADER_LENGTH(s) + 6 +
                                        max_ticket_overhead + session_len)) {
-      OPENSSL_free(session);
-      return -1;
+      goto err;
     }
     p = ssl_handshake_start(s);
-    EVP_CIPHER_CTX_init(&ctx);
-    HMAC_CTX_init(&hctx);
     /* Initialize HMAC and cipher contexts. If callback present it does all the
      * work otherwise use generated values from parent ctx. */
     if (tctx->tlsext_ticket_key_cb) {
-      if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx, &hctx, 1) < 0) {
-        OPENSSL_free(session);
-        EVP_CIPHER_CTX_cleanup(&ctx);
-        HMAC_CTX_cleanup(&hctx);
-        return -1;
+      if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx, &hctx,
+                                     1 /* encrypt */) < 0) {
+        goto err;
       }
     } else {
       if (!RAND_bytes(iv, 16) ||
@@ -2566,10 +2446,7 @@
                               tctx->tlsext_tick_aes_key, iv) ||
           !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
                         NULL)) {
-        OPENSSL_free(session);
-        EVP_CIPHER_CTX_cleanup(&ctx);
-        HMAC_CTX_cleanup(&hctx);
-        return -1;
+        goto err;
       }
       memcpy(key_name, tctx->tlsext_tick_key_name, 16);
     }
@@ -2589,15 +2466,19 @@
     memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
     p += EVP_CIPHER_CTX_iv_length(&ctx);
     /* Encrypt session data */
-    EVP_EncryptUpdate(&ctx, p, &len, session, session_len);
+    if (!EVP_EncryptUpdate(&ctx, p, &len, session, session_len)) {
+      goto err;
+    }
     p += len;
-    EVP_EncryptFinal_ex(&ctx, p, &len);
+    if (!EVP_EncryptFinal_ex(&ctx, p, &len)) {
+      goto err;
+    }
     p += len;
-    EVP_CIPHER_CTX_cleanup(&ctx);
 
-    HMAC_Update(&hctx, macstart, p - macstart);
-    HMAC_Final(&hctx, p, &hlen);
-    HMAC_CTX_cleanup(&hctx);
+    if (!HMAC_Update(&hctx, macstart, p - macstart) ||
+        !HMAC_Final(&hctx, p, &hlen)) {
+      goto err;
+    }
 
     p += hlen;
     /* Now write out lengths: p points to end of data written */
@@ -2606,13 +2487,20 @@
     /* Skip ticket lifetime hint */
     p = ssl_handshake_start(s) + 4;
     s2n(len - 6, p);
-    ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len);
+    if (!ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len)) {
+      goto err;
+    }
     s->state = SSL3_ST_SW_SESSION_TICKET_B;
-    OPENSSL_free(session);
   }
 
   /* SSL3_ST_SW_SESSION_TICKET_B */
-  return ssl_do_write(s);
+  ret = ssl_do_write(s);
+
+err:
+  OPENSSL_free(session);
+  EVP_CIPHER_CTX_cleanup(&ctx);
+  HMAC_CTX_cleanup(&hctx);
+  return ret;
 }
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
@@ -2633,7 +2521,7 @@
   n = s->method->ssl_get_message(s, SSL3_ST_SR_NEXT_PROTO_A,
                                  SSL3_ST_SR_NEXT_PROTO_B, SSL3_MT_NEXT_PROTO,
                                  514, /* See the payload format below */
-                                 SSL_GET_MESSAGE_HASH_MESSAGE, &ok);
+                                 ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -2688,7 +2576,7 @@
   n = s->method->ssl_get_message(
       s, SSL3_ST_SR_CHANNEL_ID_A, SSL3_ST_SR_CHANNEL_ID_B,
       SSL3_MT_ENCRYPTED_EXTENSIONS, 2 + 2 + TLSEXT_CHANNEL_ID_SIZE,
-      SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok);
+      ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -2707,7 +2595,9 @@
   EVP_MD_CTX_cleanup(&md_ctx);
   assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
 
-  ssl3_hash_current_message(s);
+  if (!ssl3_hash_current_message(s)) {
+    return -1;
+  }
 
   /* s->state doesn't reflect whether ChangeCipherSpec has been received in
    * this handshake, but s->s3->change_cipher_spec does (will be reset by
@@ -2757,6 +2647,9 @@
   BN_init(&y);
   sig.r = BN_new();
   sig.s = BN_new();
+  if (sig.r == NULL || sig.s == NULL) {
+    goto err;
+  }
 
   p = CBS_data(&extension);
   if (BN_bin2bn(p + 0, 32, &x) == NULL ||
@@ -2794,14 +2687,8 @@
   BN_free(&y);
   BN_free(sig.r);
   BN_free(sig.s);
-  if (key) {
-    EC_KEY_free(key);
-  }
-  if (point) {
-    EC_POINT_free(point);
-  }
-  if (p256) {
-    EC_GROUP_free(p256);
-  }
+  EC_KEY_free(key);
+  EC_POINT_free(point);
+  EC_GROUP_free(p256);
   return ret;
 }
diff --git a/src/ssl/ssl_algs.c b/src/ssl/ssl_algs.c
index 6ec88bf..fda39a5 100644
--- a/src/ssl/ssl_algs.c
+++ b/src/ssl/ssl_algs.c
@@ -54,18 +54,13 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 #include <openssl/crypto.h>
 
-extern const ERR_STRING_DATA SSL_error_string_data[];
-
 int SSL_library_init(void) {
   CRYPTO_library_init();
-  ERR_load_crypto_strings();
-  ERR_load_strings(SSL_error_string_data);
   return 1;
 }
 
-void SSL_load_error_strings(void) {
-}
+void SSL_load_error_strings(void) {}
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index d39da87..eb0c725 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -85,9 +85,10 @@
 
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
+#include <openssl/mem.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 /* An SSL_SESSION is serialized as the following ASN.1 structure:
@@ -177,14 +178,14 @@
                      for_ticket ? 0 : in->session_id_length) ||
       !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) ||
       !CBB_add_bytes(&child, in->master_key, in->master_key_length)) {
-    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
     goto err;
   }
 
   if (in->time != 0) {
     if (!CBB_add_asn1(&session, &child, kTimeTag) ||
         !CBB_add_asn1_uint64(&child, in->time)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -192,7 +193,7 @@
   if (in->timeout != 0) {
     if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
         !CBB_add_asn1_uint64(&child, in->timeout)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -207,7 +208,7 @@
     }
     if (!CBB_add_asn1(&session, &child, kPeerTag) ||
         !CBB_add_space(&child, &buf, len)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
     if (buf != NULL && i2d_X509(in->peer, &buf) < 0) {
@@ -220,14 +221,14 @@
   if (!CBB_add_asn1(&session, &child, kSessionIDContextTag) ||
       !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
       !CBB_add_bytes(&child2, in->sid_ctx, in->sid_ctx_length)) {
-    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
     goto err;
   }
 
   if (in->verify_result != X509_V_OK) {
     if (!CBB_add_asn1(&session, &child, kVerifyResultTag) ||
         !CBB_add_asn1_uint64(&child, in->verify_result)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -237,7 +238,7 @@
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, (const uint8_t *)in->tlsext_hostname,
                        strlen(in->tlsext_hostname))) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -247,7 +248,7 @@
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity,
                        strlen(in->psk_identity))) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -255,7 +256,7 @@
   if (in->tlsext_tick_lifetime_hint > 0) {
     if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) ||
         !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -264,7 +265,7 @@
     if (!CBB_add_asn1(&session, &child, kTicketTag) ||
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -273,7 +274,7 @@
     if (!CBB_add_asn1(&session, &child, kPeerSHA256Tag) ||
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, in->peer_sha256, sizeof(in->peer_sha256))) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -283,7 +284,7 @@
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, in->original_handshake_hash,
                        in->original_handshake_hash_len)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -293,7 +294,7 @@
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, in->tlsext_signed_cert_timestamp_list,
                        in->tlsext_signed_cert_timestamp_list_length)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -302,7 +303,7 @@
     if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) ||
         !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_bytes(&child2, in->ocsp_response, in->ocsp_response_length)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
@@ -311,13 +312,13 @@
     if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) ||
         !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) ||
         !CBB_add_u8(&child2, 0xff)) {
-      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
       goto err;
     }
   }
 
   if (!CBB_finish(&cbb, out_data, out_len)) {
-    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE);
     goto err;
   }
   return 1;
@@ -381,7 +382,7 @@
       OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE);
       return 0;
     }
-  } else if (*out) {
+  } else {
     OPENSSL_free(*out);
     *out = NULL;
   }
@@ -409,7 +410,7 @@
 }
 
 SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) {
-  SSL_SESSION *ret = NULL;
+  SSL_SESSION *ret, *allocated = NULL;
   CBS cbs, session, cipher, session_id, master_key;
   CBS peer, sid_ctx, peer_sha256, original_handshake_hash;
   int has_peer, has_peer_sha256, extended_master_secret;
@@ -419,8 +420,8 @@
   if (a && *a) {
     ret = *a;
   } else {
-    ret = SSL_SESSION_new();
-    if (ret == NULL) {
+    ret = allocated = SSL_SESSION_new();
+    if (allocated == NULL) {
       goto err;
     }
   }
@@ -525,10 +526,8 @@
   ret->time = session_time;
   ret->timeout = timeout;
 
-  if (ret->peer != NULL) {
-    X509_free(ret->peer);
-    ret->peer = NULL;
-  }
+  X509_free(ret->peer);
+  ret->peer = NULL;
   if (has_peer) {
     const uint8_t *ptr;
     ptr = CBS_data(&peer);
@@ -584,8 +583,6 @@
   return ret;
 
 err:
-  if (a && *a != ret) {
-    SSL_SESSION_free(ret);
-  }
+  SSL_SESSION_free(allocated);
   return NULL;
 }
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 624c41a..770912b 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -112,7 +112,9 @@
  * ECC cipher suite support in OpenSSL originally developed by
  * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */
 
+#include <errno.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <openssl/bio.h>
 #include <openssl/bn.h>
@@ -126,7 +128,7 @@
 
 #include "../crypto/dh/internal.h"
 #include "../crypto/directory.h"
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 int SSL_get_ex_data_X509_STORE_CTX_idx(void) {
@@ -178,7 +180,6 @@
     OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
-
   memset(ret, 0, sizeof(CERT));
 
   ret->key = &ret->pkeys[cert->key - &cert->pkeys[0]];
@@ -213,15 +214,8 @@
   }
   ret->dh_tmp_cb = cert->dh_tmp_cb;
 
-  if (cert->ecdh_tmp) {
-    ret->ecdh_tmp = EC_KEY_dup(cert->ecdh_tmp);
-    if (ret->ecdh_tmp == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_EC_LIB);
-      goto err;
-    }
-  }
+  ret->ecdh_nid = cert->ecdh_nid;
   ret->ecdh_tmp_cb = cert->ecdh_tmp_cb;
-  ret->ecdh_tmp_auto = cert->ecdh_tmp_auto;
 
   for (i = 0; i < SSL_PKEY_NUM; i++) {
     CERT_PKEY *cpk = cert->pkeys + i;
@@ -231,7 +225,7 @@
     }
 
     if (cpk->privatekey != NULL) {
-      rpk->privatekey = EVP_PKEY_dup(cpk->privatekey);
+      rpk->privatekey = EVP_PKEY_up_ref(cpk->privatekey);
     }
 
     if (cpk->chain) {
@@ -243,34 +237,24 @@
     }
   }
 
-  /* Peer sigalgs set to NULL as we get these from handshake too */
-  ret->peer_sigalgs = NULL;
-  ret->peer_sigalgslen = 0;
-  /* Configured sigalgs however we copy across */
-
+  /* Copy over signature algorithm configuration. */
   if (cert->conf_sigalgs) {
-    ret->conf_sigalgs = OPENSSL_malloc(cert->conf_sigalgslen);
+    ret->conf_sigalgs = BUF_memdup(cert->conf_sigalgs, cert->conf_sigalgslen);
     if (!ret->conf_sigalgs) {
       goto err;
     }
-    memcpy(ret->conf_sigalgs, cert->conf_sigalgs, cert->conf_sigalgslen);
     ret->conf_sigalgslen = cert->conf_sigalgslen;
-  } else {
-    ret->conf_sigalgs = NULL;
   }
 
   if (cert->client_sigalgs) {
-    ret->client_sigalgs = OPENSSL_malloc(cert->client_sigalgslen);
+    ret->client_sigalgs = BUF_memdup(cert->client_sigalgs,
+                                     cert->client_sigalgslen);
     if (!ret->client_sigalgs) {
       goto err;
     }
-    memcpy(ret->client_sigalgs, cert->client_sigalgs, cert->client_sigalgslen);
     ret->client_sigalgslen = cert->client_sigalgslen;
-  } else {
-    ret->client_sigalgs = NULL;
   }
-  /* Shared sigalgs also NULL */
-  ret->shared_sigalgs = NULL;
+
   /* Copy any custom client certificate types */
   if (cert->client_certificate_types) {
     ret->client_certificate_types = BUF_memdup(
@@ -281,8 +265,6 @@
     ret->num_client_certificate_types = cert->num_client_certificate_types;
   }
 
-  ret->cert_flags = cert->cert_flags;
-
   ret->cert_cb = cert->cert_cb;
   ret->cert_cb_arg = cert->cert_cb_arg;
 
@@ -296,8 +278,6 @@
     ret->chain_store = cert->chain_store;
   }
 
-  ret->ciphers_raw = NULL;
-
   return ret;
 
 err:
@@ -334,79 +314,32 @@
     return;
   }
 
-  if (c->dh_tmp) {
-    DH_free(c->dh_tmp);
-  }
-  if (c->ecdh_tmp) {
-    EC_KEY_free(c->ecdh_tmp);
-  }
+  DH_free(c->dh_tmp);
 
   ssl_cert_clear_certs(c);
-  if (c->peer_sigalgs) {
-    OPENSSL_free(c->peer_sigalgs);
-  }
-  if (c->conf_sigalgs) {
-    OPENSSL_free(c->conf_sigalgs);
-  }
-  if (c->client_sigalgs) {
-    OPENSSL_free(c->client_sigalgs);
-  }
-  if (c->shared_sigalgs) {
-    OPENSSL_free(c->shared_sigalgs);
-  }
-  if (c->client_certificate_types) {
-    OPENSSL_free(c->client_certificate_types);
-  }
-  if (c->verify_store) {
-    X509_STORE_free(c->verify_store);
-  }
-  if (c->chain_store) {
-    X509_STORE_free(c->chain_store);
-  }
-  if (c->ciphers_raw) {
-    OPENSSL_free(c->ciphers_raw);
-  }
+  OPENSSL_free(c->peer_sigalgs);
+  OPENSSL_free(c->conf_sigalgs);
+  OPENSSL_free(c->client_sigalgs);
+  OPENSSL_free(c->shared_sigalgs);
+  OPENSSL_free(c->client_certificate_types);
+  X509_STORE_free(c->verify_store);
+  X509_STORE_free(c->chain_store);
 
   OPENSSL_free(c);
 }
 
-int ssl_cert_inst(CERT **o) {
-  /* Create a CERT if there isn't already one (which cannot really happen, as
-   * it is initially created in SSL_CTX_new; but the earlier code usually
-   * allows for that one being non-existant, so we follow that behaviour, as it
-   * might turn out that there actually is a reason for it -- but I'm not sure
-   * that *all* of the existing code could cope with s->cert being NULL,
-   * otherwise we could do without the initialization in SSL_CTX_new). */
-
-  if (o == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ssl_cert_inst, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-  if (*o == NULL) {
-    *o = ssl_cert_new();
-    if (*o == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ssl_cert_new, ERR_R_MALLOC_FAILURE);
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) * chain) {
+int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain) {
   CERT_PKEY *cpk = c->key;
   if (!cpk) {
     return 0;
   }
-  if (cpk->chain) {
-    sk_X509_pop_free(cpk->chain, X509_free);
-  }
+  sk_X509_pop_free(cpk->chain, X509_free);
   cpk->chain = chain;
   return 1;
 }
 
-int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) * chain) {
-  STACK_OF(X509) * dchain;
+int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain) {
+  STACK_OF(X509) *dchain;
   if (!chain) {
     return ssl_cert_set0_chain(c, NULL);
   }
@@ -499,22 +432,14 @@
     return;
   }
 
-  if (sc->cert_chain != NULL) {
-    sk_X509_pop_free(sc->cert_chain, X509_free);
-  }
+  sk_X509_pop_free(sc->cert_chain, X509_free);
 
   for (i = 0; i < SSL_PKEY_NUM; i++) {
-    if (sc->peer_pkeys[i].x509 != NULL) {
-      X509_free(sc->peer_pkeys[i].x509);
-    }
+    X509_free(sc->peer_pkeys[i].x509);
   }
 
-  if (sc->peer_dh_tmp != NULL) {
-    DH_free(sc->peer_dh_tmp);
-  }
-  if (sc->peer_ecdh_tmp != NULL) {
-    EC_KEY_free(sc->peer_ecdh_tmp);
-  }
+  DH_free(sc->peer_dh_tmp);
+  EC_KEY_free(sc->peer_ecdh_tmp);
 
   OPENSSL_free(sc);
 }
@@ -524,7 +449,7 @@
   return 1;
 }
 
-int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) * sk) {
+int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) {
   X509 *x;
   int i;
   X509_STORE *verify_store;
@@ -571,18 +496,15 @@
   return i;
 }
 
-static void set_client_CA_list(STACK_OF(X509_NAME) * *ca_list,
-                               STACK_OF(X509_NAME) * name_list) {
-  if (*ca_list != NULL) {
-    sk_X509_NAME_pop_free(*ca_list, X509_NAME_free);
-  }
-
+static void set_client_CA_list(STACK_OF(X509_NAME) **ca_list,
+                               STACK_OF(X509_NAME) *name_list) {
+  sk_X509_NAME_pop_free(*ca_list, X509_NAME_free);
   *ca_list = name_list;
 }
 
-STACK_OF(X509_NAME) * SSL_dup_CA_list(STACK_OF(X509_NAME) * sk) {
+STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *sk) {
   size_t i;
-  STACK_OF(X509_NAME) * ret;
+  STACK_OF(X509_NAME) *ret;
   X509_NAME *name;
 
   ret = sk_X509_NAME_new_null();
@@ -597,19 +519,19 @@
   return ret;
 }
 
-void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) * name_list) {
+void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list) {
   set_client_CA_list(&(s->client_CA), name_list);
 }
 
-void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) * name_list) {
+void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) {
   set_client_CA_list(&(ctx->client_CA), name_list);
 }
 
-STACK_OF(X509_NAME) * SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
+STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
   return ctx->client_CA;
 }
 
-STACK_OF(X509_NAME) * SSL_get_client_CA_list(const SSL *s) {
+STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s) {
   if (s->server) {
     if (s->client_CA != NULL) {
       return s->client_CA;
@@ -625,7 +547,7 @@
   }
 }
 
-static int add_client_CA(STACK_OF(X509_NAME) * *sk, X509 *x) {
+static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x) {
   X509_NAME *name;
 
   if (x == NULL) {
@@ -670,7 +592,7 @@
  *
  * \param file the file containing one or more certs.
  * \return a ::STACK containing the certs. */
-STACK_OF(X509_NAME) * SSL_load_client_CA_file(const char *file) {
+STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) {
   BIO *in;
   X509 *x = NULL;
   X509_NAME *xn = NULL;
@@ -719,21 +641,13 @@
 
   if (0) {
   err:
-    if (ret != NULL) {
-      sk_X509_NAME_pop_free(ret, X509_NAME_free);
-    }
+    sk_X509_NAME_pop_free(ret, X509_NAME_free);
     ret = NULL;
   }
 
-  if (sk != NULL) {
-    sk_X509_NAME_free(sk);
-  }
-  if (in != NULL) {
-    BIO_free(in);
-  }
-  if (x != NULL) {
-    X509_free(x);
-  }
+  sk_X509_NAME_free(sk);
+  BIO_free(in);
+  X509_free(x);
   if (ret != NULL) {
     ERR_clear_error();
   }
@@ -747,7 +661,7 @@
  *     already in the stack will be added.
  * \return 1 for success, 0 for failure. Note that in the case of failure some
  *     certs may have been added to \c stack. */
-int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) * stack,
+int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
                                         const char *file) {
   BIO *in;
   X509 *x = NULL;
@@ -794,12 +708,8 @@
     ret = 0;
   }
 
-  if (in != NULL) {
-    BIO_free(in);
-  }
-  if (x != NULL) {
-    X509_free(x);
-  }
+  BIO_free(in);
+  X509_free(x);
 
   (void) sk_X509_NAME_set_cmp_func(stack, oldcmp);
 
@@ -815,7 +725,7 @@
  *     be included.
  * \return 1 for success, 0 for failure. Note that in the case of failure some
  *     certs may have been added to \c stack. */
-int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) * stack,
+int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
                                        const char *dir) {
   OPENSSL_DIR_CTX *d = NULL;
   const char *filename;
@@ -842,7 +752,7 @@
   }
 
   if (errno) {
-    OPENSSL_PUT_ERROR(SSL, SSL_add_file_cert_subjects_to_stack, ERR_R_SYS_LIB);
+    OPENSSL_PUT_ERROR(SSL, SSL_add_dir_cert_subjects_to_stack, ERR_R_SYS_LIB);
     ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')");
     goto err;
   }
@@ -881,12 +791,13 @@
   int no_chain = 0;
   size_t i;
 
-  X509 *x = NULL;
-  STACK_OF(X509) * extra_certs;
+  X509 *x = cpk->x509;
+  STACK_OF(X509) *extra_certs;
   X509_STORE *chain_store;
 
-  if (cpk) {
-    x = cpk->x509;
+  if (x == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, SSL_R_NO_CERTIFICATE_SET);
+    return 0;
   }
 
   if (s->cert->chain_store) {
@@ -906,44 +817,36 @@
     no_chain = 1;
   }
 
-  /* TLSv1 sends a chain with nothing in it, instead of an alert. */
-  if (!BUF_MEM_grow_clean(buf, 10)) {
-    OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_BUF_LIB);
-    return 0;
-  }
-
-  if (x != NULL) {
-    if (no_chain) {
-      if (!ssl_add_cert_to_buf(buf, l, x)) {
-        return 0;
-      }
-    } else {
-      X509_STORE_CTX xs_ctx;
-
-      if (!X509_STORE_CTX_init(&xs_ctx, chain_store, x, NULL)) {
-        OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_X509_LIB);
-        return 0;
-      }
-      X509_verify_cert(&xs_ctx);
-      /* Don't leave errors in the queue */
-      ERR_clear_error();
-      for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) {
-        x = sk_X509_value(xs_ctx.chain, i);
-
-        if (!ssl_add_cert_to_buf(buf, l, x)) {
-          X509_STORE_CTX_cleanup(&xs_ctx);
-          return 0;
-        }
-      }
-      X509_STORE_CTX_cleanup(&xs_ctx);
-    }
-  }
-
-  for (i = 0; i < sk_X509_num(extra_certs); i++) {
-    x = sk_X509_value(extra_certs, i);
+  if (no_chain) {
     if (!ssl_add_cert_to_buf(buf, l, x)) {
       return 0;
     }
+
+    for (i = 0; i < sk_X509_num(extra_certs); i++) {
+      x = sk_X509_value(extra_certs, i);
+      if (!ssl_add_cert_to_buf(buf, l, x)) {
+        return 0;
+      }
+    }
+  } else {
+    X509_STORE_CTX xs_ctx;
+
+    if (!X509_STORE_CTX_init(&xs_ctx, chain_store, x, NULL)) {
+      OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_X509_LIB);
+      return 0;
+    }
+    X509_verify_cert(&xs_ctx);
+    /* Don't leave errors in the queue */
+    ERR_clear_error();
+    for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) {
+      x = sk_X509_value(xs_ctx.chain, i);
+
+      if (!ssl_add_cert_to_buf(buf, l, x)) {
+        X509_STORE_CTX_cleanup(&xs_ctx);
+        return 0;
+      }
+    }
+    X509_STORE_CTX_cleanup(&xs_ctx);
   }
 
   return 1;
@@ -956,7 +859,7 @@
   STACK_OF(X509) *chain = NULL, *untrusted = NULL;
   X509 *x;
   int i, rv = 0;
-  unsigned long error;
+  uint32_t error;
 
   if (!cpk->x509) {
     OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, SSL_R_NO_CERTIFICATE_SET);
@@ -1050,8 +953,9 @@
   }
 
   cpk->chain = chain;
-  if (rv == 0)
+  if (rv == 0) {
     rv = 1;
+  }
 
 err:
   if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) {
@@ -1069,9 +973,7 @@
     pstore = &c->verify_store;
   }
 
-  if (*pstore) {
-    X509_STORE_free(*pstore);
-  }
+  X509_STORE_free(*pstore);
   *pstore = store;
 
   if (ref && store) {
diff --git a/src/ssl/ssl_ciph.c b/src/ssl/ssl_cipher.c
similarity index 64%
rename from src/ssl/ssl_ciph.c
rename to src/ssl/ssl_cipher.c
index 60b9747..2cafeb9 100644
--- a/src/ssl/ssl_ciph.c
+++ b/src/ssl/ssl_cipher.c
@@ -138,20 +138,22 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
-#include <openssl/engine.h>
+#include <openssl/buf.h>
+#include <openssl/err.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/sha.h>
+#include <openssl/stack.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 struct handshake_digest {
-  long mask;
+  uint32_t mask;
   const EVP_MD *(*md_func)(void);
 };
 
@@ -171,78 +173,92 @@
 typedef struct cipher_order_st {
   const SSL_CIPHER *cipher;
   int active;
-  int dead;
   int in_group;
   struct cipher_order_st *next, *prev;
 } CIPHER_ORDER;
 
-static const SSL_CIPHER cipher_aliases[] =
-    {
-     {0, SSL_TXT_ALL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+typedef struct cipher_alias_st {
+  /* name is the name of the cipher alias. */
+  const char *name;
 
-     /* "COMPLEMENTOFDEFAULT" (does *not* include ciphersuites not found in
-        ALL!) */
-     {0, SSL_TXT_CMPDEF, 0, SSL_kEDH | SSL_kEECDH, SSL_aNULL, 0, 0, 0, 0, 0, 0,
-      0},
+  /* The following fields are bitmasks for the corresponding fields on
+   * |SSL_CIPHER|. A cipher matches a cipher alias iff, for each bitmask, the
+   * bit corresponding to the cipher's value is set to 1. If any bitmask is
+   * all zeroes, the alias matches nothing. Use |~0u| for the default value. */
+  uint32_t algorithm_mkey;
+  uint32_t algorithm_auth;
+  uint32_t algorithm_enc;
+  uint32_t algorithm_mac;
+  uint32_t algorithm_ssl;
+  uint32_t algo_strength;
+} CIPHER_ALIAS;
+
+static const CIPHER_ALIAS kCipherAliases[] =
+    {
+     {SSL_TXT_ALL, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u},
+
+     /* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */
 
      /* key exchange aliases
       * (some of those using only a single bit here combine
       * multiple key exchange algs according to the RFCs,
       * e.g. kEDH combines DHE_DSS and DHE_RSA) */
-     {0, SSL_TXT_kRSA, 0, SSL_kRSA, 0, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_kRSA, SSL_kRSA, ~0u, ~0u, ~0u, ~0u, ~0u},
 
-     {0, SSL_TXT_kEDH, 0, SSL_kEDH, 0, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_DH, 0, SSL_kEDH, 0, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_kDHE, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_kEDH, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_DH, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
 
-     {0, SSL_TXT_kEECDH, 0, SSL_kEECDH, 0, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_ECDH, 0, SSL_kEECDH, 0, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_kECDHE, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_kEECDH, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_ECDH, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
 
-     {0, SSL_TXT_kPSK, 0, SSL_kPSK, 0, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_kPSK, SSL_kPSK, ~0u, ~0u, ~0u, ~0u, ~0u},
 
      /* server authentication aliases */
-     {0, SSL_TXT_aRSA, 0, 0, SSL_aRSA, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_aNULL, 0, 0, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_aECDSA, 0, 0, SSL_aECDSA, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_ECDSA, 0, 0, SSL_aECDSA, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_aPSK, 0, 0, SSL_aPSK, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_aRSA, ~0u, SSL_aRSA, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_aECDSA, ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_ECDSA, ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_aPSK, ~0u, SSL_aPSK, ~0u, ~0u, ~0u, ~0u},
 
      /* aliases combining key exchange and server authentication */
-     {0, SSL_TXT_EDH, 0, SSL_kEDH, ~SSL_aNULL, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_EECDH, 0, SSL_kEECDH, ~SSL_aNULL, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_RSA, 0, SSL_kRSA, SSL_aRSA, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_ADH, 0, SSL_kEDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_AECDH, 0, SSL_kEECDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_PSK, 0, SSL_kPSK, SSL_aPSK, 0, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_DHE, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_EDH, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_ECDHE, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_EECDH, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_RSA, SSL_kRSA, SSL_aRSA, ~0u, ~0u, ~0u, ~0u},
+     {SSL_TXT_PSK, SSL_kPSK, SSL_aPSK, ~0u, ~0u, ~0u, ~0u},
 
      /* symmetric encryption aliases */
-     {0, SSL_TXT_3DES, 0, 0, 0, SSL_3DES, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_RC4, 0, 0, 0, SSL_RC4, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_AES128, 0, 0, 0, SSL_AES128 | SSL_AES128GCM, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_AES256, 0, 0, 0, SSL_AES256 | SSL_AES256GCM, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_AES, 0, 0, 0, SSL_AES, 0, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_AES_GCM, 0, 0, 0, SSL_AES128GCM | SSL_AES256GCM, 0, 0, 0, 0, 0,
-      0},
-     {0, SSL_TXT_CHACHA20, 0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, 0, 0, 0},
+     {SSL_TXT_3DES, ~0u, ~0u, SSL_3DES, ~0u, ~0u, ~0u},
+     {SSL_TXT_RC4, ~0u, ~0u, SSL_RC4, ~0u, ~0u, ~0u},
+     {SSL_TXT_AES128, ~0u, ~0u, SSL_AES128 | SSL_AES128GCM, ~0u, ~0u, ~0u},
+     {SSL_TXT_AES256, ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, ~0u, ~0u},
+     {SSL_TXT_AES, ~0u, ~0u, SSL_AES, ~0u, ~0u, ~0u},
+     {SSL_TXT_AES_GCM, ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, ~0u, ~0u},
+     {SSL_TXT_CHACHA20, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, ~0u, ~0u},
 
      /* MAC aliases */
-     {0, SSL_TXT_MD5, 0, 0, 0, 0, SSL_MD5, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_SHA1, 0, 0, 0, 0, SSL_SHA1, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_SHA, 0, 0, 0, 0, SSL_SHA1, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_SHA256, 0, 0, 0, 0, SSL_SHA256, 0, 0, 0, 0, 0},
-     {0, SSL_TXT_SHA384, 0, 0, 0, 0, SSL_SHA384, 0, 0, 0, 0, 0},
+     {SSL_TXT_MD5, ~0u, ~0u, ~0u, SSL_MD5, ~0u, ~0u},
+     {SSL_TXT_SHA1, ~0u, ~0u, ~0u, SSL_SHA1, ~0u, ~0u},
+     {SSL_TXT_SHA, ~0u, ~0u, ~0u, SSL_SHA1, ~0u, ~0u},
+     {SSL_TXT_SHA256, ~0u, ~0u, ~0u, SSL_SHA256, ~0u, ~0u},
+     {SSL_TXT_SHA384, ~0u, ~0u, ~0u, SSL_SHA384, ~0u, ~0u},
 
      /* protocol version aliases */
-     {0, SSL_TXT_SSLV3, 0, 0, 0, 0, 0, SSL_SSLV3, 0, 0, 0, 0},
-     {0, SSL_TXT_TLSV1, 0, 0, 0, 0, 0, SSL_TLSV1, 0, 0, 0, 0},
-     {0, SSL_TXT_TLSV1_2, 0, 0, 0, 0, 0, SSL_TLSV1_2, 0, 0, 0, 0},
+     {SSL_TXT_SSLV3, ~0u, ~0u, ~0u, ~0u, SSL_SSLV3, ~0u},
+     {SSL_TXT_TLSV1, ~0u, ~0u, ~0u, ~0u, SSL_TLSV1, ~0u},
+     {SSL_TXT_TLSV1_2, ~0u, ~0u, ~0u, ~0u, SSL_TLSV1_2, ~0u},
 
      /* strength classes */
-     {0, SSL_TXT_MEDIUM, 0, 0, 0, 0, 0, 0, SSL_MEDIUM, 0, 0, 0},
-     {0, SSL_TXT_HIGH, 0, 0, 0, 0, 0, 0, SSL_HIGH, 0, 0, 0},
+     {SSL_TXT_MEDIUM, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_MEDIUM},
+     {SSL_TXT_HIGH, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_HIGH},
      /* FIPS 140-2 approved ciphersuite */
-     {0, SSL_TXT_FIPS, 0, 0, 0, 0, 0, 0, SSL_FIPS, 0, 0, 0},
+     {SSL_TXT_FIPS, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_FIPS},
 };
 
+#define NUM_CIPHER_ALIASES (sizeof(kCipherAliases) / sizeof(kCipherAliases[0]))
+
 int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
                             size_t *out_mac_secret_len,
                             size_t *out_fixed_iv_len,
@@ -360,18 +376,26 @@
   }
 }
 
-int ssl_get_handshake_digest(size_t idx, long *mask, const EVP_MD **md) {
+int ssl_get_handshake_digest(uint32_t *out_mask, const EVP_MD **out_md,
+                             size_t idx) {
   if (idx >= SSL_MAX_DIGEST) {
     return 0;
   }
-  *mask = ssl_handshake_digests[idx].mask;
-  *md = ssl_handshake_digests[idx].md_func();
+  *out_mask = ssl_handshake_digests[idx].mask;
+  *out_md = ssl_handshake_digests[idx].md_func();
   return 1;
 }
 
 #define ITEM_SEP(a) \
   (((a) == ':') || ((a) == ' ') || ((a) == ';') || ((a) == ','))
 
+/* rule_equals returns one iff the NUL-terminated string |rule| is equal to the
+ * |buf_len| bytes at |buf|. */
+static int rule_equals(const char *rule, const char *buf, size_t buf_len) {
+  /* |strncmp| alone only checks that |buf| is a prefix of |rule|. */
+  return strncmp(rule, buf, buf_len) == 0 && rule[buf_len] == '\0';
+}
+
 static void ll_append_tail(CIPHER_ORDER **head, CIPHER_ORDER *curr,
                            CIPHER_ORDER **tail) {
   if (curr == *tail) {
@@ -413,12 +437,11 @@
 }
 
 static void ssl_cipher_collect_ciphers(const SSL_PROTOCOL_METHOD *ssl_method,
-                                       int num_of_ciphers,
+                                       size_t num_of_ciphers,
                                        CIPHER_ORDER *co_list,
                                        CIPHER_ORDER **head_p,
                                        CIPHER_ORDER **tail_p) {
-  int i, co_list_num;
-  const SSL_CIPHER *c;
+  size_t i, co_list_num;
 
   /* We have num_of_ciphers descriptions compiled in, depending on the method
    * selected (SSLv2 and/or SSLv3, TLSv1 etc). These will later be sorted in a
@@ -427,9 +450,8 @@
   /* Get the initial list of ciphers */
   co_list_num = 0; /* actual count of ciphers */
   for (i = 0; i < num_of_ciphers; i++) {
-    c = ssl_method->get_cipher(i);
-    /* drop those that use any of that is not available */
-    if (c != NULL && c->valid) {
+    const SSL_CIPHER *c = ssl_method->get_cipher(i);
+    if (c != NULL) {
       co_list[co_list_num].cipher = c;
       co_list[co_list_num].next = NULL;
       co_list[co_list_num].prev = NULL;
@@ -461,43 +483,31 @@
   }
 }
 
-static void ssl_cipher_collect_aliases(const SSL_CIPHER **ca_list,
-                                       int num_of_group_aliases,
-                                       CIPHER_ORDER *head) {
-  CIPHER_ORDER *ciph_curr;
-  const SSL_CIPHER **ca_curr;
-  int i;
-
-  /* First, add the real ciphers as already collected. */
-  ciph_curr = head;
-  ca_curr = ca_list;
-  while (ciph_curr != NULL) {
-    *ca_curr = ciph_curr->cipher;
-    ca_curr++;
-    ciph_curr = ciph_curr->next;
-  }
-
-  /* Now we add the available ones from the cipher_aliases[] table. They
-   * represent either one or more algorithms, some of which in any affected
-   * category must be supported (set in enabled_mask), or represent a cipher
-   * strength value (will be added in any case because algorithms=0). */
-  for (i = 0; i < num_of_group_aliases; i++) {
-    *ca_curr = cipher_aliases + i;
-    ca_curr++;
-  }
-
-  *ca_curr = NULL; /* end of list */
-}
-
+/* ssl_cipher_apply_rule applies the rule type |rule| to ciphers matching its
+ * parameters in the linked list from |*head_p| to |*tail_p|. It writes the new
+ * head and tail of the list to |*head_p| and |*tail_p|, respectively.
+ *
+ * - If |cipher_id| is non-zero, only that cipher is selected.
+ * - Otherwise, if |strength_bits| is non-negative, it selects ciphers
+ *   of that strength.
+ * - Otherwise, it selects ciphers that match each bitmasks in |alg_*| and
+ *   |algo_strength|. */
 static void ssl_cipher_apply_rule(
-    unsigned long cipher_id, unsigned long alg_mkey, unsigned long alg_auth,
-    unsigned long alg_enc, unsigned long alg_mac, unsigned long alg_ssl,
-    unsigned long algo_strength, int rule, int strength_bits, int in_group,
+    uint32_t cipher_id, uint32_t alg_mkey, uint32_t alg_auth,
+    uint32_t alg_enc, uint32_t alg_mac, uint32_t alg_ssl,
+    uint32_t algo_strength, int rule, int strength_bits, int in_group,
     CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p) {
   CIPHER_ORDER *head, *tail, *curr, *next, *last;
   const SSL_CIPHER *cp;
   int reverse = 0;
 
+  if (cipher_id == 0 && strength_bits == -1 &&
+      (alg_mkey == 0 || alg_auth == 0 || alg_enc == 0 || alg_mac == 0 ||
+       alg_ssl == 0 || algo_strength == 0)) {
+    /* The rule matches nothing, so bail early. */
+    return;
+  }
+
   if (rule == CIPHER_DEL) {
     /* needed to maintain sorting between currently deleted ciphers */
     reverse = 1;
@@ -528,21 +538,23 @@
     next = reverse ? curr->prev : curr->next;
     cp = curr->cipher;
 
-    /* Selection criteria is either the value of strength_bits
-     * or the algorithms used. */
-    if (strength_bits >= 0) {
+    /* Selection criteria is either a specific cipher, the value of
+     * |strength_bits|, or the algorithms used. */
+    if (cipher_id != 0) {
+      if (cipher_id != cp->id) {
+        continue;
+      }
+    } else if (strength_bits >= 0) {
       if (strength_bits != cp->strength_bits) {
         continue;
       }
-    } else {
-      if ((alg_mkey && !(alg_mkey & cp->algorithm_mkey)) ||
-          (alg_auth && !(alg_auth & cp->algorithm_auth)) ||
-          (alg_enc && !(alg_enc & cp->algorithm_enc)) ||
-          (alg_mac && !(alg_mac & cp->algorithm_mac)) ||
-          (alg_ssl && !(alg_ssl & cp->algorithm_ssl)) ||
-          (algo_strength && !(algo_strength & cp->algo_strength))) {
-        continue;
-      }
+    } else if (!(alg_mkey & cp->algorithm_mkey) ||
+               !(alg_auth & cp->algorithm_auth) ||
+               !(alg_enc & cp->algorithm_enc) ||
+               !(alg_mac & cp->algorithm_mac) ||
+               !(alg_ssl & cp->algorithm_ssl) ||
+               !(algo_strength & cp->algo_strength)) {
+      continue;
     }
 
     /* add the cipher if it has not been added yet. */
@@ -644,14 +656,15 @@
   return 1;
 }
 
-static int ssl_cipher_process_rulestr(const char *rule_str,
+static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method,
+                                      const char *rule_str,
                                       CIPHER_ORDER **head_p,
-                                      CIPHER_ORDER **tail_p,
-                                      const SSL_CIPHER **ca_list) {
-  unsigned long alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength;
+                                      CIPHER_ORDER **tail_p) {
+  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength;
   const char *l, *buf;
-  int j, multi, found, rule, retval, ok, buflen, in_group = 0, has_group = 0;
-  unsigned long cipher_id = 0;
+  int multi, rule, retval, ok, in_group = 0, has_group = 0;
+  size_t j, buf_len;
+  uint32_t cipher_id;
   char ch;
 
   retval = 1;
@@ -665,12 +678,6 @@
 
     if (in_group) {
       if (ch == ']') {
-        if (!in_group) {
-          OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
-                            SSL_R_UNEXPECTED_GROUP_CLOSE);
-          retval = found = in_group = 0;
-          break;
-        }
         if (*tail_p) {
           (*tail_p)->in_group = 0;
         }
@@ -687,7 +694,7 @@
                  !(ch >= '0' && ch <= '9')) {
         OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
                           SSL_R_UNEXPECTED_OPERATOR_IN_GROUP);
-        retval = found = in_group = 0;
+        retval = in_group = 0;
         break;
       } else {
         rule = CIPHER_ADD;
@@ -707,7 +714,7 @@
     } else if (ch == '[') {
       if (in_group) {
         OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_NESTED_GROUP);
-        retval = found = in_group = 0;
+        retval = in_group = 0;
         break;
       }
       in_group = 1;
@@ -723,7 +730,7 @@
     if (has_group && rule != CIPHER_ADD) {
       OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
                         SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS);
-      retval = found = in_group = 0;
+      retval = in_group = 0;
       break;
     }
 
@@ -732,159 +739,82 @@
       continue;
     }
 
-    alg_mkey = 0;
-    alg_auth = 0;
-    alg_enc = 0;
-    alg_mac = 0;
-    alg_ssl = 0;
-    algo_strength = 0;
+    multi = 0;
+    cipher_id = 0;
+    alg_mkey = ~0u;
+    alg_auth = ~0u;
+    alg_enc = ~0u;
+    alg_mac = ~0u;
+    alg_ssl = ~0u;
+    algo_strength = ~0u;
 
     for (;;) {
       ch = *l;
       buf = l;
-      buflen = 0;
+      buf_len = 0;
       while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) ||
              ((ch >= 'a') && (ch <= 'z')) || (ch == '-') || (ch == '.')) {
         ch = *(++l);
-        buflen++;
+        buf_len++;
       }
 
-      if (buflen == 0) {
+      if (buf_len == 0) {
         /* We hit something we cannot deal with, it is no command or separator
          * nor alphanumeric, so we call this an error. */
         OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
                           SSL_R_INVALID_COMMAND);
-        retval = found = in_group = 0;
+        retval = in_group = 0;
         l++;
         break;
       }
 
       if (rule == CIPHER_SPECIAL) {
-        found = 0; /* unused -- avoid compiler warning */
-        break;     /* special treatment */
-      }
-
-      /* check for multi-part specification */
-      if (ch == '+') {
-        multi = 1;
-        l++;
-      } else {
-        multi = 0;
-      }
-
-      /* Now search for the cipher alias in the ca_list. Be careful with the
-       * strncmp, because the "buflen" limitation will make the rule "ADH:SOME"
-       * and the cipher "ADH-MY-CIPHER" look like a match for buflen=3. So
-       * additionally check whether the cipher name found has the correct
-       * length. We can save a strlen() call: just checking for the '\0' at the
-       * right place is sufficient, we have to strncmp() anyway. (We cannot use
-       * strcmp(), because buf is not '\0' terminated.) */
-      j = found = 0;
-      cipher_id = 0;
-      while (ca_list[j]) {
-        if (!strncmp(buf, ca_list[j]->name, buflen) &&
-            (ca_list[j]->name[buflen] == '\0')) {
-          found = 1;
-          break;
-        } else {
-          j++;
-        }
-      }
-
-      if (!found) {
-        break; /* ignore this entry */
-      }
-
-      if (ca_list[j]->algorithm_mkey) {
-        if (alg_mkey) {
-          alg_mkey &= ca_list[j]->algorithm_mkey;
-          if (!alg_mkey) {
-            found = 0;
-            break;
-          }
-        } else {
-          alg_mkey = ca_list[j]->algorithm_mkey;
-        }
-      }
-
-      if (ca_list[j]->algorithm_auth) {
-        if (alg_auth) {
-          alg_auth &= ca_list[j]->algorithm_auth;
-          if (!alg_auth) {
-            found = 0;
-            break;
-          }
-        } else {
-          alg_auth = ca_list[j]->algorithm_auth;
-        }
-      }
-
-      if (ca_list[j]->algorithm_enc) {
-        if (alg_enc) {
-          alg_enc &= ca_list[j]->algorithm_enc;
-          if (!alg_enc) {
-            found = 0;
-            break;
-          }
-        } else {
-          alg_enc = ca_list[j]->algorithm_enc;
-        }
-      }
-
-      if (ca_list[j]->algorithm_mac) {
-        if (alg_mac) {
-          alg_mac &= ca_list[j]->algorithm_mac;
-          if (!alg_mac) {
-            found = 0;
-            break;
-          }
-        } else {
-          alg_mac = ca_list[j]->algorithm_mac;
-        }
-      }
-
-      if (ca_list[j]->algo_strength) {
-        if (algo_strength) {
-          algo_strength &= ca_list[j]->algo_strength;
-          if (!algo_strength) {
-            found = 0;
-            break;
-          }
-        } else {
-          algo_strength |= ca_list[j]->algo_strength;
-        }
-      }
-
-      if (ca_list[j]->valid) {
-        /* explicit ciphersuite found; its protocol version does not become
-         * part of the search pattern! */
-        cipher_id = ca_list[j]->id;
-      } else {
-        /* not an explicit ciphersuite; only in this case, the protocol version
-         * is considered part of the search pattern. */
-        if (ca_list[j]->algorithm_ssl) {
-          if (alg_ssl) {
-            alg_ssl &= ca_list[j]->algorithm_ssl;
-            if (!alg_ssl) {
-              found = 0;
-              break;
-            }
-          } else {
-            alg_ssl = ca_list[j]->algorithm_ssl;
-          }
-        }
-      }
-
-      if (!multi) {
         break;
       }
+
+      /* Look for a matching exact cipher. These aren't allowed in multipart
+       * rules. */
+      if (!multi && ch != '+') {
+        size_t num_ciphers = ssl_method->num_ciphers();
+        for (j = 0; j < num_ciphers; j++) {
+          const SSL_CIPHER *cipher = ssl_method->get_cipher(j);
+          if (cipher != NULL && rule_equals(cipher->name, buf, buf_len)) {
+            cipher_id = cipher->id;
+            break;
+          }
+        }
+      }
+      if (cipher_id == 0) {
+        /* If not an exact cipher, look for a matching cipher alias. */
+        for (j = 0; j < NUM_CIPHER_ALIASES; j++) {
+          if (rule_equals(kCipherAliases[j].name, buf, buf_len)) {
+            alg_mkey &= kCipherAliases[j].algorithm_mkey;
+            alg_auth &= kCipherAliases[j].algorithm_auth;
+            alg_enc &= kCipherAliases[j].algorithm_enc;
+            alg_mac &= kCipherAliases[j].algorithm_mac;
+            alg_ssl &= kCipherAliases[j].algorithm_ssl;
+            algo_strength &= kCipherAliases[j].algo_strength;
+            break;
+          }
+        }
+        if (j == NUM_CIPHER_ALIASES) {
+          alg_mkey = alg_auth = alg_enc = alg_mac = alg_ssl = algo_strength = 0;
+        }
+      }
+
+      /* Check for a multipart rule. */
+      if (ch != '+') {
+        break;
+      }
+      l++;
+      multi = 1;
     }
 
     /* Ok, we have the rule, now apply it. */
     if (rule == CIPHER_SPECIAL) {
       /* special command */
       ok = 0;
-      if (buflen == 8 && !strncmp(buf, "STRENGTH", 8)) {
+      if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) {
         ok = ssl_cipher_strength_sort(head_p, tail_p);
       } else {
         OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr,
@@ -900,14 +830,10 @@
       while (*l != '\0' && !ITEM_SEP(*l)) {
         l++;
       }
-    } else if (found) {
+    } else {
       ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac,
                             alg_ssl, algo_strength, rule, -1, in_group, head_p,
                             tail_p);
-    } else {
-      while (*l != '\0' && !ITEM_SEP(*l)) {
-        l++;
-      }
     }
   }
 
@@ -921,20 +847,20 @@
 
 STACK_OF(SSL_CIPHER) *
 ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
-                       struct ssl_cipher_preference_list_st **cipher_list,
-                       STACK_OF(SSL_CIPHER) * *cipher_list_by_id,
-                       const char *rule_str, CERT *c) {
-  int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases;
+                       struct ssl_cipher_preference_list_st **out_cipher_list,
+                       STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
+                       const char *rule_str) {
+  int ok;
+  size_t num_of_ciphers;
   STACK_OF(SSL_CIPHER) *cipherstack = NULL, *tmp_cipher_list = NULL;
   const char *rule_p;
   CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;
-  const SSL_CIPHER **ca_list = NULL;
   uint8_t *in_group_flags = NULL;
   unsigned int num_in_group_flags = 0;
   struct ssl_cipher_preference_list_st *pref_list = NULL;
 
   /* Return with error if nothing to do. */
-  if (rule_str == NULL || cipher_list == NULL) {
+  if (rule_str == NULL || out_cipher_list == NULL) {
     return NULL;
   }
 
@@ -956,84 +882,64 @@
 
   /* Everything else being equal, prefer ECDHE_ECDSA then ECDHE_RSA over other
    * key exchange mechanisms */
-  ssl_cipher_apply_rule(0, SSL_kEECDH, SSL_aECDSA, 0, 0, 0, 0, CIPHER_ADD, -1,
+ ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u,
+                       CIPHER_ADD, -1, 0, &head, &tail);
+  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
                         0, &head, &tail);
-  ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head,
-                        &tail);
-  ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head,
-                        &tail);
+  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_DEL, -1,
+                        0, &head, &tail);
 
   /* Order the bulk ciphers. First the preferred AEAD ciphers. We prefer
    * CHACHA20 unless there is hardware support for fast and constant-time
    * AES_GCM. */
   if (EVP_has_aes_hardware()) {
-    ssl_cipher_apply_rule(0, 0, 0, SSL_AES256GCM, 0, 0, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
-    ssl_cipher_apply_rule(0, 0, 0, SSL_AES128GCM, 0, 0, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
-    ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, CIPHER_ADD,
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
                           -1, 0, &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
+                          -1, 0, &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, ~0u, ~0u,
+                          CIPHER_ADD, -1, 0, &head, &tail);
   } else {
-    ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, CIPHER_ADD,
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, ~0u, ~0u,
+                          CIPHER_ADD, -1, 0, &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
                           -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, 0, 0, SSL_AES256GCM, 0, 0, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
-    ssl_cipher_apply_rule(0, 0, 0, SSL_AES128GCM, 0, 0, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
+                          -1, 0, &head, &tail);
   }
 
   /* Then the legacy non-AEAD ciphers: AES_256_CBC, AES-128_CBC, RC4_128_SHA,
    * RC4_128_MD5, 3DES_EDE_CBC_SHA. */
-  ssl_cipher_apply_rule(0, 0, 0, SSL_AES256, 0, 0, 0, CIPHER_ADD, -1, 0, &head,
-                        &tail);
-  ssl_cipher_apply_rule(0, 0, 0, SSL_AES128, 0, 0, 0, CIPHER_ADD, -1, 0, &head,
-                        &tail);
-  ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, ~SSL_MD5, 0, 0, CIPHER_ADD, -1, 0,
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
+                        0, &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
+                        0, &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, ~SSL_MD5, ~0u, ~0u, CIPHER_ADD,
+                        -1, 0, &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, SSL_MD5, ~0u, ~0u, CIPHER_ADD, -1,
+                        0, &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, ~0u, ~0u, CIPHER_ADD, -1, 0,
                         &head, &tail);
-  ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, SSL_MD5, 0, 0, CIPHER_ADD, -1, 0,
-                        &head, &tail);
-  ssl_cipher_apply_rule(0, 0, 0, SSL_3DES, 0, 0, 0, CIPHER_ADD, -1, 0, &head,
-                        &tail);
 
   /* Temporarily enable everything else for sorting */
-  ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_ADD, -1, 0,
+                        &head, &tail);
 
   /* Move ciphers without forward secrecy to the end. */
-  ssl_cipher_apply_rule(0, ~(SSL_kEDH | SSL_kEECDH), 0, 0, 0, 0, 0, CIPHER_ORD,
-                        -1, 0, &head, &tail);
-
-  /* Move anonymous ciphers to the end.  Usually, these will remain disabled.
-   * (For applications that allow them, they aren't too bad, but we prefer
-   * authenticated ciphers.)
-   * TODO(davidben): Remove them altogether? */
-  ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head,
-                        &tail);
+  ssl_cipher_apply_rule(0, ~(SSL_kDHE | SSL_kECDHE), ~0u, ~0u, ~0u, ~0u, ~0u,
+                        CIPHER_ORD, -1, 0, &head, &tail);
 
   /* Now disable everything (maintaining the ordering!) */
-  ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head, &tail);
-
-  /* We also need cipher aliases for selecting based on the rule_str. There
-   * might be two types of entries in the rule_str: 1) names of ciphers
-   * themselves 2) aliases for groups of ciphers. For 1) we need the available
-   * ciphers and for 2) the cipher groups of cipher_aliases added together in
-   * one list (otherwise we would be happy with just the cipher_aliases
-   * table). */
-  num_of_group_aliases = sizeof(cipher_aliases) / sizeof(SSL_CIPHER);
-  num_of_alias_max = num_of_ciphers + num_of_group_aliases + 1;
-  ca_list = OPENSSL_malloc(sizeof(SSL_CIPHER *) * num_of_alias_max);
-  if (ca_list == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ssl_create_cipher_list, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-  ssl_cipher_collect_aliases(ca_list, num_of_group_aliases, head);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_DEL, -1, 0,
+                        &head, &tail);
 
   /* If the rule_string begins with DEFAULT, apply the default rule before
    * using the (possibly available) additional rules. */
   ok = 1;
   rule_p = rule_str;
   if (strncmp(rule_str, "DEFAULT", 7) == 0) {
-    ok = ssl_cipher_process_rulestr(SSL_DEFAULT_CIPHER_LIST, &head, &tail,
-                                    ca_list);
+    ok = ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head,
+                                    &tail);
     rule_p += 7;
     if (*rule_p == ':') {
       rule_p++;
@@ -1041,11 +947,9 @@
   }
 
   if (ok && strlen(rule_p) > 0) {
-    ok = ssl_cipher_process_rulestr(rule_p, &head, &tail, ca_list);
+    ok = ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail);
   }
 
-  OPENSSL_free((void *)ca_list); /* Not needed anymore */
-
   if (!ok) {
     goto err;
   }
@@ -1091,21 +995,20 @@
   memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags);
   OPENSSL_free(in_group_flags);
   in_group_flags = NULL;
-  if (*cipher_list != NULL) {
-    ssl_cipher_preference_list_free(*cipher_list);
+  if (*out_cipher_list != NULL) {
+    ssl_cipher_preference_list_free(*out_cipher_list);
   }
-  *cipher_list = pref_list;
+  *out_cipher_list = pref_list;
   pref_list = NULL;
 
-  if (cipher_list_by_id != NULL) {
-    if (*cipher_list_by_id != NULL) {
-      sk_SSL_CIPHER_free(*cipher_list_by_id);
-    }
-    *cipher_list_by_id = tmp_cipher_list;
+  if (out_cipher_list_by_id != NULL) {
+    sk_SSL_CIPHER_free(*out_cipher_list_by_id);
+    *out_cipher_list_by_id = tmp_cipher_list;
     tmp_cipher_list = NULL;
-    (void) sk_SSL_CIPHER_set_cmp_func(*cipher_list_by_id, ssl_cipher_ptr_id_cmp);
+    (void) sk_SSL_CIPHER_set_cmp_func(*out_cipher_list_by_id,
+                                      ssl_cipher_ptr_id_cmp);
 
-    sk_SSL_CIPHER_sort(*cipher_list_by_id);
+    sk_SSL_CIPHER_sort(*out_cipher_list_by_id);
   } else {
     sk_SSL_CIPHER_free(tmp_cipher_list);
     tmp_cipher_list = NULL;
@@ -1114,32 +1017,177 @@
   return cipherstack;
 
 err:
-  if (co_list) {
-    OPENSSL_free(co_list);
-  }
-  if (in_group_flags) {
-    OPENSSL_free(in_group_flags);
-  }
-  if (cipherstack) {
-    sk_SSL_CIPHER_free(cipherstack);
-  }
-  if (tmp_cipher_list) {
-    sk_SSL_CIPHER_free(tmp_cipher_list);
-  }
-  if (pref_list && pref_list->in_group_flags) {
+  OPENSSL_free(co_list);
+  OPENSSL_free(in_group_flags);
+  sk_SSL_CIPHER_free(cipherstack);
+  sk_SSL_CIPHER_free(tmp_cipher_list);
+  if (pref_list) {
     OPENSSL_free(pref_list->in_group_flags);
   }
-  if (pref_list) {
-    OPENSSL_free(pref_list);
-  }
+  OPENSSL_free(pref_list);
   return NULL;
 }
 
+uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher) { return cipher->id; }
+
+int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_enc & SSL_AES) != 0;
+}
+
+int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mac & SSL_MD5) != 0;
+}
+
+int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mac & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
+}
+
+int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_enc & SSL_CHACHA20POLY1305) != 0;
+}
+
+/* return the actual cipher being used */
+const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) {
+  if (cipher != NULL) {
+    return cipher->name;
+  }
+
+  return "(NONE)";
+}
+
+const char *SSL_CIPHER_get_kx_name(const SSL_CIPHER *cipher) {
+  if (cipher == NULL) {
+    return "";
+  }
+
+  switch (cipher->algorithm_mkey) {
+    case SSL_kRSA:
+      return "RSA";
+
+    case SSL_kDHE:
+      switch (cipher->algorithm_auth) {
+        case SSL_aRSA:
+          return "DHE_RSA";
+        default:
+          assert(0);
+          return "UNKNOWN";
+      }
+
+    case SSL_kECDHE:
+      switch (cipher->algorithm_auth) {
+        case SSL_aECDSA:
+          return "ECDHE_ECDSA";
+        case SSL_aRSA:
+          return "ECDHE_RSA";
+        case SSL_aPSK:
+          return "ECDHE_PSK";
+        default:
+          assert(0);
+          return "UNKNOWN";
+      }
+
+    case SSL_kPSK:
+      assert(cipher->algorithm_auth == SSL_aPSK);
+      return "PSK";
+
+    default:
+      assert(0);
+      return "UNKNOWN";
+  }
+}
+
+static const char *ssl_cipher_get_enc_name(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_enc) {
+    case SSL_3DES:
+      return "3DES_EDE_CBC";
+    case SSL_RC4:
+      return "RC4";
+    case SSL_AES128:
+      return "AES_128_CBC";
+    case SSL_AES256:
+      return "AES_256_CBC";
+    case SSL_AES128GCM:
+      return "AES_128_GCM";
+    case SSL_AES256GCM:
+      return "AES_256_GCM";
+    case SSL_CHACHA20POLY1305:
+      return "CHACHA20_POLY1305";
+      break;
+    default:
+      assert(0);
+      return "UNKNOWN";
+  }
+}
+
+static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) {
+  if ((cipher->algorithm2 & TLS1_PRF) == TLS1_PRF) {
+    /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which is
+     * only ever MD5 or SHA-1. */
+    switch (cipher->algorithm_mac) {
+      case SSL_MD5:
+        return "MD5";
+      case SSL_SHA1:
+        return "SHA";
+      default:
+        assert(0);
+        return "UNKNOWN";
+    }
+  } else if (cipher->algorithm2 & TLS1_PRF_SHA256) {
+    return "SHA256";
+  } else if (cipher->algorithm2 & TLS1_PRF_SHA384) {
+    return "SHA384";
+  } else {
+    assert(0);
+    return "UNKNOWN";
+  }
+}
+
+char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher) {
+  if (cipher == NULL) {
+    return NULL;
+  }
+
+  const char *kx_name = SSL_CIPHER_get_kx_name(cipher);
+  const char *enc_name = ssl_cipher_get_enc_name(cipher);
+  const char *prf_name = ssl_cipher_get_prf_name(cipher);
+
+  /* The final name is TLS_{kx_name}_WITH_{enc_name}_{prf_name}. */
+  size_t len = 4 + strlen(kx_name) + 6 + strlen(enc_name) + 1 +
+      strlen(prf_name) + 1;
+  char *ret = OPENSSL_malloc(len);
+  if (ret == NULL) {
+    return NULL;
+  }
+  if (BUF_strlcpy(ret, "TLS_", len) >= len ||
+      BUF_strlcat(ret, kx_name, len) >= len ||
+      BUF_strlcat(ret, "_WITH_", len) >= len ||
+      BUF_strlcat(ret, enc_name, len) >= len ||
+      BUF_strlcat(ret, "_", len) >= len ||
+      BUF_strlcat(ret, prf_name, len) >= len) {
+    assert(0);
+    OPENSSL_free(ret);
+    return NULL;
+  }
+  assert(strlen(ret) + 1 == len);
+  return ret;
+}
+
+int SSL_CIPHER_get_bits(const SSL_CIPHER *cipher, int *out_alg_bits) {
+  if (cipher == NULL) {
+    return 0;
+  }
+
+  if (out_alg_bits != NULL) {
+    *out_alg_bits = cipher->alg_bits;
+  }
+  return cipher->strength_bits;
+}
+
 const char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf,
                                    int len) {
   const char *ver;
   const char *kx, *au, *enc, *mac;
-  unsigned long alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl;
+  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl;
   static const char *format = "%-23s %s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n";
 
   alg_mkey = cipher->algorithm_mkey;
@@ -1161,11 +1209,11 @@
       kx = "RSA";
       break;
 
-    case SSL_kEDH:
+    case SSL_kDHE:
       kx = "DH";
       break;
 
-    case SSL_kEECDH:
+    case SSL_kECDHE:
       kx = "ECDH";
       break;
 
@@ -1182,10 +1230,6 @@
       au = "RSA";
       break;
 
-    case SSL_aNULL:
-      au = "None";
-      break;
-
     case SSL_aECDSA:
       au = "ECDSA";
       break;
@@ -1262,8 +1306,9 @@
   if (buf == NULL) {
     len = 128;
     buf = OPENSSL_malloc(len);
-    if (buf == NULL)
-      return "OPENSSL_malloc Error";
+    if (buf == NULL) {
+      return NULL;
+    }
   } else if (len < 128) {
     return "Buffer too small";
   }
@@ -1272,109 +1317,18 @@
   return buf;
 }
 
-int SSL_CIPHER_is_AES(const SSL_CIPHER *c) {
-  return (c->algorithm_enc & SSL_AES) != 0;
+const char *SSL_CIPHER_get_version(const SSL_CIPHER *cipher) {
+  return "TLSv1/SSLv3";
 }
 
-int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *c) {
-  return (c->algorithm_mac & SSL_MD5) != 0;
-}
-
-int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *c) {
-  return (c->algorithm_mac & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
-}
-
-int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *c) {
-  return (c->algorithm_enc & SSL_CHACHA20POLY1305) != 0;
-}
-
-const char *SSL_CIPHER_get_version(const SSL_CIPHER *c) {
-  int i;
-
-  if (c == NULL) {
-    return "(NONE)";
-  }
-
-  i = (int)(c->id >> 24L);
-  if (i == 3) {
-    return "TLSv1/SSLv3";
-  } else if (i == 2) {
-    return "SSLv2";
-  } else {
-    return "unknown";
-  }
-}
-
-/* return the actual cipher being used */
-const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) {
-  if (c != NULL) {
-    return c->name;
-  }
-
-  return "(NONE)";
-}
-
-const char *SSL_CIPHER_get_kx_name(const SSL_CIPHER *cipher) {
-  if (cipher == NULL) {
-    return "";
-  }
-
-  switch (cipher->algorithm_mkey) {
-    case SSL_kRSA:
-      return SSL_TXT_RSA;
-
-    case SSL_kEDH:
-      switch (cipher->algorithm_auth) {
-        case SSL_aRSA:
-          return "DHE_" SSL_TXT_RSA;
-        case SSL_aNULL:
-          return SSL_TXT_DH "_anon";
-        default:
-          return "UNKNOWN";
-      }
-
-    case SSL_kEECDH:
-      switch (cipher->algorithm_auth) {
-        case SSL_aECDSA:
-          return "ECDHE_" SSL_TXT_ECDSA;
-        case SSL_aRSA:
-          return "ECDHE_" SSL_TXT_RSA;
-        case SSL_aNULL:
-          return SSL_TXT_ECDH "_anon";
-        default:
-          return "UNKNOWN";
-      }
-
-    default:
-      return "UNKNOWN";
-  }
-}
-
-/* number of bits for symmetric cipher */
-int SSL_CIPHER_get_bits(const SSL_CIPHER *c, int *alg_bits) {
-  int ret = 0;
-
-  if (c != NULL) {
-    if (alg_bits != NULL) {
-      *alg_bits = c->alg_bits;
-    }
-    ret = c->strength_bits;
-  }
-
-  return ret;
-}
-
-unsigned long SSL_CIPHER_get_id(const SSL_CIPHER *c) { return c->id; }
-
 void *SSL_COMP_get_compression_methods(void) { return NULL; }
 
 int SSL_COMP_add_compression_method(int id, void *cm) { return 1; }
 
 const char *SSL_COMP_get_name(const void *comp) { return NULL; }
 
-/* For a cipher return the index corresponding to the certificate type */
-int ssl_cipher_get_cert_index(const SSL_CIPHER *c) {
-  unsigned long alg_a = c->algorithm_auth;
+int ssl_cipher_get_cert_index(const SSL_CIPHER *cipher) {
+  uint32_t alg_a = cipher->algorithm_auth;
 
   if (alg_a & SSL_aECDSA) {
     return SSL_PKEY_ECC;
@@ -1385,16 +1339,9 @@
   return -1;
 }
 
-/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server
- * public key in the key exchange, sent in a server Certificate message.
- * Otherwise it returns 0. */
 int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher) {
-  /* Anonymous ciphers do not include a server certificate. */
-  if (cipher->algorithm_auth & SSL_aNULL) {
-    return 0;
-  }
-
-  /* Neither do PSK ciphers, except for RSA_PSK. */
+  /* PSK-authenticated ciphers do not use a public key, except for
+   * RSA_PSK. */
   if ((cipher->algorithm_auth & SSL_aPSK) &&
       !(cipher->algorithm_mkey & SSL_kRSA)) {
     return 0;
@@ -1404,15 +1351,9 @@
   return 1;
 }
 
-/* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a
- * ServerKeyExchange message. Otherwise it returns 0.
- *
- * Unlike ssl_cipher_has_server_public_key, some ciphers take optional
- * ServerKeyExchanges. PSK and RSA_PSK only use the ServerKeyExchange to
- * communicate a psk_identity_hint, so it is optional. */
 int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) {
   /* Ephemeral Diffie-Hellman key exchanges require a ServerKeyExchange. */
-  if (cipher->algorithm_mkey & SSL_kEDH || cipher->algorithm_mkey & SSL_kEECDH) {
+  if (cipher->algorithm_mkey & SSL_kDHE || cipher->algorithm_mkey & SSL_kECDHE) {
     return 1;
   }
 
diff --git a/src/ssl/ssl_error.c b/src/ssl/ssl_error.c
deleted file mode 100644
index 2ffb9e6..0000000
--- a/src/ssl/ssl_error.c
+++ /dev/null
@@ -1,566 +0,0 @@
-/* Copyright (c) 2014, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#include <openssl/err.h>
-
-#include <openssl/ssl.h>
-
-const ERR_STRING_DATA SSL_error_string_data[] = {
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_D2I_SSL_SESSION, 0), "D2I_SSL_SESSION"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_check_private_key, 0), "SSL_CTX_check_private_key"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_new, 0), "SSL_CTX_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_cipher_list, 0), "SSL_CTX_set_cipher_list"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_cipher_list_tls11, 0), "SSL_CTX_set_cipher_list_tls11"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_session_id_context, 0), "SSL_CTX_set_session_id_context"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_ssl_version, 0), "SSL_CTX_set_ssl_version"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey, 0), "SSL_CTX_use_PrivateKey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey_ASN1, 0), "SSL_CTX_use_PrivateKey_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey_file, 0), "SSL_CTX_use_PrivateKey_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey, 0), "SSL_CTX_use_RSAPrivateKey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey_ASN1, 0), "SSL_CTX_use_RSAPrivateKey_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey_file, 0), "SSL_CTX_use_RSAPrivateKey_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_authz, 0), "SSL_CTX_use_authz"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate, 0), "SSL_CTX_use_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_ASN1, 0), "SSL_CTX_use_certificate_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_chain_file, 0), "SSL_CTX_use_certificate_chain_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_file, 0), "SSL_CTX_use_certificate_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_psk_identity_hint, 0), "SSL_CTX_use_psk_identity_hint"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_new, 0), "SSL_SESSION_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_print_fp, 0), "SSL_SESSION_print_fp"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_set1_id_context, 0), "SSL_SESSION_set1_id_context"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_to_bytes_full, 0), "SSL_SESSION_to_bytes_full"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_accept, 0), "SSL_accept"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_dir_cert_subjects_to_stack, 0), "SSL_add_dir_cert_subjects_to_stack"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_file_cert_subjects_to_stack, 0), "SSL_add_file_cert_subjects_to_stack"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_check_private_key, 0), "SSL_check_private_key"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_clear, 0), "SSL_clear"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_connect, 0), "SSL_connect"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_do_handshake, 0), "SSL_do_handshake"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_load_client_CA_file, 0), "SSL_load_client_CA_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_new, 0), "SSL_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_peek, 0), "SSL_peek"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_read, 0), "SSL_read"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_cipher_list, 0), "SSL_set_cipher_list"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_fd, 0), "SSL_set_fd"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_rfd, 0), "SSL_set_rfd"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session, 0), "SSL_set_session"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session_id_context, 0), "SSL_set_session_id_context"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session_ticket_ext, 0), "SSL_set_session_ticket_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_wfd, 0), "SSL_set_wfd"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_shutdown, 0), "SSL_shutdown"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey, 0), "SSL_use_PrivateKey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey_ASN1, 0), "SSL_use_PrivateKey_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey_file, 0), "SSL_use_PrivateKey_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey, 0), "SSL_use_RSAPrivateKey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey_ASN1, 0), "SSL_use_RSAPrivateKey_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey_file, 0), "SSL_use_RSAPrivateKey_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_authz, 0), "SSL_use_authz"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate, 0), "SSL_use_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate_ASN1, 0), "SSL_use_certificate_ASN1"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate_file, 0), "SSL_use_certificate_file"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_psk_identity_hint, 0), "SSL_use_psk_identity_hint"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_write, 0), "SSL_write"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_authz_find_data, 0), "authz_find_data"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_check_suiteb_cipher_list, 0), "check_suiteb_cipher_list"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION, 0), "d2i_SSL_SESSION"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_octet_string, 0), "d2i_SSL_SESSION_get_octet_string"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_string, 0), "d2i_SSL_SESSION_get_string"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_do_dtls1_write, 0), "do_dtls1_write"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_do_ssl3_write, 0), "do_ssl3_write"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_accept, 0), "dtls1_accept"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_buffer_record, 0), "dtls1_buffer_record"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_check_timeout_num, 0), "dtls1_check_timeout_num"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_connect, 0), "dtls1_connect"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_do_write, 0), "dtls1_do_write"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_hello_verify, 0), "dtls1_get_hello_verify"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_message, 0), "dtls1_get_message"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_message_fragment, 0), "dtls1_get_message_fragment"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_heartbeat, 0), "dtls1_heartbeat"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_preprocess_fragment, 0), "dtls1_preprocess_fragment"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_process_record, 0), "dtls1_process_record"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_read_bytes, 0), "dtls1_read_bytes"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_send_hello_verify_request, 0), "dtls1_send_hello_verify_request"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_write_app_data_bytes, 0), "dtls1_write_app_data_bytes"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_fclose, 0), "fclose"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_fprintf, 0), "fprintf"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_i2d_SSL_SESSION, 0), "i2d_SSL_SESSION"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_printf, 0), "printf"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_read_authz, 0), "read_authz"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_accept, 0), "ssl23_accept"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_client_hello, 0), "ssl23_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_connect, 0), "ssl23_connect"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_client_hello, 0), "ssl23_get_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_server_hello, 0), "ssl23_get_server_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_v2_client_hello, 0), "ssl23_get_v2_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_peek, 0), "ssl23_peek"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_read, 0), "ssl23_read"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_write, 0), "ssl23_write"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_accept, 0), "ssl3_accept"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_callback_ctrl, 0), "ssl3_callback_ctrl"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_cert_verify_hash, 0), "ssl3_cert_verify_hash"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_change_cipher_state, 0), "ssl3_change_cipher_state"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_check_cert_and_algorithm, 0), "ssl3_check_cert_and_algorithm"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_check_client_hello, 0), "ssl3_check_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_connect, 0), "ssl3_connect"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_ctrl, 0), "ssl3_ctrl"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_ctx_ctrl, 0), "ssl3_ctx_ctrl"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_digest_cached_records, 0), "ssl3_digest_cached_records"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_do_change_cipher_spec, 0), "ssl3_do_change_cipher_spec"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_expect_change_cipher_spec, 0), "ssl3_expect_change_cipher_spec"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_generate_key_block, 0), "ssl3_generate_key_block"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_cert_status, 0), "ssl3_get_cert_status"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_cert_verify, 0), "ssl3_get_cert_verify"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_certificate_request, 0), "ssl3_get_certificate_request"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_channel_id, 0), "ssl3_get_channel_id"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_certificate, 0), "ssl3_get_client_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_hello, 0), "ssl3_get_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_key_exchange, 0), "ssl3_get_client_key_exchange"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_finished, 0), "ssl3_get_finished"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_initial_bytes, 0), "ssl3_get_initial_bytes"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_message, 0), "ssl3_get_message"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_new_session_ticket, 0), "ssl3_get_new_session_ticket"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_next_proto, 0), "ssl3_get_next_proto"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_record, 0), "ssl3_get_record"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_certificate, 0), "ssl3_get_server_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_done, 0), "ssl3_get_server_done"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_hello, 0), "ssl3_get_server_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_key_exchange, 0), "ssl3_get_server_key_exchange"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_v2_client_hello, 0), "ssl3_get_v2_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_handshake_mac, 0), "ssl3_handshake_mac"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_prf, 0), "ssl3_prf"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_bytes, 0), "ssl3_read_bytes"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_n, 0), "ssl3_read_n"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_cert_verify, 0), "ssl3_send_cert_verify"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_certificate_request, 0), "ssl3_send_certificate_request"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_channel_id, 0), "ssl3_send_channel_id"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_certificate, 0), "ssl3_send_client_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_hello, 0), "ssl3_send_client_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_key_exchange, 0), "ssl3_send_client_key_exchange"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_new_session_ticket, 0), "ssl3_send_new_session_ticket"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_certificate, 0), "ssl3_send_server_certificate"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_hello, 0), "ssl3_send_server_hello"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_key_exchange, 0), "ssl3_send_server_key_exchange"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_key_block, 0), "ssl3_setup_key_block"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_read_buffer, 0), "ssl3_setup_read_buffer"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_write_buffer, 0), "ssl3_setup_write_buffer"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_write_bytes, 0), "ssl3_write_bytes"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_write_pending, 0), "ssl3_write_pending"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_cert_chain, 0), "ssl_add_cert_chain"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_cert_to_buf, 0), "ssl_add_cert_to_buf"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_renegotiate_ext, 0), "ssl_add_clienthello_renegotiate_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_tlsext, 0), "ssl_add_clienthello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_use_srtp_ext, 0), "ssl_add_clienthello_use_srtp_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_renegotiate_ext, 0), "ssl_add_serverhello_renegotiate_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_tlsext, 0), "ssl_add_serverhello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_use_srtp_ext, 0), "ssl_add_serverhello_use_srtp_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_bad_method, 0), "ssl_bad_method"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_build_cert_chain, 0), "ssl_build_cert_chain"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_bytes_to_cipher_list, 0), "ssl_bytes_to_cipher_list"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_dup, 0), "ssl_cert_dup"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_inst, 0), "ssl_cert_inst"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_new, 0), "ssl_cert_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_check_serverhello_tlsext, 0), "ssl_check_serverhello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_check_srvr_ecc_cert_and_alg, 0), "ssl_check_srvr_ecc_cert_and_alg"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_process_rulestr, 0), "ssl_cipher_process_rulestr"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_strength_sort, 0), "ssl_cipher_strength_sort"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_create_cipher_list, 0), "ssl_create_cipher_list"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_master_secret, 0), "ssl_ctx_log_master_secret"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_rsa_client_key_exchange, 0), "ssl_ctx_log_rsa_client_key_exchange"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_make_profiles, 0), "ssl_ctx_make_profiles"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_new_session, 0), "ssl_get_new_session"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_prev_session, 0), "ssl_get_prev_session"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_server_cert_index, 0), "ssl_get_server_cert_index"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_sign_pkey, 0), "ssl_get_sign_pkey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_init_wbio_buffer, 0), "ssl_init_wbio_buffer"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_new, 0), "ssl_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_renegotiate_ext, 0), "ssl_parse_clienthello_renegotiate_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_tlsext, 0), "ssl_parse_clienthello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_use_srtp_ext, 0), "ssl_parse_clienthello_use_srtp_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_renegotiate_ext, 0), "ssl_parse_serverhello_renegotiate_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_tlsext, 0), "ssl_parse_serverhello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_use_srtp_ext, 0), "ssl_parse_serverhello_use_srtp_ext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_prepare_clienthello_tlsext, 0), "ssl_prepare_clienthello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_scan_clienthello_tlsext, 0), "ssl_scan_clienthello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_scan_serverhello_tlsext, 0), "ssl_scan_serverhello_tlsext"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_sess_cert_new, 0), "ssl_sess_cert_new"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_authz, 0), "ssl_set_authz"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_cert, 0), "ssl_set_cert"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_pkey, 0), "ssl_set_pkey"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_const_function, 0), "ssl_undefined_const_function"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_function, 0), "ssl_undefined_function"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_void_function, 0), "ssl_undefined_void_function"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_verify_cert_chain, 0), "ssl_verify_cert_chain"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls12_check_peer_sigalg, 0), "tls12_check_peer_sigalg"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_aead_ctx_init, 0), "tls1_aead_ctx_init"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_cert_verify_mac, 0), "tls1_cert_verify_mac"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state, 0), "tls1_change_cipher_state"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_aead, 0), "tls1_change_cipher_state_aead"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_cipher, 0), "tls1_change_cipher_state_cipher"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_check_duplicate_extensions, 0), "tls1_check_duplicate_extensions"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_enc, 0), "tls1_enc"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_export_keying_material, 0), "tls1_export_keying_material"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_get_server_supplemental_data, 0), "tls1_get_server_supplemental_data"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_heartbeat, 0), "tls1_heartbeat"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_prf, 0), "tls1_prf"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_send_server_supplemental_data, 0), "tls1_send_server_supplemental_data"},
-  {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_setup_key_block, 0), "tls1_setup_key_block"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APP_DATA_IN_HANDSHAKE), "APP_DATA_IN_HANDSHAKE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT), "ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_AUTHZ_DATA_TOO_LARGE), "AUTHZ_DATA_TOO_LARGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ALERT), "BAD_ALERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ALERT_RECORD), "BAD_ALERT_RECORD"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_AUTHENTICATION_TYPE), "BAD_AUTHENTICATION_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHANGE_CIPHER_SPEC), "BAD_CHANGE_CIPHER_SPEC"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHECKSUM), "BAD_CHECKSUM"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA), "BAD_DATA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK), "BAD_DATA_RETURNED_BY_CALLBACK"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DECOMPRESSION), "BAD_DECOMPRESSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_G_LENGTH), "BAD_DH_G_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_PUB_KEY_LENGTH), "BAD_DH_PUB_KEY_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_P_LENGTH), "BAD_DH_P_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DIGEST_LENGTH), "BAD_DIGEST_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DSA_SIGNATURE), "BAD_DSA_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECC_CERT), "BAD_ECC_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECDSA_SIGNATURE), "BAD_ECDSA_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECPOINT), "BAD_ECPOINT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HANDSHAKE_LENGTH), "BAD_HANDSHAKE_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HELLO_REQUEST), "BAD_HELLO_REQUEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_LENGTH), "BAD_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MAC_DECODE), "BAD_MAC_DECODE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MAC_LENGTH), "BAD_MAC_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MESSAGE_TYPE), "BAD_MESSAGE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PACKET_LENGTH), "BAD_PACKET_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PROTOCOL_VERSION_NUMBER), "BAD_PROTOCOL_VERSION_NUMBER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK_IDENTITY_HINT_LENGTH), "BAD_PSK_IDENTITY_HINT_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RESPONSE_ARGUMENT), "BAD_RESPONSE_ARGUMENT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_DECRYPT), "BAD_RSA_DECRYPT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_ENCRYPT), "BAD_RSA_ENCRYPT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_E_LENGTH), "BAD_RSA_E_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_MODULUS_LENGTH), "BAD_RSA_MODULUS_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_SIGNATURE), "BAD_RSA_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SIGNATURE), "BAD_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_A_LENGTH), "BAD_SRP_A_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_B_LENGTH), "BAD_SRP_B_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_G_LENGTH), "BAD_SRP_G_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_N_LENGTH), "BAD_SRP_N_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_S_LENGTH), "BAD_SRP_S_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_MKI_VALUE), "BAD_SRTP_MKI_VALUE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST), "BAD_SRTP_PROTECTION_PROFILE_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SSL_FILETYPE), "BAD_SSL_FILETYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SSL_SESSION_ID_LENGTH), "BAD_SSL_SESSION_ID_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_STATE), "BAD_STATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_VALUE), "BAD_VALUE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_WRITE_RETRY), "BAD_WRITE_RETRY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BIO_NOT_SET), "BIO_NOT_SET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG), "BLOCK_CIPHER_PAD_IS_WRONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BN_LIB), "BN_LIB"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CANNOT_SERIALIZE_PUBLIC_KEY), "CANNOT_SERIALIZE_PUBLIC_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_DN_LENGTH_MISMATCH), "CA_DN_LENGTH_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_DN_TOO_LONG), "CA_DN_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CCS_RECEIVED_EARLY), "CCS_RECEIVED_EARLY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED), "CERTIFICATE_VERIFY_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_CB_ERROR), "CERT_CB_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_LENGTH_MISMATCH), "CERT_LENGTH_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHALLENGE_IS_DIFFERENT), "CHALLENGE_IS_DIFFERENT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHANNEL_ID_NOT_P256), "CHANNEL_ID_NOT_P256"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHANNEL_ID_SIGNATURE_INVALID), "CHANNEL_ID_SIGNATURE_INVALID"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_CODE_WRONG_LENGTH), "CIPHER_CODE_WRONG_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_OR_HASH_UNAVAILABLE), "CIPHER_OR_HASH_UNAVAILABLE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_TABLE_SRC_ERROR), "CIPHER_TABLE_SRC_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CLIENTHELLO_PARSE_FAILED), "CLIENTHELLO_PARSE_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CLIENTHELLO_TLSEXT), "CLIENTHELLO_TLSEXT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSED_LENGTH_TOO_LONG), "COMPRESSED_LENGTH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_DISABLED), "COMPRESSION_DISABLED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_FAILURE), "COMPRESSION_FAILURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE), "COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_LIBRARY_ERROR), "COMPRESSION_LIBRARY_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_ID_IS_DIFFERENT), "CONNECTION_ID_IS_DIFFERENT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_REJECTED), "CONNECTION_REJECTED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_TYPE_NOT_SET), "CONNECTION_TYPE_NOT_SET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_MISMATCH), "COOKIE_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_D2I_ECDSA_SIG), "D2I_ECDSA_SIG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED), "DATA_BETWEEN_CCS_AND_FINISHED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_LENGTH_TOO_LONG), "DATA_LENGTH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECODE_ERROR), "DECODE_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED), "DECRYPTION_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC), "DECRYPTION_FAILED_OR_BAD_RECORD_MAC"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG), "DH_PUBLIC_VALUE_LENGTH_IS_WRONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DIGEST_CHECK_FAILED), "DIGEST_CHECK_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DTLS_MESSAGE_TOO_BIG), "DTLS_MESSAGE_TOO_BIG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DUPLICATE_COMPRESSION_ID), "DUPLICATE_COMPRESSION_ID"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_NOT_FOR_KEY_AGREEMENT), "ECC_CERT_NOT_FOR_KEY_AGREEMENT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_NOT_FOR_SIGNING), "ECC_CERT_NOT_FOR_SIGNING"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE), "ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE), "ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER), "ECGROUP_TOO_LARGE_FOR_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST), "EMPTY_SRTP_PROTECTION_PROFILE_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ENCRYPTED_LENGTH_TOO_LONG), "ENCRYPTED_LENGTH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_GENERATING_TMP_RSA_KEY), "ERROR_GENERATING_TMP_RSA_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST), "ERROR_IN_RECEIVED_CIPHER_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EVP_DIGESTSIGNFINAL_FAILED), "EVP_DIGESTSIGNFINAL_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EVP_DIGESTSIGNINIT_FAILED), "EVP_DIGESTSIGNINIT_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCESSIVE_MESSAGE_SIZE), "EXCESSIVE_MESSAGE_SIZE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTRA_DATA_IN_MESSAGE), "EXTRA_DATA_IN_MESSAGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOST_NOT_SUPPORTED), "GOST_NOT_SUPPORTED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_A_FIN_BEFORE_A_CCS), "GOT_A_FIN_BEFORE_A_CCS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS), "GOT_CHANNEL_ID_BEFORE_A_CCS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS), "GOT_NEXT_PROTO_BEFORE_A_CCS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION), "GOT_NEXT_PROTO_WITHOUT_EXTENSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO), "HANDSHAKE_FAILURE_ON_CLIENT_HELLO"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS), "HANDSHAKE_RECORD_BEFORE_CCS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTPS_PROXY_REQUEST), "HTTPS_PROXY_REQUEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTP_REQUEST), "HTTP_REQUEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_PADDING), "ILLEGAL_PADDING"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_SUITEB_DIGEST), "ILLEGAL_SUITEB_DIGEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INAPPROPRIATE_FALLBACK), "INAPPROPRIATE_FALLBACK"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_COMPRESSION), "INCONSISTENT_COMPRESSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_AUDIT_PROOF), "INVALID_AUDIT_PROOF"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_AUTHZ_DATA), "INVALID_AUTHZ_DATA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CHALLENGE_LENGTH), "INVALID_CHALLENGE_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "INVALID_COMMAND"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMPRESSION_ALGORITHM), "INVALID_COMPRESSION_ALGORITHM"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_MESSAGE), "INVALID_MESSAGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_NULL_CMD_NAME), "INVALID_NULL_CMD_NAME"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_PURPOSE), "INVALID_PURPOSE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA), "INVALID_SERVERINFO_DATA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME), "INVALID_SRP_USERNAME"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SSL_SESSION), "INVALID_SSL_SESSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE), "INVALID_STATUS_RESPONSE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TICKET_KEYS_LENGTH), "INVALID_TICKET_KEYS_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TRUST), "INVALID_TRUST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KEY_ARG_TOO_LONG), "KEY_ARG_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5), "KRB5"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_CC_PRINC), "KRB5_C_CC_PRINC"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_GET_CRED), "KRB5_C_GET_CRED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_INIT), "KRB5_C_INIT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_MK_REQ), "KRB5_C_MK_REQ"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_BAD_TICKET), "KRB5_S_BAD_TICKET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_INIT), "KRB5_S_INIT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_RD_REQ), "KRB5_S_RD_REQ"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_EXPIRED), "KRB5_S_TKT_EXPIRED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_NYV), "KRB5_S_TKT_NYV"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_SKEW), "KRB5_S_TKT_SKEW"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_MISMATCH), "LENGTH_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_TOO_SHORT), "LENGTH_TOO_SHORT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_BUG), "LIBRARY_BUG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_HAS_NO_CIPHERS), "LIBRARY_HAS_NO_CIPHERS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MESSAGE_TOO_LONG), "MESSAGE_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_DSA_CERT), "MISSING_DH_DSA_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_KEY), "MISSING_DH_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_RSA_CERT), "MISSING_DH_RSA_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DSA_SIGNING_CERT), "MISSING_DSA_SIGNING_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDH_CERT), "MISSING_ECDH_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDSA_SIGNING_CERT), "MISSING_ECDSA_SIGNING_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_EXPORT_TMP_DH_KEY), "MISSING_EXPORT_TMP_DH_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_EXPORT_TMP_RSA_KEY), "MISSING_EXPORT_TMP_RSA_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE), "MISSING_RSA_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT), "MISSING_RSA_ENCRYPTING_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_SIGNING_CERT), "MISSING_RSA_SIGNING_CERT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SRP_PARAM), "MISSING_SRP_PARAM"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_DH_KEY), "MISSING_TMP_DH_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_ECDH_KEY), "MISSING_TMP_ECDH_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_KEY), "MISSING_TMP_RSA_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_PKEY), "MISSING_TMP_RSA_PKEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_VERIFY_MESSAGE), "MISSING_VERIFY_MESSAGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS), "MIXED_SPECIAL_OPERATOR_WITH_GROUPS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MTU_TOO_SMALL), "MTU_TOO_SMALL"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MULTIPLE_SGC_RESTARTS), "MULTIPLE_SGC_RESTARTS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NESTED_GROUP), "NESTED_GROUP"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NON_SSLV2_INITIAL_PACKET), "NON_SSLV2_INITIAL_PACKET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED), "NO_CERTIFICATES_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_ASSIGNED), "NO_CERTIFICATE_ASSIGNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_RETURNED), "NO_CERTIFICATE_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_SET), "NO_CERTIFICATE_SET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_SPECIFIED), "NO_CERTIFICATE_SPECIFIED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_AVAILABLE), "NO_CIPHERS_AVAILABLE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_PASSED), "NO_CIPHERS_PASSED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_SPECIFIED), "NO_CIPHERS_SPECIFIED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHER_LIST), "NO_CIPHER_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHER_MATCH), "NO_CIPHER_MATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CLIENT_CERT_METHOD), "NO_CLIENT_CERT_METHOD"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CLIENT_CERT_RECEIVED), "NO_CLIENT_CERT_RECEIVED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_COMPRESSION_SPECIFIED), "NO_COMPRESSION_SPECIFIED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER), "NO_GOST_CERTIFICATE_SENT_BY_PEER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_METHOD_SPECIFIED), "NO_METHOD_SPECIFIED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_P256_SUPPORT), "NO_P256_SUPPORT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PEM_EXTENSIONS), "NO_PEM_EXTENSIONS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PRIVATEKEY), "NO_PRIVATEKEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PRIVATE_KEY_ASSIGNED), "NO_PRIVATE_KEY_ASSIGNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PROTOCOLS_AVAILABLE), "NO_PROTOCOLS_AVAILABLE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PUBLICKEY), "NO_PUBLICKEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_RENEGOTIATION), "NO_RENEGOTIATION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_REQUIRED_DIGEST), "NO_REQUIRED_DIGEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_CIPHER), "NO_SHARED_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_SIGATURE_ALGORITHMS), "NO_SHARED_SIGATURE_ALGORITHMS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SRTP_PROFILES), "NO_SRTP_PROFILES"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_VERIFY_CALLBACK), "NO_VERIFY_CALLBACK"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_CTX), "NULL_SSL_CTX"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_METHOD_PASSED), "NULL_SSL_METHOD_PASSED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED), "OLD_SESSION_CIPHER_NOT_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED), "OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE), "ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE), "ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE), "ONLY_TLS_ALLOWED_IN_FIPS_MODE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OPAQUE_PRF_INPUT_TOO_LONG), "OPAQUE_PRF_INPUT_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PACKET_LENGTH_TOO_LONG), "PACKET_LENGTH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PARSE_TLSEXT), "PARSE_TLSEXT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PATH_TOO_LONG), "PATH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE), "PEER_DID_NOT_RETURN_A_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR), "PEER_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_CERTIFICATE), "PEER_ERROR_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_NO_CERTIFICATE), "PEER_ERROR_NO_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_NO_CIPHER), "PEER_ERROR_NO_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE), "PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_BAD_PREFIX), "PEM_NAME_BAD_PREFIX"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_TOO_SHORT), "PEM_NAME_TOO_SHORT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PRE_MAC_LENGTH_TOO_LONG), "PRE_MAC_LENGTH_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS), "PROBLEMS_MAPPING_CIPHER_FUNCTIONS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN), "PROTOCOL_IS_SHUTDOWN"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND), "PSK_IDENTITY_NOT_FOUND"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_CLIENT_CB), "PSK_NO_CLIENT_CB"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_SERVER_CB), "PSK_NO_SERVER_CB"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_ENCRYPT_ERROR), "PUBLIC_KEY_ENCRYPT_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_IS_NOT_RSA), "PUBLIC_KEY_IS_NOT_RSA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_NOT_RSA), "PUBLIC_KEY_NOT_RSA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_BIO_NOT_SET), "READ_BIO_NOT_SET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_TIMEOUT_EXPIRED), "READ_TIMEOUT_EXPIRED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_WRONG_PACKET_TYPE), "READ_WRONG_PACKET_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_LENGTH_MISMATCH), "RECORD_LENGTH_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_TOO_LARGE), "RECORD_TOO_LARGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_TOO_SMALL), "RECORD_TOO_SMALL"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATE_EXT_TOO_LONG), "RENEGOTIATE_EXT_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_ENCODING_ERR), "RENEGOTIATION_ENCODING_ERR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_MISMATCH), "RENEGOTIATION_MISMATCH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_CIPHER_MISSING), "REQUIRED_CIPHER_MISSING"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING), "REQUIRED_COMPRESSSION_ALGORITHM_MISSING"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CERT_LENGTH_NOT_ZERO), "REUSE_CERT_LENGTH_NOT_ZERO"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CERT_TYPE_NOT_ZERO), "REUSE_CERT_TYPE_NOT_ZERO"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CIPHER_LIST_NOT_ZERO), "REUSE_CIPHER_LIST_NOT_ZERO"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING), "SCSV_RECEIVED_WHEN_RENEGOTIATING"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SERVERHELLO_TLSEXT), "SERVERHELLO_TLSEXT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED), "SESSION_ID_CONTEXT_UNINITIALIZED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SESSION_MAY_NOT_BE_CREATED), "SESSION_MAY_NOT_BE_CREATED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ), "SHORT_READ"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_ALGORITHMS_ERROR), "SIGNATURE_ALGORITHMS_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE), "SIGNATURE_FOR_NON_SIGNING_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRP_A_CALC), "SRP_A_CALC"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES), "SRTP_COULD_NOT_ALLOCATE_PROFILES"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG), "SRTP_PROTECTION_PROFILE_LIST_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE), "SRTP_UNKNOWN_PROTECTION_PROFILE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL23_DOING_SESSION_ID_REUSE), "SSL23_DOING_SESSION_ID_REUSE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL2_CONNECTION_ID_TOO_LONG), "SSL2_CONNECTION_ID_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_ECPOINTFORMAT), "SSL3_EXT_INVALID_ECPOINTFORMAT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME), "SSL3_EXT_INVALID_SERVERNAME"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE), "SSL3_EXT_INVALID_SERVERNAME_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_SESSION_ID_TOO_LONG), "SSL3_SESSION_ID_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_SESSION_ID_TOO_SHORT), "SSL3_SESSION_ID_TOO_SHORT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE), "SSLV3_ALERT_BAD_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC), "SSLV3_ALERT_BAD_RECORD_MAC"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED), "SSLV3_ALERT_CERTIFICATE_EXPIRED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED), "SSLV3_ALERT_CERTIFICATE_REVOKED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN), "SSLV3_ALERT_CERTIFICATE_UNKNOWN"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CLOSE_NOTIFY), "SSLV3_ALERT_CLOSE_NOTIFY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE), "SSLV3_ALERT_DECOMPRESSION_FAILURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE), "SSLV3_ALERT_HANDSHAKE_FAILURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER), "SSLV3_ALERT_ILLEGAL_PARAMETER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_NO_CERTIFICATE), "SSLV3_ALERT_NO_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE), "SSLV3_ALERT_UNEXPECTED_MESSAGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE), "SSLV3_ALERT_UNSUPPORTED_CERTIFICATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION), "SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_HANDSHAKE_FAILURE), "SSL_HANDSHAKE_FAILURE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS), "SSL_LIBRARY_HAS_NO_CIPHERS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED), "SSL_SESSION_ID_CALLBACK_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONFLICT), "SSL_SESSION_ID_CONFLICT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG), "SSL_SESSION_ID_CONTEXT_TOO_LONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH), "SSL_SESSION_ID_HAS_BAD_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_IS_DIFFERENT), "SSL_SESSION_ID_IS_DIFFERENT"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_ACCESS_DENIED), "TLSV1_ALERT_ACCESS_DENIED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECODE_ERROR), "TLSV1_ALERT_DECODE_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED), "TLSV1_ALERT_DECRYPTION_FAILED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPT_ERROR), "TLSV1_ALERT_DECRYPT_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION), "TLSV1_ALERT_EXPORT_RESTRICTION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK), "TLSV1_ALERT_INAPPROPRIATE_FALLBACK"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY), "TLSV1_ALERT_INSUFFICIENT_SECURITY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INTERNAL_ERROR), "TLSV1_ALERT_INTERNAL_ERROR"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION), "TLSV1_ALERT_NO_RENEGOTIATION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION), "TLSV1_ALERT_PROTOCOL_VERSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW), "TLSV1_ALERT_RECORD_OVERFLOW"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_UNKNOWN_CA), "TLSV1_ALERT_UNKNOWN_CA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_USER_CANCELLED), "TLSV1_ALERT_USER_CANCELLED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE), "TLSV1_BAD_CERTIFICATE_HASH_VALUE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE), "TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE), "TLSV1_CERTIFICATE_UNOBTAINABLE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNRECOGNIZED_NAME), "TLSV1_UNRECOGNIZED_NAME"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNSUPPORTED_EXTENSION), "TLSV1_UNSUPPORTED_EXTENSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER), "TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL), "TLS_ILLEGAL_EXPORTER_LABEL"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST), "TLS_INVALID_ECPOINTFORMAT_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST), "TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG), "TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_EMPTY_FRAGMENTS), "TOO_MANY_EMPTY_FRAGMENTS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TRIED_TO_USE_UNSUPPORTED_CIPHER), "TRIED_TO_USE_UNSUPPORTED_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_DECODE_DH_CERTS), "UNABLE_TO_DECODE_DH_CERTS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_DECODE_ECDH_CERTS), "UNABLE_TO_DECODE_ECDH_CERTS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_EXTRACT_PUBLIC_KEY), "UNABLE_TO_EXTRACT_PUBLIC_KEY"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_DH_PARAMETERS), "UNABLE_TO_FIND_DH_PARAMETERS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS), "UNABLE_TO_FIND_ECDH_PARAMETERS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS), "UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_SSL_METHOD), "UNABLE_TO_FIND_SSL_METHOD"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL2_MD5_ROUTINES"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL3_MD5_ROUTINES"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES), "UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_GROUP_CLOSE), "UNEXPECTED_GROUP_CLOSE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), "UNEXPECTED_MESSAGE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP), "UNEXPECTED_OPERATOR_IN_GROUP"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_RECORD), "UNEXPECTED_RECORD"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNINITIALIZED), "UNINITIALIZED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_ALERT_TYPE), "UNKNOWN_ALERT_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_AUTHZ_DATA_TYPE), "UNKNOWN_AUTHZ_DATA_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CERTIFICATE_TYPE), "UNKNOWN_CERTIFICATE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_RETURNED), "UNKNOWN_CIPHER_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_TYPE), "UNKNOWN_CIPHER_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CMD_NAME), "UNKNOWN_CMD_NAME"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_DIGEST), "UNKNOWN_DIGEST"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE), "UNKNOWN_KEY_EXCHANGE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PKEY_TYPE), "UNKNOWN_PKEY_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PROTOCOL), "UNKNOWN_PROTOCOL"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_REMOTE_ERROR_TYPE), "UNKNOWN_REMOTE_ERROR_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_SSL_VERSION), "UNKNOWN_SSL_VERSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_STATE), "UNKNOWN_STATE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_SUPPLEMENTAL_DATA_TYPE), "UNKNOWN_SUPPLEMENTAL_DATA_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNPROCESSED_HANDSHAKE_DATA), "UNPROCESSED_HANDSHAKE_DATA"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED), "UNSAFE_LEGACY_RENEGOTIATION_DISABLED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_CIPHER), "UNSUPPORTED_CIPHER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM), "UNSUPPORTED_COMPRESSION_ALGORITHM"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_DIGEST_TYPE), "UNSUPPORTED_DIGEST_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE), "UNSUPPORTED_ELLIPTIC_CURVE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_PROTOCOL), "UNSUPPORTED_PROTOCOL"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_SSL_VERSION), "UNSUPPORTED_SSL_VERSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_STATUS_TYPE), "UNSUPPORTED_STATUS_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_USE_SRTP_NOT_NEGOTIATED), "USE_SRTP_NOT_NEGOTIATED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRITE_BIO_NOT_SET), "WRITE_BIO_NOT_SET"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CERTIFICATE_TYPE), "WRONG_CERTIFICATE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED), "WRONG_CIPHER_RETURNED"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "WRONG_CURVE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_MESSAGE_TYPE), "WRONG_MESSAGE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_NUMBER_OF_KEY_BITS), "WRONG_NUMBER_OF_KEY_BITS"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH), "WRONG_SIGNATURE_LENGTH"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE), "WRONG_SIGNATURE_SIZE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_TYPE), "WRONG_SIGNATURE_TYPE"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SSL_VERSION), "WRONG_SSL_VERSION"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_VERSION_NUMBER), "WRONG_VERSION_NUMBER"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_LIB), "X509_LIB"},
-  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS), "X509_VERIFICATION_SETUP_PROBLEMS"},
-  {0, NULL},
-};
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 35eb1ec..6c8e2c9 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -138,19 +138,22 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <openssl/bytestring.h>
 #include <openssl/dh.h>
-#include <openssl/engine.h>
+#include <openssl/err.h>
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509v3.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
+#include "../crypto/internal.h"
+
 
 /* Some error codes are special. Ensure the make_errors.go script never
  * regresses this. */
@@ -158,6 +161,12 @@
                            SSL_AD_NO_RENEGOTIATION + SSL_AD_REASON_OFFSET,
                        ssl_alert_reason_code_mismatch);
 
+/* kMaxHandshakeSize is the maximum size, in bytes, of a handshake message. */
+static const size_t kMaxHandshakeSize = (1u << 24) - 1;
+
+static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl = CRYPTO_EX_DATA_CLASS_INIT;
+static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl_ctx = CRYPTO_EX_DATA_CLASS_INIT;
+
 int SSL_clear(SSL *s) {
   if (s->method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_clear, SSL_R_NO_METHOD_SPECIFIED);
@@ -199,21 +208,17 @@
   s->rwstate = SSL_NOTHING;
   s->rstate = SSL_ST_READ_HEADER;
 
-  if (s->init_buf != NULL) {
-    BUF_MEM_free(s->init_buf);
-    s->init_buf = NULL;
-  }
+  BUF_MEM_free(s->init_buf);
+  s->init_buf = NULL;
 
   s->packet = NULL;
   s->packet_length = 0;
 
   ssl_clear_cipher_ctx(s);
 
-  if (s->next_proto_negotiated) {
-    OPENSSL_free(s->next_proto_negotiated);
-    s->next_proto_negotiated = NULL;
-    s->next_proto_negotiated_len = 0;
-  }
+  OPENSSL_free(s->next_proto_negotiated);
+  s->next_proto_negotiated = NULL;
+  s->next_proto_negotiated_len = 0;
 
   /* The s->d1->mtu is simultaneously configuration (preserved across
    * clear) and connection-specific state (gets reset).
@@ -265,21 +270,9 @@
   s->mode = ctx->mode;
   s->max_cert_list = ctx->max_cert_list;
 
-  if (ctx->cert != NULL) {
-    /* Earlier library versions used to copy the pointer to the CERT, not its
-     * contents; only when setting new parameters for the per-SSL copy,
-     * ssl_cert_new would be called (and the direct reference to the
-     * per-SSL_CTX settings would be lost, but those still were indirectly
-     * accessed for various purposes, and for that reason they used to be known
-     * as s->ctx->default_cert). Now we don't look at the SSL_CTX's CERT after
-     * having duplicated it once. */
-
-    s->cert = ssl_cert_dup(ctx->cert);
-    if (s->cert == NULL) {
-      goto err;
-    }
-  } else {
-    s->cert = NULL; /* Cannot really happen (see SSL_CTX_new) */
+  s->cert = ssl_cert_dup(ctx->cert);
+  if (s->cert == NULL) {
+    goto err;
   }
 
   s->read_ahead = ctx->read_ahead;
@@ -302,8 +295,6 @@
 
   CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX);
   s->ctx = ctx;
-  s->tlsext_debug_cb = 0;
-  s->tlsext_debug_arg = NULL;
   s->tlsext_ticket_expected = 0;
   CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX);
   s->initial_ctx = ctx;
@@ -345,12 +336,10 @@
   s->enc_method = ssl3_get_enc_method(s->version);
   assert(s->enc_method != NULL);
 
-  s->references = 1;
-
   s->rwstate = SSL_NOTHING;
   s->rstate = SSL_ST_READ_HEADER;
 
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class_ssl, s, &s->ex_data);
 
   s->psk_identity_hint = NULL;
   if (ctx->psk_identity_hint) {
@@ -364,7 +353,8 @@
 
   s->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
   if (ctx->tlsext_channel_id_private) {
-    s->tlsext_channel_id_private = EVP_PKEY_dup(ctx->tlsext_channel_id_private);
+    s->tlsext_channel_id_private =
+        EVP_PKEY_up_ref(ctx->tlsext_channel_id_private);
   }
 
   s->signed_cert_timestamps_enabled = s->ctx->signed_cert_timestamps_enabled;
@@ -373,9 +363,7 @@
   return s;
 
 err:
-  if (s != NULL) {
-    SSL_free(s);
-  }
+  SSL_free(s);
   OPENSSL_PUT_ERROR(SSL, SSL_new, ERR_R_MALLOC_FAILURE);
 
   return NULL;
@@ -415,9 +403,7 @@
 }
 
 int SSL_set_generate_session_id(SSL *ssl, GEN_SESSION_CB cb) {
-  CRYPTO_w_lock(CRYPTO_LOCK_SSL);
   ssl->generate_session_id = cb;
-  CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
   return 1;
 }
 
@@ -470,6 +456,9 @@
 
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list) {
+  if (cipher_list == NULL) {
+    return;
+  }
   sk_SSL_CIPHER_free(cipher_list->ciphers);
   OPENSSL_free(cipher_list->in_group_flags);
   OPENSSL_free(cipher_list);
@@ -499,17 +488,12 @@
   return ret;
 
 err:
-  if (ret && ret->ciphers) {
-    sk_SSL_CIPHER_free(ret->ciphers);
-  }
-  if (ret) {
-    OPENSSL_free(ret);
-  }
+  ssl_cipher_preference_list_free(ret);
   return NULL;
 }
 
 struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers(
-    STACK_OF(SSL_CIPHER) * ciphers) {
+    STACK_OF(SSL_CIPHER) *ciphers) {
   struct ssl_cipher_preference_list_st *ret = NULL;
   size_t n = sk_SSL_CIPHER_num(ciphers);
 
@@ -531,12 +515,7 @@
   return ret;
 
 err:
-  if (ret && ret->ciphers) {
-    sk_SSL_CIPHER_free(ret->ciphers);
-  }
-  if (ret) {
-    OPENSSL_free(ret);
-  }
+  ssl_cipher_preference_list_free(ret);
   return NULL;
 }
 
@@ -547,22 +526,13 @@
 void SSL_certs_clear(SSL *s) { ssl_cert_clear_certs(s->cert); }
 
 void SSL_free(SSL *s) {
-  int i;
-
   if (s == NULL) {
     return;
   }
 
-  i = CRYPTO_add(&s->references, -1, CRYPTO_LOCK_SSL);
-  if (i > 0) {
-    return;
-  }
+  X509_VERIFY_PARAM_free(s->param);
 
-  if (s->param) {
-    X509_VERIFY_PARAM_free(s->param);
-  }
-
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class_ssl, s, &s->ex_data);
 
   if (s->bbio != NULL) {
     /* If the buffering BIO is in place, pop it off */
@@ -573,74 +543,40 @@
     s->bbio = NULL;
   }
 
-  if (s->rbio != NULL) {
-    BIO_free_all(s->rbio);
-  }
-
-  if (s->wbio != NULL && s->wbio != s->rbio) {
+  int free_wbio = s->wbio != s->rbio;
+  BIO_free_all(s->rbio);
+  if (free_wbio) {
     BIO_free_all(s->wbio);
   }
 
-  if (s->init_buf != NULL) {
-    BUF_MEM_free(s->init_buf);
-  }
+  BUF_MEM_free(s->init_buf);
 
   /* add extra stuff */
-  if (s->cipher_list != NULL) {
-    ssl_cipher_preference_list_free(s->cipher_list);
-  }
-  if (s->cipher_list_by_id != NULL) {
-    sk_SSL_CIPHER_free(s->cipher_list_by_id);
-  }
+  ssl_cipher_preference_list_free(s->cipher_list);
+  sk_SSL_CIPHER_free(s->cipher_list_by_id);
 
-  if (s->session != NULL) {
-    ssl_clear_bad_session(s);
-    SSL_SESSION_free(s->session);
-  }
+  ssl_clear_bad_session(s);
+  SSL_SESSION_free(s->session);
 
   ssl_clear_cipher_ctx(s);
 
-  if (s->cert != NULL) {
-    ssl_cert_free(s->cert);
-  }
+  ssl_cert_free(s->cert);
 
-  if (s->tlsext_hostname) {
-    OPENSSL_free(s->tlsext_hostname);
-  }
-  if (s->initial_ctx) {
-    SSL_CTX_free(s->initial_ctx);
-  }
-  if (s->tlsext_ecpointformatlist) {
-    OPENSSL_free(s->tlsext_ecpointformatlist);
-  }
-  if (s->tlsext_ellipticcurvelist) {
-    OPENSSL_free(s->tlsext_ellipticcurvelist);
-  }
-  if (s->alpn_client_proto_list) {
-    OPENSSL_free(s->alpn_client_proto_list);
-  }
-  if (s->tlsext_channel_id_private) {
-    EVP_PKEY_free(s->tlsext_channel_id_private);
-  }
-  if (s->psk_identity_hint) {
-    OPENSSL_free(s->psk_identity_hint);
-  }
-  if (s->client_CA != NULL) {
-    sk_X509_NAME_pop_free(s->client_CA, X509_NAME_free);
-  }
-  if (s->next_proto_negotiated) {
-    OPENSSL_free(s->next_proto_negotiated);
-  }
-  if (s->srtp_profiles) {
-    sk_SRTP_PROTECTION_PROFILE_free(s->srtp_profiles);
-  }
+  OPENSSL_free(s->tlsext_hostname);
+  SSL_CTX_free(s->initial_ctx);
+  OPENSSL_free(s->tlsext_ecpointformatlist);
+  OPENSSL_free(s->tlsext_ellipticcurvelist);
+  OPENSSL_free(s->alpn_client_proto_list);
+  EVP_PKEY_free(s->tlsext_channel_id_private);
+  OPENSSL_free(s->psk_identity_hint);
+  sk_X509_NAME_pop_free(s->client_CA, X509_NAME_free);
+  OPENSSL_free(s->next_proto_negotiated);
+  sk_SRTP_PROTECTION_PROFILE_free(s->srtp_profiles);
 
   if (s->method != NULL) {
     s->method->ssl_free(s);
   }
-  if (s->ctx) {
-    SSL_CTX_free(s->ctx);
-  }
+  SSL_CTX_free(s->ctx);
 
   OPENSSL_free(s);
 }
@@ -654,10 +590,10 @@
     }
   }
 
-  if (s->rbio != NULL && s->rbio != rbio) {
+  if (s->rbio != rbio) {
     BIO_free_all(s->rbio);
   }
-  if (s->wbio != NULL && s->wbio != wbio && s->rbio != s->wbio) {
+  if (s->wbio != wbio && s->rbio != s->wbio) {
     BIO_free_all(s->wbio);
   }
   s->rbio = rbio;
@@ -822,10 +758,14 @@
   X509_VERIFY_PARAM_set_depth(s->param, depth);
 }
 
-void SSL_set_read_ahead(SSL *s, int yes) { s->read_ahead = yes; }
+int SSL_CTX_get_read_ahead(const SSL_CTX *ctx) { return ctx->read_ahead; }
 
 int SSL_get_read_ahead(const SSL *s) { return s->read_ahead; }
 
+void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { ctx->read_ahead = !!yes; }
+
+void SSL_set_read_ahead(SSL *s, int yes) { s->read_ahead = !!yes; }
+
 int SSL_pending(const SSL *s) {
   /* SSL_pending cannot work properly if read-ahead is enabled
    * (SSL_[CTX_]ctrl(..., SSL_CTRL_SET_READ_AHEAD, 1, NULL)), and it is
@@ -851,8 +791,8 @@
   return X509_up_ref(r);
 }
 
-STACK_OF(X509) * SSL_get_peer_cert_chain(const SSL *s) {
-  STACK_OF(X509) * r;
+STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s) {
+  STACK_OF(X509) *r;
 
   if (s == NULL || s->session == NULL || s->session->sess_cert == NULL) {
     r = NULL;
@@ -919,7 +859,7 @@
   }
 
   if (s->handshake_func != s->method->ssl_accept) {
-    OPENSSL_PUT_ERROR(SSL, SSL_connect, ERR_R_INTERNAL_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_accept, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
@@ -1005,6 +945,12 @@
 }
 
 int SSL_renegotiate(SSL *s) {
+  if (SSL_IS_DTLS(s)) {
+    /* Renegotiation is not supported for DTLS. */
+    OPENSSL_PUT_ERROR(SSL, SSL_renegotiate, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
   if (s->renegotiate == 0) {
     s->renegotiate = 1;
   }
@@ -1013,236 +959,146 @@
   return s->method->ssl_renegotiate(s);
 }
 
-int SSL_renegotiate_abbreviated(SSL *s) {
-  if (s->renegotiate == 0) {
-    s->renegotiate = 1;
-  }
-
-  s->new_session = 0;
-  return s->method->ssl_renegotiate(s);
-}
-
 int SSL_renegotiate_pending(SSL *s) {
   /* becomes true when negotiation is requested; false again once a handshake
    * has finished */
   return s->renegotiate != 0;
 }
 
-long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) {
-  long l;
-
-  switch (cmd) {
-    case SSL_CTRL_GET_READ_AHEAD:
-      return s->read_ahead;
-
-    case SSL_CTRL_SET_READ_AHEAD:
-      l = s->read_ahead;
-      s->read_ahead = larg;
-      return l;
-
-    case SSL_CTRL_SET_MSG_CALLBACK_ARG:
-      s->msg_callback_arg = parg;
-      return 1;
-
-    case SSL_CTRL_OPTIONS:
-      return s->options |= larg;
-
-    case SSL_CTRL_CLEAR_OPTIONS:
-      return s->options &= ~larg;
-
-    case SSL_CTRL_MODE:
-      return s->mode |= larg;
-
-    case SSL_CTRL_CLEAR_MODE:
-      return s->mode &= ~larg;
-
-    case SSL_CTRL_GET_MAX_CERT_LIST:
-      return s->max_cert_list;
-
-    case SSL_CTRL_SET_MAX_CERT_LIST:
-      l = s->max_cert_list;
-      s->max_cert_list = larg;
-      return l;
-
-    case SSL_CTRL_SET_MTU:
-      if (larg < (long)dtls1_min_mtu()) {
-        return 0;
-      }
-      if (SSL_IS_DTLS(s)) {
-        s->d1->mtu = larg;
-        return larg;
-      }
-      return 0;
-
-    case SSL_CTRL_SET_MAX_SEND_FRAGMENT:
-      if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH) {
-        return 0;
-      }
-      s->max_send_fragment = larg;
-      return 1;
-
-    case SSL_CTRL_GET_RI_SUPPORT:
-      if (s->s3) {
-        return s->s3->send_connection_binding;
-      }
-      return 0;
-
-    case SSL_CTRL_CERT_FLAGS:
-      return s->cert->cert_flags |= larg;
-
-    case SSL_CTRL_CLEAR_CERT_FLAGS:
-      return s->cert->cert_flags &= ~larg;
-
-    case SSL_CTRL_GET_RAW_CIPHERLIST:
-      if (parg) {
-        if (s->cert->ciphers_raw == NULL) {
-          return 0;
-        }
-        *(uint8_t **)parg = s->cert->ciphers_raw;
-        return (int)s->cert->ciphers_rawlen;
-      }
-
-      /* Passing a NULL |parg| returns the size of a single
-       * cipher suite value. */
-      return 2;
-
-    default:
-      return s->method->ssl_ctrl(s, cmd, larg, parg);
-  }
+uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
+  ctx->options |= options;
+  return ctx->options;
 }
 
-long SSL_callback_ctrl(SSL *s, int cmd, void (*fp)(void)) {
-  switch (cmd) {
-    case SSL_CTRL_SET_MSG_CALLBACK:
-      s->msg_callback =
-          (void (*)(int write_p, int version, int content_type, const void *buf,
-                    size_t len, SSL *ssl, void *arg))(fp);
-      return 1;
+uint32_t SSL_set_options(SSL *ssl, uint32_t options) {
+  ssl->options |= options;
+  return ssl->options;
+}
 
-    default:
-      return s->method->ssl_callback_ctrl(s, cmd, fp);
+uint32_t SSL_CTX_clear_options(SSL_CTX *ctx, uint32_t options) {
+  ctx->options &= ~options;
+  return ctx->options;
+}
+
+uint32_t SSL_clear_options(SSL *ssl, uint32_t options) {
+  ssl->options &= ~options;
+  return ssl->options;
+}
+
+uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) { return ctx->options; }
+
+uint32_t SSL_get_options(const SSL *ssl) { return ssl->options; }
+
+uint32_t SSL_CTX_set_mode(SSL_CTX *ctx, uint32_t mode) {
+  ctx->mode |= mode;
+  return ctx->mode;
+}
+
+uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) {
+  ssl->mode |= mode;
+  return ssl->mode;
+}
+
+uint32_t SSL_CTX_clear_mode(SSL_CTX *ctx, uint32_t mode) {
+  ctx->mode &= ~mode;
+  return ctx->mode;
+}
+
+uint32_t SSL_clear_mode(SSL *ssl, uint32_t mode) {
+  ssl->mode &= ~mode;
+  return ssl->mode;
+}
+
+uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return ctx->mode; }
+
+uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; }
+
+size_t SSL_CTX_get_max_cert_list(const SSL_CTX *ctx) {
+  return ctx->max_cert_list;
+}
+
+void SSL_CTX_set_max_cert_list(SSL_CTX *ctx, size_t max_cert_list) {
+  if (max_cert_list > kMaxHandshakeSize) {
+    max_cert_list = kMaxHandshakeSize;
   }
+  ctx->max_cert_list = (uint32_t)max_cert_list;
+}
+
+size_t SSL_get_max_cert_list(const SSL *ssl) {
+  return ssl->max_cert_list;
+}
+
+void SSL_set_max_cert_list(SSL *ssl, size_t max_cert_list) {
+  if (max_cert_list > kMaxHandshakeSize) {
+    max_cert_list = kMaxHandshakeSize;
+  }
+  ssl->max_cert_list = (uint32_t)max_cert_list;
+}
+
+void SSL_CTX_set_max_send_fragment(SSL_CTX *ctx, size_t max_send_fragment) {
+  if (max_send_fragment < 512) {
+    max_send_fragment = 512;
+  }
+  if (max_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) {
+    max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
+  }
+  ctx->max_send_fragment = (uint16_t)max_send_fragment;
+}
+
+void SSL_set_max_send_fragment(SSL *ssl, size_t max_send_fragment) {
+  if (max_send_fragment < 512) {
+    max_send_fragment = 512;
+  }
+  if (max_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) {
+    max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
+  }
+  ssl->max_send_fragment = (uint16_t)max_send_fragment;
+}
+
+int SSL_set_mtu(SSL *ssl, unsigned mtu) {
+  if (!SSL_IS_DTLS(ssl) || mtu < dtls1_min_mtu()) {
+    return 0;
+  }
+  ssl->d1->mtu = mtu;
+  return 1;
+}
+
+int SSL_get_secure_renegotiation_support(const SSL *ssl) {
+  return ssl->s3->send_connection_binding;
+}
+
+long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) {
+  return s->method->ssl_ctrl(s, cmd, larg, parg);
 }
 
 LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx) { return ctx->sessions; }
 
-long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) {
-  long l;
-
-  switch (cmd) {
-    case SSL_CTRL_GET_READ_AHEAD:
-      return ctx->read_ahead;
-
-    case SSL_CTRL_SET_READ_AHEAD:
-      l = ctx->read_ahead;
-      ctx->read_ahead = larg;
-      return l;
-
-    case SSL_CTRL_SET_MSG_CALLBACK_ARG:
-      ctx->msg_callback_arg = parg;
-      return 1;
-
-    case SSL_CTRL_GET_MAX_CERT_LIST:
-      return ctx->max_cert_list;
-
-    case SSL_CTRL_SET_MAX_CERT_LIST:
-      l = ctx->max_cert_list;
-      ctx->max_cert_list = larg;
-      return l;
-
-    case SSL_CTRL_SET_SESS_CACHE_SIZE:
-      l = ctx->session_cache_size;
-      ctx->session_cache_size = larg;
-      return l;
-
-    case SSL_CTRL_GET_SESS_CACHE_SIZE:
-      return ctx->session_cache_size;
-
-    case SSL_CTRL_SET_SESS_CACHE_MODE:
-      l = ctx->session_cache_mode;
-      ctx->session_cache_mode = larg;
-      return l;
-
-    case SSL_CTRL_GET_SESS_CACHE_MODE:
-      return ctx->session_cache_mode;
-
-    case SSL_CTRL_SESS_NUMBER:
-      return lh_SSL_SESSION_num_items(ctx->sessions);
-
-    case SSL_CTRL_SESS_CONNECT:
-      return ctx->stats.sess_connect;
-
-    case SSL_CTRL_SESS_CONNECT_GOOD:
-      return ctx->stats.sess_connect_good;
-
-    case SSL_CTRL_SESS_CONNECT_RENEGOTIATE:
-      return ctx->stats.sess_connect_renegotiate;
-
-    case SSL_CTRL_SESS_ACCEPT:
-      return ctx->stats.sess_accept;
-
-    case SSL_CTRL_SESS_ACCEPT_GOOD:
-      return ctx->stats.sess_accept_good;
-
-    case SSL_CTRL_SESS_ACCEPT_RENEGOTIATE:
-      return ctx->stats.sess_accept_renegotiate;
-
-    case SSL_CTRL_SESS_HIT:
-      return ctx->stats.sess_hit;
-
-    case SSL_CTRL_SESS_CB_HIT:
-      return ctx->stats.sess_cb_hit;
-
-    case SSL_CTRL_SESS_MISSES:
-      return ctx->stats.sess_miss;
-
-    case SSL_CTRL_SESS_TIMEOUTS:
-      return ctx->stats.sess_timeout;
-
-    case SSL_CTRL_SESS_CACHE_FULL:
-      return ctx->stats.sess_cache_full;
-
-    case SSL_CTRL_OPTIONS:
-      return ctx->options |= larg;
-
-    case SSL_CTRL_CLEAR_OPTIONS:
-      return ctx->options &= ~larg;
-
-    case SSL_CTRL_MODE:
-      return ctx->mode |= larg;
-
-    case SSL_CTRL_CLEAR_MODE:
-      return ctx->mode &= ~larg;
-
-    case SSL_CTRL_SET_MAX_SEND_FRAGMENT:
-      if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH) {
-        return 0;
-      }
-      ctx->max_send_fragment = larg;
-      return 1;
-
-    case SSL_CTRL_CERT_FLAGS:
-      return ctx->cert->cert_flags |= larg;
-
-    case SSL_CTRL_CLEAR_CERT_FLAGS:
-      return ctx->cert->cert_flags &= ~larg;
-
-    default:
-      return ctx->method->ssl_ctx_ctrl(ctx, cmd, larg, parg);
-  }
+size_t SSL_CTX_sess_number(const SSL_CTX *ctx) {
+  return lh_SSL_SESSION_num_items(ctx->sessions);
 }
 
-long SSL_CTX_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) {
-  switch (cmd) {
-    case SSL_CTRL_SET_MSG_CALLBACK:
-      ctx->msg_callback =
-          (void (*)(int write_p, int version, int content_type, const void *buf,
-                    size_t len, SSL *ssl, void *arg))(fp);
-      return 1;
+unsigned long SSL_CTX_sess_set_cache_size(SSL_CTX *ctx, unsigned long size) {
+  unsigned long ret = ctx->session_cache_size;
+  ctx->session_cache_size = size;
+  return ret;
+}
 
-    default:
-      return ctx->method->ssl_ctx_callback_ctrl(ctx, cmd, fp);
-  }
+unsigned long SSL_CTX_sess_get_cache_size(const SSL_CTX *ctx) {
+  return ctx->session_cache_size;
+}
+
+int SSL_CTX_set_session_cache_mode(SSL_CTX *ctx, int mode) {
+  int ret = ctx->session_cache_mode;
+  ctx->session_cache_mode = mode;
+  return ret;
+}
+
+int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx) {
+  return ctx->session_cache_mode;
+}
+
+long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) {
+  return ctx->method->ssl_ctx_ctrl(ctx, cmd, larg, parg);
 }
 
 int ssl_cipher_id_cmp(const void *in_a, const void *in_b) {
@@ -1275,7 +1131,7 @@
 
 /* return a STACK of the ciphers available for the SSL and in order of
  * preference */
-STACK_OF(SSL_CIPHER) * SSL_get_ciphers(const SSL *s) {
+STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s) {
   if (s == NULL) {
     return NULL;
   }
@@ -1298,7 +1154,7 @@
 
 /* return a STACK of the ciphers available for the SSL and in order of
  * algorithm id */
-STACK_OF(SSL_CIPHER) * ssl_get_ciphers_by_id(SSL *s) {
+STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *s) {
   if (s == NULL) {
     return NULL;
   }
@@ -1317,7 +1173,7 @@
 /* The old interface to get the same thing as SSL_get_ciphers() */
 const char *SSL_get_cipher_list(const SSL *s, int n) {
   const SSL_CIPHER *c;
-  STACK_OF(SSL_CIPHER) * sk;
+  STACK_OF(SSL_CIPHER) *sk;
 
   if (s == NULL) {
     return NULL;
@@ -1341,7 +1197,7 @@
   STACK_OF(SSL_CIPHER) *sk;
 
   sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list,
-                              &ctx->cipher_list_by_id, str, ctx->cert);
+                              &ctx->cipher_list_by_id, str);
   /* ssl_create_cipher_list may return an empty stack if it was unable to find
    * a cipher matching the given rule string (for example if the rule string
    * specifies a cipher which has been disabled). This is not an error as far
@@ -1360,8 +1216,7 @@
 int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) {
   STACK_OF(SSL_CIPHER) *sk;
 
-  sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls11, NULL, str,
-                              ctx->cert);
+  sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls11, NULL, str);
   if (sk == NULL) {
     return 0;
   } else if (sk_SSL_CIPHER_num(sk) == 0) {
@@ -1378,7 +1233,7 @@
   STACK_OF(SSL_CIPHER) *sk;
 
   sk = ssl_create_cipher_list(s->ctx->method, &s->cipher_list,
-                              &s->cipher_list_by_id, str, s->cert);
+                              &s->cipher_list_by_id, str);
 
   /* see comment in SSL_CTX_set_cipher_list */
   if (sk == NULL) {
@@ -1435,7 +1290,7 @@
 STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) {
   CBS cipher_suites = *cbs;
   const SSL_CIPHER *c;
-  STACK_OF(SSL_CIPHER) * sk;
+  STACK_OF(SSL_CIPHER) *sk;
 
   if (s->s3) {
     s->s3->send_connection_binding = 0;
@@ -1453,12 +1308,6 @@
     goto err;
   }
 
-  if (!CBS_stow(&cipher_suites, &s->cert->ciphers_raw,
-                &s->cert->ciphers_rawlen)) {
-    OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
   while (CBS_len(&cipher_suites) > 0) {
     uint16_t cipher_suite;
 
@@ -1503,9 +1352,7 @@
   return sk;
 
 err:
-  if (sk != NULL) {
-    sk_SSL_CIPHER_free(sk);
-  }
+  sk_SSL_CIPHER_free(sk);
   return NULL;
 }
 
@@ -1691,17 +1538,9 @@
   ctx->next_proto_select_cb_arg = arg;
 }
 
-/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|.
- * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit
- * length-prefixed strings).
- *
- * Returns 0 on success. */
 int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos,
                             unsigned protos_len) {
-  if (ctx->alpn_client_proto_list) {
-    OPENSSL_free(ctx->alpn_client_proto_list);
-  }
-
+  OPENSSL_free(ctx->alpn_client_proto_list);
   ctx->alpn_client_proto_list = BUF_memdup(protos, protos_len);
   if (!ctx->alpn_client_proto_list) {
     return 1;
@@ -1711,16 +1550,8 @@
   return 0;
 }
 
-/* SSL_set_alpn_protos sets the ALPN protocol list on |ssl| to |protos|.
- * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit
- * length-prefixed strings).
- *
- * Returns 0 on success. */
 int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) {
-  if (ssl->alpn_client_proto_list) {
-    OPENSSL_free(ssl->alpn_client_proto_list);
-  }
-
+  OPENSSL_free(ssl->alpn_client_proto_list);
   ssl->alpn_client_proto_list = BUF_memdup(protos, protos_len);
   if (!ssl->alpn_client_proto_list) {
     return 1;
@@ -1759,15 +1590,16 @@
   }
 }
 
-int SSL_export_keying_material(SSL *s, uint8_t *out, size_t olen,
-                               const char *label, size_t llen, const uint8_t *p,
-                               size_t plen, int use_context) {
+int SSL_export_keying_material(SSL *s, uint8_t *out, size_t out_len,
+                               const char *label, size_t label_len,
+                               const uint8_t *context, size_t context_len,
+                               int use_context) {
   if (s->version < TLS1_VERSION) {
-    return -1;
+    return 0;
   }
 
-  return s->enc_method->export_keying_material(s, out, olen, label, llen, p,
-                                               plen, use_context);
+  return s->enc_method->export_keying_material(
+      s, out, out_len, label, label_len, context, context_len, use_context);
 }
 
 static uint32_t ssl_session_hash(const SSL_SESSION *a) {
@@ -1833,8 +1665,6 @@
   ret->get_session_cb = 0;
   ret->generate_session_id = 0;
 
-  memset((char *)&ret->stats, 0, sizeof(ret->stats));
-
   ret->references = 1;
   ret->quiet_shutdown = 0;
 
@@ -1858,8 +1688,6 @@
   ret->default_passwd_callback = 0;
   ret->default_passwd_callback_userdata = NULL;
   ret->client_cert_cb = 0;
-  ret->app_gen_cookie_cb = 0;
-  ret->app_verify_cookie_cb = 0;
 
   ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp);
   if (ret->sessions == NULL) {
@@ -1871,8 +1699,7 @@
   }
 
   ssl_create_cipher_list(ret->method, &ret->cipher_list,
-                         &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST,
-                         ret->cert);
+                         &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST);
   if (ret->cipher_list == NULL ||
       sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_LIBRARY_HAS_NO_CIPHERS);
@@ -1889,7 +1716,7 @@
     goto err;
   }
 
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class_ssl_ctx, ret, &ret->ex_data);
 
   ret->extra_certs = NULL;
 
@@ -1904,9 +1731,6 @@
     ret->options |= SSL_OP_NO_TICKET;
   }
 
-  ret->tlsext_status_cb = 0;
-  ret->tlsext_status_arg = NULL;
-
   ret->next_protos_advertised_cb = 0;
   ret->next_proto_select_cb = 0;
   ret->psk_identity_hint = NULL;
@@ -1929,27 +1753,17 @@
 err:
   OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, ERR_R_MALLOC_FAILURE);
 err2:
-  if (ret != NULL) {
-    SSL_CTX_free(ret);
-  }
+  SSL_CTX_free(ret);
   return NULL;
 }
 
-void SSL_CTX_free(SSL_CTX *a) {
-  int i;
-
-  if (a == NULL) {
+void SSL_CTX_free(SSL_CTX *ctx) {
+  if (ctx == NULL ||
+      CRYPTO_add(&ctx->references, -1, CRYPTO_LOCK_SSL_CTX) > 0) {
     return;
   }
 
-  i = CRYPTO_add(&a->references, -1, CRYPTO_LOCK_SSL_CTX);
-  if (i > 0) {
-    return;
-  }
-
-  if (a->param) {
-    X509_VERIFY_PARAM_free(a->param);
-  }
+  X509_VERIFY_PARAM_free(ctx->param);
 
   /* Free internal session cache. However: the remove_cb() may reference the
    * ex_data of SSL_CTX, thus the ex_data store can only be removed after the
@@ -1957,59 +1771,27 @@
    * the session cache, the most secure solution seems to be: empty (flush) the
    * cache, then free ex_data, then finally free the cache. (See ticket
    * [openssl.org #212].) */
-  if (a->sessions != NULL) {
-    SSL_CTX_flush_sessions(a, 0);
-  }
+  SSL_CTX_flush_sessions(ctx, 0);
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
+  CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, ctx, &ctx->ex_data);
 
-  if (a->sessions != NULL) {
-    lh_SSL_SESSION_free(a->sessions);
-  }
-  if (a->cert_store != NULL) {
-    X509_STORE_free(a->cert_store);
-  }
-  if (a->cipher_list != NULL) {
-    ssl_cipher_preference_list_free(a->cipher_list);
-  }
-  if (a->cipher_list_by_id != NULL) {
-    sk_SSL_CIPHER_free(a->cipher_list_by_id);
-  }
-  if (a->cipher_list_tls11 != NULL) {
-    ssl_cipher_preference_list_free(a->cipher_list_tls11);
-  }
-  if (a->cert != NULL) {
-    ssl_cert_free(a->cert);
-  }
-  if (a->client_CA != NULL) {
-    sk_X509_NAME_pop_free(a->client_CA, X509_NAME_free);
-  }
-  if (a->extra_certs != NULL) {
-    sk_X509_pop_free(a->extra_certs, X509_free);
-  }
-  if (a->srtp_profiles) {
-    sk_SRTP_PROTECTION_PROFILE_free(a->srtp_profiles);
-  }
-  if (a->psk_identity_hint) {
-    OPENSSL_free(a->psk_identity_hint);
-  }
-  if (a->tlsext_ecpointformatlist) {
-    OPENSSL_free(a->tlsext_ecpointformatlist);
-  }
-  if (a->tlsext_ellipticcurvelist) {
-    OPENSSL_free(a->tlsext_ellipticcurvelist);
-  }
-  if (a->alpn_client_proto_list != NULL) {
-    OPENSSL_free(a->alpn_client_proto_list);
-  }
-  if (a->tlsext_channel_id_private) {
-    EVP_PKEY_free(a->tlsext_channel_id_private);
-  }
-  if (a->keylog_bio) {
-    BIO_free(a->keylog_bio);
-  }
+  lh_SSL_SESSION_free(ctx->sessions);
+  X509_STORE_free(ctx->cert_store);
+  ssl_cipher_preference_list_free(ctx->cipher_list);
+  sk_SSL_CIPHER_free(ctx->cipher_list_by_id);
+  ssl_cipher_preference_list_free(ctx->cipher_list_tls11);
+  ssl_cert_free(ctx->cert);
+  sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free);
+  sk_X509_pop_free(ctx->extra_certs, X509_free);
+  sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles);
+  OPENSSL_free(ctx->psk_identity_hint);
+  OPENSSL_free(ctx->tlsext_ecpointformatlist);
+  OPENSSL_free(ctx->tlsext_ellipticcurvelist);
+  OPENSSL_free(ctx->alpn_client_proto_list);
+  EVP_PKEY_free(ctx->tlsext_channel_id_private);
+  BIO_free(ctx->keylog_bio);
 
-  OPENSSL_free(a);
+  OPENSSL_free(ctx);
 }
 
 void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) {
@@ -2051,13 +1833,12 @@
   return cpk->x509 && cpk->privatekey;
 }
 
-void ssl_get_compatible_server_ciphers(SSL *s, unsigned long *out_mask_k,
-                                       unsigned long *out_mask_a) {
+void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k,
+                                       uint32_t *out_mask_a) {
   CERT *c = s->cert;
   int rsa_enc, rsa_sign, dh_tmp;
-  unsigned long mask_k, mask_a;
+  uint32_t mask_k, mask_a;
   int have_ecc_cert, ecdsa_ok;
-  int have_ecdh_tmp;
   X509 *x;
 
   if (c == NULL) {
@@ -2069,7 +1850,6 @@
 
   dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL);
 
-  have_ecdh_tmp = (c->ecdh_tmp || c->ecdh_tmp_cb || c->ecdh_tmp_auto);
   rsa_enc = ssl_has_key(s, SSL_PKEY_RSA_ENC);
   rsa_sign = ssl_has_key(s, SSL_PKEY_RSA_SIGN);
   have_ecc_cert = ssl_has_key(s, SSL_PKEY_ECC);
@@ -2080,14 +1860,12 @@
     mask_k |= SSL_kRSA;
   }
   if (dh_tmp) {
-    mask_k |= SSL_kEDH;
+    mask_k |= SSL_kDHE;
   }
   if (rsa_enc || rsa_sign) {
     mask_a |= SSL_aRSA;
   }
 
-  mask_a |= SSL_aNULL;
-
   /* An ECC certificate may be usable for ECDSA cipher suites depending on the
    * key usage extension and on the client's curve preferences. */
   if (have_ecc_cert) {
@@ -2107,8 +1885,8 @@
 
   /* If we are considering an ECC cipher suite that uses an ephemeral EC
    * key, check it. */
-  if (have_ecdh_tmp && tls1_check_ec_tmp_key(s)) {
-    mask_k |= SSL_kEECDH;
+  if (tls1_check_ec_tmp_key(s)) {
+    mask_k |= SSL_kECDHE;
   }
 
   /* PSK requires a server callback. */
@@ -2126,11 +1904,9 @@
   (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
 
 int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s) {
-  unsigned long alg_a;
-  int signature_nid = 0, md_nid = 0, pk_nid = 0;
   const SSL_CIPHER *cs = s->s3->tmp.new_cipher;
-
-  alg_a = cs->algorithm_auth;
+  uint32_t alg_a = cs->algorithm_auth;
+  int signature_nid = 0, md_nid = 0, pk_nid = 0;
 
   /* This call populates the ex_flags field correctly */
   X509_check_purpose(x, -1, 0);
@@ -2175,13 +1951,10 @@
 }
 
 EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *cipher) {
-  unsigned long alg_a;
-  CERT *c;
+  uint32_t alg_a = cipher->algorithm_auth;
+  CERT *c = s->cert;
   int idx = -1;
 
-  alg_a = cipher->algorithm_auth;
-  c = s->cert;
-
   if (alg_a & SSL_aRSA) {
     if (c->pkeys[SSL_PKEY_RSA_SIGN].privatekey != NULL) {
       idx = SSL_PKEY_RSA_SIGN;
@@ -2202,56 +1975,62 @@
 }
 
 void ssl_update_cache(SSL *s, int mode) {
-  int i;
-
-  /* If the session_id_length is 0, we are not supposed to cache it, and it
-   * would be rather hard to do anyway :-) */
+  /* Never cache sessions with empty session IDs. */
   if (s->session->session_id_length == 0) {
     return;
   }
 
-  i = s->initial_ctx->session_cache_mode;
-  if ((i & mode) && !s->hit &&
-      ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) ||
-       SSL_CTX_add_session(s->initial_ctx, s->session)) &&
-      s->initial_ctx->new_session_cb != NULL) {
-    CRYPTO_add(&s->session->references, 1, CRYPTO_LOCK_SSL_SESSION);
-    if (!s->initial_ctx->new_session_cb(s, s->session)) {
+  SSL_CTX *ctx = s->initial_ctx;
+  if ((ctx->session_cache_mode & mode) == mode && !s->hit &&
+      ((ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) ||
+       SSL_CTX_add_session(ctx, s->session)) &&
+      ctx->new_session_cb != NULL) {
+    /* Note: |new_session_cb| is called whether the internal session cache is
+     * used or not. */
+    if (!ctx->new_session_cb(s, SSL_SESSION_up_ref(s->session))) {
       SSL_SESSION_free(s->session);
     }
   }
 
-  /* auto flush every 255 connections */
-  if ((!(i & SSL_SESS_CACHE_NO_AUTO_CLEAR)) && ((i & mode) == mode)) {
-    if ((((mode & SSL_SESS_CACHE_CLIENT)
-              ? s->initial_ctx->stats.sess_connect_good
-              : s->initial_ctx->stats.sess_accept_good) &
-         0xff) == 0xff) {
-      SSL_CTX_flush_sessions(s->initial_ctx, (unsigned long)time(NULL));
+  if (!(ctx->session_cache_mode & SSL_SESS_CACHE_NO_AUTO_CLEAR) &&
+      !(ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) &&
+      (ctx->session_cache_mode & mode) == mode) {
+    /* Automatically flush the internal session cache every 255 connections. */
+    int flush_cache = 0;
+    CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+    ctx->handshakes_since_cache_flush++;
+    if (ctx->handshakes_since_cache_flush >= 255) {
+      flush_cache = 1;
+      ctx->handshakes_since_cache_flush = 0;
+    }
+    CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+
+    if (flush_cache) {
+      SSL_CTX_flush_sessions(ctx, (unsigned long)time(NULL));
     }
   }
 }
 
-int SSL_get_error(const SSL *s, int i) {
+int SSL_get_error(const SSL *s, int ret_code) {
   int reason;
-  unsigned long l;
+  uint32_t err;
   BIO *bio;
 
-  if (i > 0) {
+  if (ret_code > 0) {
     return SSL_ERROR_NONE;
   }
 
   /* Make things return SSL_ERROR_SYSCALL when doing SSL_do_handshake etc,
    * where we do encode the error */
-  l = ERR_peek_error();
-  if (l != 0) {
-    if (ERR_GET_LIB(l) == ERR_LIB_SYS) {
+  err = ERR_peek_error();
+  if (err != 0) {
+    if (ERR_GET_LIB(err) == ERR_LIB_SYS) {
       return SSL_ERROR_SYSCALL;
     }
     return SSL_ERROR_SSL;
   }
 
-  if (i == 0) {
+  if (ret_code == 0) {
     if ((s->shutdown & SSL_RECEIVED_SHUTDOWN) &&
         (s->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) {
       /* The socket was cleanly shut down with a close_notify. */
@@ -2371,24 +2150,6 @@
   ssl_clear_cipher_ctx(s);
 }
 
-int ssl_undefined_function(SSL *s) {
-  OPENSSL_PUT_ERROR(SSL, ssl_undefined_function,
-                    ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-  return 0;
-}
-
-int ssl_undefined_void_function(void) {
-  OPENSSL_PUT_ERROR(SSL, ssl_undefined_void_function,
-                    ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-  return 0;
-}
-
-int ssl_undefined_const_function(const SSL *s) {
-  OPENSSL_PUT_ERROR(SSL, ssl_undefined_const_function,
-                    ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-  return 0;
-}
-
 static const char *ssl_get_version(int version) {
   switch (version) {
     case TLS1_2_VERSION:
@@ -2403,6 +2164,12 @@
     case SSL3_VERSION:
       return "SSLv3";
 
+    case DTLS1_VERSION:
+      return "DTLSv1";
+
+    case DTLS1_2_VERSION:
+      return "DTLSv1.2";
+
     default:
       return "unknown";
   }
@@ -2552,15 +2319,11 @@
     ctx = ssl->initial_ctx;
   }
 
-  if (ssl->cert != NULL) {
-    ssl_cert_free(ssl->cert);
-  }
-
+  ssl_cert_free(ssl->cert);
   ssl->cert = ssl_cert_dup(ctx->cert);
+
   CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX);
-  if (ssl->ctx != NULL) {
-    SSL_CTX_free(ssl->ctx); /* decrement reference count */
-  }
+  SSL_CTX_free(ssl->ctx); /* decrement reference count */
   ssl->ctx = ctx;
 
   ssl->sid_ctx_length = ctx->sid_ctx_length;
@@ -2599,8 +2362,12 @@
 
 int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                          CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl, &index, argl, argp,
+                               new_func, dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_set_ex_data(SSL *s, int idx, void *arg) {
@@ -2614,8 +2381,12 @@
 int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                              CRYPTO_EX_dup *dup_func,
                              CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, argl, argp, new_func,
-                                 dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl_ctx, &index, argl, argp,
+                               new_func, dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg) {
@@ -2633,9 +2404,7 @@
 }
 
 void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store) {
-  if (ctx->cert_store != NULL) {
-    X509_STORE_free(ctx->cert_store);
-  }
+  X509_STORE_free(ctx->cert_store);
   ctx->cert_store = store;
 }
 
@@ -2644,35 +2413,33 @@
 void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx,
                                   RSA *(*cb)(SSL *ssl, int is_export,
                                              int keylength)) {
-  SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_RSA_CB, (void (*)(void))cb);
 }
 
 void SSL_set_tmp_rsa_callback(SSL *ssl, RSA *(*cb)(SSL *ssl, int is_export,
                                                    int keylength)) {
-  SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_RSA_CB, (void (*)(void))cb);
 }
 
 void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx,
-                                 DH *(*dh)(SSL *ssl, int is_export,
-                                           int keylength)) {
-  SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_DH_CB, (void (*)(void))dh);
+                                 DH *(*callback)(SSL *ssl, int is_export,
+                                                 int keylength)) {
+  ctx->cert->dh_tmp_cb = callback;
 }
 
-void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*dh)(SSL *ssl, int is_export,
-                                                 int keylength)) {
-  SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_DH_CB, (void (*)(void))dh);
+void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*callback)(SSL *ssl, int is_export,
+                                                       int keylength)) {
+  ssl->cert->dh_tmp_cb = callback;
 }
 
 void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx,
-                                   EC_KEY *(*ecdh)(SSL *ssl, int is_export,
-                                                   int keylength)) {
-  SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH_CB, (void (*)(void))ecdh);
+                                   EC_KEY *(*callback)(SSL *ssl, int is_export,
+                                                       int keylength)) {
+  ctx->cert->ecdh_tmp_cb = callback;
 }
 
 void SSL_set_tmp_ecdh_callback(SSL *ssl,
-                               EC_KEY *(*ecdh)(SSL *ssl, int is_export,
-                                               int keylength)) {
-  SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_ECDH_CB, (void (*)(void))ecdh);
+                               EC_KEY *(*callback)(SSL *ssl, int is_export,
+                                                   int keylength)) {
+  ssl->cert->ecdh_tmp_cb = callback;
 }
 
 int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
@@ -2682,9 +2449,7 @@
     return 0;
   }
 
-  if (ctx->psk_identity_hint != NULL) {
-    OPENSSL_free(ctx->psk_identity_hint);
-  }
+  OPENSSL_free(ctx->psk_identity_hint);
 
   if (identity_hint != NULL) {
     ctx->psk_identity_hint = BUF_strdup(identity_hint);
@@ -2710,10 +2475,8 @@
   }
 
   /* Clear currently configured hint, if any. */
-  if (s->psk_identity_hint != NULL) {
-    OPENSSL_free(s->psk_identity_hint);
-    s->psk_identity_hint = NULL;
-  }
+  OPENSSL_free(s->psk_identity_hint);
+  s->psk_identity_hint = NULL;
 
   if (identity_hint != NULL) {
     s->psk_identity_hint = BUF_strdup(identity_hint);
@@ -2786,19 +2549,26 @@
                               void (*cb)(int write_p, int version,
                                          int content_type, const void *buf,
                                          size_t len, SSL *ssl, void *arg)) {
-  SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb);
+  ctx->msg_callback = cb;
 }
+
+void SSL_CTX_set_msg_callback_arg(SSL_CTX *ctx, void *arg) {
+  ctx->msg_callback_arg = arg;
+}
+
 void SSL_set_msg_callback(SSL *ssl,
                           void (*cb)(int write_p, int version, int content_type,
                                      const void *buf, size_t len, SSL *ssl,
                                      void *arg)) {
-  SSL_callback_ctrl(ssl, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb);
+  ssl->msg_callback = cb;
+}
+
+void SSL_set_msg_callback_arg(SSL *ssl, void *arg) {
+  ssl->msg_callback_arg = arg;
 }
 
 void SSL_CTX_set_keylog_bio(SSL_CTX *ctx, BIO *keylog_bio) {
-  if (ctx->keylog_bio != NULL) {
-    BIO_free(ctx->keylog_bio);
-  }
+  BIO_free(ctx->keylog_bio);
   ctx->keylog_bio = keylog_bio;
 }
 
@@ -2904,19 +2674,12 @@
   return ret;
 }
 
+int SSL_in_false_start(const SSL *s) {
+  return s->s3->tmp.in_false_start;
+}
+
 int SSL_cutthrough_complete(const SSL *s) {
-  return (
-      !s->server && /* cutthrough only applies to clients */
-      !s->hit &&    /* full-handshake */
-      s->version >= SSL3_VERSION &&
-      s->s3->in_read_app_data == 0 && /* cutthrough only applies to write() */
-      (SSL_get_mode((SSL *)s) &
-       SSL_MODE_HANDSHAKE_CUTTHROUGH) && /* cutthrough enabled */
-      ssl3_can_cutthrough(s) &&          /* cutthrough allowed */
-      s->s3->previous_server_finished_len ==
-          0 && /* not a renegotiation handshake */
-      (s->state == SSL3_ST_CR_SESSION_TICKET_A || /* ready to write app-data*/
-       s->state == SSL3_ST_CR_CHANGE || s->state == SSL3_ST_CR_FINISHED_A));
+  return SSL_in_false_start(s);
 }
 
 void SSL_get_structure_sizes(size_t *ssl_size, size_t *ssl_ctx_size,
@@ -2926,27 +2689,18 @@
   *ssl_session_size = sizeof(SSL_SESSION);
 }
 
-int ssl3_can_cutthrough(const SSL *s) {
-  const SSL_CIPHER *c;
+int ssl3_can_false_start(const SSL *s) {
+  const SSL_CIPHER *const cipher = SSL_get_current_cipher(s);
 
-  /* require a strong enough cipher */
-  if (SSL_get_cipher_bits(s, NULL) < 128) {
-    return 0;
-  }
-
-  /* require ALPN or NPN extension */
-  if (!s->s3->alpn_selected && !s->s3->next_proto_neg_seen) {
-    return 0;
-  }
-
-  /* require a forward-secret cipher */
-  c = SSL_get_current_cipher(s);
-  if (!c ||
-      (c->algorithm_mkey != SSL_kEDH && c->algorithm_mkey != SSL_kEECDH)) {
-    return 0;
-  }
-
-  return 1;
+  /* False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN. */
+  return !SSL_IS_DTLS(s) &&
+      SSL_version(s) >= TLS1_2_VERSION &&
+      (s->s3->alpn_selected || s->s3->next_proto_neg_seen) &&
+      cipher != NULL &&
+      cipher->algorithm_mkey == SSL_kECDHE &&
+      (cipher->algorithm_enc == SSL_AES128GCM ||
+       cipher->algorithm_enc == SSL_AES256GCM ||
+       cipher->algorithm_enc == SSL_CHACHA20POLY1305);
 }
 
 const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) {
@@ -2957,18 +2711,14 @@
     case TLS1_VERSION:
       return &TLSv1_enc_data;
 
+    case DTLS1_VERSION:
     case TLS1_1_VERSION:
       return &TLSv1_1_enc_data;
 
+    case DTLS1_2_VERSION:
     case TLS1_2_VERSION:
       return &TLSv1_2_enc_data;
 
-    case DTLS1_VERSION:
-      return &DTLSv1_enc_data;
-
-    case DTLS1_2_VERSION:
-      return &DTLSv1_2_enc_data;
-
     default:
       return NULL;
   }
@@ -3016,7 +2766,7 @@
     if (client_version <= DTLS1_2_VERSION && !(s->options & SSL_OP_NO_DTLSv1_2)) {
       version = DTLS1_2_VERSION;
     } else if (client_version <= DTLS1_VERSION &&
-             !(s->options & SSL_OP_NO_DTLSv1)) {
+               !(s->options & SSL_OP_NO_DTLSv1)) {
       version = DTLS1_VERSION;
     }
 
@@ -3051,7 +2801,7 @@
 }
 
 uint16_t ssl3_get_max_client_version(SSL *s) {
-  unsigned long options = s->options;
+  uint32_t options = s->options;
   uint16_t version = 0;
 
   /* OpenSSL's API for controlling versions entails blacklisting individual
@@ -3169,6 +2919,41 @@
 
 int SSL_is_server(SSL *s) { return s->server; }
 
+void SSL_CTX_set_dos_protection_cb(
+    SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *)) {
+  ctx->dos_protection_cb = cb;
+}
+
 void SSL_enable_fastradio_padding(SSL *s, char on_off) {
   s->fastradio_padding = on_off;
 }
+
+void SSL_set_reject_peer_renegotiations(SSL *s, int reject) {
+  s->reject_peer_renegotiations = !!reject;
+}
+
+const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
+  return ssl3_get_cipher_by_value(value);
+}
+
+int SSL_get_rc4_state(const SSL *ssl, const RC4_KEY **read_key,
+                      const RC4_KEY **write_key) {
+  if (ssl->aead_read_ctx == NULL || ssl->aead_write_ctx == NULL) {
+    return 0;
+  }
+
+  return EVP_AEAD_CTX_get_rc4_state(&ssl->aead_read_ctx->ctx, read_key) &&
+         EVP_AEAD_CTX_get_rc4_state(&ssl->aead_write_ctx->ctx, write_key);
+}
+
+int SSL_CTX_sess_connect(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_connect_good(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_connect_renegotiate(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_accept(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_accept_renegotiate(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_accept_good(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_hits(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_cb_hits(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_misses(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_timeouts(const SSL_CTX *ctx) { return 0; }
+int SSL_CTX_sess_cache_full(const SSL_CTX *ctx) { return 0; }
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 3d1bc62..87f4c1c 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -64,7 +64,7 @@
 #include <openssl/pem.h>
 #include <openssl/x509.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 static int ssl_set_cert(CERT *c, X509 *x509);
 static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey);
@@ -74,10 +74,6 @@
     OPENSSL_PUT_ERROR(SSL, SSL_use_certificate, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  if (!ssl_cert_inst(&ssl->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_use_certificate, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
   return ssl_set_cert(ssl->cert, x);
 }
 
@@ -118,12 +114,8 @@
   ret = SSL_use_certificate(ssl, x);
 
 end:
-  if (x != NULL) {
-    X509_free(x);
-  }
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  X509_free(x);
+  BIO_free(in);
 
   return ret;
 }
@@ -152,11 +144,6 @@
     return 0;
   }
 
-  if (!ssl_cert_inst(&ssl->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
   pkey = EVP_PKEY_new();
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_EVP_LIB);
@@ -182,12 +169,6 @@
   }
 
   if (c->pkeys[i].x509 != NULL) {
-    EVP_PKEY *pktmp;
-    pktmp = X509_get_pubkey(c->pkeys[i].x509);
-    EVP_PKEY_copy_parameters(pktmp, pkey);
-    EVP_PKEY_free(pktmp);
-    ERR_clear_error();
-
     /* 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) &&
@@ -198,10 +179,8 @@
     }
   }
 
-  if (c->pkeys[i].privatekey != NULL) {
-    EVP_PKEY_free(c->pkeys[i].privatekey);
-  }
-  c->pkeys[i].privatekey = EVP_PKEY_dup(pkey);
+  EVP_PKEY_free(c->pkeys[i].privatekey);
+  c->pkeys[i].privatekey = EVP_PKEY_up_ref(pkey);
   c->key = &(c->pkeys[i]);
 
   return 1;
@@ -244,9 +223,7 @@
   RSA_free(rsa);
 
 end:
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  BIO_free(in);
   return ret;
 }
 
@@ -275,11 +252,6 @@
     return 0;
   }
 
-  if (!ssl_cert_inst(&ssl->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
   ret = ssl_set_pkey(ssl->cert, pkey);
   return ret;
 }
@@ -320,9 +292,7 @@
   EVP_PKEY_free(pkey);
 
 end:
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  BIO_free(in);
   return ret;
 }
 
@@ -349,10 +319,6 @@
                       ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  if (!ssl_cert_inst(&ctx->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
 
   return ssl_set_cert(ctx->cert, x);
 }
@@ -375,9 +341,6 @@
   }
 
   if (c->pkeys[i].privatekey != NULL) {
-    EVP_PKEY_copy_parameters(pkey, c->pkeys[i].privatekey);
-    ERR_clear_error();
-
     /* 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->pkeys[i].privatekey) &&
@@ -394,9 +357,7 @@
 
   EVP_PKEY_free(pkey);
 
-  if (c->pkeys[i].x509 != NULL) {
-    X509_free(c->pkeys[i].x509);
-  }
+  X509_free(c->pkeys[i].x509);
   c->pkeys[i].x509 = X509_up_ref(x);
   c->key = &(c->pkeys[i]);
 
@@ -441,12 +402,8 @@
   ret = SSL_CTX_use_certificate(ctx, x);
 
 end:
-  if (x != NULL) {
-    X509_free(x);
-  }
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  X509_free(x);
+  BIO_free(in);
   return ret;
 }
 
@@ -475,11 +432,6 @@
     return 0;
   }
 
-  if (!ssl_cert_inst(&ctx->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
   pkey = EVP_PKEY_new();
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, ERR_R_EVP_LIB);
@@ -531,9 +483,7 @@
   RSA_free(rsa);
 
 end:
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  BIO_free(in);
   return ret;
 }
 
@@ -560,11 +510,6 @@
     return 0;
   }
 
-  if (!ssl_cert_inst(&ctx->cert)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-
   return ssl_set_pkey(ctx->cert, pkey);
 }
 
@@ -604,9 +549,7 @@
   EVP_PKEY_free(pkey);
 
 end:
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  BIO_free(in);
   return ret;
 }
 
@@ -668,7 +611,7 @@
      * certificates. */
     X509 *ca;
     int r;
-    unsigned long err;
+    uint32_t err;
 
     SSL_CTX_clear_chain_certs(ctx);
 
@@ -697,11 +640,7 @@
   }
 
 end:
-  if (x != NULL) {
-    X509_free(x);
-  }
-  if (in != NULL) {
-    BIO_free(in);
-  }
+  X509_free(x);
+  BIO_free(in);
   return ret;
 }
diff --git a/src/ssl/ssl_sess.c b/src/ssl/ssl_sess.c
index c5069d8..3eb428f 100644
--- a/src/ssl/ssl_sess.c
+++ b/src/ssl/ssl_sess.c
@@ -134,22 +134,26 @@
  * OTHERWISE. */
 
 #include <stdio.h>
+#include <string.h>
 
-#include <openssl/engine.h>
 #include <openssl/err.h>
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
+#include "../crypto/internal.h"
+
 
 /* The address of this is a magic value, a pointer to which is returned by
  * SSL_magic_pending_session_ptr(). It allows a session callback to indicate
  * that it needs to asynchronously fetch session information. */
 static const char g_pending_session_magic = 0;
 
+static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
+
 static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s);
-static void SSL_SESSION_list_add(SSL_CTX *ctx,SSL_SESSION *s);
+static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s);
 static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck);
 
 SSL_SESSION *SSL_magic_pending_session_ptr(void) {
@@ -164,25 +168,18 @@
 
 SSL_SESSION *SSL_get1_session(SSL *ssl) {
   /* variant of SSL_get_session: caller really gets something */
-  SSL_SESSION *sess;
-  /* Need to lock this all up rather than just use CRYPTO_add so that
-   * somebody doesn't free ssl->session between when we check it's
-   * non-null and when we up the reference count. */
-  CRYPTO_w_lock(CRYPTO_LOCK_SSL_SESSION);
-  sess = ssl->session;
-  if (sess) {
-    sess->references++;
-  }
-  CRYPTO_w_unlock(CRYPTO_LOCK_SSL_SESSION);
-
-  return sess;
+  return SSL_SESSION_up_ref(ssl->session);
 }
 
 int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
                                  CRYPTO_EX_dup *dup_func,
                                  CRYPTO_EX_free *free_func) {
-  return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_SESSION, argl, argp,
-                                 new_func, dup_func, free_func);
+  int index;
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
+                               dup_func, free_func)) {
+    return -1;
+  }
+  return index;
 }
 
 int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg) {
@@ -207,7 +204,7 @@
   ss->references = 1;
   ss->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   ss->time = (unsigned long)time(NULL);
-  CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
+  CRYPTO_new_ex_data(&g_ex_data_class, ss, &ss->ex_data);
   return ss;
 }
 
@@ -275,10 +272,8 @@
     ss->timeout = s->initial_ctx->session_timeout;
   }
 
-  if (s->session != NULL) {
-    SSL_SESSION_free(s->session);
-    s->session = NULL;
-  }
+  SSL_SESSION_free(s->session);
+  s->session = NULL;
 
   if (session) {
     if (s->version == SSL3_VERSION || s->version == TLS1_VERSION ||
@@ -425,15 +420,9 @@
     }
     memcpy(data.session_id, ctx->session_id, ctx->session_id_len);
     CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
-    ret = lh_SSL_SESSION_retrieve(s->initial_ctx->sessions, &data);
-    if (ret != NULL) {
-      /* don't allow other threads to steal it: */
-      CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION);
-    }
+    ret = SSL_SESSION_up_ref(lh_SSL_SESSION_retrieve(s->initial_ctx->sessions,
+                                                     &data));
     CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
-    if (ret == NULL) {
-      s->initial_ctx->stats.sess_miss++;
-    }
   }
 
   if (try_session_cache && ret == NULL &&
@@ -448,14 +437,13 @@
          * unwind the stack and figure out the session asynchronously. */
         return PENDING_SESSION;
       }
-      s->initial_ctx->stats.sess_cb_hit++;
 
       /* Increment reference count now if the session callback asks us to do so
        * (note that if the session structures returned by the callback are
        * shared between threads, it must handle the reference count itself
        * [i.e. copy == 0], or things won't be thread-safe). */
       if (copy) {
-        CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION);
+        SSL_SESSION_up_ref(ret);
       }
 
       /* Add the externally cached session to the internal cache as well if and
@@ -499,7 +487,6 @@
 
   if (ret->timeout < (long)(time(NULL) - ret->time)) {
     /* timeout */
-    s->initial_ctx->stats.sess_timeout++;
     if (try_session_cache) {
       /* session was from the cache, so remove it */
       SSL_CTX_remove_session(s->initial_ctx, ret);
@@ -507,11 +494,7 @@
     goto err;
   }
 
-  s->initial_ctx->stats.sess_hit++;
-
-  if (s->session != NULL) {
-    SSL_SESSION_free(s->session);
-  }
+  SSL_SESSION_free(s->session);
   s->session = ret;
   s->verify_result = s->session->verify_result;
   return 1;
@@ -538,7 +521,7 @@
   /* add just 1 reference count for the SSL_CTX's session cache even though it
    * has two ways of access: each session is in a doubly linked list and an
    * lhash */
-  CRYPTO_add(&c->references, 1, CRYPTO_LOCK_SSL_SESSION);
+  SSL_SESSION_up_ref(c);
   /* if session c is in already in cache, we take back the increment later */
 
   CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
@@ -579,7 +562,6 @@
         if (!remove_session_lock(ctx, ctx->session_cache_tail, 0)) {
           break;
         }
-        ctx->stats.sess_cache_full++;
       }
     }
   }
@@ -623,45 +605,32 @@
   return ret;
 }
 
-void SSL_SESSION_free(SSL_SESSION *ss) {
-  int i;
+SSL_SESSION *SSL_SESSION_up_ref(SSL_SESSION *session) {
+  if (session) {
+    CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION);
+  }
+  return session;
+}
 
-  if (ss == NULL) {
+void SSL_SESSION_free(SSL_SESSION *session) {
+  if (session == NULL ||
+      CRYPTO_add(&session->references, -1, CRYPTO_LOCK_SSL_SESSION) > 0) {
     return;
   }
 
-  i = CRYPTO_add(&ss->references, -1, CRYPTO_LOCK_SSL_SESSION);
-  if (i > 0) {
-    return;
-  }
+  CRYPTO_free_ex_data(&g_ex_data_class, session, &session->ex_data);
 
-  CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
-
-  OPENSSL_cleanse(ss->master_key, sizeof ss->master_key);
-  OPENSSL_cleanse(ss->session_id, sizeof ss->session_id);
-  if (ss->sess_cert != NULL) {
-    ssl_sess_cert_free(ss->sess_cert);
-  }
-  if (ss->peer != NULL) {
-    X509_free(ss->peer);
-  }
-  if (ss->tlsext_hostname != NULL) {
-    OPENSSL_free(ss->tlsext_hostname);
-  }
-  if (ss->tlsext_tick != NULL) {
-    OPENSSL_free(ss->tlsext_tick);
-  }
-  if (ss->tlsext_signed_cert_timestamp_list != NULL) {
-    OPENSSL_free(ss->tlsext_signed_cert_timestamp_list);
-  }
-  if (ss->ocsp_response != NULL) {
-    OPENSSL_free(ss->ocsp_response);
-  }
-  if (ss->psk_identity != NULL) {
-    OPENSSL_free(ss->psk_identity);
-  }
-  OPENSSL_cleanse(ss, sizeof(*ss));
-  OPENSSL_free(ss);
+  OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
+  OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
+  ssl_sess_cert_free(session->sess_cert);
+  X509_free(session->peer);
+  OPENSSL_free(session->tlsext_hostname);
+  OPENSSL_free(session->tlsext_tick);
+  OPENSSL_free(session->tlsext_signed_cert_timestamp_list);
+  OPENSSL_free(session->ocsp_response);
+  OPENSSL_free(session->psk_identity);
+  OPENSSL_cleanse(session, sizeof(*session));
+  OPENSSL_free(session);
 }
 
 int SSL_set_session(SSL *s, SSL_SESSION *session) {
@@ -669,12 +638,10 @@
     return 1;
   }
 
-  if (s->session != NULL) {
-    SSL_SESSION_free(s->session);
-  }
+  SSL_SESSION_free(s->session);
   s->session = session;
   if (session != NULL) {
-    CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION);
+    SSL_SESSION_up_ref(session);
     s->verify_result = session->verify_result;
   }
 
@@ -753,7 +720,7 @@
 typedef struct timeout_param_st {
   SSL_CTX *ctx;
   long time;
-  LHASH_OF(SSL_SESSION) * cache;
+  LHASH_OF(SSL_SESSION) *cache;
 } TIMEOUT_PARAM;
 
 static void timeout_doall_arg(SSL_SESSION *sess, void *void_param) {
@@ -896,18 +863,6 @@
   return ctx->client_cert_cb;
 }
 
-void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx,
-                                    int (*cb)(SSL *ssl, uint8_t *cookie,
-                                              size_t *cookie_len)) {
-  ctx->app_gen_cookie_cb = cb;
-}
-
-void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx,
-                                  int (*cb)(SSL *ssl, const uint8_t *cookie,
-                                            size_t cookie_len)) {
-  ctx->app_verify_cookie_cb = cb;
-}
-
 void SSL_CTX_set_channel_id_cb(SSL_CTX *ctx,
                                void (*cb)(SSL *ssl, EVP_PKEY **pkey)) {
   ctx->channel_id_cb = cb;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index 450ed7c..8bed9ad 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -83,7 +83,7 @@
  */
 
 #include <stdio.h>
-#include "ssl_locl.h"
+#include "internal.h"
 
 const char *SSL_state_string_long(const SSL *s) {
   const char *str;
@@ -382,14 +382,6 @@
       str = "DTLS1 read hello verify request B";
       break;
 
-    case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A:
-      str = "DTLS1 write hello verify request A";
-      break;
-
-    case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B:
-      str = "DTLS1 write hello verify request B";
-      break;
-
     default:
       str = "unknown state";
       break;
@@ -691,14 +683,6 @@
       str = "DRCHVB";
       break;
 
-    case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A:
-      str = "DWCHVA";
-      break;
-
-    case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B:
-      str = "DWCHVB";
-      break;
-
     default:
       str = "UNKWN ";
       break;
diff --git a/src/ssl/ssl_test.c b/src/ssl/ssl_test.c
deleted file mode 100644
index 70291a2..0000000
--- a/src/ssl/ssl_test.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/* Copyright (c) 2014, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#include <stdio.h>
-#include <string.h>
-
-#include <openssl/base64.h>
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-
-typedef struct {
-  int id;
-  int in_group_flag;
-} EXPECTED_CIPHER;
-
-typedef struct {
-  /* The rule string to apply. */
-  const char *rule;
-  /* The list of expected ciphers, in order, terminated with -1. */
-  const EXPECTED_CIPHER *expected;
-} CIPHER_TEST;
-
-/* Selecting individual ciphers should work. */
-static const char kRule1[] =
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "ECDHE-ECDSA-AES128-GCM-SHA256:"
-    "ECDHE-RSA-AES128-GCM-SHA256";
-
-static const EXPECTED_CIPHER kExpected1[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* + reorders selected ciphers to the end, keeping their relative
- * order. */
-static const char kRule2[] =
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "ECDHE-ECDSA-AES128-GCM-SHA256:"
-    "ECDHE-RSA-AES128-GCM-SHA256:"
-    "+aRSA";
-
-static const EXPECTED_CIPHER kExpected2[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* ! banishes ciphers from future selections. */
-static const char kRule3[] =
-    "!aRSA:"
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "ECDHE-ECDSA-AES128-GCM-SHA256:"
-    "ECDHE-RSA-AES128-GCM-SHA256";
-
-static const EXPECTED_CIPHER kExpected3[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* Multiple masks can be ANDed in a single rule. */
-static const char kRule4[] = "kRSA+AESGCM+AES128";
-
-static const EXPECTED_CIPHER kExpected4[] = {
-  { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* - removes selected ciphers, but preserves their order for future
- * selections. Select AES_128_GCM, but order the key exchanges RSA,
- * DHE_RSA, ECDHE_RSA. */
-static const char kRule5[] =
-    "ALL:-kEECDH:-kEDH:-kRSA:-ALL:"
-    "AESGCM+AES128+aRSA";
-
-static const EXPECTED_CIPHER kExpected5[] = {
-  { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* Unknown selectors are no-ops. */
-static const char kRule6[] =
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "ECDHE-ECDSA-AES128-GCM-SHA256:"
-    "ECDHE-RSA-AES128-GCM-SHA256:"
-    "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4";
-
-static const EXPECTED_CIPHER kExpected6[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* Square brackets specify equi-preference groups. */
-static const char kRule7[] =
-    "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:"
-    "[ECDHE-RSA-CHACHA20-POLY1305]:"
-    "ECDHE-RSA-AES128-GCM-SHA256";
-
-static const EXPECTED_CIPHER kExpected7[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 1 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { -1, -1 },
-};
-
-/* @STRENGTH performs a stable strength-sort of the selected
- * ciphers and only the selected ciphers. */
-static const char kRule8[] =
-    /* To simplify things, banish all but {ECDHE_RSA,RSA} x
-     * {CHACHA20,AES_256_CBC,AES_128_CBC,RC4} x SHA1. */
-    "!kEDH:!AESGCM:!3DES:!SHA256:!MD5:!SHA384:"
-    /* Order some ciphers backwards by strength. */
-    "ALL:-CHACHA20:-AES256:-AES128:-RC4:-ALL:"
-    /* Select ECDHE ones and sort them by strength. Ties should resolve
-     * based on the order above. */
-    "kEECDH:@STRENGTH:-ALL:"
-    /* Now bring back everything uses RSA. ECDHE_RSA should be first,
-     * sorted by strength. Then RSA, backwards by strength. */
-    "aRSA";
-
-static const EXPECTED_CIPHER kExpected8[] = {
-  { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 },
-  { SSL3_CK_RSA_RC4_128_SHA, 0 },
-  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
-  { TLS1_CK_RSA_WITH_AES_256_SHA, 0 },
-  { -1, -1 },
-};
-
-static CIPHER_TEST kCipherTests[] = {
-  { kRule1, kExpected1 },
-  { kRule2, kExpected2 },
-  { kRule3, kExpected3 },
-  { kRule4, kExpected4 },
-  { kRule5, kExpected5 },
-  { kRule6, kExpected6 },
-  { kRule7, kExpected7 },
-  { kRule8, kExpected8 },
-  { NULL, NULL },
-};
-
-static const char *kBadRules[] = {
-  /* Invalid brackets. */
-  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256",
-  "RSA]",
-  "[[RSA]]",
-  /* Operators inside brackets */
-  "[+RSA]",
-  /* Unknown directive. */
-  "@BOGUS",
-  /* Empty cipher lists error at SSL_CTX_set_cipher_list. */
-  "",
-  "BOGUS",
-  /* Invalid command. */
-  "?BAR",
-  /* Special operators are not allowed if groups are used. */
-  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:+FOO",
-  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:!FOO",
-  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:-FOO",
-  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:@STRENGTH",
-  NULL,
-};
-
-static void print_cipher_preference_list(
-    struct ssl_cipher_preference_list_st *list) {
-  size_t i;
-  int in_group = 0;
-  for (i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
-    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i);
-    if (!in_group && list->in_group_flags[i]) {
-      fprintf(stderr, "\t[\n");
-      in_group = 1;
-    }
-    fprintf(stderr, "\t");
-    if (in_group) {
-      fprintf(stderr, "  ");
-    }
-    fprintf(stderr, "%s\n", SSL_CIPHER_get_name(cipher));
-    if (in_group && !list->in_group_flags[i]) {
-      fprintf(stderr, "\t]\n");
-      in_group = 0;
-    }
-  }
-}
-
-static int test_cipher_rule(CIPHER_TEST *t) {
-  int ret = 0;
-  SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
-  size_t i;
-
-  if (!SSL_CTX_set_cipher_list(ctx, t->rule)) {
-    fprintf(stderr, "Error testing cipher rule '%s'\n", t->rule);
-    BIO_print_errors_fp(stderr);
-    goto done;
-  }
-
-  /* Compare the two lists. */
-  for (i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
-    const SSL_CIPHER *cipher =
-        sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i);
-    if (t->expected[i].id != SSL_CIPHER_get_id(cipher) ||
-        t->expected[i].in_group_flag != ctx->cipher_list->in_group_flags[i]) {
-      fprintf(stderr, "Error: cipher rule '%s' evaluted to:\n", t->rule);
-      print_cipher_preference_list(ctx->cipher_list);
-      goto done;
-    }
-  }
-
-  if (t->expected[i].id != -1) {
-    fprintf(stderr, "Error: cipher rule '%s' evaluted to:\n", t->rule);
-    print_cipher_preference_list(ctx->cipher_list);
-    goto done;
-  }
-
-  ret = 1;
-done:
-  SSL_CTX_free(ctx);
-  return ret;
-}
-
-static int test_cipher_rules(void) {
-  size_t i;
-  for (i = 0; kCipherTests[i].rule != NULL; i++) {
-    if (!test_cipher_rule(&kCipherTests[i])) {
-      return 0;
-    }
-  }
-
-  for (i = 0; kBadRules[i] != NULL; i++) {
-    SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
-    if (SSL_CTX_set_cipher_list(ctx, kBadRules[i])) {
-      fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", kBadRules[i]);
-      return 0;
-    }
-    ERR_clear_error();
-    SSL_CTX_free(ctx);
-  }
-
-  return 1;
-}
-
-/* kOpenSSLSession is a serialized SSL_SESSION generated from openssl
- * s_client -sess_out. */
-static const char kOpenSSLSession[] =
-    "MIIFpQIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
-    "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
-    "IWoJoQYCBFRDO46iBAICASyjggR6MIIEdjCCA16gAwIBAgIIK9dUvsPWSlUwDQYJ"
-    "KoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMx"
-    "JTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTQxMDA4"
-    "MTIwNzU3WhcNMTUwMTA2MDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwK"
-    "Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29v"
-    "Z2xlIEluYzEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEB"
-    "AQUAA4IBDwAwggEKAoIBAQCcKeLrplAC+Lofy8t/wDwtB6eu72CVp0cJ4V3lknN6"
-    "huH9ct6FFk70oRIh/VBNBBz900jYy+7111Jm1b8iqOTQ9aT5C7SEhNcQFJvqzH3e"
-    "MPkb6ZSWGm1yGF7MCQTGQXF20Sk/O16FSjAynU/b3oJmOctcycWYkY0ytS/k3LBu"
-    "Id45PJaoMqjB0WypqvNeJHC3q5JjCB4RP7Nfx5jjHSrCMhw8lUMW4EaDxjaR9KDh"
-    "PLgjsk+LDIySRSRDaCQGhEOWLJZVLzLo4N6/UlctCHEllpBUSvEOyFga52qroGjg"
-    "rf3WOQ925MFwzd6AK+Ich0gDRg8sQfdLH5OuP1cfLfU1AgMBAAGjggFBMIIBPTAd"
-    "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdv"
-    "b2dsZS5jb20waAYIKwYBBQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtp"
-    "Lmdvb2dsZS5jb20vR0lBRzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50"
-    "czEuZ29vZ2xlLmNvbS9vY3NwMB0GA1UdDgQWBBQ7a+CcxsZByOpc+xpYFcIbnUMZ"
-    "hTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEv"
-    "MBcGA1UdIAQQMA4wDAYKKwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRw"
-    "Oi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCa"
-    "OXCBdoqUy5bxyq+Wrh1zsyyCFim1PH5VU2+yvDSWrgDY8ibRGJmfff3r4Lud5kal"
-    "dKs9k8YlKD3ITG7P0YT/Rk8hLgfEuLcq5cc0xqmE42xJ+Eo2uzq9rYorc5emMCxf"
-    "5L0TJOXZqHQpOEcuptZQ4OjdYMfSxk5UzueUhA3ogZKRcRkdB3WeWRp+nYRhx4St"
-    "o2rt2A0MKmY9165GHUqMK9YaaXHDXqBu7Sefr1uSoAP9gyIJKeihMivsGqJ1TD6Z"
-    "cc6LMe+dN2P8cZEQHtD1y296ul4Mivqk3jatUVL8/hCwgch9A8O4PGZq9WqBfEWm"
-    "IyHh1dPtbg1lOXdYCWtjpAIEAKUDAgEUqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36S"
-    "YTcLEkXqKwOBfF9vE4KX0NxeLwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9B"
-    "sNHM362zZnY27GpTw+Kwd751CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yE"
-    "OTDKPNj3+inbMaVigtK4PLyPq+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdA"
-    "i4gv7Y5oliyn";
-
-/* kCustomSession is a custom serialized SSL_SESSION generated by
- * filling in missing fields from |kOpenSSLSession|. This includes
- * providing |peer_sha256|, so |peer| is not serialized. */
-static const char kCustomSession[] =
-    "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
-    "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
-    "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE"
-    "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe"
-    "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751"
-    "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP"
-    "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG"
-    "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF";
-
-static int decode_base64(uint8_t **out, size_t *out_len, const char *in) {
-  size_t len;
-
-  if (!EVP_DecodedLength(&len, strlen(in))) {
-    fprintf(stderr, "EVP_DecodedLength failed\n");
-    return 0;
-  }
-
-  *out = OPENSSL_malloc(len);
-  if (*out == NULL) {
-    fprintf(stderr, "malloc failed\n");
-    return 0;
-  }
-
-  if (!EVP_DecodeBase64(*out, out_len, len, (const uint8_t *)in,
-                        strlen(in))) {
-    fprintf(stderr, "EVP_DecodeBase64 failed\n");
-    OPENSSL_free(*out);
-    *out = NULL;
-    return 0;
-  }
-  return 1;
-}
-
-static int test_ssl_session_asn1(const char *input_b64) {
-  int ret = 0, len;
-  size_t input_len, encoded_len;
-  uint8_t *input = NULL, *encoded = NULL;
-  const uint8_t *cptr;
-  uint8_t *ptr;
-  SSL_SESSION *session = NULL;
-
-  /* Decode the input. */
-  if (!decode_base64(&input, &input_len, input_b64)) {
-    goto done;
-  }
-
-  /* Verify the SSL_SESSION decodes. */
-  cptr = input;
-  session = d2i_SSL_SESSION(NULL, &cptr, input_len);
-  if (session == NULL || cptr != input + input_len) {
-    fprintf(stderr, "d2i_SSL_SESSION failed\n");
-    goto done;
-  }
-
-  /* Verify the SSL_SESSION encoding round-trips. */
-  if (!SSL_SESSION_to_bytes(session, &encoded, &encoded_len)) {
-    fprintf(stderr, "SSL_SESSION_to_bytes failed\n");
-    goto done;
-  }
-  if (encoded_len != input_len ||
-      memcmp(input, encoded, input_len) != 0) {
-    fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n");
-    goto done;
-  }
-  OPENSSL_free(encoded);
-  encoded = NULL;
-
-  /* Verify the SSL_SESSION encoding round-trips via the legacy API. */
-  len = i2d_SSL_SESSION(session, NULL);
-  if (len < 0 || (size_t)len != input_len) {
-    fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n");
-    goto done;
-  }
-
-  encoded = OPENSSL_malloc(input_len);
-  if (encoded == NULL) {
-    fprintf(stderr, "malloc failed\n");
-    goto done;
-  }
-  ptr = encoded;
-  len = i2d_SSL_SESSION(session, &ptr);
-  if (len < 0 || (size_t)len != input_len) {
-    fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n");
-    goto done;
-  }
-  if (ptr != encoded + input_len) {
-    fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n");
-    goto done;
-  }
-  if (memcmp(input, encoded, input_len) != 0) {
-    fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n");
-    goto done;
-  }
-
-  ret = 1;
-
- done:
-  if (!ret) {
-    BIO_print_errors_fp(stderr);
-  }
-
-  if (session) {
-    SSL_SESSION_free(session);
-  }
-  if (input) {
-    OPENSSL_free(input);
-  }
-  if (encoded) {
-    OPENSSL_free(encoded);
-  }
-  return ret;
-}
-
-int test_default_version(uint16_t version, const SSL_METHOD *(*method)(void)) {
-  SSL_CTX *ctx;
-  int ret;
-
-  ctx = SSL_CTX_new(method());
-  if (ctx == NULL) {
-    return 0;
-  }
-
-  ret = ctx->min_version == version && ctx->max_version == version;
-  SSL_CTX_free(ctx);
-  return ret;
-}
-
-int main(void) {
-  SSL_library_init();
-
-  if (!test_cipher_rules() ||
-      !test_ssl_session_asn1(kOpenSSLSession) ||
-      !test_ssl_session_asn1(kCustomSession) ||
-      !test_default_version(0, &TLS_method) ||
-      !test_default_version(SSL3_VERSION, &SSLv3_method) ||
-      !test_default_version(TLS1_VERSION, &TLSv1_method) ||
-      !test_default_version(TLS1_1_VERSION, &TLSv1_1_method) ||
-      !test_default_version(TLS1_2_VERSION, &TLSv1_2_method) ||
-      !test_default_version(0, &DTLS_method) ||
-      !test_default_version(DTLS1_VERSION, &DTLSv1_method) ||
-      !test_default_version(DTLS1_2_VERSION, &DTLSv1_2_method)) {
-    return 1;
-  }
-
-  printf("PASS\n");
-  return 0;
-}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
new file mode 100644
index 0000000..7886304
--- /dev/null
+++ b/src/ssl/ssl_test.cc
@@ -0,0 +1,509 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <openssl/base64.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "test/scoped_types.h"
+
+struct ExpectedCipher {
+  unsigned long id;
+  int in_group_flag;
+};
+
+struct CipherTest {
+  // The rule string to apply.
+  const char *rule;
+  // The list of expected ciphers, in order, terminated with -1.
+  const ExpectedCipher *expected;
+};
+
+// Selecting individual ciphers should work.
+static const char kRule1[] =
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256";
+
+static const ExpectedCipher kExpected1[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// + reorders selected ciphers to the end, keeping their relative
+// order.
+static const char kRule2[] =
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256:"
+    "+aRSA";
+
+static const ExpectedCipher kExpected2[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// ! banishes ciphers from future selections.
+static const char kRule3[] =
+    "!aRSA:"
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256";
+
+static const ExpectedCipher kExpected3[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// Multiple masks can be ANDed in a single rule.
+static const char kRule4[] = "kRSA+AESGCM+AES128";
+
+static const ExpectedCipher kExpected4[] = {
+  { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// - removes selected ciphers, but preserves their order for future
+// selections. Select AES_128_GCM, but order the key exchanges RSA,
+// DHE_RSA, ECDHE_RSA.
+static const char kRule5[] =
+    "ALL:-kECDHE:-kDHE:-kRSA:-ALL:"
+    "AESGCM+AES128+aRSA";
+
+static const ExpectedCipher kExpected5[] = {
+  { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// Unknown selectors are no-ops.
+static const char kRule6[] =
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256:"
+    "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4";
+
+static const ExpectedCipher kExpected6[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// Square brackets specify equi-preference groups.
+static const char kRule7[] =
+    "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:"
+    "[ECDHE-RSA-CHACHA20-POLY1305]:"
+    "ECDHE-RSA-AES128-GCM-SHA256";
+
+static const ExpectedCipher kExpected7[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 1 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// @STRENGTH performs a stable strength-sort of the selected
+// ciphers and only the selected ciphers.
+static const char kRule8[] =
+    // To simplify things, banish all but {ECDHE_RSA,RSA} x
+    // {CHACHA20,AES_256_CBC,AES_128_CBC,RC4} x SHA1.
+    "!kEDH:!AESGCM:!3DES:!SHA256:!MD5:!SHA384:"
+    // Order some ciphers backwards by strength.
+    "ALL:-CHACHA20:-AES256:-AES128:-RC4:-ALL:"
+    // Select ECDHE ones and sort them by strength. Ties should resolve
+    // based on the order above.
+    "kECDHE:@STRENGTH:-ALL:"
+    // Now bring back everything uses RSA. ECDHE_RSA should be first,
+    // sorted by strength. Then RSA, backwards by strength.
+    "aRSA";
+
+static const ExpectedCipher kExpected8[] = {
+  { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 },
+  { SSL3_CK_RSA_RC4_128_SHA, 0 },
+  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
+  { TLS1_CK_RSA_WITH_AES_256_SHA, 0 },
+  { 0, 0 },
+};
+
+// Exact ciphers may not be used in multi-part rules; they are treated
+// as unknown aliases.
+static const char kRule9[] =
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
+    "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305";
+
+static const ExpectedCipher kExpected9[] = {
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 },
+  { 0, 0 },
+};
+
+static CipherTest kCipherTests[] = {
+  { kRule1, kExpected1 },
+  { kRule2, kExpected2 },
+  { kRule3, kExpected3 },
+  { kRule4, kExpected4 },
+  { kRule5, kExpected5 },
+  { kRule6, kExpected6 },
+  { kRule7, kExpected7 },
+  { kRule8, kExpected8 },
+  { kRule9, kExpected9 },
+  { NULL, NULL },
+};
+
+static const char *kBadRules[] = {
+  // Invalid brackets.
+  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256",
+  "RSA]",
+  "[[RSA]]",
+  // Operators inside brackets.
+  "[+RSA]",
+  // Unknown directive.
+  "@BOGUS",
+  // Empty cipher lists error at SSL_CTX_set_cipher_list.
+  "",
+  "BOGUS",
+  // COMPLEMENTOFDEFAULT is empty.
+  "COMPLEMENTOFDEFAULT",
+  // Invalid command.
+  "?BAR",
+  // Special operators are not allowed if groups are used.
+  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:+FOO",
+  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:!FOO",
+  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:-FOO",
+  "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:@STRENGTH",
+  NULL,
+};
+
+static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) {
+  bool in_group = false;
+  for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
+    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i);
+    if (!in_group && list->in_group_flags[i]) {
+      fprintf(stderr, "\t[\n");
+      in_group = true;
+    }
+    fprintf(stderr, "\t");
+    if (in_group) {
+      fprintf(stderr, "  ");
+    }
+    fprintf(stderr, "%s\n", SSL_CIPHER_get_name(cipher));
+    if (in_group && !list->in_group_flags[i]) {
+      fprintf(stderr, "\t]\n");
+      in_group = false;
+    }
+  }
+}
+
+static bool TestCipherRule(CipherTest *t) {
+  ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method()));
+  if (!ctx) {
+    return false;
+  }
+
+  if (!SSL_CTX_set_cipher_list(ctx.get(), t->rule)) {
+    fprintf(stderr, "Error testing cipher rule '%s'\n", t->rule);
+    return false;
+  }
+
+  // Compare the two lists.
+  size_t i;
+  for (i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
+    const SSL_CIPHER *cipher =
+        sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i);
+    if (t->expected[i].id != SSL_CIPHER_get_id(cipher) ||
+        t->expected[i].in_group_flag != ctx->cipher_list->in_group_flags[i]) {
+      fprintf(stderr, "Error: cipher rule '%s' evaluated to:\n", t->rule);
+      PrintCipherPreferenceList(ctx->cipher_list);
+      return false;
+    }
+  }
+
+  if (t->expected[i].id != 0) {
+    fprintf(stderr, "Error: cipher rule '%s' evaluated to:\n", t->rule);
+    PrintCipherPreferenceList(ctx->cipher_list);
+    return false;
+  }
+
+  return true;
+}
+
+static bool TestCipherRules() {
+  for (size_t i = 0; kCipherTests[i].rule != NULL; i++) {
+    if (!TestCipherRule(&kCipherTests[i])) {
+      return false;
+    }
+  }
+
+  for (size_t i = 0; kBadRules[i] != NULL; i++) {
+    ScopedSSL_CTX ctx(SSL_CTX_new(SSLv23_server_method()));
+    if (!ctx) {
+      return false;
+    }
+    if (SSL_CTX_set_cipher_list(ctx.get(), kBadRules[i])) {
+      fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", kBadRules[i]);
+      return false;
+    }
+    ERR_clear_error();
+  }
+
+  return true;
+}
+
+// kOpenSSLSession is a serialized SSL_SESSION generated from openssl
+// s_client -sess_out.
+static const char kOpenSSLSession[] =
+    "MIIFpQIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
+    "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
+    "IWoJoQYCBFRDO46iBAICASyjggR6MIIEdjCCA16gAwIBAgIIK9dUvsPWSlUwDQYJ"
+    "KoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMx"
+    "JTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTQxMDA4"
+    "MTIwNzU3WhcNMTUwMTA2MDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwK"
+    "Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29v"
+    "Z2xlIEluYzEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEB"
+    "AQUAA4IBDwAwggEKAoIBAQCcKeLrplAC+Lofy8t/wDwtB6eu72CVp0cJ4V3lknN6"
+    "huH9ct6FFk70oRIh/VBNBBz900jYy+7111Jm1b8iqOTQ9aT5C7SEhNcQFJvqzH3e"
+    "MPkb6ZSWGm1yGF7MCQTGQXF20Sk/O16FSjAynU/b3oJmOctcycWYkY0ytS/k3LBu"
+    "Id45PJaoMqjB0WypqvNeJHC3q5JjCB4RP7Nfx5jjHSrCMhw8lUMW4EaDxjaR9KDh"
+    "PLgjsk+LDIySRSRDaCQGhEOWLJZVLzLo4N6/UlctCHEllpBUSvEOyFga52qroGjg"
+    "rf3WOQ925MFwzd6AK+Ich0gDRg8sQfdLH5OuP1cfLfU1AgMBAAGjggFBMIIBPTAd"
+    "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdv"
+    "b2dsZS5jb20waAYIKwYBBQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtp"
+    "Lmdvb2dsZS5jb20vR0lBRzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50"
+    "czEuZ29vZ2xlLmNvbS9vY3NwMB0GA1UdDgQWBBQ7a+CcxsZByOpc+xpYFcIbnUMZ"
+    "hTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEv"
+    "MBcGA1UdIAQQMA4wDAYKKwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRw"
+    "Oi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCa"
+    "OXCBdoqUy5bxyq+Wrh1zsyyCFim1PH5VU2+yvDSWrgDY8ibRGJmfff3r4Lud5kal"
+    "dKs9k8YlKD3ITG7P0YT/Rk8hLgfEuLcq5cc0xqmE42xJ+Eo2uzq9rYorc5emMCxf"
+    "5L0TJOXZqHQpOEcuptZQ4OjdYMfSxk5UzueUhA3ogZKRcRkdB3WeWRp+nYRhx4St"
+    "o2rt2A0MKmY9165GHUqMK9YaaXHDXqBu7Sefr1uSoAP9gyIJKeihMivsGqJ1TD6Z"
+    "cc6LMe+dN2P8cZEQHtD1y296ul4Mivqk3jatUVL8/hCwgch9A8O4PGZq9WqBfEWm"
+    "IyHh1dPtbg1lOXdYCWtjpAIEAKUDAgEUqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36S"
+    "YTcLEkXqKwOBfF9vE4KX0NxeLwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9B"
+    "sNHM362zZnY27GpTw+Kwd751CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yE"
+    "OTDKPNj3+inbMaVigtK4PLyPq+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdA"
+    "i4gv7Y5oliyn";
+
+// kCustomSession is a custom serialized SSL_SESSION generated by
+// filling in missing fields from |kOpenSSLSession|. This includes
+// providing |peer_sha256|, so |peer| is not serialized.
+static const char kCustomSession[] =
+    "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
+    "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
+    "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE"
+    "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe"
+    "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751"
+    "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP"
+    "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG"
+    "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF";
+
+static bool DecodeBase64(std::vector<uint8_t> *out, const char *in) {
+  size_t len;
+  if (!EVP_DecodedLength(&len, strlen(in))) {
+    fprintf(stderr, "EVP_DecodedLength failed\n");
+    return false;
+  }
+
+  out->resize(len);
+  if (!EVP_DecodeBase64(bssl::vector_data(out), &len, len, (const uint8_t *)in,
+                        strlen(in))) {
+    fprintf(stderr, "EVP_DecodeBase64 failed\n");
+    return false;
+  }
+  out->resize(len);
+  return true;
+}
+
+static bool TestSSL_SESSIONEncoding(const char *input_b64) {
+  const uint8_t *cptr;
+  uint8_t *ptr;
+
+  // Decode the input.
+  std::vector<uint8_t> input;
+  if (!DecodeBase64(&input, input_b64)) {
+    return false;
+  }
+
+  // Verify the SSL_SESSION decodes.
+  cptr = bssl::vector_data(&input);
+  ScopedSSL_SESSION session(d2i_SSL_SESSION(NULL, &cptr, input.size()));
+  if (!session || cptr != bssl::vector_data(&input) + input.size()) {
+    fprintf(stderr, "d2i_SSL_SESSION failed\n");
+    return false;
+  }
+
+  // Verify the SSL_SESSION encoding round-trips.
+  size_t encoded_len;
+  ScopedOpenSSLBytes encoded;
+  uint8_t *encoded_raw;
+  if (!SSL_SESSION_to_bytes(session.get(), &encoded_raw, &encoded_len)) {
+    fprintf(stderr, "SSL_SESSION_to_bytes failed\n");
+    return false;
+  }
+  encoded.reset(encoded_raw);
+  if (encoded_len != input.size() ||
+      memcmp(bssl::vector_data(&input), encoded.get(), input.size()) != 0) {
+    fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n");
+    return false;
+  }
+
+  // Verify the SSL_SESSION encoding round-trips via the legacy API.
+  int len = i2d_SSL_SESSION(session.get(), NULL);
+  if (len < 0 || (size_t)len != input.size()) {
+    fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n");
+    return false;
+  }
+
+  encoded.reset((uint8_t *)OPENSSL_malloc(input.size()));
+  if (!encoded) {
+    fprintf(stderr, "malloc failed\n");
+    return false;
+  }
+
+  ptr = encoded.get();
+  len = i2d_SSL_SESSION(session.get(), &ptr);
+  if (len < 0 || (size_t)len != input.size()) {
+    fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n");
+    return false;
+  }
+  if (ptr != encoded.get() + input.size()) {
+    fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n");
+    return false;
+  }
+  if (memcmp(bssl::vector_data(&input), encoded.get(), input.size()) != 0) {
+    fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n");
+    return false;
+  }
+
+  return true;
+}
+
+static bool TestDefaultVersion(uint16_t version,
+                               const SSL_METHOD *(*method)(void)) {
+  ScopedSSL_CTX ctx(SSL_CTX_new(method()));
+  if (!ctx) {
+    return false;
+  }
+  return ctx->min_version == version && ctx->max_version == version;
+}
+
+static bool CipherGetRFCName(std::string *out, uint16_t value) {
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(value);
+  if (cipher == NULL) {
+    return false;
+  }
+  ScopedOpenSSLString rfc_name(SSL_CIPHER_get_rfc_name(cipher));
+  out->assign(rfc_name.get());
+  return true;
+}
+
+typedef struct {
+  int id;
+  const char *rfc_name;
+} CIPHER_RFC_NAME_TEST;
+
+static const CIPHER_RFC_NAME_TEST kCipherRFCNameTests[] = {
+  { SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA" },
+  { SSL3_CK_RSA_RC4_128_MD5, "TLS_RSA_WITH_RC4_MD5" },
+  { TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA" },
+  { TLS1_CK_DHE_RSA_WITH_AES_256_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" },
+  { TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
+    "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256" },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" },
+  { TLS1_CK_PSK_WITH_RC4_128_SHA, "TLS_PSK_WITH_RC4_SHA" },
+  // These names are non-standard:
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305,
+    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" },
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305,
+    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" },
+  { TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+    "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256" },
+};
+
+static bool TestCipherGetRFCName(void) {
+  for (size_t i = 0;
+       i < sizeof(kCipherRFCNameTests) / sizeof(kCipherRFCNameTests[0]); i++) {
+    const CIPHER_RFC_NAME_TEST *test = &kCipherRFCNameTests[i];
+    std::string rfc_name;
+    if (!CipherGetRFCName(&rfc_name, test->id & 0xffff)) {
+      fprintf(stderr, "SSL_CIPHER_get_rfc_name failed\n");
+      return false;
+    }
+    if (rfc_name != test->rfc_name) {
+      fprintf(stderr, "SSL_CIPHER_get_rfc_name: got '%s', wanted '%s'\n",
+              rfc_name.c_str(), test->rfc_name);
+      return false;
+    }
+  }
+  return true;
+}
+
+int main(void) {
+  SSL_library_init();
+
+  if (!TestCipherRules() ||
+      !TestSSL_SESSIONEncoding(kOpenSSLSession) ||
+      !TestSSL_SESSIONEncoding(kCustomSession) ||
+      !TestDefaultVersion(0, &TLS_method) ||
+      !TestDefaultVersion(SSL3_VERSION, &SSLv3_method) ||
+      !TestDefaultVersion(TLS1_VERSION, &TLSv1_method) ||
+      !TestDefaultVersion(TLS1_1_VERSION, &TLSv1_1_method) ||
+      !TestDefaultVersion(TLS1_2_VERSION, &TLSv1_2_method) ||
+      !TestDefaultVersion(0, &DTLS_method) ||
+      !TestDefaultVersion(DTLS1_VERSION, &DTLSv1_method) ||
+      !TestDefaultVersion(DTLS1_2_VERSION, &DTLSv1_2_method) ||
+      !TestCipherGetRFCName()) {
+    ERR_print_errors_fp(stderr);
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}
diff --git a/src/ssl/ssl_txt.c b/src/ssl/ssl_txt.c
index c950ce8..2275f16 100644
--- a/src/ssl/ssl_txt.c
+++ b/src/ssl/ssl_txt.c
@@ -87,7 +87,7 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 int SSL_SESSION_print_fp(FILE *fp, const SSL_SESSION *x) {
@@ -145,8 +145,9 @@
   }
 
   for (i = 0; i < x->session_id_length; i++) {
-    if (BIO_printf(bp, "%02X", x->session_id[i]) <= 0)
+    if (BIO_printf(bp, "%02X", x->session_id[i]) <= 0) {
       goto err;
+    }
   }
 
   if (BIO_puts(bp, "\n    Session-ID-ctx: ") <= 0) {
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 014bc88..3eaffe7 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -133,8 +133,9 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <openssl/err.h>
 #include <openssl/evp.h>
@@ -144,7 +145,7 @@
 #include <openssl/obj.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 /* tls1_P_hash computes the TLS P_<hash> function as described in RFC 5246,
@@ -225,7 +226,7 @@
              const uint8_t *seed2, size_t seed2_len) {
   size_t idx, len, count, i;
   const uint8_t *S1;
-  long m;
+  uint32_t m;
   const EVP_MD *md;
   int ret = 0;
   uint8_t *tmp;
@@ -243,7 +244,7 @@
 
   /* Count number of digests and partition |secret| evenly. */
   count = 0;
-  for (idx = 0; ssl_get_handshake_digest(idx, &m, &md); idx++) {
+  for (idx = 0; ssl_get_handshake_digest(&m, &md, idx); idx++) {
     if ((m << TLS1_PRF_DGST_SHIFT) & ssl_get_algorithm2(s)) {
       count++;
     }
@@ -258,7 +259,7 @@
   }
   S1 = secret;
   memset(out, 0, out_len);
-  for (idx = 0; ssl_get_handshake_digest(idx, &m, &md); idx++) {
+  for (idx = 0; ssl_get_handshake_digest(&m, &md, idx); idx++) {
     if ((m << TLS1_PRF_DGST_SHIFT) & ssl_get_algorithm2(s)) {
       /* If |count| is 2 and |secret_len| is odd, |secret| is partitioned into
        * two halves with an overlapping byte. */
@@ -340,14 +341,12 @@
     }
     aead_ctx = s->aead_read_ctx;
   } else {
-    /* When updating the cipher state for DTLS, we do not wish to overwrite the
-     * old ones because DTLS stores pointers to them in order to implement
-     * retransmission. See dtls1_hm_fragment_free.
-     *
-     * TODO(davidben): Simplify aead_write_ctx ownership, probably by just
-     * forbidding DTLS renego. */
-    if (SSL_IS_DTLS(s)) {
-      s->aead_write_ctx = NULL;
+    if (SSL_IS_DTLS(s) && s->aead_write_ctx != NULL) {
+      /* DTLS renegotiation is unsupported, so a CCS can only switch away from
+       * the NULL cipher. This simplifies renegotiation. */
+      OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead,
+                        ERR_R_INTERNAL_ERROR);
+      return 0;
     }
     if (!tls1_aead_ctx_init(&s->aead_write_ctx)) {
       return 0;
@@ -355,8 +354,9 @@
     aead_ctx = s->aead_write_ctx;
   }
 
-  if (!EVP_AEAD_CTX_init(&aead_ctx->ctx, aead, key, key_len,
-                         EVP_AEAD_DEFAULT_TAG_LENGTH, NULL /* engine */)) {
+  if (!EVP_AEAD_CTX_init_with_direction(
+          &aead_ctx->ctx, aead, key, key_len, EVP_AEAD_DEFAULT_TAG_LENGTH,
+          is_read ? evp_aead_open : evp_aead_seal)) {
     OPENSSL_free(aead_ctx);
     if (is_read) {
       s->aead_read_ctx = NULL;
@@ -578,7 +578,7 @@
     aead = s->aead_read_ctx;
   }
 
-  if (s->session == NULL || aead == NULL) {
+  if (aead == NULL) {
     /* Handle the initial NULL cipher. */
     memmove(rec->data, rec->input, rec->length);
     rec->input = rec->data;
@@ -598,13 +598,9 @@
     memcpy(p, &seq[2], 6);
     memcpy(ad, dtlsseq, 8);
   } else {
-    int i;
     memcpy(ad, seq, 8);
-    for (i = 7; i >= 0; i--) {
-      ++seq[i];
-      if (seq[i] != 0) {
-        break;
-      }
+    if (!ssl3_record_sequence_update(seq, 8)) {
+      return 0;
     }
   }
 
@@ -739,7 +735,10 @@
   }
 
   EVP_MD_CTX_init(&ctx);
-  EVP_MD_CTX_copy_ex(&ctx, d);
+  if (!EVP_MD_CTX_copy_ex(&ctx, d)) {
+    EVP_MD_CTX_cleanup(&ctx);
+    return 0;
+  }
   EVP_DigestFinal_ex(&ctx, out, &ret);
   EVP_MD_CTX_cleanup(&ctx);
 
@@ -756,11 +755,11 @@
   EVP_MD_CTX ctx;
   int err = 0, len = 0;
   size_t i;
-  long mask;
+  uint32_t mask;
 
   EVP_MD_CTX_init(&ctx);
 
-  for (i = 0; ssl_get_handshake_digest(i, &mask, &md); i++) {
+  for (i = 0; ssl_get_handshake_digest(&mask, &md, i); i++) {
     size_t hash_size;
     unsigned int digest_len;
     EVP_MD_CTX *hdgst = s->s3->handshake_dgst[i];
@@ -863,82 +862,42 @@
   return SSL3_MASTER_SECRET_SIZE;
 }
 
-int tls1_export_keying_material(SSL *s, uint8_t *out, size_t olen,
-                                const char *label, size_t llen,
-                                const uint8_t *context, size_t contextlen,
+int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len,
+                                const char *label, size_t label_len,
+                                const uint8_t *context, size_t context_len,
                                 int use_context) {
-  uint8_t *val = NULL;
-  size_t vallen, currentvalpos;
-  int ret;
-
-  /* construct PRF arguments we construct the PRF argument ourself rather than
-   * passing separate values into the TLS PRF to ensure that the concatenation
-   * of values does not create a prohibited label. */
-  vallen = llen + SSL3_RANDOM_SIZE * 2;
-  if (use_context) {
-    vallen += 2 + contextlen;
+  if (!s->s3->have_version || s->version == SSL3_VERSION) {
+    OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material,
+                      ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
   }
 
-  val = OPENSSL_malloc(vallen);
-  if (val == NULL) {
-    goto err2;
-  }
-
-  currentvalpos = 0;
-  memcpy(val + currentvalpos, (uint8_t *)label, llen);
-  currentvalpos += llen;
-  memcpy(val + currentvalpos, s->s3->client_random, SSL3_RANDOM_SIZE);
-  currentvalpos += SSL3_RANDOM_SIZE;
-  memcpy(val + currentvalpos, s->s3->server_random, SSL3_RANDOM_SIZE);
-  currentvalpos += SSL3_RANDOM_SIZE;
-
+  size_t seed_len = 2 * SSL3_RANDOM_SIZE;
   if (use_context) {
-    val[currentvalpos] = (contextlen >> 8) & 0xff;
-    currentvalpos++;
-    val[currentvalpos] = contextlen & 0xff;
-    currentvalpos++;
-    if (contextlen > 0 || context != NULL) {
-      memcpy(val + currentvalpos, context, contextlen);
+    if (context_len >= 1u << 16) {
+      OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_OVERFLOW);
+      return 0;
     }
+    seed_len += 2 + context_len;
+  }
+  uint8_t *seed = OPENSSL_malloc(seed_len);
+  if (seed == NULL) {
+    OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_MALLOC_FAILURE);
+    return 0;
   }
 
-  /* disallow prohibited labels note that SSL3_RANDOM_SIZE > max(prohibited
-   * label len) = 15, so size of val > max(prohibited label len) = 15 and the
-   * comparisons won't have buffer overflow. */
-  if (memcmp(val, TLS_MD_CLIENT_FINISH_CONST,
-             TLS_MD_CLIENT_FINISH_CONST_SIZE) == 0 ||
-      memcmp(val, TLS_MD_SERVER_FINISH_CONST,
-             TLS_MD_SERVER_FINISH_CONST_SIZE) == 0 ||
-      memcmp(val, TLS_MD_MASTER_SECRET_CONST,
-             TLS_MD_MASTER_SECRET_CONST_SIZE) == 0 ||
-      memcmp(val, TLS_MD_KEY_EXPANSION_CONST,
-             TLS_MD_KEY_EXPANSION_CONST_SIZE) == 0) {
-    goto err1;
+  memcpy(seed, s->s3->client_random, SSL3_RANDOM_SIZE);
+  memcpy(seed + SSL3_RANDOM_SIZE, s->s3->server_random, SSL3_RANDOM_SIZE);
+  if (use_context) {
+    seed[2 * SSL3_RANDOM_SIZE] = (uint8_t)(context_len >> 8);
+    seed[2 * SSL3_RANDOM_SIZE + 1] = (uint8_t)context_len;
+    memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len);
   }
 
-  /* SSL_export_keying_material is not implemented for SSLv3, so passing
-   * everything through the label parameter works. */
-  assert(s->version != SSL3_VERSION);
-  ret = s->enc_method->prf(s, out, olen, s->session->master_key,
-                           s->session->master_key_length, (const char *)val,
-                           vallen, NULL, 0, NULL, 0);
-  goto out;
-
-err1:
-  OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material,
-                    SSL_R_TLS_ILLEGAL_EXPORTER_LABEL);
-  ret = 0;
-  goto out;
-
-err2:
-  OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_MALLOC_FAILURE);
-  ret = 0;
-
-out:
-  if (val != NULL) {
-    OPENSSL_free(val);
-  }
-
+  int ret = s->enc_method->prf(s, out, out_len, s->session->master_key,
+                               s->session->master_key_length, label, label_len,
+                               seed, seed_len, NULL, 0);
+  OPENSSL_free(seed);
   return ret;
 }
 
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index e26351b..433a647 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -106,18 +106,20 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <assert.h>
+#include <string.h>
 
 #include <openssl/bytestring.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/rand.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 static int tls_decrypt_ticket(SSL *s, const uint8_t *tick, int ticklen,
@@ -133,16 +135,12 @@
     tls1_generate_master_secret,
     tls1_change_cipher_state,
     tls1_final_finish_mac,
-    TLS1_FINISH_MAC_LENGTH,
     tls1_cert_verify_mac,
     TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
     TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
     tls1_alert_code,
     tls1_export_keying_material,
     0,
-    SSL3_HM_HEADER_LENGTH,
-    ssl3_set_handshake_header,
-    ssl3_handshake_write,
 };
 
 const SSL3_ENC_METHOD TLSv1_1_enc_data = {
@@ -152,16 +150,12 @@
     tls1_generate_master_secret,
     tls1_change_cipher_state,
     tls1_final_finish_mac,
-    TLS1_FINISH_MAC_LENGTH,
     tls1_cert_verify_mac,
     TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
     TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
     tls1_alert_code,
     tls1_export_keying_material,
     SSL_ENC_FLAG_EXPLICIT_IV,
-    SSL3_HM_HEADER_LENGTH,
-    ssl3_set_handshake_header,
-    ssl3_handshake_write,
 };
 
 const SSL3_ENC_METHOD TLSv1_2_enc_data = {
@@ -171,7 +165,6 @@
     tls1_generate_master_secret,
     tls1_change_cipher_state,
     tls1_final_finish_mac,
-    TLS1_FINISH_MAC_LENGTH,
     tls1_cert_verify_mac,
     TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
     TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
@@ -179,9 +172,6 @@
     tls1_export_keying_material,
     SSL_ENC_FLAG_EXPLICIT_IV|SSL_ENC_FLAG_SIGALGS|SSL_ENC_FLAG_SHA256_PRF
             |SSL_ENC_FLAG_TLS1_2_CIPHERS,
-    SSL3_HM_HEADER_LENGTH,
-    ssl3_set_handshake_header,
-    ssl3_handshake_write,
 };
 
 static int compare_uint16_t(const void *p1, const void *p2) {
@@ -255,8 +245,7 @@
   ret = 1;
 
 done:
-  if (extension_types)
-    OPENSSL_free(extension_types);
+  OPENSSL_free(extension_types);
   return ret;
 }
 
@@ -367,7 +356,7 @@
 };
 
 static const uint16_t eccurves_default[] = {
-    23, /* X9_64_prime256v1 */
+    23, /* X9_62_prime256v1 */
     24, /* secp384r1 */
 };
 
@@ -399,6 +388,9 @@
                                const uint16_t **out_curve_ids,
                                size_t *out_curve_ids_len) {
   if (get_peer_curves) {
+    /* Only clients send a curve list, so this function is only called
+     * on the server. */
+    assert(s->server);
     *out_curve_ids = s->s3->tmp.peer_ellipticcurvelist;
     *out_curve_ids_len = s->s3->tmp.peer_ellipticcurvelist_length;
     return;
@@ -437,22 +429,38 @@
 }
 
 int tls1_get_shared_curve(SSL *s) {
-  const uint16_t *pref, *supp;
-  size_t preflen, supplen, i, j;
+  const uint16_t *curves, *peer_curves, *pref, *supp;
+  size_t curves_len, peer_curves_len, pref_len, supp_len, i, j;
 
   /* Can't do anything on client side */
   if (s->server == 0) {
     return NID_undef;
   }
 
-  /* Return first preference shared curve */
-  tls1_get_curvelist(s, !!(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), &supp,
-                     &supplen);
-  tls1_get_curvelist(s, !(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), &pref,
-                     &preflen);
+  tls1_get_curvelist(s, 0 /* local curves */, &curves, &curves_len);
+  tls1_get_curvelist(s, 1 /* peer curves */, &peer_curves, &peer_curves_len);
 
-  for (i = 0; i < preflen; i++) {
-    for (j = 0; j < supplen; j++) {
+  if (peer_curves_len == 0) {
+    /* Clients are not required to send a supported_curves extension. In this
+     * case, the server is free to pick any curve it likes. See RFC 4492,
+     * section 4, paragraph 3. */
+    return (curves_len == 0) ? NID_undef : tls1_ec_curve_id2nid(curves[0]);
+  }
+
+  if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
+    pref = curves;
+    pref_len = curves_len;
+    supp = peer_curves;
+    supp_len = peer_curves_len;
+  } else {
+    pref = peer_curves;
+    pref_len = peer_curves_len;
+    supp = curves;
+    supp_len = curves_len;
+  }
+
+  for (i = 0; i < pref_len; i++) {
+    for (j = 0; j < supp_len; j++) {
       if (pref[i] == supp[j]) {
         return tls1_ec_curve_id2nid(pref[i]);
       }
@@ -479,9 +487,7 @@
     }
   }
 
-  if (*out_curve_ids) {
-    OPENSSL_free(*out_curve_ids);
-  }
+  OPENSSL_free(*out_curve_ids);
   *out_curve_ids = curve_ids;
   *out_curve_ids_len = ncurves;
 
@@ -556,11 +562,23 @@
  * preferences are checked; the peer (the server) does not send preferences. */
 static int tls1_check_curve_id(SSL *s, uint16_t curve_id) {
   const uint16_t *curves;
-  size_t curves_len, i, j;
+  size_t curves_len, i, get_peer_curves;
 
   /* Check against our list, then the peer's list. */
-  for (j = 0; j <= 1; j++) {
-    tls1_get_curvelist(s, j, &curves, &curves_len);
+  for (get_peer_curves = 0; get_peer_curves <= 1; get_peer_curves++) {
+    if (get_peer_curves && !s->server) {
+      /* Servers do not present a preference list so, if we are a client, only
+       * check our list. */
+      continue;
+    }
+
+    tls1_get_curvelist(s, get_peer_curves, &curves, &curves_len);
+    if (get_peer_curves && curves_len == 0) {
+      /* Clients are not required to send a supported_curves extension. In this
+       * case, the server is free to pick any curve it likes. See RFC 4492,
+       * section 4, paragraph 3. */
+      continue;
+    }
     for (i = 0; i < curves_len; i++) {
       if (curves[i] == curve_id) {
         break;
@@ -570,12 +588,6 @@
     if (i == curves_len) {
       return 0;
     }
-
-    /* Servers do not present a preference list so, if we are a client, only
-     * check our list. */
-    if (!s->server) {
-      return 1;
-    }
   }
 
   return 1;
@@ -610,30 +622,27 @@
   ret = 1;
 
 done:
-  if (pkey) {
-    EVP_PKEY_free(pkey);
-  }
+  EVP_PKEY_free(pkey);
   return ret;
 }
 
 int tls1_check_ec_tmp_key(SSL *s) {
-  uint16_t curve_id;
-  EC_KEY *ec = s->cert->ecdh_tmp;
-
-  if (s->cert->ecdh_tmp_auto) {
-    /* Need a shared curve */
-    return tls1_get_shared_curve(s) != NID_undef;
+  if (s->cert->ecdh_nid != NID_undef) {
+    /* If the curve is preconfigured, ECDH is acceptable iff the peer supports
+     * the curve. */
+    uint16_t curve_id;
+    return tls1_ec_nid2curve_id(&curve_id, s->cert->ecdh_nid) &&
+           tls1_check_curve_id(s, curve_id);
   }
 
-  if (!ec) {
-    if (s->cert->ecdh_tmp_cb) {
-      return 1;
-    }
-    return 0;
+  if (s->cert->ecdh_tmp_cb != NULL) {
+    /* Assume the callback will provide an acceptable curve. */
+    return 1;
   }
 
-  return tls1_curve_params_from_ec_key(&curve_id, NULL, ec) &&
-         tls1_check_curve_id(s, curve_id);
+  /* Otherwise, the curve gets selected automatically. ECDH is acceptable iff
+   * there is a shared curve. */
+  return tls1_get_shared_curve(s) != NID_undef;
 }
 
 /* List of supported signature algorithms and hashes. Should make this
@@ -803,7 +812,7 @@
 
   if (s->version >= TLS1_VERSION || SSL_IS_DTLS(s)) {
     size_t i;
-    unsigned long alg_k, alg_a;
+    uint32_t alg_k, alg_a;
     STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(s);
 
     for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
@@ -811,7 +820,7 @@
 
       alg_k = c->algorithm_mkey;
       alg_a = c->algorithm_auth;
-      if ((alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA)) {
+      if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) {
         using_ecc = 1;
         break;
       }
@@ -1117,9 +1126,9 @@
   uint8_t *orig = buf;
   uint8_t *ret = buf;
   int next_proto_neg_seen;
-  unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-  unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth;
-  int using_ecc = (alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA);
+  uint32_t alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
+  uint32_t alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+  int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA);
   using_ecc = using_ecc && (s->s3->tmp.peer_ecpointformatlist != NULL);
 
   /* don't add extensions for SSLv3, unless doing secure renegotiation */
@@ -1331,9 +1340,7 @@
       s, &selected, &selected_len, CBS_data(&protocol_name_list),
       CBS_len(&protocol_name_list), s->ctx->alpn_select_cb_arg);
   if (r == SSL_TLSEXT_ERR_OK) {
-    if (s->s3->alpn_selected) {
-      OPENSSL_free(s->s3->alpn_selected);
-    }
+    OPENSSL_free(s->s3->alpn_selected);
     s->s3->alpn_selected = BUF_memdup(selected, selected_len);
     if (!s->s3->alpn_selected) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -1359,35 +1366,27 @@
   s->s3->tmp.certificate_status_expected = 0;
   s->s3->tmp.extended_master_secret = 0;
 
-  if (s->s3->alpn_selected) {
-    OPENSSL_free(s->s3->alpn_selected);
-    s->s3->alpn_selected = NULL;
-  }
+  OPENSSL_free(s->s3->alpn_selected);
+  s->s3->alpn_selected = NULL;
 
   /* Clear any signature algorithms extension received */
-  if (s->cert->peer_sigalgs) {
-    OPENSSL_free(s->cert->peer_sigalgs);
-    s->cert->peer_sigalgs = NULL;
-  }
+  OPENSSL_free(s->cert->peer_sigalgs);
+  s->cert->peer_sigalgs = NULL;
+  s->cert->peer_sigalgslen = 0;
 
   /* Clear any shared signature algorithms */
-  if (s->cert->shared_sigalgs) {
-    OPENSSL_free(s->cert->shared_sigalgs);
-    s->cert->shared_sigalgs = NULL;
-  }
+  OPENSSL_free(s->cert->shared_sigalgs);
+  s->cert->shared_sigalgs = NULL;
+  s->cert->shared_sigalgslen = 0;
 
   /* Clear ECC extensions */
-  if (s->s3->tmp.peer_ecpointformatlist != 0) {
-    OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
-    s->s3->tmp.peer_ecpointformatlist = NULL;
-    s->s3->tmp.peer_ecpointformatlist_length = 0;
-  }
+  OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
+  s->s3->tmp.peer_ecpointformatlist = NULL;
+  s->s3->tmp.peer_ecpointformatlist_length = 0;
 
-  if (s->s3->tmp.peer_ellipticcurvelist != 0) {
-    OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-    s->s3->tmp.peer_ellipticcurvelist = NULL;
-    s->s3->tmp.peer_ellipticcurvelist_length = 0;
-  }
+  OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
+  s->s3->tmp.peer_ellipticcurvelist = NULL;
+  s->s3->tmp.peer_ellipticcurvelist_length = 0;
 
   /* There may be no extensions. */
   if (CBS_len(cbs) == 0) {
@@ -1412,11 +1411,6 @@
       return 0;
     }
 
-    if (s->tlsext_debug_cb) {
-      s->tlsext_debug_cb(s, 0, type, (uint8_t *)CBS_data(&extension),
-                         CBS_len(&extension), s->tlsext_debug_arg);
-    }
-
     /* The servername extension is treated as follows:
 
        - Only the hostname type is supported with a maximum length of 255.
@@ -1529,10 +1523,8 @@
         return 0;
       }
 
-      if (s->s3->tmp.peer_ellipticcurvelist) {
-        OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-        s->s3->tmp.peer_ellipticcurvelist_length = 0;
-      }
+      OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
+      s->s3->tmp.peer_ellipticcurvelist_length = 0;
 
       s->s3->tmp.peer_ellipticcurvelist =
           (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list));
@@ -1586,7 +1578,7 @@
       }
       /* If sigalgs received and no shared algorithms fatal error. */
       if (s->cert->peer_sigalgs && !s->cert->shared_sigalgs) {
-        OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext,
+        OPENSSL_PUT_ERROR(SSL, ssl_scan_clienthello_tlsext,
                           SSL_R_NO_SHARED_SIGATURE_ALGORITHMS);
         *out_alert = SSL_AD_ILLEGAL_PARAMETER;
         return 0;
@@ -1714,17 +1706,13 @@
   s->s3->tmp.extended_master_secret = 0;
   s->srtp_profile = NULL;
 
-  if (s->s3->alpn_selected) {
-    OPENSSL_free(s->s3->alpn_selected);
-    s->s3->alpn_selected = NULL;
-  }
+  OPENSSL_free(s->s3->alpn_selected);
+  s->s3->alpn_selected = NULL;
 
   /* Clear ECC extensions */
-  if (s->s3->tmp.peer_ecpointformatlist != 0) {
-    OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
-    s->s3->tmp.peer_ecpointformatlist = NULL;
-    s->s3->tmp.peer_ecpointformatlist_length = 0;
-  }
+  OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
+  s->s3->tmp.peer_ecpointformatlist = NULL;
+  s->s3->tmp.peer_ecpointformatlist_length = 0;
 
   /* There may be no extensions. */
   if (CBS_len(cbs) == 0) {
@@ -1749,11 +1737,6 @@
       return 0;
     }
 
-    if (s->tlsext_debug_cb) {
-      s->tlsext_debug_cb(s, 1, type, (uint8_t *)CBS_data(&extension),
-                         CBS_len(&extension), s->tlsext_debug_arg);
-    }
-
     if (type == TLSEXT_TYPE_server_name) {
       /* The extension must be empty. */
       if (CBS_len(&extension) != 0) {
@@ -1987,9 +1970,9 @@
   /* If we are client and using an elliptic curve cryptography cipher suite,
    * then if server returns an EC point formats lists extension it must contain
    * uncompressed. */
-  unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-  unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth;
-  if (((alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA)) &&
+  uint32_t alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
+  uint32_t alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+  if (((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) &&
       !tls1_check_point_format(s, TLSEXT_ECPOINTFORMAT_uncompressed)) {
     OPENSSL_PUT_ERROR(SSL, ssl_check_serverhello_tlsext,
                       SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST);
@@ -2001,7 +1984,7 @@
     ret = s->ctx->tlsext_servername_callback(s, &al,
                                              s->ctx->tlsext_servername_arg);
   } else if (s->initial_ctx != NULL &&
-           s->initial_ctx->tlsext_servername_callback != 0) {
+             s->initial_ctx->tlsext_servername_callback != 0) {
     ret = s->initial_ctx->tlsext_servername_callback(
         s, &al, s->initial_ctx->tlsext_servername_arg);
   }
@@ -2133,8 +2116,11 @@
   EVP_CIPHER_CTX ctx;
   SSL_CTX *tctx = s->initial_ctx;
 
-  /* Need at least keyname + iv + some encrypted data */
-  if (eticklen < 48) {
+  /* Ensure there is room for the key name and the largest IV
+   * |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but
+   * the maximum IV length should be well under the minimum size for the
+   * session material and HMAC. */
+  if (eticklen < 16 + EVP_MAX_IV_LENGTH) {
     return 2;
   }
 
@@ -2143,7 +2129,8 @@
   EVP_CIPHER_CTX_init(&ctx);
   if (tctx->tlsext_ticket_key_cb) {
     uint8_t *nctick = (uint8_t *)etick;
-    int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16, &ctx, &hctx, 0);
+    int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16, &ctx, &hctx,
+                                        0 /* decrypt */);
     if (rv < 0) {
       return -1;
     }
@@ -2168,13 +2155,13 @@
     }
   }
 
-  /* Attempt to process session ticket, first conduct sanity and integrity
-   * checks on ticket. */
+  /* First, check the MAC. The MAC is at the end of the ticket. */
   mlen = HMAC_size(&hctx);
-  if (mlen < 0) {
+  if ((size_t) eticklen < 16 + EVP_CIPHER_CTX_iv_length(&ctx) + 1 + mlen) {
+    /* The ticket must be large enough for key name, IV, data, and MAC. */
     HMAC_CTX_cleanup(&hctx);
     EVP_CIPHER_CTX_cleanup(&ctx);
-    return -1;
+    return 2;
   }
   eticklen -= mlen;
   /* Check HMAC of encrypted ticket */
@@ -2407,10 +2394,9 @@
   TLS_SIGALGS *salgs = NULL;
   CERT *c = s->cert;
 
-  if (c->shared_sigalgs) {
-    OPENSSL_free(c->shared_sigalgs);
-    c->shared_sigalgs = NULL;
-  }
+  OPENSSL_free(c->shared_sigalgs);
+  c->shared_sigalgs = NULL;
+  c->shared_sigalgslen = 0;
 
   /* If client use client signature algorithms if not NULL */
   if (!s->server && c->client_sigalgs) {
@@ -2460,21 +2446,12 @@
     return 1;
   }
 
-  /* Length must be even */
-  if (CBS_len(sigalgs) % 2 != 0) {
+  if (CBS_len(sigalgs) % 2 != 0 ||
+      !CBS_stow(sigalgs, &c->peer_sigalgs, &c->peer_sigalgslen) ||
+      !tls1_set_shared_sigalgs(s)) {
     return 0;
   }
 
-  /* Should never happen */
-  if (!c) {
-    return 0;
-  }
-
-  if (!CBS_stow(sigalgs, &c->peer_sigalgs, &c->peer_sigalgslen)) {
-    return 0;
-  }
-
-  tls1_set_shared_sigalgs(s);
   return 1;
 }
 
@@ -2583,7 +2560,10 @@
     if (s->s3->handshake_dgst[i] == NULL) {
       continue;
     }
-    EVP_MD_CTX_copy_ex(&ctx, s->s3->handshake_dgst[i]);
+    if (!EVP_MD_CTX_copy_ex(&ctx, s->s3->handshake_dgst[i])) {
+      EVP_MD_CTX_cleanup(&ctx);
+      return 0;
+    }
     EVP_DigestFinal_ex(&ctx, temp_digest, &temp_digest_len);
     EVP_DigestUpdate(md, temp_digest, temp_digest_len);
   }
@@ -2650,15 +2630,11 @@
   }
 
   if (client) {
-    if (c->client_sigalgs) {
-      OPENSSL_free(c->client_sigalgs);
-    }
+    OPENSSL_free(c->client_sigalgs);
     c->client_sigalgs = sigalgs;
     c->client_sigalgslen = salglen;
   } else {
-    if (c->conf_sigalgs) {
-      OPENSSL_free(c->conf_sigalgs);
-    }
+    OPENSSL_free(c->conf_sigalgs);
     c->conf_sigalgs = sigalgs;
     c->conf_sigalgslen = salglen;
   }
diff --git a/src/ssl/t1_reneg.c b/src/ssl/t1_reneg.c
index 2d9fbc0..d0009c1 100644
--- a/src/ssl/t1_reneg.c
+++ b/src/ssl/t1_reneg.c
@@ -106,14 +106,14 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
-#include <stdio.h>
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <openssl/bytestring.h>
-#include <openssl/obj.h>
 #include <openssl/err.h>
 
-#include "ssl_locl.h"
+#include "internal.h"
 
 
 /* Add the client's renegotiation binding */
@@ -170,8 +170,7 @@
                                         int maxlen) {
   if (p) {
     if (s->s3->previous_client_finished_len +
-            s->s3->previous_server_finished_len + 1 >
-        maxlen) {
+            s->s3->previous_server_finished_len + 1 > maxlen) {
       OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_renegotiate_ext,
                         SSL_R_RENEGOTIATE_EXT_TOO_LONG);
       return 0;
diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt
index 9992360..a0d7a5e 100644
--- a/src/ssl/test/CMakeLists.txt
+++ b/src/ssl/test/CMakeLists.txt
@@ -11,6 +11,3 @@
 )
 
 target_link_libraries(bssl_shim ssl crypto)
-if (NOT APPLE AND NOT WIN32)
-  target_link_libraries(bssl_shim dl)
-endif()
diff --git a/src/ssl/test/async_bio.cc b/src/ssl/test/async_bio.cc
index c007ffa..0534845 100644
--- a/src/ssl/test/async_bio.cc
+++ b/src/ssl/test/async_bio.cc
@@ -22,23 +22,23 @@
 
 namespace {
 
-extern const BIO_METHOD async_bio_method;
+extern const BIO_METHOD g_async_bio_method;
 
-struct async_bio {
+struct AsyncBio {
   bool datagram;
   size_t read_quota;
   size_t write_quota;
 };
 
-async_bio *get_data(BIO *bio) {
-  if (bio->method != &async_bio_method) {
+AsyncBio *GetData(BIO *bio) {
+  if (bio->method != &g_async_bio_method) {
     return NULL;
   }
-  return (async_bio *)bio->ptr;
+  return (AsyncBio *)bio->ptr;
 }
 
-static int async_write(BIO *bio, const char *in, int inl) {
-  async_bio *a = get_data(bio);
+static int AsyncWrite(BIO *bio, const char *in, int inl) {
+  AsyncBio *a = GetData(bio);
   if (a == NULL || bio->next_bio == NULL) {
     return 0;
   }
@@ -69,8 +69,8 @@
   return ret;
 }
 
-static int async_read(BIO *bio, char *out, int outl) {
-  async_bio *a = get_data(bio);
+static int AsyncRead(BIO *bio, char *out, int outl) {
+  AsyncBio *a = GetData(bio);
   if (a == NULL || bio->next_bio == NULL) {
     return 0;
   }
@@ -95,7 +95,7 @@
   return ret;
 }
 
-static long async_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
   if (bio->next_bio == NULL) {
     return 0;
   }
@@ -105,8 +105,8 @@
   return ret;
 }
 
-static int async_new(BIO *bio) {
-  async_bio *a = (async_bio *)OPENSSL_malloc(sizeof(*a));
+static int AsyncNew(BIO *bio) {
+  AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
   if (a == NULL) {
     return 0;
   }
@@ -116,7 +116,7 @@
   return 1;
 }
 
-static int async_free(BIO *bio) {
+static int AsyncFree(BIO *bio) {
   if (bio == NULL) {
     return 0;
   }
@@ -128,51 +128,51 @@
   return 1;
 }
 
-static long async_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
   if (bio->next_bio == NULL) {
     return 0;
   }
   return BIO_callback_ctrl(bio->next_bio, cmd, fp);
 }
 
-const BIO_METHOD async_bio_method = {
+const BIO_METHOD g_async_bio_method = {
   BIO_TYPE_FILTER,
   "async bio",
-  async_write,
-  async_read,
+  AsyncWrite,
+  AsyncRead,
   NULL /* puts */,
   NULL /* gets */,
-  async_ctrl,
-  async_new,
-  async_free,
-  async_callback_ctrl,
+  AsyncCtrl,
+  AsyncNew,
+  AsyncFree,
+  AsyncCallbackCtrl,
 };
 
 }  // namespace
 
-BIO *async_bio_create() {
-  return BIO_new(&async_bio_method);
+ScopedBIO AsyncBioCreate() {
+  return ScopedBIO(BIO_new(&g_async_bio_method));
 }
 
-BIO *async_bio_create_datagram() {
-  BIO *ret = BIO_new(&async_bio_method);
+ScopedBIO AsyncBioCreateDatagram() {
+  ScopedBIO ret(BIO_new(&g_async_bio_method));
   if (!ret) {
-    return NULL;
+    return nullptr;
   }
-  get_data(ret)->datagram = true;
+  GetData(ret.get())->datagram = true;
   return ret;
 }
 
-void async_bio_allow_read(BIO *bio, size_t count) {
-  async_bio *a = get_data(bio);
+void AsyncBioAllowRead(BIO *bio, size_t count) {
+  AsyncBio *a = GetData(bio);
   if (a == NULL) {
     return;
   }
   a->read_quota += count;
 }
 
-void async_bio_allow_write(BIO *bio, size_t count) {
-  async_bio *a = get_data(bio);
+void AsyncBioAllowWrite(BIO *bio, size_t count) {
+  AsyncBio *a = GetData(bio);
   if (a == NULL) {
     return;
   }
diff --git a/src/ssl/test/async_bio.h b/src/ssl/test/async_bio.h
index 2904036..1ccdf9b 100644
--- a/src/ssl/test/async_bio.h
+++ b/src/ssl/test/async_bio.h
@@ -17,24 +17,26 @@
 
 #include <openssl/bio.h>
 
+#include "../../crypto/test/scoped_types.h"
 
-// async_bio_create creates a filter BIO for testing asynchronous state
+
+// AsyncBioCreate creates a filter BIO for testing asynchronous state
 // machines which consume a stream socket. Reads and writes will fail
 // and return EAGAIN unless explicitly allowed. Each async BIO has a
 // read quota and a write quota. Initially both are zero. As each is
 // incremented, bytes are allowed to flow through the BIO.
-BIO *async_bio_create();
+ScopedBIO AsyncBioCreate();
 
-// async_bio_create_datagram creates a filter BIO for testing for
+// AsyncBioCreateDatagram creates a filter BIO for testing for
 // asynchronous state machines which consume datagram sockets. The read
 // and write quota count in packets rather than bytes.
-BIO *async_bio_create_datagram();
+ScopedBIO AsyncBioCreateDatagram();
 
-// async_bio_allow_read increments |bio|'s read quota by |count|.
-void async_bio_allow_read(BIO *bio, size_t count);
+// AsyncBioAllowRead increments |bio|'s read quota by |count|.
+void AsyncBioAllowRead(BIO *bio, size_t count);
 
-// async_bio_allow_write increments |bio|'s write quota by |count|.
-void async_bio_allow_write(BIO *bio, size_t count);
+// AsyncBioAllowWrite increments |bio|'s write quota by |count|.
+void AsyncBioAllowWrite(BIO *bio, size_t count);
 
 
 #endif  // HEADER_ASYNC_BIO
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 37891b9..1cf96f2 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -17,9 +17,19 @@
 #if !defined(OPENSSL_WINDOWS)
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <signal.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
+#else
+#include <io.h>
+#pragma warning(push, 3)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma warning(pop)
+
+#pragma comment(lib, "Ws2_32.lib")
 #endif
 
 #include <string.h>
@@ -28,126 +38,198 @@
 #include <openssl/bio.h>
 #include <openssl/buf.h>
 #include <openssl/bytestring.h>
+#include <openssl/err.h>
 #include <openssl/ssl.h>
 
+#include <memory>
+#include <vector>
+
+#include "../../crypto/test/scoped_types.h"
 #include "async_bio.h"
 #include "packeted_bio.h"
+#include "scoped_types.h"
 #include "test_config.h"
 
-static int usage(const char *program) {
-  fprintf(stderr, "Usage: %s [flags...]\n",
-          program);
+
+#if !defined(OPENSSL_WINDOWS)
+static int closesocket(int sock) {
+  return close(sock);
+}
+
+static void PrintSocketError(const char *func) {
+  perror(func);
+}
+#else
+static void PrintSocketError(const char *func) {
+  fprintf(stderr, "%s: %d\n", func, WSAGetLastError());
+}
+#endif
+
+static int Usage(const char *program) {
+  fprintf(stderr, "Usage: %s [flags...]\n", program);
   return 1;
 }
 
-static int g_ex_data_index = 0;
+struct TestState {
+  TestState() {
+    // MSVC cannot initialize these inline.
+    memset(&clock, 0, sizeof(clock));
+    memset(&clock_delta, 0, sizeof(clock_delta));
+  }
+
+  // async_bio is async BIO which pauses reads and writes.
+  BIO *async_bio = nullptr;
+  // clock is the current time for the SSL connection.
+  timeval clock;
+  // clock_delta is how far the clock advanced in the most recent failed
+  // |BIO_read|.
+  timeval clock_delta;
+  ScopedEVP_PKEY channel_id;
+  bool cert_ready = false;
+  ScopedSSL_SESSION session;
+  ScopedSSL_SESSION pending_session;
+  bool early_callback_called = false;
+  bool handshake_done = false;
+};
+
+static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+			    int index, long argl, void *argp) {
+  delete ((TestState *)ptr);
+}
+
+static int g_config_index = 0;
+static int g_state_index = 0;
 
 static bool SetConfigPtr(SSL *ssl, const TestConfig *config) {
-  return SSL_set_ex_data(ssl, g_ex_data_index, (void *)config) == 1;
+  return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
 }
 
-static const TestConfig *GetConfigPtr(SSL *ssl) {
-  return (const TestConfig *)SSL_get_ex_data(ssl, g_ex_data_index);
+static const TestConfig *GetConfigPtr(const SSL *ssl) {
+  return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
 }
 
-static EVP_PKEY *LoadPrivateKey(const std::string &file) {
-  BIO *bio = BIO_new(BIO_s_file());
-  if (bio == NULL) {
-    return NULL;
+static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> async) {
+  if (SSL_set_ex_data(ssl, g_state_index, (void *)async.get()) == 1) {
+    async.release();
+    return true;
   }
-  if (!BIO_read_filename(bio, file.c_str())) {
-    BIO_free(bio);
-    return NULL;
+  return false;
+}
+
+static TestState *GetTestState(const SSL *ssl) {
+  return (TestState *)SSL_get_ex_data(ssl, g_state_index);
+}
+
+static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) {
+  ScopedBIO bio(BIO_new(BIO_s_file()));
+  if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
+    return nullptr;
   }
-  EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-  BIO_free(bio);
+  ScopedEVP_PKEY pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
   return pkey;
 }
 
-static int early_callback_called = 0;
-
-static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) {
-  early_callback_called = 1;
-
-  const TestConfig *config = GetConfigPtr(ctx->ssl);
-
-  if (config->expected_server_name.empty()) {
-    return 1;
-  }
-
-  const uint8_t *extension_data;
-  size_t extension_len;
-  CBS extension, server_name_list, host_name;
-  uint8_t name_type;
-
-  if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
-                                            &extension_data,
-                                            &extension_len)) {
-    fprintf(stderr, "Could not find server_name extension.\n");
-    return -1;
-  }
-
-  CBS_init(&extension, extension_data, extension_len);
-  if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
-      CBS_len(&extension) != 0 ||
-      !CBS_get_u8(&server_name_list, &name_type) ||
-      name_type != TLSEXT_NAMETYPE_host_name ||
-      !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
-      CBS_len(&server_name_list) != 0) {
-    fprintf(stderr, "Could not decode server_name extension.\n");
-    return -1;
-  }
-
-  if (!CBS_mem_equal(&host_name,
-                     (const uint8_t*)config->expected_server_name.data(),
-                     config->expected_server_name.size())) {
-    fprintf(stderr, "Server name mismatch.\n");
-  }
-
-  return 1;
-}
-
-static int skip_verify(int preverify_ok, X509_STORE_CTX *store_ctx) {
-  return 1;
-}
-
-static int next_protos_advertised_callback(SSL *ssl,
-                                           const uint8_t **out,
-                                           unsigned int *out_len,
-                                           void *arg) {
+static bool InstallCertificate(SSL *ssl) {
   const TestConfig *config = GetConfigPtr(ssl);
-  if (config->advertise_npn.empty())
+  if (!config->key_file.empty() &&
+      !SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
+                               SSL_FILETYPE_PEM)) {
+    return false;
+  }
+  if (!config->cert_file.empty() &&
+      !SSL_use_certificate_file(ssl, config->cert_file.c_str(),
+                                SSL_FILETYPE_PEM)) {
+    return false;
+  }
+  return true;
+}
+
+static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
+  const TestConfig *config = GetConfigPtr(ctx->ssl);
+  GetTestState(ctx->ssl)->early_callback_called = true;
+
+  if (!config->expected_server_name.empty()) {
+    const uint8_t *extension_data;
+    size_t extension_len;
+    CBS extension, server_name_list, host_name;
+    uint8_t name_type;
+
+    if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
+                                              &extension_data,
+                                              &extension_len)) {
+      fprintf(stderr, "Could not find server_name extension.\n");
+      return -1;
+    }
+
+    CBS_init(&extension, extension_data, extension_len);
+    if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
+        CBS_len(&extension) != 0 ||
+        !CBS_get_u8(&server_name_list, &name_type) ||
+        name_type != TLSEXT_NAMETYPE_host_name ||
+        !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
+        CBS_len(&server_name_list) != 0) {
+      fprintf(stderr, "Could not decode server_name extension.\n");
+      return -1;
+    }
+
+    if (!CBS_mem_equal(&host_name,
+                       (const uint8_t*)config->expected_server_name.data(),
+                       config->expected_server_name.size())) {
+      fprintf(stderr, "Server name mismatch.\n");
+    }
+  }
+
+  if (config->fail_early_callback) {
+    return -1;
+  }
+
+  // Install the certificate in the early callback.
+  if (config->use_early_callback) {
+    if (config->async) {
+      // Install the certificate asynchronously.
+      return 0;
+    }
+    if (!InstallCertificate(ctx->ssl)) {
+      return -1;
+    }
+  }
+  return 1;
+}
+
+static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) {
+  return 1;
+}
+
+static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
+                                        unsigned int *out_len, void *arg) {
+  const TestConfig *config = GetConfigPtr(ssl);
+  if (config->advertise_npn.empty()) {
     return SSL_TLSEXT_ERR_NOACK;
+  }
 
   *out = (const uint8_t*)config->advertise_npn.data();
   *out_len = config->advertise_npn.size();
   return SSL_TLSEXT_ERR_OK;
 }
 
-static int next_proto_select_callback(SSL* ssl,
-                                      uint8_t** out,
-                                      uint8_t* outlen,
-                                      const uint8_t* in,
-                                      unsigned inlen,
-                                      void* arg) {
+static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
+                                   const uint8_t* in, unsigned inlen, void* arg) {
   const TestConfig *config = GetConfigPtr(ssl);
-  if (config->select_next_proto.empty())
+  if (config->select_next_proto.empty()) {
     return SSL_TLSEXT_ERR_NOACK;
+  }
 
   *out = (uint8_t*)config->select_next_proto.data();
   *outlen = config->select_next_proto.size();
   return SSL_TLSEXT_ERR_OK;
 }
 
-static int alpn_select_callback(SSL* ssl,
-                                const uint8_t** out,
-                                uint8_t* outlen,
-                                const uint8_t* in,
-                                unsigned inlen,
-                                void* arg) {
+static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,
+                              const uint8_t* in, unsigned inlen, void* arg) {
   const TestConfig *config = GetConfigPtr(ssl);
-  if (config->select_alpn.empty())
+  if (config->select_alpn.empty()) {
     return SSL_TLSEXT_ERR_NOACK;
+  }
 
   if (!config->expected_advertised_alpn.empty() &&
       (config->expected_advertised_alpn.size() != inlen ||
@@ -162,34 +244,10 @@
   return SSL_TLSEXT_ERR_OK;
 }
 
-static int cookie_generate_callback(SSL *ssl, uint8_t *cookie, size_t *cookie_len) {
-  if (*cookie_len < 32) {
-    fprintf(stderr, "Insufficient space for cookie\n");
-    return 0;
-  }
-  *cookie_len = 32;
-  memset(cookie, 42, *cookie_len);
-  return 1;
-}
-
-static int cookie_verify_callback(SSL *ssl, const uint8_t *cookie, size_t cookie_len) {
-  if (cookie_len != 32) {
-    fprintf(stderr, "Cookie length mismatch.\n");
-    return 0;
-  }
-  for (size_t i = 0; i < cookie_len; i++) {
-    if (cookie[i] != 42) {
-      fprintf(stderr, "Cookie mismatch.\n");
-      return 0;
-    }
-  }
-  return 1;
-}
-
-static unsigned psk_client_callback(SSL *ssl, const char *hint,
-                                    char *out_identity,
-                                    unsigned max_identity_len,
-                                    uint8_t *out_psk, unsigned max_psk_len) {
+static unsigned PskClientCallback(SSL *ssl, const char *hint,
+                                  char *out_identity,
+                                  unsigned max_identity_len,
+                                  uint8_t *out_psk, unsigned max_psk_len) {
   const TestConfig *config = GetConfigPtr(ssl);
 
   if (strcmp(hint ? hint : "", config->psk_identity.c_str()) != 0) {
@@ -210,8 +268,8 @@
   return config->psk.size();
 }
 
-static unsigned psk_server_callback(SSL *ssl, const char *identity,
-                                    uint8_t *out_psk, unsigned max_psk_len) {
+static unsigned PskServerCallback(SSL *ssl, const char *identity,
+                                  uint8_t *out_psk, unsigned max_psk_len) {
   const TestConfig *config = GetConfigPtr(ssl);
 
   if (strcmp(identity, config->psk_identity.c_str()) != 0) {
@@ -228,13 +286,124 @@
   return config->psk.size();
 }
 
-static SSL_CTX *setup_ctx(const TestConfig *config) {
-  SSL_CTX *ssl_ctx = NULL;
-  DH *dh = NULL;
+static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
+  *out_clock = GetTestState(ssl)->clock;
+}
 
-  ssl_ctx = SSL_CTX_new(config->is_dtls ? DTLS_method() : TLS_method());
-  if (ssl_ctx == NULL) {
-    goto err;
+static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
+  *out_pkey = GetTestState(ssl)->channel_id.release();
+}
+
+static int CertCallback(SSL *ssl, void *arg) {
+  if (!GetTestState(ssl)->cert_ready) {
+    return -1;
+  }
+  if (!InstallCertificate(ssl)) {
+    return 0;
+  }
+  return 1;
+}
+
+static SSL_SESSION *GetSessionCallback(SSL *ssl, uint8_t *data, int len,
+                                       int *copy) {
+  TestState *async_state = GetTestState(ssl);
+  if (async_state->session) {
+    *copy = 0;
+    return async_state->session.release();
+  } else if (async_state->pending_session) {
+    return SSL_magic_pending_session_ptr();
+  } else {
+    return NULL;
+  }
+}
+
+static int DDoSCallback(const struct ssl_early_callback_ctx *early_context) {
+  const TestConfig *config = GetConfigPtr(early_context->ssl);
+  static int callback_num = 0;
+
+  callback_num++;
+  if (config->fail_ddos_callback ||
+      (config->fail_second_ddos_callback && callback_num == 2)) {
+    return 0;
+  }
+  return 1;
+}
+
+static void InfoCallback(const SSL *ssl, int type, int val) {
+  if (type == SSL_CB_HANDSHAKE_DONE) {
+    if (GetConfigPtr(ssl)->handshake_never_done) {
+      fprintf(stderr, "handshake completed\n");
+      // Abort before any expected error code is printed, to ensure the overall
+      // test fails.
+      abort();
+    }
+    GetTestState(ssl)->handshake_done = true;
+  }
+}
+
+// Connect returns a new socket connected to localhost on |port| or -1 on
+// error.
+static int Connect(uint16_t port) {
+  int sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock == -1) {
+    PrintSocketError("socket");
+    return -1;
+  }
+  int nodelay = 1;
+  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+          reinterpret_cast<const char*>(&nodelay), sizeof(nodelay)) != 0) {
+    PrintSocketError("setsockopt");
+    closesocket(sock);
+    return -1;
+  }
+  sockaddr_in sin;
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(port);
+  if (!inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr)) {
+    PrintSocketError("inet_pton");
+    closesocket(sock);
+    return -1;
+  }
+  if (connect(sock, reinterpret_cast<const sockaddr*>(&sin),
+              sizeof(sin)) != 0) {
+    PrintSocketError("connect");
+    closesocket(sock);
+    return -1;
+  }
+  return sock;
+}
+
+class SocketCloser {
+ public:
+  explicit SocketCloser(int sock) : sock_(sock) {}
+  ~SocketCloser() {
+    // Half-close and drain the socket before releasing it. This seems to be
+    // necessary for graceful shutdown on Windows. It will also avoid write
+    // failures in the test runner.
+#if defined(OPENSSL_WINDOWS)
+    shutdown(sock_, SD_SEND);
+#else
+    shutdown(sock_, SHUT_WR);
+#endif
+    while (true) {
+      char buf[1024];
+      if (recv(sock_, buf, sizeof(buf), 0) <= 0) {
+        break;
+      }
+    }
+    closesocket(sock_);
+  }
+
+ private:
+  const int sock_;
+};
+
+static ScopedSSL_CTX SetupCtx(const TestConfig *config) {
+  ScopedSSL_CTX ssl_ctx(SSL_CTX_new(
+      config->is_dtls ? DTLS_method() : TLS_method()));
+  if (!ssl_ctx) {
+    return nullptr;
   }
 
   if (config->is_dtls) {
@@ -242,376 +411,473 @@
     //
     // TODO(davidben): this should not be necessary. DTLS code should only
     // expect a datagram BIO.
-    SSL_CTX_set_read_ahead(ssl_ctx, 1);
+    SSL_CTX_set_read_ahead(ssl_ctx.get(), 1);
   }
 
-  if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) {
-    goto err;
+  if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) {
+    return nullptr;
   }
 
-  if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) {
-    goto err;
+  ScopedDH dh(DH_get_2048_256(NULL));
+  if (!dh || !SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) {
+    return nullptr;
   }
 
-  dh = DH_get_2048_256(NULL);
-  if (dh == NULL ||
-      !SSL_CTX_set_tmp_dh(ssl_ctx, dh)) {
-    goto err;
+  if (config->async && config->is_server) {
+    // Disable the internal session cache. To test asynchronous session lookup,
+    // we use an external session cache.
+    SSL_CTX_set_session_cache_mode(
+        ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);
+    SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback);
+  } else {
+    SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH);
   }
 
-  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
-
-  ssl_ctx->select_certificate_cb = select_certificate_callback;
+  ssl_ctx->select_certificate_cb = SelectCertificateCallback;
 
   SSL_CTX_set_next_protos_advertised_cb(
-      ssl_ctx, next_protos_advertised_callback, NULL);
+      ssl_ctx.get(), NextProtosAdvertisedCallback, NULL);
   if (!config->select_next_proto.empty()) {
-    SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, NULL);
+    SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback,
+                                     NULL);
   }
 
   if (!config->select_alpn.empty()) {
-    SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_callback, NULL);
+    SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
   }
 
-  SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback);
-  SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback);
-
   ssl_ctx->tlsext_channel_id_enabled_new = 1;
+  SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
 
-  DH_free(dh);
+  ssl_ctx->current_time_cb = CurrentTimeCallback;
+
+  SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
+
   return ssl_ctx;
-
- err:
-  if (dh != NULL) {
-    DH_free(dh);
-  }
-  if (ssl_ctx != NULL) {
-    SSL_CTX_free(ssl_ctx);
-  }
-  return NULL;
 }
 
-static int retry_async(SSL *ssl, int ret, BIO *bio) {
+// RetryAsync is called after a failed operation on |ssl| with return code
+// |ret|. If the operation should be retried, it simulates one asynchronous
+// event and returns true. Otherwise it returns false.
+static bool RetryAsync(SSL *ssl, int ret) {
   // No error; don't retry.
   if (ret >= 0) {
-    return 0;
+    return false;
   }
+
+  TestState *test_state = GetTestState(ssl);
+  if (test_state->clock_delta.tv_usec != 0 ||
+      test_state->clock_delta.tv_sec != 0) {
+    // Process the timeout and retry.
+    test_state->clock.tv_usec += test_state->clock_delta.tv_usec;
+    test_state->clock.tv_sec += test_state->clock.tv_usec / 1000000;
+    test_state->clock.tv_usec %= 1000000;
+    test_state->clock.tv_sec += test_state->clock_delta.tv_sec;
+    memset(&test_state->clock_delta, 0, sizeof(test_state->clock_delta));
+
+    if (DTLSv1_handle_timeout(ssl) < 0) {
+      fprintf(stderr, "Error retransmitting.\n");
+      return false;
+    }
+    return true;
+  }
+
   // See if we needed to read or write more. If so, allow one byte through on
   // the appropriate end to maximally stress the state machine.
-  int err = SSL_get_error(ssl, ret);
-  if (err == SSL_ERROR_WANT_READ) {
-    async_bio_allow_read(bio, 1);
-    return 1;
-  } else if (err == SSL_ERROR_WANT_WRITE) {
-    async_bio_allow_write(bio, 1);
-    return 1;
+  switch (SSL_get_error(ssl, ret)) {
+    case SSL_ERROR_WANT_READ:
+      AsyncBioAllowRead(test_state->async_bio, 1);
+      return true;
+    case SSL_ERROR_WANT_WRITE:
+      AsyncBioAllowWrite(test_state->async_bio, 1);
+      return true;
+    case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
+      ScopedEVP_PKEY pkey = LoadPrivateKey(GetConfigPtr(ssl)->send_channel_id);
+      if (!pkey) {
+        return false;
+      }
+      test_state->channel_id = std::move(pkey);
+      return true;
+    }
+    case SSL_ERROR_WANT_X509_LOOKUP:
+      test_state->cert_ready = true;
+      return true;
+    case SSL_ERROR_PENDING_SESSION:
+      test_state->session = std::move(test_state->pending_session);
+      return true;
+    case SSL_ERROR_PENDING_CERTIFICATE:
+      // The handshake will resume without a second call to the early callback.
+      return InstallCertificate(ssl);
+    default:
+      return false;
   }
-  return 0;
 }
 
-static int do_exchange(SSL_SESSION **out_session,
-                       SSL_CTX *ssl_ctx,
-                       const TestConfig *config,
-                       bool is_resume,
-                       int fd,
+// DoRead reads from |ssl|, resolving any asynchronous operations. It returns
+// the result value of the final |SSL_read| call.
+static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
+  const TestConfig *config = GetConfigPtr(ssl);
+  int ret;
+  do {
+    ret = SSL_read(ssl, out, max_out);
+  } while (config->async && RetryAsync(ssl, ret));
+  return ret;
+}
+
+// WriteAll writes |in_len| bytes from |in| to |ssl|, resolving any asynchronous
+// operations. It returns the result of the final |SSL_write| call.
+static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) {
+  const TestConfig *config = GetConfigPtr(ssl);
+  int ret;
+  do {
+    ret = SSL_write(ssl, in, in_len);
+    if (ret > 0) {
+      in += ret;
+      in_len -= ret;
+    }
+  } while ((config->async && RetryAsync(ssl, ret)) || (ret > 0 && in_len > 0));
+  return ret;
+}
+
+// DoExchange runs a test SSL exchange against the peer. On success, it returns
+// true and sets |*out_session| to the negotiated SSL session. If the test is a
+// resumption attempt, |is_resume| is true and |session| is the session from the
+// previous exchange.
+static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
+                       const TestConfig *config, bool is_resume,
                        SSL_SESSION *session) {
-  early_callback_called = 0;
-
-  SSL *ssl = SSL_new(ssl_ctx);
-  if (ssl == NULL) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+  ScopedSSL ssl(SSL_new(ssl_ctx));
+  if (!ssl) {
+    return false;
   }
 
-  if (!SetConfigPtr(ssl, config)) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+  if (!SetConfigPtr(ssl.get(), config) ||
+      !SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) {
+    return false;
   }
 
-  if (config->fallback_scsv) {
-    if (!SSL_enable_fallback_scsv(ssl)) {
-      BIO_print_errors_fp(stdout);
-      return 1;
-    }
+  if (config->fallback_scsv &&
+      !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
+    return false;
   }
-  if (!config->key_file.empty()) {
-    if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
-                                 SSL_FILETYPE_PEM)) {
-      BIO_print_errors_fp(stdout);
-      return 1;
-    }
-  }
-  if (!config->cert_file.empty()) {
-    if (!SSL_use_certificate_file(ssl, config->cert_file.c_str(),
-                                  SSL_FILETYPE_PEM)) {
-      BIO_print_errors_fp(stdout);
-      return 1;
+  if (!config->use_early_callback) {
+    if (config->async) {
+      // TODO(davidben): Also test |s->ctx->client_cert_cb| on the client.
+      SSL_set_cert_cb(ssl.get(), CertCallback, NULL);
+    } else if (!InstallCertificate(ssl.get())) {
+      return false;
     }
   }
   if (config->require_any_client_certificate) {
-    SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
-                   skip_verify);
+    SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                   SkipVerify);
   }
   if (config->false_start) {
-    SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
+    SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
   }
   if (config->cbc_record_splitting) {
-    SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING);
+    SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING);
   }
   if (config->partial_write) {
-    SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+    SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
   }
   if (config->no_tls12) {
-    SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+    SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
   }
   if (config->no_tls11) {
-    SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+    SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1);
   }
   if (config->no_tls1) {
-    SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+    SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1);
   }
   if (config->no_ssl3) {
-    SSL_set_options(ssl, SSL_OP_NO_SSLv3);
-  }
-  if (config->cookie_exchange) {
-    SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
+    SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3);
   }
   if (config->tls_d5_bug) {
-    SSL_set_options(ssl, SSL_OP_TLS_D5_BUG);
+    SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG);
   }
   if (config->allow_unsafe_legacy_renegotiation) {
-    SSL_set_options(ssl, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+    SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
   }
   if (!config->expected_channel_id.empty()) {
-    SSL_enable_tls_channel_id(ssl);
+    SSL_enable_tls_channel_id(ssl.get());
   }
   if (!config->send_channel_id.empty()) {
-    EVP_PKEY *pkey = LoadPrivateKey(config->send_channel_id);
-    if (pkey == NULL) {
-      BIO_print_errors_fp(stdout);
-      return 1;
+    SSL_enable_tls_channel_id(ssl.get());
+    if (!config->async) {
+      // The async case will be supplied by |ChannelIdCallback|.
+      ScopedEVP_PKEY pkey = LoadPrivateKey(config->send_channel_id);
+      if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
+        return false;
+      }
     }
-    SSL_enable_tls_channel_id(ssl);
-    if (!SSL_set1_tls_channel_id(ssl, pkey)) {
-      EVP_PKEY_free(pkey);
-      BIO_print_errors_fp(stdout);
-      return 1;
-    }
-    EVP_PKEY_free(pkey);
   }
-  if (!config->host_name.empty()) {
-    SSL_set_tlsext_host_name(ssl, config->host_name.c_str());
+  if (!config->host_name.empty() &&
+      !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) {
+    return false;
   }
-  if (!config->advertise_alpn.empty()) {
-    SSL_set_alpn_protos(ssl, (const uint8_t *)config->advertise_alpn.data(),
-                        config->advertise_alpn.size());
+  if (!config->advertise_alpn.empty() &&
+      SSL_set_alpn_protos(ssl.get(),
+                          (const uint8_t *)config->advertise_alpn.data(),
+                          config->advertise_alpn.size()) != 0) {
+    return false;
   }
   if (!config->psk.empty()) {
-    SSL_set_psk_client_callback(ssl, psk_client_callback);
-    SSL_set_psk_server_callback(ssl, psk_server_callback);
+    SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
+    SSL_set_psk_server_callback(ssl.get(), PskServerCallback);
   }
   if (!config->psk_identity.empty() &&
-      !SSL_use_psk_identity_hint(ssl, config->psk_identity.c_str())) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+      !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) {
+    return false;
   }
   if (!config->srtp_profiles.empty() &&
-      !SSL_set_srtp_profiles(ssl, config->srtp_profiles.c_str())) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+      !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) {
+    return false;
   }
   if (config->enable_ocsp_stapling &&
-      !SSL_enable_ocsp_stapling(ssl)) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+      !SSL_enable_ocsp_stapling(ssl.get())) {
+    return false;
   }
   if (config->enable_signed_cert_timestamps &&
-      !SSL_enable_signed_cert_timestamps(ssl)) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+      !SSL_enable_signed_cert_timestamps(ssl.get())) {
+    return false;
   }
-  SSL_enable_fastradio_padding(ssl, config->fastradio_padding);
+  SSL_enable_fastradio_padding(ssl.get(), config->fastradio_padding);
   if (config->min_version != 0) {
-    SSL_set_min_version(ssl, (uint16_t)config->min_version);
+    SSL_set_min_version(ssl.get(), (uint16_t)config->min_version);
   }
   if (config->max_version != 0) {
-    SSL_set_max_version(ssl, (uint16_t)config->max_version);
+    SSL_set_max_version(ssl.get(), (uint16_t)config->max_version);
   }
   if (config->mtu != 0) {
-    SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU);
-    SSL_set_mtu(ssl, config->mtu);
+    SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
+    SSL_set_mtu(ssl.get(), config->mtu);
+  }
+  if (config->install_ddos_callback) {
+    SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
+  }
+  if (!config->cipher.empty() &&
+      !SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) {
+    return false;
+  }
+  if (config->reject_peer_renegotiations) {
+    SSL_set_reject_peer_renegotiations(ssl.get(), 1);
   }
 
-  BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
-  if (bio == NULL) {
-    BIO_print_errors_fp(stdout);
-    return 1;
+  int sock = Connect(config->port);
+  if (sock == -1) {
+    return false;
+  }
+  SocketCloser closer(sock);
+
+  ScopedBIO bio(BIO_new_socket(sock, BIO_NOCLOSE));
+  if (!bio) {
+    return false;
   }
   if (config->is_dtls) {
-    BIO *packeted = packeted_bio_create();
-    BIO_push(packeted, bio);
-    bio = packeted;
+    ScopedBIO packeted =
+        PacketedBioCreate(&GetTestState(ssl.get())->clock_delta);
+    BIO_push(packeted.get(), bio.release());
+    bio = std::move(packeted);
   }
   if (config->async) {
-    BIO *async =
-        config->is_dtls ? async_bio_create_datagram() : async_bio_create();
-    BIO_push(async, bio);
-    bio = async;
+    ScopedBIO async_scoped =
+        config->is_dtls ? AsyncBioCreateDatagram() : AsyncBioCreate();
+    BIO_push(async_scoped.get(), bio.release());
+    GetTestState(ssl.get())->async_bio = async_scoped.get();
+    bio = std::move(async_scoped);
   }
-  SSL_set_bio(ssl, bio, bio);
+  SSL_set_bio(ssl.get(), bio.get(), bio.get());
+  bio.release();  // SSL_set_bio takes ownership.
 
   if (session != NULL) {
-    if (SSL_set_session(ssl, session) != 1) {
-      fprintf(stderr, "failed to set session\n");
-      return 2;
+    if (!config->is_server) {
+      if (SSL_set_session(ssl.get(), session) != 1) {
+        return false;
+      }
+    } else if (config->async) {
+      // The internal session cache is disabled, so install the session
+      // manually.
+      GetTestState(ssl.get())->pending_session.reset(
+          SSL_SESSION_up_ref(session));
     }
   }
 
   int ret;
-  do {
+  if (config->implicit_handshake) {
     if (config->is_server) {
-      ret = SSL_accept(ssl);
+      SSL_set_accept_state(ssl.get());
     } else {
-      ret = SSL_connect(ssl);
+      SSL_set_connect_state(ssl.get());
     }
-  } while (config->async && retry_async(ssl, ret, bio));
-  if (ret != 1) {
-    SSL_free(ssl);
-    BIO_print_errors_fp(stdout);
-    return 2;
-  }
-
-  if (is_resume && (!!SSL_session_reused(ssl) == config->expect_session_miss)) {
-    fprintf(stderr, "session was%s reused\n",
-            SSL_session_reused(ssl) ? "" : " not");
-    return 2;
-  }
-
-  if (!config->expected_server_name.empty()) {
-    const char *server_name =
-        SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-    if (server_name != config->expected_server_name) {
-      fprintf(stderr, "servername mismatch (got %s; want %s)\n",
-              server_name, config->expected_server_name.c_str());
-      return 2;
+  } else {
+    do {
+      if (config->is_server) {
+        ret = SSL_accept(ssl.get());
+      } else {
+        ret = SSL_connect(ssl.get());
+      }
+    } while (config->async && RetryAsync(ssl.get(), ret));
+    if (ret != 1) {
+      return false;
     }
 
-    if (!early_callback_called) {
+    if (is_resume &&
+        (!!SSL_session_reused(ssl.get()) == config->expect_session_miss)) {
+      fprintf(stderr, "session was%s reused\n",
+              SSL_session_reused(ssl.get()) ? "" : " not");
+      return false;
+    }
+
+    bool expect_handshake_done = is_resume || !config->false_start;
+    if (expect_handshake_done != GetTestState(ssl.get())->handshake_done) {
+      fprintf(stderr, "handshake was%s completed\n",
+              GetTestState(ssl.get())->handshake_done ? "" : " not");
+      return false;
+    }
+
+    if (config->is_server && !GetTestState(ssl.get())->early_callback_called) {
       fprintf(stderr, "early callback not called\n");
-      return 2;
+      return false;
     }
-  }
 
-  if (!config->expected_certificate_types.empty()) {
-    uint8_t *certificate_types;
-    int num_certificate_types =
-        SSL_get0_certificate_types(ssl, &certificate_types);
-    if (num_certificate_types !=
-        (int)config->expected_certificate_types.size() ||
-        memcmp(certificate_types,
-               config->expected_certificate_types.data(),
-               num_certificate_types) != 0) {
-      fprintf(stderr, "certificate types mismatch\n");
-      return 2;
+    if (!config->expected_server_name.empty()) {
+      const char *server_name =
+        SSL_get_servername(ssl.get(), TLSEXT_NAMETYPE_host_name);
+      if (server_name != config->expected_server_name) {
+        fprintf(stderr, "servername mismatch (got %s; want %s)\n",
+                server_name, config->expected_server_name.c_str());
+        return false;
+      }
     }
-  }
 
-  if (!config->expected_next_proto.empty()) {
-    const uint8_t *next_proto;
-    unsigned next_proto_len;
-    SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
-    if (next_proto_len != config->expected_next_proto.size() ||
-        memcmp(next_proto, config->expected_next_proto.data(),
-               next_proto_len) != 0) {
-      fprintf(stderr, "negotiated next proto mismatch\n");
-      return 2;
+    if (!config->expected_certificate_types.empty()) {
+      uint8_t *certificate_types;
+      int num_certificate_types =
+        SSL_get0_certificate_types(ssl.get(), &certificate_types);
+      if (num_certificate_types !=
+          (int)config->expected_certificate_types.size() ||
+          memcmp(certificate_types,
+                 config->expected_certificate_types.data(),
+                 num_certificate_types) != 0) {
+        fprintf(stderr, "certificate types mismatch\n");
+        return false;
+      }
     }
-  }
 
-  if (!config->expected_alpn.empty()) {
-    const uint8_t *alpn_proto;
-    unsigned alpn_proto_len;
-    SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len);
-    if (alpn_proto_len != config->expected_alpn.size() ||
-        memcmp(alpn_proto, config->expected_alpn.data(),
-               alpn_proto_len) != 0) {
-      fprintf(stderr, "negotiated alpn proto mismatch\n");
-      return 2;
+    if (!config->expected_next_proto.empty()) {
+      const uint8_t *next_proto;
+      unsigned next_proto_len;
+      SSL_get0_next_proto_negotiated(ssl.get(), &next_proto, &next_proto_len);
+      if (next_proto_len != config->expected_next_proto.size() ||
+          memcmp(next_proto, config->expected_next_proto.data(),
+                 next_proto_len) != 0) {
+        fprintf(stderr, "negotiated next proto mismatch\n");
+        return false;
+      }
     }
-  }
 
-  if (!config->expected_channel_id.empty()) {
-    uint8_t channel_id[64];
-    if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
-      fprintf(stderr, "no channel id negotiated\n");
-      return 2;
+    if (!config->expected_alpn.empty()) {
+      const uint8_t *alpn_proto;
+      unsigned alpn_proto_len;
+      SSL_get0_alpn_selected(ssl.get(), &alpn_proto, &alpn_proto_len);
+      if (alpn_proto_len != config->expected_alpn.size() ||
+          memcmp(alpn_proto, config->expected_alpn.data(),
+                 alpn_proto_len) != 0) {
+        fprintf(stderr, "negotiated alpn proto mismatch\n");
+        return false;
+      }
     }
-    if (config->expected_channel_id.size() != 64 ||
-        memcmp(config->expected_channel_id.data(),
-               channel_id, 64) != 0) {
-      fprintf(stderr, "channel id mismatch\n");
-      return 2;
-    }
-  }
 
-  if (config->expect_extended_master_secret) {
-    if (!ssl->session->extended_master_secret) {
-      fprintf(stderr, "No EMS for session when expected");
-      return 2;
+    if (!config->expected_channel_id.empty()) {
+      uint8_t channel_id[64];
+      if (!SSL_get_tls_channel_id(ssl.get(), channel_id, sizeof(channel_id))) {
+        fprintf(stderr, "no channel id negotiated\n");
+        return false;
+      }
+      if (config->expected_channel_id.size() != 64 ||
+          memcmp(config->expected_channel_id.data(),
+                 channel_id, 64) != 0) {
+        fprintf(stderr, "channel id mismatch\n");
+        return false;
+      }
     }
-  }
 
-  if (!config->expected_ocsp_response.empty()) {
-    const uint8_t *data;
-    size_t len;
-    SSL_get0_ocsp_response(ssl, &data, &len);
-    if (config->expected_ocsp_response.size() != len ||
-        memcmp(config->expected_ocsp_response.data(), data, len) != 0) {
-      fprintf(stderr, "OCSP response mismatch\n");
-      return 2;
+    if (config->expect_extended_master_secret) {
+      if (!ssl->session->extended_master_secret) {
+        fprintf(stderr, "No EMS for session when expected");
+        return false;
+      }
     }
-  }
 
-  if (!config->expected_signed_cert_timestamps.empty()) {
-    const uint8_t *data;
-    size_t len;
-    SSL_get0_signed_cert_timestamp_list(ssl, &data, &len);
-    if (config->expected_signed_cert_timestamps.size() != len ||
-        memcmp(config->expected_signed_cert_timestamps.data(),
-               data, len) != 0) {
-      fprintf(stderr, "SCT list mismatch\n");
-      return 2;
+    if (!config->expected_ocsp_response.empty()) {
+      const uint8_t *data;
+      size_t len;
+      SSL_get0_ocsp_response(ssl.get(), &data, &len);
+      if (config->expected_ocsp_response.size() != len ||
+          memcmp(config->expected_ocsp_response.data(), data, len) != 0) {
+        fprintf(stderr, "OCSP response mismatch\n");
+        return false;
+      }
+    }
+
+    if (!config->expected_signed_cert_timestamps.empty()) {
+      const uint8_t *data;
+      size_t len;
+      SSL_get0_signed_cert_timestamp_list(ssl.get(), &data, &len);
+      if (config->expected_signed_cert_timestamps.size() != len ||
+          memcmp(config->expected_signed_cert_timestamps.data(),
+                 data, len) != 0) {
+        fprintf(stderr, "SCT list mismatch\n");
+        return false;
+      }
     }
   }
 
   if (config->renegotiate) {
     if (config->async) {
-      fprintf(stderr, "--renegotiate is not supported with --async.\n");
-      return 2;
+      fprintf(stderr, "-renegotiate is not supported with -async.\n");
+      return false;
+    }
+    if (config->implicit_handshake) {
+      fprintf(stderr, "-renegotiate is not supported with -implicit-handshake.\n");
+      return false;
     }
 
-    SSL_renegotiate(ssl);
+    SSL_renegotiate(ssl.get());
 
-    ret = SSL_do_handshake(ssl);
+    ret = SSL_do_handshake(ssl.get());
     if (ret != 1) {
-      SSL_free(ssl);
-      BIO_print_errors_fp(stdout);
-      return 2;
+      return false;
     }
 
-    SSL_set_state(ssl, SSL_ST_ACCEPT);
-    ret = SSL_do_handshake(ssl);
+    SSL_set_state(ssl.get(), SSL_ST_ACCEPT);
+    ret = SSL_do_handshake(ssl.get());
     if (ret != 1) {
-      SSL_free(ssl);
-      BIO_print_errors_fp(stdout);
-      return 2;
+      return false;
+    }
+  }
+
+  if (config->export_keying_material > 0) {
+    std::vector<uint8_t> result(
+        static_cast<size_t>(config->export_keying_material));
+    if (!SSL_export_keying_material(
+            ssl.get(), result.data(), result.size(),
+            config->export_label.data(), config->export_label.size(),
+            reinterpret_cast<const uint8_t*>(config->export_context.data()),
+            config->export_context.size(), config->use_export_context)) {
+      fprintf(stderr, "failed to export keying material\n");
+      return false;
+    }
+    if (WriteAll(ssl.get(), result.data(), result.size()) < 0) {
+      return false;
     }
   }
 
   if (config->write_different_record_sizes) {
     if (config->is_dtls) {
       fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
-      return 6;
+      return false;
     }
     // This mode writes a number of different record sizes in an attempt to
     // trip up the CBC record splitting code.
@@ -621,138 +887,123 @@
         0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
     for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]);
          i++) {
-      int w;
       const size_t len = kRecordSizes[i];
-      size_t off = 0;
-
       if (len > sizeof(buf)) {
         fprintf(stderr, "Bad kRecordSizes value.\n");
-        return 5;
+        return false;
       }
-
-      do {
-        w = SSL_write(ssl, buf + off, len - off);
-        if (w > 0) {
-          off += (size_t) w;
-        }
-      } while ((config->async && retry_async(ssl, w, bio)) ||
-               (w > 0 && off < len));
-
-      if (w < 0 || off != len) {
-        SSL_free(ssl);
-        BIO_print_errors_fp(stdout);
-        return 4;
+      if (WriteAll(ssl.get(), buf, len) < 0) {
+        return false;
       }
     }
   } else {
     if (config->shim_writes_first) {
-      int w;
-      do {
-        w = SSL_write(ssl, "hello", 5);
-      } while (config->async && retry_async(ssl, w, bio));
+      if (WriteAll(ssl.get(), reinterpret_cast<const uint8_t *>("hello"),
+                   5) < 0) {
+        return false;
+      }
     }
     for (;;) {
       uint8_t buf[512];
-      int n;
-      do {
-        n = SSL_read(ssl, buf, sizeof(buf));
-      } while (config->async && retry_async(ssl, n, bio));
-      int err = SSL_get_error(ssl, n);
+      int n = DoRead(ssl.get(), buf, sizeof(buf));
+      int err = SSL_get_error(ssl.get(), n);
       if (err == SSL_ERROR_ZERO_RETURN ||
           (n == 0 && err == SSL_ERROR_SYSCALL)) {
         if (n != 0) {
           fprintf(stderr, "Invalid SSL_get_error output\n");
-          return 3;
+          return false;
         }
-        /* Accept shutdowns with or without close_notify.
-         * TODO(davidben): Write tests which distinguish these two cases. */
+        // Accept shutdowns with or without close_notify.
+        // TODO(davidben): Write tests which distinguish these two cases.
         break;
       } else if (err != SSL_ERROR_NONE) {
         if (n > 0) {
           fprintf(stderr, "Invalid SSL_get_error output\n");
-          return 3;
+          return false;
         }
-        SSL_free(ssl);
-        BIO_print_errors_fp(stdout);
-        return 3;
+        return false;
       }
-      /* Successfully read data. */
+      // Successfully read data.
       if (n <= 0) {
         fprintf(stderr, "Invalid SSL_get_error output\n");
-        return 3;
+        return false;
       }
+
+      // After a successful read, with or without False Start, the handshake
+      // must be complete.
+      if (!GetTestState(ssl.get())->handshake_done) {
+        fprintf(stderr, "handshake was not completed after SSL_read\n");
+        return false;
+      }
+
       for (int i = 0; i < n; i++) {
         buf[i] ^= 0xff;
       }
-      int w;
-      do {
-        w = SSL_write(ssl, buf, n);
-      } while (config->async && retry_async(ssl, w, bio));
-      if (w != n) {
-        SSL_free(ssl);
-        BIO_print_errors_fp(stdout);
-        return 4;
+      if (WriteAll(ssl.get(), buf, n) < 0) {
+        return false;
       }
     }
   }
 
   if (out_session) {
-    *out_session = SSL_get1_session(ssl);
+    out_session->reset(SSL_get1_session(ssl.get()));
   }
 
-  SSL_shutdown(ssl);
-  SSL_free(ssl);
-  return 0;
+  SSL_shutdown(ssl.get());
+  return true;
 }
 
 int main(int argc, char **argv) {
-#if !defined(OPENSSL_WINDOWS)
+#if defined(OPENSSL_WINDOWS)
+  /* Initialize Winsock. */
+  WORD wsa_version = MAKEWORD(2, 2);
+  WSADATA wsa_data;
+  int wsa_err = WSAStartup(wsa_version, &wsa_data);
+  if (wsa_err != 0) {
+    fprintf(stderr, "WSAStartup failed: %d\n", wsa_err);
+    return 1;
+  }
+  if (wsa_data.wVersion != wsa_version) {
+    fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion);
+    return 1;
+  }
+#else
   signal(SIGPIPE, SIG_IGN);
 #endif
 
   if (!SSL_library_init()) {
     return 1;
   }
-  g_ex_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
-  if (g_ex_data_index < 0) {
+  g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+  g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree);
+  if (g_config_index < 0 || g_state_index < 0) {
     return 1;
   }
 
   TestConfig config;
   if (!ParseConfig(argc - 1, argv + 1, &config)) {
-    return usage(argv[0]);
+    return Usage(argv[0]);
   }
 
-  SSL_CTX *ssl_ctx = setup_ctx(&config);
-  if (ssl_ctx == NULL) {
-    BIO_print_errors_fp(stdout);
+  ScopedSSL_CTX ssl_ctx = SetupCtx(&config);
+  if (!ssl_ctx) {
+    ERR_print_errors_fp(stderr);
     return 1;
   }
 
-  SSL_SESSION *session = NULL;
-  int ret = do_exchange(&session,
-                        ssl_ctx, &config,
-                        false /* is_resume */,
-                        3 /* fd */, NULL /* session */);
-  if (ret != 0) {
-    goto out;
+  ScopedSSL_SESSION session;
+  if (!DoExchange(&session, ssl_ctx.get(), &config, false /* is_resume */,
+                  NULL /* session */)) {
+    ERR_print_errors_fp(stderr);
+    return 1;
   }
 
-  if (config.resume) {
-    ret = do_exchange(NULL,
-                      ssl_ctx, &config,
-                      true /* is_resume */,
-                      4 /* fd */,
-                      config.is_server ? NULL : session);
-    if (ret != 0) {
-      goto out;
-    }
+  if (config.resume &&
+      !DoExchange(NULL, ssl_ctx.get(), &config, true /* is_resume */,
+                  session.get())) {
+    ERR_print_errors_fp(stderr);
+    return 1;
   }
 
-  ret = 0;
-
-out:
-  SSL_SESSION_free(session);
-  SSL_CTX_free(ssl_ctx);
-  return ret;
+  return 0;
 }
diff --git a/src/ssl/test/malloc.cc b/src/ssl/test/malloc.cc
index 6cc0b33..2ec5582 100644
--- a/src/ssl/test/malloc.cc
+++ b/src/ssl/test/malloc.cc
@@ -14,15 +14,24 @@
 
 #include <openssl/base.h>
 
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define OPENSSL_ASAN
+#endif
+#endif
+
 // This file isn't built on ARM or Aarch64 because we link statically in those
-// builds and trying to override malloc in a static link doesn't work.
-#if defined(__linux__) && !defined(OPENSSL_ARM) && !defined(OPENSSL_AARCH64)
+// builds and trying to override malloc in a static link doesn't work. It's also
+// disabled on ASan builds as this interferes with ASan's malloc interceptor.
+//
+// TODO(davidben): See if this and ASan's interceptors can be made to coexist.
+#if defined(__linux__) && !defined(OPENSSL_ARM) && \
+    !defined(OPENSSL_AARCH64) && !defined(OPENSSL_ASAN)
 
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <unistd.h>
-#include <stdio.h>
 
 #include <new>
 
@@ -127,4 +136,4 @@
 
 }  // extern "C"
 
-#endif  /* defined(linux) && !ARM && !AARCH64 */
+#endif  /* defined(linux) && !ARM && !AARCH64 && !ASAN */
diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc
index 93b2164..e831082 100644
--- a/src/ssl/test/packeted_bio.cc
+++ b/src/ssl/test/packeted_bio.cc
@@ -15,7 +15,8 @@
 #include "packeted_bio.h"
 
 #include <assert.h>
-#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
 #include <string.h>
 
 #include <openssl/mem.h>
@@ -23,58 +24,135 @@
 
 namespace {
 
-extern const BIO_METHOD packeted_bio_method;
+extern const BIO_METHOD g_packeted_bio_method;
 
-static int packeted_write(BIO *bio, const char *in, int inl) {
+const uint8_t kOpcodePacket = 'P';
+const uint8_t kOpcodeTimeout = 'T';
+const uint8_t kOpcodeTimeoutAck = 't';
+
+// ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
+// 0 or -1 on error.
+static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
+  while (len > 0) {
+    int chunk_len = INT_MAX;
+    if (len <= INT_MAX) {
+      chunk_len = (int)len;
+    }
+    int ret = BIO_read(bio, out, chunk_len);
+    if (ret <= 0) {
+      return ret;
+    }
+    out += ret;
+    len -= ret;
+  }
+  return 1;
+}
+
+static int PacketedWrite(BIO *bio, const char *in, int inl) {
   if (bio->next_bio == NULL) {
     return 0;
   }
 
   BIO_clear_retry_flags(bio);
 
-  // Write the length prefix.
-  uint8_t len_bytes[4];
-  len_bytes[0] = (inl >> 24) & 0xff;
-  len_bytes[1] = (inl >> 16) & 0xff;
-  len_bytes[2] = (inl >> 8) & 0xff;
-  len_bytes[3] = inl & 0xff;
-  int ret = BIO_write(bio->next_bio, len_bytes, sizeof(len_bytes));
+  // Write the header.
+  uint8_t header[5];
+  header[0] = kOpcodePacket;
+  header[1] = (inl >> 24) & 0xff;
+  header[2] = (inl >> 16) & 0xff;
+  header[3] = (inl >> 8) & 0xff;
+  header[4] = inl & 0xff;
+  int ret = BIO_write(bio->next_bio, header, sizeof(header));
   if (ret <= 0) {
     BIO_copy_next_retry(bio);
     return ret;
   }
 
-  // Write the buffer. BIOs for which this operation fails are not supported.
+  // Write the buffer.
   ret = BIO_write(bio->next_bio, in, inl);
+  if (ret < 0 || (inl > 0 && ret == 0)) {
+    BIO_copy_next_retry(bio);
+    return ret;
+  }
   assert(ret == inl);
   return ret;
 }
 
-static int packeted_read(BIO *bio, char *out, int outl) {
+static int PacketedRead(BIO *bio, char *out, int outl) {
   if (bio->next_bio == NULL) {
     return 0;
   }
 
   BIO_clear_retry_flags(bio);
 
-  // Read the length prefix.
-  uint8_t len_bytes[4];
-  int ret = BIO_read(bio->next_bio, &len_bytes, sizeof(len_bytes));
+  // Read the opcode.
+  uint8_t opcode;
+  int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
   if (ret <= 0) {
     BIO_copy_next_retry(bio);
     return ret;
   }
-  // BIOs for which a partial length comes back are not supported.
-  assert(ret == 4);
+
+  if (opcode == kOpcodeTimeout) {
+    // Process the timeout.
+    uint8_t buf[8];
+    ret = ReadAll(bio->next_bio, buf, sizeof(buf));
+    if (ret <= 0) {
+      BIO_copy_next_retry(bio);
+      return ret;
+    }
+    uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
+        (static_cast<uint64_t>(buf[1]) << 48) |
+        (static_cast<uint64_t>(buf[2]) << 40) |
+        (static_cast<uint64_t>(buf[3]) << 32) |
+        (static_cast<uint64_t>(buf[4]) << 24) |
+        (static_cast<uint64_t>(buf[5]) << 16) |
+        (static_cast<uint64_t>(buf[6]) << 8) |
+        static_cast<uint64_t>(buf[7]);
+    timeout /= 1000;  // Convert nanoseconds to microseconds.
+    timeval *out_timeout = reinterpret_cast<timeval *>(bio->ptr);
+    assert(out_timeout->tv_usec == 0);
+    assert(out_timeout->tv_sec == 0);
+    out_timeout->tv_usec = timeout % 1000000;
+    out_timeout->tv_sec = timeout / 1000000;
+
+    // Send an ACK to the peer.
+    ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
+    if (ret <= 0) {
+      return ret;
+    }
+    assert(ret == 1);
+
+    // Signal to the caller to retry the read, after processing the
+    // new clock.
+    BIO_set_retry_read(bio);
+    return -1;
+  }
+
+  if (opcode != kOpcodePacket) {
+    fprintf(stderr, "Unknown opcode, %u\n", opcode);
+    return -1;
+  }
+
+  // Read the length prefix.
+  uint8_t len_bytes[4];
+  ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
+  if (ret <= 0) {
+    BIO_copy_next_retry(bio);
+    return ret;
+  }
 
   uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
       (len_bytes[2] << 8) | len_bytes[3];
-  char *buf = (char *)OPENSSL_malloc(len);
+  uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
   if (buf == NULL) {
     return -1;
   }
-  ret = BIO_read(bio->next_bio, buf, len);
-  assert(ret == (int)len);
+  ret = ReadAll(bio->next_bio, buf, len);
+  if (ret <= 0) {
+    fprintf(stderr, "Packeted BIO was truncated\n");
+    return -1;
+  }
 
   if (outl > (int)len) {
     outl = len;
@@ -84,7 +162,7 @@
   return outl;
 }
 
-static long packeted_ctrl(BIO *bio, int cmd, long num, void *ptr) {
+static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
   if (bio->next_bio == NULL) {
     return 0;
   }
@@ -94,12 +172,12 @@
   return ret;
 }
 
-static int packeted_new(BIO *bio) {
+static int PacketedNew(BIO *bio) {
   bio->init = 1;
   return 1;
 }
 
-static int packeted_free(BIO *bio) {
+static int PacketedFree(BIO *bio) {
   if (bio == NULL) {
     return 0;
   }
@@ -108,28 +186,33 @@
   return 1;
 }
 
-static long packeted_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
+static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
   if (bio->next_bio == NULL) {
     return 0;
   }
   return BIO_callback_ctrl(bio->next_bio, cmd, fp);
 }
 
-const BIO_METHOD packeted_bio_method = {
+const BIO_METHOD g_packeted_bio_method = {
   BIO_TYPE_FILTER,
   "packeted bio",
-  packeted_write,
-  packeted_read,
+  PacketedWrite,
+  PacketedRead,
   NULL /* puts */,
   NULL /* gets */,
-  packeted_ctrl,
-  packeted_new,
-  packeted_free,
-  packeted_callback_ctrl,
+  PacketedCtrl,
+  PacketedNew,
+  PacketedFree,
+  PacketedCallbackCtrl,
 };
 
 }  // namespace
 
-BIO *packeted_bio_create() {
-  return BIO_new(&packeted_bio_method);
+ScopedBIO PacketedBioCreate(timeval *out_timeout) {
+  ScopedBIO bio(BIO_new(&g_packeted_bio_method));
+  if (!bio) {
+    return nullptr;
+  }
+  bio->ptr = out_timeout;
+  return bio;
 }
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 384bd64..30697a5 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -15,18 +15,30 @@
 #ifndef HEADER_PACKETED_BIO
 #define HEADER_PACKETED_BIO
 
+#include <openssl/base.h>
 #include <openssl/bio.h>
 
+#include "../../crypto/test/scoped_types.h"
 
-// packeted_bio_create creates a filter BIO for testing protocols which expect
-// datagram BIOs. It implements a reliable datagram socket and reads and writes
-// packets by prefixing each packet with a big-endian 32-bit length. It must be
-// layered over a reliable blocking stream BIO.
+#if defined(OPENSSL_WINDOWS)
+#pragma warning(push, 3)
+#include <winsock2.h>
+#pragma warning(pop)
+#else
+#include <sys/types.h>
+#endif
+
+
+// PacketedBioCreate creates a filter BIO which implements a reliable in-order
+// blocking datagram socket. The resulting BIO, on |BIO_read|, may simulate a
+// timeout which sets |*out_timeout| to the timeout and fails the read.
+// |*out_timeout| must be zero on entry to |BIO_read|; it is an error to not
+// apply the timeout before the next |BIO_read|.
 //
-// Note: packeted_bio_create exists because a SOCK_DGRAM socketpair on OS X is
-// does not block the caller, unlike on Linux. Writes simply fail with
-// ENOBUFS. POSIX also does not guarantee that such sockets are reliable.
-BIO *packeted_bio_create();
+// Note: The read timeout simulation is intended to be used with the async BIO
+// wrapper. It doesn't simulate BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, used in DTLS's
+// blocking mode.
+ScopedBIO PacketedBioCreate(timeval *out_timeout);
 
 
 #endif  // HEADER_PACKETED_BIO
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
new file mode 100644
index 0000000..42911d4
--- /dev/null
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -0,0 +1,159 @@
+package main
+
+import (
+	"crypto/cipher"
+	"crypto/subtle"
+	"encoding/binary"
+	"errors"
+)
+
+// See draft-agl-tls-chacha20poly1305-04 and
+// draft-irtf-cfrg-chacha20-poly1305-10. Where the two differ, the
+// draft-agl-tls-chacha20poly1305-04 variant is implemented.
+
+func leftRotate(a uint32, n uint) uint32 {
+	return (a << n) | (a >> (32 - n))
+}
+
+func chaChaQuarterRound(state *[16]uint32, a, b, c, d int) {
+	state[a] += state[b]
+	state[d] = leftRotate(state[d]^state[a], 16)
+
+	state[c] += state[d]
+	state[b] = leftRotate(state[b]^state[c], 12)
+
+	state[a] += state[b]
+	state[d] = leftRotate(state[d]^state[a], 8)
+
+	state[c] += state[d]
+	state[b] = leftRotate(state[b]^state[c], 7)
+}
+
+func chaCha20Block(state *[16]uint32, out []byte) {
+	var workingState [16]uint32
+	copy(workingState[:], state[:])
+	for i := 0; i < 10; i++ {
+		chaChaQuarterRound(&workingState, 0, 4, 8, 12)
+		chaChaQuarterRound(&workingState, 1, 5, 9, 13)
+		chaChaQuarterRound(&workingState, 2, 6, 10, 14)
+		chaChaQuarterRound(&workingState, 3, 7, 11, 15)
+		chaChaQuarterRound(&workingState, 0, 5, 10, 15)
+		chaChaQuarterRound(&workingState, 1, 6, 11, 12)
+		chaChaQuarterRound(&workingState, 2, 7, 8, 13)
+		chaChaQuarterRound(&workingState, 3, 4, 9, 14)
+	}
+	for i := 0; i < 16; i++ {
+		binary.LittleEndian.PutUint32(out[i*4:i*4+4], workingState[i]+state[i])
+	}
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+	if total := len(in) + n; cap(in) >= total {
+		head = in[:total]
+	} else {
+		head = make([]byte, total)
+		copy(head, in)
+	}
+	tail = head[len(in):]
+	return
+}
+
+type chaCha20Poly1305 struct {
+	key [32]byte
+}
+
+func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) {
+	if len(key) != 32 {
+		return nil, errors.New("bad key length")
+	}
+	aead := new(chaCha20Poly1305)
+	copy(aead.key[:], key)
+	return aead, nil
+}
+
+func (c *chaCha20Poly1305) NonceSize() int { return 8 }
+func (c *chaCha20Poly1305) Overhead() int  { return 16 }
+
+func (c *chaCha20Poly1305) chaCha20(out, in, nonce []byte, counter uint64) {
+	var state [16]uint32
+	state[0] = 0x61707865
+	state[1] = 0x3320646e
+	state[2] = 0x79622d32
+	state[3] = 0x6b206574
+	for i := 0; i < 8; i++ {
+		state[4+i] = binary.LittleEndian.Uint32(c.key[i*4 : i*4+4])
+	}
+	state[14] = binary.LittleEndian.Uint32(nonce[0:4])
+	state[15] = binary.LittleEndian.Uint32(nonce[4:8])
+
+	for i := 0; i < len(in); i += 64 {
+		state[12] = uint32(counter & 0xffffffff)
+		state[13] = uint32(counter >> 32)
+
+		var tmp [64]byte
+		chaCha20Block(&state, tmp[:])
+		count := 64
+		if len(in)-i < count {
+			count = len(in) - i
+		}
+		for j := 0; j < count; j++ {
+			out[i+j] = in[i+j] ^ tmp[j]
+		}
+
+		counter++
+	}
+}
+
+func (c *chaCha20Poly1305) poly1305(tag *[16]byte, nonce, ciphertext, additionalData []byte) {
+	input := make([]byte, 0, len(additionalData)+8+len(ciphertext)+8)
+	input = append(input, additionalData...)
+	input, out := sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(additionalData)))
+	input = append(input, ciphertext...)
+	input, out = sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(ciphertext)))
+
+	var poly1305Key [32]byte
+	c.chaCha20(poly1305Key[:], poly1305Key[:], nonce, 0)
+
+	poly1305Sum(tag, input, &poly1305Key)
+}
+
+func (c *chaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	if len(nonce) != 8 {
+		panic("Bad nonce length")
+	}
+
+	ret, out := sliceForAppend(dst, len(plaintext)+16)
+	c.chaCha20(out[:len(plaintext)], plaintext, nonce, 1)
+
+	var tag [16]byte
+	c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+	copy(out[len(plaintext):], tag[:])
+
+	return ret
+}
+
+func (c *chaCha20Poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	if len(nonce) != 8 {
+		panic("Bad nonce length")
+	}
+	if len(ciphertext) < 16 {
+		return nil, errors.New("chacha20: message authentication failed")
+	}
+	plaintextLen := len(ciphertext) - 16
+
+	var tag [16]byte
+	c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	if subtle.ConstantTimeCompare(tag[:], ciphertext[plaintextLen:]) != 1 {
+		return nil, errors.New("chacha20: message authentication failed")
+	}
+
+	ret, out := sliceForAppend(dst, plaintextLen)
+	c.chaCha20(out, ciphertext[:plaintextLen], nonce, 1)
+	return ret, nil
+}
diff --git a/src/ssl/test/runner/chacha20_poly1305_test.go b/src/ssl/test/runner/chacha20_poly1305_test.go
new file mode 100644
index 0000000..726f482
--- /dev/null
+++ b/src/ssl/test/runner/chacha20_poly1305_test.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+	"bytes"
+	"encoding/hex"
+	"testing"
+)
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.1.1.
+func TestChaChaQuarterRound(t *testing.T) {
+	state := [16]uint32{0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567}
+	chaChaQuarterRound(&state, 0, 1, 2, 3)
+
+	a, b, c, d := state[0], state[1], state[2], state[3]
+	if a != 0xea2a92f4 || b != 0xcb1cf8ce || c != 0x4581472e || d != 0x5881c4bb {
+		t.Errorf("Incorrect results: %x", state)
+	}
+}
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.2.1.
+func TestChaChaQuarterRoundState(t *testing.T) {
+	state := [16]uint32{
+		0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
+		0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
+		0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
+		0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320,
+	}
+	chaChaQuarterRound(&state, 2, 7, 8, 13)
+
+	expected := [16]uint32{
+		0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a,
+		0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2,
+		0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963,
+		0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320,
+	}
+	for i := range state {
+		if state[i] != expected[i] {
+			t.Errorf("Mismatch at %d: %x vs %x", i, state, expected)
+		}
+	}
+}
+
+// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.3.2.
+func TestChaCha20Block(t *testing.T) {
+	state := [16]uint32{
+		0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+		0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
+		0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
+		0x00000001, 0x09000000, 0x4a000000, 0x00000000,
+	}
+	out := make([]byte, 64)
+	chaCha20Block(&state, out)
+
+	expected := []byte{
+		0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
+		0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
+		0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
+		0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
+		0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
+		0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
+		0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
+		0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
+	}
+	if !bytes.Equal(out, expected) {
+		t.Errorf("Got %x, wanted %x", out, expected)
+	}
+}
+
+// See draft-agl-tls-chacha20poly1305-04, section 7.
+func TestChaCha20Poly1305(t *testing.T) {
+	key, _ := hex.DecodeString("4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
+	input, _ := hex.DecodeString("86d09974840bded2a5ca")
+	nonce, _ := hex.DecodeString("cd7cf67be39c794a")
+	ad, _ := hex.DecodeString("87e229d4500845a079c0")
+	output, _ := hex.DecodeString("e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6")
+
+	aead, err := newChaCha20Poly1305(key)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	out, err := aead.Open(nil, nonce, output, ad)
+	if err != nil {
+		t.Errorf("Open failed: %s", err)
+	} else if !bytes.Equal(out, input) {
+		t.Errorf("Open gave %x, wanted %x", out, input)
+	}
+
+	out = aead.Seal(nil, nonce, input, ad)
+	if !bytes.Equal(out, output) {
+		t.Errorf("Open gave %x, wanted %x", out, output)
+	}
+
+	out[0]++
+	_, err = aead.Open(nil, nonce, out, ad)
+	if err == nil {
+		t.Errorf("Open on malformed data unexpectedly succeeded")
+	}
+}
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 89e75c8..162c0c0 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -62,6 +62,11 @@
 	suitePSK
 )
 
+type tlsAead struct {
+	cipher.AEAD
+	explicitNonce bool
+}
+
 // A cipherSuite is a specific combination of key agreement, cipher and MAC
 // function. All cipher suites currently assume RSA key agreement.
 type cipherSuite struct {
@@ -75,12 +80,14 @@
 	flags  int
 	cipher func(key, iv []byte, isRead bool) interface{}
 	mac    func(version uint16, macKey []byte) macFunction
-	aead   func(key, fixedNonce []byte) cipher.AEAD
+	aead   func(key, fixedNonce []byte) *tlsAead
 }
 
 var cipherSuites = []*cipherSuite{
 	// Ciphersuite order is chosen so that ECDHE comes before plain RSA
 	// and RC4 comes before AES (because of the Lucky13 attack).
+	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
 	{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
@@ -95,6 +102,7 @@
 	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil},
 	{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+	{TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, dheRSAKA, suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
 	{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil},
@@ -216,7 +224,7 @@
 	return f.aead.Open(out, f.openNonce, plaintext, additionalData)
 }
 
-func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
+func aeadAESGCM(key, fixedNonce []byte) *tlsAead {
 	aes, err := aes.NewCipher(key)
 	if err != nil {
 		panic(err)
@@ -230,7 +238,15 @@
 	copy(nonce1, fixedNonce)
 	copy(nonce2, fixedNonce)
 
-	return &fixedNonceAEAD{nonce1, nonce2, aead}
+	return &tlsAead{&fixedNonceAEAD{nonce1, nonce2, aead}, true}
+}
+
+func aeadCHACHA20POLY1305(key, fixedNonce []byte) *tlsAead {
+	aead, err := newChaCha20Poly1305(key)
+	if err != nil {
+		panic(err)
+	}
+	return &tlsAead{aead, false}
 }
 
 // ssl30MAC implements the SSLv3 MAC function, as defined in
@@ -289,7 +305,7 @@
 }
 
 func rsaKA(version uint16) keyAgreement {
-	return &rsaKeyAgreement{}
+	return &rsaKeyAgreement{version: version}
 }
 
 func ecdheECDSAKA(version uint16) keyAgreement {
@@ -391,5 +407,8 @@
 
 // Additional cipher suite IDs, not IANA-assigned.
 const (
-	TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe
+	TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256         uint16 = 0xcafe
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   uint16 = 0xcc13
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc14
+	TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     uint16 = 0xcc15
 )
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 7aaf9a2..4ac7250 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -97,6 +97,7 @@
 type CurveID uint16
 
 const (
+	CurveP224 CurveID = 21
 	CurveP256 CurveID = 23
 	CurveP384 CurveID = 24
 	CurveP521 CurveID = 25
@@ -429,15 +430,32 @@
 	// ServerKeyExchange.
 	UnauthenticatedECDH bool
 
+	// SkipHelloVerifyRequest causes a DTLS server to skip the
+	// HelloVerifyRequest message.
+	SkipHelloVerifyRequest bool
+
+	// SkipCertificateStatus, if true, causes the server to skip the
+	// CertificateStatus message. This is legal because CertificateStatus is
+	// optional, even with a status_request in ServerHello.
+	SkipCertificateStatus bool
+
 	// SkipServerKeyExchange causes the server to skip sending
 	// ServerKeyExchange messages.
 	SkipServerKeyExchange bool
 
+	// SkipNewSessionTicket causes the server to skip sending the
+	// NewSessionTicket message despite promising to in ServerHello.
+	SkipNewSessionTicket bool
+
 	// SkipChangeCipherSpec causes the implementation to skip
 	// sending the ChangeCipherSpec message (and adjusting cipher
 	// state accordingly for the Finished message).
 	SkipChangeCipherSpec bool
 
+	// SkipFinished causes the implementation to skip sending the Finished
+	// message.
+	SkipFinished bool
+
 	// EarlyChangeCipherSpec causes the client to send an early
 	// ChangeCipherSpec message before the ClientKeyExchange. A value of
 	// zero disables this behavior. One and two configure variants for 0.9.8
@@ -449,10 +467,6 @@
 	// messages.
 	FragmentAcrossChangeCipherSpec bool
 
-	// SkipNewSessionTicket causes the server to skip sending the
-	// NewSessionTicket message despite promising to in ServerHello.
-	SkipNewSessionTicket bool
-
 	// SendV2ClientHello causes the client to send a V2ClientHello
 	// instead of a normal ClientHello.
 	SendV2ClientHello bool
@@ -475,8 +489,9 @@
 	// two records.
 	FragmentAlert bool
 
-	// SendSpuriousAlert will cause an spurious, unwanted alert to be sent.
-	SendSpuriousAlert bool
+	// SendSpuriousAlert, if non-zero, will cause an spurious, unwanted
+	// alert to be sent.
+	SendSpuriousAlert alert
 
 	// RsaClientKeyExchangeVersion, if non-zero, causes the client to send a
 	// ClientKeyExchange with the specified version rather than the
@@ -491,16 +506,19 @@
 	// TLS version in the ClientHello than the maximum supported version.
 	SendClientVersion uint16
 
-	// SkipHelloVerifyRequest causes a DTLS server to skip the
-	// HelloVerifyRequest message.
-	SkipHelloVerifyRequest bool
-
 	// ExpectFalseStart causes the server to, on full handshakes,
 	// expect the peer to False Start; the server Finished message
 	// isn't sent until we receive an application data record
 	// from the peer.
 	ExpectFalseStart bool
 
+	// AlertBeforeFalseStartTest, if non-zero, causes the server to, on full
+	// handshakes, send an alert just before reading the application data
+	// record to test False Start. This can be used in a negative False
+	// Start test to determine whether the peer processed the alert (and
+	// closed the connection) before or after sending app data.
+	AlertBeforeFalseStartTest alert
+
 	// SSL3RSAKeyExchange causes the client to always send an RSA
 	// ClientKeyExchange message without the two-byte length
 	// prefix, as if it were SSL3.
@@ -557,9 +575,10 @@
 	// retransmit at the record layer.
 	SequenceNumberIncrement uint64
 
-	// RSAServerKeyExchange, if true, causes the server to send a
-	// ServerKeyExchange message in the plain RSA key exchange.
-	RSAServerKeyExchange bool
+	// RSAEphemeralKey, if true, causes the server to send a
+	// ServerKeyExchange message containing an ephemeral key (as in
+	// RSA_EXPORT) in the plain RSA key exchange.
+	RSAEphemeralKey bool
 
 	// SRTPMasterKeyIdentifer, if not empty, is the SRTP MKI value that the
 	// client offers when negotiating SRTP. MKI support is still missing so
@@ -578,6 +597,10 @@
 	// still be enforced.
 	NoSignatureAndHashes bool
 
+	// NoSupportedCurves, if true, causes the client to omit the
+	// supported_curves extension.
+	NoSupportedCurves bool
+
 	// RequireSameRenegoClientVersion, if true, causes the server
 	// to require that all ClientHellos match in offered version
 	// across a renego.
@@ -603,6 +626,87 @@
 	// AppDataAfterChangeCipherSpec, if not null, causes application data to
 	// be sent immediately after ChangeCipherSpec.
 	AppDataAfterChangeCipherSpec []byte
+
+	// AlertAfterChangeCipherSpec, if non-zero, causes an alert to be sent
+	// immediately after ChangeCipherSpec.
+	AlertAfterChangeCipherSpec alert
+
+	// TimeoutSchedule is the schedule of packet drops and simulated
+	// timeouts for before each handshake leg from the peer.
+	TimeoutSchedule []time.Duration
+
+	// PacketAdaptor is the packetAdaptor to use to simulate timeouts.
+	PacketAdaptor *packetAdaptor
+
+	// ReorderHandshakeFragments, if true, causes handshake fragments in
+	// DTLS to overlap and be sent in the wrong order. It also causes
+	// pre-CCS flights to be sent twice. (Post-CCS flights consist of
+	// Finished and will trigger a spurious retransmit.)
+	ReorderHandshakeFragments bool
+
+	// MixCompleteMessageWithFragments, if true, causes handshake
+	// messages in DTLS to redundantly both fragment the message
+	// and include a copy of the full one.
+	MixCompleteMessageWithFragments bool
+
+	// SendInvalidRecordType, if true, causes a record with an invalid
+	// content type to be sent immediately following the handshake.
+	SendInvalidRecordType bool
+
+	// WrongCertificateMessageType, if true, causes Certificate message to
+	// be sent with the wrong message type.
+	WrongCertificateMessageType bool
+
+	// FragmentMessageTypeMismatch, if true, causes all non-initial
+	// handshake fragments in DTLS to have the wrong message type.
+	FragmentMessageTypeMismatch bool
+
+	// FragmentMessageLengthMismatch, if true, causes all non-initial
+	// handshake fragments in DTLS to have the wrong message length.
+	FragmentMessageLengthMismatch bool
+
+	// SplitFragmentHeader, if true, causes the handshake fragments in DTLS
+	// to be split across two records.
+	SplitFragmentHeader bool
+
+	// SplitFragmentBody, if true, causes the handshake bodies in DTLS to be
+	// split across two records.
+	//
+	// TODO(davidben): There's one final split to test: when the header and
+	// body are split across two records. But those are (incorrectly)
+	// accepted right now.
+	SplitFragmentBody bool
+
+	// SendEmptyFragments, if true, causes handshakes to include empty
+	// fragments in DTLS.
+	SendEmptyFragments bool
+
+	// NeverResumeOnRenego, if true, causes renegotiations to always be full
+	// handshakes.
+	NeverResumeOnRenego bool
+
+	// NoSignatureAlgorithmsOnRenego, if true, causes renegotiations to omit
+	// the signature_algorithms extension.
+	NoSignatureAlgorithmsOnRenego bool
+
+	// IgnorePeerCipherPreferences, if true, causes the peer's cipher
+	// preferences to be ignored.
+	IgnorePeerCipherPreferences bool
+
+	// IgnorePeerSignatureAlgorithmPreferences, if true, causes the peer's
+	// signature algorithm preferences to be ignored.
+	IgnorePeerSignatureAlgorithmPreferences bool
+
+	// IgnorePeerCurvePreferences, if true, causes the peer's curve
+	// preferences to be ignored.
+	IgnorePeerCurvePreferences bool
+
+	// SendWarningAlerts, if non-zero, causes every record to be prefaced by
+	// a warning alert.
+	SendWarningAlerts alert
+
+	// BadFinished, if true, causes the Finished hash to be broken.
+	BadFinished bool
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index d4a6817..fd198ca 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -37,14 +37,16 @@
 	handshakeComplete    bool
 	didResume            bool // whether this connection was a session resumption
 	extendedMasterSecret bool // whether this session used an extended master secret
-	cipherSuite          uint16
+	cipherSuite          *cipherSuite
 	ocspResponse         []byte // stapled OCSP response
 	peerCertificates     []*x509.Certificate
 	// verifiedChains contains the certificate chains that we built, as
 	// opposed to the ones presented by the server.
 	verifiedChains [][]*x509.Certificate
 	// serverName contains the server name indicated by the client, if any.
-	serverName string
+	serverName                 string
+	clientRandom, serverRandom [32]byte
+	masterSecret               [48]byte
 
 	clientProtocol         string
 	clientProtocolFallback bool
@@ -69,8 +71,9 @@
 	// DTLS state
 	sendHandshakeSeq uint16
 	recvHandshakeSeq uint16
-	handMsg          []byte // pending assembled handshake message
-	handMsgLen       int    // handshake message length, not including the header
+	handMsg          []byte   // pending assembled handshake message
+	handMsgLen       int      // handshake message length, not including the header
+	pendingFragments [][]byte // pending outgoing handshake fragments.
 
 	tmp [16]byte
 }
@@ -131,6 +134,7 @@
 
 	nextCipher interface{} // next encryption state
 	nextMac    macFunction // next MAC algorithm
+	nextSeq    [6]byte     // next epoch's starting sequence number in DTLS
 
 	// used to save allocating a new buffer for each MAC.
 	inDigestBuf, outDigestBuf []byte
@@ -200,10 +204,20 @@
 	}
 }
 
-// incEpoch resets the sequence number. In DTLS, it increments the
-// epoch half of the sequence number.
+// incNextSeq increments the starting sequence number for the next epoch.
+func (hc *halfConn) incNextSeq() {
+	for i := len(hc.nextSeq) - 1; i >= 0; i-- {
+		hc.nextSeq[i]++
+		if hc.nextSeq[i] != 0 {
+			return
+		}
+	}
+	panic("TLS: sequence number wraparound")
+}
+
+// incEpoch resets the sequence number. In DTLS, it also increments the epoch
+// half of the sequence number.
 func (hc *halfConn) incEpoch() {
-	limit := 0
 	if hc.isDTLS {
 		for i := 1; i >= 0; i-- {
 			hc.seq[i]++
@@ -214,11 +228,14 @@
 				panic("TLS: epoch number wraparound")
 			}
 		}
-		limit = 2
-	}
-	seq := hc.seq[limit:]
-	for i := range seq {
-		seq[i] = 0
+		copy(hc.seq[2:], hc.nextSeq[:])
+		for i := range hc.nextSeq {
+			hc.nextSeq[i] = 0
+		}
+	} else {
+		for i := range hc.seq {
+			hc.seq[i] = 0
+		}
 	}
 }
 
@@ -321,13 +338,16 @@
 		switch c := hc.cipher.(type) {
 		case cipher.Stream:
 			c.XORKeyStream(payload, payload)
-		case cipher.AEAD:
-			explicitIVLen = 8
-			if len(payload) < explicitIVLen {
-				return false, 0, alertBadRecordMAC
+		case *tlsAead:
+			nonce := seq
+			if c.explicitNonce {
+				explicitIVLen = 8
+				if len(payload) < explicitIVLen {
+					return false, 0, alertBadRecordMAC
+				}
+				nonce = payload[:8]
+				payload = payload[8:]
 			}
-			nonce := payload[:8]
-			payload = payload[8:]
 
 			var additionalData [13]byte
 			copy(additionalData[:], seq)
@@ -451,10 +471,13 @@
 		switch c := hc.cipher.(type) {
 		case cipher.Stream:
 			c.XORKeyStream(payload, payload)
-		case cipher.AEAD:
+		case *tlsAead:
 			payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
 			b.resize(len(b.data) + c.Overhead())
-			nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+			nonce := hc.seq[:]
+			if c.explicitNonce {
+				nonce = b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+			}
 			payload := b.data[recordHeaderLen+explicitIVLen:]
 			payload = payload[:payloadLen]
 
@@ -756,11 +779,8 @@
 		if typ != want {
 			// A client might need to process a HelloRequest from
 			// the server, thus receiving a handshake message when
-			// application data is expected is ok. Moreover, a DTLS
-			// peer who sends Finished second may retransmit the
-			// final leg. BoringSSL retrainsmits on an internal
-			// timer, so this may also occur in test code.
-			if !c.isClient && !c.isDTLS {
+			// application data is expected is ok.
+			if !c.isClient {
 				return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
 			}
 		}
@@ -817,6 +837,13 @@
 // to the connection and updates the record layer state.
 // c.out.Mutex <= L.
 func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
+	if typ != recordTypeAlert && c.config.Bugs.SendWarningAlerts != 0 {
+		alert := make([]byte, 2)
+		alert[0] = alertLevelWarning
+		alert[1] = byte(c.config.Bugs.SendWarningAlerts)
+		c.writeRecord(recordTypeAlert, alert)
+	}
+
 	if c.isDTLS {
 		return c.dtlsWriteRecord(typ, data)
 	}
@@ -851,7 +878,7 @@
 			}
 		}
 		if explicitIVLen == 0 {
-			if _, ok := c.out.cipher.(cipher.AEAD); ok {
+			if aead, ok := c.out.cipher.(*tlsAead); ok && aead.explicitNonce {
 				explicitIVLen = 8
 				// The AES-GCM construction in TLS has an
 				// explicit nonce so that the nonce can be
@@ -1003,6 +1030,67 @@
 	return m, nil
 }
 
+// skipPacket processes all the DTLS records in packet. It updates
+// sequence number expectations but otherwise ignores them.
+func (c *Conn) skipPacket(packet []byte) error {
+	for len(packet) > 0 {
+		// Dropped packets are completely ignored save to update
+		// expected sequence numbers for this and the next epoch. (We
+		// don't assert on the contents of the packets both for
+		// simplicity and because a previous test with one shorter
+		// timeout schedule would have done so.)
+		epoch := packet[3:5]
+		seq := packet[5:11]
+		length := uint16(packet[11])<<8 | uint16(packet[12])
+		if bytes.Equal(c.in.seq[:2], epoch) {
+			if !bytes.Equal(c.in.seq[2:], seq) {
+				return errors.New("tls: sequence mismatch")
+			}
+			c.in.incSeq(false)
+		} else {
+			if !bytes.Equal(c.in.nextSeq[:], seq) {
+				return errors.New("tls: sequence mismatch")
+			}
+			c.in.incNextSeq()
+		}
+		packet = packet[13+length:]
+	}
+	return nil
+}
+
+// simulatePacketLoss simulates the loss of a handshake leg from the
+// peer based on the schedule in c.config.Bugs. If resendFunc is
+// non-nil, it is called after each simulated timeout to retransmit
+// handshake messages from the local end. This is used in cases where
+// the peer retransmits on a stale Finished rather than a timeout.
+func (c *Conn) simulatePacketLoss(resendFunc func()) error {
+	if len(c.config.Bugs.TimeoutSchedule) == 0 {
+		return nil
+	}
+	if !c.isDTLS {
+		return errors.New("tls: TimeoutSchedule may only be set in DTLS")
+	}
+	if c.config.Bugs.PacketAdaptor == nil {
+		return errors.New("tls: TimeoutSchedule set without PacketAdapter")
+	}
+	for _, timeout := range c.config.Bugs.TimeoutSchedule {
+		// Simulate a timeout.
+		packets, err := c.config.Bugs.PacketAdaptor.SendReadTimeout(timeout)
+		if err != nil {
+			return err
+		}
+		for _, packet := range packets {
+			if err := c.skipPacket(packet); err != nil {
+				return err
+			}
+		}
+		if resendFunc != nil {
+			resendFunc()
+		}
+	}
+	return nil
+}
+
 // Write writes data to the connection.
 func (c *Conn) Write(b []byte) (int, error) {
 	if err := c.Handshake(); err != nil {
@@ -1020,8 +1108,8 @@
 		return 0, alertInternalError
 	}
 
-	if c.config.Bugs.SendSpuriousAlert {
-		c.sendAlertLocked(alertRecordOverflow)
+	if c.config.Bugs.SendSpuriousAlert != 0 {
+		c.sendAlertLocked(c.config.Bugs.SendSpuriousAlert)
 	}
 
 	// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
@@ -1096,9 +1184,9 @@
 				// Soft error, like EAGAIN
 				return 0, err
 			}
-			if c.hand.Len() > 0 && !c.isDTLS {
+			if c.hand.Len() > 0 {
 				// We received handshake bytes, indicating the
-				// start of a renegotiation or a DTLS retransmit.
+				// start of a renegotiation.
 				if err := c.handleRenegotiation(); err != nil {
 					return 0, err
 				}
@@ -1177,6 +1265,9 @@
 	} else {
 		c.handshakeErr = c.serverHandshake()
 	}
+	if c.handshakeErr == nil && c.config.Bugs.SendInvalidRecordType {
+		c.writeRecord(recordType(42), []byte("invalid record"))
+	}
 	return c.handshakeErr
 }
 
@@ -1193,7 +1284,7 @@
 		state.DidResume = c.didResume
 		state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
 		state.NegotiatedProtocolFromALPN = c.usedALPN
-		state.CipherSuite = c.cipherSuite
+		state.CipherSuite = c.cipherSuite.id
 		state.PeerCertificates = c.peerCertificates
 		state.VerifiedChains = c.verifiedChains
 		state.ServerName = c.serverName
@@ -1227,3 +1318,28 @@
 	}
 	return c.peerCertificates[0].VerifyHostname(host)
 }
+
+// ExportKeyingMaterial exports keying material from the current connection
+// state, as per RFC 5705.
+func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContext bool) ([]byte, error) {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+	if !c.handshakeComplete {
+		return nil, errors.New("tls: handshake has not yet been performed")
+	}
+
+	seedLen := len(c.clientRandom) + len(c.serverRandom)
+	if useContext {
+		seedLen += 2 + len(context)
+	}
+	seed := make([]byte, 0, seedLen)
+	seed = append(seed, c.clientRandom[:]...)
+	seed = append(seed, c.serverRandom[:]...)
+	if useContext {
+		seed = append(seed, byte(len(context)>>8), byte(len(context)))
+		seed = append(seed, context...)
+	}
+	result := make([]byte, length)
+	prfForVersion(c.vers, c.cipherSuite)(result, c.masterSecret[:], label, seed)
+	return result, nil
+}
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index a395980..85c4247 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -16,10 +16,10 @@
 
 import (
 	"bytes"
-	"crypto/cipher"
 	"errors"
 	"fmt"
 	"io"
+	"math/rand"
 	"net"
 )
 
@@ -38,7 +38,6 @@
 }
 
 func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
-Again:
 	recordHeaderLen := dtlsRecordHeaderLen
 
 	if c.rawInput == nil {
@@ -82,13 +81,6 @@
 		}
 	}
 	seq := b.data[3:11]
-	if !bytes.Equal(seq[:2], c.in.seq[:2]) {
-		// If the epoch didn't match, silently drop the record.
-		// BoringSSL retransmits on an internal timer, so it may flakily
-		// revisit the previous epoch if retransmiting ChangeCipherSpec
-		// and Finished.
-		goto Again
-	}
 	// For test purposes, we assume a reliable channel. Require
 	// that the explicit sequence number matches the incrementing
 	// one we maintain. A real implementation would maintain a
@@ -113,7 +105,134 @@
 	return typ, b, nil
 }
 
+func (c *Conn) makeFragment(header, data []byte, fragOffset, fragLen int) []byte {
+	fragment := make([]byte, 0, 12+fragLen)
+	fragment = append(fragment, header...)
+	fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq))
+	fragment = append(fragment, byte(fragOffset>>16), byte(fragOffset>>8), byte(fragOffset))
+	fragment = append(fragment, byte(fragLen>>16), byte(fragLen>>8), byte(fragLen))
+	fragment = append(fragment, data[fragOffset:fragOffset+fragLen]...)
+	return fragment
+}
+
 func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
+	if typ != recordTypeHandshake {
+		// Only handshake messages are fragmented.
+		return c.dtlsWriteRawRecord(typ, data)
+	}
+
+	maxLen := c.config.Bugs.MaxHandshakeRecordLength
+	if maxLen <= 0 {
+		maxLen = 1024
+	}
+
+	// Handshake messages have to be modified to include fragment
+	// offset and length and with the header replicated. Save the
+	// TLS header here.
+	//
+	// TODO(davidben): This assumes that data contains exactly one
+	// handshake message. This is incompatible with
+	// FragmentAcrossChangeCipherSpec. (Which is unfortunate
+	// because OpenSSL's DTLS implementation will probably accept
+	// such fragmentation and could do with a fix + tests.)
+	header := data[:4]
+	data = data[4:]
+
+	isFinished := header[0] == typeFinished
+
+	if c.config.Bugs.SendEmptyFragments {
+		fragment := c.makeFragment(header, data, 0, 0)
+		c.pendingFragments = append(c.pendingFragments, fragment)
+	}
+
+	firstRun := true
+	fragOffset := 0
+	for firstRun || fragOffset < len(data) {
+		firstRun = false
+		fragLen := len(data) - fragOffset
+		if fragLen > maxLen {
+			fragLen = maxLen
+		}
+
+		fragment := c.makeFragment(header, data, fragOffset, fragLen)
+		if c.config.Bugs.FragmentMessageTypeMismatch && fragOffset > 0 {
+			fragment[0]++
+		}
+		if c.config.Bugs.FragmentMessageLengthMismatch && fragOffset > 0 {
+			fragment[3]++
+		}
+
+		// Buffer the fragment for later. They will be sent (and
+		// reordered) on flush.
+		c.pendingFragments = append(c.pendingFragments, fragment)
+		if c.config.Bugs.ReorderHandshakeFragments {
+			// Don't duplicate Finished to avoid the peer
+			// interpreting it as a retransmit request.
+			if !isFinished {
+				c.pendingFragments = append(c.pendingFragments, fragment)
+			}
+
+			if fragLen > (maxLen+1)/2 {
+				// Overlap each fragment by half.
+				fragLen = (maxLen + 1) / 2
+			}
+		}
+		fragOffset += fragLen
+		n += fragLen
+	}
+	if !isFinished && c.config.Bugs.MixCompleteMessageWithFragments {
+		fragment := c.makeFragment(header, data, 0, len(data))
+		c.pendingFragments = append(c.pendingFragments, fragment)
+	}
+
+	// Increment the handshake sequence number for the next
+	// handshake message.
+	c.sendHandshakeSeq++
+	return
+}
+
+func (c *Conn) dtlsFlushHandshake() error {
+	if !c.isDTLS {
+		return nil
+	}
+
+	var fragments [][]byte
+	fragments, c.pendingFragments = c.pendingFragments, fragments
+
+	if c.config.Bugs.ReorderHandshakeFragments {
+		perm := rand.New(rand.NewSource(0)).Perm(len(fragments))
+		tmp := make([][]byte, len(fragments))
+		for i := range tmp {
+			tmp[i] = fragments[perm[i]]
+		}
+		fragments = tmp
+	}
+
+	// Send them all.
+	for _, fragment := range fragments {
+		if c.config.Bugs.SplitFragmentHeader {
+			if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:2]); err != nil {
+				return err
+			}
+			fragment = fragment[2:]
+		} else if c.config.Bugs.SplitFragmentBody && len(fragment) > 12 {
+			if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:13]); err != nil {
+				return err
+			}
+			fragment = fragment[13:]
+		}
+
+		// TODO(davidben): A real DTLS implementation needs to
+		// retransmit handshake messages. For testing purposes, we don't
+		// actually care.
+		if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) {
 	recordHeaderLen := dtlsRecordHeaderLen
 	maxLen := c.config.Bugs.MaxHandshakeRecordLength
 	if maxLen <= 0 {
@@ -122,118 +241,60 @@
 
 	b := c.out.newBlock()
 
-	var header []byte
-	if typ == recordTypeHandshake {
-		// Handshake messages have to be modified to include
-		// fragment offset and length and with the header
-		// replicated. Save the header here.
-		//
-		// TODO(davidben): This assumes that data contains
-		// exactly one handshake message. This is incompatible
-		// with FragmentAcrossChangeCipherSpec. (Which is
-		// unfortunate because OpenSSL's DTLS implementation
-		// will probably accept such fragmentation and could
-		// do with a fix + tests.)
-		if len(data) < 4 {
-			// This should not happen.
-			panic(data)
-		}
-		header = data[:4]
-		data = data[4:]
-	}
+	explicitIVLen := 0
+	explicitIVIsSeq := false
 
-	firstRun := true
-	for firstRun || len(data) > 0 {
-		firstRun = false
-		m := len(data)
-		var fragment []byte
-		// Handshake messages get fragmented. Other records we
-		// pass-through as is. DTLS should be a packet
-		// interface.
-		if typ == recordTypeHandshake {
-			if m > maxLen {
-				m = maxLen
-			}
-
-			// Standard handshake header.
-			fragment = make([]byte, 0, 12+m)
-			fragment = append(fragment, header...)
-			// message_seq
-			fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq))
-			// fragment_offset
-			fragment = append(fragment, byte(n>>16), byte(n>>8), byte(n))
-			// fragment_length
-			fragment = append(fragment, byte(m>>16), byte(m>>8), byte(m))
-			fragment = append(fragment, data[:m]...)
-		} else {
-			fragment = data[:m]
-		}
-
-		// Send the fragment.
-		explicitIVLen := 0
-		explicitIVIsSeq := false
-
-		if cbc, ok := c.out.cipher.(cbcMode); ok {
-			// Block cipher modes have an explicit IV.
-			explicitIVLen = cbc.BlockSize()
-		} else if _, ok := c.out.cipher.(cipher.AEAD); ok {
+	if cbc, ok := c.out.cipher.(cbcMode); ok {
+		// Block cipher modes have an explicit IV.
+		explicitIVLen = cbc.BlockSize()
+	} else if aead, ok := c.out.cipher.(*tlsAead); ok {
+		if aead.explicitNonce {
 			explicitIVLen = 8
-			// The AES-GCM construction in TLS has an
-			// explicit nonce so that the nonce can be
-			// random. However, the nonce is only 8 bytes
-			// which is too small for a secure, random
-			// nonce. Therefore we use the sequence number
-			// as the nonce.
+			// The AES-GCM construction in TLS has an explicit nonce so that
+			// the nonce can be random. However, the nonce is only 8 bytes
+			// which is too small for a secure, random nonce. Therefore we
+			// use the sequence number as the nonce.
 			explicitIVIsSeq = true
-		} else if c.out.cipher != nil {
-			panic("Unknown cipher")
 		}
-		b.resize(recordHeaderLen + explicitIVLen + len(fragment))
-		b.data[0] = byte(typ)
-		vers := c.vers
-		if vers == 0 {
-			// Some TLS servers fail if the record version is
-			// greater than TLS 1.0 for the initial ClientHello.
-			vers = VersionTLS10
-		}
-		vers = versionToWire(vers, c.isDTLS)
-		b.data[1] = byte(vers >> 8)
-		b.data[2] = byte(vers)
-		// DTLS records include an explicit sequence number.
-		copy(b.data[3:11], c.out.seq[0:])
-		b.data[11] = byte(len(fragment) >> 8)
-		b.data[12] = byte(len(fragment))
-		if explicitIVLen > 0 {
-			explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
-			if explicitIVIsSeq {
-				copy(explicitIV, c.out.seq[:])
-			} else {
-				if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
-					break
-				}
+	} else if c.out.cipher != nil {
+		panic("Unknown cipher")
+	}
+	b.resize(recordHeaderLen + explicitIVLen + len(data))
+	b.data[0] = byte(typ)
+	vers := c.vers
+	if vers == 0 {
+		// Some TLS servers fail if the record version is greater than
+		// TLS 1.0 for the initial ClientHello.
+		vers = VersionTLS10
+	}
+	vers = versionToWire(vers, c.isDTLS)
+	b.data[1] = byte(vers >> 8)
+	b.data[2] = byte(vers)
+	// DTLS records include an explicit sequence number.
+	copy(b.data[3:11], c.out.seq[0:])
+	b.data[11] = byte(len(data) >> 8)
+	b.data[12] = byte(len(data))
+	if explicitIVLen > 0 {
+		explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+		if explicitIVIsSeq {
+			copy(explicitIV, c.out.seq[:])
+		} else {
+			if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
+				return
 			}
 		}
-		copy(b.data[recordHeaderLen+explicitIVLen:], fragment)
-		c.out.encrypt(b, explicitIVLen)
-
-		// TODO(davidben): A real DTLS implementation needs to
-		// retransmit handshake messages. For testing
-		// purposes, we don't actually care.
-		_, err = c.conn.Write(b.data)
-		if err != nil {
-			break
-		}
-		n += m
-		data = data[m:]
 	}
+	copy(b.data[recordHeaderLen+explicitIVLen:], data)
+	c.out.encrypt(b, explicitIVLen)
+
+	_, err = c.conn.Write(b.data)
+	if err != nil {
+		return
+	}
+	n = len(data)
+
 	c.out.freeBlock(b)
 
-	// Increment the handshake sequence number for the next
-	// handshake message.
-	if typ == recordTypeHandshake {
-		c.sendHandshakeSeq++
-	}
-
 	if typ == recordTypeChangeCipherSpec {
 		err = c.out.changeCipherSpec(c.config)
 		if err != nil {
@@ -250,9 +311,9 @@
 
 func (c *Conn) dtlsDoReadHandshake() ([]byte, error) {
 	// Assemble a full handshake message.  For test purposes, this
-	// implementation assumes fragments arrive in order, but tolerates
-	// retransmits. It may need to be cleverer if we ever test BoringSSL's
-	// retransmit behavior.
+	// implementation assumes fragments arrive in order. It may
+	// need to be cleverer if we ever test BoringSSL's retransmit
+	// behavior.
 	for len(c.handMsg) < 4+c.handMsgLen {
 		// Get a new handshake record if the previous has been
 		// exhausted.
@@ -281,16 +342,9 @@
 		}
 		fragment := c.hand.Next(fragLen)
 
-		if fragSeq < c.recvHandshakeSeq {
-			// BoringSSL retransmits based on an internal timer, so
-			// it may flakily retransmit part of a handshake
-			// message. Ignore those fragments.
-			//
-			// TODO(davidben): Revise this if BoringSSL's retransmit
-			// logic is made more deterministic.
-			continue
-		} else if fragSeq > c.recvHandshakeSeq {
-			return nil, errors.New("dtls: handshake messages sent out of order")
+		// Check it's a fragment for the right message.
+		if fragSeq != c.recvHandshakeSeq {
+			return nil, errors.New("dtls: bad handshake sequence number")
 		}
 
 		// Check that the length is consistent.
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index f297fc1..0dac05d 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -6,7 +6,6 @@
 
 import (
 	"bytes"
-	"crypto"
 	"crypto/ecdsa"
 	"crypto/elliptic"
 	"crypto/rsa"
@@ -22,13 +21,14 @@
 )
 
 type clientHandshakeState struct {
-	c            *Conn
-	serverHello  *serverHelloMsg
-	hello        *clientHelloMsg
-	suite        *cipherSuite
-	finishedHash finishedHash
-	masterSecret []byte
-	session      *ClientSessionState
+	c             *Conn
+	serverHello   *serverHelloMsg
+	hello         *clientHelloMsg
+	suite         *cipherSuite
+	finishedHash  finishedHash
+	masterSecret  []byte
+	session       *ClientSessionState
+	finishedBytes []byte
 }
 
 func (c *Conn) clientHandshake() error {
@@ -83,6 +83,10 @@
 		hello.extendedMasterSecret = false
 	}
 
+	if c.config.Bugs.NoSupportedCurves {
+		hello.supportedCurves = nil
+	}
+
 	if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo {
 		if c.config.Bugs.BadRenegotiationInfo {
 			hello.secureRenegotiation = append(hello.secureRenegotiation, c.clientVerify...)
@@ -129,13 +133,16 @@
 		return errors.New("tls: short read from Rand: " + err.Error())
 	}
 
-	if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
+	if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes && (c.cipherSuite == nil || !c.config.Bugs.NoSignatureAlgorithmsOnRenego) {
 		hello.signatureAndHashes = c.config.signatureAndHashesForClient()
 	}
 
 	var session *ClientSessionState
 	var cacheKey string
 	sessionCache := c.config.ClientSessionCache
+	if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
+		sessionCache = nil
+	}
 
 	if sessionCache != nil {
 		hello.ticketSupported = !c.config.SessionTicketsDisabled
@@ -213,7 +220,11 @@
 		helloBytes = hello.marshal()
 		c.writeRecord(recordTypeHandshake, helloBytes)
 	}
+	c.dtlsFlushHandshake()
 
+	if err := c.simulatePacketLoss(nil); err != nil {
+		return err
+	}
 	msg, err := c.readHandshake()
 	if err != nil {
 		return err
@@ -233,7 +244,11 @@
 			hello.cookie = helloVerifyRequest.cookie
 			helloBytes = hello.marshal()
 			c.writeRecord(recordTypeHandshake, helloBytes)
+			c.dtlsFlushHandshake()
 
+			if err := c.simulatePacketLoss(nil); err != nil {
+				return err
+			}
 			msg, err = c.readHandshake()
 			if err != nil {
 				return err
@@ -317,6 +332,15 @@
 		if err := hs.sendFinished(isResume); err != nil {
 			return err
 		}
+		// Most retransmits are triggered by a timeout, but the final
+		// leg of the handshake is retransmited upon re-receiving a
+		// Finished.
+		if err := c.simulatePacketLoss(func() {
+			c.writeRecord(recordTypeHandshake, hs.finishedBytes)
+			c.dtlsFlushHandshake()
+		}); err != nil {
+			return err
+		}
 		if err := hs.readSessionTicket(); err != nil {
 			return err
 		}
@@ -331,7 +355,10 @@
 
 	c.didResume = isResume
 	c.handshakeComplete = true
-	c.cipherSuite = suite.id
+	c.cipherSuite = suite
+	copy(c.clientRandom[:], hs.hello.random)
+	copy(c.serverRandom[:], hs.serverHello.random)
+	copy(c.masterSecret[:], hs.masterSecret)
 	return nil
 }
 
@@ -559,33 +586,39 @@
 			hasSignatureAndHash: c.vers >= VersionTLS12,
 		}
 
+		// Determine the hash to sign.
+		var signatureType uint8
+		switch c.config.Certificates[0].PrivateKey.(type) {
+		case *ecdsa.PrivateKey:
+			signatureType = signatureECDSA
+		case *rsa.PrivateKey:
+			signatureType = signatureRSA
+		default:
+			c.sendAlert(alertInternalError)
+			return errors.New("unknown private key type")
+		}
+		if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
+			certReq.signatureAndHashes = c.config.signatureAndHashesForClient()
+		}
+		certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, c.config.signatureAndHashesForClient(), signatureType)
+		if err != nil {
+			c.sendAlert(alertInternalError)
+			return err
+		}
+		digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
+		if err != nil {
+			c.sendAlert(alertInternalError)
+			return err
+		}
+
 		switch key := c.config.Certificates[0].PrivateKey.(type) {
 		case *ecdsa.PrivateKey:
-			certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureECDSA)
-			if err != nil {
-				break
-			}
-			var digest []byte
-			digest, _, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
-			if err != nil {
-				break
-			}
 			var r, s *big.Int
 			r, s, err = ecdsa.Sign(c.config.rand(), key, digest)
 			if err == nil {
 				signed, err = asn1.Marshal(ecdsaSignature{r, s})
 			}
 		case *rsa.PrivateKey:
-			certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureRSA)
-			if err != nil {
-				break
-			}
-			var digest []byte
-			var hashFunc crypto.Hash
-			digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
-			if err != nil {
-				break
-			}
 			signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
 		default:
 			err = errors.New("unknown private key type")
@@ -599,6 +632,7 @@
 		hs.writeClientHash(certVerify.marshal())
 		c.writeRecord(recordTypeHandshake, certVerify.marshal())
 	}
+	c.dtlsFlushHandshake()
 
 	hs.finishedHash.discardHandshakeBuffer()
 
@@ -825,15 +859,19 @@
 	} else {
 		finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
 	}
+	if c.config.Bugs.BadFinished {
+		finished.verifyData[0]++
+	}
 	c.clientVerify = append(c.clientVerify[:0], finished.verifyData...)
-	finishedBytes := finished.marshal()
-	hs.writeHash(finishedBytes, seqno)
-	postCCSBytes = append(postCCSBytes, finishedBytes...)
+	hs.finishedBytes = finished.marshal()
+	hs.writeHash(hs.finishedBytes, seqno)
+	postCCSBytes = append(postCCSBytes, hs.finishedBytes...)
 
 	if c.config.Bugs.FragmentAcrossChangeCipherSpec {
 		c.writeRecord(recordTypeHandshake, postCCSBytes[:5])
 		postCCSBytes = postCCSBytes[5:]
 	}
+	c.dtlsFlushHandshake()
 
 	if !c.config.Bugs.SkipChangeCipherSpec &&
 		c.config.Bugs.EarlyChangeCipherSpec == 0 {
@@ -843,8 +881,15 @@
 	if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
 		c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec)
 	}
+	if c.config.Bugs.AlertAfterChangeCipherSpec != 0 {
+		c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec)
+		return errors.New("tls: simulating post-CCS alert")
+	}
 
-	c.writeRecord(recordTypeHandshake, postCCSBytes)
+	if !c.config.Bugs.SkipFinished {
+		c.writeRecord(recordTypeHandshake, postCCSBytes)
+		c.dtlsFlushHandshake()
+	}
 	return nil
 }
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 1234a57..59ed9df 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -33,6 +33,7 @@
 	masterSecret    []byte
 	certsFromClient [][]byte
 	cert            *Certificate
+	finishedBytes   []byte
 }
 
 // serverHandshake performs a TLS handshake as a server.
@@ -71,6 +72,15 @@
 		if err := hs.sendFinished(); err != nil {
 			return err
 		}
+		// Most retransmits are triggered by a timeout, but the final
+		// leg of the handshake is retransmited upon re-receiving a
+		// Finished.
+		if err := c.simulatePacketLoss(func() {
+			c.writeRecord(recordTypeHandshake, hs.finishedBytes)
+			c.dtlsFlushHandshake()
+		}); err != nil {
+			return err
+		}
 		if err := hs.readFinished(isResume); err != nil {
 			return err
 		}
@@ -87,9 +97,12 @@
 		if err := hs.readFinished(isResume); err != nil {
 			return err
 		}
+		if c.config.Bugs.AlertBeforeFalseStartTest != 0 {
+			c.sendAlert(c.config.Bugs.AlertBeforeFalseStartTest)
+		}
 		if c.config.Bugs.ExpectFalseStart {
 			if err := c.readRecord(recordTypeApplicationData); err != nil {
-				return err
+				return fmt.Errorf("tls: peer did not false start: %s", err)
 			}
 		}
 		if err := hs.sendSessionTicket(); err != nil {
@@ -100,6 +113,9 @@
 		}
 	}
 	c.handshakeComplete = true
+	copy(c.clientRandom[:], hs.clientHello.random)
+	copy(c.serverRandom[:], hs.hello.random)
+	copy(c.masterSecret[:], hs.masterSecret)
 
 	return nil
 }
@@ -110,6 +126,9 @@
 	config := hs.c.config
 	c := hs.c
 
+	if err := c.simulatePacketLoss(nil); err != nil {
+		return false, err
+	}
 	msg, err := c.readHandshake()
 	if err != nil {
 		return false, err
@@ -136,7 +155,11 @@
 			return false, errors.New("dtls: short read from Rand: " + err.Error())
 		}
 		c.writeRecord(recordTypeHandshake, helloVerifyRequest.marshal())
+		c.dtlsFlushHandshake()
 
+		if err := c.simulatePacketLoss(nil); err != nil {
+			return false, err
+		}
 		msg, err := c.readHandshake()
 		if err != nil {
 			return false, err
@@ -176,6 +199,9 @@
 	if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAndHashes) > 0 {
 		return false, fmt.Errorf("tls: client included signature_algorithms before TLS 1.2")
 	}
+	if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
+		hs.clientHello.signatureAndHashes = config.signatureAndHashesForServer()
+	}
 
 	c.vers, ok = config.mutualVersion(hs.clientHello.vers)
 	if !ok {
@@ -189,6 +215,9 @@
 
 	supportedCurve := false
 	preferredCurves := config.curvePreferences()
+	if config.Bugs.IgnorePeerCurvePreferences {
+		hs.clientHello.supportedCurves = preferredCurves
+	}
 Curves:
 	for _, curve := range hs.clientHello.supportedCurves {
 		for _, supported := range preferredCurves {
@@ -323,6 +352,9 @@
 		return false, errors.New("tls: fallback SCSV found when not expected")
 	}
 
+	if config.Bugs.IgnorePeerCipherPreferences {
+		hs.clientHello.cipherSuites = c.config.cipherSuites()
+	}
 	var preferenceList, supportedList []uint16
 	if c.config.PreferServerCipherSuites {
 		preferenceList = c.config.cipherSuites()
@@ -350,6 +382,10 @@
 func (hs *serverHandshakeState) checkForResumption() bool {
 	c := hs.c
 
+	if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil {
+		return false
+	}
+
 	if len(hs.clientHello.sessionTicket) > 0 {
 		if c.config.SessionTicketsDisabled {
 			return false
@@ -410,6 +446,9 @@
 	c := hs.c
 
 	hs.hello.cipherSuite = hs.suite.id
+	if c.config.Bugs.SendCipherSuite != 0 {
+		hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
+	}
 	// We echo the client's session ID in the ServerHello to let it know
 	// that we're doing a resumption.
 	hs.hello.sessionId = hs.clientHello.sessionId
@@ -473,12 +512,16 @@
 		certMsg := new(certificateMsg)
 		certMsg.certificates = hs.cert.Certificate
 		if !config.Bugs.UnauthenticatedECDH {
-			hs.writeServerHash(certMsg.marshal())
-			c.writeRecord(recordTypeHandshake, certMsg.marshal())
+			certMsgBytes := certMsg.marshal()
+			if config.Bugs.WrongCertificateMessageType {
+				certMsgBytes[0] += 42
+			}
+			hs.writeServerHash(certMsgBytes)
+			c.writeRecord(recordTypeHandshake, certMsgBytes)
 		}
 	}
 
-	if hs.hello.ocspStapling {
+	if hs.hello.ocspStapling && !c.config.Bugs.SkipCertificateStatus {
 		certStatus := new(certificateStatusMsg)
 		certStatus.statusType = statusTypeOCSP
 		certStatus.response = hs.cert.OCSPStaple
@@ -530,9 +573,13 @@
 	helloDone := new(serverHelloDoneMsg)
 	hs.writeServerHash(helloDone.marshal())
 	c.writeRecord(recordTypeHandshake, helloDone.marshal())
+	c.dtlsFlushHandshake()
 
 	var pub crypto.PublicKey // public key for client auth, if any
 
+	if err := c.simulatePacketLoss(nil); err != nil {
+		return err
+	}
 	msg, err := c.readHandshake()
 	if err != nil {
 		return err
@@ -811,14 +858,19 @@
 
 	finished := new(finishedMsg)
 	finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+	if c.config.Bugs.BadFinished {
+		finished.verifyData[0]++
+	}
 	c.serverVerify = append(c.serverVerify[:0], finished.verifyData...)
-	postCCSBytes := finished.marshal()
-	hs.writeServerHash(postCCSBytes)
+	hs.finishedBytes = finished.marshal()
+	hs.writeServerHash(hs.finishedBytes)
+	postCCSBytes := hs.finishedBytes
 
 	if c.config.Bugs.FragmentAcrossChangeCipherSpec {
 		c.writeRecord(recordTypeHandshake, postCCSBytes[:5])
 		postCCSBytes = postCCSBytes[5:]
 	}
+	c.dtlsFlushHandshake()
 
 	if !c.config.Bugs.SkipChangeCipherSpec {
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@@ -827,10 +879,17 @@
 	if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
 		c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec)
 	}
+	if c.config.Bugs.AlertAfterChangeCipherSpec != 0 {
+		c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec)
+		return errors.New("tls: simulating post-CCS alert")
+	}
 
-	c.writeRecord(recordTypeHandshake, postCCSBytes)
+	if !c.config.Bugs.SkipFinished {
+		c.writeRecord(recordTypeHandshake, postCCSBytes)
+		c.dtlsFlushHandshake()
+	}
 
-	c.cipherSuite = hs.suite.id
+	c.cipherSuite = hs.suite
 
 	return nil
 }
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 116dfd8..5e44b54 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -25,19 +25,73 @@
 // rsaKeyAgreement implements the standard TLS key agreement where the client
 // encrypts the pre-master secret to the server's public key.
 type rsaKeyAgreement struct {
+	version       uint16
 	clientVersion uint16
+	exportKey     *rsa.PrivateKey
 }
 
 func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
 	// Save the client version for comparison later.
 	ka.clientVersion = versionToWire(clientHello.vers, clientHello.isDTLS)
 
-	if config.Bugs.RSAServerKeyExchange {
-		// Send an empty ServerKeyExchange message.
-		return &serverKeyExchangeMsg{}, nil
+	if !config.Bugs.RSAEphemeralKey {
+		return nil, nil
 	}
 
-	return nil, nil
+	// Generate an ephemeral RSA key to use instead of the real
+	// one, as in RSA_EXPORT.
+	key, err := rsa.GenerateKey(config.rand(), 512)
+	if err != nil {
+		return nil, err
+	}
+	ka.exportKey = key
+
+	modulus := key.N.Bytes()
+	exponent := big.NewInt(int64(key.E)).Bytes()
+	serverRSAParams := make([]byte, 0, 2+len(modulus)+2+len(exponent))
+	serverRSAParams = append(serverRSAParams, byte(len(modulus)>>8), byte(len(modulus)))
+	serverRSAParams = append(serverRSAParams, modulus...)
+	serverRSAParams = append(serverRSAParams, byte(len(exponent)>>8), byte(len(exponent)))
+	serverRSAParams = append(serverRSAParams, exponent...)
+
+	var tls12HashId uint8
+	if ka.version >= VersionTLS12 {
+		if tls12HashId, err = pickTLS12HashForSignature(signatureRSA, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
+			return nil, err
+		}
+	}
+
+	digest, hashFunc, err := hashForServerKeyExchange(signatureRSA, tls12HashId, ka.version, clientHello.random, hello.random, serverRSAParams)
+	if err != nil {
+		return nil, err
+	}
+	privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("RSA ephemeral key requires an RSA server private key")
+	}
+	sig, err := rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
+	if err != nil {
+		return nil, errors.New("failed to sign RSA parameters: " + err.Error())
+	}
+
+	skx := new(serverKeyExchangeMsg)
+	sigAndHashLen := 0
+	if ka.version >= VersionTLS12 {
+		sigAndHashLen = 2
+	}
+	skx.key = make([]byte, len(serverRSAParams)+sigAndHashLen+2+len(sig))
+	copy(skx.key, serverRSAParams)
+	k := skx.key[len(serverRSAParams):]
+	if ka.version >= VersionTLS12 {
+		k[0] = tls12HashId
+		k[1] = signatureRSA
+		k = k[2:]
+	}
+	k[0] = byte(len(sig) >> 8)
+	k[1] = byte(len(sig))
+	copy(k[2:], sig)
+
+	return skx, nil
 }
 
 func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
@@ -60,7 +114,11 @@
 		ciphertext = ckx.ciphertext[2:]
 	}
 
-	err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
+	key := cert.PrivateKey.(*rsa.PrivateKey)
+	if ka.exportKey != nil {
+		key = ka.exportKey
+	}
+	err = rsa.DecryptPKCS1v15SessionKey(config.rand(), key, ciphertext, preMasterSecret)
 	if err != nil {
 		return nil, err
 	}
@@ -154,20 +212,19 @@
 // pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
 // ServerKeyExchange given the signature type being used and the client's
 // advertized list of supported signature and hash combinations.
-func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
-	if len(clientSignatureAndHashes) == 0 {
+func pickTLS12HashForSignature(sigType uint8, clientList, serverList []signatureAndHash) (uint8, error) {
+	if len(clientList) == 0 {
 		// If the client didn't specify any signature_algorithms
 		// extension then we can assume that it supports SHA1. See
 		// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
 		return hashSHA1, nil
 	}
 
-	for _, sigAndHash := range clientSignatureAndHashes {
+	for _, sigAndHash := range clientList {
 		if sigAndHash.signature != sigType {
 			continue
 		}
-		switch sigAndHash.hash {
-		case hashSHA1, hashSHA256:
+		if isSupportedSignatureAndHash(sigAndHash, serverList) {
 			return sigAndHash.hash, nil
 		}
 	}
@@ -177,6 +234,8 @@
 
 func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
 	switch id {
+	case CurveP224:
+		return elliptic.P224(), true
 	case CurveP256:
 		return elliptic.P256(), true
 	case CurveP384:
@@ -221,7 +280,7 @@
 	var tls12HashId uint8
 	var err error
 	if ka.version >= VersionTLS12 {
-		if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
+		if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
 			return nil, err
 		}
 	}
diff --git a/src/ssl/test/runner/packet_adapter.go b/src/ssl/test/runner/packet_adapter.go
index 671b413..bbcd388 100644
--- a/src/ssl/test/runner/packet_adapter.go
+++ b/src/ssl/test/runner/packet_adapter.go
@@ -6,52 +6,117 @@
 
 import (
 	"encoding/binary"
-	"errors"
+	"fmt"
+	"io"
 	"net"
+	"time"
 )
 
+// opcodePacket signals a packet, encoded with a 32-bit length prefix, followed
+// by the payload.
+const opcodePacket = byte('P')
+
+// opcodeTimeout signals a read timeout, encoded by a 64-bit number of
+// nanoseconds. On receipt, the peer should reply with
+// opcodeTimeoutAck. opcodeTimeout may only be sent by the Go side.
+const opcodeTimeout = byte('T')
+
+// opcodeTimeoutAck acknowledges a read timeout. This opcode has no payload and
+// may only be sent by the C side. Timeout ACKs act as a synchronization point
+// at the timeout, to bracket one flight of messages from C.
+const opcodeTimeoutAck = byte('t')
+
 type packetAdaptor struct {
 	net.Conn
 }
 
-// newPacketAdaptor wraps a reliable streaming net.Conn into a
-// reliable packet-based net.Conn. Every packet is encoded with a
-// 32-bit length prefix as a framing layer.
-func newPacketAdaptor(conn net.Conn) net.Conn {
+// newPacketAdaptor wraps a reliable streaming net.Conn into a reliable
+// packet-based net.Conn. The stream contains packets and control commands,
+// distinguished by a one byte opcode.
+func newPacketAdaptor(conn net.Conn) *packetAdaptor {
 	return &packetAdaptor{conn}
 }
 
-func (p *packetAdaptor) Read(b []byte) (int, error) {
-	var length uint32
-	if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil {
+func (p *packetAdaptor) readOpcode() (byte, error) {
+	out := make([]byte, 1)
+	if _, err := io.ReadFull(p.Conn, out); err != nil {
 		return 0, err
 	}
+	return out[0], nil
+}
+
+func (p *packetAdaptor) readPacketBody() ([]byte, error) {
+	var length uint32
+	if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil {
+		return nil, err
+	}
 	out := make([]byte, length)
-	n, err := p.Conn.Read(out)
+	if _, err := io.ReadFull(p.Conn, out); err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (p *packetAdaptor) Read(b []byte) (int, error) {
+	opcode, err := p.readOpcode()
 	if err != nil {
 		return 0, err
 	}
-	if n != int(length) {
-		return 0, errors.New("internal error: length mismatch!")
+	if opcode != opcodePacket {
+		return 0, fmt.Errorf("unexpected opcode '%d'", opcode)
+	}
+	out, err := p.readPacketBody()
+	if err != nil {
+		return 0, err
 	}
 	return copy(b, out), nil
 }
 
 func (p *packetAdaptor) Write(b []byte) (int, error) {
-	length := uint32(len(b))
-	if err := binary.Write(p.Conn, binary.BigEndian, length); err != nil {
+	payload := make([]byte, 1+4+len(b))
+	payload[0] = opcodePacket
+	binary.BigEndian.PutUint32(payload[1:5], uint32(len(b)))
+	copy(payload[5:], b)
+	if _, err := p.Conn.Write(payload); err != nil {
 		return 0, err
 	}
-	n, err := p.Conn.Write(b)
-	if err != nil {
-		return 0, err
-	}
-	if n != len(b) {
-		return 0, errors.New("internal error: length mismatch!")
-	}
 	return len(b), nil
 }
 
+// SendReadTimeout instructs the peer to simulate a read timeout. It then waits
+// for acknowledgement of the timeout, buffering any packets received since
+// then. The packets are then returned.
+func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) {
+	payload := make([]byte, 1+8)
+	payload[0] = opcodeTimeout
+	binary.BigEndian.PutUint64(payload[1:], uint64(d.Nanoseconds()))
+	if _, err := p.Conn.Write(payload); err != nil {
+		return nil, err
+	}
+
+	var packets [][]byte
+	for {
+		opcode, err := p.readOpcode()
+		if err != nil {
+			return nil, err
+		}
+		switch opcode {
+		case opcodeTimeoutAck:
+			// Done! Return the packets buffered and continue.
+			return packets, nil
+		case opcodePacket:
+			// Buffer the packet for the caller to process.
+			packet, err := p.readPacketBody()
+			if err != nil {
+				return nil, err
+			}
+			packets = append(packets, packet)
+		default:
+			return nil, fmt.Errorf("unexpected opcode '%d'", opcode)
+		}
+	}
+}
+
 type replayAdaptor struct {
 	net.Conn
 	prevWrite []byte
diff --git a/src/ssl/test/runner/poly1305.go b/src/ssl/test/runner/poly1305.go
new file mode 100644
index 0000000..51a1009
--- /dev/null
+++ b/src/ssl/test/runner/poly1305.go
@@ -0,0 +1,1540 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Based on original, public domain implementation from NaCl by D. J.
+// Bernstein.
+
+import (
+	"crypto/subtle"
+	"math"
+)
+
+const (
+	alpham80 = 0.00000000558793544769287109375
+	alpham48 = 24.0
+	alpham16 = 103079215104.0
+	alpha0   = 6755399441055744.0
+	alpha18  = 1770887431076116955136.0
+	alpha32  = 29014219670751100192948224.0
+	alpha50  = 7605903601369376408980219232256.0
+	alpha64  = 124615124604835863084731911901282304.0
+	alpha82  = 32667107224410092492483962313449748299776.0
+	alpha96  = 535217884764734955396857238543560676143529984.0
+	alpha112 = 35076039295941670036888435985190792471742381031424.0
+	alpha130 = 9194973245195333150150082162901855101712434733101613056.0
+	scale    = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125
+	offset0  = 6755408030990331.0
+	offset1  = 29014256564239239022116864.0
+	offset2  = 124615283061160854719918951570079744.0
+	offset3  = 535219245894202480694386063513315216128475136.0
+)
+
+// poly1305Verify returns true if mac is a valid authenticator for m with the
+// given key.
+func poly1305Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+	var tmp [16]byte
+	poly1305Sum(&tmp, m, key)
+	return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
+
+// poly1305Sum generates an authenticator for m using a one-time key and puts
+// the 16-byte result into out. Authenticating two different messages with the
+// same key allows an attacker to forge messages at will.
+func poly1305Sum(out *[16]byte, m []byte, key *[32]byte) {
+	r := key
+	s := key[16:]
+	var (
+		y7        float64
+		y6        float64
+		y1        float64
+		y0        float64
+		y5        float64
+		y4        float64
+		x7        float64
+		x6        float64
+		x1        float64
+		x0        float64
+		y3        float64
+		y2        float64
+		x5        float64
+		r3lowx0   float64
+		x4        float64
+		r0lowx6   float64
+		x3        float64
+		r3highx0  float64
+		x2        float64
+		r0highx6  float64
+		r0lowx0   float64
+		sr1lowx6  float64
+		r0highx0  float64
+		sr1highx6 float64
+		sr3low    float64
+		r1lowx0   float64
+		sr2lowx6  float64
+		r1highx0  float64
+		sr2highx6 float64
+		r2lowx0   float64
+		sr3lowx6  float64
+		r2highx0  float64
+		sr3highx6 float64
+		r1highx4  float64
+		r1lowx4   float64
+		r0highx4  float64
+		r0lowx4   float64
+		sr3highx4 float64
+		sr3lowx4  float64
+		sr2highx4 float64
+		sr2lowx4  float64
+		r0lowx2   float64
+		r0highx2  float64
+		r1lowx2   float64
+		r1highx2  float64
+		r2lowx2   float64
+		r2highx2  float64
+		sr3lowx2  float64
+		sr3highx2 float64
+		z0        float64
+		z1        float64
+		z2        float64
+		z3        float64
+		m0        int64
+		m1        int64
+		m2        int64
+		m3        int64
+		m00       uint32
+		m01       uint32
+		m02       uint32
+		m03       uint32
+		m10       uint32
+		m11       uint32
+		m12       uint32
+		m13       uint32
+		m20       uint32
+		m21       uint32
+		m22       uint32
+		m23       uint32
+		m30       uint32
+		m31       uint32
+		m32       uint32
+		m33       uint64
+		lbelow2   int32
+		lbelow3   int32
+		lbelow4   int32
+		lbelow5   int32
+		lbelow6   int32
+		lbelow7   int32
+		lbelow8   int32
+		lbelow9   int32
+		lbelow10  int32
+		lbelow11  int32
+		lbelow12  int32
+		lbelow13  int32
+		lbelow14  int32
+		lbelow15  int32
+		s00       uint32
+		s01       uint32
+		s02       uint32
+		s03       uint32
+		s10       uint32
+		s11       uint32
+		s12       uint32
+		s13       uint32
+		s20       uint32
+		s21       uint32
+		s22       uint32
+		s23       uint32
+		s30       uint32
+		s31       uint32
+		s32       uint32
+		s33       uint32
+		bits32    uint64
+		f         uint64
+		f0        uint64
+		f1        uint64
+		f2        uint64
+		f3        uint64
+		f4        uint64
+		g         uint64
+		g0        uint64
+		g1        uint64
+		g2        uint64
+		g3        uint64
+		g4        uint64
+	)
+
+	var p int32
+
+	l := int32(len(m))
+
+	r00 := uint32(r[0])
+
+	r01 := uint32(r[1])
+
+	r02 := uint32(r[2])
+	r0 := int64(2151)
+
+	r03 := uint32(r[3])
+	r03 &= 15
+	r0 <<= 51
+
+	r10 := uint32(r[4])
+	r10 &= 252
+	r01 <<= 8
+	r0 += int64(r00)
+
+	r11 := uint32(r[5])
+	r02 <<= 16
+	r0 += int64(r01)
+
+	r12 := uint32(r[6])
+	r03 <<= 24
+	r0 += int64(r02)
+
+	r13 := uint32(r[7])
+	r13 &= 15
+	r1 := int64(2215)
+	r0 += int64(r03)
+
+	d0 := r0
+	r1 <<= 51
+	r2 := int64(2279)
+
+	r20 := uint32(r[8])
+	r20 &= 252
+	r11 <<= 8
+	r1 += int64(r10)
+
+	r21 := uint32(r[9])
+	r12 <<= 16
+	r1 += int64(r11)
+
+	r22 := uint32(r[10])
+	r13 <<= 24
+	r1 += int64(r12)
+
+	r23 := uint32(r[11])
+	r23 &= 15
+	r2 <<= 51
+	r1 += int64(r13)
+
+	d1 := r1
+	r21 <<= 8
+	r2 += int64(r20)
+
+	r30 := uint32(r[12])
+	r30 &= 252
+	r22 <<= 16
+	r2 += int64(r21)
+
+	r31 := uint32(r[13])
+	r23 <<= 24
+	r2 += int64(r22)
+
+	r32 := uint32(r[14])
+	r2 += int64(r23)
+	r3 := int64(2343)
+
+	d2 := r2
+	r3 <<= 51
+
+	r33 := uint32(r[15])
+	r33 &= 15
+	r31 <<= 8
+	r3 += int64(r30)
+
+	r32 <<= 16
+	r3 += int64(r31)
+
+	r33 <<= 24
+	r3 += int64(r32)
+
+	r3 += int64(r33)
+	h0 := alpha32 - alpha32
+
+	d3 := r3
+	h1 := alpha32 - alpha32
+
+	h2 := alpha32 - alpha32
+
+	h3 := alpha32 - alpha32
+
+	h4 := alpha32 - alpha32
+
+	r0low := math.Float64frombits(uint64(d0))
+	h5 := alpha32 - alpha32
+
+	r1low := math.Float64frombits(uint64(d1))
+	h6 := alpha32 - alpha32
+
+	r2low := math.Float64frombits(uint64(d2))
+	h7 := alpha32 - alpha32
+
+	r0low -= alpha0
+
+	r1low -= alpha32
+
+	r2low -= alpha64
+
+	r0high := r0low + alpha18
+
+	r3low := math.Float64frombits(uint64(d3))
+
+	r1high := r1low + alpha50
+	sr1low := scale * r1low
+
+	r2high := r2low + alpha82
+	sr2low := scale * r2low
+
+	r0high -= alpha18
+	r0high_stack := r0high
+
+	r3low -= alpha96
+
+	r1high -= alpha50
+	r1high_stack := r1high
+
+	sr1high := sr1low + alpham80
+
+	r0low -= r0high
+
+	r2high -= alpha82
+	sr3low = scale * r3low
+
+	sr2high := sr2low + alpham48
+
+	r1low -= r1high
+	r1low_stack := r1low
+
+	sr1high -= alpham80
+	sr1high_stack := sr1high
+
+	r2low -= r2high
+	r2low_stack := r2low
+
+	sr2high -= alpham48
+	sr2high_stack := sr2high
+
+	r3high := r3low + alpha112
+	r0low_stack := r0low
+
+	sr1low -= sr1high
+	sr1low_stack := sr1low
+
+	sr3high := sr3low + alpham16
+	r2high_stack := r2high
+
+	sr2low -= sr2high
+	sr2low_stack := sr2low
+
+	r3high -= alpha112
+	r3high_stack := r3high
+
+	sr3high -= alpham16
+	sr3high_stack := sr3high
+
+	r3low -= r3high
+	r3low_stack := r3low
+
+	sr3low -= sr3high
+	sr3low_stack := sr3low
+
+	if l < 16 {
+		goto addatmost15bytes
+	}
+
+	m00 = uint32(m[p+0])
+	m0 = 2151
+
+	m0 <<= 51
+	m1 = 2215
+	m01 = uint32(m[p+1])
+
+	m1 <<= 51
+	m2 = 2279
+	m02 = uint32(m[p+2])
+
+	m2 <<= 51
+	m3 = 2343
+	m03 = uint32(m[p+3])
+
+	m10 = uint32(m[p+4])
+	m01 <<= 8
+	m0 += int64(m00)
+
+	m11 = uint32(m[p+5])
+	m02 <<= 16
+	m0 += int64(m01)
+
+	m12 = uint32(m[p+6])
+	m03 <<= 24
+	m0 += int64(m02)
+
+	m13 = uint32(m[p+7])
+	m3 <<= 51
+	m0 += int64(m03)
+
+	m20 = uint32(m[p+8])
+	m11 <<= 8
+	m1 += int64(m10)
+
+	m21 = uint32(m[p+9])
+	m12 <<= 16
+	m1 += int64(m11)
+
+	m22 = uint32(m[p+10])
+	m13 <<= 24
+	m1 += int64(m12)
+
+	m23 = uint32(m[p+11])
+	m1 += int64(m13)
+
+	m30 = uint32(m[p+12])
+	m21 <<= 8
+	m2 += int64(m20)
+
+	m31 = uint32(m[p+13])
+	m22 <<= 16
+	m2 += int64(m21)
+
+	m32 = uint32(m[p+14])
+	m23 <<= 24
+	m2 += int64(m22)
+
+	m33 = uint64(m[p+15])
+	m2 += int64(m23)
+
+	d0 = m0
+	m31 <<= 8
+	m3 += int64(m30)
+
+	d1 = m1
+	m32 <<= 16
+	m3 += int64(m31)
+
+	d2 = m2
+	m33 += 256
+
+	m33 <<= 24
+	m3 += int64(m32)
+
+	m3 += int64(m33)
+	d3 = m3
+
+	p += 16
+	l -= 16
+
+	z0 = math.Float64frombits(uint64(d0))
+
+	z1 = math.Float64frombits(uint64(d1))
+
+	z2 = math.Float64frombits(uint64(d2))
+
+	z3 = math.Float64frombits(uint64(d3))
+
+	z0 -= alpha0
+
+	z1 -= alpha32
+
+	z2 -= alpha64
+
+	z3 -= alpha96
+
+	h0 += z0
+
+	h1 += z1
+
+	h3 += z2
+
+	h5 += z3
+
+	if l < 16 {
+		goto multiplyaddatmost15bytes
+	}
+
+multiplyaddatleast16bytes:
+
+	m2 = 2279
+	m20 = uint32(m[p+8])
+	y7 = h7 + alpha130
+
+	m2 <<= 51
+	m3 = 2343
+	m21 = uint32(m[p+9])
+	y6 = h6 + alpha130
+
+	m3 <<= 51
+	m0 = 2151
+	m22 = uint32(m[p+10])
+	y1 = h1 + alpha32
+
+	m0 <<= 51
+	m1 = 2215
+	m23 = uint32(m[p+11])
+	y0 = h0 + alpha32
+
+	m1 <<= 51
+	m30 = uint32(m[p+12])
+	y7 -= alpha130
+
+	m21 <<= 8
+	m2 += int64(m20)
+	m31 = uint32(m[p+13])
+	y6 -= alpha130
+
+	m22 <<= 16
+	m2 += int64(m21)
+	m32 = uint32(m[p+14])
+	y1 -= alpha32
+
+	m23 <<= 24
+	m2 += int64(m22)
+	m33 = uint64(m[p+15])
+	y0 -= alpha32
+
+	m2 += int64(m23)
+	m00 = uint32(m[p+0])
+	y5 = h5 + alpha96
+
+	m31 <<= 8
+	m3 += int64(m30)
+	m01 = uint32(m[p+1])
+	y4 = h4 + alpha96
+
+	m32 <<= 16
+	m02 = uint32(m[p+2])
+	x7 = h7 - y7
+	y7 *= scale
+
+	m33 += 256
+	m03 = uint32(m[p+3])
+	x6 = h6 - y6
+	y6 *= scale
+
+	m33 <<= 24
+	m3 += int64(m31)
+	m10 = uint32(m[p+4])
+	x1 = h1 - y1
+
+	m01 <<= 8
+	m3 += int64(m32)
+	m11 = uint32(m[p+5])
+	x0 = h0 - y0
+
+	m3 += int64(m33)
+	m0 += int64(m00)
+	m12 = uint32(m[p+6])
+	y5 -= alpha96
+
+	m02 <<= 16
+	m0 += int64(m01)
+	m13 = uint32(m[p+7])
+	y4 -= alpha96
+
+	m03 <<= 24
+	m0 += int64(m02)
+	d2 = m2
+	x1 += y7
+
+	m0 += int64(m03)
+	d3 = m3
+	x0 += y6
+
+	m11 <<= 8
+	m1 += int64(m10)
+	d0 = m0
+	x7 += y5
+
+	m12 <<= 16
+	m1 += int64(m11)
+	x6 += y4
+
+	m13 <<= 24
+	m1 += int64(m12)
+	y3 = h3 + alpha64
+
+	m1 += int64(m13)
+	d1 = m1
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+	z2 = math.Float64frombits(uint64(d2))
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+	z3 = math.Float64frombits(uint64(d3))
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+	z2 -= alpha64
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+	z3 -= alpha96
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	p += 16
+	l -= 16
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	z1 = math.Float64frombits(uint64(d1))
+	h0 += sr3lowx2
+
+	z0 = math.Float64frombits(uint64(d0))
+	h1 += sr3highx2
+
+	z1 -= alpha32
+
+	z0 -= alpha0
+
+	h5 += z3
+
+	h3 += z2
+
+	h1 += z1
+
+	h0 += z0
+
+	if l >= 16 {
+		goto multiplyaddatleast16bytes
+	}
+
+multiplyaddatmost15bytes:
+
+	y7 = h7 + alpha130
+
+	y6 = h6 + alpha130
+
+	y1 = h1 + alpha32
+
+	y0 = h0 + alpha32
+
+	y7 -= alpha130
+
+	y6 -= alpha130
+
+	y1 -= alpha32
+
+	y0 -= alpha32
+
+	y5 = h5 + alpha96
+
+	y4 = h4 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	x6 = h6 - y6
+	y6 *= scale
+
+	x1 = h1 - y1
+
+	x0 = h0 - y0
+
+	y5 -= alpha96
+
+	y4 -= alpha96
+
+	x1 += y7
+
+	x0 += y6
+
+	x7 += y5
+
+	x6 += y4
+
+	y3 = h3 + alpha64
+
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	h0 += sr3lowx2
+
+	h1 += sr3highx2
+
+addatmost15bytes:
+
+	if l == 0 {
+		goto nomorebytes
+	}
+
+	lbelow2 = l - 2
+
+	lbelow3 = l - 3
+
+	lbelow2 >>= 31
+	lbelow4 = l - 4
+
+	m00 = uint32(m[p+0])
+	lbelow3 >>= 31
+	p += lbelow2
+
+	m01 = uint32(m[p+1])
+	lbelow4 >>= 31
+	p += lbelow3
+
+	m02 = uint32(m[p+2])
+	p += lbelow4
+	m0 = 2151
+
+	m03 = uint32(m[p+3])
+	m0 <<= 51
+	m1 = 2215
+
+	m0 += int64(m00)
+	m01 &^= uint32(lbelow2)
+
+	m02 &^= uint32(lbelow3)
+	m01 -= uint32(lbelow2)
+
+	m01 <<= 8
+	m03 &^= uint32(lbelow4)
+
+	m0 += int64(m01)
+	lbelow2 -= lbelow3
+
+	m02 += uint32(lbelow2)
+	lbelow3 -= lbelow4
+
+	m02 <<= 16
+	m03 += uint32(lbelow3)
+
+	m03 <<= 24
+	m0 += int64(m02)
+
+	m0 += int64(m03)
+	lbelow5 = l - 5
+
+	lbelow6 = l - 6
+	lbelow7 = l - 7
+
+	lbelow5 >>= 31
+	lbelow8 = l - 8
+
+	lbelow6 >>= 31
+	p += lbelow5
+
+	m10 = uint32(m[p+4])
+	lbelow7 >>= 31
+	p += lbelow6
+
+	m11 = uint32(m[p+5])
+	lbelow8 >>= 31
+	p += lbelow7
+
+	m12 = uint32(m[p+6])
+	m1 <<= 51
+	p += lbelow8
+
+	m13 = uint32(m[p+7])
+	m10 &^= uint32(lbelow5)
+	lbelow4 -= lbelow5
+
+	m10 += uint32(lbelow4)
+	lbelow5 -= lbelow6
+
+	m11 &^= uint32(lbelow6)
+	m11 += uint32(lbelow5)
+
+	m11 <<= 8
+	m1 += int64(m10)
+
+	m1 += int64(m11)
+	m12 &^= uint32(lbelow7)
+
+	lbelow6 -= lbelow7
+	m13 &^= uint32(lbelow8)
+
+	m12 += uint32(lbelow6)
+	lbelow7 -= lbelow8
+
+	m12 <<= 16
+	m13 += uint32(lbelow7)
+
+	m13 <<= 24
+	m1 += int64(m12)
+
+	m1 += int64(m13)
+	m2 = 2279
+
+	lbelow9 = l - 9
+	m3 = 2343
+
+	lbelow10 = l - 10
+	lbelow11 = l - 11
+
+	lbelow9 >>= 31
+	lbelow12 = l - 12
+
+	lbelow10 >>= 31
+	p += lbelow9
+
+	m20 = uint32(m[p+8])
+	lbelow11 >>= 31
+	p += lbelow10
+
+	m21 = uint32(m[p+9])
+	lbelow12 >>= 31
+	p += lbelow11
+
+	m22 = uint32(m[p+10])
+	m2 <<= 51
+	p += lbelow12
+
+	m23 = uint32(m[p+11])
+	m20 &^= uint32(lbelow9)
+	lbelow8 -= lbelow9
+
+	m20 += uint32(lbelow8)
+	lbelow9 -= lbelow10
+
+	m21 &^= uint32(lbelow10)
+	m21 += uint32(lbelow9)
+
+	m21 <<= 8
+	m2 += int64(m20)
+
+	m2 += int64(m21)
+	m22 &^= uint32(lbelow11)
+
+	lbelow10 -= lbelow11
+	m23 &^= uint32(lbelow12)
+
+	m22 += uint32(lbelow10)
+	lbelow11 -= lbelow12
+
+	m22 <<= 16
+	m23 += uint32(lbelow11)
+
+	m23 <<= 24
+	m2 += int64(m22)
+
+	m3 <<= 51
+	lbelow13 = l - 13
+
+	lbelow13 >>= 31
+	lbelow14 = l - 14
+
+	lbelow14 >>= 31
+	p += lbelow13
+	lbelow15 = l - 15
+
+	m30 = uint32(m[p+12])
+	lbelow15 >>= 31
+	p += lbelow14
+
+	m31 = uint32(m[p+13])
+	p += lbelow15
+	m2 += int64(m23)
+
+	m32 = uint32(m[p+14])
+	m30 &^= uint32(lbelow13)
+	lbelow12 -= lbelow13
+
+	m30 += uint32(lbelow12)
+	lbelow13 -= lbelow14
+
+	m3 += int64(m30)
+	m31 &^= uint32(lbelow14)
+
+	m31 += uint32(lbelow13)
+	m32 &^= uint32(lbelow15)
+
+	m31 <<= 8
+	lbelow14 -= lbelow15
+
+	m3 += int64(m31)
+	m32 += uint32(lbelow14)
+	d0 = m0
+
+	m32 <<= 16
+	m33 = uint64(lbelow15 + 1)
+	d1 = m1
+
+	m33 <<= 24
+	m3 += int64(m32)
+	d2 = m2
+
+	m3 += int64(m33)
+	d3 = m3
+
+	z3 = math.Float64frombits(uint64(d3))
+
+	z2 = math.Float64frombits(uint64(d2))
+
+	z1 = math.Float64frombits(uint64(d1))
+
+	z0 = math.Float64frombits(uint64(d0))
+
+	z3 -= alpha96
+
+	z2 -= alpha64
+
+	z1 -= alpha32
+
+	z0 -= alpha0
+
+	h5 += z3
+
+	h3 += z2
+
+	h1 += z1
+
+	h0 += z0
+
+	y7 = h7 + alpha130
+
+	y6 = h6 + alpha130
+
+	y1 = h1 + alpha32
+
+	y0 = h0 + alpha32
+
+	y7 -= alpha130
+
+	y6 -= alpha130
+
+	y1 -= alpha32
+
+	y0 -= alpha32
+
+	y5 = h5 + alpha96
+
+	y4 = h4 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	x6 = h6 - y6
+	y6 *= scale
+
+	x1 = h1 - y1
+
+	x0 = h0 - y0
+
+	y5 -= alpha96
+
+	y4 -= alpha96
+
+	x1 += y7
+
+	x0 += y6
+
+	x7 += y5
+
+	x6 += y4
+
+	y3 = h3 + alpha64
+
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	h0 += sr3lowx2
+
+	h1 += sr3highx2
+
+nomorebytes:
+
+	y7 = h7 + alpha130
+
+	y0 = h0 + alpha32
+
+	y1 = h1 + alpha32
+
+	y2 = h2 + alpha64
+
+	y7 -= alpha130
+
+	y3 = h3 + alpha64
+
+	y4 = h4 + alpha96
+
+	y5 = h5 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	y0 -= alpha32
+
+	y1 -= alpha32
+
+	y2 -= alpha64
+
+	h6 += x7
+
+	y3 -= alpha64
+
+	y4 -= alpha96
+
+	y5 -= alpha96
+
+	y6 = h6 + alpha130
+
+	x0 = h0 - y0
+
+	x1 = h1 - y1
+
+	x2 = h2 - y2
+
+	y6 -= alpha130
+
+	x0 += y7
+
+	x3 = h3 - y3
+
+	x4 = h4 - y4
+
+	x5 = h5 - y5
+
+	x6 = h6 - y6
+
+	y6 *= scale
+
+	x2 += y0
+
+	x3 += y1
+
+	x4 += y2
+
+	x0 += y6
+
+	x5 += y3
+
+	x6 += y4
+
+	x2 += x3
+
+	x0 += x1
+
+	x4 += x5
+
+	x6 += y5
+
+	x2 += offset1
+	d1 = int64(math.Float64bits(x2))
+
+	x0 += offset0
+	d0 = int64(math.Float64bits(x0))
+
+	x4 += offset2
+	d2 = int64(math.Float64bits(x4))
+
+	x6 += offset3
+	d3 = int64(math.Float64bits(x6))
+
+	f0 = uint64(d0)
+
+	f1 = uint64(d1)
+	bits32 = math.MaxUint64
+
+	f2 = uint64(d2)
+	bits32 >>= 32
+
+	f3 = uint64(d3)
+	f = f0 >> 32
+
+	f0 &= bits32
+	f &= 255
+
+	f1 += f
+	g0 = f0 + 5
+
+	g = g0 >> 32
+	g0 &= bits32
+
+	f = f1 >> 32
+	f1 &= bits32
+
+	f &= 255
+	g1 = f1 + g
+
+	g = g1 >> 32
+	f2 += f
+
+	f = f2 >> 32
+	g1 &= bits32
+
+	f2 &= bits32
+	f &= 255
+
+	f3 += f
+	g2 = f2 + g
+
+	g = g2 >> 32
+	g2 &= bits32
+
+	f4 = f3 >> 32
+	f3 &= bits32
+
+	f4 &= 255
+	g3 = f3 + g
+
+	g = g3 >> 32
+	g3 &= bits32
+
+	g4 = f4 + g
+
+	g4 = g4 - 4
+	s00 = uint32(s[0])
+
+	f = uint64(int64(g4) >> 63)
+	s01 = uint32(s[1])
+
+	f0 &= f
+	g0 &^= f
+	s02 = uint32(s[2])
+
+	f1 &= f
+	f0 |= g0
+	s03 = uint32(s[3])
+
+	g1 &^= f
+	f2 &= f
+	s10 = uint32(s[4])
+
+	f3 &= f
+	g2 &^= f
+	s11 = uint32(s[5])
+
+	g3 &^= f
+	f1 |= g1
+	s12 = uint32(s[6])
+
+	f2 |= g2
+	f3 |= g3
+	s13 = uint32(s[7])
+
+	s01 <<= 8
+	f0 += uint64(s00)
+	s20 = uint32(s[8])
+
+	s02 <<= 16
+	f0 += uint64(s01)
+	s21 = uint32(s[9])
+
+	s03 <<= 24
+	f0 += uint64(s02)
+	s22 = uint32(s[10])
+
+	s11 <<= 8
+	f1 += uint64(s10)
+	s23 = uint32(s[11])
+
+	s12 <<= 16
+	f1 += uint64(s11)
+	s30 = uint32(s[12])
+
+	s13 <<= 24
+	f1 += uint64(s12)
+	s31 = uint32(s[13])
+
+	f0 += uint64(s03)
+	f1 += uint64(s13)
+	s32 = uint32(s[14])
+
+	s21 <<= 8
+	f2 += uint64(s20)
+	s33 = uint32(s[15])
+
+	s22 <<= 16
+	f2 += uint64(s21)
+
+	s23 <<= 24
+	f2 += uint64(s22)
+
+	s31 <<= 8
+	f3 += uint64(s30)
+
+	s32 <<= 16
+	f3 += uint64(s31)
+
+	s33 <<= 24
+	f3 += uint64(s32)
+
+	f2 += uint64(s23)
+	f3 += uint64(s33)
+
+	out[0] = byte(f0)
+	f0 >>= 8
+	out[1] = byte(f0)
+	f0 >>= 8
+	out[2] = byte(f0)
+	f0 >>= 8
+	out[3] = byte(f0)
+	f0 >>= 8
+	f1 += f0
+
+	out[4] = byte(f1)
+	f1 >>= 8
+	out[5] = byte(f1)
+	f1 >>= 8
+	out[6] = byte(f1)
+	f1 >>= 8
+	out[7] = byte(f1)
+	f1 >>= 8
+	f2 += f1
+
+	out[8] = byte(f2)
+	f2 >>= 8
+	out[9] = byte(f2)
+	f2 >>= 8
+	out[10] = byte(f2)
+	f2 >>= 8
+	out[11] = byte(f2)
+	f2 >>= 8
+	f3 += f2
+
+	out[12] = byte(f3)
+	f3 >>= 8
+	out[13] = byte(f3)
+	f3 >>= 8
+	out[14] = byte(f3)
+	f3 >>= 8
+	out[15] = byte(f3)
+}
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index 75a8933..d445e76 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -323,14 +323,14 @@
 
 // selectClientCertSignatureAlgorithm returns a signatureAndHash to sign a
 // client's CertificateVerify with, or an error if none can be found.
-func (h finishedHash) selectClientCertSignatureAlgorithm(serverList []signatureAndHash, sigType uint8) (signatureAndHash, error) {
+func (h finishedHash) selectClientCertSignatureAlgorithm(serverList, clientList []signatureAndHash, sigType uint8) (signatureAndHash, error) {
 	if h.version < VersionTLS12 {
 		// Nothing to negotiate before TLS 1.2.
 		return signatureAndHash{signature: sigType}, nil
 	}
 
 	for _, v := range serverList {
-		if v.signature == sigType && v.hash == hashSHA256 {
+		if v.signature == sigType && isSupportedSignatureAndHash(v, clientList) {
 			return v, nil
 		}
 	}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index aaa2a4d..ec2fede 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -20,14 +20,17 @@
 	"strings"
 	"sync"
 	"syscall"
+	"time"
 )
 
 var (
-	useValgrind            = flag.Bool("valgrind", false, "If true, run code under valgrind")
-	useGDB                 = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
-	flagDebug       *bool  = flag.Bool("debug", false, "Hexdump the contents of the connection")
-	mallocTest      *int64 = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
-	mallocTestDebug *bool  = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
+	useValgrind     = flag.Bool("valgrind", false, "If true, run code under valgrind")
+	useGDB          = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+	flagDebug       = flag.Bool("debug", false, "Hexdump the contents of the connection")
+	mallocTest      = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
+	mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
+	jsonOutput      = flag.String("json-output", "", "The file to output JSON results to.")
+	pipe            = flag.Bool("pipe", false, "If true, print status output suitable for piping into another program.")
 )
 
 const (
@@ -132,6 +135,9 @@
 	// expectedResumeVersion, if non-zero, specifies the TLS version that
 	// must be negotiated on resumption. If zero, expectedVersion is used.
 	expectedResumeVersion uint16
+	// expectedCipher, if non-zero, specifies the TLS cipher suite that
+	// should be negotiated.
+	expectedCipher uint16
 	// expectChannelID controls whether the connection should have
 	// negotiated a Channel ID with channelIDKey.
 	expectChannelID bool
@@ -181,6 +187,12 @@
 	// damageFirstWrite, if true, configures the underlying transport to
 	// damage the final byte of the first application data write.
 	damageFirstWrite bool
+	// exportKeyingMaterial, if non-zero, configures the test to exchange
+	// keying material and verify they match.
+	exportKeyingMaterial int
+	exportLabel          string
+	exportContext        string
+	useExportContext     bool
 	// flags, if not empty, contains a list of command-line flags that will
 	// be passed to the shim program.
 	flags []string
@@ -292,6 +304,18 @@
 		expectedError: ":UNEXPECTED_MESSAGE:",
 	},
 	{
+		name: "SkipCertificateStatus",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				SkipCertificateStatus: true,
+			},
+		},
+		flags: []string{
+			"-enable-ocsp-stapling",
+		},
+	},
+	{
 		name: "SkipServerKeyExchange",
 		config: Config{
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
@@ -376,11 +400,47 @@
 	},
 	{
 		testType: serverTest,
+		name:     "Alert",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendSpuriousAlert: alertRecordOverflow,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+	},
+	{
+		protocol: dtls,
+		testType: serverTest,
+		name:     "Alert-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendSpuriousAlert: alertRecordOverflow,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+	},
+	{
+		testType: serverTest,
 		name:     "FragmentAlert",
 		config: Config{
 			Bugs: ProtocolBugs{
 				FragmentAlert:     true,
-				SendSpuriousAlert: true,
+				SendSpuriousAlert: alertRecordOverflow,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":BAD_ALERT:",
+	},
+	{
+		protocol: dtls,
+		testType: serverTest,
+		name:     "FragmentAlert-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				FragmentAlert:     true,
+				SendSpuriousAlert: alertRecordOverflow,
 			},
 		},
 		shouldFail:    true,
@@ -536,11 +596,11 @@
 		expectedError: ":WRONG_CIPHER_RETURNED:",
 	},
 	{
-		name: "RSAServerKeyExchange",
+		name: "RSAEphemeralKey",
 		config: Config{
 			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
 			Bugs: ProtocolBugs{
-				RSAServerKeyExchange: true,
+				RSAEphemeralKey: true,
 			},
 		},
 		shouldFail:    true,
@@ -650,6 +710,380 @@
 				AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"),
 			},
 		},
+		// BoringSSL's DTLS implementation will drop the out-of-order
+		// application data.
+	},
+	{
+		name: "AlertAfterChangeCipherSpec",
+		config: Config{
+			Bugs: ProtocolBugs{
+				AlertAfterChangeCipherSpec: alertRecordOverflow,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+	},
+	{
+		protocol: dtls,
+		name:     "AlertAfterChangeCipherSpec-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				AlertAfterChangeCipherSpec: alertRecordOverflow,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:",
+	},
+	{
+		protocol: dtls,
+		name:     "ReorderHandshakeFragments-Small-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				ReorderHandshakeFragments: true,
+				// Small enough that every handshake message is
+				// fragmented.
+				MaxHandshakeRecordLength: 2,
+			},
+		},
+	},
+	{
+		protocol: dtls,
+		name:     "ReorderHandshakeFragments-Large-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				ReorderHandshakeFragments: true,
+				// Large enough that no handshake message is
+				// fragmented.
+				MaxHandshakeRecordLength: 2048,
+			},
+		},
+	},
+	{
+		protocol: dtls,
+		name:     "MixCompleteMessageWithFragments-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				ReorderHandshakeFragments:       true,
+				MixCompleteMessageWithFragments: true,
+				MaxHandshakeRecordLength:        2,
+			},
+		},
+	},
+	{
+		name: "SendInvalidRecordType",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendInvalidRecordType: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_RECORD:",
+	},
+	{
+		protocol: dtls,
+		name:     "SendInvalidRecordType-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendInvalidRecordType: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_RECORD:",
+	},
+	{
+		name: "FalseStart-SkipServerSecondLeg",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				SkipNewSessionTicket: true,
+				SkipChangeCipherSpec: true,
+				SkipFinished:         true,
+				ExpectFalseStart:     true,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-handshake-never-done",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst: true,
+		shouldFail:      true,
+		expectedError:   ":UNEXPECTED_RECORD:",
+	},
+	{
+		name: "FalseStart-SkipServerSecondLeg-Implicit",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				SkipNewSessionTicket: true,
+				SkipChangeCipherSpec: true,
+				SkipFinished:         true,
+			},
+		},
+		flags: []string{
+			"-implicit-handshake",
+			"-false-start",
+			"-handshake-never-done",
+			"-advertise-alpn", "\x03foo",
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_RECORD:",
+	},
+	{
+		testType:           serverTest,
+		name:               "FailEarlyCallback",
+		flags:              []string{"-fail-early-callback"},
+		shouldFail:         true,
+		expectedError:      ":CONNECTION_REJECTED:",
+		expectedLocalError: "remote error: access denied",
+	},
+	{
+		name: "WrongMessageType",
+		config: Config{
+			Bugs: ProtocolBugs{
+				WrongCertificateMessageType: true,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	},
+	{
+		protocol: dtls,
+		name:     "WrongMessageType-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				WrongCertificateMessageType: true,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	},
+	{
+		protocol: dtls,
+		name:     "FragmentMessageTypeMismatch-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength:    2,
+				FragmentMessageTypeMismatch: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":FRAGMENT_MISMATCH:",
+	},
+	{
+		protocol: dtls,
+		name:     "FragmentMessageLengthMismatch-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength:      2,
+				FragmentMessageLengthMismatch: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":FRAGMENT_MISMATCH:",
+	},
+	{
+		protocol: dtls,
+		name:     "SplitFragmentHeader-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SplitFragmentHeader: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	},
+	{
+		protocol: dtls,
+		name:     "SplitFragmentBody-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SplitFragmentBody: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	},
+	{
+		protocol: dtls,
+		name:     "SendEmptyFragments-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendEmptyFragments: true,
+			},
+		},
+	},
+	{
+		name: "UnsupportedCipherSuite",
+		config: Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+			Bugs: ProtocolBugs{
+				IgnorePeerCipherPreferences: true,
+			},
+		},
+		flags:         []string{"-cipher", "DEFAULT:!RC4"},
+		shouldFail:    true,
+		expectedError: ":WRONG_CIPHER_RETURNED:",
+	},
+	{
+		name: "UnsupportedCurve",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			// BoringSSL implements P-224 but doesn't enable it by
+			// default.
+			CurvePreferences: []CurveID{CurveP224},
+			Bugs: ProtocolBugs{
+				IgnorePeerCurvePreferences: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_CURVE:",
+	},
+	{
+		name: "SendWarningAlerts",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendWarningAlerts: alertAccessDenied,
+			},
+		},
+	},
+	{
+		protocol: dtls,
+		name:     "SendWarningAlerts-DTLS",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendWarningAlerts: alertAccessDenied,
+			},
+		},
+	},
+	{
+		name: "BadFinished",
+		config: Config{
+			Bugs: ProtocolBugs{
+				BadFinished: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":DIGEST_CHECK_FAILED:",
+	},
+	{
+		name: "FalseStart-BadFinished",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				BadFinished:      true,
+				ExpectFalseStart: true,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-handshake-never-done",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst: true,
+		shouldFail:      true,
+		expectedError:   ":DIGEST_CHECK_FAILED:",
+	},
+	{
+		name: "NoFalseStart-NoALPN",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				ExpectFalseStart:          true,
+				AlertBeforeFalseStartTest: alertAccessDenied,
+			},
+		},
+		flags: []string{
+			"-false-start",
+		},
+		shimWritesFirst:    true,
+		shouldFail:         true,
+		expectedError:      ":TLSV1_ALERT_ACCESS_DENIED:",
+		expectedLocalError: "tls: peer did not false start: EOF",
+	},
+	{
+		name: "NoFalseStart-NoAEAD",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				ExpectFalseStart:          true,
+				AlertBeforeFalseStartTest: alertAccessDenied,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst:    true,
+		shouldFail:         true,
+		expectedError:      ":TLSV1_ALERT_ACCESS_DENIED:",
+		expectedLocalError: "tls: peer did not false start: EOF",
+	},
+	{
+		name: "NoFalseStart-RSA",
+		config: Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				ExpectFalseStart:          true,
+				AlertBeforeFalseStartTest: alertAccessDenied,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst:    true,
+		shouldFail:         true,
+		expectedError:      ":TLSV1_ALERT_ACCESS_DENIED:",
+		expectedLocalError: "tls: peer did not false start: EOF",
+	},
+	{
+		name: "NoFalseStart-DHE_RSA",
+		config: Config{
+			CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+			NextProtos:   []string{"foo"},
+			Bugs: ProtocolBugs{
+				ExpectFalseStart:          true,
+				AlertBeforeFalseStartTest: alertAccessDenied,
+			},
+		},
+		flags: []string{
+			"-false-start",
+			"-advertise-alpn", "\x03foo",
+		},
+		shimWritesFirst:    true,
+		shouldFail:         true,
+		expectedError:      ":TLSV1_ALERT_ACCESS_DENIED:",
+		expectedLocalError: "tls: peer did not false start: EOF",
+	},
+	{
+		testType: serverTest,
+		name:     "NoSupportedCurves",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				NoSupportedCurves: true,
+			},
+		},
+	},
+	{
+		testType: serverTest,
+		name:     "NoCommonCurves",
+		config: Config{
+			CipherSuites: []uint16{
+				TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+				TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+			},
+			CurvePreferences: []CurveID{CurveP224},
+		},
+		expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
 	},
 }
 
@@ -665,7 +1099,8 @@
 	}
 
 	if test.protocol == dtls {
-		conn = newPacketAdaptor(conn)
+		config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
+		conn = config.Bugs.PacketAdaptor
 		if test.replayWrites {
 			conn = newReplayAdaptor(conn)
 		}
@@ -713,6 +1148,10 @@
 		return fmt.Errorf("got version %x, expected %x", vers, expectedVersion)
 	}
 
+	if cipher := tlsConn.ConnectionState().CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher {
+		return fmt.Errorf("got cipher %x, expected %x", cipher, test.expectedCipher)
+	}
+
 	if test.expectChannelID {
 		channelID := tlsConn.ConnectionState().ChannelID
 		if channelID == nil {
@@ -741,6 +1180,20 @@
 		return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile)
 	}
 
+	if test.exportKeyingMaterial > 0 {
+		actual := make([]byte, test.exportKeyingMaterial)
+		if _, err := io.ReadFull(tlsConn, actual); err != nil {
+			return err
+		}
+		expected, err := tlsConn.ExportKeyingMaterial(test.exportKeyingMaterial, []byte(test.exportLabel), []byte(test.exportContext), test.useExportContext)
+		if err != nil {
+			return err
+		}
+		if !bytes.Equal(actual, expected) {
+			return fmt.Errorf("keying material mismatch")
+		}
+	}
+
 	if test.shimWritesFirst {
 		var buf [5]byte
 		_, err := io.ReadFull(tlsConn, buf[:])
@@ -778,21 +1231,14 @@
 		return err
 	}
 
-	var testMessage []byte
-	if config.Bugs.AppDataAfterChangeCipherSpec != nil {
-		// We've already sent a message. Expect the shim to echo it
-		// back.
-		testMessage = config.Bugs.AppDataAfterChangeCipherSpec
-	} else {
-		if messageLen == 0 {
-			messageLen = 32
-		}
-		testMessage = make([]byte, messageLen)
-		for i := range testMessage {
-			testMessage[i] = 0x42
-		}
-		tlsConn.Write(testMessage)
+	if messageLen == 0 {
+		messageLen = 32
 	}
+	testMessage := make([]byte, messageLen)
+	for i := range testMessage {
+		testMessage[i] = 0x42
+	}
+	tlsConn.Write(testMessage)
 
 	buf := make([]byte, len(testMessage))
 	if test.protocol == dtls {
@@ -840,27 +1286,6 @@
 	return exec.Command("xterm", xtermArgs...)
 }
 
-func openSocketPair() (shimEnd *os.File, conn net.Conn) {
-	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
-	if err != nil {
-		panic(err)
-	}
-
-	syscall.CloseOnExec(socks[0])
-	syscall.CloseOnExec(socks[1])
-	shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
-	connFile := os.NewFile(uintptr(socks[1]), "our end")
-	conn, err = net.FileConn(connFile)
-	if err != nil {
-		panic(err)
-	}
-	connFile.Close()
-	if err != nil {
-		panic(err)
-	}
-	return shimEnd, conn
-}
-
 type moreMallocsError struct{}
 
 func (moreMallocsError) Error() string {
@@ -869,16 +1294,45 @@
 
 var errMoreMallocs = moreMallocsError{}
 
+// accept accepts a connection from listener, unless waitChan signals a process
+// exit first.
+func acceptOrWait(listener net.Listener, waitChan chan error) (net.Conn, error) {
+	type connOrError struct {
+		conn net.Conn
+		err  error
+	}
+	connChan := make(chan connOrError, 1)
+	go func() {
+		conn, err := listener.Accept()
+		connChan <- connOrError{conn, err}
+		close(connChan)
+	}()
+	select {
+	case result := <-connChan:
+		return result.conn, result.err
+	case childErr := <-waitChan:
+		waitChan <- childErr
+		return nil, fmt.Errorf("child exited early: %s", childErr)
+	}
+}
+
 func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
 	if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) {
 		panic("Error expected without shouldFail in " + test.name)
 	}
 
-	shimEnd, conn := openSocketPair()
-	shimEndResume, connResume := openSocketPair()
+	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
+	if err != nil {
+		panic(err)
+	}
+	defer func() {
+		if listener != nil {
+			listener.Close()
+		}
+	}()
 
 	shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
-	var flags []string
+	flags := []string{"-port", strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)}
 	if test.testType == serverTest {
 		flags = append(flags, "-server")
 
@@ -909,6 +1363,15 @@
 		flags = append(flags, "-shim-writes-first")
 	}
 
+	if test.exportKeyingMaterial > 0 {
+		flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial))
+		flags = append(flags, "-export-label", test.exportLabel)
+		flags = append(flags, "-export-context", test.exportContext)
+		if test.useExportContext {
+			flags = append(flags, "-use-export-context")
+		}
+	}
+
 	flags = append(flags, test.flags...)
 
 	var shim *exec.Cmd
@@ -919,13 +1382,13 @@
 	} else {
 		shim = exec.Command(shim_path, flags...)
 	}
-	shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
 	shim.Stdin = os.Stdin
 	var stdoutBuf, stderrBuf bytes.Buffer
 	shim.Stdout = &stdoutBuf
 	shim.Stderr = &stderrBuf
 	if mallocNumToFail >= 0 {
-		shim.Env = []string{"MALLOC_NUMBER_TO_FAIL=" + strconv.FormatInt(mallocNumToFail, 10)}
+		shim.Env = os.Environ()
+		shim.Env = append(shim.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10))
 		if *mallocTestDebug {
 			shim.Env = append(shim.Env, "MALLOC_ABORT_ON_FAIL=1")
 		}
@@ -935,8 +1398,8 @@
 	if err := shim.Start(); err != nil {
 		panic(err)
 	}
-	shimEnd.Close()
-	shimEndResume.Close()
+	waitChan := make(chan error, 1)
+	go func() { waitChan <- shim.Wait() }()
 
 	config := test.config
 	config.ClientSessionCache = NewLRUClientSessionCache(1)
@@ -945,16 +1408,27 @@
 		if len(config.Certificates) == 0 {
 			config.Certificates = []Certificate{getRSACertificate()}
 		}
+	} else {
+		// Supply a ServerName to ensure a constant session cache key,
+		// rather than falling back to net.Conn.RemoteAddr.
+		if len(config.ServerName) == 0 {
+			config.ServerName = "test"
+		}
 	}
 
-	err := doExchange(test, &config, conn, test.messageLen,
-		false /* not a resumption */)
-	conn.Close()
+	conn, err := acceptOrWait(listener, waitChan)
+	if err == nil {
+		err = doExchange(test, &config, conn, test.messageLen, false /* not a resumption */)
+		conn.Close()
+	}
 
 	if err == nil && test.resumeSession {
 		var resumeConfig Config
 		if test.resumeConfig != nil {
 			resumeConfig = *test.resumeConfig
+			if len(resumeConfig.ServerName) == 0 {
+				resumeConfig.ServerName = config.ServerName
+			}
 			if len(resumeConfig.Certificates) == 0 {
 				resumeConfig.Certificates = []Certificate{getRSACertificate()}
 			}
@@ -966,12 +1440,20 @@
 		} else {
 			resumeConfig = config
 		}
-		err = doExchange(test, &resumeConfig, connResume, test.messageLen,
-			true /* resumption */)
+		var connResume net.Conn
+		connResume, err = acceptOrWait(listener, waitChan)
+		if err == nil {
+			err = doExchange(test, &resumeConfig, connResume, test.messageLen, true /* resumption */)
+			connResume.Close()
+		}
 	}
-	connResume.Close()
 
-	childErr := shim.Wait()
+	// Close the listener now. This is to avoid hangs should the shim try to
+	// open more connections than expected.
+	listener.Close()
+	listener = nil
+
+	childErr := <-waitChan
 	if exitError, ok := childErr.(*exec.ExitError); ok {
 		if exitError.Sys().(syscall.WaitStatus).ExitStatus() == 88 {
 			return errMoreMallocs
@@ -981,7 +1463,7 @@
 	stdout := string(stdoutBuf.Bytes())
 	stderr := string(stderrBuf.Bytes())
 	failed := err != nil || childErr != nil
-	correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
+	correctFailure := len(test.expectedError) == 0 || strings.Contains(stderr, test.expectedError)
 	localError := "none"
 	if err != nil {
 		localError = err.Error()
@@ -1008,10 +1490,10 @@
 			panic("internal error")
 		}
 
-		return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
+		return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, stdout, stderr)
 	}
 
-	if !*useValgrind && len(stderr) > 0 {
+	if !*useValgrind && !failed && len(stderr) > 0 {
 		println(stderr)
 	}
 
@@ -1047,12 +1529,14 @@
 	{"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
 	{"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
 	{"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
+	{"DHE-RSA-CHACHA20-POLY1305", TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
 	{"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
 	{"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
 	{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
+	{"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
 	{"ECDHE-PSK-AES128-GCM-SHA256", TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
@@ -1061,6 +1545,7 @@
 	{"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
 	{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
+	{"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
 	{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
 	{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
@@ -1076,7 +1561,8 @@
 func isTLS12Only(suiteName string) bool {
 	return hasComponent(suiteName, "GCM") ||
 		hasComponent(suiteName, "SHA256") ||
-		hasComponent(suiteName, "SHA384")
+		hasComponent(suiteName, "SHA384") ||
+		hasComponent(suiteName, "POLY1305")
 }
 
 func isDTLSCipher(suiteName string) bool {
@@ -1454,6 +1940,17 @@
 	})
 	testCases = append(testCases, testCase{
 		protocol: protocol,
+		name:     "Basic-Client-Implicit" + suffix,
+		config: Config{
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength: maxHandshakeRecordLength,
+			},
+		},
+		flags:         append(flags, "-implicit-handshake"),
+		resumeSession: true,
+	})
+	testCases = append(testCases, testCase{
+		protocol: protocol,
 		testType: serverTest,
 		name:     "Basic-Server" + suffix,
 		config: Config{
@@ -1477,6 +1974,30 @@
 		flags:         flags,
 		resumeSession: true,
 	})
+	testCases = append(testCases, testCase{
+		protocol: protocol,
+		testType: serverTest,
+		name:     "Basic-Server-Implicit" + suffix,
+		config: Config{
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength: maxHandshakeRecordLength,
+			},
+		},
+		flags:         append(flags, "-implicit-handshake"),
+		resumeSession: true,
+	})
+	testCases = append(testCases, testCase{
+		protocol: protocol,
+		testType: serverTest,
+		name:     "Basic-Server-EarlyCallback" + suffix,
+		config: Config{
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength: maxHandshakeRecordLength,
+			},
+		},
+		flags:         append(flags, "-use-early-callback"),
+		resumeSession: true,
+	})
 
 	// TLS client auth.
 	testCases = append(testCases, testCase{
@@ -1588,6 +2109,8 @@
 			expectedNextProtoType: npn,
 		})
 
+		// TODO(davidben): Add tests for when False Start doesn't trigger.
+
 		// Client does False Start and negotiates NPN.
 		testCases = append(testCases, testCase{
 			protocol: protocol,
@@ -1626,9 +2149,27 @@
 			resumeSession:   true,
 		})
 
+		// Client does False Start but doesn't explicitly call
+		// SSL_connect.
+		testCases = append(testCases, testCase{
+			protocol: protocol,
+			name:     "FalseStart-Implicit" + suffix,
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				NextProtos:   []string{"foo"},
+				Bugs: ProtocolBugs{
+					MaxHandshakeRecordLength: maxHandshakeRecordLength,
+				},
+			},
+			flags: append(flags,
+				"-implicit-handshake",
+				"-false-start",
+				"-advertise-alpn", "\x03foo"),
+		})
+
 		// False Start without session tickets.
 		testCases = append(testCases, testCase{
-			name: "FalseStart-SessionTicketsDisabled",
+			name: "FalseStart-SessionTicketsDisabled" + suffix,
 			config: Config{
 				CipherSuites:           []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 				NextProtos:             []string{"foo"},
@@ -1710,17 +2251,36 @@
 			},
 			flags: flags,
 		})
+	}
+}
+
+func addDDoSCallbackTests() {
+	// DDoS callback.
+
+	for _, resume := range []bool{false, true} {
+		suffix := "Resume"
+		if resume {
+			suffix = "No" + suffix
+		}
 
 		testCases = append(testCases, testCase{
-			testType: serverTest,
-			protocol: protocol,
-			name:     "CookieExchange" + suffix,
-			config: Config{
-				Bugs: ProtocolBugs{
-					MaxHandshakeRecordLength: maxHandshakeRecordLength,
-				},
-			},
-			flags: append(flags, "-cookie-exchange"),
+			testType:      serverTest,
+			name:          "Server-DDoS-OK-" + suffix,
+			flags:         []string{"-install-ddos-callback"},
+			resumeSession: resume,
+		})
+
+		failFlag := "-fail-ddos-callback"
+		if resume {
+			failFlag = "-fail-second-ddos-callback"
+		}
+		testCases = append(testCases, testCase{
+			testType:      serverTest,
+			name:          "Server-DDoS-Reject-" + suffix,
+			flags:         []string{"-install-ddos-callback", failFlag},
+			resumeSession: resume,
+			shouldFail:    true,
+			expectedError: ":CONNECTION_REJECTED:",
 		})
 	}
 }
@@ -1976,7 +2536,7 @@
 	})
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "ServerNameExtensionClient",
+		name:     "ServerNameExtensionClientMismatch",
 		config: Config{
 			Bugs: ProtocolBugs{
 				ExpectServerName: "mismatch.com",
@@ -1988,7 +2548,7 @@
 	})
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "ServerNameExtensionClient",
+		name:     "ServerNameExtensionClientMissing",
 		config: Config{
 			Bugs: ProtocolBugs{
 				ExpectServerName: "missing.com",
@@ -2201,27 +2761,40 @@
 					suffix += "-DTLS"
 				}
 
-				testCases = append(testCases, testCase{
-					protocol:      protocol,
-					name:          "Resume-Client" + suffix,
-					resumeSession: true,
-					config: Config{
-						MaxVersion:   sessionVers.version,
-						CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
-						Bugs: ProtocolBugs{
-							AllowSessionVersionMismatch: true,
+				if sessionVers.version == resumeVers.version {
+					testCases = append(testCases, testCase{
+						protocol:      protocol,
+						name:          "Resume-Client" + suffix,
+						resumeSession: true,
+						config: Config{
+							MaxVersion:   sessionVers.version,
+							CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
 						},
-					},
-					expectedVersion: sessionVers.version,
-					resumeConfig: &Config{
-						MaxVersion:   resumeVers.version,
-						CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
-						Bugs: ProtocolBugs{
-							AllowSessionVersionMismatch: true,
+						expectedVersion:       sessionVers.version,
+						expectedResumeVersion: resumeVers.version,
+					})
+				} else {
+					testCases = append(testCases, testCase{
+						protocol:      protocol,
+						name:          "Resume-Client-Mismatch" + suffix,
+						resumeSession: true,
+						config: Config{
+							MaxVersion:   sessionVers.version,
+							CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
 						},
-					},
-					expectedResumeVersion: resumeVers.version,
-				})
+						expectedVersion: sessionVers.version,
+						resumeConfig: &Config{
+							MaxVersion:   resumeVers.version,
+							CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+							Bugs: ProtocolBugs{
+								AllowSessionVersionMismatch: true,
+							},
+						},
+						expectedResumeVersion: resumeVers.version,
+						shouldFail:            true,
+						expectedError:         ":OLD_SESSION_VERSION_NOT_RETURNED:",
+					})
+				}
 
 				testCases = append(testCases, testCase{
 					protocol:      protocol,
@@ -2265,6 +2838,22 @@
 			}
 		}
 	}
+
+	testCases = append(testCases, testCase{
+		name:          "Resume-Client-CipherMismatch",
+		resumeSession: true,
+		config: Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+		},
+		resumeConfig: &Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				SendCipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
+	})
 }
 
 func addRenegotiationTests() {
@@ -2276,6 +2865,17 @@
 	})
 	testCases = append(testCases, testCase{
 		testType: serverTest,
+		name:     "Renegotiate-Server-Full",
+		config: Config{
+			Bugs: ProtocolBugs{
+				NeverResumeOnRenego: true,
+			},
+		},
+		flags:           []string{"-renegotiate"},
+		shimWritesFirst: true,
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
 		name:     "Renegotiate-Server-EmptyExt",
 		config: Config{
 			Bugs: ProtocolBugs{
@@ -2328,12 +2928,43 @@
 		},
 		flags: []string{"-allow-unsafe-legacy-renegotiation"},
 	})
+	testCases = append(testCases, testCase{
+		testType:           serverTest,
+		name:               "Renegotiate-Server-ClientInitiated-Forbidden",
+		renegotiate:        true,
+		flags:              []string{"-reject-peer-renegotiations"},
+		shouldFail:         true,
+		expectedError:      ":NO_RENEGOTIATION:",
+		expectedLocalError: "remote error: no renegotiation",
+	})
+	// Regression test for CVE-2015-0291.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "Renegotiate-Server-NoSignatureAlgorithms",
+		config: Config{
+			Bugs: ProtocolBugs{
+				NeverResumeOnRenego:           true,
+				NoSignatureAlgorithmsOnRenego: true,
+			},
+		},
+		flags:           []string{"-renegotiate"},
+		shimWritesFirst: true,
+	})
 	// TODO(agl): test the renegotiation info SCSV.
 	testCases = append(testCases, testCase{
 		name:        "Renegotiate-Client",
 		renegotiate: true,
 	})
 	testCases = append(testCases, testCase{
+		name: "Renegotiate-Client-Full",
+		config: Config{
+			Bugs: ProtocolBugs{
+				NeverResumeOnRenego: true,
+			},
+		},
+		renegotiate: true,
+	})
+	testCases = append(testCases, testCase{
 		name:        "Renegotiate-Client-EmptyExt",
 		renegotiate: true,
 		config: Config{
@@ -2372,6 +3003,14 @@
 		renegotiateCiphers: []uint16{TLS_RSA_WITH_RC4_128_SHA},
 	})
 	testCases = append(testCases, testCase{
+		name:               "Renegotiate-Client-Forbidden",
+		renegotiate:        true,
+		flags:              []string{"-reject-peer-renegotiations"},
+		shouldFail:         true,
+		expectedError:      ":NO_RENEGOTIATION:",
+		expectedLocalError: "remote error: no renegotiation",
+	})
+	testCases = append(testCases, testCase{
 		name:        "Renegotiate-SameClientVersion",
 		renegotiate: true,
 		config: Config{
@@ -2418,7 +3057,7 @@
 	})
 	testCases = append(testCases, testCase{
 		protocol: dtls,
-		name:     "FastRadio-Padding",
+		name:     "FastRadio-Padding-DTLS",
 		config: Config{
 			Bugs: ProtocolBugs{
 				RequireFastradioPadding: true,
@@ -2534,6 +3173,196 @@
 			},
 		},
 	})
+
+	// Test that hash preferences are enforced. BoringSSL defaults to
+	// rejecting MD5 signatures.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SigningHash-ClientAuth-Enforced",
+		config: Config{
+			Certificates: []Certificate{rsaCertificate},
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashMD5},
+				// Advertise SHA-1 so the handshake will
+				// proceed, but the shim's preferences will be
+				// ignored in CertificateVerify generation, so
+				// MD5 will be chosen.
+				{signatureRSA, hashSHA1},
+			},
+			Bugs: ProtocolBugs{
+				IgnorePeerSignatureAlgorithmPreferences: true,
+			},
+		},
+		flags:         []string{"-require-any-client-certificate"},
+		shouldFail:    true,
+		expectedError: ":WRONG_SIGNATURE_TYPE:",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "SigningHash-ServerKeyExchange-Enforced",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			SignatureAndHashes: []signatureAndHash{
+				{signatureRSA, hashMD5},
+			},
+			Bugs: ProtocolBugs{
+				IgnorePeerSignatureAlgorithmPreferences: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_SIGNATURE_TYPE:",
+	})
+}
+
+// timeouts is the retransmit schedule for BoringSSL. It doubles and
+// caps at 60 seconds. On the 13th timeout, it gives up.
+var timeouts = []time.Duration{
+	1 * time.Second,
+	2 * time.Second,
+	4 * time.Second,
+	8 * time.Second,
+	16 * time.Second,
+	32 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+}
+
+func addDTLSRetransmitTests() {
+	// Test that this is indeed the timeout schedule. Stress all
+	// four patterns of handshake.
+	for i := 1; i < len(timeouts); i++ {
+		number := strconv.Itoa(i)
+		testCases = append(testCases, testCase{
+			protocol: dtls,
+			name:     "DTLS-Retransmit-Client-" + number,
+			config: Config{
+				Bugs: ProtocolBugs{
+					TimeoutSchedule: timeouts[:i],
+				},
+			},
+			resumeSession: true,
+			flags:         []string{"-async"},
+		})
+		testCases = append(testCases, testCase{
+			protocol: dtls,
+			testType: serverTest,
+			name:     "DTLS-Retransmit-Server-" + number,
+			config: Config{
+				Bugs: ProtocolBugs{
+					TimeoutSchedule: timeouts[:i],
+				},
+			},
+			resumeSession: true,
+			flags:         []string{"-async"},
+		})
+	}
+
+	// Test that exceeding the timeout schedule hits a read
+	// timeout.
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		name:     "DTLS-Retransmit-Timeout",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule: timeouts,
+			},
+		},
+		resumeSession: true,
+		flags:         []string{"-async"},
+		shouldFail:    true,
+		expectedError: ":READ_TIMEOUT_EXPIRED:",
+	})
+
+	// Test that timeout handling has a fudge factor, due to API
+	// problems.
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		name:     "DTLS-Retransmit-Fudge",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule: []time.Duration{
+					timeouts[0] - 10*time.Millisecond,
+				},
+			},
+		},
+		resumeSession: true,
+		flags:         []string{"-async"},
+	})
+
+	// Test that the final Finished retransmitting isn't
+	// duplicated if the peer badly fragments everything.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		protocol: dtls,
+		name:     "DTLS-Retransmit-Fragmented",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule:          []time.Duration{timeouts[0]},
+				MaxHandshakeRecordLength: 2,
+			},
+		},
+		flags: []string{"-async"},
+	})
+}
+
+func addExportKeyingMaterialTests() {
+	for _, vers := range tlsVersions {
+		if vers.version == VersionSSL30 {
+			continue
+		}
+		testCases = append(testCases, testCase{
+			name: "ExportKeyingMaterial-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			exportKeyingMaterial: 1024,
+			exportLabel:          "label",
+			exportContext:        "context",
+			useExportContext:     true,
+		})
+		testCases = append(testCases, testCase{
+			name: "ExportKeyingMaterial-NoContext-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			exportKeyingMaterial: 1024,
+		})
+		testCases = append(testCases, testCase{
+			name: "ExportKeyingMaterial-EmptyContext-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			exportKeyingMaterial: 1024,
+			useExportContext:     true,
+		})
+		testCases = append(testCases, testCase{
+			name: "ExportKeyingMaterial-Small-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			exportKeyingMaterial: 1,
+			exportLabel:          "label",
+			exportContext:        "context",
+			useExportContext:     true,
+		})
+	}
+	testCases = append(testCases, testCase{
+		name: "ExportKeyingMaterial-SSL3",
+		config: Config{
+			MaxVersion: VersionSSL30,
+		},
+		exportKeyingMaterial: 1024,
+		exportLabel:          "label",
+		exportContext:        "context",
+		useExportContext:     true,
+		shouldFail:           true,
+		expectedError:        "failed to export keying material",
+	})
 }
 
 func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
@@ -2566,27 +3395,47 @@
 	err     error
 }
 
-func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
+func statusPrinter(doneChan chan *testOutput, statusChan chan statusMsg, total int) {
 	var started, done, failed, lineLen int
-	defer close(doneChan)
 
+	testOutput := newTestOutput()
 	for msg := range statusChan {
+		if !*pipe {
+			// Erase the previous status line.
+			var erase string
+			for i := 0; i < lineLen; i++ {
+				erase += "\b \b"
+			}
+			fmt.Print(erase)
+		}
+
 		if msg.started {
 			started++
 		} else {
 			done++
+
+			if msg.err != nil {
+				fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
+				failed++
+				testOutput.addResult(msg.test.name, "FAIL")
+			} else {
+				if *pipe {
+					// Print each test instead of a status line.
+					fmt.Printf("PASSED (%s)\n", msg.test.name)
+				}
+				testOutput.addResult(msg.test.name, "PASS")
+			}
 		}
 
-		fmt.Printf("\x1b[%dD\x1b[K", lineLen)
-
-		if msg.err != nil {
-			fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
-			failed++
+		if !*pipe {
+			// Print a new status line.
+			line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
+			lineLen = len(line)
+			os.Stdout.WriteString(line)
 		}
-		line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
-		lineLen = len(line)
-		os.Stdout.WriteString(line)
 	}
+
+	doneChan <- testOutput
 }
 
 func main() {
@@ -2601,6 +3450,7 @@
 	addCBCPaddingTests()
 	addCBCSplittingTests()
 	addClientAuthTests()
+	addDDoSCallbackTests()
 	addVersionNegotiationTests()
 	addMinimumVersionTests()
 	addD5BugTests()
@@ -2611,6 +3461,8 @@
 	addDTLSReplayTests()
 	addSigningHashTests()
 	addFastRadioPaddingTests()
+	addDTLSRetransmitTests()
+	addExportKeyingMaterialTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {
@@ -2625,7 +3477,7 @@
 
 	statusChan := make(chan statusMsg, numWorkers)
 	testChan := make(chan *testCase, numWorkers)
-	doneChan := make(chan struct{})
+	doneChan := make(chan *testOutput)
 
 	go statusPrinter(doneChan, statusChan, len(testCases))
 
@@ -2643,7 +3495,17 @@
 	close(testChan)
 	wg.Wait()
 	close(statusChan)
-	<-doneChan
+	testOutput := <-doneChan
 
 	fmt.Printf("\n")
+
+	if *jsonOutput != "" {
+		if err := testOutput.writeTo(*jsonOutput); err != nil {
+			fmt.Fprintf(os.Stderr, "Error: %s\n", err)
+		}
+	}
+
+	if !testOutput.allPassed {
+		os.Exit(1)
+	}
 }
diff --git a/src/ssl/test/runner/test_output.go b/src/ssl/test/runner/test_output.go
new file mode 100644
index 0000000..bcb7a93
--- /dev/null
+++ b/src/ssl/test/runner/test_output.go
@@ -0,0 +1,79 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+package main
+
+import (
+	"encoding/json"
+	"os"
+	"time"
+)
+
+// testOutput is a representation of Chromium's JSON test result format. See
+// https://www.chromium.org/developers/the-json-test-results-format
+type testOutput struct {
+	Version           int                   `json:"version"`
+	Interrupted       bool                  `json:"interrupted"`
+	PathDelimiter     string                `json:"path_delimiter"`
+	SecondsSinceEpoch float64               `json:"seconds_since_epoch"`
+	NumFailuresByType map[string]int        `json:"num_failures_by_type"`
+	Tests             map[string]testResult `json:"tests"`
+	allPassed         bool
+}
+
+type testResult struct {
+	Actual       string `json:"actual"`
+	Expected     string `json:"expected"`
+	IsUnexpected bool   `json:"is_unexpected"`
+}
+
+func newTestOutput() *testOutput {
+	return &testOutput{
+		Version:           3,
+		PathDelimiter:     ".",
+		SecondsSinceEpoch: float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond),
+		NumFailuresByType: make(map[string]int),
+		Tests:             make(map[string]testResult),
+		allPassed:         true,
+	}
+}
+
+func (t *testOutput) addResult(name, result string) {
+	if _, found := t.Tests[name]; found {
+		panic(name)
+	}
+	t.Tests[name] = testResult{
+		Actual:       result,
+		Expected:     "PASS",
+		IsUnexpected: result != "PASS",
+	}
+	t.NumFailuresByType[result]++
+	if result != "PASS" {
+		t.allPassed = false
+	}
+}
+
+func (t *testOutput) writeTo(name string) error {
+	file, err := os.Create(name)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	out, err := json.MarshalIndent(t, "", "  ")
+	if err != nil {
+		return err
+	}
+	_, err = file.Write(out)
+	return err
+}
diff --git a/src/ssl/test/scoped_types.h b/src/ssl/test/scoped_types.h
new file mode 100644
index 0000000..7e92cee
--- /dev/null
+++ b/src/ssl/test/scoped_types.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
+#define OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
+
+#include <openssl/ssl.h>
+
+#include "../../crypto/test/scoped_types.h"
+
+
+using ScopedSSL = ScopedOpenSSLType<SSL, SSL_free>;
+using ScopedSSL_CTX = ScopedOpenSSLType<SSL_CTX, SSL_CTX_free>;
+using ScopedSSL_SESSION = ScopedOpenSSLType<SSL_SESSION, SSL_SESSION_free>;
+
+
+#endif  // OPENSSL_HEADER_SSL_TEST_SCOPED_TYPES_H
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index c032d96..25906f7 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -60,7 +60,6 @@
   { "-no-tls11", &TestConfig::no_tls11 },
   { "-no-tls1", &TestConfig::no_tls1 },
   { "-no-ssl3", &TestConfig::no_ssl3 },
-  { "-cookie-exchange", &TestConfig::cookie_exchange },
   { "-shim-writes-first", &TestConfig::shim_writes_first },
   { "-tls-d5-bug", &TestConfig::tls_d5_bug },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
@@ -73,6 +72,15 @@
   { "-enable-signed-cert-timestamps",
     &TestConfig::enable_signed_cert_timestamps },
   { "-fastradio-padding", &TestConfig::fastradio_padding },
+  { "-implicit-handshake", &TestConfig::implicit_handshake },
+  { "-use-early-callback", &TestConfig::use_early_callback },
+  { "-fail-early-callback", &TestConfig::fail_early_callback },
+  { "-install-ddos-callback", &TestConfig::install_ddos_callback },
+  { "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
+  { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
+  { "-handshake-never-done", &TestConfig::handshake_never_done },
+  { "-use-export-context", &TestConfig::use_export_context },
+  { "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations },
 };
 
 const Flag<std::string> kStringFlags[] = {
@@ -91,6 +99,9 @@
   { "-psk", &TestConfig::psk },
   { "-psk-identity", &TestConfig::psk_identity },
   { "-srtp-profiles", &TestConfig::srtp_profiles },
+  { "-cipher", &TestConfig::cipher },
+  { "-export-label", &TestConfig::export_label },
+  { "-export-context", &TestConfig::export_context },
 };
 
 const Flag<std::string> kBase64Flags[] = {
@@ -102,43 +113,15 @@
 };
 
 const Flag<int> kIntFlags[] = {
+  { "-port", &TestConfig::port },
   { "-min-version", &TestConfig::min_version },
   { "-max-version", &TestConfig::max_version },
   { "-mtu", &TestConfig::mtu },
+  { "-export-keying-material", &TestConfig::export_keying_material },
 };
 
 }  // namespace
 
-TestConfig::TestConfig()
-    : is_server(false),
-      is_dtls(false),
-      resume(false),
-      fallback_scsv(false),
-      require_any_client_certificate(false),
-      false_start(false),
-      async(false),
-      write_different_record_sizes(false),
-      cbc_record_splitting(false),
-      partial_write(false),
-      no_tls12(false),
-      no_tls11(false),
-      no_tls1(false),
-      no_ssl3(false),
-      cookie_exchange(false),
-      shim_writes_first(false),
-      tls_d5_bug(false),
-      expect_session_miss(false),
-      expect_extended_master_secret(false),
-      renegotiate(false),
-      allow_unsafe_legacy_renegotiation(false),
-      enable_ocsp_stapling(false),
-      enable_signed_cert_timestamps(false),
-      fastradio_padding(false),
-      min_version(0),
-      max_version(0),
-      mtu(0) {
-}
-
 bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
   for (int i = 0; i < argc; i++) {
     bool *bool_field = FindField(out_config, kBoolFlags, argv[i]);
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index ba54227..f107a0f 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -19,54 +19,65 @@
 
 
 struct TestConfig {
-  TestConfig();
-
-  bool is_server;
-  bool is_dtls;
-  bool resume;
-  bool fallback_scsv;
+  int port = 0;
+  bool is_server = false;
+  bool is_dtls = false;
+  bool resume = false;
+  bool fallback_scsv = false;
   std::string key_file;
   std::string cert_file;
   std::string expected_server_name;
   std::string expected_certificate_types;
-  bool require_any_client_certificate;
+  bool require_any_client_certificate = false;
   std::string advertise_npn;
   std::string expected_next_proto;
-  bool false_start;
+  bool false_start = false;
   std::string select_next_proto;
-  bool async;
-  bool write_different_record_sizes;
-  bool cbc_record_splitting;
-  bool partial_write;
-  bool no_tls12;
-  bool no_tls11;
-  bool no_tls1;
-  bool no_ssl3;
-  bool cookie_exchange;
+  bool async = false;
+  bool write_different_record_sizes = false;
+  bool cbc_record_splitting = false;
+  bool partial_write = false;
+  bool no_tls12 = false;
+  bool no_tls11 = false;
+  bool no_tls1 = false;
+  bool no_ssl3 = false;
   std::string expected_channel_id;
   std::string send_channel_id;
-  bool shim_writes_first;
-  bool tls_d5_bug;
+  bool shim_writes_first = false;
+  bool tls_d5_bug = false;
   std::string host_name;
   std::string advertise_alpn;
   std::string expected_alpn;
   std::string expected_advertised_alpn;
   std::string select_alpn;
-  bool expect_session_miss;
-  bool expect_extended_master_secret;
+  bool expect_session_miss = false;
+  bool expect_extended_master_secret = false;
   std::string psk;
   std::string psk_identity;
-  bool renegotiate;
-  bool allow_unsafe_legacy_renegotiation;
+  bool renegotiate = false;
+  bool allow_unsafe_legacy_renegotiation = false;
   std::string srtp_profiles;
-  bool enable_ocsp_stapling;
+  bool enable_ocsp_stapling = false;
   std::string expected_ocsp_response;
-  bool enable_signed_cert_timestamps;
+  bool enable_signed_cert_timestamps = false;
   std::string expected_signed_cert_timestamps;
-  bool fastradio_padding;
-  int min_version;
-  int max_version;
-  int mtu;
+  bool fastradio_padding = false;
+  int min_version = 0;
+  int max_version = 0;
+  int mtu = 0;
+  bool implicit_handshake = false;
+  bool use_early_callback = false;
+  bool fail_early_callback = false;
+  bool install_ddos_callback = false;
+  bool fail_ddos_callback = false;
+  bool fail_second_ddos_callback = false;
+  std::string cipher;
+  bool handshake_never_done = false;
+  int export_keying_material = 0;
+  std::string export_label;
+  std::string export_context;
+  bool use_export_context = false;
+  bool reject_peer_renegotiations = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);