runner: Test different V2ClientHello challenge lengths.

The challenge field, at least per our implementation and OpenSSL, may be
either left-padded or truncated to form the ClientHello random. Test
both cases, as well as an exact match.

Change-Id: Icdedf899ef483225d8ed20580ad15818b5e52e91
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46631
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 3f0d245..83439a0 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -760,6 +760,10 @@
 	// instead of a normal ClientHello.
 	SendV2ClientHello bool
 
+	// V2ClientHelloChallengeLength is the length of the challenge field to send
+	// in V2ClientHello.
+	V2ClientHelloChallengeLength int
+
 	// SendFallbackSCSV causes the client to include
 	// TLS_FALLBACK_SCSV in the ClientHello.
 	SendFallbackSCSV bool
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 6ba4e4b..d368701 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -471,11 +471,28 @@
 	var helloBytes []byte
 	if c.config.Bugs.SendV2ClientHello {
 		hello.isV2ClientHello = true
+
 		// The V2ClientHello "challenge" field is variable-length and is
-		// left-padded to become the SSL3/TLS random. Test this behavior by
-		// forcing the left padding.
-		hello.random[0] = 0
-		hello.v2Challenge = hello.random[1:]
+		// left-padded or truncated to become the SSL3/TLS random.
+		challengeLength := c.config.Bugs.V2ClientHelloChallengeLength
+		if challengeLength == 0 {
+			challengeLength = len(hello.random)
+		}
+		if challengeLength <= len(hello.random) {
+			skip := len(hello.random) - challengeLength
+			for i := 0; i < skip; i++ {
+				hello.random[i] = 0
+			}
+			hello.v2Challenge = hello.random[skip:]
+		} else {
+			hello.v2Challenge = make([]byte, challengeLength)
+			copy(hello.v2Challenge, hello.random)
+			if _, err := io.ReadFull(c.config.rand(), hello.v2Challenge[len(hello.random):]); err != nil {
+				c.sendAlert(alertInternalError)
+				return fmt.Errorf("tls: short read from Rand: %s", err)
+			}
+		}
+
 		helloBytes = hello.marshal()
 		c.writeV2Record(helloBytes)
 	} else {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index e648b68..0e250c2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5628,23 +5628,26 @@
 			shimWritesFirst: true,
 		})
 
-		// Server parses a V2ClientHello.
-		tests = append(tests, testCase{
-			testType: serverTest,
-			name:     "SendV2ClientHello",
-			config: Config{
-				// Choose a cipher suite that does not involve
-				// elliptic curves, so no extensions are
-				// involved.
-				MaxVersion:   VersionTLS12,
-				CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA},
-				Bugs: ProtocolBugs{
-					SendV2ClientHello: true,
+		// Server parses a V2ClientHello. Test different lengths for the
+		// challenge field.
+		for _, challengeLength := range []int{16, 31, 32, 33, 48} {
+			tests = append(tests, testCase{
+				testType: serverTest,
+				name:     fmt.Sprintf("SendV2ClientHello-%d", challengeLength),
+				config: Config{
+					// Choose a cipher suite that does not involve
+					// elliptic curves, so no extensions are
+					// involved.
+					MaxVersion:   VersionTLS12,
+					CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+					Bugs: ProtocolBugs{
+						SendV2ClientHello:            true,
+						V2ClientHelloChallengeLength: challengeLength,
+					},
 				},
-			},
-			flags: []string{
-				"-expect-msg-callback",
-				`read v2clienthello
+				flags: []string{
+					"-expect-msg-callback",
+					`read v2clienthello
 write hs 2
 write hs 11
 write hs 14
@@ -5655,8 +5658,9 @@
 write hs 20
 read alert 1 0
 `,
-			},
-		})
+				},
+			})
+		}
 
 		// Channel ID and NPN at the same time, to ensure their relative
 		// ordering is correct.