Update to TLS 1.3 draft 18.
This is the squash of the following CLs:
https://boringssl-review.googlesource.com/c/12021/9
https://boringssl-review.googlesource.com/c/12022/9
https://boringssl-review.googlesource.com/c/12107/19
https://boringssl-review.googlesource.com/c/12141/22
https://boringssl-review.googlesource.com/c/12181/33
The Go portions were written by Nick Harper
BUG=112
Change-Id: I375a1fcead493ec3e0282e231ccc8d7c4dde5063
Reviewed-on: https://boringssl-review.googlesource.com/12300
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 4b3df2d..1e0d313 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -16,6 +16,7 @@
"fmt"
"io"
"math/big"
+ "time"
)
// serverHandshakeState contains details of a server handshake in progress.
@@ -404,78 +405,75 @@
}
pskIdentities := hs.clientHello.pskIdentities
+ pskKEModes := hs.clientHello.pskKEModes
+
if len(pskIdentities) == 0 && len(hs.clientHello.sessionTicket) > 0 && c.config.Bugs.AcceptAnySession {
psk := pskIdentity{
- keModes: []byte{pskDHEKEMode},
- authModes: []byte{pskAuthMode},
- ticket: hs.clientHello.sessionTicket,
+ ticket: hs.clientHello.sessionTicket,
}
pskIdentities = []pskIdentity{psk}
+ pskKEModes = []byte{pskDHEKEMode}
}
- for i, pskIdentity := range pskIdentities {
- foundKE := false
- foundAuth := false
- for _, keMode := range pskIdentity.keModes {
- if keMode == pskDHEKEMode {
- foundKE = true
- }
- }
-
- for _, authMode := range pskIdentity.authModes {
- if authMode == pskAuthMode {
- foundAuth = true
- }
- }
-
- if !foundKE || !foundAuth {
- continue
- }
-
- sessionState, ok := c.decryptTicket(pskIdentity.ticket)
- if !ok {
- continue
- }
- if config.Bugs.AcceptAnySession {
- // Replace the cipher suite with one known to work, to
- // test cross-version resumption attempts.
- sessionState.cipherSuite = TLS_AES_128_GCM_SHA256
- } else {
- if sessionState.vers != c.vers && c.config.Bugs.AcceptAnySession {
- continue
- }
- if sessionState.ticketExpiration.Before(c.config.time()) {
+ var pskIndex int
+ foundKEMode := bytes.IndexByte(pskKEModes, pskDHEKEMode) >= 0
+ if foundKEMode {
+ for i, pskIdentity := range pskIdentities {
+ // TODO(svaldez): Check the obfuscatedTicketAge before accepting 0-RTT.
+ sessionState, ok := c.decryptTicket(pskIdentity.ticket)
+ if !ok {
continue
}
- cipherSuiteOk := false
- // Check that the client is still offering the ciphersuite in the session.
- for _, id := range hs.clientHello.cipherSuites {
- if id == sessionState.cipherSuite {
- cipherSuiteOk = true
- break
+ if config.Bugs.AcceptAnySession {
+ // Replace the cipher suite with one known to work, to
+ // test cross-version resumption attempts.
+ sessionState.cipherSuite = TLS_AES_128_GCM_SHA256
+ } else {
+ if sessionState.vers != c.vers && c.config.Bugs.AcceptAnySession {
+ continue
}
+ if sessionState.ticketExpiration.Before(c.config.time()) {
+ continue
+ }
+
+ clientTicketAge := time.Duration(uint32(pskIdentity.obfuscatedTicketAge-sessionState.ticketAgeAdd)) * time.Millisecond
+ if config.Bugs.ExpectTicketAge != 0 && clientTicketAge != config.Bugs.ExpectTicketAge {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: invalid ticket age")
+ }
+
+ cipherSuiteOk := false
+ // Check that the client is still offering the ciphersuite in the session.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == sessionState.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ continue
+ }
+
}
- if !cipherSuiteOk {
+
+ // Check that we also support the ciphersuite from the session.
+ suite := c.tryCipherSuite(sessionState.cipherSuite, c.config.cipherSuites(), c.vers, true, true)
+ if suite == nil {
continue
}
- }
- // Check that we also support the ciphersuite from the session.
- suite := c.tryCipherSuite(sessionState.cipherSuite, c.config.cipherSuites(), c.vers, true, true)
- if suite == nil {
- continue
+ hs.sessionState = sessionState
+ hs.suite = suite
+ hs.hello.hasPSKIdentity = true
+ hs.hello.pskIdentity = uint16(i)
+ pskIndex = i
+ if config.Bugs.SelectPSKIdentityOnResume != 0 {
+ hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
+ }
+ c.didResume = true
+ break
}
-
- hs.sessionState = sessionState
- hs.suite = suite
- hs.hello.hasPSKIdentity = true
- hs.hello.pskIdentity = uint16(i)
- if config.Bugs.SelectPSKIdentityOnResume != 0 {
- hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
- }
- c.didResume = true
- break
}
if config.Bugs.AlwaysSelectPSKIdentity {
@@ -483,6 +481,15 @@
hs.hello.pskIdentity = 0
}
+ // Verify the PSK binder. Note there may not be a PSK binder if
+ // AcceptAnyBinder is set. See https://crbug.com/boringssl/115.
+ if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+ binderToVerify := hs.clientHello.pskBinders[pskIndex]
+ if err := verifyPSKBinder(hs.clientHello, hs.sessionState, binderToVerify, []byte{}); err != nil {
+ return err
+ }
+ }
+
// If not resuming, select the cipher suite.
if hs.suite == nil {
var preferenceList, supportedList []uint16
@@ -515,26 +522,16 @@
hs.finishedHash.discardHandshakeBuffer()
hs.writeClientHash(hs.clientHello.marshal())
- hs.hello.useCertAuth = hs.sessionState == nil
-
// Resolve PSK and compute the early secret.
var psk []byte
if hs.sessionState != nil {
- psk = deriveResumptionPSK(hs.suite, hs.sessionState.masterSecret)
- hs.finishedHash.setResumptionContext(deriveResumptionContext(hs.suite, hs.sessionState.masterSecret))
+ psk = hs.sessionState.masterSecret
} else {
psk = hs.finishedHash.zeroSecret()
- hs.finishedHash.setResumptionContext(hs.finishedHash.zeroSecret())
}
earlySecret := hs.finishedHash.extractKey(hs.finishedHash.zeroSecret(), psk)
- if config.Bugs.OmitServerHelloSignatureAlgorithms {
- hs.hello.useCertAuth = false
- } else if config.Bugs.IncludeServerHelloSignatureAlgorithms {
- hs.hello.useCertAuth = true
- }
-
hs.hello.hasKeyShare = true
if hs.sessionState != nil && config.Bugs.NegotiatePSKResumption {
hs.hello.hasKeyShare = false
@@ -598,6 +595,7 @@
}
if sendHelloRetryRequest {
+ oldClientHelloBytes := hs.clientHello.marshal()
hs.writeServerHash(helloRetryRequest.marshal())
c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
c.flushHandshake()
@@ -627,11 +625,11 @@
if helloRetryRequest.hasSelectedGroup {
newKeyShares := newClientHelloCopy.keyShares
- if len(newKeyShares) == 0 || newKeyShares[len(newKeyShares)-1].group != helloRetryRequest.selectedGroup {
- return errors.New("tls: KeyShare from HelloRetryRequest not present in new ClientHello")
+ if len(newKeyShares) != 1 || newKeyShares[0].group != helloRetryRequest.selectedGroup {
+ return errors.New("tls: KeyShare from HelloRetryRequest not in new ClientHello")
}
- selectedKeyShare = &newKeyShares[len(newKeyShares)-1]
- newClientHelloCopy.keyShares = newKeyShares[:len(newKeyShares)-1]
+ selectedKeyShare = &newKeyShares[0]
+ newClientHelloCopy.keyShares = oldClientHelloCopy.keyShares
}
if len(helloRetryRequest.cookie) > 0 {
@@ -640,6 +638,7 @@
}
newClientHelloCopy.tls13Cookie = nil
}
+ newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders
if !oldClientHelloCopy.equal(&newClientHelloCopy) {
return errors.New("tls: new ClientHello does not match")
@@ -649,6 +648,16 @@
firstHelloRetryRequest = false
goto ResendHelloRetryRequest
}
+
+ // Verify the PSK binder. Note there may not be a PSK binder if
+ // AcceptAnyBinder is set. See https://crbug.com/115.
+ if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+ binderToVerify := newClientHello.pskBinders[pskIndex]
+ err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...))
+ if err != nil {
+ return err
+ }
+ }
}
// Resolve ECDHE and compute the handshake secret.
@@ -728,25 +737,9 @@
// Switch to handshake traffic keys.
serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
- c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
- c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
-
- if hs.hello.useCertAuth {
- if hs.clientHello.ocspStapling {
- encryptedExtensions.extensions.ocspResponse = hs.cert.OCSPStaple
- }
- if hs.clientHello.sctListSupported {
- encryptedExtensions.extensions.sctList = hs.cert.SignedCertificateTimestampList
- }
- } else {
- if config.Bugs.SendOCSPResponseOnResume != nil {
- encryptedExtensions.extensions.ocspResponse = config.Bugs.SendOCSPResponseOnResume
- }
- if config.Bugs.SendSCTListOnResume != nil {
- encryptedExtensions.extensions.sctList = config.Bugs.SendSCTListOnResume
- }
- }
+ c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
// Send EncryptedExtensions.
hs.writeServerHash(encryptedExtensions.marshal())
@@ -757,7 +750,7 @@
c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal())
}
- if hs.hello.useCertAuth {
+ if hs.sessionState == nil {
if config.ClientAuth >= RequestClientCert {
// Request a client certificate
certReq := &certificateRequestMsg{
@@ -785,7 +778,29 @@
hasRequestContext: true,
}
if !config.Bugs.EmptyCertificateList {
- certMsg.certificates = hs.cert.Certificate
+ for i, certData := range hs.cert.Certificate {
+ cert := certificateEntry{
+ data: certData,
+ }
+ if i == 0 {
+ if hs.clientHello.ocspStapling {
+ cert.ocspResponse = hs.cert.OCSPStaple
+ }
+ if hs.clientHello.sctListSupported {
+ cert.sctList = hs.cert.SignedCertificateTimestampList
+ }
+ cert.duplicateExtensions = config.Bugs.SendDuplicateCertExtensions
+ cert.extraExtension = config.Bugs.SendExtensionOnCertificate
+ } else {
+ if config.Bugs.SendOCSPOnIntermediates != nil {
+ cert.ocspResponse = config.Bugs.SendOCSPOnIntermediates
+ }
+ if config.Bugs.SendSCTOnIntermediates != nil {
+ cert.sctList = config.Bugs.SendSCTOnIntermediates
+ }
+ }
+ certMsg.certificates = append(certMsg.certificates, cert)
+ }
}
certMsgBytes := certMsg.marshal()
hs.writeServerHash(certMsgBytes)
@@ -844,10 +859,11 @@
masterSecret := hs.finishedHash.extractKey(handshakeSecret, hs.finishedHash.zeroSecret())
clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
+ c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
// Switch to application data keys on write. In particular, any alerts
// from the client certificate are sent over these keys.
- c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
// If we requested a client certificate, then the client must send a
// certificate message, even if it's empty.
@@ -873,7 +889,17 @@
}
}
- pub, err := hs.processCertsFromClient(certMsg.certificates)
+ var certs [][]byte
+ for _, cert := range certMsg.certificates {
+ certs = append(certs, cert.data)
+ // OCSP responses and SCT lists are not negotiated in
+ // client certificates.
+ if cert.ocspResponse != nil || cert.sctList != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected extensions in the client certificate")
+ }
+ }
+ pub, err := hs.processCertsFromClient(certs)
if err != nil {
return err
}
@@ -941,15 +967,14 @@
hs.writeClientHash(clientFinished.marshal())
// Switch to application data keys on read.
- c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
+ c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
c.cipherSuite = hs.suite
- c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
// TODO(davidben): Allow configuring the number of tickets sent for
// testing.
- if !c.config.SessionTicketsDisabled {
+ if !c.config.SessionTicketsDisabled && foundKEMode {
ticketCount := 2
for i := 0; i < ticketCount; i++ {
c.SendNewSessionTicket()
@@ -1134,7 +1159,7 @@
if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
serverExtensions.nextProtoNeg = true
serverExtensions.nextProtos = config.NextProtos
- serverExtensions.npnLast = config.Bugs.SwapNPNAndALPN
+ serverExtensions.npnAfterAlpn = config.Bugs.SwapNPNAndALPN
}
}
}
@@ -1340,7 +1365,11 @@
if !isPSK {
certMsg := new(certificateMsg)
if !config.Bugs.EmptyCertificateList {
- certMsg.certificates = hs.cert.Certificate
+ for _, certData := range hs.cert.Certificate {
+ certMsg.certificates = append(certMsg.certificates, certificateEntry{
+ data: certData,
+ })
+ }
}
if !config.Bugs.UnauthenticatedECDH {
certMsgBytes := certMsg.marshal()
@@ -1428,7 +1457,9 @@
}
hs.writeClientHash(certMsg.marshal())
- certificates = certMsg.certificates
+ for _, cert := range certMsg.certificates {
+ certificates = append(certificates, cert.data)
+ }
} else if c.vers != VersionSSL30 {
// In TLS, the Certificate message is required. In SSL
// 3.0, the peer skips it when sending no certificates.
@@ -1876,3 +1907,24 @@
func isGREASEValue(val uint16) bool {
return val&0x0f0f == 0x0a0a && val&0xff == val>>8
}
+
+func verifyPSKBinder(clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, transcript []byte) error {
+ binderLen := 2
+ for _, binder := range clientHello.pskBinders {
+ binderLen += 1 + len(binder)
+ }
+
+ truncatedHello := clientHello.marshal()
+ truncatedHello = truncatedHello[:len(truncatedHello)-binderLen]
+ pskCipherSuite := cipherSuiteFromID(sessionState.cipherSuite)
+ if pskCipherSuite == nil {
+ return errors.New("tls: Unknown cipher suite for PSK in session")
+ }
+
+ binder := computePSKBinder(sessionState.masterSecret, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello)
+ if !bytes.Equal(binder, binderToVerify) {
+ return errors.New("tls: PSK binder does not verify")
+ }
+
+ return nil
+}