Enforce that pre_shared_key must come with psk_key_exchange_modes.

Omitting the extension means we'll never issue tickets, but if the
client were to offer a ticket anyway, RFC8446 4.2.9 says we MUST reject
the ClientHello. It's not clear on what alert to use, but
missing_extension is probably appropriate.

Thanks to Ben Kaduk for pointing this out.

Change-Id: Ie5c720eac9dd2e1a27ba8a13c59b707c109eaa4e
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46464
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index f209f4a..34f0fd1 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1240,8 +1240,8 @@
 	// session ticket.
 	SendEmptySessionTicket bool
 
-	// SendPSKKeyExchangeModes, if present, determines the PSK key exchange modes
-	// to send.
+	// SendPSKKeyExchangeModes, if not nil, determines the PSK key exchange
+	// modes to send. If a non-nil empty slice, no extension will be sent.
 	SendPSKKeyExchangeModes []byte
 
 	// ExpectNoNewSessionTicket, if present, means that the client will fail upon
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index efb8a18..510cbce 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -186,7 +186,7 @@
 		hello.supportedCurves = nil
 	}
 
-	if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
+	if c.config.Bugs.SendPSKKeyExchangeModes != nil {
 		hello.pskKEModes = c.config.Bugs.SendPSKKeyExchangeModes
 	}
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 86974e9..e555f46 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -12169,6 +12169,26 @@
 		expectResumeRejected: true,
 	})
 
+	// Test that the server rejects ClientHellos with pre_shared_key but without
+	// psk_key_exchange_modes.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-SendNoKEMModesWithPSK-Server",
+		config: Config{
+			MaxVersion: VersionTLS13,
+		},
+		resumeConfig: &Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendPSKKeyExchangeModes: []byte{},
+			},
+		},
+		resumeSession:      true,
+		shouldFail:         true,
+		expectedLocalError: "remote error: missing extension",
+		expectedError:      ":MISSING_EXTENSION:",
+	})
+
 	// Test that the client ticket age is sent correctly.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 6e9cd07..d33afa5 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -252,6 +252,16 @@
     return ssl_ticket_aead_ignore_ticket;
   }
 
+  // Per RFC8446, section 4.2.9, servers MUST abort the handshake if the client
+  // sends pre_shared_key without psk_key_exchange_modes.
+  CBS unused;
+  if (!ssl_client_hello_get_extension(client_hello, &unused,
+                                      TLSEXT_TYPE_psk_key_exchange_modes)) {
+    *out_alert = SSL_AD_MISSING_EXTENSION;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
+    return ssl_ticket_aead_error;
+  }
+
   CBS ticket, binders;
   uint32_t client_ticket_age;
   if (!ssl_ext_pre_shared_key_parse_clienthello(