Skip to content

Commit d0b3160

Browse files
rolandshoemakergopherbot
authored andcommitted
x509roots: add new module
Adds the nss parser, under x509roots/nss, and the fallback module/package, with the initial generated bundle. Fixes golang/go#57792 Change-Id: Iebb1052e49126fa5baba1236f4ebc8dd8a823179 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/462036 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Roland Shoemaker <roland@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Roland Shoemaker <roland@golang.org>
1 parent 1622238 commit d0b3160

File tree

6 files changed

+5388
-0
lines changed

6 files changed

+5388
-0
lines changed

Diff for: x509roots/fallback/bundle.go

+3,742
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: x509roots/fallback/fallback.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.20
6+
// +build go1.20
7+
8+
// Package fallback embeds a set of fallback X.509 trusted roots in the
9+
// application by automatically invoking [x509.SetFallbackRoots]. This allows
10+
// the application to work correctly even if the operating system does not
11+
// provide a verifier or system roots pool.
12+
//
13+
// To use it, import the package like
14+
//
15+
// import _ "golang.org/x/crypto/x509roots/fallback"
16+
//
17+
// It's recommended that only binaries, and not libraries, import this package.
18+
//
19+
// This package must be kept up to date for security and compatibility reasons.
20+
// Use govulncheck to be notified of when new versions of the package are
21+
// available.
22+
package fallback
23+
24+
import "crypto/x509"
25+
26+
func init() {
27+
p := x509.NewCertPool()
28+
for _, c := range bundle {
29+
p.AddCert(c)
30+
}
31+
x509.SetFallbackRoots(p)
32+
}

Diff for: x509roots/fallback/go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module golang.org/x/crypto/x509roots/fallback
2+
3+
go 1.20

Diff for: x509roots/gen_fallback_bundle.go

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build gen
6+
// +build gen
7+
8+
//go:generate go run gen_fallback_bundle.go
9+
10+
package main
11+
12+
import (
13+
"bytes"
14+
"crypto/sha256"
15+
"encoding/pem"
16+
"flag"
17+
"fmt"
18+
"go/format"
19+
"io"
20+
"log"
21+
"net/http"
22+
"os"
23+
"sort"
24+
"time"
25+
26+
"golang.org/x/crypto/x509roots/nss"
27+
)
28+
29+
const tmpl = `// Code generated by gen.go at %s; DO NOT EDIT.
30+
// list hash: %x
31+
32+
package fallback
33+
34+
import "crypto/x509"
35+
import "encoding/pem"
36+
37+
func mustParse(b []byte) []*x509.Certificate {
38+
var roots []*x509.Certificate
39+
for len(b) > 0 {
40+
var block *pem.Block
41+
block, b = pem.Decode(b)
42+
if block == nil {
43+
break
44+
}
45+
if block.Type != "CERTIFICATE" {
46+
panic("unexpected PEM block type: "+block.Type)
47+
}
48+
cert, err := x509.ParseCertificate(block.Bytes)
49+
if err != nil {
50+
panic(err)
51+
}
52+
roots = append(roots, cert)
53+
}
54+
return roots
55+
}
56+
57+
var bundle = mustParse([]byte(pemRoots))
58+
59+
// Format of the PEM list is:
60+
// * Subject common name
61+
// * SHA256 hash
62+
// * PEM block
63+
64+
`
65+
66+
var (
67+
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 (only one of certdata-url and certdata-path may be specified)")
68+
certDataPath = flag.String("certdata-path", "", "Path to the NSS certdata.txt file to parse (only one of certdata-url and certdata-path may be specified)")
69+
output = flag.String("output", "fallback/bundle.go", "Path to file to write output to")
70+
)
71+
72+
func main() {
73+
flag.Parse()
74+
75+
if *certDataPath != "" && *certDataURL != "" {
76+
log.Fatal("Only one of --certdata-url and --certdata-path may be supplied")
77+
}
78+
79+
var certdata io.Reader
80+
81+
if *certDataPath != "" {
82+
f, err := os.Open(*certDataPath)
83+
if err != nil {
84+
log.Fatalf("unable to open %q: %s", *certDataPath, err)
85+
}
86+
defer f.Close()
87+
certdata = f
88+
} else {
89+
resp, err := http.Get(*certDataURL)
90+
if err != nil {
91+
log.Fatalf("failed to request %q: %s", *certDataURL, err)
92+
}
93+
defer resp.Body.Close()
94+
certdata = resp.Body
95+
}
96+
97+
certs, err := nss.Parse(certdata)
98+
if err != nil {
99+
log.Fatalf("failed to parse %q: %s", *certDataPath, err)
100+
}
101+
102+
sort.Slice(certs, func(i, j int) bool {
103+
return certs[i].X509.Subject.String() < certs[j].X509.Subject.String()
104+
})
105+
106+
h := sha256.New()
107+
for _, c := range certs {
108+
h.Write(c.X509.Raw)
109+
}
110+
listHash := h.Sum(nil)
111+
112+
b := bytes.NewBuffer(nil)
113+
b.Write([]byte(fmt.Sprintf(tmpl, time.Now().Format(time.RFC1123), listHash)))
114+
b.Write([]byte("const pemRoots = `\n"))
115+
for _, c := range certs {
116+
if len(c.Constraints) > 0 {
117+
// Until the constrained roots API lands, skip anything that has any
118+
// additional constraints. Once that API is available, we can add
119+
// build constraints that support both the current version and the
120+
// new version.
121+
continue
122+
}
123+
b.Write([]byte(fmt.Sprintf("# %s\n# %x\n", c.X509.Subject.String(), sha256.Sum256(c.X509.Raw))))
124+
pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})
125+
}
126+
b.Write([]byte("`\n"))
127+
128+
formatted, err := format.Source(b.Bytes())
129+
if err != nil {
130+
log.Fatalf("failed to format source: %s", err)
131+
}
132+
133+
if err := os.WriteFile(*output, formatted, 0644); err != nil {
134+
log.Fatalf("failed to write to %q: %s", *output, err)
135+
}
136+
}

0 commit comments

Comments
 (0)