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:
Andres Erbsen 2014-08-11 16:40:42 -07:00 committed by Adam Langley
parent 9e441ebf1d
commit 16b2f42015
5 changed files with 75 additions and 12 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}
}