Add SSL_get_tls_unique.

SSL_get_tls_unique returns the tls-unique channel-binding value as
defined in https://tools.ietf.org/html/rfc5929#section-3.1.

Change-Id: Id9644328a7db8a91cf3ff0deee9dd6ce0d3e00ba
Reviewed-on: https://boringssl-review.googlesource.com/4984
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index feef551..edebba1 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -188,6 +188,7 @@
 	VerifiedChains             [][]*x509.Certificate // verified chains built from PeerCertificates
 	ChannelID                  *ecdsa.PublicKey      // the channel ID for this connection
 	SRTPProtectionProfile      uint16                // the negotiated DTLS-SRTP protection profile
+	TLSUnique                  []byte
 }
 
 // ClientAuthType declares the policy the server will follow for
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index ec7a4a0..adbc1c3 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -44,7 +44,11 @@
 	// opposed to the ones presented by the server.
 	verifiedChains [][]*x509.Certificate
 	// serverName contains the server name indicated by the client, if any.
-	serverName                 string
+	serverName string
+	// firstFinished contains the first Finished hash sent during the
+	// handshake. This is the "tls-unique" channel binding value.
+	firstFinished [12]byte
+
 	clientRandom, serverRandom [32]byte
 	masterSecret               [48]byte
 
@@ -1299,6 +1303,7 @@
 		state.ServerName = c.serverName
 		state.ChannelID = c.channelID
 		state.SRTPProtectionProfile = c.srtpProtectionProfile
+		state.TLSUnique = c.firstFinished[:]
 	}
 
 	return state
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 0c5df73..a950313 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -313,10 +313,10 @@
 		if err := hs.readSessionTicket(); err != nil {
 			return err
 		}
-		if err := hs.readFinished(); err != nil {
+		if err := hs.readFinished(c.firstFinished[:]); err != nil {
 			return err
 		}
-		if err := hs.sendFinished(isResume); err != nil {
+		if err := hs.sendFinished(nil, isResume); err != nil {
 			return err
 		}
 	} else {
@@ -326,7 +326,7 @@
 		if err := hs.establishKeys(); err != nil {
 			return err
 		}
-		if err := hs.sendFinished(isResume); err != nil {
+		if err := hs.sendFinished(c.firstFinished[:], isResume); err != nil {
 			return err
 		}
 		// Most retransmits are triggered by a timeout, but the final
@@ -341,7 +341,7 @@
 		if err := hs.readSessionTicket(); err != nil {
 			return err
 		}
-		if err := hs.readFinished(); err != nil {
+		if err := hs.readFinished(nil); err != nil {
 			return err
 		}
 	}
@@ -740,7 +740,7 @@
 	return false, nil
 }
 
-func (hs *clientHandshakeState) readFinished() error {
+func (hs *clientHandshakeState) readFinished(out []byte) error {
 	c := hs.c
 
 	c.readRecord(recordTypeChangeCipherSpec)
@@ -767,6 +767,7 @@
 		}
 	}
 	c.serverVerify = append(c.serverVerify[:0], serverFinished.verifyData...)
+	copy(out, serverFinished.verifyData)
 	hs.writeServerHash(serverFinished.marshal())
 	return nil
 }
@@ -810,7 +811,7 @@
 	return nil
 }
 
-func (hs *clientHandshakeState) sendFinished(isResume bool) error {
+func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error {
 	c := hs.c
 
 	var postCCSBytes []byte
@@ -862,6 +863,7 @@
 	} else {
 		finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
 	}
+	copy(out, finished.verifyData)
 	if c.config.Bugs.BadFinished {
 		finished.verifyData[0]++
 	}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 8ca18e5..85cc0d2 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -69,7 +69,7 @@
 				return err
 			}
 		}
-		if err := hs.sendFinished(); err != nil {
+		if err := hs.sendFinished(c.firstFinished[:]); err != nil {
 			return err
 		}
 		// Most retransmits are triggered by a timeout, but the final
@@ -81,7 +81,7 @@
 		}); err != nil {
 			return err
 		}
-		if err := hs.readFinished(isResume); err != nil {
+		if err := hs.readFinished(nil, isResume); err != nil {
 			return err
 		}
 		c.didResume = true
@@ -94,7 +94,7 @@
 		if err := hs.establishKeys(); err != nil {
 			return err
 		}
-		if err := hs.readFinished(isResume); err != nil {
+		if err := hs.readFinished(c.firstFinished[:], isResume); err != nil {
 			return err
 		}
 		if c.config.Bugs.AlertBeforeFalseStartTest != 0 {
@@ -108,7 +108,7 @@
 		if err := hs.sendSessionTicket(); err != nil {
 			return err
 		}
-		if err := hs.sendFinished(); err != nil {
+		if err := hs.sendFinished(nil); err != nil {
 			return err
 		}
 	}
@@ -754,7 +754,7 @@
 	return nil
 }
 
-func (hs *serverHandshakeState) readFinished(isResume bool) error {
+func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error {
 	c := hs.c
 
 	c.readRecord(recordTypeChangeCipherSpec)
@@ -823,6 +823,7 @@
 		return errors.New("tls: client's Finished message is incorrect")
 	}
 	c.clientVerify = append(c.clientVerify[:0], clientFinished.verifyData...)
+	copy(out, clientFinished.verifyData)
 
 	hs.writeClientHash(clientFinished.marshal())
 	return nil
@@ -859,11 +860,12 @@
 	return nil
 }
 
-func (hs *serverHandshakeState) sendFinished() error {
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
 	c := hs.c
 
 	finished := new(finishedMsg)
 	finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+	copy(out, finished.verifyData)
 	if c.config.Bugs.BadFinished {
 		finished.verifyData[0]++
 	}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 2b25d35..bd03cb1 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -201,6 +201,9 @@
 	// flags, if not empty, contains a list of command-line flags that will
 	// be passed to the shim program.
 	flags []string
+	// testTLSUnique, if true, causes the shim to send the tls-unique value
+	// which will be compared against the expected value.
+	testTLSUnique bool
 }
 
 var testCases = []testCase{
@@ -1246,6 +1249,17 @@
 		}
 	}
 
+	if test.testTLSUnique {
+		var peersValue [12]byte
+		if _, err := io.ReadFull(tlsConn, peersValue[:]); err != nil {
+			return err
+		}
+		expected := tlsConn.ConnectionState().TLSUnique
+		if !bytes.Equal(peersValue[:], expected) {
+			return fmt.Errorf("tls-unique mismatch: peer sent %x, but %x was expected", peersValue[:], expected)
+		}
+	}
+
 	if test.shimWritesFirst {
 		var buf [5]byte
 		_, err := io.ReadFull(tlsConn, buf[:])
@@ -1431,6 +1445,10 @@
 		flags = append(flags, "-expect-session-miss")
 	}
 
+	if test.testTLSUnique {
+		flags = append(flags, "-tls-unique")
+	}
+
 	flags = append(flags, test.flags...)
 
 	var shim *exec.Cmd
@@ -3369,6 +3387,59 @@
 	})
 }
 
+func addTLSUniqueTests() {
+	for _, isClient := range []bool{false, true} {
+		for _, isResumption := range []bool{false, true} {
+			for _, hasEMS := range []bool{false, true} {
+				var suffix string
+				if isResumption {
+					suffix = "Resume-"
+				} else {
+					suffix = "Full-"
+				}
+
+				if hasEMS {
+					suffix += "EMS-"
+				} else {
+					suffix += "NoEMS-"
+				}
+
+				if isClient {
+					suffix += "Client"
+				} else {
+					suffix += "Server"
+				}
+
+				test := testCase{
+					name:          "TLSUnique-" + suffix,
+					testTLSUnique: true,
+					config: Config{
+						Bugs: ProtocolBugs{
+							NoExtendedMasterSecret: !hasEMS,
+						},
+					},
+				}
+
+				if isResumption {
+					test.resumeSession = true
+					test.resumeConfig = &Config{
+						Bugs: ProtocolBugs{
+							NoExtendedMasterSecret: !hasEMS,
+						},
+					}
+				}
+
+				if isResumption && !hasEMS {
+					test.shouldFail = true
+					test.expectedError = "failed to get tls-unique"
+				}
+
+				testCases = append(testCases, test)
+			}
+		}
+	}
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -3467,6 +3538,7 @@
 	addFastRadioPaddingTests()
 	addDTLSRetransmitTests()
 	addExportKeyingMaterialTests()
+	addTLSUniqueTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {