external/boringssl: Sync to ee7aa02744a78bf4630913b1c83d0fe36aa45efc.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/a93bc1124c00b1ac0a68ea5cb14b158d6c8366e1..ee7aa02744a78bf4630913b1c83d0fe36aa45efc

Test: BoringSSL CTS Presubmits.
Change-Id: I5a05899374e616003f841983b6545f5c90e7c71d
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 3e9cd40..0c09443 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -29,6 +29,7 @@
   ssl_session.c
   ssl_stat.c
   ssl_transcript.c
+  ssl_versions.c
   ssl_x509.c
   t1_enc.c
   t1_lib.c
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 6084789..dd8d786 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -66,35 +66,6 @@
 #include "internal.h"
 
 
-static int dtls1_version_from_wire(uint16_t *out_version,
-                                   uint16_t wire_version) {
-  switch (wire_version) {
-    case DTLS1_VERSION:
-      /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
-      *out_version = TLS1_1_VERSION;
-      return 1;
-    case DTLS1_2_VERSION:
-      *out_version = TLS1_2_VERSION;
-      return 1;
-  }
-
-  return 0;
-}
-
-static uint16_t dtls1_version_to_wire(uint16_t version) {
-  switch (version) {
-    case TLS1_1_VERSION:
-      /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
-      return DTLS1_VERSION;
-    case TLS1_2_VERSION:
-      return DTLS1_2_VERSION;
-  }
-
-  /* It is an error to use this function with an invalid version. */
-  assert(0);
-  return 0;
-}
-
 static int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
   return cipher->algorithm_enc != SSL_eNULL;
 }
@@ -134,10 +105,6 @@
 
 static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = {
     1 /* is_dtls */,
-    TLS1_1_VERSION,
-    TLS1_2_VERSION,
-    dtls1_version_from_wire,
-    dtls1_version_to_wire,
     dtls1_new,
     dtls1_free,
     dtls1_get_message,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index dddf602..c772f77 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -717,33 +717,26 @@
     return -1;
   }
 
-  uint16_t max_wire_version = ssl->method->version_to_wire(hs->max_version);
-  assert(hs->state == SSL3_ST_CW_CLNT_HELLO_A);
-  if (!ssl->s3->have_version) {
-    ssl->version = max_wire_version;
-  }
-
   /* Always advertise the ClientHello version from the original maximum version,
    * even on renegotiation. The static RSA key exchange uses this field, and
    * some servers fail when it changes across handshakes. */
-  hs->client_version = max_wire_version;
-  if (hs->max_version >= TLS1_3_VERSION) {
-    hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
+  if (SSL_is_dtls(hs->ssl)) {
+    hs->client_version =
+        hs->max_version >= TLS1_2_VERSION ? DTLS1_2_VERSION : DTLS1_VERSION;
+  } else {
+    hs->client_version =
+        hs->max_version >= TLS1_2_VERSION ? TLS1_2_VERSION : hs->max_version;
   }
 
   /* If the configured session has expired or was created at a disabled
    * version, drop it. */
   if (ssl->session != NULL) {
-    uint16_t session_version;
     if (ssl->session->is_server ||
-        !ssl->method->version_from_wire(&session_version,
-                                        ssl->session->ssl_version) ||
-        (session_version < TLS1_3_VERSION &&
-         ssl->session->session_id_length == 0) ||
+        !ssl_supports_version(hs, ssl->session->ssl_version) ||
+        (ssl->session->session_id_length == 0 &&
+         ssl->session->tlsext_ticklen == 0) ||
         ssl->session->not_resumable ||
-        !ssl_session_is_time_valid(ssl, ssl->session) ||
-        session_version < hs->min_version ||
-        session_version > hs->max_version) {
+        !ssl_session_is_time_valid(ssl, ssl->session)) {
       ssl_set_session(ssl, NULL);
     }
   }
@@ -798,7 +791,7 @@
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   CBS server_hello, server_random, session_id;
-  uint16_t server_wire_version, cipher_suite;
+  uint16_t server_version, cipher_suite;
   uint8_t compression_method;
 
   int ret = ssl->method->ssl_get_message(ssl);
@@ -826,15 +819,13 @@
 
   CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
 
-  if (!CBS_get_u16(&server_hello, &server_wire_version)) {
+  if (!CBS_get_u16(&server_hello, &server_version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return -1;
   }
 
-  uint16_t server_version;
-  if (!ssl->method->version_from_wire(&server_version, server_wire_version) ||
-      server_version < hs->min_version || server_version > hs->max_version) {
+  if (!ssl_supports_version(hs, server_version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return -1;
@@ -842,11 +833,11 @@
 
   assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
   if (!ssl->s3->have_version) {
-    ssl->version = server_wire_version;
+    ssl->version = server_version;
     /* 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;
-  } else if (server_wire_version != ssl->version) {
+  } else if (server_version != ssl->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return -1;
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index d591c80..64abd5d 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -471,12 +471,9 @@
                              const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
   assert(!ssl->s3->have_version);
-  uint16_t version = 0;
-  /* Check supported_versions extension if it is present. */
-  CBS supported_versions;
+  CBS supported_versions, versions;
   if (ssl_client_hello_get_extension(client_hello, &supported_versions,
                                      TLSEXT_TYPE_supported_versions)) {
-    CBS versions;
     if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) ||
         CBS_len(&supported_versions) != 0 ||
         CBS_len(&versions) == 0) {
@@ -484,89 +481,63 @@
       *out_alert = SSL_AD_DECODE_ERROR;
       return 0;
     }
-
-    /* Choose the newest commonly-supported version advertised by the client.
-     * The client orders the versions according to its preferences, but we're
-     * not required to honor the client's preferences. */
-    int found_version = 0;
-    while (CBS_len(&versions) != 0) {
-      uint16_t ext_version;
-      if (!CBS_get_u16(&versions, &ext_version)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-      if (!ssl->method->version_from_wire(&ext_version, ext_version)) {
-        continue;
-      }
-      if (hs->min_version <= ext_version &&
-          ext_version <= hs->max_version &&
-          (!found_version || version < ext_version)) {
-        version = ext_version;
-        found_version = 1;
-      }
-    }
-
-    if (!found_version) {
-      goto unsupported_protocol;
-    }
   } else {
-    /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2
-     * do not use this mechanism. */
+    /* Convert the ClientHello version to an equivalent supported_versions
+     * extension. */
+    static const uint8_t kTLSVersions[] = {
+        0x03, 0x03, /* TLS 1.2 */
+        0x03, 0x02, /* TLS 1.1 */
+        0x03, 0x01, /* TLS 1 */
+        0x03, 0x00, /* SSL 3 */
+    };
+
+    static const uint8_t kDTLSVersions[] = {
+        0xfe, 0xfd, /* DTLS 1.2 */
+        0xfe, 0xff, /* DTLS 1.0 */
+    };
+
+    size_t versions_len = 0;
     if (SSL_is_dtls(ssl)) {
       if (client_hello->version <= DTLS1_2_VERSION) {
-        version = TLS1_2_VERSION;
+        versions_len = 4;
       } else if (client_hello->version <= DTLS1_VERSION) {
-        version = TLS1_1_VERSION;
-      } else {
-        goto unsupported_protocol;
+        versions_len = 2;
       }
+      CBS_init(&versions, kDTLSVersions + sizeof(kDTLSVersions) - versions_len,
+               versions_len);
     } else {
       if (client_hello->version >= TLS1_2_VERSION) {
-        version = TLS1_2_VERSION;
+        versions_len = 8;
       } else if (client_hello->version >= TLS1_1_VERSION) {
-        version = TLS1_1_VERSION;
+        versions_len = 6;
       } else if (client_hello->version >= TLS1_VERSION) {
-        version = TLS1_VERSION;
+        versions_len = 4;
       } else if (client_hello->version >= SSL3_VERSION) {
-        version = SSL3_VERSION;
-      } else {
-        goto unsupported_protocol;
+        versions_len = 2;
       }
-    }
-
-    /* Apply our minimum and maximum version. */
-    if (version > hs->max_version) {
-      version = hs->max_version;
-    }
-
-    if (version < hs->min_version) {
-      goto unsupported_protocol;
+      CBS_init(&versions, kTLSVersions + sizeof(kTLSVersions) - versions_len,
+               versions_len);
     }
   }
 
-  /* Handle FALLBACK_SCSV. */
-  if (ssl_client_cipher_list_contains_cipher(client_hello,
-                                             SSL3_CK_FALLBACK_SCSV & 0xffff) &&
-      version < hs->max_version) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
-    *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
+  if (!ssl_negotiate_version(hs, out_alert, &ssl->version, &versions)) {
     return 0;
   }
 
-  hs->client_version = client_hello->version;
-  ssl->version = ssl->method->version_to_wire(version);
-
   /* 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;
 
-  return 1;
+  /* Handle FALLBACK_SCSV. */
+  if (ssl_client_cipher_list_contains_cipher(client_hello,
+                                             SSL3_CK_FALLBACK_SCSV & 0xffff) &&
+      ssl3_protocol_version(ssl) < hs->max_version) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
+    *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
+    return 0;
+  }
 
-unsupported_protocol:
-  OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
-  *out_alert = SSL_AD_PROTOCOL_VERSION;
-  return 0;
+  return 1;
 }
 
 static STACK_OF(SSL_CIPHER) *
@@ -611,12 +582,6 @@
                                               uint32_t *out_mask_k,
                                               uint32_t *out_mask_a) {
   SSL *const ssl = hs->ssl;
-  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-    *out_mask_k = SSL_kGENERIC;
-    *out_mask_a = SSL_aGENERIC;
-    return;
-  }
-
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
@@ -759,7 +724,7 @@
     return -1;
   }
 
-  /* Load the client random. */
+  hs->client_version = client_hello.version;
   if (client_hello.random_len != SSL3_RANDOM_SIZE) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
@@ -1083,7 +1048,7 @@
       /* Determine the group to use. */
       uint16_t group_id;
       if (!tls1_get_shared_group(hs, &group_id)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
       }
@@ -1478,7 +1443,7 @@
     /* The key exchange state may now be discarded. */
     SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
   } else if (!(alg_k & SSL_kPSK)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_TYPE);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
     goto err;
   }
@@ -1487,7 +1452,7 @@
    * pre-shared key. */
   if (alg_a & SSL_aPSK) {
     if (ssl->psk_server_callback == NULL) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB);
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       goto err;
     }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index d56c73b..f6cea7a 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -163,6 +163,57 @@
 #endif
 
 
+typedef struct ssl_handshake_st SSL_HANDSHAKE;
+
+/* Protocol versions.
+ *
+ * Due to DTLS's historical wire version differences and to support multiple
+ * variants of the same protocol during development, we maintain two notions of
+ * version.
+ *
+ * The "version" or "wire version" is the actual 16-bit value that appears on
+ * the wire. It uniquely identifies a version and is also used at API
+ * boundaries. The set of supported versions differs between TLS and DTLS. Wire
+ * versions are opaque values and may not be compared numerically.
+ *
+ * The "protocol version" identifies the high-level handshake variant being
+ * used. DTLS versions map to the corresponding TLS versions. Draft TLS 1.3
+ * variants all map to TLS 1.3. Protocol versions are sequential and may be
+ * compared numerically. */
+
+/* ssl_protocol_version_from_wire sets |*out| to the protocol version
+ * corresponding to wire version |version| and returns one. If |version| is not
+ * a valid TLS or DTLS version, it returns zero.
+ *
+ * Note this simultaneously handles both DTLS and TLS. Use one of the
+ * higher-level functions below for most operations. */
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version);
+
+/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
+ * minimum and maximum enabled protocol versions, respectively. */
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+                          uint16_t *out_max_version);
+
+/* ssl_supports_version returns one if |hs| supports |version| and zero
+ * otherwise. */
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version);
+
+/* ssl_add_supported_versions writes the supported versions of |hs| to |cbb|, in
+ * decreasing preference order. */
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb);
+
+/* ssl_negotiate_version negotiates a common version based on |hs|'s preferences
+ * and the peer preference list in |peer_versions|. On success, it returns one
+ * and sets |*out_version| to the selected version. Otherwise, it returns zero
+ * and sets |*out_alert| to an alert to send. */
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                          uint16_t *out_version, const CBS *peer_versions);
+
+/* 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);
+
+
 /* Cipher suites. */
 
 /* Bits for |algorithm_mkey| (key exchange algorithm). */
@@ -542,8 +593,6 @@
 
 /* Private key operations. */
 
-typedef struct ssl_handshake_st SSL_HANDSHAKE;
-
 /* ssl_has_private_key returns one if |ssl| has a private key
  * configured and zero otherwise. */
 int ssl_has_private_key(const SSL *ssl);
@@ -1414,17 +1463,6 @@
 struct ssl_protocol_method_st {
   /* is_dtls is one if the protocol is DTLS and zero otherwise. */
   char is_dtls;
-  /* min_version is the minimum implemented version. */
-  uint16_t min_version;
-  /* max_version is the maximum implemented version. */
-  uint16_t max_version;
-  /* version_from_wire maps |wire_version| to a protocol version. On success, it
-   * sets |*out_version| to the result and returns one. If the version is
-   * unknown, it returns zero. */
-  int (*version_from_wire)(uint16_t *out_version, uint16_t wire_version);
-  /* version_to_wire maps |version| to the wire representation. It is an error
-   * to call it with an invalid version. */
-  uint16_t (*version_to_wire)(uint16_t version);
   int (*ssl_new)(SSL *ssl);
   void (*ssl_free)(SSL *ssl);
   /* ssl_get_message reads the next handshake message. On success, it returns
@@ -1846,7 +1884,7 @@
   const SSL_PROTOCOL_METHOD *method;
 
   /* version is the protocol version. */
-  int version;
+  uint16_t version;
 
   /* conf_max_version is the maximum acceptable protocol version configured by
    * |SSL_set_max_proto_version|. Note this version is normalized in DTLS and is
@@ -2049,10 +2087,12 @@
 int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
                              const SSL_SESSION *session);
 
-/* SSL_SESSION_get_digest returns the digest used in |session|. If the digest is
- * invalid, it returns NULL. */
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
-                                     const SSL *ssl);
+/* SSL_SESSION_protocol_version returns the protocol version associated with
+ * |session|. */
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session);
+
+/* SSL_SESSION_get_digest returns the digest used in |session|. */
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session);
 
 void ssl_set_session(SSL *ssl, SSL_SESSION *session);
 
@@ -2285,15 +2325,6 @@
 /* ssl_can_read returns one if |ssl| is allowed to read and zero otherwise. */
 int ssl_can_read(const SSL *ssl);
 
-/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
- * minimum and maximum enabled protocol versions, respectively. */
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
-                          uint16_t *out_max_version);
-
-/* 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);
-
 void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock);
 
 /* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index f368ff2..cc6a559 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -576,10 +576,17 @@
 
   CBS session;
   uint64_t version, ssl_version;
+  uint16_t unused;
   if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) ||
       !CBS_get_asn1_uint64(&session, &version) ||
       version != kVersion ||
-      !CBS_get_asn1_uint64(&session, &ssl_version)) {
+      !CBS_get_asn1_uint64(&session, &ssl_version) ||
+      /* Require sessions have versions valid in either TLS or DTLS. The session
+       * will not be used by the handshake if not applicable, but, for
+       * simplicity, never parse a session that does not pass
+       * |ssl_protocol_version_from_wire|. */
+      ssl_version > UINT16_MAX ||
+      !ssl_protocol_version_from_wire(&unused, ssl_version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
   }
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 562c1f3..5d88878 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -1035,7 +1035,7 @@
   uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
   uint16_t min_version;
   const char *l, *buf;
-  int multi, skip_rule, rule, ok, in_group = 0, has_group = 0;
+  int multi, skip_rule, rule, in_group = 0, has_group = 0;
   size_t j, buf_len;
   uint32_t cipher_id;
   char ch;
@@ -1082,10 +1082,7 @@
       rule = CIPHER_SPECIAL;
       l++;
     } else if (ch == '[') {
-      if (in_group) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_NESTED_GROUP);
-        return 0;
-      }
+      assert(!in_group);
       in_group = 1;
       has_group = 1;
       l++;
@@ -1185,15 +1182,11 @@
 
     /* Ok, we have the rule, now apply it. */
     if (rule == CIPHER_SPECIAL) {
-      /* special command */
-      ok = 0;
-      if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) {
-        ok = ssl_cipher_strength_sort(head_p, tail_p);
-      } else {
+      if (buf_len != 8 || strncmp(buf, "STRENGTH", 8) != 0) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
+        return 0;
       }
-
-      if (ok == 0) {
+      if (!ssl_cipher_strength_sort(head_p, tail_p)) {
         return 0;
       }
 
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 1b002a5..109dfd0 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -978,60 +978,6 @@
   return SSL_ERROR_SYSCALL;
 }
 
-static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
-                           uint16_t version) {
-  /* Zero is interpreted as the default minimum version. */
-  if (version == 0) {
-    *out = method->min_version;
-    /* SSL 3.0 is disabled unless explicitly enabled. */
-    if (*out < TLS1_VERSION) {
-      *out = TLS1_VERSION;
-    }
-    return 1;
-  }
-
-  if (version == TLS1_3_VERSION) {
-    version = TLS1_3_DRAFT_VERSION;
-  }
-
-  return method->version_from_wire(out, version);
-}
-
-static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
-                           uint16_t version) {
-  /* Zero is interpreted as the default maximum version. */
-  if (version == 0) {
-    *out = method->max_version;
-    /* TODO(svaldez): Enable TLS 1.3 by default once fully implemented. */
-    if (*out > TLS1_2_VERSION) {
-      *out = TLS1_2_VERSION;
-    }
-    return 1;
-  }
-
-  if (version == TLS1_3_VERSION) {
-    version = TLS1_3_DRAFT_VERSION;
-  }
-
-  return method->version_from_wire(out, version);
-}
-
-int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_min_version(ctx->method, &ctx->conf_min_version, version);
-}
-
-int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_max_version(ctx->method, &ctx->conf_max_version, version);
-}
-
-int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
-  return set_min_version(ssl->method, &ssl->conf_min_version, version);
-}
-
-int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
-  return set_max_version(ssl->method, &ssl->conf_max_version, version);
-}
-
 uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
   ctx->options |= options;
   return ctx->options;
@@ -1901,43 +1847,6 @@
   }
 }
 
-static const char *ssl_get_version(int version) {
-  switch (version) {
-    /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
-    case TLS1_3_DRAFT_VERSION:
-      return "TLSv1.3";
-
-    case TLS1_2_VERSION:
-      return "TLSv1.2";
-
-    case TLS1_1_VERSION:
-      return "TLSv1.1";
-
-    case TLS1_VERSION:
-      return "TLSv1";
-
-    case SSL3_VERSION:
-      return "SSLv3";
-
-    case DTLS1_VERSION:
-      return "DTLSv1";
-
-    case DTLS1_2_VERSION:
-      return "DTLSv1.2";
-
-    default:
-      return "unknown";
-  }
-}
-
-const char *SSL_get_version(const SSL *ssl) {
-  return ssl_get_version(ssl->version);
-}
-
-const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
-  return ssl_get_version(session->ssl_version);
-}
-
 EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
   if (ssl->cert != NULL) {
     return ssl->cert->privatekey;
@@ -2015,15 +1924,6 @@
   return ret;
 }
 
-int SSL_version(const SSL *ssl) {
-  /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
-  if (ssl->version == TLS1_3_DRAFT_VERSION) {
-    return TLS1_3_VERSION;
-  }
-
-  return ssl->version;
-}
-
 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; }
 
 SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) {
@@ -2329,102 +2229,6 @@
       cipher->algorithm_mac == SSL_AEAD;
 }
 
-const struct {
-  uint16_t version;
-  uint32_t flag;
-} kVersions[] = {
-    {SSL3_VERSION, SSL_OP_NO_SSLv3},
-    {TLS1_VERSION, SSL_OP_NO_TLSv1},
-    {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
-    {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
-    {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
-};
-
-static const size_t kVersionsLen = OPENSSL_ARRAY_SIZE(kVersions);
-
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
-                          uint16_t *out_max_version) {
-  /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
-   * DTLS 1.0 should be mapped to TLS 1.1. */
-  uint32_t options = ssl->options;
-  if (SSL_is_dtls(ssl)) {
-    options &= ~SSL_OP_NO_TLSv1_1;
-    if (options & SSL_OP_NO_DTLSv1) {
-      options |= SSL_OP_NO_TLSv1_1;
-    }
-  }
-
-  uint16_t min_version = ssl->conf_min_version;
-  uint16_t max_version = ssl->conf_max_version;
-
-  /* Bound the range to only those implemented in this protocol. */
-  if (min_version < ssl->method->min_version) {
-    min_version = ssl->method->min_version;
-  }
-  if (max_version > ssl->method->max_version) {
-    max_version = ssl->method->max_version;
-  }
-
-  /* OpenSSL's API for controlling versions entails blacklisting individual
-   * protocols. This has two problems. First, on the client, the protocol can
-   * only express a contiguous range of versions. Second, a library consumer
-   * trying to set a maximum version cannot disable protocol versions that get
-   * added in a future version of the library.
-   *
-   * To account for both of these, OpenSSL interprets the client-side bitmask
-   * as a min/max range by picking the lowest contiguous non-empty range of
-   * enabled protocols. Note that this means it is impossible to set a maximum
-   * version of the higest supported TLS version in a future-proof way. */
-  int any_enabled = 0;
-  for (size_t i = 0; i < kVersionsLen; i++) {
-    /* Only look at the versions already enabled. */
-    if (min_version > kVersions[i].version) {
-      continue;
-    }
-    if (max_version < kVersions[i].version) {
-      break;
-    }
-
-    if (!(options & kVersions[i].flag)) {
-      /* The minimum version is the first enabled version. */
-      if (!any_enabled) {
-        any_enabled = 1;
-        min_version = kVersions[i].version;
-      }
-      continue;
-    }
-
-    /* If there is a disabled version after the first enabled one, all versions
-     * after it are implicitly disabled. */
-    if (any_enabled) {
-      max_version = kVersions[i-1].version;
-      break;
-    }
-  }
-
-  if (!any_enabled) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
-    return 0;
-  }
-
-  *out_min_version = min_version;
-  *out_max_version = max_version;
-  return 1;
-}
-
-uint16_t ssl3_protocol_version(const SSL *ssl) {
-  assert(ssl->s3->have_version);
-  uint16_t version;
-  if (!ssl->method->version_from_wire(&version, ssl->version)) {
-    /* TODO(davidben): Use the internal version representation for ssl->version
-     * and map to the public API representation at API boundaries. */
-    assert(0);
-    return 0;
-  }
-
-  return version;
-}
-
 int SSL_is_server(const SSL *ssl) { return ssl->server; }
 
 int SSL_is_dtls(const SSL *ssl) { return ssl->method->is_dtls; }
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index f025364..3e2c9f4 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -505,14 +505,21 @@
   return CRYPTO_get_ex_data(&session->ex_data, idx);
 }
 
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
-                                     const SSL *ssl) {
-  uint16_t version;
-  if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
-    return NULL;
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session) {
+  uint16_t ret;
+  if (!ssl_protocol_version_from_wire(&ret, session->ssl_version)) {
+    /* An |SSL_SESSION| will never have an invalid version. This is enforced by
+     * the parser. */
+    assert(0);
+    return 0;
   }
 
-  return ssl_get_handshake_digest(session->cipher->algorithm_prf, version);
+  return ret;
+}
+
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session) {
+  return ssl_get_handshake_digest(session->cipher->algorithm_prf,
+                                  SSL_SESSION_protocol_version(session));
 }
 
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
@@ -952,7 +959,6 @@
     }
 
     if (ret) {
-      found_session->not_resumable = 1;
       if (ctx->remove_session_cb != NULL) {
         ctx->remove_session_cb(ctx, found_session);
       }
@@ -1031,7 +1037,6 @@
      * save on locking overhead */
     (void) lh_SSL_SESSION_delete(param->cache, session);
     SSL_SESSION_list_remove(param->ctx, session);
-    session->not_resumable = 1;
     if (param->ctx->remove_session_cb != NULL) {
       param->ctx->remove_session_cb(param->ctx, session);
     }
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 84b7496..2c648ac 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -180,7 +180,7 @@
     // Standard names may be used instead of OpenSSL names.
     {
         "[TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256|"
-         "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]:"
+        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]:"
         "[TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256]:"
         "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
         {
@@ -214,6 +214,32 @@
         },
         false,
     },
+    // Additional masks after @STRENGTH get silently discarded.
+    //
+    // TODO(davidben): Make this an error. If not silently discarded, they get
+    // interpreted as + opcodes which are very different.
+    {
+        "ECDHE-RSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES256-GCM-SHA384:"
+        "@STRENGTH+AES256",
+        {
+            {TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+        false,
+    },
+    {
+        "ECDHE-RSA-AES128-GCM-SHA256:"
+        "ECDHE-RSA-AES256-GCM-SHA384:"
+        "@STRENGTH+AES256:"
+        "ECDHE-RSA-CHACHA20-POLY1305",
+        {
+            {TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+        },
+        false,
+    },
     // Exact ciphers may not be used in multi-part rules; they are treated
     // as unknown aliases.
     {
@@ -2567,6 +2593,10 @@
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION));
   EXPECT_EQ(TLS1_3_VERSION, ctx->conf_max_version);
 
+  // TLS1_3_DRAFT_VERSION is not an API-level version.
+  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_DRAFT_VERSION));
+  ERR_clear_error();
+
   ctx.reset(SSL_CTX_new(DTLS_method()));
   ASSERT_TRUE(ctx);
 
diff --git a/src/ssl/ssl_versions.c b/src/ssl/ssl_versions.c
new file mode 100644
index 0000000..5d92771
--- /dev/null
+++ b/src/ssl/ssl_versions.c
@@ -0,0 +1,343 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+#include "../crypto/internal.h"
+
+
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) {
+  switch (version) {
+    case SSL3_VERSION:
+    case TLS1_VERSION:
+    case TLS1_1_VERSION:
+    case TLS1_2_VERSION:
+      *out = version;
+      return 1;
+
+    case TLS1_3_DRAFT_VERSION:
+      *out = TLS1_3_VERSION;
+      return 1;
+
+    case DTLS1_VERSION:
+      /* DTLS 1.0 is analogous to TLS 1.1, not TLS 1.0. */
+      *out = TLS1_1_VERSION;
+      return 1;
+
+    case DTLS1_2_VERSION:
+      *out = TLS1_2_VERSION;
+      return 1;
+
+    default:
+      return 0;
+  }
+}
+
+/* The follow arrays are the supported versions for TLS and DTLS, in order of
+ * decreasing preference. */
+
+static const uint16_t kTLSVersions[] = {
+    TLS1_3_DRAFT_VERSION,
+    TLS1_2_VERSION,
+    TLS1_1_VERSION,
+    TLS1_VERSION,
+    SSL3_VERSION,
+};
+
+static const uint16_t kDTLSVersions[] = {
+    DTLS1_2_VERSION,
+    DTLS1_VERSION,
+};
+
+static void get_method_versions(const SSL_PROTOCOL_METHOD *method,
+                                const uint16_t **out, size_t *out_num) {
+  if (method->is_dtls) {
+    *out = kDTLSVersions;
+    *out_num = OPENSSL_ARRAY_SIZE(kDTLSVersions);
+  } else {
+    *out = kTLSVersions;
+    *out_num = OPENSSL_ARRAY_SIZE(kTLSVersions);
+  }
+}
+
+static int method_supports_version(const SSL_PROTOCOL_METHOD *method,
+                                   uint16_t version) {
+  const uint16_t *versions;
+  size_t num_versions;
+  get_method_versions(method, &versions, &num_versions);
+  for (size_t i = 0; i < num_versions; i++) {
+    if (versions[i] == version) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int set_version_bound(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+                             uint16_t version) {
+  /* The public API uses wire versions, except we use |TLS1_3_VERSION|
+   * everywhere to refer to any draft TLS 1.3 versions. In this direction, we
+   * map it to some representative TLS 1.3 draft version. */
+  if (version == TLS1_3_DRAFT_VERSION) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
+    return 0;
+  }
+  if (version == TLS1_3_VERSION) {
+    version = TLS1_3_DRAFT_VERSION;
+  }
+
+  if (!method_supports_version(method, version) ||
+      !ssl_protocol_version_from_wire(out, version)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+                           uint16_t version) {
+  /* Zero is interpreted as the default minimum version. */
+  if (version == 0) {
+    /* SSL 3.0 is disabled by default and TLS 1.0 does not exist in DTLS. */
+    *out = method->is_dtls ? TLS1_1_VERSION : TLS1_VERSION;
+    return 1;
+  }
+
+  return set_version_bound(method, out, version);
+}
+
+static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+                           uint16_t version) {
+  /* Zero is interpreted as the default maximum version. */
+  if (version == 0) {
+    *out = TLS1_2_VERSION;
+    return 1;
+  }
+
+  return set_version_bound(method, out, version);
+}
+
+int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
+  return set_min_version(ctx->method, &ctx->conf_min_version, version);
+}
+
+int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
+  return set_max_version(ctx->method, &ctx->conf_max_version, version);
+}
+
+int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
+  return set_min_version(ssl->method, &ssl->conf_min_version, version);
+}
+
+int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
+  return set_max_version(ssl->method, &ssl->conf_max_version, version);
+}
+
+const struct {
+  uint16_t version;
+  uint32_t flag;
+} kProtocolVersions[] = {
+    {SSL3_VERSION, SSL_OP_NO_SSLv3},
+    {TLS1_VERSION, SSL_OP_NO_TLSv1},
+    {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
+    {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
+    {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
+};
+
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+                          uint16_t *out_max_version) {
+  /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
+   * DTLS 1.0 should be mapped to TLS 1.1. */
+  uint32_t options = ssl->options;
+  if (SSL_is_dtls(ssl)) {
+    options &= ~SSL_OP_NO_TLSv1_1;
+    if (options & SSL_OP_NO_DTLSv1) {
+      options |= SSL_OP_NO_TLSv1_1;
+    }
+  }
+
+  uint16_t min_version = ssl->conf_min_version;
+  uint16_t max_version = ssl->conf_max_version;
+
+  /* OpenSSL's API for controlling versions entails blacklisting individual
+   * protocols. This has two problems. First, on the client, the protocol can
+   * only express a contiguous range of versions. Second, a library consumer
+   * trying to set a maximum version cannot disable protocol versions that get
+   * added in a future version of the library.
+   *
+   * To account for both of these, OpenSSL interprets the client-side bitmask
+   * as a min/max range by picking the lowest contiguous non-empty range of
+   * enabled protocols. Note that this means it is impossible to set a maximum
+   * version of the higest supported TLS version in a future-proof way. */
+  int any_enabled = 0;
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kProtocolVersions); i++) {
+    /* Only look at the versions already enabled. */
+    if (min_version > kProtocolVersions[i].version) {
+      continue;
+    }
+    if (max_version < kProtocolVersions[i].version) {
+      break;
+    }
+
+    if (!(options & kProtocolVersions[i].flag)) {
+      /* The minimum version is the first enabled version. */
+      if (!any_enabled) {
+        any_enabled = 1;
+        min_version = kProtocolVersions[i].version;
+      }
+      continue;
+    }
+
+    /* If there is a disabled version after the first enabled one, all versions
+     * after it are implicitly disabled. */
+    if (any_enabled) {
+      max_version = kProtocolVersions[i-1].version;
+      break;
+    }
+  }
+
+  if (!any_enabled) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
+    return 0;
+  }
+
+  *out_min_version = min_version;
+  *out_max_version = max_version;
+  return 1;
+}
+
+static uint16_t ssl_version(const SSL *ssl) {
+  /* In early data, we report the predicted version. */
+  if (SSL_in_early_data(ssl) && !ssl->server) {
+    return ssl->s3->hs->early_session->ssl_version;
+  }
+  return ssl->version;
+}
+
+int SSL_version(const SSL *ssl) {
+  uint16_t ret = ssl_version(ssl);
+  /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
+  if (ret == TLS1_3_DRAFT_VERSION) {
+    return TLS1_3_VERSION;
+  }
+  return ret;
+}
+
+static const char *ssl_get_version(int version) {
+  switch (version) {
+    /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
+    case TLS1_3_DRAFT_VERSION:
+      return "TLSv1.3";
+
+    case TLS1_2_VERSION:
+      return "TLSv1.2";
+
+    case TLS1_1_VERSION:
+      return "TLSv1.1";
+
+    case TLS1_VERSION:
+      return "TLSv1";
+
+    case SSL3_VERSION:
+      return "SSLv3";
+
+    case DTLS1_VERSION:
+      return "DTLSv1";
+
+    case DTLS1_2_VERSION:
+      return "DTLSv1.2";
+
+    default:
+      return "unknown";
+  }
+}
+
+const char *SSL_get_version(const SSL *ssl) {
+  return ssl_get_version(ssl_version(ssl));
+}
+
+const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
+  return ssl_get_version(session->ssl_version);
+}
+
+uint16_t ssl3_protocol_version(const SSL *ssl) {
+  assert(ssl->s3->have_version);
+  uint16_t version;
+  if (!ssl_protocol_version_from_wire(&version, ssl->version)) {
+    /* |ssl->version| will always be set to a valid version. */
+    assert(0);
+    return 0;
+  }
+
+  return version;
+}
+
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
+  uint16_t protocol_version;
+  return method_supports_version(hs->ssl->method, version) &&
+         ssl_protocol_version_from_wire(&protocol_version, version) &&
+         hs->min_version <= protocol_version &&
+         protocol_version <= hs->max_version;
+}
+
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) {
+  const uint16_t *versions;
+  size_t num_versions;
+  get_method_versions(hs->ssl->method, &versions, &num_versions);
+  for (size_t i = 0; i < num_versions; i++) {
+    if (ssl_supports_version(hs, versions[i]) &&
+        !CBB_add_u16(cbb, versions[i])) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                          uint16_t *out_version, const CBS *peer_versions) {
+  const uint16_t *versions;
+  size_t num_versions;
+  get_method_versions(hs->ssl->method, &versions, &num_versions);
+  for (size_t i = 0; i < num_versions; i++) {
+    if (!ssl_supports_version(hs, versions[i])) {
+      continue;
+    }
+
+    CBS copy = *peer_versions;
+    while (CBS_len(&copy) != 0) {
+      uint16_t version;
+      if (!CBS_get_u16(&copy, &version)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        *out_alert = SSL_AD_DECODE_ERROR;
+        return 0;
+      }
+
+      if (version == versions[i]) {
+        *out_version = version;
+        return 1;
+      }
+    }
+  }
+
+  OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
+  *out_alert = SSL_AD_PROTOCOL_VERSION;
+  return 0;
+}
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 1b14371..8e858c4 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -799,16 +799,24 @@
   }
 
   const uint8_t *d = CBS_data(&renegotiated_connection);
-  if (CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
-        ssl->s3->previous_client_finished_len)) {
+  int ok = CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
+                         ssl->s3->previous_client_finished_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  ok = 1;
+#endif
+  if (!ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
     return 0;
   }
   d += ssl->s3->previous_client_finished_len;
 
-  if (CRYPTO_memcmp(d, ssl->s3->previous_server_finished,
-        ssl->s3->previous_server_finished_len)) {
+  ok = CRYPTO_memcmp(d, ssl->s3->previous_server_finished,
+                     ssl->s3->previous_server_finished_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  ok = 1;
+#endif
+  if (!ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     return 0;
@@ -970,14 +978,11 @@
    * advertise the extension to avoid potentially breaking servers which carry
    * over the state from the previous handshake, such as OpenSSL servers
    * without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */
-  uint16_t session_version;
   if (!ssl->s3->initial_handshake_complete &&
       ssl->session != NULL &&
       ssl->session->tlsext_tick != NULL &&
       /* Don't send TLS 1.3 session tickets in the ticket extension. */
-      ssl->method->version_from_wire(&session_version,
-                                     ssl->session->ssl_version) &&
-      session_version < TLS1_3_VERSION) {
+      SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
     ticket_data = ssl->session->tlsext_tick;
     ticket_len = ssl->session->tlsext_ticklen;
   }
@@ -1863,31 +1868,19 @@
 
 static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint16_t session_version;
   if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
-      !ssl->method->version_from_wire(&session_version,
-                                      ssl->session->ssl_version) ||
-      session_version < TLS1_3_VERSION) {
+      SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
     return 0;
   }
 
-  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
-  if (digest == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  size_t binder_len = EVP_MD_size(digest);
+  size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
   return 15 + ssl->session->tlsext_ticklen + binder_len;
 }
 
 static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t session_version;
   if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
-      !ssl->method->version_from_wire(&session_version,
-                                      ssl->session->ssl_version) ||
-      session_version < TLS1_3_VERSION) {
+      SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
     return 1;
   }
 
@@ -1899,14 +1892,7 @@
   /* Fill in a placeholder zero binder of the appropriate length. It will be
    * computed and filled in later after length prefixes are computed. */
   uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
-
-  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
-  if (digest == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  size_t binder_len = EVP_MD_size(digest);
+  size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
 
   CBB contents, identity, ticket, binders, binder;
   if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
@@ -2071,11 +2057,8 @@
 
 static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t session_version;
   if (ssl->session == NULL ||
-      !ssl->method->version_from_wire(&session_version,
-                                      ssl->session->ssl_version) ||
-      session_version < TLS1_3_VERSION ||
+      SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION ||
       ssl->session->ticket_max_early_data == 0 ||
       hs->received_hello_retry_request ||
       !ssl->cert->enable_early_data) {
@@ -2375,14 +2358,8 @@
     return 0;
   }
 
-  for (uint16_t version = hs->max_version; version >= hs->min_version;
-       version--) {
-    if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) {
-      return 0;
-    }
-  }
-
-  if (!CBB_flush(out)) {
+  if (!ssl_add_supported_versions(hs, &versions) ||
+      !CBB_flush(out)) {
     return 0;
   }
 
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 3cb2856..6d3e6d1 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -62,6 +62,7 @@
 #include "../../crypto/internal.h"
 #include "../internal.h"
 #include "async_bio.h"
+#include "fuzzer.h"
 #include "packeted_bio.h"
 #include "test_config.h"
 
@@ -1371,6 +1372,13 @@
     return false;
   }
 
+  if (config->expect_version != 0 &&
+      SSL_version(ssl) != config->expect_version) {
+    fprintf(stderr, "want version %04x, got %04x\n", config->expect_version,
+            SSL_version(ssl));
+    return false;
+  }
+
   bool expect_resume =
       is_resume && (!config->expect_session_miss || SSL_in_early_data(ssl));
   if (!!SSL_session_reused(ssl) != expect_resume) {
@@ -1645,6 +1653,61 @@
   return true;
 }
 
+static bool WriteSettings(int i, const TestConfig *config,
+                          const SSL_SESSION *session) {
+  if (config->write_settings.empty()) {
+    return true;
+  }
+
+  // Treat write_settings as a path prefix for each connection in the run.
+  char buf[DECIMAL_SIZE(int)];
+  snprintf(buf, sizeof(buf), "%d", i);
+  std::string path = config->write_settings + buf;
+
+  bssl::ScopedCBB cbb;
+  if (!CBB_init(cbb.get(), 64)) {
+    return false;
+  }
+
+  if (session != nullptr) {
+    uint8_t *data;
+    size_t len;
+    if (!SSL_SESSION_to_bytes(session, &data, &len)) {
+      return false;
+    }
+    bssl::UniquePtr<uint8_t> free_data(data);
+    CBB child;
+    if (!CBB_add_u16(cbb.get(), kSessionTag) ||
+        !CBB_add_u24_length_prefixed(cbb.get(), &child) ||
+        !CBB_add_bytes(&child, data, len) ||
+        !CBB_flush(cbb.get())) {
+      return false;
+    }
+  }
+
+  if (config->is_server &&
+      (config->require_any_client_certificate || config->verify_peer) &&
+      !CBB_add_u16(cbb.get(), kRequestClientCert)) {
+    return false;
+  }
+
+  uint8_t *settings;
+  size_t settings_len;
+  if (!CBB_add_u16(cbb.get(), kDataTag) ||
+      !CBB_finish(cbb.get(), &settings, &settings_len)) {
+    return false;
+  }
+  bssl::UniquePtr<uint8_t> free_settings(settings);
+
+  using ScopedFILE = std::unique_ptr<FILE, decltype(&fclose)>;
+  ScopedFILE file(fopen(path.c_str(), "w"), fclose);
+  if (!file) {
+    return false;
+  }
+
+  return fwrite(settings, settings_len, 1, file.get()) == 1;
+}
+
 static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl,
                        const TestConfig *config, bool is_resume, bool is_retry);
 
@@ -1683,12 +1746,20 @@
     SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
   }
   if (config->require_any_client_certificate) {
-    SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+    SSL_set_verify(ssl.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                    NULL);
   }
   if (config->verify_peer) {
     SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL);
   }
+  if (config->verify_peer_if_no_obc) {
+    // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client
+    // certificates were requested is easy.
+    SSL_set_verify(ssl.get(),
+                   SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC |
+                       SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+                   NULL);
+  }
   if (config->false_start) {
     SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
   }
@@ -2227,6 +2298,10 @@
     }
 
     bssl::UniquePtr<SSL_SESSION> offer_session = std::move(session);
+    if (!WriteSettings(i, config, offer_session.get())) {
+      fprintf(stderr, "Error writing settings.\n");
+      return 1;
+    }
     if (!DoConnection(&session, ssl_ctx.get(), config, &retry_config, is_resume,
                       offer_session.get())) {
       fprintf(stderr, "Connection %d failed.\n", i + 1);
diff --git a/src/ssl/test/fuzzer.h b/src/ssl/test/fuzzer.h
new file mode 100644
index 0000000..2f81653
--- /dev/null
+++ b/src/ssl/test/fuzzer.h
@@ -0,0 +1,271 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef HEADER_SSL_TEST_FUZZER
+#define HEADER_SSL_TEST_FUZZER
+
+#include <openssl/bytestring.h>
+#include <openssl/ssl.h>
+
+
+// SSL fuzzer utilities.
+//
+// The TLS client and server fuzzers coordinate with bssl_shim on a common
+// format to encode configuration parameters in a fuzzer file. To add a new
+// configuration, define a tag, update |SetupTest| below to parse it, and
+// update |WriteSettings| in bssl_shim to serialize it. Finally, record
+// transcripts from a test run, and use the BORINGSSL_FUZZER_DEBUG environment
+// variable to confirm the transcripts are compatible.
+
+// kDataTag denotes that the remainder of the input should be passed to the TLS
+// stack.
+static const uint16_t kDataTag = 0;
+
+// kSessionTag is followed by a u24-length-prefixed serialized SSL_SESSION to
+// resume.
+static const uint16_t kSessionTag = 1;
+
+// kRequestClientCert denotes that the server should request client
+// certificates.
+static const uint16_t kRequestClientCert = 2;
+
+// SetupTest parses parameters from |cbs| and returns a newly-configured |SSL|
+// object or nullptr on error. On success, the caller should feed the remaining
+// input in |cbs| to the SSL stack.
+static inline bssl::UniquePtr<SSL> SetupTest(CBS *cbs, SSL_CTX *ctx,
+                                             bool is_server) {
+  // Clear any sessions saved in |ctx| from the previous run.
+  SSL_CTX_flush_sessions(ctx, 0);
+
+  bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
+  if (is_server) {
+    SSL_set_accept_state(ssl.get());
+  } else {
+    SSL_set_connect_state(ssl.get());
+  }
+
+  for (;;) {
+    uint16_t tag;
+    if (!CBS_get_u16(cbs, &tag)) {
+      return nullptr;
+    }
+    switch (tag) {
+      case kDataTag:
+        return ssl;
+
+      case kSessionTag: {
+        CBS data;
+        if (!CBS_get_u24_length_prefixed(cbs, &data)) {
+          return nullptr;
+        }
+        bssl::UniquePtr<SSL_SESSION> session(
+            SSL_SESSION_from_bytes(CBS_data(&data), CBS_len(&data), ctx));
+        if (!session) {
+          return nullptr;
+        }
+
+        if (is_server) {
+          SSL_CTX_add_session(ctx, session.get());
+        } else {
+          SSL_set_session(ssl.get(), session.get());
+        }
+        break;
+      }
+
+      case kRequestClientCert:
+        if (!is_server) {
+          return nullptr;
+        }
+        SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
+        break;
+    }
+  }
+}
+
+
+// Additional shared constants.
+
+static const uint8_t kCertificateDER[] = {
+    0x30, 0x82, 0x02, 0xff, 0x30, 0x82, 0x01, 0xe7, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x02, 0x11, 0x00, 0xb1, 0x84, 0xee, 0x34, 0x99, 0x98, 0x76, 0xfb,
+    0x6f, 0xb2, 0x15, 0xc8, 0x47, 0x79, 0x05, 0x9b, 0x30, 0x0d, 0x06, 0x09,
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+    0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07,
+    0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+    0x35, 0x31, 0x31, 0x30, 0x37, 0x30, 0x30, 0x32, 0x34, 0x35, 0x36, 0x5a,
+    0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x36, 0x30, 0x30, 0x32, 0x34,
+    0x35, 0x36, 0x5a, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+    0x04, 0x0a, 0x13, 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30,
+    0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+    0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, 0x47, 0xcb, 0x11,
+    0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14, 0xaf, 0xc7, 0xea, 0xb6,
+    0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc, 0xfc, 0x59, 0x42, 0xb9,
+    0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe, 0xac, 0x03, 0x9e, 0x71,
+    0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac, 0x65, 0xf6, 0x03, 0xc8,
+    0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41, 0x92, 0x74, 0xa6, 0x57,
+    0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e, 0x63, 0x88, 0x22, 0xe3,
+    0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0, 0x4c, 0xf2, 0x3c, 0x20,
+    0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5, 0xc9, 0x48, 0xa0, 0xca,
+    0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1, 0xe3, 0x43, 0xc1, 0x2a,
+    0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23, 0x74, 0xa7, 0x37, 0xb0,
+    0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8, 0xf6, 0xdb, 0x86, 0x40,
+    0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15, 0x2a, 0x23, 0xca, 0xea,
+    0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a, 0x49, 0x49, 0x0e, 0xe7,
+    0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21, 0x0c, 0x26, 0x2b, 0x5d,
+    0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c, 0xf3, 0x3b, 0xf3, 0x09,
+    0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46, 0x8f, 0x76, 0x50, 0xbf,
+    0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1, 0x77, 0xd0, 0xfb, 0xa9,
+    0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41, 0x67, 0x01, 0xbe, 0x33,
+    0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde, 0x5d, 0xc1, 0xe8, 0xbb,
+    0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7, 0x18, 0xab, 0x0f, 0x94,
+    0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7, 0x05, 0x47, 0x10, 0x41,
+    0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0e, 0x06,
+    0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05,
+    0xa0, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a,
+    0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0c,
+    0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+    0x30, 0x19, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x12, 0x30, 0x10, 0x82,
+    0x0e, 0x66, 0x75, 0x7a, 0x7a, 0x2e, 0x62, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+    0x73, 0x73, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x92,
+    0xde, 0xef, 0x96, 0x06, 0x7b, 0xff, 0x71, 0x7d, 0x4e, 0xa0, 0x7d, 0xae,
+    0xb8, 0x22, 0xb4, 0x2c, 0xf7, 0x96, 0x9c, 0x37, 0x1d, 0x8f, 0xe7, 0xd9,
+    0x47, 0xff, 0x3f, 0xe9, 0x35, 0x95, 0x0e, 0xdd, 0xdc, 0x7f, 0xc8, 0x8a,
+    0x1e, 0x36, 0x1d, 0x38, 0x47, 0xfc, 0x76, 0xd2, 0x1f, 0x98, 0xa1, 0x36,
+    0xac, 0xc8, 0x70, 0x38, 0x0a, 0x3d, 0x51, 0x8d, 0x0f, 0x03, 0x1b, 0xef,
+    0x62, 0xa1, 0xcb, 0x2b, 0x4a, 0x8c, 0x12, 0x2b, 0x54, 0x50, 0x9a, 0x6b,
+    0xfe, 0xaf, 0xd9, 0xf6, 0xbf, 0x58, 0x11, 0x58, 0x5e, 0xe5, 0x86, 0x1e,
+    0x3b, 0x6b, 0x30, 0x7e, 0x72, 0x89, 0xe8, 0x6b, 0x7b, 0xb7, 0xaf, 0xef,
+    0x8b, 0xa9, 0x3e, 0xb0, 0xcd, 0x0b, 0xef, 0xb0, 0x0c, 0x96, 0x2b, 0xc5,
+    0x3b, 0xd5, 0xf1, 0xc2, 0xae, 0x3a, 0x60, 0xd9, 0x0f, 0x75, 0x37, 0x55,
+    0x4d, 0x62, 0xd2, 0xed, 0x96, 0xac, 0x30, 0x6b, 0xda, 0xa1, 0x48, 0x17,
+    0x96, 0x23, 0x85, 0x9a, 0x57, 0x77, 0xe9, 0x22, 0xa2, 0x37, 0x03, 0xba,
+    0x49, 0x77, 0x40, 0x3b, 0x76, 0x4b, 0xda, 0xc1, 0x04, 0x57, 0x55, 0x34,
+    0x22, 0x83, 0x45, 0x29, 0xab, 0x2e, 0x11, 0xff, 0x0d, 0xab, 0x55, 0xb1,
+    0xa7, 0x58, 0x59, 0x05, 0x25, 0xf9, 0x1e, 0x3d, 0xb7, 0xac, 0x04, 0x39,
+    0x2c, 0xf9, 0xaf, 0xb8, 0x68, 0xfb, 0x8e, 0x35, 0x71, 0x32, 0xff, 0x70,
+    0xe9, 0x46, 0x6d, 0x5c, 0x06, 0x90, 0x88, 0x23, 0x48, 0x0c, 0x50, 0xeb,
+    0x0a, 0xa9, 0xae, 0xe8, 0xfc, 0xbe, 0xa5, 0x76, 0x94, 0xd7, 0x64, 0x22,
+    0x38, 0x98, 0x17, 0xa4, 0x3a, 0xa7, 0x59, 0x9f, 0x1d, 0x3b, 0x75, 0x90,
+    0x1a, 0x81, 0xef, 0x19, 0xfb, 0x2b, 0xb7, 0xa7, 0x64, 0x61, 0x22, 0xa4,
+    0x6f, 0x7b, 0xfa, 0x58, 0xbb, 0x8c, 0x4e, 0x77, 0x67, 0xd0, 0x5d, 0x58,
+    0x76, 0x8a, 0xbb,
+};
+
+static const uint8_t kRSAPrivateKeyDER[] = {
+    0x30, 0x82, 0x04, 0xa5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
+    0xce, 0x47, 0xcb, 0x11, 0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14,
+    0xaf, 0xc7, 0xea, 0xb6, 0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc,
+    0xfc, 0x59, 0x42, 0xb9, 0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe,
+    0xac, 0x03, 0x9e, 0x71, 0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac,
+    0x65, 0xf6, 0x03, 0xc8, 0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41,
+    0x92, 0x74, 0xa6, 0x57, 0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e,
+    0x63, 0x88, 0x22, 0xe3, 0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0,
+    0x4c, 0xf2, 0x3c, 0x20, 0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5,
+    0xc9, 0x48, 0xa0, 0xca, 0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1,
+    0xe3, 0x43, 0xc1, 0x2a, 0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23,
+    0x74, 0xa7, 0x37, 0xb0, 0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8,
+    0xf6, 0xdb, 0x86, 0x40, 0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15,
+    0x2a, 0x23, 0xca, 0xea, 0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a,
+    0x49, 0x49, 0x0e, 0xe7, 0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21,
+    0x0c, 0x26, 0x2b, 0x5d, 0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c,
+    0xf3, 0x3b, 0xf3, 0x09, 0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46,
+    0x8f, 0x76, 0x50, 0xbf, 0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1,
+    0x77, 0xd0, 0xfb, 0xa9, 0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41,
+    0x67, 0x01, 0xbe, 0x33, 0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde,
+    0x5d, 0xc1, 0xe8, 0xbb, 0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7,
+    0x18, 0xab, 0x0f, 0x94, 0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7,
+    0x05, 0x47, 0x10, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
+    0x01, 0x00, 0xa8, 0x47, 0xb9, 0x4a, 0x06, 0x47, 0x93, 0x71, 0x3d, 0xef,
+    0x7b, 0xca, 0xb4, 0x7c, 0x0a, 0xe6, 0x82, 0xd0, 0xe7, 0x0d, 0xa9, 0x08,
+    0xf6, 0xa4, 0xfd, 0xd8, 0x73, 0xae, 0x6f, 0x56, 0x29, 0x5e, 0x25, 0x72,
+    0xa8, 0x30, 0x44, 0x73, 0xcf, 0x56, 0x26, 0xb9, 0x61, 0xde, 0x42, 0x81,
+    0xf4, 0xf0, 0x1f, 0x5d, 0xcb, 0x47, 0xf2, 0x26, 0xe9, 0xe0, 0x93, 0x28,
+    0xa3, 0x10, 0x3b, 0x42, 0x1e, 0x51, 0x11, 0x12, 0x06, 0x5e, 0xaf, 0xce,
+    0xb0, 0xa5, 0x14, 0xdd, 0x82, 0x58, 0xa1, 0xa4, 0x12, 0xdf, 0x65, 0x1d,
+    0x51, 0x70, 0x64, 0xd5, 0x58, 0x68, 0x11, 0xa8, 0x6a, 0x23, 0xc2, 0xbf,
+    0xa1, 0x25, 0x24, 0x47, 0xb3, 0xa4, 0x3c, 0x83, 0x96, 0xb7, 0x1f, 0xf4,
+    0x44, 0xd4, 0xd1, 0xe9, 0xfc, 0x33, 0x68, 0x5e, 0xe2, 0x68, 0x99, 0x9c,
+    0x91, 0xe8, 0x72, 0xc9, 0xd7, 0x8c, 0x80, 0x20, 0x8e, 0x77, 0x83, 0x4d,
+    0xe4, 0xab, 0xf9, 0x74, 0xa1, 0xdf, 0xd3, 0xc0, 0x0d, 0x5b, 0x05, 0x51,
+    0xc2, 0x6f, 0xb2, 0x91, 0x02, 0xec, 0xc0, 0x02, 0x1a, 0x5c, 0x91, 0x05,
+    0xf1, 0xe3, 0xfa, 0x65, 0xc2, 0xad, 0x24, 0xe6, 0xe5, 0x3c, 0xb6, 0x16,
+    0xf1, 0xa1, 0x67, 0x1a, 0x9d, 0x37, 0x56, 0xbf, 0x01, 0xd7, 0x3b, 0x35,
+    0x30, 0x57, 0x73, 0xf4, 0xf0, 0x5e, 0xa7, 0xe8, 0x0a, 0xc1, 0x94, 0x17,
+    0xcf, 0x0a, 0xbd, 0xf5, 0x31, 0xa7, 0x2d, 0xf7, 0xf5, 0xd9, 0x8c, 0xc2,
+    0x01, 0xbd, 0xda, 0x16, 0x8e, 0xb9, 0x30, 0x40, 0xa6, 0x6e, 0xbd, 0xcd,
+    0x4d, 0x84, 0x67, 0x4e, 0x0b, 0xce, 0xd5, 0xef, 0xf8, 0x08, 0x63, 0x02,
+    0xc6, 0xc7, 0xf7, 0x67, 0x92, 0xe2, 0x23, 0x9d, 0x27, 0x22, 0x1d, 0xc6,
+    0x67, 0x5e, 0x66, 0xbf, 0x03, 0xb8, 0xa9, 0x67, 0xd4, 0x39, 0xd8, 0x75,
+    0xfa, 0xe8, 0xed, 0x56, 0xb8, 0x81, 0x02, 0x81, 0x81, 0x00, 0xf7, 0x46,
+    0x68, 0xc6, 0x13, 0xf8, 0xba, 0x0f, 0x83, 0xdb, 0x05, 0xa8, 0x25, 0x00,
+    0x70, 0x9c, 0x9e, 0x8b, 0x12, 0x34, 0x0d, 0x96, 0xcf, 0x0d, 0x98, 0x9b,
+    0x8d, 0x9c, 0x96, 0x78, 0xd1, 0x3c, 0x01, 0x8c, 0xb9, 0x35, 0x5c, 0x20,
+    0x42, 0xb4, 0x38, 0xe3, 0xd6, 0x54, 0xe7, 0x55, 0xd6, 0x26, 0x8a, 0x0c,
+    0xf6, 0x1f, 0xe0, 0x04, 0xc1, 0x22, 0x42, 0x19, 0x61, 0xc4, 0x94, 0x7c,
+    0x07, 0x2e, 0x80, 0x52, 0xfe, 0x8d, 0xe6, 0x92, 0x3a, 0x91, 0xfe, 0x72,
+    0x99, 0xe1, 0x2a, 0x73, 0x76, 0xb1, 0x24, 0x20, 0x67, 0xde, 0x28, 0xcb,
+    0x0e, 0xe6, 0x52, 0xb5, 0xfa, 0xfb, 0x8b, 0x1e, 0x6a, 0x1d, 0x09, 0x26,
+    0xb9, 0xa7, 0x61, 0xba, 0xf8, 0x79, 0xd2, 0x66, 0x57, 0x28, 0xd7, 0x31,
+    0xb5, 0x0b, 0x27, 0x19, 0x1e, 0x6f, 0x46, 0xfc, 0x54, 0x95, 0xeb, 0x78,
+    0x01, 0xb6, 0xd9, 0x79, 0x5a, 0x4d, 0x02, 0x81, 0x81, 0x00, 0xd5, 0x8f,
+    0x16, 0x53, 0x2f, 0x57, 0x93, 0xbf, 0x09, 0x75, 0xbf, 0x63, 0x40, 0x3d,
+    0x27, 0xfd, 0x23, 0x21, 0xde, 0x9b, 0xe9, 0x73, 0x3f, 0x49, 0x02, 0xd2,
+    0x38, 0x96, 0xcf, 0xc3, 0xba, 0x92, 0x07, 0x87, 0x52, 0xa9, 0x35, 0xe3,
+    0x0c, 0xe4, 0x2f, 0x05, 0x7b, 0x37, 0xa5, 0x40, 0x9c, 0x3b, 0x94, 0xf7,
+    0xad, 0xa0, 0xee, 0x3a, 0xa8, 0xfb, 0x1f, 0x11, 0x1f, 0xd8, 0x9a, 0x80,
+    0x42, 0x3d, 0x7f, 0xa4, 0xb8, 0x9a, 0xaa, 0xea, 0x72, 0xc1, 0xe3, 0xed,
+    0x06, 0x60, 0x92, 0x37, 0xf9, 0xba, 0xfb, 0x9e, 0xed, 0x05, 0xa6, 0xd4,
+    0x72, 0x68, 0x4f, 0x63, 0xfe, 0xd6, 0x10, 0x0d, 0x4f, 0x0a, 0x93, 0xc6,
+    0xb9, 0xd7, 0xaf, 0xfd, 0xd9, 0x57, 0x7d, 0xcb, 0x75, 0xe8, 0x93, 0x2b,
+    0xae, 0x4f, 0xea, 0xd7, 0x30, 0x0b, 0x58, 0x44, 0x82, 0x0f, 0x84, 0x5d,
+    0x62, 0x11, 0x78, 0xea, 0x5f, 0xc5, 0x02, 0x81, 0x81, 0x00, 0x82, 0x0c,
+    0xc1, 0xe6, 0x0b, 0x72, 0xf1, 0x48, 0x5f, 0xac, 0xbd, 0x98, 0xe5, 0x7d,
+    0x09, 0xbd, 0x15, 0x95, 0x47, 0x09, 0xa1, 0x6c, 0x03, 0x91, 0xbf, 0x05,
+    0x70, 0xc1, 0x3e, 0x52, 0x64, 0x99, 0x0e, 0xa7, 0x98, 0x70, 0xfb, 0xf6,
+    0xeb, 0x9e, 0x25, 0x9d, 0x8e, 0x88, 0x30, 0xf2, 0xf0, 0x22, 0x6c, 0xd0,
+    0xcc, 0x51, 0x8f, 0x5c, 0x70, 0xc7, 0x37, 0xc4, 0x69, 0xab, 0x1d, 0xfc,
+    0xed, 0x3a, 0x03, 0xbb, 0xa2, 0xad, 0xb6, 0xea, 0x89, 0x6b, 0x67, 0x4b,
+    0x96, 0xaa, 0xd9, 0xcc, 0xc8, 0x4b, 0xfa, 0x18, 0x21, 0x08, 0xb2, 0xa3,
+    0xb9, 0x3e, 0x61, 0x99, 0xdc, 0x5a, 0x97, 0x9c, 0x73, 0x6a, 0xb9, 0xf9,
+    0x68, 0x03, 0x24, 0x5f, 0x55, 0x77, 0x9c, 0xb4, 0xbe, 0x7a, 0x78, 0x53,
+    0x68, 0x48, 0x69, 0x53, 0xc8, 0xb1, 0xf5, 0xbf, 0x98, 0x2d, 0x11, 0x1e,
+    0x98, 0xa8, 0x36, 0x50, 0xa0, 0xb1, 0x02, 0x81, 0x81, 0x00, 0x90, 0x88,
+    0x30, 0x71, 0xc7, 0xfe, 0x9b, 0x6d, 0x95, 0x37, 0x6d, 0x79, 0xfc, 0x85,
+    0xe7, 0x44, 0x78, 0xbc, 0x79, 0x6e, 0x47, 0x86, 0xc9, 0xf3, 0xdd, 0xc6,
+    0xec, 0xa9, 0x94, 0x9f, 0x40, 0xeb, 0x87, 0xd0, 0xdb, 0xee, 0xcd, 0x1b,
+    0x87, 0x23, 0xff, 0x76, 0xd4, 0x37, 0x8a, 0xcd, 0xb9, 0x6e, 0xd1, 0x98,
+    0xf6, 0x97, 0x8d, 0xe3, 0x81, 0x6d, 0xc3, 0x4e, 0xd1, 0xa0, 0xc4, 0x9f,
+    0xbd, 0x34, 0xe5, 0xe8, 0x53, 0x4f, 0xca, 0x10, 0xb5, 0xed, 0xe7, 0x16,
+    0x09, 0x54, 0xde, 0x60, 0xa7, 0xd1, 0x16, 0x6e, 0x2e, 0xb7, 0xbe, 0x7a,
+    0xd5, 0x9b, 0x26, 0xef, 0xe4, 0x0e, 0x77, 0xfa, 0xa9, 0xdd, 0xdc, 0xb9,
+    0x88, 0x19, 0x23, 0x70, 0xc7, 0xe1, 0x60, 0xaf, 0x8c, 0x73, 0x04, 0xf7,
+    0x71, 0x17, 0x81, 0x36, 0x75, 0xbb, 0x97, 0xd7, 0x75, 0xb6, 0x8e, 0xbc,
+    0xac, 0x9c, 0x6a, 0x9b, 0x24, 0x89, 0x02, 0x81, 0x80, 0x5a, 0x2b, 0xc7,
+    0x6b, 0x8c, 0x65, 0xdb, 0x04, 0x73, 0xab, 0x25, 0xe1, 0x5b, 0xbc, 0x3c,
+    0xcf, 0x5a, 0x3c, 0x04, 0xae, 0x97, 0x2e, 0xfd, 0xa4, 0x97, 0x1f, 0x05,
+    0x17, 0x27, 0xac, 0x7c, 0x30, 0x85, 0xb4, 0x82, 0x3f, 0x5b, 0xb7, 0x94,
+    0x3b, 0x7f, 0x6c, 0x0c, 0xc7, 0x16, 0xc6, 0xa0, 0xbd, 0x80, 0xb0, 0x81,
+    0xde, 0xa0, 0x23, 0xa6, 0xf6, 0x75, 0x33, 0x51, 0x35, 0xa2, 0x75, 0x55,
+    0x70, 0x4d, 0x42, 0xbb, 0xcf, 0x54, 0xe4, 0xdb, 0x2d, 0x88, 0xa0, 0x7a,
+    0xf2, 0x17, 0xa7, 0xdd, 0x13, 0x44, 0x9f, 0x5f, 0x6b, 0x2c, 0x42, 0x42,
+    0x8b, 0x13, 0x4d, 0xf9, 0x5b, 0xf8, 0x33, 0x42, 0xd9, 0x9e, 0x50, 0x1c,
+    0x7c, 0xbc, 0xfa, 0x62, 0x85, 0x0b, 0xcf, 0x99, 0xda, 0x9e, 0x04, 0x90,
+    0xb2, 0xc6, 0xb2, 0x0a, 0x2a, 0x7c, 0x6d, 0x6a, 0x40, 0xfc, 0xf5, 0x50,
+    0x98, 0x46, 0x89, 0x82, 0x40,
+};
+
+
+#endif  // HEADER_SSL_TEST_FUZZER
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 0a6648f..9e7b204 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -26,9 +26,27 @@
 	VersionTLS13 = 0x0304
 )
 
+const (
+	VersionDTLS10 = 0xfeff
+	VersionDTLS12 = 0xfefd
+)
+
 // A draft version of TLS 1.3 that is sent over the wire for the current draft.
 const tls13DraftVersion = 0x7f12
 
+var allTLSWireVersions = []uint16{
+	tls13DraftVersion,
+	VersionTLS12,
+	VersionTLS11,
+	VersionTLS10,
+	VersionSSL30,
+}
+
+var allDTLSWireVersions = []uint16{
+	VersionDTLS12,
+	VersionDTLS10,
+}
+
 const (
 	maxPlaintext        = 16384        // maximum plaintext payload length
 	maxCiphertext       = 16384 + 2048 // maximum ciphertext payload length
@@ -625,12 +643,12 @@
 	SendSupportedVersions []uint16
 
 	// NegotiateVersion, if non-zero, causes the server to negotiate the
-	// specifed TLS version rather than the version supported by either
+	// specifed wire version rather than the version supported by either
 	// peer.
 	NegotiateVersion uint16
 
 	// NegotiateVersionOnRenego, if non-zero, causes the server to negotiate
-	// the specified TLS version on renegotiation rather than retaining it.
+	// the specified wire version on renegotiation rather than retaining it.
 	NegotiateVersionOnRenego uint16
 
 	// ExpectFalseStart causes the server to, on full handshakes,
@@ -710,9 +728,13 @@
 	EmptyRenegotiationInfo bool
 
 	// BadRenegotiationInfo causes the renegotiation extension value in a
-	// renegotiation handshake to be incorrect.
+	// renegotiation handshake to be incorrect at the start.
 	BadRenegotiationInfo bool
 
+	// BadRenegotiationInfoEnd causes the renegotiation extension value in
+	// a renegotiation handshake to be incorrect at the end.
+	BadRenegotiationInfoEnd bool
+
 	// NoRenegotiationInfo disables renegotiation info support in all
 	// handshakes.
 	NoRenegotiationInfo bool
@@ -1257,6 +1279,10 @@
 	// send in the ClientHello.
 	SendCompressionMethods []byte
 
+	// SendCompressionMethod is the compression method to send in the
+	// ServerHello.
+	SendCompressionMethod byte
+
 	// AlwaysSendPreSharedKeyIdentityHint, if true, causes the server to
 	// always send a ServerKeyExchange for PSK ciphers, even if the identity
 	// hint is empty.
@@ -1438,10 +1464,29 @@
 	return defaultCurves
 }
 
-// isSupportedVersion returns true if the specified protocol version is
-// acceptable.
-func (c *Config) isSupportedVersion(vers uint16, isDTLS bool) bool {
-	return c.minVersion(isDTLS) <= vers && vers <= c.maxVersion(isDTLS)
+// isSupportedVersion checks if the specified wire version is acceptable. If so,
+// it returns true and the corresponding protocol version. Otherwise, it returns
+// false.
+func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
+	vers, ok := wireToVersion(wireVers, isDTLS)
+	if !ok || c.minVersion(isDTLS) > vers || vers > c.maxVersion(isDTLS) {
+		return 0, false
+	}
+	return vers, true
+}
+
+func (c *Config) supportedVersions(isDTLS bool) []uint16 {
+	versions := allTLSWireVersions
+	if isDTLS {
+		versions = allDTLSWireVersions
+	}
+	var ret []uint16
+	for _, vers := range versions {
+		if _, ok := c.isSupportedVersion(vers, isDTLS); ok {
+			ret = append(ret, vers)
+		}
+	}
+	return ret
 }
 
 // getCertificateForName returns the best certificate for the given name,
@@ -1717,3 +1762,12 @@
 	downgradeTLS13 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01}
 	downgradeTLS12 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00}
 )
+
+func containsGREASE(values []uint16) bool {
+	for _, v := range values {
+		if isGREASEValue(v) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index fce0049..61fc9d3 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -35,6 +35,7 @@
 	// constant after handshake; protected by handshakeMutex
 	handshakeMutex       sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
 	handshakeErr         error      // error resulting from handshake
+	wireVersion          uint16     // TLS wire version
 	vers                 uint16     // TLS version
 	haveVers             bool       // version has been negotiated
 	config               *Config    // configuration passed to constructor
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index e273bc7..d46b247 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -23,32 +23,12 @@
 	"net"
 )
 
-func versionToWire(vers uint16, isDTLS bool) uint16 {
-	if isDTLS {
-		switch vers {
-		case VersionTLS12:
-			return 0xfefd
-		case VersionTLS10:
-			return 0xfeff
-		}
-	} else {
-		switch vers {
-		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
-			return vers
-		case VersionTLS13:
-			return tls13DraftVersion
-		}
-	}
-
-	panic("unknown version")
-}
-
 func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) {
 	if isDTLS {
 		switch vers {
-		case 0xfefd:
+		case VersionDTLS12:
 			return VersionTLS12, true
-		case 0xfeff:
+		case VersionDTLS10:
 			return VersionTLS10, true
 		}
 	} else {
@@ -102,9 +82,9 @@
 	// version is irrelevant.)
 	if typ != recordTypeAlert {
 		if c.haveVers {
-			if wireVers := versionToWire(c.vers, c.isDTLS); vers != wireVers {
+			if vers != c.wireVersion {
 				c.sendAlert(alertProtocolVersion)
-				return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, wireVers))
+				return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion))
 			}
 		} else {
 			// Pre-version-negotiation alerts may be sent with any version.
@@ -368,13 +348,16 @@
 	// TODO(nharper): DTLS 1.3 will likely need to set this to
 	// recordTypeApplicationData if c.out.cipher != nil.
 	b.data[0] = byte(typ)
-	vers := c.vers
+	vers := c.wireVersion
 	if vers == 0 {
 		// Some TLS servers fail if the record version is greater than
 		// TLS 1.0 for the initial ClientHello.
-		vers = VersionTLS10
+		if c.isDTLS {
+			vers = VersionDTLS10
+		} else {
+			vers = VersionTLS10
+		}
 	}
-	vers = versionToWire(vers, c.isDTLS)
 	b.data[1] = byte(vers >> 8)
 	b.data[2] = byte(vers)
 	// DTLS records include an explicit sequence number.
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index fd819f9..d2f64ef 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -46,6 +46,8 @@
     "TLS13-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
     "TLS13-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
     "TLS13-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher."
+    "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher.",
+
+    "Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info."
   }
 }
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index c531a28..f0bfca4 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -35,6 +35,21 @@
 	finishedBytes []byte
 }
 
+func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 {
+	if !isDTLS {
+		return vers
+	}
+
+	switch vers {
+	case VersionTLS12:
+		return VersionDTLS12
+	case VersionTLS10:
+		return VersionDTLS10
+	}
+
+	panic("Unknown ClientHello version.")
+}
+
 func (c *Conn) clientHandshake() error {
 	if c.config == nil {
 		c.config = defaultConfig()
@@ -63,7 +78,6 @@
 	maxVersion := c.config.maxVersion(c.isDTLS)
 	hello := &clientHelloMsg{
 		isDTLS:                  c.isDTLS,
-		vers:                    versionToWire(maxVersion, c.isDTLS),
 		compressionMethods:      []uint8{compressionNone},
 		random:                  make([]byte, 32),
 		ocspStapling:            !c.config.Bugs.NoOCSPStapling,
@@ -85,6 +99,23 @@
 		pskBinderFirst:          c.config.Bugs.PSKBinderFirst,
 	}
 
+	if maxVersion >= VersionTLS13 {
+		hello.vers = mapClientHelloVersion(VersionTLS12, c.isDTLS)
+		if !c.config.Bugs.OmitSupportedVersions {
+			hello.supportedVersions = c.config.supportedVersions(c.isDTLS)
+		}
+	} else {
+		hello.vers = mapClientHelloVersion(maxVersion, c.isDTLS)
+	}
+
+	if c.config.Bugs.SendClientVersion != 0 {
+		hello.vers = c.config.Bugs.SendClientVersion
+	}
+
+	if len(c.config.Bugs.SendSupportedVersions) > 0 {
+		hello.supportedVersions = c.config.Bugs.SendSupportedVersions
+	}
+
 	disableEMS := c.config.Bugs.NoExtendedMasterSecret
 	if c.cipherSuite != nil {
 		disableEMS = c.config.Bugs.NoExtendedMasterSecretOnRenegotiation
@@ -310,23 +341,6 @@
 		}
 	}
 
-	if maxVersion == VersionTLS13 && !c.config.Bugs.OmitSupportedVersions {
-		if hello.vers >= VersionTLS13 {
-			hello.vers = VersionTLS12
-		}
-		for version := maxVersion; version >= minVersion; version-- {
-			hello.supportedVersions = append(hello.supportedVersions, versionToWire(version, c.isDTLS))
-		}
-	}
-
-	if len(c.config.Bugs.SendSupportedVersions) > 0 {
-		hello.supportedVersions = c.config.Bugs.SendSupportedVersions
-	}
-
-	if c.config.Bugs.SendClientVersion != 0 {
-		hello.vers = c.config.Bugs.SendClientVersion
-	}
-
 	if c.config.Bugs.SendCipherSuites != nil {
 		hello.cipherSuites = c.config.Bugs.SendCipherSuites
 	}
@@ -409,7 +423,7 @@
 	if c.isDTLS {
 		helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg)
 		if ok {
-			if helloVerifyRequest.vers != versionToWire(VersionTLS10, c.isDTLS) {
+			if helloVerifyRequest.vers != VersionDTLS10 {
 				// Per RFC 6347, the version field in
 				// HelloVerifyRequest SHOULD be always DTLS
 				// 1.0. Enforce this for testing purposes.
@@ -443,14 +457,12 @@
 		return fmt.Errorf("tls: received unexpected message of type %T when waiting for HelloRetryRequest or ServerHello", msg)
 	}
 
-	serverVersion, ok := wireToVersion(serverWireVersion, c.isDTLS)
-	if ok {
-		ok = c.config.isSupportedVersion(serverVersion, c.isDTLS)
-	}
+	serverVersion, ok := c.config.isSupportedVersion(serverWireVersion, c.isDTLS)
 	if !ok {
 		c.sendAlert(alertProtocolVersion)
 		return fmt.Errorf("tls: server selected unsupported protocol version %x", c.vers)
 	}
+	c.wireVersion = serverWireVersion
 	c.vers = serverVersion
 	c.haveVers = true
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 3a182ec..f70f469 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -161,7 +161,7 @@
 		// Per RFC 6347, the version field in HelloVerifyRequest SHOULD
 		// be always DTLS 1.0
 		helloVerifyRequest := &helloVerifyRequestMsg{
-			vers:   versionToWire(VersionTLS10, c.isDTLS),
+			vers:   VersionDTLS10,
 			cookie: make([]byte, 32),
 		}
 		if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil {
@@ -210,74 +210,69 @@
 
 	c.clientVersion = hs.clientHello.vers
 
-	// Convert the ClientHello wire version to a protocol version.
-	var clientVersion uint16
-	if c.isDTLS {
-		if hs.clientHello.vers <= 0xfefd {
-			clientVersion = VersionTLS12
-		} else if hs.clientHello.vers <= 0xfeff {
-			clientVersion = VersionTLS10
+	// Use the versions extension if supplied, otherwise use the legacy ClientHello version.
+	if len(hs.clientHello.supportedVersions) == 0 {
+		if c.isDTLS {
+			if hs.clientHello.vers <= VersionDTLS12 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionDTLS12)
+			}
+			if hs.clientHello.vers <= VersionDTLS10 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionDTLS10)
+			}
+		} else {
+			if hs.clientHello.vers >= VersionTLS12 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS12)
+			}
+			if hs.clientHello.vers >= VersionTLS11 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS11)
+			}
+			if hs.clientHello.vers >= VersionTLS10 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS10)
+			}
+			if hs.clientHello.vers >= VersionSSL30 {
+				hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionSSL30)
+			}
 		}
-	} else {
-		if hs.clientHello.vers >= VersionTLS12 {
-			clientVersion = VersionTLS12
-		} else if hs.clientHello.vers >= VersionTLS11 {
-			clientVersion = VersionTLS11
-		} else if hs.clientHello.vers >= VersionTLS10 {
-			clientVersion = VersionTLS10
-		} else if hs.clientHello.vers >= VersionSSL30 {
-			clientVersion = VersionSSL30
-		}
+	} else if config.Bugs.ExpectGREASE && !containsGREASE(hs.clientHello.supportedVersions) {
+		return errors.New("tls: no GREASE version value found")
 	}
 
-	if config.Bugs.NegotiateVersion != 0 {
-		c.vers = config.Bugs.NegotiateVersion
-	} else if c.haveVers && config.Bugs.NegotiateVersionOnRenego != 0 {
-		c.vers = config.Bugs.NegotiateVersionOnRenego
-	} else if len(hs.clientHello.supportedVersions) > 0 {
-		// Use the versions extension if supplied.
-		var foundVersion, foundGREASE bool
-		for _, extVersion := range hs.clientHello.supportedVersions {
-			if isGREASEValue(extVersion) {
-				foundGREASE = true
+	if !c.haveVers {
+		if config.Bugs.NegotiateVersion != 0 {
+			c.wireVersion = config.Bugs.NegotiateVersion
+		} else {
+			var found bool
+			for _, vers := range hs.clientHello.supportedVersions {
+				if _, ok := config.isSupportedVersion(vers, c.isDTLS); ok {
+					c.wireVersion = vers
+					found = true
+					break
+				}
 			}
-			extVersion, ok = wireToVersion(extVersion, c.isDTLS)
-			if !ok {
-				continue
-			}
-			if config.isSupportedVersion(extVersion, c.isDTLS) && !foundVersion {
-				c.vers = extVersion
-				foundVersion = true
-				break
+			if !found {
+				c.sendAlert(alertProtocolVersion)
+				return errors.New("tls: client did not offer any supported protocol versions")
 			}
 		}
-		if !foundVersion {
-			c.sendAlert(alertProtocolVersion)
-			return errors.New("tls: client did not offer any supported protocol versions")
-		}
-		if config.Bugs.ExpectGREASE && !foundGREASE {
-			return errors.New("tls: no GREASE version value found")
-		}
-	} else {
-		// Otherwise, use the legacy ClientHello version.
-		version := clientVersion
-		if maxVersion := config.maxVersion(c.isDTLS); version > maxVersion {
-			version = maxVersion
-		}
-		if version == 0 || !config.isSupportedVersion(version, c.isDTLS) {
-			return fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
-		}
-		c.vers = version
+	} else if config.Bugs.NegotiateVersionOnRenego != 0 {
+		c.wireVersion = config.Bugs.NegotiateVersionOnRenego
+	}
+
+	c.vers, ok = wireToVersion(c.wireVersion, c.isDTLS)
+	if !ok {
+		panic("Could not map wire version")
 	}
 	c.haveVers = true
 
+	clientProtocol, ok := wireToVersion(c.clientVersion, c.isDTLS)
+
 	// Reject < 1.2 ClientHellos with signature_algorithms.
-	if clientVersion < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 {
+	if ok && clientProtocol < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 {
 		return fmt.Errorf("tls: client included signature_algorithms before TLS 1.2")
 	}
 
 	// Check the client cipher list is consistent with the version.
-	if clientVersion < VersionTLS12 {
+	if ok && clientProtocol < VersionTLS12 {
 		for _, id := range hs.clientHello.cipherSuites {
 			if isTLS12Cipher(id) {
 				return fmt.Errorf("tls: client offered TLS 1.2 cipher before TLS 1.2")
@@ -298,13 +293,11 @@
 		return fmt.Errorf("tls: client offered unexpected PSK identities")
 	}
 
-	var scsvFound, greaseFound bool
+	var scsvFound bool
 	for _, cipherSuite := range hs.clientHello.cipherSuites {
 		if cipherSuite == fallbackSCSV {
 			scsvFound = true
-		}
-		if isGREASEValue(cipherSuite) {
-			greaseFound = true
+			break
 		}
 	}
 
@@ -314,11 +307,11 @@
 		return errors.New("tls: fallback SCSV found when not expected")
 	}
 
-	if !greaseFound && config.Bugs.ExpectGREASE {
+	if config.Bugs.ExpectGREASE && !containsGREASE(hs.clientHello.cipherSuites) {
 		return errors.New("tls: no GREASE cipher suite value found")
 	}
 
-	greaseFound = false
+	var greaseFound bool
 	for _, curve := range hs.clientHello.supportedCurves {
 		if isGREASEValue(uint16(curve)) {
 			greaseFound = true
@@ -367,7 +360,7 @@
 
 	hs.hello = &serverHelloMsg{
 		isDTLS:          c.isDTLS,
-		vers:            versionToWire(c.vers, c.isDTLS),
+		vers:            c.wireVersion,
 		versOverride:    config.Bugs.SendServerHelloVersion,
 		customExtension: config.Bugs.CustomUnencryptedExtension,
 		unencryptedALPN: config.Bugs.SendUnencryptedALPN,
@@ -526,7 +519,7 @@
 ResendHelloRetryRequest:
 	var sendHelloRetryRequest bool
 	helloRetryRequest := &helloRetryRequestMsg{
-		vers:                versionToWire(c.vers, c.isDTLS),
+		vers:                c.wireVersion,
 		duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions,
 	}
 
@@ -1049,9 +1042,9 @@
 
 	hs.hello = &serverHelloMsg{
 		isDTLS:            c.isDTLS,
-		vers:              versionToWire(c.vers, c.isDTLS),
+		vers:              c.wireVersion,
 		versOverride:      config.Bugs.SendServerHelloVersion,
-		compressionMethod: compressionNone,
+		compressionMethod: config.Bugs.SendCompressionMethod,
 	}
 
 	hs.hello.random = make([]byte, 32)
@@ -1168,6 +1161,9 @@
 			if c.config.Bugs.BadRenegotiationInfo {
 				serverExtensions.secureRenegotiation[0] ^= 0x80
 			}
+			if c.config.Bugs.BadRenegotiationInfoEnd {
+				serverExtensions.secureRenegotiation[len(serverExtensions.secureRenegotiation)-1] ^= 0x80
+			}
 		} else {
 			serverExtensions.secureRenegotiation = hs.clientHello.secureRenegotiation
 		}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index d9218f2..03ba755 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -263,6 +263,31 @@
 	panic("Unknown test certificate")
 }
 
+// configVersionToWire maps a protocol version to the default wire version to
+// test at that protocol.
+//
+// TODO(davidben): Rather than mapping these, make tlsVersions contains a list
+// of wire versions and test all of them.
+func configVersionToWire(vers uint16, protocol protocol) uint16 {
+	if protocol == dtls {
+		switch vers {
+		case VersionTLS12:
+			return VersionDTLS12
+		case VersionTLS10:
+			return VersionDTLS10
+		}
+	} else {
+		switch vers {
+		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
+			return vers
+		case VersionTLS13:
+			return tls13DraftVersion
+		}
+	}
+
+	panic("unknown version")
+}
+
 // encodeDERValues encodes a series of bytestrings in comma-separated-hex form.
 func encodeDERValues(values [][]byte) string {
 	var ret string
@@ -440,30 +465,20 @@
 
 var testCases []testCase
 
-func writeTranscript(test *testCase, num int, data []byte) {
+func writeTranscript(test *testCase, path string, 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)
+	settings, err := ioutil.ReadFile(path)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", path, err)
 		return
 	}
 
-	name := fmt.Sprintf("%s-%d", test.name, num)
-	if err := ioutil.WriteFile(path.Join(dir, name), data, 0644); err != nil {
-		fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", name, err)
+	settings = append(settings, data...)
+	if err := ioutil.WriteFile(path, settings, 0644); err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", path, err)
 	}
 }
 
@@ -487,7 +502,7 @@
 	return t.Conn.Write(b)
 }
 
-func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, num int) error {
+func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, transcriptPrefix string, num int) error {
 	if !test.noSessionCache {
 		if config.ClientSessionCache == nil {
 			config.ClientSessionCache = NewLRUClientSessionCache(1)
@@ -536,9 +551,10 @@
 		if *flagDebug {
 			defer connDebug.WriteTo(os.Stdout)
 		}
-		if len(*transcriptDir) != 0 {
+		if len(transcriptPrefix) != 0 {
 			defer func() {
-				writeTranscript(test, num, connDebug.Transcript())
+				path := transcriptPrefix + strconv.Itoa(num)
+				writeTranscript(test, path, connDebug.Transcript())
 			}()
 		}
 
@@ -613,6 +629,8 @@
 			channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
 			return fmt.Errorf("incorrect channel ID")
 		}
+	} else if connState.ChannelID != nil {
+		return fmt.Errorf("channel ID unexpectedly negotiated")
 	}
 
 	if expected := test.expectedNextProto; expected != "" {
@@ -1000,6 +1018,26 @@
 		flags = append(flags, "-tls-unique")
 	}
 
+	var transcriptPrefix string
+	if len(*transcriptDir) != 0 {
+		protocol := "tls"
+		if test.protocol == dtls {
+			protocol = "dtls"
+		}
+
+		side := "client"
+		if test.testType == serverTest {
+			side = "server"
+		}
+
+		dir := filepath.Join(*transcriptDir, protocol, side)
+		if err := os.MkdirAll(dir, 0755); err != nil {
+			return err
+		}
+		transcriptPrefix = filepath.Join(dir, test.name+"-")
+		flags = append(flags, "-write-settings", transcriptPrefix)
+	}
+
 	flags = append(flags, test.flags...)
 
 	var shim *exec.Cmd
@@ -1039,7 +1077,7 @@
 
 	conn, err := acceptOrWait(listener, waitChan)
 	if err == nil {
-		err = doExchange(test, &config, conn, false /* not a resumption */, 0)
+		err = doExchange(test, &config, conn, false /* not a resumption */, transcriptPrefix, 0)
 		conn.Close()
 	}
 
@@ -1059,7 +1097,7 @@
 		var connResume net.Conn
 		connResume, err = acceptOrWait(listener, waitChan)
 		if err == nil {
-			err = doExchange(test, &resumeConfig, connResume, true /* resumption */, i+1)
+			err = doExchange(test, &resumeConfig, connResume, true /* resumption */, transcriptPrefix, i+1)
 			connResume.Close()
 		}
 	}
@@ -1155,16 +1193,27 @@
 type tlsVersion struct {
 	name    string
 	version uint16
-	flag    string
-	hasDTLS bool
+	// excludeFlag is the legacy shim flag to disable the version.
+	excludeFlag string
+	hasDTLS     bool
+	// shimTLS and shimDTLS are values the shim uses to refer to these
+	// versions in TLS and DTLS, respectively.
+	shimTLS, shimDTLS int
+}
+
+func (vers tlsVersion) shimFlag(protocol protocol) string {
+	if protocol == dtls {
+		return strconv.Itoa(vers.shimDTLS)
+	}
+	return strconv.Itoa(vers.shimTLS)
 }
 
 var tlsVersions = []tlsVersion{
-	{"SSL3", VersionSSL30, "-no-ssl3", false},
-	{"TLS1", VersionTLS10, "-no-tls1", true},
-	{"TLS11", VersionTLS11, "-no-tls11", false},
-	{"TLS12", VersionTLS12, "-no-tls12", true},
-	{"TLS13", VersionTLS13, "-no-tls13", false},
+	{"SSL3", VersionSSL30, "-no-ssl3", false, VersionSSL30, 0},
+	{"TLS1", VersionTLS10, "-no-tls1", true, VersionTLS10, VersionDTLS10},
+	{"TLS11", VersionTLS11, "-no-tls11", false, VersionTLS11, 0},
+	{"TLS12", VersionTLS12, "-no-tls12", true, VersionTLS12, VersionDTLS12},
+	{"TLS13", VersionTLS13, "-no-tls13", false, VersionTLS13, 0},
 }
 
 type testCipherSuite struct {
@@ -2461,6 +2510,21 @@
 			expectedError:      ":INVALID_COMPRESSION_LIST:",
 			expectedLocalError: "remote error: illegal parameter",
 		},
+		// Test that the client rejects invalid compression methods
+		// from the server.
+		{
+			testType: clientTest,
+			name:     "InvalidCompressionMethod",
+			config: Config{
+				MaxVersion: VersionTLS12,
+				Bugs: ProtocolBugs{
+					SendCompressionMethod: 1,
+				},
+			},
+			shouldFail:         true,
+			expectedError:      ":UNSUPPORTED_COMPRESSION_ALGORITHM:",
+			expectedLocalError: "remote error: illegal parameter",
+		},
 		{
 			name: "GREASE-Client-TLS12",
 			config: Config{
@@ -2957,6 +3021,116 @@
 		shouldFail:    true,
 		expectedError: ":NO_SHARED_CIPHER:",
 	})
+
+	// Test cipher suite negotiation works as expected. Configure a
+	// complicated cipher suite configuration.
+	const negotiationTestCiphers = "" +
+		"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:" +
+		"[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:" +
+		"TLS_RSA_WITH_AES_128_GCM_SHA256:" +
+		"TLS_RSA_WITH_AES_128_CBC_SHA:" +
+		"[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]"
+	negotiationTests := []struct {
+		ciphers  []uint16
+		expected uint16
+	}{
+		// Server preferences are honored, including when
+		// equipreference groups are involved.
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_128_CBC_SHA,
+				TLS_RSA_WITH_AES_128_GCM_SHA256,
+				TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+				TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+			},
+			TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+		},
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_128_CBC_SHA,
+				TLS_RSA_WITH_AES_128_GCM_SHA256,
+				TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+			},
+			TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+		},
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_128_CBC_SHA,
+				TLS_RSA_WITH_AES_128_GCM_SHA256,
+			},
+			TLS_RSA_WITH_AES_128_GCM_SHA256,
+		},
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_128_CBC_SHA,
+			},
+			TLS_RSA_WITH_AES_128_CBC_SHA,
+		},
+		// Equipreference groups use the client preference.
+		{
+			[]uint16{
+				TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+				TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+				TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			},
+			TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+		},
+		{
+			[]uint16{
+				TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+				TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			},
+			TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+		},
+		{
+			[]uint16{
+				TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+			},
+			TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+		},
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_256_CBC_SHA,
+			},
+			TLS_RSA_WITH_AES_256_GCM_SHA384,
+		},
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_CBC_SHA,
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+			},
+			TLS_RSA_WITH_AES_256_CBC_SHA,
+		},
+		// If there are two equipreference groups, the preferred one
+		// takes precedence.
+		{
+			[]uint16{
+				TLS_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_RSA_WITH_AES_256_CBC_SHA,
+				TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+				TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+			},
+			TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+		},
+	}
+	for i, t := range negotiationTests {
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "CipherNegotiation-" + strconv.Itoa(i),
+			config: Config{
+				MaxVersion:   VersionTLS12,
+				CipherSuites: t.ciphers,
+			},
+			flags:          []string{"-cipher", negotiationTestCiphers},
+			expectedCipher: t.expected,
+		})
+	}
 }
 
 func addBadECDSASignatureTests() {
@@ -3036,36 +3210,46 @@
 }
 
 func addCBCSplittingTests() {
-	testCases = append(testCases, testCase{
-		name: "CBCRecordSplitting",
-		config: Config{
-			MaxVersion:   VersionTLS10,
-			MinVersion:   VersionTLS10,
-			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
-		},
-		messageLen:    -1, // read until EOF
-		resumeSession: true,
-		flags: []string{
-			"-async",
-			"-write-different-record-sizes",
-			"-cbc-record-splitting",
-		},
-	})
-	testCases = append(testCases, testCase{
-		name: "CBCRecordSplittingPartialWrite",
-		config: Config{
-			MaxVersion:   VersionTLS10,
-			MinVersion:   VersionTLS10,
-			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
-		},
-		messageLen: -1, // read until EOF
-		flags: []string{
-			"-async",
-			"-write-different-record-sizes",
-			"-cbc-record-splitting",
-			"-partial-write",
-		},
-	})
+	var cbcCiphers = []struct {
+		name   string
+		cipher uint16
+	}{
+		{"3DES", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+		{"AES128", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+		{"AES256", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+	}
+	for _, t := range cbcCiphers {
+		testCases = append(testCases, testCase{
+			name: "CBCRecordSplitting-" + t.name,
+			config: Config{
+				MaxVersion:   VersionTLS10,
+				MinVersion:   VersionTLS10,
+				CipherSuites: []uint16{t.cipher},
+			},
+			messageLen:    -1, // read until EOF
+			resumeSession: true,
+			flags: []string{
+				"-async",
+				"-write-different-record-sizes",
+				"-cbc-record-splitting",
+			},
+		})
+		testCases = append(testCases, testCase{
+			name: "CBCRecordSplittingPartialWrite-" + t.name,
+			config: Config{
+				MaxVersion:   VersionTLS10,
+				MinVersion:   VersionTLS10,
+				CipherSuites: []uint16{t.cipher},
+			},
+			messageLen: -1, // read until EOF
+			flags: []string{
+				"-async",
+				"-write-different-record-sizes",
+				"-cbc-record-splitting",
+				"-partial-write",
+			},
+		})
+	}
 }
 
 func addClientAuthTests() {
@@ -3209,6 +3393,37 @@
 				shouldFail:    true,
 				expectedError: ":UNEXPECTED_MESSAGE:",
 			})
+
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "VerifyPeerIfNoOBC-NoChannelID-" + ver.name,
+				config: Config{
+					MinVersion: ver.version,
+					MaxVersion: ver.version,
+				},
+				flags: []string{
+					"-enable-channel-id",
+					"-verify-peer-if-no-obc",
+				},
+				shouldFail:         true,
+				expectedError:      ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+				expectedLocalError: certificateRequired,
+			})
+
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "VerifyPeerIfNoOBC-ChannelID-" + ver.name,
+				config: Config{
+					MinVersion: ver.version,
+					MaxVersion: ver.version,
+					ChannelID:  channelIDKey,
+				},
+				expectChannelID: true,
+				flags: []string{
+					"-enable-channel-id",
+					"-verify-peer-if-no-obc",
+				},
+			})
 		}
 
 		testCases = append(testCases, testCase{
@@ -4363,6 +4578,36 @@
 				shouldFail:    true,
 				expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
 			})
+
+			if ver.version < VersionTLS13 {
+				// Channel ID requires ECDHE ciphers.
+				tests = append(tests, testCase{
+					testType: serverTest,
+					name:     "ChannelID-NoECDHE-" + ver.name,
+					config: Config{
+						MaxVersion:   ver.version,
+						CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+						ChannelID:    channelIDKey,
+					},
+					expectChannelID: false,
+					flags:           []string{"-enable-channel-id"},
+				})
+
+				// Sanity-check setting expectChannelID false works.
+				tests = append(tests, testCase{
+					testType: serverTest,
+					name:     "ChannelID-ECDHE-" + ver.name,
+					config: Config{
+						MaxVersion:   ver.version,
+						CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+						ChannelID:    channelIDKey,
+					},
+					expectChannelID:    false,
+					flags:              []string{"-enable-channel-id"},
+					shouldFail:         true,
+					expectedLocalError: "channel ID unexpectedly negotiated",
+				})
+			}
 		}
 
 		// Channel ID and NPN at the same time, to ensure their relative
@@ -4541,7 +4786,7 @@
 		// Assemble flags to disable all newer versions on the shim.
 		var flags []string
 		for _, vers := range tlsVersions[i+1:] {
-			flags = append(flags, vers.flag)
+			flags = append(flags, vers.excludeFlag)
 		}
 
 		// Test configuring the runner's maximum version.
@@ -4561,19 +4806,17 @@
 					suffix += "-DTLS"
 				}
 
-				shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls)))
-
 				// Determine the expected initial record-layer versions.
 				clientVers := shimVers.version
 				if clientVers > VersionTLS10 {
 					clientVers = VersionTLS10
 				}
-				clientVers = versionToWire(clientVers, protocol == dtls)
+				clientVers = configVersionToWire(clientVers, protocol)
 				serverVers := expectedVersion
 				if expectedVersion >= VersionTLS13 {
 					serverVers = VersionTLS10
 				}
-				serverVers = versionToWire(serverVers, protocol == dtls)
+				serverVers = configVersionToWire(serverVers, protocol)
 
 				testCases = append(testCases, testCase{
 					protocol: protocol,
@@ -4598,7 +4841,7 @@
 							ExpectInitialRecordVersion: clientVers,
 						},
 					},
-					flags:           []string{"-max-version", shimVersFlag},
+					flags:           []string{"-max-version", shimVers.shimFlag(protocol)},
 					expectedVersion: expectedVersion,
 				})
 
@@ -4625,7 +4868,7 @@
 							ExpectInitialRecordVersion: serverVers,
 						},
 					},
-					flags:           []string{"-max-version", shimVersFlag},
+					flags:           []string{"-max-version", shimVers.shimFlag(protocol)},
 					expectedVersion: expectedVersion,
 				})
 			}
@@ -4644,7 +4887,7 @@
 				suffix += "-DTLS"
 			}
 
-			wireVersion := versionToWire(vers.version, protocol == dtls)
+			wireVersion := configVersionToWire(vers.version, protocol)
 			testCases = append(testCases, testCase{
 				protocol: protocol,
 				testType: serverTest,
@@ -4882,7 +5125,7 @@
 		// Assemble flags to disable all older versions on the shim.
 		var flags []string
 		for _, vers := range tlsVersions[:i] {
-			flags = append(flags, vers.flag)
+			flags = append(flags, vers.excludeFlag)
 		}
 
 		for _, runnerVers := range tlsVersions {
@@ -4895,7 +5138,6 @@
 				if protocol == dtls {
 					suffix += "-DTLS"
 				}
-				shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls)))
 
 				var expectedVersion uint16
 				var shouldFail bool
@@ -4918,7 +5160,7 @@
 							// Ensure the server does not decline to
 							// select a version (versions extension) or
 							// cipher (some ciphers depend on versions).
-							NegotiateVersion:            runnerVers.version,
+							NegotiateVersion:            configVersionToWire(runnerVers.version, protocol),
 							IgnorePeerCipherPreferences: shouldFail,
 						},
 					},
@@ -4938,11 +5180,11 @@
 							// Ensure the server does not decline to
 							// select a version (versions extension) or
 							// cipher (some ciphers depend on versions).
-							NegotiateVersion:            runnerVers.version,
+							NegotiateVersion:            configVersionToWire(runnerVers.version, protocol),
 							IgnorePeerCipherPreferences: shouldFail,
 						},
 					},
-					flags:              []string{"-min-version", shimVersFlag},
+					flags:              []string{"-min-version", shimVers.shimFlag(protocol)},
 					expectedVersion:    expectedVersion,
 					shouldFail:         shouldFail,
 					expectedError:      expectedError,
@@ -4969,7 +5211,7 @@
 					config: Config{
 						MaxVersion: runnerVers.version,
 					},
-					flags:              []string{"-min-version", shimVersFlag},
+					flags:              []string{"-min-version", shimVers.shimFlag(protocol)},
 					expectedVersion:    expectedVersion,
 					shouldFail:         shouldFail,
 					expectedError:      expectedError,
@@ -6464,6 +6706,19 @@
 		expectedError: ":RENEGOTIATION_MISMATCH:",
 	})
 	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-BadExt2",
+		renegotiate: 1,
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				BadRenegotiationInfoEnd: true,
+			},
+		},
+		flags:         []string{"-renegotiate-freely"},
+		shouldFail:    true,
+		expectedError: ":RENEGOTIATION_MISMATCH:",
+	})
+	testCases = append(testCases, testCase{
 		name:        "Renegotiate-Client-Downgrade",
 		renegotiate: 1,
 		config: Config{
@@ -10900,6 +11155,24 @@
 			"-enable-early-data",
 		},
 	})
+
+	// Test that the client reports TLS 1.3 as the version while sending
+	// early data.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-EarlyData-Client-VersionAPI",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-accept-early-data",
+			"-expect-version", strconv.Itoa(VersionTLS13),
+		},
+	})
 }
 
 func addTLS13CipherPreferenceTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 960240e..195371f 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -95,6 +95,7 @@
   { "-shim-shuts-down", &TestConfig::shim_shuts_down },
   { "-verify-fail", &TestConfig::verify_fail },
   { "-verify-peer", &TestConfig::verify_peer },
+  { "-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc },
   { "-expect-verify-result", &TestConfig::expect_verify_result },
   { "-renegotiate-once", &TestConfig::renegotiate_once },
   { "-renegotiate-freely", &TestConfig::renegotiate_freely },
@@ -131,6 +132,7 @@
 };
 
 const Flag<std::string> kStringFlags[] = {
+  { "-write-settings", &TestConfig::write_settings },
   { "-digest-prefs", &TestConfig::digest_prefs },
   { "-key-file", &TestConfig::key_file },
   { "-cert-file", &TestConfig::cert_file },
@@ -172,6 +174,7 @@
   { "-resume-count", &TestConfig::resume_count },
   { "-min-version", &TestConfig::min_version },
   { "-max-version", &TestConfig::max_version },
+  { "-expect-version", &TestConfig::expect_version },
   { "-mtu", &TestConfig::mtu },
   { "-export-keying-material", &TestConfig::export_keying_material },
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 8bc8892..7b4b342 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -24,6 +24,7 @@
   bool is_server = false;
   bool is_dtls = false;
   int resume_count = 0;
+  std::string write_settings;
   bool fallback_scsv = false;
   std::string digest_prefs;
   std::vector<int> signing_prefs;
@@ -68,6 +69,7 @@
   std::string expected_signed_cert_timestamps;
   int min_version = 0;
   int max_version = 0;
+  int expect_version = 0;
   int mtu = 0;
   bool implicit_handshake = false;
   bool use_early_callback = false;
@@ -100,6 +102,7 @@
   bool shim_shuts_down = false;
   bool verify_fail = false;
   bool verify_peer = false;
+  bool verify_peer_if_no_obc = false;
   bool expect_verify_result = false;
   std::string signed_cert_timestamps;
   int expect_total_renegotiations = 0;
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index 2137fe2..97f0ed9 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -54,15 +54,8 @@
 
 int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint16_t session_version;
-  if (!ssl->method->version_from_wire(&session_version,
-                                      ssl->session->ssl_version) ||
-      !init_key_schedule(hs, session_version,
-                         ssl->session->cipher->algorithm_prf)) {
-    return 0;
-  }
-
-  return 1;
+  return init_key_schedule(hs, SSL_SESSION_protocol_version(ssl->session),
+                           ssl->session->cipher->algorithm_prf);
 }
 
 int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
@@ -122,11 +115,7 @@
                           const uint8_t *traffic_secret,
                           size_t traffic_secret_len) {
   const SSL_SESSION *session = SSL_get_session(ssl);
-  uint16_t version;
-  if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
+  uint16_t version = SSL_SESSION_protocol_version(session);
 
   if (traffic_secret_len > 0xff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
@@ -141,8 +130,7 @@
     return 0;
   }
 
-  const EVP_MD *digest = ssl_get_handshake_digest(
-      session->cipher->algorithm_prf, version);
+  const EVP_MD *digest = SSL_SESSION_get_digest(session);
 
   /* Derive the key. */
   size_t key_len = EVP_AEAD_key_length(aead);
@@ -383,11 +371,7 @@
 
 int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
   SSL *const ssl = hs->ssl;
-  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
-  if (digest == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
+  const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session);
   size_t hash_len = EVP_MD_size(digest);
 
   if (len < hash_len + 3) {
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 0a5e1a2..fe2463b 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -537,11 +537,14 @@
     goto err;
   }
 
-  /* Determine whether to request a client certificate. */
-  hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
-  /* CertificateRequest may only be sent in non-resumption handshakes. */
-  if (ssl->s3->session_reused) {
-    hs->cert_request = 0;
+  if (!ssl->s3->session_reused) {
+    /* Determine whether to request a client certificate. */
+    hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+    /* Only request a certificate if Channel ID isn't negotiated. */
+    if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+        ssl->s3->tlsext_channel_id_valid) {
+      hs->cert_request = 0;
+    }
   }
 
   /* Send a CertificateRequest, if necessary. */
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 6144f86..d039b7d 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -65,39 +65,6 @@
 #include "internal.h"
 
 
-static int ssl3_version_from_wire(uint16_t *out_version,
-                                  uint16_t wire_version) {
-  switch (wire_version) {
-    case SSL3_VERSION:
-    case TLS1_VERSION:
-    case TLS1_1_VERSION:
-    case TLS1_2_VERSION:
-      *out_version = wire_version;
-      return 1;
-    case TLS1_3_DRAFT_VERSION:
-      *out_version = TLS1_3_VERSION;
-      return 1;
-  }
-
-  return 0;
-}
-
-static uint16_t ssl3_version_to_wire(uint16_t version) {
-  switch (version) {
-    case SSL3_VERSION:
-    case TLS1_VERSION:
-    case TLS1_1_VERSION:
-    case TLS1_2_VERSION:
-      return version;
-    case TLS1_3_VERSION:
-      return TLS1_3_DRAFT_VERSION;
-  }
-
-  /* It is an error to use this function with an invalid version. */
-  assert(0);
-  return 0;
-}
-
 static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
 
 static void ssl3_expect_flight(SSL *ssl) {}
@@ -130,10 +97,6 @@
 
 static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = {
     0 /* is_dtls */,
-    SSL3_VERSION,
-    TLS1_3_VERSION,
-    ssl3_version_from_wire,
-    ssl3_version_to_wire,
     ssl3_new,
     ssl3_free,
     ssl3_get_message,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index a5bbe93..3bc0b29 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -389,8 +389,10 @@
    * ClientHellos should use SSL 3.0 and pre-TLS-1.3 expects the version
    * to change after version negotiation. */
   uint16_t wire_version = TLS1_VERSION;
-  if (ssl->version == SSL3_VERSION ||
-      (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION)) {
+  if (ssl->s3->hs != NULL && ssl->s3->hs->max_version == SSL3_VERSION) {
+    wire_version = SSL3_VERSION;
+  }
+  if (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
     wire_version = ssl->version;
   }