Go version
go 1.26.1
Output of go env in your module/workspace:
What did you do?
Note: This was reported to security@golang.org and assigned a PUBLIC track.
Confirmed on Go 1.26.1 that x509.Certificate.Verify can become pathologically slow during chain building when the intermediates pool contains many distinct certificates that all look like plausible parents for the same child.
A representative TLS mTLS setup with a client certificate message just under the TLS certificate-message size limit caused about 434 ms in verification on Apple M2 before failing with:
x509: signature check attempts limit reached while verifying certificate chain
The same buildChains path is also reachable outside TLS when applications call Verify on attacker-controlled intermediate pools.
What did you see happen?
In crypto/x509/cert_pool.go, findPotentialParents returns all certificates whose raw subject matches the child certificate's issuer, ordered by key identifier plausibility.
In crypto/x509/verify.go, buildChains then walks those candidates recursively. Its loop prevention is alreadyInChain, which considers two certificates the same only if their subject, subject public key info, and SAN extension (if present) match.
That means many otherwise similar intermediates can still be treated as distinct by varying SAN bytes, so chain building explores far more candidates than expected.
This also seems related to the ordering in buildChains: alreadyInChain is checked before the signature-attempt limit is incremented and enforced. As a result, after the verification is already heading toward the maxChainSignatureChecks failure path, the verifier can still spend substantial time rescanning the existing chain for the remaining candidates.
In practice this can cause significant CPU use before verification fails. In TLS, this is constrained by the 256 KiB certificate-message limit and only applies when a server is configured to verify client certificates (for example VerifyClientCertIfGiven or RequireAndVerifyClientCert). Outside TLS, the practical impact depends on how much attacker-controlled certificate data an application accepts.
What did you expect to see?
Chain building should avoid spending large amounts of CPU on many lookalike parent candidates, and once the signature-attempt limit has effectively determined failure it should not keep paying the full alreadyInChain cost for the remaining candidates.
Additional reproduction details were shared with the security team.
Go version
go 1.26.1
Output of
go envin your module/workspace:What did you do?
Note: This was reported to security@golang.org and assigned a PUBLIC track.
Confirmed on Go 1.26.1 that x509.Certificate.Verify can become pathologically slow during chain building when the intermediates pool contains many distinct certificates that all look like plausible parents for the same child.
A representative TLS mTLS setup with a client certificate message just under the TLS certificate-message size limit caused about 434 ms in verification on Apple M2 before failing with:
The same buildChains path is also reachable outside TLS when applications call Verify on attacker-controlled intermediate pools.
What did you see happen?
In crypto/x509/cert_pool.go, findPotentialParents returns all certificates whose raw subject matches the child certificate's issuer, ordered by key identifier plausibility.
In crypto/x509/verify.go, buildChains then walks those candidates recursively. Its loop prevention is alreadyInChain, which considers two certificates the same only if their subject, subject public key info, and SAN extension (if present) match.
That means many otherwise similar intermediates can still be treated as distinct by varying SAN bytes, so chain building explores far more candidates than expected.
This also seems related to the ordering in buildChains: alreadyInChain is checked before the signature-attempt limit is incremented and enforced. As a result, after the verification is already heading toward the maxChainSignatureChecks failure path, the verifier can still spend substantial time rescanning the existing chain for the remaining candidates.
In practice this can cause significant CPU use before verification fails. In TLS, this is constrained by the 256 KiB certificate-message limit and only applies when a server is configured to verify client certificates (for example VerifyClientCertIfGiven or RequireAndVerifyClientCert). Outside TLS, the practical impact depends on how much attacker-controlled certificate data an application accepts.
What did you expect to see?
Chain building should avoid spending large amounts of CPU on many lookalike parent candidates, and once the signature-attempt limit has effectively determined failure it should not keep paying the full alreadyInChain cost for the remaining candidates.
Additional reproduction details were shared with the security team.