Test renegotiation with BoringSSL as the client.

This also contains a test for the issue fixed in
88333ef7d7d47221ede66a2a31626fc426466297.

Change-Id: Id705a82cee34c018491dc301eba8b5097b9c83d5
Reviewed-on: https://boringssl-review.googlesource.com/2083
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 897175e..e76f9d1 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -1031,6 +1031,16 @@
 	return c.Handshake()
 }
 
+func (c *Conn) Renegotiate() error {
+	if !c.isClient {
+		helloReq := new(helloRequestMsg)
+		c.writeRecord(recordTypeHandshake, helloReq.marshal())
+	}
+
+	c.handshakeComplete = false
+	return c.Handshake()
+}
+
 // Read can be made to time out and return a net.Error with Timeout() == true
 // after a fixed time limit; see SetDeadline and SetReadDeadline.
 func (c *Conn) Read(b []byte) (n int, err error) {
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 3288b0d..41d588a 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -215,11 +215,21 @@
 		return false, err
 	}
 
-	if len(hs.clientHello.secureRenegotiation) > 1 {
+	if !bytes.Equal(c.clientVerify, hs.clientHello.secureRenegotiation) {
 		c.sendAlert(alertHandshakeFailure)
-		return false, errors.New("tls: client is doing a renegotiation handshake")
+		return false, errors.New("tls: renegotiation mismatch")
 	}
-	hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
+
+	if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo {
+		hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.clientVerify...)
+		hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.serverVerify...)
+		if c.config.Bugs.BadRenegotiationInfo {
+			hs.hello.secureRenegotiation[0] ^= 0x80
+		}
+	} else {
+		hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
+	}
+
 	hs.hello.compressionMethod = compressionNone
 	hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension
 	if len(hs.clientHello.serverName) > 0 {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index ef72374..4b43481 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -150,6 +150,12 @@
 	// shimWritesFirst controls whether the shim sends an initial "hello"
 	// message before doing a roundtrip with the runner.
 	shimWritesFirst bool
+	// renegotiate indicates the the connection should be renegotiated
+	// during the exchange.
+	renegotiate bool
+	// renegotiateCiphers is a list of ciphersuite ids that will be
+	// switched in just before renegotiation.
+	renegotiateCiphers []uint16
 	// flags, if not empty, contains a list of command-line flags that will
 	// be passed to the shim program.
 	flags []string
@@ -565,6 +571,17 @@
 		}
 	}
 
+	if test.renegotiate {
+		if test.renegotiateCiphers != nil {
+			config.CipherSuites = test.renegotiateCiphers
+		}
+		if err := tlsConn.Renegotiate(); err != nil {
+			return err
+		}
+	} else if test.renegotiateCiphers != nil {
+		panic("renegotiateCiphers without renegotiate")
+	}
+
 	if messageLen < 0 {
 		if test.protocol == dtls {
 			return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
@@ -1793,6 +1810,48 @@
 		expectedError:   ":RENEGOTIATION_MISMATCH:",
 	})
 	// TODO(agl): test the renegotiation info SCSV.
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client",
+		renegotiate: true,
+	})
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-EmptyExt",
+		renegotiate: true,
+		config: Config{
+			Bugs: ProtocolBugs{
+				EmptyRenegotiationInfo: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":RENEGOTIATION_MISMATCH:",
+	})
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-BadExt",
+		renegotiate: true,
+		config: Config{
+			Bugs: ProtocolBugs{
+				BadRenegotiationInfo: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":RENEGOTIATION_MISMATCH:",
+	})
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-SwitchCiphers",
+		renegotiate: true,
+		config: Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+		},
+		renegotiateCiphers: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+	})
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-SwitchCiphers2",
+		renegotiate: true,
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+		},
+		renegotiateCiphers: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+	})
 }
 
 func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {