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 <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
This commit is contained in:
yuhan6665
2026-09-08 21:33:46 -04:00
parent 50d86eacd4
commit 05ffd4ebfa
+22 -9
View File
@@ -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