From 05ffd4ebfa14992e79c694f4afb00bc81985436c Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sun, 10 May 2026 22:02:37 -0400 Subject: [PATCH] crypto/tls: revalidate whole chain on resumption on Windows and macOS TestHandshakeChangeRootCAsResumption and TestHandshakeGetConfigForClientDifferentClientCAs changed because previously rootA and rootB shared Subject and SPKI, which made the new full-chain revalidation check succeed, as the same leaf would verify against both roots. Fixes #77376 Cq-Include-Trybots: luci.golang.try:gotip-darwin-arm64-longtest Change-Id: I60bed694bdc621c9e83f1bd8a8224c016a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/741361 Auto-Submit: Filippo Valsorda Reviewed-by: Dmitri Shuralyov Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Auto-Submit: Roland Shoemaker --- common.go | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/common.go b/common.go index 971a568..04b079a 100644 --- a/common.go +++ b/common.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "net" + "runtime" "slices" "strings" "sync" @@ -1907,15 +1908,27 @@ func anyValidVerifiedChain(verifiedChains [][]*x509.Certificate, opts x509.Verif }) { continue } - // Since we already validated the chain, we only care that it is - // rooted in a CA in CAs, or in the system pool. On platforms where - // we control chain validation (e.g. not Windows or macOS) this is a - // simple lookup in the CertPool internal hash map. On other - // platforms, this may be more expensive, depending on how they - // implement verification of just root certificates. - root := chain[len(chain)-1] - if _, err := root.Verify(opts); err == nil { - return true + // Since we already validated the chain, we only care that it is rooted + // in a CA in opts.Roots. On platforms where we control chain validation + // (e.g. not Windows or macOS) this is a simple lookup in the CertPool + // internal hash map, which we can simulate by running Verify on the + // root. On other platforms, we have to do full verification again, + // because EKU handling might differ. We will want to replace this with + // CertPool.Contains if/once that is available. See go.dev/issue/77376. + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + opts.Intermediates = x509.NewCertPool() + for _, cert := range chain[1:max(1, len(chain)-1)] { + opts.Intermediates.AddCert(cert) + } + leaf := chain[0] + if _, err := leaf.Verify(opts); err == nil { + return true + } + } else { + root := chain[len(chain)-1] + if _, err := root.Verify(opts); err == nil { + return true + } } } return false