From 44ed54255add58aee7194c6c0756323b4c5e1a95 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 31 Jul 2019 13:17:29 -0400 Subject: [PATCH] deps: update github.com/cloudflare/cfssl to v1.3.4 This will unblock pre-issuance linting support by updating the `github.com/cloudflare/cfssl` dependency to the `1.3.4` tag which notably includes the zlint integration developed in cloudflare/cfssl#1015 --- go.mod | 2 +- go.sum | 3 + .../cloudflare/cfssl/config/config.go | 55 +++++++- vendor/github.com/cloudflare/cfssl/csr/csr.go | 48 +++---- .../cloudflare/cfssl/signer/local/local.go | 128 ++++++++++++++++-- vendor/modules.txt | 2 +- 6 files changed, 194 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index 9a9c8a23320..f89af14ec32 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect github.com/beeker1121/goque v0.0.0-20170321141813-4044bc29b280 github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 // indirect - github.com/cloudflare/cfssl v0.0.0-20190616170404-1bf3e59ec1cf + github.com/cloudflare/cfssl v0.0.0-20190716004220-2185c182e6ba github.com/go-gorp/gorp v2.0.0+incompatible // indirect github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4 github.com/golang/mock v1.2.0 diff --git a/go.sum b/go.sum index 29599cd0d1d..29c0d79aa1f 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,9 @@ github.com/cloudflare/cfssl v0.0.0-20190409034051-768cd563887f h1:+2gpkLTePKn3qD github.com/cloudflare/cfssl v0.0.0-20190409034051-768cd563887f/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/cfssl v0.0.0-20190616170404-1bf3e59ec1cf h1:0/1jvWAjicn0BZTNnv5KLnf29+B01Yh1O0BMLrgOCb0= github.com/cloudflare/cfssl v0.0.0-20190616170404-1bf3e59ec1cf/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= +github.com/cloudflare/cfssl v0.0.0-20190716004220-2185c182e6ba h1:gI0EFi8pFYkjYBA3A78uFXRUzAx1pkYS1lVcsYdSZhE= +github.com/cloudflare/cfssl v0.0.0-20190716004220-2185c182e6ba/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= +github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1CUzYdAZXijuvLuRMirgiXdf3zsM2Ig= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/vendor/github.com/cloudflare/cfssl/config/config.go b/vendor/github.com/cloudflare/cfssl/config/config.go index 9a1a488be27..376d8af26be 100644 --- a/vendor/github.com/cloudflare/cfssl/config/config.go +++ b/vendor/github.com/cloudflare/cfssl/config/config.go @@ -19,6 +19,7 @@ import ( "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/log" ocspConfig "github.com/cloudflare/cfssl/ocsp/config" + "github.com/zmap/zlint/lints" ) // A CSRWhitelist stores booleans for fields in the CSR. If a CSRWhitelist is @@ -81,6 +82,7 @@ type SigningProfile struct { ExpiryString string `json:"expiry"` BackdateString string `json:"backdate"` AuthKeyName string `json:"auth_key"` + PrevAuthKeyName string `json:"prev_auth_key"` // to suppport key rotation RemoteName string `json:"remote"` NotBefore time.Time `json:"not_before"` NotAfter time.Time `json:"not_after"` @@ -89,11 +91,25 @@ type SigningProfile struct { CTLogServers []string `json:"ct_log_servers"` AllowedExtensions []OID `json:"allowed_extensions"` CertStore string `json:"cert_store"` + // LintErrLevel controls preissuance linting for the signing profile. + // 0 = no linting is performed [default] + // 2..3 = reserved + // 3 = all lint results except pass are considered errors + // 4 = all lint results except pass and notice are considered errors + // 5 = all lint results except pass, notice and warn are considered errors + // 6 = all lint results except pass, notice, warn and error are considered errors. + // 7 = lint is performed, no lint results are treated as errors. + LintErrLevel lints.LintStatus `json:"lint_error_level"` + // IgnoredLints lists zlint lint names to ignore. Any lint results from + // matching lints will be ignored no matter what the configured LintErrLevel + // is. + IgnoredLints []string `json:"ignored_lints"` Policies []CertificatePolicy Expiry time.Duration Backdate time.Duration Provider auth.Provider + PrevProvider auth.Provider // to suppport key rotation RemoteProvider auth.Provider RemoteServer string RemoteCAs *x509.CertPool @@ -102,6 +118,9 @@ type SigningProfile struct { NameWhitelist *regexp.Regexp ExtensionWhitelist map[string]bool ClientProvidesSerialNumbers bool + // IgnoredLintsMap is a bool map created from IgnoredLints when the profile is + // loaded. It facilitates set membership testing. + IgnoredLintsMap map[string]bool } // UnmarshalJSON unmarshals a JSON string into an OID. @@ -229,7 +248,7 @@ func (p *SigningProfile) populate(cfg *Config) error { if p.AuthKeyName != "" { log.Debug("match auth key in profile to auth_keys section") - if key, ok := cfg.AuthKeys[p.AuthKeyName]; ok == true { + if key, ok := cfg.AuthKeys[p.AuthKeyName]; ok { if key.Type == "standard" { p.Provider, err = auth.New(key.Key, nil) if err != nil { @@ -248,6 +267,27 @@ func (p *SigningProfile) populate(cfg *Config) error { } } + if p.PrevAuthKeyName != "" { + log.Debug("match previous auth key in profile to auth_keys section") + if key, ok := cfg.AuthKeys[p.PrevAuthKeyName]; ok { + if key.Type == "standard" { + p.PrevProvider, err = auth.New(key.Key, nil) + if err != nil { + log.Debugf("failed to create new standard auth provider: %v", err) + return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, + errors.New("failed to create new standard auth provider")) + } + } else { + log.Debugf("unknown authentication type %v", key.Type) + return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, + errors.New("unknown authentication type")) + } + } else { + return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, + errors.New("failed to find prev_auth_key in auth_keys section")) + } + } + if p.AuthRemote.AuthKeyName != "" { log.Debug("match auth remote key in profile to auth_keys section") if key, ok := cfg.AuthKeys[p.AuthRemote.AuthKeyName]; ok == true { @@ -284,6 +324,11 @@ func (p *SigningProfile) populate(cfg *Config) error { p.ExtensionWhitelist[asn1.ObjectIdentifier(oid).String()] = true } + p.IgnoredLintsMap = map[string]bool{} + for _, lintName := range p.IgnoredLints { + p.IgnoredLintsMap[lintName] = true + } + return nil } @@ -404,7 +449,8 @@ func (p *SigningProfile) Usages() (ku x509.KeyUsage, eku []x509.ExtKeyUsage, unk // valid local default profile has defined at least a default expiration. // A valid remote profile (default or not) has remote signer initialized. // In addition, a remote profile must has a valid auth provider if auth -// key defined. +// key defined. A valid profile must not include a lint_error_level outside of +// [0,8). func (p *SigningProfile) validProfile(isDefault bool) bool { if p == nil { return false @@ -461,6 +507,11 @@ func (p *SigningProfile) validProfile(isDefault bool) bool { } } + if p.LintErrLevel < 0 || p.LintErrLevel >= 8 { + log.Debugf("invalid profile: lint_error_level outside of range [0,8)") + return false + } + log.Debugf("profile is valid") return true } diff --git a/vendor/github.com/cloudflare/cfssl/csr/csr.go b/vendor/github.com/cloudflare/cfssl/csr/csr.go index 17c69460f32..94f05d9be84 100644 --- a/vendor/github.com/cloudflare/cfssl/csr/csr.go +++ b/vendor/github.com/cloudflare/cfssl/csr/csr.go @@ -30,46 +30,38 @@ const ( // A Name contains the SubjectInfo fields. type Name struct { - C string // Country - ST string // State - L string // Locality - O string // OrganisationName - OU string // OrganisationalUnitName - SerialNumber string + C string `json:"C,omitempty" yaml:"C,omitempty"` // Country + ST string `json:"ST,omitempty" yaml:"ST,omitempty"` // State + L string `json:"L,omitempty" yaml:"L,omitempty"` // Locality + O string `json:"O,omitempty" yaml:"O,omitempty"` // OrganisationName + OU string `json:"OU,omitempty" yaml:"OU,omitempty"` // OrganisationalUnitName + SerialNumber string `json:"SerialNumber,omitempty" yaml:"SerialNumber,omitempty"` } -// A KeyRequest is a generic request for a new key. -type KeyRequest interface { - Algo() string - Size() int - Generate() (crypto.PrivateKey, error) - SigAlgo() x509.SignatureAlgorithm -} - -// A BasicKeyRequest contains the algorithm and key size for a new private key. -type BasicKeyRequest struct { +// A KeyRequest contains the algorithm and key size for a new private key. +type KeyRequest struct { A string `json:"algo" yaml:"algo"` S int `json:"size" yaml:"size"` } -// NewBasicKeyRequest returns a default BasicKeyRequest. -func NewBasicKeyRequest() *BasicKeyRequest { - return &BasicKeyRequest{"ecdsa", curveP256} +// NewKeyRequest returns a default KeyRequest. +func NewKeyRequest() *KeyRequest { + return &KeyRequest{"ecdsa", curveP256} } // Algo returns the requested key algorithm represented as a string. -func (kr *BasicKeyRequest) Algo() string { +func (kr *KeyRequest) Algo() string { return kr.A } // Size returns the requested key size. -func (kr *BasicKeyRequest) Size() int { +func (kr *KeyRequest) Size() int { return kr.S } // Generate generates a key as specified in the request. Currently, // only ECDSA and RSA are supported. -func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) { +func (kr *KeyRequest) Generate() (crypto.PrivateKey, error) { log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size()) switch kr.Algo() { case "rsa": @@ -100,7 +92,7 @@ func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) { // SigAlgo returns an appropriate X.509 signature algorithm given the // key request's type and size. -func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm { +func (kr *KeyRequest) SigAlgo() x509.SignatureAlgorithm { switch kr.Algo() { case "rsa": switch { @@ -140,19 +132,19 @@ type CAConfig struct { // A CertificateRequest encapsulates the API interface to the // certificate request functionality. type CertificateRequest struct { - CN string + CN string `json:"CN" yaml:"CN"` Names []Name `json:"names" yaml:"names"` Hosts []string `json:"hosts" yaml:"hosts"` - KeyRequest KeyRequest `json:"key,omitempty" yaml:"key,omitempty"` + KeyRequest *KeyRequest `json:"key,omitempty" yaml:"key,omitempty"` CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"` SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"` } // New returns a new, empty CertificateRequest with a -// BasicKeyRequest. +// KeyRequest. func New() *CertificateRequest { return &CertificateRequest{ - KeyRequest: NewBasicKeyRequest(), + KeyRequest: NewKeyRequest(), } } @@ -194,7 +186,7 @@ type BasicConstraints struct { func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) { log.Info("received CSR") if req.KeyRequest == nil { - req.KeyRequest = NewBasicKeyRequest() + req.KeyRequest = NewKeyRequest() } log.Infof("generating key: %s-%d", req.KeyRequest.Algo(), req.KeyRequest.Size()) diff --git a/vendor/github.com/cloudflare/cfssl/signer/local/local.go b/vendor/github.com/cloudflare/cfssl/signer/local/local.go index f247d785ba5..d4004649e74 100644 --- a/vendor/github.com/cloudflare/cfssl/signer/local/local.go +++ b/vendor/github.com/cloudflare/cfssl/signer/local/local.go @@ -4,6 +4,8 @@ package local import ( "bytes" "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" @@ -11,6 +13,7 @@ import ( "encoding/hex" "encoding/pem" "errors" + "fmt" "io" "math/big" "net" @@ -29,14 +32,21 @@ import ( "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/client" "github.com/google/certificate-transparency-go/jsonclient" + + zx509 "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint" + "github.com/zmap/zlint/lints" "golang.org/x/net/context" ) // Signer contains a signer that uses the standard library to // support both ECDSA and RSA CA keys. type Signer struct { - ca *x509.Certificate - priv crypto.Signer + ca *x509.Certificate + priv crypto.Signer + // lintPriv is generated randomly when pre-issuance linting is configured and + // used to sign TBSCertificates for linting. + lintPriv crypto.Signer policy *config.Signing sigAlgo x509.SignatureAlgorithm dbAccessor certdb.Accessor @@ -55,11 +65,30 @@ func NewSigner(priv crypto.Signer, cert *x509.Certificate, sigAlgo x509.Signatur return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy) } + var lintPriv crypto.Signer + // If there is at least one profile (including the default) that configures + // pre-issuance linting then generate the one-off lintPriv key. + for _, profile := range policy.Profiles { + if profile.LintErrLevel > 0 || policy.Default.LintErrLevel > 0 { + // In the future there may be demand for specifying the type of signer used + // for pre-issuance linting in configuration. For now we assume that signing + // with a randomly generated P-256 ECDSA private key is acceptable for all cases + // where linting is requested. + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, cferr.New(cferr.PrivateKeyError, cferr.GenerationFailed) + } + lintPriv = k + break + } + } + return &Signer{ - ca: cert, - priv: priv, - sigAlgo: sigAlgo, - policy: policy, + ca: cert, + priv: priv, + lintPriv: lintPriv, + sigAlgo: sigAlgo, + policy: policy, }, nil } @@ -97,7 +126,73 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy) } -func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) { +// LintError is an error type returned when pre-issuance linting is configured +// in a signing profile and a TBS Certificate fails linting. It wraps the +// concrete zlint LintResults so that callers can further inspect the cause of +// the failing lints. +type LintError struct { + ErrorResults map[string]lints.LintResult +} + +func (e *LintError) Error() string { + return fmt.Sprintf("pre-issuance linting found %d error results", + len(e.ErrorResults)) +} + +// lint performs pre-issuance linting of a given TBS certificate template when +// the provided errLevel is > 0. Any lint results with a status higher than the +// errLevel that isn't created by a lint in the ignoreMap will result in +// a LintError being returned to the caller. Note that the template is provided +// by-value and not by-reference. This is important as the lint function needs +// to mutate the template's signature algorithm to match the lintPriv. +func (s *Signer) lint(template x509.Certificate, errLevel lints.LintStatus, ignoreMap map[string]bool) error { + // Always return nil when linting is disabled (lints.Reserved == 0). + if errLevel == lints.Reserved { + return nil + } + // without a lintPriv key to use to sign the tbsCertificate we can't lint it. + if s.lintPriv == nil { + return cferr.New(cferr.PrivateKeyError, cferr.Unavailable) + } + + // The template's SignatureAlgorithm must be mutated to match the lintPriv or + // x509.CreateCertificate will error because of the mismatch. At the time of + // writing s.lintPriv is always an ECDSA private key. This switch will need to + // be expanded if the lint key type is made configurable. + switch s.lintPriv.(type) { + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA256 + default: + return cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch) + } + + prelintBytes, err := x509.CreateCertificate(rand.Reader, &template, s.ca, template.PublicKey, s.lintPriv) + if err != nil { + return cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) + } + prelintCert, err := zx509.ParseCertificate(prelintBytes) + if err != nil { + return cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + errorResults := map[string]lints.LintResult{} + results := zlint.LintCertificate(prelintCert) + for name, res := range results.Results { + if ignoreMap[name] { + continue + } + if res.Status > errLevel { + errorResults[name] = *res + } + } + if len(errorResults) > 0 { + return &LintError{ + ErrorResults: errorResults, + } + } + return nil +} + +func (s *Signer) sign(template *x509.Certificate, lintErrLevel lints.LintStatus, lintIgnore map[string]bool) (cert []byte, err error) { var initRoot bool if s.ca == nil { if !template.IsCA { @@ -111,6 +206,10 @@ func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) { initRoot = true } + if err := s.lint(*template, lintErrLevel, lintIgnore); err != nil { + return nil, err + } + derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) @@ -355,7 +454,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}} var poisonedPreCert = certTBS poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension) - cert, err = s.sign(&poisonedPreCert) + cert, err = s.sign(&poisonedPreCert, profile.LintErrLevel, profile.IgnoredLintsMap) if err != nil { return } @@ -398,8 +497,9 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { var SCTListExtension = pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedSCTList} certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension) } + var signedCert []byte - signedCert, err = s.sign(&certTBS) + signedCert, err = s.sign(&certTBS, profile.LintErrLevel, profile.IgnoredLintsMap) if err != nil { return nil, err } @@ -437,7 +537,9 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { // except for the removal of the poison extension and the addition of the SCT list // extension. SignFromPrecert does not verify that the contents of the certificate // still match the signing profile of the signer, it only requires that the precert -// was previously signed by the Signers CA. +// was previously signed by the Signers CA. Similarly, any linting configured +// by the profile used to sign the precert will not be re-applied to the final +// cert and must be done separately by the caller. func (s *Signer) SignFromPrecert(precert *x509.Certificate, scts []ct.SignedCertificateTimestamp) ([]byte, error) { // Verify certificate was signed by s.ca if err := precert.CheckSignatureFrom(s.ca); err != nil { @@ -506,8 +608,10 @@ func (s *Signer) SignFromPrecert(precert *x509.Certificate, scts []ct.SignedCert // Insert the SCT list extension tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions, sctExt) - // Sign the tbsCert - return s.sign(&tbsCert) + // Sign the tbsCert. Linting is always disabled because there is no way for + // this API to know the correct lint settings to use because there is no + // reference to the signing profile of the precert available. + return s.sign(&tbsCert, 0, nil) } // Info return a populated info.Resp struct or an error. diff --git a/vendor/modules.txt b/vendor/modules.txt index f0c05e0617b..cd6d5a8113c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2,7 +2,7 @@ github.com/beeker1121/goque # github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 github.com/beorn7/perks/quantile -# github.com/cloudflare/cfssl v0.0.0-20190616170404-1bf3e59ec1cf +# github.com/cloudflare/cfssl v0.0.0-20190716004220-2185c182e6ba github.com/cloudflare/cfssl/config github.com/cloudflare/cfssl/errors github.com/cloudflare/cfssl/ocsp