x509roots: support constrained roots
Adds support for roots with the distrust-after bit set. The constraint function construction is a little funky, but I couldn't think of an obvious better way to do it. Fixes golang/go#70777 Fixes golang/go#70623 Change-Id: I780f866416b626360eaee9368185768da7bc75ef Reviewed-on: https://go-review.googlesource.com/c/crypto/+/652996 Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Daniel McCarney <daniel@binaryparadox.net> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
+1155
-439
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,19 @@ import "crypto/x509"
|
||||
|
||||
func init() {
|
||||
p := x509.NewCertPool()
|
||||
for _, c := range bundle {
|
||||
p.AddCert(c)
|
||||
for _, c := range parsedCertificates {
|
||||
if len(c.constraints) == 0 {
|
||||
p.AddCert(c.cert)
|
||||
} else {
|
||||
p.AddCertWithConstraint(c.cert, func(chain []*x509.Certificate) error {
|
||||
for _, constraint := range c.constraints {
|
||||
if err := constraint(chain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
x509.SetFallbackRoots(p)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/x509roots/nss"
|
||||
)
|
||||
@@ -29,36 +30,68 @@ const tmpl = `// Code generated by gen_fallback_bundle.go; DO NOT EDIT.
|
||||
|
||||
package fallback
|
||||
|
||||
import "crypto/x509"
|
||||
import "encoding/pem"
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mustParse(b []byte) []*x509.Certificate {
|
||||
var roots []*x509.Certificate
|
||||
for len(b) > 0 {
|
||||
var block *pem.Block
|
||||
block, b = pem.Decode(b)
|
||||
type unparsedCertificate struct {
|
||||
cn string
|
||||
sha256Hash string
|
||||
pem string
|
||||
|
||||
// possible constraints
|
||||
distrustAfter string
|
||||
}
|
||||
|
||||
type parsedCertificate struct {
|
||||
cert *x509.Certificate
|
||||
constraints []func([]*x509.Certificate) error
|
||||
}
|
||||
|
||||
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
|
||||
var b []parsedCertificate
|
||||
for _, unparsed := range unparsedCerts {
|
||||
block, rest := pem.Decode([]byte(unparsed.pem))
|
||||
if block == nil {
|
||||
break
|
||||
panic(fmt.Sprintf("unexpected nil PEM block for %q", unparsed.cn))
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
panic(fmt.Sprintf("unexpected trailing data in PEM for %q", unparsed.cn))
|
||||
}
|
||||
if block.Type != "CERTIFICATE" {
|
||||
panic("unexpected PEM block type: " + block.Type)
|
||||
panic(fmt.Sprintf("unexpected PEM block type for %q: %s", unparsed.cn, block.Type))
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
roots = append(roots, cert)
|
||||
parsed := parsedCertificate{cert: cert}
|
||||
// parse possible constraints, this should check all fields of unparsedCertificate.
|
||||
if unparsed.distrustAfter != "" {
|
||||
distrustAfter, err := time.Parse(time.RFC3339, unparsed.distrustAfter)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse distrustAfter %q: %s", unparsed.distrustAfter, err))
|
||||
}
|
||||
parsed.constraints = append(parsed.constraints, func(chain []*x509.Certificate) error {
|
||||
for _, c := range chain {
|
||||
if c.NotBefore.After(distrustAfter) {
|
||||
return fmt.Errorf("certificate issued after distrust-after date %q", distrustAfter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
b = append(b, parsed)
|
||||
}
|
||||
return roots
|
||||
return b
|
||||
}
|
||||
|
||||
var bundle = mustParse([]byte(pemRoots))
|
||||
|
||||
// Format of the PEM list is:
|
||||
// * Subject common name
|
||||
// * SHA256 hash
|
||||
// * PEM block
|
||||
var parsedCertificates = mustParse(unparsedCertificates)
|
||||
|
||||
var unparsedCertificates = []unparsedCertificate{
|
||||
`
|
||||
|
||||
var (
|
||||
@@ -122,19 +155,33 @@ func main() {
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
b.WriteString(tmpl)
|
||||
fmt.Fprintln(b, "const pemRoots = `")
|
||||
for _, c := range certs {
|
||||
if len(c.Constraints) > 0 {
|
||||
// Until the constrained roots API lands, skip anything that has any
|
||||
// additional constraints. Once that API is available, we can add
|
||||
// build constraints that support both the current version and the
|
||||
// new version.
|
||||
var constraints []string
|
||||
var skip bool
|
||||
for _, constraint := range c.Constraints {
|
||||
switch t := constraint.(type) {
|
||||
case nss.DistrustAfter:
|
||||
constraints = append(constraints, fmt.Sprintf("distrustAfter: \"%s\",", time.Time(t).Format(time.RFC3339)))
|
||||
default:
|
||||
// If we encounter any constraints we don't support, skip the certificate.
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(b, "# %s\n# %x\n", c.X509.Subject.String(), sha256.Sum256(c.X509.Raw))
|
||||
pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})
|
||||
fmt.Fprintf(b, "{\ncn: %q,\nsha256Hash: \"%x\",\npem: `%s`,\n",
|
||||
c.X509.Subject.String(),
|
||||
sha256.Sum256(c.X509.Raw),
|
||||
string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})),
|
||||
)
|
||||
for _, constraint := range constraints {
|
||||
fmt.Fprintln(b, constraint)
|
||||
}
|
||||
fmt.Fprintln(b, "},")
|
||||
}
|
||||
fmt.Fprintln(b, "`")
|
||||
fmt.Fprintln(b, "}")
|
||||
|
||||
formatted, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user