Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/boringssl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/ssl/test
diff options
context:
space:
mode:
authorAdam Langley <agl@google.com>2014-10-11 03:23:43 +0400
committerAdam Langley <agl@google.com>2014-10-25 01:19:44 +0400
commit7571292eaca1745f3ecda2374ba1e8163b58c3b5 (patch)
treeb1d31320bc47132e373b8ee8d0d277227ba8b0f9 /ssl/test
parent89abaea141b60061dacb6e03d58345d50ae23b81 (diff)
Extended master secret support.
This change implements support for the extended master secret. See https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 https://secure-resumption.com/ Change-Id: Ifc7327763149ab0894b4f1d48cdc35e0f1093b93 Reviewed-on: https://boringssl-review.googlesource.com/1930 Reviewed-by: David Benjamin <davidben@chromium.org> Reviewed-by: Adam Langley <agl@google.com>
Diffstat (limited to 'ssl/test')
-rw-r--r--ssl/test/bssl_shim.cc7
-rw-r--r--ssl/test/runner/common.go42
-rw-r--r--ssl/test/runner/conn.go21
-rw-r--r--ssl/test/runner/handshake_client.go44
-rw-r--r--ssl/test/runner/handshake_messages.go109
-rw-r--r--ssl/test/runner/handshake_server.go12
-rw-r--r--ssl/test/runner/prf.go48
-rw-r--r--ssl/test/runner/recordingconn.go130
-rw-r--r--ssl/test/runner/runner.go75
-rw-r--r--ssl/test/runner/ticket.go30
-rw-r--r--ssl/test/test_config.cc5
-rw-r--r--ssl/test/test_config.h1
12 files changed, 411 insertions, 113 deletions
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 6b27e261..d04c3c03 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -482,6 +482,13 @@ static int do_exchange(SSL_SESSION **out_session,
}
}
+ if (config->expect_extended_master_secret) {
+ if (!ssl->session->extended_master_secret) {
+ fprintf(stderr, "No EMS for session when expected");
+ return 2;
+ }
+ }
+
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 8b2c7503..935fd15f 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -71,16 +71,17 @@ const (
// TLS extension numbers
const (
- extensionServerName uint16 = 0
- extensionStatusRequest uint16 = 5
- extensionSupportedCurves uint16 = 10
- extensionSupportedPoints uint16 = 11
- extensionSignatureAlgorithms uint16 = 13
- extensionALPN uint16 = 16
- extensionSessionTicket uint16 = 35
- extensionNextProtoNeg uint16 = 13172 // not IANA assigned
- extensionRenegotiationInfo uint16 = 0xff01
- extensionChannelID uint16 = 30032 // not IANA assigned
+ extensionServerName uint16 = 0
+ extensionStatusRequest uint16 = 5
+ extensionSupportedCurves uint16 = 10
+ extensionSupportedPoints uint16 = 11
+ extensionSignatureAlgorithms uint16 = 13
+ extensionALPN uint16 = 16
+ extensionExtendedMasterSecret uint16 = 23
+ extensionSessionTicket uint16 = 35
+ extensionNextProtoNeg uint16 = 13172 // not IANA assigned
+ extensionRenegotiationInfo uint16 = 0xff01
+ extensionChannelID uint16 = 30032 // not IANA assigned
)
// TLS signaling cipher suite values
@@ -189,12 +190,13 @@ const (
// ClientSessionState contains the state needed by clients to resume TLS
// sessions.
type ClientSessionState struct {
- sessionTicket []uint8 // Encrypted ticket used for session resumption with server
- vers uint16 // SSL/TLS version negotiated for the session
- cipherSuite uint16 // Ciphersuite negotiated for the session
- masterSecret []byte // MasterSecret generated by client on a full handshake
- handshakeHash []byte // Handshake hash for Channel ID purposes.
- serverCertificates []*x509.Certificate // Certificate chain presented by the server
+ sessionTicket []uint8 // Encrypted ticket used for session resumption with server
+ vers uint16 // SSL/TLS version negotiated for the session
+ cipherSuite uint16 // Ciphersuite negotiated for the session
+ masterSecret []byte // MasterSecret generated by client on a full handshake
+ handshakeHash []byte // Handshake hash for Channel ID purposes.
+ serverCertificates []*x509.Certificate // Certificate chain presented by the server
+ extendedMasterSecret bool // Whether an extended master secret was used to generate the session
}
// ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -472,6 +474,14 @@ type ProtocolBugs struct {
// OversizedSessionId causes the session id that is sent with a ticket
// resumption attempt to be too large (33 bytes).
OversizedSessionId bool
+
+ // RequireExtendedMasterSecret, if true, requires that the peer support
+ // the extended master secret option.
+ RequireExtendedMasterSecret bool
+
+ // NoExtendedMasterSecret causes the client and server to behave is if
+ // they didn't support an extended master secret.
+ NoExtendedMasterSecret bool
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 9f0c328b..3ce6c764 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -29,16 +29,17 @@ type Conn struct {
isClient bool
// constant after handshake; protected by handshakeMutex
- handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
- handshakeErr error // error resulting from handshake
- vers uint16 // TLS version
- haveVers bool // version has been negotiated
- config *Config // configuration passed to constructor
- handshakeComplete bool
- didResume bool // whether this connection was a session resumption
- cipherSuite uint16
- ocspResponse []byte // stapled OCSP response
- peerCertificates []*x509.Certificate
+ handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
+ handshakeErr error // error resulting from handshake
+ vers uint16 // TLS version
+ haveVers bool // version has been negotiated
+ config *Config // configuration passed to constructor
+ handshakeComplete bool
+ didResume bool // whether this connection was a session resumption
+ extendedMasterSecret bool // whether this session used an extended master secret
+ cipherSuite uint16
+ ocspResponse []byte // stapled OCSP response
+ peerCertificates []*x509.Certificate
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index f4cadc26..2f9fe12d 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -56,26 +56,31 @@ func (c *Conn) clientHandshake() error {
}
hello := &clientHelloMsg{
- isDTLS: c.isDTLS,
- vers: c.config.maxVersion(),
- compressionMethods: []uint8{compressionNone},
- random: make([]byte, 32),
- ocspStapling: true,
- serverName: c.config.ServerName,
- supportedCurves: c.config.curvePreferences(),
- supportedPoints: []uint8{pointFormatUncompressed},
- nextProtoNeg: len(c.config.NextProtos) > 0,
- secureRenegotiation: true,
- alpnProtocols: c.config.NextProtos,
- duplicateExtension: c.config.Bugs.DuplicateExtension,
- channelIDSupported: c.config.ChannelID != nil,
- npnLast: c.config.Bugs.SwapNPNAndALPN,
+ isDTLS: c.isDTLS,
+ vers: c.config.maxVersion(),
+ compressionMethods: []uint8{compressionNone},
+ random: make([]byte, 32),
+ ocspStapling: true,
+ serverName: c.config.ServerName,
+ supportedCurves: c.config.curvePreferences(),
+ supportedPoints: []uint8{pointFormatUncompressed},
+ nextProtoNeg: len(c.config.NextProtos) > 0,
+ secureRenegotiation: true,
+ alpnProtocols: c.config.NextProtos,
+ duplicateExtension: c.config.Bugs.DuplicateExtension,
+ channelIDSupported: c.config.ChannelID != nil,
+ npnLast: c.config.Bugs.SwapNPNAndALPN,
+ extendedMasterSecret: c.config.maxVersion() >= VersionTLS10,
}
if c.config.Bugs.SendClientVersion != 0 {
hello.vers = c.config.Bugs.SendClientVersion
}
+ if c.config.Bugs.NoExtendedMasterSecret {
+ hello.extendedMasterSecret = false
+ }
+
possibleCipherSuites := c.config.cipherSuites()
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
@@ -503,7 +508,15 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.writeRecord(recordTypeHandshake, ckx.marshal())
}
- hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+ if hs.serverHello.extendedMasterSecret && c.vers >= VersionTLS10 {
+ hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash)
+ c.extendedMasterSecret = true
+ } else {
+ if c.config.Bugs.RequireExtendedMasterSecret {
+ return errors.New("tls: extended master secret required but not supported by peer")
+ }
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+ }
if chainToSend != nil {
var signed []byte
@@ -629,6 +642,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
// Restore masterSecret and peerCerts from previous state
hs.masterSecret = hs.session.masterSecret
c.peerCertificates = hs.session.serverCertificates
+ c.extendedMasterSecret = hs.session.extendedMasterSecret
hs.finishedHash.discardHandshakeBuffer()
return true, nil
}
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 136360d0..1114a6fb 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -7,27 +7,28 @@ package main
import "bytes"
type clientHelloMsg struct {
- raw []byte
- isDTLS bool
- vers uint16
- random []byte
- sessionId []byte
- cookie []byte
- cipherSuites []uint16
- compressionMethods []uint8
- nextProtoNeg bool
- serverName string
- ocspStapling bool
- supportedCurves []CurveID
- supportedPoints []uint8
- ticketSupported bool
- sessionTicket []uint8
- signatureAndHashes []signatureAndHash
- secureRenegotiation bool
- alpnProtocols []string
- duplicateExtension bool
- channelIDSupported bool
- npnLast bool
+ raw []byte
+ isDTLS bool
+ vers uint16
+ random []byte
+ sessionId []byte
+ cookie []byte
+ cipherSuites []uint16
+ compressionMethods []uint8
+ nextProtoNeg bool
+ serverName string
+ ocspStapling bool
+ supportedCurves []CurveID
+ supportedPoints []uint8
+ ticketSupported bool
+ sessionTicket []uint8
+ signatureAndHashes []signatureAndHash
+ secureRenegotiation bool
+ alpnProtocols []string
+ duplicateExtension bool
+ channelIDSupported bool
+ npnLast bool
+ extendedMasterSecret bool
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -56,7 +57,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
m.duplicateExtension == m1.duplicateExtension &&
m.channelIDSupported == m1.channelIDSupported &&
- m.npnLast == m1.npnLast
+ m.npnLast == m1.npnLast &&
+ m.extendedMasterSecret == m1.extendedMasterSecret
}
func (m *clientHelloMsg) marshal() []byte {
@@ -118,6 +120,9 @@ func (m *clientHelloMsg) marshal() []byte {
}
numExtensions++
}
+ if m.extendedMasterSecret {
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -319,6 +324,12 @@ func (m *clientHelloMsg) marshal() []byte {
z[1] = 0xff
z = z[4:]
}
+ if m.extendedMasterSecret {
+ // https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+ z[0] = byte(extensionExtendedMasterSecret >> 8)
+ z[1] = byte(extensionExtendedMasterSecret & 0xff)
+ z = z[4:]
+ }
m.raw = x
@@ -385,6 +396,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.sessionTicket = nil
m.signatureAndHashes = nil
m.alpnProtocols = nil
+ m.extendedMasterSecret = false
if len(data) == 0 {
// ClientHello is optionally followed by extension data
@@ -517,6 +529,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false
}
m.channelIDSupported = true
+ case extensionExtendedMasterSecret:
+ if length != 0 {
+ return false
+ }
+ m.extendedMasterSecret = true
}
data = data[length:]
}
@@ -525,21 +542,22 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
}
type serverHelloMsg struct {
- raw []byte
- isDTLS bool
- vers uint16
- random []byte
- sessionId []byte
- cipherSuite uint16
- compressionMethod uint8
- nextProtoNeg bool
- nextProtos []string
- ocspStapling bool
- ticketSupported bool
- secureRenegotiation bool
- alpnProtocol string
- duplicateExtension bool
- channelIDRequested bool
+ raw []byte
+ isDTLS bool
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuite uint16
+ compressionMethod uint8
+ nextProtoNeg bool
+ nextProtos []string
+ ocspStapling bool
+ ticketSupported bool
+ secureRenegotiation bool
+ alpnProtocol string
+ duplicateExtension bool
+ channelIDRequested bool
+ extendedMasterSecret bool
}
func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -562,7 +580,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.secureRenegotiation == m1.secureRenegotiation &&
m.alpnProtocol == m1.alpnProtocol &&
m.duplicateExtension == m1.duplicateExtension &&
- m.channelIDRequested == m1.channelIDRequested
+ m.channelIDRequested == m1.channelIDRequested &&
+ m.extendedMasterSecret == m1.extendedMasterSecret
}
func (m *serverHelloMsg) marshal() []byte {
@@ -606,6 +625,9 @@ func (m *serverHelloMsg) marshal() []byte {
extensionsLength += 2 + 1 + alpnLen
numExtensions++
}
+ if m.extendedMasterSecret {
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
@@ -699,6 +721,11 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = 0xff
z = z[4:]
}
+ if m.extendedMasterSecret {
+ z[0] = byte(extensionExtendedMasterSecret >> 8)
+ z[1] = byte(extensionExtendedMasterSecret & 0xff)
+ z = z[4:]
+ }
m.raw = x
@@ -730,6 +757,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
m.ocspStapling = false
m.ticketSupported = false
m.alpnProtocol = ""
+ m.extendedMasterSecret = false
if len(data) == 0 {
// ServerHello is optionally followed by extension data
@@ -805,6 +833,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false
}
m.channelIDRequested = true
+ case extensionExtendedMasterSecret:
+ if length != 0 {
+ return false
+ }
+ m.extendedMasterSecret = true
}
data = data[length:]
}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 1eb3f110..645a67c0 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -237,6 +237,7 @@ Curves:
hs.hello.nextProtos = config.NextProtos
}
}
+ hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret
if len(config.Certificates) == 0 {
c.sendAlert(alertInternalError)
@@ -373,6 +374,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
}
hs.masterSecret = hs.sessionState.masterSecret
+ c.extendedMasterSecret = hs.sessionState.extendedMasterSecret
return nil
}
@@ -387,6 +389,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
hs.hello.cipherSuite = hs.suite.id
+ c.extendedMasterSecret = hs.hello.extendedMasterSecret
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.writeClientHash(hs.clientHello.marshal())
@@ -502,7 +505,14 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.sendAlert(alertHandshakeFailure)
return err
}
- hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+ if c.extendedMasterSecret {
+ hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash)
+ } else {
+ if c.config.Bugs.RequireExtendedMasterSecret {
+ return errors.New("tls: extended master secret required but not supported by peer")
+ }
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+ }
// If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index 6d0db977..d45c080d 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -117,6 +117,7 @@ const (
)
var masterSecretLabel = []byte("master secret")
+var extendedMasterSecretLabel = []byte("extended master secret")
var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished")
@@ -150,6 +151,15 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr
return masterSecret
}
+// extendedMasterFromPreMasterSecret generates the master secret from the
+// pre-master secret when the Triple Handshake fix is in effect. See
+// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, h finishedHash) []byte {
+ masterSecret := make([]byte, masterSecretLength)
+ prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, h.Sum())
+ return masterSecret
+}
+
// keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
@@ -221,6 +231,16 @@ func (h *finishedHash) Write(msg []byte) (n int, err error) {
return len(msg), nil
}
+func (h finishedHash) Sum() []byte {
+ if h.version >= VersionTLS12 {
+ return h.client.Sum(nil)
+ }
+
+ out := make([]byte, 0, md5.Size+sha1.Size)
+ out = h.clientMD5.Sum(out)
+ return h.client.Sum(out)
+}
+
// finishedSum30 calculates the contents of the verify_data member of a SSLv3
// Finished message given the MD5 and SHA1 hashes of a set of handshake
// messages.
@@ -264,15 +284,7 @@ func (h finishedHash) clientSum(masterSecret []byte) []byte {
}
out := make([]byte, finishedVerifyLength)
- if h.version >= VersionTLS12 {
- seed := h.client.Sum(nil)
- h.prf(out, masterSecret, clientFinishedLabel, seed)
- } else {
- seed := make([]byte, 0, md5.Size+sha1.Size)
- seed = h.clientMD5.Sum(seed)
- seed = h.client.Sum(seed)
- h.prf(out, masterSecret, clientFinishedLabel, seed)
- }
+ h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
return out
}
@@ -284,15 +296,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
}
out := make([]byte, finishedVerifyLength)
- if h.version >= VersionTLS12 {
- seed := h.server.Sum(nil)
- h.prf(out, masterSecret, serverFinishedLabel, seed)
- } else {
- seed := make([]byte, 0, md5.Size+sha1.Size)
- seed = h.serverMD5.Sum(seed)
- seed = h.server.Sum(seed)
- h.prf(out, masterSecret, serverFinishedLabel, seed)
- }
+ h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
return out
}
@@ -334,14 +338,10 @@ func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash
return digest[:], crypto.SHA256, nil
}
if signatureAndHash.signature == signatureECDSA {
- digest := h.server.Sum(nil)
- return digest, crypto.SHA1, nil
+ return h.server.Sum(nil), crypto.SHA1, nil
}
- digest := make([]byte, 0, 36)
- digest = h.serverMD5.Sum(digest)
- digest = h.server.Sum(digest)
- return digest, crypto.MD5SHA1, nil
+ return h.Sum(), crypto.MD5SHA1, nil
}
// hashForChannelID returns the hash to be signed for TLS Channel
diff --git a/ssl/test/runner/recordingconn.go b/ssl/test/runner/recordingconn.go
new file mode 100644
index 00000000..a67fa48f
--- /dev/null
+++ b/ssl/test/runner/recordingconn.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+ "bufio"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// recordingConn is a net.Conn that records the traffic that passes through it.
+// WriteTo can be used to produce output that can be later be loaded with
+// ParseTestData.
+type recordingConn struct {
+ net.Conn
+ sync.Mutex
+ flows [][]byte
+ reading bool
+}
+
+func (r *recordingConn) Read(b []byte) (n int, err error) {
+ if n, err = r.Conn.Read(b); n == 0 {
+ return
+ }
+ b = b[:n]
+
+ r.Lock()
+ defer r.Unlock()
+
+ if l := len(r.flows); l == 0 || !r.reading {
+ buf := make([]byte, len(b))
+ copy(buf, b)
+ r.flows = append(r.flows, buf)
+ } else {
+ r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+ }
+ r.reading = true
+ return
+}
+
+func (r *recordingConn) Write(b []byte) (n int, err error) {
+ if n, err = r.Conn.Write(b); n == 0 {
+ return
+ }
+ b = b[:n]
+
+ r.Lock()
+ defer r.Unlock()
+
+ if l := len(r.flows); l == 0 || r.reading {
+ buf := make([]byte, len(b))
+ copy(buf, b)
+ r.flows = append(r.flows, buf)
+ } else {
+ r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+ }
+ r.reading = false
+ return
+}
+
+// WriteTo writes hex dumps to w that contains the recorded traffic.
+func (r *recordingConn) WriteTo(w io.Writer) {
+ // TLS always starts with a client to server flow.
+ clientToServer := true
+
+ for i, flow := range r.flows {
+ source, dest := "client", "server"
+ if !clientToServer {
+ source, dest = dest, source
+ }
+ fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
+ dumper := hex.Dumper(w)
+ dumper.Write(flow)
+ dumper.Close()
+ clientToServer = !clientToServer
+ }
+}
+
+func parseTestData(r io.Reader) (flows [][]byte, err error) {
+ var currentFlow []byte
+
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ line := scanner.Text()
+ // If the line starts with ">>> " then it marks the beginning
+ // of a new flow.
+ if strings.HasPrefix(line, ">>> ") {
+ if len(currentFlow) > 0 || len(flows) > 0 {
+ flows = append(flows, currentFlow)
+ currentFlow = nil
+ }
+ continue
+ }
+
+ // Otherwise the line is a line of hex dump that looks like:
+ // 00000170 fc f5 06 bf (...) |.....X{&?......!|
+ // (Some bytes have been omitted from the middle section.)
+
+ if i := strings.IndexByte(line, ' '); i >= 0 {
+ line = line[i:]
+ } else {
+ return nil, errors.New("invalid test data")
+ }
+
+ if i := strings.IndexByte(line, '|'); i >= 0 {
+ line = line[:i]
+ } else {
+ return nil, errors.New("invalid test data")
+ }
+
+ hexBytes := strings.Fields(line)
+ for _, hexByte := range hexBytes {
+ val, err := strconv.ParseUint(hexByte, 16, 8)
+ if err != nil {
+ return nil, errors.New("invalid hex byte in test data: " + err.Error())
+ }
+ currentFlow = append(currentFlow, byte(val))
+ }
+ }
+
+ if len(currentFlow) > 0 {
+ flows = append(flows, currentFlow)
+ }
+
+ return flows, nil
+}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index b4c2e612..10f86c97 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -22,6 +22,8 @@ import (
)
var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
+var useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+var flagDebug *bool = flag.Bool("debug", false, "Hexdump the contents of the connection")
const (
rsaCertificateFile = "cert.pem"
@@ -693,10 +695,11 @@ func runTest(test *testCase, buildDir string) error {
var shim *exec.Cmd
if *useValgrind {
shim = valgrindOf(false, shim_path, flags...)
+ } else if *useGDB {
+ shim = gdbOf(shim_path, flags...)
} else {
shim = exec.Command(shim_path, flags...)
}
- // shim = gdbOf(shim_path, flags...)
shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
shim.Stdin = os.Stdin
var stdoutBuf, stderrBuf bytes.Buffer
@@ -717,8 +720,19 @@ func runTest(test *testCase, buildDir string) error {
}
}
+ var connDebug *recordingConn
+ if *flagDebug {
+ connDebug = &recordingConn{Conn: conn}
+ conn = connDebug
+ }
+
err := doExchange(test, &config, conn, test.messageLen,
false /* not a resumption */)
+
+ if *flagDebug {
+ connDebug.WriteTo(os.Stdout)
+ }
+
conn.Close()
if err == nil && test.resumeSession {
var resumeConfig Config
@@ -1070,6 +1084,62 @@ func addClientAuthTests() {
}
}
+func addExtendedMasterSecretTests() {
+ const expectEMSFlag = "-expect-extended-master-secret"
+
+ for _, with := range []bool{false, true} {
+ prefix := "No"
+ var flags []string
+ if with {
+ prefix = ""
+ flags = []string{expectEMSFlag}
+ }
+
+ for _, isClient := range []bool{false, true} {
+ suffix := "-Server"
+ testType := serverTest
+ if isClient {
+ suffix = "-Client"
+ testType = clientTest
+ }
+
+ for _, ver := range tlsVersions {
+ test := testCase{
+ testType: testType,
+ name: prefix + "ExtendedMasterSecret-" + ver.name + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: !with,
+ RequireExtendedMasterSecret: with,
+ },
+ },
+ flags: flags,
+ shouldFail: ver.version == VersionSSL30 && with,
+ }
+ if test.shouldFail {
+ test.expectedLocalError = "extended master secret required but not supported by peer"
+ }
+ testCases = append(testCases, test)
+ }
+ }
+ }
+
+ // When a session is resumed, it should still be aware that its master
+ // secret was generated via EMS and thus it's safe to use tls-unique.
+ testCases = append(testCases, testCase{
+ name: "ExtendedMasterSecret-Resume",
+ config: Config{
+ Bugs: ProtocolBugs{
+ RequireExtendedMasterSecret: true,
+ },
+ },
+ flags: []string{expectEMSFlag},
+ resumeSession: true,
+ })
+}
+
// Adds tests that try to cover the range of the handshake state machine, under
// various conditions. Some of these are redundant with other tests, but they
// only cover the synchronous case.
@@ -1568,7 +1638,7 @@ func addExtensionTests() {
},
},
resumeSession: true,
- shouldFail: true,
+ shouldFail: true,
expectedError: ":DECODE_ERROR:",
})
}
@@ -1690,6 +1760,7 @@ func main() {
addD5BugTests()
addExtensionTests()
addResumptionVersionTests()
+ addExtendedMasterSecretTests()
for _, async := range []bool{false, true} {
for _, splitHandshake := range []bool{false, true} {
for _, protocol := range []protocol{tls, dtls} {
diff --git a/ssl/test/runner/ticket.go b/ssl/test/runner/ticket.go
index 74791d67..83558228 100644
--- a/ssl/test/runner/ticket.go
+++ b/ssl/test/runner/ticket.go
@@ -18,11 +18,12 @@ import (
// sessionState contains the information that is serialized into a session
// ticket in order to later resume a connection.
type sessionState struct {
- vers uint16
- cipherSuite uint16
- masterSecret []byte
- handshakeHash []byte
- certificates [][]byte
+ vers uint16
+ cipherSuite uint16
+ masterSecret []byte
+ handshakeHash []byte
+ certificates [][]byte
+ extendedMasterSecret bool
}
func (s *sessionState) equal(i interface{}) bool {
@@ -34,7 +35,8 @@ func (s *sessionState) equal(i interface{}) bool {
if s.vers != s1.vers ||
s.cipherSuite != s1.cipherSuite ||
!bytes.Equal(s.masterSecret, s1.masterSecret) ||
- !bytes.Equal(s.handshakeHash, s1.handshakeHash) {
+ !bytes.Equal(s.handshakeHash, s1.handshakeHash) ||
+ s.extendedMasterSecret != s1.extendedMasterSecret {
return false
}
@@ -56,6 +58,7 @@ func (s *sessionState) marshal() []byte {
for _, cert := range s.certificates {
length += 4 + len(cert)
}
+ length++
ret := make([]byte, length)
x := ret
@@ -88,6 +91,11 @@ func (s *sessionState) marshal() []byte {
x = x[4+len(cert):]
}
+ if s.extendedMasterSecret {
+ x[0] = 1
+ }
+ x = x[1:]
+
return ret
}
@@ -144,6 +152,16 @@ func (s *sessionState) unmarshal(data []byte) bool {
data = data[certLen:]
}
+ if len(data) < 1 {
+ return false
+ }
+
+ s.extendedMasterSecret = false
+ if data[0] == 1 {
+ s.extendedMasterSecret = true
+ }
+ data = data[1:]
+
if len(data) > 0 {
return false
}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 270fbfb5..737c78d5 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -57,6 +57,8 @@ const BoolFlag kBoolFlags[] = {
{ "-shim-writes-first", &TestConfig::shim_writes_first },
{ "-tls-d5-bug", &TestConfig::tls_d5_bug },
{ "-expect-session-miss", &TestConfig::expect_session_miss },
+ { "-expect-extended-master-secret",
+ &TestConfig::expect_extended_master_secret },
};
const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]);
@@ -105,7 +107,8 @@ TestConfig::TestConfig()
cookie_exchange(false),
shim_writes_first(false),
tls_d5_bug(false),
- expect_session_miss(false) {
+ expect_session_miss(false),
+ expect_extended_master_secret(false) {
}
bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index acce5043..d7f1be88 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -53,6 +53,7 @@ struct TestConfig {
std::string expected_advertised_alpn;
std::string select_alpn;
bool expect_session_miss;
+ bool expect_extended_master_secret;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);