blob: e4b53398b517120ef73d4ae66f092ec0696ec84f [file] [log] [blame]
// 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.
// 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"
"encoding/pem"
"fmt"
"time"
)
func init() {
x509.SetFallbackRoots(newFallbackCertPool())
}
func newFallbackCertPool() *x509.CertPool {
p := x509.NewCertPool()
for _, c := range mustParse(unparsedCertificates) {
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
})
}
}
return p
}
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 {
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)
if err != nil {
panic(err)
}
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 b
}