Convert x509util.WithOptions to new modifiers.

This commit is contained in:
Mariano Cano 2020-07-07 19:00:06 -07:00
parent dcb962bdde
commit 9032018cf2

View file

@ -6,13 +6,14 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"encoding/json"
"net" "net"
"net/url" "net/url"
"reflect" "reflect"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/certificates/x509util"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
) )
@ -21,6 +22,7 @@ import (
type Options struct { type Options struct {
NotAfter TimeDuration `json:"notAfter"` NotAfter TimeDuration `json:"notAfter"`
NotBefore TimeDuration `json:"notBefore"` NotBefore TimeDuration `json:"notBefore"`
UserData json.RawMessage `json:"data"`
Backdate time.Duration `json:"-"` Backdate time.Duration `json:"-"`
} }
@ -28,41 +30,30 @@ type Options struct {
// Sign method. // Sign method.
type SignOption interface{} type SignOption interface{}
// CertificateValidator is the interface used to validate a X.509 certificate. // CertificateValidator is an interface used to validate a given X.509 certificate.
type CertificateValidator interface { type CertificateValidator interface {
SignOption Valid(cert *x509.Certificate, opts Options) error
Valid(cert *x509.Certificate, o Options) error
} }
// CertificateRequestValidator is the interface used to validate a X.509 // CertificateRequestValidator is an interface used to validate a given X.509 certificate request.
// certificate request.
type CertificateRequestValidator interface { type CertificateRequestValidator interface {
SignOption Valid(cr *x509.CertificateRequest) error
Valid(req *x509.CertificateRequest) error
} }
// ProfileModifier is the interface used to add custom options to the profile // CertificateModifier is an interface used to modify a given X.509 certificate.
// constructor. The options are used to modify the final certificate. // Types implementing this interface will be validated with a
type ProfileModifier interface { // CertificateValidator.
SignOption type CertificateModifier interface {
Option(o Options) x509util.WithOption Modify(cert *x509.Certificate, opts Options) error
} }
// CertificateEnforcer is the interface used to modify a certificate after // CertificateEnforcer is an interface used to modify a given X.509 certificate.
// validation. // Types implemented this interface will NOT be validated with a
// CertificateValidator.
type CertificateEnforcer interface { type CertificateEnforcer interface {
SignOption
Enforce(cert *x509.Certificate) error Enforce(cert *x509.Certificate) error
} }
// profileWithOption is a wrapper against x509util.WithOption to conform the
// interface.
type profileWithOption x509util.WithOption
func (v profileWithOption) Option(Options) x509util.WithOption {
return x509util.WithOption(v)
}
// emailOnlyIdentity is a CertificateRequestValidator that checks that the only // emailOnlyIdentity is a CertificateRequestValidator that checks that the only
// SAN provided is the given email address. // SAN provided is the given email address.
type emailOnlyIdentity string type emailOnlyIdentity string
@ -254,28 +245,27 @@ func (eee ExtraExtsEnforcer) Enforce(cert *x509.Certificate) error {
return nil return nil
} }
// profileDefaultDuration is a wrapper against x509util.WithOption to conform // profileDefaultDuration is a modifier that sets the certificate
// the SignOption interface. // duration.
type profileDefaultDuration time.Duration type profileDefaultDuration time.Duration
func (v profileDefaultDuration) Option(so Options) x509util.WithOption { func (v profileDefaultDuration) Modify(cert *x509.Certificate, so Options) error {
var backdate time.Duration var backdate time.Duration
notBefore := so.NotBefore.Time() notBefore := so.NotBefore.Time()
if notBefore.IsZero() { if notBefore.IsZero() {
notBefore = now() notBefore = now()
backdate = -1 * so.Backdate backdate = -1 * so.Backdate
} }
notAfter := so.NotAfter.RelativeTime(notBefore) notAfter := so.NotAfter.RelativeTime(notBefore)
return func(p x509util.Profile) error { if notAfter.IsZero() {
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v)) notAfter = notBefore.Add(time.Duration(v))
if err := fn(p); err != nil {
return err
} }
crt := p.Subject()
crt.NotBefore = crt.NotBefore.Add(backdate) cert.NotBefore = notBefore.Add(backdate)
cert.NotAfter = notAfter
return nil return nil
} }
}
// profileLimitDuration is an x509 profile option that modifies an x509 validity // profileLimitDuration is an x509 profile option that modifies an x509 validity
// period according to an imposed expiration time. // period according to an imposed expiration time.
@ -286,13 +276,11 @@ type profileLimitDuration struct {
// Option returns an x509util option that limits the validity period of a // Option returns an x509util option that limits the validity period of a
// certificate to one that is superficially imposed. // certificate to one that is superficially imposed.
func (v profileLimitDuration) Option(so Options) x509util.WithOption { func (v profileLimitDuration) Modify(cert *x509.Certificate, so Options) error {
return func(p x509util.Profile) error {
var backdate time.Duration var backdate time.Duration
n := now()
notBefore := so.NotBefore.Time() notBefore := so.NotBefore.Time()
if notBefore.IsZero() { if notBefore.IsZero() {
notBefore = n notBefore = now()
backdate = -1 * so.Backdate backdate = -1 * so.Backdate
} }
if notBefore.Before(v.notBefore) { if notBefore.Before(v.notBefore) {
@ -315,12 +303,11 @@ func (v profileLimitDuration) Option(so Options) x509util.WithOption {
notAfter = t notAfter = t
} }
} }
crt := p.Subject()
crt.NotBefore = notBefore.Add(backdate) cert.NotBefore = notBefore.Add(backdate)
crt.NotAfter = notAfter cert.NotAfter = notAfter
return nil return nil
} }
}
// validityValidator validates the certificate validity settings. // validityValidator validates the certificate validity settings.
type validityValidator struct { type validityValidator struct {
@ -385,23 +372,22 @@ func newForceCNOption(forceCN bool) *forceCNOption {
return &forceCNOption{forceCN} return &forceCNOption{forceCN}
} }
func (o *forceCNOption) Option(Options) x509util.WithOption { func (o *forceCNOption) Modify(cert *x509.Certificate, _ Options) error {
return func(p x509util.Profile) error {
if !o.ForceCN { if !o.ForceCN {
// Forcing CN is disabled, do nothing to certificate // Forcing CN is disabled, do nothing to certificate
return nil return nil
} }
crt := p.Subject()
if crt.Subject.CommonName == "" { if cert.Subject.CommonName == "" {
if len(crt.DNSNames) > 0 { if len(cert.DNSNames) > 0 {
crt.Subject.CommonName = crt.DNSNames[0] cert.Subject.CommonName = cert.DNSNames[0]
} else { } else {
return errors.New("Cannot force CN, DNSNames is empty") return errors.New("Cannot force CN, DNSNames is empty")
} }
} }
return nil return nil
} }
}
type provisionerExtensionOption struct { type provisionerExtensionOption struct {
Type int Type int
@ -419,9 +405,7 @@ func newProvisionerExtensionOption(typ Type, name, credentialID string, keyValue
} }
} }
func (o *provisionerExtensionOption) Option(Options) x509util.WithOption { func (o *provisionerExtensionOption) Modify(cert *x509.Certificate, _ Options) error {
return func(p x509util.Profile) error {
crt := p.Subject()
ext, err := createProvisionerExtension(o.Type, o.Name, o.CredentialID, o.KeyValuePairs...) ext, err := createProvisionerExtension(o.Type, o.Name, o.CredentialID, o.KeyValuePairs...)
if err != nil { if err != nil {
return err return err
@ -433,10 +417,9 @@ func (o *provisionerExtensionOption) Option(Options) x509util.WithOption {
// ExtraExtension. If we were to append (rather than prepend) the correct // ExtraExtension. If we were to append (rather than prepend) the correct
// stepOIDProvisioner extension, then the resulting certificate would // stepOIDProvisioner extension, then the resulting certificate would
// contain the malicious extension, rather than the one applied by step-ca. // contain the malicious extension, rather than the one applied by step-ca.
crt.ExtraExtensions = append([]pkix.Extension{ext}, crt.ExtraExtensions...) cert.ExtraExtensions = append([]pkix.Extension{ext}, cert.ExtraExtensions...)
return nil return nil
} }
}
func createProvisionerExtension(typ int, name, credentialID string, keyValuePairs ...string) (pkix.Extension, error) { func createProvisionerExtension(typ int, name, credentialID string, keyValuePairs ...string) (pkix.Extension, error) {
b, err := asn1.Marshal(stepProvisionerASN1{ b, err := asn1.Marshal(stepProvisionerASN1{
@ -454,8 +437,3 @@ func createProvisionerExtension(typ int, name, credentialID string, keyValuePair
Value: b, Value: b,
}, nil }, nil
} }
func init() {
// Avoid dead-code warning in profileWithOption
_ = profileWithOption(nil)
}