external/boringssl: Sync to d18cb77.

This includes the following changes which are far too many to list here:

https://boringssl.googlesource.com/boringssl/+log/7b8b9c17db93ea5287575b437c77fb36eeb81b31..d18cb77864dcc4b5c7cb08c2331008c01165f34f

This also retires one function from android_compat_hacks.c which is no longer
necessary.

Change-Id: Ie00536d7ad815464b2b031f7bcd1b683e12c1623
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index ee4cbc9..cc95a70 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -115,14 +115,12 @@
 
 #include <assert.h>
 #include <limits.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
@@ -292,12 +290,11 @@
   /* During the handshake, wbio is buffered to pack messages together. Flush the
    * buffer if the ChangeCipherSpec would not fit in a packet. */
   if (dtls1_max_record_size(ssl) == 0) {
-    ssl->rwstate = SSL_WRITING;
     int ret = BIO_flush(SSL_get_wbio(ssl));
     if (ret <= 0) {
+      ssl->rwstate = SSL_WRITING;
       return ret;
     }
-    ssl->rwstate = SSL_NOTHING;
   }
 
   static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
@@ -342,13 +339,12 @@
     /* During the handshake, wbio is buffered to pack messages together. Flush
      * the buffer if there isn't enough room to make progress. */
     if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
-      ssl->rwstate = SSL_WRITING;
       int flush_ret = BIO_flush(SSL_get_wbio(ssl));
       if (flush_ret <= 0) {
+        ssl->rwstate = SSL_WRITING;
         ret = flush_ret;
         goto err;
       }
-      ssl->rwstate = SSL_NOTHING;
       assert(BIO_wpending(SSL_get_wbio(ssl)) == 0);
     }
 
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index ad5eb50..2fc9094 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -115,7 +115,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/bn.h>
@@ -125,7 +124,6 @@
 #include <openssl/err.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/rand.h>
 
 #include "internal.h"
@@ -143,7 +141,6 @@
   assert(!ssl->server);
   assert(SSL_IS_DTLS(ssl));
 
-  ERR_clear_error();
   ERR_clear_system_error();
 
   if (ssl->info_callback != NULL) {
@@ -152,8 +149,6 @@
     cb = ssl->ctx->info_callback;
   }
 
-  ssl->in_handshake++;
-
   for (;;) {
     state = ssl->state;
 
@@ -368,10 +363,7 @@
         ssl->state = SSL3_ST_CW_FINISHED_A;
         ssl->init_num = 0;
 
-        ssl->session->cipher = ssl->s3->tmp.new_cipher;
-        if (!ssl->enc_method->setup_key_block(ssl) ||
-            !ssl->enc_method->change_cipher_state(
-                ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -383,10 +375,8 @@
           dtls1_start_timer(ssl);
         }
 
-        ret =
-            ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B,
-                               ssl->enc_method->client_finished_label,
-                               ssl->enc_method->client_finished_label_len);
+        ret = ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A,
+                                 SSL3_ST_CW_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
@@ -431,7 +421,7 @@
           goto end;
         }
 
-        if (!ssl3_do_change_cipher_spec(ssl)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
           ret = -1;
           goto end;
         }
@@ -457,12 +447,11 @@
         break;
 
       case SSL3_ST_CW_FLUSH:
-        ssl->rwstate = SSL_WRITING;
         if (BIO_flush(ssl->wbio) <= 0) {
+          ssl->rwstate = SSL_WRITING;
           ret = -1;
           goto end;
         }
-        ssl->rwstate = SSL_NOTHING;
         ssl->state = ssl->s3->tmp.next_state;
         break;
 
@@ -508,8 +497,6 @@
   }
 
 end:
-  ssl->in_handshake--;
-
   BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(ssl, SSL_CB_CONNECT_EXIT, ret);
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index 7f08b06..e48fbf1 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -57,12 +57,11 @@
 #include <openssl/ssl.h>
 
 #include <limits.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/err.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 
 #include "internal.h"
 
@@ -110,11 +109,11 @@
 
   ssl->d1 = d1;
 
-  /* Set the version to the highest version for DTLS. This controls the initial
-   * state of |ssl->enc_method| and what the API reports as the version prior to
-   * negotiation.
+  /* Set the version to the highest supported version.
    *
-   * TODO(davidben): This is fragile and confusing. */
+   * TODO(davidben): Move this field into |s3|, have it store the normalized
+   * protocol version, and implement this pre-negotiation quirk in |SSL_version|
+   * at the API boundary rather than in internal state. */
   ssl->version = DTLS1_2_VERSION;
   return 1;
 }
@@ -272,6 +271,10 @@
 }
 
 int DTLSv1_handle_timeout(SSL *ssl) {
+  ssl->rwstate = SSL_NOTHING;
+  /* Functions which use SSL_get_error must clear the error queue on entry. */
+  ERR_clear_error();
+
   if (!SSL_IS_DTLS(ssl)) {
     return -1;
   }
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index f2ade3a..4690486 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -112,7 +112,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -186,6 +185,7 @@
 }
 
 int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) {
+  assert(!SSL_in_init(ssl));
   return dtls1_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
@@ -233,7 +233,7 @@
  * This function must handle any surprises the peer may have for us, such as
  * Alert records (e.g. close_notify) and out of records. */
 int dtls1_read_bytes(SSL *ssl, int type, unsigned char *buf, int len, int peek) {
-  int al, i, ret;
+  int al, ret;
   unsigned int n;
   SSL3_RECORD *rr;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
@@ -245,21 +245,7 @@
     return -1;
   }
 
-  if (!ssl->in_handshake && SSL_in_init(ssl)) {
-    /* type == SSL3_RT_APPLICATION_DATA */
-    i = ssl->handshake_func(ssl);
-    if (i < 0) {
-      return i;
-    }
-    if (i == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-  }
-
 start:
-  ssl->rwstate = SSL_NOTHING;
-
   /* ssl->s3->rrec.type     - is the type of record
    * ssl->s3->rrec.data     - data
    * ssl->s3->rrec.off      - offset into 'data' for next read
@@ -291,7 +277,6 @@
    * 'peek' mode) */
   if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
     rr->length = 0;
-    ssl->rwstate = SSL_NOTHING;
     return 0;
   }
 
@@ -300,7 +285,7 @@
     /* Make sure that we are not getting application data when we
      * are doing a handshake for the first time. */
     if (SSL_in_init(ssl) && (type == SSL3_RT_APPLICATION_DATA) &&
-        (ssl->aead_read_ctx == NULL)) {
+        (ssl->s3->aead_read_ctx == NULL)) {
       /* TODO(davidben): Is this check redundant with the handshake_func
        * check? */
       al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -338,11 +323,10 @@
 
   /* If we get here, then type != rr->type. */
 
-  /* If an alert record, process one alert out of the record. Note that we allow
-   * a single record to contain multiple alerts. */
+  /* If an alert record, process the alert. */
   if (rr->type == SSL3_RT_ALERT) {
-    /* Alerts may not be fragmented. */
-    if (rr->length < 2) {
+    /* Alerts records may not contain fragmented or multiple alerts. */
+    if (rr->length != 2) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
       goto f_err;
@@ -369,16 +353,14 @@
     }
 
     if (alert_level == SSL3_AL_WARNING) {
-      ssl->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
+        ssl->s3->clean_shutdown = 1;
         ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
         return 0;
       }
     } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
-      ssl->rwstate = SSL_NOTHING;
-      ssl->s3->fatal_alert = alert_descr;
       OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
       BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr);
       ERR_add_error_data(2, "SSL alert number ", tmp);
@@ -403,8 +385,10 @@
    * Application data must come in the encrypted epoch, and ChangeCipherSpec in
    * the unencrypted epoch (we never renegotiate). Other cases fall through and
    * fail with a fatal error. */
-  if ((rr->type == SSL3_RT_APPLICATION_DATA && ssl->aead_read_ctx != NULL) ||
-      (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC && ssl->aead_read_ctx == NULL)) {
+  if ((rr->type == SSL3_RT_APPLICATION_DATA &&
+       ssl->s3->aead_read_ctx != NULL) ||
+      (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC &&
+       ssl->s3->aead_read_ctx == NULL)) {
     rr->length = 0;
     goto start;
   }
@@ -458,39 +442,23 @@
 }
 
 int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) {
-  int i;
-
-  if (SSL_in_init(ssl) && !ssl->in_handshake) {
-    i = ssl->handshake_func(ssl);
-    if (i < 0) {
-      return i;
-    }
-    if (i == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-  }
+  assert(!SSL_in_init(ssl));
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG);
     return -1;
   }
 
-  i = dtls1_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf_, len,
-                        dtls1_use_current_epoch);
-  return i;
+  return dtls1_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf_, len,
+                           dtls1_use_current_epoch);
 }
 
 /* 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 *ssl, int type, const void *buf, int len,
                       enum dtls1_use_epoch_t use_epoch) {
-  int i;
-
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
-  ssl->rwstate = SSL_NOTHING;
-  i = do_dtls1_write(ssl, type, buf, len, use_epoch);
-  return i;
+  return do_dtls1_write(ssl, type, buf, len, use_epoch);
 }
 
 static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf,
@@ -537,42 +505,35 @@
 }
 
 int dtls1_dispatch_alert(SSL *ssl) {
-  int i, j;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-  uint8_t buf[DTLS1_AL_HEADER_LENGTH];
-  uint8_t *ptr = &buf[0];
-
   ssl->s3->alert_dispatch = 0;
-
-  memset(buf, 0x00, sizeof(buf));
-  *ptr++ = ssl->s3->send_alert[0];
-  *ptr++ = ssl->s3->send_alert[1];
-
-  i = do_dtls1_write(ssl, SSL3_RT_ALERT, &buf[0], sizeof(buf),
-                     dtls1_use_current_epoch);
-  if (i <= 0) {
+  int ret = do_dtls1_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2,
+                           dtls1_use_current_epoch);
+  if (ret <= 0) {
     ssl->s3->alert_dispatch = 1;
-  } else {
-    if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
-      (void)BIO_flush(ssl->wbio);
-    }
-
-    if (ssl->msg_callback) {
-      ssl->msg_callback(1, ssl->version, SSL3_RT_ALERT, ssl->s3->send_alert, 2,
-                        ssl, ssl->msg_callback_arg);
-    }
-
-    if (ssl->info_callback != NULL) {
-      cb = ssl->info_callback;
-    } else if (ssl->ctx->info_callback != NULL) {
-      cb = ssl->ctx->info_callback;
-    }
-
-    if (cb != NULL) {
-      j = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
-      cb(ssl, SSL_CB_WRITE_ALERT, j);
-    }
+    return ret;
   }
 
-  return i;
+  /* If the alert is fatal, flush the BIO now. */
+  if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
+    BIO_flush(ssl->wbio);
+  }
+
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_ALERT,
+                      ssl->s3->send_alert, 2, ssl, ssl->msg_callback_arg);
+  }
+
+  void (*cb)(const SSL *ssl, int type, int value) = NULL;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
+  }
+
+  if (cb != NULL) {
+    int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+    cb(ssl, SSL_CB_WRITE_ALERT, alert);
+  }
+
+  return 1;
 }
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index 5dba8ef..e7b1607 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -116,12 +116,10 @@
 
 #include <openssl/ssl.h>
 
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
-#include <openssl/obj.h>
 
 #include "internal.h"
 
@@ -212,7 +210,7 @@
     return ssl->srtp_profiles;
   }
 
-  if (ssl->ctx != NULL && ssl->ctx->srtp_profiles != NULL) {
+  if (ssl->ctx->srtp_profiles != NULL) {
     return ssl->ctx->srtp_profiles;
   }
 
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
index 3ba9411..d353281 100644
--- a/src/ssl/d1_srvr.c
+++ b/src/ssl/d1_srvr.c
@@ -115,7 +115,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 
 #include <openssl/bn.h>
 #include <openssl/buf.h>
@@ -123,7 +122,6 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/md5.h>
-#include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 
@@ -141,7 +139,6 @@
   assert(ssl->server);
   assert(SSL_IS_DTLS(ssl));
 
-  ERR_clear_error();
   ERR_clear_system_error();
 
   if (ssl->info_callback != NULL) {
@@ -150,8 +147,6 @@
     cb = ssl->ctx->info_callback;
   }
 
-  ssl->in_handshake++;
-
   for (;;) {
     state = ssl->state;
 
@@ -307,12 +302,11 @@
         break;
 
       case SSL3_ST_SW_FLUSH:
-        ssl->rwstate = SSL_WRITING;
         if (BIO_flush(ssl->wbio) <= 0) {
+          ssl->rwstate = SSL_WRITING;
           ret = -1;
           goto end;
         }
-        ssl->rwstate = SSL_NOTHING;
         ssl->state = ssl->s3->tmp.next_state;
         break;
 
@@ -355,7 +349,7 @@
           goto end;
         }
 
-        if (!ssl3_do_change_cipher_spec(ssl)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_READ)) {
           ret = -1;
           goto end;
         }
@@ -393,12 +387,6 @@
 
       case SSL3_ST_SW_CHANGE_A:
       case SSL3_ST_SW_CHANGE_B:
-        ssl->session->cipher = ssl->s3->tmp.new_cipher;
-        if (!ssl->enc_method->setup_key_block(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
         ret = dtls1_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
                                             SSL3_ST_SW_CHANGE_B);
 
@@ -409,8 +397,7 @@
         ssl->state = SSL3_ST_SW_FINISHED_A;
         ssl->init_num = 0;
 
-        if (!ssl->enc_method->change_cipher_state(
-                ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -419,9 +406,7 @@
       case SSL3_ST_SW_FINISHED_A:
       case SSL3_ST_SW_FINISHED_B:
         ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
-                                 SSL3_ST_SW_FINISHED_B,
-                                 ssl->enc_method->server_finished_label,
-                                 ssl->enc_method->server_finished_label_len);
+                                 SSL3_ST_SW_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
@@ -476,7 +461,6 @@
   }
 
 end:
-  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.c
index 940494a..eaf6df7 100644
--- a/src/ssl/dtls_record.c
+++ b/src/ssl/dtls_record.c
@@ -213,7 +213,7 @@
 
   /* Decrypt the body. */
   size_t plaintext_len;
-  if (!SSL_AEAD_CTX_open(ssl->aead_read_ctx, out, &plaintext_len, max_out,
+  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, &plaintext_len, max_out,
                          type, version, sequence, CBS_data(&body),
                          CBS_len(&body))) {
     /* Bad packets are silently dropped in DTLS. See section 4.2.1 of RFC 6347.
@@ -250,7 +250,7 @@
                      enum dtls1_use_epoch_t use_epoch) {
   /* Determine the parameters for the current epoch. */
   uint16_t epoch = ssl->d1->w_epoch;
-  SSL_AEAD_CTX *aead = ssl->aead_write_ctx;
+  SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx;
   uint8_t *seq = ssl->s3->write_sequence;
   if (use_epoch == dtls1_use_previous_epoch) {
     /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
@@ -286,7 +286,7 @@
   if (!SSL_AEAD_CTX_seal(aead, out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len,
                          max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version,
                          &out[3] /* seq */, in, in_len) ||
-      !ssl3_record_sequence_update(&seq[2], 6)) {
+      !ssl_record_sequence_update(&seq[2], 6)) {
     return 0;
   }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 5acb598..982f304 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -346,6 +346,10 @@
 
 /* Record layer. */
 
+/* ssl_record_sequence_update increments the sequence number in |seq|. It
+ * returns one on success and zero on wraparound. */
+int ssl_record_sequence_update(uint8_t *seq, size_t seq_len);
+
 /* ssl_record_prefix_len returns the length of the prefix before the ciphertext
  * of a record for |ssl|.
  *
@@ -432,6 +436,14 @@
                      uint8_t type, const uint8_t *in, size_t in_len,
                      enum dtls1_use_epoch_t use_epoch);
 
+/* ssl_set_read_state sets |ssl|'s read cipher state to |aead_ctx|. It takes
+ * ownership of |aead_ctx|. */
+void ssl_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
+
+/* ssl_set_write_state sets |ssl|'s write cipher state to |aead_ctx|. It takes
+ * ownership of |aead_ctx|. */
+void ssl_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
+
 
 /* Private key operations. */
 
@@ -519,7 +531,7 @@
 #define SSL_CURVE_SECP256R1 23
 #define SSL_CURVE_SECP384R1 24
 #define SSL_CURVE_SECP521R1 25
-#define SSL_CURVE_ECDH_X25519 29
+#define SSL_CURVE_X25519 29
 
 /* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
  * TLS. */
@@ -735,12 +747,6 @@
 
 /* Check if an SSL structure is using DTLS */
 #define SSL_IS_DTLS(ssl) (ssl->method->is_dtls)
-/* See if we need explicit IV */
-#define SSL_USE_EXPLICIT_IV(ssl) \
-  (ssl->enc_method->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV)
-/* See if we use signature algorithms extension and signature algorithm before
- * signatures. */
-#define SSL_USE_SIGALGS(ssl) (ssl->enc_method->enc_flags & SSL_ENC_FLAG_SIGALGS)
 
 /* From RFC4492, used in encoding the curve type in ECParameters */
 #define NAMED_CURVE_TYPE 3
@@ -797,6 +803,10 @@
    * supported signature algorithms or curves. */
   int (*cert_cb)(SSL *ssl, void *arg);
   void *cert_cb_arg;
+
+  /* Optional X509_STORE for certificate validation. If NULL the parent SSL_CTX
+   * store is used instead. */
+  X509_STORE *verify_store;
 } CERT;
 
 /* SSL_METHOD is a compatibility structure to support the legacy version-locked
@@ -840,22 +850,16 @@
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
  * of a mess of functions, but hell, think of it as an opaque structure. */
 struct ssl3_enc_method {
-  int (*prf)(SSL *, uint8_t *, size_t, const uint8_t *, size_t, const char *,
-             size_t, const uint8_t *, size_t, const uint8_t *, size_t);
-  int (*setup_key_block)(SSL *);
-  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 *);
+  /* prf computes the PRF function for |ssl|. It writes |out_len| bytes to
+   * |out|, using |secret| as the secret and |label| as the label. |seed1| and
+   * |seed2| are concatenated to form the seed parameter. It returns one on
+   * success and zero on failure. */
+  int (*prf)(const SSL *ssl, uint8_t *out, size_t out_len,
+             const uint8_t *secret, size_t secret_len, const char *label,
+             size_t label_len, const uint8_t *seed1, size_t seed1_len,
+             const uint8_t *seed2, size_t seed2_len);
+  int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out);
   int (*cert_verify_mac)(SSL *, int, uint8_t *);
-  const char *client_finished_label;
-  int client_finished_label_len;
-  const char *server_finished_label;
-  int server_finished_label_len;
-  int (*alert_value)(int);
-  int (*export_keying_material)(SSL *, uint8_t *, size_t, const char *, size_t,
-                                const uint8_t *, size_t, int use_context);
-  /* Various flags indicating protocol version requirements */
-  unsigned int enc_flags;
 };
 
 #define SSL_HM_HEADER_LENGTH(ssl) ssl->method->hhlen
@@ -865,15 +869,6 @@
   ssl->method->set_handshake_header(ssl, htype, len)
 #define ssl_do_write(ssl) ssl->method->do_write(ssl)
 
-/* Values for enc_flags */
-
-/* Uses explicit IV for CBC mode */
-#define SSL_ENC_FLAG_EXPLICIT_IV 0x1
-/* Uses signature algorithms extension */
-#define SSL_ENC_FLAG_SIGALGS 0x2
-/* Uses SHA256 default PRF */
-#define SSL_ENC_FLAG_SHA256_PRF 0x4
-
 /* lengths of messages */
 #define DTLS1_COOKIE_LENGTH 256
 
@@ -966,12 +961,9 @@
 } DTLS1_STATE;
 
 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 SRTP_PROTECTION_PROFILE kSRTPProfiles[];
 
-void ssl_clear_cipher_ctx(SSL *ssl);
 int ssl_clear_bad_session(SSL *ssl);
 CERT *ssl_cert_new(void);
 CERT *ssl_cert_dup(CERT *cert);
@@ -1030,10 +1022,6 @@
 int ssl3_send_certificate_status(SSL *ssl);
 int ssl3_get_finished(SSL *ssl, int state_a, int state_b);
 int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
-int ssl3_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
-             size_t secret_len, const char *label, size_t label_len,
-             const uint8_t *seed1, size_t seed1_len,
-             const uint8_t *seed2, size_t seed2_len);
 void ssl3_cleanup_key_block(SSL *ssl);
 int ssl3_do_write(SSL *ssl, int type);
 int ssl3_send_alert(SSL *ssl, int level, int desc);
@@ -1054,7 +1042,7 @@
 int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
                           const EVP_MD **out_md, int pkey_type);
 
-int ssl3_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
+int ssl3_send_finished(SSL *ssl, int a, int b);
 int ssl3_supports_cipher(const SSL_CIPHER *cipher);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
@@ -1063,8 +1051,6 @@
 int ssl3_read_bytes(SSL *ssl, int type, uint8_t *buf, int len, int peek);
 int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
 int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
-int ssl3_final_finish_mac(SSL *ssl, const char *sender, int slen, uint8_t *p);
-int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p);
 int ssl3_output_cert_chain(SSL *ssl);
 const SSL_CIPHER *ssl3_choose_cipher(
     SSL *ssl, STACK_OF(SSL_CIPHER) *clnt,
@@ -1075,12 +1061,6 @@
 int ssl3_accept(SSL *ssl);
 int ssl3_connect(SSL *ssl);
 
-/* 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);
-
 int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len);
 int ssl3_handshake_write(SSL *ssl);
 
@@ -1159,29 +1139,11 @@
 int ssl_init_wbio_buffer(SSL *ssl, int push);
 void ssl_free_wbio_buffer(SSL *ssl);
 
-/* tls1_prf computes the TLS PRF function for |ssl| as described in RFC 5246,
- * section 5 and RFC 2246 section 5. It writes |out_len| bytes to |out|, using
- * |secret| as the secret and |label| as the label. |seed1| and |seed2| are
- * concatenated to form the seed parameter. It returns one on success and zero
- * on failure. */
-int tls1_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
-             size_t secret_len, const char *label, size_t label_len,
-             const uint8_t *seed1, size_t seed1_len,
-             const uint8_t *seed2, size_t seed2_len);
-
 int tls1_change_cipher_state(SSL *ssl, int which);
 int tls1_setup_key_block(SSL *ssl);
 int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
-int tls1_final_finish_mac(SSL *ssl, const char *str, int slen, uint8_t *p);
-int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p);
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
                                 size_t premaster_len);
-int tls1_export_keying_material(SSL *ssl, 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);
 
 char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx);
 
@@ -1219,13 +1181,13 @@
 
 #define tlsext_tick_md EVP_sha256
 
-/* tls_process_ticket processes the session ticket extension. On success, it
- * sets |*out_session| to the decrypted session or NULL if the ticket was
- * rejected. It sets |*out_send_ticket| to whether a new ticket should be sent
- * at the end of the handshake. It returns one on success and zero on fatal
+/* tls_process_ticket processes a session ticket from the client. On success,
+ * it sets |*out_session| to the decrypted session or NULL if the ticket was
+ * rejected. If the ticket was valid, it sets |*out_renew_ticket| to whether
+ * the ticket should be renewed. It returns one on success and zero on fatal
  * error. */
 int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
-                       int *out_send_ticket, const uint8_t *ticket,
+                       int *out_renew_ticket, const uint8_t *ticket,
                        size_t ticket_len, const uint8_t *session_id,
                        size_t session_id_len);
 
@@ -1294,9 +1256,13 @@
  *
  * TODO(davidben): To normalize some DTLS-specific code, move away from using
  * the wire version except at API boundaries. */
-uint16_t ssl3_version_from_wire(SSL *ssl, uint16_t wire_version);
+uint16_t ssl3_version_from_wire(const SSL *ssl, uint16_t wire_version);
 
-uint32_t ssl_get_algorithm_prf(SSL *ssl);
+/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
+ * call this function before the version is determined. */
+uint16_t ssl3_protocol_version(const SSL *ssl);
+
+uint32_t ssl_get_algorithm_prf(const SSL *ssl);
 int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *sigalgs);
 
 /* tls1_choose_signing_digest returns a digest for use with |ssl|'s private key
diff --git a/src/ssl/pqueue/pqueue.c b/src/ssl/pqueue/pqueue.c
index 14bd9b6..e689761 100644
--- a/src/ssl/pqueue/pqueue.c
+++ b/src/ssl/pqueue/pqueue.c
@@ -69,7 +69,7 @@
 
 
 pitem *pitem_new(uint8_t prio64be[8], void *data) {
-  pitem *item = (pitem *)OPENSSL_malloc(sizeof(pitem));
+  pitem *item = OPENSSL_malloc(sizeof(pitem));
   if (item == NULL) {
     return NULL;
   }
@@ -91,7 +91,7 @@
 }
 
 pqueue pqueue_new(void) {
-  pqueue_s *pq = (pqueue_s *)OPENSSL_malloc(sizeof(pqueue_s));
+  pqueue_s *pq = OPENSSL_malloc(sizeof(pqueue_s));
   if (pq == NULL) {
     return NULL;
   }
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 01a7e8c..5d364ab 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -114,7 +114,6 @@
 
 #include <assert.h>
 #include <limits.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -122,7 +121,7 @@
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/md5.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/rand.h>
 #include <openssl/sha.h>
 #include <openssl/x509.h>
@@ -156,15 +155,15 @@
   return 0;
 }
 
-int ssl3_send_finished(SSL *ssl, int a, int b, const char *sender, int slen) {
+int ssl3_send_finished(SSL *ssl, int a, int b) {
   uint8_t *p;
   int n;
 
   if (ssl->state == a) {
     p = ssl_handshake_start(ssl);
 
-    n = ssl->enc_method->final_finish_mac(ssl, sender, slen,
-                                          ssl->s3->tmp.finish_md);
+    n = ssl->s3->enc_method->final_finish_mac(ssl, ssl->server,
+                                              ssl->s3->tmp.finish_md);
     if (n == 0) {
       return 0;
     }
@@ -202,25 +201,14 @@
 /* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen
  * so far. */
 static void ssl3_take_mac(SSL *ssl) {
-  const char *sender;
-  int slen;
-
   /* If no new cipher setup then return immediately: other functions will set
    * the appropriate error. */
   if (ssl->s3->tmp.new_cipher == NULL) {
     return;
   }
 
-  if (ssl->state & SSL_ST_CONNECT) {
-    sender = ssl->enc_method->server_finished_label;
-    slen = ssl->enc_method->server_finished_label_len;
-  } else {
-    sender = ssl->enc_method->client_finished_label;
-    slen = ssl->enc_method->client_finished_label_len;
-  }
-
-  ssl->s3->tmp.peer_finish_md_len = ssl->enc_method->final_finish_mac(
-      ssl, sender, slen, ssl->s3->tmp.peer_finish_md);
+  ssl->s3->tmp.peer_finish_md_len = ssl->s3->enc_method->final_finish_mac(
+      ssl, !ssl->server, ssl->s3->tmp.peer_finish_md);
 }
 
 int ssl3_get_finished(SSL *ssl, int a, int b) {
@@ -250,7 +238,12 @@
     goto f_err;
   }
 
-  if (CRYPTO_memcmp(p, ssl->s3->tmp.peer_finish_md, finished_len) != 0) {
+  int finished_ret =
+      CRYPTO_memcmp(p, ssl->s3->tmp.peer_finish_md, finished_len);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  finished_ret = 0;
+#endif
+  if (finished_ret != 0) {
     al = SSL_AD_DECRYPT_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     goto f_err;
@@ -277,13 +270,6 @@
   return 0;
 }
 
-/* for these 2 messages, we need to
- * ssl->enc_read_ctx      re-init
- * ssl->s3->read_sequence   zero
- * ssl->s3->read_mac_secret   re-init
- * ssl->session->read_sym_enc   assign
- * ssl->session->read_compression assign
- * ssl->session->read_hash    assign */
 int ssl3_send_change_cipher_spec(SSL *ssl, int a, int b) {
   if (ssl->state == a) {
     *((uint8_t *)ssl->init_buf->data) = SSL3_MT_CCS;
@@ -407,7 +393,6 @@
     int bytes_read =
         ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], n, 0);
     if (bytes_read <= 0) {
-      ssl->rwstate = SSL_READING;
       *ok = 0;
       return bytes_read;
     }
@@ -452,7 +437,7 @@
   /* For TLS v1.2 send signature algorithm and signature using
    * agreed digest and cached handshake records. Otherwise, use
    * SHA1 or MD5 + SHA1 depending on key type.  */
-  if (SSL_USE_SIGALGS(ssl)) {
+  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     EVP_MD_CTX mctx;
     unsigned len;
 
@@ -467,15 +452,15 @@
     }
     *out_len = len;
   } else if (pkey_type == EVP_PKEY_RSA) {
-    if (ssl->enc_method->cert_verify_mac(ssl, NID_md5, out) == 0 ||
-        ssl->enc_method->cert_verify_mac(ssl, NID_sha1,
-                                         out + MD5_DIGEST_LENGTH) == 0) {
+    if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_md5, out) == 0 ||
+        ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1,
+                                             out + MD5_DIGEST_LENGTH) == 0) {
       return 0;
     }
     *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH;
     *out_md = EVP_md5_sha1();
   } else if (pkey_type == EVP_PKEY_EC) {
-    if (ssl->enc_method->cert_verify_mac(ssl, NID_sha1, out) == 0) {
+    if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1, out) == 0) {
       return 0;
     }
     *out_len = SHA_DIGEST_LENGTH;
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 5f68037..6f381cf 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -150,7 +150,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/bn.h>
@@ -163,7 +162,6 @@
 #include <openssl/evp.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
@@ -182,7 +180,6 @@
   assert(!ssl->server);
   assert(!SSL_IS_DTLS(ssl));
 
-  ERR_clear_error();
   ERR_clear_system_error();
 
   if (ssl->info_callback != NULL) {
@@ -191,8 +188,6 @@
     cb = ssl->ctx->info_callback;
   }
 
-  ssl->in_handshake++;
-
   for (;;) {
     state = ssl->state;
 
@@ -388,10 +383,7 @@
         }
         ssl->init_num = 0;
 
-        ssl->session->cipher = ssl->s3->tmp.new_cipher;
-        if (!ssl->enc_method->setup_key_block(ssl) ||
-            !ssl->enc_method->change_cipher_state(
-                ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -424,9 +416,7 @@
       case SSL3_ST_CW_FINISHED_A:
       case SSL3_ST_CW_FINISHED_B:
         ret = ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A,
-                                 SSL3_ST_CW_FINISHED_B,
-                                 ssl->enc_method->client_finished_label,
-                                 ssl->enc_method->client_finished_label_len);
+                                 SSL3_ST_CW_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
@@ -487,7 +477,7 @@
           goto end;
         }
 
-        if (!ssl3_do_change_cipher_spec(ssl)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
           ret = -1;
           goto end;
         }
@@ -511,12 +501,11 @@
         break;
 
       case SSL3_ST_CW_FLUSH:
-        ssl->rwstate = SSL_WRITING;
         if (BIO_flush(ssl->wbio) <= 0) {
+          ssl->rwstate = SSL_WRITING;
           ret = -1;
           goto end;
         }
-        ssl->rwstate = SSL_NOTHING;
         ssl->state = ssl->s3->tmp.next_state;
         break;
 
@@ -581,7 +570,6 @@
   }
 
 end:
-  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(ssl, SSL_CB_CONNECT_EXIT, ret);
@@ -676,13 +664,12 @@
     ssl->client_version = max_version;
   }
 
-  /* If the configured session has expired or was created at a version higher
-   * than our maximum version, drop it. */
+  /* If the configured session has expired or was created at a disabled
+   * version, drop it. */
   if (ssl->session != NULL &&
       (ssl->session->session_id_length == 0 || ssl->session->not_resumable ||
        ssl->session->timeout < (long)(time(NULL) - ssl->session->time) ||
-       (!SSL_IS_DTLS(ssl) && ssl->session->ssl_version > ssl->version) ||
-       (SSL_IS_DTLS(ssl) && ssl->session->ssl_version < ssl->version))) {
+       !ssl3_is_version_enabled(ssl, ssl->session->ssl_version))) {
     SSL_set_session(ssl, NULL);
   }
 
@@ -791,8 +778,8 @@
       goto f_err;
     }
     ssl->version = server_version;
-    ssl->enc_method = ssl3_get_enc_method(server_version);
-    assert(ssl->enc_method != NULL);
+    ssl->s3->enc_method = ssl3_get_enc_method(server_version);
+    assert(ssl->s3->enc_method != NULL);
     /* At this point, the connection's version is known and ssl->version is
      * fixed. Begin enforcing the record-layer version. */
     ssl->s3->have_version = 1;
@@ -841,8 +828,7 @@
   /* If the cipher is disabled then we didn't sent it in the ClientHello, so if
    * the server selected it, it's an error. */
   if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
-      SSL_CIPHER_get_min_version(c) >
-          ssl3_version_from_wire(ssl, ssl->version)) {
+      SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     goto f_err;
@@ -867,6 +853,8 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
       goto f_err;
     }
+  } else {
+    ssl->session->cipher = c;
   }
   ssl->s3->tmp.new_cipher = c;
 
@@ -878,7 +866,7 @@
   /* If doing a full handshake with TLS 1.2, the server may request a client
    * certificate which requires hashing the handshake transcript under a
    * different hash. Otherwise, the handshake buffer may be released. */
-  if (!SSL_USE_SIGALGS(ssl) || ssl->hit) {
+  if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -1060,19 +1048,16 @@
 int ssl3_get_server_key_exchange(SSL *ssl) {
   EVP_MD_CTX md_ctx;
   int al, ok;
-  long n, alg_k, alg_a;
   EVP_PKEY *pkey = NULL;
-  const EVP_MD *md = NULL;
   DH *dh = NULL;
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
-  CBS server_key_exchange, server_key_exchange_orig, parameter;
 
   /* use same message size as in ssl3_get_certificate_request() as
    * ServerKeyExchange message may be skipped */
-  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_KEY_EXCH_A,
-                                 SSL3_ST_CR_KEY_EXCH_B, -1, ssl->max_cert_list,
-                                 ssl_hash_message, &ok);
+  long n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_CR_KEY_EXCH_A, SSL3_ST_CR_KEY_EXCH_B, -1, ssl->max_cert_list,
+      ssl_hash_message, &ok);
   if (!ok) {
     return n;
   }
@@ -1097,11 +1082,12 @@
   }
 
   /* Retain a copy of the original CBS to compute the signature over. */
+  CBS server_key_exchange;
   CBS_init(&server_key_exchange, ssl->init_msg, n);
-  server_key_exchange_orig = server_key_exchange;
+  CBS server_key_exchange_orig = server_key_exchange;
 
-  alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
-  alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
+  uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+  uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
   EVP_MD_CTX_init(&md_ctx);
 
   if (alg_a & SSL_aPSK) {
@@ -1224,6 +1210,7 @@
   /* At this point, |server_key_exchange| contains the signature, if any, while
    * |server_key_exchange_orig| contains the entire message. From that, derive
    * a CBS containing just the parameter. */
+  CBS parameter;
   CBS_init(&parameter, CBS_data(&server_key_exchange_orig),
            CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange));
 
@@ -1234,7 +1221,8 @@
       goto err;
     }
 
-    if (SSL_USE_SIGALGS(ssl)) {
+    const EVP_MD *md = NULL;
+    if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
       uint8_t hash, signature;
       if (!CBS_get_u8(&server_key_exchange, &hash) ||
           !CBS_get_u8(&server_key_exchange, &signature)) {
@@ -1261,15 +1249,20 @@
       goto f_err;
     }
 
-    if (!EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) ||
-        !EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->client_random,
-                                SSL3_RANDOM_SIZE) ||
-        !EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->server_random,
-                                SSL3_RANDOM_SIZE) ||
-        !EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&parameter),
-                                CBS_len(&parameter)) ||
-        !EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
-                               CBS_len(&signature))) {
+    int sig_ok = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) &&
+                 EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->client_random,
+                                        SSL3_RANDOM_SIZE) &&
+                 EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->server_random,
+                                        SSL3_RANDOM_SIZE) &&
+                 EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&parameter),
+                                        CBS_len(&parameter)) &&
+                 EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
+                                       CBS_len(&signature));
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+    sig_ok = 1;
+    ERR_clear_error();
+#endif
+    if (!sig_ok) {
       /* bad signature */
       al = SSL_AD_DECRYPT_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
@@ -1306,17 +1299,12 @@
 
 int ssl3_get_certificate_request(SSL *ssl) {
   int ok, ret = 0;
-  unsigned long n;
   X509_NAME *xn = NULL;
   STACK_OF(X509_NAME) *ca_sk = NULL;
-  CBS cbs;
-  CBS certificate_types;
-  CBS certificate_authorities;
-  const uint8_t *data;
 
-  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_CERT_REQ_A,
-                                 SSL3_ST_CR_CERT_REQ_B, -1, ssl->max_cert_list,
-                                 ssl_hash_message, &ok);
+  long n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_CR_CERT_REQ_A, SSL3_ST_CR_CERT_REQ_B, -1, ssl->max_cert_list,
+      ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1334,10 +1322,11 @@
 
   if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     goto err;
   }
 
+  CBS cbs;
   CBS_init(&cbs, ssl->init_msg, n);
 
   ca_sk = sk_X509_NAME_new(ca_dn_cmp);
@@ -1347,6 +1336,7 @@
   }
 
   /* get the certificate types */
+  CBS certificate_types;
   if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -1359,7 +1349,7 @@
     goto err;
   }
 
-  if (SSL_USE_SIGALGS(ssl)) {
+  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     CBS supported_signature_algorithms;
     if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
         !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
@@ -1370,6 +1360,7 @@
   }
 
   /* get the CA RDNs */
+  CBS certificate_authorities;
   if (!CBS_get_u16_length_prefixed(&cbs, &certificate_authorities)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH);
@@ -1385,25 +1376,13 @@
       goto err;
     }
 
-    data = CBS_data(&distinguished_name);
-
+    const uint8_t *data = CBS_data(&distinguished_name);
     /* A u16 length cannot overflow a long. */
     xn = d2i_X509_NAME(NULL, &data, (long)CBS_len(&distinguished_name));
-    if (xn == NULL) {
+    if (xn == NULL ||
+        data != CBS_data(&distinguished_name) + CBS_len(&distinguished_name)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-      goto err;
-    }
-
-    if (!CBS_skip(&distinguished_name, data - CBS_data(&distinguished_name))) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-
-    if (CBS_len(&distinguished_name) != 0) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_LENGTH_MISMATCH);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto err;
     }
 
@@ -1411,6 +1390,7 @@
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
+    xn = NULL;
   }
 
   /* we should setup a certificate to return.... */
@@ -1422,6 +1402,7 @@
   ret = 1;
 
 err:
+  X509_NAME_free(xn);
   sk_X509_NAME_pop_free(ca_sk, X509_NAME_free);
   return ret;
 }
@@ -1755,8 +1736,8 @@
   }
   ssl->state = SSL3_ST_CW_KEY_EXCH_B;
 
-  ssl->session->master_key_length = ssl->enc_method->generate_master_secret(
-      ssl, ssl->session->master_key, pms, pms_len);
+  ssl->session->master_key_length =
+      tls1_generate_master_secret(ssl, ssl->session->master_key, pms, pms_len);
   if (ssl->session->master_key_length == 0) {
     goto err;
   }
@@ -1768,6 +1749,7 @@
   return ssl_do_write(ssl);
 
 err:
+  CBB_cleanup(&cbb);
   if (pms != NULL) {
     OPENSSL_cleanse(pms, pms_len);
     OPENSSL_free(pms);
@@ -1794,7 +1776,7 @@
   if (ssl->state == SSL3_ST_CW_CERT_VRFY_A) {
     /* Select and write out the digest type in TLS 1.2. */
     const EVP_MD *md = NULL;
-    if (SSL_USE_SIGALGS(ssl)) {
+    if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
       md = tls1_choose_signing_digest(ssl);
       if (!tls12_add_sigandhash(ssl, &cbb, md)) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1828,7 +1810,8 @@
     /* Skip over the already written signature algorithm and retry the
      * signature. */
     uint8_t *ptr;
-    if ((SSL_USE_SIGALGS(ssl) && !CBB_did_write(&cbb, 2)) ||
+    if ((ssl3_protocol_version(ssl) >= TLS1_2_VERSION &&
+         !CBB_did_write(&cbb, 2)) ||
         !CBB_add_u16_length_prefixed(&cbb, &child) ||
         !CBB_reserve(&child, &ptr, max_sig_len)) {
       goto err;
@@ -1839,10 +1822,8 @@
 
   switch (sign_result) {
     case ssl_private_key_success:
-      ssl->rwstate = SSL_NOTHING;
       break;
     case ssl_private_key_failure:
-      ssl->rwstate = SSL_NOTHING;
       goto err;
     case ssl_private_key_retry:
       ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
@@ -1872,23 +1853,18 @@
 }
 
 int ssl3_send_client_certificate(SSL *ssl) {
-  X509 *x509 = NULL;
-  EVP_PKEY *pkey = NULL;
-  int i;
-
   if (ssl->state == SSL3_ST_CW_CERT_A) {
-    /* Let cert callback update client certificates if required */
+    /* Call cert_cb to update the certificate. */
     if (ssl->cert->cert_cb) {
-      i = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
-      if (i < 0) {
+      int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+      if (ret < 0) {
         ssl->rwstate = SSL_X509_LOOKUP;
         return -1;
       }
-      if (i == 0) {
+      if (ret == 0) {
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        return 0;
+        return -1;
       }
-      ssl->rwstate = SSL_NOTHING;
     }
 
     if (ssl3_has_client_certificate(ssl)) {
@@ -1898,52 +1874,42 @@
     }
   }
 
-  /* We need to get a client cert */
   if (ssl->state == SSL3_ST_CW_CERT_B) {
-    /* If we get an error, we need to:
-     *   ssl->rwstate=SSL_X509_LOOKUP; return(-1);
-     * We then get retried later */
-    i = ssl_do_client_cert_cb(ssl, &x509, &pkey);
-    if (i < 0) {
+    /* Call client_cert_cb to update the certificate. */
+    X509 *x509 = NULL;
+    EVP_PKEY *pkey = NULL;
+    int ret = ssl_do_client_cert_cb(ssl, &x509, &pkey);
+    if (ret < 0) {
       ssl->rwstate = SSL_X509_LOOKUP;
       return -1;
     }
-    ssl->rwstate = SSL_NOTHING;
-    if (i == 1 && pkey != NULL && x509 != NULL) {
-      ssl->state = SSL3_ST_CW_CERT_B;
-      if (!SSL_use_certificate(ssl, x509) || !SSL_use_PrivateKey(ssl, pkey)) {
-        i = 0;
-      }
-    } else if (i == 1) {
-      i = 0;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK);
-    }
 
+    int setup_error = ret == 1 && (!SSL_use_certificate(ssl, x509) ||
+                                   !SSL_use_PrivateKey(ssl, pkey));
     X509_free(x509);
     EVP_PKEY_free(pkey);
-    if (i && !ssl3_has_client_certificate(ssl)) {
-      i = 0;
-    }
-    if (i == 0) {
-      if (ssl->version == SSL3_VERSION) {
-        ssl->s3->tmp.cert_req = 0;
-        ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
-        return 1;
-      } else {
-        ssl->s3->tmp.cert_req = 2;
-        /* There is no client certificate, so the handshake buffer may be
-         * released. */
-        ssl3_free_handshake_buffer(ssl);
-      }
+    if (setup_error) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return -1;
     }
 
-    /* Ok, we have a cert */
     ssl->state = SSL3_ST_CW_CERT_C;
   }
 
   if (ssl->state == SSL3_ST_CW_CERT_C) {
-    if (ssl->s3->tmp.cert_req == 2) {
-      /* Send an empty Certificate message. */
+    if (!ssl3_has_client_certificate(ssl)) {
+      /* Without a client certificate, the handshake buffer may be released. */
+      ssl3_free_handshake_buffer(ssl);
+
+      if (ssl->version == SSL3_VERSION) {
+        /* In SSL 3.0, send no certificate by skipping both messages. */
+        ssl->s3->tmp.cert_req = 0;
+        ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
+        return 1;
+      }
+
+      /* In TLS, send an empty Certificate message. */
+      ssl->s3->tmp.cert_req = 2;
       uint8_t *p = ssl_handshake_start(ssl);
       l2n3(0, p);
       if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) {
@@ -1955,7 +1921,7 @@
     ssl->state = SSL3_ST_CW_CERT_D;
   }
 
-  /* SSL3_ST_CW_CERT_D */
+  assert(ssl->state == SSL3_ST_CW_CERT_D);
   return ssl_do_write(ssl);
 }
 
@@ -1967,7 +1933,7 @@
   assert(ssl->state == SSL3_ST_CW_NEXT_PROTO_A);
 
   static const uint8_t kZero[32] = {0};
-  size_t padding_len = 32 - ((ssl->next_proto_negotiated_len + 2) % 32);
+  size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
 
   CBB cbb, child;
   size_t length;
@@ -1975,8 +1941,8 @@
   if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
                       ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
       !CBB_add_u8_length_prefixed(&cbb, &child) ||
-      !CBB_add_bytes(&child, ssl->next_proto_negotiated,
-                     ssl->next_proto_negotiated_len) ||
+      !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated,
+                     ssl->s3->next_proto_negotiated_len) ||
       !CBB_add_u8_length_prefixed(&cbb, &child) ||
       !CBB_add_bytes(&child, kZero, padding_len) ||
       !CBB_finish(&cbb, NULL, &length) ||
@@ -2013,7 +1979,6 @@
     ssl->rwstate = SSL_CHANNEL_ID_LOOKUP;
     return -1;
   }
-  ssl->rwstate = SSL_NOTHING;
 
   EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
   if (ec_key == NULL) {
@@ -2075,7 +2040,15 @@
   if (ssl->ctx->client_cert_cb == NULL) {
     return 0;
   }
-  return ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey);
+
+  int ret = ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey);
+  if (ret <= 0) {
+    return ret;
+  }
+
+  assert(*out_x509 != NULL);
+  assert(*out_pkey != NULL);
+  return 1;
 }
 
 int ssl3_verify_server_cert(SSL *ssl) {
diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c
index 89d861a..04aa08c 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/s3_enc.c
@@ -136,39 +136,24 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/md5.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 
 #include "internal.h"
 
 
-static const uint8_t ssl3_pad_1[48] = {
-    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
-};
+static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
+                              size_t sender_len, uint8_t *p);
 
-static const uint8_t ssl3_pad_2[48] = {
-    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
-    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
-    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
-    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
-};
-
-static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, int len,
-                              uint8_t *p);
-
-int ssl3_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
-             size_t secret_len, const char *label, size_t label_len,
-             const uint8_t *seed1, size_t seed1_len,
-             const uint8_t *seed2, size_t seed2_len) {
+static int ssl3_prf(const SSL *ssl, uint8_t *out, size_t out_len,
+                    const uint8_t *secret, size_t secret_len, const char *label,
+                    size_t label_len, const uint8_t *seed1, size_t seed1_len,
+                    const uint8_t *seed2, size_t seed2_len) {
   EVP_MD_CTX md5;
   EVP_MD_CTX sha1;
   uint8_t buf[16], smd[SHA_DIGEST_LENGTH];
@@ -309,20 +294,23 @@
   return 1;
 }
 
-int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p) {
+static int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p) {
   return ssl3_handshake_mac(ssl, md_nid, NULL, 0, p);
 }
 
-int ssl3_final_finish_mac(SSL *ssl, const char *sender, int len, uint8_t *p) {
+static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
+  const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
+                                   : SSL3_MD_CLIENT_FINISHED_CONST;
+  const size_t sender_len = 4;
   int ret, sha1len;
-  ret = ssl3_handshake_mac(ssl, NID_md5, sender, len, p);
+  ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out);
   if (ret == 0) {
     return 0;
   }
 
-  p += ret;
+  out += ret;
 
-  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, len, p);
+  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out);
   if (sha1len == 0) {
     return 0;
   }
@@ -331,8 +319,8 @@
   return ret;
 }
 
-static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, int len,
-                              uint8_t *p) {
+static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
+                              size_t sender_len, uint8_t *p) {
   unsigned int ret;
   size_t npad, n;
   unsigned int i;
@@ -356,15 +344,29 @@
     return 0;
   }
 
+  static const uint8_t kPad1[48] = {
+      0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+      0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+      0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+      0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+  };
+
+  static const uint8_t kPad2[48] = {
+      0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+      0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+      0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+      0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+  };
+
   n = EVP_MD_CTX_size(&ctx);
 
   npad = (48 / n) * n;
   if (sender != NULL) {
-    EVP_DigestUpdate(&ctx, sender, len);
+    EVP_DigestUpdate(&ctx, sender, sender_len);
   }
   EVP_DigestUpdate(&ctx, ssl->session->master_key,
                    ssl->session->master_key_length);
-  EVP_DigestUpdate(&ctx, ssl3_pad_1, npad);
+  EVP_DigestUpdate(&ctx, kPad1, npad);
   EVP_DigestFinal_ex(&ctx, md_buf, &i);
 
   if (!EVP_DigestInit_ex(&ctx, EVP_MD_CTX_md(&ctx), NULL)) {
@@ -374,7 +376,7 @@
   }
   EVP_DigestUpdate(&ctx, ssl->session->master_key,
                    ssl->session->master_key_length);
-  EVP_DigestUpdate(&ctx, ssl3_pad_2, npad);
+  EVP_DigestUpdate(&ctx, kPad2, npad);
   EVP_DigestUpdate(&ctx, md_buf, i);
   EVP_DigestFinal_ex(&ctx, p, &ret);
 
@@ -383,114 +385,10 @@
   return ret;
 }
 
-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) {
-      return 1;
-    }
-  }
-  OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-  return 0;
-}
 
-int ssl3_alert_code(int code) {
-  switch (code) {
-    case SSL_AD_CLOSE_NOTIFY:
-      return SSL3_AD_CLOSE_NOTIFY;
 
-    case SSL_AD_UNEXPECTED_MESSAGE:
-      return SSL3_AD_UNEXPECTED_MESSAGE;
-
-    case SSL_AD_BAD_RECORD_MAC:
-      return SSL3_AD_BAD_RECORD_MAC;
-
-    case SSL_AD_DECRYPTION_FAILED:
-      return SSL3_AD_BAD_RECORD_MAC;
-
-    case SSL_AD_RECORD_OVERFLOW:
-      return SSL3_AD_BAD_RECORD_MAC;
-
-    case SSL_AD_DECOMPRESSION_FAILURE:
-      return SSL3_AD_DECOMPRESSION_FAILURE;
-
-    case SSL_AD_HANDSHAKE_FAILURE:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_NO_CERTIFICATE:
-      return SSL3_AD_NO_CERTIFICATE;
-
-    case SSL_AD_BAD_CERTIFICATE:
-      return SSL3_AD_BAD_CERTIFICATE;
-
-    case SSL_AD_UNSUPPORTED_CERTIFICATE:
-      return SSL3_AD_UNSUPPORTED_CERTIFICATE;
-
-    case SSL_AD_CERTIFICATE_REVOKED:
-      return SSL3_AD_CERTIFICATE_REVOKED;
-
-    case SSL_AD_CERTIFICATE_EXPIRED:
-      return SSL3_AD_CERTIFICATE_EXPIRED;
-
-    case SSL_AD_CERTIFICATE_UNKNOWN:
-      return SSL3_AD_CERTIFICATE_UNKNOWN;
-
-    case SSL_AD_ILLEGAL_PARAMETER:
-      return SSL3_AD_ILLEGAL_PARAMETER;
-
-    case SSL_AD_UNKNOWN_CA:
-      return SSL3_AD_BAD_CERTIFICATE;
-
-    case SSL_AD_ACCESS_DENIED:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_DECODE_ERROR:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_DECRYPT_ERROR:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_EXPORT_RESTRICTION:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_PROTOCOL_VERSION:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_INSUFFICIENT_SECURITY:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_INTERNAL_ERROR:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_USER_CANCELLED:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_NO_RENEGOTIATION:
-      return -1; /* Don't send it. */
-
-    case SSL_AD_UNSUPPORTED_EXTENSION:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_CERTIFICATE_UNOBTAINABLE:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_UNRECOGNIZED_NAME:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_BAD_CERTIFICATE_HASH_VALUE:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_UNKNOWN_PSK_IDENTITY:
-      return TLS1_AD_UNKNOWN_PSK_IDENTITY;
-
-    case SSL_AD_INAPPROPRIATE_FALLBACK:
-      return SSL3_AD_INAPPROPRIATE_FALLBACK;
-
-    default:
-      return -1;
-  }
-}
+const SSL3_ENC_METHOD SSLv3_enc_data = {
+    ssl3_prf,
+    ssl3_final_finish_mac,
+    ssl3_cert_verify_mac,
+};
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 64f9f8c..7df046f 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -149,7 +149,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -158,25 +157,11 @@
 #include <openssl/err.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 
 #include "internal.h"
 
 
-const SSL3_ENC_METHOD SSLv3_enc_data = {
-    ssl3_prf,
-    tls1_setup_key_block,
-    tls1_generate_master_secret,
-    tls1_change_cipher_state,
-    ssl3_final_finish_mac,
-    ssl3_cert_verify_mac,
-    SSL3_MD_CLIENT_FINISHED_CONST, 4,
-    SSL3_MD_SERVER_FINISHED_CONST, 4,
-    ssl3_alert_code,
-    tls1_export_keying_material,
-    0,
-};
-
 int ssl3_supports_cipher(const SSL_CIPHER *cipher) {
   return 1;
 }
@@ -211,11 +196,11 @@
 
   ssl->s3 = s3;
 
-  /* Set the version to the highest supported version for TLS. This controls the
-   * initial state of |ssl->enc_method| and what the API reports as the version
-   * prior to negotiation.
+  /* Set the version to the highest supported version.
    *
-   * TODO(davidben): This is fragile and confusing. */
+   * TODO(davidben): Move this field into |s3|, have it store the normalized
+   * protocol version, and implement this pre-negotiation quirk in |SSL_version|
+   * at the API boundary rather than in internal state. */
   ssl->version = TLS1_2_VERSION;
   return 1;
 err:
@@ -239,7 +224,10 @@
   OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
   ssl3_free_handshake_buffer(ssl);
   ssl3_free_handshake_hash(ssl);
+  OPENSSL_free(ssl->s3->next_proto_negotiated);
   OPENSSL_free(ssl->s3->alpn_selected);
+  SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
+  SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
 
   OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3);
   OPENSSL_free(ssl->s3);
@@ -262,7 +250,7 @@
   return 0;
 }
 
-int SSL_need_rsa(const SSL *ssl) {
+int SSL_need_tmp_RSA(const SSL *ssl) {
   return 0;
 }
 
@@ -322,23 +310,28 @@
   return 1;
 }
 
+static int is_p256_key(EVP_PKEY *private_key) {
+  const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key);
+  return ec_key != NULL &&
+         EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) ==
+             NID_X9_62_prime256v1;
+}
+
 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) {
+  if (!is_p256_key(private_key)) {
     OPENSSL_PUT_ERROR(SSL, 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);
+  ctx->tlsext_channel_id_enabled = 1;
+
   return 1;
 }
 
 int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
-  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key);
-  if (ec_key == NULL ||
-      EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) !=
-          NID_X9_62_prime256v1) {
+  if (!is_p256_key(private_key)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
     return 0;
   }
@@ -365,7 +358,9 @@
   if (name == NULL) {
     return 1;
   }
-  if (strlen(name) > TLSEXT_MAXLEN_host_name) {
+
+  size_t len = strlen(name);
+  if (len == 0 || len > TLSEXT_MAXLEN_host_name) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
     return 0;
   }
@@ -452,17 +447,15 @@
     return ssl->cipher_list;
   }
 
-  if (ssl->version >= TLS1_1_VERSION && ssl->ctx != NULL &&
-      ssl->ctx->cipher_list_tls11 != NULL) {
+  if (ssl->version >= TLS1_1_VERSION && ssl->ctx->cipher_list_tls11 != NULL) {
     return ssl->ctx->cipher_list_tls11;
   }
 
-  if (ssl->version >= TLS1_VERSION && ssl->ctx != NULL &&
-      ssl->ctx->cipher_list_tls10 != NULL) {
+  if (ssl->version >= TLS1_VERSION && ssl->ctx->cipher_list_tls10 != NULL) {
     return ssl->ctx->cipher_list_tls10;
   }
 
-  if (ssl->ctx != NULL && ssl->ctx->cipher_list != NULL) {
+  if (ssl->ctx->cipher_list != NULL) {
     return ssl->ctx->cipher_list;
   }
 
@@ -505,8 +498,7 @@
     ok = 1;
 
     /* Check the TLS version. */
-    if (SSL_CIPHER_get_min_version(c) >
-        ssl3_version_from_wire(ssl, ssl->version)) {
+    if (SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl)) {
       ok = 0;
     }
 
@@ -578,10 +570,10 @@
 
 /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
  * handshake macs if required. */
-uint32_t ssl_get_algorithm_prf(SSL *ssl) {
+uint32_t ssl_get_algorithm_prf(const SSL *ssl) {
   uint32_t algorithm_prf = ssl->s3->tmp.new_cipher->algorithm_prf;
-  if (ssl->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF &&
-      algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) {
+  if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT &&
+      ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     return SSL_HANDSHAKE_MAC_SHA256;
   }
   return algorithm_prf;
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 4c1133c..d9c21d4 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -110,7 +110,6 @@
 
 #include <assert.h>
 #include <limits.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -183,6 +182,8 @@
 }
 
 int ssl3_write_app_data(SSL *ssl, const void *buf, int len) {
+  assert(!SSL_in_init(ssl) || SSL_in_false_start(ssl));
+
   return ssl3_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len);
 }
 
@@ -190,25 +191,12 @@
  * not all data has been sent or non-blocking IO. */
 int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, int len) {
   const uint8_t *buf = buf_;
-  unsigned int tot, n, nw;
-  int i;
+  unsigned tot, n, nw;
 
-  ssl->rwstate = SSL_NOTHING;
   assert(ssl->s3->wnum <= INT_MAX);
   tot = ssl->s3->wnum;
   ssl->s3->wnum = 0;
 
-  if (!ssl->in_handshake && SSL_in_init(ssl) && !SSL_in_false_start(ssl)) {
-    i = ssl->handshake_func(ssl);
-    if (i < 0) {
-      return i;
-    }
-    if (i == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-  }
-
   /* Ensure that if we end up with a smaller value of data to write out than
    * the the original len from a write which didn't complete for non-blocking
    * I/O and also somehow ended up avoiding the check for this in
@@ -232,19 +220,19 @@
       nw = n;
     }
 
-    i = do_ssl3_write(ssl, type, &buf[tot], nw);
-    if (i <= 0) {
+    int ret = do_ssl3_write(ssl, type, &buf[tot], nw);
+    if (ret <= 0) {
       ssl->s3->wnum = tot;
-      return i;
+      return ret;
     }
 
-    if (i == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
-                        (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
-      return tot + i;
+    if (ret == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
+                          (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
+      return tot + ret;
     }
 
-    n -= i;
-    tot += i;
+    n -= ret;
+    tot += ret;
   }
 }
 
@@ -315,6 +303,7 @@
 }
 
 int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) {
+  assert(!SSL_in_init(ssl));
   return ssl3_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
@@ -387,26 +376,7 @@
     return -1;
   }
 
-  /* This may require multiple iterations. False Start will cause
-   * |ssl->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 (!ssl->in_handshake && SSL_in_init(ssl)) {
-    assert(type == SSL3_RT_APPLICATION_DATA);
-    i = ssl->handshake_func(ssl);
-    if (i < 0) {
-      return i;
-    }
-    if (i == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-  }
-
 start:
-  ssl->rwstate = SSL_NOTHING;
-
   /* ssl->s3->rrec.type    - is the type of record
    * ssl->s3->rrec.data    - data
    * ssl->s3->rrec.off     - offset into 'data' for next read
@@ -427,7 +397,6 @@
    * 'peek' mode) */
   if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
     rr->length = 0;
-    ssl->rwstate = SSL_NOTHING;
     return 0;
   }
 
@@ -437,7 +406,7 @@
     /* Make sure that we are not getting application data when we are doing a
      * handshake for the first time. */
     if (SSL_in_init(ssl) && type == SSL3_RT_APPLICATION_DATA &&
-        ssl->aead_read_ctx == NULL) {
+        ssl->s3->aead_read_ctx == NULL) {
       /* TODO(davidben): Is this check redundant with the handshake_func
        * check? */
       al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -546,11 +515,10 @@
     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 an alert record, process the alert. */
   if (rr->type == SSL3_RT_ALERT) {
-    /* Alerts may not be fragmented. */
-    if (rr->length < 2) {
+    /* Alerts records may not contain fragmented or multiple alerts. */
+    if (rr->length != 2) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
       goto f_err;
@@ -577,25 +545,12 @@
     }
 
     if (alert_level == SSL3_AL_WARNING) {
-      ssl->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
+        ssl->s3->clean_shutdown = 1;
         ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
         return 0;
       }
 
-      /* This is a warning but we receive it if we requested renegotiation and
-       * the peer denied it. Terminate with a fatal alert because if
-       * application tried to renegotiatie it presumably had a good reason and
-       * expects it to succeed.
-       *
-       * In future we might have a renegotiation where we don't care if the
-       * peer refused it where we carry on. */
-      else if (alert_descr == SSL_AD_NO_RENEGOTIATION) {
-        al = SSL_AD_HANDSHAKE_FAILURE;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
-        goto f_err;
-      }
-
       ssl->s3->warning_alert_count++;
       if (ssl->s3->warning_alert_count > kMaxWarningAlerts) {
         al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -605,8 +560,6 @@
     } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
-      ssl->rwstate = SSL_NOTHING;
-      ssl->s3->fatal_alert = alert_descr;
       OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
       BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
       ERR_add_error_data(2, "SSL alert number ", tmp);
@@ -637,46 +590,7 @@
   return -1;
 }
 
-int ssl3_do_change_cipher_spec(SSL *ssl) {
-  int i;
-
-  if (ssl->state & SSL_ST_ACCEPT) {
-    i = SSL3_CHANGE_CIPHER_SERVER_READ;
-  } else {
-    i = SSL3_CHANGE_CIPHER_CLIENT_READ;
-  }
-
-  if (ssl->s3->tmp.key_block == NULL) {
-    if (ssl->session == NULL || ssl->session->master_key_length == 0) {
-      /* might happen if dtls1_read_bytes() calls this */
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY);
-      return 0;
-    }
-
-    ssl->session->cipher = ssl->s3->tmp.new_cipher;
-    if (!ssl->enc_method->setup_key_block(ssl)) {
-      return 0;
-    }
-  }
-
-  if (!ssl->enc_method->change_cipher_state(ssl, i)) {
-    return 0;
-  }
-
-  return 1;
-}
-
 int ssl3_send_alert(SSL *ssl, int level, int desc) {
-  /* Map tls/ssl alert value to correct one */
-  desc = ssl->enc_method->alert_value(desc);
-  if (ssl->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION) {
-    /* SSL 3.0 does not have protocol_version alerts */
-    desc = SSL_AD_HANDSHAKE_FAILURE;
-  }
-  if (desc < 0) {
-    return -1;
-  }
-
   /* If a fatal one, remove from cache */
   if (level == 2 && ssl->session != NULL) {
     SSL_CTX_remove_session(ssl->ctx, ssl->session);
@@ -697,36 +611,34 @@
 }
 
 int ssl3_dispatch_alert(SSL *ssl) {
-  int i, j;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-
   ssl->s3->alert_dispatch = 0;
-  i = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
-  if (i <= 0) {
+  int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
+  if (ret <= 0) {
     ssl->s3->alert_dispatch = 1;
-  } else {
-    /* Alert sent to BIO.  If it is important, flush it now. If the message
-     * does not get sent due to non-blocking IO, we will not worry too much. */
-    if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
-      BIO_flush(ssl->wbio);
-    }
-
-    if (ssl->msg_callback) {
-      ssl->msg_callback(1, ssl->version, SSL3_RT_ALERT, ssl->s3->send_alert, 2,
-                        ssl, ssl->msg_callback_arg);
-    }
-
-    if (ssl->info_callback != NULL) {
-      cb = ssl->info_callback;
-    } else if (ssl->ctx->info_callback != NULL) {
-      cb = ssl->ctx->info_callback;
-    }
-
-    if (cb != NULL) {
-      j = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
-      cb(ssl, SSL_CB_WRITE_ALERT, j);
-    }
+    return ret;
   }
 
-  return i;
+  /* If the alert is fatal, flush the BIO now. */
+  if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
+    BIO_flush(ssl->wbio);
+  }
+
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_ALERT,
+                      ssl->s3->send_alert, 2, ssl, ssl->msg_callback_arg);
+  }
+
+  void (*cb)(const SSL *ssl, int type, int value) = NULL;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
+  }
+
+  if (cb != NULL) {
+    int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+    cb(ssl, SSL_CB_WRITE_ALERT, alert);
+  }
+
+  return 1;
 }
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index 49a1a95..f06ee56 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -149,7 +149,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/bn.h>
@@ -164,7 +163,7 @@
 #include <openssl/hmac.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/rand.h>
 #include <openssl/sha.h>
 #include <openssl/x509.h>
@@ -185,7 +184,6 @@
   assert(ssl->server);
   assert(!SSL_IS_DTLS(ssl));
 
-  ERR_clear_error();
   ERR_clear_system_error();
 
   if (ssl->info_callback != NULL) {
@@ -194,8 +192,6 @@
     cb = ssl->ctx->info_callback;
   }
 
-  ssl->in_handshake++;
-
   if (ssl->cert == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
     return -1;
@@ -375,12 +371,11 @@
          * in PR#1939. The proposed fix doesn't completely resolve this issue
          * as buggy implementations of BIO_CTRL_PENDING still exist. So instead
          * we just flush unconditionally. */
-        ssl->rwstate = SSL_WRITING;
         if (BIO_flush(ssl->wbio) <= 0) {
+          ssl->rwstate = SSL_WRITING;
           ret = -1;
           goto end;
         }
-        ssl->rwstate = SSL_NOTHING;
 
         ssl->state = ssl->s3->tmp.next_state;
         break;
@@ -425,7 +420,7 @@
           goto end;
         }
 
-        if (!ssl3_do_change_cipher_spec(ssl)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_READ)) {
           ret = -1;
           goto end;
         }
@@ -502,12 +497,6 @@
 
       case SSL3_ST_SW_CHANGE_A:
       case SSL3_ST_SW_CHANGE_B:
-        ssl->session->cipher = ssl->s3->tmp.new_cipher;
-        if (!ssl->enc_method->setup_key_block(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
         ret = ssl3_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
                                            SSL3_ST_SW_CHANGE_B);
         if (ret <= 0) {
@@ -516,8 +505,7 @@
         ssl->state = SSL3_ST_SW_FINISHED_A;
         ssl->init_num = 0;
 
-        if (!ssl->enc_method->change_cipher_state(
-                ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -526,9 +514,7 @@
       case SSL3_ST_SW_FINISHED_A:
       case SSL3_ST_SW_FINISHED_B:
         ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
-                                 SSL3_ST_SW_FINISHED_B,
-                                 ssl->enc_method->server_finished_label,
-                                 ssl->enc_method->server_finished_label_len);
+                                 SSL3_ST_SW_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
@@ -590,7 +576,6 @@
   }
 
 end:
-  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
     cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
@@ -880,8 +865,8 @@
       goto f_err;
     }
     ssl->version = version;
-    ssl->enc_method = ssl3_get_enc_method(version);
-    assert(ssl->enc_method != NULL);
+    ssl->s3->enc_method = ssl3_get_enc_method(version);
+    assert(ssl->s3->enc_method != NULL);
     /* At this point, the connection's version is known and |ssl->version| is
      * fixed. Begin enforcing the record-layer version. */
     ssl->s3->have_version = 1;
@@ -1050,7 +1035,6 @@
         ssl->rwstate = SSL_X509_LOOKUP;
         goto err;
       }
-      ssl->rwstate = SSL_NOTHING;
     }
     c = ssl3_choose_cipher(ssl, ciphers, ssl_get_cipher_preferences(ssl));
 
@@ -1059,6 +1043,7 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
       goto f_err;
     }
+    ssl->session->cipher = c;
     ssl->s3->tmp.new_cipher = c;
 
     /* Determine whether to request a client certificate. */
@@ -1085,7 +1070,8 @@
 
   /* In TLS 1.2, client authentication requires hashing the handshake transcript
    * under a different hash. Otherwise, release the handshake buffer. */
-  if (!SSL_USE_SIGALGS(ssl) || !ssl->s3->tmp.cert_request) {
+  if (!ssl->s3->tmp.cert_request ||
+      ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -1305,7 +1291,7 @@
 
       /* Determine signature algorithm. */
       const EVP_MD *md;
-      if (SSL_USE_SIGALGS(ssl)) {
+      if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
         md = tls1_choose_signing_digest(ssl);
         if (!tls12_add_sigandhash(ssl, &cbb, md)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1353,13 +1339,11 @@
 
     switch (sign_result) {
       case ssl_private_key_success:
-        ssl->rwstate = SSL_NOTHING;
         if (!CBB_did_write(&child, sig_len)) {
           goto err;
         }
         break;
       case ssl_private_key_failure:
-        ssl->rwstate = SSL_NOTHING;
         goto err;
       case ssl_private_key_retry:
         /* Discard the unfinished signature and save the state of |cbb| for the
@@ -1405,7 +1389,7 @@
     p += n;
     n++;
 
-    if (SSL_USE_SIGALGS(ssl)) {
+    if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
       const uint8_t *psigs;
       nl = tls12_get_psigalgs(ssl, &psigs);
       s2n(nl, p);
@@ -1576,10 +1560,8 @@
 
     switch (decrypt_result) {
       case ssl_private_key_success:
-        ssl->rwstate = SSL_NOTHING;
         break;
       case ssl_private_key_failure:
-        ssl->rwstate = SSL_NOTHING;
         goto err;
       case ssl_private_key_retry:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
@@ -1587,7 +1569,11 @@
         goto err;
     }
 
-    assert(decrypt_len == rsa_size);
+    if (decrypt_len != rsa_size) {
+      al = SSL_AD_DECRYPT_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+      goto f_err;
+    }
 
     /* Prepare a random premaster, to be used on invalid padding. See RFC 5246,
      * section 7.4.7.1. */
@@ -1706,7 +1692,7 @@
   }
 
   /* Compute the master secret */
-  ssl->session->master_key_length = ssl->enc_method->generate_master_secret(
+  ssl->session->master_key_length = tls1_generate_master_secret(
       ssl, ssl->session->master_key, premaster_secret, premaster_secret_len);
   if (ssl->session->master_key_length == 0) {
     goto err;
@@ -1772,7 +1758,7 @@
   CBS_init(&certificate_verify, ssl->init_msg, n);
 
   /* Determine the digest type if needbe. */
-  if (SSL_USE_SIGALGS(ssl)) {
+  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     uint8_t hash, signature_type;
     if (!CBS_get_u8(&certificate_verify, &hash) ||
         !CBS_get_u8(&certificate_verify, &signature_type)) {
@@ -1809,10 +1795,15 @@
   if (pctx == NULL) {
     goto err;
   }
-  if (!EVP_PKEY_verify_init(pctx) ||
-      !EVP_PKEY_CTX_set_signature_md(pctx, md) ||
-      !EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), digest,
-                       digest_length)) {
+  int sig_ok = EVP_PKEY_verify_init(pctx) &&
+               EVP_PKEY_CTX_set_signature_md(pctx, md) &&
+               EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature),
+                               digest, digest_length);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  sig_ok = 1;
+  ERR_clear_error();
+#endif
+  if (!sig_ok) {
     al = SSL_AD_DECRYPT_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
     goto f_err;
@@ -1833,7 +1824,7 @@
 }
 
 int ssl3_get_client_certificate(SSL *ssl) {
-  int i, ok, al, ret = -1;
+  int ok, al, ret = -1;
   X509 *x = NULL;
   unsigned long n;
   STACK_OF(X509) *sk = NULL;
@@ -1841,6 +1832,7 @@
   CBS certificate_msg, certificate_list;
   int is_first_certificate = 1;
 
+  assert(ssl->s3->tmp.cert_request);
   n = ssl->method->ssl_get_message(ssl, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B,
                                    -1, (long)ssl->max_cert_list,
                                    ssl_hash_message, &ok);
@@ -1849,29 +1841,23 @@
     return n;
   }
 
-  if (ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
-    if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
-        (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      goto f_err;
-    }
-
-    /* If tls asked for a client cert, the client must return a 0 list */
-    if (ssl->version > SSL3_VERSION && ssl->s3->tmp.cert_request) {
-      OPENSSL_PUT_ERROR(SSL,
-                        SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST);
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      goto f_err;
-    }
-    ssl->s3->tmp.reuse_message = 1;
-
-    return 1;
-  }
-
   if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
+    if (ssl->version == SSL3_VERSION &&
+        ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
+      /* In SSL 3.0, the Certificate message is omitted to signal no certificate. */
+      if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+          (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
+        al = SSL_AD_HANDSHAKE_FAILURE;
+        goto f_err;
+      }
+
+      ssl->s3->tmp.reuse_message = 1;
+      return 1;
+    }
+
     al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     goto f_err;
   }
 
@@ -1940,15 +1926,14 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
       goto f_err;
     } else if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
-             (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+               (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
       /* Fail for TLS only if we required a certificate */
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
       al = SSL_AD_HANDSHAKE_FAILURE;
       goto f_err;
     }
   } else {
-    i = ssl_verify_cert_chain(ssl, sk);
-    if (i <= 0) {
+    if (ssl_verify_cert_chain(ssl, sk) <= 0) {
       al = ssl_verify_alarm_type(ssl->verify_result);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
       goto f_err;
@@ -2156,8 +2141,8 @@
   if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
       !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
       CBS_len(&next_protocol) != 0 ||
-      !CBS_stow(&selected_protocol, &ssl->next_proto_negotiated,
-                &ssl->next_proto_negotiated_len)) {
+      !CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
+                &ssl->s3->next_proto_negotiated_len)) {
     return 0;
   }
 
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 8829679..4de9d45 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -56,7 +56,7 @@
     enc_key_len += fixed_iv_len;
   }
 
-  SSL_AEAD_CTX *aead_ctx = (SSL_AEAD_CTX *)OPENSSL_malloc(sizeof(SSL_AEAD_CTX));
+  SSL_AEAD_CTX *aead_ctx = OPENSSL_malloc(sizeof(SSL_AEAD_CTX));
   if (aead_ctx == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -111,6 +111,10 @@
 }
 
 size_t SSL_AEAD_CTX_explicit_nonce_len(SSL_AEAD_CTX *aead) {
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  aead = NULL;
+#endif
+
   if (aead != NULL && aead->variable_nonce_included_in_record) {
     return aead->variable_nonce_len;
   }
@@ -118,11 +122,15 @@
 }
 
 size_t SSL_AEAD_CTX_max_overhead(SSL_AEAD_CTX *aead) {
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  aead = NULL;
+#endif
+
   if (aead == NULL) {
     return 0;
   }
   return EVP_AEAD_max_overhead(aead->ctx.aead) +
-      SSL_AEAD_CTX_explicit_nonce_len(aead);
+         SSL_AEAD_CTX_explicit_nonce_len(aead);
 }
 
 /* ssl_aead_ctx_get_ad writes the additional data for |aead| into |out| and
@@ -149,6 +157,10 @@
                       size_t max_out, uint8_t type, uint16_t wire_version,
                       const uint8_t seqnum[8], const uint8_t *in,
                       size_t in_len) {
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  aead = NULL;
+#endif
+
   if (aead == NULL) {
     /* Handle the initial NULL cipher. */
     if (in_len > max_out) {
@@ -222,6 +234,10 @@
                       size_t max_out, uint8_t type, uint16_t wire_version,
                       const uint8_t seqnum[8], const uint8_t *in,
                       size_t in_len) {
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  aead = NULL;
+#endif
+
   if (aead == NULL) {
     /* Handle the initial NULL cipher. */
     if (in_len > max_out) {
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c
index 7fd74e4..272b13b 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.c
@@ -113,12 +113,11 @@
   }
 
   /* Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int. */
-  ssl->rwstate = SSL_READING;
   int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap);
   if (ret <= 0) {
+    ssl->rwstate = SSL_READING;
     return ret;
   }
-  ssl->rwstate = SSL_NOTHING;
   /* |BIO_read| was bound by |buf->cap|, so this cannot overflow. */
   buf->len = (uint16_t)ret;
   return 1;
@@ -136,13 +135,12 @@
   while (buf->len < len) {
     /* The amount of data to read is bounded by |buf->cap|, which must fit in an
      * int. */
-    ssl->rwstate = SSL_READING;
     int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len,
                        (int)(len - buf->len));
     if (ret <= 0) {
+      ssl->rwstate = SSL_READING;
       return ret;
     }
-    ssl->rwstate = SSL_NOTHING;
     /* |BIO_read| was bound by |buf->cap - buf->len|, so this cannot
      * overflow. */
     buf->len += (uint16_t)ret;
@@ -268,12 +266,11 @@
   SSL3_BUFFER *buf = &ssl->s3->write_buffer;
 
   while (buf->len > 0) {
-    ssl->rwstate = SSL_WRITING;
     int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
     if (ret <= 0) {
+      ssl->rwstate = SSL_WRITING;
       return ret;
     }
-    ssl->rwstate = SSL_NOTHING;
     consume_buffer(buf, (size_t)ret);
   }
   ssl_write_buffer_clear(ssl);
@@ -286,16 +283,15 @@
     return 1;
   }
 
-  ssl->rwstate = SSL_WRITING;
   int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
   if (ret <= 0) {
+    ssl->rwstate = SSL_WRITING;
     /* If the write failed, drop the write buffer anyway. Datagram transports
      * can't write half a packet, so the caller is expected to retry from the
      * top. */
     ssl_write_buffer_clear(ssl);
     return ret;
   }
-  ssl->rwstate = SSL_NOTHING;
   ssl_write_buffer_clear(ssl);
   return 1;
 }
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 4952cfd..0eb0d8b 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -139,7 +139,7 @@
 }
 
 CERT *ssl_cert_new(void) {
-  CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT));
+  CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -150,7 +150,7 @@
 }
 
 CERT *ssl_cert_dup(CERT *cert) {
-  CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT));
+  CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -185,9 +185,16 @@
     }
   }
 
+  ret->key_method = cert->key_method;
+
   ret->cert_cb = cert->cert_cb;
   ret->cert_cb_arg = cert->cert_cb_arg;
 
+  if (cert->verify_store != NULL) {
+    X509_STORE_up_ref(cert->verify_store);
+    ret->verify_store = cert->verify_store;
+  }
+
   return ret;
 
 err:
@@ -220,6 +227,7 @@
   ssl_cert_clear_certs(c);
   OPENSSL_free(c->peer_sigalgs);
   OPENSSL_free(c->digest_nids);
+  X509_STORE_free(c->verify_store);
 
   OPENSSL_free(c);
 }
@@ -279,10 +287,15 @@
     return 0;
   }
 
+  X509_STORE *verify_store = ssl->ctx->cert_store;
+  if (ssl->cert->verify_store != NULL) {
+    verify_store = ssl->cert->verify_store;
+  }
+
   X509 *leaf = sk_X509_value(cert_chain, 0);
   int ret = 0;
   X509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, cert_chain)) {
+  if (!X509_STORE_CTX_init(&ctx, verify_store, leaf, cert_chain)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
@@ -409,13 +422,18 @@
   uint8_t *p;
 
   n = i2d_X509(x, NULL);
-  if (!BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) {
+  if (n < 0 || !BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
   }
   p = (uint8_t *)&(buf->data[*l]);
   l2n3(n, p);
-  i2d_X509(x, &p);
+  n = i2d_X509(x, &p);
+  if (n < 0) {
+      /* This shouldn't happen. */
+      OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+      return 0;
+  }
   *l += n + 3;
 
   return 1;
@@ -475,6 +493,33 @@
   return 1;
 }
 
+static int set_cert_store(X509_STORE **store_ptr, X509_STORE *new_store, int take_ref) {
+  X509_STORE_free(*store_ptr);
+  *store_ptr = new_store;
+
+  if (new_store != NULL && take_ref) {
+    X509_STORE_up_ref(new_store);
+  }
+
+  return 1;
+}
+
+int SSL_CTX_set0_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) {
+  return set_cert_store(&ctx->cert->verify_store, store, 0);
+}
+
+int SSL_CTX_set1_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) {
+  return set_cert_store(&ctx->cert->verify_store, store, 1);
+}
+
+int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store) {
+  return set_cert_store(&ssl->cert->verify_store, store, 0);
+}
+
+int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store) {
+  return set_cert_store(&ssl->cert->verify_store, store, 1);
+}
+
 int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
   return ssl_cert_set0_chain(ctx->cert, chain);
 }
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 77fa8fa..58ce582 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -141,7 +141,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -1392,7 +1391,7 @@
   /* Now we have to collect the available ciphers from the compiled in ciphers.
    * We cannot get more than the number compiled in, so it is used for
    * allocation. */
-  co_list = (CIPHER_ORDER *)OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen);
+  co_list = OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen);
   if (co_list == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
@@ -1417,10 +1416,10 @@
    * AES_GCM. Of the two CHACHA20 variants, the new one is preferred over the
    * old one. */
   if (EVP_has_aes_hardware()) {
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
                           &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
+                          &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD,
                           -1, 0, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
@@ -1430,24 +1429,24 @@
                           -1, 0, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
                           CIPHER_ADD, -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
-                          &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
                           &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, 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, ~0u, ~0u, SSL_AES256, ~0u, 0, CIPHER_ADD, -1, 0,
-                        &head, &tail);
+  /* Then the legacy non-AEAD ciphers: AES_128_CBC, AES_256_CBC,
+   * 3DES_EDE_CBC_SHA, RC4_128_SHA, RC4_128_MD5. */
   ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128, ~0u, 0, CIPHER_ADD, -1, 0,
                         &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256, ~0u, 0, CIPHER_ADD, -1, 0,
+                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, 0, CIPHER_ADD, -1, 0, &head,
+                        &tail);
   ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, ~SSL_MD5, 0, CIPHER_ADD, -1, 0,
                         &head, &tail);
   ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, SSL_MD5, 0, CIPHER_ADD, -1, 0,
                         &head, &tail);
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, 0, CIPHER_ADD, -1, 0, &head,
-                        &tail);
 
   /* Temporarily enable everything else for sorting */
   ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0, &head,
@@ -1577,6 +1576,10 @@
   return (cipher->algorithm_mac & SSL_SHA1) != 0;
 }
 
+int SSL_CIPHER_has_SHA256_HMAC(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mac & SSL_SHA256) != 0;
+}
+
 int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
 }
@@ -1804,7 +1807,6 @@
                                    int len) {
   const char *kx, *au, *enc, *mac;
   uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
-  static const char *format = "%-23s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n";
 
   alg_mkey = cipher->algorithm_mkey;
   alg_auth = cipher->algorithm_auth;
@@ -1928,7 +1930,8 @@
     return "Buffer too small";
   }
 
-  BIO_snprintf(buf, len, format, cipher->name, kx, au, enc, mac);
+  BIO_snprintf(buf, len, "%-23s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n",
+               cipher->name, kx, au, enc, mac);
   return buf;
 }
 
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.c
index 45c5b26..d48c93f 100644
--- a/src/ssl/ssl_ecdh.c
+++ b/src/ssl/ssl_ecdh.c
@@ -23,7 +23,7 @@
 #include <openssl/ec.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 
 #include "internal.h"
 
@@ -65,21 +65,12 @@
     }
   } while (BN_is_zero(private_key));
 
-  /* Compute the corresponding public key. */
+  /* Compute the corresponding public key and serialize it. */
   public_key = EC_POINT_new(group);
   if (public_key == NULL ||
-      !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx)) {
-    goto err;
-  }
-
-  /* Serialize the public key. */
-  size_t len = EC_POINT_point2oct(
-      group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
-  uint8_t *ptr;
-  if (len == 0 ||
-      !CBB_add_space(out, &ptr, len) ||
-      EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, ptr,
-                         len, bn_ctx) != len) {
+      !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx) ||
+      !EC_POINT_point2cbb(out, group, public_key, POINT_CONVERSION_UNCOMPRESSED,
+                          bn_ctx)) {
     goto err;
   }
 
@@ -93,9 +84,12 @@
   return ret;
 }
 
-int ssl_ec_point_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
-                                size_t *out_secret_len, uint8_t *out_alert,
-                                const uint8_t *peer_key, size_t peer_key_len) {
+static int ssl_ec_point_compute_secret(SSL_ECDH_CTX *ctx,
+                                       uint8_t **out_secret,
+                                       size_t *out_secret_len,
+                                       uint8_t *out_alert,
+                                       const uint8_t *peer_key,
+                                       size_t peer_key_len) {
   BIGNUM *private_key = (BIGNUM *)ctx->data;
   assert(private_key != NULL);
   *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -297,8 +291,8 @@
         ssl_ec_point_compute_secret,
     },
     {
-        NID_x25519,
-        SSL_CURVE_ECDH_X25519,
+        NID_X25519,
+        SSL_CURVE_X25519,
         "X25519",
         ssl_x25519_cleanup,
         ssl_x25519_generate_keypair,
diff --git a/src/ssl/ssl_file.c b/src/ssl/ssl_file.c
index 42cf800..748d50c 100644
--- a/src/ssl/ssl_file.c
+++ b/src/ssl/ssl_file.c
@@ -121,7 +121,6 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
-#include "../crypto/directory.h"
 #include "internal.h"
 
 
@@ -247,53 +246,6 @@
   return ret;
 }
 
-/* Add a directory of certs to a stack.
- *
- * \param stack the stack to append to.
- * \param dir the directory to append from. All files in this directory will be
- *     examined as potential certs. Any that are acceptable to
- *     SSL_add_dir_cert_subjects_to_stack() that are not already in the stack will
- *     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,
-                                       const char *dir) {
-  OPENSSL_DIR_CTX *d = NULL;
-  const char *filename;
-  int ret = 0;
-
-  /* Note that a side effect is that the CAs will be sorted by name */
-  while ((filename = OPENSSL_DIR_read(&d, dir))) {
-    char buf[1024];
-    int r;
-
-    if (strlen(dir) + strlen(filename) + 2 > sizeof(buf)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PATH_TOO_LONG);
-      goto err;
-    }
-
-    r = BIO_snprintf(buf, sizeof buf, "%s/%s", dir, filename);
-    if (r <= 0 || r >= (int)sizeof(buf) ||
-        !SSL_add_file_cert_subjects_to_stack(stack, buf)) {
-      goto err;
-    }
-  }
-
-  if (errno) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB);
-    ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')");
-    goto err;
-  }
-
-  ret = 1;
-
-err:
-  if (d) {
-    OPENSSL_DIR_end(&d);
-  }
-  return ret;
-}
-
 int SSL_use_certificate_file(SSL *ssl, const char *file, int type) {
   int reason_code;
   BIO *in;
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 08578a6..d62cdae 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -141,7 +141,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/bytestring.h>
@@ -150,7 +149,6 @@
 #include <openssl/err.h>
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/x509v3.h>
 
@@ -181,12 +179,21 @@
   return 1;
 }
 
-static uint32_t ssl_session_hash(const SSL_SESSION *a) {
+static uint32_t ssl_session_hash(const SSL_SESSION *sess) {
+  const uint8_t *session_id = sess->session_id;
+
+  uint8_t tmp_storage[sizeof(uint32_t)];
+  if (sess->session_id_length < sizeof(tmp_storage)) {
+    memset(tmp_storage, 0, sizeof(tmp_storage));
+    memcpy(tmp_storage, sess->session_id, sess->session_id_length);
+    session_id = tmp_storage;
+  }
+
   uint32_t hash =
-      ((uint32_t)a->session_id[0]) |
-      ((uint32_t)a->session_id[1] << 8) |
-      ((uint32_t)a->session_id[2] << 16) |
-      ((uint32_t)a->session_id[3] << 24);
+      ((uint32_t)session_id[0]) |
+      ((uint32_t)session_id[1] << 8) |
+      ((uint32_t)session_id[2] << 16) |
+      ((uint32_t)session_id[3] << 24);
 
   return hash;
 }
@@ -221,7 +228,7 @@
     goto err;
   }
 
-  ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
+  ret = OPENSSL_malloc(sizeof(SSL_CTX));
   if (ret == NULL) {
     goto err;
   }
@@ -353,7 +360,7 @@
     return NULL;
   }
 
-  SSL *ssl = (SSL *)OPENSSL_malloc(sizeof(SSL));
+  SSL *ssl = OPENSSL_malloc(sizeof(SSL));
   if (ssl == NULL) {
     goto err;
   }
@@ -417,8 +424,6 @@
   if (!ssl->method->ssl_new(ssl)) {
     goto err;
   }
-  ssl->enc_method = ssl3_get_enc_method(ssl->version);
-  assert(ssl->enc_method != NULL);
 
   ssl->rwstate = SSL_NOTHING;
 
@@ -486,8 +491,6 @@
   ssl_clear_bad_session(ssl);
   SSL_SESSION_free(ssl->session);
 
-  ssl_clear_cipher_ctx(ssl);
-
   ssl_cert_free(ssl->cert);
 
   OPENSSL_free(ssl->tlsext_hostname);
@@ -497,7 +500,6 @@
   EVP_PKEY_free(ssl->tlsext_channel_id_private);
   OPENSSL_free(ssl->psk_identity_hint);
   sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free);
-  OPENSSL_free(ssl->next_proto_negotiated);
   sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
 
   if (ssl->method != NULL) {
@@ -513,8 +515,6 @@
   ssl->shutdown = 0;
   ssl->state = SSL_ST_CONNECT;
   ssl->handshake_func = ssl->method->ssl_connect;
-  /* clear the current cipher */
-  ssl_clear_cipher_ctx(ssl);
 }
 
 void SSL_set_accept_state(SSL *ssl) {
@@ -522,8 +522,6 @@
   ssl->shutdown = 0;
   ssl->state = SSL_ST_ACCEPT;
   ssl->handshake_func = ssl->method->ssl_accept;
-  /* clear the current cipher */
-  ssl_clear_cipher_ctx(ssl);
 }
 
 void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
@@ -550,6 +548,10 @@
 BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
 
 int SSL_do_handshake(SSL *ssl) {
+  ssl->rwstate = SSL_NOTHING;
+  /* Functions which use SSL_get_error must clear the error queue on entry. */
+  ERR_clear_error();
+
   if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_TYPE_NOT_SET);
     return -1;
@@ -563,91 +565,117 @@
 }
 
 int SSL_connect(SSL *ssl) {
-  if (ssl->handshake_func == 0) {
+  if (ssl->handshake_func == NULL) {
     /* Not properly initialized yet */
     SSL_set_connect_state(ssl);
   }
 
-  if (ssl->handshake_func != ssl->method->ssl_connect) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
-  }
+  assert(ssl->handshake_func == ssl->method->ssl_connect);
 
-  return ssl->handshake_func(ssl);
+  return SSL_do_handshake(ssl);
 }
 
 int SSL_accept(SSL *ssl) {
-  if (ssl->handshake_func == 0) {
+  if (ssl->handshake_func == NULL) {
     /* Not properly initialized yet */
     SSL_set_accept_state(ssl);
   }
 
-  if (ssl->handshake_func != ssl->method->ssl_accept) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  assert(ssl->handshake_func == ssl->method->ssl_accept);
+
+  return SSL_do_handshake(ssl);
+}
+
+static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) {
+  ssl->rwstate = SSL_NOTHING;
+  /* Functions which use SSL_get_error must clear the error queue on entry. */
+  ERR_clear_error();
+  ERR_clear_system_error();
+
+  if (ssl->handshake_func == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
     return -1;
   }
 
-  return ssl->handshake_func(ssl);
+  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
+    return 0;
+  }
+
+  /* This may require multiple iterations. False Start will cause
+   * |ssl->handshake_func| to signal success one step early, but the handshake
+   * must be completely finished before other modes are accepted. */
+  while (SSL_in_init(ssl)) {
+    int ret = SSL_do_handshake(ssl);
+    if (ret < 0) {
+      return ret;
+    }
+    if (ret == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+      return -1;
+    }
+  }
+
+  return ssl->method->ssl_read_app_data(ssl, buf, num, peek);
 }
 
 int SSL_read(SSL *ssl, void *buf, int num) {
-  if (ssl->handshake_func == 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
-    return -1;
-  }
-
-  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
-    ssl->rwstate = SSL_NOTHING;
-    return 0;
-  }
-
-  ERR_clear_system_error();
-  return ssl->method->ssl_read_app_data(ssl, buf, num, 0);
+  return ssl_read_impl(ssl, buf, num, 0 /* consume bytes */);
 }
 
 int SSL_peek(SSL *ssl, void *buf, int num) {
-  if (ssl->handshake_func == 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
-    return -1;
-  }
-
-  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
-    return 0;
-  }
-
-  ERR_clear_system_error();
-  return ssl->method->ssl_read_app_data(ssl, buf, num, 1);
+  return ssl_read_impl(ssl, buf, num, 1 /* peek */);
 }
 
 int SSL_write(SSL *ssl, const void *buf, int num) {
-  if (ssl->handshake_func == 0) {
+  ssl->rwstate = SSL_NOTHING;
+  /* Functions which use SSL_get_error must clear the error queue on entry. */
+  ERR_clear_error();
+  ERR_clear_system_error();
+
+  if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
     return -1;
   }
 
   if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
-    ssl->rwstate = SSL_NOTHING;
     OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
     return -1;
   }
 
-  ERR_clear_system_error();
+  /* If necessary, complete the handshake implicitly. */
+  if (SSL_in_init(ssl) && !SSL_in_false_start(ssl)) {
+    int ret = SSL_do_handshake(ssl);
+    if (ret < 0) {
+      return ret;
+    }
+    if (ret == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+      return -1;
+    }
+  }
+
   return ssl->method->ssl_write_app_data(ssl, buf, num);
 }
 
 int SSL_shutdown(SSL *ssl) {
+  ssl->rwstate = SSL_NOTHING;
+  /* Functions which use SSL_get_error must clear the error queue on entry. */
+  ERR_clear_error();
+
   /* Note that this function behaves differently from what one might expect.
    * Return values are 0 for no success (yet), 1 for success; but calling it
    * once is usually not enough, even if blocking I/O is used (see
    * ssl3_shutdown). */
 
-  if (ssl->handshake_func == 0) {
+  if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
     return -1;
   }
 
+  /* We can't shutdown properly if we are in the middle of a handshake. */
   if (SSL_in_init(ssl)) {
-    return 1;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_SHUTDOWN_WHILE_IN_INIT);
+    return -1;
   }
 
   /* Do nothing if configured not to send a close_notify. */
@@ -710,8 +738,7 @@
   }
 
   if (ret_code == 0) {
-    if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) &&
-        (ssl->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) {
+    if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) && ssl->s3->clean_shutdown) {
       /* The socket was cleanly shut down with a close_notify. */
       return SSL_ERROR_ZERO_RETURN;
     }
@@ -997,7 +1024,7 @@
 }
 
 int SSL_set_fd(SSL *ssl, int fd) {
-  BIO *bio = BIO_new(BIO_s_fd());
+  BIO *bio = BIO_new(BIO_s_socket());
   if (bio == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
     return 0;
@@ -1009,9 +1036,9 @@
 
 int SSL_set_wfd(SSL *ssl, int fd) {
   if (ssl->rbio == NULL ||
-      BIO_method_type(ssl->rbio) != BIO_TYPE_FD ||
+      BIO_method_type(ssl->rbio) != BIO_TYPE_SOCKET ||
       BIO_get_fd(ssl->rbio, NULL) != fd) {
-    BIO *bio = BIO_new(BIO_s_fd());
+    BIO *bio = BIO_new(BIO_s_socket());
     if (bio == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
       return 0;
@@ -1026,9 +1053,9 @@
 }
 
 int SSL_set_rfd(SSL *ssl, int fd) {
-  if (ssl->wbio == NULL || BIO_method_type(ssl->wbio) != BIO_TYPE_FD ||
+  if (ssl->wbio == NULL || BIO_method_type(ssl->wbio) != BIO_TYPE_SOCKET ||
       BIO_get_fd(ssl->wbio, NULL) != fd) {
-    BIO *bio = BIO_new(BIO_s_fd());
+    BIO *bio = BIO_new(BIO_s_socket());
     if (bio == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
       return 0;
@@ -1187,7 +1214,7 @@
   ssl->max_cert_list = (uint32_t)max_cert_list;
 }
 
-void SSL_CTX_set_max_send_fragment(SSL_CTX *ctx, size_t max_send_fragment) {
+int SSL_CTX_set_max_send_fragment(SSL_CTX *ctx, size_t max_send_fragment) {
   if (max_send_fragment < 512) {
     max_send_fragment = 512;
   }
@@ -1195,9 +1222,11 @@
     max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
   }
   ctx->max_send_fragment = (uint16_t)max_send_fragment;
+
+  return 1;
 }
 
-void SSL_set_max_send_fragment(SSL *ssl, size_t max_send_fragment) {
+int SSL_set_max_send_fragment(SSL *ssl, size_t max_send_fragment) {
   if (max_send_fragment < 512) {
     max_send_fragment = 512;
   }
@@ -1205,6 +1234,8 @@
     max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
   }
   ssl->max_send_fragment = (uint16_t)max_send_fragment;
+
+  return 1;
 }
 
 int SSL_set_mtu(SSL *ssl, unsigned mtu) {
@@ -1254,17 +1285,15 @@
     return ssl->cipher_list->ciphers;
   }
 
-  if (ssl->version >= TLS1_1_VERSION && ssl->ctx != NULL &&
-      ssl->ctx->cipher_list_tls11 != NULL) {
+  if (ssl->version >= TLS1_1_VERSION && ssl->ctx->cipher_list_tls11 != NULL) {
     return ssl->ctx->cipher_list_tls11->ciphers;
   }
 
-  if (ssl->version >= TLS1_VERSION && ssl->ctx != NULL &&
-      ssl->ctx->cipher_list_tls10 != NULL) {
+  if (ssl->version >= TLS1_VERSION && ssl->ctx->cipher_list_tls10 != NULL) {
     return ssl->ctx->cipher_list_tls10->ciphers;
   }
 
-  if (ssl->ctx != NULL && ssl->ctx->cipher_list != NULL) {
+  if (ssl->ctx->cipher_list != NULL) {
     return ssl->ctx->cipher_list->ciphers;
   }
 
@@ -1282,7 +1311,7 @@
     return ssl->cipher_list_by_id;
   }
 
-  if (ssl->ctx != NULL && ssl->ctx->cipher_list_by_id != NULL) {
+  if (ssl->ctx->cipher_list_by_id != NULL) {
     return ssl->ctx->cipher_list_by_id;
   }
 
@@ -1574,11 +1603,11 @@
 
 void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data,
                                     unsigned *out_len) {
-  *out_data = ssl->next_proto_negotiated;
+  *out_data = ssl->s3->next_proto_negotiated;
   if (*out_data == NULL) {
     *out_len = 0;
   } else {
-    *out_len = ssl->next_proto_negotiated_len;
+    *out_len = ssl->s3->next_proto_negotiated_len;
   }
 }
 
@@ -1643,18 +1672,6 @@
   }
 }
 
-int SSL_export_keying_material(SSL *ssl, 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 (ssl->version < TLS1_VERSION) {
-    return 0;
-  }
-
-  return ssl->enc_method->export_keying_material(
-      ssl, out, out_len, label, label_len, context, context_len, use_context);
-}
-
 void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,
                                       int (*cb)(X509_STORE_CTX *store_ctx,
                                                 void *arg),
@@ -1822,13 +1839,6 @@
   return ssl_get_version(session->ssl_version);
 }
 
-void ssl_clear_cipher_ctx(SSL *ssl) {
-  SSL_AEAD_CTX_free(ssl->aead_read_ctx);
-  ssl->aead_read_ctx = NULL;
-  SSL_AEAD_CTX_free(ssl->aead_write_ctx);
-  ssl->aead_write_ctx = NULL;
-}
-
 X509 *SSL_get_certificate(const SSL *ssl) {
   if (ssl->cert != NULL) {
     return ssl->cert->x509;
@@ -1862,16 +1872,18 @@
 }
 
 const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl) {
-  if (ssl->aead_write_ctx == NULL) {
+  if (ssl->s3->aead_write_ctx == NULL) {
     return NULL;
   }
-  return ssl->aead_write_ctx->cipher;
+  return ssl->s3->aead_write_ctx->cipher;
 }
 
 const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
 
 const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; }
 
+int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
+
 int ssl_init_wbio_buffer(SSL *ssl, int push) {
   BIO *bbio;
 
@@ -1995,6 +2007,14 @@
 
 void SSL_set_state(SSL *ssl, int state) { }
 
+char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len) {
+  if (len <= 0) {
+    return NULL;
+  }
+  buf[0] = '\0';
+  return buf;
+}
+
 void SSL_set_verify_result(SSL *ssl, long result) {
   ssl->verify_result = result;
 }
@@ -2305,15 +2325,11 @@
       return &SSLv3_enc_data;
 
     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:
+    case DTLS1_2_VERSION:
+      return &TLSv1_enc_data;
 
     default:
       return NULL;
@@ -2495,7 +2511,7 @@
   }
 }
 
-uint16_t ssl3_version_from_wire(SSL *ssl, uint16_t wire_version) {
+uint16_t ssl3_version_from_wire(const SSL *ssl, uint16_t wire_version) {
   if (!SSL_IS_DTLS(ssl)) {
     return wire_version;
   }
@@ -2516,6 +2532,11 @@
   return version;
 }
 
+uint16_t ssl3_protocol_version(const SSL *ssl) {
+  assert(ssl->s3->have_version);
+  return ssl3_version_from_wire(ssl, ssl->version);
+}
+
 int SSL_cache_hit(SSL *ssl) { return SSL_session_reused(ssl); }
 
 int SSL_is_server(SSL *ssl) { return ssl->server; }
@@ -2541,23 +2562,24 @@
 
 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) {
+  if (ssl->s3->aead_read_ctx == NULL || ssl->s3->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);
+  return EVP_AEAD_CTX_get_rc4_state(&ssl->s3->aead_read_ctx->ctx, read_key) &&
+         EVP_AEAD_CTX_get_rc4_state(&ssl->s3->aead_write_ctx->ctx, write_key);
 }
 
 int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv,
                 const uint8_t **out_write_iv, size_t *out_iv_len) {
-  if (ssl->aead_read_ctx == NULL || ssl->aead_write_ctx == NULL) {
+  if (ssl->s3->aead_read_ctx == NULL || ssl->s3->aead_write_ctx == NULL) {
     return 0;
   }
 
   size_t write_iv_len;
-  if (!EVP_AEAD_CTX_get_iv(&ssl->aead_read_ctx->ctx, out_read_iv, out_iv_len) ||
-      !EVP_AEAD_CTX_get_iv(&ssl->aead_write_ctx->ctx, out_write_iv,
+  if (!EVP_AEAD_CTX_get_iv(&ssl->s3->aead_read_ctx->ctx, out_read_iv,
+                           out_iv_len) ||
+      !EVP_AEAD_CTX_get_iv(&ssl->s3->aead_write_ctx->ctx, out_write_iv,
                            &write_iv_len) ||
       *out_iv_len != write_iv_len) {
     return 0;
@@ -2566,10 +2588,69 @@
   return 1;
 }
 
+static uint64_t be_to_u64(const uint8_t in[8]) {
+  return (((uint64_t)in[0]) << 56) | (((uint64_t)in[1]) << 48) |
+         (((uint64_t)in[2]) << 40) | (((uint64_t)in[3]) << 32) |
+         (((uint64_t)in[4]) << 24) | (((uint64_t)in[5]) << 16) |
+         (((uint64_t)in[6]) << 8) | ((uint64_t)in[7]);
+}
+
+uint64_t SSL_get_read_sequence(const SSL *ssl) {
+  /* TODO(davidben): Internally represent sequence numbers as uint64_t. */
+  if (SSL_IS_DTLS(ssl)) {
+    /* max_seq_num already includes the epoch. */
+    assert(ssl->d1->r_epoch == (ssl->d1->bitmap.max_seq_num >> 48));
+    return ssl->d1->bitmap.max_seq_num;
+  }
+  return be_to_u64(ssl->s3->read_sequence);
+}
+
+uint64_t SSL_get_write_sequence(const SSL *ssl) {
+  uint64_t ret = be_to_u64(ssl->s3->write_sequence);
+  if (SSL_IS_DTLS(ssl)) {
+    assert((ret >> 48) == 0);
+    ret |= ((uint64_t)ssl->d1->w_epoch) << 48;
+  }
+  return ret;
+}
+
 uint8_t SSL_get_server_key_exchange_hash(const SSL *ssl) {
   return ssl->s3->tmp.server_key_exchange_hash;
 }
 
+size_t SSL_get_client_random(const SSL *ssl, uint8_t *out, size_t max_out) {
+  if (max_out == 0) {
+    return sizeof(ssl->s3->client_random);
+  }
+  if (max_out > sizeof(ssl->s3->client_random)) {
+    max_out = sizeof(ssl->s3->client_random);
+  }
+  memcpy(out, ssl->s3->client_random, max_out);
+  return max_out;
+}
+
+size_t SSL_get_server_random(const SSL *ssl, uint8_t *out, size_t max_out) {
+  if (max_out == 0) {
+    return sizeof(ssl->s3->server_random);
+  }
+  if (max_out > sizeof(ssl->s3->server_random)) {
+    max_out = sizeof(ssl->s3->server_random);
+  }
+  memcpy(out, ssl->s3->server_random, max_out);
+  return max_out;
+}
+
+const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) {
+  if (!SSL_in_init(ssl)) {
+    return NULL;
+  }
+  return ssl->s3->tmp.new_cipher;
+}
+
+void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
+  ctx->retain_only_sha256_of_client_certs = !!enabled;
+}
+
 int SSL_clear(SSL *ssl) {
   if (ssl->method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_METHOD_SPECIFIED);
@@ -2608,12 +2689,6 @@
   BUF_MEM_free(ssl->init_buf);
   ssl->init_buf = NULL;
 
-  ssl_clear_cipher_ctx(ssl);
-
-  OPENSSL_free(ssl->next_proto_negotiated);
-  ssl->next_proto_negotiated = NULL;
-  ssl->next_proto_negotiated_len = 0;
-
   /* The ssl->d1->mtu is simultaneously configuration (preserved across
    * clear) and connection-specific state (gets reset).
    *
@@ -2627,8 +2702,6 @@
   if (!ssl->method->ssl_new(ssl)) {
     return 0;
   }
-  ssl->enc_method = ssl3_get_enc_method(ssl->version);
-  assert(ssl->enc_method != NULL);
 
   if (SSL_IS_DTLS(ssl) && (SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
     ssl->d1->mtu = mtu;
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 990979b..c17d2da 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -326,6 +326,11 @@
   ssl->cert->key_method = key_method;
 }
 
+void SSL_CTX_set_private_key_method(SSL_CTX *ctx,
+                                    const SSL_PRIVATE_KEY_METHOD *key_method) {
+  ctx->cert->key_method = key_method;
+}
+
 int SSL_set_private_key_digest_prefs(SSL *ssl, const int *digest_nids,
                                      size_t num_digests) {
   OPENSSL_free(ssl->cert->digest_nids);
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 3d59bc3..12d065e 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -136,7 +136,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/err.h>
@@ -161,7 +160,7 @@
 static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock);
 
 SSL_SESSION *SSL_SESSION_new(void) {
-  SSL_SESSION *session = (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION));
+  SSL_SESSION *session = OPENSSL_malloc(sizeof(SSL_SESSION));
   if (session == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
@@ -232,6 +231,19 @@
   return session->peer;
 }
 
+size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, uint8_t *out,
+                                  size_t max_out) {
+  /* TODO(davidben): Fix master_key_length's type and remove these casts. */
+  if (max_out == 0) {
+    return (size_t)session->master_key_length;
+  }
+  if (max_out > (size_t)session->master_key_length) {
+    max_out = (size_t)session->master_key_length;
+  }
+  memcpy(out, session->master_key, max_out);
+  return max_out;
+}
+
 long SSL_SESSION_set_time(SSL_SESSION *session, long time) {
   if (session == NULL) {
     return 0;
@@ -430,7 +442,7 @@
   /* This is used only by servers. */
   assert(ssl->server);
   SSL_SESSION *session = NULL;
-  int send_ticket = 0;
+  int renew_ticket = 0;
 
   /* If tickets are disabled, always behave as if no tickets are present. */
   const uint8_t *ticket = NULL;
@@ -440,24 +452,27 @@
       ssl->version > SSL3_VERSION &&
       SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket,
                                            &ticket, &ticket_len);
-  if (tickets_supported) {
-    if (!tls_process_ticket(ssl, &session, &send_ticket, ticket, ticket_len,
+  int from_cache = 0;
+  if (tickets_supported && ticket_len > 0) {
+    if (!tls_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
                             ctx->session_id, ctx->session_id_len)) {
       return ssl_session_error;
     }
   } else {
-    /* The client does not support session tickets, so the session ID should be
-     * used instead. */
+    /* The client didn't send a ticket, so the session ID is a real ID. */
     enum ssl_session_result_t lookup_ret = ssl_lookup_session(
         ssl, &session, ctx->session_id, ctx->session_id_len);
     if (lookup_ret != ssl_session_success) {
       return lookup_ret;
     }
+    from_cache = 1;
   }
 
   if (session == NULL ||
       session->sid_ctx_length != ssl->sid_ctx_length ||
       memcmp(session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length) != 0) {
+    /* The client did not offer a suitable ticket or session ID. If supported,
+     * the new session should use a ticket. */
     goto no_session;
   }
 
@@ -471,11 +486,12 @@
      * effectively disable the session cache by accident without anyone
      * noticing). */
     OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED);
-    goto fatal_error;
+    SSL_SESSION_free(session);
+    return ssl_session_error;
   }
 
   if (session->timeout < (long)(time(NULL) - session->time)) {
-    if (!tickets_supported) {
+    if (from_cache) {
       /* The session was from the cache, so remove it. */
       SSL_CTX_remove_session(ssl->initial_ctx, session);
     }
@@ -483,13 +499,9 @@
   }
 
   *out_session = session;
-  *out_send_ticket = send_ticket;
+  *out_send_ticket = renew_ticket;
   return ssl_session_success;
 
-fatal_error:
-  SSL_SESSION_free(session);
-  return ssl_session_error;
-
 no_session:
   *out_session = NULL;
   *out_send_ticket = tickets_supported;
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 9558f1c..590a2c1 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -18,13 +18,16 @@
 
 #include <algorithm>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <openssl/base64.h>
 #include <openssl/bio.h>
 #include <openssl/crypto.h>
 #include <openssl/err.h>
+#include <openssl/pem.h>
 #include <openssl/ssl.h>
+#include <openssl/x509.h>
 
 #include "test/scoped_types.h"
 #include "../crypto/test/test_util.h"
@@ -38,215 +41,178 @@
 struct CipherTest {
   // The rule string to apply.
   const char *rule;
-  // The list of expected ciphers, in order, terminated with -1.
-  const ExpectedCipher *expected;
+  // The list of expected ciphers, in order.
+  std::vector<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_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 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_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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_WITH_CHACHA20_POLY1305_SHA256, 1 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 1 },
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 1 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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-AES128-GCM-SHA256:"
-    "ECDHE-RSA-AES128-GCM-SHA256:"
-    "!ECDHE-RSA-AES128-GCM-SHA256+RSA:"
-    "!ECDSA+ECDHE-ECDSA-AES128-GCM-SHA256";
-
-static const ExpectedCipher kExpected9[] = {
-  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
-  { 0, 0 },
-};
-
-// SSLv3 matches everything that existed before TLS 1.2.
-static const char kRule10[] = "AES128-SHA:AES128-SHA256:!SSLv3";
-
-static const ExpectedCipher kExpected10[] = {
-  { TLS1_CK_RSA_WITH_AES_128_SHA256, 0 },
-  { 0, 0 },
-};
-
-// TLSv1.2 matches everything added in TLS 1.2.
-static const char kRule11[] = "AES128-SHA:AES128-SHA256:!TLSv1.2";
-
-static const ExpectedCipher kExpected11[] = {
-  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
-  { 0, 0 },
-};
-
-// The two directives have no intersection.
-static const char kRule12[] = "AES128-SHA:AES128-SHA256:!TLSv1.2+SSLv3";
-
-static const ExpectedCipher kExpected12[] = {
-  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
-  { TLS1_CK_RSA_WITH_AES_128_SHA256, 0 },
-  { 0, 0 },
-};
-
-// The shared name of the CHACHA20_POLY1305 variants behaves like a cipher name
-// and not an alias. It may not be used in a multipart rule. (That the shared
-// name works is covered by the standard tests.)
-static const char kRule13[] =
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
-    "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305";
-
-static const ExpectedCipher kExpected13[] = {
-  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
-  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
-  { 0, 0 },
-};
-
-static CipherTest kCipherTests[] = {
-  { kRule1, kExpected1 },
-  { kRule2, kExpected2 },
-  { kRule3, kExpected3 },
-  { kRule4, kExpected4 },
-  { kRule5, kExpected5 },
-  { kRule6, kExpected6 },
-  { kRule7, kExpected7 },
-  { kRule8, kExpected8 },
-  { kRule9, kExpected9 },
-  { kRule10, kExpected10 },
-  { kRule11, kExpected11 },
-  { kRule12, kExpected12 },
-  { kRule13, kExpected13 },
-  { NULL, NULL },
+static const CipherTest kCipherTests[] = {
+    // Selecting individual ciphers should work.
+    {
+        "ECDHE-ECDSA-CHACHA20-POLY1305:"
+        "ECDHE-RSA-CHACHA20-POLY1305:"
+        "ECDHE-ECDSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES128-GCM-SHA256",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // + reorders selected ciphers to the end, keeping their relative order.
+    {
+        "ECDHE-ECDSA-CHACHA20-POLY1305:"
+        "ECDHE-RSA-CHACHA20-POLY1305:"
+        "ECDHE-ECDSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES128-GCM-SHA256:"
+        "+aRSA",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // ! banishes ciphers from future selections.
+    {
+        "!aRSA:"
+        "ECDHE-ECDSA-CHACHA20-POLY1305:"
+        "ECDHE-RSA-CHACHA20-POLY1305:"
+        "ECDHE-ECDSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES128-GCM-SHA256",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // Multiple masks can be ANDed in a single rule.
+    {
+        "kRSA+AESGCM+AES128",
+        {
+            {TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 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.
+    {
+        "ALL:-kECDHE:-kDHE:-kRSA:-ALL:"
+        "AESGCM+AES128+aRSA",
+        {
+            {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},
+        },
+    },
+    // Unknown selectors are no-ops.
+    {
+        "ECDHE-ECDSA-CHACHA20-POLY1305:"
+        "ECDHE-RSA-CHACHA20-POLY1305:"
+        "ECDHE-ECDSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES128-GCM-SHA256:"
+        "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // Square brackets specify equi-preference groups.
+    {
+        "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:"
+        "[ECDHE-RSA-CHACHA20-POLY1305]:"
+        "ECDHE-RSA-AES128-GCM-SHA256",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 1},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 1},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 1},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // @STRENGTH performs a stable strength-sort of the selected ciphers and
+    // only the selected ciphers.
+    {
+        // 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",
+        {
+            {TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 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},
+        },
+    },
+    // Exact ciphers may not be used in multi-part rules; they are treated
+    // as unknown aliases.
+    {
+        "ECDHE-ECDSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES128-GCM-SHA256:"
+        "!ECDHE-RSA-AES128-GCM-SHA256+RSA:"
+        "!ECDSA+ECDHE-ECDSA-AES128-GCM-SHA256",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+    },
+    // SSLv3 matches everything that existed before TLS 1.2.
+    {
+        "AES128-SHA:AES128-SHA256:!SSLv3",
+        {
+            {TLS1_CK_RSA_WITH_AES_128_SHA256, 0},
+        },
+    },
+    // TLSv1.2 matches everything added in TLS 1.2.
+    {
+        "AES128-SHA:AES128-SHA256:!TLSv1.2",
+        {
+            {TLS1_CK_RSA_WITH_AES_128_SHA, 0},
+        },
+    },
+    // The two directives have no intersection.
+    {
+        "AES128-SHA:AES128-SHA256:!TLSv1.2+SSLv3",
+        {
+            {TLS1_CK_RSA_WITH_AES_128_SHA, 0},
+            {TLS1_CK_RSA_WITH_AES_128_SHA256, 0},
+        },
+    },
+    // The shared name of the CHACHA20_POLY1305 variants behaves like a cipher
+    // name and not an alias. It may not be used in a multipart rule. (That the
+    // shared name works is covered by the standard tests.)
+    {
+        "ECDHE-ECDSA-CHACHA20-POLY1305:"
+        "ECDHE-RSA-CHACHA20-POLY1305:"
+        "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
+        "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0},
+        },
+    },
 };
 
 static const char *kBadRules[] = {
@@ -270,7 +236,6 @@
   "[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 const char *kMustNotIncludeNull[] = {
@@ -287,7 +252,6 @@
   "SSLv3",
   "TLSv1",
   "TLSv1.2",
-  NULL
 };
 
 static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) {
@@ -310,36 +274,35 @@
   }
 }
 
-static bool TestCipherRule(CipherTest *t) {
+static bool TestCipherRule(const 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);
+  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++) {
+  if (sk_SSL_CIPHER_num(ctx->cipher_list->ciphers) != t.expected.size()) {
+    fprintf(stderr, "Error: cipher rule '%s' evaluated to:\n", t.rule);
+    PrintCipherPreferenceList(ctx->cipher_list);
+    return false;
+  }
+
+  for (size_t i = 0; i < t.expected.size(); 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);
+    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;
 }
 
@@ -362,26 +325,26 @@
 }
 
 static bool TestCipherRules() {
-  for (size_t i = 0; kCipherTests[i].rule != NULL; i++) {
-    if (!TestCipherRule(&kCipherTests[i])) {
+  for (const CipherTest &test : kCipherTests) {
+    if (!TestCipherRule(test)) {
       return false;
     }
   }
 
-  for (size_t i = 0; kBadRules[i] != NULL; i++) {
+  for (const char *rule : kBadRules) {
     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]);
+    if (SSL_CTX_set_cipher_list(ctx.get(), rule)) {
+      fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", rule);
       return false;
     }
     ERR_clear_error();
   }
 
-  for (size_t i = 0; kMustNotIncludeNull[i] != NULL; i++) {
-    if (!TestRuleDoesNotIncludeNull(kMustNotIncludeNull[i])) {
+  for (const char *rule : kMustNotIncludeNull) {
+    if (!TestRuleDoesNotIncludeNull(rule)) {
       return false;
     }
   }
@@ -932,23 +895,6 @@
   return ret;
 }
 
-// TODO(davidben): Switch this to a |std::vector<ScopedSSL_SESSION>| once we can
-// rely on a move-aware |std::vector|.
-class ScopedSessionVector {
- public:
-  explicit ScopedSessionVector(std::vector<SSL_SESSION*> *sessions)
-      : sessions_(sessions) {}
-
-  ~ScopedSessionVector() {
-    for (SSL_SESSION *session : *sessions_) {
-      SSL_SESSION_free(session);
-    }
-  }
-
- private:
-  std::vector<SSL_SESSION*> *const sessions_;
-};
-
 // Test that the internal session cache behaves as expected.
 static bool TestInternalSessionCache() {
   ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method()));
@@ -957,38 +903,38 @@
   }
 
   // Prepare 10 test sessions.
-  std::vector<SSL_SESSION*> sessions;
-  ScopedSessionVector cleanup(&sessions);
+  std::vector<ScopedSSL_SESSION> sessions;
   for (int i = 0; i < 10; i++) {
     ScopedSSL_SESSION session = CreateTestSession(i);
     if (!session) {
       return false;
     }
-    sessions.push_back(session.release());
+    sessions.push_back(std::move(session));
   }
 
   SSL_CTX_sess_set_cache_size(ctx.get(), 5);
 
   // Insert all the test sessions.
-  for (SSL_SESSION *session : sessions) {
-    if (!SSL_CTX_add_session(ctx.get(), session)) {
+  for (const auto &session : sessions) {
+    if (!SSL_CTX_add_session(ctx.get(), session.get())) {
       return false;
     }
   }
 
   // Only the last five should be in the list.
-  std::vector<SSL_SESSION*> expected;
-  expected.push_back(sessions[9]);
-  expected.push_back(sessions[8]);
-  expected.push_back(sessions[7]);
-  expected.push_back(sessions[6]);
-  expected.push_back(sessions[5]);
+  std::vector<SSL_SESSION*> expected = {
+      sessions[9].get(),
+      sessions[8].get(),
+      sessions[7].get(),
+      sessions[6].get(),
+      sessions[5].get(),
+  };
   if (!ExpectCache(ctx.get(), expected)) {
     return false;
   }
 
   // Inserting an element already in the cache should fail.
-  if (SSL_CTX_add_session(ctx.get(), sessions[7]) ||
+  if (SSL_CTX_add_session(ctx.get(), sessions[7].get()) ||
       !ExpectCache(ctx.get(), expected)) {
     return false;
   }
@@ -999,32 +945,34 @@
   if (!collision || !SSL_CTX_add_session(ctx.get(), collision.get())) {
     return false;
   }
-  expected.clear();
-  expected.push_back(collision.get());
-  expected.push_back(sessions[9]);
-  expected.push_back(sessions[8]);
-  expected.push_back(sessions[6]);
-  expected.push_back(sessions[5]);
+  expected = {
+      collision.get(),
+      sessions[9].get(),
+      sessions[8].get(),
+      sessions[6].get(),
+      sessions[5].get(),
+  };
   if (!ExpectCache(ctx.get(), expected)) {
     return false;
   }
 
   // Removing sessions behaves correctly.
-  if (!SSL_CTX_remove_session(ctx.get(), sessions[6])) {
+  if (!SSL_CTX_remove_session(ctx.get(), sessions[6].get())) {
     return false;
   }
-  expected.clear();
-  expected.push_back(collision.get());
-  expected.push_back(sessions[9]);
-  expected.push_back(sessions[8]);
-  expected.push_back(sessions[5]);
+  expected = {
+      collision.get(),
+      sessions[9].get(),
+      sessions[8].get(),
+      sessions[5].get(),
+  };
   if (!ExpectCache(ctx.get(), expected)) {
     return false;
   }
 
   // Removing sessions requires an exact match.
-  if (SSL_CTX_remove_session(ctx.get(), sessions[0]) ||
-      SSL_CTX_remove_session(ctx.get(), sessions[7]) ||
+  if (SSL_CTX_remove_session(ctx.get(), sessions[0].get()) ||
+      SSL_CTX_remove_session(ctx.get(), sessions[7].get()) ||
       !ExpectCache(ctx.get(), expected)) {
     return false;
   }
@@ -1032,6 +980,157 @@
   return true;
 }
 
+static uint16_t EpochFromSequence(uint64_t seq) {
+  return static_cast<uint16_t>(seq >> 48);
+}
+
+static ScopedX509 GetTestCertificate() {
+  static const char kCertPEM[] =
+      "-----BEGIN CERTIFICATE-----\n"
+      "MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV\n"
+      "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n"
+      "aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF\n"
+      "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n"
+      "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n"
+      "gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci\n"
+      "HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV\n"
+      "W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV\n"
+      "HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f\n"
+      "Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht\n"
+      "ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr\n"
+      "T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f\n"
+      "j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==\n"
+      "-----END CERTIFICATE-----\n";
+  ScopedBIO bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
+  return ScopedX509(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+}
+
+static ScopedEVP_PKEY GetTestKey() {
+  static const char kKeyPEM[] =
+      "-----BEGIN RSA PRIVATE KEY-----\n"
+      "MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92\n"
+      "kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF\n"
+      "KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB\n"
+      "AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe\n"
+      "i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+\n"
+      "WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ\n"
+      "m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj\n"
+      "QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk\n"
+      "aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj\n"
+      "LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk\n"
+      "104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/\n"
+      "tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd\n"
+      "moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ==\n"
+      "-----END RSA PRIVATE KEY-----\n";
+  ScopedBIO bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
+  return ScopedEVP_PKEY(
+      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+}
+
+static bool TestSequenceNumber(bool dtls) {
+  ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  if (!client_ctx || !server_ctx) {
+    return false;
+  }
+
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  // Create a client and server connected to each other.
+  ScopedSSL client(SSL_new(client_ctx.get())), server(SSL_new(server_ctx.get()));
+  if (!client || !server) {
+    return false;
+  }
+  SSL_set_connect_state(client.get());
+  SSL_set_accept_state(server.get());
+
+  BIO *bio1, *bio2;
+  if (!BIO_new_bio_pair(&bio1, 0, &bio2, 0)) {
+    return false;
+  }
+  // SSL_set_bio takes ownership.
+  SSL_set_bio(client.get(), bio1, bio1);
+  SSL_set_bio(server.get(), bio2, bio2);
+
+  // Drive both their handshakes to completion.
+  for (;;) {
+    int client_ret = SSL_do_handshake(client.get());
+    int client_err = SSL_get_error(client.get(), client_ret);
+    if (client_err != SSL_ERROR_NONE &&
+        client_err != SSL_ERROR_WANT_READ &&
+        client_err != SSL_ERROR_WANT_WRITE) {
+      fprintf(stderr, "Client error: %d\n", client_err);
+      return false;
+    }
+
+    int server_ret = SSL_do_handshake(server.get());
+    int server_err = SSL_get_error(server.get(), server_ret);
+    if (server_err != SSL_ERROR_NONE &&
+        server_err != SSL_ERROR_WANT_READ &&
+        server_err != SSL_ERROR_WANT_WRITE) {
+      fprintf(stderr, "Server error: %d\n", server_err);
+      return false;
+    }
+
+    if (client_ret == 1 && server_ret == 1) {
+      break;
+    }
+  }
+
+  uint64_t client_read_seq = SSL_get_read_sequence(client.get());
+  uint64_t client_write_seq = SSL_get_write_sequence(client.get());
+  uint64_t server_read_seq = SSL_get_read_sequence(server.get());
+  uint64_t server_write_seq = SSL_get_write_sequence(server.get());
+
+  if (dtls) {
+    // Both client and server must be at epoch 1.
+    if (EpochFromSequence(client_read_seq) != 1 ||
+        EpochFromSequence(client_write_seq) != 1 ||
+        EpochFromSequence(server_read_seq) != 1 ||
+        EpochFromSequence(server_write_seq) != 1) {
+      fprintf(stderr, "Bad epochs.\n");
+      return false;
+    }
+
+    // The next record to be written should exceed the largest received.
+    if (client_write_seq <= server_read_seq ||
+        server_write_seq <= client_read_seq) {
+      fprintf(stderr, "Inconsistent sequence numbers.\n");
+      return false;
+    }
+  } else {
+    // The next record to be written should equal the next to be received.
+    if (client_write_seq != server_read_seq ||
+        server_write_seq != client_write_seq) {
+      fprintf(stderr, "Inconsistent sequence numbers.\n");
+      return false;
+    }
+  }
+
+  // Send a record from client to server.
+  uint8_t byte = 0;
+  if (SSL_write(client.get(), &byte, 1) != 1 ||
+      SSL_read(server.get(), &byte, 1) != 1) {
+    fprintf(stderr, "Could not send byte.\n");
+    return false;
+  }
+
+  // The client write and server read sequence numbers should have incremented.
+  if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) ||
+      server_read_seq + 1 != SSL_get_read_sequence(server.get())) {
+    fprintf(stderr, "Sequence numbers did not increment.\n");\
+    return false;
+  }
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1053,7 +1152,9 @@
       !TestCipherGetRFCName() ||
       !TestPaddingExtension() ||
       !TestClientCAList() ||
-      !TestInternalSessionCache()) {
+      !TestInternalSessionCache() ||
+      !TestSequenceNumber(false /* TLS */) ||
+      !TestSequenceNumber(true /* DTLS */)) {
     ERR_print_errors_fp(stderr);
     return 1;
   }
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 0f1d683..b599207 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -136,7 +136,6 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <openssl/err.h>
@@ -144,7 +143,7 @@
 #include <openssl/hmac.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/rand.h>
 
 #include "internal.h"
@@ -224,11 +223,10 @@
   return ret;
 }
 
-int tls1_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
-             size_t secret_len, const char *label, size_t label_len,
-             const uint8_t *seed1, size_t seed1_len,
-             const uint8_t *seed2, size_t seed2_len) {
-
+static int tls1_prf(const SSL *ssl, uint8_t *out, size_t out_len,
+                    const uint8_t *secret, size_t secret_len, const char *label,
+                    size_t label_len, const uint8_t *seed1, size_t seed1_len,
+                    const uint8_t *seed2, size_t seed2_len) {
   if (out_len == 0) {
     return 1;
   }
@@ -260,15 +258,12 @@
   return 1;
 }
 
-static int tls1_generate_key_block(SSL *ssl, uint8_t *out, size_t out_len) {
-  return ssl->enc_method->prf(
-      ssl, out, out_len, ssl->session->master_key,
-      ssl->session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
-      TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random, SSL3_RANDOM_SIZE,
-      ssl->s3->client_random, SSL3_RANDOM_SIZE);
-}
-
 int tls1_change_cipher_state(SSL *ssl, int which) {
+  /* Ensure the key block is set up. */
+  if (!tls1_setup_key_block(ssl)) {
+    return 0;
+  }
+
   /* is_read is true if we have just read a ChangeCipherSpec message - i.e. we
    * need to update the read cipherspec. Otherwise we have just written one. */
   const char is_read = (which & SSL3_CC_READ) != 0;
@@ -277,63 +272,28 @@
    * or a server reading a client's ChangeCipherSpec. */
   const char use_client_keys = which == SSL3_CHANGE_CIPHER_CLIENT_WRITE ||
                                which == SSL3_CHANGE_CIPHER_SERVER_READ;
-  const uint8_t *client_write_mac_secret, *server_write_mac_secret, *mac_secret;
-  const uint8_t *client_write_key, *server_write_key, *key;
-  const uint8_t *client_write_iv, *server_write_iv, *iv;
-  const EVP_AEAD *aead = ssl->s3->tmp.new_aead;
-  size_t key_len, iv_len, mac_secret_len;
-  const uint8_t *key_data;
 
-  /* Reset sequence number to zero. */
-  if (is_read) {
-    if (SSL_IS_DTLS(ssl)) {
-      ssl->d1->r_epoch++;
-      memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap));
-    }
-    memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
-  } else {
-    if (SSL_IS_DTLS(ssl)) {
-      ssl->d1->w_epoch++;
-      memcpy(ssl->d1->last_write_sequence, ssl->s3->write_sequence,
-             sizeof(ssl->s3->write_sequence));
-    }
-    memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
-  }
+  size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
+  size_t key_len = ssl->s3->tmp.new_key_len;
+  size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
+  assert((mac_secret_len + key_len + iv_len) * 2 ==
+         ssl->s3->tmp.key_block_length);
 
-  mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
-  iv_len = ssl->s3->tmp.new_fixed_iv_len;
-
-  if (aead == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  key_len = EVP_AEAD_key_length(aead);
-  if (mac_secret_len > 0) {
-    /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher
-     * suites) the key length reported by |EVP_AEAD_key_length| will
-     * include the MAC and IV key bytes. */
-    if (key_len < mac_secret_len + iv_len) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-    key_len -= mac_secret_len + iv_len;
-  }
-
-  key_data = ssl->s3->tmp.key_block;
-  client_write_mac_secret = key_data;
+  const uint8_t *key_data = ssl->s3->tmp.key_block;
+  const uint8_t *client_write_mac_secret = key_data;
   key_data += mac_secret_len;
-  server_write_mac_secret = key_data;
+  const uint8_t *server_write_mac_secret = key_data;
   key_data += mac_secret_len;
-  client_write_key = key_data;
+  const uint8_t *client_write_key = key_data;
   key_data += key_len;
-  server_write_key = key_data;
+  const uint8_t *server_write_key = key_data;
   key_data += key_len;
-  client_write_iv = key_data;
+  const uint8_t *client_write_iv = key_data;
   key_data += iv_len;
-  server_write_iv = key_data;
+  const uint8_t *server_write_iv = key_data;
   key_data += iv_len;
 
+  const uint8_t *mac_secret, *key, *iv;
   if (use_client_keys) {
     mac_secret = client_write_mac_secret;
     key = client_write_key;
@@ -344,50 +304,51 @@
     iv = server_write_iv;
   }
 
-  if (key_data - ssl->s3->tmp.key_block != ssl->s3->tmp.key_block_length) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  SSL_AEAD_CTX *aead_ctx =
+      SSL_AEAD_CTX_new(is_read ? evp_aead_open : evp_aead_seal,
+                       ssl3_protocol_version(ssl), ssl->s3->tmp.new_cipher, key,
+                       key_len, mac_secret, mac_secret_len, iv, iv_len);
+  if (aead_ctx == NULL) {
     return 0;
   }
 
   if (is_read) {
-    SSL_AEAD_CTX_free(ssl->aead_read_ctx);
-    ssl->aead_read_ctx = SSL_AEAD_CTX_new(
-        evp_aead_open, ssl3_version_from_wire(ssl, ssl->version),
-        ssl->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
-        iv_len);
-    return ssl->aead_read_ctx != NULL;
+    ssl_set_read_state(ssl, aead_ctx);
+  } else {
+    ssl_set_write_state(ssl, aead_ctx);
   }
+  return 1;
+}
 
-  SSL_AEAD_CTX_free(ssl->aead_write_ctx);
-  ssl->aead_write_ctx = SSL_AEAD_CTX_new(
-      evp_aead_seal, ssl3_version_from_wire(ssl, ssl->version),
-      ssl->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
-      iv_len);
-  return ssl->aead_write_ctx != NULL;
+size_t SSL_get_key_block_len(const SSL *ssl) {
+  return 2 * ((size_t)ssl->s3->tmp.new_mac_secret_len +
+              (size_t)ssl->s3->tmp.new_key_len +
+              (size_t)ssl->s3->tmp.new_fixed_iv_len);
+}
+
+int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
+  return ssl->s3->enc_method->prf(
+      ssl, out, out_len, ssl->session->master_key,
+      ssl->session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
+      TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random, SSL3_RANDOM_SIZE,
+      ssl->s3->client_random, SSL3_RANDOM_SIZE);
 }
 
 int tls1_setup_key_block(SSL *ssl) {
-  uint8_t *p;
-  const EVP_AEAD *aead = NULL;
-  int ret = 0;
-  size_t mac_secret_len, fixed_iv_len, variable_iv_len, key_len;
-  size_t key_block_len;
-
   if (ssl->s3->tmp.key_block_length != 0) {
     return 1;
   }
 
-  if (ssl->session->cipher == NULL) {
-    goto cipher_unavailable_err;
-  }
-
-  if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+  const EVP_AEAD *aead = NULL;
+  size_t mac_secret_len, fixed_iv_len;
+  if (ssl->session->cipher == NULL ||
+      !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
                                ssl->session->cipher,
-                               ssl3_version_from_wire(ssl, ssl->version))) {
-    goto cipher_unavailable_err;
+                               ssl3_protocol_version(ssl))) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
+    return 0;
   }
-  key_len = EVP_AEAD_key_length(aead);
-  variable_iv_len = EVP_AEAD_nonce_length(aead);
+  size_t key_len = EVP_AEAD_key_length(aead);
   if (mac_secret_len > 0) {
     /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
      * key length reported by |EVP_AEAD_key_length| will include the MAC key
@@ -397,53 +358,38 @@
       return 0;
     }
     key_len -= mac_secret_len + fixed_iv_len;
-  } else {
-    /* The nonce is split into a fixed portion and a variable portion. */
-    if (variable_iv_len < fixed_iv_len) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-    variable_iv_len -= fixed_iv_len;
   }
 
   assert(mac_secret_len < 256);
+  assert(key_len < 256);
   assert(fixed_iv_len < 256);
-  assert(variable_iv_len < 256);
 
-  ssl->s3->tmp.new_aead = aead;
   ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
+  ssl->s3->tmp.new_key_len = (uint8_t)key_len;
   ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
-  ssl->s3->tmp.new_variable_iv_len = (uint8_t)variable_iv_len;
 
-  key_block_len = key_len + mac_secret_len + fixed_iv_len;
-  key_block_len *= 2;
+  size_t key_block_len = SSL_get_key_block_len(ssl);
 
   ssl3_cleanup_key_block(ssl);
 
-  p = (uint8_t *)OPENSSL_malloc(key_block_len);
-  if (p == NULL) {
+  uint8_t *keyblock = OPENSSL_malloc(key_block_len);
+  if (keyblock == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+    return 0;
   }
 
-  ssl->s3->tmp.key_block_length = key_block_len;
-  ssl->s3->tmp.key_block = p;
-
-  if (!tls1_generate_key_block(ssl, p, key_block_len)) {
-    goto err;
+  if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) {
+    OPENSSL_free(keyblock);
+    return 0;
   }
 
-  ret = 1;
-
-err:
-  return ret;
-
-cipher_unavailable_err:
-  OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
-  return 0;
+  assert(key_block_len < 256);
+  ssl->s3->tmp.key_block_length = (uint8_t)key_block_len;
+  ssl->s3->tmp.key_block = keyblock;
+  return 1;
 }
 
-int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *out) {
+static int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *out) {
   const EVP_MD_CTX *ctx_template;
   if (md_nid == NID_md5) {
     ctx_template = &ssl->s3->handshake_md5;
@@ -512,57 +458,58 @@
   return (int)(md5_len + len);
 }
 
-int tls1_final_finish_mac(SSL *ssl, const char *str, int slen, uint8_t *out) {
-  uint8_t buf[2 * EVP_MAX_MD_SIZE];
-  int err = 0;
-  int digests_len;
-
+static int tls1_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
   /* At this point, the handshake should have released the handshake buffer on
    * its own. */
   assert(ssl->s3->handshake_buffer == NULL);
 
-  digests_len = tls1_handshake_digest(ssl, buf, sizeof(buf));
+  const char *label = TLS_MD_CLIENT_FINISH_CONST;
+  size_t label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
+  if (from_server) {
+    label = TLS_MD_SERVER_FINISH_CONST;
+    label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
+  }
+
+  uint8_t buf[EVP_MAX_MD_SIZE];
+  int digests_len = tls1_handshake_digest(ssl, buf, sizeof(buf));
   if (digests_len < 0) {
-    err = 1;
-    digests_len = 0;
-  }
-
-  if (!ssl->enc_method->prf(ssl, out, 12, ssl->session->master_key,
-                            ssl->session->master_key_length, str, slen, buf,
-                            digests_len, NULL, 0)) {
-    err = 1;
-  }
-
-  if (err) {
     return 0;
-  } else {
-    return 12;
   }
+
+  static const size_t kFinishedLen = 12;
+  if (!ssl->s3->enc_method->prf(ssl, out, kFinishedLen,
+                                ssl->session->master_key,
+                                ssl->session->master_key_length, label,
+                                label_len, buf, digests_len, NULL, 0)) {
+    return 0;
+  }
+
+  return (int)kFinishedLen;
 }
 
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out,
                                 const uint8_t *premaster,
                                 size_t premaster_len) {
   if (ssl->s3->tmp.extended_master_secret) {
-    uint8_t digests[2 * EVP_MAX_MD_SIZE];
+    uint8_t digests[EVP_MAX_MD_SIZE];
     int digests_len = tls1_handshake_digest(ssl, digests, sizeof(digests));
     if (digests_len == -1) {
       return 0;
     }
 
-    if (!ssl->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                              premaster_len,
-                              TLS_MD_EXTENDED_MASTER_SECRET_CONST,
-                              TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests,
-                              digests_len, NULL, 0)) {
+    if (!ssl->s3->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
+                                  premaster_len,
+                                  TLS_MD_EXTENDED_MASTER_SECRET_CONST,
+                                  TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE,
+                                  digests, digests_len, NULL, 0)) {
       return 0;
     }
   } else {
-    if (!ssl->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                              premaster_len, TLS_MD_MASTER_SECRET_CONST,
-                              TLS_MD_MASTER_SECRET_CONST_SIZE,
-                              ssl->s3->client_random, SSL3_RANDOM_SIZE,
-                              ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
+    if (!ssl->s3->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
+                                  premaster_len, TLS_MD_MASTER_SECRET_CONST,
+                                  TLS_MD_MASTER_SECRET_CONST_SIZE,
+                                  ssl->s3->client_random, SSL3_RANDOM_SIZE,
+                                  ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
       return 0;
     }
   }
@@ -570,12 +517,11 @@
   return SSL3_MASTER_SECRET_SIZE;
 }
 
-int tls1_export_keying_material(SSL *ssl, 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 SSL_export_keying_material(SSL *ssl, 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 (!ssl->s3->have_version || ssl->version == SSL3_VERSION) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
 
@@ -601,108 +547,16 @@
     memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len);
   }
 
-  int ret = ssl->enc_method->prf(ssl, out, out_len, ssl->session->master_key,
-                                 ssl->session->master_key_length, label,
-                                 label_len, seed, seed_len, NULL, 0);
+  int ret =
+      ssl->s3->enc_method->prf(ssl, out, out_len, ssl->session->master_key,
+                               ssl->session->master_key_length, label,
+                               label_len, seed, seed_len, NULL, 0);
   OPENSSL_free(seed);
   return ret;
 }
 
-int tls1_alert_code(int code) {
-  switch (code) {
-    case SSL_AD_CLOSE_NOTIFY:
-      return SSL3_AD_CLOSE_NOTIFY;
-
-    case SSL_AD_UNEXPECTED_MESSAGE:
-      return SSL3_AD_UNEXPECTED_MESSAGE;
-
-    case SSL_AD_BAD_RECORD_MAC:
-      return SSL3_AD_BAD_RECORD_MAC;
-
-    case SSL_AD_DECRYPTION_FAILED:
-      return TLS1_AD_DECRYPTION_FAILED;
-
-    case SSL_AD_RECORD_OVERFLOW:
-      return TLS1_AD_RECORD_OVERFLOW;
-
-    case SSL_AD_DECOMPRESSION_FAILURE:
-      return SSL3_AD_DECOMPRESSION_FAILURE;
-
-    case SSL_AD_HANDSHAKE_FAILURE:
-      return SSL3_AD_HANDSHAKE_FAILURE;
-
-    case SSL_AD_NO_CERTIFICATE:
-      return -1;
-
-    case SSL_AD_BAD_CERTIFICATE:
-      return SSL3_AD_BAD_CERTIFICATE;
-
-    case SSL_AD_UNSUPPORTED_CERTIFICATE:
-      return SSL3_AD_UNSUPPORTED_CERTIFICATE;
-
-    case SSL_AD_CERTIFICATE_REVOKED:
-      return SSL3_AD_CERTIFICATE_REVOKED;
-
-    case SSL_AD_CERTIFICATE_EXPIRED:
-      return SSL3_AD_CERTIFICATE_EXPIRED;
-
-    case SSL_AD_CERTIFICATE_UNKNOWN:
-      return SSL3_AD_CERTIFICATE_UNKNOWN;
-
-    case SSL_AD_ILLEGAL_PARAMETER:
-      return SSL3_AD_ILLEGAL_PARAMETER;
-
-    case SSL_AD_UNKNOWN_CA:
-      return TLS1_AD_UNKNOWN_CA;
-
-    case SSL_AD_ACCESS_DENIED:
-      return TLS1_AD_ACCESS_DENIED;
-
-    case SSL_AD_DECODE_ERROR:
-      return TLS1_AD_DECODE_ERROR;
-
-    case SSL_AD_DECRYPT_ERROR:
-      return TLS1_AD_DECRYPT_ERROR;
-    case SSL_AD_EXPORT_RESTRICTION:
-      return TLS1_AD_EXPORT_RESTRICTION;
-
-    case SSL_AD_PROTOCOL_VERSION:
-      return TLS1_AD_PROTOCOL_VERSION;
-
-    case SSL_AD_INSUFFICIENT_SECURITY:
-      return TLS1_AD_INSUFFICIENT_SECURITY;
-
-    case SSL_AD_INTERNAL_ERROR:
-      return TLS1_AD_INTERNAL_ERROR;
-
-    case SSL_AD_USER_CANCELLED:
-      return TLS1_AD_USER_CANCELLED;
-
-    case SSL_AD_NO_RENEGOTIATION:
-      return TLS1_AD_NO_RENEGOTIATION;
-
-    case SSL_AD_UNSUPPORTED_EXTENSION:
-      return TLS1_AD_UNSUPPORTED_EXTENSION;
-
-    case SSL_AD_CERTIFICATE_UNOBTAINABLE:
-      return TLS1_AD_CERTIFICATE_UNOBTAINABLE;
-
-    case SSL_AD_UNRECOGNIZED_NAME:
-      return TLS1_AD_UNRECOGNIZED_NAME;
-
-    case SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE:
-      return TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE;
-
-    case SSL_AD_BAD_CERTIFICATE_HASH_VALUE:
-      return TLS1_AD_BAD_CERTIFICATE_HASH_VALUE;
-
-    case SSL_AD_UNKNOWN_PSK_IDENTITY:
-      return TLS1_AD_UNKNOWN_PSK_IDENTITY;
-
-    case SSL_AD_INAPPROPRIATE_FALLBACK:
-      return SSL3_AD_INAPPROPRIATE_FALLBACK;
-
-    default:
-      return -1;
-  }
-}
+const SSL3_ENC_METHOD TLSv1_enc_data = {
+    tls1_prf,
+    tls1_final_finish_mac,
+    tls1_cert_verify_mac,
+};
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 5aea08b..eac9579 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -110,7 +110,6 @@
 
 #include <assert.h>
 #include <limits.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -120,7 +119,7 @@
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/rand.h>
 #include <openssl/type_check.h>
 
@@ -130,48 +129,6 @@
 static int ssl_check_clienthello_tlsext(SSL *ssl);
 static int ssl_check_serverhello_tlsext(SSL *ssl);
 
-const SSL3_ENC_METHOD TLSv1_enc_data = {
-    tls1_prf,
-    tls1_setup_key_block,
-    tls1_generate_master_secret,
-    tls1_change_cipher_state,
-    tls1_final_finish_mac,
-    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,
-};
-
-const SSL3_ENC_METHOD TLSv1_1_enc_data = {
-    tls1_prf,
-    tls1_setup_key_block,
-    tls1_generate_master_secret,
-    tls1_change_cipher_state,
-    tls1_final_finish_mac,
-    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,
-};
-
-const SSL3_ENC_METHOD TLSv1_2_enc_data = {
-    tls1_prf,
-    tls1_setup_key_block,
-    tls1_generate_master_secret,
-    tls1_change_cipher_state,
-    tls1_final_finish_mac,
-    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|SSL_ENC_FLAG_SIGALGS|SSL_ENC_FLAG_SHA256_PRF,
-};
-
 static int compare_uint16_t(const void *p1, const void *p2) {
   uint16_t u1 = *((const uint16_t *)p1);
   uint16_t u2 = *((const uint16_t *)p2);
@@ -211,8 +168,7 @@
     return 1;
   }
 
-  extension_types =
-      (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * num_extensions);
+  extension_types = OPENSSL_malloc(sizeof(uint16_t) * num_extensions);
   if (extension_types == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto done;
@@ -336,6 +292,7 @@
 }
 
 static const uint16_t eccurves_default[] = {
+    SSL_CURVE_X25519,
     SSL_CURVE_SECP256R1,
     SSL_CURVE_SECP384R1,
 #if defined(BORINGSSL_ANDROID_SYSTEM)
@@ -418,7 +375,7 @@
   uint16_t *curve_ids;
   size_t i;
 
-  curve_ids = (uint16_t *)OPENSSL_malloc(ncurves * sizeof(uint16_t));
+  curve_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
   if (curve_ids == NULL) {
     return 0;
   }
@@ -551,7 +508,6 @@
     tlsext_sigalg(TLSEXT_hash_sha512)
     tlsext_sigalg(TLSEXT_hash_sha384)
     tlsext_sigalg(TLSEXT_hash_sha256)
-    tlsext_sigalg(TLSEXT_hash_sha224)
     tlsext_sigalg(TLSEXT_hash_sha1)
 };
 
@@ -731,82 +687,44 @@
     return 1;
   }
 
-  /* The servername extension is treated as follows:
-   *
-   * - Only the hostname type is supported with a maximum length of 255.
-   * - The servername is rejected if too long or if it contains zeros, in
-   *   which case an fatal alert is generated.
-   * - The servername field is maintained together with the session cache.
-   * - When a session is resumed, the servername callback is invoked in order
-   *   to allow the application to position itself to the right context.
-   * - The servername is acknowledged if it is new for a session or when
-   *   it is identical to a previously used for the same session.
-   *   Applications can control the behaviour.  They can at any time
-   *   set a 'desirable' servername for a new SSL object. This can be the
-   *   case for example with HTTPS when a Host: header field is received and
-   *   a renegotiation is requested. In this case, a possible servername
-   *   presented in the new client hello is only acknowledged if it matches
-   *   the value of the Host: field.
-   * - Applications must  use SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
-   *   if they provide for changing an explicit servername context for the
-   *   session,
-   *   i.e. when the session has been established with a servername extension.
-   */
-
-  CBS server_name_list;
-  char have_seen_host_name = 0;
-
+  CBS server_name_list, host_name;
+  uint8_t name_type;
   if (!CBS_get_u16_length_prefixed(contents, &server_name_list) ||
-      CBS_len(&server_name_list) == 0 ||
+      !CBS_get_u8(&server_name_list, &name_type) ||
+      /* Although the server_name extension was intended to be extensible to
+       * new name types and multiple names, OpenSSL 1.0.x had a bug which meant
+       * different name types will cause an error. Further, RFC 4366 originally
+       * defined syntax inextensibly. RFC 6066 corrected this mistake, but
+       * adding new name types is no longer feasible.
+       *
+       * Act as if the extensibility does not exist to simplify parsing. */
+      !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
+      CBS_len(&server_name_list) != 0 ||
       CBS_len(contents) != 0) {
     return 0;
   }
 
-  /* Decode each ServerName in the extension. */
-  while (CBS_len(&server_name_list) > 0) {
-    uint8_t name_type;
-    CBS host_name;
+  if (name_type != TLSEXT_NAMETYPE_host_name ||
+      CBS_len(&host_name) == 0 ||
+      CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
+      CBS_contains_zero_byte(&host_name)) {
+    *out_alert = SSL_AD_UNRECOGNIZED_NAME;
+    return 0;
+  }
 
-    if (!CBS_get_u8(&server_name_list, &name_type) ||
-        !CBS_get_u16_length_prefixed(&server_name_list, &host_name)) {
+  /* TODO(davidben): SNI should be resolved before resumption. We have the
+   * early callback as a replacement, but we should fix the current callback
+   * and avoid the need for |SSL_CTX_set_session_id_context|. */
+  if (!ssl->hit) {
+    assert(ssl->session->tlsext_hostname == NULL);
+
+    /* Copy the hostname as a string. */
+    if (!CBS_strdup(&host_name, &ssl->session->tlsext_hostname)) {
+      *out_alert = SSL_AD_INTERNAL_ERROR;
       return 0;
     }
 
-    /* Only host_name is supported. */
-    if (name_type != TLSEXT_NAMETYPE_host_name) {
-      continue;
-    }
-
-    if (have_seen_host_name) {
-      /* The ServerNameList MUST NOT contain more than one name of the same
-       * name_type. */
-      return 0;
-    }
-
-    have_seen_host_name = 1;
-
-    if (CBS_len(&host_name) == 0 ||
-        CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
-        CBS_contains_zero_byte(&host_name)) {
-      *out_alert = SSL_AD_UNRECOGNIZED_NAME;
-      return 0;
-    }
-
-    if (!ssl->hit) {
-      assert(ssl->session->tlsext_hostname == NULL);
-      if (ssl->session->tlsext_hostname) {
-        /* This should be impossible. */
-        return 0;
-      }
-
-      /* Copy the hostname as a string. */
-      if (!CBS_strdup(&host_name, &ssl->session->tlsext_hostname)) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-
-      ssl->s3->tmp.should_ack_sni = 1;
-    }
+    ssl->s3->tmp.should_ack_sni = 1;
   }
 
   return 1;
@@ -975,7 +893,7 @@
 
 /* Extended Master Secret.
  *
- * https://tools.ietf.org/html/draft-ietf-tls-session-hash-05 */
+ * https://tools.ietf.org/html/rfc7627 */
 
 static void ext_ems_init(SSL *ssl) {
   ssl->s3->tmp.extended_master_secret = 0;
@@ -1185,6 +1103,7 @@
 
 static void ext_ocsp_init(SSL *ssl) {
   ssl->s3->tmp.certificate_status_expected = 0;
+  ssl->tlsext_status_type = -1;
 }
 
 static int ext_ocsp_add_clienthello(SSL *ssl, CBB *out) {
@@ -1202,6 +1121,7 @@
     return 0;
   }
 
+  ssl->tlsext_status_type = TLSEXT_STATUSTYPE_ocsp;
   return 1;
 }
 
@@ -1317,14 +1237,14 @@
     return 0;
   }
 
-  OPENSSL_free(ssl->next_proto_negotiated);
-  ssl->next_proto_negotiated = BUF_memdup(selected, selected_len);
-  if (ssl->next_proto_negotiated == NULL) {
+  OPENSSL_free(ssl->s3->next_proto_negotiated);
+  ssl->s3->next_proto_negotiated = BUF_memdup(selected, selected_len);
+  if (ssl->s3->next_proto_negotiated == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
   }
 
-  ssl->next_proto_negotiated_len = selected_len;
+  ssl->s3->next_proto_negotiated_len = selected_len;
   ssl->s3->next_proto_neg_seen = 1;
 
   return 1;
@@ -1945,9 +1865,7 @@
     return 0;
   }
 
-  ssl->s3->tmp.peer_ellipticcurvelist =
-      (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list));
-
+  ssl->s3->tmp.peer_ellipticcurvelist = OPENSSL_malloc(CBS_len(&elliptic_curve_list));
   if (ssl->s3->tmp.peer_ellipticcurvelist == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
@@ -2081,6 +1999,9 @@
     ext_ec_point_parse_clienthello,
     ext_ec_point_add_serverhello,
   },
+  /* The final extension must be non-empty. WebSphere Application Server 7.0 is
+   * intolerant to the last extension being zero-length. See
+   * https://crbug.com/363583. */
   {
     TLSEXT_TYPE_elliptic_curves,
     ext_ec_curves_init,
@@ -2167,9 +2088,10 @@
        * NB: because this code works out the length of all existing extensions
        * it MUST always appear last. */
       size_t padding_len = 0x200 - header_len;
-      /* Extensions take at least four bytes to encode. Always include least
+      /* Extensions take at least four bytes to encode. Always include at least
        * one byte of data if including the extension. WebSphere Application
-       * Server 7.0 is intolerant to the last extension being zero-length. */
+       * Server 7.0 is intolerant to the last extension being zero-length. See
+       * https://crbug.com/363583. */
       if (padding_len >= 4 + 1) {
         padding_len -= 4;
       } else {
@@ -2411,14 +2333,10 @@
   int ret = SSL_TLSEXT_ERR_NOACK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
-  /* The handling of the ECPointFormats extension is done elsewhere, namely in
-   * ssl3_choose_cipher in s3_lib.c. */
-
-  if (ssl->ctx != NULL && ssl->ctx->tlsext_servername_callback != 0) {
+  if (ssl->ctx->tlsext_servername_callback != 0) {
     ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
-                                             ssl->ctx->tlsext_servername_arg);
-  } else if (ssl->initial_ctx != NULL &&
-             ssl->initial_ctx->tlsext_servername_callback != 0) {
+                                               ssl->ctx->tlsext_servername_arg);
+  } else if (ssl->initial_ctx->tlsext_servername_callback != 0) {
     ret = ssl->initial_ctx->tlsext_servername_callback(
         ssl, &al, ssl->initial_ctx->tlsext_servername_arg);
   }
@@ -2445,11 +2363,10 @@
   int ret = SSL_TLSEXT_ERR_OK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
-  if (ssl->ctx != NULL && ssl->ctx->tlsext_servername_callback != 0) {
+  if (ssl->ctx->tlsext_servername_callback != 0) {
     ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
-                                             ssl->ctx->tlsext_servername_arg);
-  } else if (ssl->initial_ctx != NULL &&
-             ssl->initial_ctx->tlsext_servername_callback != 0) {
+                                               ssl->ctx->tlsext_servername_arg);
+  } else if (ssl->initial_ctx->tlsext_servername_callback != 0) {
     ret = ssl->initial_ctx->tlsext_servername_callback(
         ssl, &al, ssl->initial_ctx->tlsext_servername_arg);
   }
@@ -2484,7 +2401,7 @@
 }
 
 int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
-                       int *out_send_ticket, const uint8_t *ticket,
+                       int *out_renew_ticket, const uint8_t *ticket,
                        size_t ticket_len, const uint8_t *session_id,
                        size_t session_id_len) {
   int ret = 1; /* Most errors are non-fatal. */
@@ -2496,19 +2413,13 @@
   EVP_CIPHER_CTX cipher_ctx;
   EVP_CIPHER_CTX_init(&cipher_ctx);
 
-  *out_send_ticket = 0;
+  *out_renew_ticket = 0;
   *out_session = NULL;
 
   if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
     goto done;
   }
 
-  if (ticket_len == 0) {
-    /* The client will accept a ticket but doesn't currently have one. */
-    *out_send_ticket = 1;
-    goto done;
-  }
-
   /* 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
@@ -2530,7 +2441,7 @@
       goto done;
     }
     if (cb_ret == 2) {
-      *out_send_ticket = 1;
+      *out_renew_ticket = 1;
     }
   } else {
     /* Check the key name matches. */
@@ -2609,12 +2520,12 @@
   int id;
 } tls12_lookup;
 
-static const tls12_lookup tls12_md[] = {{NID_md5, TLSEXT_hash_md5},
-                                        {NID_sha1, TLSEXT_hash_sha1},
-                                        {NID_sha224, TLSEXT_hash_sha224},
-                                        {NID_sha256, TLSEXT_hash_sha256},
-                                        {NID_sha384, TLSEXT_hash_sha384},
-                                        {NID_sha512, TLSEXT_hash_sha512}};
+static const tls12_lookup tls12_md[] = {
+    {NID_sha1, TLSEXT_hash_sha1},
+    {NID_sha256, TLSEXT_hash_sha256},
+    {NID_sha384, TLSEXT_hash_sha384},
+    {NID_sha512, TLSEXT_hash_sha512},
+};
 
 static const tls12_lookup tls12_sig[] = {{EVP_PKEY_RSA, TLSEXT_signature_rsa},
                                          {EVP_PKEY_EC, TLSEXT_signature_ecdsa}};
@@ -2648,15 +2559,9 @@
 
 const EVP_MD *tls12_get_hash(uint8_t hash_alg) {
   switch (hash_alg) {
-    case TLSEXT_hash_md5:
-      return EVP_md5();
-
     case TLSEXT_hash_sha1:
       return EVP_sha1();
 
-    case TLSEXT_hash_sha224:
-      return EVP_sha224();
-
     case TLSEXT_hash_sha256:
       return EVP_sha256();
 
@@ -2691,7 +2596,7 @@
 
 int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *in_sigalgs) {
   /* Extension ignored for inappropriate versions */
-  if (!SSL_USE_SIGALGS(ssl)) {
+  if (ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
     return 1;
   }
 
@@ -2742,7 +2647,7 @@
   size_t i, j;
 
   static const int kDefaultDigestList[] = {NID_sha256, NID_sha384, NID_sha512,
-                                           NID_sha224, NID_sha1};
+                                           NID_sha1};
 
   const int *digest_nids = kDefaultDigestList;
   size_t num_digest_nids =
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 74674a4..5effa58 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -46,7 +46,7 @@
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/hmac.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
@@ -138,13 +138,20 @@
   return (TestState *)SSL_get_ex_data(ssl, g_state_index);
 }
 
+static ScopedX509 LoadCertificate(const std::string &file) {
+  ScopedBIO bio(BIO_new(BIO_s_file()));
+  if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
+    return nullptr;
+  }
+  return ScopedX509(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL));
+}
+
 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;
   }
-  ScopedEVP_PKEY pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
-  return pkey;
+  return ScopedEVP_PKEY(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
 }
 
 static int AsyncPrivateKeyType(SSL *ssl) {
@@ -291,9 +298,9 @@
   }
 };
 
-static bool InstallCertificate(SSL *ssl) {
+static bool GetCertificate(SSL *ssl, ScopedX509 *out_x509,
+                           ScopedEVP_PKEY *out_pkey) {
   const TestConfig *config = GetConfigPtr(ssl);
-  TestState *test_state = GetTestState(ssl);
 
   if (!config->digest_prefs.empty()) {
     std::unique_ptr<char, Free<char>> digest_prefs(
@@ -317,21 +324,16 @@
   }
 
   if (!config->key_file.empty()) {
-    if (config->async) {
-      test_state->private_key = LoadPrivateKey(config->key_file.c_str());
-      if (!test_state->private_key) {
-        return false;
-      }
-      SSL_set_private_key_method(ssl, &g_async_private_key_method);
-    } else if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(),
-                                        SSL_FILETYPE_PEM)) {
+    *out_pkey = LoadPrivateKey(config->key_file.c_str());
+    if (!*out_pkey) {
       return false;
     }
   }
-  if (!config->cert_file.empty() &&
-      !SSL_use_certificate_file(ssl, config->cert_file.c_str(),
-                                SSL_FILETYPE_PEM)) {
-    return false;
+  if (!config->cert_file.empty()) {
+    *out_x509 = LoadCertificate(config->cert_file.c_str());
+    if (!*out_x509) {
+      return false;
+    }
   }
   if (!config->ocsp_response.empty() &&
       !SSL_CTX_set_ocsp_response(ssl->ctx,
@@ -342,6 +344,31 @@
   return true;
 }
 
+static bool InstallCertificate(SSL *ssl) {
+  ScopedX509 x509;
+  ScopedEVP_PKEY pkey;
+  if (!GetCertificate(ssl, &x509, &pkey)) {
+    return false;
+  }
+
+  if (pkey) {
+    TestState *test_state = GetTestState(ssl);
+    const TestConfig *config = GetConfigPtr(ssl);
+    if (config->async) {
+      test_state->private_key = std::move(pkey);
+      SSL_set_private_key_method(ssl, &g_async_private_key_method);
+    } else if (!SSL_use_PrivateKey(ssl, pkey.get())) {
+      return false;
+    }
+  }
+
+  if (x509 && !SSL_use_certificate(ssl, x509.get())) {
+    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;
@@ -394,6 +421,28 @@
   return 1;
 }
 
+static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
+  if (GetConfigPtr(ssl)->async && !GetTestState(ssl)->cert_ready) {
+    return -1;
+  }
+
+  ScopedX509 x509;
+  ScopedEVP_PKEY pkey;
+  if (!GetCertificate(ssl, &x509, &pkey)) {
+    return -1;
+  }
+
+  // Return zero for no certificate.
+  if (!x509) {
+    return 0;
+  }
+
+  // Asynchronous private keys are not supported with client_cert_cb.
+  *out_x509 = x509.release();
+  *out_pkey = pkey.release();
+  return 1;
+}
+
 static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) {
   SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
       SSL_get_ex_data_X509_STORE_CTX_idx());
@@ -444,7 +493,7 @@
 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->decline_alpn) {
     return SSL_TLSEXT_ERR_NOACK;
   }
 
@@ -732,6 +781,9 @@
   }
 
   ScopedDH dh(DH_get_2048_256(NULL));
+  if (!dh) {
+    return nullptr;
+  }
 
   if (config->use_sparse_dh_prime) {
     // This prime number is 2^1024 + 643 – a value just above a power of two.
@@ -747,10 +799,12 @@
         !BN_set_word(dh->g, 2)) {
       return nullptr;
     }
+    BN_free(dh->q);
+    dh->q = NULL;
     dh->priv_length = 0;
   }
 
-  if (!dh || !SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) {
+  if (!SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) {
     return nullptr;
   }
 
@@ -766,6 +820,10 @@
 
   SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback);
 
+  if (config->use_old_client_cert_callback) {
+    SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback);
+  }
+
   SSL_CTX_set_next_protos_advertised_cb(
       ssl_ctx.get(), NextProtosAdvertisedCallback, NULL);
   if (!config->select_next_proto.empty()) {
@@ -773,7 +831,7 @@
                                      NULL);
   }
 
-  if (!config->select_alpn.empty()) {
+  if (!config->select_alpn.empty() || config->decline_alpn) {
     SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
   }
 
@@ -1138,9 +1196,8 @@
       !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
     return false;
   }
-  if (!config->use_early_callback) {
+  if (!config->use_early_callback && !config->use_old_client_cert_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;
@@ -1253,7 +1310,7 @@
   }
   if (config->enable_all_curves) {
     static const int kAllCurves[] = {
-        NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_x25519,
+        NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
     };
     if (!SSL_set1_curves(ssl.get(), kAllCurves,
                          sizeof(kAllCurves) / sizeof(kAllCurves[0]))) {
@@ -1274,12 +1331,18 @@
   if (config->is_dtls) {
     ScopedBIO packeted =
         PacketedBioCreate(&GetTestState(ssl.get())->clock_delta);
+    if (!packeted) {
+      return false;
+    }
     BIO_push(packeted.get(), bio.release());
     bio = std::move(packeted);
   }
   if (config->async) {
     ScopedBIO async_scoped =
         config->is_dtls ? AsyncBioCreateDatagram() : AsyncBioCreate();
+    if (!async_scoped) {
+      return false;
+    }
     BIO_push(async_scoped.get(), bio.release());
     GetTestState(ssl.get())->async_bio = async_scoped.get();
     bio = std::move(async_scoped);
@@ -1483,7 +1546,16 @@
   return true;
 }
 
+class StderrDelimiter {
+ public:
+  ~StderrDelimiter() { fprintf(stderr, "--- DONE ---\n"); }
+};
+
 int main(int argc, char **argv) {
+  // To distinguish ASan's output from ours, add a trailing message to stderr.
+  // Anything following this line will be considered an error.
+  StderrDelimiter delimiter;
+
 #if defined(OPENSSL_WINDOWS)
   /* Initialize Winsock. */
   WORD wsa_version = MAKEWORD(2, 2);
diff --git a/src/ssl/test/runner/alert.go b/src/ssl/test/runner/alert.go
index 541216e..7db6826 100644
--- a/src/ssl/test/runner/alert.go
+++ b/src/ssl/test/runner/alert.go
@@ -22,6 +22,7 @@
 	alertRecordOverflow         alert = 22
 	alertDecompressionFailure   alert = 30
 	alertHandshakeFailure       alert = 40
+	alertNoCertficate           alert = 41
 	alertBadCertificate         alert = 42
 	alertUnsupportedCertificate alert = 43
 	alertCertificateRevoked     alert = 44
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index db3c675..2e9ce04 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -424,6 +424,10 @@
 	// to be wrong.
 	InvalidSKXCurve bool
 
+	// InvalidECDHPoint, if true, causes the ECC points in
+	// ServerKeyExchange or ClientKeyExchange messages to be invalid.
+	InvalidECDHPoint bool
+
 	// BadECDSAR controls ways in which the 'r' value of an ECDSA signature
 	// can be invalid.
 	BadECDSAR BadValue
@@ -469,6 +473,10 @@
 	// NewSessionTicket message despite promising to in ServerHello.
 	SkipNewSessionTicket bool
 
+	// SkipClientCertificate causes the client to skip the Certificate
+	// message.
+	SkipClientCertificate bool
+
 	// SkipChangeCipherSpec causes the implementation to skip
 	// sending the ChangeCipherSpec message (and adjusting cipher
 	// state accordingly for the Finished message).
@@ -517,6 +525,10 @@
 	// two records.
 	FragmentAlert bool
 
+	// DoubleAlert will cause all alerts to be sent as two copies packed
+	// within one record.
+	DoubleAlert bool
+
 	// SendSpuriousAlert, if non-zero, will cause an spurious, unwanted
 	// alert to be sent.
 	SendSpuriousAlert alert
@@ -814,6 +826,14 @@
 	// BadHelloRequest, if not nil, is what to send instead of a
 	// HelloRequest.
 	BadHelloRequest []byte
+
+	// RequireSessionTickets, if true, causes the client to require new
+	// sessions use session tickets instead of session IDs.
+	RequireSessionTickets bool
+
+	// NullAllCiphers, if true, causes every cipher to behave like the null
+	// cipher.
+	NullAllCiphers bool
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index cb60a92..43548e8 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -189,6 +189,11 @@
 	hc.nextMac = nil
 	hc.config = config
 	hc.incEpoch()
+
+	if config.Bugs.NullAllCiphers {
+		hc.cipher = nil
+		hc.mac = nil
+	}
 	return nil
 }
 
@@ -837,6 +842,9 @@
 	if c.config.Bugs.FragmentAlert {
 		c.writeRecord(recordTypeAlert, c.tmp[0:1])
 		c.writeRecord(recordTypeAlert, c.tmp[1:2])
+	} else if c.config.Bugs.DoubleAlert {
+		copy(c.tmp[2:4], c.tmp[0:2])
+		c.writeRecord(recordTypeAlert, c.tmp[0:4])
 	} else {
 		c.writeRecord(recordTypeAlert, c.tmp[0:2])
 	}
@@ -851,7 +859,7 @@
 // L < c.out.Mutex.
 func (c *Conn) sendAlert(err alert) error {
 	level := byte(alertLevelError)
-	if err == alertNoRenegotiation || err == alertCloseNotify {
+	if err == alertNoRenegotiation || err == alertCloseNotify || err == alertNoCertficate {
 		level = alertLevelWarning
 	}
 	return c.SendAlert(level, err)
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 64630ba..67609fc 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -363,6 +363,9 @@
 	}
 
 	if sessionCache != nil && hs.session != nil && session != hs.session {
+		if c.config.Bugs.RequireSessionTickets && len(hs.session.sessionTicket) == 0 {
+			return errors.New("tls: new session used session IDs instead of tickets")
+		}
 		sessionCache.Put(cacheKey, hs.session)
 	}
 
@@ -561,15 +564,20 @@
 	hs.writeServerHash(shd.marshal())
 
 	// If the server requested a certificate then we have to send a
-	// Certificate message, even if it's empty because we don't have a
-	// certificate to send.
+	// Certificate message in TLS, even if it's empty because we don't have
+	// a certificate to send. In SSL 3.0, skip the message and send a
+	// no_certificate warning alert.
 	if certRequested {
-		certMsg := new(certificateMsg)
-		if chainToSend != nil {
-			certMsg.certificates = chainToSend.Certificate
+		if c.vers == VersionSSL30 && chainToSend == nil {
+			c.sendAlert(alertNoCertficate)
+		} else if !c.config.Bugs.SkipClientCertificate {
+			certMsg := new(certificateMsg)
+			if chainToSend != nil {
+				certMsg.certificates = chainToSend.Certificate
+			}
+			hs.writeClientHash(certMsg.marshal())
+			c.writeRecord(recordTypeHandshake, certMsg.marshal())
 		}
-		hs.writeClientHash(certMsg.marshal())
-		c.writeRecord(recordTypeHandshake, certMsg.marshal())
 	}
 
 	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, leaf)
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 530ddbc..092f51e 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -349,7 +349,7 @@
 		z = z[4:]
 	}
 	if m.extendedMasterSecret {
-		// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+		// https://tools.ietf.org/html/rfc7627
 		z[0] = byte(extensionExtendedMasterSecret >> 8)
 		z[1] = byte(extensionExtendedMasterSecret & 0xff)
 		z = z[4:]
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 0232772..d2cac98 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -626,13 +626,22 @@
 	// certificate message, even if it's empty.
 	if config.ClientAuth >= RequestClientCert {
 		var certMsg *certificateMsg
-		if certMsg, ok = msg.(*certificateMsg); !ok {
+		var certificates [][]byte
+		if certMsg, ok = msg.(*certificateMsg); ok {
+			if c.vers == VersionSSL30 && len(certMsg.certificates) == 0 {
+				return errors.New("tls: empty certificate message in SSL 3.0")
+			}
+
+			hs.writeClientHash(certMsg.marshal())
+			certificates = certMsg.certificates
+		} else if c.vers != VersionSSL30 {
+			// In TLS, the Certificate message is required. In SSL
+			// 3.0, the peer skips it when sending no certificates.
 			c.sendAlert(alertUnexpectedMessage)
 			return unexpectedMessageError(certMsg, msg)
 		}
-		hs.writeClientHash(certMsg.marshal())
 
-		if len(certMsg.certificates) == 0 {
+		if len(certificates) == 0 {
 			// The client didn't actually send a certificate
 			switch config.ClientAuth {
 			case RequireAnyClientCert, RequireAndVerifyClientCert:
@@ -641,14 +650,16 @@
 			}
 		}
 
-		pub, err = hs.processCertsFromClient(certMsg.certificates)
+		pub, err = hs.processCertsFromClient(certificates)
 		if err != nil {
 			return err
 		}
 
-		msg, err = c.readHandshake()
-		if err != nil {
-			return err
+		if ok {
+			msg, err = c.readHandshake()
+			if err != nil {
+				return err
+			}
 		}
 	}
 
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 4f399d9..54aa3d3 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -312,8 +312,7 @@
 	copy(peerKeyCopy[:], peerKey)
 	curve25519.ScalarMult(&out, &e.privateKey, &peerKeyCopy)
 
-	// Per draft-irtf-cfrg-curves-11, reject the all-zero value in constant
-	// time.
+	// Per RFC 7748, reject the all-zero value in constant time.
 	var zeros [32]byte
 	if subtle.ConstantTimeCompare(zeros[:], out[:]) == 1 {
 		return nil, errors.New("tls: X25519 value with wrong order")
@@ -567,6 +566,9 @@
 	}
 	serverECDHParams[3] = byte(len(publicKey))
 	copy(serverECDHParams[4:], publicKey)
+	if config.Bugs.InvalidECDHPoint {
+		serverECDHParams[4] ^= 0xff
+	}
 
 	return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams)
 }
@@ -623,6 +625,9 @@
 	ckx.ciphertext = make([]byte, 1+len(publicKey))
 	ckx.ciphertext[0] = byte(len(publicKey))
 	copy(ckx.ciphertext[1:], publicKey)
+	if config.Bugs.InvalidECDHPoint {
+		ckx.ciphertext[1] ^= 0xff
+	}
 
 	return preMasterSecret, ckx, nil
 }
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index 8521aba..f1b26de 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -153,7 +153,7 @@
 
 // extendedMasterFromPreMasterSecret generates the master secret from the
 // pre-master secret when the Triple Handshake fix is in effect. See
-// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+// https://tools.ietf.org/html/rfc7627
 func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, h finishedHash) []byte {
 	masterSecret := make([]byte, masterSecretLength)
 	prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, h.Sum())
diff --git a/src/ssl/test/runner/recordingconn.go b/src/ssl/test/runner/recordingconn.go
index 39deb19..752610e 100644
--- a/src/ssl/test/runner/recordingconn.go
+++ b/src/ssl/test/runner/recordingconn.go
@@ -92,6 +92,17 @@
 	}
 }
 
+func (r *recordingConn) Transcript() []byte {
+	var ret []byte
+	for _, flow := range r.flows {
+		if flow.flowType != writeFlow {
+			continue
+		}
+		ret = append(ret, flow.data...)
+	}
+	return ret
+}
+
 func parseTestData(r io.Reader) (flows [][]byte, err error) {
 	var currentFlow []byte
 
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 45bb0b7..5b746c6 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -37,6 +37,9 @@
 	numWorkers      = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
 	shimPath        = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.")
 	resourceDir     = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.")
+	fuzzer          = flag.Bool("fuzzer", false, "If true, tests against a BoringSSL built in fuzzer mode.")
+	transcriptDir   = flag.String("transcript-dir", "", "The directory in which to write transcripts.")
+	timeout         = flag.Int("timeout", 15, "The number of seconds to wait for a read or write to bssl_shim.")
 )
 
 const (
@@ -244,15 +247,68 @@
 
 var testCases []testCase
 
+func writeTranscript(test *testCase, isResume bool, data []byte) {
+	if len(data) == 0 {
+		return
+	}
+
+	protocol := "tls"
+	if test.protocol == dtls {
+		protocol = "dtls"
+	}
+
+	side := "client"
+	if test.testType == serverTest {
+		side = "server"
+	}
+
+	dir := path.Join(*transcriptDir, protocol, side)
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		fmt.Fprintf(os.Stderr, "Error making %s: %s\n", dir, err)
+		return
+	}
+
+	name := test.name
+	if isResume {
+		name += "-Resume"
+	} else {
+		name += "-Normal"
+	}
+
+	if err := ioutil.WriteFile(path.Join(dir, name), data, 0644); err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", name, err)
+	}
+}
+
+// A timeoutConn implements an idle timeout on each Read and Write operation.
+type timeoutConn struct {
+	net.Conn
+	timeout time.Duration
+}
+
+func (t *timeoutConn) Read(b []byte) (int, error) {
+	if err := t.SetReadDeadline(time.Now().Add(t.timeout)); err != nil {
+		return 0, err
+	}
+	return t.Conn.Read(b)
+}
+
+func (t *timeoutConn) Write(b []byte) (int, error) {
+	if err := t.SetWriteDeadline(time.Now().Add(t.timeout)); err != nil {
+		return 0, err
+	}
+	return t.Conn.Write(b)
+}
+
 func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) error {
-	var connDamage *damageAdaptor
+	conn = &timeoutConn{conn, time.Duration(*timeout) * time.Second}
 
 	if test.protocol == dtls {
 		config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
 		conn = config.Bugs.PacketAdaptor
 	}
 
-	if *flagDebug {
+	if *flagDebug || len(*transcriptDir) != 0 {
 		local, peer := "client", "server"
 		if test.testType == clientTest {
 			local, peer = peer, local
@@ -264,9 +320,14 @@
 			peer:       peer,
 		}
 		conn = connDebug
-		defer func() {
-			connDebug.WriteTo(os.Stdout)
-		}()
+		if *flagDebug {
+			defer connDebug.WriteTo(os.Stdout)
+		}
+		if len(*transcriptDir) != 0 {
+			defer func() {
+				writeTranscript(test, isResume, connDebug.Transcript())
+			}()
+		}
 
 		if config.Bugs.PacketAdaptor != nil {
 			config.Bugs.PacketAdaptor.debug = connDebug
@@ -277,6 +338,7 @@
 		conn = newReplayAdaptor(conn)
 	}
 
+	var connDamage *damageAdaptor
 	if test.damageFirstWrite {
 		connDamage = newDamageAdaptor(conn)
 		conn = connDamage
@@ -686,6 +748,9 @@
 			config.ServerName = "test"
 		}
 	}
+	if *fuzzer {
+		config.Bugs.NullAllCiphers = true
+	}
 
 	conn, err := acceptOrWait(listener, waitChan)
 	if err == nil {
@@ -713,6 +778,9 @@
 				resumeConfig.ClientSessionCache = config.ClientSessionCache
 				resumeConfig.ServerSessionCache = config.ServerSessionCache
 			}
+			if *fuzzer {
+				resumeConfig.Bugs.NullAllCiphers = true
+			}
 		} else {
 			resumeConfig = config
 		}
@@ -736,8 +804,18 @@
 		}
 	}
 
-	stdout := string(stdoutBuf.Bytes())
-	stderr := string(stderrBuf.Bytes())
+	// Account for Windows line endings.
+	stdout := strings.Replace(string(stdoutBuf.Bytes()), "\r\n", "\n", -1)
+	stderr := strings.Replace(string(stderrBuf.Bytes()), "\r\n", "\n", -1)
+
+	// Separate the errors from the shim and those from tools like
+	// AddressSanitizer.
+	var extraStderr string
+	if stderrParts := strings.SplitN(stderr, "--- DONE ---\n", 2); len(stderrParts) == 2 {
+		stderr = stderrParts[0]
+		extraStderr = stderrParts[1]
+	}
+
 	failed := err != nil || childErr != nil
 	correctFailure := len(test.expectedError) == 0 || strings.Contains(stderr, test.expectedError)
 	localError := "none"
@@ -769,8 +847,8 @@
 		return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, stdout, stderr)
 	}
 
-	if !*useValgrind && !failed && len(stderr) > 0 {
-		println(stderr)
+	if !*useValgrind && (len(extraStderr) > 0 || (!failed && len(stderr) > 0)) {
+		return fmt.Errorf("unexpected error output:\n%s\n%s", stderr, extraStderr)
 	}
 
 	return nil
@@ -1128,6 +1206,31 @@
 		},
 		{
 			testType: serverTest,
+			name:     "DoubleAlert",
+			config: Config{
+				Bugs: ProtocolBugs{
+					DoubleAlert:       true,
+					SendSpuriousAlert: alertRecordOverflow,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_ALERT:",
+		},
+		{
+			protocol: dtls,
+			testType: serverTest,
+			name:     "DoubleAlert-DTLS",
+			config: Config{
+				Bugs: ProtocolBugs{
+					DoubleAlert:       true,
+					SendSpuriousAlert: alertRecordOverflow,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_ALERT:",
+		},
+		{
+			testType: serverTest,
 			name:     "EarlyChangeCipherSpec-server-1",
 			config: Config{
 				Bugs: ProtocolBugs{
@@ -1708,7 +1811,18 @@
 			expectedError: ":WRONG_CURVE:",
 		},
 		{
-			name: "BadFinished",
+			name: "BadFinished-Client",
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadFinished: true,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":DIGEST_CHECK_FAILED:",
+		},
+		{
+			testType: serverTest,
+			name:     "BadFinished-Server",
 			config: Config{
 				Bugs: ProtocolBugs{
 					BadFinished: true,
@@ -2088,6 +2202,40 @@
 			shouldFail:    true,
 			expectedError: ":BAD_HELLO_REQUEST:",
 		},
+		{
+			testType: serverTest,
+			name:     "SupportTicketsWithSessionID",
+			config: Config{
+				SessionTicketsDisabled: true,
+			},
+			resumeConfig:  &Config{},
+			resumeSession: true,
+		},
+		{
+			name: "InvalidECDHPoint-Client",
+			config: Config{
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{CurveP256},
+				Bugs: ProtocolBugs{
+					InvalidECDHPoint: true,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":INVALID_ENCODING:",
+		},
+		{
+			testType: serverTest,
+			name:     "InvalidECDHPoint-Server",
+			config: Config{
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{CurveP256},
+				Bugs: ProtocolBugs{
+					InvalidECDHPoint: true,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":INVALID_ENCODING:",
+		},
 	}
 	testCases = append(testCases, basicTests...)
 }
@@ -2280,6 +2428,16 @@
 		flags: []string{"-use-sparse-dh-prime"},
 	})
 
+	// The server must be tolerant to bogus ciphers.
+	const bogusCipher = 0x1234
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "UnknownCipher",
+		config: Config{
+			CipherSuites: []uint16{bogusCipher, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+		},
+	})
+
 	// versionSpecificCiphersTest specifies a test for the TLS 1.0 and TLS
 	// 1.1 specific cipher suite settings. A server is setup with the given
 	// cipher lists and then a connection is made for each member of
@@ -2383,7 +2541,7 @@
 					},
 				},
 				shouldFail:    true,
-				expectedError: "SIGNATURE",
+				expectedError: ":BAD_SIGNATURE:",
 			})
 		}
 	}
@@ -2409,7 +2567,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
+		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
 	})
 	// OpenSSL previously had an issue where the first byte of padding in
 	// 255 bytes of padding wasn't checked.
@@ -2424,7 +2582,7 @@
 		},
 		messageLen:    12, // 20 bytes of SHA-1 + 12 == 0 % block size
 		shouldFail:    true,
-		expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
+		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
 	})
 }
 
@@ -2523,6 +2681,39 @@
 			})
 		}
 	}
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "RequireAnyClientCertificate",
+		flags:         []string{"-require-any-client-certificate"},
+		shouldFail:    true,
+		expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "RequireAnyClientCertificate-SSL3",
+		config: Config{
+			MaxVersion: VersionSSL30,
+		},
+		flags:         []string{"-require-any-client-certificate"},
+		shouldFail:    true,
+		expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipClientCertificate",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SkipClientCertificate: true,
+			},
+		},
+		// Setting SSL_VERIFY_PEER allows anonymous clients.
+		flags:         []string{"-verify-peer"},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	})
 }
 
 func addExtendedMasterSecretTests() {
@@ -2667,6 +2858,8 @@
 	tests = append(tests, testCase{
 		name:          "Basic-Client",
 		resumeSession: true,
+		// Ensure session tickets are used, not session IDs.
+		noSessionCache: true,
 	})
 	tests = append(tests, testCase{
 		name: "Basic-Client-RenewTicket",
@@ -2691,8 +2884,13 @@
 		resumeSession: true,
 	})
 	tests = append(tests, testCase{
-		testType:      serverTest,
-		name:          "Basic-Server",
+		testType: serverTest,
+		name:     "Basic-Server",
+		config: Config{
+			Bugs: ProtocolBugs{
+				RequireSessionTickets: true,
+			},
+		},
 		resumeSession: true,
 	})
 	tests = append(tests, testCase{
@@ -2719,6 +2917,46 @@
 	// TLS client auth.
 	tests = append(tests, testCase{
 		testType: clientTest,
+		name:     "ClientAuth-NoCertificate-Client",
+		config: Config{
+			ClientAuth: RequestClientCert,
+		},
+	})
+	tests = append(tests, testCase{
+		testType: serverTest,
+		name:     "ClientAuth-NoCertificate-Server",
+		// Setting SSL_VERIFY_PEER allows anonymous clients.
+		flags: []string{"-verify-peer"},
+	})
+	if protocol == tls {
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "ClientAuth-NoCertificate-Client-SSL3",
+			config: Config{
+				MaxVersion: VersionSSL30,
+				ClientAuth: RequestClientCert,
+			},
+		})
+		tests = append(tests, testCase{
+			testType: serverTest,
+			name:     "ClientAuth-NoCertificate-Server-SSL3",
+			config: Config{
+				MaxVersion: VersionSSL30,
+			},
+			// Setting SSL_VERIFY_PEER allows anonymous clients.
+			flags: []string{"-verify-peer"},
+		})
+	}
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-NoCertificate-OldCallback",
+		config: Config{
+			ClientAuth: RequestClientCert,
+		},
+		flags: []string{"-use-old-client-cert-callback"},
+	})
+	tests = append(tests, testCase{
+		testType: clientTest,
 		name:     "ClientAuth-RSA-Client",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
@@ -2739,6 +2977,19 @@
 			"-key-file", path.Join(*resourceDir, ecdsaKeyFile),
 		},
 	})
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-OldCallback",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-use-old-client-cert-callback",
+		},
+	})
+
 	if async {
 		// Test async keys against each key exchange.
 		tests = append(tests, testCase{
@@ -3223,11 +3474,7 @@
 				} else {
 					shouldFail = true
 					expectedError = ":UNSUPPORTED_PROTOCOL:"
-					if runnerVers.version > VersionSSL30 {
-						expectedLocalError = "remote error: protocol version not supported"
-					} else {
-						expectedLocalError = "remote error: handshake failure"
-					}
+					expectedLocalError = "remote error: protocol version not supported"
 				}
 
 				testCases = append(testCases, testCase{
@@ -3381,6 +3628,16 @@
 		expectedNextProtoType: alpn,
 		resumeSession:         true,
 	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "ALPNServer-Decline",
+		config: Config{
+			NextProtos: []string{"foo", "bar", "baz"},
+		},
+		flags:             []string{"-decline-alpn"},
+		expectNoNextProto: true,
+		resumeSession:     true,
+	})
 	// Test that the server prefers ALPN over NPN.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -4087,7 +4344,6 @@
 	id   uint8
 }{
 	{"SHA1", hashSHA1},
-	{"SHA224", hashSHA224},
 	{"SHA256", hashSHA256},
 	{"SHA384", hashSHA384},
 	{"SHA512", hashSHA512},
@@ -4668,6 +4924,17 @@
 			flags: []string{"-enable-all-curves"},
 		})
 	}
+
+	// The server must be tolerant to bogus curves.
+	const bogusCurve = 0x1234
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "UnknownCurve",
+		config: Config{
+			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			CurvePreferences: []CurveID{bogusCurve, CurveP256},
+		},
+	})
 }
 
 func addKeyExchangeInfoTests() {
@@ -4845,11 +5112,17 @@
 		go worker(statusChan, testChan, *shimPath, &wg)
 	}
 
+	var foundTest bool
 	for i := range testCases {
 		if len(*testToRun) == 0 || *testToRun == testCases[i].name {
+			foundTest = true
 			testChan <- &testCases[i]
 		}
 	}
+	if !foundTest {
+		fmt.Fprintf(os.Stderr, "No test named '%s'\n", *testToRun)
+		os.Exit(1)
+	}
 
 	close(testChan)
 	wg.Wait()
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 1cf316d..67a017d 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -62,6 +62,7 @@
   { "-no-ssl3", &TestConfig::no_ssl3 },
   { "-shim-writes-first", &TestConfig::shim_writes_first },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
+  { "-decline-alpn", &TestConfig::decline_alpn },
   { "-expect-extended-master-secret",
     &TestConfig::expect_extended_master_secret },
   { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
@@ -98,6 +99,8 @@
   { "-p384-only", &TestConfig::p384_only },
   { "-enable-all-curves", &TestConfig::enable_all_curves },
   { "-use-sparse-dh-prime", &TestConfig::use_sparse_dh_prime },
+  { "-use-old-client-cert-callback",
+    &TestConfig::use_old_client_cert_callback },
 };
 
 const Flag<std::string> kStringFlags[] = {
@@ -178,12 +181,14 @@
       size_t len;
       if (!EVP_DecodedLength(&len, strlen(argv[i]))) {
         fprintf(stderr, "Invalid base64: %s\n", argv[i]);
+        return false;
       }
       std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
       if (!EVP_DecodeBase64(decoded.get(), &len, len,
                             reinterpret_cast<const uint8_t *>(argv[i]),
                             strlen(argv[i]))) {
         fprintf(stderr, "Invalid base64: %s\n", argv[i]);
+        return false;
       }
       base64_field->assign(reinterpret_cast<const char *>(decoded.get()), len);
       continue;
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 4e0a46a..fe117d8 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -50,6 +50,7 @@
   std::string expected_alpn;
   std::string expected_advertised_alpn;
   std::string select_alpn;
+  bool decline_alpn = false;
   bool expect_session_miss = false;
   bool expect_extended_master_secret = false;
   std::string psk;
@@ -102,6 +103,7 @@
   bool enable_all_curves = false;
   bool use_sparse_dh_prime = false;
   int expect_key_exchange_info = 0;
+  bool use_old_client_cert_callback = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 3381eae..b71da17 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -109,6 +109,7 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
+#include <string.h>
 
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
@@ -125,31 +126,44 @@
 /* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
  * state needs record-splitting and zero otherwise. */
 static int ssl_needs_record_splitting(const SSL *ssl) {
-  return !SSL_USE_EXPLICIT_IV(ssl) && ssl->aead_write_ctx != NULL &&
+  return ssl->s3->aead_write_ctx != NULL &&
+         ssl3_protocol_version(ssl) < TLS1_1_VERSION &&
          (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
-         SSL_CIPHER_is_block_cipher(ssl->aead_write_ctx->cipher);
+         SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher);
+}
+
+int ssl_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) {
+      return 1;
+    }
+  }
+  OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+  return 0;
 }
 
 size_t ssl_record_prefix_len(const SSL *ssl) {
   if (SSL_IS_DTLS(ssl)) {
     return DTLS1_RT_HEADER_LENGTH +
-           SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_read_ctx);
+           SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_read_ctx);
   } else {
     return SSL3_RT_HEADER_LENGTH +
-           SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_read_ctx);
+           SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_read_ctx);
   }
 }
 
 size_t ssl_seal_prefix_len(const SSL *ssl) {
   if (SSL_IS_DTLS(ssl)) {
     return DTLS1_RT_HEADER_LENGTH +
-           SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_write_ctx);
+           SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx);
   } else {
     size_t ret = SSL3_RT_HEADER_LENGTH +
-                 SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_write_ctx);
+                 SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx);
     if (ssl_needs_record_splitting(ssl)) {
       ret += SSL3_RT_HEADER_LENGTH;
-      ret += ssl_cipher_get_record_split_len(ssl->aead_write_ctx->cipher);
+      ret += ssl_cipher_get_record_split_len(ssl->s3->aead_write_ctx->cipher);
     }
     return ret;
   }
@@ -158,10 +172,10 @@
 size_t ssl_max_seal_overhead(const SSL *ssl) {
   if (SSL_IS_DTLS(ssl)) {
     return DTLS1_RT_HEADER_LENGTH +
-           SSL_AEAD_CTX_max_overhead(ssl->aead_write_ctx);
+           SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   } else {
     size_t ret = SSL3_RT_HEADER_LENGTH +
-                 SSL_AEAD_CTX_max_overhead(ssl->aead_write_ctx);
+                 SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
     if (ssl_needs_record_splitting(ssl)) {
       ret *= 2;
     }
@@ -215,14 +229,14 @@
 
   /* Decrypt the body. */
   size_t plaintext_len;
-  if (!SSL_AEAD_CTX_open(ssl->aead_read_ctx, out, &plaintext_len, max_out,
+  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, &plaintext_len, max_out,
                          type, version, ssl->s3->read_sequence, CBS_data(&body),
                          CBS_len(&body))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
     *out_alert = SSL_AD_BAD_RECORD_MAC;
     return ssl_open_record_error;
   }
-  if (!ssl3_record_sequence_update(ssl->s3->read_sequence, 8)) {
+  if (!ssl_record_sequence_update(ssl->s3->read_sequence, 8)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return ssl_open_record_error;
   }
@@ -280,11 +294,11 @@
   out[2] = wire_version & 0xff;
 
   size_t ciphertext_len;
-  if (!SSL_AEAD_CTX_seal(ssl->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH,
+  if (!SSL_AEAD_CTX_seal(ssl->s3->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH,
                          &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH,
                          type, wire_version, ssl->s3->write_sequence, in,
                          in_len) ||
-      !ssl3_record_sequence_update(ssl->s3->write_sequence, 8)) {
+      !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) {
     return 0;
   }
 
@@ -329,9 +343,11 @@
     out += frag_len;
     max_out -= frag_len;
 
-    assert(SSL3_RT_HEADER_LENGTH +
-               ssl_cipher_get_record_split_len(ssl->aead_write_ctx->cipher) ==
+#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+    assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len(
+                                       ssl->s3->aead_write_ctx->cipher) ==
            frag_len);
+#endif
   }
 
   if (!do_seal_record(ssl, out, out_len, max_out, type, in, in_len)) {
@@ -340,3 +356,26 @@
   *out_len += frag_len;
   return 1;
 }
+
+void ssl_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
+  if (SSL_IS_DTLS(ssl)) {
+    ssl->d1->r_epoch++;
+    memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap));
+  }
+  memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
+
+  SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
+  ssl->s3->aead_read_ctx = aead_ctx;
+}
+
+void ssl_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
+  if (SSL_IS_DTLS(ssl)) {
+    ssl->d1->w_epoch++;
+    memcpy(ssl->d1->last_write_sequence, ssl->s3->write_sequence,
+           sizeof(ssl->s3->write_sequence));
+  }
+  memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
+
+  SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
+  ssl->s3->aead_write_ctx = aead_ctx;
+}