| // 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 |
| } |