crypto/x509: simplify candidate chain filtering

Use slices.DeleteFunc to remove chains with invalid policies and
incompatible key usage, instead of iterating over the chains and
reconstructing the slice.

Change-Id: I8ad2bc1ac2469d0d18b2c090e3d4f702b1b577cb
Reviewed-on: https://go-review.googlesource.com/c/go/+/708415
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Roland Shoemaker 2025-10-01 08:57:00 -07:00
parent 29046398bb
commit 0d3dab9b1d
2 changed files with 27 additions and 36 deletions

View file

@ -17,6 +17,7 @@ import (
"net/url"
"reflect"
"runtime"
"slices"
"strings"
"time"
"unicode/utf8"
@ -801,7 +802,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
// Certificates other than c in the returned chains should not be modified.
//
// WARNING: this function doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
func (c *Certificate) Verify(opts VerifyOptions) ([][]*Certificate, error) {
// Platform-specific verification needs the ASN.1 contents so
// this makes the behavior consistent across platforms.
if len(c.Raw) == 0 {
@ -843,15 +844,15 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
}
}
err = c.isValid(leafCertificate, nil, &opts)
err := c.isValid(leafCertificate, nil, &opts)
if err != nil {
return
return nil, err
}
if len(opts.DNSName) > 0 {
err = c.VerifyHostname(opts.DNSName)
if err != nil {
return
return nil, err
}
}
@ -865,26 +866,12 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
}
}
chains = make([][]*Certificate, 0, len(candidateChains))
var invalidPoliciesChains int
for _, candidate := range candidateChains {
if !policiesValid(candidate, opts) {
invalidPoliciesChains++
continue
}
chains = append(chains, candidate)
}
if len(chains) == 0 {
return nil, CertificateInvalidError{c, NoValidChains, "all candidate chains have invalid policies"}
}
anyKeyUsage := false
for _, eku := range opts.KeyUsages {
if eku == ExtKeyUsageAny {
// If any key usage is acceptable, no need to check the chain for
// key usages.
return chains, nil
// The presence of anyExtendedKeyUsage overrides any other key usage.
anyKeyUsage = true
break
}
}
@ -892,34 +879,38 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
}
candidateChains = chains
chains = chains[:0]
var invalidPoliciesChains int
var incompatibleKeyUsageChains int
for _, candidate := range candidateChains {
if !checkChainForKeyUsage(candidate, opts.KeyUsages) {
candidateChains = slices.DeleteFunc(candidateChains, func(chain []*Certificate) bool {
if !policiesValid(chain, opts) {
invalidPoliciesChains++
return true
}
// If any key usage is acceptable, no need to check the chain for
// key usages.
if !anyKeyUsage && !checkChainForKeyUsage(chain, opts.KeyUsages) {
incompatibleKeyUsageChains++
continue
}
chains = append(chains, candidate)
return true
}
return false
})
if len(chains) == 0 {
if len(candidateChains) == 0 {
var details []string
if incompatibleKeyUsageChains > 0 {
if invalidPoliciesChains == 0 {
return nil, CertificateInvalidError{c, IncompatibleUsage, ""}
}
details = append(details, fmt.Sprintf("%d chains with incompatible key usage", incompatibleKeyUsageChains))
details = append(details, fmt.Sprintf("%d candidate chains with incompatible key usage", incompatibleKeyUsageChains))
}
if invalidPoliciesChains > 0 {
details = append(details, fmt.Sprintf("%d chains with invalid policies", invalidPoliciesChains))
details = append(details, fmt.Sprintf("%d candidate chains with invalid policies", invalidPoliciesChains))
}
err = CertificateInvalidError{c, NoValidChains, strings.Join(details, ", ")}
return nil, err
}
return chains, nil
return candidateChains, nil
}
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {

View file

@ -3031,7 +3031,7 @@ func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) {
testOID3 := mustNewOIDFromInts([]uint64{1, 2, 840, 113554, 4, 1, 72585, 2, 3})
root, intermediate, leaf := loadTestCert(t, "testdata/policy_root.pem"), loadTestCert(t, "testdata/policy_intermediate_require.pem"), loadTestCert(t, "testdata/policy_leaf.pem")
expectedErr := "x509: no valid chains built: all candidate chains have invalid policies"
expectedErr := "x509: no valid chains built: 1 candidate chains with invalid policies"
roots, intermediates := NewCertPool(), NewCertPool()
roots.AddCert(root)