Skip to content

Commit 6d05bc7

Browse files
committed
crypto/tls: use SessionState on the client side
Another internal change, that allows exposing the new APIs easily in following CLs. For #60105 Change-Id: I9c61b9f6e9d29af633f952444f514bcbbe82fe4e Reviewed-on: https://go-review.googlesource.com/c/go/+/496819 Reviewed-by: Matthew Dempsky <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]>
1 parent 9624e67 commit 6d05bc7

9 files changed

+351
-169
lines changed

src/crypto/tls/cache.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type certCache struct {
3939
sync.Map
4040
}
4141

42-
var clientCertCache = new(certCache)
42+
var globalCertCache = new(certCache)
4343

4444
// activeCert is a handle to a certificate held in the cache. Once there are
4545
// no alive activeCerts for a given certificate, the certificate is removed

src/crypto/tls/common.go

-19
Original file line numberDiff line numberDiff line change
@@ -330,25 +330,6 @@ func requiresClientCert(c ClientAuthType) bool {
330330
}
331331
}
332332

333-
// ClientSessionState contains the state needed by clients to resume TLS
334-
// sessions.
335-
type ClientSessionState struct {
336-
sessionTicket []uint8 // Encrypted ticket used for session resumption with server
337-
vers uint16 // TLS version negotiated for the session
338-
cipherSuite uint16 // Ciphersuite negotiated for the session
339-
masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
340-
serverCertificates []*x509.Certificate // Certificate chain presented by the server
341-
verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
342-
receivedAt time.Time // When the session ticket was received from the server
343-
ocspResponse []byte // Stapled OCSP response presented by the server
344-
scts [][]byte // SCTs presented by the server
345-
346-
// TLS 1.3 fields.
347-
nonce []byte // Ticket nonce sent by the server, to derive PSK
348-
useBy time.Time // Expiration of the ticket lifetime as set by the server
349-
ageAdd uint32 // Random obfuscation factor for sending the ticket age
350-
}
351-
352333
// ClientSessionCache is a cache of ClientSessionState objects that can be used
353334
// by a client to resume a TLS session with a given server. ClientSessionCache
354335
// implementations should expect to be called concurrently from different

src/crypto/tls/handshake_client.go

+72-56
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ type clientHandshakeState struct {
3131
suite *cipherSuite
3232
finishedHash finishedHash
3333
masterSecret []byte
34-
session *ClientSessionState
34+
session *SessionState // the session being resumed
35+
ticket []byte // a fresh ticket received during this handshake
3536
}
3637

3738
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
@@ -177,11 +178,11 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
177178
}
178179
c.serverName = hello.serverName
179180

180-
cacheKey, session, earlySecret, binderKey, err := c.loadSession(hello)
181+
session, earlySecret, binderKey, err := c.loadSession(hello)
181182
if err != nil {
182183
return err
183184
}
184-
if cacheKey != "" && session != nil {
185+
if session != nil {
185186
defer func() {
186187
// If we got a handshake failure when resuming a session, throw away
187188
// the session ticket. See RFC 5077, Section 3.2.
@@ -190,7 +191,9 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
190191
// does require servers to abort on invalid binders, so we need to
191192
// delete tickets to recover from a corrupted PSK.
192193
if err != nil {
193-
c.config.ClientSessionCache.Put(cacheKey, nil)
194+
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
195+
c.config.ClientSessionCache.Put(cacheKey, nil)
196+
}
194197
}
195198
}()
196199
}
@@ -255,19 +258,13 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
255258
return err
256259
}
257260

258-
// If we had a successful handshake and hs.session is different from
259-
// the one already cached - cache a new one.
260-
if cacheKey != "" && hs.session != nil && session != hs.session {
261-
c.config.ClientSessionCache.Put(cacheKey, hs.session)
262-
}
263-
264261
return nil
265262
}
266263

267-
func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
268-
session *ClientSessionState, earlySecret, binderKey []byte, err error) {
264+
func (c *Conn) loadSession(hello *clientHelloMsg) (
265+
session *SessionState, earlySecret, binderKey []byte, err error) {
269266
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
270-
return "", nil, nil, nil, nil
267+
return nil, nil, nil, nil
271268
}
272269

273270
hello.ticketSupported = true
@@ -282,29 +279,30 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
282279
// renegotiation is primarily used to allow a client to send a client
283280
// certificate, which would be skipped if session resumption occurred.
284281
if c.handshakes != 0 {
285-
return "", nil, nil, nil, nil
282+
return nil, nil, nil, nil
286283
}
287284

288285
// Try to resume a previously negotiated TLS session, if available.
289-
cacheKey = c.clientSessionCacheKey()
286+
cacheKey := c.clientSessionCacheKey()
290287
if cacheKey == "" {
291-
return "", nil, nil, nil, nil
288+
return nil, nil, nil, nil
292289
}
293-
session, ok := c.config.ClientSessionCache.Get(cacheKey)
294-
if !ok || session == nil {
295-
return cacheKey, nil, nil, nil, nil
290+
cs, ok := c.config.ClientSessionCache.Get(cacheKey)
291+
if !ok || cs == nil {
292+
return nil, nil, nil, nil
296293
}
294+
session = cs.session
297295

298296
// Check that version used for the previous session is still valid.
299297
versOk := false
300298
for _, v := range hello.supportedVersions {
301-
if v == session.vers {
299+
if v == session.version {
302300
versOk = true
303301
break
304302
}
305303
}
306304
if !versOk {
307-
return cacheKey, nil, nil, nil, nil
305+
return nil, nil, nil, nil
308306
}
309307

310308
// Check that the cached server certificate is not expired, and that it's
@@ -313,41 +311,41 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
313311
if !c.config.InsecureSkipVerify {
314312
if len(session.verifiedChains) == 0 {
315313
// The original connection had InsecureSkipVerify, while this doesn't.
316-
return cacheKey, nil, nil, nil, nil
314+
return nil, nil, nil, nil
317315
}
318-
serverCert := session.serverCertificates[0]
316+
serverCert := session.peerCertificates[0]
319317
if c.config.time().After(serverCert.NotAfter) {
320318
// Expired certificate, delete the entry.
321319
c.config.ClientSessionCache.Put(cacheKey, nil)
322-
return cacheKey, nil, nil, nil, nil
320+
return nil, nil, nil, nil
323321
}
324322
if err := serverCert.VerifyHostname(c.config.ServerName); err != nil {
325-
return cacheKey, nil, nil, nil, nil
323+
return nil, nil, nil, nil
326324
}
327325
}
328326

329-
if session.vers != VersionTLS13 {
327+
if session.version != VersionTLS13 {
330328
// In TLS 1.2 the cipher suite must match the resumed session. Ensure we
331329
// are still offering it.
332330
if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil {
333-
return cacheKey, nil, nil, nil, nil
331+
return nil, nil, nil, nil
334332
}
335333

336-
hello.sessionTicket = session.sessionTicket
334+
hello.sessionTicket = cs.ticket
337335
return
338336
}
339337

340338
// Check that the session ticket is not expired.
341-
if c.config.time().After(session.useBy) {
339+
if c.config.time().After(time.Unix(int64(session.useBy), 0)) {
342340
c.config.ClientSessionCache.Put(cacheKey, nil)
343-
return cacheKey, nil, nil, nil, nil
341+
return nil, nil, nil, nil
344342
}
345343

346344
// In TLS 1.3 the KDF hash must match the resumed session. Ensure we
347345
// offer at least one cipher suite with that hash.
348346
cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
349347
if cipherSuite == nil {
350-
return cacheKey, nil, nil, nil, nil
348+
return nil, nil, nil, nil
351349
}
352350
cipherSuiteOk := false
353351
for _, offeredID := range hello.cipherSuites {
@@ -358,32 +356,30 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
358356
}
359357
}
360358
if !cipherSuiteOk {
361-
return cacheKey, nil, nil, nil, nil
359+
return nil, nil, nil, nil
362360
}
363361

364362
// Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1.
365-
ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond)
363+
ticketAge := c.config.time().Sub(time.Unix(int64(session.createdAt), 0))
366364
identity := pskIdentity{
367-
label: session.sessionTicket,
368-
obfuscatedTicketAge: ticketAge + session.ageAdd,
365+
label: cs.ticket,
366+
obfuscatedTicketAge: uint32(ticketAge/time.Millisecond) + session.ageAdd,
369367
}
370368
hello.pskIdentities = []pskIdentity{identity}
371369
hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
372370

373371
// Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
374-
psk := cipherSuite.expandLabel(session.masterSecret, "resumption",
375-
session.nonce, cipherSuite.hash.Size())
376-
earlySecret = cipherSuite.extract(psk, nil)
372+
earlySecret = cipherSuite.extract(session.secret, nil)
377373
binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil)
378374
transcript := cipherSuite.hash.New()
379375
helloBytes, err := hello.marshalWithoutBinders()
380376
if err != nil {
381-
return "", nil, nil, nil, err
377+
return nil, nil, nil, err
382378
}
383379
transcript.Write(helloBytes)
384380
pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)}
385381
if err := hello.updateBinders(pskBinders); err != nil {
386-
return "", nil, nil, nil, err
382+
return nil, nil, nil, err
387383
}
388384

389385
return
@@ -485,6 +481,9 @@ func (hs *clientHandshakeState) handshake() error {
485481
return err
486482
}
487483
}
484+
if err := hs.saveSessionTicket(); err != nil {
485+
return err
486+
}
488487

489488
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
490489
c.isHandshakeComplete.Store(true)
@@ -752,7 +751,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
752751
return false, nil
753752
}
754753

755-
if hs.session.vers != c.vers {
754+
if hs.session.version != c.vers {
756755
c.sendAlert(alertHandshakeFailure)
757756
return false, errors.New("tls: server resumed a session with a different version")
758757
}
@@ -762,9 +761,10 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
762761
return false, errors.New("tls: server resumed a session with a different cipher suite")
763762
}
764763

765-
// Restore masterSecret, peerCerts, and ocspResponse from previous state
766-
hs.masterSecret = hs.session.masterSecret
767-
c.peerCertificates = hs.session.serverCertificates
764+
// Restore master secret and certificates from previous state
765+
hs.masterSecret = hs.session.secret
766+
c.peerCertificates = hs.session.peerCertificates
767+
c.activeCertHandles = hs.c.activeCertHandles
768768
c.verifiedChains = hs.session.verifiedChains
769769
c.ocspResponse = hs.session.ocspResponse
770770
// Let the ServerHello SCTs override the session SCTs from the original
@@ -836,8 +836,13 @@ func (hs *clientHandshakeState) readSessionTicket() error {
836836
if !hs.serverHello.ticketSupported {
837837
return nil
838838
}
839-
840839
c := hs.c
840+
841+
if !hs.hello.ticketSupported {
842+
c.sendAlert(alertIllegalParameter)
843+
return errors.New("tls: server sent unrequested session ticket")
844+
}
845+
841846
msg, err := c.readHandshake(&hs.finishedHash)
842847
if err != nil {
843848
return err
@@ -848,18 +853,29 @@ func (hs *clientHandshakeState) readSessionTicket() error {
848853
return unexpectedMessageError(sessionTicketMsg, msg)
849854
}
850855

851-
hs.session = &ClientSessionState{
852-
sessionTicket: sessionTicketMsg.ticket,
853-
vers: c.vers,
854-
cipherSuite: hs.suite.id,
855-
masterSecret: hs.masterSecret,
856-
serverCertificates: c.peerCertificates,
857-
verifiedChains: c.verifiedChains,
858-
receivedAt: c.config.time(),
859-
ocspResponse: c.ocspResponse,
860-
scts: c.scts,
856+
hs.ticket = sessionTicketMsg.ticket
857+
return nil
858+
}
859+
860+
func (hs *clientHandshakeState) saveSessionTicket() error {
861+
if hs.ticket == nil {
862+
return nil
863+
}
864+
c := hs.c
865+
866+
cacheKey := c.clientSessionCacheKey()
867+
if cacheKey == "" {
868+
return nil
869+
}
870+
871+
session, err := c.sessionState()
872+
if err != nil {
873+
return err
861874
}
875+
session.secret = hs.masterSecret
862876

877+
cs := &ClientSessionState{ticket: hs.ticket, session: session}
878+
c.config.ClientSessionCache.Put(cacheKey, cs)
863879
return nil
864880
}
865881

@@ -885,7 +901,7 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
885901
activeHandles := make([]*activeCert, len(certificates))
886902
certs := make([]*x509.Certificate, len(certificates))
887903
for i, asn1Data := range certificates {
888-
cert, err := clientCertCache.newCert(asn1Data)
904+
cert, err := globalCertCache.newCert(asn1Data)
889905
if err != nil {
890906
c.sendAlert(alertBadCertificate)
891907
return errors.New("tls: failed to parse certificate from server: " + err.Error())

src/crypto/tls/handshake_client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -916,14 +916,14 @@ func testResumption(t *testing.T, version uint16) {
916916
}
917917

918918
getTicket := func() []byte {
919-
return clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.sessionTicket
919+
return clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.ticket
920920
}
921921
deleteTicket := func() {
922922
ticketKey := clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).sessionKey
923923
clientConfig.ClientSessionCache.Put(ticketKey, nil)
924924
}
925925
corruptTicket := func() {
926-
clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.masterSecret[0] ^= 0xff
926+
clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.session.secret[0] ^= 0xff
927927
}
928928
randomKey := func() [32]byte {
929929
var k [32]byte

0 commit comments

Comments
 (0)