go: crypto/x509: VerifyOptions.Roots does not enforce that certificates are root certificates

What version of Go are you using (go version)?

$ go version

1.18

What did you do?

https://go.dev/play/p/InpOJXUXnUl

A leaf and intermediate certificate can be provided to the root pool.

What did you expect to see?

I expected that verification would fail due to the lack of root certificates.

What did you see instead?

Successful verification.

It’s unclear to me if this is an intentional feature or a bug.

Should a certificate in a root pool always be a CA certificate (related to RFC5280 4.2.1.9)?
The check that a parent is a CA certificate occurs in CheckSignatureFrom, called in buildChains, which is bypassed because the leaf certificate is in the root pool (https://go.dev/src/crypto/x509/verify.go#L781). Allowing a non-CA certificate to be verified successfully against itself seems prone to bugs.

Is a root certificate meant to be defined as a self-signed certificate, or any certificate that forms the trust anchor?
I expected that it must be a self-signed certificate, but an intermediate was permitted. This also seems prone to bugs, for example if a client supports a user-provided certificate chain and the client isn’t enforcing that a chain is formed from a root.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 17 (9 by maintainers)

Most upvoted comments

I’m not suggesting restricting adding to the pool, I am suggesting rejecting in Verify chains where the signer doesn’t have the isCA bit, even if it’s a root certificate. Direct match of the leaf with the roots pool would still work, because it doesn’t involve a signature check.

In other words, I am suggesting changing this line to also apply the check for root certificates.

https://github.com/golang/go/blob/2580d0e08d5e9f979b943758d3c49877fb2324cb/src/crypto/x509/verify.go#L687-L689

Requiring the isCA bit would would break cert pinning, which is normal (perhaps unfortunately so) for some applications.