From 63e36ecd7a860ca847b8fd7c20dd0cc761d9638e Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 11 May 2020 18:47:22 -0700 Subject: [PATCH] Refactor the initialization of KeyManagers. --- kms/apiv1/options.go | 17 +++++++++++++ kms/apiv1/registry.go | 27 +++++++++++++++++++++ kms/cloudkms/cloudkms.go | 6 +++++ kms/kms.go | 36 ++++++++++------------------ kms/softkms/softkms.go | 6 +++++ kms/yubikey/yubikey.go | 6 +++++ kms/yubikey/yubikey_no_cgo.go | 45 ++++++----------------------------- 7 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 kms/apiv1/registry.go diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 62ca97e9..c136e3f4 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -1,11 +1,28 @@ package apiv1 import ( + "crypto" + "crypto/x509" "strings" "github.com/pkg/errors" ) +// KeyManager is the interface implemented by all the KMS. +type KeyManager interface { + GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error) + CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error) + CreateSigner(req *CreateSignerRequest) (crypto.Signer, error) + Close() error +} + +// CertificateManager is the interface implemented by the KMS that can load and +// store x509.Certificates. +type CertificateManager interface { + LoadCerticate(req *LoadCertificateRequest) (*x509.Certificate, error) + StoreCertificate(req *StoreCertificateRequest) error +} + // ErrNotImplemented type ErrNotImplemented struct { msg string diff --git a/kms/apiv1/registry.go b/kms/apiv1/registry.go new file mode 100644 index 00000000..5a8cf4db --- /dev/null +++ b/kms/apiv1/registry.go @@ -0,0 +1,27 @@ +package apiv1 + +import ( + "context" + "sync" +) + +var registry = new(sync.Map) + +// KeyManagerNewFunc is the type that represents the method to initialize a new +// KeyManager. +type KeyManagerNewFunc func(ctx context.Context, opts Options) (KeyManager, error) + +// Register adds to the registry a method to create a KeyManager of type t. +func Register(t Type, fn KeyManagerNewFunc) { + registry.Store(t, fn) +} + +// LoadKeyManagerNewFunc returns the function initialize a KayManager. +func LoadKeyManagerNewFunc(t Type) (KeyManagerNewFunc, bool) { + v, ok := registry.Load(t) + if !ok { + return nil, false + } + fn, ok := v.(KeyManagerNewFunc) + return fn, ok +} diff --git a/kms/cloudkms/cloudkms.go b/kms/cloudkms/cloudkms.go index d434ce48..01cbcab2 100644 --- a/kms/cloudkms/cloudkms.go +++ b/kms/cloudkms/cloudkms.go @@ -93,6 +93,12 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudKMS, error) { }, nil } +func init() { + apiv1.Register(apiv1.CloudKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + return New(ctx, opts) + }) +} + // NewCloudKMS creates a CloudKMS with a given client. func NewCloudKMS(client KeyManagementClient) *CloudKMS { return &CloudKMS{ diff --git a/kms/kms.go b/kms/kms.go index 880bb9df..8daf148f 100644 --- a/kms/kms.go +++ b/kms/kms.go @@ -2,30 +2,18 @@ package kms import ( "context" - "crypto" - "crypto/x509" "strings" "github.com/pkg/errors" "github.com/smallstep/certificates/kms/apiv1" - "github.com/smallstep/certificates/kms/cloudkms" - "github.com/smallstep/certificates/kms/softkms" - "github.com/smallstep/certificates/kms/yubikey" ) // KeyManager is the interface implemented by all the KMS. -type KeyManager interface { - GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) - CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) - CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) - Close() error -} +type KeyManager = apiv1.KeyManager -// CertificateManager is the interface implemented by the KMS that can load and store x509.Certificates. -type CertificateManager interface { - LoadCerticate(req *apiv1.LoadCertificateRequest) (*x509.Certificate, error) - StoreCertificate(req *apiv1.StoreCertificateRequest) error -} +// CertificateManager is the interface implemented by the KMS that can load and +// store x509.Certificates. +type CertificateManager = apiv1.CertificateManager // New initializes a new KMS from the given type. func New(ctx context.Context, opts apiv1.Options) (KeyManager, error) { @@ -33,14 +21,14 @@ func New(ctx context.Context, opts apiv1.Options) (KeyManager, error) { return nil, err } - switch apiv1.Type(strings.ToLower(opts.Type)) { - case apiv1.DefaultKMS, apiv1.SoftKMS: - return softkms.New(ctx, opts) - case apiv1.CloudKMS: - return cloudkms.New(ctx, opts) - case apiv1.YubiKey: - return yubikey.New(ctx, opts) - default: + t := apiv1.Type(strings.ToLower(opts.Type)) + if t == apiv1.DefaultKMS { + t = apiv1.SoftKMS + } + + fn, ok := apiv1.LoadKeyManagerNewFunc(t) + if !ok { return nil, errors.Errorf("unsupported kms type '%s'", opts.Type) } + return fn(ctx, opts) } diff --git a/kms/softkms/softkms.go b/kms/softkms/softkms.go index fb38a1c5..3db9cbcc 100644 --- a/kms/softkms/softkms.go +++ b/kms/softkms/softkms.go @@ -52,6 +52,12 @@ func New(ctx context.Context, opts apiv1.Options) (*SoftKMS, error) { return &SoftKMS{}, nil } +func init() { + apiv1.Register(apiv1.SoftKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + return New(ctx, opts) + }) +} + // Close is a noop that just returns nil. func (k *SoftKMS) Close() error { return nil diff --git a/kms/yubikey/yubikey.go b/kms/yubikey/yubikey.go index f97a677c..adde5fe6 100644 --- a/kms/yubikey/yubikey.go +++ b/kms/yubikey/yubikey.go @@ -42,6 +42,12 @@ func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) { }, nil } +func init() { + apiv1.Register(apiv1.YubiKey, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + return New(ctx, opts) + }) +} + // LoadCertificate implements kms.CertificateManager and loads a certificate // from the YubiKey. func (k *YubiKey) LoadCertificate(req *apiv1.LoadCertificateRequest) (*x509.Certificate, error) { diff --git a/kms/yubikey/yubikey_no_cgo.go b/kms/yubikey/yubikey_no_cgo.go index 916cf151..6ed7c630 100644 --- a/kms/yubikey/yubikey_no_cgo.go +++ b/kms/yubikey/yubikey_no_cgo.go @@ -4,47 +4,16 @@ package yubikey import ( "context" - "crypto" - "crypto/x509" + "os" + "path/filepath" "github.com/pkg/errors" "github.com/smallstep/certificates/kms/apiv1" ) -// YubiKey implements the KMS interface on a YubiKey. -type YubiKey struct{} - -// New always fails without CGO. -func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) { - return nil, errors.New("YubiKey is not supported without cgo") -} - -// LoadCertificate always fails without CGO. -func (k *YubiKey) LoadCertificate(req *apiv1.LoadCertificateRequest) (*x509.Certificate, error) { - return nil, errors.New("YubiKey is not supported without cgo") -} - -// StoreCertificate always fails without CGO. -func (k *YubiKey) StoreCertificate(req *apiv1.StoreCertificateRequest) error { - return errors.New("YubiKey is not supported without cgo") -} - -// GetPublicKey always fails without CGO. -func (k *YubiKey) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) { - return nil, errors.New("YubiKey is not supported without cgo") -} - -// CreateKey always fails without CGO. -func (k *YubiKey) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) { - return nil, errors.New("YubiKey is not supported without cgo") -} - -// CreateSigner always fails without CGO. -func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) { - return nil, errors.New("YubiKey is not supported without cgo") -} - -// Close always fails without CGO. -func (k *YubiKey) Close() error { - return errors.New("YubiKey is not supported without cgo") +func init() { + apiv1.Register(apiv1.YubiKey, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + name := filepath.Base(os.Args[0]) + return nil, errors.Errorf("unsupported kms type 'yubikey': %s is compiled without cgo support", name) + }) }