Add experiment without client CCS and fix session ID bug.

Change-Id: Id6cf63caf5a00d4d4ca66a5c7530c48c2d9ed91f
Reviewed-on: https://boringssl-review.googlesource.com/20164
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index c81ce6a..36f61a6 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -591,6 +591,7 @@
 #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
@@ -3179,6 +3180,7 @@
   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
diff --git a/ssl/dtls_record.cc b/ssl/dtls_record.cc
index dbc8fa2..5009f04 100644
--- a/ssl/dtls_record.cc
+++ b/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/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 52d2e94..18dd58f 100644
--- a/ssl/handshake_client.cc
+++ b/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/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 10e618d..cd99ec9 100644
--- a/ssl/handshake_server.cc
+++ b/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/ssl/internal.h b/ssl/internal.h
index ddc91c7..2fb3614 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -286,6 +286,20 @@
 // 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.
 
 // Bits for |algorithm_mkey| (key exchange algorithm).
@@ -469,7 +483,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;
 
@@ -477,7 +491,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
@@ -489,7 +503,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.
@@ -512,7 +539,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
@@ -520,7 +547,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
@@ -539,17 +566,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_;
@@ -557,8 +585,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;
diff --git a/ssl/s3_lib.cc b/ssl/s3_lib.cc
index dcf9559..3df8e1b 100644
--- a/ssl/s3_lib.cc
+++ b/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/ssl/ssl_aead_ctx.cc b/ssl/ssl_aead_ctx.cc
index 69129af..d03a4a0 100644
--- a/ssl/ssl_aead_ctx.cc
+++ b/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/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index f6dea8c..560d0cf 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -37,6 +37,7 @@
     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;
@@ -59,6 +60,7 @@
 // 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,
@@ -106,6 +108,7 @@
   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;
@@ -233,6 +236,7 @@
     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";
 
@@ -280,6 +284,7 @@
     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;
     }
@@ -289,6 +294,8 @@
          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 &&
@@ -350,9 +357,25 @@
 
 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;
@@ -379,6 +402,7 @@
   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/ssl/t1_enc.cc b/ssl/t1_enc.cc
index f917ed7..0283c6e 100644
--- a/ssl/t1_enc.cc
+++ b/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/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 61540bb..19edb7f 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -36,6 +36,7 @@
 	tls13DraftVersion                = 0x7f12
 	tls13ExperimentVersion           = 0x7e01
 	tls13Experiment2Version          = 0x7e02
+	tls13Experiment3Version          = 0x7e03
 	tls13RecordTypeExperimentVersion = 0x7a12
 )
 
@@ -45,10 +46,12 @@
 	TLS13RecordTypeExperiment  = 2
 	TLS13NoSessionIDExperiment = 3
 	TLS13Experiment2           = 4
+	TLS13Experiment3           = 5
 )
 
 var allTLSWireVersions = []uint16{
 	tls13DraftVersion,
+	tls13Experiment3Version,
 	tls13Experiment2Version,
 	tls13ExperimentVersion,
 	tls13RecordTypeExperimentVersion,
@@ -279,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.
@@ -1559,7 +1563,7 @@
 		switch vers {
 		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
 			return vers, true
-		case tls13DraftVersion, tls13ExperimentVersion, tls13Experiment2Version, tls13RecordTypeExperimentVersion:
+		case tls13DraftVersion, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version, tls13RecordTypeExperimentVersion:
 			return VersionTLS13, true
 		}
 	}
@@ -1568,15 +1572,24 @@
 }
 
 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/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 615e224..25123b1 100644
--- a/ssl/test/runner/conn.go
+++ b/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,7 +792,7 @@
 			if c.vers >= VersionTLS13 {
 				expect = VersionTLS10
 			}
-			if c.wireVersion == tls13Experiment2Version {
+			if isResumptionRecordVersionExperiment(c.wireVersion) {
 				expect = VersionTLS12
 			}
 		} else {
@@ -1128,8 +1139,9 @@
 			// layer to {3, 1}.
 			vers = VersionTLS10
 		}
-		if c.wireVersion == tls13Experiment2Version {
+		if isResumptionRecordVersionExperiment(c.wireVersion) || isResumptionRecordVersionExperiment(c.out.wireVersion) {
 			vers = VersionTLS12
+		} else {
 		}
 
 		if c.config.Bugs.SendRecordVersion != 0 {
@@ -1465,6 +1477,7 @@
 	session := &ClientSessionState{
 		sessionTicket:      newSessionTicket.ticket,
 		vers:               c.vers,
+		wireVersion:        c.wireVersion,
 		cipherSuite:        cipherSuite.id,
 		masterSecret:       c.resumptionSecret,
 		serverCertificates: c.peerCertificates,
@@ -1888,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/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 13c3a19..a04ffd0 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -412,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
@@ -755,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 {
@@ -889,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.
@@ -931,11 +931,11 @@
 		c.sendAlert(alertEndOfEarlyData)
 	}
 
-	if isResumptionExperiment(c.wireVersion) {
+	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{
@@ -1021,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
@@ -1288,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/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 3fbd828..0a67a80 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -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 {
@@ -769,7 +769,7 @@
 
 	// 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 isResumptionExperiment(c.wireVersion) && !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/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 0bd24ff..ee72c2e 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -1310,6 +1310,13 @@
 		tls13Variant: TLS13Experiment2,
 	},
 	{
+		name:         "TLS13Experiment3",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13Experiment3Version,
+		tls13Variant: TLS13Experiment3,
+	},
+	{
 		name:         "TLS13RecordTypeExperiment",
 		version:      VersionTLS13,
 		excludeFlag:  "-no-tls13",
@@ -4105,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,
@@ -4270,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,
@@ -5189,7 +5055,7 @@
 				serverVers := expectedServerVersion
 				if expectedServerVersion >= VersionTLS13 {
 					serverVers = VersionTLS10
-					if runnerVers.tls13Variant == TLS13Experiment2 {
+					if runnerVers.tls13Variant == TLS13Experiment2 || runnerVers.tls13Variant == TLS13Experiment3 {
 						serverVers = VersionTLS12
 					}
 				}
@@ -10844,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,
@@ -11355,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/ssl/tls13_client.cc b/ssl/tls13_client.cc
index dad7cad..f50b077 100644
--- a/ssl/tls13_client.cc
+++ b/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;
   }
 
@@ -367,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_is_resumption_experiment(ssl->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)) {
@@ -575,7 +581,7 @@
   }
 
   if (hs->early_data_offered) {
-    if ((ssl_is_resumption_experiment(ssl->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/ssl/tls13_enc.cc b/ssl/tls13_enc.cc
index 7bd87c5..6ff9972 100644
--- a/ssl/tls13_enc.cc
+++ b/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/ssl/tls13_server.cc b/ssl/tls13_server.cc
index a0f115b..550f3b5 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -706,7 +706,7 @@
   if (hs->early_data_offered && !hs->ssl->early_data_accepted) {
     return ssl_hs_ok;
   }
-  return ssl_is_resumption_experiment(hs->ssl->version)
+  return ssl_is_resumption_client_ccs_experiment(hs->ssl->version)
              ? ssl_hs_read_change_cipher_spec
              : ssl_hs_ok;
 }
diff --git a/ssl/tls_record.cc b/ssl/tls_record.cc
index 511f489..5eeff3c 100644
--- a/ssl/tls_record.cc
+++ b/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,19 +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 if (ssl->version == TLS1_3_EXPERIMENT2_VERSION) {
-    version_ok = version == TLS1_2_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) {
@@ -276,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);
@@ -352,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;
@@ -381,30 +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;
-  }
-  if (ssl->s3->have_version && ssl->version == TLS1_3_EXPERIMENT2_VERSION) {
-    wire_version = TLS1_2_VERSION;
-  }
+  uint16_t record_version = ssl->s3->aead_write_ctx->RecordVersion();
 
-  out_prefix[1] = wire_version >> 8;
-  out_prefix[2] = wire_version & 0xff;
+  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;
   }
@@ -435,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;
   }
@@ -685,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/tool/client.cc b/tool/client.cc
index cde3397..e2da29e 100644
--- a/tool/client.cc
+++ b/tool/client.cc
@@ -322,6 +322,10 @@
     *out = tls13_experiment2;
     return true;
   }
+  if (in == "experiment3") {
+    *out = tls13_experiment3;
+    return true;
+  }
   if (in == "record-type") {
     *out = tls13_record_type_experiment;
     return true;