Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/peterbourgon/diskv/v3 v3.0.1
github.com/pkg/errors v0.9.1
github.com/schollz/jsonstore v1.1.0
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca
github.com/smallstep/go-attestation v0.4.9
github.com/stretchr/testify v1.11.1
go.uber.org/mock v0.6.0
golang.org/x/crypto v0.50.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -758,8 +758,10 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4=
github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/go-attestation v0.4.8 h1:yX7oiDFYlXywl+9Hss1RvhcSyPB7q9mFmGZSNhEfi1Q=
github.com/smallstep/go-attestation v0.4.8/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/go-attestation v0.4.9 h1:/fVmzB8A8tk7B2PCGPlszWzyl/GDcEQbynqcg2PMaZ0=
github.com/smallstep/go-attestation v0.4.9/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
Expand Down
3 changes: 3 additions & 0 deletions kms/tpmkms/tpmkms.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ func ParseTPMOptions(u *uri.URI) []tpm.NewTPMOption {
if storageDirectory := u.Get("storage-directory"); storageDirectory != "" {
opts = append(opts, tpm.WithStore(storage.NewDirstore(storageDirectory)))
}
if u.Get("store-location") == "machine" {
opts = append(opts, tpm.WithMachineKey())
}
return opts
}

Expand Down
6 changes: 4 additions & 2 deletions tpm/ak.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,10 @@ func (t *TPM) DeleteAK(ctx context.Context, name string) (err error) {
return fmt.Errorf("failed deleting AK %q because %d key(s) exist that were attested by it", name, len(keys))
}

var attestErr error
if err := t.attestTPM.DeleteKey(ak.Data); err != nil { // TODO: we could add a DeleteAK to go-attestation; under the hood it's loaded the same as a key though.
return fmt.Errorf("failed deleting AK %q: %w", name, err)
// Preserve the PCP error but still clean up file-store metadata below.
attestErr = fmt.Errorf("failed deleting AK %q: %w", name, err)
}

if err := t.store.DeleteAK(name); err != nil {
Expand All @@ -298,7 +300,7 @@ func (t *TPM) DeleteAK(ctx context.Context, name string) (err error) {
return fmt.Errorf("failed persisting storage: %w", err)
}

return
return attestErr
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the goal to be able to act on this error case? A typed error might help.

}

// AttestationParameters returns information about the AK, typically used to
Expand Down
4 changes: 4 additions & 0 deletions tpm/internal/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ type CreateConfig struct {
// Size is used to specify the bit size of the key or elliptic curve. For
// example, '256' is used to specify curve P-256.
Size int
// MachineKey instructs the Windows NCrypt PCP provider to create the key in
// the machine (local machine) key store rather than the current user store.
// Only relevant on Windows; ignored on other platforms.
MachineKey bool
}

func (c *CreateConfig) Validate() error {
Expand Down
2 changes: 1 addition & 1 deletion tpm/internal/key/key_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func create(_ io.ReadWriteCloser, keyName string, config CreateConfig) ([]byte, error) {
pcp, err := openPCP()
pcp, err := openPCP(config.MachineKey)
if err != nil {
return nil, fmt.Errorf("failed to open PCP: %w", err)
}
Expand Down
18 changes: 15 additions & 3 deletions tpm/internal/key/pcp_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (
// The below is documented in this Microsoft whitepaper:
// https://github.com/Microsoft/TSS.MSR/blob/master/PCPTool.v11/Using%20the%20Windows%208%20Platform%20Crypto%20Provider%20and%20Associated%20TPM%20Functionality.pdf
ncryptOverwriteKeyFlag = 0x80
// ncryptMachineKeyFlag instructs NCrypt to create or open a key in the
// machine (local machine) key store rather than the current user key store.
ncryptMachineKeyFlag uint32 = 0x00000020
// Key usage value for generic keys
nCryptPropertyPCPKeyUsagePolicyGeneric = 0x3
// Key usage value for AKs.
Expand Down Expand Up @@ -282,7 +285,8 @@ func getNCryptBufferProperty(hnd uintptr, field string) ([]byte, error) {

// winPCP represents a reference to the Platform Crypto Provider.
type winPCP struct {
hProv uintptr
hProv uintptr
machineKey bool
}

// Close releases all resources managed by the Handle.
Expand All @@ -301,8 +305,13 @@ func (h *winPCP) newKey(name string, alg string, length uint32, policy uint32) (
return 0, nil, nil, err
}

var flags uint32
if h.machineKey {
flags = ncryptMachineKeyFlag
}

// Create a persistent RSA key of the specified name.
r, _, msg := nCryptCreatePersistedKey.Call(h.hProv, uintptr(unsafe.Pointer(&kh)), uintptr(unsafe.Pointer(&utf16RSA[0])), uintptr(unsafe.Pointer(&utf16Name[0])), 0, 0)
r, _, msg := nCryptCreatePersistedKey.Call(h.hProv, uintptr(unsafe.Pointer(&kh)), uintptr(unsafe.Pointer(&utf16RSA[0])), uintptr(unsafe.Pointer(&utf16Name[0])), 0, uintptr(flags))
if r != 0 {
if tpmErr := maybeWinErr(r); tpmErr != nil {
msg = tpmErr
Expand Down Expand Up @@ -491,10 +500,13 @@ func decodeKeyBlob(keyBlob []byte) ([]byte, []byte, error) {
}

// openPCP initializes a reference to the Microsoft PCP provider.
// Pass machineKey=true to create and open keys in the machine (local machine)
// key store rather than the current user key store.
// The Caller is expected to call Close() when they are done.
func openPCP() (*winPCP, error) {
func openPCP(machineKey bool) (*winPCP, error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing this via openPCP, it looks better to me if passed via the KeyConfig.

var err error
var h winPCP
h.machineKey = machineKey
pname, err := windows.UTF16FromString(pcpProviderName)
if err != nil {
return nil, err
Expand Down
13 changes: 9 additions & 4 deletions tpm/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ func (t *TPM) CreateKey(ctx context.Context, name string, config CreateKeyConfig
}

createConfig := internalkey.CreateConfig{
Algorithm: config.Algorithm,
Size: config.Size,
Algorithm: config.Algorithm,
Size: config.Size,
MachineKey: t.options.attestConfig.MachineKey,
}
if err := t.validate(&createConfig); err != nil {
return nil, fmt.Errorf("invalid key creation parameters: %w", err)
Expand Down Expand Up @@ -386,8 +387,12 @@ func (t *TPM) DeleteKey(ctx context.Context, name string) (err error) {
return fmt.Errorf("failed getting key %q: %w", name, err)
}

var attestErr error
if err := t.attestTPM.DeleteKey(key.Data); err != nil {
return fmt.Errorf("failed deleting key %q: %w", name, err)
// Preserve the PCP error but still clean up file-store metadata below.
// On Windows the PCP key may be inaccessible when the reset command runs
// in a different session than the agent service that created the key.
attestErr = fmt.Errorf("failed deleting key %q: %w", name, err)
}

if err := t.store.DeleteKey(name); err != nil {
Expand All @@ -398,7 +403,7 @@ func (t *TPM) DeleteKey(ctx context.Context, name string) (err error) {
return fmt.Errorf("failed persisting storage: %w", err)
}

return
return attestErr
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the remark for the other case

}

// Signer returns a crypto.Signer backed by the Key.
Expand Down
13 changes: 13 additions & 0 deletions tpm/tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ func WithCommandChannel(commandChannel CommandChannel) NewTPMOption {
}
}

// WithMachineKey configures the TPM to create and open keys in the machine
// (local machine) key store rather than the current user key store. On Windows
// this causes NCRYPT_MACHINE_KEY_FLAG to be passed to NCrypt key operations.
// This option has no effect on non-Windows platforms.
func WithMachineKey() NewTPMOption {
return func(o *options) error {
o.attestConfig.MachineKey = true
return nil
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option will affect operations on all keys going through the specific tpm.TPM instance it is applied on. It might be OK, if separate tpm.TPM instances are used, but I was wondering if the alternative was considered that passes this config for specific keys.

// WithCapabilities explicitly sets the capabilities rather
// than acquiring them from the TPM directly. The primary use
// for this option is to ease testing different TPM capabilities.
Expand Down Expand Up @@ -241,6 +252,7 @@ func (t *TPM) initializeCommandChannel() error {
t.attestConfig = &attest.OpenConfig{
TPMVersion: t.options.attestConfig.TPMVersion,
CommandChannel: t.commandChannel,
MachineKey: t.options.attestConfig.MachineKey,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option probably shouldn't sit on attestConfig. That's used for higher level config of opening a TPM in a specific mode / configuration. Can probably sit on options directly, since it's just passed around.

}
return nil
}
Expand Down Expand Up @@ -275,6 +287,7 @@ func (t *TPM) initializeCommandChannel() error {
t.attestConfig = &attest.OpenConfig{
TPMVersion: t.options.attestConfig.TPMVersion,
CommandChannel: t.commandChannel,
MachineKey: t.options.attestConfig.MachineKey,
}

return nil
Expand Down
Loading