diff --git a/kms/apiv1/options.go b/kms/apiv1/options.go index 3b50b942..667d2fb5 100644 --- a/kms/apiv1/options.go +++ b/kms/apiv1/options.go @@ -18,6 +18,17 @@ type KeyManager interface { Close() error } +// KeyDeleter is an optional interface for KMS implementations that support +// deleting keys. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a later +// release. +type KeyDeleter interface { + DeleteKey(req *DeleteKeyRequest) error +} + // SearchableKeyManager is an optional interface for KMS implementations // that support searching for keys based on certain attributes. // @@ -54,6 +65,17 @@ type CertificateChainManager interface { StoreCertificateChain(req *StoreCertificateChainRequest) error } +// CertificateDeleter is an optional interface for KMS implementations that +// support deleting certificates. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a later +// release. +type CertificateDeleter interface { + DeleteCertificate(req *DeleteCertificateRequest) error +} + // NameValidator is an interface that KeyManager can implement to validate a // given name or URI. type NameValidator interface { @@ -151,6 +173,9 @@ const ( TPMKMS Type = "tpmkms" // MacKMS is the KMS implementation using macOS Keychain and Secure Enclave. MacKMS Type = "mackms" + // PlatformKMS is the KMS implementation that uses TPMKMS on Windows and + // Linux and MacKMS on macOS.. + PlatformKMS Type = "kms" ) // TypeOf returns the type of of the given uri. diff --git a/kms/platform/kms.go b/kms/platform/kms.go new file mode 100644 index 00000000..3015d09a --- /dev/null +++ b/kms/platform/kms.go @@ -0,0 +1,76 @@ +package platform + +import ( + "context" + "crypto" + "crypto/x509" + + "go.step.sm/crypto/kms/apiv1" +) + +const Scheme = "kms" + +func init() { + apiv1.Register(apiv1.PlatformKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) { + return New(ctx, opts) + }) +} + +type extendedKeyManager interface { + apiv1.KeyManager + apiv1.KeyDeleter + apiv1.CertificateManager + apiv1.CertificateChainManager +} + +var _ apiv1.KeyManager = (*KMS)(nil) +var _ apiv1.CertificateManager = (*KMS)(nil) +var _ apiv1.CertificateChainManager = (*KMS)(nil) + +type KMS struct { + backend extendedKeyManager +} + +func New(ctx context.Context, opts apiv1.Options) (*KMS, error) { + return newKMS(ctx, opts) +} + +func (k *KMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) { + return k.backend.GetPublicKey(req) +} + +func (k *KMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) { + return k.backend.CreateKey(req) +} + +func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) { + return k.backend.CreateSigner(req) +} + +func (k *KMS) Close() error { + return k.backend.Close() +} + +func (k *KMS) DeleteKey(req *apiv1.DeleteKeyRequest) error { + return k.backend.DeleteKey(req) +} + +func (k *KMS) LoadCertificate(req *apiv1.LoadCertificateRequest) (*x509.Certificate, error) { + return k.backend.LoadCertificate(req) +} + +func (k *KMS) StoreCertificate(req *apiv1.StoreCertificateRequest) error { + return k.backend.StoreCertificate(req) +} + +func (k *KMS) LoadCertificateChain(req *apiv1.LoadCertificateChainRequest) ([]*x509.Certificate, error) { + return k.backend.LoadCertificateChain(req) +} + +func (k *KMS) StoreCertificateChain(req *apiv1.StoreCertificateChainRequest) error { + if km, ok := k.backend.(apiv1.CertificateChainManager); ok { + return km.StoreCertificateChain(req) + } + + return apiv1.NotImplementedError{} +} diff --git a/kms/platform/kms_darwin.go b/kms/platform/kms_darwin.go new file mode 100644 index 00000000..cda75366 --- /dev/null +++ b/kms/platform/kms_darwin.go @@ -0,0 +1,33 @@ +package platform + +import ( + "context" + + "go.step.sm/crypto/kms/apiv1" + "go.step.sm/crypto/kms/mackms" +) + +var _ apiv1.SearchableKeyManager = (*KMS)(nil) + +func newKMS(ctx context.Context, opts apiv1.Options) (*KMS, error) { + if opts.Type == apiv1.TPMKMS { + return newTPMKMS(ctx, opts) + } + + km, err := mackms.New(ctx, opts) + if err != nil { + return nil, err + } + + return &KMS{ + backend: km, + }, nil +} + +func (k *KMS) SearchKeys(req *apiv1.SearchKeysRequest) (*apiv1.SearchKeysResponse, error) { + if km, ok := k.backend.(apiv1.SearchableKeyManager); ok { + return km.SearchKeys(req) + } + + return nil, apiv1.NotImplementedError{} +} diff --git a/kms/platform/kms_other.go b/kms/platform/kms_other.go new file mode 100644 index 00000000..fb844bad --- /dev/null +++ b/kms/platform/kms_other.go @@ -0,0 +1,13 @@ +//go:build !darwin && !windows + +package platform + +import ( + "context" + + "go.step.sm/crypto/kms/apiv1" +) + +func newKMS(ctx context.Context, opts apiv1.Options) (*KMS, error) { + return newTPMKMS(ctx, opts) +} diff --git a/kms/platform/kms_tpm.go b/kms/platform/kms_tpm.go new file mode 100644 index 00000000..bca38f2f --- /dev/null +++ b/kms/platform/kms_tpm.go @@ -0,0 +1,41 @@ +package platform + +import ( + "context" + + "go.step.sm/crypto/kms/apiv1" + "go.step.sm/crypto/kms/tpmkms" + "go.step.sm/crypto/tpm" +) + +var _ apiv1.Attester = (*KMS)(nil) + +func newTPMKMS(ctx context.Context, opts apiv1.Options) (*KMS, error) { + km, err := tpmkms.New(ctx, opts) + if err != nil { + return nil, err + } + + return &KMS{ + backend: km, + }, nil +} + +func NewWithTPM(ctx context.Context, t *tpm.TPM, opts ...tpmkms.Option) (*KMS, error) { + km, err := tpmkms.NewWithTPM(ctx, t, opts...) + if err != nil { + return nil, err + } + + return &KMS{ + backend: km, + }, nil +} + +func (k *KMS) CreateAttestation(req *apiv1.CreateAttestationRequest) (*apiv1.CreateAttestationResponse, error) { + if km, ok := k.backend.(apiv1.Attester); ok { + return km.CreateAttestation(req) + } + + return nil, apiv1.NotImplementedError{} +} diff --git a/kms/platform/kms_windows.go b/kms/platform/kms_windows.go new file mode 100644 index 00000000..3de31245 --- /dev/null +++ b/kms/platform/kms_windows.go @@ -0,0 +1,38 @@ +//go:build windows + +package platform + +import ( + "context" + + "go.step.sm/crypto/kms/apiv1" + "go.step.sm/crypto/kms/capi" + "go.step.sm/crypto/kms/uri" +) + +func newKMS(ctx context.Context, opts apiv1.Options) (*KMS, error) { + if opts.Type == apiv1.CAPIKMS { + km, err := capi.New(ctx, opts) + if err != nil { + return nil, err + } + + return &KMS{ + backend: km, + }, nil + } + + if opts.URI != "" { + u, err := uri.Parse(opts.URI) + if err != nil { + return nil, err + } + + if !u.Has("enable-cng") { + u.Values.Set("enable-cng", "true") + } + opts.URI = u.String() + } + + return newTPMKMS(ctx, opts) +}