2021-02-12 11:03:08 +00:00
package provisioner
import (
2021-02-26 13:00:47 +00:00
"context"
2023-05-26 21:52:24 +00:00
"crypto"
2023-08-03 14:09:51 +00:00
"crypto/rsa"
2023-05-01 20:09:42 +00:00
"crypto/subtle"
2023-05-26 21:52:24 +00:00
"crypto/x509"
2023-06-01 10:10:54 +00:00
"encoding/pem"
2023-05-01 20:09:42 +00:00
"fmt"
"net/http"
2021-02-12 16:02:39 +00:00
"time"
2021-02-12 11:03:08 +00:00
"github.com/pkg/errors"
2023-05-01 20:09:42 +00:00
2023-05-26 21:52:24 +00:00
"go.step.sm/crypto/kms"
kmsapi "go.step.sm/crypto/kms/apiv1"
2023-07-26 22:55:39 +00:00
"go.step.sm/crypto/kms/uri"
2022-09-30 00:16:26 +00:00
"go.step.sm/linkedca"
2023-05-01 20:09:42 +00:00
"github.com/smallstep/certificates/webhook"
2021-02-12 11:03:08 +00:00
)
// SCEP is the SCEP provisioner type, an entity that can authorize the
// SCEP provisioning flow
type SCEP struct {
* base
2022-03-10 02:43:45 +00:00
ID string ` json:"-" `
Type string ` json:"type" `
Name string ` json:"name" `
2021-03-06 23:30:37 +00:00
ForceCN bool ` json:"forceCN,omitempty" `
ChallengePassword string ` json:"challenge,omitempty" `
2021-03-06 23:50:00 +00:00
Capabilities [ ] string ` json:"capabilities,omitempty" `
2022-03-10 02:43:45 +00:00
2022-01-19 10:31:33 +00:00
// IncludeRoot makes the provisioner return the CA root in addition to the
// intermediate in the GetCACerts response
IncludeRoot bool ` json:"includeRoot,omitempty" `
2022-03-10 02:43:45 +00:00
2023-09-04 12:55:27 +00:00
// ExcludeIntermediate makes the provisioner skip the intermediate CA in the
// GetCACerts response
ExcludeIntermediate bool ` json:"excludeIntermediate,omitempty" `
2021-05-06 20:56:28 +00:00
// MinimumPublicKeyLength is the minimum length for public keys in CSRs
2022-01-14 09:48:23 +00:00
MinimumPublicKeyLength int ` json:"minimumPublicKeyLength,omitempty" `
2022-03-10 02:43:45 +00:00
2023-07-26 22:55:39 +00:00
// TODO(hs): also support a separate signer configuration?
2023-09-25 17:48:12 +00:00
DecrypterCertificate [ ] byte ` json:"decrypterCertificate,omitempty" `
DecrypterKeyPEM [ ] byte ` json:"decrypterKeyPEM,omitempty" `
DecrypterKeyURI string ` json:"decrypterKey,omitempty" `
2023-09-26 17:36:58 +00:00
DecrypterKeyPassword string ` json:"decrypterKeyPassword,omitempty" `
2023-05-26 21:52:24 +00:00
2022-01-14 09:48:23 +00:00
// 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
2022-01-18 14:54:18 +00:00
// Defaults to 0, being DES-CBC
EncryptionAlgorithmIdentifier int ` json:"encryptionAlgorithmIdentifier,omitempty" `
2022-01-14 09:48:23 +00:00
Options * Options ` json:"options,omitempty" `
Claims * Claims ` json:"claims,omitempty" `
2022-03-24 11:36:12 +00:00
ctl * Controller
2022-01-31 13:43:46 +00:00
encryptionAlgorithm int
2023-05-01 20:09:42 +00:00
challengeValidationController * challengeValidationController
2023-09-21 10:01:03 +00:00
notificationController * notificationController
2023-05-26 21:52:24 +00:00
keyManager kmsapi . KeyManager
decrypter crypto . Decrypter
decrypterCertificate * x509 . Certificate
2023-07-26 22:55:39 +00:00
signer crypto . Signer
2023-08-03 23:55:52 +00:00
signerCertificate * x509 . Certificate
2021-02-12 11:03:08 +00:00
}
2021-02-12 11:03:08 +00:00
// GetID returns the provisioner unique identifier.
2021-05-03 19:48:20 +00:00
func ( s * SCEP ) GetID ( ) string {
if s . ID != "" {
return s . ID
}
return s . GetIDForToken ( )
}
2021-07-03 03:22:35 +00:00
// GetIDForToken returns an identifier that will be used to load the provisioner
// from a token.
func ( s * SCEP ) GetIDForToken ( ) string {
2021-02-12 11:03:08 +00:00
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 ( ) Type {
return 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.
2023-05-10 06:47:28 +00:00
func ( s * SCEP ) GetTokenID ( string ) ( string , error ) {
2021-02-12 11:03:08 +00:00
return "" , errors . New ( "scep provisioner does not implement GetTokenID" )
}
2021-02-12 16:02:39 +00:00
// GetOptions returns the configured provisioner options.
func ( s * SCEP ) GetOptions ( ) * Options {
return s . Options
2021-02-12 11:03:08 +00:00
}
2021-02-12 16:02:39 +00:00
// DefaultTLSCertDuration returns the default TLS cert duration enforced by
// the provisioner.
func ( s * SCEP ) DefaultTLSCertDuration ( ) time . Duration {
2022-03-10 02:43:45 +00:00
return s . ctl . Claimer . DefaultTLSCertDuration ( )
2021-02-12 11:03:08 +00:00
}
2023-05-01 20:09:42 +00:00
type challengeValidationController struct {
client * http . Client
webhooks [ ] * Webhook
}
// newChallengeValidationController creates a new challengeValidationController
// that performs challenge validation through webhooks.
2023-05-01 22:52:11 +00:00
func newChallengeValidationController ( client * http . Client , webhooks [ ] * Webhook ) * challengeValidationController {
2023-05-01 20:09:42 +00:00
scepHooks := [ ] * Webhook { }
for _ , wh := range webhooks {
if wh . Kind != linkedca . Webhook_SCEPCHALLENGE . String ( ) {
continue
}
if ! isCertTypeOK ( wh ) {
continue
}
scepHooks = append ( scepHooks , wh )
}
return & challengeValidationController {
client : client ,
webhooks : scepHooks ,
2023-05-01 22:52:11 +00:00
}
2023-05-01 20:09:42 +00:00
}
var (
2023-09-21 10:01:03 +00:00
ErrSCEPChallengeInvalid = errors . New ( "webhook server did not allow request" )
ErrSCEPNotificationFailed = errors . New ( "scep notification failed" )
2023-05-01 20:09:42 +00:00
)
// Validate executes zero or more configured webhooks to
// validate the SCEP challenge. If at least one of them indicates
// the challenge value is accepted, validation succeeds. In
// 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.
2023-11-08 18:43:13 +00:00
func ( c * challengeValidationController ) Validate ( ctx context . Context , csr * x509 . CertificateRequest , provisionerName , challenge , transactionID string ) error {
2023-05-01 20:09:42 +00:00
for _ , wh := range c . webhooks {
2023-09-07 12:11:53 +00:00
req , err := webhook . NewRequestBody ( webhook . WithX509CertificateRequest ( csr ) )
if err != nil {
return fmt . Errorf ( "failed creating new webhook request: %w" , err )
2023-05-01 20:09:42 +00:00
}
2023-11-08 19:09:52 +00:00
req . ProvisionerName = provisionerName
2023-09-07 12:11:53 +00:00
req . SCEPChallenge = challenge
req . SCEPTransactionID = transactionID
2023-05-01 20:09:42 +00:00
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 )
}
if resp . Allow {
return nil // return early when response is positive
}
}
return ErrSCEPChallengeInvalid
}
2023-09-21 10:01:03 +00:00
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 & notificationController {
client : client ,
webhooks : scepHooks ,
}
}
func ( c * notificationController ) Success ( ctx context . Context , csr * x509 . CertificateRequest , cert * x509 . Certificate , transactionID string ) error {
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 )
}
2023-09-21 10:05:58 +00:00
req . X509Certificate . Raw = cert . Raw // adding the full certificate DER bytes
2023-09-21 10:01:03 +00:00
req . SCEPTransactionID = transactionID
2023-09-22 10:40:14 +00:00
if _ , err = wh . DoWithContext ( ctx , c . client , req , nil ) ; err != nil {
return fmt . Errorf ( "failed executing webhook request: %w: %w" , ErrSCEPNotificationFailed , err )
2023-09-21 10:01:03 +00:00
}
}
2023-09-21 10:05:58 +00:00
return nil
2023-09-21 10:01:03 +00:00
}
2023-09-21 16:11:55 +00:00
func ( c * notificationController ) Failure ( ctx context . Context , csr * x509 . CertificateRequest , transactionID string , errorCode int , errorDescription string ) error {
2023-09-21 10:01:03 +00:00
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 )
}
req . SCEPTransactionID = transactionID
2023-09-21 16:11:55 +00:00
req . SCEPErrorCode = errorCode
req . SCEPErrorDescription = errorDescription
2023-09-22 10:40:14 +00:00
if _ , err = wh . DoWithContext ( ctx , c . client , req , nil ) ; err != nil {
return fmt . Errorf ( "failed executing webhook request: %w: %w" , ErrSCEPNotificationFailed , err )
2023-09-21 10:01:03 +00:00
}
}
2023-09-21 10:05:58 +00:00
return nil
2023-09-21 10:01:03 +00:00
}
2023-05-01 20:09:42 +00:00
// isCertTypeOK returns whether or not the webhook can be used
// with the SCEP challenge validation webhook controller.
func isCertTypeOK ( wh * Webhook ) bool {
if wh . CertType == linkedca . Webhook_ALL . String ( ) || wh . CertType == "" {
return true
}
return linkedca . Webhook_X509 . String ( ) == wh . CertType
}
2021-02-25 23:32:21 +00:00
// Init initializes and validates the fields of a SCEP type.
2021-02-12 11:03:08 +00:00
func ( s * SCEP ) Init ( config Config ) ( err error ) {
switch {
case s . Type == "" :
return errors . New ( "provisioner type cannot be empty" )
case s . Name == "" :
return errors . New ( "provisioner name cannot be empty" )
}
2021-05-06 20:56:28 +00:00
// Default to 2048 bits minimum public key length (for CSRs) if not set
if s . MinimumPublicKeyLength == 0 {
s . MinimumPublicKeyLength = 2048
}
if s . MinimumPublicKeyLength % 8 != 0 {
2022-01-18 14:54:18 +00:00
return errors . Errorf ( "%d bits is not exactly divisible by 8" , s . MinimumPublicKeyLength )
2021-05-06 20:56:28 +00:00
}
2023-08-03 14:09:51 +00:00
// Set the encryption algorithm to use
2022-01-19 10:31:33 +00:00
s . encryptionAlgorithm = s . EncryptionAlgorithmIdentifier // TODO(hs): we might want to upgrade the default security to AES-CBC?
2022-01-18 14:54:18 +00:00
if s . encryptionAlgorithm < 0 || s . encryptionAlgorithm > 4 {
return errors . New ( "only encryption algorithm identifiers from 0 to 4 are valid" )
2021-05-06 20:56:28 +00:00
}
2023-08-03 14:09:51 +00:00
// Prepare the SCEP challenge validator
2023-05-01 22:52:11 +00:00
s . challengeValidationController = newChallengeValidationController (
2023-05-01 20:09:42 +00:00
config . WebhookClient ,
s . GetOptions ( ) . GetWebhooks ( ) ,
2023-05-01 22:52:11 +00:00
)
2023-05-01 20:09:42 +00:00
2023-09-21 10:01:03 +00:00
// Prepare the SCEP notification controller
s . notificationController = newNotificationController (
config . WebhookClient ,
s . GetOptions ( ) . GetWebhooks ( ) ,
)
2023-09-22 09:10:22 +00:00
// 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 {
2023-09-22 09:46:51 +00:00
Type : kmsapi . SoftKMS ,
2023-09-22 09:10:22 +00:00
}
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 ,
2023-09-26 17:36:58 +00:00
Password : [ ] byte ( s . DecrypterKeyPassword ) ,
2023-09-22 09:10:22 +00:00
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?
2023-09-26 17:36:58 +00:00
Password : [ ] byte ( s . DecrypterKeyPassword ) ,
2023-09-22 09:10:22 +00:00
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 )
2023-07-26 22:55:39 +00:00
if err != nil {
return fmt . Errorf ( "failed parsing decrypter key: %w" , err )
}
2023-09-22 09:46:51 +00:00
var kmsType kmsapi . Type
2023-07-26 22:55:39 +00:00
switch {
case u . Scheme != "" :
2023-09-22 09:10:22 +00:00
kmsType = kms . Type ( u . Scheme )
2023-07-26 22:55:39 +00:00
default :
2023-09-22 09:46:51 +00:00
kmsType = kmsapi . SoftKMS
2023-07-26 22:55:39 +00:00
}
opts := kms . Options {
2023-09-22 09:10:22 +00:00
Type : kmsType ,
URI : s . DecrypterKeyURI ,
2023-07-26 22:55:39 +00:00
}
if s . keyManager , err = kms . New ( context . Background ( ) , opts ) ; err != nil {
2023-05-26 21:52:24 +00:00
return fmt . Errorf ( "failed initializing kms: %w" , err )
}
2023-07-26 22:55:39 +00:00
kmsDecrypter , ok := s . keyManager . ( kmsapi . Decrypter )
2023-05-26 21:52:24 +00:00
if ! ok {
2023-07-26 22:55:39 +00:00
return fmt . Errorf ( "%q is not a kmsapi.Decrypter" , opts . Type )
2023-05-26 21:52:24 +00:00
}
2023-07-26 22:55:39 +00:00
if kmsType != "softkms" { // TODO(hs): this should likely become more transparent?
2023-09-22 09:10:22 +00:00
decryptionKeyURI = u . Opaque
2023-07-26 22:55:39 +00:00
}
if s . decrypter , err = kmsDecrypter . CreateDecrypter ( & kmsapi . CreateDecrypterRequest {
2023-09-22 09:10:22 +00:00
DecryptionKey : decryptionKeyURI ,
2023-09-26 17:36:58 +00:00
Password : [ ] byte ( s . DecrypterKeyPassword ) ,
2023-08-04 20:47:02 +00:00
PasswordPrompter : kmsapi . NonInteractivePasswordPrompter ,
2023-07-26 22:55:39 +00:00
} ) ; err != nil {
return fmt . Errorf ( "failed creating decrypter: %w" , err )
}
if s . signer , err = s . keyManager . CreateSigner ( & kmsapi . CreateSignerRequest {
2023-09-22 09:10:22 +00:00
SigningKey : decryptionKeyURI , // TODO(hs): support distinct signer key in the future?
2023-09-26 17:36:58 +00:00
Password : [ ] byte ( s . DecrypterKeyPassword ) ,
2023-08-04 20:47:02 +00:00
PasswordPrompter : kmsapi . NonInteractivePasswordPrompter ,
2023-07-26 22:55:39 +00:00
} ) ; err != nil {
return fmt . Errorf ( "failed creating signer: %w" , err )
2023-05-26 21:52:24 +00:00
}
}
2023-07-26 22:55:39 +00:00
// 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 )
}
2023-08-03 23:55:52 +00:00
// the decrypter certificate is also the signer certificate
s . signerCertificate = s . decrypterCertificate
2023-07-26 22:55:39 +00:00
}
// TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager
2023-08-03 14:09:51 +00:00
// and load the certificate corresponding to the decryption key?
2023-08-04 20:47:02 +00:00
// Final validation for the decrypter.
if s . decrypter != nil {
2023-08-03 14:09:51 +00:00
decrypterPublicKey , ok := s . decrypter . Public ( ) . ( * rsa . PublicKey )
if ! ok {
return fmt . Errorf ( "only RSA keys are supported" )
}
2023-08-04 20:47:02 +00:00
if s . decrypterCertificate == nil {
return fmt . Errorf ( "provisioner %q does not have a decrypter certificate set" , s . Name )
}
2023-08-03 14:09:51 +00:00
if ! decrypterPublicKey . Equal ( s . decrypterCertificate . PublicKey ) {
2023-08-04 20:47:02 +00:00
return errors . New ( "mismatch between decrypter certificate and decrypter public keys" )
2023-08-03 14:09:51 +00:00
}
2023-07-26 22:55:39 +00:00
}
2021-02-25 23:32:21 +00:00
// TODO: add other, SCEP specific, options?
2022-04-21 23:20:38 +00:00
s . ctl , err = NewController ( s , s . Claims , config , s . Options )
2022-03-10 02:43:45 +00:00
return
2021-02-12 11:03:08 +00:00
}
2021-03-06 23:30:37 +00:00
// AuthorizeSign does not do any verification, because all verification is handled
2021-02-26 13:00:47 +00:00
// in the SCEP protocol. This method returns a list of modifiers / constraints
// on the resulting certificate.
2023-05-10 06:47:28 +00:00
func ( s * SCEP ) AuthorizeSign ( context . Context , string ) ( [ ] SignOption , error ) {
2021-02-26 13:00:47 +00:00
return [ ] SignOption {
2022-03-22 02:21:40 +00:00
s ,
2021-02-26 13:00:47 +00:00
// modifiers / withOptions
2023-07-20 17:59:38 +00:00
newProvisionerExtensionOption ( TypeSCEP , s . Name , "" ) . WithControllerOptions ( s . ctl ) ,
2021-02-26 13:00:47 +00:00
newForceCNOption ( s . ForceCN ) ,
2022-03-10 02:43:45 +00:00
profileDefaultDuration ( s . ctl . Claimer . DefaultTLSCertDuration ( ) ) ,
2021-02-26 13:00:47 +00:00
// validators
2021-05-06 20:56:28 +00:00
newPublicKeyMinimumLengthValidator ( s . MinimumPublicKeyLength ) ,
2022-03-10 02:43:45 +00:00
newValidityValidator ( s . ctl . Claimer . MinTLSCertDuration ( ) , s . ctl . Claimer . MaxTLSCertDuration ( ) ) ,
2022-05-05 10:32:53 +00:00
newX509NamePolicyValidator ( s . ctl . getPolicy ( ) . getX509 ( ) ) ,
2022-09-30 00:16:26 +00:00
s . ctl . newWebhookController ( nil , linkedca . Webhook_X509 ) ,
2021-02-26 13:00:47 +00:00
} , nil
}
2021-03-06 23:50:00 +00:00
// GetCapabilities returns the CA capabilities
func ( s * SCEP ) GetCapabilities ( ) [ ] string {
return s . Capabilities
}
2022-01-14 09:48:23 +00:00
2022-01-19 10:31:33 +00:00
// ShouldIncludeRootInChain indicates if the CA should
2022-01-14 09:48:23 +00:00
// return its intermediate, which is currently used for
2022-01-19 10:31:33 +00:00
// both signing and decryption, as well as the root in
// its chain.
func ( s * SCEP ) ShouldIncludeRootInChain ( ) bool {
return s . IncludeRoot
2022-01-14 09:48:23 +00:00
}
2023-09-04 12:55:27 +00:00
// ShouldIncludeIntermediateInChain indicates if the
// CA should include the intermediate CA certificate in the
// GetCACerts response. This is true by default, but can be
2023-09-04 13:36:37 +00:00
// overridden through configuration in case SCEP clients
2023-09-04 12:55:27 +00:00
// don't pick the right recipient.
func ( s * SCEP ) ShouldIncludeIntermediateInChain ( ) bool {
return ! s . ExcludeIntermediate
}
2022-01-14 09:48:23 +00:00
// GetContentEncryptionAlgorithm returns the numeric identifier
// for the pkcs7 package encryption algorithm to use.
func ( s * SCEP ) GetContentEncryptionAlgorithm ( ) int {
return s . encryptionAlgorithm
}
2023-05-01 20:09:42 +00:00
// ValidateChallenge validates the provided challenge. It starts by
// selecting the validation method to use, then performs validation
// according to that method.
2023-09-07 12:11:53 +00:00
func ( s * SCEP ) ValidateChallenge ( ctx context . Context , csr * x509 . CertificateRequest , challenge , transactionID string ) error {
2023-05-01 20:09:42 +00:00
if s . challengeValidationController == nil {
return fmt . Errorf ( "provisioner %q wasn't initialized" , s . Name )
}
switch s . selectValidationMethod ( ) {
case validationMethodWebhook :
2023-11-08 18:43:13 +00:00
return s . challengeValidationController . Validate ( ctx , csr , s . Name , challenge , transactionID )
2023-05-01 20:09:42 +00:00
default :
2023-05-04 09:43:57 +00:00
if subtle . ConstantTimeCompare ( [ ] byte ( s . ChallengePassword ) , [ ] byte ( challenge ) ) == 0 {
2023-05-01 20:09:42 +00:00
return errors . New ( "invalid challenge password provided" )
}
return nil
}
}
2023-09-21 10:01:03 +00:00
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 )
}
2023-09-21 16:11:55 +00:00
func ( s * SCEP ) NotifyFailure ( ctx context . Context , csr * x509 . CertificateRequest , transactionID string , errorCode int , errorDescription string ) error {
2023-09-21 10:01:03 +00:00
if s . notificationController == nil {
return fmt . Errorf ( "provisioner %q wasn't initialized" , s . Name )
}
2023-09-21 16:11:55 +00:00
return s . notificationController . Failure ( ctx , csr , transactionID , errorCode , errorDescription )
2023-09-21 10:01:03 +00:00
}
2023-05-01 20:09:42 +00:00
type validationMethod string
const (
validationMethodNone validationMethod = "none"
validationMethodStatic validationMethod = "static"
validationMethodWebhook validationMethod = "webhook"
)
// selectValidationMethod returns the method to validate SCEP
// challenges. If a webhook is configured with kind `SCEPCHALLENGE`,
// the webhook method will be used. If a challenge password is set,
// the static method is used. It will default to the `none` method.
func ( s * SCEP ) selectValidationMethod ( ) validationMethod {
if len ( s . challengeValidationController . webhooks ) > 0 {
return validationMethodWebhook
}
2023-05-04 09:43:57 +00:00
if s . ChallengePassword != "" {
2023-05-01 20:09:42 +00:00
return validationMethodStatic
}
return validationMethodNone
}
2023-05-26 21:52:24 +00:00
2023-08-03 14:09:51 +00:00
// 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.
2023-05-26 21:52:24 +00:00
func ( s * SCEP ) GetDecrypter ( ) ( * x509 . Certificate , crypto . Decrypter ) {
return s . decrypterCertificate , s . decrypter
}
2023-07-26 22:55:39 +00:00
2023-08-03 14:09:51 +00:00
// 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.
2023-07-26 22:55:39 +00:00
func ( s * SCEP ) GetSigner ( ) ( * x509 . Certificate , crypto . Signer ) {
2023-08-03 23:55:52 +00:00
return s . signerCertificate , s . signer
2023-07-26 22:55:39 +00:00
}