From 0377fe559b2bd9be96d6c02efc8d95226c8299a4 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 26 May 2023 23:52:24 +0200 Subject: [PATCH 01/39] Add basic version of provisioner specific SCEP decrypter --- api/api.go | 22 +++++++-- authority/authority.go | 22 ++++++++- authority/provisioner/scep.go | 47 +++++++++++++++++++ authority/provisioner/webhook.go | 3 ++ scep/api/api.go | 3 ++ scep/authority.go | 79 +++++++++++++++++++++----------- scep/options.go | 46 +++++++++++-------- scep/provisioner.go | 3 ++ scep/service.go | 16 ++++--- 9 files changed, 184 insertions(+), 57 deletions(-) diff --git a/api/api.go b/api/api.go index c9820351..0474471a 100644 --- a/api/api.go +++ b/api/api.go @@ -244,11 +244,25 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { continue } - old := scepProv.ChallengePassword + type old struct { + challengePassword string + decrypterCertificate string + decrypterKey string + decrypterKeyPassword string + } + o := old{scepProv.ChallengePassword, scepProv.DecrypterCert, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword} scepProv.ChallengePassword = "*** REDACTED ***" - defer func(p string) { //nolint:gocritic // defer in loop required to restore initial state of provisioners - scepProv.ChallengePassword = p - }(old) + // TODO: remove the details in the API response + // scepProv.DecrypterCert = "" + // scepProv.DecrypterKey = "" + // scepProv.DecrtyperKeyPassword = "" + + defer func(o old) { //nolint:gocritic // defer in loop required to restore initial state of provisioners + scepProv.ChallengePassword = o.challengePassword + scepProv.DecrypterCert = o.decrypterCertificate + scepProv.DecrypterKey = o.decrypterKey + scepProv.DecrypterKeyPassword = o.decrypterKeyPassword + }(o) } var list = struct { diff --git a/authority/authority.go b/authority/authority.go index ae85c018..ef51b61d 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto" + "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/hex" @@ -666,13 +667,30 @@ func (a *Authority) init() error { return err } + options.SignerCert = options.CertificateChain[0] + options.DecrypterCert = options.CertificateChain[0] + + // TODO: instead of creating the decrypter here, pass the + // intermediate key + chain down to the SCEP service / authority, + // and only instantiate it when required there. + // TODO: if moving the logic, try improving the logic for the + // decrypter password too? if km, ok := a.keyManager.(kmsapi.Decrypter); ok { options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ DecryptionKey: a.config.IntermediateKey, Password: a.password, }) - if err != nil { - return err + if err == nil { + // when creating the decrypter fails, ignore the error + // TODO(hs): decide if this is OK. It could fail at startup, but it + // could be up later. Right now decryption would always fail. + key, ok := options.Decrypter.Public().(*rsa.PublicKey) + if !ok { + return errors.New("only RSA keys are currently supported as decrypters") + } + if !key.Equal(options.DecrypterCert.PublicKey) { + return errors.New("mismatch between decryption certificate and decrypter public keys") + } } } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index b0acc8fe..77c02e8f 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -2,13 +2,19 @@ package provisioner import ( "context" + "crypto" + "crypto/rsa" "crypto/subtle" + "crypto/x509" "fmt" "net/http" "time" "github.com/pkg/errors" + "go.step.sm/crypto/kms" + kmsapi "go.step.sm/crypto/kms/apiv1" + "go.step.sm/crypto/pemutil" "go.step.sm/linkedca" "github.com/smallstep/certificates/webhook" @@ -32,6 +38,12 @@ type SCEP struct { // MinimumPublicKeyLength is the minimum length for public keys in CSRs MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` + // TODO + KMS *kms.Options `json:"kms,omitempty"` + DecrypterCert string `json:"decrypterCert"` + DecrypterKey string `json:"decrypterKey"` + DecrypterKeyPassword string `json:"decrypterKeyPassword"` + // Numerical identifier for the ContentEncryptionAlgorithm as defined in github.com/mozilla-services/pkcs7 // at https://github.com/mozilla-services/pkcs7/blob/33d05740a3526e382af6395d3513e73d4e66d1cb/encrypt.go#L63 // Defaults to 0, being DES-CBC @@ -41,6 +53,9 @@ type SCEP struct { ctl *Controller encryptionAlgorithm int challengeValidationController *challengeValidationController + keyManager kmsapi.KeyManager + decrypter crypto.Decrypter + decrypterCertificate *x509.Certificate } // GetID returns the provisioner unique identifier. @@ -177,6 +192,34 @@ func (s *SCEP) Init(config Config) (err error) { s.GetOptions().GetWebhooks(), ) + if s.KMS != nil { + if s.keyManager, err = kms.New(context.Background(), *s.KMS); err != nil { + return fmt.Errorf("failed initializing kms: %w", err) + } + km, ok := s.keyManager.(kmsapi.Decrypter) + if !ok { + return fmt.Errorf(`%q is not a kmsapi.Decrypter`, s.KMS.Type) + } + if s.DecrypterKey != "" || s.DecrypterCert != "" { + if s.decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: s.DecrypterKey, + Password: []byte(s.DecrypterKeyPassword), + }); err != nil { + return fmt.Errorf("failed creating decrypter: %w", err) + } + if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCert); err != nil { + return fmt.Errorf("failed reading certificate: %w", err) + } + decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) + if !ok { + return fmt.Errorf("only RSA keys are supported") + } + if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { + return errors.New("mismatch between decryption certificate and decrypter public keys") + } + } + } + // TODO: add other, SCEP specific, options? s.ctl, err = NewController(s, s.Claims, config, s.Options) @@ -259,3 +302,7 @@ func (s *SCEP) selectValidationMethod() validationMethod { } return validationMethodNone } + +func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) { + return s.decrypterCertificate, s.decrypter +} diff --git a/authority/provisioner/webhook.go b/authority/provisioner/webhook.go index cb15547d..3266e131 100644 --- a/authority/provisioner/webhook.go +++ b/authority/provisioner/webhook.go @@ -152,6 +152,8 @@ retry: return nil, err } + fmt.Println(req) + secret, err := base64.StdEncoding.DecodeString(w.Secret) if err != nil { return nil, err @@ -201,6 +203,7 @@ retry: time.Sleep(time.Second) goto retry } + fmt.Println(fmt.Sprintf("%#+v", resp)) if resp.StatusCode >= 400 { return nil, fmt.Errorf("Webhook server responded with %d", resp.StatusCode) } diff --git a/scep/api/api.go b/scep/api/api.go index 98da818b..00f693a8 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -308,6 +308,8 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { transactionID := string(msg.TransactionID) challengePassword := msg.CSRReqMessage.ChallengePassword + fmt.Println("challenge password: ", challengePassword) + // NOTE: we're blocking the RenewalReq if the challenge does not match, because otherwise we don't have any authentication. // The macOS SCEP client performs renewals using PKCSreq. The CertNanny SCEP client will use PKCSreq with challenge too, it seems, // even if using the renewal flow as described in the README.md. MicroMDM SCEP client also only does PKCSreq by default, unless @@ -315,6 +317,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // We'll have to see how it works out. if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { if err := auth.ValidateChallenge(ctx, challengePassword, transactionID); err != nil { + fmt.Println(err) if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) { return createFailureResponse(ctx, csr, msg, microscep.BadRequest, err) } diff --git a/scep/authority.go b/scep/authority.go index 23c28813..af9bdf42 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -2,6 +2,7 @@ package scep import ( "context" + "crypto" "crypto/x509" "errors" "fmt" @@ -18,12 +19,10 @@ import ( // Authority is the layer that handles all SCEP interactions. type Authority struct { - prefix string - dns string - intermediateCertificate *x509.Certificate - caCerts []*x509.Certificate // TODO(hs): change to use these instead of root and intermediate - service *Service - signAuth SignAuthority + prefix string + dns string + service *Service + signAuth SignAuthority } type authorityKey struct{} @@ -74,18 +73,8 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { prefix: ops.Prefix, dns: ops.DNS, signAuth: signAuth, + service: ops.Service, } - - // TODO: this is not really nice to do; the Service should be removed - // in its entirety to make this more interoperable with the rest of - // step-ca, I think. - if ops.Service != nil { - authority.caCerts = ops.Service.certificateChain - // TODO(hs): look into refactoring SCEP into using just caCerts everywhere, if it makes sense for more elaborate SCEP configuration. Keeping it like this for clarity (for now). - authority.intermediateCertificate = ops.Service.certificateChain[0] - authority.service = ops.Service - } - return authority, nil } @@ -165,30 +154,46 @@ func (a *Authority) GetCACertificates(ctx context.Context) ([]*x509.Certificate, return nil, err } - if len(a.caCerts) == 0 { + if len(a.service.certificateChain) == 0 { return nil, errors.New("no intermediate certificate available in SCEP authority") } certs := []*x509.Certificate{} - certs = append(certs, a.caCerts[0]) + if decrypterCertificate, _ := p.GetDecrypter(); decrypterCertificate != nil { + certs = append(certs, decrypterCertificate) + certs = append(certs, a.service.signerCertificate) + } else { + certs = append(certs, a.service.defaultDecrypterCertificate) + } // NOTE: we're adding the CA roots here, but they are (highly likely) different than what the RFC means. // Clients are responsible to select the right cert(s) to use, though. - if p.ShouldIncludeRootInChain() && len(a.caCerts) > 1 { - certs = append(certs, a.caCerts[1]) + if p.ShouldIncludeRootInChain() && len(a.service.certificateChain) > 1 { + certs = append(certs, a.service.certificateChain[1]) } return certs, nil } // DecryptPKIEnvelope decrypts an enveloped message -func (a *Authority) DecryptPKIEnvelope(_ context.Context, msg *PKIMessage) error { +func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error { p7c, err := pkcs7.Parse(msg.P7.Content) if err != nil { return fmt.Errorf("error parsing pkcs7 content: %w", err) } - envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.decrypter) + fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypterCertificate)) + fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypter)) + + cert, pkey, err := a.selectDecrypter(ctx) + if err != nil { + return fmt.Errorf("failed selecting decrypter: %w", err) + } + + fmt.Println(fmt.Sprintf("%#+v", cert)) + fmt.Println(fmt.Sprintf("%#+v", pkey)) + + envelope, err := p7c.Decrypt(cert, pkey) if err != nil { return fmt.Errorf("error decrypting encrypted pkcs7 content: %w", err) } @@ -208,6 +213,9 @@ func (a *Authority) DecryptPKIEnvelope(_ context.Context, msg *PKIMessage) error if err != nil { return fmt.Errorf("parse CSR from pkiEnvelope: %w", err) } + if err := csr.CheckSignature(); err != nil { + return fmt.Errorf("invalid CSR signature; %w", err) + } // check for challengePassword cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) if err != nil { @@ -226,6 +234,24 @@ func (a *Authority) DecryptPKIEnvelope(_ context.Context, msg *PKIMessage) error return nil } +func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { + p, err := provisionerFromContext(ctx) + if err != nil { + return nil, nil, err + } + + // return provisioner specific decrypter, if available + if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { + return + } + + // fallback to the CA wide decrypter + cert = a.service.defaultDecrypterCertificate + pkey = a.service.defaultDecrypter + + return +} + // SignCSR creates an x509.Certificate based on a CSR template and Cert Authority credentials // returns a new PKIMessage with CertRep data func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) { @@ -358,10 +384,11 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // as the first certificate in the array signedData.AddCertificate(cert) - authCert := a.intermediateCertificate + authCert := a.service.signerCertificate + signer := a.service.signer // sign the attributes - if err := signedData.AddSigner(authCert, a.service.signer, config); err != nil { + if err := signedData.AddSigner(authCert, signer, config); err != nil { return nil, err } @@ -429,7 +456,7 @@ func (a *Authority) CreateFailureResponse(_ context.Context, _ *x509.Certificate } // sign the attributes - if err := signedData.AddSigner(a.intermediateCertificate, a.service.signer, config); err != nil { + if err := signedData.AddSigner(a.service.signerCertificate, a.service.signer, config); err != nil { return nil, err } diff --git a/scep/options.go b/scep/options.go index 201f1beb..00662ae9 100644 --- a/scep/options.go +++ b/scep/options.go @@ -2,7 +2,6 @@ package scep import ( "crypto" - "crypto/rsa" "crypto/x509" "github.com/pkg/errors" @@ -12,6 +11,8 @@ type Options struct { // CertificateChain is the issuer certificate, along with any other bundled certificates // to be returned in the chain for consumers. Configured in the ca.json crt property. CertificateChain []*x509.Certificate + SignerCert *x509.Certificate + DecrypterCert *x509.Certificate // Signer signs CSRs in SCEP. Configured in the ca.json key property. Signer crypto.Signer `json:"-"` // Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property. @@ -35,36 +36,43 @@ func (o *Options) Validate() error { // Other algorithms than RSA do not seem to be supported in certnanny/sscep, but it might work // in micromdm/scep. Currently only RSA is allowed, but it might be an option // to try other algorithms in the future. - intermediate := o.CertificateChain[0] - if intermediate.PublicKeyAlgorithm != x509.RSA { - return errors.New("only the RSA algorithm is (currently) supported") - } + //intermediate := o.CertificateChain[0] + //intermediate := o.SignerCert + // if intermediate.PublicKeyAlgorithm != x509.RSA { + // return errors.New("only the RSA algorithm is (currently) supported") + // } // TODO: add checks for key usage? - signerPublicKey, ok := o.Signer.Public().(*rsa.PublicKey) - if !ok { - return errors.New("only RSA public keys are (currently) supported as signers") - } + //signerPublicKey, ok := o.Signer.Public().(*rsa.PublicKey) + // if !ok { + // return errors.New("only RSA public keys are (currently) supported as signers") + // } // check if the intermediate ca certificate has the same public key as the signer. // According to the RFC it seems valid to have different keys for the intermediate // and the CA signing new certificates, so this might change in the future. - if !signerPublicKey.Equal(intermediate.PublicKey) { - return errors.New("mismatch between certificate chain and signer public keys") - } + // if !signerPublicKey.Equal(intermediate.PublicKey) { + // return errors.New("mismatch between certificate chain and signer public keys") + // } - decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) - if !ok { - return errors.New("only RSA public keys are (currently) supported as decrypters") - } + // TODO: this could be a different decrypter, based on the value + // in the provisioner. + // decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) + // if !ok { + // return errors.New("only RSA public keys are (currently) supported as decrypters") + // } // check if intermediate public key is the same as the decrypter public key. // In certnanny/sscep it's mentioned that the signing key can be different // from the decrypting (and encrypting) key. Currently that's not supported. - if !decrypterPublicKey.Equal(intermediate.PublicKey) { - return errors.New("mismatch between certificate chain and decrypter public keys") - } + // if !decrypterPublicKey.Equal(intermediate.PublicKey) { + // return errors.New("mismatch between certificate chain and decrypter public keys") + // } + + // if !decrypterPublicKey.Equal(o.DecrypterCert.PublicKey) { + // return errors.New("mismatch between certificate chain and decrypter public keys") + // } return nil } diff --git a/scep/provisioner.go b/scep/provisioner.go index 8120057e..cb41ed47 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -2,6 +2,8 @@ package scep import ( "context" + "crypto" + "crypto/x509" "time" "github.com/smallstep/certificates/authority/provisioner" @@ -16,6 +18,7 @@ type Provisioner interface { GetOptions() *provisioner.Options GetCapabilities() []string ShouldIncludeRootInChain() bool + GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetContentEncryptionAlgorithm() int ValidateChallenge(ctx context.Context, challenge, transactionID string) error } diff --git a/scep/service.go b/scep/service.go index 85f7c73f..ffb4166a 100644 --- a/scep/service.go +++ b/scep/service.go @@ -8,9 +8,11 @@ import ( // Service is a wrapper for crypto.Signer and crypto.Decrypter type Service struct { - certificateChain []*x509.Certificate - signer crypto.Signer - decrypter crypto.Decrypter + certificateChain []*x509.Certificate + signerCertificate *x509.Certificate + signer crypto.Signer + defaultDecrypterCertificate *x509.Certificate + defaultDecrypter crypto.Decrypter } // NewService returns a new Service type. @@ -21,8 +23,10 @@ func NewService(_ context.Context, opts Options) (*Service, error) { // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? return &Service{ - certificateChain: opts.CertificateChain, - signer: opts.Signer, - decrypter: opts.Decrypter, + certificateChain: opts.CertificateChain, + signerCertificate: opts.SignerCert, + signer: opts.Signer, + defaultDecrypterCertificate: opts.DecrypterCert, + defaultDecrypter: opts.Decrypter, }, nil } From 180162bd6a82b0ad1b34daa4d1f5f39cca37d591 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 1 Jun 2023 12:10:54 +0200 Subject: [PATCH 02/39] Refactor SCEP provisioner and decrypter --- api/api.go | 9 ++- authority/authority.go | 81 +++++++-------------- authority/authority_test.go | 4 +- authority/provisioner/scep.go | 26 +++++-- authority/provisioners.go | 20 ++++- ca/ca.go | 27 ++++--- go.mod | 20 ++--- go.sum | 39 +++++----- scep/authority.go | 133 +++++++++++++--------------------- scep/options.go | 99 +++++++++++++------------ scep/service.go | 26 +++---- 11 files changed, 234 insertions(+), 250 deletions(-) diff --git a/api/api.go b/api/api.go index 0474471a..849fd24f 100644 --- a/api/api.go +++ b/api/api.go @@ -244,13 +244,16 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { continue } + fmt.Println(scepProv.KMS) + fmt.Println(fmt.Sprintf("%#+v", scepProv)) + type old struct { challengePassword string - decrypterCertificate string + decrypterCertificate []byte decrypterKey string decrypterKeyPassword string } - o := old{scepProv.ChallengePassword, scepProv.DecrypterCert, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword} + o := old{scepProv.ChallengePassword, scepProv.DecrypterCertificate, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword} scepProv.ChallengePassword = "*** REDACTED ***" // TODO: remove the details in the API response // scepProv.DecrypterCert = "" @@ -259,7 +262,7 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { defer func(o old) { //nolint:gocritic // defer in loop required to restore initial state of provisioners scepProv.ChallengePassword = o.challengePassword - scepProv.DecrypterCert = o.decrypterCertificate + scepProv.DecrypterCertificate = o.decrypterCertificate scepProv.DecrypterKey = o.decrypterKey scepProv.DecrypterKeyPassword = o.decrypterKeyPassword }(o) diff --git a/authority/authority.go b/authority/authority.go index ef51b61d..8b93a634 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -641,56 +641,45 @@ func (a *Authority) init() error { return err } - // Check if a KMS with decryption capability is required and available - if a.requiresDecrypter() { - if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { - return errors.New("keymanager doesn't provide crypto.Decrypter") - } - } - - // TODO: decide if this is a good approach for providing the SCEP functionality - // It currently mirrors the logic for the x509CAService - if a.requiresSCEPService() && a.scepService == nil { + // The SCEP functionality is provided through an instance of + // scep.Service. It is initialized once when the CA is started. + // TODO: should the SCEP service support reloading? For example, + // when the admin resources are reloaded, specifically the provisioners, + // it can happen that the SCEP service is no longer required and can + // be destroyed, or that it needs to be instantiated. It may also need + // to be revalidated, because not all SCEP provisioner may have a + // valid decrypter available. + if a.requiresSCEP() && a.GetSCEP() == nil { var options scep.Options - - // Read intermediate and create X509 signer and decrypter for default CAS. - options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) + options.Roots = a.rootX509Certs + options.Intermediates, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) if err != nil { return err } - options.CertificateChain = append(options.CertificateChain, a.rootX509Certs...) - options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + options.SignerCert = options.Intermediates[0] + if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.IntermediateKey, Password: a.password, - }) - if err != nil { + }); err != nil { return err } - options.SignerCert = options.CertificateChain[0] - options.DecrypterCert = options.CertificateChain[0] - // TODO: instead of creating the decrypter here, pass the // intermediate key + chain down to the SCEP service / authority, - // and only instantiate it when required there. + // and only instantiate it when required there. Is that possible? + // Also with entering passwords? // TODO: if moving the logic, try improving the logic for the - // decrypter password too? - if km, ok := a.keyManager.(kmsapi.Decrypter); ok { - options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + // decrypter password too? Right now it needs to be entered multiple + // times; I've observed it to be three times maximum, every time + // the intermediate key is read. + _, isRSA := options.Signer.Public().(*rsa.PublicKey) + if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA { + if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ DecryptionKey: a.config.IntermediateKey, Password: a.password, - }) - if err == nil { - // when creating the decrypter fails, ignore the error - // TODO(hs): decide if this is OK. It could fail at startup, but it - // could be up later. Right now decryption would always fail. - key, ok := options.Decrypter.Public().(*rsa.PublicKey) - if !ok { - return errors.New("only RSA keys are currently supported as decrypters") - } - if !key.Equal(options.DecrypterCert.PublicKey) { - return errors.New("mismatch between decryption certificate and decrypter public keys") - } + }); err == nil { + // only pass the decrypter down when it was successfully created + options.Decrypter = decrypter } } @@ -698,8 +687,6 @@ func (a *Authority) init() error { if err != nil { return err } - - // TODO: mimick the x509CAService GetCertificateAuthority here too? } // Load X509 constraints engine. @@ -851,17 +838,9 @@ func (a *Authority) IsRevoked(sn string) (bool, error) { return a.db.IsRevoked(sn) } -// requiresDecrypter returns whether the Authority -// requires a KMS that provides a crypto.Decrypter -// Currently this is only required when SCEP is -// enabled. -func (a *Authority) requiresDecrypter() bool { - return a.requiresSCEPService() -} - // requiresSCEPService iterates over the configured provisioners // and determines if one of them is a SCEP provisioner. -func (a *Authority) requiresSCEPService() bool { +func (a *Authority) requiresSCEP() bool { for _, p := range a.config.AuthorityConfig.Provisioners { if p.GetType() == provisioner.TypeSCEP { return true @@ -870,12 +849,8 @@ func (a *Authority) requiresSCEPService() bool { return false } -// GetSCEPService returns the configured SCEP Service. -// -// TODO: this function is intended to exist temporarily in order to make SCEP -// work more easily. It can be made more correct by using the right -// interfaces/abstractions after it works as expected. -func (a *Authority) GetSCEPService() *scep.Service { +// GetSCEP returns the configured SCEP Service. +func (a *Authority) GetSCEP() *scep.Service { return a.scepService } diff --git a/authority/authority_test.go b/authority/authority_test.go index 82a05a3e..45c7cd86 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -478,7 +478,7 @@ func testScepAuthority(t *testing.T, opts ...Option) *Authority { return a } -func TestAuthority_GetSCEPService(t *testing.T) { +func TestAuthority_GetSCEP(t *testing.T) { _ = testScepAuthority(t) p := provisioner.List{ &provisioner.SCEP{ @@ -542,7 +542,7 @@ func TestAuthority_GetSCEPService(t *testing.T) { return } if tt.wantService { - if got := a.GetSCEPService(); (got != nil) != tt.wantService { + if got := a.GetSCEP(); (got != nil) != tt.wantService { t.Errorf("Authority.GetSCEPService() = %v, wantService %v", got, tt.wantService) } } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 77c02e8f..192c75b1 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -6,6 +6,7 @@ import ( "crypto/rsa" "crypto/subtle" "crypto/x509" + "encoding/pem" "fmt" "net/http" "time" @@ -14,7 +15,6 @@ import ( "go.step.sm/crypto/kms" kmsapi "go.step.sm/crypto/kms/apiv1" - "go.step.sm/crypto/pemutil" "go.step.sm/linkedca" "github.com/smallstep/certificates/webhook" @@ -40,7 +40,7 @@ type SCEP struct { // TODO KMS *kms.Options `json:"kms,omitempty"` - DecrypterCert string `json:"decrypterCert"` + DecrypterCertificate []byte `json:"decrypterCertificate"` DecrypterKey string `json:"decrypterKey"` DecrypterKeyPassword string `json:"decrypterKeyPassword"` @@ -198,18 +198,32 @@ func (s *SCEP) Init(config Config) (err error) { } km, ok := s.keyManager.(kmsapi.Decrypter) if !ok { - return fmt.Errorf(`%q is not a kmsapi.Decrypter`, s.KMS.Type) + return fmt.Errorf("%q is not a kmsapi.Decrypter", s.KMS.Type) } - if s.DecrypterKey != "" || s.DecrypterCert != "" { + if s.DecrypterKey != "" || len(s.DecrypterCertificate) > 0 { if s.decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ DecryptionKey: s.DecrypterKey, Password: []byte(s.DecrypterKeyPassword), }); err != nil { return fmt.Errorf("failed creating decrypter: %w", err) } - if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCert); err != nil { - return fmt.Errorf("failed reading certificate: %w", err) + + // Parse decrypter certificate + block, rest := pem.Decode(s.DecrypterCertificate) + if len(rest) > 0 { + fmt.Println(string(rest)) + return errors.New("failed parsing decrypter certificate: trailing data") + } + if block == nil { + return errors.New("failed parsing decrypter certificate: no PEM block found") } + if s.decrypterCertificate, err = x509.ParseCertificate(block.Bytes); err != nil { + return fmt.Errorf("failed parsing decrypter certificate: %w", err) + } + + // if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCertFile); err != nil { + // return fmt.Errorf("failed reading certificate: %w", err) + // } decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) if !ok { return fmt.Errorf("only RSA keys are supported") diff --git a/authority/provisioners.go b/authority/provisioners.go index 5d594536..35030933 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -8,6 +8,7 @@ import ( "encoding/pem" "fmt" "os" + "strings" "github.com/pkg/errors" "gopkg.in/square/go-jose.v2/jwt" @@ -15,6 +16,7 @@ import ( "go.step.sm/cli-utils/step" "go.step.sm/cli-utils/ui" "go.step.sm/crypto/jose" + "go.step.sm/crypto/kms" "go.step.sm/linkedca" "github.com/smallstep/certificates/authority/admin" @@ -235,7 +237,7 @@ func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisi } if err := certProv.Init(provisionerConfig); err != nil { - return admin.WrapError(admin.ErrorBadRequestType, err, "error validating configuration for provisioner %s", prov.Name) + return admin.WrapError(admin.ErrorBadRequestType, err, "error validating configuration for provisioner %q", prov.Name) } // Store to database -- this will set the ID. @@ -960,7 +962,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, }, nil case *linkedca.ProvisionerDetails_SCEP: cfg := d.SCEP - return &provisioner.SCEP{ + s := &provisioner.SCEP{ ID: p.Id, Type: p.Type.String(), Name: p.Name, @@ -972,7 +974,19 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, EncryptionAlgorithmIdentifier: int(cfg.EncryptionAlgorithmIdentifier), Claims: claims, Options: options, - }, nil + } + if decrypter := cfg.GetDecrypter(); decrypter != nil { + if dkms := decrypter.GetKms(); dkms != nil { + s.KMS = &kms.Options{ + Type: kms.Type(strings.ToLower(linkedca.KMS_Type_name[int32(dkms.Type)])), + CredentialsFile: dkms.CredentialsFile, + } + } + s.DecrypterCertificate = decrypter.DecrypterCertificate + s.DecrypterKey = decrypter.DecrypterKey + s.DecrypterKeyPassword = decrypter.DecrypterKeyPassword + } + return s, nil case *linkedca.ProvisionerDetails_Nebula: var roots []byte for i, root := range d.Nebula.GetRoots() { diff --git a/ca/ca.go b/ca/ca.go index b8f65332..a3f261e7 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -250,19 +250,24 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { var scepAuthority *scep.Authority if ca.shouldServeSCEPEndpoints() { - scepPrefix := "scep" - scepAuthority, err = scep.New(auth, scep.AuthorityOptions{ - Service: auth.GetSCEPService(), - DNS: dns, - Prefix: scepPrefix, - }) - if err != nil { - return nil, errors.Wrap(err, "error creating SCEP authority") + if scepAuthority, err = scep.New(auth, scep.AuthorityOptions{ + Service: auth.GetSCEP(), + }); err != nil { + return nil, errors.Wrap(err, "failed creating SCEP authority") + } + + // TODO: validate that the scepAuthority is fully valid? E.g. initialization + // may have configured the default decrypter, but if that's not set or if it's + // somehow not usable, all SCEP provisioners should have a valid decrypter + // configured by now. + if err := scepAuthority.Validate(); err != nil { + return nil, errors.Wrap(err, "failed validating SCEP authority") } // According to the RFC (https://tools.ietf.org/html/rfc8894#section-7.10), // SCEP operations are performed using HTTP, so that's why the API is mounted // to the insecure mux. + scepPrefix := "scep" insecureMux.Route("/"+scepPrefix, func(r chi.Router) { scepAPI.Route(r) }) @@ -584,10 +589,10 @@ func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, *tls.Config, // shouldServeSCEPEndpoints returns if the CA should be // configured with endpoints for SCEP. This is assumed to be -// true if a SCEPService exists, which is true in case a -// SCEP provisioner was configured. +// true if a SCEPService exists, which is true in case at +// least one SCEP provisioner was configured. func (ca *CA) shouldServeSCEPEndpoints() bool { - return ca.auth.GetSCEPService() != nil + return ca.auth.GetSCEP() != nil } //nolint:unused // useful for debugging diff --git a/go.mod b/go.mod index 220ae3f3..b35d3d61 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/google/go-tpm v0.3.3 github.com/google/uuid v1.3.0 - github.com/googleapis/gax-go/v2 v2.8.0 + github.com/googleapis/gax-go/v2 v2.9.0 github.com/hashicorp/vault/api v1.9.1 github.com/hashicorp/vault/api/auth/approle v0.4.0 github.com/hashicorp/vault/api/auth/kubernetes v0.4.0 @@ -25,16 +25,16 @@ require ( github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738 github.com/smallstep/nosql v0.6.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 github.com/urfave/cli v1.22.13 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.6 - go.step.sm/crypto v0.30.0 + go.step.sm/crypto v0.31.0 go.step.sm/linkedca v0.19.1 - golang.org/x/crypto v0.8.0 + golang.org/x/crypto v0.9.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 - golang.org/x/net v0.9.0 - google.golang.org/api v0.121.0 + golang.org/x/net v0.10.0 + google.golang.org/api v0.123.0 google.golang.org/grpc v1.55.0 google.golang.org/protobuf v1.30.0 gopkg.in/square/go-jose.v2 v2.6.0 @@ -57,7 +57,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect - github.com/aws/aws-sdk-go v1.44.259 // indirect + github.com/aws/aws-sdk-go v1.44.267 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -80,7 +80,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/certificate-transparency-go v1.1.4 // indirect - github.com/google/go-tpm-tools v0.3.11 // indirect + github.com/google/go-tpm-tools v0.3.12 // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/s2a-go v0.1.3 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect @@ -129,7 +129,7 @@ require ( go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -145,3 +145,5 @@ require ( // use github.com/smallstep/pkcs7 fork with patches applied replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948 + +replace go.step.sm/linkedca => ./../linkedca diff --git a/go.sum b/go.sum index 2065094e..e19265ad 100644 --- a/go.sum +++ b/go.sum @@ -165,8 +165,8 @@ github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.259 h1:7yDn1dcv4DZFMKpu+2exIH5O6ipNj9qXrKfdMUaIJwY= -github.com/aws/aws-sdk-go v1.44.259/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.267 h1:Asrp6EMqqRxZvjK0NjzkWcrOk15RnWtupuUrUuZMabk= +github.com/aws/aws-sdk-go v1.44.267/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= @@ -430,7 +430,7 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= -github.com/google/go-sev-guest v0.5.2 h1:dlCehnxU9aJWEIcTb0j7oZ/yM4qeno7AO6zWokb4mu0= +github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0= github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= github.com/google/go-tpm v0.3.2/go.mod h1:j71sMBTfp3X5jPHz852ZOfQMUOf65Gb/Th8pRmp7fvg= @@ -441,8 +441,8 @@ github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N github.com/google/go-tpm-tools v0.2.1/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= github.com/google/go-tpm-tools v0.3.1/go.mod h1:PSg+r5hSZI5tP3X7LBQx2sW1VSZUqZHBSrKyDqrB21U= github.com/google/go-tpm-tools v0.3.9/go.mod h1:22JvWmHcD5w55cs+nMeqDGDxgNS15/2pDq2cLqnc3rc= -github.com/google/go-tpm-tools v0.3.11 h1:imObhmECgDS+ua4aAVPkMfCzE9LTZjS/MmVMCrAG4VY= -github.com/google/go-tpm-tools v0.3.11/go.mod h1:5UcOsOyG5B2hWhKsqNI3TtOjTcZs5sh+3913uMN29Y8= +github.com/google/go-tpm-tools v0.3.12 h1:hpWglH4RaZnGVbgOK3IThI5K++jnFvjQ94EIN34xrUU= +github.com/google/go-tpm-tools v0.3.12/go.mod h1:2OtmyPGPuaWWIOjr+IDhNQb6t5njjbSmZtzc350Q6Ro= github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= @@ -491,8 +491,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.9.0 h1:ie5/2yPjucjZW6fEGjLhS5+PhEg6owWMrFB5d7TFFhw= +github.com/googleapis/gax-go/v2 v2.9.0/go.mod h1:qf/E3rjAvrwLsAnQW+IClIu+z03yUf4KOoO82NfZ+QY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= @@ -968,8 +968,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= @@ -1062,8 +1063,8 @@ go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16g go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/cli-utils v0.7.6 h1:YkpLVrepmy2c5+eaz/wduiGxlgrRx3YdAStE37if25g= go.step.sm/cli-utils v0.7.6/go.mod h1:j+FxFZ2gbWkAJl0eded/rksuxmNqWpmyxbkXcukGJaY= -go.step.sm/crypto v0.30.0 h1:EzqPTvW1g6kxEnfIf/exDW+MhHGeEhtoNMhQX7P/UwI= -go.step.sm/crypto v0.30.0/go.mod h1:6jFFgUoafyHvb6rNq3NJrBByof4SCzj1n8ThyXuMVAM= +go.step.sm/crypto v0.31.0 h1:8ZG/BxC+0+LzPpk/764h5yubpG3GfxcRVR4E+Aye72g= +go.step.sm/crypto v0.31.0/go.mod h1:Dv4lpkijKiZVkoc6zp+Xaw1xmy+voia1mykvbpQIvuc= go.step.sm/linkedca v0.19.1 h1:uY0ByT/uB3FCQ8zIo9mU7MWG7HKf5sDXNEBeN94MuP8= go.step.sm/linkedca v0.19.1/go.mod h1:vPV2ad3LFQJmV7XWt87VlnJSs6UOqgsbVGVWe3veEmI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1111,8 +1112,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1211,8 +1212,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1340,8 +1341,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1349,7 +1350,7 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1490,8 +1491,8 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.121.0 h1:8Oopoo8Vavxx6gt+sgs8s8/X60WBAtKQq6JqnkF+xow= -google.golang.org/api v0.121.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.123.0 h1:yHVU//vA+qkOhm4reEC9LtzHVUCN/IqqNRl1iQ9xE20= +google.golang.org/api v0.123.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/scep/authority.go b/scep/authority.go index af9bdf42..14e5cd1c 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -6,7 +6,6 @@ import ( "crypto/x509" "errors" "fmt" - "net/url" microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util" microscep "github.com/micromdm/scep/v2/scep" @@ -19,9 +18,7 @@ import ( // Authority is the layer that handles all SCEP interactions. type Authority struct { - prefix string - dns string - service *Service + service *Service // TODO: refactor, so that this is not required signAuth SignAuthority } @@ -50,15 +47,9 @@ func MustFromContext(ctx context.Context) *Authority { // AuthorityOptions required to create a new SCEP Authority. type AuthorityOptions struct { - // Service provides the certificate chain, the signer and the decrypter to the Authority + // Service provides the roots, intermediates, the signer and the (default) + // decrypter to the SCEP Authority. Service *Service - // DNS is the host used to generate accurate SCEP links. By default the authority - // will use the Host from the request, so this value will only be used if - // request.Host is empty. - DNS string - // Prefix is a URL path prefix under which the SCEP api is served. This - // prefix is required to generate accurate SCEP links. - Prefix string } // SignAuthority is the interface for a signing authority @@ -70,14 +61,35 @@ type SignAuthority interface { // New returns a new Authority that implements the SCEP interface. func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { authority := &Authority{ - prefix: ops.Prefix, - dns: ops.DNS, signAuth: signAuth, service: ops.Service, } return authority, nil } +func (a *Authority) Validate() error { + // if a default decrypter is set, the Authority is able + // to decrypt SCEP requests. No need to verify the provisioners. + if a.service.defaultDecrypter != nil { + return nil + } + + for _, name := range []string{"scepca"} { // TODO: correct names; provided through options + p, err := a.LoadProvisionerByName(name) + if err != nil { + fmt.Println("prov load fail: %w", err) + } + if scepProv, ok := p.(*provisioner.SCEP); ok { + if cert, decrypter := scepProv.GetDecrypter(); cert == nil || decrypter == nil { + fmt.Println(fmt.Sprintf("SCEP provisioner %q doesn't have valid decrypter", scepProv.GetName())) + // TODO: return error + } + } + } + + return nil +} + var ( // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 defaultCapabilities = []string{ @@ -97,79 +109,38 @@ func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, e return a.signAuth.LoadProvisionerByName(name) } -// GetLinkExplicit returns the requested link from the directory. -func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, inputs ...string) string { - return a.getLinkExplicit(provName, abs, baseURL, inputs...) -} - -// getLinkExplicit returns an absolute or partial path to the given resource and a base -// URL dynamically obtained from the request for which the link is being calculated. -func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *url.URL, _ ...string) string { - link := "/" + provisionerName - if abs { - // Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351 - u := url.URL{} - if baseURL != nil { - u = *baseURL - } - - // If no Scheme is set, then default to http (in case of SCEP) - if u.Scheme == "" { - u.Scheme = "http" - } - - // If no Host is set, then use the default (first DNS attr in the ca.json). - if u.Host == "" { - u.Host = a.dns - } - - u.Path = a.prefix + link - return u.String() - } - - return link -} - -// GetCACertificates returns the certificate (chain) for the CA -func (a *Authority) GetCACertificates(ctx context.Context) ([]*x509.Certificate, error) { - // TODO: this should return: the "SCEP Server (RA)" certificate, the issuing CA up to and excl. the root - // Some clients do need the root certificate however; also see: https://github.com/openxpki/openxpki/issues/73 - // - // This means we might need to think about if we should use the current intermediate CA - // certificate as the "SCEP Server (RA)" certificate. It might be better to have a distinct - // RA certificate, with a corresponding rsa.PrivateKey, just for SCEP usage, which is signed by - // the intermediate CA. Will need to look how we can provide this nicely within step-ca. - // - // This might also mean that we might want to use a distinct instance of KMS for doing the key operations, - // so that we can use RSA just for SCEP. - // - // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in - // https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now. - // - // The certificate to use should probably depend on the (configured) provisioner and may - // use a distinct certificate, apart from the intermediate. - +// GetCACertificates returns the certificate (chain) for the CA. +// +// This methods returns the "SCEP Server (RA)" certificate, the issuing CA up to and excl. the root. +// Some clients do need the root certificate however; also see: https://github.com/openxpki/openxpki/issues/73 +// +// In case a provisioner specific decrypter is available, this is used as the "SCEP Server (RA)" certificate +// instead of the CA intermediate directly. This uses a distinct instance of a KMS for doing the SCEp key +// operations, so that RSA can be used for just SCEP. +// +// Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in +// https://tools.ietf.org/id/draft-nourse-scep-21.html. +func (a *Authority) GetCACertificates(ctx context.Context) (certs []*x509.Certificate, err error) { p, err := provisionerFromContext(ctx) if err != nil { - return nil, err - } - - if len(a.service.certificateChain) == 0 { - return nil, errors.New("no intermediate certificate available in SCEP authority") + return } - certs := []*x509.Certificate{} + // if a provisioner specific RSA decrypter is available, it is returned as + // the first certificate. if decrypterCertificate, _ := p.GetDecrypter(); decrypterCertificate != nil { certs = append(certs, decrypterCertificate) - certs = append(certs, a.service.signerCertificate) - } else { - certs = append(certs, a.service.defaultDecrypterCertificate) } - // NOTE: we're adding the CA roots here, but they are (highly likely) different than what the RFC means. - // Clients are responsible to select the right cert(s) to use, though. - if p.ShouldIncludeRootInChain() && len(a.service.certificateChain) > 1 { - certs = append(certs, a.service.certificateChain[1]) + // TODO: ensure logic, so that signer is first intermediate and that + // there are no doubles certificates. + //certs = append(certs, a.service.signerCertificate) + certs = append(certs, a.service.intermediates...) + + // the CA roots are added for completeness. Clients are responsible + // to select the right cert(s) to store and use. + if p.ShouldIncludeRootInChain() { + certs = append(certs, a.service.roots...) } return certs, nil @@ -182,7 +153,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return fmt.Errorf("error parsing pkcs7 content: %w", err) } - fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypterCertificate)) + fmt.Println(fmt.Sprintf("%#+v", a.service.signerCertificate)) fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypter)) cert, pkey, err := a.selectDecrypter(ctx) @@ -246,7 +217,7 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate } // fallback to the CA wide decrypter - cert = a.service.defaultDecrypterCertificate + cert = a.service.signerCertificate pkey = a.service.defaultDecrypter return diff --git a/scep/options.go b/scep/options.go index 00662ae9..43f41fba 100644 --- a/scep/options.go +++ b/scep/options.go @@ -2,77 +2,76 @@ package scep import ( "crypto" + "crypto/rsa" "crypto/x509" - - "github.com/pkg/errors" + "errors" ) type Options struct { - // CertificateChain is the issuer certificate, along with any other bundled certificates - // to be returned in the chain for consumers. Configured in the ca.json crt property. - CertificateChain []*x509.Certificate - SignerCert *x509.Certificate - DecrypterCert *x509.Certificate + // Roots contains the (federated) CA roots certificate(s) + Roots []*x509.Certificate `json:"-"` + // Intermediates points issuer certificate, along with any other bundled certificates + // to be returned in the chain for consumers. + Intermediates []*x509.Certificate `json:"-"` + // SignerCert points to the certificate of the CA signer. It usually is the same as the + // first certificate in the CertificateChain. + SignerCert *x509.Certificate `json:"-"` // Signer signs CSRs in SCEP. Configured in the ca.json key property. Signer crypto.Signer `json:"-"` // Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property. Decrypter crypto.Decrypter `json:"-"` } +type comparablePublicKey interface { + Equal(crypto.PublicKey) bool +} + // Validate checks the fields in Options. func (o *Options) Validate() error { - if o.CertificateChain == nil { - return errors.New("certificate chain not configured correctly") + switch { + case len(o.Intermediates) == 0: + return errors.New("no intermediate certificate available for SCEP authority") + case o.Signer == nil: + return errors.New("no signer available for SCEP authority") + case o.SignerCert == nil: + return errors.New("no signer certificate available for SCEP authority") } - if len(o.CertificateChain) < 1 { - return errors.New("certificate chain should at least have one certificate") + // check if the signer (intermediate CA) certificate has the same public key as + // the signer. According to the RFC it seems valid to have different keys for + // the intermediate and the CA signing new certificates, so this might change + // in the future. + signerPublicKey := o.Signer.Public().(comparablePublicKey) + if !signerPublicKey.Equal(o.SignerCert.PublicKey) { + return errors.New("mismatch between signer certificate and public key") } - // According to the RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP - // can be used with something different than RSA, but requires the encryption - // to be performed using the challenge password. An older version of specification - // states that only RSA is supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1 - // Other algorithms than RSA do not seem to be supported in certnanny/sscep, but it might work + // decrypter can be nil in case a signing only key is used; validation complete. + if o.Decrypter == nil { + return nil + } + + // If a decrypter is available, check that it's backed by an RSA key. According to the + // RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something + // different than RSA, but requires the encryption to be performed using the challenge + // password in that case. An older version of specification states that only RSA is + // supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other + // algorithms do not seem to be supported in certnanny/sscep, but it might work // in micromdm/scep. Currently only RSA is allowed, but it might be an option // to try other algorithms in the future. - //intermediate := o.CertificateChain[0] - //intermediate := o.SignerCert - // if intermediate.PublicKeyAlgorithm != x509.RSA { - // return errors.New("only the RSA algorithm is (currently) supported") - // } - - // TODO: add checks for key usage? - - //signerPublicKey, ok := o.Signer.Public().(*rsa.PublicKey) - // if !ok { - // return errors.New("only RSA public keys are (currently) supported as signers") - // } - - // check if the intermediate ca certificate has the same public key as the signer. - // According to the RFC it seems valid to have different keys for the intermediate - // and the CA signing new certificates, so this might change in the future. - // if !signerPublicKey.Equal(intermediate.PublicKey) { - // return errors.New("mismatch between certificate chain and signer public keys") - // } - - // TODO: this could be a different decrypter, based on the value - // in the provisioner. - // decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) - // if !ok { - // return errors.New("only RSA public keys are (currently) supported as decrypters") - // } + decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) + if !ok { + return errors.New("only RSA keys are (currently) supported as decrypters") + } // check if intermediate public key is the same as the decrypter public key. // In certnanny/sscep it's mentioned that the signing key can be different - // from the decrypting (and encrypting) key. Currently that's not supported. - // if !decrypterPublicKey.Equal(intermediate.PublicKey) { - // return errors.New("mismatch between certificate chain and decrypter public keys") - // } - - // if !decrypterPublicKey.Equal(o.DecrypterCert.PublicKey) { - // return errors.New("mismatch between certificate chain and decrypter public keys") - // } + // from the decrypting (and encrypting) key. These options are only used and + // validated when the intermediate CA is also used as the decrypter, though, + // so they should match. + if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) { + return errors.New("mismatch between certificate chain and decrypter public keys") + } return nil } diff --git a/scep/service.go b/scep/service.go index ffb4166a..f3a6d097 100644 --- a/scep/service.go +++ b/scep/service.go @@ -6,13 +6,15 @@ import ( "crypto/x509" ) -// Service is a wrapper for crypto.Signer and crypto.Decrypter +// Service is a wrapper for a crypto.Decrypter and crypto.Signer for +// decrypting SCEP requests and signing certificates in response to +// SCEP certificate requests. type Service struct { - certificateChain []*x509.Certificate - signerCertificate *x509.Certificate - signer crypto.Signer - defaultDecrypterCertificate *x509.Certificate - defaultDecrypter crypto.Decrypter + roots []*x509.Certificate + intermediates []*x509.Certificate + signerCertificate *x509.Certificate + signer crypto.Signer + defaultDecrypter crypto.Decrypter } // NewService returns a new Service type. @@ -20,13 +22,11 @@ func NewService(_ context.Context, opts Options) (*Service, error) { if err := opts.Validate(); err != nil { return nil, err } - - // TODO: should this become similar to the New CertificateAuthorityService as in x509CAService? return &Service{ - certificateChain: opts.CertificateChain, - signerCertificate: opts.SignerCert, - signer: opts.Signer, - defaultDecrypterCertificate: opts.DecrypterCert, - defaultDecrypter: opts.Decrypter, + roots: opts.Roots, + intermediates: opts.Intermediates, + signerCertificate: opts.SignerCert, + signer: opts.Signer, + defaultDecrypter: opts.Decrypter, }, nil } From 6985b4be6220ce961bc3e8c6cf9d89640b3ef6b6 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 1 Jun 2023 14:43:32 +0200 Subject: [PATCH 03/39] Clean up the SCEP authority and provisioner --- api/api.go | 10 +++------- authority/authority.go | 30 ++++++++++++++++++++++++++---- authority/provisioner/scep.go | 7 ++----- authority/provisioner/webhook.go | 3 --- ca/ca.go | 15 ++++++++++----- scep/api/api.go | 3 --- scep/authority.go | 30 +++++++++++++----------------- scep/options.go | 4 ++++ scep/service.go | 26 ++++++++++++++++---------- 9 files changed, 74 insertions(+), 54 deletions(-) diff --git a/api/api.go b/api/api.go index 849fd24f..b91aef97 100644 --- a/api/api.go +++ b/api/api.go @@ -244,9 +244,6 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { continue } - fmt.Println(scepProv.KMS) - fmt.Println(fmt.Sprintf("%#+v", scepProv)) - type old struct { challengePassword string decrypterCertificate []byte @@ -255,10 +252,9 @@ func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { } o := old{scepProv.ChallengePassword, scepProv.DecrypterCertificate, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword} scepProv.ChallengePassword = "*** REDACTED ***" - // TODO: remove the details in the API response - // scepProv.DecrypterCert = "" - // scepProv.DecrypterKey = "" - // scepProv.DecrtyperKeyPassword = "" + scepProv.DecrypterCertificate = []byte("*** REDACTED ***") + scepProv.DecrypterKey = "*** REDACTED ***" + scepProv.DecrypterKeyPassword = "*** REDACTED ***" defer func(o old) { //nolint:gocritic // defer in loop required to restore initial state of provisioners scepProv.ChallengePassword = o.challengePassword diff --git a/authority/authority.go b/authority/authority.go index 8b93a634..7cd0f2ac 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -262,6 +262,14 @@ func (a *Authority) ReloadAdminResources(ctx context.Context) error { a.config.AuthorityConfig.Admins = adminList a.admins = adminClxn + // update the SCEP service with the currently active SCEP + // provisioner names. + // TODO(hs): trigger SCEP authority (re)validation using + // the current set of SCEP provisioners. + if a.scepService != nil { + a.scepService.UpdateProvisioners(a.getSCEPProvisionerNames()) + } + return nil } @@ -643,7 +651,7 @@ func (a *Authority) init() error { // The SCEP functionality is provided through an instance of // scep.Service. It is initialized once when the CA is started. - // TODO: should the SCEP service support reloading? For example, + // TODO(hs): should the SCEP service support reloading? For example, // when the admin resources are reloaded, specifically the provisioners, // it can happen that the SCEP service is no longer required and can // be destroyed, or that it needs to be instantiated. It may also need @@ -664,11 +672,11 @@ func (a *Authority) init() error { return err } - // TODO: instead of creating the decrypter here, pass the + // TODO(hs): instead of creating the decrypter here, pass the // intermediate key + chain down to the SCEP service / authority, // and only instantiate it when required there. Is that possible? // Also with entering passwords? - // TODO: if moving the logic, try improving the logic for the + // TODO(hs): if moving the logic, try improving the logic for the // decrypter password too? Right now it needs to be entered multiple // times; I've observed it to be three times maximum, every time // the intermediate key is read. @@ -678,11 +686,16 @@ func (a *Authority) init() error { DecryptionKey: a.config.IntermediateKey, Password: a.password, }); err == nil { - // only pass the decrypter down when it was successfully created + // only pass the decrypter down when it was successfully created, + // meaning it's an RSA key, and `CreateDecrypter` did not fail. options.Decrypter = decrypter } } + // provide the current SCEP provisioner names, so that the provisioners + // can be validated when the CA is started. + options.SCEPProvisionerNames = a.getSCEPProvisionerNames() + a.scepService, err = scep.NewService(ctx, options) if err != nil { return err @@ -849,6 +862,15 @@ func (a *Authority) requiresSCEP() bool { return false } +func (a *Authority) getSCEPProvisionerNames() (names []string) { + for _, p := range a.config.AuthorityConfig.Provisioners { + if p.GetType() == provisioner.TypeSCEP { + names = append(names, p.GetName()) + } + } + return +} + // GetSCEP returns the configured SCEP Service. func (a *Authority) GetSCEP() *scep.Service { return a.scepService diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 192c75b1..7b780d6a 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -208,10 +208,9 @@ func (s *SCEP) Init(config Config) (err error) { return fmt.Errorf("failed creating decrypter: %w", err) } - // Parse decrypter certificate + // parse the decrypter certificate block, rest := pem.Decode(s.DecrypterCertificate) if len(rest) > 0 { - fmt.Println(string(rest)) return errors.New("failed parsing decrypter certificate: trailing data") } if block == nil { @@ -221,9 +220,7 @@ func (s *SCEP) Init(config Config) (err error) { return fmt.Errorf("failed parsing decrypter certificate: %w", err) } - // if s.decrypterCertificate, err = pemutil.ReadCertificate(s.DecrypterCertFile); err != nil { - // return fmt.Errorf("failed reading certificate: %w", err) - // } + // validate the decrypter key decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) if !ok { return fmt.Errorf("only RSA keys are supported") diff --git a/authority/provisioner/webhook.go b/authority/provisioner/webhook.go index 3266e131..cb15547d 100644 --- a/authority/provisioner/webhook.go +++ b/authority/provisioner/webhook.go @@ -152,8 +152,6 @@ retry: return nil, err } - fmt.Println(req) - secret, err := base64.StdEncoding.DecodeString(w.Secret) if err != nil { return nil, err @@ -203,7 +201,6 @@ retry: time.Sleep(time.Second) goto retry } - fmt.Println(fmt.Sprintf("%#+v", resp)) if resp.StatusCode >= 400 { return nil, fmt.Errorf("Webhook server responded with %d", resp.StatusCode) } diff --git a/ca/ca.go b/ca/ca.go index a3f261e7..6b9a2a13 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -256,12 +256,17 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { return nil, errors.Wrap(err, "failed creating SCEP authority") } - // TODO: validate that the scepAuthority is fully valid? E.g. initialization - // may have configured the default decrypter, but if that's not set or if it's - // somehow not usable, all SCEP provisioners should have a valid decrypter - // configured by now. + // validate the SCEP authority configuration. Currently this + // will not result in a failure to start if one or more SCEP + // provisioners are not correctly configured. Only a log will + // be emitted. + shouldFail := false if err := scepAuthority.Validate(); err != nil { - return nil, errors.Wrap(err, "failed validating SCEP authority") + err = errors.Wrap(err, "failed validating SCEP authority") + if shouldFail { + return nil, err + } + log.Println(err) } // According to the RFC (https://tools.ietf.org/html/rfc8894#section-7.10), diff --git a/scep/api/api.go b/scep/api/api.go index 00f693a8..98da818b 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -308,8 +308,6 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { transactionID := string(msg.TransactionID) challengePassword := msg.CSRReqMessage.ChallengePassword - fmt.Println("challenge password: ", challengePassword) - // NOTE: we're blocking the RenewalReq if the challenge does not match, because otherwise we don't have any authentication. // The macOS SCEP client performs renewals using PKCSreq. The CertNanny SCEP client will use PKCSreq with challenge too, it seems, // even if using the renewal flow as described in the README.md. MicroMDM SCEP client also only does PKCSreq by default, unless @@ -317,7 +315,6 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // We'll have to see how it works out. if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { if err := auth.ValidateChallenge(ctx, challengePassword, transactionID); err != nil { - fmt.Println(err) if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) { return createFailureResponse(ctx, csr, msg, microscep.BadRequest, err) } diff --git a/scep/authority.go b/scep/authority.go index 14e5cd1c..6f2387c1 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -67,22 +67,24 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { return authority, nil } +// Validate validates if the SCEP Authority has a valid configuration. +// The validation includes a check if a decrypter is available, either +// an authority wide decrypter, or a provisioner specific decrypter. func (a *Authority) Validate() error { - // if a default decrypter is set, the Authority is able - // to decrypt SCEP requests. No need to verify the provisioners. - if a.service.defaultDecrypter != nil { - return nil - } - - for _, name := range []string{"scepca"} { // TODO: correct names; provided through options + noDefaultDecrypterAvailable := a.service.defaultDecrypter == nil + for _, name := range a.service.scepProvisionerNames { p, err := a.LoadProvisionerByName(name) if err != nil { - fmt.Println("prov load fail: %w", err) + return fmt.Errorf("failed loading provisioner %q: %w", name, err) } if scepProv, ok := p.(*provisioner.SCEP); ok { - if cert, decrypter := scepProv.GetDecrypter(); cert == nil || decrypter == nil { - fmt.Println(fmt.Sprintf("SCEP provisioner %q doesn't have valid decrypter", scepProv.GetName())) - // TODO: return error + cert, decrypter := scepProv.GetDecrypter() + // TODO: return sentinel/typed error, to be able to ignore/log these cases during init? + if cert == nil && noDefaultDecrypterAvailable { + return fmt.Errorf("SCEP provisioner %q does not have a decrypter certificate", name) + } + if decrypter == nil && noDefaultDecrypterAvailable { + return fmt.Errorf("SCEP provisioner %q does not have decrypter", name) } } } @@ -153,17 +155,11 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return fmt.Errorf("error parsing pkcs7 content: %w", err) } - fmt.Println(fmt.Sprintf("%#+v", a.service.signerCertificate)) - fmt.Println(fmt.Sprintf("%#+v", a.service.defaultDecrypter)) - cert, pkey, err := a.selectDecrypter(ctx) if err != nil { return fmt.Errorf("failed selecting decrypter: %w", err) } - fmt.Println(fmt.Sprintf("%#+v", cert)) - fmt.Println(fmt.Sprintf("%#+v", pkey)) - envelope, err := p7c.Decrypt(cert, pkey) if err != nil { return fmt.Errorf("error decrypting encrypted pkcs7 content: %w", err) diff --git a/scep/options.go b/scep/options.go index 43f41fba..7ba7cfc2 100644 --- a/scep/options.go +++ b/scep/options.go @@ -20,6 +20,10 @@ type Options struct { Signer crypto.Signer `json:"-"` // Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property. Decrypter crypto.Decrypter `json:"-"` + // SCEPProvisionerNames contains the currently configured SCEP provioner names. These + // are used to be able to load the provisioners when the SCEP authority is being + // validated. + SCEPProvisionerNames []string } type comparablePublicKey interface { diff --git a/scep/service.go b/scep/service.go index f3a6d097..60d4c8b2 100644 --- a/scep/service.go +++ b/scep/service.go @@ -10,11 +10,12 @@ import ( // decrypting SCEP requests and signing certificates in response to // SCEP certificate requests. type Service struct { - roots []*x509.Certificate - intermediates []*x509.Certificate - signerCertificate *x509.Certificate - signer crypto.Signer - defaultDecrypter crypto.Decrypter + roots []*x509.Certificate + intermediates []*x509.Certificate + signerCertificate *x509.Certificate + signer crypto.Signer + defaultDecrypter crypto.Decrypter + scepProvisionerNames []string } // NewService returns a new Service type. @@ -23,10 +24,15 @@ func NewService(_ context.Context, opts Options) (*Service, error) { return nil, err } return &Service{ - roots: opts.Roots, - intermediates: opts.Intermediates, - signerCertificate: opts.SignerCert, - signer: opts.Signer, - defaultDecrypter: opts.Decrypter, + roots: opts.Roots, + intermediates: opts.Intermediates, + signerCertificate: opts.SignerCert, + signer: opts.Signer, + defaultDecrypter: opts.Decrypter, + scepProvisionerNames: opts.SCEPProvisionerNames, }, nil } + +func (s *Service) UpdateProvisioners(scepProvisionerNames []string) { + s.scepProvisionerNames = scepProvisionerNames +} From 8fc3a46387f8df1e948efda043d290200758240b Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 1 Jun 2023 15:46:21 +0200 Subject: [PATCH 04/39] Refactor the SCEP authority initialization Instead of relying on an intermediate `scep.Service` struct, initialize the `scep.Authority` directly. This removes one redundant layer of indirection. --- authority/authority.go | 27 ++++++++++------- ca/ca.go | 9 ++---- scep/authority.go | 66 +++++++++++++++++++++++++----------------- scep/common.go | 29 ------------------- scep/database.go | 7 ----- scep/provisioner.go | 24 +++++++++++++++ scep/service.go | 38 ------------------------ 7 files changed, 82 insertions(+), 118 deletions(-) delete mode 100644 scep/common.go delete mode 100644 scep/database.go delete mode 100644 scep/service.go diff --git a/authority/authority.go b/authority/authority.go index 7cd0f2ac..29cbf846 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -62,7 +62,7 @@ type Authority struct { x509Enforcers []provisioner.CertificateEnforcer // SCEP CA - scepService *scep.Service + scepAuthority *scep.Authority // SSH CA sshHostPassword []byte @@ -263,11 +263,12 @@ func (a *Authority) ReloadAdminResources(ctx context.Context) error { a.admins = adminClxn // update the SCEP service with the currently active SCEP - // provisioner names. - // TODO(hs): trigger SCEP authority (re)validation using - // the current set of SCEP provisioners. - if a.scepService != nil { - a.scepService.UpdateProvisioners(a.getSCEPProvisionerNames()) + // provisioner names and revalidate the configuration. + if a.scepAuthority != nil { + a.scepAuthority.UpdateProvisioners(a.getSCEPProvisionerNames()) + if err := a.scepAuthority.Validate(); err != nil { + log.Printf("failed validating SCEP authority: %v\n", err) + } } return nil @@ -696,10 +697,16 @@ func (a *Authority) init() error { // can be validated when the CA is started. options.SCEPProvisionerNames = a.getSCEPProvisionerNames() - a.scepService, err = scep.NewService(ctx, options) + // create a new SCEP authority + a.scepAuthority, err = scep.New(a, options) if err != nil { return err } + + // validate the SCEP authority + if err := a.scepAuthority.Validate(); err != nil { + a.initLogf("failed validating SCEP authority: %v", err) + } } // Load X509 constraints engine. @@ -871,9 +878,9 @@ func (a *Authority) getSCEPProvisionerNames() (names []string) { return } -// GetSCEP returns the configured SCEP Service. -func (a *Authority) GetSCEP() *scep.Service { - return a.scepService +// GetSCEP returns the configured SCEP Authority +func (a *Authority) GetSCEP() *scep.Authority { + return a.scepAuthority } func (a *Authority) startCRLGenerator() error { diff --git a/ca/ca.go b/ca/ca.go index 6b9a2a13..c13496a6 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -250,19 +250,14 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { var scepAuthority *scep.Authority if ca.shouldServeSCEPEndpoints() { - if scepAuthority, err = scep.New(auth, scep.AuthorityOptions{ - Service: auth.GetSCEP(), - }); err != nil { - return nil, errors.Wrap(err, "failed creating SCEP authority") - } - // validate the SCEP authority configuration. Currently this // will not result in a failure to start if one or more SCEP // provisioners are not correctly configured. Only a log will // be emitted. - shouldFail := false + scepAuthority = auth.GetSCEP() if err := scepAuthority.Validate(); err != nil { err = errors.Wrap(err, "failed validating SCEP authority") + shouldFail := false if shouldFail { return nil, err } diff --git a/scep/authority.go b/scep/authority.go index 6f2387c1..55fd2086 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -18,8 +18,13 @@ import ( // Authority is the layer that handles all SCEP interactions. type Authority struct { - service *Service // TODO: refactor, so that this is not required - signAuth SignAuthority + signAuth SignAuthority + roots []*x509.Certificate + intermediates []*x509.Certificate + signerCertificate *x509.Certificate + signer crypto.Signer + defaultDecrypter crypto.Decrypter + scepProvisionerNames []string } type authorityKey struct{} @@ -45,13 +50,6 @@ func MustFromContext(ctx context.Context) *Authority { } } -// AuthorityOptions required to create a new SCEP Authority. -type AuthorityOptions struct { - // Service provides the roots, intermediates, the signer and the (default) - // decrypter to the SCEP Authority. - Service *Service -} - // SignAuthority is the interface for a signing authority type SignAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) @@ -59,10 +57,18 @@ type SignAuthority interface { } // New returns a new Authority that implements the SCEP interface. -func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { +func New(signAuth SignAuthority, opts Options) (*Authority, error) { + if err := opts.Validate(); err != nil { + return nil, err + } authority := &Authority{ - signAuth: signAuth, - service: ops.Service, + signAuth: signAuth, // TODO: provide signAuth through context instead? + roots: opts.Roots, + intermediates: opts.Intermediates, + signerCertificate: opts.SignerCert, + signer: opts.Signer, + defaultDecrypter: opts.Decrypter, + scepProvisionerNames: opts.SCEPProvisionerNames, } return authority, nil } @@ -71,15 +77,15 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) { // The validation includes a check if a decrypter is available, either // an authority wide decrypter, or a provisioner specific decrypter. func (a *Authority) Validate() error { - noDefaultDecrypterAvailable := a.service.defaultDecrypter == nil - for _, name := range a.service.scepProvisionerNames { + noDefaultDecrypterAvailable := a.defaultDecrypter == nil + for _, name := range a.scepProvisionerNames { p, err := a.LoadProvisionerByName(name) if err != nil { return fmt.Errorf("failed loading provisioner %q: %w", name, err) } if scepProv, ok := p.(*provisioner.SCEP); ok { cert, decrypter := scepProv.GetDecrypter() - // TODO: return sentinel/typed error, to be able to ignore/log these cases during init? + // TODO(hs): return sentinel/typed error, to be able to ignore/log these cases during init? if cert == nil && noDefaultDecrypterAvailable { return fmt.Errorf("SCEP provisioner %q does not have a decrypter certificate", name) } @@ -92,6 +98,13 @@ func (a *Authority) Validate() error { return nil } +// UpdateProvisioners updates the SCEP Authority with the new, and hopefully +// current SCEP provisioners configured. This allows the Authority to be +// validated with the latest data. +func (a *Authority) UpdateProvisioners(scepProvisionerNames []string) { + a.scepProvisionerNames = scepProvisionerNames +} + var ( // TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2 defaultCapabilities = []string{ @@ -134,15 +147,14 @@ func (a *Authority) GetCACertificates(ctx context.Context) (certs []*x509.Certif certs = append(certs, decrypterCertificate) } - // TODO: ensure logic, so that signer is first intermediate and that - // there are no doubles certificates. - //certs = append(certs, a.service.signerCertificate) - certs = append(certs, a.service.intermediates...) + // TODO(hs): ensure logic is in place that checks the signer is the first + // intermediate and that there are no double certificates. + certs = append(certs, a.intermediates...) - // the CA roots are added for completeness. Clients are responsible - // to select the right cert(s) to store and use. + // the CA roots are added for completeness when configured to do so. Clients + // are responsible to select the right cert(s) to store and use. if p.ShouldIncludeRootInChain() { - certs = append(certs, a.service.roots...) + certs = append(certs, a.roots...) } return certs, nil @@ -213,8 +225,8 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate } // fallback to the CA wide decrypter - cert = a.service.signerCertificate - pkey = a.service.defaultDecrypter + cert = a.signerCertificate + pkey = a.defaultDecrypter return } @@ -351,8 +363,8 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // as the first certificate in the array signedData.AddCertificate(cert) - authCert := a.service.signerCertificate - signer := a.service.signer + authCert := a.signerCertificate + signer := a.signer // sign the attributes if err := signedData.AddSigner(authCert, signer, config); err != nil { @@ -423,7 +435,7 @@ func (a *Authority) CreateFailureResponse(_ context.Context, _ *x509.Certificate } // sign the attributes - if err := signedData.AddSigner(a.service.signerCertificate, a.service.signer, config); err != nil { + if err := signedData.AddSigner(a.signerCertificate, a.signer, config); err != nil { return nil, err } diff --git a/scep/common.go b/scep/common.go deleted file mode 100644 index 73b16ed4..00000000 --- a/scep/common.go +++ /dev/null @@ -1,29 +0,0 @@ -package scep - -import ( - "context" - "errors" -) - -// ContextKey is the key type for storing and searching for SCEP request -// essentials in the context of a request. -type ContextKey string - -const ( - // ProvisionerContextKey provisioner key - ProvisionerContextKey = ContextKey("provisioner") -) - -// provisionerFromContext searches the context for a SCEP provisioner. -// Returns the provisioner or an error. -func provisionerFromContext(ctx context.Context) (Provisioner, error) { - val := ctx.Value(ProvisionerContextKey) - if val == nil { - return nil, errors.New("provisioner expected in request context") - } - p, ok := val.(Provisioner) - if !ok || p == nil { - return nil, errors.New("provisioner in context is not a SCEP provisioner") - } - return p, nil -} diff --git a/scep/database.go b/scep/database.go deleted file mode 100644 index f73573fd..00000000 --- a/scep/database.go +++ /dev/null @@ -1,7 +0,0 @@ -package scep - -import "crypto/x509" - -type DB interface { - StoreCertificate(crt *x509.Certificate) error -} diff --git a/scep/provisioner.go b/scep/provisioner.go index cb41ed47..79852e22 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -4,6 +4,7 @@ import ( "context" "crypto" "crypto/x509" + "errors" "time" "github.com/smallstep/certificates/authority/provisioner" @@ -22,3 +23,26 @@ type Provisioner interface { GetContentEncryptionAlgorithm() int ValidateChallenge(ctx context.Context, challenge, transactionID string) error } + +// ContextKey is the key type for storing and searching for SCEP request +// essentials in the context of a request. +type ContextKey string + +const ( + // ProvisionerContextKey provisioner key + ProvisionerContextKey = ContextKey("provisioner") +) + +// provisionerFromContext searches the context for a SCEP provisioner. +// Returns the provisioner or an error. +func provisionerFromContext(ctx context.Context) (Provisioner, error) { + val := ctx.Value(ProvisionerContextKey) + if val == nil { + return nil, errors.New("provisioner expected in request context") + } + p, ok := val.(Provisioner) + if !ok || p == nil { + return nil, errors.New("provisioner in context is not a SCEP provisioner") + } + return p, nil +} diff --git a/scep/service.go b/scep/service.go deleted file mode 100644 index 60d4c8b2..00000000 --- a/scep/service.go +++ /dev/null @@ -1,38 +0,0 @@ -package scep - -import ( - "context" - "crypto" - "crypto/x509" -) - -// Service is a wrapper for a crypto.Decrypter and crypto.Signer for -// decrypting SCEP requests and signing certificates in response to -// SCEP certificate requests. -type Service struct { - roots []*x509.Certificate - intermediates []*x509.Certificate - signerCertificate *x509.Certificate - signer crypto.Signer - defaultDecrypter crypto.Decrypter - scepProvisionerNames []string -} - -// NewService returns a new Service type. -func NewService(_ context.Context, opts Options) (*Service, error) { - if err := opts.Validate(); err != nil { - return nil, err - } - return &Service{ - roots: opts.Roots, - intermediates: opts.Intermediates, - signerCertificate: opts.SignerCert, - signer: opts.Signer, - defaultDecrypter: opts.Decrypter, - scepProvisionerNames: opts.SCEPProvisionerNames, - }, nil -} - -func (s *Service) UpdateProvisioners(scepProvisionerNames []string) { - s.scepProvisionerNames = scepProvisionerNames -} From b2bf2c330bf83ce3d2060a6d14ebfef08759f8d6 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 1 Jun 2023 16:22:00 +0200 Subject: [PATCH 05/39] Simplify SCEP provisioner context handling --- authority/authority.go | 4 ++-- scep/api/api.go | 2 +- scep/authority.go | 25 +++++-------------------- scep/provisioner.go | 32 +++++++++++++------------------- 4 files changed, 21 insertions(+), 42 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 29cbf846..8be23ed3 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -858,8 +858,8 @@ func (a *Authority) IsRevoked(sn string) (bool, error) { return a.db.IsRevoked(sn) } -// requiresSCEPService iterates over the configured provisioners -// and determines if one of them is a SCEP provisioner. +// requiresSCEP iterates over the configured provisioners +// and determines if at least one of them is a SCEP provisioner. func (a *Authority) requiresSCEP() bool { for _, p := range a.config.AuthorityConfig.Provisioners { if p.GetType() == provisioner.TypeSCEP { diff --git a/scep/api/api.go b/scep/api/api.go index 98da818b..1615313f 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -221,7 +221,7 @@ func lookupProvisioner(next http.HandlerFunc) http.HandlerFunc { return } - ctx = context.WithValue(ctx, scep.ProvisionerContextKey, scep.Provisioner(prov)) + ctx = scep.NewProvisionerContext(ctx, scep.Provisioner(prov)) next(w, r.WithContext(ctx)) } } diff --git a/scep/authority.go b/scep/authority.go index 55fd2086..5e02468d 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -136,10 +136,7 @@ func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, e // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in // https://tools.ietf.org/id/draft-nourse-scep-21.html. func (a *Authority) GetCACertificates(ctx context.Context) (certs []*x509.Certificate, err error) { - p, err := provisionerFromContext(ctx) - if err != nil { - return - } + p := provisionerFromContext(ctx) // if a provisioner specific RSA decrypter is available, it is returned as // the first certificate. @@ -214,10 +211,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err } func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { - p, err := provisionerFromContext(ctx) - if err != nil { - return nil, nil, err - } + p := provisionerFromContext(ctx) // return provisioner specific decrypter, if available if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { @@ -239,10 +233,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // poll for the status. It seems to be similar as what can happen in ACME, so might want to model // the implementation after the one in the ACME authority. Requires storage, etc. - p, err := provisionerFromContext(ctx) - if err != nil { - return nil, err - } + p := provisionerFromContext(ctx) // check if CSRReqMessage has already been decrypted if msg.CSRReqMessage.CSR == nil { @@ -463,10 +454,7 @@ func (a *Authority) CreateFailureResponse(_ context.Context, _ *x509.Certificate // GetCACaps returns the CA capabilities func (a *Authority) GetCACaps(ctx context.Context) []string { - p, err := provisionerFromContext(ctx) - if err != nil { - return defaultCapabilities - } + p := provisionerFromContext(ctx) caps := p.GetCapabilities() if len(caps) == 0 { @@ -483,9 +471,6 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { } func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { - p, err := provisionerFromContext(ctx) - if err != nil { - return err - } + p := provisionerFromContext(ctx) return p.ValidateChallenge(ctx, challenge, transactionID) } diff --git a/scep/provisioner.go b/scep/provisioner.go index 79852e22..a1796b5b 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -4,7 +4,6 @@ import ( "context" "crypto" "crypto/x509" - "errors" "time" "github.com/smallstep/certificates/authority/provisioner" @@ -24,25 +23,20 @@ type Provisioner interface { ValidateChallenge(ctx context.Context, challenge, transactionID string) error } -// ContextKey is the key type for storing and searching for SCEP request -// essentials in the context of a request. -type ContextKey string - -const ( - // ProvisionerContextKey provisioner key - ProvisionerContextKey = ContextKey("provisioner") -) +// provisionerKey is the key type for storing and searching a +// SCEP provisioner in the context. +type provisionerKey struct{} // provisionerFromContext searches the context for a SCEP provisioner. -// Returns the provisioner or an error. -func provisionerFromContext(ctx context.Context) (Provisioner, error) { - val := ctx.Value(ProvisionerContextKey) - if val == nil { - return nil, errors.New("provisioner expected in request context") - } - p, ok := val.(Provisioner) - if !ok || p == nil { - return nil, errors.New("provisioner in context is not a SCEP provisioner") +// Returns the provisioner or panics if no SCEP provisioner is found. +func provisionerFromContext(ctx context.Context) Provisioner { + p, ok := ctx.Value(provisionerKey{}).(Provisioner) + if !ok { + panic("SCEP provisioner expected in request context") } - return p, nil + return p +} + +func NewProvisionerContext(ctx context.Context, p Provisioner) context.Context { + return context.WithValue(ctx, provisionerKey{}, p) } From 557672bb4bc37bff8fade535bdb4638d73765268 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 26 Jul 2023 19:11:51 +0200 Subject: [PATCH 06/39] Add some notes for SCEP provisioners --- authority/authority.go | 10 +++++----- scep/api/api.go | 3 ++- scep/provisioner.go | 10 ++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 8be23ed3..4318246b 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -262,7 +262,7 @@ func (a *Authority) ReloadAdminResources(ctx context.Context) error { a.config.AuthorityConfig.Admins = adminList a.admins = adminClxn - // update the SCEP service with the currently active SCEP + // update the SCEP Authority with the currently active SCEP // provisioner names and revalidate the configuration. if a.scepAuthority != nil { a.scepAuthority.UpdateProvisioners(a.getSCEPProvisionerNames()) @@ -651,10 +651,10 @@ func (a *Authority) init() error { } // The SCEP functionality is provided through an instance of - // scep.Service. It is initialized once when the CA is started. - // TODO(hs): should the SCEP service support reloading? For example, + // scep.Authority. It is initialized once when the CA is started. + // TODO(hs): should the SCEP Authority support reloading? For example, // when the admin resources are reloaded, specifically the provisioners, - // it can happen that the SCEP service is no longer required and can + // it can happen that the SCEP Authority is no longer required and can // be destroyed, or that it needs to be instantiated. It may also need // to be revalidated, because not all SCEP provisioner may have a // valid decrypter available. @@ -674,7 +674,7 @@ func (a *Authority) init() error { } // TODO(hs): instead of creating the decrypter here, pass the - // intermediate key + chain down to the SCEP service / authority, + // intermediate key + chain down to the SCEP authority, // and only instantiate it when required there. Is that possible? // Also with entering passwords? // TODO(hs): if moving the logic, try improving the logic for the diff --git a/scep/api/api.go b/scep/api/api.go index 1615313f..b618607c 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -18,6 +18,7 @@ import ( "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/api/log" + "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/scep" ) @@ -208,7 +209,7 @@ func lookupProvisioner(next http.HandlerFunc) http.HandlerFunc { } ctx := r.Context() - auth := scep.MustFromContext(ctx) + auth := authority.MustFromContext(ctx) p, err := auth.LoadProvisionerByName(provisionerName) if err != nil { fail(w, err) diff --git a/scep/provisioner.go b/scep/provisioner.go index a1796b5b..e9b9c30f 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -4,17 +4,15 @@ import ( "context" "crypto" "crypto/x509" - "time" "github.com/smallstep/certificates/authority/provisioner" ) -// Provisioner is an interface that implements a subset of the provisioner.Interface -- -// only those methods required by the SCEP api/authority. +// Provisioner is an interface that embeds the +// provisioner.Interface and adds some SCEP specific +// functions. type Provisioner interface { - AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) - GetName() string - DefaultTLSCertDuration() time.Duration + provisioner.Interface GetOptions() *provisioner.Options GetCapabilities() []string ShouldIncludeRootInChain() bool From 567fc254040834af6580fb7f9554ef178d08ae4d Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 27 Jul 2023 00:55:39 +0200 Subject: [PATCH 07/39] Use the RSA decryption configuration for signing responses too --- authority/provisioner/scep.go | 116 +++++++++++++++++++++++----------- authority/provisioners.go | 8 --- go.mod | 50 +++++++++------ go.sum | 97 +++++++++++++++------------- scep/authority.go | 23 +++++-- scep/provisioner.go | 1 + 6 files changed, 179 insertions(+), 116 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 7b780d6a..14f6a57b 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -3,7 +3,6 @@ package provisioner import ( "context" "crypto" - "crypto/rsa" "crypto/subtle" "crypto/x509" "encoding/pem" @@ -15,6 +14,7 @@ import ( "go.step.sm/crypto/kms" kmsapi "go.step.sm/crypto/kms/apiv1" + "go.step.sm/crypto/kms/uri" "go.step.sm/linkedca" "github.com/smallstep/certificates/webhook" @@ -38,11 +38,10 @@ type SCEP struct { // MinimumPublicKeyLength is the minimum length for public keys in CSRs MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` - // TODO - KMS *kms.Options `json:"kms,omitempty"` - DecrypterCertificate []byte `json:"decrypterCertificate"` - DecrypterKey string `json:"decrypterKey"` - DecrypterKeyPassword string `json:"decrypterKeyPassword"` + // TODO(hs): also support a separate signer configuration? + DecrypterCertificate []byte `json:"decrypterCertificate"` + DecrypterKey string `json:"decrypterKey"` + DecrypterKeyPassword string `json:"decrypterKeyPassword"` // Numerical identifier for the ContentEncryptionAlgorithm as defined in github.com/mozilla-services/pkcs7 // at https://github.com/mozilla-services/pkcs7/blob/33d05740a3526e382af6395d3513e73d4e66d1cb/encrypt.go#L63 @@ -56,6 +55,7 @@ type SCEP struct { keyManager kmsapi.KeyManager decrypter crypto.Decrypter decrypterCertificate *x509.Certificate + signer crypto.Signer } // GetID returns the provisioner unique identifier. @@ -192,45 +192,81 @@ func (s *SCEP) Init(config Config) (err error) { s.GetOptions().GetWebhooks(), ) - if s.KMS != nil { - if s.keyManager, err = kms.New(context.Background(), *s.KMS); err != nil { + skip := false // TODO(hs): remove this; currently a helper for debugging + if decryptionKey := s.DecrypterKey; decryptionKey != "" && !skip { + u, err := uri.Parse(s.DecrypterKey) + if err != nil { + return fmt.Errorf("failed parsing decrypter key: %w", err) + } + var kmsType string + switch { + case u.Scheme != "": + kmsType = u.Scheme + default: + kmsType = "softkms" + } + opts := kms.Options{ + Type: kms.Type(kmsType), + URI: s.DecrypterKey, + } + if s.keyManager, err = kms.New(context.Background(), opts); err != nil { return fmt.Errorf("failed initializing kms: %w", err) } - km, ok := s.keyManager.(kmsapi.Decrypter) + kmsDecrypter, ok := s.keyManager.(kmsapi.Decrypter) if !ok { - return fmt.Errorf("%q is not a kmsapi.Decrypter", s.KMS.Type) + return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type) } - if s.DecrypterKey != "" || len(s.DecrypterCertificate) > 0 { - if s.decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: s.DecrypterKey, - Password: []byte(s.DecrypterKeyPassword), - }); err != nil { - return fmt.Errorf("failed creating decrypter: %w", err) - } - - // parse the decrypter certificate - block, rest := pem.Decode(s.DecrypterCertificate) - if len(rest) > 0 { - return errors.New("failed parsing decrypter certificate: trailing data") - } - if block == nil { - return errors.New("failed parsing decrypter certificate: no PEM block found") - } - if s.decrypterCertificate, err = x509.ParseCertificate(block.Bytes); err != nil { - return fmt.Errorf("failed parsing decrypter certificate: %w", err) - } - - // validate the decrypter key - decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) - if !ok { - return fmt.Errorf("only RSA keys are supported") - } - if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { - return errors.New("mismatch between decryption certificate and decrypter public keys") - } + if kmsType != "softkms" { // TODO(hs): this should likely become more transparent? + decryptionKey = u.Opaque + } + if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: decryptionKey, + Password: []byte(s.DecrypterKeyPassword), + }); err != nil { + return fmt.Errorf("failed creating decrypter: %w", err) + } + if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: decryptionKey, // TODO(hs): support distinct signer key + Password: []byte(s.DecrypterKeyPassword), + }); err != nil { + return fmt.Errorf("failed creating signer: %w", err) } } + // parse the decrypter certificate contents if available + if len(s.DecrypterCertificate) > 0 { + block, rest := pem.Decode(s.DecrypterCertificate) + if len(rest) > 0 { + return errors.New("failed parsing decrypter certificate: trailing data") + } + if block == nil { + return errors.New("failed parsing decrypter certificate: no PEM block found") + } + if s.decrypterCertificate, err = x509.ParseCertificate(block.Bytes); err != nil { + return fmt.Errorf("failed parsing decrypter certificate: %w", err) + } + } + + // TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager + // and load the certificate corresponding to the decryption key. + + // final validation for the decrypter + if s.decrypter != nil { + // // TODO(hs): enable this validation again + // if s.decrypterCertificate == nil { + // // TODO: don't hard skip at init? + // return fmt.Errorf("no decrypter certificate available for decrypter in %q", s.Name) + // } + // // validate the decrypter key + // decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) + // if !ok { + // return fmt.Errorf("only RSA keys are supported") + // } + // if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { + // return errors.New("mismatch between decryption certificate and decrypter public keys") + // } + } + // TODO: add other, SCEP specific, options? s.ctl, err = NewController(s, s.Claims, config, s.Options) @@ -317,3 +353,7 @@ func (s *SCEP) selectValidationMethod() validationMethod { func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) { return s.decrypterCertificate, s.decrypter } + +func (s *SCEP) GetSigner() (*x509.Certificate, crypto.Signer) { + return s.decrypterCertificate, s.signer +} diff --git a/authority/provisioners.go b/authority/provisioners.go index 35030933..7e976dec 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -8,7 +8,6 @@ import ( "encoding/pem" "fmt" "os" - "strings" "github.com/pkg/errors" "gopkg.in/square/go-jose.v2/jwt" @@ -16,7 +15,6 @@ import ( "go.step.sm/cli-utils/step" "go.step.sm/cli-utils/ui" "go.step.sm/crypto/jose" - "go.step.sm/crypto/kms" "go.step.sm/linkedca" "github.com/smallstep/certificates/authority/admin" @@ -976,12 +974,6 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Options: options, } if decrypter := cfg.GetDecrypter(); decrypter != nil { - if dkms := decrypter.GetKms(); dkms != nil { - s.KMS = &kms.Options{ - Type: kms.Type(strings.ToLower(linkedca.KMS_Type_name[int32(dkms.Type)])), - CredentialsFile: dkms.CredentialsFile, - } - } s.DecrypterCertificate = decrypter.DecrypterCertificate s.DecrypterKey = decrypter.DecrypterKey s.DecrypterKeyPassword = decrypter.DecrypterKeyPassword diff --git a/go.mod b/go.mod index 74a3c54d..528067fc 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/google/go-tpm v0.3.3 github.com/google/uuid v1.3.0 - github.com/googleapis/gax-go/v2 v2.9.1 + github.com/googleapis/gax-go/v2 v2.11.0 github.com/hashicorp/vault/api v1.9.2 github.com/hashicorp/vault/api/auth/approle v0.4.1 github.com/hashicorp/vault/api/auth/kubernetes v0.4.1 @@ -25,30 +25,30 @@ require ( github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738 github.com/smallstep/nosql v0.6.0 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.13 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.7.6 - go.step.sm/crypto v0.31.0 + go.step.sm/crypto v0.32.4 go.step.sm/linkedca v0.19.1 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 - golang.org/x/net v0.10.0 - google.golang.org/api v0.123.0 - google.golang.org/grpc v1.55.0 - google.golang.org/protobuf v1.30.0 + golang.org/x/net v0.12.0 + google.golang.org/api v0.130.0 + google.golang.org/grpc v1.56.1 + google.golang.org/protobuf v1.31.0 gopkg.in/square/go-jose.v2 v2.6.0 ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.0 // indirect + cloud.google.com/go v0.110.2 // indirect + cloud.google.com/go/compute v1.19.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/kms v1.10.2 // indirect + cloud.google.com/go/iam v1.1.0 // indirect + cloud.google.com/go/kms v1.13.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // indirect @@ -57,7 +57,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect - github.com/aws/aws-sdk-go v1.44.267 // indirect + github.com/aws/aws-sdk-go v1.44.295 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -83,8 +83,8 @@ require ( github.com/google/certificate-transparency-go v1.1.4 // indirect github.com/google/go-tpm-tools v0.3.12 // indirect github.com/google/go-tspi v0.3.0 // indirect - github.com/google/s2a-go v0.1.3 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect + github.com/google/s2a-go v0.1.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -106,6 +106,8 @@ require ( github.com/jackc/pgx/v4 v4.18.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.15.11 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect @@ -119,8 +121,10 @@ require ( github.com/peterbourgon/diskv/v3 v3.0.1 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/ryboe/q v1.0.19 // indirect github.com/schollz/jsonstore v1.1.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect @@ -129,12 +133,14 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/oauth2 v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -145,6 +151,8 @@ require ( // replace go.step.sm/linkedca => ../linkedca // use github.com/smallstep/pkcs7 fork with patches applied -replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948 +//replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230615175518-7ce6486b74eb + +replace go.mozilla.org/pkcs7 => ./../pkcs7 replace go.step.sm/linkedca => ./../linkedca diff --git a/go.sum b/go.sum index 4ea3af6b..34d84886 100644 --- a/go.sum +++ b/go.sum @@ -31,25 +31,25 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= +cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/kms v1.10.2 h1:8UePKEypK3SQ6g+4mn/s/VgE5L7XOh+FwGGRUqvY3Hw= -cloud.google.com/go/kms v1.10.2/go.mod h1:9mX3Q6pdroWzL20pbK6RaOdBbXBEhMNgK4Pfz2bweb4= +cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= +cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= +cloud.google.com/go/kms v1.13.0 h1:s+sRhcowXwuLsa2Z8g3Tmh5l0HWNBf//HogCgiuDs/0= +cloud.google.com/go/kms v1.13.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/longrunning v0.4.2 h1:WDKiiNXFTaQ6qz/G8FCOkuY9kJmOJGY67wPUC1M2RbE= cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= @@ -88,8 +88,8 @@ github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuE github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= @@ -165,8 +165,8 @@ github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.267 h1:Asrp6EMqqRxZvjK0NjzkWcrOk15RnWtupuUrUuZMabk= -github.com/aws/aws-sdk-go v1.44.267/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.295 h1:SGjU1+MqttXfRiWHD6WU0DRhaanJgAFY+xIhEaugV8Y= +github.com/aws/aws-sdk-go v1.44.295/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= @@ -474,8 +474,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= -github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= github.com/google/trillian v1.3.14-0.20210409160123-c5ea3abd4a41/go.mod h1:1dPv0CUjNQVFEDuAUFhZql16pw/VlPgaX8qj+g5pVzQ= @@ -488,13 +488,13 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= -github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= +github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= @@ -681,6 +681,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -824,6 +826,7 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -875,6 +878,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -890,6 +895,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/ryboe/q v1.0.19 h1:1dO1anK4gorZRpXBD/edBZkMxIC1tFIwN03nfyOV13A= +github.com/ryboe/q v1.0.19/go.mod h1:IoEB3Q2/p6n1qbhIQVuNyakxtnV4rNJ/XJPK+jsEa0M= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -919,8 +926,6 @@ github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738 h1:h+cZ github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738/go.mod h1:mk2hyNbyai1oon+ilW9t42BuBVw7ee8elDdgrPq4394= github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= -github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948 h1:/80FqDt6pzL9clNW8G2IsRAzKGNAuzsEs7g1Y5oaM/Y= -github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= @@ -970,8 +975,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= @@ -1064,8 +1069,8 @@ go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16g go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/cli-utils v0.7.6 h1:YkpLVrepmy2c5+eaz/wduiGxlgrRx3YdAStE37if25g= go.step.sm/cli-utils v0.7.6/go.mod h1:j+FxFZ2gbWkAJl0eded/rksuxmNqWpmyxbkXcukGJaY= -go.step.sm/crypto v0.31.0 h1:8ZG/BxC+0+LzPpk/764h5yubpG3GfxcRVR4E+Aye72g= -go.step.sm/crypto v0.31.0/go.mod h1:Dv4lpkijKiZVkoc6zp+Xaw1xmy+voia1mykvbpQIvuc= +go.step.sm/crypto v0.32.4 h1:jSr5sB6vJCciqFB3BFKgK5ykRtuzKqdl4j9+CYkS8Hc= +go.step.sm/crypto v0.32.4/go.mod h1:A009Gtqx80nTz/9DreRMflMGgaSWTuhK8En6XycK9yA= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1111,8 +1116,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1211,8 +1216,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1232,8 +1237,8 @@ golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= +golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1247,7 +1252,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1339,15 +1344,15 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1360,8 +1365,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1487,8 +1492,8 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.123.0 h1:yHVU//vA+qkOhm4reEC9LtzHVUCN/IqqNRl1iQ9xE20= -google.golang.org/api v0.123.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.130.0 h1:A50ujooa1h9iizvfzA4rrJr2B7uRmWexwbekQ2+5FPQ= +google.golang.org/api v0.130.0/go.mod h1:J/LCJMYSDFvAVREGCbrESb53n4++NMBDetSHGL5I5RY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1566,8 +1571,12 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= +google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= +google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1603,8 +1612,8 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= +google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1621,8 +1630,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/scep/authority.go b/scep/authority.go index 5e02468d..19a36c3a 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -130,7 +130,7 @@ func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, e // Some clients do need the root certificate however; also see: https://github.com/openxpki/openxpki/issues/73 // // In case a provisioner specific decrypter is available, this is used as the "SCEP Server (RA)" certificate -// instead of the CA intermediate directly. This uses a distinct instance of a KMS for doing the SCEp key +// instead of the CA intermediate directly. This uses a distinct instance of a KMS for doing the SCEP key // operations, so that RSA can be used for just SCEP. // // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in @@ -354,8 +354,12 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // as the first certificate in the array signedData.AddCertificate(cert) - authCert := a.signerCertificate - signer := a.signer + // authCert := a.signerCertificate + // signer := a.signer + + sc, sr := p.GetSigner() + authCert := sc + signer := sr // sign the attributes if err := signedData.AddSigner(authCert, signer, config); err != nil { @@ -386,7 +390,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m } // CreateFailureResponse creates an appropriately signed reply for PKI operations -func (a *Authority) CreateFailureResponse(_ context.Context, _ *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) { +func (a *Authority) CreateFailureResponse(ctx context.Context, _ *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) { config := pkcs7.SignerInfoConfig{ ExtraSignedAttributes: []pkcs7.Attribute{ { @@ -425,8 +429,17 @@ func (a *Authority) CreateFailureResponse(_ context.Context, _ *x509.Certificate return nil, err } + p := provisionerFromContext(ctx) + + // authCert := a.signerCertificate + // signer := a.signer + + sc, sr := p.GetSigner() + authCert := sc + signer := sr + // sign the attributes - if err := signedData.AddSigner(a.signerCertificate, a.signer, config); err != nil { + if err := signedData.AddSigner(authCert, signer, config); err != nil { return nil, err } diff --git a/scep/provisioner.go b/scep/provisioner.go index e9b9c30f..f8fd46f1 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -17,6 +17,7 @@ type Provisioner interface { GetCapabilities() []string ShouldIncludeRootInChain() bool GetDecrypter() (*x509.Certificate, crypto.Decrypter) + GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int ValidateChallenge(ctx context.Context, challenge, transactionID string) error } From 59b7419dcfd115366b35df647ce78399fbce89bb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 2 Aug 2023 15:49:32 +0200 Subject: [PATCH 08/39] Rely on latest `linkedca` commit with `SCEPDecrypter` support --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 53f00d1b..781d0ef4 100644 --- a/go.mod +++ b/go.mod @@ -145,4 +145,4 @@ require ( replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230615175518-7ce6486b74eb // temporary replace until https://github.com/smallstep/linkedca/pull/55 is merged -replace go.step.sm/linkedca => ./../linkedca +replace go.step.sm/linkedca => go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d diff --git a/go.sum b/go.sum index a6c485cb..d45d1908 100644 --- a/go.sum +++ b/go.sum @@ -1065,6 +1065,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.33.0 h1:fP8awo6YkZ0/rrLhzbHYA3U8g24VnWEebZRnGwUobRo= go.step.sm/crypto v0.33.0/go.mod h1:rMETKeIA1ZsLBiKT6phQ2IIeBH3GL+XqimeobcqUw1g= +go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d h1:hzGucxw/NM3IyAVcLw5z9Z5VtHvXydjFY02BosVLtEk= +go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= From 7163c4f95f01dde54680d5c523608674a2c79d4f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 2 Aug 2023 16:01:58 +0200 Subject: [PATCH 09/39] Add helper for getting the appropriate SCEP response signer --- scep/authority.go | 71 ++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/scep/authority.go b/scep/authority.go index 19a36c3a..b0a5420a 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -210,21 +210,6 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return nil } -func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { - p := provisionerFromContext(ctx) - - // return provisioner specific decrypter, if available - if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { - return - } - - // fallback to the CA wide decrypter - cert = a.signerCertificate - pkey = a.defaultDecrypter - - return -} - // SignCSR creates an x509.Certificate based on a CSR template and Cert Authority credentials // returns a new PKIMessage with CertRep data func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) { @@ -354,15 +339,13 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // as the first certificate in the array signedData.AddCertificate(cert) - // authCert := a.signerCertificate - // signer := a.signer - - sc, sr := p.GetSigner() - authCert := sc - signer := sr + signerCert, signer, err := a.selectSigner(ctx) + if err != nil { + return nil, fmt.Errorf("failed selecting signer: %w", err) + } // sign the attributes - if err := signedData.AddSigner(authCert, signer, config); err != nil { + if err := signedData.AddSigner(signerCert, signer, config); err != nil { return nil, err } @@ -429,17 +412,13 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, _ *x509.Certifica return nil, err } - p := provisionerFromContext(ctx) - - // authCert := a.signerCertificate - // signer := a.signer - - sc, sr := p.GetSigner() - authCert := sc - signer := sr + signerCert, signer, err := a.selectSigner(ctx) + if err != nil { + return nil, fmt.Errorf("failed selecting signer: %w", err) + } // sign the attributes - if err := signedData.AddSigner(authCert, signer, config); err != nil { + if err := signedData.AddSigner(signerCert, signer, config); err != nil { return nil, err } @@ -487,3 +466,33 @@ func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactio p := provisionerFromContext(ctx) return p.ValidateChallenge(ctx, challenge, transactionID) } + +func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { + p := provisionerFromContext(ctx) + + // return provisioner specific decrypter, if available + if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { + return + } + + // fallback to the CA wide decrypter + cert = a.signerCertificate + pkey = a.defaultDecrypter + + return +} + +func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { + p := provisionerFromContext(ctx) + + // return provisioner specific decrypter, if available + if cert, pkey = p.GetSigner(); cert != nil && pkey != nil { + return + } + + // fallback to the CA wide signer + cert = a.signerCertificate + pkey = a.defaultDecrypter + + return +} From fc1fb51854d7677ac14444527da1eedcd1a66043 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Wed, 2 Aug 2023 18:26:50 +0200 Subject: [PATCH 10/39] Improve SCEP authority initialization and reload --- authority/authority.go | 54 ++++++++++++++++++++++++++++++++---------- ca/ca.go | 14 ++--------- scep/authority.go | 17 +++++++++++++ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 4318246b..e6044e4d 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -262,13 +262,22 @@ func (a *Authority) ReloadAdminResources(ctx context.Context) error { a.config.AuthorityConfig.Admins = adminList a.admins = adminClxn - // update the SCEP Authority with the currently active SCEP - // provisioner names and revalidate the configuration. - if a.scepAuthority != nil { + switch { + case a.requiresSCEP() && a.GetSCEP() == nil: + // TODO(hs): try to initialize SCEP here too? It's a bit + // problematic if this method is called as part of an update + // via Admin API and a password needs to be provided. + case a.requiresSCEP() && a.GetSCEP() != nil: + // update the SCEP Authority with the currently active SCEP + // provisioner names and revalidate the configuration. a.scepAuthority.UpdateProvisioners(a.getSCEPProvisionerNames()) if err := a.scepAuthority.Validate(); err != nil { log.Printf("failed validating SCEP authority: %v\n", err) } + case !a.requiresSCEP() && a.GetSCEP() != nil: + // TODO(hs): don't remove the authority if we can't also + // reload it. + //a.scepAuthority = nil } return nil @@ -651,14 +660,17 @@ func (a *Authority) init() error { } // The SCEP functionality is provided through an instance of - // scep.Authority. It is initialized once when the CA is started. - // TODO(hs): should the SCEP Authority support reloading? For example, - // when the admin resources are reloaded, specifically the provisioners, - // it can happen that the SCEP Authority is no longer required and can - // be destroyed, or that it needs to be instantiated. It may also need - // to be revalidated, because not all SCEP provisioner may have a - // valid decrypter available. - if a.requiresSCEP() && a.GetSCEP() == nil { + // scep.Authority. It is initialized when the CA is started and + // if it doesn't exist yet. It gets refreshed if it already + // exists. If the SCEP authority is no longer required on reload, + // it gets removed. + // TODO(hs): reloading through SIGHUP doesn't hit these cases. This + // is because an entirely new authority.Authority is created, including + // a new scep.Authority. Look into this to see if we want this to + // keep working like that, or want to reuse a single instance and + // update that. + switch { + case a.requiresSCEP() && a.GetSCEP() == nil: var options scep.Options options.Roots = a.rootX509Certs options.Intermediates, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) @@ -698,15 +710,28 @@ func (a *Authority) init() error { options.SCEPProvisionerNames = a.getSCEPProvisionerNames() // create a new SCEP authority - a.scepAuthority, err = scep.New(a, options) + scepAuthority, err := scep.New(a, options) if err != nil { return err } // validate the SCEP authority - if err := a.scepAuthority.Validate(); err != nil { + if err := scepAuthority.Validate(); err != nil { a.initLogf("failed validating SCEP authority: %v", err) } + + // set the SCEP authority + a.scepAuthority = scepAuthority + case !a.requiresSCEP() && a.GetSCEP() != nil: + // clear the SCEP authority if it's no longer required + a.scepAuthority = nil + case a.requiresSCEP() && a.GetSCEP() != nil: + // update the SCEP Authority with the currently active SCEP + // provisioner names and revalidate the configuration. + a.scepAuthority.UpdateProvisioners(a.getSCEPProvisionerNames()) + if err := a.scepAuthority.Validate(); err != nil { + log.Printf("failed validating SCEP authority: %v\n", err) + } } // Load X509 constraints engine. @@ -869,12 +894,15 @@ func (a *Authority) requiresSCEP() bool { return false } +// getSCEPProvisionerNames returns the names of the SCEP provisioners +// that are currently available in the CA. func (a *Authority) getSCEPProvisionerNames() (names []string) { for _, p := range a.config.AuthorityConfig.Provisioners { if p.GetType() == provisioner.TypeSCEP { names = append(names, p.GetName()) } } + return } diff --git a/ca/ca.go b/ca/ca.go index c13496a6..911cb2d7 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -250,19 +250,9 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { var scepAuthority *scep.Authority if ca.shouldServeSCEPEndpoints() { - // validate the SCEP authority configuration. Currently this - // will not result in a failure to start if one or more SCEP - // provisioners are not correctly configured. Only a log will - // be emitted. + // get the SCEP authority configuration. Validation is + // performed within the authority instantiation process. scepAuthority = auth.GetSCEP() - if err := scepAuthority.Validate(); err != nil { - err = errors.Wrap(err, "failed validating SCEP authority") - shouldFail := false - if shouldFail { - return nil, err - } - log.Println(err) - } // According to the RFC (https://tools.ietf.org/html/rfc8894#section-7.10), // SCEP operations are performed using HTTP, so that's why the API is mounted diff --git a/scep/authority.go b/scep/authority.go index b0a5420a..60141191 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "errors" "fmt" + "sync" microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util" microscep "github.com/micromdm/scep/v2/scep" @@ -25,6 +26,8 @@ type Authority struct { signer crypto.Signer defaultDecrypter crypto.Decrypter scepProvisionerNames []string + + mu sync.RWMutex } type authorityKey struct{} @@ -77,6 +80,13 @@ func New(signAuth SignAuthority, opts Options) (*Authority, error) { // The validation includes a check if a decrypter is available, either // an authority wide decrypter, or a provisioner specific decrypter. func (a *Authority) Validate() error { + if a == nil { + return nil + } + + a.mu.RLock() + defer a.mu.RUnlock() + noDefaultDecrypterAvailable := a.defaultDecrypter == nil for _, name := range a.scepProvisionerNames { p, err := a.LoadProvisionerByName(name) @@ -102,6 +112,13 @@ func (a *Authority) Validate() error { // current SCEP provisioners configured. This allows the Authority to be // validated with the latest data. func (a *Authority) UpdateProvisioners(scepProvisionerNames []string) { + if a == nil { + return + } + + a.mu.Lock() + defer a.mu.Unlock() + a.scepProvisionerNames = scepProvisionerNames } From 0f35bb1af5c8cf3f627c9b79ad37259bde4e8172 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Aug 2023 15:21:06 +0200 Subject: [PATCH 11/39] Defer missing decrypter/signer configuration errors to SCEP authority --- scep/authority.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scep/authority.go b/scep/authority.go index 60141191..5fd7223b 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -23,7 +23,7 @@ type Authority struct { roots []*x509.Certificate intermediates []*x509.Certificate signerCertificate *x509.Certificate - signer crypto.Signer + defaultSigner crypto.Signer defaultDecrypter crypto.Decrypter scepProvisionerNames []string @@ -69,7 +69,7 @@ func New(signAuth SignAuthority, opts Options) (*Authority, error) { roots: opts.Roots, intermediates: opts.Intermediates, signerCertificate: opts.SignerCert, - signer: opts.Signer, + defaultSigner: opts.Signer, defaultDecrypter: opts.Decrypter, scepProvisionerNames: opts.SCEPProvisionerNames, } @@ -484,7 +484,7 @@ func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactio return p.ValidateChallenge(ctx, challenge, transactionID) } -func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { +func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.Decrypter, err error) { p := provisionerFromContext(ctx) // return provisioner specific decrypter, if available @@ -492,24 +492,34 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate return } - // fallback to the CA wide decrypter + // fallback to the CA wide RSA decrypter, which is the + // intermediate CA. cert = a.signerCertificate pkey = a.defaultDecrypter + if cert == nil || pkey == nil { + return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName()) + } + return } func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { p := provisionerFromContext(ctx) - // return provisioner specific decrypter, if available + // return provisioner specific signer, if available if cert, pkey = p.GetSigner(); cert != nil && pkey != nil { return } - // fallback to the CA wide signer + // fallback to the CA wide RSA signer, which is the + // intermediate CA. cert = a.signerCertificate - pkey = a.defaultDecrypter + pkey = a.defaultSigner + + if cert == nil || pkey == nil { + return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName()) + } return } From 88ed900dc39d4e2660ff57a10c1ddb8cf15b4622 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Aug 2023 15:37:18 +0200 Subject: [PATCH 12/39] Rely on the latest linkedca --- go.mod | 13 +++++-------- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 781d0ef4..3e0688fb 100644 --- a/go.mod +++ b/go.mod @@ -32,10 +32,10 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.33.0 - go.step.sm/linkedca v0.20.0 + go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 - golang.org/x/net v0.12.0 + golang.org/x/net v0.13.0 google.golang.org/api v0.134.0 google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 @@ -46,7 +46,7 @@ require ( cloud.google.com/go v0.110.4 // indirect cloud.google.com/go/compute v1.20.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.0 // indirect + cloud.google.com/go/iam v1.1.1 // indirect cloud.google.com/go/kms v1.15.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect @@ -135,14 +135,11 @@ require ( golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect + google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) // use github.com/smallstep/pkcs7 fork with patches applied replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230615175518-7ce6486b74eb - -// temporary replace until https://github.com/smallstep/linkedca/pull/55 is merged -replace go.step.sm/linkedca => go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d diff --git a/go.sum b/go.sum index d45d1908..b13493d0 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs= cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= @@ -1065,8 +1065,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.33.0 h1:fP8awo6YkZ0/rrLhzbHYA3U8g24VnWEebZRnGwUobRo= go.step.sm/crypto v0.33.0/go.mod h1:rMETKeIA1ZsLBiKT6phQ2IIeBH3GL+XqimeobcqUw1g= -go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d h1:hzGucxw/NM3IyAVcLw5z9Z5VtHvXydjFY02BosVLtEk= -go.step.sm/linkedca v0.0.0-20230802134415-b577c7565f6d/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= +go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d h1:PbcfXsW0Jc8a5LvvzqT3pyxiLBkU9LgAO/JpYjIZbTE= +go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1212,8 +1212,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1568,12 +1568,12 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU= google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 h1:Z8qdAF9GFsmcUuWQ5KVYIpP3PCKydn/YKORnghIalu4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= From c0a1837cd9d31f9db253befabfe751061a1abfd4 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Aug 2023 16:09:51 +0200 Subject: [PATCH 13/39] Verify full decrypter/signer configuration at usage time When changing the SCEP configuration it is possible that one or both of the decrypter configurations required are not available or have been provided in a way that's not usable for actual SCEP requests. Instead of failing hard when provisioners are loaded, which could result in the CA not starting properly, this type of problematic configuration errors will now be handled at usage time instead. --- authority/provisioner/scep.go | 48 ++++++++++++++++++++--------------- scep/authority.go | 46 ++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 7bd213c6..626d26ab 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -3,6 +3,7 @@ package provisioner import ( "context" "crypto" + "crypto/rsa" "crypto/subtle" "crypto/x509" "encoding/pem" @@ -177,23 +178,23 @@ func (s *SCEP) Init(config Config) (err error) { if s.MinimumPublicKeyLength == 0 { s.MinimumPublicKeyLength = 2048 } - if s.MinimumPublicKeyLength%8 != 0 { return errors.Errorf("%d bits is not exactly divisible by 8", s.MinimumPublicKeyLength) } + // Set the encryption algorithm to use s.encryptionAlgorithm = s.EncryptionAlgorithmIdentifier // TODO(hs): we might want to upgrade the default security to AES-CBC? if s.encryptionAlgorithm < 0 || s.encryptionAlgorithm > 4 { return errors.New("only encryption algorithm identifiers from 0 to 4 are valid") } + // Prepare the SCEP challenge validator s.challengeValidationController = newChallengeValidationController( config.WebhookClient, s.GetOptions().GetWebhooks(), ) - skip := false // TODO(hs): remove this; currently a helper for debugging - if decryptionKey := s.DecrypterKey; decryptionKey != "" && !skip { + if decryptionKey := s.DecrypterKey; decryptionKey != "" { u, err := uri.Parse(s.DecrypterKey) if err != nil { return fmt.Errorf("failed parsing decrypter key: %w", err) @@ -226,7 +227,7 @@ func (s *SCEP) Init(config Config) (err error) { return fmt.Errorf("failed creating decrypter: %w", err) } if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: decryptionKey, // TODO(hs): support distinct signer key + SigningKey: decryptionKey, // TODO(hs): support distinct signer key in the future? Password: []byte(s.DecrypterKeyPassword), }); err != nil { return fmt.Errorf("failed creating signer: %w", err) @@ -248,23 +249,19 @@ func (s *SCEP) Init(config Config) (err error) { } // TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager - // and load the certificate corresponding to the decryption key. - - // final validation for the decrypter - if s.decrypter != nil { - // // TODO(hs): enable this validation again - // if s.decrypterCertificate == nil { - // // TODO: don't hard skip at init? - // return fmt.Errorf("no decrypter certificate available for decrypter in %q", s.Name) - // } - // // validate the decrypter key - // decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) - // if !ok { - // return fmt.Errorf("only RSA keys are supported") - // } - // if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { - // return errors.New("mismatch between decryption certificate and decrypter public keys") - // } + // and load the certificate corresponding to the decryption key? + + // Final validation for the decrypter. If both the decrypter and the certificate + // are available, the public keys must match. We currently allow the decrypter to + // be set without a certificate without warning the user, but + if s.decrypter != nil && s.decrypterCertificate != nil { + decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) + if !ok { + return fmt.Errorf("only RSA keys are supported") + } + if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { + return errors.New("mismatch between decryption certificate and decrypter public keys") + } } // TODO: add other, SCEP specific, options? @@ -350,10 +347,19 @@ func (s *SCEP) selectValidationMethod() validationMethod { return validationMethodNone } +// GetDecrypter returns the provisioner specific decrypter, +// used to decrypt SCEP request messages sent by a SCEP client. +// The decrypter consists of a crypto.Decrypter (a private key) +// and a certificate for the public key corresponding to the +// private key. func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) { return s.decrypterCertificate, s.decrypter } +// GetSigner returns the provisioner specific signer, used to +// sign SCEP response messages for the client. The signer consists +// of a crypto.Signer and a certificate for the public key +// corresponding to the private key. func (s *SCEP) GetSigner() (*x509.Certificate, crypto.Signer) { return s.decrypterCertificate, s.signer } diff --git a/scep/authority.go b/scep/authority.go index 5fd7223b..c839ab99 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -486,19 +486,22 @@ func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactio func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.Decrypter, err error) { p := provisionerFromContext(ctx) - - // return provisioner specific decrypter, if available - if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { + cert, pkey = p.GetDecrypter() + switch { + case cert != nil && pkey != nil: return + case cert == nil && pkey != nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter certificate available", p.GetName()) + case cert != nil && pkey == nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName()) } - // fallback to the CA wide RSA decrypter, which is the - // intermediate CA. - cert = a.signerCertificate - pkey = a.defaultDecrypter - - if cert == nil || pkey == nil { - return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName()) + cert, pkey = a.signerCertificate, a.defaultDecrypter + switch { + case cert == nil && pkey != nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter certificate available", p.GetName()) + case cert != nil && pkey == nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter available", p.GetName()) } return @@ -506,19 +509,22 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { p := provisionerFromContext(ctx) - - // return provisioner specific signer, if available - if cert, pkey = p.GetSigner(); cert != nil && pkey != nil { + cert, pkey = p.GetSigner() + switch { + case cert != nil && pkey != nil: return + case cert == nil && pkey != nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a signer certificate available", p.GetName()) + case cert != nil && pkey == nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName()) } - // fallback to the CA wide RSA signer, which is the - // intermediate CA. - cert = a.signerCertificate - pkey = a.defaultSigner - - if cert == nil || pkey == nil { - return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName()) + cert, pkey = a.signerCertificate, a.defaultSigner + switch { + case cert == nil && pkey != nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a default signer certificate available", p.GetName()) + case cert != nil && pkey == nil: + return nil, nil, fmt.Errorf("provisioner %q does not have a default signer available", p.GetName()) } return From d754000a684f595972c2b61812b52588dd10c341 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Aug 2023 16:20:04 +0200 Subject: [PATCH 14/39] Fix SCEP provisioner API test --- api/api_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/api_test.go b/api/api_test.go index 1c90d91b..f9848331 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -1593,6 +1593,9 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { ChallengePassword: "not-so-secret", MinimumPublicKeyLength: 2048, EncryptionAlgorithmIdentifier: 2, + DecrypterCertificate: []byte{1, 2, 3, 4}, + DecrypterKey: "softkms:path=/path/to/private.key", + DecrypterKeyPassword: "super-secret-password", }, &provisioner.JWK{ EncryptedKey: "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlhOdmYxQjgxSUlLMFA2NUkwcmtGTGcifQ.XaN9zcPQeWt49zchUDm34FECUTHfQTn_.tmNHPQDqR3ebsWfd.9WZr3YVdeOyJh36vvx0VlRtluhvYp4K7jJ1KGDr1qypwZ3ziBVSNbYYQ71du7fTtrnfG1wgGTVR39tWSzBU-zwQ5hdV3rpMAaEbod5zeW6SHd95H3Bvcb43YiiqJFNL5sGZzFb7FqzVmpsZ1efiv6sZaGDHtnCAL6r12UG5EZuqGfM0jGCZitUz2m9TUKXJL5DJ7MOYbFfkCEsUBPDm_TInliSVn2kMJhFa0VOe5wZk5YOuYM3lNYW64HGtbf-llN2Xk-4O9TfeSPizBx9ZqGpeu8pz13efUDT2WL9tWo6-0UE-CrG0bScm8lFTncTkHcu49_a5NaUBkYlBjEiw.thPcx3t1AUcWuEygXIY3Fg", @@ -1610,6 +1613,9 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { "type": "scep", "name": "scep", "challenge": "*** REDACTED ***", + "decrypterCertificate": "KioqIFJFREFDVEVEICoqKg==", // base64 representation of "*** REDACTED ***"" + "decrypterKey": "*** REDACTED ***", + "decrypterKeyPassword": "*** REDACTED ***", "minimumPublicKeyLength": 2048, "encryptionAlgorithmIdentifier": 2, }, @@ -1646,6 +1652,9 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { ChallengePassword: "not-so-secret", MinimumPublicKeyLength: 2048, EncryptionAlgorithmIdentifier: 2, + DecrypterCertificate: []byte{1, 2, 3, 4}, + DecrypterKey: "softkms:path=/path/to/private.key", + DecrypterKeyPassword: "super-secret-password", }, &provisioner.JWK{ EncryptedKey: "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlhOdmYxQjgxSUlLMFA2NUkwcmtGTGcifQ.XaN9zcPQeWt49zchUDm34FECUTHfQTn_.tmNHPQDqR3ebsWfd.9WZr3YVdeOyJh36vvx0VlRtluhvYp4K7jJ1KGDr1qypwZ3ziBVSNbYYQ71du7fTtrnfG1wgGTVR39tWSzBU-zwQ5hdV3rpMAaEbod5zeW6SHd95H3Bvcb43YiiqJFNL5sGZzFb7FqzVmpsZ1efiv6sZaGDHtnCAL6r12UG5EZuqGfM0jGCZitUz2m9TUKXJL5DJ7MOYbFfkCEsUBPDm_TInliSVn2kMJhFa0VOe5wZk5YOuYM3lNYW64HGtbf-llN2Xk-4O9TfeSPizBx9ZqGpeu8pz13efUDT2WL9tWo6-0UE-CrG0bScm8lFTncTkHcu49_a5NaUBkYlBjEiw.thPcx3t1AUcWuEygXIY3Fg", From 4186b2c2d078e197fe5e7834993083e46d351ce6 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 3 Aug 2023 17:21:50 +0200 Subject: [PATCH 15/39] Change JSON marshaling for SCEP provisioners Instead of the old method that redacted sensitive information by overriding the value of the property and changing it back to the original, the API now uses a model specifically meant for API responses. This prevents potential race conditions. This may be iterated on a bit so that we don't need to rely on the [provisioner.Interface] interface, which requires the API model to implement unnecessary methods. --- api/api.go | 45 ++++++++++-------- api/api_test.go | 2 +- api/models/scep.go | 116 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 api/models/scep.go diff --git a/api/api.go b/api/api.go index b91aef97..6a3e348e 100644 --- a/api/api.go +++ b/api/api.go @@ -25,6 +25,7 @@ import ( "golang.org/x/crypto/ssh" "github.com/smallstep/certificates/api/log" + "github.com/smallstep/certificates/api/models" "github.com/smallstep/certificates/api/render" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/config" @@ -231,6 +232,27 @@ type ProvisionersResponse struct { NextCursor string } +const redacted = "*** REDACTED ***" + +func scepFromProvisioner(p *provisioner.SCEP) *models.SCEP { + return &models.SCEP{ + ID: p.ID, + Type: p.Type, + Name: p.Name, + ForceCN: p.ForceCN, + ChallengePassword: redacted, + Capabilities: p.Capabilities, + IncludeRoot: p.IncludeRoot, + MinimumPublicKeyLength: p.MinimumPublicKeyLength, + DecrypterCertificate: redacted, + DecrypterKey: redacted, + DecrypterKeyPassword: redacted, + EncryptionAlgorithmIdentifier: p.EncryptionAlgorithmIdentifier, + Options: p.Options, + Claims: p.Claims, + } +} + // MarshalJSON implements json.Marshaler. It marshals the ProvisionersResponse // into a byte slice. // @@ -238,37 +260,22 @@ type ProvisionersResponse struct { // challenge secret that MUST NOT be leaked in (public) HTTP responses. The // challenge value is thus redacted in HTTP responses. func (p ProvisionersResponse) MarshalJSON() ([]byte, error) { + var responseProvisioners provisioner.List for _, item := range p.Provisioners { scepProv, ok := item.(*provisioner.SCEP) if !ok { + responseProvisioners = append(responseProvisioners, item) continue } - type old struct { - challengePassword string - decrypterCertificate []byte - decrypterKey string - decrypterKeyPassword string - } - o := old{scepProv.ChallengePassword, scepProv.DecrypterCertificate, scepProv.DecrypterKey, scepProv.DecrypterKeyPassword} - scepProv.ChallengePassword = "*** REDACTED ***" - scepProv.DecrypterCertificate = []byte("*** REDACTED ***") - scepProv.DecrypterKey = "*** REDACTED ***" - scepProv.DecrypterKeyPassword = "*** REDACTED ***" - - defer func(o old) { //nolint:gocritic // defer in loop required to restore initial state of provisioners - scepProv.ChallengePassword = o.challengePassword - scepProv.DecrypterCertificate = o.decrypterCertificate - scepProv.DecrypterKey = o.decrypterKey - scepProv.DecrypterKeyPassword = o.decrypterKeyPassword - }(o) + responseProvisioners = append(responseProvisioners, scepFromProvisioner(scepProv)) } var list = struct { Provisioners []provisioner.Interface `json:"provisioners"` NextCursor string `json:"nextCursor"` }{ - Provisioners: []provisioner.Interface(p.Provisioners), + Provisioners: []provisioner.Interface(responseProvisioners), NextCursor: p.NextCursor, } diff --git a/api/api_test.go b/api/api_test.go index f9848331..a1ebf532 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -1613,7 +1613,7 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { "type": "scep", "name": "scep", "challenge": "*** REDACTED ***", - "decrypterCertificate": "KioqIFJFREFDVEVEICoqKg==", // base64 representation of "*** REDACTED ***"" + "decrypterCertificate": "*** REDACTED ***", "decrypterKey": "*** REDACTED ***", "decrypterKeyPassword": "*** REDACTED ***", "minimumPublicKeyLength": 2048, diff --git a/api/models/scep.go b/api/models/scep.go new file mode 100644 index 00000000..b11ca996 --- /dev/null +++ b/api/models/scep.go @@ -0,0 +1,116 @@ +package models + +import ( + "context" + "crypto/x509" + "errors" + + "github.com/smallstep/certificates/authority/provisioner" + "golang.org/x/crypto/ssh" +) + +var errDummyImplementation = errors.New("dummy implementation") + +// SCEP is the SCEP provisioner model used solely in CA API +// responses. All methods for the [provisioner.Interface] interface +// are implemented, but return a dummy error. +// TODO(hs): remove reliance on the interface for the API responses +type SCEP struct { + ID string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + ForceCN bool `json:"forceCN,omitempty"` + ChallengePassword string `json:"challenge,omitempty"` + Capabilities []string `json:"capabilities,omitempty"` + IncludeRoot bool `json:"includeRoot,omitempty"` + MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` + DecrypterCertificate string `json:"decrypterCertificate"` + DecrypterKey string `json:"decrypterKey"` + DecrypterKeyPassword string `json:"decrypterKeyPassword"` + EncryptionAlgorithmIdentifier int `json:"encryptionAlgorithmIdentifier,omitempty"` + Options *provisioner.Options `json:"options,omitempty"` + Claims *provisioner.Claims `json:"claims,omitempty"` +} + +// GetID returns the provisioner unique identifier. +func (s *SCEP) GetID() string { + if s.ID != "" { + return s.ID + } + return s.GetIDForToken() +} + +// GetIDForToken returns an identifier that will be used to load the provisioner +// from a token. +func (s *SCEP) GetIDForToken() string { + return "scep/" + s.Name +} + +// GetName returns the name of the provisioner. +func (s *SCEP) GetName() string { + return s.Name +} + +// GetType returns the type of provisioner. +func (s *SCEP) GetType() provisioner.Type { + return provisioner.TypeSCEP +} + +// GetEncryptedKey returns the base provisioner encrypted key if it's defined. +func (s *SCEP) GetEncryptedKey() (string, string, bool) { + return "", "", false +} + +// GetTokenID returns the identifier of the token. +func (s *SCEP) GetTokenID(string) (string, error) { + return "", errDummyImplementation +} + +// Init initializes and validates the fields of a SCEP type. +func (s *SCEP) Init(config provisioner.Config) (err error) { + return errDummyImplementation +} + +// AuthorizeSign returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for signing x509 Certificates. +func (s *SCEP) AuthorizeSign(context.Context, string) ([]provisioner.SignOption, error) { + return nil, errDummyImplementation +} + +// AuthorizeRevoke returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for revoking x509 Certificates. +func (s *SCEP) AuthorizeRevoke(context.Context, string) error { + return errDummyImplementation +} + +// AuthorizeRenew returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for renewing x509 Certificates. +func (s *SCEP) AuthorizeRenew(context.Context, *x509.Certificate) error { + return errDummyImplementation +} + +// AuthorizeSSHSign returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for signing SSH Certificates. +func (s *SCEP) AuthorizeSSHSign(context.Context, string) ([]provisioner.SignOption, error) { + return nil, errDummyImplementation +} + +// AuthorizeRevoke returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for revoking SSH Certificates. +func (s *SCEP) AuthorizeSSHRevoke(context.Context, string) error { + return errDummyImplementation +} + +// AuthorizeSSHRenew returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for renewing SSH Certificates. +func (s *SCEP) AuthorizeSSHRenew(context.Context, string) (*ssh.Certificate, error) { + return nil, errDummyImplementation +} + +// AuthorizeSSHRekey returns an unimplemented error. Provisioners should overwrite +// this method if they will support authorizing tokens for rekeying SSH Certificates. +func (s *SCEP) AuthorizeSSHRekey(context.Context, string) (*ssh.Certificate, []provisioner.SignOption, error) { + return nil, nil, errDummyImplementation +} + +var _ provisioner.Interface = (*SCEP)(nil) From e2e9bf5494c3f144710b412c2864284bc9533d15 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Aug 2023 01:55:52 +0200 Subject: [PATCH 16/39] Clarify some SCEP properties --- authority/authority.go | 1 + authority/provisioner/scep.go | 5 +++- scep/authority.go | 50 ++++++++++++++++++----------------- scep/options.go | 2 ++ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index e6044e4d..f9c58ba6 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -702,6 +702,7 @@ func (a *Authority) init() error { // only pass the decrypter down when it was successfully created, // meaning it's an RSA key, and `CreateDecrypter` did not fail. options.Decrypter = decrypter + options.DecrypterCert = options.Intermediates[0] } } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 626d26ab..396df8e4 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -57,6 +57,7 @@ type SCEP struct { decrypter crypto.Decrypter decrypterCertificate *x509.Certificate signer crypto.Signer + signerCertificate *x509.Certificate } // GetID returns the provisioner unique identifier. @@ -246,6 +247,8 @@ func (s *SCEP) Init(config Config) (err error) { if s.decrypterCertificate, err = x509.ParseCertificate(block.Bytes); err != nil { return fmt.Errorf("failed parsing decrypter certificate: %w", err) } + // the decrypter certificate is also the signer certificate + s.signerCertificate = s.decrypterCertificate } // TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager @@ -361,5 +364,5 @@ func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) { // of a crypto.Signer and a certificate for the public key // corresponding to the private key. func (s *SCEP) GetSigner() (*x509.Certificate, crypto.Signer) { - return s.decrypterCertificate, s.signer + return s.signerCertificate, s.signer } diff --git a/scep/authority.go b/scep/authority.go index c839ab99..d4ca37f2 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -22,9 +22,10 @@ type Authority struct { signAuth SignAuthority roots []*x509.Certificate intermediates []*x509.Certificate - signerCertificate *x509.Certificate defaultSigner crypto.Signer + signerCertificate *x509.Certificate defaultDecrypter crypto.Decrypter + decrypterCertificate *x509.Certificate scepProvisionerNames []string mu sync.RWMutex @@ -64,16 +65,17 @@ func New(signAuth SignAuthority, opts Options) (*Authority, error) { if err := opts.Validate(); err != nil { return nil, err } - authority := &Authority{ + + return &Authority{ signAuth: signAuth, // TODO: provide signAuth through context instead? roots: opts.Roots, intermediates: opts.Intermediates, - signerCertificate: opts.SignerCert, defaultSigner: opts.Signer, + signerCertificate: opts.SignerCert, defaultDecrypter: opts.Decrypter, + decrypterCertificate: opts.SignerCert, // the intermediate signer cert is also the decrypter cert (if RSA) scepProvisionerNames: opts.SCEPProvisionerNames, - } - return authority, nil + }, nil } // Validate validates if the SCEP Authority has a valid configuration. @@ -181,12 +183,12 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return fmt.Errorf("error parsing pkcs7 content: %w", err) } - cert, pkey, err := a.selectDecrypter(ctx) + cert, decrypter, err := a.selectDecrypter(ctx) if err != nil { return fmt.Errorf("failed selecting decrypter: %w", err) } - envelope, err := p7c.Decrypt(cert, pkey) + envelope, err := p7c.Decrypt(cert, decrypter) if err != nil { return fmt.Errorf("error decrypting encrypted pkcs7 content: %w", err) } @@ -209,7 +211,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err if err := csr.CheckSignature(); err != nil { return fmt.Errorf("invalid CSR signature; %w", err) } - // check for challengePassword + // extract the challenge password cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) if err != nil { return fmt.Errorf("parse challenge password in pkiEnvelope: %w", err) @@ -484,46 +486,46 @@ func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactio return p.ValidateChallenge(ctx, challenge, transactionID) } -func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.Decrypter, err error) { +func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, decrypter crypto.Decrypter, err error) { p := provisionerFromContext(ctx) - cert, pkey = p.GetDecrypter() + cert, decrypter = p.GetDecrypter() switch { - case cert != nil && pkey != nil: + case cert != nil && decrypter != nil: return - case cert == nil && pkey != nil: + case cert == nil && decrypter != nil: return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter certificate available", p.GetName()) - case cert != nil && pkey == nil: + case cert != nil && decrypter == nil: return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName()) } - cert, pkey = a.signerCertificate, a.defaultDecrypter + cert, decrypter = a.decrypterCertificate, a.defaultDecrypter switch { - case cert == nil && pkey != nil: + case cert == nil && decrypter != nil: return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter certificate available", p.GetName()) - case cert != nil && pkey == nil: + case cert != nil && decrypter == nil: return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter available", p.GetName()) } return } -func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { +func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, signer crypto.Signer, err error) { p := provisionerFromContext(ctx) - cert, pkey = p.GetSigner() + cert, signer = p.GetSigner() switch { - case cert != nil && pkey != nil: + case cert != nil && signer != nil: return - case cert == nil && pkey != nil: + case cert == nil && signer != nil: return nil, nil, fmt.Errorf("provisioner %q does not have a signer certificate available", p.GetName()) - case cert != nil && pkey == nil: + case cert != nil && signer == nil: return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName()) } - cert, pkey = a.signerCertificate, a.defaultSigner + cert, signer = a.signerCertificate, a.defaultSigner switch { - case cert == nil && pkey != nil: + case cert == nil && signer != nil: return nil, nil, fmt.Errorf("provisioner %q does not have a default signer certificate available", p.GetName()) - case cert != nil && pkey == nil: + case cert != nil && signer == nil: return nil, nil, fmt.Errorf("provisioner %q does not have a default signer available", p.GetName()) } diff --git a/scep/options.go b/scep/options.go index 7ba7cfc2..8bc30a61 100644 --- a/scep/options.go +++ b/scep/options.go @@ -20,6 +20,8 @@ type Options struct { Signer crypto.Signer `json:"-"` // Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property. Decrypter crypto.Decrypter `json:"-"` + // DecrypterCert points to the certificate of the CA decrypter. + DecrypterCert *x509.Certificate `json:"-"` // SCEPProvisionerNames contains the currently configured SCEP provioner names. These // are used to be able to load the provisioners when the SCEP authority is being // validated. From 0d09f3e2025e1b01dc831dbf6f87f1e26fe217de Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Aug 2023 12:14:29 +0200 Subject: [PATCH 17/39] Prevent data races with multiple PKCS7 encryption operations --- scep/authority.go | 46 +++++++++++++++++--------- scep/authority_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 scep/authority_test.go diff --git a/scep/authority.go b/scep/authority.go index d4ca37f2..8f270c15 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -28,7 +28,8 @@ type Authority struct { decrypterCertificate *x509.Certificate scepProvisionerNames []string - mu sync.RWMutex + provisionersMutex sync.RWMutex + encryptionAlgorithmMutex sync.Mutex } type authorityKey struct{} @@ -86,8 +87,8 @@ func (a *Authority) Validate() error { return nil } - a.mu.RLock() - defer a.mu.RUnlock() + a.provisionersMutex.RLock() + defer a.provisionersMutex.RUnlock() noDefaultDecrypterAvailable := a.defaultDecrypter == nil for _, name := range a.scepProvisionerNames { @@ -118,8 +119,8 @@ func (a *Authority) UpdateProvisioners(scepProvisionerNames []string) { return } - a.mu.Lock() - defer a.mu.Unlock() + a.provisionersMutex.Lock() + defer a.provisionersMutex.Unlock() a.scepProvisionerNames = scepProvisionerNames } @@ -307,20 +308,13 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // and create a degenerate cert structure deg, err := microscep.DegenerateCertificates([]*x509.Certificate{cert}) if err != nil { - return nil, err + return nil, fmt.Errorf("failed generating degenerate certificate: %w", err) } - // apparently the pkcs7 library uses a global default setting for the content encryption - // algorithm to use when en- or decrypting data. We need to restore the current setting after - // the cryptographic operation, so that other usages of the library are not influenced by - // this call to Encrypt(). We are not required to use the same algorithm the SCEP client uses. - encryptionAlgorithmToRestore := pkcs7.ContentEncryptionAlgorithm - pkcs7.ContentEncryptionAlgorithm = p.GetContentEncryptionAlgorithm() - e7, err := pkcs7.Encrypt(deg, msg.P7.Certificates) + e7, err := a.encrypt(deg, msg.P7.Certificates, p.GetContentEncryptionAlgorithm()) if err != nil { - return nil, err + return nil, fmt.Errorf("failed encrypting degenerate certificate: %w", err) } - pkcs7.ContentEncryptionAlgorithm = encryptionAlgorithmToRestore // PKIMessageAttributes to be signed config := pkcs7.SignerInfoConfig{ @@ -391,6 +385,28 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return crepMsg, nil } +func (a *Authority) encrypt(content []byte, recipients []*x509.Certificate, algorithm int) ([]byte, error) { + // apparently the pkcs7 library uses a global default setting for the content encryption + // algorithm to use when en- or decrypting data. We need to restore the current setting after + // the cryptographic operation, so that other usages of the library are not influenced by + // this call to Encrypt(). We are not required to use the same algorithm the SCEP client uses. + a.encryptionAlgorithmMutex.Lock() + defer a.encryptionAlgorithmMutex.Unlock() + + encryptionAlgorithmToRestore := pkcs7.ContentEncryptionAlgorithm + defer func() { + pkcs7.ContentEncryptionAlgorithm = encryptionAlgorithmToRestore + }() + + pkcs7.ContentEncryptionAlgorithm = algorithm + e7, err := pkcs7.Encrypt(content, recipients) + if err != nil { + return nil, err + } + + return e7, nil +} + // CreateFailureResponse creates an appropriately signed reply for PKI operations func (a *Authority) CreateFailureResponse(ctx context.Context, _ *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) { config := pkcs7.SignerInfoConfig{ diff --git a/scep/authority_test.go b/scep/authority_test.go new file mode 100644 index 00000000..0aa81b49 --- /dev/null +++ b/scep/authority_test.go @@ -0,0 +1,73 @@ +package scep + +import ( + "crypto/x509" + "crypto/x509/pkix" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.mozilla.org/pkcs7" + "go.step.sm/crypto/keyutil" + "go.step.sm/crypto/minica" + "go.step.sm/crypto/randutil" +) + +func generateContent(t *testing.T, size int) []byte { + t.Helper() + b, err := randutil.Bytes(size) + require.NoError(t, err) + return b +} + +func generateRecipients(t *testing.T) []*x509.Certificate { + ca, err := minica.New() + require.NoError(t, err) + s, err := keyutil.GenerateSigner("RSA", "", 2048) + require.NoError(t, err) + tmpl := &x509.Certificate{ + PublicKey: s.Public(), + Subject: pkix.Name{CommonName: "Test PKCS#7 Encryption"}, + } + cert, err := ca.Sign(tmpl) + require.NoError(t, err) + return []*x509.Certificate{cert} +} + +func TestAuthority_encrypt(t *testing.T) { + t.Parallel() + a := &Authority{} + recipients := generateRecipients(t) + type args struct { + content []byte + recipients []*x509.Certificate + algorithm int + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"alg-0", args{generateContent(t, 32), recipients, pkcs7.EncryptionAlgorithmDESCBC}, false}, + {"alg-1", args{generateContent(t, 32), recipients, pkcs7.EncryptionAlgorithmAES128CBC}, false}, + {"alg-2", args{generateContent(t, 32), recipients, pkcs7.EncryptionAlgorithmAES256CBC}, false}, + {"alg-3", args{generateContent(t, 32), recipients, pkcs7.EncryptionAlgorithmAES128GCM}, false}, + {"alg-4", args{generateContent(t, 32), recipients, pkcs7.EncryptionAlgorithmAES256GCM}, false}, + {"alg-unknown", args{generateContent(t, 32), recipients, 42}, true}, + } + for _, tt := range tests { + tc := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := a.encrypt(tc.args.content, tc.args.recipients, tc.args.algorithm) + if tc.wantErr { + assert.Error(t, err) + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + assert.NotEmpty(t, got) + }) + } +} From 645b6ffc18a8aaca93f55027ebb2d5c7082f0188 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 4 Aug 2023 22:47:02 +0200 Subject: [PATCH 18/39] Ensure no prompt is fired for loading provisioner decrypter --- authority/provisioner/scep.go | 21 ++++++++++++--------- go.mod | 2 +- go.sum | 3 +++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 396df8e4..c70c0d66 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -222,14 +222,16 @@ func (s *SCEP) Init(config Config) (err error) { decryptionKey = u.Opaque } if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: decryptionKey, - Password: []byte(s.DecrypterKeyPassword), + DecryptionKey: decryptionKey, + Password: []byte(s.DecrypterKeyPassword), + PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, }); err != nil { return fmt.Errorf("failed creating decrypter: %w", err) } if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: decryptionKey, // TODO(hs): support distinct signer key in the future? - Password: []byte(s.DecrypterKeyPassword), + SigningKey: decryptionKey, // TODO(hs): support distinct signer key in the future? + Password: []byte(s.DecrypterKeyPassword), + PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, }); err != nil { return fmt.Errorf("failed creating signer: %w", err) } @@ -254,16 +256,17 @@ func (s *SCEP) Init(config Config) (err error) { // TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager // and load the certificate corresponding to the decryption key? - // Final validation for the decrypter. If both the decrypter and the certificate - // are available, the public keys must match. We currently allow the decrypter to - // be set without a certificate without warning the user, but - if s.decrypter != nil && s.decrypterCertificate != nil { + // Final validation for the decrypter. + if s.decrypter != nil { decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) if !ok { return fmt.Errorf("only RSA keys are supported") } + if s.decrypterCertificate == nil { + return fmt.Errorf("provisioner %q does not have a decrypter certificate set", s.Name) + } if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) { - return errors.New("mismatch between decryption certificate and decrypter public keys") + return errors.New("mismatch between decrypter certificate and decrypter public keys") } } diff --git a/go.mod b/go.mod index 811fe86f..f2feae91 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/urfave/cli v1.22.14 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 - go.step.sm/crypto v0.34.0 + go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 diff --git a/go.sum b/go.sum index 44450981..3a5c550f 100644 --- a/go.sum +++ b/go.sum @@ -352,6 +352,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -1065,6 +1066,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.34.0 h1:ogSsqUu4G/yT0Jtx14q3ilAjKp3nMO4YJdwrFDmBtEY= go.step.sm/crypto v0.34.0/go.mod h1:60g76zZ4KJTK0BTHuO2G5W0aBt8scwNLkVHOBg6MBek= +go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf h1:nypT34HWuPvw+eUW/VgO96KBnjpvDdBC99CyC41L4UA= +go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf/go.mod h1:60g76zZ4KJTK0BTHuO2G5W0aBt8scwNLkVHOBg6MBek= go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d h1:PbcfXsW0Jc8a5LvvzqT3pyxiLBkU9LgAO/JpYjIZbTE= go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= From 9d3b78ae49ab21973a71313c153550679773d306 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 4 Sep 2023 14:55:27 +0200 Subject: [PATCH 19/39] Add `excludeIntermediate` to SCEP provisioner --- api/api.go | 1 + api/models/scep.go | 1 + authority/provisioner/scep.go | 13 +++++++ authority/provisioners.go | 2 ++ go.mod | 28 +++++++-------- go.sum | 67 +++++++++++++++++------------------ scep/authority.go | 12 +++++-- scep/provisioner.go | 1 + 8 files changed, 74 insertions(+), 51 deletions(-) diff --git a/api/api.go b/api/api.go index 6a3e348e..ea0a1899 100644 --- a/api/api.go +++ b/api/api.go @@ -243,6 +243,7 @@ func scepFromProvisioner(p *provisioner.SCEP) *models.SCEP { ChallengePassword: redacted, Capabilities: p.Capabilities, IncludeRoot: p.IncludeRoot, + ExcludeIntermediate: p.ExcludeIntermediate, MinimumPublicKeyLength: p.MinimumPublicKeyLength, DecrypterCertificate: redacted, DecrypterKey: redacted, diff --git a/api/models/scep.go b/api/models/scep.go index b11ca996..ce542d6b 100644 --- a/api/models/scep.go +++ b/api/models/scep.go @@ -23,6 +23,7 @@ type SCEP struct { ChallengePassword string `json:"challenge,omitempty"` Capabilities []string `json:"capabilities,omitempty"` IncludeRoot bool `json:"includeRoot,omitempty"` + ExcludeIntermediate bool `json:"excludeIntermediate,omitempty"` MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` DecrypterCertificate string `json:"decrypterCertificate"` DecrypterKey string `json:"decrypterKey"` diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index c70c0d66..110f7874 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -36,6 +36,10 @@ type SCEP struct { // intermediate in the GetCACerts response IncludeRoot bool `json:"includeRoot,omitempty"` + // ExcludeIntermediate makes the provisioner skip the intermediate CA in the + // GetCACerts response + ExcludeIntermediate bool `json:"excludeIntermediate,omitempty"` + // MinimumPublicKeyLength is the minimum length for public keys in CSRs MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` @@ -307,6 +311,15 @@ func (s *SCEP) ShouldIncludeRootInChain() bool { return s.IncludeRoot } +// ShouldIncludeIntermediateInChain indicates if the +// CA should include the intermediate CA certificate in the +// GetCACerts response. This is true by default, but can be +// overriden through configuration in case SCEP clients +// don't pick the right recipient. +func (s *SCEP) ShouldIncludeIntermediateInChain() bool { + return !s.ExcludeIntermediate +} + // GetContentEncryptionAlgorithm returns the numeric identifier // for the pkcs7 package encryption algorithm to use. func (s *SCEP) GetContentEncryptionAlgorithm() int { diff --git a/authority/provisioners.go b/authority/provisioners.go index b4714009..96b7f4ee 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -974,6 +974,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, ChallengePassword: cfg.Challenge, Capabilities: cfg.Capabilities, IncludeRoot: cfg.IncludeRoot, + ExcludeIntermediate: cfg.ExcludeIntermediate, MinimumPublicKeyLength: int(cfg.MinimumPublicKeyLength), EncryptionAlgorithmIdentifier: int(cfg.EncryptionAlgorithmIdentifier), Claims: claims, @@ -1239,6 +1240,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro Capabilities: p.Capabilities, MinimumPublicKeyLength: int32(p.MinimumPublicKeyLength), IncludeRoot: p.IncludeRoot, + ExcludeIntermediate: p.ExcludeIntermediate, EncryptionAlgorithmIdentifier: int32(p.EncryptionAlgorithmIdentifier), }, }, diff --git a/go.mod b/go.mod index f2feae91..b60e0da1 100644 --- a/go.mod +++ b/go.mod @@ -32,10 +32,10 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf - go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d - golang.org/x/crypto v0.11.0 + go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 + golang.org/x/crypto v0.12.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 - golang.org/x/net v0.13.0 + golang.org/x/net v0.14.0 google.golang.org/api v0.134.0 google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 @@ -43,8 +43,8 @@ require ( ) require ( - cloud.google.com/go v0.110.4 // indirect - cloud.google.com/go/compute v1.20.1 // indirect + cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect cloud.google.com/go/kms v1.15.0 // indirect @@ -68,19 +68,19 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-kit/kit v0.10.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-piv/piv-go v1.11.0 // indirect - github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/certificate-transparency-go v1.1.4 // indirect + github.com/google/certificate-transparency-go v1.1.6 // indirect github.com/google/go-tpm-tools v0.3.12 // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/s2a-go v0.1.4 // indirect @@ -131,13 +131,13 @@ require ( go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect - golang.org/x/time v0.1.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3a5c550f..ff4f4db0 100644 --- a/go.sum +++ b/go.sum @@ -31,16 +31,16 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -263,8 +263,9 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -334,8 +335,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -352,7 +353,6 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -409,8 +409,8 @@ github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErV github.com/google/certificate-transparency-go v1.1.2-0.20210422104406-9f33727a7a18/go.mod h1:6CKh9dscIRoqc2kC6YUFICHZMT9NrClyPrRVFrdw1QQ= github.com/google/certificate-transparency-go v1.1.2-0.20210512142713-bed466244fa6/go.mod h1:aF2dp7Dh81mY8Y/zpzyXps4fQW5zQbDu2CxfpJB6NkI= github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ= -github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= -github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= +github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY= +github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ= github.com/google/go-attestation v0.3.2/go.mod h1:N0ADdnY0cr7eLJyZ75o8kofGGTUF2XrZTJuTPo5acwk= github.com/google/go-attestation v0.4.4-0.20220404204839-8820d49b18d9/go.mod h1:KDsPHk8a2MX9g20kYSdxB21t7je5NghSaFeVn0Zu3Ao= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -682,6 +682,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -696,8 +697,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -781,7 +782,6 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/newrelic/go-agent/v3 v3.23.1 h1:n4CK4EEod2A47T74wQFztavh9g3wHxxmlndj53ksbVg= github.com/newrelic/go-agent/v3 v3.23.1/go.mod h1:dG7Q7yLUrqOo7SYVJADVDN9+P8c/87xp9axldPxmdHM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -876,6 +876,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -1064,12 +1065,10 @@ go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16g go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= -go.step.sm/crypto v0.34.0 h1:ogSsqUu4G/yT0Jtx14q3ilAjKp3nMO4YJdwrFDmBtEY= -go.step.sm/crypto v0.34.0/go.mod h1:60g76zZ4KJTK0BTHuO2G5W0aBt8scwNLkVHOBg6MBek= go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf h1:nypT34HWuPvw+eUW/VgO96KBnjpvDdBC99CyC41L4UA= go.step.sm/crypto v0.34.1-0.20230804202808-557c2649a5bf/go.mod h1:60g76zZ4KJTK0BTHuO2G5W0aBt8scwNLkVHOBg6MBek= -go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d h1:PbcfXsW0Jc8a5LvvzqT3pyxiLBkU9LgAO/JpYjIZbTE= -go.step.sm/linkedca v0.20.1-0.20230802134415-b577c7565f6d/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= +go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 h1:F8CJdanbISusu7jX/ETOAVtPuLfcdTNl+wO22DB+y/8= +go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1115,8 +1114,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1215,8 +1214,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= -golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1344,15 +1343,15 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1365,8 +1364,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1374,8 +1373,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1571,12 +1570,12 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1635,8 +1634,8 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/scep/authority.go b/scep/authority.go index 8f270c15..9b548e40 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -164,9 +164,15 @@ func (a *Authority) GetCACertificates(ctx context.Context) (certs []*x509.Certif certs = append(certs, decrypterCertificate) } - // TODO(hs): ensure logic is in place that checks the signer is the first - // intermediate and that there are no double certificates. - certs = append(certs, a.intermediates...) + // the CA intermediate is added to the chain by default. It's possible to + // exclude it from being added through configuration. This can be useful in + // environments where the SCEP client doesn't select the right RSA decrypter + // certificate, resulting in the wrong recipient in the PKCS7 message. + if p.ShouldIncludeIntermediateInChain() || len(certs) == 0 { + // TODO(hs): ensure logic is in place that checks the signer is the first + // intermediate and that there are no double certificates. + certs = append(certs, a.intermediates...) + } // the CA roots are added for completeness when configured to do so. Clients // are responsible to select the right cert(s) to store and use. diff --git a/scep/provisioner.go b/scep/provisioner.go index f8fd46f1..3ef4eceb 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -16,6 +16,7 @@ type Provisioner interface { GetOptions() *provisioner.Options GetCapabilities() []string ShouldIncludeRootInChain() bool + ShouldIncludeIntermediateInChain() bool GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int From 98d015b5c36eb43b04e8fbde1d7b949139f58d1f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 4 Sep 2023 15:36:37 +0200 Subject: [PATCH 20/39] Fix linting issues --- api/models/scep.go | 2 +- authority/provisioner/scep.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/models/scep.go b/api/models/scep.go index ce542d6b..a9cec1e0 100644 --- a/api/models/scep.go +++ b/api/models/scep.go @@ -68,7 +68,7 @@ func (s *SCEP) GetTokenID(string) (string, error) { } // Init initializes and validates the fields of a SCEP type. -func (s *SCEP) Init(config provisioner.Config) (err error) { +func (s *SCEP) Init(_ provisioner.Config) (err error) { return errDummyImplementation } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 110f7874..e547deff 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -314,7 +314,7 @@ func (s *SCEP) ShouldIncludeRootInChain() bool { // ShouldIncludeIntermediateInChain indicates if the // CA should include the intermediate CA certificate in the // GetCACerts response. This is true by default, but can be -// overriden through configuration in case SCEP clients +// overridden through configuration in case SCEP clients // don't pick the right recipient. func (s *SCEP) ShouldIncludeIntermediateInChain() bool { return !s.ExcludeIntermediate From 36f1dd70bfdc0bce36f6897f922fe5c5fde6cafb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 7 Sep 2023 14:11:53 +0200 Subject: [PATCH 21/39] Add CSR to `SCEPCHALLENGE` webhook request body --- authority/provisioner/scep.go | 14 ++++++++------ authority/provisioner/scep_test.go | 4 ++-- scep/api/api.go | 2 +- scep/authority.go | 4 ++-- scep/provisioner.go | 2 +- webhook/options.go | 3 +++ 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index e547deff..6f81a4d7 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -143,12 +143,14 @@ var ( // that case, the other webhooks will be skipped. If none of // the webhooks indicates the value of the challenge was accepted, // an error is returned. -func (c *challengeValidationController) Validate(ctx context.Context, challenge, transactionID string) error { +func (c *challengeValidationController) Validate(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { for _, wh := range c.webhooks { - req := &webhook.RequestBody{ - SCEPChallenge: challenge, - SCEPTransactionID: transactionID, + req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr)) + if err != nil { + return fmt.Errorf("failed creating new webhook request: %w", err) } + req.SCEPChallenge = challenge + req.SCEPTransactionID = transactionID resp, err := wh.DoWithContext(ctx, c.client, req, nil) // TODO(hs): support templated URL? Requires some refactoring if err != nil { return fmt.Errorf("failed executing webhook request: %w", err) @@ -329,13 +331,13 @@ func (s *SCEP) GetContentEncryptionAlgorithm() int { // ValidateChallenge validates the provided challenge. It starts by // selecting the validation method to use, then performs validation // according to that method. -func (s *SCEP) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { +func (s *SCEP) ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { if s.challengeValidationController == nil { return fmt.Errorf("provisioner %q wasn't initialized", s.Name) } switch s.selectValidationMethod() { case validationMethodWebhook: - return s.challengeValidationController.Validate(ctx, challenge, transactionID) + return s.challengeValidationController.Validate(ctx, csr, challenge, transactionID) default: if subtle.ConstantTimeCompare([]byte(s.ChallengePassword), []byte(challenge)) == 0 { return errors.New("invalid challenge password provided") diff --git a/authority/provisioner/scep_test.go b/authority/provisioner/scep_test.go index acf047fb..0c1049ca 100644 --- a/authority/provisioner/scep_test.go +++ b/authority/provisioner/scep_test.go @@ -141,7 +141,7 @@ func Test_challengeValidationController_Validate(t *testing.T) { } ctx := context.Background() - err := c.Validate(ctx, tt.args.challenge, tt.args.transactionID) + err := c.Validate(ctx, nil, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) @@ -330,7 +330,7 @@ func TestSCEP_ValidateChallenge(t *testing.T) { require.NoError(t, err) ctx := context.Background() - err = tt.p.ValidateChallenge(ctx, tt.args.challenge, tt.args.transactionID) + err = tt.p.ValidateChallenge(ctx, nil, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) return diff --git a/scep/api/api.go b/scep/api/api.go index b618607c..2ac496e4 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -315,7 +315,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // a certificate exists; then it will use RenewalReq. Adding the challenge check here may be a small breaking change for clients. // We'll have to see how it works out. if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { - if err := auth.ValidateChallenge(ctx, challengePassword, transactionID); err != nil { + if err := auth.ValidateChallenge(ctx, csr, challengePassword, transactionID); err != nil { if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) { return createFailureResponse(ctx, csr, msg, microscep.BadRequest, err) } diff --git a/scep/authority.go b/scep/authority.go index 9b548e40..5f8231db 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -503,9 +503,9 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { return caps } -func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { +func (a *Authority) ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error { p := provisionerFromContext(ctx) - return p.ValidateChallenge(ctx, challenge, transactionID) + return p.ValidateChallenge(ctx, csr, challenge, transactionID) } func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, decrypter crypto.Decrypter, err error) { diff --git a/scep/provisioner.go b/scep/provisioner.go index 3ef4eceb..7b8116af 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -20,7 +20,7 @@ type Provisioner interface { GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int - ValidateChallenge(ctx context.Context, challenge, transactionID string) error + ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error } // provisionerKey is the key type for storing and searching a diff --git a/webhook/options.go b/webhook/options.go index 86923709..66eb87a5 100644 --- a/webhook/options.go +++ b/webhook/options.go @@ -24,6 +24,9 @@ func NewRequestBody(options ...RequestBodyOption) (*RequestBody, error) { func WithX509CertificateRequest(cr *x509.CertificateRequest) RequestBodyOption { return func(rb *RequestBody) error { + if cr == nil { + return nil + } rb.X509CertificateRequest = &X509CertificateRequest{ CertificateRequest: x509util.NewCertificateRequestFromX509(cr), PublicKeyAlgorithm: cr.PublicKeyAlgorithm.String(), From 33e661ce7d2737d1a4bc9eadce2e3712d721ba47 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 7 Sep 2023 20:37:29 +0200 Subject: [PATCH 22/39] Add a dummy CSR to SCEP request body tests --- authority/provisioner/scep_test.go | 29 +++++++++++++++++++++++------ webhook/options.go | 3 --- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/authority/provisioner/scep_test.go b/authority/provisioner/scep_test.go index 0c1049ca..4efb3dd8 100644 --- a/authority/provisioner/scep_test.go +++ b/authority/provisioner/scep_test.go @@ -2,6 +2,7 @@ package provisioner import ( "context" + "crypto/x509" "encoding/json" "errors" "net/http" @@ -12,12 +13,18 @@ import ( "github.com/stretchr/testify/require" "go.step.sm/linkedca" + + "github.com/smallstep/certificates/webhook" ) func Test_challengeValidationController_Validate(t *testing.T) { + dummyCSR := &x509.CertificateRequest{ + Raw: []byte{1}, + } type request struct { - Challenge string `json:"scepChallenge"` - TransactionID string `json:"scepTransactionID"` + Request *webhook.X509CertificateRequest `json:"x509CertificateRequest,omitempty"` + Challenge string `json:"scepChallenge"` + TransactionID string `json:"scepTransactionID"` } type response struct { Allow bool `json:"allow"` @@ -39,6 +46,9 @@ func Test_challengeValidationController_Validate(t *testing.T) { require.NoError(t, err) assert.Equal(t, "challenge", req.Challenge) assert.Equal(t, "transaction-1", req.TransactionID) + if assert.NotNil(t, req.Request) { + assert.Equal(t, []byte{1}, req.Request.Raw) + } b, err := json.Marshal(response{Allow: true}) require.NoError(t, err) w.WriteHeader(200) @@ -141,7 +151,7 @@ func Test_challengeValidationController_Validate(t *testing.T) { } ctx := context.Background() - err := c.Validate(ctx, nil, tt.args.challenge, tt.args.transactionID) + err := c.Validate(ctx, dummyCSR, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) @@ -221,9 +231,13 @@ func Test_selectValidationMethod(t *testing.T) { } func TestSCEP_ValidateChallenge(t *testing.T) { + dummyCSR := &x509.CertificateRequest{ + Raw: []byte{1}, + } type request struct { - Challenge string `json:"scepChallenge"` - TransactionID string `json:"scepTransactionID"` + Request *webhook.X509CertificateRequest `json:"x509CertificateRequest,omitempty"` + Challenge string `json:"scepChallenge"` + TransactionID string `json:"scepTransactionID"` } type response struct { Allow bool `json:"allow"` @@ -234,6 +248,9 @@ func TestSCEP_ValidateChallenge(t *testing.T) { require.NoError(t, err) assert.Equal(t, "webhook-challenge", req.Challenge) assert.Equal(t, "webhook-transaction-1", req.TransactionID) + if assert.NotNil(t, req.Request) { + assert.Equal(t, []byte{1}, req.Request.Raw) + } b, err := json.Marshal(response{Allow: true}) require.NoError(t, err) w.WriteHeader(200) @@ -330,7 +347,7 @@ func TestSCEP_ValidateChallenge(t *testing.T) { require.NoError(t, err) ctx := context.Background() - err = tt.p.ValidateChallenge(ctx, nil, tt.args.challenge, tt.args.transactionID) + err = tt.p.ValidateChallenge(ctx, dummyCSR, tt.args.challenge, tt.args.transactionID) if tt.expErr != nil { assert.EqualError(t, err, tt.expErr.Error()) return diff --git a/webhook/options.go b/webhook/options.go index 66eb87a5..86923709 100644 --- a/webhook/options.go +++ b/webhook/options.go @@ -24,9 +24,6 @@ func NewRequestBody(options ...RequestBodyOption) (*RequestBody, error) { func WithX509CertificateRequest(cr *x509.CertificateRequest) RequestBodyOption { return func(rb *RequestBody) error { - if cr == nil { - return nil - } rb.X509CertificateRequest = &X509CertificateRequest{ CertificateRequest: x509util.NewCertificateRequestFromX509(cr), PublicKeyAlgorithm: cr.PublicKeyAlgorithm.String(), From 52bc96760b9dc4821b4603ee8298b222de8a22dd Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 21 Sep 2023 12:01:03 +0200 Subject: [PATCH 23/39] Add SCEP certificate issuance notification webhook --- authority/admin/api/webhook.go | 2 +- authority/provisioner/scep.go | 96 +++++++++++++++++++++++++++++++++- go.mod | 8 +-- go.sum | 21 ++++++++ scep/api/api.go | 9 ++++ scep/authority.go | 10 ++++ scep/context.go | 1 + scep/provisioner.go | 2 + 8 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 scep/context.go diff --git a/authority/admin/api/webhook.go b/authority/admin/api/webhook.go index 3939d55e..574cbf18 100644 --- a/authority/admin/api/webhook.go +++ b/authority/admin/api/webhook.go @@ -57,7 +57,7 @@ func validateWebhook(webhook *linkedca.Webhook) error { // kind switch webhook.Kind { - case linkedca.Webhook_ENRICHING, linkedca.Webhook_AUTHORIZING, linkedca.Webhook_SCEPCHALLENGE: + case linkedca.Webhook_ENRICHING, linkedca.Webhook_AUTHORIZING, linkedca.Webhook_SCEPCHALLENGE, linkedca.Webhook_NOTIFYING: default: return admin.NewError(admin.ErrorBadRequestType, "webhook kind %q is invalid", webhook.Kind) } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 6f81a4d7..fad428e2 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -57,6 +57,7 @@ type SCEP struct { ctl *Controller encryptionAlgorithm int challengeValidationController *challengeValidationController + notificationController *notificationController keyManager kmsapi.KeyManager decrypter crypto.Decrypter decrypterCertificate *x509.Certificate @@ -134,7 +135,8 @@ func newChallengeValidationController(client *http.Client, webhooks []*Webhook) } var ( - ErrSCEPChallengeInvalid = errors.New("webhook server did not allow request") + ErrSCEPChallengeInvalid = errors.New("webhook server did not allow request") + ErrSCEPNotificationFailed = errors.New("scep notification failed") ) // Validate executes zero or more configured webhooks to @@ -163,6 +165,78 @@ func (c *challengeValidationController) Validate(ctx context.Context, csr *x509. return ErrSCEPChallengeInvalid } +type notificationController struct { + client *http.Client + webhooks []*Webhook +} + +// newNotificationController creates a new notificationController +// that performs SCEP notifications through webhooks. +func newNotificationController(client *http.Client, webhooks []*Webhook) *notificationController { + scepHooks := []*Webhook{} + for _, wh := range webhooks { + if wh.Kind != linkedca.Webhook_NOTIFYING.String() { + continue + } + if !isCertTypeOK(wh) { + continue + } + scepHooks = append(scepHooks, wh) + } + return ¬ificationController{ + client: client, + webhooks: scepHooks, + } +} + +func (c *notificationController) Success(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error { + if len(c.webhooks) == 0 { + return nil + } + + for _, wh := range c.webhooks { + req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr), webhook.WithX509Certificate(nil, cert)) // TODO(hs): pass in the x509util.Certifiate too? + if err != nil { + return fmt.Errorf("failed creating new webhook request: %w", err) + } + // TODO(hs): more properties required? + req.SCEPTransactionID = transactionID + resp, err := wh.DoWithContext(ctx, c.client, req, nil) + if err != nil { + return fmt.Errorf("failed executing webhook request: %w", err) + } + if resp.Allow { // TODO(hs): different response for notifying? + return nil // return early when response is positive + } + } + + return ErrSCEPNotificationFailed +} + +func (c *notificationController) Failure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { + if len(c.webhooks) == 0 { + return nil + } + + for _, wh := range c.webhooks { + req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr)) + if err != nil { + return fmt.Errorf("failed creating new webhook request: %w", err) + } + // TODO(hs): more properties, such as error message / code required? + req.SCEPTransactionID = transactionID + resp, err := wh.DoWithContext(ctx, c.client, req, nil) + if err != nil { + return fmt.Errorf("failed executing webhook request: %w", err) + } + if resp.Allow { // TODO(hs): different response for notifying? + return nil // return early when response is positive + } + } + + return ErrSCEPNotificationFailed +} + // isCertTypeOK returns whether or not the webhook can be used // with the SCEP challenge validation webhook controller. func isCertTypeOK(wh *Webhook) bool { @@ -201,6 +275,12 @@ func (s *SCEP) Init(config Config) (err error) { s.GetOptions().GetWebhooks(), ) + // Prepare the SCEP notification controller + s.notificationController = newNotificationController( + config.WebhookClient, + s.GetOptions().GetWebhooks(), + ) + if decryptionKey := s.DecrypterKey; decryptionKey != "" { u, err := uri.Parse(s.DecrypterKey) if err != nil { @@ -346,6 +426,20 @@ func (s *SCEP) ValidateChallenge(ctx context.Context, csr *x509.CertificateReque } } +func (s *SCEP) NotifySuccess(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error { + if s.notificationController == nil { + return fmt.Errorf("provisioner %q wasn't initialized", s.Name) + } + return s.notificationController.Success(ctx, csr, cert, transactionID) +} + +func (s *SCEP) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { + if s.notificationController == nil { + return fmt.Errorf("provisioner %q wasn't initialized", s.Name) + } + return s.notificationController.Failure(ctx, csr, transactionID) +} + type validationMethod string const ( diff --git a/go.mod b/go.mod index 1d91fef1..22e49116 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.35.1 - go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 + go.step.sm/linkedca v0.20.1-0.20230921084813-2442dc2382ef golang.org/x/crypto v0.13.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 golang.org/x/net v0.15.0 @@ -43,7 +43,7 @@ require ( ) require ( - cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go v0.110.7 // indirect cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect @@ -135,9 +135,9 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4bb2c876..0b905480 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= @@ -98,6 +100,7 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -175,6 +178,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -255,6 +260,7 @@ github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -386,8 +392,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -460,6 +468,7 @@ github.com/newrelic/go-agent/v3 v3.24.1/go.mod h1:29qGunRQA4+IGWn5WRiqVKA+pqYsCI github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -553,6 +562,7 @@ github.com/smallstep/pkcs7 v0.0.0-20230615175518-7ce6486b74eb/go.mod h1:SNgMg+Eg github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -567,6 +577,7 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -590,6 +601,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -617,6 +630,8 @@ go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY= go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA= go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 h1:F8CJdanbISusu7jX/ETOAVtPuLfcdTNl+wO22DB+y/8= go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= +go.step.sm/linkedca v0.20.1-0.20230921084813-2442dc2382ef h1:PL+DADogXN9QXVFQpECCywtgz/FkVqdeBi3got39jKU= +go.step.sm/linkedca v0.20.1-0.20230921084813-2442dc2382ef/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -803,10 +818,14 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 h1:o4LtQxebKIJ4vkzyhtD2rfUNZ20Zf0ik5YVP5E7G7VE= google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -840,10 +859,12 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/scep/api/api.go b/scep/api/api.go index 2ac496e4..f7beb7a1 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -333,9 +333,18 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { certRep, err := auth.SignCSR(ctx, csr, msg) if err != nil { + if notifyErr := auth.NotifyFailure(ctx, csr, transactionID); notifyErr != nil { + // TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good + _ = notifyErr + } return createFailureResponse(ctx, csr, msg, microscep.BadRequest, fmt.Errorf("error when signing new certificate: %w", err)) } + if notifyErr := auth.NotifySuccess(ctx, csr, certRep.Certificate, transactionID); notifyErr != nil { + // TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good + _ = notifyErr + } + res := Response{ Operation: opnPKIOperation, Data: certRep.Raw, diff --git a/scep/authority.go b/scep/authority.go index 5f8231db..027d11fe 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -508,6 +508,16 @@ func (a *Authority) ValidateChallenge(ctx context.Context, csr *x509.Certificate return p.ValidateChallenge(ctx, csr, challenge, transactionID) } +func (a *Authority) NotifySuccess(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error { + p := provisionerFromContext(ctx) + return p.NotifySuccess(ctx, csr, cert, transactionID) +} + +func (a *Authority) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { + p := provisionerFromContext(ctx) + return p.NotifyFailure(ctx, csr, transactionID) +} + func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, decrypter crypto.Decrypter, err error) { p := provisionerFromContext(ctx) cert, decrypter = p.GetDecrypter() diff --git a/scep/context.go b/scep/context.go new file mode 100644 index 00000000..ce73569b --- /dev/null +++ b/scep/context.go @@ -0,0 +1 @@ +package scep diff --git a/scep/provisioner.go b/scep/provisioner.go index 7b8116af..cb894c05 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -21,6 +21,8 @@ type Provisioner interface { GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error + NotifySuccess(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error + NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error } // provisionerKey is the key type for storing and searching a From 63257e057639cdd6cd513c1232ce02dc5f396f40 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 21 Sep 2023 12:05:58 +0200 Subject: [PATCH 24/39] Add full certificate DER bytes to success notification webhook --- authority/provisioner/scep.go | 22 ++++++++-------------- webhook/types.go | 1 + 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index fad428e2..3e3e77b9 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -190,34 +190,28 @@ func newNotificationController(client *http.Client, webhooks []*Webhook) *notifi } func (c *notificationController) Success(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error { - if len(c.webhooks) == 0 { - return nil - } - for _, wh := range c.webhooks { req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr), webhook.WithX509Certificate(nil, cert)) // TODO(hs): pass in the x509util.Certifiate too? if err != nil { return fmt.Errorf("failed creating new webhook request: %w", err) } + req.X509Certificate.Raw = cert.Raw // adding the full certificate DER bytes + // TODO(hs): more properties required? req.SCEPTransactionID = transactionID resp, err := wh.DoWithContext(ctx, c.client, req, nil) if err != nil { return fmt.Errorf("failed executing webhook request: %w", err) } - if resp.Allow { // TODO(hs): different response for notifying? - return nil // return early when response is positive + if !resp.Allow { // TODO(hs): different response for notifying? + return ErrSCEPNotificationFailed // return early } } - return ErrSCEPNotificationFailed + return nil } func (c *notificationController) Failure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { - if len(c.webhooks) == 0 { - return nil - } - for _, wh := range c.webhooks { req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr)) if err != nil { @@ -229,12 +223,12 @@ func (c *notificationController) Failure(ctx context.Context, csr *x509.Certific if err != nil { return fmt.Errorf("failed executing webhook request: %w", err) } - if resp.Allow { // TODO(hs): different response for notifying? - return nil // return early when response is positive + if !resp.Allow { // TODO(hs): different response for notifying? + return ErrSCEPNotificationFailed // return early } } - return ErrSCEPNotificationFailed + return nil } // isCertTypeOK returns whether or not the webhook can be used diff --git a/webhook/types.go b/webhook/types.go index 9eda0578..330250f5 100644 --- a/webhook/types.go +++ b/webhook/types.go @@ -30,6 +30,7 @@ type X509Certificate struct { PublicKeyAlgorithm string `json:"publicKeyAlgorithm"` NotBefore time.Time `json:"notBefore"` NotAfter time.Time `json:"notAfter"` + Raw []byte `json:"raw"` } // SSHCertificateRequest is the certificate request sent to webhook servers for From b6c95d7be27fbcf5f0c9a3456f76d695c298e755 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 21 Sep 2023 18:11:55 +0200 Subject: [PATCH 25/39] Add additional properties to SCEP notify webhook request body --- authority/provisioner/scep.go | 11 +++++------ scep/api/api.go | 3 +++ scep/authority.go | 4 ++-- scep/provisioner.go | 2 +- webhook/types.go | 6 ++++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 3e3e77b9..0c514275 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -196,8 +196,6 @@ func (c *notificationController) Success(ctx context.Context, csr *x509.Certific return fmt.Errorf("failed creating new webhook request: %w", err) } req.X509Certificate.Raw = cert.Raw // adding the full certificate DER bytes - - // TODO(hs): more properties required? req.SCEPTransactionID = transactionID resp, err := wh.DoWithContext(ctx, c.client, req, nil) if err != nil { @@ -211,14 +209,15 @@ func (c *notificationController) Success(ctx context.Context, csr *x509.Certific return nil } -func (c *notificationController) Failure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { +func (c *notificationController) Failure(ctx context.Context, csr *x509.CertificateRequest, transactionID string, errorCode int, errorDescription string) error { for _, wh := range c.webhooks { req, err := webhook.NewRequestBody(webhook.WithX509CertificateRequest(csr)) if err != nil { return fmt.Errorf("failed creating new webhook request: %w", err) } - // TODO(hs): more properties, such as error message / code required? req.SCEPTransactionID = transactionID + req.SCEPErrorCode = errorCode + req.SCEPErrorDescription = errorDescription resp, err := wh.DoWithContext(ctx, c.client, req, nil) if err != nil { return fmt.Errorf("failed executing webhook request: %w", err) @@ -427,11 +426,11 @@ func (s *SCEP) NotifySuccess(ctx context.Context, csr *x509.CertificateRequest, return s.notificationController.Success(ctx, csr, cert, transactionID) } -func (s *SCEP) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { +func (s *SCEP) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string, errorCode int, errorDescription string) error { if s.notificationController == nil { return fmt.Errorf("provisioner %q wasn't initialized", s.Name) } - return s.notificationController.Failure(ctx, csr, transactionID) + return s.notificationController.Failure(ctx, csr, transactionID, errorCode, errorDescription) } type validationMethod string diff --git a/scep/api/api.go b/scep/api/api.go index f7beb7a1..74259a8e 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -333,6 +333,9 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { certRep, err := auth.SignCSR(ctx, csr, msg) if err != nil { + // default to ERROR_INTERNAL_ERROR: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d + errorCode := 0x0000054F + errorDescription := err.Error() if notifyErr := auth.NotifyFailure(ctx, csr, transactionID); notifyErr != nil { // TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good _ = notifyErr diff --git a/scep/authority.go b/scep/authority.go index 027d11fe..292c7004 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -513,9 +513,9 @@ func (a *Authority) NotifySuccess(ctx context.Context, csr *x509.CertificateRequ return p.NotifySuccess(ctx, csr, cert, transactionID) } -func (a *Authority) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error { +func (a *Authority) NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string, errorCode int, errorDescription string) error { p := provisionerFromContext(ctx) - return p.NotifyFailure(ctx, csr, transactionID) + return p.NotifyFailure(ctx, csr, transactionID, errorCode, errorDescription) } func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, decrypter crypto.Decrypter, err error) { diff --git a/scep/provisioner.go b/scep/provisioner.go index cb894c05..3df4b367 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -22,7 +22,7 @@ type Provisioner interface { GetContentEncryptionAlgorithm() int ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) error NotifySuccess(ctx context.Context, csr *x509.CertificateRequest, cert *x509.Certificate, transactionID string) error - NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string) error + NotifyFailure(ctx context.Context, csr *x509.CertificateRequest, transactionID string, errorCode int, errorDescription string) error } // provisionerKey is the key type for storing and searching a diff --git a/webhook/types.go b/webhook/types.go index 330250f5..4c443969 100644 --- a/webhook/types.go +++ b/webhook/types.go @@ -81,8 +81,10 @@ type RequestBody struct { SSHCertificateRequest *SSHCertificateRequest `json:"sshCertificateRequest,omitempty"` SSHCertificate *SSHCertificate `json:"sshCertificate,omitempty"` // Only set for SCEP challenge validation requests - SCEPChallenge string `json:"scepChallenge,omitempty"` - SCEPTransactionID string `json:"scepTransactionID,omitempty"` + SCEPChallenge string `json:"scepChallenge,omitempty"` + SCEPTransactionID string `json:"scepTransactionID,omitempty"` + SCEPErrorCode int `json:"scepErrorCode,omitempty"` + SCEPErrorDescription string `json:"scepErrorDescription,omitempty"` // Only set for X5C provisioners X5CCertificate *X5CCertificate `json:"x5cCertificate,omitempty"` // Set for X5C, AWS, GCP, and Azure provisioners From 6d2d21e989d4d7bb24937c4017fe94c67064e835 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 21 Sep 2023 18:15:03 +0200 Subject: [PATCH 26/39] Fix undefined and unused variables Forgot to save the latest version... --- scep/api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scep/api/api.go b/scep/api/api.go index 74259a8e..a321f59f 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -336,7 +336,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // default to ERROR_INTERNAL_ERROR: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d errorCode := 0x0000054F errorDescription := err.Error() - if notifyErr := auth.NotifyFailure(ctx, csr, transactionID); notifyErr != nil { + if notifyErr := auth.NotifyFailure(ctx, csr, transactionID, errorCode, errorDescription); notifyErr != nil { // TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good _ = notifyErr } From 3ade92f8d5d11aeac0c87878cb86b3bde6da3c81 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 22 Sep 2023 11:10:22 +0200 Subject: [PATCH 27/39] Support both a decrypter key URI as well as PEM --- api/api.go | 3 +- api/models/scep.go | 3 +- authority/provisioner/scep.go | 66 +++++++++++++++++++++++++++-------- go.mod | 10 +++--- go.sum | 20 +++++------ 5 files changed, 71 insertions(+), 31 deletions(-) diff --git a/api/api.go b/api/api.go index ea0a1899..8b950c2a 100644 --- a/api/api.go +++ b/api/api.go @@ -246,7 +246,8 @@ func scepFromProvisioner(p *provisioner.SCEP) *models.SCEP { ExcludeIntermediate: p.ExcludeIntermediate, MinimumPublicKeyLength: p.MinimumPublicKeyLength, DecrypterCertificate: redacted, - DecrypterKey: redacted, + DecrypterKeyPEM: redacted, + DecrypterKeyURI: redacted, DecrypterKeyPassword: redacted, EncryptionAlgorithmIdentifier: p.EncryptionAlgorithmIdentifier, Options: p.Options, diff --git a/api/models/scep.go b/api/models/scep.go index a9cec1e0..5de7ecf5 100644 --- a/api/models/scep.go +++ b/api/models/scep.go @@ -26,7 +26,8 @@ type SCEP struct { ExcludeIntermediate bool `json:"excludeIntermediate,omitempty"` MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` DecrypterCertificate string `json:"decrypterCertificate"` - DecrypterKey string `json:"decrypterKey"` + DecrypterKeyPEM string `json:"decrypterKeyPEM"` + DecrypterKeyURI string `json:"decrypterKey"` DecrypterKeyPassword string `json:"decrypterKeyPassword"` EncryptionAlgorithmIdentifier int `json:"encryptionAlgorithmIdentifier,omitempty"` Options *provisioner.Options `json:"options,omitempty"` diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 6f81a4d7..676bc338 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" "go.step.sm/crypto/kms" + "go.step.sm/crypto/kms/apiv1" kmsapi "go.step.sm/crypto/kms/apiv1" "go.step.sm/crypto/kms/uri" "go.step.sm/linkedca" @@ -45,8 +46,9 @@ type SCEP struct { // TODO(hs): also support a separate signer configuration? DecrypterCertificate []byte `json:"decrypterCertificate"` - DecrypterKey string `json:"decrypterKey"` - DecrypterKeyPassword string `json:"decrypterKeyPassword"` + DecrypterKeyPEM []byte `json:"decrypterKeyPEM"` + DecrypterKeyURI string `json:"decrypterKey"` + DecrypterKeyPassword []byte `json:"decrypterKeyPassword"` // Numerical identifier for the ContentEncryptionAlgorithm as defined in github.com/mozilla-services/pkcs7 // at https://github.com/mozilla-services/pkcs7/blob/33d05740a3526e382af6395d3513e73d4e66d1cb/encrypt.go#L63 @@ -201,21 +203,57 @@ func (s *SCEP) Init(config Config) (err error) { s.GetOptions().GetWebhooks(), ) - if decryptionKey := s.DecrypterKey; decryptionKey != "" { - u, err := uri.Parse(s.DecrypterKey) + // parse the decrypter key PEM contents if available + if decryptionKeyPEM := s.DecrypterKeyPEM; len(decryptionKeyPEM) > 0 { + // try reading the PEM for validation + block, rest := pem.Decode(decryptionKeyPEM) + if len(rest) > 0 { + return errors.New("failed parsing decrypter key: trailing data") + } + if block == nil { + return errors.New("failed parsing decrypter key: no PEM block found") + } + opts := kms.Options{ + Type: apiv1.SoftKMS, + } + if s.keyManager, err = kms.New(context.Background(), opts); err != nil { + return fmt.Errorf("failed initializing kms: %w", err) + } + kmsDecrypter, ok := s.keyManager.(kmsapi.Decrypter) + if !ok { + return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type) + } + if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKeyPEM: decryptionKeyPEM, + Password: s.DecrypterKeyPassword, + PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, + }); err != nil { + return fmt.Errorf("failed creating decrypter: %w", err) + } + if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKeyPEM: decryptionKeyPEM, // TODO(hs): support distinct signer key in the future? + Password: s.DecrypterKeyPassword, + PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, + }); err != nil { + return fmt.Errorf("failed creating signer: %w", err) + } + } + + if decryptionKeyURI := s.DecrypterKeyURI; len(decryptionKeyURI) > 0 { + u, err := uri.Parse(s.DecrypterKeyURI) if err != nil { return fmt.Errorf("failed parsing decrypter key: %w", err) } - var kmsType string + var kmsType apiv1.Type switch { case u.Scheme != "": - kmsType = u.Scheme + kmsType = kms.Type(u.Scheme) default: - kmsType = "softkms" + kmsType = apiv1.SoftKMS } opts := kms.Options{ - Type: kms.Type(kmsType), - URI: s.DecrypterKey, + Type: kmsType, + URI: s.DecrypterKeyURI, } if s.keyManager, err = kms.New(context.Background(), opts); err != nil { return fmt.Errorf("failed initializing kms: %w", err) @@ -225,18 +263,18 @@ func (s *SCEP) Init(config Config) (err error) { return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type) } if kmsType != "softkms" { // TODO(hs): this should likely become more transparent? - decryptionKey = u.Opaque + decryptionKeyURI = u.Opaque } if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: decryptionKey, - Password: []byte(s.DecrypterKeyPassword), + DecryptionKey: decryptionKeyURI, + Password: s.DecrypterKeyPassword, PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, }); err != nil { return fmt.Errorf("failed creating decrypter: %w", err) } if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: decryptionKey, // TODO(hs): support distinct signer key in the future? - Password: []byte(s.DecrypterKeyPassword), + SigningKey: decryptionKeyURI, // TODO(hs): support distinct signer key in the future? + Password: s.DecrypterKeyPassword, PasswordPrompter: kmsapi.NonInteractivePasswordPrompter, }); err != nil { return fmt.Errorf("failed creating signer: %w", err) diff --git a/go.mod b/go.mod index 1d91fef1..5010ec03 100644 --- a/go.mod +++ b/go.mod @@ -32,18 +32,18 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.35.1 - go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 + go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893 golang.org/x/crypto v0.13.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 golang.org/x/net v0.15.0 google.golang.org/api v0.141.0 - google.golang.org/grpc v1.58.1 + google.golang.org/grpc v1.58.2 google.golang.org/protobuf v1.31.0 gopkg.in/square/go-jose.v2 v2.6.0 ) require ( - cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go v0.110.7 // indirect cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect @@ -135,9 +135,9 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4bb2c876..b1abf28e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= @@ -615,8 +615,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY= go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA= -go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36 h1:F8CJdanbISusu7jX/ETOAVtPuLfcdTNl+wO22DB+y/8= -go.step.sm/linkedca v0.20.1-0.20230904124610-b6e003ee7e36/go.mod h1:QLWVNpZKKYukwVwQTfK22n5WmDs5c/xc4vakguT/THg= +go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893 h1:PmvYAEYTBPXyWMZAYNrj9eiaj3Bj0qfDEnyiyQDsWcU= +go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -801,12 +801,12 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 h1:o4LtQxebKIJ4vkzyhtD2rfUNZ20Zf0ik5YVP5E7G7VE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -819,8 +819,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58= -google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 5fd70af2c81e31a1df37ec9ccd170f063e7ab045 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 22 Sep 2023 11:38:03 +0200 Subject: [PATCH 28/39] Make API responses aware of the new SCEP decrypter properties --- api/api_test.go | 28 +++++++++++++++++++++------- api/models/scep.go | 12 ++++++------ authority/provisioners.go | 9 ++++++++- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/api/api_test.go b/api/api_test.go index b0ee255d..a123fd2e 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -1569,7 +1569,6 @@ func mustCertificate(t *testing.T, pub, priv interface{}) *x509.Certificate { } func TestProvisionersResponse_MarshalJSON(t *testing.T) { - k := map[string]any{ "use": "sig", "kty": "EC", @@ -1581,9 +1580,14 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { } key := squarejose.JSONWebKey{} b, err := json.Marshal(k) - assert.FatalError(t, err) + require.NoError(t, err) err = json.Unmarshal(b, &key) - assert.FatalError(t, err) + require.NoError(t, err) + + var encodedPassword bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &encodedPassword) + _, err = enc.Write([]byte("super-secret-password")) + require.NoError(t, err) r := ProvisionersResponse{ Provisioners: provisioner.List{ @@ -1593,9 +1597,12 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { ChallengePassword: "not-so-secret", MinimumPublicKeyLength: 2048, EncryptionAlgorithmIdentifier: 2, + IncludeRoot: true, + ExcludeIntermediate: true, DecrypterCertificate: []byte{1, 2, 3, 4}, - DecrypterKey: "softkms:path=/path/to/private.key", - DecrypterKeyPassword: "super-secret-password", + DecrypterKeyPEM: []byte{5, 6, 7, 8}, + DecrypterKeyURI: "softkms:path=/path/to/private.key", + DecrypterKeyPassword: encodedPassword.Bytes(), }, &provisioner.JWK{ EncryptedKey: "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlhOdmYxQjgxSUlLMFA2NUkwcmtGTGcifQ.XaN9zcPQeWt49zchUDm34FECUTHfQTn_.tmNHPQDqR3ebsWfd.9WZr3YVdeOyJh36vvx0VlRtluhvYp4K7jJ1KGDr1qypwZ3ziBVSNbYYQ71du7fTtrnfG1wgGTVR39tWSzBU-zwQ5hdV3rpMAaEbod5zeW6SHd95H3Bvcb43YiiqJFNL5sGZzFb7FqzVmpsZ1efiv6sZaGDHtnCAL6r12UG5EZuqGfM0jGCZitUz2m9TUKXJL5DJ7MOYbFfkCEsUBPDm_TInliSVn2kMJhFa0VOe5wZk5YOuYM3lNYW64HGtbf-llN2Xk-4O9TfeSPizBx9ZqGpeu8pz13efUDT2WL9tWo6-0UE-CrG0bScm8lFTncTkHcu49_a5NaUBkYlBjEiw.thPcx3t1AUcWuEygXIY3Fg", @@ -1612,9 +1619,13 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { { "type": "scep", "name": "scep", + "forceCN": false, + "includeRoot": true, + "excludeIntermediate": true, "challenge": "*** REDACTED ***", "decrypterCertificate": "*** REDACTED ***", "decrypterKey": "*** REDACTED ***", + "decrypterKeyPEM": "*** REDACTED ***", "decrypterKeyPassword": "*** REDACTED ***", "minimumPublicKeyLength": 2048, "encryptionAlgorithmIdentifier": 2, @@ -1652,9 +1663,12 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { ChallengePassword: "not-so-secret", MinimumPublicKeyLength: 2048, EncryptionAlgorithmIdentifier: 2, + IncludeRoot: true, + ExcludeIntermediate: true, DecrypterCertificate: []byte{1, 2, 3, 4}, - DecrypterKey: "softkms:path=/path/to/private.key", - DecrypterKeyPassword: "super-secret-password", + DecrypterKeyPEM: []byte{5, 6, 7, 8}, + DecrypterKeyURI: "softkms:path=/path/to/private.key", + DecrypterKeyPassword: encodedPassword.Bytes(), }, &provisioner.JWK{ EncryptedKey: "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlhOdmYxQjgxSUlLMFA2NUkwcmtGTGcifQ.XaN9zcPQeWt49zchUDm34FECUTHfQTn_.tmNHPQDqR3ebsWfd.9WZr3YVdeOyJh36vvx0VlRtluhvYp4K7jJ1KGDr1qypwZ3ziBVSNbYYQ71du7fTtrnfG1wgGTVR39tWSzBU-zwQ5hdV3rpMAaEbod5zeW6SHd95H3Bvcb43YiiqJFNL5sGZzFb7FqzVmpsZ1efiv6sZaGDHtnCAL6r12UG5EZuqGfM0jGCZitUz2m9TUKXJL5DJ7MOYbFfkCEsUBPDm_TInliSVn2kMJhFa0VOe5wZk5YOuYM3lNYW64HGtbf-llN2Xk-4O9TfeSPizBx9ZqGpeu8pz13efUDT2WL9tWo6-0UE-CrG0bScm8lFTncTkHcu49_a5NaUBkYlBjEiw.thPcx3t1AUcWuEygXIY3Fg", diff --git a/api/models/scep.go b/api/models/scep.go index 5de7ecf5..71c003a7 100644 --- a/api/models/scep.go +++ b/api/models/scep.go @@ -19,17 +19,17 @@ type SCEP struct { ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` - ForceCN bool `json:"forceCN,omitempty"` - ChallengePassword string `json:"challenge,omitempty"` + ForceCN bool `json:"forceCN"` + ChallengePassword string `json:"challenge"` Capabilities []string `json:"capabilities,omitempty"` - IncludeRoot bool `json:"includeRoot,omitempty"` - ExcludeIntermediate bool `json:"excludeIntermediate,omitempty"` - MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` + IncludeRoot bool `json:"includeRoot"` + ExcludeIntermediate bool `json:"excludeIntermediate"` + MinimumPublicKeyLength int `json:"minimumPublicKeyLength"` DecrypterCertificate string `json:"decrypterCertificate"` DecrypterKeyPEM string `json:"decrypterKeyPEM"` DecrypterKeyURI string `json:"decrypterKey"` DecrypterKeyPassword string `json:"decrypterKeyPassword"` - EncryptionAlgorithmIdentifier int `json:"encryptionAlgorithmIdentifier,omitempty"` + EncryptionAlgorithmIdentifier int `json:"encryptionAlgorithmIdentifier"` Options *provisioner.Options `json:"options,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` } diff --git a/authority/provisioners.go b/authority/provisioners.go index 63fb1191..77a319b2 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -990,7 +990,8 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, } if decrypter := cfg.GetDecrypter(); decrypter != nil { s.DecrypterCertificate = decrypter.DecrypterCertificate - s.DecrypterKey = decrypter.DecrypterKey + s.DecrypterKeyPEM = decrypter.DecrypterKey + s.DecrypterKeyURI = decrypter.DecrypterKeyUri s.DecrypterKeyPassword = decrypter.DecrypterKeyPassword } return s, nil @@ -1250,6 +1251,12 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro IncludeRoot: p.IncludeRoot, ExcludeIntermediate: p.ExcludeIntermediate, EncryptionAlgorithmIdentifier: int32(p.EncryptionAlgorithmIdentifier), + Decrypter: &linkedca.SCEPDecrypter{ + DecrypterCertificate: p.DecrypterCertificate, + DecrypterKey: p.DecrypterKeyPEM, + DecrypterKeyUri: p.DecrypterKeyURI, + DecrypterKeyPassword: p.DecrypterKeyPassword, + }, }, }, }, From 4fd4227b7385c09f1406e257ab03f2c117ad9beb Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 22 Sep 2023 11:44:49 +0200 Subject: [PATCH 29/39] Use shorter SCEP decrypter property names from linkedca --- authority/provisioners.go | 16 ++++++++-------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/authority/provisioners.go b/authority/provisioners.go index 77a319b2..747517c9 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -989,10 +989,10 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Options: options, } if decrypter := cfg.GetDecrypter(); decrypter != nil { - s.DecrypterCertificate = decrypter.DecrypterCertificate - s.DecrypterKeyPEM = decrypter.DecrypterKey - s.DecrypterKeyURI = decrypter.DecrypterKeyUri - s.DecrypterKeyPassword = decrypter.DecrypterKeyPassword + s.DecrypterCertificate = decrypter.Certificate + s.DecrypterKeyPEM = decrypter.Key + s.DecrypterKeyURI = decrypter.KeyUri + s.DecrypterKeyPassword = decrypter.KeyPassword } return s, nil case *linkedca.ProvisionerDetails_Nebula: @@ -1252,10 +1252,10 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro ExcludeIntermediate: p.ExcludeIntermediate, EncryptionAlgorithmIdentifier: int32(p.EncryptionAlgorithmIdentifier), Decrypter: &linkedca.SCEPDecrypter{ - DecrypterCertificate: p.DecrypterCertificate, - DecrypterKey: p.DecrypterKeyPEM, - DecrypterKeyUri: p.DecrypterKeyURI, - DecrypterKeyPassword: p.DecrypterKeyPassword, + Certificate: p.DecrypterCertificate, + Key: p.DecrypterKeyPEM, + KeyUri: p.DecrypterKeyURI, + KeyPassword: p.DecrypterKeyPassword, }, }, }, diff --git a/go.mod b/go.mod index 5010ec03..6c582c9d 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.35.1 - go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893 + go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a golang.org/x/crypto v0.13.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 golang.org/x/net v0.15.0 diff --git a/go.sum b/go.sum index b1abf28e..b50010e4 100644 --- a/go.sum +++ b/go.sum @@ -615,8 +615,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY= go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA= -go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893 h1:PmvYAEYTBPXyWMZAYNrj9eiaj3Bj0qfDEnyiyQDsWcU= -go.step.sm/linkedca v0.20.1-0.20230922085851-78fa28647893/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= +go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a h1:hTueTggXiuwPGnoeE5vV7x57bjA895Qhz55L2B0gRr4= +go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= From 5f8e0de1c3170bdd4f52debf71959be8be5e9fa1 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 22 Sep 2023 11:46:51 +0200 Subject: [PATCH 30/39] Fix duplicate import in SCEP provisioner --- authority/provisioner/scep.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 676bc338..beada7ec 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -14,7 +14,6 @@ import ( "github.com/pkg/errors" "go.step.sm/crypto/kms" - "go.step.sm/crypto/kms/apiv1" kmsapi "go.step.sm/crypto/kms/apiv1" "go.step.sm/crypto/kms/uri" "go.step.sm/linkedca" @@ -214,7 +213,7 @@ func (s *SCEP) Init(config Config) (err error) { return errors.New("failed parsing decrypter key: no PEM block found") } opts := kms.Options{ - Type: apiv1.SoftKMS, + Type: kmsapi.SoftKMS, } if s.keyManager, err = kms.New(context.Background(), opts); err != nil { return fmt.Errorf("failed initializing kms: %w", err) @@ -244,12 +243,12 @@ func (s *SCEP) Init(config Config) (err error) { if err != nil { return fmt.Errorf("failed parsing decrypter key: %w", err) } - var kmsType apiv1.Type + var kmsType kmsapi.Type switch { case u.Scheme != "": kmsType = kms.Type(u.Scheme) default: - kmsType = apiv1.SoftKMS + kmsType = kmsapi.SoftKMS } opts := kms.Options{ Type: kmsType, From ba72710e2d11295287a9816a6d3dbd2b544ff075 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 22 Sep 2023 12:40:14 +0200 Subject: [PATCH 31/39] Address code review remarks --- authority/admin/api/webhook.go | 4 +--- authority/provisioner/scep.go | 16 ++++------------ scep/api/api.go | 5 +---- scep/context.go | 1 - webhook/types.go | 2 +- 5 files changed, 7 insertions(+), 21 deletions(-) delete mode 100644 scep/context.go diff --git a/authority/admin/api/webhook.go b/authority/admin/api/webhook.go index 574cbf18..c83c85e7 100644 --- a/authority/admin/api/webhook.go +++ b/authority/admin/api/webhook.go @@ -56,9 +56,7 @@ func validateWebhook(webhook *linkedca.Webhook) error { } // kind - switch webhook.Kind { - case linkedca.Webhook_ENRICHING, linkedca.Webhook_AUTHORIZING, linkedca.Webhook_SCEPCHALLENGE, linkedca.Webhook_NOTIFYING: - default: + if _, ok := linkedca.Webhook_Kind_name[int32(webhook.Kind)]; !ok || webhook.Kind == linkedca.Webhook_NO_KIND { return admin.NewError(admin.ErrorBadRequestType, "webhook kind %q is invalid", webhook.Kind) } diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 0c514275..0c106927 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -197,12 +197,8 @@ func (c *notificationController) Success(ctx context.Context, csr *x509.Certific } req.X509Certificate.Raw = cert.Raw // adding the full certificate DER bytes req.SCEPTransactionID = transactionID - resp, err := wh.DoWithContext(ctx, c.client, req, nil) - if err != nil { - return fmt.Errorf("failed executing webhook request: %w", err) - } - if !resp.Allow { // TODO(hs): different response for notifying? - return ErrSCEPNotificationFailed // return early + if _, err = wh.DoWithContext(ctx, c.client, req, nil); err != nil { + return fmt.Errorf("failed executing webhook request: %w: %w", ErrSCEPNotificationFailed, err) } } @@ -218,12 +214,8 @@ func (c *notificationController) Failure(ctx context.Context, csr *x509.Certific req.SCEPTransactionID = transactionID req.SCEPErrorCode = errorCode req.SCEPErrorDescription = errorDescription - resp, err := wh.DoWithContext(ctx, c.client, req, nil) - if err != nil { - return fmt.Errorf("failed executing webhook request: %w", err) - } - if !resp.Allow { // TODO(hs): different response for notifying? - return ErrSCEPNotificationFailed // return early + if _, err = wh.DoWithContext(ctx, c.client, req, nil); err != nil { + return fmt.Errorf("failed executing webhook request: %w: %w", ErrSCEPNotificationFailed, err) } } diff --git a/scep/api/api.go b/scep/api/api.go index a321f59f..b8ec5bea 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -333,10 +333,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { certRep, err := auth.SignCSR(ctx, csr, msg) if err != nil { - // default to ERROR_INTERNAL_ERROR: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d - errorCode := 0x0000054F - errorDescription := err.Error() - if notifyErr := auth.NotifyFailure(ctx, csr, transactionID, errorCode, errorDescription); notifyErr != nil { + if notifyErr := auth.NotifyFailure(ctx, csr, transactionID, 0, err.Error()); notifyErr != nil { // TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good _ = notifyErr } diff --git a/scep/context.go b/scep/context.go deleted file mode 100644 index ce73569b..00000000 --- a/scep/context.go +++ /dev/null @@ -1 +0,0 @@ -package scep diff --git a/webhook/types.go b/webhook/types.go index 4c443969..2d7832b8 100644 --- a/webhook/types.go +++ b/webhook/types.go @@ -80,7 +80,7 @@ type RequestBody struct { X509Certificate *X509Certificate `json:"x509Certificate,omitempty"` SSHCertificateRequest *SSHCertificateRequest `json:"sshCertificateRequest,omitempty"` SSHCertificate *SSHCertificate `json:"sshCertificate,omitempty"` - // Only set for SCEP challenge validation requests + // Only set for SCEP webhook requests SCEPChallenge string `json:"scepChallenge,omitempty"` SCEPTransactionID string `json:"scepTransactionID,omitempty"` SCEPErrorCode int `json:"scepErrorCode,omitempty"` From aea3f752bd490a90e167447075e01a64d8607ef8 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 25 Sep 2023 19:47:33 +0200 Subject: [PATCH 32/39] Upgrade to linkedca v0.20.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e9a4850b..ac8da3c0 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 go.step.sm/cli-utils v0.8.0 go.step.sm/crypto v0.35.1 - go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a + go.step.sm/linkedca v0.20.1 golang.org/x/crypto v0.13.0 golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 golang.org/x/net v0.15.0 diff --git a/go.sum b/go.sum index b6553db4..8d76598d 100644 --- a/go.sum +++ b/go.sum @@ -615,8 +615,8 @@ go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.35.1 h1:QAZZ7Q8xaM4TdungGSAYw/zxpyH4fMYTkfaXVV9H7pY= go.step.sm/crypto v0.35.1/go.mod h1:vn8Vkx/Mbqgoe7AG8btC0qZ995Udm3e+JySuDS1LCJA= -go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a h1:hTueTggXiuwPGnoeE5vV7x57bjA895Qhz55L2B0gRr4= -go.step.sm/linkedca v0.20.1-0.20230922094312-7d2f2f79fa6a/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= +go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU= +go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= From 4554f86f16e8840d9b76b13f0f0b0085ac60b882 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 25 Sep 2023 19:48:12 +0200 Subject: [PATCH 33/39] Make SCEP decrypter properties use `omitempty` --- authority/provisioner/scep.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 5281884b..7648d3b0 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -44,10 +44,10 @@ type SCEP struct { MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` // TODO(hs): also support a separate signer configuration? - DecrypterCertificate []byte `json:"decrypterCertificate"` - DecrypterKeyPEM []byte `json:"decrypterKeyPEM"` - DecrypterKeyURI string `json:"decrypterKey"` - DecrypterKeyPassword []byte `json:"decrypterKeyPassword"` + DecrypterCertificate []byte `json:"decrypterCertificate,omitempty"` + DecrypterKeyPEM []byte `json:"decrypterKeyPEM,omitempty"` + DecrypterKeyURI string `json:"decrypterKey,omitempty"` + DecrypterKeyPassword []byte `json:"decrypterKeyPassword,omitempty"` // Numerical identifier for the ContentEncryptionAlgorithm as defined in github.com/mozilla-services/pkcs7 // at https://github.com/mozilla-services/pkcs7/blob/33d05740a3526e382af6395d3513e73d4e66d1cb/encrypt.go#L63 From f1da256ca48a6717a1aad3304cd361b99ba7d28c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 25 Sep 2023 21:55:19 +0200 Subject: [PATCH 34/39] Change SCEP authority initialization --- authority/authority.go | 65 +++++++++++++++++++++++------------------- authority/options.go | 9 ++++++ scep/authority.go | 5 +++- scep/options.go | 42 +++++++++++++-------------- 4 files changed, 69 insertions(+), 52 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index f9c58ba6..a3d068a1 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -62,7 +62,10 @@ type Authority struct { x509Enforcers []provisioner.CertificateEnforcer // SCEP CA - scepAuthority *scep.Authority + scepAuthority *scep.Authority + scepCertificate *x509.Certificate + scepSigner crypto.Signer + scepDecrypter crypto.Decrypter // SSH CA sshHostPassword []byte @@ -673,37 +676,39 @@ func (a *Authority) init() error { case a.requiresSCEP() && a.GetSCEP() == nil: var options scep.Options options.Roots = a.rootX509Certs - options.Intermediates, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) - if err != nil { - return err - } + options.Intermediates = a.intermediateX509Certs options.SignerCert = options.Intermediates[0] - if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: a.config.IntermediateKey, - Password: a.password, - }); err != nil { - return err - } - - // TODO(hs): instead of creating the decrypter here, pass the - // intermediate key + chain down to the SCEP authority, - // and only instantiate it when required there. Is that possible? - // Also with entering passwords? - // TODO(hs): if moving the logic, try improving the logic for the - // decrypter password too? Right now it needs to be entered multiple - // times; I've observed it to be three times maximum, every time - // the intermediate key is read. - _, isRSA := options.Signer.Public().(*rsa.PublicKey) - if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA { - if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: a.password, - }); err == nil { - // only pass the decrypter down when it was successfully created, - // meaning it's an RSA key, and `CreateDecrypter` did not fail. - options.Decrypter = decrypter - options.DecrypterCert = options.Intermediates[0] + if a.config.IntermediateKey != "" { + if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: a.password, + }); err != nil { + return err + } + // TODO(hs): instead of creating the decrypter here, pass the + // intermediate key + chain down to the SCEP authority, + // and only instantiate it when required there. Is that possible? + // Also with entering passwords? + // TODO(hs): if moving the logic, try improving the logic for the + // decrypter password too? Right now it needs to be entered multiple + // times; I've observed it to be three times maximum, every time + // the intermediate key is read. + _, isRSA := options.Signer.Public().(*rsa.PublicKey) + if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA { + if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: a.password, + }); err == nil { + // only pass the decrypter down when it was successfully created, + // meaning it's an RSA key, and `CreateDecrypter` did not fail. + options.Decrypter = decrypter + options.DecrypterCert = options.Intermediates[0] + } } + } else { + options.Signer = a.scepSigner + options.Decrypter = a.scepDecrypter + options.DecrypterCert = a.scepCertificate } // provide the current SCEP provisioner names, so that the provisioners diff --git a/authority/options.go b/authority/options.go index bf443ed6..79cd6206 100644 --- a/authority/options.go +++ b/authority/options.go @@ -205,6 +205,15 @@ func WithX509SignerFunc(fn func() ([]*x509.Certificate, crypto.Signer, error)) O } } +func WithSCEPOptions(crt *x509.Certificate, s crypto.Signer, d crypto.Decrypter) Option { + return func(a *Authority) error { + a.scepCertificate = crt + a.scepSigner = s + a.scepDecrypter = d + return nil + } +} + // WithSSHUserSigner defines the signer used to sign SSH user certificates. func WithSSHUserSigner(s crypto.Signer) Option { return func(a *Authority) error { diff --git a/scep/authority.go b/scep/authority.go index 292c7004..8fa8a66f 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -83,7 +83,10 @@ func New(signAuth SignAuthority, opts Options) (*Authority, error) { // The validation includes a check if a decrypter is available, either // an authority wide decrypter, or a provisioner specific decrypter. func (a *Authority) Validate() error { - if a == nil { + // TODO(hs): don't return early + return nil //nolint:revive // validation temporarily disabled + + if a == nil { //nolint:govet // validation temporarily disabled return nil } diff --git a/scep/options.go b/scep/options.go index 8bc30a61..50c82338 100644 --- a/scep/options.go +++ b/scep/options.go @@ -2,7 +2,6 @@ package scep import ( "crypto" - "crypto/rsa" "crypto/x509" "errors" ) @@ -57,27 +56,28 @@ func (o *Options) Validate() error { return nil } - // If a decrypter is available, check that it's backed by an RSA key. According to the - // RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something - // different than RSA, but requires the encryption to be performed using the challenge - // password in that case. An older version of specification states that only RSA is - // supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other - // algorithms do not seem to be supported in certnanny/sscep, but it might work - // in micromdm/scep. Currently only RSA is allowed, but it might be an option - // to try other algorithms in the future. - decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) - if !ok { - return errors.New("only RSA keys are (currently) supported as decrypters") - } + // TODO(hs): reenable this validation + // // If a decrypter is available, check that it's backed by an RSA key. According to the + // // RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something + // // different than RSA, but requires the encryption to be performed using the challenge + // // password in that case. An older version of specification states that only RSA is + // // supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other + // // algorithms do not seem to be supported in certnanny/sscep, but it might work + // // in micromdm/scep. Currently only RSA is allowed, but it might be an option + // // to try other algorithms in the future. + // decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) + // if !ok { + // return errors.New("only RSA keys are (currently) supported as decrypters") + // } - // check if intermediate public key is the same as the decrypter public key. - // In certnanny/sscep it's mentioned that the signing key can be different - // from the decrypting (and encrypting) key. These options are only used and - // validated when the intermediate CA is also used as the decrypter, though, - // so they should match. - if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) { - return errors.New("mismatch between certificate chain and decrypter public keys") - } + // // check if intermediate public key is the same as the decrypter public key. + // // In certnanny/sscep it's mentioned that the signing key can be different + // // from the decrypting (and encrypting) key. These options are only used and + // // validated when the intermediate CA is also used as the decrypter, though, + // // so they should match. + // if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) { + // return errors.New("mismatch between certificate chain and decrypter public keys") + // } return nil } From 15c46ebbaa65427b069abcce907d07535a59d629 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 25 Sep 2023 22:00:30 +0200 Subject: [PATCH 35/39] Switch logic for SCEP initialization around --- authority/authority.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index a3d068a1..1ba480af 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -678,7 +678,11 @@ func (a *Authority) init() error { options.Roots = a.rootX509Certs options.Intermediates = a.intermediateX509Certs options.SignerCert = options.Intermediates[0] - if a.config.IntermediateKey != "" { + if a.scepSigner != nil { + options.Signer = a.scepSigner + options.Decrypter = a.scepDecrypter + options.DecrypterCert = a.scepCertificate + } else { if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.IntermediateKey, Password: a.password, @@ -705,10 +709,6 @@ func (a *Authority) init() error { options.DecrypterCert = options.Intermediates[0] } } - } else { - options.Signer = a.scepSigner - options.Decrypter = a.scepDecrypter - options.DecrypterCert = a.scepCertificate } // provide the current SCEP provisioner names, so that the provisioners From 4dc5a688fd0415e7b97cf3da8bf052cef2e0f250 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 25 Sep 2023 22:24:13 +0200 Subject: [PATCH 36/39] Set SCEP authority options once --- authority/authority.go | 43 +++++++++++++++++++++--------------------- authority/options.go | 17 +++++++++++++---- scep/authority.go | 5 +---- scep/options.go | 42 ++++++++++++++++++++--------------------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 1ba480af..875c3a14 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -62,10 +62,9 @@ type Authority struct { x509Enforcers []provisioner.CertificateEnforcer // SCEP CA - scepAuthority *scep.Authority - scepCertificate *x509.Certificate - scepSigner crypto.Signer - scepDecrypter crypto.Decrypter + scepOptions *scep.Options + validateSCEP bool + scepAuthority *scep.Authority // SSH CA sshHostPassword []byte @@ -126,6 +125,7 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) { var a = &Authority{ config: cfg, certificates: new(sync.Map), + validateSCEP: true, } // Apply options. @@ -674,15 +674,12 @@ func (a *Authority) init() error { // update that. switch { case a.requiresSCEP() && a.GetSCEP() == nil: - var options scep.Options - options.Roots = a.rootX509Certs - options.Intermediates = a.intermediateX509Certs - options.SignerCert = options.Intermediates[0] - if a.scepSigner != nil { - options.Signer = a.scepSigner - options.Decrypter = a.scepDecrypter - options.DecrypterCert = a.scepCertificate - } else { + if a.scepOptions == nil { + options := &scep.Options{ + Roots: a.rootX509Certs, + Intermediates: a.intermediateX509Certs, + SignerCert: a.intermediateX509Certs[0], + } if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ SigningKey: a.config.IntermediateKey, Password: a.password, @@ -709,21 +706,25 @@ func (a *Authority) init() error { options.DecrypterCert = options.Intermediates[0] } } - } - // provide the current SCEP provisioner names, so that the provisioners - // can be validated when the CA is started. - options.SCEPProvisionerNames = a.getSCEPProvisionerNames() + // provide the current SCEP provisioner names, so that the provisioners + // can be validated when the CA is started. + options.SCEPProvisionerNames = a.getSCEPProvisionerNames() + + a.scepOptions = options + } // create a new SCEP authority - scepAuthority, err := scep.New(a, options) + scepAuthority, err := scep.New(a, *a.scepOptions) if err != nil { return err } - // validate the SCEP authority - if err := scepAuthority.Validate(); err != nil { - a.initLogf("failed validating SCEP authority: %v", err) + if a.validateSCEP { + // validate the SCEP authority + if err := scepAuthority.Validate(); err != nil { + a.initLogf("failed validating SCEP authority: %v", err) + } } // set the SCEP authority diff --git a/authority/options.go b/authority/options.go index 79cd6206..f053b99c 100644 --- a/authority/options.go +++ b/authority/options.go @@ -18,6 +18,7 @@ import ( "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" + "github.com/smallstep/certificates/scep" ) // Option sets options to the Authority. @@ -205,11 +206,19 @@ func WithX509SignerFunc(fn func() ([]*x509.Certificate, crypto.Signer, error)) O } } -func WithSCEPOptions(crt *x509.Certificate, s crypto.Signer, d crypto.Decrypter) Option { +// func WithSCEPOptions(crt *x509.Certificate, s crypto.Signer, d crypto.Decrypter) Option { +// return func(a *Authority) error { +// a.scepCertificate = crt +// a.scepSigner = s +// a.scepDecrypter = d +// return nil +// } +// } + +func WithFullSCEPOptions(options *scep.Options) Option { return func(a *Authority) error { - a.scepCertificate = crt - a.scepSigner = s - a.scepDecrypter = d + a.scepOptions = options + a.validateSCEP = false return nil } } diff --git a/scep/authority.go b/scep/authority.go index 8fa8a66f..292c7004 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -83,10 +83,7 @@ func New(signAuth SignAuthority, opts Options) (*Authority, error) { // The validation includes a check if a decrypter is available, either // an authority wide decrypter, or a provisioner specific decrypter. func (a *Authority) Validate() error { - // TODO(hs): don't return early - return nil //nolint:revive // validation temporarily disabled - - if a == nil { //nolint:govet // validation temporarily disabled + if a == nil { return nil } diff --git a/scep/options.go b/scep/options.go index 50c82338..8bc30a61 100644 --- a/scep/options.go +++ b/scep/options.go @@ -2,6 +2,7 @@ package scep import ( "crypto" + "crypto/rsa" "crypto/x509" "errors" ) @@ -56,28 +57,27 @@ func (o *Options) Validate() error { return nil } - // TODO(hs): reenable this validation - // // If a decrypter is available, check that it's backed by an RSA key. According to the - // // RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something - // // different than RSA, but requires the encryption to be performed using the challenge - // // password in that case. An older version of specification states that only RSA is - // // supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other - // // algorithms do not seem to be supported in certnanny/sscep, but it might work - // // in micromdm/scep. Currently only RSA is allowed, but it might be an option - // // to try other algorithms in the future. - // decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) - // if !ok { - // return errors.New("only RSA keys are (currently) supported as decrypters") - // } + // If a decrypter is available, check that it's backed by an RSA key. According to the + // RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something + // different than RSA, but requires the encryption to be performed using the challenge + // password in that case. An older version of specification states that only RSA is + // supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other + // algorithms do not seem to be supported in certnanny/sscep, but it might work + // in micromdm/scep. Currently only RSA is allowed, but it might be an option + // to try other algorithms in the future. + decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey) + if !ok { + return errors.New("only RSA keys are (currently) supported as decrypters") + } - // // check if intermediate public key is the same as the decrypter public key. - // // In certnanny/sscep it's mentioned that the signing key can be different - // // from the decrypting (and encrypting) key. These options are only used and - // // validated when the intermediate CA is also used as the decrypter, though, - // // so they should match. - // if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) { - // return errors.New("mismatch between certificate chain and decrypter public keys") - // } + // check if intermediate public key is the same as the decrypter public key. + // In certnanny/sscep it's mentioned that the signing key can be different + // from the decrypting (and encrypting) key. These options are only used and + // validated when the intermediate CA is also used as the decrypter, though, + // so they should match. + if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) { + return errors.New("mismatch between certificate chain and decrypter public keys") + } return nil } From c0fbace88227fcfeb5f92415fa6158e4859638b3 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 26 Sep 2023 00:00:08 +0200 Subject: [PATCH 37/39] Address review remarks --- authority/authority.go | 8 ++++---- authority/options.go | 12 +++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 875c3a14..a4a76293 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -707,13 +707,13 @@ func (a *Authority) init() error { } } - // provide the current SCEP provisioner names, so that the provisioners - // can be validated when the CA is started. - options.SCEPProvisionerNames = a.getSCEPProvisionerNames() - a.scepOptions = options } + // provide the current SCEP provisioner names, so that the provisioners + // can be validated when the CA is started. + a.scepOptions.SCEPProvisionerNames = a.getSCEPProvisionerNames() + // create a new SCEP authority scepAuthority, err := scep.New(a, *a.scepOptions) if err != nil { diff --git a/authority/options.go b/authority/options.go index f053b99c..4fc5a20f 100644 --- a/authority/options.go +++ b/authority/options.go @@ -206,15 +206,9 @@ func WithX509SignerFunc(fn func() ([]*x509.Certificate, crypto.Signer, error)) O } } -// func WithSCEPOptions(crt *x509.Certificate, s crypto.Signer, d crypto.Decrypter) Option { -// return func(a *Authority) error { -// a.scepCertificate = crt -// a.scepSigner = s -// a.scepDecrypter = d -// return nil -// } -// } - +// WithFullSCEPOptions defines the options used for SCEP support. +// +// This feature is EXPERIMENTAL and might change at any time. func WithFullSCEPOptions(options *scep.Options) Option { return func(a *Authority) error { a.scepOptions = options From 4d5fbfa439681f94e95e46affb607667a6a7fe1f Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 25 Sep 2023 15:49:45 -0700 Subject: [PATCH 38/39] Fix redacted types in SCEP provisioner This commit uses the same types for the fields in the provisioner.SCEP type and the "redacted" models.SCEP. --- api/api.go | 6 +++--- api/models/scep.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/api.go b/api/api.go index 0dc961d8..c586a43a 100644 --- a/api/api.go +++ b/api/api.go @@ -245,10 +245,10 @@ func scepFromProvisioner(p *provisioner.SCEP) *models.SCEP { IncludeRoot: p.IncludeRoot, ExcludeIntermediate: p.ExcludeIntermediate, MinimumPublicKeyLength: p.MinimumPublicKeyLength, - DecrypterCertificate: redacted, - DecrypterKeyPEM: redacted, + DecrypterCertificate: []byte(redacted), + DecrypterKeyPEM: []byte(redacted), DecrypterKeyURI: redacted, - DecrypterKeyPassword: redacted, + DecrypterKeyPassword: []byte(redacted), EncryptionAlgorithmIdentifier: p.EncryptionAlgorithmIdentifier, Options: p.Options, Claims: p.Claims, diff --git a/api/models/scep.go b/api/models/scep.go index 71c003a7..c4fea502 100644 --- a/api/models/scep.go +++ b/api/models/scep.go @@ -25,10 +25,10 @@ type SCEP struct { IncludeRoot bool `json:"includeRoot"` ExcludeIntermediate bool `json:"excludeIntermediate"` MinimumPublicKeyLength int `json:"minimumPublicKeyLength"` - DecrypterCertificate string `json:"decrypterCertificate"` - DecrypterKeyPEM string `json:"decrypterKeyPEM"` + DecrypterCertificate []byte `json:"decrypterCertificate"` + DecrypterKeyPEM []byte `json:"decrypterKeyPEM"` DecrypterKeyURI string `json:"decrypterKey"` - DecrypterKeyPassword string `json:"decrypterKeyPassword"` + DecrypterKeyPassword []byte `json:"decrypterKeyPassword"` EncryptionAlgorithmIdentifier int `json:"encryptionAlgorithmIdentifier"` Options *provisioner.Options `json:"options,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` From 33bdae4a3478df320e7565273b97db6258a80e37 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 25 Sep 2023 15:57:01 -0700 Subject: [PATCH 39/39] Fix redacted tests --- api/api_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/api_test.go b/api/api_test.go index f9b707f2..efca024a 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -1623,10 +1623,10 @@ func TestProvisionersResponse_MarshalJSON(t *testing.T) { "includeRoot": true, "excludeIntermediate": true, "challenge": "*** REDACTED ***", - "decrypterCertificate": "*** REDACTED ***", + "decrypterCertificate": []byte("*** REDACTED ***"), "decrypterKey": "*** REDACTED ***", - "decrypterKeyPEM": "*** REDACTED ***", - "decrypterKeyPassword": "*** REDACTED ***", + "decrypterKeyPEM": []byte("*** REDACTED ***"), + "decrypterKeyPassword": []byte("*** REDACTED ***"), "minimumPublicKeyLength": 2048, "encryptionAlgorithmIdentifier": 2, },