x509roots/fallback: store bundle certs directly in DER

goos: linux
goarch: amd64
pkg: golang.org/x/crypto/x509roots/fallback
cpu: AMD Ryzen 5 4600G with Radeon Graphics
            │ /tmp/before │             /tmp/after              │
            │   sec/op    │   sec/op     vs base                │
InitTime-12   1.726m ± 0%   1.101m ± 1%  -36.20% (p=0.000 n=30)

            │  /tmp/before  │              /tmp/after              │
            │     B/op      │     B/op      vs base                │
InitTime-12   1178.2Ki ± 0%   779.8Ki ± 0%  -33.81% (p=0.000 n=30)

            │ /tmp/before │             /tmp/after             │
            │  allocs/op  │  allocs/op   vs base               │
InitTime-12   11.35k ± 0%   10.64k ± 0%  -6.32% (p=0.000 n=30)

Updates golang/go#73691

Change-Id: Ic33f2fdfc65001c41afeb3b6af8a383288d10de6
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676217
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <mark@golang.org>
This commit is contained in:
Mateusz Poliwczak
2025-08-03 12:47:17 -07:00
committed by Sean Liao
parent 1fda73153f
commit c247dead11
5 changed files with 811 additions and 4214 deletions
Binary file not shown.
+750 -4189
View File
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fallback
import (
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"testing"
)
func TestBundle(t *testing.T) {
for i, unparsed := range unparsedCertificates {
cert, err := x509.ParseCertificate(rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength])
if err != nil {
t.Errorf("ParseCertificate(unparsedCertificates[%v]) unexpected error: %v", i, err)
continue
}
if unparsed.cn != cert.Subject.String() {
t.Errorf("unparsedCertificates[%v].cn = %q; want = %q", i, unparsed.cn, cert.Subject.String())
}
sum := sha256.Sum256(cert.Raw)
sumHex := hex.EncodeToString(sum[:])
if sumHex != unparsed.sha256Hash {
t.Errorf("unparsedCertificates[%v].sha256Hash = %q; want = %q", i, unparsed.sha256Hash, sumHex)
}
}
}
+10 -16
View File
@@ -20,11 +20,14 @@ package fallback
import (
"crypto/x509"
"encoding/pem"
_ "embed"
"fmt"
"time"
)
//go:embed bundle.der
var rawCerts []byte
func init() {
x509.SetFallbackRoots(newFallbackCertPool())
}
@@ -49,9 +52,10 @@ func newFallbackCertPool() *x509.CertPool {
}
type unparsedCertificate struct {
cn string
sha256Hash string
pem string
cn string
sha256Hash string
certStartOff int
certLength int
// possible constraints
distrustAfter string
@@ -63,19 +67,9 @@ type parsedCertificate struct {
}
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
var b []parsedCertificate
b := make([]parsedCertificate, 0, len(unparsedCerts))
for _, unparsed := range unparsedCerts {
block, rest := pem.Decode([]byte(unparsed.pem))
if block == nil {
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(fmt.Sprintf("unexpected PEM block type for %q: %s", unparsed.cn, block.Type))
}
cert, err := x509.ParseCertificate(block.Bytes)
cert, err := x509.ParseCertificate(rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength])
if err != nil {
panic(err)
}
+19 -9
View File
@@ -11,7 +11,6 @@ package main
import (
"bytes"
"crypto/sha256"
"encoding/pem"
"flag"
"fmt"
"go/format"
@@ -37,6 +36,7 @@ var (
certDataURL = flag.String("certdata-url", "https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt", "URL to the raw certdata.txt file to parse (certdata-path overrides this, if provided)")
certDataPath = flag.String("certdata-path", "", "Path to the NSS certdata.txt file to parse (this overrides certdata-url, if provided)")
output = flag.String("output", "fallback/bundle.go", "Path to file to write output to")
derOutput = flag.String("deroutput", "fallback/bundle.der", "Path to file to write output to (DER certificate bundle)")
)
func main() {
@@ -92,8 +92,9 @@ func main() {
return subjI < subjJ
})
b := new(bytes.Buffer)
b.WriteString(tmpl)
rawCertsData := new(bytes.Buffer)
goSrcOut := new(bytes.Buffer)
goSrcOut.WriteString(tmpl)
for _, c := range certs {
var constraints []string
var skip bool
@@ -110,19 +111,24 @@ func main() {
if skip {
continue
}
fmt.Fprintf(b, "{\ncn: %q,\nsha256Hash: \"%x\",\npem: `%s`,\n",
off := rawCertsData.Len()
rawCertsData.Write(c.X509.Raw)
fmt.Fprintf(goSrcOut, "{\ncn: %q,\nsha256Hash: \"%x\",\ncertStartOff: %v,\ncertLength: %v,\n",
c.X509.Subject.String(),
sha256.Sum256(c.X509.Raw),
string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})),
off,
len(c.X509.Raw),
)
for _, constraint := range constraints {
fmt.Fprintln(b, constraint)
fmt.Fprintln(goSrcOut, constraint)
}
fmt.Fprintln(b, "},")
fmt.Fprintln(goSrcOut, "},")
}
fmt.Fprintln(b, "}")
fmt.Fprintln(goSrcOut, "}")
formatted, err := format.Source(b.Bytes())
formatted, err := format.Source(goSrcOut.Bytes())
if err != nil {
log.Fatalf("failed to format source: %s", err)
}
@@ -130,4 +136,8 @@ func main() {
if err := os.WriteFile(*output, formatted, 0644); err != nil {
log.Fatalf("failed to write to %q: %s", *output, err)
}
if err := os.WriteFile(*derOutput, rawCertsData.Bytes(), 0644); err != nil {
log.Fatalf("failed to write to %q: %s", *output, err)
}
}