Skip to content

Commit d669cc4

Browse files
committed
crypto/tls: implement TLS 1.3 PSK authentication (client side)
Also check original certificate validity when resuming TLS 1.0–1.2. Will refuse to resume a session if the certificate is expired or if the original connection had InsecureSkipVerify and the resumed one doesn't. Support only PSK+DHE to protect forward secrecy even with lack of a strong session ticket rotation story. Tested with NSS because s_server does not provide any way of getting the same session ticket key across invocations. Will self-test like TLS 1.0–1.2 once server side is implemented. Incorporates CL 128477 by @santoshankr. Fixes #24919 Updates #9671 Change-Id: Id3eaa5b6c77544a1357668bf9ff255f3420ecc34 Reviewed-on: https://go-review.googlesource.com/c/147420 Reviewed-by: Adam Langley <[email protected]>
1 parent dc0be72 commit d669cc4

11 files changed

+442
-156
lines changed

src/crypto/tls/cipher_suites.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,16 @@ func ecdheRSAKA(version uint16) keyAgreement {
399399
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
400400
for _, id := range have {
401401
if id == want {
402-
for _, suite := range cipherSuites {
403-
if suite.id == want {
404-
return suite
405-
}
406-
}
407-
return nil
402+
return cipherSuiteByID(id)
403+
}
404+
}
405+
return nil
406+
}
407+
408+
func cipherSuiteByID(id uint16) *cipherSuite {
409+
for _, cipherSuite := range cipherSuites {
410+
if cipherSuite.id == id {
411+
return cipherSuite
408412
}
409413
}
410414
return nil
@@ -413,12 +417,16 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
413417
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
414418
for _, id := range have {
415419
if id == want {
416-
for _, suite := range cipherSuitesTLS13 {
417-
if suite.id == want {
418-
return suite
419-
}
420-
}
421-
return nil
420+
return cipherSuiteTLS13ByID(id)
421+
}
422+
}
423+
return nil
424+
}
425+
426+
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
427+
for _, cipherSuite := range cipherSuitesTLS13 {
428+
if cipherSuite.id == id {
429+
return cipherSuite
422430
}
423431
}
424432
return nil

src/crypto/tls/common.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -240,22 +240,32 @@ type ClientSessionState struct {
240240
sessionTicket []uint8 // Encrypted ticket used for session resumption with server
241241
vers uint16 // SSL/TLS version negotiated for the session
242242
cipherSuite uint16 // Ciphersuite negotiated for the session
243-
masterSecret []byte // MasterSecret generated by client on a full handshake
243+
masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
244244
serverCertificates []*x509.Certificate // Certificate chain presented by the server
245245
verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
246+
receivedAt time.Time // When the session ticket was received from the server
247+
248+
// TLS 1.3 fields.
249+
nonce []byte // Ticket nonce sent by the server, to derive PSK
250+
useBy time.Time // Expiration of the ticket lifetime as set by the server
251+
ageAdd uint32 // Random obfuscation factor for sending the ticket age
246252
}
247253

248254
// ClientSessionCache is a cache of ClientSessionState objects that can be used
249255
// by a client to resume a TLS session with a given server. ClientSessionCache
250256
// implementations should expect to be called concurrently from different
251-
// goroutines. Only ticket-based resumption is supported, not SessionID-based
252-
// resumption.
257+
// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
258+
// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
259+
// are supported via this interface.
253260
type ClientSessionCache interface {
254261
// Get searches for a ClientSessionState associated with the given key.
255262
// On return, ok is true if one was found.
256263
Get(sessionKey string) (session *ClientSessionState, ok bool)
257264

258-
// Put adds the ClientSessionState to the cache with the given key.
265+
// Put adds the ClientSessionState to the cache with the given key. It might
266+
// get called multiple times in a connection if a TLS 1.3 server provides
267+
// more than one session ticket. If called with a nil *ClientSessionState,
268+
// it should remove the cache entry.
259269
Put(sessionKey string, cs *ClientSessionState)
260270
}
261271

@@ -502,19 +512,19 @@ type Config struct {
502512
// the order of elements in CipherSuites, is used.
503513
PreferServerCipherSuites bool
504514

505-
// SessionTicketsDisabled may be set to true to disable session ticket
506-
// (resumption) support. Note that on clients, session ticket support is
515+
// SessionTicketsDisabled may be set to true to disable session ticket and
516+
// PSK (resumption) support. Note that on clients, session ticket support is
507517
// also disabled if ClientSessionCache is nil.
508518
SessionTicketsDisabled bool
509519

510-
// SessionTicketKey is used by TLS servers to provide session
511-
// resumption. See RFC 5077. If zero, it will be filled with
512-
// random data before the first server handshake.
520+
// SessionTicketKey is used by TLS servers to provide session resumption.
521+
// See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
522+
// with random data before the first server handshake.
513523
//
514524
// If multiple servers are terminating connections for the same host
515525
// they should all have the same SessionTicketKey. If the
516526
// SessionTicketKey leaks, previously recorded and future TLS
517-
// connections using that key are compromised.
527+
// connections using that key might be compromised.
518528
SessionTicketKey [32]byte
519529

520530
// ClientSessionCache is a cache of ClientSessionState entries for TLS
@@ -937,15 +947,21 @@ func NewLRUClientSessionCache(capacity int) ClientSessionCache {
937947
}
938948
}
939949

940-
// Put adds the provided (sessionKey, cs) pair to the cache.
950+
// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry
951+
// corresponding to sessionKey is removed from the cache instead.
941952
func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
942953
c.Lock()
943954
defer c.Unlock()
944955

945956
if elem, ok := c.m[sessionKey]; ok {
946-
entry := elem.Value.(*lruSessionCacheEntry)
947-
entry.state = cs
948-
c.q.MoveToFront(elem)
957+
if cs == nil {
958+
c.q.Remove(elem)
959+
delete(c.m, sessionKey)
960+
} else {
961+
entry := elem.Value.(*lruSessionCacheEntry)
962+
entry.state = cs
963+
c.q.MoveToFront(elem)
964+
}
949965
return
950966
}
951967

src/crypto/tls/conn.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ type Conn struct {
5757
secureRenegotiation bool
5858
// ekm is a closure for exporting keying material.
5959
ekm func(label string, context []byte, length int) ([]byte, error)
60+
// resumptionSecret is the resumption_master_secret for generating or
61+
// handling NewSessionTicket messages. nil if config.SessionTicketsDisabled.
62+
resumptionSecret []byte
6063

6164
// clientFinishedIsFirst is true if the client sent the first Finished
6265
// message during the most recent handshake. This is recorded because
@@ -1169,10 +1172,15 @@ func (c *Conn) handlePostHandshakeMessage() error {
11691172
return err
11701173
}
11711174

1175+
c.retryCount++
1176+
if c.retryCount > maxUselessRecords {
1177+
c.sendAlert(alertUnexpectedMessage)
1178+
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
1179+
}
1180+
11721181
switch msg := msg.(type) {
11731182
case *newSessionTicketMsgTLS13:
1174-
// TODO(filippo): TLS 1.3 session ticket not implemented.
1175-
return nil
1183+
return c.handleNewSessionTicket(msg)
11761184
case *keyUpdateMsg:
11771185
return c.handleKeyUpdate(msg)
11781186
default:
@@ -1182,19 +1190,7 @@ func (c *Conn) handlePostHandshakeMessage() error {
11821190
}
11831191

11841192
func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
1185-
c.retryCount++
1186-
if c.retryCount > maxUselessRecords {
1187-
c.sendAlert(alertUnexpectedMessage)
1188-
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
1189-
}
1190-
1191-
var cipherSuite *cipherSuiteTLS13
1192-
for _, suite := range cipherSuitesTLS13 {
1193-
if suite.id == c.cipherSuite {
1194-
cipherSuite = suite
1195-
break
1196-
}
1197-
}
1193+
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
11981194
if cipherSuite == nil {
11991195
return c.in.setErrorLocked(c.sendAlert(alertInternalError))
12001196
}

0 commit comments

Comments
 (0)