From f07a54079ccf2983cced89f86be37442562023ba Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sun, 10 May 2026 21:52:38 -0400 Subject: [PATCH] crypto/tls: add verifiedChains expiration checking during resumption When resuming a session, check that the verifiedChains contain at least one chain that is still valid at the time of resumption. If not, trigger a new handshake. Updates #77113 Updates #77217 Updates CVE-2025-68121 Change-Id: I14f585c43da17802513cbdd5b10c552d7a38b34e Reviewed-on: https://go-review.googlesource.com/c/go/+/739321 Reviewed-by: Coia Prant Reviewed-by: Filippo Valsorda Auto-Submit: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Reviewed-by: Dmitri Shuralyov --- common.go | 13 +++++++++++++ handshake_client.go | 10 +++++++--- handshake_server.go | 2 +- handshake_server_tls13.go | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/common.go b/common.go index 074c988..8f357da 100644 --- a/common.go +++ b/common.go @@ -1880,3 +1880,16 @@ func fipsAllowChain(chain []*x509.Certificate) bool { return true } + +// anyUnexpiredChain reports if at least one of verifiedChains is still +// unexpired. If verifiedChains is empty, it returns false. +func anyUnexpiredChain(verifiedChains [][]*x509.Certificate, now time.Time) bool { + for _, chain := range verifiedChains { + if len(chain) != 0 && !slices.ContainsFunc(chain, func(cert *x509.Certificate) bool { + return now.Before(cert.NotBefore) || now.After(cert.NotAfter) // cert is expired + }) { + return true + } + } + return false +} diff --git a/handshake_client.go b/handshake_client.go index 1c8b931..bb2f8eb 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -399,9 +399,6 @@ func (c *Conn) loadSession(hello *clientHelloMsg) ( return nil, nil, nil, nil } - // Check that the cached server certificate is not expired, and that it's - // valid for the ServerName. This should be ensured by the cache key, but - // protect the application from a faulty ClientSessionCache implementation. if c.config.time().After(session.peerCertificates[0].NotAfter) { // Expired certificate, delete the entry. c.config.ClientSessionCache.Put(cacheKey, nil) @@ -413,6 +410,13 @@ func (c *Conn) loadSession(hello *clientHelloMsg) ( return nil, nil, nil, nil } if err := session.peerCertificates[0].VerifyHostname(c.config.ServerName); err != nil { + // This should be ensured by the cache key, but protect the + // application from a faulty ClientSessionCache implementation. + return nil, nil, nil, nil + } + if !anyUnexpiredChain(session.verifiedChains, c.config.time()) { + // No valid chains, delete the entry. + c.config.ClientSessionCache.Put(cacheKey, nil) return nil, nil, nil, nil } } diff --git a/handshake_server.go b/handshake_server.go index 8ec3e34..3c8a571 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -511,7 +511,7 @@ func (hs *serverHandshakeState) checkForResumption() error { return nil } if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven && - len(sessionState.verifiedChains) == 0 { + !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) { return nil } diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index ff28fe2..d4f9ef8 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -465,7 +465,7 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { continue } if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven && - len(sessionState.verifiedChains) == 0 { + !anyUnexpiredChain(sessionState.verifiedChains, c.config.time()) { continue }