Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UNIX socket TPM connection support #273

Merged
merged 7 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions kms/tpmkms/tpmkms_simulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ func withSimulator(t *testing.T) tpmp.NewTPMOption {
err := sim.Close()
require.NoError(t, err)
})
sim = simulator.New()
err := sim.Open()
sim, err := simulator.New()
require.NoError(t, err)
err = sim.Open()
require.NoError(t, err)
return tpmp.WithSimulator(sim)
}
Expand Down
5 changes: 3 additions & 2 deletions tpm/attestation/client_simulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ func withSimulator(t *testing.T) tpm.NewTPMOption {
err := sim.Close()
require.NoError(t, err)
})
sim = simulator.New()
err := sim.Open()
sim, err := simulator.New()
require.NoError(t, err)
err = sim.Open()
require.NoError(t, err)
return tpm.WithSimulator(sim)
}
Expand Down
15 changes: 15 additions & 0 deletions tpm/available/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package available

import (
"fmt"

"go.step.sm/crypto/tpm"
)

func Check(opts ...tpm.NewTPMOption) error {
t, err := tpm.New(opts...)
if err != nil {
return fmt.Errorf("failed creating TPM instance: %w", err)
}
return t.Available()
}
15 changes: 15 additions & 0 deletions tpm/internal/close/close.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package closer

import (
"io"

"github.com/smallstep/go-attestation/attest"
)

func RWC(rwc io.ReadWriteCloser) error {
return closeRWC(rwc)

Check warning on line 10 in tpm/internal/close/close.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/close/close.go#L9-L10

Added lines #L9 - L10 were not covered by tests
}

func AttestTPM(t *attest.TPM, c *attest.OpenConfig) error {
return attestTPM(t, c)
}
27 changes: 27 additions & 0 deletions tpm/internal/close/close_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build !windows
// +build !windows

package closer

import (
"io"

"github.com/google/go-tpm/tpmutil"
"github.com/smallstep/go-attestation/attest"

"go.step.sm/crypto/tpm/internal/socket"
)

func closeRWC(rwc io.ReadWriteCloser) error {
if _, ok := rwc.(*tpmutil.EmulatorReadWriteCloser); ok {
return nil // EmulatorReadWriteCloser automatically closes on every write/read cycle
}
return rwc.Close()

Check warning on line 19 in tpm/internal/close/close_others.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/close/close_others.go#L15-L19

Added lines #L15 - L19 were not covered by tests
}

func attestTPM(t *attest.TPM, c *attest.OpenConfig) error {
if _, ok := c.CommandChannel.(*socket.CommandChannelWithoutMeasurementLog); ok {
return nil // backed by tpmutil.EmulatorReadWriteCloser; already closed
}

Check warning on line 25 in tpm/internal/close/close_others.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/close/close_others.go#L24-L25

Added lines #L24 - L25 were not covered by tests
return t.Close()
}
18 changes: 18 additions & 0 deletions tpm/internal/close/close_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//go:build windows
// +build windows

package closer

import (
"io"

"github.com/smallstep/go-attestation/attest"
)

func closeRWC(rwc io.ReadWriteCloser) error {
return rwc.Close()
}

func attestTPM(t *attest.TPM, _ *attest.OpenConfig) error {
return t.Close()
}
4 changes: 3 additions & 1 deletion tpm/internal/open/open.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package open

import "io"
import (
"io"
)

func TPM(deviceName string) (io.ReadWriteCloser, error) {
return open(deviceName)
Expand Down
23 changes: 23 additions & 0 deletions tpm/internal/socket/socket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package socket

import (
"errors"
"io"
)

var (
ErrNotAvailable = errors.New("socket not available")
ErrNotSupported = errors.New("connecting to a TPM using a UNIX socket is not supported on Windows")
Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good.

There is limited support for UNIX sockets on Windows, but the proper solution are named pipes using https://github.com/microsoft/go-winio, for example, step connects to the agent using:

conn, err := winio.DialPipeContext(context.Background(), `\\.\\pipe\\openssh-ssh-agent`)

The same library can create a listener on a pipe using:

lis, err := winio.ListenPipe(`\\.\\pipe\\step-agent`, nil) 

If the simulator in windows can use those, they will not be difficult to implement in a future PR.

)

func New(path string) (io.ReadWriteCloser, error) {
return newSocket(path)

Check warning on line 14 in tpm/internal/socket/socket.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/socket/socket.go#L13-L14

Added lines #L13 - L14 were not covered by tests
}

type CommandChannelWithoutMeasurementLog struct {
io.ReadWriteCloser
}

func (c *CommandChannelWithoutMeasurementLog) MeasurementLog() ([]byte, error) {
return nil, nil

Check warning on line 22 in tpm/internal/socket/socket.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/socket/socket.go#L21-L22

Added lines #L21 - L22 were not covered by tests
}
25 changes: 25 additions & 0 deletions tpm/internal/socket/socket_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build !windows
// +build !windows

package socket

import (
"io"
"os"

"github.com/google/go-tpm/tpmutil"
)

func newSocket(path string) (io.ReadWriteCloser, error) {
if path == "" {
return nil, ErrNotAvailable
}
fi, err := os.Stat(path)
if err != nil { // TODO(hs): handle specific errors here?
return nil, err
}
if fi.Mode()&os.ModeSocket != 0 {
return tpmutil.NewEmulatorReadWriteCloser(path), nil
}
return nil, ErrNotAvailable

Check warning on line 24 in tpm/internal/socket/socket_others.go

View check run for this annotation

Codecov / codecov/patch

tpm/internal/socket/socket_others.go#L13-L24

Added lines #L13 - L24 were not covered by tests
}
12 changes: 12 additions & 0 deletions tpm/internal/socket/socket_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build windows
// +build windows

package socket

import (
"io"
)

func newSocket(_ string) (io.ReadWriteCloser, error) {
return nil, ErrNotSupported
}
15 changes: 3 additions & 12 deletions tpm/rand/rand_simulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"errors"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -26,18 +25,13 @@ func withSimulator(t *testing.T) tpm.NewTPMOption {
err := sim.Close()
require.NoError(t, err)
})
sim = simulator.New()
err := sim.Open()
sim, err := simulator.New()
require.NoError(t, err)
err = sim.Open()
require.NoError(t, err)
return tpm.WithSimulator(sim)
}

func withNewErrorSimulator(t *testing.T) tpm.NewTPMOption {
return func(t *tpm.TPM) error {
return errors.New("forced new error")
}
}

func TestNew(t *testing.T) {
r, err := New(withSimulator(t))
require.NoError(t, err)
Expand All @@ -55,7 +49,4 @@ func TestNew(t *testing.T) {
if assert.NotNil(t, rsaKey) {
require.Equal(t, 256, rsaKey.Size()) // 2048 bits; 256 bytes expected to have been read
}

_, err = New(withNewErrorSimulator(t))
require.Error(t, err)
}
6 changes: 3 additions & 3 deletions tpm/simulator/simulator_disabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
type NoSimulator struct {
}

func New() Simulator {
return &NoSimulator{}
func New() (Simulator, error) {
return &NoSimulator{}, errors.New("no simulator available")

Check warning on line 15 in tpm/simulator/simulator_disabled.go

View check run for this annotation

Codecov / codecov/patch

tpm/simulator/simulator_disabled.go#L14-L15

Added lines #L14 - L15 were not covered by tests
}

func (s *NoSimulator) Open() error {
return errors.New("no simulator available")
return errors.New("cannot open: no simulator available")

Check warning on line 19 in tpm/simulator/simulator_disabled.go

View check run for this annotation

Codecov / codecov/patch

tpm/simulator/simulator_disabled.go#L19

Added line #L19 was not covered by tests
}

func (s *NoSimulator) Close() error {
Expand Down
42 changes: 38 additions & 4 deletions tpm/simulator/simulator_enabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
package simulator

import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io"

Expand All @@ -12,22 +15,53 @@

type WrappingSimulator struct {
wrapped *gotpm.Simulator
seed *int64
}

func New() Simulator {
return &WrappingSimulator{}
type NewSimulatorOption func(ws *WrappingSimulator) error

func WithSeed(seed string) NewSimulatorOption {
return func(ws *WrappingSimulator) error {
b, err := hex.DecodeString(seed)
if err != nil {
return fmt.Errorf("failed decoding %q: %w", seed, err)
}
if len(b) != 8 {
return fmt.Errorf("%q has wrong number of bytes (%d)", seed, len(b))
}
var intSeed int64
buf := bytes.NewBuffer(b)
if err := binary.Read(buf, binary.BigEndian, &intSeed); err != nil {
return fmt.Errorf("failed reading %q into int64: %w", seed, err)
}
ws.seed = &intSeed
return nil

Check warning on line 38 in tpm/simulator/simulator_enabled.go

View check run for this annotation

Codecov / codecov/patch

tpm/simulator/simulator_enabled.go#L23-L38

Added lines #L23 - L38 were not covered by tests
}
}

func New(opts ...NewSimulatorOption) (Simulator, error) {
ws := &WrappingSimulator{}
for _, applyTo := range opts {
if err := applyTo(ws); err != nil {
return nil, fmt.Errorf("failed initializing TPM simulator: %w", err)
}

Check warning on line 47 in tpm/simulator/simulator_enabled.go

View check run for this annotation

Codecov / codecov/patch

tpm/simulator/simulator_enabled.go#L45-L47

Added lines #L45 - L47 were not covered by tests
}
return ws, nil
}

func (s *WrappingSimulator) Open() error {
var sim *gotpm.Simulator
var err error
if s.wrapped == nil {
sim, err = gotpm.Get()
if s.seed == nil {
sim, err = gotpm.Get()
} else {
sim, err = gotpm.GetWithFixedSeedInsecure(*s.seed)
}

Check warning on line 60 in tpm/simulator/simulator_enabled.go

View check run for this annotation

Codecov / codecov/patch

tpm/simulator/simulator_enabled.go#L59-L60

Added lines #L59 - L60 were not covered by tests
if err != nil {
return err
}
}

s.wrapped = sim
return nil
}
Expand Down