Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
813695d
Start implementation of Windows TPM enrollment
strideynet May 8, 2023
e6137d1
Basic device data collection for windows
strideynet May 8, 2023
b606716
Add AK get/creatiom
strideynet May 8, 2023
2ca61e5
Add helpers for converting tpm protos
strideynet May 9, 2023
54efbc2
Don't create AK in inappropriate circumstances
strideynet May 9, 2023
11cdee7
Furhter simplify AK load/create
strideynet May 9, 2023
d0a5bcb
Add tests for proto/attest conversions
strideynet May 9, 2023
a1eca1e
Ensure that digestalg varies between test cases
strideynet May 9, 2023
4960b43
More accurate proto field name
strideynet May 9, 2023
90b9c49
Missing license header
strideynet May 9, 2023
57509b9
Add credential fingerprinting function
strideynet May 10, 2023
3b048ec
Add getDeviceCredential implementation for windows
strideynet May 10, 2023
2c958ba
Add dependencies so this builds
strideynet May 10, 2023
bcf5f43
Fix generation of credential id
strideynet May 10, 2023
5a4ec19
Introduce AKPublic field
strideynet May 10, 2023
410dc76
Collect other key data
strideynet May 11, 2023
9e5bae4
Add some additional debug logging
strideynet May 11, 2023
91487c7
Add more specific serial number fields to dcd
strideynet May 11, 2023
a37a6c6
Use faster powershell call for determining OS version
strideynet May 15, 2023
ed98cbd
Fix missing field in DeviceFromResource
strideynet May 15, 2023
d5b2202
Add link to to-do issue
strideynet May 15, 2023
f743ce4
Merge remote-tracking branch 'origin/master' into strideynet/windows-…
strideynet May 15, 2023
990b443
Add packages necessary for enterprise submodule
strideynet May 15, 2023
dc06f74
Fix import orders
strideynet May 15, 2023
d168829
bump go-tpm-tools to latest versions
strideynet May 15, 2023
d99fb0f
Merge remote-tracking branch 'origin/master' into strideynet/windows-…
strideynet May 16, 2023
ed1342c
Tidy up returned errors
strideynet May 16, 2023
31a3dbf
Add failure case test for Linux enrollment
strideynet May 16, 2023
4c82533
move linux device fake to lib/devicetrust/testenv
strideynet May 16, 2023
1a7f3d7
Add test to exercise `RunCeremony`
strideynet May 17, 2023
c69f99c
Tidier assertion messages
strideynet May 17, 2023
14f5d72
Further simplifcations of test assertions/errors
strideynet May 17, 2023
a422d2a
Apply suggestions from code review
strideynet May 22, 2023
0acf776
Further fixes as per the llama's suggestions
strideynet May 22, 2023
972eaa9
Further simplification and header on logs
strideynet May 22, 2023
56bf50f
Use BadParameter rather than platform unsupported
strideynet May 22, 2023
027de3b
Add further notes on RSAness of `go-attestation`
strideynet May 22, 2023
a9122c0
Minor adjustments to comments
strideynet May 22, 2023
78fc1f9
Unused import removed
strideynet May 22, 2023
188e7ef
License headers
strideynet May 22, 2023
751017c
rename `mustRandomBytes` -> `randomBytes`
strideynet May 22, 2023
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
51 changes: 47 additions & 4 deletions lib/devicetrust/enroll/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@ import (
"runtime"

"github.com/gravitational/trace"
"golang.org/x/exp/slices"

devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/devicetrust"
)

// RunCeremony performs the client-side device enrollment ceremony.
func RunCeremony(ctx context.Context, devicesClient devicepb.DeviceTrustServiceClient, enrollToken string) (*devicepb.Device, error) {
// Start by checking the OSType, this lets us exit early with a nicer message
// for non-supported OSes.
if getOSType() != devicepb.OSType_OS_TYPE_MACOS {
return nil, trace.BadParameter("device enrollment not supported for current OS (%v)", runtime.GOOS)
osType := getOSType()
Comment thread
strideynet marked this conversation as resolved.
if !slices.Contains([]devicepb.OSType{
devicepb.OSType_OS_TYPE_MACOS,
devicepb.OSType_OS_TYPE_WINDOWS,
}, osType) {
return nil, trace.BadParameter(
"device enrollment not supported for current OS (%s)",
types.ResourceOSTypeToString(osType),
)
}

init, err := enrollInit()
Expand Down Expand Up @@ -57,10 +66,21 @@ func RunCeremony(ctx context.Context, devicesClient devicepb.DeviceTrustServiceC
// Unimplemented errors are not expected to happen after this point.

// 2. Challenge.
// Only macOS is supported, see the guard at the beginning of the method.
if err := enrollDeviceMacOS(stream, resp); err != nil {
switch osType {
case devicepb.OSType_OS_TYPE_MACOS:
err = enrollDeviceMacOS(stream, resp)
// err handled below
case devicepb.OSType_OS_TYPE_WINDOWS:
err = enrollDeviceTPM(stream, resp)
// err handled below
default:
// This should be caught by the OSType guard at start of function.
panic("no enrollment function provided for os")
}
if err != nil {
return nil, trace.Wrap(err)
}

resp, err = stream.Recv()
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -93,6 +113,29 @@ func enrollDeviceMacOS(stream devicepb.DeviceTrustService_EnrollDeviceClient, re
return trace.Wrap(err)
}

func enrollDeviceTPM(stream devicepb.DeviceTrustService_EnrollDeviceClient, resp *devicepb.EnrollDeviceResponse) error {
challenge := resp.GetTpmChallenge()
switch {
case challenge == nil:
return trace.BadParameter("unexpected challenge payload from server: %T", resp.Payload)
case challenge.EncryptedCredential == nil:
return trace.BadParameter("missing encrypted_credential in challenge from server")
case len(challenge.AttestationNonce) == 0:
return trace.BadParameter("missing attestation_nonce in challenge from server")
}

challengeResponse, err := solveTPMEnrollChallenge(challenge)
if err != nil {
return trace.Wrap(err)
}
err = stream.Send(&devicepb.EnrollDeviceRequest{
Payload: &devicepb.EnrollDeviceRequest_TpmChallengeResponse{
TpmChallengeResponse: challengeResponse,
},
})
return trace.Wrap(err)
}

func getDeviceOSType() devicepb.OSType {
switch runtime.GOOS {
case "darwin":
Expand Down
48 changes: 43 additions & 5 deletions lib/devicetrust/enroll/enroll_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"os"
"testing"

"github.com/gravitational/trace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand All @@ -37,25 +38,61 @@ func TestRunCeremony(t *testing.T) {

macOSDev1, err := testenv.NewFakeMacOSDevice()
require.NoError(t, err, "NewFakeMacOSDevice failed")
windowsDev1 := testenv.NewFakeWindowsDevice()

tests := []struct {
name string
dev fakeDevice
name string
dev fakeDevice
assertErr func(t *testing.T, err error)
assertGotDevice func(t *testing.T, device *devicepb.Device)
}{
{
name: "macOS device",
name: "macOS device succeeds",
dev: macOSDev1,
assertErr: func(t *testing.T, err error) {
assert.NoError(t, err, "RunCeremony returned an error")
},
assertGotDevice: func(t *testing.T, d *devicepb.Device) {
assert.NotNil(t, d, "RunCeremony returned nil device")
},
},
{
name: "windows device succeeds",
dev: windowsDev1,
assertErr: func(t *testing.T, err error) {
assert.NoError(t, err, "RunCeremony returned an error")
},
assertGotDevice: func(t *testing.T, d *devicepb.Device) {
require.NotNil(t, d, "RunCeremony returned nil device")
require.NotNil(t, d.Credential, "device credential is nil")
assert.Equal(t, windowsDev1.CredentialID, d.Credential.Id, "device credential mismatch")
},
},
{
name: "linux device fails",
dev: testenv.NewFakeLinuxDevice(),
assertErr: func(t *testing.T, err error) {
require.Error(t, err)
assert.True(
t, trace.IsBadParameter(err), "RunCeremony did not return a BadParameter error",
)
assert.ErrorContains(t, err, "linux", "RunCeremony error mismatch")
},
assertGotDevice: func(t *testing.T, d *devicepb.Device) {
assert.Nil(t, d, "RunCeremony returned an unexpected, non-nil device")
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
*enroll.GetOSType = test.dev.GetOSType
*enroll.EnrollInit = test.dev.EnrollDeviceInit
*enroll.SignChallenge = test.dev.SignChallenge
*enroll.SolveTPMEnrollChallenge = test.dev.SolveTPMEnrollChallenge

got, err := enroll.RunCeremony(ctx, devices, "faketoken")
require.NoError(t, err, "RunCeremony failed")
assert.NotNil(t, got, "RunCeremony returned nil device")
test.assertErr(t, err)
test.assertGotDevice(t, got)
})
}
}
Expand Down Expand Up @@ -85,4 +122,5 @@ type fakeDevice interface {
EnrollDeviceInit() (*devicepb.EnrollDeviceInit, error)
GetOSType() devicepb.OSType
SignChallenge(chal []byte) (sig []byte, err error)
SolveTPMEnrollChallenge(challenge *devicepb.TPMEnrollChallenge) (*devicepb.TPMEnrollChallengeResponse, error)
}
9 changes: 5 additions & 4 deletions lib/devicetrust/enroll/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
package enroll

var (
CollectDeviceData = &collectDeviceData
EnrollInit = &enrollInit
GetOSType = &getOSType
SignChallenge = &signChallenge
CollectDeviceData = &collectDeviceData
EnrollInit = &enrollInit
GetOSType = &getOSType
SignChallenge = &signChallenge
SolveTPMEnrollChallenge = &solveTPMEnrollChallenge
)
9 changes: 5 additions & 4 deletions lib/devicetrust/enroll/native_shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import "github.com/gravitational/teleport/lib/devicetrust/native"

// vars below are used to fake OSes and switch implementations for tests.
var (
collectDeviceData = native.CollectDeviceData
enrollInit = native.EnrollDeviceInit
getOSType = getDeviceOSType
signChallenge = native.SignChallenge
collectDeviceData = native.CollectDeviceData
enrollInit = native.EnrollDeviceInit
getOSType = getDeviceOSType
signChallenge = native.SignChallenge
solveTPMEnrollChallenge = native.SolveTPMEnrollChallenge
)
9 changes: 8 additions & 1 deletion lib/devicetrust/native/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

package native

import devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
import (
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
)

// EnrollDeviceInit creates the initial enrollment data for the device.
// This includes fetching or creating a device credential, collecting device
Expand All @@ -39,3 +41,8 @@ func SignChallenge(chal []byte) (sig []byte, err error) {
func GetDeviceCredential() (*devicepb.DeviceCredential, error) {
return getDeviceCredential()
}

// SolveTPMEnrollChallenge completes a TPM enrollment challenge.
func SolveTPMEnrollChallenge(challenge *devicepb.TPMEnrollChallenge) (*devicepb.TPMEnrollChallengeResponse, error) {
return solveTPMEnrollChallenge(challenge)
}
12 changes: 9 additions & 3 deletions lib/devicetrust/native/device_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ func collectDeviceData() (*devicepb.DeviceCollectedData, error) {
return nil, trace.Wrap(statusErrorFromC(res))
}

sn := C.GoString(dd.serial_number)
return &devicepb.DeviceCollectedData{
CollectTime: timestamppb.Now(),
OsType: devicepb.OSType_OS_TYPE_MACOS,
SerialNumber: C.GoString(dd.serial_number),
CollectTime: timestamppb.Now(),
OsType: devicepb.OSType_OS_TYPE_MACOS,
SerialNumber: sn,
SystemSerialNumber: sn,
Comment thread
strideynet marked this conversation as resolved.
}, nil
}

Expand Down Expand Up @@ -142,3 +144,7 @@ func getDeviceCredential() (*devicepb.DeviceCredential, error) {
func statusErrorFromC(res C.int32_t) error {
return &statusError{status: int32(res)}
}

func solveTPMEnrollChallenge(challenge *devicepb.TPMEnrollChallenge) (*devicepb.TPMEnrollChallengeResponse, error) {
return nil, trace.BadParameter("called solveTPMEnrollChallenge on darwin")
}
Loading