Skip to content

Commit

Permalink
x509roots: add new module
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rolandshoemaker authored and gopherbot committed Jun 9, 2023
1 parent 1622238 commit d0b3160
Show file tree
Hide file tree
Showing 6 changed files with 5,388 additions and 0 deletions.
3,742 changes: 3,742 additions & 0 deletions x509roots/fallback/bundle.go

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions x509roots/fallback/fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 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.

//go:build go1.20
// +build go1.20

// Package fallback embeds a set of fallback X.509 trusted roots in the
// application by automatically invoking [x509.SetFallbackRoots]. This allows
// the application to work correctly even if the operating system does not
// provide a verifier or system roots pool.
//
// To use it, import the package like
//
// import _ "golang.org/x/crypto/x509roots/fallback"
//
// It's recommended that only binaries, and not libraries, import this package.
//
// This package must be kept up to date for security and compatibility reasons.
// Use govulncheck to be notified of when new versions of the package are
// available.
package fallback

import "crypto/x509"

func init() {
p := x509.NewCertPool()
for _, c := range bundle {
p.AddCert(c)
}
x509.SetFallbackRoots(p)
}
3 changes: 3 additions & 0 deletions x509roots/fallback/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module golang.org/x/crypto/x509roots/fallback

go 1.20
136 changes: 136 additions & 0 deletions x509roots/gen_fallback_bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2023 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.

//go:build gen
// +build gen

//go:generate go run gen_fallback_bundle.go

package main

import (
"bytes"
"crypto/sha256"
"encoding/pem"
"flag"
"fmt"
"go/format"
"io"
"log"
"net/http"
"os"
"sort"
"time"

"golang.org/x/crypto/x509roots/nss"
)

const tmpl = `// Code generated by gen.go at %s; DO NOT EDIT.
// list hash: %x
package fallback
import "crypto/x509"
import "encoding/pem"
func mustParse(b []byte) []*x509.Certificate {
var roots []*x509.Certificate
for len(b) > 0 {
var block *pem.Block
block, b = pem.Decode(b)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
panic("unexpected PEM block type: "+block.Type)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
panic(err)
}
roots = append(roots, cert)
}
return roots
}
var bundle = mustParse([]byte(pemRoots))
// Format of the PEM list is:
// * Subject common name
// * SHA256 hash
// * PEM block
`

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 (only one of certdata-url and certdata-path may be specified)")
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)")
output = flag.String("output", "fallback/bundle.go", "Path to file to write output to")
)

func main() {
flag.Parse()

if *certDataPath != "" && *certDataURL != "" {
log.Fatal("Only one of --certdata-url and --certdata-path may be supplied")
}

var certdata io.Reader

if *certDataPath != "" {
f, err := os.Open(*certDataPath)
if err != nil {
log.Fatalf("unable to open %q: %s", *certDataPath, err)
}
defer f.Close()
certdata = f
} else {
resp, err := http.Get(*certDataURL)
if err != nil {
log.Fatalf("failed to request %q: %s", *certDataURL, err)
}
defer resp.Body.Close()
certdata = resp.Body
}

certs, err := nss.Parse(certdata)
if err != nil {
log.Fatalf("failed to parse %q: %s", *certDataPath, err)
}

sort.Slice(certs, func(i, j int) bool {
return certs[i].X509.Subject.String() < certs[j].X509.Subject.String()
})

h := sha256.New()
for _, c := range certs {
h.Write(c.X509.Raw)
}
listHash := h.Sum(nil)

b := bytes.NewBuffer(nil)
b.Write([]byte(fmt.Sprintf(tmpl, time.Now().Format(time.RFC1123), listHash)))
b.Write([]byte("const pemRoots = `\n"))
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.
continue
}
b.Write([]byte(fmt.Sprintf("# %s\n# %x\n", c.X509.Subject.String(), sha256.Sum256(c.X509.Raw))))
pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})
}
b.Write([]byte("`\n"))

formatted, err := format.Source(b.Bytes())
if err != nil {
log.Fatalf("failed to format source: %s", err)
}

if err := os.WriteFile(*output, formatted, 0644); err != nil {
log.Fatalf("failed to write to %q: %s", *output, err)
}
}
Loading

0 comments on commit d0b3160

Please sign in to comment.