Run TLS 1.3 tests at all variants and fix bugs.

We were only running a random subset of TLS 1.3 tests with variants and
let a lot of bugs through as a result.

- HelloRetryRequest-EmptyCookie wasn't actually testing what we were
  trying to test.

- The second HelloRetryRequest detection needs tweaks in draft-22.

- The empty HelloRetryRequest logic can't be based on non-empty
  extensions in draft-22.

- We weren't sending ChangeCipherSpec correctly in HRR or testing it
  right.

- Rework how runner reads ChangeCipherSpec by setting a flag which
  affects the next readRecord. This cuts down a lot of cases and works
  correctly if the client didn't send early data. (In that case, we
  don't flush CCS until EndOfEarlyData and runner deadlocks waiting for
  the ChangeCipherSpec to arrive.)

Change-Id: I559c96ea3a8b350067e391941231713c6edb2f78
Reviewed-on: https://boringssl-review.googlesource.com/23125
Reviewed-by: Steven Valdez <svaldez@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index a528874..dec7347 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -53,6 +53,7 @@
 SSL,257,DUPLICATE_EXTENSION
 SSL,264,DUPLICATE_KEY_SHARE
 SSL,144,ECC_CERT_NOT_FOR_SIGNING
+SSL,282,EMPTY_HELLO_RETRY_REQUEST
 SSL,145,EMS_STATE_INCONSISTENT
 SSL,146,ENCRYPTED_LENGTH_TOO_LONG
 SSL,147,ERROR_ADDING_EXTENSION
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 8c36ad5..eba2fa3 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4599,6 +4599,7 @@
 #define SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA 279
 #define SSL_R_NO_SUPPORTED_VERSIONS_ENABLED 280
 #define SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE 281
+#define SSL_R_EMPTY_HELLO_RETRY_REQUEST 282
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index c633b50..21f491a 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -39,7 +39,6 @@
 	vers                 uint16     // TLS version
 	haveVers             bool       // version has been negotiated
 	config               *Config    // configuration passed to constructor
-	hadHelloRetryRequest bool
 	handshakeComplete    bool
 	skipEarlyData        bool // On a server, indicates that the client is sending early data that must be skipped over.
 	didResume            bool // whether this connection was a session resumption
@@ -102,6 +101,8 @@
 	keyUpdateRequested bool
 	seenOneByteRecord  bool
 
+	expectTLS13ChangeCipherSpec bool
+
 	tmp [16]byte
 }
 
@@ -878,6 +879,47 @@
 	return typ, b, nil
 }
 
+func (c *Conn) readTLS13ChangeCipherSpec() error {
+	if !c.expectTLS13ChangeCipherSpec {
+		panic("c.expectTLS13ChangeCipherSpec not set")
+	}
+
+	// Read the ChangeCipherSpec.
+	if c.rawInput == nil {
+		c.rawInput = c.in.newBlock()
+	}
+	b := c.rawInput
+	if err := b.readFromUntil(c.conn, 1); err != nil {
+		return c.in.setErrorLocked(fmt.Errorf("tls: error reading TLS 1.3 ChangeCipherSpec: %s", err))
+	}
+	if recordType(b.data[0]) == recordTypeAlert {
+		// If the client is sending an alert, allow the ChangeCipherSpec
+		// to be skipped. It may be rejecting a sufficiently malformed
+		// ServerHello that it can't parse out the version.
+		c.expectTLS13ChangeCipherSpec = false
+		return nil
+	}
+	if err := b.readFromUntil(c.conn, 6); err != nil {
+		return c.in.setErrorLocked(fmt.Errorf("tls: error reading TLS 1.3 ChangeCipherSpec: %s", err))
+	}
+
+	// Check they match that we expect.
+	expected := [6]byte{byte(recordTypeChangeCipherSpec), 3, 1, 0, 1, 1}
+	if isResumptionRecordVersionExperiment(c.wireVersion) {
+		expected[2] = 3
+	}
+	if !bytes.Equal(b.data[:6], expected[:]) {
+		return c.in.setErrorLocked(fmt.Errorf("tls: error invalid TLS 1.3 ChangeCipherSpec: %x", b.data[:6]))
+	}
+
+	// Discard the data.
+	b, c.rawInput = c.in.splitBlock(b, 6)
+	c.in.freeBlock(b)
+
+	c.expectTLS13ChangeCipherSpec = false
+	return nil
+}
+
 // readRecord reads the next TLS record from the connection
 // and updates the record layer state.
 // c.in.Mutex <= L; c.input == nil.
@@ -898,6 +940,12 @@
 		break
 	}
 
+	if c.expectTLS13ChangeCipherSpec {
+		if err := c.readTLS13ChangeCipherSpec(); err != nil {
+			return err
+		}
+	}
+
 Again:
 	typ, b, err := c.doReadRecord(want)
 	if err != nil {
@@ -952,14 +1000,12 @@
 			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 			break
 		}
-		if !isResumptionExperiment(c.wireVersion) {
-			if c.hand.Len() != 0 {
-				c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
-				break
-			}
-			if err := c.in.changeCipherSpec(c.config); err != nil {
-				c.in.setErrorLocked(c.sendAlert(err.(alert)))
-			}
+		if c.hand.Len() != 0 {
+			c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+			break
+		}
+		if err := c.in.changeCipherSpec(c.config); err != nil {
+			c.in.setErrorLocked(c.sendAlert(err.(alert)))
 		}
 
 	case recordTypeApplicationData:
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 7c8dbb5..5f22ecd 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -485,12 +485,18 @@
 	c.vers = serverVersion
 	c.haveVers = true
 
+	if isDraft22(c.wireVersion) {
+		// The first server message must be followed by a ChangeCipherSpec.
+		c.expectTLS13ChangeCipherSpec = true
+	}
+
 	helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg)
 	var secondHelloBytes []byte
 	if haveHelloRetryRequest {
-		c.hadHelloRetryRequest = true
 		if isDraft22(c.wireVersion) {
-			if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
+			// Explicitly read the ChangeCipherSpec now; it should
+			// be attached to the first flight, not the second flight.
+			if err := c.readTLS13ChangeCipherSpec(); err != nil {
 				return err
 			}
 		}
@@ -718,6 +724,12 @@
 func (hs *clientHandshakeState) doTLS13Handshake() error {
 	c := hs.c
 
+	if isResumptionExperiment(c.wireVersion) && !isDraft22(c.wireVersion) {
+		// Early versions of the middlebox hacks inserted
+		// ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
+		c.expectTLS13ChangeCipherSpec = true
+	}
+
 	if isResumptionExperiment(c.wireVersion) && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
 		return errors.New("tls: session IDs did not match.")
 	}
@@ -774,12 +786,6 @@
 		hs.finishedHash.addEntropy(zeroSecret)
 	}
 
-	if isResumptionExperiment(c.wireVersion) && !c.hadHelloRetryRequest {
-		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
-			return err
-		}
-	}
-
 	clientLabel := clientHandshakeTrafficLabel
 	serverLabel := serverHandshakeTrafficLabel
 	if isDraft21(c.wireVersion) {
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index d5e3951..93da121 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -1460,7 +1460,8 @@
 			extensions.addU16(2) // length
 			extensions.addU16(uint16(m.selectedGroup))
 		}
-		if len(m.cookie) > 0 {
+		// m.cookie may be a non-nil empty slice for empty cookie tests.
+		if m.cookie != nil {
 			extensions.addU16(extensionCookie)
 			body := extensions.addU16LengthPrefixed()
 			body.addU16LengthPrefixed().addBytes(m.cookie)
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 2513a00..4286911 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -358,6 +358,12 @@
 	c := hs.c
 	config := c.config
 
+	// We've read the ClientHello, so the next record in draft 22 must be
+	// preceded with ChangeCipherSpec.
+	if isDraft22(c.wireVersion) {
+		c.expectTLS13ChangeCipherSpec = true
+	}
+
 	hs.hello = &serverHelloMsg{
 		isDTLS:                c.isDTLS,
 		vers:                  c.wireVersion,
@@ -691,12 +697,6 @@
 				earlyLabel = earlyTrafficLabelDraft21
 			}
 
-			if isDraft22(c.wireVersion) {
-				if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
-					return err
-				}
-			}
-
 			earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyLabel)
 			if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil {
 				return err
@@ -996,11 +996,10 @@
 			}
 		}
 	}
-
-	if isResumptionClientCCSExperiment(c.wireVersion) && !c.skipEarlyData && !encryptedExtensions.extensions.hasEarlyData {
-		if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
-			return err
-		}
+	if isResumptionClientCCSExperiment(c.wireVersion) && !isDraft22(c.wireVersion) && !hs.clientHello.hasEarlyData {
+		// Early versions of the middlebox hacks inserted
+		// ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
+		c.expectTLS13ChangeCipherSpec = true
 	}
 
 	// Switch input stream to handshake traffic keys.
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index fe337c4..7d02c15 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -10943,59 +10943,6 @@
 }
 
 func addTLS13HandshakeTests() {
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "NegotiatePSKResumption-TLS13",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				NegotiatePSKResumption: true,
-			},
-		},
-		resumeSession: true,
-		shouldFail:    true,
-		expectedError: ":MISSING_KEY_SHARE:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "MissingKeyShare-Client",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				MissingKeyShare: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":MISSING_KEY_SHARE:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "MissingKeyShare-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				MissingKeyShare: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":MISSING_KEY_SHARE:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "DuplicateKeyShares",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				DuplicateKeyShares: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DUPLICATE_KEY_SHARE:",
-	})
-
 	for _, version := range allVersions(tls) {
 		if version.version != VersionTLS13 {
 			continue
@@ -11004,6 +10951,63 @@
 		variant := version.tls13Variant
 
 		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "NegotiatePSKResumption-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					NegotiatePSKResumption: true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			shouldFail:    true,
+			expectedError: ":MISSING_KEY_SHARE:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "MissingKeyShare-Client-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					MissingKeyShare: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":MISSING_KEY_SHARE:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "MissingKeyShare-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					MissingKeyShare: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":MISSING_KEY_SHARE:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "DuplicateKeyShares-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					DuplicateKeyShares: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":DUPLICATE_KEY_SHARE:",
+		})
+
+		testCases = append(testCases, testCase{
 			testType: serverTest,
 			name:     "SkipEarlyData-" + name,
 			config: Config{
@@ -11176,1280 +11180,1357 @@
 				"-expect-accept-early-data",
 			},
 		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-OmitEarlyDataExtension-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+					OmitEarlyDataExtension:  true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-TooMuchData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 16384 + 1,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-Interleaved-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+					InterleaveEarlyData:     true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-EarlyDataInTLS12-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_RECORD:",
+			flags:         []string{"-max-version", strconv.Itoa(VersionTLS12)},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-HRR-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+				},
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-HRR-Interleaved-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 4,
+					InterleaveEarlyData:     true,
+				},
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_RECORD:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-HRR-TooMuchData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendFakeEarlyDataLength: 16384 + 1,
+				},
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+		})
+
+		// Test that skipping early data looking for cleartext correctly
+		// processes an alert record.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-HRR-FatalAlert-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyAlert:          true,
+					SendFakeEarlyDataLength: 4,
+				},
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SkipEarlyData-SecondClientHelloEarlyData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyDataOnSecondClientHello: true,
+				},
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedLocalError: "remote error: bad record MAC",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EmptyEncryptedExtensions-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					EmptyEncryptedExtensions: true,
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedLocalError: "remote error: error decoding message",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EncryptedExtensionsWithKeyShare-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					EncryptedExtensionsWithKeyShare: true,
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedLocalError: "remote error: unsupported extension",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SendHelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// Require a HelloRetryRequest for every curve.
+				DefaultCurves: []CurveID{},
+			},
+			tls13Variant:    variant,
+			expectedCurveID: CurveX25519,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SendHelloRetryRequest-2-" + name,
+			config: Config{
+				MaxVersion:    VersionTLS13,
+				DefaultCurves: []CurveID{CurveP384},
+			},
+			tls13Variant: variant,
+			// Although the ClientHello did not predict our preferred curve,
+			// we always select it whether it is predicted or not.
+			expectedCurveID: CurveX25519,
+		})
+
+		testCases = append(testCases, testCase{
+			name: "UnknownCurve-HelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCurve: bogusCurve,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		if isDraft21(version.versionWire) {
+			testCases = append(testCases, testCase{
+				name: "HelloRetryRequest-CipherChange-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					// P-384 requires HelloRetryRequest in BoringSSL.
+					CurvePreferences: []CurveID{CurveP384},
+					Bugs: ProtocolBugs{
+						SendCipherSuite:                  TLS_AES_128_GCM_SHA256,
+						SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256,
+					},
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":WRONG_CIPHER_RETURNED:",
+			})
+
+			// Test that the client does not offer a PSK in the second ClientHello if the
+			// HelloRetryRequest is incompatible with it.
+			testCases = append(testCases, testCase{
+				testType: clientTest,
+				name:     "HelloRetryRequest-NonResumableCipher-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					CipherSuites: []uint16{
+						TLS_AES_128_GCM_SHA256,
+					},
+				},
+				resumeConfig: &Config{
+					MaxVersion: VersionTLS13,
+					// P-384 requires HelloRetryRequest in BoringSSL.
+					CurvePreferences: []CurveID{CurveP384},
+					Bugs: ProtocolBugs{
+						ExpectNoTLS13PSKAfterHRR: true,
+					},
+					CipherSuites: []uint16{
+						TLS_AES_256_GCM_SHA384,
+					},
+				},
+				tls13Variant:         variant,
+				resumeSession:        true,
+				expectResumeRejected: true,
+			})
+		}
+
+		testCases = append(testCases, testCase{
+			name: "DisabledCurve-HelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				CurvePreferences: []CurveID{CurveP256},
+				Bugs: ProtocolBugs{
+					IgnorePeerCurvePreferences: true,
+				},
+			},
+			tls13Variant:  variant,
+			flags:         []string{"-p384-only"},
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "UnnecessaryHelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				CurvePreferences: []CurveID{CurveX25519},
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCurve: CurveX25519,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "SecondHelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SecondHelloRetryRequest: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_MESSAGE:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-Empty-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					AlwaysSendHelloRetryRequest: true,
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedError:      ":EMPTY_HELLO_RETRY_REQUEST:",
+			expectedLocalError: "remote error: illegal parameter",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-DuplicateCurve-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires a HelloRetryRequest against BoringSSL's default
+				// configuration. Assert this ExpectMissingKeyShare.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					ExpectMissingKeyShare:                true,
+					DuplicateHelloRetryRequestExtensions: true,
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedError:      ":DUPLICATE_EXTENSION:",
+			expectedLocalError: "remote error: illegal parameter",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-Cookie-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie: []byte("cookie"),
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-DuplicateCookie-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie:          []byte("cookie"),
+					DuplicateHelloRetryRequestExtensions: true,
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedError:      ":DUPLICATE_EXTENSION:",
+			expectedLocalError: "remote error: illegal parameter",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-EmptyCookie-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie: []byte{},
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":DECODE_ERROR:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-Cookie-Curve-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie: []byte("cookie"),
+					ExpectMissingKeyShare:       true,
+				},
+			},
+			tls13Variant: variant,
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequest-Unknown-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomHelloRetryRequestExtension: "extension",
+				},
+			},
+			tls13Variant:       variant,
+			shouldFail:         true,
+			expectedError:      ":UNEXPECTED_EXTENSION:",
+			expectedLocalError: "remote error: unsupported extension",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SecondClientHelloMissingKeyShare-" + name,
+			config: Config{
+				MaxVersion:    VersionTLS13,
+				DefaultCurves: []CurveID{},
+				Bugs: ProtocolBugs{
+					SecondClientHelloMissingKeyShare: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":MISSING_KEY_SHARE:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SecondClientHelloWrongCurve-" + name,
+			config: Config{
+				MaxVersion:    VersionTLS13,
+				DefaultCurves: []CurveID{},
+				Bugs: ProtocolBugs{
+					MisinterpretHelloRetryRequestCurve: CurveP521,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequestVersionMismatch-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SendServerHelloVersion: 0x0305,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_VERSION_NUMBER:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "HelloRetryRequestCurveMismatch-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					// Send P-384 (correct) in the HelloRetryRequest.
+					SendHelloRetryRequestCurve: CurveP384,
+					// But send P-256 in the ServerHello.
+					SendCurve: CurveP256,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		// Test the server selecting a curve that requires a HelloRetryRequest
+		// without sending it.
+		testCases = append(testCases, testCase{
+			name: "SkipHelloRetryRequest-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				// P-384 requires HelloRetryRequest in BoringSSL.
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SkipHelloRetryRequest: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":WRONG_CURVE:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "RequestContextInHandshake-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				MinVersion: VersionTLS13,
+				ClientAuth: RequireAnyClientCert,
+				Bugs: ProtocolBugs{
+					SendRequestContext: []byte("request context"),
+				},
+			},
+			tls13Variant: variant,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			},
+			shouldFail:    true,
+			expectedError: ":DECODE_ERROR:",
+		})
+
+		if isDraft21(version.versionWire) {
+			testCases = append(testCases, testCase{
+				name: "UnknownExtensionInCertificateRequest-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					MinVersion: VersionTLS13,
+					ClientAuth: RequireAnyClientCert,
+					Bugs: ProtocolBugs{
+						SendCustomCertificateRequest: 0x1212,
+					},
+				},
+				tls13Variant: variant,
+				flags: []string{
+					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+					"-key-file", path.Join(*resourceDir, rsaKeyFile),
+				},
+			})
+
+			testCases = append(testCases, testCase{
+				name: "MissingSignatureAlgorithmsInCertificateRequest-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					MinVersion: VersionTLS13,
+					ClientAuth: RequireAnyClientCert,
+					Bugs: ProtocolBugs{
+						OmitCertificateRequestAlgorithms: true,
+					},
+				},
+				tls13Variant: variant,
+				flags: []string{
+					"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+					"-key-file", path.Join(*resourceDir, rsaKeyFile),
+				},
+				shouldFail:    true,
+				expectedError: ":DECODE_ERROR:",
+			})
+		}
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "TrailingKeyShareData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					TrailingKeyShareData: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":DECODE_ERROR:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "AlwaysSelectPSKIdentity-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					AlwaysSelectPSKIdentity: true,
+				},
+			},
+			tls13Variant:  variant,
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_EXTENSION:",
+		})
+
+		testCases = append(testCases, testCase{
+			name: "InvalidPSKIdentity-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SelectPSKIdentityOnResume: 1,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			shouldFail:    true,
+			expectedError: ":PSK_IDENTITY_NOT_FOUND:",
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ExtraPSKIdentity-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					ExtraPSKIdentity:   true,
+					SendExtraPSKBinder: true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+		})
+
+		// Test that unknown NewSessionTicket extensions are tolerated.
+		testCases = append(testCases, testCase{
+			name: "CustomTicketExtension-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomTicketExtension: "1234",
+				},
+			},
+			tls13Variant: variant,
+		})
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-RejectTicket-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Certificates:     []Certificate{rsaCertificate},
+			},
+			resumeConfig: &Config{
+				MaxVersion:             VersionTLS13,
+				MaxEarlyDataSize:       16384,
+				Certificates:           []Certificate{ecdsaP256Certificate},
+				SessionTicketsDisabled: true,
+			},
+			tls13Variant:         variant,
+			resumeSession:        true,
+			expectResumeRejected: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+				"-on-resume-shim-writes-first",
+				"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+				"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+				"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+				// Session tickets are disabled, so the runner will not send a ticket.
+				"-on-retry-expect-no-session",
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-HRR-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+			},
+		})
+
+		// The client must check the server does not send the early_data
+		// extension while rejecting the session.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyDataWithoutResume-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:             VersionTLS13,
+				SessionTicketsDisabled: true,
+				Bugs: ProtocolBugs{
+					SendEarlyDataExtension: true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+			},
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_EXTENSION:",
+		})
+
+		// The client must fail with a dedicated error code if the server
+		// responds with TLS 1.2 when offering 0-RTT.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyDataVersionDowngrade-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS12,
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+			},
+			shouldFail:    true,
+			expectedError: ":WRONG_VERSION_ON_EARLY_DATA:",
+		})
+
+		// Test that the client rejects an (unsolicited) early_data extension if
+		// the server sent an HRR.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "ServerAcceptsEarlyDataOnHRR-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
+					SendEarlyDataExtension:      true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+			},
+			shouldFail:    true,
+			expectedError: ":UNEXPECTED_EXTENSION:",
+		})
+
+		if isDraft22(version.versionWire) {
+			testCases = append(testCases, testCase{
+				testType: clientTest,
+				name:     "SkipChangeCipherSpec-Client-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					Bugs: ProtocolBugs{
+						SkipChangeCipherSpec: true,
+					},
+				},
+				tls13Variant: variant,
+			})
+
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "SkipChangeCipherSpec-Server-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					Bugs: ProtocolBugs{
+						SkipChangeCipherSpec: true,
+					},
+				},
+				tls13Variant: variant,
+			})
+
+			testCases = append(testCases, testCase{
+				testType: clientTest,
+				name:     "TooManyChangeCipherSpec-Client-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					Bugs: ProtocolBugs{
+						SendExtraChangeCipherSpec: 33,
+					},
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+			})
+
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "TooManyChangeCipherSpec-Server-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					Bugs: ProtocolBugs{
+						SendExtraChangeCipherSpec: 33,
+					},
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+			})
+
+			testCases = append(testCases, testCase{
+				name: "SendPostHandshakeChangeCipherSpec-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					Bugs: ProtocolBugs{
+						SendPostHandshakeChangeCipherSpec: true,
+					},
+				},
+				tls13Variant:       variant,
+				shouldFail:         true,
+				expectedError:      ":UNEXPECTED_RECORD:",
+				expectedLocalError: "remote error: unexpected message",
+			})
+		}
+
+		fooString := "foo"
+		barString := "bar"
+
+		// Test that the client reports the correct ALPN after a 0-RTT reject
+		// that changed it.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-ALPNMismatch-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ALPNProtocol: &fooString,
+				},
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ALPNProtocol: &barString,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-advertise-alpn", "\x03foo\x03bar",
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+				"-on-initial-expect-alpn", "foo",
+				"-on-resume-expect-alpn", "foo",
+				"-on-retry-expect-alpn", "bar",
+			},
+		})
+
+		// Test that the client reports the correct ALPN after a 0-RTT reject if
+		// ALPN was omitted from the first connection.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-ALPNOmitted1-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				NextProtos:       []string{"foo"},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-advertise-alpn", "\x03foo\x03bar",
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+				"-on-initial-expect-alpn", "",
+				"-on-resume-expect-alpn", "",
+				"-on-retry-expect-alpn", "foo",
+				"-on-resume-shim-writes-first",
+			},
+		})
+
+		// Test that the client reports the correct ALPN after a 0-RTT reject if
+		// ALPN was omitted from the second connection.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-ALPNOmitted2-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				NextProtos:       []string{"foo"},
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-advertise-alpn", "\x03foo\x03bar",
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-reject-early-data",
+				"-on-initial-expect-alpn", "foo",
+				"-on-resume-expect-alpn", "foo",
+				"-on-retry-expect-alpn", "",
+				"-on-resume-shim-writes-first",
+			},
+		})
+
+		// Test that the client enforces ALPN match on 0-RTT accept.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-BadALPNMismatch-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ALPNProtocol: &fooString,
+				},
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					AlwaysAcceptEarlyData: true,
+					ALPNProtocol:          &barString,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-advertise-alpn", "\x03foo\x03bar",
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-on-initial-expect-alpn", "foo",
+				"-on-resume-expect-alpn", "foo",
+				"-on-retry-expect-alpn", "bar",
+			},
+			shouldFail:    true,
+			expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
+		})
+
+		// Test that the client does not offer early data if it is incompatible
+		// with ALPN preferences.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-ALPNPreferenceChanged-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				NextProtos:       []string{"foo", "bar"},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-no-offer-early-data",
+				"-on-initial-advertise-alpn", "\x03foo",
+				"-on-resume-advertise-alpn", "\x03bar",
+				"-on-initial-expect-alpn", "foo",
+				"-on-resume-expect-alpn", "bar",
+			},
+		})
+
+		// Test that the server correctly rejects 0-RTT when the previous
+		// session did not allow early data on resumption.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-NonZeroRTTSession-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: false,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-on-resume-enable-early-data",
+				"-expect-reject-early-data",
+			},
+		})
+
+		// Test that we reject early data where ALPN is omitted from the first
+		// connection.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-ALPNOmitted1-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{},
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{"foo"},
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: false,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-on-initial-select-alpn", "",
+				"-on-resume-select-alpn", "foo",
+			},
+		})
+
+		// Test that we reject early data where ALPN is omitted from the second
+		// connection.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-ALPNOmitted2-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{"foo"},
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{},
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: false,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-on-initial-select-alpn", "foo",
+				"-on-resume-select-alpn", "",
+			},
+		})
+
+		// Test that we reject early data with mismatched ALPN.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-ALPNMismatch-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{"foo"},
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+				NextProtos: []string{"bar"},
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: false,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-on-initial-select-alpn", "foo",
+				"-on-resume-select-alpn", "bar",
+			},
+		})
+
+		// Test that the client offering 0-RTT and Channel ID forbids the server
+		// from accepting both.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyDataChannelID-AcceptBoth-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				RequestChannelID: true,
+			},
+			tls13Variant:    variant,
+			resumeSession:   true,
+			expectChannelID: true,
+			shouldFail:      true,
+			expectedError:   ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+			},
+		})
+
+		// Test that the client offering Channel ID and 0-RTT allows the server
+		// to decline 0-RTT.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyDataChannelID-AcceptChannelID-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				RequestChannelID: true,
+				Bugs: ProtocolBugs{
+					AlwaysRejectEarlyData: true,
+				},
+			},
+			tls13Variant:    variant,
+			resumeSession:   true,
+			expectChannelID: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+				"-expect-reject-early-data",
+			},
+		})
+
+		// Test that the client offering Channel ID and 0-RTT allows the server
+		// to decline Channel ID.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyDataChannelID-AcceptEarlyData-Client-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+				"-expect-accept-early-data",
+			},
+		})
+
+		// Test that the server supporting Channel ID and 0-RTT declines 0-RTT
+		// if it would negotiate Channel ID.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyDataChannelID-OfferBoth-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				ChannelID:  channelIDKey,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: false,
+				},
+			},
+			tls13Variant:    variant,
+			resumeSession:   true,
+			expectChannelID: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-reject-early-data",
+				"-expect-channel-id",
+				base64.StdEncoding.EncodeToString(channelIDBytes),
+			},
+		})
+
+		// Test that the server supporting Channel ID and 0-RTT accepts 0-RTT
+		// if not offered Channel ID.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyDataChannelID-OfferEarlyData-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				},
+			},
+			tls13Variant:    variant,
+			resumeSession:   true,
+			expectChannelID: false,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+				"-enable-channel-id",
+			},
+		})
+
+		// Test that the server rejects 0-RTT streams without end_of_early_data.
+		// The subsequent records should fail to decrypt.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-SkipEndOfEarlyData-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					SkipEndOfEarlyData:      true,
+				},
+			},
+			tls13Variant:       variant,
+			resumeSession:      true,
+			flags:              []string{"-enable-early-data"},
+			shouldFail:         true,
+			expectedLocalError: "remote error: bad record MAC",
+			expectedError:      ":BAD_DECRYPT:",
+		})
+
+		expectedError := ":UNEXPECTED_RECORD:"
+		if isDraft21(version.versionWire) {
+			// In draft-21 and up, early data is expected to be
+			// terminated by a handshake message, though we test
+			// with the wrong one.
+			expectedError = ":UNEXPECTED_MESSAGE:"
+		}
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-UnexpectedHandshake-Server-" + name,
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					SendStrayEarlyHandshake: true,
+					ExpectEarlyDataAccepted: true,
+				},
+			},
+			tls13Variant:       variant,
+			resumeSession:      true,
+			shouldFail:         true,
+			expectedError:      expectedError,
+			expectedLocalError: "remote error: unexpected message",
+			flags: []string{
+				"-enable-early-data",
+			},
+		})
+
+		// Test that the client reports TLS 1.3 as the version while sending
+		// early data.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-Client-VersionAPI-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-accept-early-data",
+				"-expect-version", strconv.Itoa(VersionTLS13),
+			},
+		})
+
+		// Test that client and server both notice handshake errors after data
+		// has started flowing.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "EarlyData-Client-BadFinished-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					BadFinished: true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-ticket-supports-early-data",
+				"-expect-accept-early-data",
+			},
+			shouldFail:         true,
+			expectedError:      ":DIGEST_CHECK_FAILED:",
+			expectedLocalError: "remote error: error decrypting message",
+		})
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "EarlyData-Server-BadFinished-" + name,
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+					ExpectEarlyDataAccepted: true,
+					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+					BadFinished:             true,
+				},
+			},
+			tls13Variant:  variant,
+			resumeSession: true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-accept-early-data",
+			},
+			shouldFail:         true,
+			expectedError:      ":DIGEST_CHECK_FAILED:",
+			expectedLocalError: "remote error: error decrypting message",
+		})
+
+		if isDraft21(version.versionWire) {
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "Server-NonEmptyEndOfEarlyData-" + name,
+				config: Config{
+					MaxVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+				},
+				resumeConfig: &Config{
+					MaxVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+					Bugs: ProtocolBugs{
+						SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+						ExpectEarlyDataAccepted: true,
+						NonEmptyEndOfEarlyData:  true,
+					},
+				},
+				resumeSession: true,
+				flags: []string{
+					"-enable-early-data",
+					"-expect-ticket-supports-early-data",
+					"-expect-accept-early-data",
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":DECODE_ERROR:",
+			})
+		}
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ServerSkipCertificateVerify-" + name,
+			config: Config{
+				MinVersion:   VersionTLS13,
+				MaxVersion:   VersionTLS13,
+				Certificates: []Certificate{rsaChainCertificate},
+				Bugs: ProtocolBugs{
+					SkipCertificateVerify: true,
+				},
+			},
+			tls13Variant:          variant,
+			expectPeerCertificate: &rsaChainCertificate,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+				"-require-any-client-certificate",
+			},
+			shouldFail:         true,
+			expectedError:      ":UNEXPECTED_MESSAGE:",
+			expectedLocalError: "remote error: unexpected message",
+		})
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "ClientSkipCertificateVerify-" + name,
+			config: Config{
+				MinVersion:   VersionTLS13,
+				MaxVersion:   VersionTLS13,
+				Certificates: []Certificate{rsaChainCertificate},
+				Bugs: ProtocolBugs{
+					SkipCertificateVerify: true,
+				},
+			},
+			tls13Variant:          variant,
+			expectPeerCertificate: &rsaChainCertificate,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+			},
+			shouldFail:         true,
+			expectedError:      ":UNEXPECTED_MESSAGE:",
+			expectedLocalError: "remote error: unexpected message",
+		})
 	}
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-OmitEarlyDataExtension",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-				OmitEarlyDataExtension:  true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-TooMuchData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 16384 + 1,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-Interleaved",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-				InterleaveEarlyData:     true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-EarlyDataInTLS12",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_RECORD:",
-		flags:         []string{"-max-version", strconv.Itoa(VersionTLS12)},
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-HRR",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-			},
-			DefaultCurves: []CurveID{},
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-HRR-Interleaved",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 4,
-				InterleaveEarlyData:     true,
-			},
-			DefaultCurves: []CurveID{},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_RECORD:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-HRR-TooMuchData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendFakeEarlyDataLength: 16384 + 1,
-			},
-			DefaultCurves: []CurveID{},
-		},
-		shouldFail:    true,
-		expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
-	})
-
-	// Test that skipping early data looking for cleartext correctly
-	// processes an alert record.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-HRR-FatalAlert",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyAlert:          true,
-				SendFakeEarlyDataLength: 4,
-			},
-			DefaultCurves: []CurveID{},
-		},
-		shouldFail:    true,
-		expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SkipEarlyData-SecondClientHelloEarlyData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyDataOnSecondClientHello: true,
-			},
-			DefaultCurves: []CurveID{},
-		},
-		shouldFail:         true,
-		expectedLocalError: "remote error: bad record MAC",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "EmptyEncryptedExtensions",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				EmptyEncryptedExtensions: true,
-			},
-		},
-		shouldFail:         true,
-		expectedLocalError: "remote error: error decoding message",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "EncryptedExtensionsWithKeyShare",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				EncryptedExtensionsWithKeyShare: true,
-			},
-		},
-		shouldFail:         true,
-		expectedLocalError: "remote error: unsupported extension",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SendHelloRetryRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// Require a HelloRetryRequest for every curve.
-			DefaultCurves: []CurveID{},
-		},
-		expectedCurveID: CurveX25519,
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SendHelloRetryRequest-2",
-		config: Config{
-			MaxVersion:    VersionTLS13,
-			DefaultCurves: []CurveID{CurveP384},
-		},
-		// Although the ClientHello did not predict our preferred curve,
-		// we always select it whether it is predicted or not.
-		expectedCurveID: CurveX25519,
-	})
-
-	testCases = append(testCases, testCase{
-		name: "UnknownCurve-HelloRetryRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCurve: bogusCurve,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13Draft21-HelloRetryRequest-CipherChange",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SendCipherSuite:                  TLS_AES_128_GCM_SHA256,
-				SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256,
-			},
-		},
-		tls13Variant:  TLS13Draft21,
-		shouldFail:    true,
-		expectedError: ":WRONG_CIPHER_RETURNED:",
-	})
-
-	// Test that the client does not offer a PSK in the second ClientHello if the
-	// HelloRetryRequest is incompatible with it.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13Draft21-HelloRetryRequest-NonResumableCipher",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			CipherSuites: []uint16{
-				TLS_AES_128_GCM_SHA256,
-			},
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				ExpectNoTLS13PSKAfterHRR: true,
-			},
-			CipherSuites: []uint16{
-				TLS_AES_256_GCM_SHA384,
-			},
-		},
-		tls13Variant:         TLS13Draft21,
-		resumeSession:        true,
-		expectResumeRejected: true,
-	})
-
-	testCases = append(testCases, testCase{
-		name: "DisabledCurve-HelloRetryRequest",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			CurvePreferences: []CurveID{CurveP256},
-			Bugs: ProtocolBugs{
-				IgnorePeerCurvePreferences: true,
-			},
-		},
-		flags:         []string{"-p384-only"},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "UnnecessaryHelloRetryRequest",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			CurvePreferences: []CurveID{CurveX25519},
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCurve: CurveX25519,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "SecondHelloRetryRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SecondHelloRetryRequest: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_MESSAGE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-Empty",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				AlwaysSendHelloRetryRequest: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-DuplicateCurve",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires a HelloRetryRequest against BoringSSL's default
-			// configuration. Assert this ExpectMissingKeyShare.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				ExpectMissingKeyShare:                true,
-				DuplicateHelloRetryRequestExtensions: true,
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":DUPLICATE_EXTENSION:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-Cookie",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie: []byte("cookie"),
-			},
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-DuplicateCookie",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie:          []byte("cookie"),
-				DuplicateHelloRetryRequestExtensions: true,
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":DUPLICATE_EXTENSION:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-EmptyCookie",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie: []byte{},
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-Cookie-Curve",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie: []byte("cookie"),
-				ExpectMissingKeyShare:       true,
-			},
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequest-Unknown",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				CustomHelloRetryRequestExtension: "extension",
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":UNEXPECTED_EXTENSION:",
-		expectedLocalError: "remote error: unsupported extension",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SecondClientHelloMissingKeyShare",
-		config: Config{
-			MaxVersion:    VersionTLS13,
-			DefaultCurves: []CurveID{},
-			Bugs: ProtocolBugs{
-				SecondClientHelloMissingKeyShare: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":MISSING_KEY_SHARE:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "SecondClientHelloWrongCurve",
-		config: Config{
-			MaxVersion:    VersionTLS13,
-			DefaultCurves: []CurveID{},
-			Bugs: ProtocolBugs{
-				MisinterpretHelloRetryRequestCurve: CurveP521,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequestVersionMismatch",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SendServerHelloVersion: 0x0305,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_VERSION_NUMBER:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "HelloRetryRequestCurveMismatch",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				// Send P-384 (correct) in the HelloRetryRequest.
-				SendHelloRetryRequestCurve: CurveP384,
-				// But send P-256 in the ServerHello.
-				SendCurve: CurveP256,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	// Test the server selecting a curve that requires a HelloRetryRequest
-	// without sending it.
-	testCases = append(testCases, testCase{
-		name: "SkipHelloRetryRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			// P-384 requires HelloRetryRequest in BoringSSL.
-			CurvePreferences: []CurveID{CurveP384},
-			Bugs: ProtocolBugs{
-				SkipHelloRetryRequest: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_CURVE:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13-RequestContextInHandshake",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			MinVersion: VersionTLS13,
-			ClientAuth: RequireAnyClientCert,
-			Bugs: ProtocolBugs{
-				SendRequestContext: []byte("request context"),
-			},
-		},
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13-UnknownInCertificateRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			MinVersion: VersionTLS13,
-			ClientAuth: RequireAnyClientCert,
-			Bugs: ProtocolBugs{
-				SendCustomCertificateRequest: 0x1212,
-			},
-		},
-		tls13Variant: TLS13Draft21,
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13-MissingSignatureAlgorithmsInCertificateRequest",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			MinVersion: VersionTLS13,
-			ClientAuth: RequireAnyClientCert,
-			Bugs: ProtocolBugs{
-				OmitCertificateRequestAlgorithms: true,
-			},
-		},
-		tls13Variant: TLS13Draft21,
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaKeyFile),
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-TrailingKeyShareData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				TrailingKeyShareData: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13-AlwaysSelectPSKIdentity",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				AlwaysSelectPSKIdentity: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13-InvalidPSKIdentity",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SelectPSKIdentityOnResume: 1,
-			},
-		},
-		resumeSession: true,
-		shouldFail:    true,
-		expectedError: ":PSK_IDENTITY_NOT_FOUND:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-ExtraPSKIdentity",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				ExtraPSKIdentity:   true,
-				SendExtraPSKBinder: true,
-			},
-		},
-		resumeSession: true,
-	})
-
-	// Test that unknown NewSessionTicket extensions are tolerated.
-	testCases = append(testCases, testCase{
-		name: "TLS13-CustomTicketExtension",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				CustomTicketExtension: "1234",
-			},
-		},
-	})
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-RejectTicket-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Certificates:     []Certificate{rsaCertificate},
-		},
-		resumeConfig: &Config{
-			MaxVersion:             VersionTLS13,
-			MaxEarlyDataSize:       16384,
-			Certificates:           []Certificate{ecdsaP256Certificate},
-			SessionTicketsDisabled: true,
-		},
-		resumeSession:        true,
-		expectResumeRejected: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-			"-on-resume-shim-writes-first",
-			"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-			"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
-			// Session tickets are disabled, so the runner will not send a ticket.
-			"-on-retry-expect-no-session",
-		},
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-HRR-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-		},
-	})
-
-	// The client must check the server does not send the early_data
-	// extension while rejecting the session.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyDataWithoutResume-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:             VersionTLS13,
-			SessionTicketsDisabled: true,
-			Bugs: ProtocolBugs{
-				SendEarlyDataExtension: true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
-	// The client must fail with a dedicated error code if the server
-	// responds with TLS 1.2 when offering 0-RTT.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyDataVersionDowngrade-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS12,
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-		},
-		shouldFail:    true,
-		expectedError: ":WRONG_VERSION_ON_EARLY_DATA:",
-	})
-
-	// Test that the client rejects an (unsolicited) early_data extension if
-	// the server sent an HRR.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-ServerAcceptsEarlyDataOnHRR-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
-				SendEarlyDataExtension:      true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13Draft22-SkipChangeCipherSpec-Client",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SkipChangeCipherSpec: true,
-			},
-		},
-		tls13Variant: TLS13Draft22,
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13Draft22-SkipChangeCipherSpec-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SkipChangeCipherSpec: true,
-			},
-		},
-		tls13Variant: TLS13Draft22,
-	})
-
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13Draft22-TooManyChangeCipherSpec-Client",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendExtraChangeCipherSpec: 33,
-			},
-		},
-		tls13Variant:  TLS13Draft22,
-		shouldFail:    true,
-		expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13Draft22-TooManyChangeCipherSpec-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendExtraChangeCipherSpec: 33,
-			},
-		},
-		tls13Variant:  TLS13Draft22,
-		shouldFail:    true,
-		expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
-	})
-
-	testCases = append(testCases, testCase{
-		name: "TLS13Draft22-SendPostHandshakeChangeCipherSpec",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendPostHandshakeChangeCipherSpec: true,
-			},
-		},
-		tls13Variant:       TLS13Draft22,
-		shouldFail:         true,
-		expectedError:      ":UNEXPECTED_RECORD:",
-		expectedLocalError: "remote error: unexpected message",
-	})
-
-	fooString := "foo"
-	barString := "bar"
-
-	// Test that the client reports the correct ALPN after a 0-RTT reject
-	// that changed it.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-ALPNMismatch-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				ALPNProtocol: &fooString,
-			},
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				ALPNProtocol: &barString,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-advertise-alpn", "\x03foo\x03bar",
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "foo",
-			"-on-retry-expect-alpn", "bar",
-		},
-	})
-
-	// Test that the client reports the correct ALPN after a 0-RTT reject if
-	// ALPN was omitted from the first connection.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-ALPNOmitted1-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			NextProtos:       []string{"foo"},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-advertise-alpn", "\x03foo\x03bar",
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-			"-on-initial-expect-alpn", "",
-			"-on-resume-expect-alpn", "",
-			"-on-retry-expect-alpn", "foo",
-			"-on-resume-shim-writes-first",
-		},
-	})
-
-	// Test that the client reports the correct ALPN after a 0-RTT reject if
-	// ALPN was omitted from the second connection.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-ALPNOmitted2-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			NextProtos:       []string{"foo"},
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeSession: true,
-		flags: []string{
-			"-advertise-alpn", "\x03foo\x03bar",
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-reject-early-data",
-			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "foo",
-			"-on-retry-expect-alpn", "",
-			"-on-resume-shim-writes-first",
-		},
-	})
-
-	// Test that the client enforces ALPN match on 0-RTT accept.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-BadALPNMismatch-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				ALPNProtocol: &fooString,
-			},
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				AlwaysAcceptEarlyData: true,
-				ALPNProtocol:          &barString,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-advertise-alpn", "\x03foo\x03bar",
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "foo",
-			"-on-retry-expect-alpn", "bar",
-		},
-		shouldFail:    true,
-		expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
-	})
-
-	// Test that the client does not offer early data if it is incompatible
-	// with ALPN preferences.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-ALPNPreferenceChanged",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			NextProtos:       []string{"foo", "bar"},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-no-offer-early-data",
-			"-on-initial-advertise-alpn", "\x03foo",
-			"-on-resume-advertise-alpn", "\x03bar",
-			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "bar",
-		},
-	})
-
-	// Test that the server correctly rejects 0-RTT when the previous
-	// session did not allow early data on resumption.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-NonZeroRTTSession-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: false,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-on-resume-enable-early-data",
-			"-expect-reject-early-data",
-		},
-	})
-
-	// Test that we reject early data where ALPN is omitted from the first
-	// connection.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-ALPNOmitted1-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{},
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{"foo"},
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: false,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-on-initial-select-alpn", "",
-			"-on-resume-select-alpn", "foo",
-		},
-	})
-
-	// Test that we reject early data where ALPN is omitted from the second
-	// connection.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-ALPNOmitted2-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{"foo"},
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{},
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: false,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-on-initial-select-alpn", "foo",
-			"-on-resume-select-alpn", "",
-		},
-	})
-
-	// Test that we reject early data with mismatched ALPN.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-ALPNMismatch-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{"foo"},
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			NextProtos: []string{"bar"},
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: false,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-on-initial-select-alpn", "foo",
-			"-on-resume-select-alpn", "bar",
-		},
-	})
-
-	// Test that the client offering 0-RTT and Channel ID forbids the server
-	// from accepting both.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyDataChannelID-AcceptBoth-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			RequestChannelID: true,
-		},
-		resumeSession:   true,
-		expectChannelID: true,
-		shouldFail:      true,
-		expectedError:   ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
-		},
-	})
-
-	// Test that the client offering Channel ID and 0-RTT allows the server
-	// to decline 0-RTT.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyDataChannelID-AcceptChannelID-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			RequestChannelID: true,
-			Bugs: ProtocolBugs{
-				AlwaysRejectEarlyData: true,
-			},
-		},
-		resumeSession:   true,
-		expectChannelID: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
-			"-expect-reject-early-data",
-		},
-	})
-
-	// Test that the client offering Channel ID and 0-RTT allows the server
-	// to decline Channel ID.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyDataChannelID-AcceptEarlyData-Client",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
-			"-expect-accept-early-data",
-		},
-	})
-
-	// Test that the server supporting Channel ID and 0-RTT declines 0-RTT
-	// if it would negotiate Channel ID.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyDataChannelID-OfferBoth-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			ChannelID:  channelIDKey,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: false,
-			},
-		},
-		resumeSession:   true,
-		expectChannelID: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-reject-early-data",
-			"-expect-channel-id",
-			base64.StdEncoding.EncodeToString(channelIDBytes),
-		},
-	})
-
-	// Test that the server supporting Channel ID and 0-RTT accepts 0-RTT
-	// if not offered Channel ID.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyDataChannelID-OfferEarlyData-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: true,
-				ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
-			},
-		},
-		resumeSession:   true,
-		expectChannelID: false,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-accept-early-data",
-			"-enable-channel-id",
-		},
-	})
-
-	// Test that the server rejects 0-RTT streams without end_of_early_data.
-	// The subsequent records should fail to decrypt.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-SkipEndOfEarlyData",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: true,
-				SkipEndOfEarlyData:      true,
-			},
-		},
-		resumeSession:      true,
-		flags:              []string{"-enable-early-data"},
-		shouldFail:         true,
-		expectedLocalError: "remote error: bad record MAC",
-		expectedError:      ":BAD_DECRYPT:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-UnexpectedHandshake-Server",
-		config: Config{
-			MaxVersion: VersionTLS13,
-		},
-		resumeConfig: &Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				SendStrayEarlyHandshake: true,
-				ExpectEarlyDataAccepted: true,
-			},
-		},
-		resumeSession:      true,
-		shouldFail:         true,
-		expectedError:      ":UNEXPECTED_RECORD:",
-		expectedLocalError: "remote error: unexpected message",
-		flags: []string{
-			"-enable-early-data",
-		},
-	})
-
-	// Test that the client reports TLS 1.3 as the version while sending
-	// early data.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-Client-VersionAPI",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-accept-early-data",
-			"-expect-version", strconv.Itoa(VersionTLS13),
-		},
-	})
-
-	// Test that client and server both notice handshake errors after data
-	// has started flowing.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-EarlyData-Client-BadFinished",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				BadFinished: true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-accept-early-data",
-		},
-		shouldFail:         true,
-		expectedError:      ":DIGEST_CHECK_FAILED:",
-		expectedLocalError: "remote error: error decrypting message",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-EarlyData-Server-BadFinished",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: true,
-				ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
-				BadFinished:             true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-accept-early-data",
-		},
-		shouldFail:         true,
-		expectedError:      ":DIGEST_CHECK_FAILED:",
-		expectedLocalError: "remote error: error decrypting message",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13Draft21-Server-NonEmptyEndOfEarlyData",
-		config: Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-		},
-		resumeConfig: &Config{
-			MaxVersion:       VersionTLS13,
-			MaxEarlyDataSize: 16384,
-			Bugs: ProtocolBugs{
-				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
-				ExpectEarlyDataAccepted: true,
-				NonEmptyEndOfEarlyData:  true,
-			},
-		},
-		resumeSession: true,
-		flags: []string{
-			"-enable-early-data",
-			"-expect-ticket-supports-early-data",
-			"-expect-accept-early-data",
-		},
-		tls13Variant:  TLS13Draft21,
-		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
-	})
-
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "TLS13-ServerSkipCertificateVerify",
-		config: Config{
-			MinVersion:   VersionTLS13,
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaChainCertificate},
-			Bugs: ProtocolBugs{
-				SkipCertificateVerify: true,
-			},
-		},
-		expectPeerCertificate: &rsaChainCertificate,
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
-			"-require-any-client-certificate",
-		},
-		shouldFail:         true,
-		expectedError:      ":UNEXPECTED_MESSAGE:",
-		expectedLocalError: "remote error: unexpected message",
-	})
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "TLS13-ClientSkipCertificateVerify",
-		config: Config{
-			MinVersion:   VersionTLS13,
-			MaxVersion:   VersionTLS13,
-			Certificates: []Certificate{rsaChainCertificate},
-			Bugs: ProtocolBugs{
-				SkipCertificateVerify: true,
-			},
-		},
-		expectPeerCertificate: &rsaChainCertificate,
-		flags: []string{
-			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
-			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
-		},
-		shouldFail:         true,
-		expectedError:      ":UNEXPECTED_MESSAGE:",
-		expectedLocalError: "remote error: unexpected message",
-	})
 }
 
 func addTLS13CipherPreferenceTests() {
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 0013e61..688fa06 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -52,6 +52,7 @@
 
 static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  assert(ssl->s3->have_version);
   SSLMessage msg;
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
@@ -60,6 +61,14 @@
   CBS extensions;
   uint16_t cipher_suite = 0;
   if (ssl_is_draft22(ssl->version)) {
+    // Queue up a ChangeCipherSpec for whenever we next send something. This
+    // will be before the second ClientHello. If we offered early data, this was
+    // already done.
+    if (!hs->early_data_offered &&
+        !ssl->method->add_change_cipher_spec(ssl)) {
+      return ssl_hs_error;
+    }
+
     if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
       return ssl_hs_error;
     }
@@ -95,8 +104,6 @@
         (ssl_is_draft21(ssl->version) &&
          !CBS_get_u16(&body, &cipher_suite)) ||
         !CBS_get_u16_length_prefixed(&body, &extensions) ||
-        // HelloRetryRequest may not be empty.
-        CBS_len(&extensions) == 0 ||
         CBS_len(&body) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -146,6 +153,11 @@
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
     return ssl_hs_error;
   }
+  if (!have_cookie && !have_key_share) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_HELLO_RETRY_REQUEST);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
   if (have_cookie) {
     CBS cookie_value;
     if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
@@ -259,7 +271,14 @@
     return ssl_hs_error;
   }
 
-  assert(ssl->s3->have_version);
+  // Forbid a second HelloRetryRequest.
+  if (ssl_is_draft22(ssl->version) &&
+      CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return ssl_hs_error;
+  }
+
   OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
                  SSL3_RANDOM_SIZE);
 
@@ -401,11 +420,17 @@
   }
 
   if (!hs->early_data_offered) {
+    // Earlier versions of the resumption experiment added ChangeCipherSpec just
+    // before the Finished flight.
+    if (ssl_is_resumption_client_ccs_experiment(ssl->version) &&
+        !ssl_is_draft22(ssl->version) &&
+        !ssl->method->add_change_cipher_spec(ssl)) {
+      return ssl_hs_error;
+    }
+
     // If not sending early data, set client traffic keys now so that alerts are
     // encrypted.
-    if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
-         !ssl->method->add_change_cipher_spec(ssl)) ||
-        !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
+    if (!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
                                hs->hash_len)) {
       return ssl_hs_error;
     }