Enforce supported_versions in the second ServerHello.

We forgot to do this in our original implementation on general ecosystem
grounds. It's also mandated starting draft-26.

Just to avoid unnecessary turbulence, since draft-23 is doomed to die
anyway, condition this on our draft-28 implementation. (We don't support
24 through 27.)

We'd actually checked this already on the Go side, but the spec wants a
different alert.

Change-Id: I0014cda03d7129df0b48de077e45f8ae9fd16976
Reviewed-on: https://boringssl-review.googlesource.com/28124
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index 7b63bc8..6cedfe2 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -133,6 +133,7 @@
 SSL,204,RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION
 SSL,205,RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION
 SSL,206,SCSV_RECEIVED_WHEN_RENEGOTIATING
+SSL,288,SECOND_SERVERHELLO_VERSION_MISMATCH
 SSL,207,SERVERHELLO_TLSEXT
 SSL,273,SERVER_CERT_CHANGED
 SSL,286,SERVER_ECHOED_INVALID_SESSION_ID
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 5fe394d..1ad7042 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4746,6 +4746,7 @@
 #define SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI 285
 #define SSL_R_SERVER_ECHOED_INVALID_SESSION_ID 286
 #define SSL_R_PRIVATE_KEY_OPERATION_FAILED 287
+#define SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH 288
 #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/common.go b/ssl/test/runner/common.go
index 7115b4d..f5ddaed 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1395,9 +1395,15 @@
 	// specified value in ServerHello version field.
 	SendServerHelloVersion uint16
 
-	// SendServerSupportedExtensionVersion, if non-zero, causes the server to send
-	// the specified value in supported_versions extension in the ServerHello.
-	SendServerSupportedExtensionVersion uint16
+	// SendServerSupportedVersionExtension, if non-zero, causes the server to send
+	// the specified value in supported_versions extension in the ServerHello (but
+	// not the HelloRetryRequest).
+	SendServerSupportedVersionExtension uint16
+
+	// OmitServerSupportedVersionExtension, if true, causes the server to
+	// omit the supported_versions extension in the ServerHello (but not the
+	// HelloRetryRequest)
+	OmitServerSupportedVersionExtension bool
 
 	// SkipHelloRetryRequest, if true, causes the TLS 1.3 server to not send
 	// HelloRetryRequest.
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 4178dc1..6f6f440 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -590,7 +590,7 @@
 	}
 
 	if serverWireVersion != serverHello.vers {
-		c.sendAlert(alertProtocolVersion)
+		c.sendAlert(alertIllegalParameter)
 		return fmt.Errorf("tls: server sent non-matching version %x vs %x", serverWireVersion, serverHello.vers)
 	}
 
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 64936b4..745c8f3 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -919,6 +919,7 @@
 	vers                  uint16
 	versOverride          uint16
 	supportedVersOverride uint16
+	omitSupportedVers     bool
 	random                []byte
 	sessionId             []byte
 	cipherSuite           uint16
@@ -979,12 +980,14 @@
 			extensions.addU16(2) // Length
 			extensions.addU16(m.pskIdentity)
 		}
-		extensions.addU16(extensionSupportedVersions)
-		extensions.addU16(2) // Length
-		if m.supportedVersOverride != 0 {
-			extensions.addU16(m.supportedVersOverride)
-		} else {
-			extensions.addU16(m.vers)
+		if !m.omitSupportedVers {
+			extensions.addU16(extensionSupportedVersions)
+			extensions.addU16(2) // Length
+			if m.supportedVersOverride != 0 {
+				extensions.addU16(m.supportedVersOverride)
+			} else {
+				extensions.addU16(m.vers)
+			}
 		}
 		if len(m.customExtension) > 0 {
 			extensions.addU16(extensionCustom)
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index e5f2944..da6fddc 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -375,7 +375,8 @@
 		sessionId:             hs.clientHello.sessionId,
 		compressionMethod:     config.Bugs.SendCompressionMethod,
 		versOverride:          config.Bugs.SendServerHelloVersion,
-		supportedVersOverride: config.Bugs.SendServerSupportedExtensionVersion,
+		supportedVersOverride: config.Bugs.SendServerSupportedVersionExtension,
+		omitSupportedVers:     config.Bugs.OmitServerSupportedVersionExtension,
 		customExtension:       config.Bugs.CustomUnencryptedExtension,
 		unencryptedALPN:       config.Bugs.SendUnencryptedALPN,
 	}
@@ -1122,7 +1123,7 @@
 		versOverride:      config.Bugs.SendServerHelloVersion,
 		compressionMethod: config.Bugs.SendCompressionMethod,
 		extensions: serverExtensions{
-			supportedVersion: config.Bugs.SendServerSupportedExtensionVersion,
+			supportedVersion: config.Bugs.SendServerSupportedVersionExtension,
 		},
 		omitExtensions:  config.Bugs.OmitExtensions,
 		emptyExtensions: config.Bugs.EmptyExtensions,
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 16c039f..0ad00c2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5592,7 +5592,7 @@
 		config: Config{
 			MaxVersion: VersionTLS12,
 			Bugs: ProtocolBugs{
-				SendServerSupportedExtensionVersion: VersionTLS12,
+				SendServerSupportedVersionExtension: VersionTLS12,
 			},
 		},
 		shouldFail:    true,
@@ -12757,6 +12757,39 @@
 			expectedError: ":WRONG_CURVE:",
 		})
 
+		// Test that the supported_versions extension is enforced in the
+		// second ServerHello. Note we only enforce this starting draft 28.
+		if isDraft28(version.versionWire) {
+			testCases = append(testCases, testCase{
+				name: "SecondServerHelloNoVersion-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					// P-384 requires HelloRetryRequest in BoringSSL.
+					CurvePreferences: []CurveID{CurveP384},
+					Bugs: ProtocolBugs{
+						OmitServerSupportedVersionExtension: true,
+					},
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:",
+			})
+			testCases = append(testCases, testCase{
+				name: "SecondServerHelloWrongVersion-" + name,
+				config: Config{
+					MaxVersion: VersionTLS13,
+					// P-384 requires HelloRetryRequest in BoringSSL.
+					CurvePreferences: []CurveID{CurveP384},
+					Bugs: ProtocolBugs{
+						SendServerSupportedVersionExtension: 0x1234,
+					},
+				},
+				tls13Variant:  variant,
+				shouldFail:    true,
+				expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:",
+			})
+		}
+
 		testCases = append(testCases, testCase{
 			name: "RequestContextInHandshake-" + name,
 			config: Config{
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 579e6a6..49f528b 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -290,6 +290,18 @@
     return ssl_hs_error;
   }
 
+  if (ssl_is_draft28(ssl->version)) {
+    // Recheck supported_versions, in case this is the second ServerHello.
+    uint16_t version;
+    if (!have_supported_versions ||
+        !CBS_get_u16(&supported_versions, &version) ||
+        version != ssl->version) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      return ssl_hs_error;
+    }
+  }
+
   alert = SSL_AD_DECODE_ERROR;
   if (have_pre_shared_key) {
     if (ssl->session == NULL) {