Merge pull request #2240 from nats-io/ocsp-caching

OCSP Stapling
This commit is contained in:
Waldemar Quevedo
2021-05-26 15:21:14 -07:00
committed by GitHub
22 changed files with 3169 additions and 6 deletions

2
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/nats-io/nats.go v1.11.0
github.com/nats-io/nkeys v0.3.0
github.com/nats-io/nuid v1.0.1
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
)

2
go.sum
View File

@@ -27,6 +27,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64 h1:QuAh/1Gwc0d+u9walMU1NqzhRemNegsv5esp2ALQIY4=
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -1464,6 +1464,16 @@ func TestConfigCheck(t *testing.T) {
errorLine: 6,
errorPos: 10,
},
{
name: "ambiguous store dir",
config: `
store_dir: "foo"
jetstream {
store_dir: "bar"
}
`,
err: fmt.Errorf(`Duplicate 'store_dir' configuration`),
},
}
checkConfig := func(config string) error {
@@ -1499,10 +1509,17 @@ func TestConfigCheck(t *testing.T) {
}
if err != nil && expectedErr != nil {
msg := fmt.Sprintf("%s:%d:%d: %s", conf, test.errorLine, test.errorPos, expectedErr.Error())
if test.reason != "" {
msg += ": " + test.reason
var msg string
if test.errorPos > 0 {
msg = fmt.Sprintf("%s:%d:%d: %s", conf, test.errorLine, test.errorPos, expectedErr.Error())
if test.reason != "" {
msg += ": " + test.reason
}
} else {
msg = test.reason
}
if !strings.Contains(err.Error(), msg) {
t.Errorf("Expected:\n%q\ngot:\n%q", msg, err.Error())
}

542
server/ocsp.go Normal file
View File

@@ -0,0 +1,542 @@
// Copyright 2021 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
"golang.org/x/crypto/ocsp"
)
const (
defaultOCSPStoreDir = "ocsp"
defaultOCSPCheckInterval = 24 * time.Hour
minOCSPCheckInterval = 2 * time.Minute
)
type OCSPMode uint8
const (
// OCSPModeAuto staples a status, only if "status_request" is set in cert.
OCSPModeAuto OCSPMode = iota
// OCSPModeAlways enforces OCSP stapling for certs and shuts down the server in
// case a server is revoked or cannot get OCSP staples.
OCSPModeAlways
// OCSPModeNever disables OCSP stapling even if cert has Must-Staple flag.
OCSPModeNever
// OCSPModeMust honors the Must-Staple flag from a certificate but also causing shutdown
// in case the certificate has been revoked.
OCSPModeMust
)
// OCSPMonitor monitors the state of a staple per certificate.
type OCSPMonitor struct {
mu sync.Mutex
raw []byte
srv *Server
certFile string
resp *ocsp.Response
hc *http.Client
stopCh chan struct{}
Leaf *x509.Certificate
Issuer *x509.Certificate
shutdownOnRevoke bool
}
func (oc *OCSPMonitor) getNextRun() time.Duration {
oc.mu.Lock()
nextUpdate := oc.resp.NextUpdate
oc.mu.Unlock()
now := time.Now()
if nextUpdate.IsZero() {
// If response is missing NextUpdate, we check the day after.
// Technically, if NextUpdate is missing, we can try whenever.
// https://tools.ietf.org/html/rfc6960#section-4.2.2.1
return defaultOCSPCheckInterval
}
dur := nextUpdate.Sub(now) / 2
// If negative, then wait a couple of minutes before getting another staple.
if dur < 0 {
return minOCSPCheckInterval
}
return dur
}
func (oc *OCSPMonitor) getStatus() ([]byte, *ocsp.Response, error) {
raw, resp := oc.getCacheStatus()
if len(raw) > 0 && resp != nil {
// Check if the OCSP is still valid.
if err := validOCSPResponse(resp); err == nil {
return raw, resp, nil
}
}
var err error
raw, resp, err = oc.getLocalStatus()
if err == nil {
return raw, resp, nil
}
return oc.getRemoteStatus()
}
func (oc *OCSPMonitor) getCacheStatus() ([]byte, *ocsp.Response) {
oc.mu.Lock()
defer oc.mu.Unlock()
return oc.raw, oc.resp
}
func (oc *OCSPMonitor) getLocalStatus() ([]byte, *ocsp.Response, error) {
opts := oc.srv.getOpts()
storeDir := opts.StoreDir
if storeDir == _EMPTY_ {
return nil, nil, fmt.Errorf("store_dir not set")
}
// This key must be based upon the current full certificate, not the public key,
// so MUST be on the full raw certificate and not an SPKI or other reduced form.
key := fmt.Sprintf("%x", sha256.Sum256(oc.Leaf.Raw))
oc.mu.Lock()
raw, err := ioutil.ReadFile(filepath.Join(storeDir, defaultOCSPStoreDir, key))
oc.mu.Unlock()
if err != nil {
return nil, nil, err
}
resp, err := ocsp.ParseResponse(raw, oc.Issuer)
if err != nil {
return nil, nil, err
}
if err := validOCSPResponse(resp); err != nil {
return nil, nil, err
}
// Cache the response.
oc.mu.Lock()
oc.raw = raw
oc.resp = resp
oc.mu.Unlock()
return raw, resp, nil
}
func (oc *OCSPMonitor) getRemoteStatus() ([]byte, *ocsp.Response, error) {
opts := oc.srv.getOpts()
var overrideURLs []string
if config := opts.OCSPConfig; config != nil {
overrideURLs = config.OverrideURLs
}
getRequestBytes := func(u string, hc *http.Client) ([]byte, error) {
resp, err := hc.Get(u)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("non-ok http status: %d", resp.StatusCode)
}
return ioutil.ReadAll(resp.Body)
}
// Request documentation:
// https://tools.ietf.org/html/rfc6960#appendix-A.1
reqDER, err := ocsp.CreateRequest(oc.Leaf, oc.Issuer, nil)
if err != nil {
return nil, nil, err
}
reqEnc := base64.StdEncoding.EncodeToString(reqDER)
responders := oc.Leaf.OCSPServer
if len(overrideURLs) > 0 {
responders = overrideURLs
}
if len(responders) == 0 {
return nil, nil, fmt.Errorf("no available ocsp servers")
}
oc.mu.Lock()
hc := oc.hc
oc.mu.Unlock()
var raw []byte
for _, u := range responders {
u = strings.TrimSuffix(u, "/")
raw, err = getRequestBytes(fmt.Sprintf("%s/%s", u, reqEnc), hc)
if err == nil {
break
}
}
if err != nil {
return nil, nil, fmt.Errorf("exhausted ocsp servers: %w", err)
}
resp, err := ocsp.ParseResponse(raw, oc.Issuer)
if err != nil {
return nil, nil, err
}
if err := validOCSPResponse(resp); err != nil {
return nil, nil, err
}
if storeDir := opts.StoreDir; storeDir != _EMPTY_ {
key := fmt.Sprintf("%x", sha256.Sum256(oc.Leaf.Raw))
if err := oc.writeOCSPStatus(storeDir, key, raw); err != nil {
return nil, nil, fmt.Errorf("failed to write ocsp status: %w", err)
}
}
oc.mu.Lock()
oc.raw = raw
oc.resp = resp
oc.mu.Unlock()
return raw, resp, nil
}
func (oc *OCSPMonitor) run() {
s := oc.srv
s.mu.Lock()
quitCh := s.quitCh
s.mu.Unlock()
defer s.grWG.Done()
oc.mu.Lock()
shutdownOnRevoke := oc.shutdownOnRevoke
certFile := oc.certFile
stopCh := oc.stopCh
oc.mu.Unlock()
var nextRun time.Duration
_, resp, err := oc.getStatus()
if err == nil && resp.Status == ocsp.Good {
nextRun = oc.getNextRun()
t := resp.NextUpdate.Format(time.RFC3339Nano)
s.Noticef(
"Found existing OCSP status for certificate at '%s': good, next update %s, checking again in %s",
certFile, t, nextRun,
)
} else if err == nil && shutdownOnRevoke {
// If resp.Status is ocsp.Revoked, ocsp.Unknown, or any other value.
s.Errorf("Found existing OCSP status for certificate at '%s': %s", certFile, ocspStatusString(resp.Status))
s.Shutdown()
return
}
for {
// On reload, if the certificate changes then need to stop this monitor.
select {
case <-time.After(nextRun):
case <-stopCh:
// In case of reload and have to restart the OCSP stapling monitoring.
return
case <-quitCh:
// Server quit channel.
return
}
_, resp, err := oc.getRemoteStatus()
if err != nil {
nextRun = oc.getNextRun()
s.Errorf("Bad OCSP status update for certificate '%s': %s, trying again in %v", certFile, err, nextRun)
continue
}
switch n := resp.Status; n {
case ocsp.Good:
nextRun = oc.getNextRun()
t := resp.NextUpdate.Format(time.RFC3339Nano)
s.Noticef(
"Received OCSP status for certificate '%s': good, next update %s, checking again in %s",
certFile, t, nextRun,
)
continue
default:
s.Errorf("Received OCSP certificate status: %s", ocspStatusString(n))
if shutdownOnRevoke {
s.Shutdown()
}
return
}
}
}
func (oc *OCSPMonitor) stop() {
oc.mu.Lock()
stopCh := oc.stopCh
oc.mu.Unlock()
stopCh <- struct{}{}
}
// NewOCSPMonitor takes a TLS configuration then wraps it with the callbacks set for OCSP verification
// along with a monitor that will periodically fetch OCSP staples.
func (srv *Server) NewOCSPMonitor(tc *tls.Config) (*tls.Config, *OCSPMonitor, error) {
opts := srv.getOpts()
oc := opts.OCSPConfig
tcOpts := opts.tlsConfigOpts
var certFile, caFile string
if tcOpts != nil {
certFile = tcOpts.CertFile
caFile = tcOpts.CaFile
}
if opts.TLSCert != _EMPTY_ {
certFile = opts.TLSCert
}
if opts.TLSCaCert != _EMPTY_ {
caFile = opts.TLSCaCert
}
// NOTE: Currently OCSP Stapling is enabled only for the first certificate found.
var mon *OCSPMonitor
for _, cert := range tc.Certificates {
var shutdownOnRevoke bool
mustStaple := hasOCSPStatusRequest(cert.Leaf)
if oc != nil {
switch {
case oc.Mode == OCSPModeNever:
if mustStaple {
srv.Warnf("Certificate at '%s' has MustStaple but OCSP is disabled", certFile)
}
return tc, nil, nil
case oc.Mode == OCSPModeAlways:
// Start the monitor for this cert even if it does not have
// the MustStaple flag and shutdown the server in case the
// staple ever gets revoked.
mustStaple = true
shutdownOnRevoke = true
case oc.Mode == OCSPModeMust && mustStaple:
shutdownOnRevoke = true
case oc.Mode == OCSPModeAuto && !mustStaple:
// "status_request" MustStaple flag not set in certificate. No need to do anything.
return tc, nil, nil
}
}
if !mustStaple {
// No explicit OCSP config and cert does not have MustStaple flag either.
return tc, nil, nil
}
if err := srv.setupOCSPStapleStoreDir(); err != nil {
return nil, nil, err
}
// TODO: Add OCSP 'responder_cert' option in case CA cert not available.
issuer, err := getOCSPIssuer(caFile, cert.Certificate)
if err != nil {
return nil, nil, err
}
mon = &OCSPMonitor{
srv: srv,
hc: &http.Client{Timeout: 30 * time.Second},
shutdownOnRevoke: shutdownOnRevoke,
certFile: certFile,
stopCh: make(chan struct{}, 1),
Leaf: cert.Leaf,
Issuer: issuer,
}
// Get the certificate status from the memory, then remote OCSP responder.
_, resp, err := mon.getStatus()
if err != nil {
return nil, nil, fmt.Errorf("bad OCSP status update for certificate at '%s': %s", certFile, err)
}
if err == nil && resp.Status != ocsp.Good && shutdownOnRevoke {
return nil, nil, fmt.Errorf("found existing OCSP status for certificate at '%s': %s", certFile, ocspStatusString(resp.Status))
}
// Callbacks below will be in charge of returning the certificate instead,
// so this has to be nil.
tc.Certificates = nil
// GetCertificate returns a certificate that's presented to a
// client.
tc.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
raw, _, err := mon.getStatus()
if err != nil {
return nil, err
}
return &tls.Certificate{
OCSPStaple: raw,
Certificate: cert.Certificate,
PrivateKey: cert.PrivateKey,
SupportedSignatureAlgorithms: cert.SupportedSignatureAlgorithms,
SignedCertificateTimestamps: cert.SignedCertificateTimestamps,
Leaf: cert.Leaf,
}, nil
}
// GetClientCertificate returns a certificate that's presented to a
// server.
tc.GetClientCertificate = func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &cert, nil
}
}
return tc, mon, nil
}
func hasOCSPStatusRequest(cert *x509.Certificate) bool {
// OID for id-pe-tlsfeature defined in RFC here:
// https://datatracker.ietf.org/doc/html/rfc7633
tlsFeatures := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
const statusRequestExt = 5
// Example values:
// * [48 3 2 1 5] - seen when creating own certs locally
// * [30 3 2 1 5] - seen in the wild
// Documentation:
// https://tools.ietf.org/html/rfc6066
for _, ext := range cert.Extensions {
if !ext.Id.Equal(tlsFeatures) {
continue
}
var val []int
rest, err := asn1.Unmarshal(ext.Value, &val)
if err != nil || len(rest) > 0 {
return false
}
for _, n := range val {
if n == statusRequestExt {
return true
}
}
break
}
return false
}
// writeOCSPStatus writes an OCSP status to a temporary file then moves it to a
// new path, in an attempt to avoid corrupting existing data.
func (oc *OCSPMonitor) writeOCSPStatus(storeDir, file string, data []byte) error {
storeDir = filepath.Join(storeDir, defaultOCSPStoreDir)
tmp, err := ioutil.TempFile(storeDir, "tmp-cert-status")
if err != nil {
return err
}
if _, err := tmp.Write(data); err != nil {
tmp.Close()
os.Remove(tmp.Name())
return err
}
if err := tmp.Close(); err != nil {
return err
}
oc.mu.Lock()
err = os.Rename(tmp.Name(), filepath.Join(storeDir, file))
oc.mu.Unlock()
if err != nil {
os.Remove(tmp.Name())
return err
}
return nil
}
func parseCertPEM(name string) (*x509.Certificate, error) {
data, err := ioutil.ReadFile(name)
if err != nil {
return nil, err
}
// Ignoring left over byte slice.
block, _ := pem.Decode(data)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM cert %s", name)
}
if block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("unexpected PEM certificate type: %s", block.Type)
}
return x509.ParseCertificate(block.Bytes)
}
// getOCSPIssuer returns a CA cert from the given path. If the path is empty,
// then this checks a given cert chain. If both are empty, then it returns an
// error.
func getOCSPIssuer(issuerCert string, chain [][]byte) (*x509.Certificate, error) {
var issuer *x509.Certificate
var err error
switch {
case len(chain) == 1 && issuerCert == _EMPTY_:
err = fmt.Errorf("require ocsp ca in chain or configuration")
case issuerCert != _EMPTY_:
issuer, err = parseCertPEM(issuerCert)
case len(chain) > 1 && issuerCert == _EMPTY_:
issuer, err = x509.ParseCertificate(chain[1])
default:
err = fmt.Errorf("invalid ocsp ca configuration")
}
if err != nil {
return nil, err
} else if !issuer.IsCA {
return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuerCert)
}
return issuer, nil
}
func ocspStatusString(n int) string {
switch n {
case ocsp.Good:
return "good"
case ocsp.Revoked:
return "revoked"
default:
return "unknown"
}
}
func validOCSPResponse(r *ocsp.Response) error {
// Time validation not handled by ParseResponse.
// https://tools.ietf.org/html/rfc6960#section-4.2.2.1
if !r.NextUpdate.IsZero() && r.NextUpdate.Before(time.Now()) {
t := r.NextUpdate.Format(time.RFC3339Nano)
return fmt.Errorf("invalid ocsp NextUpdate, is past time: %s", t)
}
if r.ThisUpdate.After(time.Now()) {
t := r.ThisUpdate.Format(time.RFC3339Nano)
return fmt.Errorf("invalid ocsp ThisUpdate, is future time: %s", t)
}
return nil
}

View File

@@ -268,6 +268,10 @@ type Options struct {
// and used as a filter criteria for some system requests
Tags jwt.TagList `json:"-"`
// OCSPConfig enables OCSP Stapling in the server.
OCSPConfig *OCSPConfig
tlsConfigOpts *TLSConfigOpts
// private fields, used to know if bool options are explicitly
// defined in config and/or command line params.
inConfig map[string]bool
@@ -485,6 +489,15 @@ type TLSConfigOpts struct {
PinnedCerts PinnedCertSet
}
// OCSPConfig represents the options of OCSP stapling options.
type OCSPConfig struct {
// Mode defines the policy for OCSP stapling.
Mode OCSPMode
// OverrideURLs is the http URL endpoint used to get OCSP staples.
OverrideURLs []string
}
var tlsUsage = `
TLS configuration is specified in the tls section of a configuration file:
@@ -780,6 +793,13 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error
*errors = append(*errors, err)
return
}
case "store_dir", "storedir":
// Check if JetStream configuration is also setting the storage directory.
if o.StoreDir != "" {
*errors = append(*errors, &configErr{tk, "Duplicate 'store_dir' configuration"})
return
}
o.StoreDir = v.(string)
case "jetstream":
err := parseJetStream(tk, o, errors, warnings)
if err != nil {
@@ -841,6 +861,21 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error
o.TLSTimeout = tc.Timeout
o.TLSMap = tc.Map
o.TLSPinnedCerts = tc.PinnedCerts
// Need to keep track of path of the original TLS config
// and certs path for OCSP Stapling monitoring.
o.tlsConfigOpts = tc
case "ocsp":
switch v.(type) {
case bool:
// Default is Auto which honors Must Staple status request
// but does not shutdown the server in case it is revoked,
// letting the client choose whether to trust or not the server.
o.OCSPConfig = &OCSPConfig{Mode: OCSPModeAuto}
default:
*errors = append(*errors, &configErr{tk, fmt.Sprintf("error parsing ocsp config: unsupported type %T", v)})
return
}
case "allow_non_tls":
o.AllowNonTLS = v.(bool)
case "write_deadline":
@@ -1530,6 +1565,10 @@ func parseJetStream(v interface{}, opts *Options, errors *[]error, warnings *[]e
tk, mv = unwrapValue(mv, &lt)
switch strings.ToLower(mk) {
case "store_dir", "storedir":
// StoreDir can be set at the top level as well so have to prevent ambiguous declarations.
if opts.StoreDir != "" {
return &configErr{tk, "Duplicate 'store_dir' configuration"}
}
opts.StoreDir = mv.(string)
case "max_memory_store", "max_mem_store", "max_mem":
opts.JetStreamMaxMemory = mv.(int64)

View File

@@ -149,6 +149,7 @@ func TestTLSConfigFile(t *testing.T) {
t.Fatal("Expected opts.TLSConfig to be non-nil")
}
opts.TLSConfig = nil
opts.tlsConfigOpts = nil
checkOptionsEqual(t, golden, opts)
// Now check TLSConfig a bit more closely

View File

@@ -50,6 +50,9 @@ type option interface {
// IsAuthChange indicates if this option requires reloading authorization.
IsAuthChange() bool
// IsTLSChange indicates if this option requires reloading TLS.
IsTLSChange() bool
// IsClusterPermsChange indicates if this option requires reloading
// cluster permissions.
IsClusterPermsChange() bool
@@ -74,6 +77,10 @@ func (n noopOption) IsAuthChange() bool {
return false
}
func (n noopOption) IsTLSChange() bool {
return false
}
func (n noopOption) IsClusterPermsChange() bool {
return false
}
@@ -202,6 +209,10 @@ func (t *tlsOption) Apply(server *Server) {
server.Noticef("Reloaded: tls = %s", message)
}
func (t *tlsOption) IsTLSChange() bool {
return true
}
// tlsTimeoutOption implements the option interface for the tls `timeout`
// setting.
type tlsTimeoutOption struct {
@@ -858,7 +869,8 @@ func imposeOrder(value interface{}) error {
case WebsocketOpts:
sort.Strings(value.AllowedOrigins)
case string, bool, int, int32, int64, time.Duration, float64, nil, LeafNodeOpts, ClusterOpts, *tls.Config, PinnedCertSet,
*URLAccResolver, *MemAccResolver, *DirAccResolver, *CacheDirAccResolver, Authentication, MQTTOpts, jwt.TagList:
*URLAccResolver, *MemAccResolver, *DirAccResolver, *CacheDirAccResolver, Authentication, MQTTOpts, jwt.TagList,
*OCSPConfig:
// explicitly skipped types
default:
// this will fail during unit tests
@@ -1257,6 +1269,7 @@ func (s *Server) applyOptions(ctx *reloadContext, opts []option) {
reloadClientTrcLvl = false
reloadJetstream = false
jsEnabled = false
reloadTLS = false
)
for _, opt := range opts {
opt.Apply(s)
@@ -1269,6 +1282,9 @@ func (s *Server) applyOptions(ctx *reloadContext, opts []option) {
if opt.IsAuthChange() {
reloadAuth = true
}
if opt.IsTLSChange() {
reloadTLS = true
}
if opt.IsClusterPermsChange() {
reloadClusterPerms = true
}
@@ -1312,9 +1328,63 @@ func (s *Server) applyOptions(ctx *reloadContext, opts []option) {
s.updateRemoteLeafNodesTLSConfig(newOpts)
}
if reloadTLS {
// Restart OCSP monitoring.
if err := s.reloadOCSP(); err != nil {
s.Warnf("Can't restart OCSP Stapling: %v", err)
}
}
s.Noticef("Reloaded server configuration")
}
func (s *Server) reloadOCSP() error {
opts := s.getOpts()
if err := s.setupOCSPStapleStoreDir(); err != nil {
return err
}
s.mu.Lock()
ocsps := s.ocsps
s.mu.Unlock()
// Stop all OCSP Stapling monitors in case there were any running.
var wasEnabled bool
for _, oc := range ocsps {
wasEnabled = true
oc.stop()
}
// Restart the monitors under the new configuration.
ocspm := make([]*OCSPMonitor, 0)
if config := opts.TLSConfig; config != nil {
tc, mon, err := s.NewOCSPMonitor(config)
if err != nil {
return err
}
// Check if an OCSP stapling monitor is required for this certificate.
if mon != nil {
s.Noticef("OCSP Stapling enabled for client connections")
ocspm = append(ocspm, mon)
// Override the TLS config with one that has OCSP enabled.
s.optsMu.Lock()
s.opts.TLSConfig = tc
s.optsMu.Unlock()
s.startGoRoutine(func() { mon.run() })
} else if wasEnabled {
s.Warnf("OCSP Stapling disabled for client connections")
}
}
// Replace stopped monitors with the new ones.
s.mu.Lock()
s.ocsps = ocspm
s.mu.Unlock()
return nil
}
// Update all cached debug and trace settings for every client
func (s *Server) reloadClientTraceLevel() {
opts := s.getOpts()

View File

@@ -236,6 +236,9 @@ type Server struct {
// MQTT structure
mqtt srvMQTT
// OCSP monitoring
ocsps []*OCSPMonitor
// exporting account name the importer experienced issues with
incompleteAccExporterMap sync.Map
@@ -1467,6 +1470,48 @@ func (s *Server) fetchAccount(name string) (*Account, error) {
return acc, nil
}
func (s *Server) setupOCSPStapleStoreDir() error {
opts := s.getOpts()
storeDir := opts.StoreDir
if storeDir == _EMPTY_ {
s.Warnf("OCSP Stapling disk cache is disabled (missing 'store_dir')")
return nil
}
storeDir = filepath.Join(storeDir, defaultOCSPStoreDir)
if stat, err := os.Stat(storeDir); os.IsNotExist(err) {
if err := os.MkdirAll(storeDir, defaultDirPerms); err != nil {
return fmt.Errorf("could not create OCSP storage directory - %v", err)
}
} else if stat == nil || !stat.IsDir() {
return fmt.Errorf("OCSP storage directory is not a directory")
}
return nil
}
func (s *Server) enableOCSP() error {
opts := s.getOpts()
// Start OCSP Stapling for client connections.
if config := opts.TLSConfig; config != nil {
tc, mon, err := s.NewOCSPMonitor(config)
if err != nil {
return err
}
// Check if an OCSP stapling monitor is required for this certificate.
if mon != nil {
s.Noticef("OCSP Stapling enabled for client connections")
s.ocsps = append(s.ocsps, mon)
// Override the TLS config with one that follows OCSP.
opts.TLSConfig = tc
s.startGoRoutine(func() { mon.run() })
}
}
// FIXME: Add support for leafnodes, routes, gateways, MQTT, WebSocket
return nil
}
// Start up the server, this will block.
// Start via a Go routine if needed.
func (s *Server) Start() {
@@ -1619,6 +1664,13 @@ func (s *Server) Start() {
})
}
// Setup OCSP Stapling. This will abort server from starting if there
// are no valid staples and OCSP policy is to Always or MustStaple.
if err := s.enableOCSP(); err != nil {
s.Fatalf("Can't enable OCSP Stapling: %v", err)
return
}
// Start monitoring if needed
if err := s.StartMonitoring(); err != nil {
s.Fatalf("Can't start monitoring: %v", err)

View File

@@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
NejC2QUTFLwq9vfjyA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
IKBPu3vS3nsv67DyBA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGCjCCA/KgAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdT
eW5hZGlhMRAwDgYDVQQLDAduYXRzLmlvMRUwEwYDVQQDDAxsb2NhbGhvc3QgY2Ex
HDAaBgkqhkiG9w0BCQEWDWRlcmVrQG5hdHMuaW8wHhcNMjEwNTExMjAxMTM1WhcN
MjkxMDE0MjAxMTM1WjCBnjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYD
VQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdTeW5hZGlhMRAwDgYDVQQLDAdu
YXRzLmlvMSgwJgYDVQQDDB9sb2NhbGhvc3Qgc2VydmVyIHN0YXR1cyByZXF1ZXN0
MRwwGgYJKoZIhvcNAQkBFg1kZXJla0BuYXRzLmlvMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAou+lxMBr5LpB6o6ZZ5S2najkJWYm555IEz5DlnCNxkX3
UfDaO9StTSlJscc7ZwtmOF1aSAqwnI6KTV43Te5qSU0DolyPTPwBECjr5G4PgRJk
tvBaqCUrdc6PXcwHEUDukWw5G8XxJ73UZuIuwvCg/ZRsOO3V9XYL+Q9chdXS/W/Q
ZCY4bOb/7AJoD7VuUKQp6KY9ptnqdmDsRAlWiKjtW9HPpldzLwLG/J5s4TVHrU+2
T1or+hywSKqzcjeJFAaPTsfKGdDiYAuW3yy7YlYdebsjlldGhi3Xo5J4FiAgyPa4
I2RVdEPVa8VmOKoEbdfEL3KVmB57KmnmnDiqLpCYxyx51GvE1Opf5mi1QH8gQ0cw
r7mJqsms99A+wyBojgdAYxZa+tDcZcZTj4v0r/9OdKu+vTkVIPr2OYPprWE0PfGJ
efDILOfRrmY8OqbWLDe5d2WEVhaGHoXtxlNoUrTUY6SRW+dvubiBcQIYkWnk1c/e
p/c7zkgCicJmBQdjDcNEiadTT7r0gHIoJO2bMKSqb7gz0IBqVOl409hn2FhxNgh1
mt4MlXl5FH5rCTUsNSR8va7r5IVNbdK6wUIfRtyLUBnDFYKR6qN8UwvPfr8JWsvZ
zT2jDcBWYabAj521KHqo0hwItW9JcD/CO0M43rPq18YzsYIR00IU8FJ7zVz7olEC
AwEAAaNkMGIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEQYIKwYBBQUHARgEBTAD
AgEFMDUGA1UdEQQuMCyCCWxvY2FsaG9zdIIfc2VydmVyLXN0YXR1cy1yZXF1ZXN0
LmxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAgEAVGa9luluxfm2FU5Plo/53tys
dA1IurRs+294adJBKc+LJtZIZjCW74KmqNvlN+UKC857STjnMi1fjSHyJKRpNLfo
wyMax7G3SCbOYnNjsbMgub9BoGlGqUgdU98av3SS7dyfJ5jgeiNfNrt3RUBGZpYE
70+d+m671dPhFlpXDTlRKpbdmCno5KfMDne+aLmTsZq2D0WYpjjAXJLIlexcF6KI
1Pz+VgthkqfPczSE0CMTAtL/zuOD6c4RqtYX8Q8A5E3U09mNo7kybVvLZrQPPAfZ
vbFbPwv4qZMRZ6ZXZiNTGXXnPNbE66/Ce+Jm7TKZrrfShG7ObUmyKI+X8tObCssd
xHKn59Fe5AL+gu6gzy6wC0wfBIH5gPMtkHwg9thhKd/T02LbL5iOXHIUhAVfk6qB
h3+9GaY6eu4yhU0/C9Qrnoq8QAWM8eqiaJ0i2f9rhbs1o5Yp4NX/fD5T9jcok9+L
BE34BGO6l0b01TVvfssiYmH7a0YYO3xVMy6IgJg7hBTVIXLqFLEKfrj+rNai6Th3
vv5rjkbJh4f3wzm5+uCFqM8BUBbJ/k5Pz/8qM105IbM89maW56R0DhsFZdMxOI+7
gXee7BH3cBS9LcYhAgNIwF7lVOQX62dF3rYvIdkGEWjEouteno2SZRtKHNsSvviW
8PTvHAb9z7LxEYEjwC4=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,70 @@
-----BEGIN CERTIFICATE-----
MIIGTDCCBDSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdT
eW5hZGlhMRAwDgYDVQQLDAduYXRzLmlvMRUwEwYDVQQDDAxsb2NhbGhvc3QgY2Ex
HDAaBgkqhkiG9w0BCQEWDWRlcmVrQG5hdHMuaW8wHhcNMjEwNTExMjAxMTM1WhcN
MjkxMDE0MjAxMTM1WjCBpzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYD
VQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdTeW5hZGlhMRAwDgYDVQQLDAdu
YXRzLmlvMTEwLwYDVQQDDChsb2NhbGhvc3Qgc2VydmVyIHN0YXR1cyByZXF1ZXN0
IHdpdGggdXJsMRwwGgYJKoZIhvcNAQkBFg1kZXJla0BuYXRzLmlvMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4IEf3ITJxeRTEYSXJ13LS8rvoOyRrQTo
mGSSIWzkvs9ELJN/C7AtWy98m+0+lpdtaQLLZaY/hdh6Wu7tqJ0LS8axz9vWXkjr
GoPCV2MedhQG4+P6zDXQaJyEBfW1kchZY+O8Lm/MHKITp3soo6To4J+sIbzg9XQt
Yxl38HaPEwDP2xn0FkY/0HhaSYuoAo+K/bQ1tVj7vnSgvZtUWDvu5NhkyEx7qJxL
/cNfUPGadyetB3rKZfQGgYMfxu7dl5bB5yJwYVOx7uzAN4bjejUtSZ+E8xarliSH
0hxPbekIeSayMfyWp/XYtQ+hENSAnhsSUgezlQ++yl1Al9XLiwELRDTaehbBGjAB
zhnqkpOTxG5Eg220MKD8xyqLWuZTDSHBukOXVHGsr3ct1PRjJ1CH7WIfyzUuq9IH
eVCfGcNaO7BjqJKChGwJGLFVdqfoJqi7vc2475SKvkVa/jbNMyXw/ic0POV9frdg
Nkwxm6jXGeNJ7jaJND02eSinY/glf6Ed1aRK7FEuyh8oM73JmAlJ6up0Axr349HG
Uu6XaUjKCJy/eTwkEaDyeyQRHgsYj2fpl8uGui8CSrBK3+Ha9ai5Ie2LNzI81khq
2ZLH6wbg/Y3UJsTeoXrWhZOcin3z17KJkiOGbsUBntac4ClfMwKNlF1NcUOp6EQh
0N7tXhnimpMCAwEAAaOBnDCBmTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAxBggr
BgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcuMC4wLjE6ODg4ODAR
BggrBgEFBQcBGAQFMAMCAQUwOQYDVR0RBDIwMIIJbG9jYWxob3N0giNzZXJ2ZXIt
c3RhdHVzLXJlcXVlc3QtdXJsLmxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAgEA
Vbc6tBhSLLS2/1sYzpjDn293nvBruNeConbj3GmjdFqkp3iD2jsVilLYZbgQfjJY
KEzWXYEXWTtpcpTxVFZFZwdc1C5oMmhUsUOkm7eEcVn5+oGR8k/kQ+pbSQQd3Ii8
uqZ3Z8vmD3kItM/8MWWGUdpNjqLwronTTO4XfqPLYtNIeFd6pjLmY3xMopOzl5G/
PY8010e8ZMU5xCzYCJ73vc8bhQMevoMuJFfNEzWWQiKwy74lAiJObIQ8wHkIgaa5
mrBl7oU7dvuz22eaItCY04DO06A5e3QJKOBsW0NOjcKRw6sHXbVtU1hp0CUsxcrz
HCttM22Pmi/8u+0blmWPHK5cPQoPQsks6QOXD8Ecp74oxB9sbJNUIJ3KJSkUZqUW
T/sbQhqHe/6DGEbl4WWpPsvBp2HRHOhaLlE3tPVLDSQE55mMpaSfeC6FjHvW38MD
aG6rGRI6Y9ibJBHEcy12kPo6APjSPtIGrAdCKBGBgUothKiiWAx/6ul+sKhzSL4N
iFjNODYdwwUxlINyX3jqSyHLjq5QFNH+aF1NOou1D1qbrDhzWJ09u1FrpNRTtf0F
2sAfEPihQu5DxnODqbwH5YOSNfGNXQXbY6JxZE7a5VCfocHa6u/uhN6jJg+aNllM
rTV3nI+hWq4RtvQnqAyuhMkX2q/T+QaRugRtJ1ywUtE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -0,0 +1,36 @@
-----BEGIN CERTIFICATE-----
MIIGTDCCBDSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdT
eW5hZGlhMRAwDgYDVQQLDAduYXRzLmlvMRUwEwYDVQQDDAxsb2NhbGhvc3QgY2Ex
HDAaBgkqhkiG9w0BCQEWDWRlcmVrQG5hdHMuaW8wHhcNMjEwNTExMjAxMTM1WhcN
MjkxMDE0MjAxMTM1WjCBpzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYD
VQQHDA1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKDAdTeW5hZGlhMRAwDgYDVQQLDAdu
YXRzLmlvMTEwLwYDVQQDDChsb2NhbGhvc3Qgc2VydmVyIHN0YXR1cyByZXF1ZXN0
IHdpdGggdXJsMRwwGgYJKoZIhvcNAQkBFg1kZXJla0BuYXRzLmlvMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4IEf3ITJxeRTEYSXJ13LS8rvoOyRrQTo
mGSSIWzkvs9ELJN/C7AtWy98m+0+lpdtaQLLZaY/hdh6Wu7tqJ0LS8axz9vWXkjr
GoPCV2MedhQG4+P6zDXQaJyEBfW1kchZY+O8Lm/MHKITp3soo6To4J+sIbzg9XQt
Yxl38HaPEwDP2xn0FkY/0HhaSYuoAo+K/bQ1tVj7vnSgvZtUWDvu5NhkyEx7qJxL
/cNfUPGadyetB3rKZfQGgYMfxu7dl5bB5yJwYVOx7uzAN4bjejUtSZ+E8xarliSH
0hxPbekIeSayMfyWp/XYtQ+hENSAnhsSUgezlQ++yl1Al9XLiwELRDTaehbBGjAB
zhnqkpOTxG5Eg220MKD8xyqLWuZTDSHBukOXVHGsr3ct1PRjJ1CH7WIfyzUuq9IH
eVCfGcNaO7BjqJKChGwJGLFVdqfoJqi7vc2475SKvkVa/jbNMyXw/ic0POV9frdg
Nkwxm6jXGeNJ7jaJND02eSinY/glf6Ed1aRK7FEuyh8oM73JmAlJ6up0Axr349HG
Uu6XaUjKCJy/eTwkEaDyeyQRHgsYj2fpl8uGui8CSrBK3+Ha9ai5Ie2LNzI81khq
2ZLH6wbg/Y3UJsTeoXrWhZOcin3z17KJkiOGbsUBntac4ClfMwKNlF1NcUOp6EQh
0N7tXhnimpMCAwEAAaOBnDCBmTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAxBggr
BgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcuMC4wLjE6ODg4ODAR
BggrBgEFBQcBGAQFMAMCAQUwOQYDVR0RBDIwMIIJbG9jYWxob3N0giNzZXJ2ZXIt
c3RhdHVzLXJlcXVlc3QtdXJsLmxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAgEA
Vbc6tBhSLLS2/1sYzpjDn293nvBruNeConbj3GmjdFqkp3iD2jsVilLYZbgQfjJY
KEzWXYEXWTtpcpTxVFZFZwdc1C5oMmhUsUOkm7eEcVn5+oGR8k/kQ+pbSQQd3Ii8
uqZ3Z8vmD3kItM/8MWWGUdpNjqLwronTTO4XfqPLYtNIeFd6pjLmY3xMopOzl5G/
PY8010e8ZMU5xCzYCJ73vc8bhQMevoMuJFfNEzWWQiKwy74lAiJObIQ8wHkIgaa5
mrBl7oU7dvuz22eaItCY04DO06A5e3QJKOBsW0NOjcKRw6sHXbVtU1hp0CUsxcrz
HCttM22Pmi/8u+0blmWPHK5cPQoPQsks6QOXD8Ecp74oxB9sbJNUIJ3KJSkUZqUW
T/sbQhqHe/6DGEbl4WWpPsvBp2HRHOhaLlE3tPVLDSQE55mMpaSfeC6FjHvW38MD
aG6rGRI6Y9ibJBHEcy12kPo6APjSPtIGrAdCKBGBgUothKiiWAx/6ul+sKhzSL4N
iFjNODYdwwUxlINyX3jqSyHLjq5QFNH+aF1NOou1D1qbrDhzWJ09u1FrpNRTtf0F
2sAfEPihQu5DxnODqbwH5YOSNfGNXQXbY6JxZE7a5VCfocHa6u/uhN6jJg+aNllM
rTV3nI+hWq4RtvQnqAyuhMkX2q/T+QaRugRtJ1ywUtE=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

1152
test/ocsp_test.go Normal file

File diff suppressed because it is too large Load Diff

789
vendor/golang.org/x/crypto/ocsp/ocsp.go generated vendored Normal file
View File

@@ -0,0 +1,789 @@
// Copyright 2013 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 ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
// are signed messages attesting to the validity of a certificate for a small
// period of time. This is used to manage revocation for X.509 certificates.
package ocsp // import "golang.org/x/crypto/ocsp"
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math/big"
"strconv"
"time"
)
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
// ResponseStatus contains the result of an OCSP request. See
// https://tools.ietf.org/html/rfc6960#section-2.3
type ResponseStatus int
const (
Success ResponseStatus = 0
Malformed ResponseStatus = 1
InternalError ResponseStatus = 2
TryLater ResponseStatus = 3
// Status code four is unused in OCSP. See
// https://tools.ietf.org/html/rfc6960#section-4.2.1
SignatureRequired ResponseStatus = 5
Unauthorized ResponseStatus = 6
)
func (r ResponseStatus) String() string {
switch r {
case Success:
return "success"
case Malformed:
return "malformed"
case InternalError:
return "internal error"
case TryLater:
return "try later"
case SignatureRequired:
return "signature required"
case Unauthorized:
return "unauthorized"
default:
return "unknown OCSP status: " + strconv.Itoa(int(r))
}
}
// ResponseError is an error that may be returned by ParseResponse to indicate
// that the response itself is an error, not just that it's indicating that a
// certificate is revoked, unknown, etc.
type ResponseError struct {
Status ResponseStatus
}
func (r ResponseError) Error() string {
return "ocsp: error from server: " + r.Status.String()
}
// These are internal structures that reflect the ASN.1 structure of an OCSP
// response. See RFC 2560, section 4.2.
type certID struct {
HashAlgorithm pkix.AlgorithmIdentifier
NameHash []byte
IssuerKeyHash []byte
SerialNumber *big.Int
}
// https://tools.ietf.org/html/rfc2560#section-4.1.1
type ocspRequest struct {
TBSRequest tbsRequest
}
type tbsRequest struct {
Version int `asn1:"explicit,tag:0,default:0,optional"`
RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
RequestList []request
}
type request struct {
Cert certID
}
type responseASN1 struct {
Status asn1.Enumerated
Response responseBytes `asn1:"explicit,tag:0,optional"`
}
type responseBytes struct {
ResponseType asn1.ObjectIdentifier
Response []byte
}
type basicResponse struct {
TBSResponseData responseData
SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
}
type responseData struct {
Raw asn1.RawContent
Version int `asn1:"optional,default:0,explicit,tag:0"`
RawResponderID asn1.RawValue
ProducedAt time.Time `asn1:"generalized"`
Responses []singleResponse
}
type singleResponse struct {
CertID certID
Good asn1.Flag `asn1:"tag:0,optional"`
Revoked revokedInfo `asn1:"tag:1,optional"`
Unknown asn1.Flag `asn1:"tag:2,optional"`
ThisUpdate time.Time `asn1:"generalized"`
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"`
}
type revokedInfo struct {
RevocationTime time.Time `asn1:"generalized"`
Reason asn1.Enumerated `asn1:"explicit,tag:0,optional"`
}
var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
}
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
var signatureAlgorithmDetails = []struct {
algo x509.SignatureAlgorithm
oid asn1.ObjectIdentifier
pubKeyAlgo x509.PublicKeyAlgorithm
hash crypto.Hash
}{
{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
}
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
var pubType x509.PublicKeyAlgorithm
switch pub := pub.(type) {
case *rsa.PublicKey:
pubType = x509.RSA
hashFunc = crypto.SHA256
sigAlgo.Algorithm = oidSignatureSHA256WithRSA
sigAlgo.Parameters = asn1.RawValue{
Tag: 5,
}
case *ecdsa.PublicKey:
pubType = x509.ECDSA
switch pub.Curve {
case elliptic.P224(), elliptic.P256():
hashFunc = crypto.SHA256
sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
case elliptic.P384():
hashFunc = crypto.SHA384
sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
case elliptic.P521():
hashFunc = crypto.SHA512
sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
default:
err = errors.New("x509: unknown elliptic curve")
}
default:
err = errors.New("x509: only RSA and ECDSA keys supported")
}
if err != nil {
return
}
if requestedSigAlgo == 0 {
return
}
found := false
for _, details := range signatureAlgorithmDetails {
if details.algo == requestedSigAlgo {
if details.pubKeyAlgo != pubType {
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
return
}
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
if hashFunc == 0 {
err = errors.New("x509: cannot sign with hash function requested")
return
}
found = true
break
}
}
if !found {
err = errors.New("x509: unknown SignatureAlgorithm")
}
return
}
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
// from crypto/x509 or crypto/x509/pkix.
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
for _, details := range signatureAlgorithmDetails {
if oid.Equal(details.oid) {
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
for hash, oid := range hashOIDs {
if oid.Equal(target) {
return hash
}
}
return crypto.Hash(0)
}
func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier {
for hash, oid := range hashOIDs {
if hash == target {
return oid
}
}
return nil
}
// This is the exposed reflection of the internal OCSP structures.
// The status values that can be expressed in OCSP. See RFC 6960.
const (
// Good means that the certificate is valid.
Good = iota
// Revoked means that the certificate has been deliberately revoked.
Revoked
// Unknown means that the OCSP responder doesn't know about the certificate.
Unknown
// ServerFailed is unused and was never used (see
// https://go-review.googlesource.com/#/c/18944). ParseResponse will
// return a ResponseError when an error response is parsed.
ServerFailed
)
// The enumerated reasons for revoking a certificate. See RFC 5280.
const (
Unspecified = 0
KeyCompromise = 1
CACompromise = 2
AffiliationChanged = 3
Superseded = 4
CessationOfOperation = 5
CertificateHold = 6
RemoveFromCRL = 8
PrivilegeWithdrawn = 9
AACompromise = 10
)
// Request represents an OCSP request. See RFC 6960.
type Request struct {
HashAlgorithm crypto.Hash
IssuerNameHash []byte
IssuerKeyHash []byte
SerialNumber *big.Int
}
// Marshal marshals the OCSP request to ASN.1 DER encoded form.
func (req *Request) Marshal() ([]byte, error) {
hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm)
if hashAlg == nil {
return nil, errors.New("Unknown hash algorithm")
}
return asn1.Marshal(ocspRequest{
tbsRequest{
Version: 0,
RequestList: []request{
{
Cert: certID{
pkix.AlgorithmIdentifier{
Algorithm: hashAlg,
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
},
req.IssuerNameHash,
req.IssuerKeyHash,
req.SerialNumber,
},
},
},
},
})
}
// Response represents an OCSP response containing a single SingleResponse. See
// RFC 6960.
type Response struct {
// Status is one of {Good, Revoked, Unknown}
Status int
SerialNumber *big.Int
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
RevocationReason int
Certificate *x509.Certificate
// TBSResponseData contains the raw bytes of the signed response. If
// Certificate is nil then this can be used to verify Signature.
TBSResponseData []byte
Signature []byte
SignatureAlgorithm x509.SignatureAlgorithm
// IssuerHash is the hash used to compute the IssuerNameHash and IssuerKeyHash.
// Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384, and crypto.SHA512.
// If zero, the default is crypto.SHA1.
IssuerHash crypto.Hash
// RawResponderName optionally contains the DER-encoded subject of the
// responder certificate. Exactly one of RawResponderName and
// ResponderKeyHash is set.
RawResponderName []byte
// ResponderKeyHash optionally contains the SHA-1 hash of the
// responder's public key. Exactly one of RawResponderName and
// ResponderKeyHash is set.
ResponderKeyHash []byte
// Extensions contains raw X.509 extensions from the singleExtensions field
// of the OCSP response. When parsing certificates, this can be used to
// extract non-critical extensions that are not parsed by this package. When
// marshaling OCSP responses, the Extensions field is ignored, see
// ExtraExtensions.
Extensions []pkix.Extension
// ExtraExtensions contains extensions to be copied, raw, into any marshaled
// OCSP response (in the singleExtensions field). Values override any
// extensions that would otherwise be produced based on the other fields. The
// ExtraExtensions field is not populated when parsing certificates, see
// Extensions.
ExtraExtensions []pkix.Extension
}
// These are pre-serialized error responses for the various non-success codes
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
// responder that supports only pre-signed responses as a response to requests
// for certificates with unknown status. See RFC 5019.
var (
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
)
// CheckSignatureFrom checks that the signature in resp is a valid signature
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
// the OCSP response contained an intermediate certificate that created the
// signature. That signature is checked by ParseResponse and only
// resp.Certificate remains to be validated.
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
}
// ParseError results from an invalid OCSP response.
type ParseError string
func (p ParseError) Error() string {
return string(p)
}
// ParseRequest parses an OCSP request in DER form. It only supports
// requests for a single certificate. Signed requests are not supported.
// If a request includes a signature, it will result in a ParseError.
func ParseRequest(bytes []byte) (*Request, error) {
var req ocspRequest
rest, err := asn1.Unmarshal(bytes, &req)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP request")
}
if len(req.TBSRequest.RequestList) == 0 {
return nil, ParseError("OCSP request contains no request body")
}
innerRequest := req.TBSRequest.RequestList[0]
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
if hashFunc == crypto.Hash(0) {
return nil, ParseError("OCSP request uses unknown hash function")
}
return &Request{
HashAlgorithm: hashFunc,
IssuerNameHash: innerRequest.Cert.NameHash,
IssuerKeyHash: innerRequest.Cert.IssuerKeyHash,
SerialNumber: innerRequest.Cert.SerialNumber,
}, nil
}
// ParseResponse parses an OCSP response in DER form. The response must contain
// only one certificate status. To parse the status of a specific certificate
// from a response which may contain multiple statuses, use ParseResponseForCert
// instead.
//
// If the response contains an embedded certificate, then that certificate will
// be used to verify the response signature. If the response contains an
// embedded certificate and issuer is not nil, then issuer will be used to verify
// the signature on the embedded certificate.
//
// If the response does not contain an embedded certificate and issuer is not
// nil, then issuer will be used to verify the response signature.
//
// Invalid responses and parse failures will result in a ParseError.
// Error responses will result in a ResponseError.
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
return ParseResponseForCert(bytes, nil, issuer)
}
// ParseResponseForCert acts identically to ParseResponse, except it supports
// parsing responses that contain multiple statuses. If the response contains
// multiple statuses and cert is not nil, then ParseResponseForCert will return
// the first status which contains a matching serial, otherwise it will return an
// error. If cert is nil, then the first status in the response will be returned.
func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
var resp responseASN1
rest, err := asn1.Unmarshal(bytes, &resp)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
if status := ResponseStatus(resp.Status); status != Success {
return nil, ResponseError{status}
}
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
return nil, ParseError("bad OCSP response type")
}
var basicResp basicResponse
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 {
return nil, ParseError("OCSP response contains bad number of responses")
}
var singleResp singleResponse
if cert == nil {
singleResp = basicResp.TBSResponseData.Responses[0]
} else {
match := false
for _, resp := range basicResp.TBSResponseData.Responses {
if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
singleResp = resp
match = true
break
}
}
if !match {
return nil, ParseError("no response matching the supplied certificate")
}
}
ret := &Response{
TBSResponseData: basicResp.TBSResponseData.Raw,
Signature: basicResp.Signature.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm),
Extensions: singleResp.SingleExtensions,
SerialNumber: singleResp.CertID.SerialNumber,
ProducedAt: basicResp.TBSResponseData.ProducedAt,
ThisUpdate: singleResp.ThisUpdate,
NextUpdate: singleResp.NextUpdate,
}
// Handle the ResponderID CHOICE tag. ResponderID can be flattened into
// TBSResponseData once https://go-review.googlesource.com/34503 has been
// released.
rawResponderID := basicResp.TBSResponseData.RawResponderID
switch rawResponderID.Tag {
case 1: // Name
var rdn pkix.RDNSequence
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder name")
}
ret.RawResponderName = rawResponderID.Bytes
case 2: // KeyHash
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &ret.ResponderKeyHash); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder key hash")
}
default:
return nil, ParseError("invalid responder id tag")
}
if len(basicResp.Certificates) > 0 {
// Responders should only send a single certificate (if they
// send any) that connects the responder's certificate to the
// original issuer. We accept responses with multiple
// certificates due to a number responders sending them[1], but
// ignore all but the first.
//
// [1] https://github.com/golang/go/issues/21527
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
if err != nil {
return nil, err
}
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
return nil, ParseError("bad signature on embedded certificate: " + err.Error())
}
if issuer != nil {
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
return nil, ParseError("bad OCSP signature: " + err.Error())
}
}
} else if issuer != nil {
if err := ret.CheckSignatureFrom(issuer); err != nil {
return nil, ParseError("bad OCSP signature: " + err.Error())
}
}
for _, ext := range singleResp.SingleExtensions {
if ext.Critical {
return nil, ParseError("unsupported critical extension")
}
}
for h, oid := range hashOIDs {
if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) {
ret.IssuerHash = h
break
}
}
if ret.IssuerHash == 0 {
return nil, ParseError("unsupported issuer hash algorithm")
}
switch {
case bool(singleResp.Good):
ret.Status = Good
case bool(singleResp.Unknown):
ret.Status = Unknown
default:
ret.Status = Revoked
ret.RevokedAt = singleResp.Revoked.RevocationTime
ret.RevocationReason = int(singleResp.Revoked.Reason)
}
return ret, nil
}
// RequestOptions contains options for constructing OCSP requests.
type RequestOptions struct {
// Hash contains the hash function that should be used when
// constructing the OCSP request. If zero, SHA-1 will be used.
Hash crypto.Hash
}
func (opts *RequestOptions) hash() crypto.Hash {
if opts == nil || opts.Hash == 0 {
// SHA-1 is nearly universally used in OCSP.
return crypto.SHA1
}
return opts.Hash
}
// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
// opts is nil then sensible defaults are used.
func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
hashFunc := opts.hash()
// OCSP seems to be the only place where these raw hash identifiers are
// used. I took the following from
// http://msdn.microsoft.com/en-us/library/ff635603.aspx
_, ok := hashOIDs[hashFunc]
if !ok {
return nil, x509.ErrUnsupportedAlgorithm
}
if !hashFunc.Available() {
return nil, x509.ErrUnsupportedAlgorithm
}
h := opts.hash().New()
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
}
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
issuerNameHash := h.Sum(nil)
req := &Request{
HashAlgorithm: hashFunc,
IssuerNameHash: issuerNameHash,
IssuerKeyHash: issuerKeyHash,
SerialNumber: cert.SerialNumber,
}
return req.Marshal()
}
// CreateResponse returns a DER-encoded OCSP response with the specified contents.
// The fields in the response are populated as follows:
//
// The responder cert is used to populate the responder's name field, and the
// certificate itself is provided alongside the OCSP response signature.
//
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
//
// The template is used to populate the SerialNumber, Status, RevokedAt,
// RevocationReason, ThisUpdate, and NextUpdate fields.
//
// If template.IssuerHash is not set, SHA1 will be used.
//
// The ProducedAt date is automatically set to the current date, to the nearest minute.
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
}
if template.IssuerHash == 0 {
template.IssuerHash = crypto.SHA1
}
hashOID := getOIDFromHashAlgorithm(template.IssuerHash)
if hashOID == nil {
return nil, errors.New("unsupported issuer hash algorithm")
}
if !template.IssuerHash.Available() {
return nil, fmt.Errorf("issuer hash algorithm %v not linked into binary", template.IssuerHash)
}
h := template.IssuerHash.New()
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
issuerNameHash := h.Sum(nil)
innerResponse := singleResponse{
CertID: certID{
HashAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: hashOID,
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
},
NameHash: issuerNameHash,
IssuerKeyHash: issuerKeyHash,
SerialNumber: template.SerialNumber,
},
ThisUpdate: template.ThisUpdate.UTC(),
NextUpdate: template.NextUpdate.UTC(),
SingleExtensions: template.ExtraExtensions,
}
switch template.Status {
case Good:
innerResponse.Good = true
case Unknown:
innerResponse.Unknown = true
case Revoked:
innerResponse.Revoked = revokedInfo{
RevocationTime: template.RevokedAt.UTC(),
Reason: asn1.Enumerated(template.RevocationReason),
}
}
rawResponderID := asn1.RawValue{
Class: 2, // context-specific
Tag: 1, // Name (explicit tag)
IsCompound: true,
Bytes: responderCert.RawSubject,
}
tbsResponseData := responseData{
Version: 0,
RawResponderID: rawResponderID,
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
Responses: []singleResponse{innerResponse},
}
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
if err != nil {
return nil, err
}
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
if err != nil {
return nil, err
}
responseHash := hashFunc.New()
responseHash.Write(tbsResponseDataDER)
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
if err != nil {
return nil, err
}
response := basicResponse{
TBSResponseData: tbsResponseData,
SignatureAlgorithm: signatureAlgorithm,
Signature: asn1.BitString{
Bytes: signature,
BitLength: 8 * len(signature),
},
}
if template.Certificate != nil {
response.Certificates = []asn1.RawValue{
{FullBytes: template.Certificate.Raw},
}
}
responseDER, err := asn1.Marshal(response)
if err != nil {
return nil, err
}
return asn1.Marshal(responseASN1{
Status: asn1.Enumerated(Success),
Response: responseBytes{
ResponseType: idPKIXOCSPBasic,
Response: responseDER,
},
})
}

3
vendor/modules.txt vendored
View File

@@ -20,12 +20,13 @@ github.com/nats-io/nkeys
# github.com/nats-io/nuid v1.0.1
## explicit
github.com/nats-io/nuid
# golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b
# golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64
## explicit
golang.org/x/crypto/bcrypt
golang.org/x/crypto/blowfish
golang.org/x/crypto/ed25519
golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/ocsp
# golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
## explicit
golang.org/x/sys/cpu