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>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 6b27e26..d04c3c0 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc
@@ -482,6 +482,13 @@ } } + 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 8b2c750..935fd15 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go
@@ -71,16 +71,17 @@ // 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 @@ // 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 @@ // 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 9f0c328..3ce6c76 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go
@@ -29,16 +29,17 @@ 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 f4cadc2..2f9fe12 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go
@@ -56,26 +56,31 @@ } 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 @@ 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 @@ // 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 136360d..1114a6f 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go
@@ -7,27 +7,28 @@ 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 @@ 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 @@ } numExtensions++ } + if m.extendedMasterSecret { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -319,6 +324,12 @@ 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 @@ 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 @@ return false } m.channelIDSupported = true + case extensionExtendedMasterSecret: + if length != 0 { + return false + } + m.extendedMasterSecret = true } data = data[length:] } @@ -525,21 +542,22 @@ } 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 @@ 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 @@ extensionsLength += 2 + 1 + alpnLen numExtensions++ } + if m.extendedMasterSecret { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions @@ -699,6 +721,11 @@ 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 @@ 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 @@ 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 1eb3f11..645a67c 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go
@@ -237,6 +237,7 @@ 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 @@ } hs.masterSecret = hs.sessionState.masterSecret + c.extendedMasterSecret = hs.sessionState.extendedMasterSecret return nil } @@ -387,6 +389,7 @@ 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 @@ 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 6d0db97..d45c080 100644 --- a/ssl/test/runner/prf.go +++ b/ssl/test/runner/prf.go
@@ -117,6 +117,7 @@ ) 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 @@ 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 @@ 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 @@ } 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 @@ } 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 @@ 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 0000000..a67fa48 --- /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 b4c2e61..10f86c9 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -22,6 +22,8 @@ ) 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 @@ 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 @@ } } + 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 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 @@ }, }, resumeSession: true, - shouldFail: true, + shouldFail: true, expectedError: ":DECODE_ERROR:", }) } @@ -1690,6 +1760,7 @@ 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 74791d6..8355822 100644 --- a/ssl/test/runner/ticket.go +++ b/ssl/test/runner/ticket.go
@@ -18,11 +18,12 @@ // 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 @@ 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 @@ for _, cert := range s.certificates { length += 4 + len(cert) } + length++ ret := make([]byte, length) x := ret @@ -88,6 +91,11 @@ x = x[4+len(cert):] } + if s.extendedMasterSecret { + x[0] = 1 + } + x = x[1:] + return ret } @@ -144,6 +152,16 @@ 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 270fbfb..737c78d 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc
@@ -57,6 +57,8 @@ { "-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 @@ 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 acce504..d7f1be8 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h
@@ -53,6 +53,7 @@ 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);