ServerName string // server name requested by client, if any (server side only)
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
+
+ // TLSUnique contains the "tls-unique" channel binding value (see RFC
+ // 5929, section 3). For resumed sessions this value will be nil
+ // because resumption does not include enough context (see
+ // https://secure-resumption.com/#channelbindings). This will change in
+ // future versions of Go once the TLS master-secret fix has been
+ // standardized and implemented.
+ TLSUnique []byte
}
// ClientAuthType declares the policy the server will follow for
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
serverName string
+ // firstFinished contains the first Finished hash sent during the
+ // handshake. This is the "tls-unique" channel binding value.
+ firstFinished [12]byte
clientProtocol string
clientProtocolFallback bool
state.PeerCertificates = c.peerCertificates
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
+ if !c.didResume {
+ state.TLSUnique = c.firstFinished[:]
+ }
}
return state
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(); err != nil {
+ if err := hs.sendFinished(nil); err != nil {
return err
}
} else {
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
if err := hs.readSessionTicket(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(nil); err != nil {
return err
}
}
return false, nil
}
-func (hs *clientHandshakeState) readFinished() error {
+func (hs *clientHandshakeState) readFinished(out []byte) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
return errors.New("tls: server's Finished message was incorrect")
}
hs.finishedHash.Write(serverFinished.marshal())
+ copy(out, verify)
return nil
}
return nil
}
-func (hs *clientHandshakeState) sendFinished() error {
+func (hs *clientHandshakeState) sendFinished(out []byte) error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
hs.finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
+ copy(out, finished.verifyData)
return nil
}
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(nil); err != nil {
return err
}
c.didResume = true
if err := hs.establishKeys(); err != nil {
return err
}
- if err := hs.readFinished(); err != nil {
+ if err := hs.readFinished(c.firstFinished[:]); err != nil {
return err
}
if err := hs.sendSessionTicket(); err != nil {
return err
}
- if err := hs.sendFinished(); err != nil {
+ if err := hs.sendFinished(nil); err != nil {
return err
}
}
return nil
}
-func (hs *serverHandshakeState) readFinished() error {
+func (hs *serverHandshakeState) readFinished(out []byte) error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec)
}
hs.finishedHash.Write(clientFinished.marshal())
+ copy(out, verify)
return nil
}
return nil
}
-func (hs *serverHandshakeState) sendFinished() error {
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
c.writeRecord(recordTypeHandshake, finished.marshal())
c.cipherSuite = hs.suite.id
+ copy(out, finished.verifyData)
return nil
}
package tls
import (
+ "bytes"
"fmt"
"io"
"net"
}
return nil
}
+
+func TestTLSUniqueMatches(t *testing.T) {
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ serverTLSUniques := make(chan []byte)
+ go func() {
+ for i := 0; i < 2; i++ {
+ sconn, err := ln.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ serverConfig := *testConfig
+ srv := Server(sconn, &serverConfig)
+ if err := srv.Handshake(); err != nil {
+ t.Fatal(err)
+ }
+ serverTLSUniques <- srv.ConnectionState().TLSUnique
+ }
+ }()
+
+ clientConfig := *testConfig
+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(1)
+ conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
+ t.Error("client and server channel bindings differ")
+ }
+ conn.Close()
+
+ conn, err = Dial("tcp", ln.Addr().String(), &clientConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ if !conn.ConnectionState().DidResume {
+ t.Error("second session did not use resumption")
+ }
+ if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
+ t.Error("client and server channel bindings differ when session resumption is used")
+ }
+}