This audit by Andrew Ayer raises some good points about the complexity of CFSSL leading to a variety of gotchas, footguns, and unexpected or undesired behavior: https://www.agwa.name/blog/post/security_review_of_cfssl_signer_code. In particular, the logic around what SANs are written into the final cert is quite complex.
We use a pretty minimal subset of CFSSL, based on the limited variety of certs that we issue. For example, I don't believe we care about the notBefore and notAfter fields included in the CSR, as we issue 90-day certs. Similarly, I believe we have rules around what SANs we put in the final cert, although I don't personally know exactly what those are (only names ACME has validated?).
We should look into pulling this functionality directly into boulder, with a slimmer, more obviously-correct design.
Very basic sketch of a much smaller API
type IssuanceRequest struct {
PublicKey []byte
SignatureAlgorithm x509.SignatureAlgorithm
Serial []byte
DNSNames []string
// IPAddresses []net.IP
IncludeMustStaple bool
IncludeCTPoison bool
IncludeSCTList []byte
}
type Signer struct {
issuer *x509.Certiifcate
signer crypto.Signer
profile struct {
allowedKeyTypes map[keyType]bool
allowedSignatureAlgorithms map[x509.SignatureAlgorithm]bool
allowDNSNames bool
// allowIPAddresses bool
allowMustStaple bool
allowCTPoison bool
allowSCTList bool
// -----
keyUsage x509.KeyUsage
extKeyUsage []x509.ExtKeyUsage
ocspURL string
crlURL string
issuerURL string
policies []PolicyInformation
validityPeriod time.Duration // could also be set in IssuanceRequest with a min/max bound in profile
// ----- or -----
template *x509.Certificate
// -----
}
}
func (s Signer) Issue(req IssuanceRequest) ([]byte, error)
Most helpful comment
Very basic sketch of a much smaller API