scep: remove Interface and the dependency to pkg/errors (#872)

* scep: documented the package

* scep/api: removed some top level constants

* scep: removed dependency to pkg/errors

* scep/api: documented the package
This commit is contained in:
Panagiotis Siatras 2022-03-24 17:08:23 +02:00 committed by Mariano Cano
parent bca74cb6a7
commit a852223717
3 changed files with 35 additions and 54 deletions

View file

@ -1,3 +1,4 @@
// Package api implements a SCEP HTTP server.
package api package api
import ( import (
@ -31,12 +32,6 @@ const (
const maxPayloadSize = 2 << 20 const maxPayloadSize = 2 << 20
const (
certChainHeader = "application/x-x509-ca-ra-cert"
leafHeader = "application/x-x509-ca-cert"
pkiOperationHeader = "application/x-pki-message"
)
// request is a SCEP server request. // request is a SCEP server request.
type request struct { type request struct {
Operation string Operation string
@ -54,17 +49,19 @@ type response struct {
// handler is the SCEP request handler. // handler is the SCEP request handler.
type handler struct { type handler struct {
Auth scep.Interface auth *scep.Authority
} }
// New returns a new SCEP API router. // New returns a new SCEP API router.
func New(scepAuth scep.Interface) api.RouterHandler { func New(auth *scep.Authority) api.RouterHandler {
return &handler{scepAuth} return &handler{
auth: auth,
}
} }
// Route traffic and implement the Router interface. // Route traffic and implement the Router interface.
func (h *handler) Route(r api.Router) { func (h *handler) Route(r api.Router) {
getLink := h.Auth.GetLinkExplicit getLink := h.auth.GetLinkExplicit
r.MethodFunc(http.MethodGet, getLink("{provisionerName}/*", false, nil), h.lookupProvisioner(h.Get)) r.MethodFunc(http.MethodGet, getLink("{provisionerName}/*", false, nil), h.lookupProvisioner(h.Get))
r.MethodFunc(http.MethodGet, getLink("{provisionerName}", false, nil), h.lookupProvisioner(h.Get)) r.MethodFunc(http.MethodGet, getLink("{provisionerName}", false, nil), h.lookupProvisioner(h.Get))
r.MethodFunc(http.MethodPost, getLink("{provisionerName}/*", false, nil), h.lookupProvisioner(h.Post)) r.MethodFunc(http.MethodPost, getLink("{provisionerName}/*", false, nil), h.lookupProvisioner(h.Post))
@ -192,7 +189,7 @@ func (h *handler) lookupProvisioner(next http.HandlerFunc) http.HandlerFunc {
return return
} }
p, err := h.Auth.LoadProvisionerByName(provisionerName) p, err := h.auth.LoadProvisionerByName(provisionerName)
if err != nil { if err != nil {
fail(w, err) fail(w, err)
return return
@ -213,7 +210,7 @@ func (h *handler) lookupProvisioner(next http.HandlerFunc) http.HandlerFunc {
// GetCACert returns the CA certificates in a SCEP response // GetCACert returns the CA certificates in a SCEP response
func (h *handler) GetCACert(ctx context.Context) (response, error) { func (h *handler) GetCACert(ctx context.Context) (response, error) {
certs, err := h.Auth.GetCACertificates(ctx) certs, err := h.auth.GetCACertificates(ctx)
if err != nil { if err != nil {
return response{}, err return response{}, err
} }
@ -246,7 +243,7 @@ func (h *handler) GetCACert(ctx context.Context) (response, error) {
// GetCACaps returns the CA capabilities in a SCEP response // GetCACaps returns the CA capabilities in a SCEP response
func (h *handler) GetCACaps(ctx context.Context) (response, error) { func (h *handler) GetCACaps(ctx context.Context) (response, error) {
caps := h.Auth.GetCACaps(ctx) caps := h.auth.GetCACaps(ctx)
res := response{ res := response{
Operation: opnGetCACaps, Operation: opnGetCACaps,
@ -283,7 +280,7 @@ func (h *handler) PKIOperation(ctx context.Context, req request) (response, erro
P7: p7, P7: p7,
} }
if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { if err := h.auth.DecryptPKIEnvelope(ctx, msg); err != nil {
return response{}, err return response{}, err
} }
@ -296,7 +293,7 @@ func (h *handler) PKIOperation(ctx context.Context, req request) (response, erro
// a certificate exists; then it will use RenewalReq. Adding the challenge check here may be a small breaking change for clients. // 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. // We'll have to see how it works out.
if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq {
challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword) challengeMatches, err := h.auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword)
if err != nil { if err != nil {
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("error when checking password")) return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("error when checking password"))
} }
@ -314,7 +311,7 @@ func (h *handler) PKIOperation(ctx context.Context, req request) (response, erro
// Authentication by the (self-signed) certificate with an optional challenge is required; supporting renewals incl. verification // Authentication by the (self-signed) certificate with an optional challenge is required; supporting renewals incl. verification
// of the client cert is not. // of the client cert is not.
certRep, err := h.Auth.SignCSR(ctx, csr, msg) certRep, err := h.auth.SignCSR(ctx, csr, msg)
if err != nil { if err != nil {
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, fmt.Errorf("error when signing new certificate: %w", err)) return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, fmt.Errorf("error when signing new certificate: %w", err))
} }
@ -354,7 +351,7 @@ func fail(w http.ResponseWriter, err error) {
} }
func (h *handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, failError error) (response, error) { func (h *handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, failError error) (response, error) {
certRepMsg, err := h.Auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), failError.Error()) certRepMsg, err := h.auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), failError.Error())
if err != nil { if err != nil {
return response{}, err return response{}, err
} }
@ -367,14 +364,14 @@ func (h *handler) createFailureResponse(ctx context.Context, csr *x509.Certifica
func contentHeader(r response) string { func contentHeader(r response) string {
switch r.Operation { switch r.Operation {
case opnGetCACert:
if r.CACertNum > 1 {
return certChainHeader
}
return leafHeader
case opnPKIOperation:
return pkiOperationHeader
default: default:
return "text/plain" return "text/plain"
case opnGetCACert:
if r.CACertNum > 1 {
return "application/x-x509-ca-ra-cert"
}
return "application/x-x509-ca-cert"
case opnPKIOperation:
return "application/x-pki-message"
} }
} }

View file

@ -4,33 +4,19 @@ import (
"context" "context"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"errors"
"fmt"
"net/url" "net/url"
"github.com/smallstep/certificates/authority/provisioner"
microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util" microx509util "github.com/micromdm/scep/v2/cryptoutil/x509util"
microscep "github.com/micromdm/scep/v2/scep" microscep "github.com/micromdm/scep/v2/scep"
"github.com/pkg/errors"
"go.mozilla.org/pkcs7" "go.mozilla.org/pkcs7"
"go.step.sm/crypto/x509util" "go.step.sm/crypto/x509util"
"github.com/smallstep/certificates/authority/provisioner"
) )
// Interface is the SCEP authority interface.
type Interface interface {
LoadProvisionerByName(string) (provisioner.Interface, error)
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
GetCACertificates(ctx context.Context) ([]*x509.Certificate, error)
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error)
CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error)
MatchChallengePassword(ctx context.Context, password string) (bool, error)
GetCACaps(ctx context.Context) []string
}
// Authority is the layer that handles all SCEP interactions. // Authority is the layer that handles all SCEP interactions.
type Authority struct { type Authority struct {
prefix string prefix string
@ -180,12 +166,12 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
p7c, err := pkcs7.Parse(msg.P7.Content) p7c, err := pkcs7.Parse(msg.P7.Content)
if err != nil { if err != nil {
return errors.Wrap(err, "error parsing pkcs7 content") return fmt.Errorf("error parsing pkcs7 content: %w", err)
} }
envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.decrypter) envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.decrypter)
if err != nil { if err != nil {
return errors.Wrap(err, "error decrypting encrypted pkcs7 content") return fmt.Errorf("error decrypting encrypted pkcs7 content: %w", err)
} }
msg.pkiEnvelope = envelope msg.pkiEnvelope = envelope
@ -194,19 +180,19 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
case microscep.CertRep: case microscep.CertRep:
certs, err := microscep.CACerts(msg.pkiEnvelope) certs, err := microscep.CACerts(msg.pkiEnvelope)
if err != nil { if err != nil {
return errors.Wrap(err, "error extracting CA certs from pkcs7 degenerate data") return fmt.Errorf("error extracting CA certs from pkcs7 degenerate data: %w", err)
} }
msg.CertRepMessage.Certificate = certs[0] msg.CertRepMessage.Certificate = certs[0]
return nil return nil
case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq: case microscep.PKCSReq, microscep.UpdateReq, microscep.RenewalReq:
csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope) csr, err := x509.ParseCertificateRequest(msg.pkiEnvelope)
if err != nil { if err != nil {
return errors.Wrap(err, "parse CSR from pkiEnvelope") return fmt.Errorf("parse CSR from pkiEnvelope: %w", err)
} }
// check for challengePassword // check for challengePassword
cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope) cp, err := microx509util.ParseChallengePassword(msg.pkiEnvelope)
if err != nil { if err != nil {
return errors.Wrap(err, "parse challenge password in pkiEnvelope") return fmt.Errorf("parse challenge password in pkiEnvelope: %w", err)
} }
msg.CSRReqMessage = &microscep.CSRReqMessage{ msg.CSRReqMessage = &microscep.CSRReqMessage{
RawDecrypted: msg.pkiEnvelope, RawDecrypted: msg.pkiEnvelope,
@ -215,7 +201,7 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
} }
return nil return nil
case microscep.GetCRL, microscep.GetCert, microscep.CertPoll: case microscep.GetCRL, microscep.GetCert, microscep.CertPoll:
return errors.Errorf("not implemented") return errors.New("not implemented")
} }
return nil return nil
@ -274,19 +260,19 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod)
signOps, err := p.AuthorizeSign(ctx, "") signOps, err := p.AuthorizeSign(ctx, "")
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error retrieving authorization options from SCEP provisioner") return nil, fmt.Errorf("error retrieving authorization options from SCEP provisioner: %w", err)
} }
opts := provisioner.SignOptions{} opts := provisioner.SignOptions{}
templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error creating template options from SCEP provisioner") return nil, fmt.Errorf("error creating template options from SCEP provisioner: %w", err)
} }
signOps = append(signOps, templateOptions) signOps = append(signOps, templateOptions)
certChain, err := a.signAuth.Sign(csr, opts, signOps...) certChain, err := a.signAuth.Sign(csr, opts, signOps...)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error generating certificate for order") return nil, fmt.Errorf("error generating certificate for order: %w", err)
} }
// take the issued certificate (only); https://tools.ietf.org/html/rfc8894#section-3.3.2 // take the issued certificate (only); https://tools.ietf.org/html/rfc8894#section-3.3.2

View file

@ -1,3 +1,4 @@
// Package scep implements Simple Certificate Enrollment Protocol related functionality.
package scep package scep
import ( import (
@ -5,9 +6,6 @@ import (
"encoding/asn1" "encoding/asn1"
microscep "github.com/micromdm/scep/v2/scep" microscep "github.com/micromdm/scep/v2/scep"
//"github.com/smallstep/certificates/scep/pkcs7"
"go.mozilla.org/pkcs7" "go.mozilla.org/pkcs7"
) )