Skip to content

Commit

Permalink
Merge pull request #253 from smallstep/herman/tpmkms
Browse files Browse the repository at this point in the history
Add TPMKMS implementation
  • Loading branch information
hslatman authored Jun 7, 2023
2 parents 2ae83ed + ac157ce commit 541f830
Show file tree
Hide file tree
Showing 24 changed files with 3,828 additions and 18 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ defaulttest:
$Q $(GOFLAGS) gotestsum -- -coverpkg=./... -coverprofile=defaultcoverage.out -covermode=atomic ./...

simulatortest:
$Q $(GOFLAGS) CGO_ENALBED=1 gotestsum -- -coverpkg=./tpm -coverprofile=simulatorcoverage.out -covermode=atomic -tags tpmsimulator ./tpm
$Q $(GOFLAGS) CGO_ENABLED=1 gotestsum -- -coverpkg=./tpm/...,./kms/tpmkms -coverprofile=simulatorcoverage.out -covermode=atomic -tags tpmsimulator ./tpm ./kms/tpmkms

combinecoverage:
cat defaultcoverage.out simulatorcoverage.out > coverage.out
cat defaultcoverage.out > coverage.out
tail -n +2 simulatorcoverage.out >> coverage.out

race:
$Q $(GOFLAGS) gotestsum -- -race ./...
Expand Down
23 changes: 20 additions & 3 deletions kms/apiv1/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ type CertificateManager interface {
StoreCertificate(req *StoreCertificateRequest) error
}

// CertificateChainManager is the interface implemented by KMS implementations
// that can load certificate chains. The LoadCertificateChain method uses the
// same request object as the LoadCertificate method of the CertificateManager
// interfaces. When the LoadCertificateChain method is called, the certificate
// chain stored through the CertificateChain property in the StoreCertificateRequest
// will be returned, partially reusing the StoreCertificateRequest object.
type CertificateChainManager interface {
LoadCertificateChain(req *LoadCertificateChainRequest) ([]*x509.Certificate, error)
StoreCertificateChain(req *StoreCertificateChainRequest) error
}

// NameValidator is an interface that KeyManager can implement to validate a
// given name or URI.
type NameValidator interface {
Expand Down Expand Up @@ -61,7 +72,7 @@ func (e NotImplementedError) Error() string {
}

// AlreadyExistsError is the type of error returned if a key already exists. This
// is currently only implmented on pkcs11.
// is currently only implmented for pkcs11 and tpmkms.
type AlreadyExistsError struct {
Message string
}
Expand Down Expand Up @@ -95,6 +106,8 @@ const (
AzureKMS Type = "azurekms"
// CAPIKMS
CAPIKMS Type = "capi"
// TPMKMS
TPMKMS Type = "tpmkms"
)

// Options are the KMS options. They represent the kms object in the ca.json.
Expand All @@ -109,7 +122,7 @@ type Options struct {
// https://tools.ietf.org/html/rfc7512 and represents the configuration used
// to connect to the KMS.
//
// Used by: pkcs11
// Used by: pkcs11, tpmkms
URI string `json:"uri,omitempty"`

// Pin used to access the PKCS11 module. It can be defined in the URI using
Expand All @@ -130,6 +143,10 @@ type Options struct {

// Profile to use in AmazonKMS.
Profile string `json:"profile,omitempty"`

// StorageDirectory is the path to a directory to
// store serialized TPM objects. Only used by the TPMKMS.
StorageDirectory string `json:"storageDirectory,omitempty"`
}

// Validate checks the fields in Options.
Expand All @@ -142,7 +159,7 @@ func (o *Options) Validate() error {
switch Type(typ) {
case DefaultKMS, SoftKMS: // Go crypto based kms.
case CloudKMS, AmazonKMS, AzureKMS: // Cloud based kms.
case YubiKey, PKCS11: // Hardware based kms.
case YubiKey, PKCS11, TPMKMS: // Hardware based kms.
case SSHAgentKMS, CAPIKMS: // Others
default:
return fmt.Errorf("unsupported kms type %s", o.Type)
Expand Down
64 changes: 56 additions & 8 deletions kms/apiv1/requests.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package apiv1

import (
"context"
"crypto"
"crypto/x509"
"fmt"
Expand Down Expand Up @@ -129,7 +130,7 @@ type GetPublicKeyRequest struct {
type CreateKeyRequest struct {
// Name represents the key name or label used to identify a key.
//
// Used by: awskms, cloudkms, azurekms, pkcs11, yubikey.
// Used by: awskms, cloudkms, azurekms, pkcs11, yubikey, tpmkms.
Name string

// SignatureAlgorithm represents the type of key to create.
Expand Down Expand Up @@ -163,8 +164,9 @@ type CreateKeyRequest struct {

// CreateKeyResponse is the response value of the kms.CreateKey method.
type CreateKeyResponse struct {
Name string
PublicKey crypto.PublicKey
Name string
PublicKey crypto.PublicKey
// PrivateKey is only used by softkms
PrivateKey crypto.PrivateKey
CreateSignerRequest CreateSignerRequest
}
Expand Down Expand Up @@ -194,6 +196,10 @@ type LoadCertificateRequest struct {
Name string
}

// LoadCertificateChainRequest is the parameter used in the LoadCertificateChain method of
// a CertificateChainManager. It's an alias for LoadCertificateRequest.
type LoadCertificateChainRequest LoadCertificateRequest

// StoreCertificateRequest is the parameter used in the StoreCertificate method
// of a CertificateManager.
type StoreCertificateRequest struct {
Expand All @@ -207,6 +213,13 @@ type StoreCertificateRequest struct {
Extractable bool
}

// StoreCertificateChainRequest is the parameter used in the StoreCertificateChain method
// of a CertificateChainManager.
type StoreCertificateChainRequest struct {
Name string
CertificateChain []*x509.Certificate
}

// CreateAttestationRequest is the parameter used in the kms.CreateAttestation
// method.
//
Expand All @@ -215,19 +228,54 @@ type StoreCertificateRequest struct {
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
type CreateAttestationRequest struct {
Name string
Name string
AttestationClient AttestationClient // TODO(hs): a better name; Attestor perhaps, but that's already taken
}

// AttestationClient is an interface that provides a pluggable method for
// attesting Attestation Keys (AKs).
type AttestationClient interface {
Attest(context.Context) ([]*x509.Certificate, error)
}

// CertificationParameters encapsulates the inputs for certifying an application key.
// Only TPM 2.0 is supported at this point.
//
// This struct was copied from github.com/google/go-attestation, preventing an
// additional dependency in this package.
type CertificationParameters struct {
// Public represents the key's canonical encoding (a TPMT_PUBLIC structure).
// It includes the public key and signing parameters.
Public []byte
// CreateData represents the properties of a TPM 2.0 key. It is encoded
// as a TPMS_CREATION_DATA structure.
CreateData []byte
// CreateAttestation represents an assertion as to the details of the key.
// It is encoded as a TPMS_ATTEST structure.
CreateAttestation []byte
// CreateSignature represents a signature of the CreateAttestation structure.
// It is encoded as a TPMT_SIGNATURE structure.
CreateSignature []byte
}

// CreateAttestationResponse is the response value of the kms.CreateAttestation
// method.
//
// If a non-empty CertificateChain is returned, the first x509.Certificate is
// the same as the one in the Certificate property.
//
// When an attestation is created for a TPM key, the CertificationParameters
// property will have a record of the certification parameters at the time of
// key attestation.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release.
type CreateAttestationResponse struct {
Certificate *x509.Certificate
CertificateChain []*x509.Certificate
PublicKey crypto.PublicKey
PermanentIdentifier string
Certificate *x509.Certificate
CertificateChain []*x509.Certificate
PublicKey crypto.PublicKey
CertificationParameters *CertificationParameters
PermanentIdentifier string
}
2 changes: 2 additions & 0 deletions kms/capi/capi.go
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,5 @@ func (s *CAPISigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([
func (s *CAPISigner) Public() crypto.PublicKey {
return s.PublicKey
}

var _ apiv1.CertificateManager = (*CAPIKMS)(nil)
2 changes: 2 additions & 0 deletions kms/kmsfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func (f *fakeCM) StoreCertificate(req *apiv1.StoreCertificateRequest) error {
return nil
}

var _ apiv1.CertificateManager = (*fakeCM)(nil)

func TestMain(m *testing.M) {
apiv1.Register(apiv1.Type("fake"), func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
return &fakeCM{}, nil
Expand Down
2 changes: 2 additions & 0 deletions kms/pkcs11/pkcs11.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,5 @@ func findCertificate(ctx P11, rawuri string) (*x509.Certificate, error) {
}
return cert, nil
}

var _ apiv1.CertificateManager = (*PKCS11)(nil)
11 changes: 11 additions & 0 deletions kms/tpmkms/no_tpmkms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build notpmkms
// +build notpmkms

package tpmkms

func init() {
apiv1.Register(apiv1.TPMKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
name := filepath.Base(os.Args[0])
return nil, errors.Errorf("unsupported KMS type 'tpmkms': %s is compiled without TPM KMS support", name)
})
}
Loading

0 comments on commit 541f830

Please sign in to comment.