Implement ContentType TLS 1.3 variant.

This implements PR #1051
(https://github.com/tlswg/tls13-spec/pull/1051).

Local experiments were not able to replicate the claims in the PR, but
implement this anyway for comparison purposes.

Change-Id: Ic9baf5e671f9a44565020466a553dd08f5ec0f1b
Reviewed-on: https://boringssl-review.googlesource.com/17844
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/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 2c37ec0..48fe052 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -674,7 +674,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_default &&
+        ssl->tls13_variant == tls13_experiment &&
         !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
       return 0;
     }
@@ -759,7 +759,7 @@
   }
 
   /* Initialize a random session ID for the experimental TLS 1.3 variant. */
-  if (ssl->tls13_variant != tls13_default) {
+  if (ssl->tls13_variant == tls13_experiment) {
     hs->session_id_len = sizeof(hs->session_id);
     if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
       return -1;
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index 5eb364d..79f71fa 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -266,7 +266,15 @@
       todo = ssl->max_send_fragment;
     }
 
-    if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) {
+    uint8_t type = SSL3_RT_HANDSHAKE;
+    if (ssl->server &&
+        ssl->s3->have_version &&
+        ssl->version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION &&
+        ssl->s3->aead_write_ctx == NULL) {
+      type = SSL3_RT_PLAINTEXT_HANDSHAKE;
+    }
+
+    if (!add_record_to_flight(ssl, type, msg + added, todo)) {
       goto err;
     }
     added += todo;
diff --git a/ssl/s3_pkt.cc b/ssl/s3_pkt.cc
index beaa08e..4ae2e34 100644
--- a/ssl/s3_pkt.cc
+++ b/ssl/s3_pkt.cc
@@ -523,7 +523,13 @@
       return -1;
     }
 
-    if (rr->type != SSL3_RT_HANDSHAKE) {
+    /* Accept server_plaintext_handshake records when the content type TLS 1.3
+     * variant is enabled. */
+    if (rr->type != SSL3_RT_HANDSHAKE &&
+        !(!ssl->server &&
+          ssl->tls13_variant == tls13_record_type_experiment &&
+          ssl->s3->aead_read_ctx == NULL &&
+          rr->type == SSL3_RT_PLAINTEXT_HANDSHAKE)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return -1;
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index 387eee7..8b54bd2 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -34,6 +34,7 @@
 
     case TLS1_3_DRAFT_VERSION:
     case TLS1_3_EXPERIMENT_VERSION:
+    case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION:
       *out = TLS1_3_VERSION;
       return 1;
 
@@ -55,8 +56,9 @@
  * decreasing preference. */
 
 static const uint16_t kTLSVersions[] = {
-    TLS1_3_DRAFT_VERSION,
     TLS1_3_EXPERIMENT_VERSION,
+    TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION,
+    TLS1_3_DRAFT_VERSION,
     TLS1_2_VERSION,
     TLS1_1_VERSION,
     TLS1_VERSION,
@@ -98,7 +100,8 @@
    * everywhere to refer to any draft TLS 1.3 versions. In this direction, we
    * map it to some representative TLS 1.3 draft version. */
   if (version == TLS1_3_DRAFT_VERSION ||
-      version == TLS1_3_EXPERIMENT_VERSION) {
+      version == TLS1_3_EXPERIMENT_VERSION ||
+      version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
     return 0;
   }
@@ -238,7 +241,8 @@
 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_RECORD_TYPE_EXPERIMENT_VERSION) {
     return TLS1_3_VERSION;
   }
   return ret;
@@ -249,6 +253,7 @@
     /* 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_RECORD_TYPE_EXPERIMENT_VERSION:
       return "TLSv1.3";
 
     case TLS1_2_VERSION:
@@ -301,12 +306,15 @@
    * non-default value. */
   if (ssl->server) {
     if (ssl->tls13_variant == tls13_default &&
-        version == TLS1_3_EXPERIMENT_VERSION) {
+        (version == TLS1_3_EXPERIMENT_VERSION ||
+         version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION)) {
       return 0;
     }
   } else {
     if ((ssl->tls13_variant != tls13_experiment &&
          version == TLS1_3_EXPERIMENT_VERSION) ||
+        (ssl->tls13_variant != tls13_record_type_experiment &&
+         version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) ||
         (ssl->tls13_variant != tls13_default &&
          version == TLS1_3_DRAFT_VERSION)) {
       return 0;
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index b40f222..a3c744c 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -33,18 +33,21 @@
 
 // A draft version of TLS 1.3 that is sent over the wire for the current draft.
 const (
-	tls13DraftVersion      = 0x7f12
-	tls13ExperimentVersion = 0x7e01
+	tls13DraftVersion                = 0x7f12
+	tls13ExperimentVersion           = 0x7e01
+	tls13RecordTypeExperimentVersion = 0x7a12
 )
 
 const (
-	TLS13Default    = 0
-	TLS13Experiment = 1
+	TLS13Default              = 0
+	TLS13Experiment           = 1
+	TLS13RecordTypeExperiment = 2
 )
 
 var allTLSWireVersions = []uint16{
 	tls13DraftVersion,
 	tls13ExperimentVersion,
+	tls13RecordTypeExperimentVersion,
 	VersionTLS12,
 	VersionTLS11,
 	VersionTLS10,
@@ -71,10 +74,11 @@
 type recordType uint8
 
 const (
-	recordTypeChangeCipherSpec recordType = 20
-	recordTypeAlert            recordType = 21
-	recordTypeHandshake        recordType = 22
-	recordTypeApplicationData  recordType = 23
+	recordTypeChangeCipherSpec   recordType = 20
+	recordTypeAlert              recordType = 21
+	recordTypeHandshake          recordType = 22
+	recordTypeApplicationData    recordType = 23
+	recordTypePlaintextHandshake recordType = 24
 )
 
 // TLS handshake message types.
@@ -1485,6 +1489,7 @@
 // false.
 func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
 	if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) ||
+		(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 a50029f..c974bd4 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -762,6 +762,11 @@
 		return 0, nil, c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
 	}
 
+	// Accept server_plaintext_handshake records when the content type TLS 1.3 variant is enabled.
+	if c.isClient && c.in.cipher == nil && c.config.TLS13Variant == TLS13RecordTypeExperiment && want == recordTypeHandshake && typ == recordTypePlaintextHandshake {
+		typ = recordTypeHandshake
+	}
+
 	vers := uint16(b.data[1])<<8 | uint16(b.data[2])
 	n := int(b.data[3])<<8 | int(b.data[4])
 
diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go
index b0b6c18..72369d6 100644
--- a/ssl/test/runner/dtls.go
+++ b/ssl/test/runner/dtls.go
@@ -35,7 +35,7 @@
 		switch vers {
 		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
 			return vers, true
-		case tls13DraftVersion, tls13ExperimentVersion:
+		case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion:
 			return VersionTLS13, true
 		}
 	}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 25230a4..35d005e 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -573,7 +573,11 @@
 	if sendHelloRetryRequest {
 		oldClientHelloBytes := hs.clientHello.marshal()
 		hs.writeServerHash(helloRetryRequest.marshal())
-		c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
+		if c.vers == tls13RecordTypeExperimentVersion {
+			c.writeRecord(recordTypePlaintextHandshake, helloRetryRequest.marshal())
+		} else {
+			c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
+		}
 		c.flushHandshake()
 
 		if hs.clientHello.hasEarlyData {
@@ -751,7 +755,11 @@
 		toWrite = append(toWrite, typeEncryptedExtensions)
 		c.writeRecord(recordTypeHandshake, toWrite)
 	} else {
-		c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+		if c.vers == tls13RecordTypeExperimentVersion {
+			c.writeRecord(recordTypePlaintextHandshake, hs.hello.marshal())
+		} else {
+			c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+		}
 	}
 	c.flushHandshake()
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 0005725..86f5f71 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -1280,6 +1280,13 @@
 		versionWire:  tls13ExperimentVersion,
 		tls13Variant: TLS13Experiment,
 	},
+	{
+		name:         "TLS13RecordTypeExperiment",
+		version:      VersionTLS13,
+		excludeFlag:  "-no-tls13",
+		versionWire:  tls13RecordTypeExperimentVersion,
+		tls13Variant: TLS13RecordTypeExperiment,
+	},
 }
 
 func allVersions(protocol protocol) []tlsVersion {
@@ -4018,6 +4025,34 @@
 
 		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'}},
+				},
+			},
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
+				"-tls13-variant", "2",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: clientTest,
 			name:     "TLS13-EarlyData-TooMuchData-Client",
 			config: Config{
 				MaxVersion:       VersionTLS13,
@@ -4146,6 +4181,28 @@
 
 		tests = append(tests, testCase{
 			testType: serverTest,
+			name:     "TLS13RecordTypeExperiment-EarlyData-Server",
+			config: Config{
+				MaxVersion:   VersionTLS13,
+				MinVersion:   VersionTLS13,
+				TLS13Variant: TLS13RecordTypeExperiment,
+				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",
+				"-tls13-variant", "2",
+			},
+		})
+
+		tests = append(tests, testCase{
+			testType: serverTest,
 			name:     "TLS13-MaxEarlyData-Server",
 			config: Config{
 				MaxVersion: VersionTLS13,
@@ -10477,6 +10534,19 @@
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
+		name:     "SkipEarlyData-TLS13RecordTypeExperiment",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			TLS13Variant: TLS13RecordTypeExperiment,
+			Bugs: ProtocolBugs{
+				SendFakeEarlyDataLength: 4,
+			},
+		},
+		flags: []string{"-tls13-variant", "2"},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
 		name:     "SkipEarlyData-OmitEarlyDataExtension",
 		config: Config{
 			MaxVersion: VersionTLS13,
@@ -11003,6 +11073,32 @@
 
 	testCases = append(testCases, testCase{
 		testType: clientTest,
+		name:     "TLS13RecordTypeExperiment-EarlyData-Reject-Client",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			TLS13Variant:     TLS13RecordTypeExperiment,
+		},
+		resumeConfig: &Config{
+			MaxVersion:       VersionTLS13,
+			TLS13Variant:     TLS13RecordTypeExperiment,
+			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",
+			"-tls13-variant", "2",
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
 		name:     "TLS13-EarlyData-RejectTicket-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,