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);