crypto/tls: implement tls-unique channel binding (RFC 5929 section 3).
Tested against GnuTLS and Python. LGTM=agl R=golang-codereviews, agl, ashankar CC=agl, golang-codereviews https://golang.org/cl/117100043
This commit is contained in:
parent
9e441ebf1d
commit
16b2f42015
@ -165,6 +165,14 @@ type ConnectionState struct {
|
||||
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
|
||||
|
6
conn.go
6
conn.go
@ -42,6 +42,9 @@ type Conn struct {
|
||||
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
|
||||
@ -994,6 +997,9 @@ func (c *Conn) ConnectionState() ConnectionState {
|
||||
state.PeerCertificates = c.peerCertificates
|
||||
state.VerifiedChains = c.verifiedChains
|
||||
state.ServerName = c.serverName
|
||||
if !c.didResume {
|
||||
state.TLSUnique = c.firstFinished[:]
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
@ -187,10 +187,10 @@ NextCipherSuite:
|
||||
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 {
|
||||
@ -200,13 +200,13 @@ NextCipherSuite:
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -530,7 +530,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeState) readFinished() error {
|
||||
func (hs *clientHandshakeState) readFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
c.readRecord(recordTypeChangeCipherSpec)
|
||||
@ -555,6 +555,7 @@ func (hs *clientHandshakeState) readFinished() error {
|
||||
return errors.New("tls: server's Finished message was incorrect")
|
||||
}
|
||||
hs.finishedHash.Write(serverFinished.marshal())
|
||||
copy(out, verify)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -586,7 +587,7 @@ func (hs *clientHandshakeState) readSessionTicket() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeState) sendFinished() error {
|
||||
func (hs *clientHandshakeState) sendFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
@ -605,6 +606,7 @@ func (hs *clientHandshakeState) sendFinished() error {
|
||||
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
|
||||
hs.finishedHash.Write(finished.marshal())
|
||||
c.writeRecord(recordTypeHandshake, finished.marshal())
|
||||
copy(out, finished.verifyData)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -57,10 +57,10 @@ func (c *Conn) serverHandshake() error {
|
||||
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
|
||||
@ -73,13 +73,13 @@ func (c *Conn) serverHandshake() error {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -483,7 +483,7 @@ func (hs *serverHandshakeState) establishKeys() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) readFinished() error {
|
||||
func (hs *serverHandshakeState) readFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
c.readRecord(recordTypeChangeCipherSpec)
|
||||
@ -523,6 +523,7 @@ func (hs *serverHandshakeState) readFinished() error {
|
||||
}
|
||||
|
||||
hs.finishedHash.Write(clientFinished.marshal())
|
||||
copy(out, verify)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -552,7 +553,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) sendFinished() error {
|
||||
func (hs *serverHandshakeState) sendFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
@ -563,6 +564,7 @@ func (hs *serverHandshakeState) sendFinished() error {
|
||||
c.writeRecord(recordTypeHandshake, finished.marshal())
|
||||
|
||||
c.cipherSuite = hs.suite.id
|
||||
copy(out, finished.verifyData)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
45
tls_test.go
45
tls_test.go
@ -5,6 +5,7 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@ -235,3 +236,47 @@ func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user