Add ClientHello no_session_id variant.

Change-Id: I3d249582dea871d7b1c078a6b5f57679037d1b8f
Reviewed-on: https://boringssl-review.googlesource.com/17984
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index cee90a1..4a1a726 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3186,6 +3186,7 @@
   tls13_default = 0,
   tls13_experiment = 1,
   tls13_record_type_experiment = 2,
+  tls13_no_session_id_experiment = 3,
 };
 
 /* SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 10c10a2..e60b6ae 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -764,7 +764,8 @@
     return -1;
   }
 
-  /* Initialize a random session ID for the experimental TLS 1.3 variant. */
+  /* Initialize a random session ID for the experimental TLS 1.3 variant
+   * requiring a session id. */
   if (ssl->tls13_variant == tls13_experiment) {
     hs->session_id_len = sizeof(hs->session_id);
     if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index 8b54bd2..6fd19c6 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -312,6 +312,7 @@
     }
   } else {
     if ((ssl->tls13_variant != tls13_experiment &&
+         ssl->tls13_variant != tls13_no_session_id_experiment &&
          version == TLS1_3_EXPERIMENT_VERSION) ||
         (ssl->tls13_variant != tls13_record_type_experiment &&
          version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) ||
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index be7d88e..a6a521b 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -39,9 +39,10 @@
 )
 
 const (
-	TLS13Default              = 0
-	TLS13Experiment           = 1
-	TLS13RecordTypeExperiment = 2
+	TLS13Default               = 0
+	TLS13Experiment            = 1
+	TLS13RecordTypeExperiment  = 2
+	TLS13NoSessionIDExperiment = 3
 )
 
 var allTLSWireVersions = []uint16{
@@ -717,6 +718,18 @@
 	// normally expected to look ahead for ChangeCipherSpec.)
 	EmptyTicketSessionID bool
 
+	// SendClientHelloSessionID, if not nil, is the session ID sent in the
+	// ClientHello.
+	SendClientHelloSessionID []byte
+
+	// ExpectClientHelloSessionID, if true, causes the server to fail the
+	// connection if there is not a SessionID in the ClientHello.
+	ExpectClientHelloSessionID bool
+
+	// ExpectEmptyClientHelloSessionID, if true, causes the server to fail the
+	// connection if there is a SessionID in the ClientHello.
+	ExpectEmptyClientHelloSessionID bool
+
 	// ExpectNoTLS12Session, if true, causes the server to fail the
 	// connection if either a session ID or TLS 1.2 ticket is offered.
 	ExpectNoTLS12Session bool
@@ -1500,7 +1513,7 @@
 // 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 && wireVers == tls13ExperimentVersion) ||
+	if (c.TLS13Variant != TLS13Experiment && c.TLS13Variant != TLS13NoSessionIDExperiment && wireVers == tls13ExperimentVersion) ||
 		(c.TLS13Variant != TLS13RecordTypeExperiment && wireVers == tls13RecordTypeExperimentVersion) ||
 		(c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) {
 		return 0, false
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 05e7311..10e841a 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -358,6 +358,9 @@
 	if c.config.Bugs.OmitEarlyDataExtension {
 		hello.hasEarlyData = false
 	}
+	if c.config.Bugs.SendClientHelloSessionID != nil {
+		hello.sessionId = c.config.Bugs.SendClientHelloSessionID
+	}
 
 	var helloBytes []byte
 	if c.config.Bugs.SendV2ClientHello {
@@ -684,6 +687,10 @@
 func (hs *clientHandshakeState) doTLS13Handshake() error {
 	c := hs.c
 
+	if c.wireVersion == tls13ExperimentVersion && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+		return errors.New("tls: session IDs did not match.")
+	}
+
 	// Once the PRF hash is known, TLS 1.3 does not require a handshake
 	// buffer.
 	hs.finishedHash.discardHandshakeBuffer()
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index b31a562..3e70185 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -1087,6 +1087,13 @@
 		copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS12)
 	}
 
+	if len(hs.clientHello.sessionId) > 0 && c.config.Bugs.ExpectEmptyClientHelloSessionID {
+		return false, errors.New("tls: expected empty session ID from client")
+	}
+	if len(hs.clientHello.sessionId) == 0 && c.config.Bugs.ExpectClientHelloSessionID {
+		return false, errors.New("tls: expected non-empty session ID from client")
+	}
+
 	foundCompression := false
 	// We only support null compression, so check that the client offered it.
 	for _, compression := range hs.clientHello.compressionMethods {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 8ced44d..c5f9971 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -527,6 +527,9 @@
 	if *deterministic {
 		config.Time = func() time.Time { return time.Unix(1234, 1234) }
 	}
+	if test.tls13Variant != 0 {
+		config.TLS13Variant = test.tls13Variant
+	}
 
 	conn = &timeoutConn{conn, *idleTimeout}
 
@@ -1038,7 +1041,6 @@
 	}
 
 	if test.tls13Variant != 0 {
-		test.config.TLS13Variant = test.tls13Variant
 		flags = append(flags, "-tls13-variant", strconv.Itoa(test.tls13Variant))
 	}
 
@@ -4015,25 +4017,23 @@
 			config: Config{
 				MaxVersion:       VersionTLS13,
 				MinVersion:       VersionTLS13,
-				TLS13Variant:     TLS13Experiment,
 				MaxEarlyDataSize: 16384,
 			},
 			resumeConfig: &Config{
 				MaxVersion:       VersionTLS13,
 				MinVersion:       VersionTLS13,
-				TLS13Variant:     TLS13Experiment,
 				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",
-				"-tls13-variant", "1",
 			},
 		})
 
@@ -4055,13 +4055,13 @@
 					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",
-				"-tls13-variant", "2",
 			},
 		})
 
@@ -11038,6 +11038,93 @@
 		},
 	})
 
+	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",
@@ -11067,23 +11154,21 @@
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
-			TLS13Variant:     TLS13Experiment,
 		},
 		resumeConfig: &Config{
 			MaxVersion:       VersionTLS13,
-			TLS13Variant:     TLS13Experiment,
 			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",
-			"-tls13-variant", "1",
 		},
 	})
 
@@ -11093,23 +11178,21 @@
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
-			TLS13Variant:     TLS13RecordTypeExperiment,
 		},
 		resumeConfig: &Config{
 			MaxVersion:       VersionTLS13,
-			TLS13Variant:     TLS13RecordTypeExperiment,
 			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",
-			"-tls13-variant", "2",
 		},
 	})