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/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 40c3e42..40cb149 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -856,6 +856,26 @@
     }
   }
 
+  if (config->tls_unique) {
+    uint8_t tls_unique[16];
+    size_t tls_unique_len;
+    if (!SSL_get_tls_unique(ssl.get(), tls_unique, &tls_unique_len,
+                            sizeof(tls_unique))) {
+      fprintf(stderr, "failed to get tls-unique\n");
+      return false;
+    }
+
+    if (tls_unique_len != 12) {
+      fprintf(stderr, "expected 12 bytes of tls-unique but got %u",
+              static_cast<unsigned>(tls_unique_len));
+      return false;
+    }
+
+    if (WriteAll(ssl.get(), tls_unique, tls_unique_len) < 0) {
+      return false;
+    }
+  }
+
   if (config->write_different_record_sizes) {
     if (config->is_dtls) {
       fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
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} {
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index df8553c..363b6f3 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -81,6 +81,7 @@
   { "-use-export-context", &TestConfig::use_export_context },
   { "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations },
   { "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect },
+  { "-tls-unique", &TestConfig::tls_unique },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index ff801db..5d753c8 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -78,6 +78,7 @@
   bool use_export_context = false;
   bool reject_peer_renegotiations = false;
   bool no_legacy_server_connect = false;
+  bool tls_unique = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);