external/boringssl: Sync to 9a127b43b8b78a135d6b64a3e25b8a704c2c069f.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/54c259dec395bd991cce5691723214ffe588e51d..9a127b43b8b78a135d6b64a3e25b8a704c2c069f

Test: BoringSSL CTS Presubmits
Change-Id: I8c9fae592051cefd9b284fbebedc5c2240feda30
diff --git a/src/crypto/asn1/tasn_dec.c b/src/crypto/asn1/tasn_dec.c
index bf008af..2f5f132 100644
--- a/src/crypto/asn1/tasn_dec.c
+++ b/src/crypto/asn1/tasn_dec.c
@@ -56,6 +56,7 @@
 
 #include <openssl/asn1.h>
 
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/asn1t.h>
@@ -147,15 +148,6 @@
     return NULL;
 }
 
-int ASN1_template_d2i(ASN1_VALUE **pval,
-                      const unsigned char **in, long len,
-                      const ASN1_TEMPLATE *tt)
-{
-    ASN1_TLC c;
-    asn1_tlc_clear_nc(&c);
-    return asn1_template_ex_d2i(pval, in, len, tt, 0, &c);
-}
-
 /*
  * Decode an item, taking care of IMPLICIT tagging, if any. If 'opt' set and
  * tag mismatch return -1 to handle OPTIONAL
@@ -188,6 +180,14 @@
     else
         asn1_cb = 0;
 
+    /*
+     * Bound |len| to comfortably fit in an int. Lengths in this module often
+     * switch between int and long without overflow checks.
+     */
+    if (len > INT_MAX/2) {
+        len = INT_MAX/2;
+    }
+
     switch (it->itype) {
     case ASN1_ITYPE_PRIMITIVE:
         if (it->templates) {
diff --git a/src/crypto/asn1/tasn_enc.c b/src/crypto/asn1/tasn_enc.c
index 9286ef6..cc87d34 100644
--- a/src/crypto/asn1/tasn_enc.c
+++ b/src/crypto/asn1/tasn_enc.c
@@ -256,12 +256,6 @@
     return 0;
 }
 
-int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out,
-                      const ASN1_TEMPLATE *tt)
-{
-    return asn1_template_ex_i2d(pval, out, tt, -1, 0);
-}
-
 static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                                 const ASN1_TEMPLATE *tt, int tag, int iclass)
 {
diff --git a/src/crypto/cipher_extra/e_aesgcmsiv.c b/src/crypto/cipher_extra/e_aesgcmsiv.c
index e4c5bb3..654705b 100644
--- a/src/crypto/cipher_extra/e_aesgcmsiv.c
+++ b/src/crypto/cipher_extra/e_aesgcmsiv.c
@@ -34,6 +34,10 @@
 struct aead_aes_gcm_siv_asm_ctx {
   alignas(16) uint8_t key[16*15];
   int is_128_bit;
+  // ptr contains the original pointer from |OPENSSL_malloc|, which may only be
+  // 8-byte aligned. When freeing this structure, actually call |OPENSSL_free|
+  // on this pointer.
+  void *ptr;
 };
 
 // aes128gcmsiv_aes_ks writes an AES-128 key schedule for |key| to
@@ -64,16 +68,18 @@
     return 0;
   }
 
-  // The asm implementation expects a 16-byte-aligned address here, so we use
-  // |malloc| rather than |OPENSSL_malloc|, which would add a length prefix.
-  struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx =
-      malloc(sizeof(struct aead_aes_gcm_siv_asm_ctx));
-  if (gcm_siv_ctx == NULL) {
+  char *ptr = OPENSSL_malloc(sizeof(struct aead_aes_gcm_siv_asm_ctx) + 8);
+  if (ptr == NULL) {
     return 0;
   }
+  assert((((uintptr_t)ptr) & 7) == 0);
 
-  // malloc should return a 16-byte-aligned address.
+  // gcm_siv_ctx needs to be 16-byte aligned in a cross-platform way.
+  struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx =
+      (struct aead_aes_gcm_siv_asm_ctx *)(ptr + (((uintptr_t)ptr) & 8));
+
   assert((((uintptr_t)gcm_siv_ctx) & 15) == 0);
+  gcm_siv_ctx->ptr = ptr;
 
   if (key_bits == 128) {
     aes128gcmsiv_aes_ks(key, &gcm_siv_ctx->key[0]);
@@ -89,7 +95,8 @@
 }
 
 static void aead_aes_gcm_siv_asm_cleanup(EVP_AEAD_CTX *ctx) {
-  free(ctx->aead_state);  // allocated with native |malloc|
+  const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = ctx->aead_state;
+  OPENSSL_free(gcm_siv_ctx->ptr);
 }
 
 // aesgcmsiv_polyval_horner updates the POLYVAL value in |in_out_poly| to
diff --git a/src/crypto/cpu-arm-linux.c b/src/crypto/cpu-arm-linux.c
index a5f1f8a..839b632 100644
--- a/src/crypto/cpu-arm-linux.c
+++ b/src/crypto/cpu-arm-linux.c
@@ -288,7 +288,7 @@
 
 extern uint32_t OPENSSL_armcap_P;
 
-static int g_has_broken_neon;
+static int g_has_broken_neon, g_needs_hwcap2_workaround;
 
 void OPENSSL_cpuid_setup(void) {
   char *cpuinfo_data;
@@ -336,6 +336,7 @@
     }
     if (hwcap2 == 0) {
       hwcap2 = get_hwcap2_cpuinfo(&cpuinfo);
+      g_needs_hwcap2_workaround = hwcap2 != 0;
     }
 
     if (hwcap2 & HWCAP2_AES) {
@@ -357,4 +358,6 @@
 
 int CRYPTO_has_broken_NEON(void) { return g_has_broken_neon; }
 
+int CRYPTO_needs_hwcap2_workaround(void) { return g_needs_hwcap2_workaround; }
+
 #endif  // OPENSSL_ARM && !OPENSSL_STATIC_ARMCAP
diff --git a/src/crypto/rand_extra/fuchsia.c b/src/crypto/rand_extra/fuchsia.c
index 9355d8c..9c3783f 100644
--- a/src/crypto/rand_extra/fuchsia.c
+++ b/src/crypto/rand_extra/fuchsia.c
@@ -19,20 +19,29 @@
 #include <limits.h>
 #include <stdlib.h>
 
+// TODO(davidben): Remove this once https://crbug.com/765754 is resolved.
+#if defined(CHROMIUM_ROLLING_MAGENTA_TO_ZIRCON)
+#include <zircon/syscalls.h>
+#else
 #include <magenta/syscalls.h>
+#define ZX_CPRNG_DRAW_MAX_LEN MX_CPRNG_DRAW_MAX_LEN
+#define ZX_OK MX_OK
+#define zx_status_t mx_status_t
+#define zx_cprng_draw mx_cprng_draw
+#endif
 
 #include "../fipsmodule/rand/internal.h"
 
 void CRYPTO_sysrand(uint8_t *out, size_t requested) {
   while (requested > 0) {
-    size_t output_bytes_this_pass = MX_CPRNG_DRAW_MAX_LEN;
+    size_t output_bytes_this_pass = ZX_CPRNG_DRAW_MAX_LEN;
     if (requested < output_bytes_this_pass) {
       output_bytes_this_pass = requested;
     }
     size_t bytes_drawn;
-    mx_status_t status =
-        mx_cprng_draw(out, output_bytes_this_pass, &bytes_drawn);
-    if (status != MX_OK) {
+    zx_status_t status =
+        zx_cprng_draw(out, output_bytes_this_pass, &bytes_drawn);
+    if (status != ZX_OK) {
       abort();
     }
     requested -= bytes_drawn;
diff --git a/src/include/openssl/asn1t.h b/src/include/openssl/asn1t.h
index ae507ea..7bd7701 100644
--- a/src/include/openssl/asn1t.h
+++ b/src/include/openssl/asn1t.h
@@ -862,12 +862,10 @@
 int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
 
 void ASN1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
-int ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_TEMPLATE *tt);
 int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it,
 				int tag, int aclass, char opt, ASN1_TLC *ctx);
 
 int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass);
-int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLATE *tt);
 void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
 
 int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it);
diff --git a/src/include/openssl/cpu.h b/src/include/openssl/cpu.h
index 39e7264..dd95ddc 100644
--- a/src/include/openssl/cpu.h
+++ b/src/include/openssl/cpu.h
@@ -135,6 +135,10 @@
 // CRYPTO_has_broken_NEON returns one if the current CPU is known to have a
 // broken NEON unit. See https://crbug.com/341598.
 OPENSSL_EXPORT int CRYPTO_has_broken_NEON(void);
+
+// CRYPTO_needs_hwcap2_workaround returns one if the ARMv8 AArch32 AT_HWCAP2
+// workaround was needed. See https://crbug.com/boringssl/46.
+OPENSSL_EXPORT int CRYPTO_needs_hwcap2_workaround(void);
 #endif
 
 // CRYPTO_is_ARMv8_AES_capable returns true if the current CPU supports the
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index e917fb4..440c431 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -146,7 +146,6 @@
 
 #include <openssl/bio.h>
 #include <openssl/buf.h>
-#include <openssl/hmac.h>
 #include <openssl/lhash.h>
 #include <openssl/pem.h>
 #include <openssl/span.h>
@@ -590,6 +589,8 @@
 
 #define TLS1_3_DRAFT_VERSION 0x7f12
 #define TLS1_3_EXPERIMENT_VERSION 0x7e01
+#define TLS1_3_EXPERIMENT2_VERSION 0x7e02
+#define TLS1_3_EXPERIMENT3_VERSION 0x7e03
 #define TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION 0x7a12
 
 // SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to
@@ -3177,6 +3178,8 @@
   tls13_experiment = 1,
   tls13_record_type_experiment = 2,
   tls13_no_session_id_experiment = 3,
+  tls13_experiment2 = 4,
+  tls13_experiment3 = 5,
 };
 
 // SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
@@ -3977,22 +3980,6 @@
 
 DECLARE_STACK_OF(SSL_CUSTOM_EXTENSION)
 
-struct ssl_cipher_st {
-  // name is the OpenSSL name for the cipher.
-  const char *name;
-  // standard_name is the IETF name for the cipher.
-  const char *standard_name;
-  // id is the cipher suite value bitwise OR-d with 0x03000000.
-  uint32_t id;
-
-  // algorithm_* are internal fields. See ssl/internal.h for their values.
-  uint32_t algorithm_mkey;
-  uint32_t algorithm_auth;
-  uint32_t algorithm_enc;
-  uint32_t algorithm_mac;
-  uint32_t algorithm_prf;
-};
-
 #define SSL_MAX_SSL_SESSION_ID_LENGTH 32
 #define SSL_MAX_SID_CTX_LENGTH 32
 #define SSL_MAX_MASTER_KEY_LENGTH 48
diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc
index dbc8fa2..5009f04 100644
--- a/src/ssl/dtls_record.cc
+++ b/src/ssl/dtls_record.cc
@@ -192,14 +192,27 @@
       !CBS_get_u16(&cbs, &version) ||
       !CBS_copy_bytes(&cbs, sequence, 8) ||
       !CBS_get_u16_length_prefixed(&cbs, &body) ||
-      (ssl->s3->have_version && version != ssl->version) ||
-      (version >> 8) != DTLS1_VERSION_MAJOR ||
       CBS_len(&body) > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
     // The record header was incomplete or malformed. Drop the entire packet.
     *out_consumed = in_len;
     return ssl_open_record_discard;
   }
 
+  bool version_ok;
+  if (ssl->s3->aead_read_ctx->is_null_cipher()) {
+    // Only check the first byte. Enforcing beyond that can prevent decoding
+    // version negotiation failure alerts.
+    version_ok = (version >> 8) == DTLS1_VERSION_MAJOR;
+  } else {
+    version_ok = version == ssl->s3->aead_read_ctx->RecordVersion();
+  }
+
+  if (!version_ok) {
+    // The record header was incomplete or malformed. Drop the entire packet.
+    *out_consumed = in_len;
+    return ssl_open_record_discard;
+  }
+
   ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in,
                       DTLS1_RT_HEADER_LENGTH);
 
@@ -300,9 +313,9 @@
 
   out[0] = type;
 
-  uint16_t wire_version = ssl->s3->have_version ? ssl->version : DTLS1_VERSION;
-  out[1] = wire_version >> 8;
-  out[2] = wire_version & 0xff;
+  uint16_t record_version = ssl->s3->aead_write_ctx->RecordVersion();
+  out[1] = record_version >> 8;
+  out[2] = record_version & 0xff;
 
   out[3] = epoch >> 8;
   out[4] = epoch & 0xff;
@@ -310,7 +323,7 @@
 
   size_t ciphertext_len;
   if (!aead->Seal(out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len,
-                  max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version,
+                  max_out - DTLS1_RT_HEADER_LENGTH, type, record_version,
                   &out[3] /* seq */, in, in_len) ||
       !ssl_record_sequence_update(&seq[2], 6)) {
     return 0;
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 52d2e94..18dd58f 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -316,7 +316,7 @@
     // In TLS 1.3 experimental encodings, send a fake placeholder session ID
     // when we do not otherwise have one to send.
     if (hs->max_version >= TLS1_3_VERSION &&
-        ssl->tls13_variant == tls13_experiment &&
+        ssl_is_resumption_variant(ssl->tls13_variant) &&
         !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
       return 0;
     }
@@ -438,6 +438,12 @@
     return ssl_hs_error;
   }
 
+  // SSL 3.0 ClientHellos should use SSL 3.0 not TLS 1.0, for the record-layer
+  // version.
+  if (hs->max_version == SSL3_VERSION) {
+    ssl->s3->aead_write_ctx->SetVersionIfNullCipher(SSL3_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.
@@ -468,7 +474,7 @@
 
   // Initialize a random session ID for the experimental TLS 1.3 variant
   // requiring a session id.
-  if (ssl->tls13_variant == tls13_experiment) {
+  if (ssl_is_resumption_variant(ssl->tls13_variant)) {
     hs->session_id_len = sizeof(hs->session_id);
     if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
       return ssl_hs_error;
@@ -584,6 +590,7 @@
     // At this point, the connection's version is known and ssl->version is
     // fixed. Begin enforcing the record-layer version.
     ssl->s3->have_version = true;
+    ssl->s3->aead_write_ctx->SetVersionIfNullCipher(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);
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 10e618d..cd99ec9 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -276,6 +276,7 @@
   // At this point, the connection's version is known and |ssl->version| is
   // fixed. Begin enforcing the record-layer version.
   ssl->s3->have_version = true;
+  ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
 
   // Handle FALLBACK_SCSV.
   if (ssl_client_cipher_list_contains_cipher(client_hello,
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 0e44de8..9e67457 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -282,9 +282,46 @@
 // call this function before the version is determined.
 uint16_t ssl3_protocol_version(const SSL *ssl);
 
+// ssl_is_resumption_experiment returns whether the version corresponds to a
+// TLS 1.3 resumption experiment.
+bool ssl_is_resumption_experiment(uint16_t version);
+
+// ssl_is_resumption_variant returns whether the version corresponds to a
+// TLS 1.3 resumption experiment.
+bool ssl_is_resumption_variant(enum tls13_variant_t variant);
+
+// ssl_is_resumption_client_ccs_experiment returns whether the version
+// corresponds to a TLS 1.3 resumption experiment that sends a client CCS.
+bool ssl_is_resumption_client_ccs_experiment(uint16_t version);
+
+// ssl_is_resumption_record_version_experiment returns whether the version
+// corresponds to a TLS 1.3 resumption experiment that modifies the record
+// version.
+bool ssl_is_resumption_record_version_experiment(uint16_t version);
+
 
 // Cipher suites.
 
+}  // namespace bssl
+
+struct ssl_cipher_st {
+  // name is the OpenSSL name for the cipher.
+  const char *name;
+  // standard_name is the IETF name for the cipher.
+  const char *standard_name;
+  // id is the cipher suite value bitwise OR-d with 0x03000000.
+  uint32_t id;
+
+  // algorithm_* determine the cipher suite. See constants below for the values.
+  uint32_t algorithm_mkey;
+  uint32_t algorithm_auth;
+  uint32_t algorithm_enc;
+  uint32_t algorithm_mac;
+  uint32_t algorithm_prf;
+};
+
+namespace bssl {
+
 // Bits for |algorithm_mkey| (key exchange algorithm).
 #define SSL_kRSA 0x00000001u
 #define SSL_kECDHE 0x00000002u
@@ -466,7 +503,7 @@
 // encrypt an SSL connection.
 class SSLAEADContext {
  public:
-  SSLAEADContext(uint16_t version, const SSL_CIPHER *cipher);
+  SSLAEADContext(uint16_t version, bool is_dtls, const SSL_CIPHER *cipher);
   ~SSLAEADContext();
   static constexpr bool kAllowUniquePtr = true;
 
@@ -474,7 +511,7 @@
   SSLAEADContext &operator=(const SSLAEADContext &&) = delete;
 
   // CreateNullCipher creates an |SSLAEADContext| for the null cipher.
-  static UniquePtr<SSLAEADContext> CreateNullCipher();
+  static UniquePtr<SSLAEADContext> CreateNullCipher(bool is_dtls);
 
   // Create creates an |SSLAEADContext| using the supplied key material. It
   // returns nullptr on error. Only one of |Open| or |Seal| may be used with the
@@ -486,7 +523,20 @@
       const uint8_t *mac_key, size_t mac_key_len, const uint8_t *fixed_iv,
       size_t fixed_iv_len);
 
-  uint16_t version() const { return version_; }
+  // SetVersionIfNullCipher sets the version the SSLAEADContext for the null
+  // cipher, to make version-specific determinations in the record layer prior
+  // to a cipher being selected.
+  void SetVersionIfNullCipher(uint16_t version);
+
+  // ProtocolVersion returns the protocol version associated with this
+  // SSLAEADContext. It can only be called once |version_| has been set to a
+  // valid value.
+  uint16_t ProtocolVersion() const;
+
+  // RecordVersion returns the record version that should be used with this
+  // SSLAEADContext for record construction and crypto.
+  uint16_t RecordVersion() const;
+
   const SSL_CIPHER *cipher() const { return cipher_; }
 
   // is_null_cipher returns true if this is the null cipher.
@@ -509,7 +559,7 @@
   // success, it sets |*out| to the plaintext in |in| and returns true.
   // Otherwise, it returns false. The output will always be |ExplicitNonceLen|
   // bytes ahead of |in|.
-  bool Open(CBS *out, uint8_t type, uint16_t wire_version,
+  bool Open(CBS *out, uint8_t type, uint16_t record_version,
             const uint8_t seqnum[8], uint8_t *in, size_t in_len);
 
   // Seal encrypts and authenticates |in_len| bytes from |in| and writes the
@@ -517,7 +567,7 @@
   //
   // If |in| and |out| alias then |out| + |ExplicitNonceLen| must be == |in|.
   bool Seal(uint8_t *out, size_t *out_len, size_t max_out, uint8_t type,
-            uint16_t wire_version, const uint8_t seqnum[8], const uint8_t *in,
+            uint16_t record_version, const uint8_t seqnum[8], const uint8_t *in,
             size_t in_len);
 
   // SealScatter encrypts and authenticates |in_len| bytes from |in| and splits
@@ -536,17 +586,18 @@
   // If |in| and |out| alias then |out| must be == |in|. Other arguments may not
   // alias anything.
   bool SealScatter(uint8_t *out_prefix, uint8_t *out, uint8_t *out_suffix,
-                   uint8_t type, uint16_t wire_version, const uint8_t seqnum[8],
-                   const uint8_t *in, size_t in_len, const uint8_t *extra_in,
-                   size_t extra_in_len);
+                   uint8_t type, uint16_t record_version,
+                   const uint8_t seqnum[8], const uint8_t *in, size_t in_len,
+                   const uint8_t *extra_in, size_t extra_in_len);
 
   bool GetIV(const uint8_t **out_iv, size_t *out_iv_len) const;
 
  private:
   // GetAdditionalData writes the additional data into |out| and returns the
   // number of bytes written.
-  size_t GetAdditionalData(uint8_t out[13], uint8_t type, uint16_t wire_version,
-                           const uint8_t seqnum[8], size_t plaintext_len);
+  size_t GetAdditionalData(uint8_t out[13], uint8_t type,
+                           uint16_t record_version, const uint8_t seqnum[8],
+                           size_t plaintext_len);
 
   const SSL_CIPHER *cipher_;
   ScopedEVP_AEAD_CTX ctx_;
@@ -554,8 +605,10 @@
   // records.
   uint8_t fixed_nonce_[12];
   uint8_t fixed_nonce_len_ = 0, variable_nonce_len_ = 0;
-  // version_ is the protocol version that should be used with this AEAD.
+  // version_ is the wire version that should be used with this AEAD.
   uint16_t version_;
+  // is_dtls_ is whether DTLS is being used with this AEAD.
+  bool is_dtls_;
   // variable_nonce_included_in_record_ is true if the variable nonce
   // for a record is included as a prefix before the ciphertext.
   bool variable_nonce_included_in_record_ : 1;
@@ -1437,8 +1490,11 @@
 
 // ClientHello functions.
 
-int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
-                          const SSLMessage &msg);
+// ssl_client_hello_init parses |msg| as a ClientHello and writes the result to
+// |*out|. It returns one on success and zero on error. This function is
+// exported for unit tests.
+OPENSSL_EXPORT int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
+                                         const SSLMessage &msg);
 
 int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
                                    CBS *out, uint16_t extension_type);
@@ -1446,6 +1502,10 @@
 int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
                                            uint16_t id);
 
+// ssl_is_probably_java returns true if |client_hello| looks like a Java
+// ClientHello and false otherwise. This function is exported for tests.
+OPENSSL_EXPORT bool ssl_is_probably_java(const SSL_CLIENT_HELLO *client_hello);
+
 
 // GREASE.
 
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index dcf9559..3df8e1b 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -165,8 +165,10 @@
 namespace bssl {
 
 int ssl3_new(SSL *ssl) {
-  UniquePtr<SSLAEADContext> aead_read_ctx = SSLAEADContext::CreateNullCipher();
-  UniquePtr<SSLAEADContext> aead_write_ctx = SSLAEADContext::CreateNullCipher();
+  UniquePtr<SSLAEADContext> aead_read_ctx =
+      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
+  UniquePtr<SSLAEADContext> aead_write_ctx =
+      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
   if (!aead_read_ctx || !aead_write_ctx) {
     return 0;
   }
diff --git a/src/ssl/ssl_aead_ctx.cc b/src/ssl/ssl_aead_ctx.cc
index 69129af..d03a4a0 100644
--- a/src/ssl/ssl_aead_ctx.cc
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -33,10 +33,11 @@
 
 namespace bssl {
 
-SSLAEADContext::SSLAEADContext(uint16_t version_arg,
+SSLAEADContext::SSLAEADContext(uint16_t version_arg, bool is_dtls_arg,
                                const SSL_CIPHER *cipher_arg)
     : cipher_(cipher_arg),
       version_(version_arg),
+      is_dtls_(is_dtls_arg),
       variable_nonce_included_in_record_(false),
       random_variable_nonce_(false),
       omit_length_in_ad_(false),
@@ -48,8 +49,9 @@
 
 SSLAEADContext::~SSLAEADContext() {}
 
-UniquePtr<SSLAEADContext> SSLAEADContext::CreateNullCipher() {
-  return MakeUnique<SSLAEADContext>(0 /* version */, nullptr /* cipher */);
+UniquePtr<SSLAEADContext> SSLAEADContext::CreateNullCipher(bool is_dtls) {
+  return MakeUnique<SSLAEADContext>(0 /* version */, is_dtls,
+                                    nullptr /* cipher */);
 }
 
 UniquePtr<SSLAEADContext> SSLAEADContext::Create(
@@ -57,10 +59,13 @@
     const SSL_CIPHER *cipher, const uint8_t *enc_key, size_t enc_key_len,
     const uint8_t *mac_key, size_t mac_key_len, const uint8_t *fixed_iv,
     size_t fixed_iv_len) {
+
   const EVP_AEAD *aead;
+  uint16_t protocol_version;
   size_t expected_mac_key_len, expected_fixed_iv_len;
-  if (!ssl_cipher_get_evp_aead(&aead, &expected_mac_key_len,
-                               &expected_fixed_iv_len, cipher, version,
+  if (!ssl_protocol_version_from_wire(&protocol_version, version) ||
+      !ssl_cipher_get_evp_aead(&aead, &expected_mac_key_len,
+                               &expected_fixed_iv_len, cipher, protocol_version,
                                is_dtls) ||
       // Ensure the caller returned correct key sizes.
       expected_fixed_iv_len != fixed_iv_len ||
@@ -87,12 +92,14 @@
   }
 
   UniquePtr<SSLAEADContext> aead_ctx =
-      MakeUnique<SSLAEADContext>(version, cipher);
+      MakeUnique<SSLAEADContext>(version, is_dtls, cipher);
   if (!aead_ctx) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return nullptr;
   }
 
+  assert(aead_ctx->ProtocolVersion() == protocol_version);
+
   if (!EVP_AEAD_CTX_init_with_direction(
           aead_ctx->ctx_.get(), aead, enc_key, enc_key_len,
           EVP_AEAD_DEFAULT_TAG_LENGTH, direction)) {
@@ -125,7 +132,7 @@
 
     // The TLS 1.3 construction XORs the fixed nonce into the sequence number
     // and omits the additional data.
-    if (version >= TLS1_3_VERSION) {
+    if (protocol_version >= TLS1_3_VERSION) {
       aead_ctx->xor_fixed_nonce_ = true;
       aead_ctx->variable_nonce_len_ = 8;
       aead_ctx->variable_nonce_included_in_record_ = false;
@@ -133,16 +140,47 @@
       assert(fixed_iv_len >= aead_ctx->variable_nonce_len_);
     }
   } else {
-    assert(version < TLS1_3_VERSION);
+    assert(protocol_version < TLS1_3_VERSION);
     aead_ctx->variable_nonce_included_in_record_ = true;
     aead_ctx->random_variable_nonce_ = true;
     aead_ctx->omit_length_in_ad_ = true;
-    aead_ctx->omit_version_in_ad_ = (version == SSL3_VERSION);
+    aead_ctx->omit_version_in_ad_ = (protocol_version == SSL3_VERSION);
   }
 
   return aead_ctx;
 }
 
+void SSLAEADContext::SetVersionIfNullCipher(uint16_t version) {
+  if (is_null_cipher()) {
+    version_ = version;
+  }
+}
+
+uint16_t SSLAEADContext::ProtocolVersion() const {
+  uint16_t protocol_version;
+  if(!ssl_protocol_version_from_wire(&protocol_version, version_)) {
+    assert(false);
+    return 0;
+  }
+  return protocol_version;
+}
+
+uint16_t SSLAEADContext::RecordVersion() const {
+  if (version_ == 0) {
+    assert(is_null_cipher());
+    return is_dtls_ ? DTLS1_VERSION : TLS1_VERSION;
+  }
+
+  if (ProtocolVersion() <= TLS1_2_VERSION) {
+    return version_;
+  }
+
+  if (ssl_is_resumption_record_version_experiment(version_)) {
+    return TLS1_2_VERSION;
+  }
+  return TLS1_VERSION;
+}
+
 size_t SSLAEADContext::ExplicitNonceLen() const {
   if (!FUZZER_MODE && variable_nonce_included_in_record_) {
     return variable_nonce_len_;
@@ -168,7 +206,7 @@
 }
 
 size_t SSLAEADContext::GetAdditionalData(uint8_t out[13], uint8_t type,
-                                         uint16_t wire_version,
+                                         uint16_t record_version,
                                          const uint8_t seqnum[8],
                                          size_t plaintext_len) {
   if (omit_ad_) {
@@ -179,8 +217,8 @@
   size_t len = 8;
   out[len++] = type;
   if (!omit_version_in_ad_) {
-    out[len++] = static_cast<uint8_t>((wire_version >> 8));
-    out[len++] = static_cast<uint8_t>(wire_version);
+    out[len++] = static_cast<uint8_t>((record_version >> 8));
+    out[len++] = static_cast<uint8_t>(record_version);
   }
   if (!omit_length_in_ad_) {
     out[len++] = static_cast<uint8_t>((plaintext_len >> 8));
@@ -189,7 +227,7 @@
   return len;
 }
 
-bool SSLAEADContext::Open(CBS *out, uint8_t type, uint16_t wire_version,
+bool SSLAEADContext::Open(CBS *out, uint8_t type, uint16_t record_version,
                           const uint8_t seqnum[8], uint8_t *in, size_t in_len) {
   if (is_null_cipher() || FUZZER_MODE) {
     // Handle the initial NULL cipher.
@@ -211,7 +249,7 @@
   }
   uint8_t ad[13];
   size_t ad_len =
-      GetAdditionalData(ad, type, wire_version, seqnum, plaintext_len);
+      GetAdditionalData(ad, type, record_version, seqnum, plaintext_len);
 
   // Assemble the nonce.
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
@@ -262,9 +300,10 @@
 
 bool SSLAEADContext::SealScatter(uint8_t *out_prefix, uint8_t *out,
                                  uint8_t *out_suffix, uint8_t type,
-                                 uint16_t wire_version, const uint8_t seqnum[8],
-                                 const uint8_t *in, size_t in_len,
-                                 const uint8_t *extra_in, size_t extra_in_len) {
+                                 uint16_t record_version,
+                                 const uint8_t seqnum[8], const uint8_t *in,
+                                 size_t in_len, const uint8_t *extra_in,
+                                 size_t extra_in_len) {
   const size_t prefix_len = ExplicitNonceLen();
   size_t suffix_len;
   if (!SuffixLen(&suffix_len, in_len, extra_in_len)) {
@@ -286,7 +325,7 @@
   }
 
   uint8_t ad[13];
-  size_t ad_len = GetAdditionalData(ad, type, wire_version, seqnum, in_len);
+  size_t ad_len = GetAdditionalData(ad, type, record_version, seqnum, in_len);
 
   // Assemble the nonce.
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
@@ -343,7 +382,7 @@
 }
 
 bool SSLAEADContext::Seal(uint8_t *out, size_t *out_len, size_t max_out_len,
-                          uint8_t type, uint16_t wire_version,
+                          uint8_t type, uint16_t record_version,
                           const uint8_t seqnum[8], const uint8_t *in,
                           size_t in_len) {
   const size_t prefix_len = ExplicitNonceLen();
@@ -363,7 +402,7 @@
   }
 
   if (!SealScatter(out, out + prefix_len, out + prefix_len + in_len, type,
-                   wire_version, seqnum, in, in_len, 0, 0)) {
+                   record_version, seqnum, in, in_len, 0, 0)) {
     return false;
   }
   *out_len = prefix_len + in_len + suffix_len;
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index c21c282..e885324 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -142,6 +142,7 @@
 #include <utility>
 
 #include <openssl/err.h>
+#include <openssl/hmac.h>
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
@@ -721,6 +722,72 @@
   return ssl_hs_ok;
 }
 
+bool ssl_is_probably_java(const SSL_CLIENT_HELLO *client_hello) {
+  CBS extension, groups;
+  if (SSL_is_dtls(client_hello->ssl) ||
+      !ssl_client_hello_get_extension(client_hello, &extension,
+                                      TLSEXT_TYPE_supported_groups) ||
+      !CBS_get_u16_length_prefixed(&extension, &groups) ||
+      CBS_len(&extension) != 0) {
+    return false;
+  }
+
+  // Original Java curve list.
+  static const uint8_t kCurveList1[] = {
+      0x00, 0x17, 0x00, 0x01, 0x00, 0x03, 0x00, 0x13, 0x00, 0x15,
+      0x00, 0x06, 0x00, 0x07, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x18,
+      0x00, 0x0b, 0x00, 0x0c, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x0e,
+      0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x02, 0x00, 0x12,
+      0x00, 0x04, 0x00, 0x05, 0x00, 0x14, 0x00, 0x08, 0x00, 0x16};
+
+  // Newer Java curve list.
+  static const uint8_t kCurveList2[] = {
+      0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a,
+      0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x16};
+
+  // IcedTea curve list.
+  static const uint8_t kCurveList3[] = {0x00, 0x17, 0x00, 0x18, 0x00, 0x19};
+
+  auto groups_span = MakeConstSpan(CBS_data(&groups), CBS_len(&groups));
+  if (groups_span != kCurveList1 && groups_span != kCurveList2 &&
+      groups_span != kCurveList3) {
+    return false;
+  }
+
+  // Java has a very distinctive curve list, but IcedTea patches it to a more
+  // standard [P-256, P-384, P-521]. Additionally check the extension
+  // order. This may still flag other clients, but false positives only mean a
+  // loss of resumption. Any client new enough to support one of X25519,
+  // extended master secret, session tickets, or TLS 1.3 will be unaffected.
+  //
+  // Java sends different extensions depending on configuration and version, but
+  // those which are present are always in the same order. Check if the
+  // extensions are an ordered subset of |kJavaExtensions|.
+  static const uint16_t kJavaExtensions[] = {
+      TLSEXT_TYPE_supported_groups,
+      TLSEXT_TYPE_ec_point_formats,
+      TLSEXT_TYPE_signature_algorithms,
+      TLSEXT_TYPE_server_name,
+      17 /* status_request_v2 */,
+      TLSEXT_TYPE_status_request,
+      TLSEXT_TYPE_application_layer_protocol_negotiation,
+      TLSEXT_TYPE_renegotiate,
+  };
+  CBS extensions;
+  CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
+  for (uint16_t expected : kJavaExtensions) {
+    CBS extensions_copy = extensions, body;
+    uint16_t type;
+    // Peek at the next extension.
+    if (CBS_get_u16(&extensions_copy, &type) &&
+        CBS_get_u16_length_prefixed(&extensions_copy, &body) &&
+        type == expected) {
+      extensions = extensions_copy;
+    }
+  }
+  return CBS_len(&extensions) == 0;
+}
+
 enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
                                         UniquePtr<SSL_SESSION> *out_session,
                                         bool *out_tickets_supported,
@@ -728,8 +795,6 @@
                                         const SSL_CLIENT_HELLO *client_hello) {
   // This is used only by servers.
   assert(ssl->server);
-  UniquePtr<SSL_SESSION> session;
-  bool renew_ticket = false;
 
   // If tickets are disabled, always behave as if no tickets are present.
   const uint8_t *ticket = NULL;
@@ -739,6 +804,37 @@
       ssl->version > SSL3_VERSION &&
       SSL_early_callback_ctx_extension_get(
           client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
+
+  if (ssl_is_probably_java(client_hello)) {
+    // The Java client implementation of the 3SHAKE mitigation incorrectly
+    // rejects initial handshakes when all of the following are true:
+    //
+    // 1. The ClientHello offered a session.
+    // 2. The session was successfully resumed previously.
+    // 3. The server declines the session.
+    // 4. The server sends a certificate with a different (see below) SAN list
+    //    than in the previous session.
+    //
+    // (Note the 3SHAKE mitigation is to reject certificates changes on
+    // renegotiation, while Java's logic applies to initial handshakes as well.)
+    //
+    // The end result is long-lived Java clients break on certificate rotations
+    // where the SAN list changes too much. Older versions of Java break if the
+    // first DNS name of the two certificates is different. Newer ones will
+    // break if there is no intersection. The new logic mostly mitigates this,
+    // but this can still cause problems if switching to or from wildcards.
+    //
+    // Thus, fingerprint Java clients and decline all offered sessions. This
+    // avoids (2) while still introducing new sessions to clear any existing
+    // problematic sessions.
+    *out_session = nullptr;
+    *out_tickets_supported = tickets_supported;
+    *out_renew_ticket = false;
+    return ssl_hs_ok;
+  }
+
+  UniquePtr<SSL_SESSION> session;
+  bool renew_ticket = false;
   if (tickets_supported && ticket_len > 0) {
     switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
                                client_hello->session_id,
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 66f0304..f032b25 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -3816,6 +3816,179 @@
   EXPECT_EQ(SSL_R_NO_CIPHERS_AVAILABLE, ERR_GET_REASON(err));
 }
 
+TEST(SSLTest, IsProbablyJava) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
+  ASSERT_TRUE(ssl);
+
+  const struct {
+    const char *name;
+    std::vector<uint8_t> in;
+    bool is_probably_java;
+  } kTests[] = {
+      {"JDK 6 with IcedTea curve list",
+       {0x03, 0x01, 0x59, 0xb3, 0x10, 0xea, 0x17, 0xfe, 0x9e, 0x69, 0x7e, 0x79,
+        0xc7, 0x33, 0x10, 0x81, 0x73, 0x9e, 0xe7, 0xbf, 0x78, 0x4a, 0x33, 0x76,
+        0x12, 0x1f, 0xc5, 0x6d, 0x28, 0x8d, 0xd7, 0x60, 0xf0, 0x5e, 0x00, 0x00,
+        0x2c, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x35, 0xc0, 0x05, 0xc0, 0x0f, 0x00,
+        0x39, 0x00, 0x38, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x2f, 0xc0, 0x04, 0xc0,
+        0x0e, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x08, 0xc0, 0x12, 0x00, 0x0a, 0xc0,
+        0x03, 0xc0, 0x0d, 0x00, 0x16, 0x00, 0x13, 0x00, 0xff, 0x01, 0x00, 0x00,
+        0x12, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+        0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00},
+       true},
+      {"JDK 7 with IcedTea curve list",
+       {0x03, 0x03, 0x59, 0xb3, 0x10, 0xc8, 0x03, 0x7d, 0x10, 0x5a, 0x6b, 0x6e,
+        0x84, 0xa5, 0xbe, 0x6e, 0xe2, 0xd0, 0xb4, 0xb5, 0xcf, 0x6d, 0xa1, 0x58,
+        0xb5, 0xc0, 0x05, 0x63, 0xf6, 0x81, 0xda, 0xc2, 0xa0, 0xb0, 0x00, 0x00,
+        0x48, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x3d, 0xc0, 0x26, 0xc0, 0x2a, 0x00,
+        0x6b, 0x00, 0x6a, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x35, 0xc0, 0x05, 0xc0,
+        0x0f, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x3c, 0xc0,
+        0x25, 0xc0, 0x29, 0x00, 0x67, 0x00, 0x40, 0xc0, 0x09, 0xc0, 0x13, 0x00,
+        0x2f, 0xc0, 0x04, 0xc0, 0x0e, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x08, 0xc0,
+        0x12, 0x00, 0x0a, 0xc0, 0x03, 0xc0, 0x0d, 0x00, 0x16, 0x00, 0x13, 0x00,
+        0xff, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00,
+        0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
+        0x0d, 0x00, 0x18, 0x00, 0x16, 0x06, 0x03, 0x06, 0x01, 0x05, 0x03, 0x05,
+        0x01, 0x04, 0x03, 0x04, 0x01, 0x03, 0x03, 0x03, 0x01, 0x02, 0x03, 0x02,
+        0x01, 0x02, 0x02},
+       true},
+      {"JDK 8",
+       {0x03, 0x03, 0x59, 0xb3, 0x10, 0xfc, 0xc7, 0xcb, 0x36, 0x85, 0x8c, 0x00,
+        0xd6, 0xa7, 0x5b, 0xfb, 0x98, 0xbe, 0xf9, 0xa3, 0xa0, 0x01, 0xff, 0x35,
+        0xb9, 0x1a, 0xc1, 0x72, 0xf1, 0x51, 0x4b, 0x49, 0x96, 0x1a, 0x00, 0x00,
+        0x70, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x3d, 0xc0, 0x26, 0xc0, 0x2a, 0x00,
+        0x6b, 0x00, 0x6a, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x35, 0xc0, 0x05, 0xc0,
+        0x0f, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x3c, 0xc0,
+        0x25, 0xc0, 0x29, 0x00, 0x67, 0x00, 0x40, 0xc0, 0x09, 0xc0, 0x13, 0x00,
+        0x2f, 0xc0, 0x04, 0xc0, 0x0e, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x2c, 0xc0,
+        0x2b, 0xc0, 0x30, 0x00, 0x9d, 0xc0, 0x2e, 0xc0, 0x32, 0x00, 0x9f, 0x00,
+        0xa3, 0xc0, 0x2f, 0x00, 0x9c, 0xc0, 0x2d, 0xc0, 0x31, 0x00, 0x9e, 0x00,
+        0xa2, 0xc0, 0x08, 0xc0, 0x12, 0x00, 0x0a, 0xc0, 0x03, 0xc0, 0x0d, 0x00,
+        0x16, 0x00, 0x13, 0xc0, 0x07, 0xc0, 0x11, 0x00, 0x05, 0xc0, 0x02, 0xc0,
+        0x0c, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00, 0x5c, 0x00, 0x0a, 0x00,
+        0x34, 0x00, 0x32, 0x00, 0x17, 0x00, 0x01, 0x00, 0x03, 0x00, 0x13, 0x00,
+        0x15, 0x00, 0x06, 0x00, 0x07, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x18, 0x00,
+        0x0b, 0x00, 0x0c, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00,
+        0x10, 0x00, 0x11, 0x00, 0x02, 0x00, 0x12, 0x00, 0x04, 0x00, 0x05, 0x00,
+        0x14, 0x00, 0x08, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
+        0x0d, 0x00, 0x1a, 0x00, 0x18, 0x06, 0x03, 0x06, 0x01, 0x05, 0x03, 0x05,
+        0x01, 0x04, 0x03, 0x04, 0x01, 0x03, 0x03, 0x03, 0x01, 0x02, 0x03, 0x02,
+        0x01, 0x02, 0x02, 0x01, 0x01},
+       true},
+      {"JDK 9",
+       {0x03, 0x03, 0x0c, 0xe6, 0x06, 0xc6, 0x5d, 0x38, 0xb4, 0x5e, 0x3a, 0xd5,
+        0xb0, 0x5f, 0x5b, 0x84, 0x3b, 0xff, 0x86, 0x4f, 0xb0, 0x3f, 0xc1, 0xfd,
+        0x08, 0xf0, 0x97, 0xf3, 0x56, 0x44, 0x08, 0xe2, 0xdd, 0x2a, 0x00, 0x00,
+        0x64, 0xc0, 0x2c, 0xc0, 0x2b, 0xc0, 0x30, 0x00, 0x9d, 0xc0, 0x2e, 0xc0,
+        0x32, 0x00, 0x9f, 0x00, 0xa3, 0xc0, 0x2f, 0x00, 0x9c, 0xc0, 0x2d, 0xc0,
+        0x31, 0x00, 0x9e, 0x00, 0xa2, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x3d, 0xc0,
+        0x26, 0xc0, 0x2a, 0x00, 0x6b, 0x00, 0x6a, 0xc0, 0x0a, 0xc0, 0x14, 0x00,
+        0x35, 0xc0, 0x05, 0xc0, 0x0f, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x23, 0xc0,
+        0x27, 0x00, 0x3c, 0xc0, 0x25, 0xc0, 0x29, 0x00, 0x67, 0x00, 0x40, 0xc0,
+        0x09, 0xc0, 0x13, 0x00, 0x2f, 0xc0, 0x04, 0xc0, 0x0e, 0x00, 0x33, 0x00,
+        0x32, 0xc0, 0x08, 0xc0, 0x12, 0x00, 0x0a, 0xc0, 0x03, 0xc0, 0x0d, 0x00,
+        0x16, 0x00, 0x13, 0x00, 0xff, 0x01, 0x00, 0x00, 0x5d, 0x00, 0x0a, 0x00,
+        0x16, 0x00, 0x14, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x09, 0x00,
+        0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x16, 0x00,
+        0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, 0x06,
+        0x03, 0x06, 0x01, 0x05, 0x03, 0x05, 0x01, 0x04, 0x03, 0x04, 0x01, 0x04,
+        0x02, 0x03, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x03, 0x02, 0x01, 0x02,
+        0x02, 0x00, 0x11, 0x00, 0x10, 0x00, 0x0e, 0x02, 0x00, 0x04, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
+        0x05, 0x01, 0x00, 0x00, 0x00, 0x00},
+       true},
+      {"JDK 9 with SNI and ALPN enabled and no SCSV cipher",
+       {0x03, 0x03, 0x37, 0xa6, 0x4b, 0x58, 0x02, 0xd0, 0x77, 0xe4, 0x48, 0xaf,
+        0x90, 0x50, 0x45, 0xd5, 0x2f, 0xe7, 0x98, 0x5d, 0x54, 0x93, 0x85, 0x3a,
+        0xde, 0xb6, 0xaa, 0x47, 0xef, 0x7f, 0xb5, 0x52, 0xe6, 0xf8, 0x00, 0x00,
+        0x02, 0xc0, 0x24, 0x01, 0x00, 0x00, 0x87, 0x00, 0x0a, 0x00, 0x16, 0x00,
+        0x14, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00,
+        0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x0b, 0x00,
+        0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, 0x06, 0x03, 0x06,
+        0x01, 0x05, 0x03, 0x05, 0x01, 0x04, 0x03, 0x04, 0x01, 0x04, 0x02, 0x03,
+        0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x03, 0x02, 0x01, 0x02, 0x02, 0x00,
+        0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d,
+        0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x11, 0x00, 0x10, 0x00,
+        0x0e, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x10, 0x00, 0x0d, 0x00, 0x0b, 0x07, 0x68, 0x74, 0x74, 0x70, 0x31,
+        0x2e, 0x31, 0x02, 0x68, 0x32, 0xff, 0x01, 0x00, 0x01, 0x00},
+       true},
+      {"JDK 9 with EMS extension appended",
+       {0x03, 0x03, 0x0c, 0xe6, 0x06, 0xc6, 0x5d, 0x38, 0xb4, 0x5e, 0x3a, 0xd5,
+        0xb0, 0x5f, 0x5b, 0x84, 0x3b, 0xff, 0x86, 0x4f, 0xb0, 0x3f, 0xc1, 0xfd,
+        0x08, 0xf0, 0x97, 0xf3, 0x56, 0x44, 0x08, 0xe2, 0xdd, 0x2a, 0x00, 0x00,
+        0x64, 0xc0, 0x2c, 0xc0, 0x2b, 0xc0, 0x30, 0x00, 0x9d, 0xc0, 0x2e, 0xc0,
+        0x32, 0x00, 0x9f, 0x00, 0xa3, 0xc0, 0x2f, 0x00, 0x9c, 0xc0, 0x2d, 0xc0,
+        0x31, 0x00, 0x9e, 0x00, 0xa2, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x3d, 0xc0,
+        0x26, 0xc0, 0x2a, 0x00, 0x6b, 0x00, 0x6a, 0xc0, 0x0a, 0xc0, 0x14, 0x00,
+        0x35, 0xc0, 0x05, 0xc0, 0x0f, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x23, 0xc0,
+        0x27, 0x00, 0x3c, 0xc0, 0x25, 0xc0, 0x29, 0x00, 0x67, 0x00, 0x40, 0xc0,
+        0x09, 0xc0, 0x13, 0x00, 0x2f, 0xc0, 0x04, 0xc0, 0x0e, 0x00, 0x33, 0x00,
+        0x32, 0xc0, 0x08, 0xc0, 0x12, 0x00, 0x0a, 0xc0, 0x03, 0xc0, 0x0d, 0x00,
+        0x16, 0x00, 0x13, 0x00, 0xff, 0x01, 0x00, 0x00, 0x61, 0x00, 0x0a, 0x00,
+        0x16, 0x00, 0x14, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x09, 0x00,
+        0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x16, 0x00,
+        0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, 0x06,
+        0x03, 0x06, 0x01, 0x05, 0x03, 0x05, 0x01, 0x04, 0x03, 0x04, 0x01, 0x04,
+        0x02, 0x03, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x03, 0x02, 0x01, 0x02,
+        0x02, 0x00, 0x11, 0x00, 0x10, 0x00, 0x0e, 0x02, 0x00, 0x04, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
+        0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00},
+       false},
+      {"Chrome 61",
+       {0x03, 0x03, 0x33, 0x43, 0x8a, 0x6f, 0x76, 0xdd, 0x84, 0x3f, 0x8d, 0xaa,
+        0x43, 0xf1, 0x86, 0xee, 0xdd, 0x97, 0x96, 0x54, 0xf6, 0x17, 0x2c, 0xde,
+        0x69, 0xfe, 0x5e, 0x53, 0xaa, 0x47, 0xee, 0xad, 0xd7, 0x47, 0x00, 0x00,
+        0x1c, 0x3a, 0x3a, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc,
+        0xa9, 0xcc, 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00,
+        0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x77, 0x8a, 0x8a, 0x00,
+        0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0c,
+        0x00, 0x00, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+        0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14,
+        0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05,
+        0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00, 0x05,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00,
+        0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f,
+        0x31, 0x2e, 0x31, 0x75, 0x50, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01,
+        0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x4a, 0x4a, 0x00, 0x1d, 0x00,
+        0x17, 0x00, 0x18, 0x6a, 0x6a, 0x00, 0x01, 0x00},
+       false},
+      {"Firefox 55",
+       {0x03, 0x03, 0x06, 0x8e, 0xf8, 0xf7, 0x7a, 0xfd, 0xce, 0x45, 0xb0, 0x39,
+        0xbe, 0xa4, 0x55, 0x27, 0xe2, 0x80, 0xc4, 0x0a, 0xbd, 0xce, 0x56, 0x7a,
+        0xbc, 0x1f, 0x26, 0x2f, 0xfa, 0xb9, 0xa1, 0x7e, 0xe6, 0xde, 0x00, 0x00,
+        0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0,
+        0x30, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00,
+        0x39, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x6a, 0x00,
+        0x00, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, 0x63, 0x61,
+        0x6c, 0x68, 0x6f, 0x73, 0x74, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00,
+        0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17,
+        0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23,
+        0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08,
+        0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04,
+        0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04,
+        0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01},
+       false},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.name);
+
+    bssl::SSLMessage msg;
+    msg.is_v2_hello = false;
+    msg.type = SSL3_MT_CLIENT_HELLO;
+    CBS_init(&msg.body, t.in.data(), t.in.size());
+    CBS_init(&msg.raw, nullptr, 0);  // unused
+
+    SSL_CLIENT_HELLO client_hello;
+    ASSERT_TRUE(bssl::ssl_client_hello_init(ssl.get(), &client_hello, msg));
+    EXPECT_EQ(t.is_probably_java, bssl::ssl_is_probably_java(&client_hello));
+  }
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
diff --git a/src/ssl/ssl_versions.cc b/src/ssl/ssl_versions.cc
index 184eb44..560d0cf 100644
--- a/src/ssl/ssl_versions.cc
+++ b/src/ssl/ssl_versions.cc
@@ -36,6 +36,8 @@
 
     case TLS1_3_DRAFT_VERSION:
     case TLS1_3_EXPERIMENT_VERSION:
+    case TLS1_3_EXPERIMENT2_VERSION:
+    case TLS1_3_EXPERIMENT3_VERSION:
     case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION:
       *out = TLS1_3_VERSION;
       return 1;
@@ -58,6 +60,8 @@
 // decreasing preference.
 
 static const uint16_t kTLSVersions[] = {
+    TLS1_3_EXPERIMENT3_VERSION,
+    TLS1_3_EXPERIMENT2_VERSION,
     TLS1_3_EXPERIMENT_VERSION,
     TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION,
     TLS1_3_DRAFT_VERSION,
@@ -103,6 +107,8 @@
   // map it to some representative TLS 1.3 draft version.
   if (version == TLS1_3_DRAFT_VERSION ||
       version == TLS1_3_EXPERIMENT_VERSION ||
+      version == TLS1_3_EXPERIMENT2_VERSION ||
+      version == TLS1_3_EXPERIMENT3_VERSION ||
       version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
     return 0;
@@ -229,6 +235,8 @@
     // Report TLS 1.3 draft version as TLS 1.3 in the public API.
     case TLS1_3_DRAFT_VERSION:
     case TLS1_3_EXPERIMENT_VERSION:
+    case TLS1_3_EXPERIMENT2_VERSION:
+    case TLS1_3_EXPERIMENT3_VERSION:
     case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION:
       return "TLSv1.3";
 
@@ -275,6 +283,8 @@
   if (ssl->server) {
     if (ssl->tls13_variant == tls13_default &&
         (version == TLS1_3_EXPERIMENT_VERSION ||
+         version == TLS1_3_EXPERIMENT2_VERSION ||
+         version == TLS1_3_EXPERIMENT3_VERSION ||
          version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION)) {
       return 0;
     }
@@ -282,6 +292,10 @@
     if ((ssl->tls13_variant != tls13_experiment &&
          ssl->tls13_variant != tls13_no_session_id_experiment &&
          version == TLS1_3_EXPERIMENT_VERSION) ||
+        (ssl->tls13_variant != tls13_experiment2 &&
+         version == TLS1_3_EXPERIMENT2_VERSION) ||
+        (ssl->tls13_variant != tls13_experiment3 &&
+         version == TLS1_3_EXPERIMENT3_VERSION) ||
         (ssl->tls13_variant != tls13_record_type_experiment &&
          version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) ||
         (ssl->tls13_variant != tls13_default &&
@@ -341,6 +355,27 @@
   return 0;
 }
 
+bool ssl_is_resumption_experiment(uint16_t version) {
+  return version == TLS1_3_EXPERIMENT_VERSION ||
+         version == TLS1_3_EXPERIMENT2_VERSION ||
+         version == TLS1_3_EXPERIMENT3_VERSION;
+}
+
+bool ssl_is_resumption_variant(enum tls13_variant_t variant) {
+  return variant == tls13_experiment || variant == tls13_experiment2 ||
+         variant == tls13_experiment3;
+}
+
+bool ssl_is_resumption_client_ccs_experiment(uint16_t version) {
+  return version == TLS1_3_EXPERIMENT_VERSION ||
+         version == TLS1_3_EXPERIMENT2_VERSION;
+}
+
+bool ssl_is_resumption_record_version_experiment(uint16_t version) {
+  return version == TLS1_3_EXPERIMENT2_VERSION ||
+         version == TLS1_3_EXPERIMENT3_VERSION;
+}
+
 }  // namespace bssl
 
 using namespace bssl;
@@ -364,7 +399,10 @@
 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 || ret == TLS1_3_EXPERIMENT_VERSION ||
+  if (ret == TLS1_3_DRAFT_VERSION ||
+      ret == TLS1_3_EXPERIMENT_VERSION ||
+      ret == TLS1_3_EXPERIMENT2_VERSION ||
+      ret == TLS1_3_EXPERIMENT3_VERSION ||
       ret == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) {
     return TLS1_3_VERSION;
   }
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index f917ed7..0283c6e 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -422,7 +422,7 @@
   }
 
   UniquePtr<SSLAEADContext> aead_ctx = SSLAEADContext::Create(
-      is_read ? evp_aead_open : evp_aead_seal, ssl3_protocol_version(ssl),
+      is_read ? evp_aead_open : evp_aead_seal, ssl->version,
       SSL_is_dtls(ssl), hs->new_cipher, key, key_len, mac_secret,
       mac_secret_len, iv, iv_len);
   if (!aead_ctx) {
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index d5b017f..19edb7f 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -35,6 +35,8 @@
 const (
 	tls13DraftVersion                = 0x7f12
 	tls13ExperimentVersion           = 0x7e01
+	tls13Experiment2Version          = 0x7e02
+	tls13Experiment3Version          = 0x7e03
 	tls13RecordTypeExperimentVersion = 0x7a12
 )
 
@@ -43,10 +45,14 @@
 	TLS13Experiment            = 1
 	TLS13RecordTypeExperiment  = 2
 	TLS13NoSessionIDExperiment = 3
+	TLS13Experiment2           = 4
+	TLS13Experiment3           = 5
 )
 
 var allTLSWireVersions = []uint16{
 	tls13DraftVersion,
+	tls13Experiment3Version,
+	tls13Experiment2Version,
 	tls13ExperimentVersion,
 	tls13RecordTypeExperimentVersion,
 	VersionTLS12,
@@ -276,6 +282,7 @@
 	sessionId            []uint8             // Session ID supplied by the server. nil if the session has a ticket.
 	sessionTicket        []uint8             // Encrypted ticket used for session resumption with server
 	vers                 uint16              // SSL/TLS version negotiated for the session
+	wireVersion          uint16              // Wire SSL/TLS version negotiated for the session
 	cipherSuite          uint16              // Ciphersuite negotiated for the session
 	masterSecret         []byte              // MasterSecret generated by client on a full handshake
 	handshakeHash        []byte              // Handshake hash for Channel ID purposes.
@@ -1439,6 +1446,11 @@
 	// PadClientHello, if non-zero, pads the ClientHello to a multiple of
 	// that many bytes.
 	PadClientHello int
+
+	// SendOnlyECExtensions omits all extensions except supported_groups and
+	// ec_point_formats, in order to trigger the Java ClientHello
+	// fingerprint.
+	SendOnlyECExtensions bool
 }
 
 func (c *Config) serverInit() {
@@ -1539,11 +1551,45 @@
 	return defaultCurves
 }
 
+func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) {
+	if isDTLS {
+		switch vers {
+		case VersionDTLS12:
+			return VersionTLS12, true
+		case VersionDTLS10:
+			return VersionTLS10, true
+		}
+	} else {
+		switch vers {
+		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
+			return vers, true
+		case tls13DraftVersion, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version, tls13RecordTypeExperimentVersion:
+			return VersionTLS13, true
+		}
+	}
+
+	return 0, false
+}
+
+func isResumptionExperiment(vers uint16) bool {
+	return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Experiment3Version
+}
+
+func isResumptionClientCCSExperiment(vers uint16) bool {
+	return vers == tls13ExperimentVersion || vers == tls13Experiment2Version
+}
+
+func isResumptionRecordVersionExperiment(vers uint16) bool {
+	return vers == tls13Experiment2Version || vers == tls13Experiment3Version
+}
+
 // 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) {
 	if (c.TLS13Variant != TLS13Experiment && c.TLS13Variant != TLS13NoSessionIDExperiment && wireVers == tls13ExperimentVersion) ||
+		(c.TLS13Variant != TLS13Experiment2 && wireVers == tls13Experiment2Version) ||
+		(c.TLS13Variant != TLS13Experiment3 && wireVers == tls13Experiment3Version) ||
 		(c.TLS13Variant != TLS13RecordTypeExperiment && wireVers == tls13RecordTypeExperimentVersion) ||
 		(c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) {
 		return 0, false
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 2332e6b..25123b1 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -151,14 +151,15 @@
 type halfConn struct {
 	sync.Mutex
 
-	err     error  // first permanent error
-	version uint16 // protocol version
-	isDTLS  bool
-	cipher  interface{} // cipher algorithm
-	mac     macFunction
-	seq     [8]byte // 64-bit sequence number
-	outSeq  [8]byte // Mapped sequence number
-	bfree   *block  // list of free blocks
+	err         error  // first permanent error
+	version     uint16 // protocol version
+	wireVersion uint16 // wire version
+	isDTLS      bool
+	cipher      interface{} // cipher algorithm
+	mac         macFunction
+	seq         [8]byte // 64-bit sequence number
+	outSeq      [8]byte // Mapped sequence number
+	bfree       *block  // list of free blocks
 
 	nextCipher interface{} // next encryption state
 	nextMac    macFunction // next MAC algorithm
@@ -188,7 +189,12 @@
 // prepareCipherSpec sets the encryption and MAC states
 // that a subsequent changeCipherSpec will use.
 func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
-	hc.version = version
+	hc.wireVersion = version
+	protocolVersion, ok := wireToVersion(version, hc.isDTLS)
+	if !ok {
+		panic("TLS: unknown version")
+	}
+	hc.version = protocolVersion
 	hc.nextCipher = cipher
 	hc.nextMac = mac
 }
@@ -215,7 +221,12 @@
 
 // useTrafficSecret sets the current cipher state for TLS 1.3.
 func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) {
-	hc.version = version
+	hc.wireVersion = version
+	protocolVersion, ok := wireToVersion(version, hc.isDTLS)
+	if !ok {
+		panic("TLS: unknown version")
+	}
+	hc.version = protocolVersion
 	hc.cipher = deriveTrafficAEAD(version, suite, secret, side)
 	if hc.config.Bugs.NullAllCiphers {
 		hc.cipher = nullCipher{}
@@ -237,7 +248,7 @@
 	if c.isClient == isOutgoing {
 		side = clientWrite
 	}
-	hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
+	hc.useTrafficSecret(hc.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
 }
 
 // incSeq increments the sequence number.
@@ -781,6 +792,9 @@
 			if c.vers >= VersionTLS13 {
 				expect = VersionTLS10
 			}
+			if isResumptionRecordVersionExperiment(c.wireVersion) {
+				expect = VersionTLS12
+			}
 		} else {
 			expect = c.config.Bugs.ExpectInitialRecordVersion
 		}
@@ -929,7 +943,7 @@
 			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 			break
 		}
-		if c.wireVersion != tls13ExperimentVersion {
+		if !isResumptionExperiment(c.wireVersion) {
 			err := c.in.changeCipherSpec(c.config)
 			if err != nil {
 				c.in.setErrorLocked(c.sendAlert(err.(alert)))
@@ -1125,6 +1139,11 @@
 			// layer to {3, 1}.
 			vers = VersionTLS10
 		}
+		if isResumptionRecordVersionExperiment(c.wireVersion) || isResumptionRecordVersionExperiment(c.out.wireVersion) {
+			vers = VersionTLS12
+		} else {
+		}
+
 		if c.config.Bugs.SendRecordVersion != 0 {
 			vers = c.config.Bugs.SendRecordVersion
 		}
@@ -1156,7 +1175,7 @@
 	}
 	c.out.freeBlock(b)
 
-	if typ == recordTypeChangeCipherSpec && c.wireVersion != tls13ExperimentVersion {
+	if typ == recordTypeChangeCipherSpec && !isResumptionExperiment(c.wireVersion) {
 		err = c.out.changeCipherSpec(c.config)
 		if err != nil {
 			return n, c.sendAlertLocked(alertLevelError, err.(alert))
@@ -1458,6 +1477,7 @@
 	session := &ClientSessionState{
 		sessionTicket:      newSessionTicket.ticket,
 		vers:               c.vers,
+		wireVersion:        c.wireVersion,
 		cipherSuite:        cipherSuite.id,
 		masterSecret:       c.resumptionSecret,
 		serverCertificates: c.peerCertificates,
@@ -1881,6 +1901,10 @@
 	payload[0] = byte(recordTypeApplicationData)
 	payload[1] = 3
 	payload[2] = 1
+	if c.config.TLS13Variant == TLS13Experiment2 || c.config.TLS13Variant == TLS13Experiment3 {
+		payload[1] = 3
+		payload[2] = 3
+	}
 	payload[3] = byte(len >> 8)
 	payload[4] = byte(len)
 	_, err := c.conn.Write(payload)
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index 619710c..bd111e0 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -23,26 +23,6 @@
 	"net"
 )
 
-func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) {
-	if isDTLS {
-		switch vers {
-		case VersionDTLS12:
-			return VersionTLS12, true
-		case VersionDTLS10:
-			return VersionTLS10, true
-		}
-	} else {
-		switch vers {
-		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
-			return vers, true
-		case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion:
-			return VersionTLS13, true
-		}
-	}
-
-	return 0, false
-}
-
 func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
 	recordHeaderLen := dtlsRecordHeaderLen
 
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 9fa49b5..f2ecae0 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -44,7 +44,7 @@
     "*-EarlyData-ALPNOmitted1-Server": "Trial decryption does not work with the NULL cipher.",
     "*-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
     "*-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
-    "*-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
+    "EarlyData-Reject-Client-*": "Trial decryption does not work with the NULL cipher.",
     "*-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 83f2d7d..a04ffd0 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -97,6 +97,7 @@
 		pskBinderFirst:          c.config.Bugs.PSKBinderFirst,
 		omitExtensions:          c.config.Bugs.OmitExtensions,
 		emptyExtensions:         c.config.Bugs.EmptyExtensions,
+		sendOnlyECExtensions:    c.config.Bugs.SendOnlyECExtensions,
 	}
 
 	if maxVersion >= VersionTLS13 {
@@ -411,7 +412,7 @@
 		finishedHash.addEntropy(session.masterSecret)
 		finishedHash.Write(helloBytes)
 		earlyTrafficSecret := finishedHash.deriveSecret(earlyTrafficLabel)
-		c.out.useTrafficSecret(session.vers, pskCipherSuite, earlyTrafficSecret, clientWrite)
+		c.out.useTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret, clientWrite)
 		for _, earlyData := range c.config.Bugs.SendEarlyData {
 			if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil {
 				return err
@@ -690,7 +691,7 @@
 func (hs *clientHandshakeState) doTLS13Handshake() error {
 	c := hs.c
 
-	if c.wireVersion == tls13ExperimentVersion && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+	if isResumptionExperiment(c.wireVersion) && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
 		return errors.New("tls: session IDs did not match.")
 	}
 
@@ -744,7 +745,7 @@
 		hs.finishedHash.addEntropy(zeroSecret)
 	}
 
-	if c.wireVersion == tls13ExperimentVersion {
+	if isResumptionExperiment(c.wireVersion) {
 		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
 			return err
 		}
@@ -754,7 +755,7 @@
 	// traffic key.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -888,7 +889,7 @@
 
 	// Switch to application data keys on read. In particular, any alerts
 	// from the client certificate are read over these keys.
-	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
+	c.in.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
 
 	// If we're expecting 0.5-RTT messages from the server, read them
 	// now.
@@ -930,11 +931,11 @@
 		c.sendAlert(alertEndOfEarlyData)
 	}
 
-	if c.wireVersion == tls13ExperimentVersion {
+	if isResumptionClientCCSExperiment(c.wireVersion) {
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
-	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -1020,7 +1021,7 @@
 	c.flushHandshake()
 
 	// Switch to application data keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
+	c.out.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
 
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
 	return nil
@@ -1287,8 +1288,8 @@
 		serverCipher = hs.suite.aead(c.vers, serverKey, serverIV)
 	}
 
-	c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
-	c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+	c.in.prepareCipherSpec(c.wireVersion, serverCipher, serverHash)
+	c.out.prepareCipherSpec(c.wireVersion, clientCipher, clientHash)
 	return nil
 }
 
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index d65119f..2e6d286 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -176,6 +176,7 @@
 	omitExtensions          bool
 	emptyExtensions         bool
 	pad                     int
+	sendOnlyECExtensions    bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -224,7 +225,8 @@
 		m.pskBinderFirst == m1.pskBinderFirst &&
 		m.omitExtensions == m1.omitExtensions &&
 		m.emptyExtensions == m1.emptyExtensions &&
-		m.pad == m1.pad
+		m.pad == m1.pad &&
+		m.sendOnlyECExtensions == m1.sendOnlyECExtensions
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -312,22 +314,6 @@
 		certificateStatusRequest.addU16(0) // ResponderID length
 		certificateStatusRequest.addU16(0) // Extensions length
 	}
-	if len(m.supportedCurves) > 0 {
-		// http://tools.ietf.org/html/rfc4492#section-5.1.1
-		extensions.addU16(extensionSupportedCurves)
-		supportedCurvesList := extensions.addU16LengthPrefixed()
-		supportedCurves := supportedCurvesList.addU16LengthPrefixed()
-		for _, curve := range m.supportedCurves {
-			supportedCurves.addU16(uint16(curve))
-		}
-	}
-	if len(m.supportedPoints) > 0 {
-		// http://tools.ietf.org/html/rfc4492#section-5.1.2
-		extensions.addU16(extensionSupportedPoints)
-		supportedPointsList := extensions.addU16LengthPrefixed()
-		supportedPoints := supportedPointsList.addU8LengthPrefixed()
-		supportedPoints.addBytes(m.supportedPoints)
-	}
 	if m.hasKeyShares {
 		extensions.addU16(extensionKeyShare)
 		keyShareList := extensions.addU16LengthPrefixed()
@@ -440,6 +426,30 @@
 		customExt := extensions.addU16LengthPrefixed()
 		customExt.addBytes([]byte(m.customExtension))
 	}
+
+	// Discard all extensions but the curve-related ones to trigger the Java
+	// fingerprinter.
+	if m.sendOnlyECExtensions {
+		hello.discardChild()
+		extensions = hello.addU16LengthPrefixed()
+	}
+	if len(m.supportedCurves) > 0 {
+		// http://tools.ietf.org/html/rfc4492#section-5.1.1
+		extensions.addU16(extensionSupportedCurves)
+		supportedCurvesList := extensions.addU16LengthPrefixed()
+		supportedCurves := supportedCurvesList.addU16LengthPrefixed()
+		for _, curve := range m.supportedCurves {
+			supportedCurves.addU16(uint16(curve))
+		}
+	}
+	if len(m.supportedPoints) > 0 {
+		// http://tools.ietf.org/html/rfc4492#section-5.1.2
+		extensions.addU16(extensionSupportedPoints)
+		supportedPointsList := extensions.addU16LengthPrefixed()
+		supportedPoints := supportedPointsList.addU8LengthPrefixed()
+		supportedPoints.addBytes(m.supportedPoints)
+	}
+
 	// The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6).
 	if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
 		extensions.addU16(extensionPreSharedKey)
@@ -456,6 +466,8 @@
 		}
 	}
 
+	// This must be swapped with PSK (with some length computation) if we
+	// ever need to support PadClientHello and TLS 1.3.
 	if m.pad != 0 && hello.len()%m.pad != 0 {
 		extensions.addU16(extensionPadding)
 		padding := extensions.addU16LengthPrefixed()
@@ -881,19 +893,19 @@
 	}
 	if m.versOverride != 0 {
 		hello.addU16(m.versOverride)
-	} else if m.vers == tls13ExperimentVersion {
+	} else if isResumptionExperiment(m.vers) {
 		hello.addU16(VersionTLS12)
 	} else {
 		hello.addU16(m.vers)
 	}
 
 	hello.addBytes(m.random)
-	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
+	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
 		sessionId := hello.addU8LengthPrefixed()
 		sessionId.addBytes(m.sessionId)
 	}
 	hello.addU16(m.cipherSuite)
-	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
+	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
 		hello.addU8(m.compressionMethod)
 	}
 
@@ -912,7 +924,7 @@
 			extensions.addU16(2) // Length
 			extensions.addU16(m.pskIdentity)
 		}
-		if m.vers == tls13ExperimentVersion || m.supportedVersOverride != 0 {
+		if isResumptionExperiment(m.vers) || m.supportedVersOverride != 0 {
 			extensions.addU16(extensionSupportedVersions)
 			extensions.addU16(2) // Length
 			if m.supportedVersOverride != 0 {
@@ -966,7 +978,7 @@
 	}
 	m.random = data[6:38]
 	data = data[38:]
-	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
+	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
 		sessionIdLen := int(data[0])
 		if sessionIdLen > 32 || len(data) < 1+sessionIdLen {
 			return false
@@ -979,7 +991,7 @@
 	}
 	m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
 	data = data[2:]
-	if vers < VersionTLS13 || m.vers == tls13ExperimentVersion {
+	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
 		if len(data) < 1 {
 			return false
 		}
@@ -1068,7 +1080,7 @@
 				m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
 				m.hasPSKIdentity = true
 			case extensionSupportedVersions:
-				if m.vers != tls13ExperimentVersion {
+				if !isResumptionExperiment(m.vers) {
 					return false
 				}
 			default:
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 4a056b9..0a67a80 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -281,7 +281,7 @@
 	}
 
 	if config.Bugs.ExpectNoTLS12Session {
-		if len(hs.clientHello.sessionId) > 0 && c.wireVersion != tls13ExperimentVersion {
+		if len(hs.clientHello.sessionId) > 0 && !isResumptionExperiment(c.wireVersion) {
 			return fmt.Errorf("tls: client offered an unexpected session ID")
 		}
 		if len(hs.clientHello.sessionTicket) > 0 {
@@ -668,7 +668,7 @@
 		}
 		if encryptedExtensions.extensions.hasEarlyData {
 			earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyTrafficLabel)
-			c.in.useTrafficSecret(c.vers, hs.suite, earlyTrafficSecret, clientWrite)
+			c.in.useTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret, clientWrite)
 
 			for _, expectedMsg := range config.Bugs.ExpectEarlyData {
 				if err := c.readRecord(recordTypeApplicationData); err != nil {
@@ -763,13 +763,13 @@
 	}
 	c.flushHandshake()
 
-	if c.wireVersion == tls13ExperimentVersion {
+	if isResumptionExperiment(c.wireVersion) {
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
 	// Switch to handshake traffic keys.
 	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel)
-	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
+	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret, serverWrite)
 	// Derive handshake traffic read key, but don't switch yet.
 	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel)
 
@@ -910,7 +910,7 @@
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
+	c.out.useTrafficSecret(c.wireVersion, hs.suite, serverTrafficSecret, serverWrite)
 
 	// Send 0.5-RTT messages.
 	for _, halfRTTMsg := range config.Bugs.SendHalfRTTData {
@@ -929,14 +929,14 @@
 		}
 	}
 
-	if c.wireVersion == tls13ExperimentVersion && !c.skipEarlyData {
+	if isResumptionClientCCSExperiment(c.wireVersion) && !c.skipEarlyData {
 		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
 			return err
 		}
 	}
 
 	// Switch input stream to handshake traffic keys.
-	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
+	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientHandshakeTrafficSecret, clientWrite)
 
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
@@ -1040,7 +1040,7 @@
 	hs.writeClientHash(clientFinished.marshal())
 
 	// Switch to application data keys on read.
-	c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
+	c.in.useTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret, clientWrite)
 
 	c.cipherSuite = hs.suite
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
@@ -1697,8 +1697,8 @@
 		serverCipher = hs.suite.aead(c.vers, serverKey, serverIV)
 	}
 
-	c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
-	c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+	c.in.prepareCipherSpec(c.wireVersion, clientCipher, clientHash)
+	c.out.prepareCipherSpec(c.wireVersion, serverCipher, serverHash)
 
 	return nil
 }
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 2ffe795..ee72c2e 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1303,6 +1303,20 @@
 		tls13Variant: TLS13Experiment,
 	},
 	{
+		name:         "TLS13Experiment2",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13Experiment2Version,
+		tls13Variant: TLS13Experiment2,
+	},
+	{
+		name:         "TLS13Experiment3",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13Experiment3Version,
+		tls13Variant: TLS13Experiment3,
+	},
+	{
 		name:         "TLS13RecordTypeExperiment",
 		version:      VersionTLS13,
 		excludeFlag:  "-no-tls13",
@@ -2783,6 +2797,25 @@
 			shouldFail:         true,
 			expectedLocalError: "local error: record overflow",
 		},
+		{
+			// Test that Java-like ClientHellos are provided session
+			// IDs but resumption is always declined. This is to
+			// workaround a bug that causes connection failures when
+			// certificates rotate.
+			testType: serverTest,
+			name:     "JavaWorkaround",
+			config: Config{
+				MaxVersion:             VersionTLS12,
+				CurvePreferences:       []CurveID{CurveP256, CurveP384, CurveP521},
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					SendOnlyECExtensions: true,
+				},
+			},
+			flags:                []string{"-expect-session-id"},
+			resumeSession:        true,
+			expectResumeRejected: true,
+		},
 	}
 	testCases = append(testCases, basicTests...)
 
@@ -4079,85 +4112,6 @@
 
 		tests = append(tests, testCase{
 			testType: clientTest,
-			name:     "TLS13-EarlyData-Client",
-			config: Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				MaxEarlyDataSize: 16384,
-			},
-			resumeConfig: &Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				MaxEarlyDataSize: 16384,
-				Bugs: ProtocolBugs{
-					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
-				},
-			},
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-early-data-info",
-				"-expect-accept-early-data",
-				"-on-resume-shim-writes-first",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: clientTest,
-			name:     "TLS13Experiment-EarlyData-Client",
-			config: Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				MaxEarlyDataSize: 16384,
-			},
-			resumeConfig: &Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				MaxEarlyDataSize: 16384,
-				Bugs: ProtocolBugs{
-					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
-				},
-			},
-			tls13Variant:  TLS13Experiment,
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-early-data-info",
-				"-expect-accept-early-data",
-				"-on-resume-shim-writes-first",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: clientTest,
-			name:     "TLS13RecordTypeExperiment-EarlyData-Client",
-			config: Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				TLS13Variant:     TLS13RecordTypeExperiment,
-				MaxEarlyDataSize: 16384,
-			},
-			resumeConfig: &Config{
-				MaxVersion:       VersionTLS13,
-				MinVersion:       VersionTLS13,
-				TLS13Variant:     TLS13RecordTypeExperiment,
-				MaxEarlyDataSize: 16384,
-				Bugs: ProtocolBugs{
-					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
-				},
-			},
-			tls13Variant:  TLS13RecordTypeExperiment,
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-early-data-info",
-				"-expect-accept-early-data",
-				"-on-resume-shim-writes-first",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: clientTest,
 			name:     "TLS13-EarlyData-TooMuchData-Client",
 			config: Config{
 				MaxVersion:       VersionTLS13,
@@ -4244,68 +4198,6 @@
 
 		tests = append(tests, testCase{
 			testType: serverTest,
-			name:     "TLS13-EarlyData-Server",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				MinVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-					ExpectEarlyDataAccepted: true,
-					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
-				},
-			},
-			messageCount:  2,
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-accept-early-data",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: serverTest,
-			name:     "TLS13Experiment-EarlyData-Server",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				MinVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-					ExpectEarlyDataAccepted: true,
-					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
-				},
-			},
-			tls13Variant:  TLS13Experiment,
-			messageCount:  2,
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-accept-early-data",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: serverTest,
-			name:     "TLS13RecordTypeExperiment-EarlyData-Server",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				MinVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-					ExpectEarlyDataAccepted: true,
-					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
-				},
-			},
-			tls13Variant:  TLS13RecordTypeExperiment,
-			messageCount:  2,
-			resumeSession: true,
-			flags: []string{
-				"-enable-early-data",
-				"-expect-accept-early-data",
-			},
-		})
-
-		tests = append(tests, testCase{
-			testType: serverTest,
 			name:     "TLS13-MaxEarlyData-Server",
 			config: Config{
 				MaxVersion: VersionTLS13,
@@ -5163,6 +5055,9 @@
 				serverVers := expectedServerVersion
 				if expectedServerVersion >= VersionTLS13 {
 					serverVers = VersionTLS10
+					if runnerVers.tls13Variant == TLS13Experiment2 || runnerVers.tls13Variant == TLS13Experiment3 {
+						serverVers = VersionTLS12
+					}
 				}
 				serverVers = recordVersionToWire(serverVers, protocol)
 
@@ -10815,40 +10710,170 @@
 		expectedError: ":DUPLICATE_KEY_SHARE:",
 	})
 
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-			},
-		},
-	})
+	for _, version := range allVersions(tls) {
+		if version.version != VersionTLS13 {
+			continue
+		}
+		name := version.name
+		variant := version.tls13Variant
 
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-TLS13Experiment",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+				},
 			},
-		},
-		tls13Variant: TLS13Experiment,
-	})
+			tls13Variant: variant,
+		})
 
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-TLS13RecordTypeExperiment",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
+		// Test that enabling a TLS 1.3 variant does not interfere with
+		// TLS 1.2 session ID resumption.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "ResumeTLS12SessionID-" + name,
+			config: Config{
+				MaxVersion:             VersionTLS12,
+				SessionTicketsDisabled: true,
 			},
-		},
-		tls13Variant: TLS13RecordTypeExperiment,
-	})
+			tls13Variant:  variant,
+			resumeSession: true,
+		})
+
+		// Test that the server correctly echoes back session IDs of
+		// various lengths.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EmptySessionID-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendClientHelloSessionID: []byte{},
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ShortSessionID-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendClientHelloSessionID: make([]byte, 16),
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "FullSessionID-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendClientHelloSessionID: make([]byte, 32),
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		hasSessionID := false
+		hasEmptySessionID := false
+		if variant == TLS13NoSessionIDExperiment {
+			hasEmptySessionID = true
+		} else if variant != TLS13Default && variant != TLS13RecordTypeExperiment {
+			hasSessionID = true
+		}
+
+		// Test that the client sends a fake session ID in the correct experiments.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "TLS13SessionID-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					ExpectClientHelloSessionID:      hasSessionID,
+					ExpectEmptyClientHelloSessionID: hasEmptySessionID,
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-Reject-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					AlwaysRejectEarlyData: true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-reject-early-data",
+				"-on-resume-shim-writes-first",
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				MinVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				},
+			},
+			tls13Variant:  variant,
+			messageCount:  2,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+			},
+		})
+
+	}
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -11326,165 +11351,6 @@
 			},
 		},
 	})
-
-	for _, noSessionID := range []bool{false, true} {
-		prefix := "TLS13Experiment"
-		variant := TLS13Experiment
-		if noSessionID {
-			prefix = "TLS13NoSessionIDExperiment"
-			variant = TLS13NoSessionIDExperiment
-		}
-
-		// Test that enabling a TLS 1.3 variant does not interfere with
-		// TLS 1.2 session ID resumption.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			name:     prefix + "-ResumeTLS12SessionID",
-			config: Config{
-				MaxVersion:             VersionTLS12,
-				SessionTicketsDisabled: true,
-			},
-			resumeSession: true,
-			flags:         []string{"-tls13-variant", strconv.Itoa(variant)},
-		})
-
-		// Test that the server correctly echoes back session IDs of
-		// various lengths.
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			name:     prefix + "-EmptySessionID",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendClientHelloSessionID: []byte{},
-				},
-			},
-			tls13Variant: variant,
-		})
-
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			name:     prefix + "-ShortSessionID",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendClientHelloSessionID: make([]byte, 16),
-				},
-			},
-			tls13Variant: variant,
-		})
-
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			name:     prefix + "-FullSessionID",
-			config: Config{
-				MaxVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendClientHelloSessionID: make([]byte, 32),
-				},
-			},
-			tls13Variant: variant,
-		})
-	}
-
-	// Test that the client sends a fake session ID in TLS13Experiment.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13Experiment-RequireSessionID",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				ExpectClientHelloSessionID: true,
-			},
-		},
-		tls13Variant: TLS13Experiment,
-	})
-
-	// Test that the client does not send a fake session ID in
-	// TLS13NoSessionIDExperiment.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13NoSessionIDExperiment-RequireEmptySessionID",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				ExpectEmptyClientHelloSessionID: true,
-			},
-		},
-		tls13Variant: TLS13NoSessionIDExperiment,
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-Reject-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				AlwaysRejectEarlyData: true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-early-data-info",
-			"-expect-reject-early-data",
-			"-on-resume-shim-writes-first",
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13Experiment-EarlyData-Reject-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				AlwaysRejectEarlyData: true,
-			},
-		},
-		tls13Variant:  TLS13Experiment,
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-early-data-info",
-			"-expect-reject-early-data",
-			"-on-resume-shim-writes-first",
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13RecordTypeExperiment-EarlyData-Reject-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				AlwaysRejectEarlyData: true,
-			},
-		},
-		tls13Variant:  TLS13RecordTypeExperiment,
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-early-data-info",
-			"-expect-reject-early-data",
-			"-on-resume-shim-writes-first",
-		},
-	})
-
 	testCases = append(testCases, testCase{
 		testType: clientTest,
 		name:     "TLS13-EarlyData-RejectTicket-Client",
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 98ddaf3..f50b077 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -159,10 +159,16 @@
 static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   // Restore the null cipher. We may have switched due to 0-RTT.
-  bssl::UniquePtr<SSLAEADContext> null_ctx = SSLAEADContext::CreateNullCipher();
+  bssl::UniquePtr<SSLAEADContext> null_ctx =
+      SSLAEADContext::CreateNullCipher(SSL_is_dtls(ssl));
   if (!null_ctx ||
-      !ssl->method->set_write_state(ssl, std::move(null_ctx)) ||
-      !ssl_write_client_hello(hs)) {
+      !ssl->method->set_write_state(ssl, std::move(null_ctx))) {
+    return ssl_hs_error;
+  }
+
+  ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+
+  if (!ssl_write_client_hello(hs)) {
     return ssl_hs_error;
   }
 
@@ -186,10 +192,10 @@
   uint8_t compression_method;
   if (!CBS_get_u16(&body, &server_version) ||
       !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+      (ssl_is_resumption_experiment(ssl->version) &&
        !CBS_get_u8_length_prefixed(&body, &session_id)) ||
       !CBS_get_u16(&body, &cipher_suite) ||
-      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+      (ssl_is_resumption_experiment(ssl->version) &&
        (!CBS_get_u8(&body, &compression_method) || compression_method != 0)) ||
       !CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0) {
@@ -198,8 +204,9 @@
     return ssl_hs_error;
   }
 
-  uint16_t expected_version =
-      ssl->version == TLS1_3_EXPERIMENT_VERSION ? TLS1_2_VERSION : ssl->version;
+  uint16_t expected_version = ssl_is_resumption_experiment(ssl->version)
+                                  ? TLS1_2_VERSION
+                                  : ssl->version;
   if (server_version != expected_version) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
@@ -246,7 +253,7 @@
 
   // supported_versions is parsed in handshake_client to select the experimental
   // TLS 1.3 version.
-  if (have_supported_versions && ssl->version != TLS1_3_EXPERIMENT_VERSION) {
+  if (have_supported_versions && !ssl_is_resumption_experiment(ssl->version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
     return ssl_hs_error;
@@ -351,7 +358,7 @@
 
   ssl->method->next_message(ssl);
   hs->tls13_state = state_process_change_cipher_spec;
-  return ssl->version == TLS1_3_EXPERIMENT_VERSION
+  return ssl_is_resumption_experiment(ssl->version)
              ? ssl_hs_read_change_cipher_spec
              : ssl_hs_ok;
 }
@@ -366,7 +373,7 @@
   if (!hs->early_data_offered) {
     // If not sending early data, set client traffic keys now so that alerts are
     // encrypted.
-    if ((ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+    if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
          !ssl3_add_change_cipher_spec(ssl)) ||
         !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
                                hs->hash_len)) {
@@ -574,7 +581,7 @@
   }
 
   if (hs->early_data_offered) {
-    if ((ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+    if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
          !ssl3_add_change_cipher_spec(ssl)) ||
         !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
                                hs->hash_len)) {
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 7bd87c5..6ff9972 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -150,8 +150,8 @@
   }
 
   UniquePtr<SSLAEADContext> traffic_aead = SSLAEADContext::Create(
-      direction, version, SSL_is_dtls(ssl), session->cipher, key, key_len, NULL,
-      0, iv, iv_len);
+      direction, session->ssl_version, SSL_is_dtls(ssl), session->cipher, key,
+      key_len, NULL, 0, iv, iv_len);
   if (!traffic_aead) {
     return 0;
   }
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index cd6baa4..550f3b5 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -528,7 +528,7 @@
   SSL *const ssl = hs->ssl;
 
   uint16_t version = ssl->version;
-  if (ssl->version == TLS1_3_EXPERIMENT_VERSION) {
+  if (ssl_is_resumption_experiment(ssl->version)) {
     version = TLS1_2_VERSION;
   }
 
@@ -539,21 +539,21 @@
       !CBB_add_u16(&body, version) ||
       !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) ||
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
-      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+      (ssl_is_resumption_experiment(ssl->version) &&
        (!CBB_add_u8_length_prefixed(&body, &session_id) ||
         !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len))) ||
       !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) ||
-      (ssl->version == TLS1_3_EXPERIMENT_VERSION && !CBB_add_u8(&body, 0)) ||
+      (ssl_is_resumption_experiment(ssl->version) && !CBB_add_u8(&body, 0)) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
       !ssl_ext_key_share_add_serverhello(hs, &extensions) ||
-      (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+      (ssl_is_resumption_experiment(ssl->version) &&
        !ssl_ext_supported_versions_add_serverhello(hs, &extensions)) ||
       !ssl_add_message_cbb(ssl, cbb.get())) {
     return ssl_hs_error;
   }
 
-  if (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
+  if (ssl_is_resumption_experiment(ssl->version) &&
       !ssl3_add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
   }
@@ -706,7 +706,7 @@
   if (hs->early_data_offered && !hs->ssl->early_data_accepted) {
     return ssl_hs_ok;
   }
-  return hs->ssl->version == TLS1_3_EXPERIMENT_VERSION
+  return ssl_is_resumption_client_ccs_experiment(hs->ssl->version)
              ? ssl_hs_read_change_cipher_spec
              : ssl_hs_ok;
 }
diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc
index 043ec62..5eeff3c 100644
--- a/src/ssl/tls_record.cc
+++ b/src/ssl/tls_record.cc
@@ -143,7 +143,7 @@
 static int ssl_needs_record_splitting(const SSL *ssl) {
 #if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   return !ssl->s3->aead_write_ctx->is_null_cipher() &&
-         ssl->s3->aead_write_ctx->version() < TLS1_1_VERSION &&
+         ssl->s3->aead_write_ctx->ProtocolVersion() < TLS1_1_VERSION &&
          (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
          SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher());
 #else
@@ -205,17 +205,13 @@
     return ssl_open_record_partial;
   }
 
-  int version_ok;
+  bool version_ok;
   if (ssl->s3->aead_read_ctx->is_null_cipher()) {
     // Only check the first byte. Enforcing beyond that can prevent decoding
     // version negotiation failure alerts.
     version_ok = (version >> 8) == SSL3_VERSION_MAJOR;
-  } else if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-    // Earlier versions of TLS switch the record version.
-    version_ok = version == ssl->version;
   } else {
-    // Starting TLS 1.3, the version field is frozen at {3, 1}.
-    version_ok = version == TLS1_VERSION;
+    version_ok = version == ssl->s3->aead_read_ctx->RecordVersion();
   }
 
   if (!version_ok) {
@@ -274,7 +270,7 @@
 
   // TLS 1.3 hides the record type inside the encrypted data.
   if (!ssl->s3->aead_read_ctx->is_null_cipher() &&
-      ssl->s3->aead_read_ctx->version() >= TLS1_3_VERSION) {
+      ssl->s3->aead_read_ctx->ProtocolVersion() >= TLS1_3_VERSION) {
     // The outer record type is always application_data.
     if (type != SSL3_RT_APPLICATION_DATA) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_OUTER_RECORD_TYPE);
@@ -350,7 +346,7 @@
   uint8_t *extra_in = NULL;
   size_t extra_in_len = 0;
   if (!ssl->s3->aead_write_ctx->is_null_cipher() &&
-      ssl->s3->aead_write_ctx->version() >= TLS1_3_VERSION) {
+      ssl->s3->aead_write_ctx->ProtocolVersion() >= TLS1_3_VERSION) {
     // TLS 1.3 hides the actual record type inside the encrypted data.
     extra_in = &type;
     extra_in_len = 1;
@@ -379,26 +375,17 @@
     out_prefix[0] = type;
   }
 
-  // The TLS record-layer version number is meaningless and, starting in
-  // TLS 1.3, is frozen at TLS 1.0. But for historical reasons, SSL 3.0
-  // 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->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;
-  }
-  out_prefix[1] = wire_version >> 8;
-  out_prefix[2] = wire_version & 0xff;
+  uint16_t record_version = ssl->s3->aead_write_ctx->RecordVersion();
+
+  out_prefix[1] = record_version >> 8;
+  out_prefix[2] = record_version & 0xff;
   out_prefix[3] = ciphertext_len >> 8;
   out_prefix[4] = ciphertext_len & 0xff;
 
-  if (!ssl->s3->aead_write_ctx->SealScatter(out_prefix + SSL3_RT_HEADER_LENGTH,
-                                            out, out_suffix, type, wire_version,
-                                            ssl->s3->write_sequence, in, in_len,
-                                            extra_in, extra_in_len) ||
+  if (!ssl->s3->aead_write_ctx->SealScatter(
+          out_prefix + SSL3_RT_HEADER_LENGTH, out, out_suffix, type,
+          record_version, ssl->s3->write_sequence, in, in_len, extra_in,
+          extra_in_len) ||
       !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) {
     return 0;
   }
@@ -429,7 +416,7 @@
                                         uint8_t type, size_t in_len) {
   size_t extra_in_len = 0;
   if (!ssl->s3->aead_write_ctx->is_null_cipher() &&
-      ssl->s3->aead_write_ctx->version() >= TLS1_3_VERSION) {
+      ssl->s3->aead_write_ctx->ProtocolVersion() >= TLS1_3_VERSION) {
     // TLS 1.3 adds an extra byte for encrypted record type.
     extra_in_len = 1;
   }
@@ -679,7 +666,7 @@
   ret += ssl->s3->aead_write_ctx->MaxOverhead();
   // TLS 1.3 needs an extra byte for the encrypted record type.
   if (!ssl->s3->aead_write_ctx->is_null_cipher() &&
-      ssl->s3->aead_write_ctx->version() >= TLS1_3_VERSION) {
+      ssl->s3->aead_write_ctx->ProtocolVersion() >= TLS1_3_VERSION) {
     ret += 1;
   }
   if (ssl_needs_record_splitting(ssl)) {
diff --git a/src/tool/client.cc b/src/tool/client.cc
index a5254b2..e2da29e 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -318,6 +318,14 @@
     *out = tls13_experiment;
     return true;
   }
+  if (in == "experiment2") {
+    *out = tls13_experiment2;
+    return true;
+  }
+  if (in == "experiment3") {
+    *out = tls13_experiment3;
+    return true;
+  }
   if (in == "record-type") {
     *out = tls13_record_type_experiment;
     return true;
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 4cc183b..1d64933 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -160,6 +160,13 @@
   }
 }
 
+static FILE *g_keylog_file = nullptr;
+
+static void KeyLogCallback(const SSL *ssl, const char *line) {
+  fprintf(g_keylog_file, "%s\n", line);
+  fflush(g_keylog_file);
+}
+
 bool Server(const std::vector<std::string> &args) {
   if (!InitSocketLibrary()) {
     return false;
@@ -174,6 +181,16 @@
 
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
 
+  const char *keylog_file = getenv("SSLKEYLOGFILE");
+  if (keylog_file) {
+    g_keylog_file = fopen(keylog_file, "a");
+    if (g_keylog_file == nullptr) {
+      perror("fopen");
+      return false;
+    }
+    SSL_CTX_set_keylog_callback(ctx.get(), KeyLogCallback);
+  }
+
   // Server authentication is required.
   if (args_map.count("-key") != 0) {
     std::string key = args_map["-key"];