Fix DTLS cross-version resumption tests

For now, just have runner tolerate the PSK extension changing across
HVR. It's possible we'll want to change this, but I think we can resolve
that separately.

The tests required an additional fix to deal with a syntactic
impossibility in DTLS. (Also QUIC, if TLS 1.2 in QUIC existed.)

Bug: 42290594
Change-Id: Ia06975104933e049bcd2c62aa8a29fd74b20f474
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/73827
Reviewed-by: Nick Harper <nharper@chromium.org>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 7f890fd..2198ffd 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -323,7 +323,12 @@
 		if !bytes.Equal(newClientHello.cookie, helloVerifyRequest.cookie) {
 			return errors.New("dtls: invalid cookie")
 		}
-		if err := checkClientHellosEqual(hs.clientHello.raw, newClientHello.raw, c.isDTLS, nil); err != nil {
+		// Allow the client to recompute the pre_shared_key extension. It is
+		// possible we'll want to remove this and instead require the client
+		// keep it unchanged. (Either by remembering it or hashing the
+		// ClientHello funny.) See discussion at
+		// https://mailarchive.ietf.org/arch/msg/tls/HSTgV2voS9tn03oasGmBAdwazWA/
+		if err := checkClientHellosEqual(hs.clientHello.raw, newClientHello.raw, c.isDTLS, []uint16{extensionPreSharedKey}); err != nil {
 			return err
 		}
 		hs.clientHello = newClientHello
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 84fc93e..25651f6 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -9245,13 +9245,6 @@
 				suffix := "-" + sessionVers.name + "-" + resumeVers.name
 				suffix += "-" + protocol.String()
 
-				// TODO(crbug.com/42290594): Attempting to resume a DTLS 1.3 session
-				// when the new connection is an older version is currently broken.
-				// The current implementation recomputes the PSK binder value for the
-				// second ClientHello (sent in response to HelloVerifyRequest), but
-				// the runner expects all extension values to stay byte-for-byte the
-				// same (a possible interpretation of RFC 6347 section 4.2.1).
-				isBadDTLSResumption := protocol == dtls && sessionVers.version == VersionTLS13 && resumeVers.version < VersionTLS13
 				if sessionVers.version == resumeVers.version {
 					testCases = append(testCases, testCase{
 						protocol:      protocol,
@@ -9270,7 +9263,37 @@
 							version: resumeVers.version,
 						},
 					})
-				} else if !isBadDTLSResumption {
+				} else if protocol != tls && sessionVers.version >= VersionTLS13 && resumeVers.version < VersionTLS13 {
+					// In TLS 1.2 and below, the server indicates resumption by echoing
+					// the client's session ID, which is impossible if the client did
+					// not send a session ID. If the client offers a TLS 1.3 session, it
+					// only fills in session ID in TLS (not DTLS or QUIC) for middlebox
+					// compatibility mode. So, instead, test that the session ID was
+					// empty and it was indeed impossible to hit this path
+					testCases = append(testCases, testCase{
+						protocol:      protocol,
+						name:          "Resume-Client-Impossible" + suffix,
+						resumeSession: true,
+						config: Config{
+							MaxVersion: sessionVers.version,
+						},
+						expectations: connectionExpectations{
+							version: sessionVers.version,
+						},
+						resumeConfig: &Config{
+							MaxVersion: resumeVers.version,
+							Bugs: ProtocolBugs{
+								ExpectNoSessionID: true,
+							},
+						},
+						resumeExpectations: &connectionExpectations{
+							version: resumeVers.version,
+						},
+						expectResumeRejected: true,
+					})
+				} else {
+					// Test that the client rejects ServerHellos which resume
+					// sessions at inconsistent versions.
 					expectedError := ":OLD_SESSION_VERSION_NOT_RETURNED:"
 					if sessionVers.version < VersionTLS13 && resumeVers.version >= VersionTLS13 {
 						// The server will "resume" the session by sending pre_shared_key,
@@ -9278,6 +9301,7 @@
 						// should reject this because the extension was not allowed at all.
 						expectedError = ":UNEXPECTED_EXTENSION:"
 					}
+
 					testCases = append(testCases, testCase{
 						protocol:      protocol,
 						name:          "Resume-Client-Mismatch" + suffix,
@@ -9302,27 +9326,25 @@
 					})
 				}
 
-				if !isBadDTLSResumption {
-					testCases = append(testCases, testCase{
-						protocol:      protocol,
-						name:          "Resume-Client-NoResume" + suffix,
-						resumeSession: true,
-						config: Config{
-							MaxVersion: sessionVers.version,
-						},
-						expectations: connectionExpectations{
-							version: sessionVers.version,
-						},
-						resumeConfig: &Config{
-							MaxVersion: resumeVers.version,
-						},
-						newSessionsOnResume:  true,
-						expectResumeRejected: true,
-						resumeExpectations: &connectionExpectations{
-							version: resumeVers.version,
-						},
-					})
-				}
+				testCases = append(testCases, testCase{
+					protocol:      protocol,
+					name:          "Resume-Client-NoResume" + suffix,
+					resumeSession: true,
+					config: Config{
+						MaxVersion: sessionVers.version,
+					},
+					expectations: connectionExpectations{
+						version: sessionVers.version,
+					},
+					resumeConfig: &Config{
+						MaxVersion: resumeVers.version,
+					},
+					newSessionsOnResume:  true,
+					expectResumeRejected: true,
+					resumeExpectations: &connectionExpectations{
+						version: resumeVers.version,
+					},
+				})
 
 				testCases = append(testCases, testCase{
 					protocol:      protocol,