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

Fix TPM 1.2 migration #195

Merged
merged 4 commits into from
Jul 23, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions tpm/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,40 @@ func unseal(rw io.ReadWriter, keyHandle tpmutil.Handle, sealed *tpmStoredData, c
return outb, &ra1, &ra2, ret, nil
}

// authorizeMigrationKey authorizes a public key for migrations.
func authorizeMigrationKey(rw io.ReadWriter, migrationScheme MigrationScheme, migrationKey pubKey, ca *commandAuth) ([]byte, *responseAuth, uint32, error) {
in := []interface{}{migrationScheme, migrationKey, ca}
var ra responseAuth
var migrationAuth migrationKeyAuth
out := []interface{}{&migrationAuth, &ra}
ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordAuthorizeMigrationKey, in, out)
if err != nil {
return nil, nil, 0, err
}
authBlob, err := tpmutil.Pack(migrationAuth)
if err != nil {
return nil, nil, 0, err
}

return authBlob, &ra, ret, nil
}

// createMigrationBlob migrates a key from the TPM.
func createMigrationBlob(rw io.ReadWriter, parentHandle tpmutil.Handle, migrationScheme MigrationScheme, migrationKey []byte, encData tpmutil.U32Bytes, ca1 *commandAuth, ca2 *commandAuth) ([]byte, []byte, *responseAuth, *responseAuth, uint32, error) {
in := []interface{}{parentHandle, migrationScheme, migrationKey, encData, ca1, ca2}
var rand tpmutil.U32Bytes
var outData tpmutil.U32Bytes
var ra1 responseAuth
var ra2 responseAuth
out := []interface{}{&rand, &outData, &ra1, &ra2}
ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordCreateMigrationBlob, in, out)
if err != nil {
return nil, nil, nil, nil, 0, err
}

return rand, outData, &ra1, &ra2, ret, nil
}

// flushSpecific removes a handle from the TPM. Note that removing a handle
// doesn't require any authentication.
func flushSpecific(rw io.ReadWriter, handle tpmutil.Handle, resourceType uint32) error {
Expand Down
84 changes: 55 additions & 29 deletions tpm/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,39 @@ const (

// Supported TPM operations.
const (
ordOIAP uint32 = 0x0000000A
ordOSAP uint32 = 0x0000000B
ordTakeOwnership uint32 = 0x0000000D
ordExtend uint32 = 0x00000014
ordPCRRead uint32 = 0x00000015
ordQuote uint32 = 0x00000016
ordSeal uint32 = 0x00000017
ordUnseal uint32 = 0x00000018
ordCreateWrapKey uint32 = 0x0000001F
ordGetPubKey uint32 = 0x00000021
ordSign uint32 = 0x0000003C
ordQuote2 uint32 = 0x0000003E
ordResetLockValue uint32 = 0x00000040
ordLoadKey2 uint32 = 0x00000041
ordGetRandom uint32 = 0x00000046
ordOwnerClear uint32 = 0x0000005B
ordForceClear uint32 = 0x0000005D
ordGetCapability uint32 = 0x00000065
ordMakeIdentity uint32 = 0x00000079
ordActivateIdentity uint32 = 0x0000007A
ordReadPubEK uint32 = 0x0000007C
ordOwnerReadInternalPub uint32 = 0x00000081
ordFlushSpecific uint32 = 0x000000BA
ordNVDefineSpace uint32 = 0x000000CC
ordPcrReset uint32 = 0x000000C8
ordNVWriteValue uint32 = 0x000000CD
ordNVWriteValueAuth uint32 = 0x000000CE
ordNVReadValue uint32 = 0x000000CF
ordNVReadValueAuth uint32 = 0x000000D0
ordOIAP uint32 = 0x0000000A
ordOSAP uint32 = 0x0000000B
ordTakeOwnership uint32 = 0x0000000D
ordExtend uint32 = 0x00000014
ordPCRRead uint32 = 0x00000015
ordQuote uint32 = 0x00000016
ordSeal uint32 = 0x00000017
ordUnseal uint32 = 0x00000018
ordCreateWrapKey uint32 = 0x0000001F
ordGetPubKey uint32 = 0x00000021
ordCreateMigrationBlob uint32 = 0x00000028
ordAuthorizeMigrationKey uint32 = 0x0000002b
ordSign uint32 = 0x0000003C
ordQuote2 uint32 = 0x0000003E
ordResetLockValue uint32 = 0x00000040
ordLoadKey2 uint32 = 0x00000041
ordGetRandom uint32 = 0x00000046
ordOwnerClear uint32 = 0x0000005B
ordForceClear uint32 = 0x0000005D
ordGetCapability uint32 = 0x00000065
ordCreateEndorsementKeyPair uint32 = 0x00000078
ordMakeIdentity uint32 = 0x00000079
ordActivateIdentity uint32 = 0x0000007A
ordReadPubEK uint32 = 0x0000007C
ordOwnerReadInternalPub uint32 = 0x00000081
ordStartup uint32 = 0x00000099
ordFlushSpecific uint32 = 0x000000BA
ordNVDefineSpace uint32 = 0x000000CC
ordPcrReset uint32 = 0x000000C8
ordNVWriteValue uint32 = 0x000000CD
ordNVWriteValueAuth uint32 = 0x000000CE
ordNVReadValue uint32 = 0x000000CF
ordNVReadValueAuth uint32 = 0x000000D0
)

// Capability types.
Expand Down Expand Up @@ -294,6 +298,28 @@ const (
authPrivUseOnly byte = 0x03
)

// KeyFlags represents TPM_KEY_FLAGS.
type KeyFlags uint32

const (
keyRedirection KeyFlags = 0x00000001
keyMigratable KeyFlags = 0x00000002
keyIsVolatile KeyFlags = 0x00000004
keyPcrIgnoredOnRead KeyFlags = 0x00000008
keyMigrateAuthority KeyFlags = 0x00000010
)

// MigrationScheme represents TPM_MIGRATE_SCHEME.
type MigrationScheme uint16

const (
msMigrate MigrationScheme = 0x0001
msRewrap MigrationScheme = 0x0002
msMaint MigrationScheme = 0x0003
msRestrictMigrate MigrationScheme = 0x0004
msRestrictApprove MigrationScheme = 0x0005
)

// fixedQuote is the fixed constant string used in quoteInfo.
var fixedQuote = [4]byte{byte('Q'), byte('U'), byte('O'), byte('T')}

Expand Down
13 changes: 12 additions & 1 deletion tpm/open_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,27 @@ import (
// device, then it treats it like a normal TPM device, and if the file is a
// Unix domain socket, then it opens a connection to the socket.
func OpenTPM(path string) (io.ReadWriteCloser, error) {
return openAndStartupTPM(path, false)
}

// openAndStartupTPM opens the TPM and optionally runs TPM_Startup if needed.
// This feature is implemented only for testing.
func openAndStartupTPM(path string, doStartup bool) (io.ReadWriteCloser, error) {
rwc, err := tpmutil.OpenTPM(path)
if err != nil {
return nil, err
}

// Make sure this is a TPM 1.2
_, err = GetManufacturer(rwc)
if doStartup && err == tpmError(errInvalidPostInit) {
if err = startup(rwc); err == nil {
_, err = GetManufacturer(rwc)
}
}
if err != nil {
rwc.Close()
return nil, fmt.Errorf("open %s: device is not a TPM 1.2", path)
return nil, fmt.Errorf("open %s: device is not a TPM 1.2: %v", path, err)
}
return rwc, nil
}
9 changes: 8 additions & 1 deletion tpm/structures.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ type symmetricKeyParams struct {
type key struct {
Version uint32
KeyUsage uint16
KeyFlags uint32
KeyFlags KeyFlags
AuthDataUsage byte
AlgorithmParams keyParams
PCRInfo tpmutil.U32Bytes
Expand All @@ -283,6 +283,13 @@ type pubKey struct {
Key tpmutil.U32Bytes
}

// A migrationKeyAuth represents the target of a migration.
type migrationKeyAuth struct {
MigrationKey pubKey
MigrationScheme MigrationScheme
Digest Digest
}

// A symKey is a TPM representation of a symmetric key.
type symKey struct {
AlgID Algorithm
Expand Down
43 changes: 43 additions & 0 deletions tpm/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Testing TPM 1.2 Functionality

**TODO(https://github.com/google/go-tpm/issues/91):** Support for testing the TPM 1.2 stack against
a simulator is a work in progress. Today, it requires several manual steps.

## Overview

As TPM 1.2s are phased out of common developer devices, testing changes to the TPM 1.2 stack is
difficult without special hardware. To support development on the TPM 1.2 stack without special
hardware, a TPM 1.2 simulator or emulator may be used. This document discusses how to use
[IBM's TPM 1.2 simulator](http://ibmswtpm.sourceforge.net) (on a Linux or Mac OS device, Windows is
not yet supported) to run the go-tpm TPM 1.2 tests (in the `go-tpm/tpm/` directory).

## Downloading, building, and using the IBM TPM 1.2 Simulator

* Download the latest release of the
[IBM TPM 1.2 Simulator](https://sourceforge.net/projects/ibmswtpm/), unpack the tarball, and `cd`
into it.
* Add `-DTPM_UNIX_DOMAIN_SOCKET` to `tpm/makefile-en-ac`.
* Build the simulator with `make -f tpm/makefile-en-ac`
* Set `TEMP_TPM=/tmp/tpm` or some other suitable temporary location for the TPM state files and Unix
domain socket.
* Start the simulator with `TPM_PATH=${TEMP_TPM} TPM_PORT=${TEMP_TPM}/tpm.sock`

## Running the TPM 1.2 tests against the IBM TPM 1.2 Simulator

* Comment out the line `t.Skip()` in `TestTakeOwnership`. This test normally does not work on
physical TPMs, so it is normally disabled.
* Use `TestTakeOwnership` to take ownership of the simulated TPM with `TPM_PATH=${TEMP_TPM}/tpm.sock
go test -v ./tpm/... -run TestTakeOwnership -count=1`
* Run the full test suite with `TPM_PATH=${TEMP_TPM}/tpm.sock go test -v ./tpm/...`

## Future Improvements

* Add setup logic to the TPM 1.2 tests to take ownership of an unowned TPM under test.
* Wrap a TPM 1.2 simulator somewhere (possibly in https://github.com/google/go-tpm-tools) and
integrate it into test setup for the TPM 1.2 tests.
* Resolve issues that necessitated the use of `t.Skip()` in current tests.
* Either add an informative comment along with a skip when a test fails for an expected reason, or
remove the test.
* Resolve issues with current tests that fail on the simulator (such as `TestGetAlgs`).
* Automate the use of a simulator in a Continuous Integration environment that is accessible to
GitHub.
Loading