deps: update github.com/cloudflare/cfssl to v1.3.4 (#4377)
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
This commit is contained in:
parent
75dcac2272
commit
17b74cfb55
2
go.mod
2
go.mod
|
|
@ -7,7 +7,7 @@ require (
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect
|
||||||
github.com/beeker1121/goque v0.0.0-20170321141813-4044bc29b280
|
github.com/beeker1121/goque v0.0.0-20170321141813-4044bc29b280
|
||||||
github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1 // indirect
|
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-gorp/gorp v2.0.0+incompatible // indirect
|
||||||
github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4
|
github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4
|
||||||
github.com/golang/mock v1.2.0
|
github.com/golang/mock v1.2.0
|
||||||
|
|
|
||||||
3
go.sum
3
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-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 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-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 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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/cloudflare/cfssl/helpers"
|
"github.com/cloudflare/cfssl/helpers"
|
||||||
"github.com/cloudflare/cfssl/log"
|
"github.com/cloudflare/cfssl/log"
|
||||||
ocspConfig "github.com/cloudflare/cfssl/ocsp/config"
|
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
|
// A CSRWhitelist stores booleans for fields in the CSR. If a CSRWhitelist is
|
||||||
|
|
@ -81,6 +82,7 @@ type SigningProfile struct {
|
||||||
ExpiryString string `json:"expiry"`
|
ExpiryString string `json:"expiry"`
|
||||||
BackdateString string `json:"backdate"`
|
BackdateString string `json:"backdate"`
|
||||||
AuthKeyName string `json:"auth_key"`
|
AuthKeyName string `json:"auth_key"`
|
||||||
|
PrevAuthKeyName string `json:"prev_auth_key"` // to suppport key rotation
|
||||||
RemoteName string `json:"remote"`
|
RemoteName string `json:"remote"`
|
||||||
NotBefore time.Time `json:"not_before"`
|
NotBefore time.Time `json:"not_before"`
|
||||||
NotAfter time.Time `json:"not_after"`
|
NotAfter time.Time `json:"not_after"`
|
||||||
|
|
@ -89,11 +91,25 @@ type SigningProfile struct {
|
||||||
CTLogServers []string `json:"ct_log_servers"`
|
CTLogServers []string `json:"ct_log_servers"`
|
||||||
AllowedExtensions []OID `json:"allowed_extensions"`
|
AllowedExtensions []OID `json:"allowed_extensions"`
|
||||||
CertStore string `json:"cert_store"`
|
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
|
Policies []CertificatePolicy
|
||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
Backdate time.Duration
|
Backdate time.Duration
|
||||||
Provider auth.Provider
|
Provider auth.Provider
|
||||||
|
PrevProvider auth.Provider // to suppport key rotation
|
||||||
RemoteProvider auth.Provider
|
RemoteProvider auth.Provider
|
||||||
RemoteServer string
|
RemoteServer string
|
||||||
RemoteCAs *x509.CertPool
|
RemoteCAs *x509.CertPool
|
||||||
|
|
@ -102,6 +118,9 @@ type SigningProfile struct {
|
||||||
NameWhitelist *regexp.Regexp
|
NameWhitelist *regexp.Regexp
|
||||||
ExtensionWhitelist map[string]bool
|
ExtensionWhitelist map[string]bool
|
||||||
ClientProvidesSerialNumbers 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.
|
// UnmarshalJSON unmarshals a JSON string into an OID.
|
||||||
|
|
@ -229,7 +248,7 @@ func (p *SigningProfile) populate(cfg *Config) error {
|
||||||
|
|
||||||
if p.AuthKeyName != "" {
|
if p.AuthKeyName != "" {
|
||||||
log.Debug("match auth key in profile to auth_keys section")
|
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" {
|
if key.Type == "standard" {
|
||||||
p.Provider, err = auth.New(key.Key, nil)
|
p.Provider, err = auth.New(key.Key, nil)
|
||||||
if err != 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 != "" {
|
if p.AuthRemote.AuthKeyName != "" {
|
||||||
log.Debug("match auth remote key in profile to auth_keys section")
|
log.Debug("match auth remote key in profile to auth_keys section")
|
||||||
if key, ok := cfg.AuthKeys[p.AuthRemote.AuthKeyName]; ok == true {
|
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.ExtensionWhitelist[asn1.ObjectIdentifier(oid).String()] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.IgnoredLintsMap = map[string]bool{}
|
||||||
|
for _, lintName := range p.IgnoredLints {
|
||||||
|
p.IgnoredLintsMap[lintName] = true
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
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.
|
// valid local default profile has defined at least a default expiration.
|
||||||
// A valid remote profile (default or not) has remote signer initialized.
|
// A valid remote profile (default or not) has remote signer initialized.
|
||||||
// In addition, a remote profile must has a valid auth provider if auth
|
// 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 {
|
func (p *SigningProfile) validProfile(isDefault bool) bool {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return false
|
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")
|
log.Debugf("profile is valid")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,46 +30,38 @@ const (
|
||||||
|
|
||||||
// A Name contains the SubjectInfo fields.
|
// A Name contains the SubjectInfo fields.
|
||||||
type Name struct {
|
type Name struct {
|
||||||
C string // Country
|
C string `json:"C,omitempty" yaml:"C,omitempty"` // Country
|
||||||
ST string // State
|
ST string `json:"ST,omitempty" yaml:"ST,omitempty"` // State
|
||||||
L string // Locality
|
L string `json:"L,omitempty" yaml:"L,omitempty"` // Locality
|
||||||
O string // OrganisationName
|
O string `json:"O,omitempty" yaml:"O,omitempty"` // OrganisationName
|
||||||
OU string // OrganisationalUnitName
|
OU string `json:"OU,omitempty" yaml:"OU,omitempty"` // OrganisationalUnitName
|
||||||
SerialNumber string
|
SerialNumber string `json:"SerialNumber,omitempty" yaml:"SerialNumber,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// A KeyRequest is a generic request for a new key.
|
// A KeyRequest contains the algorithm and key size for a new private key.
|
||||||
type KeyRequest interface {
|
type KeyRequest struct {
|
||||||
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 string `json:"algo" yaml:"algo"`
|
A string `json:"algo" yaml:"algo"`
|
||||||
S int `json:"size" yaml:"size"`
|
S int `json:"size" yaml:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasicKeyRequest returns a default BasicKeyRequest.
|
// NewKeyRequest returns a default KeyRequest.
|
||||||
func NewBasicKeyRequest() *BasicKeyRequest {
|
func NewKeyRequest() *KeyRequest {
|
||||||
return &BasicKeyRequest{"ecdsa", curveP256}
|
return &KeyRequest{"ecdsa", curveP256}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Algo returns the requested key algorithm represented as a string.
|
// Algo returns the requested key algorithm represented as a string.
|
||||||
func (kr *BasicKeyRequest) Algo() string {
|
func (kr *KeyRequest) Algo() string {
|
||||||
return kr.A
|
return kr.A
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the requested key size.
|
// Size returns the requested key size.
|
||||||
func (kr *BasicKeyRequest) Size() int {
|
func (kr *KeyRequest) Size() int {
|
||||||
return kr.S
|
return kr.S
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate generates a key as specified in the request. Currently,
|
// Generate generates a key as specified in the request. Currently,
|
||||||
// only ECDSA and RSA are supported.
|
// 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())
|
log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size())
|
||||||
switch kr.Algo() {
|
switch kr.Algo() {
|
||||||
case "rsa":
|
case "rsa":
|
||||||
|
|
@ -100,7 +92,7 @@ func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) {
|
||||||
|
|
||||||
// SigAlgo returns an appropriate X.509 signature algorithm given the
|
// SigAlgo returns an appropriate X.509 signature algorithm given the
|
||||||
// key request's type and size.
|
// key request's type and size.
|
||||||
func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm {
|
func (kr *KeyRequest) SigAlgo() x509.SignatureAlgorithm {
|
||||||
switch kr.Algo() {
|
switch kr.Algo() {
|
||||||
case "rsa":
|
case "rsa":
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -140,19 +132,19 @@ type CAConfig struct {
|
||||||
// A CertificateRequest encapsulates the API interface to the
|
// A CertificateRequest encapsulates the API interface to the
|
||||||
// certificate request functionality.
|
// certificate request functionality.
|
||||||
type CertificateRequest struct {
|
type CertificateRequest struct {
|
||||||
CN string
|
CN string `json:"CN" yaml:"CN"`
|
||||||
Names []Name `json:"names" yaml:"names"`
|
Names []Name `json:"names" yaml:"names"`
|
||||||
Hosts []string `json:"hosts" yaml:"hosts"`
|
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"`
|
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
|
||||||
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
|
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new, empty CertificateRequest with a
|
// New returns a new, empty CertificateRequest with a
|
||||||
// BasicKeyRequest.
|
// KeyRequest.
|
||||||
func New() *CertificateRequest {
|
func New() *CertificateRequest {
|
||||||
return &CertificateRequest{
|
return &CertificateRequest{
|
||||||
KeyRequest: NewBasicKeyRequest(),
|
KeyRequest: NewKeyRequest(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,7 +186,7 @@ type BasicConstraints struct {
|
||||||
func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
|
func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
|
||||||
log.Info("received CSR")
|
log.Info("received CSR")
|
||||||
if req.KeyRequest == nil {
|
if req.KeyRequest == nil {
|
||||||
req.KeyRequest = NewBasicKeyRequest()
|
req.KeyRequest = NewKeyRequest()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("generating key: %s-%d", req.KeyRequest.Algo(), req.KeyRequest.Size())
|
log.Infof("generating key: %s-%d", req.KeyRequest.Algo(), req.KeyRequest.Size())
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ package local
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
|
@ -11,6 +13,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -29,14 +32,21 @@ import (
|
||||||
"github.com/google/certificate-transparency-go"
|
"github.com/google/certificate-transparency-go"
|
||||||
"github.com/google/certificate-transparency-go/client"
|
"github.com/google/certificate-transparency-go/client"
|
||||||
"github.com/google/certificate-transparency-go/jsonclient"
|
"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"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signer contains a signer that uses the standard library to
|
// Signer contains a signer that uses the standard library to
|
||||||
// support both ECDSA and RSA CA keys.
|
// support both ECDSA and RSA CA keys.
|
||||||
type Signer struct {
|
type Signer struct {
|
||||||
ca *x509.Certificate
|
ca *x509.Certificate
|
||||||
priv crypto.Signer
|
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
|
policy *config.Signing
|
||||||
sigAlgo x509.SignatureAlgorithm
|
sigAlgo x509.SignatureAlgorithm
|
||||||
dbAccessor certdb.Accessor
|
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)
|
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{
|
return &Signer{
|
||||||
ca: cert,
|
ca: cert,
|
||||||
priv: priv,
|
priv: priv,
|
||||||
sigAlgo: sigAlgo,
|
lintPriv: lintPriv,
|
||||||
policy: policy,
|
sigAlgo: sigAlgo,
|
||||||
|
policy: policy,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +126,73 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe
|
||||||
return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
|
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
|
var initRoot bool
|
||||||
if s.ca == nil {
|
if s.ca == nil {
|
||||||
if !template.IsCA {
|
if !template.IsCA {
|
||||||
|
|
@ -111,6 +206,10 @@ func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) {
|
||||||
initRoot = true
|
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)
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err)
|
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 poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}}
|
||||||
var poisonedPreCert = certTBS
|
var poisonedPreCert = certTBS
|
||||||
poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
|
poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
|
||||||
cert, err = s.sign(&poisonedPreCert)
|
cert, err = s.sign(&poisonedPreCert, profile.LintErrLevel, profile.IgnoredLintsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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}
|
var SCTListExtension = pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedSCTList}
|
||||||
certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
|
certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
var signedCert []byte
|
var signedCert []byte
|
||||||
signedCert, err = s.sign(&certTBS)
|
signedCert, err = s.sign(&certTBS, profile.LintErrLevel, profile.IgnoredLintsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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
|
// 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
|
// 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) {
|
func (s *Signer) SignFromPrecert(precert *x509.Certificate, scts []ct.SignedCertificateTimestamp) ([]byte, error) {
|
||||||
// Verify certificate was signed by s.ca
|
// Verify certificate was signed by s.ca
|
||||||
if err := precert.CheckSignatureFrom(s.ca); err != nil {
|
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
|
// Insert the SCT list extension
|
||||||
tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions, sctExt)
|
tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions, sctExt)
|
||||||
|
|
||||||
// Sign the tbsCert
|
// Sign the tbsCert. Linting is always disabled because there is no way for
|
||||||
return s.sign(&tbsCert)
|
// 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.
|
// Info return a populated info.Resp struct or an error.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
github.com/beeker1121/goque
|
github.com/beeker1121/goque
|
||||||
# github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
|
# github.com/beorn7/perks v0.0.0-20160229213445-3ac7bf7a47d1
|
||||||
github.com/beorn7/perks/quantile
|
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/config
|
||||||
github.com/cloudflare/cfssl/errors
|
github.com/cloudflare/cfssl/errors
|
||||||
github.com/cloudflare/cfssl/ocsp
|
github.com/cloudflare/cfssl/ocsp
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue