From 60cdf72745b8e08b327ee7bd08bac43a990bf930 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Wed, 31 Jul 2024 22:12:55 +0100 Subject: [PATCH] Add support for TPM2_PCR_Allocate This adds test for the actual command and in testutil. Whilst it's not expected that this code will be used in production, adjusting the PCR allocation in unit test code that uses the simulator is a valid use case. --- cmds_pcr.go | 22 +++++++++ cmds_pcr_test.go | 85 +++++++++++++++++++++++++++++++++++ internal/testutil/checkers.go | 8 ++++ testutil/export_test.go | 6 ++- testutil/suites.go | 16 ++++++- testutil/tpm.go | 44 +++++++++++------- testutil/transport.go | 84 ++++++++++++++++++++++++---------- testutil/transport_test.go | 56 ++++++++++++++++++++++- 8 files changed, 280 insertions(+), 41 deletions(-) diff --git a/cmds_pcr.go b/cmds_pcr.go index 6236339..9207925 100644 --- a/cmds_pcr.go +++ b/cmds_pcr.go @@ -103,6 +103,28 @@ func (t *TPMContext) PCRRead(pcrSelectionIn PCRSelectionList, sessions ...Sessio return pcrUpdateCounter, pcrValues, nil } +// PCRAllocate executes the TPM2_PCR_Allocate command to set the PCR allocation, which is +// persistent even across TPM2_Clear. The supplied authContext parameter must correspond to +// [HandlePlatform]. This command requires authorization of authContext with the user auth +// role, with session based authorization provided via authContextAuthSession. +// +// The desired PCR allocation is supplied via the pcrAllocation argument. The function indicates +// whether the allocation was successful. This will only be true if no error is returned. Note +// that the allocation takes effect after the next TPM reset. The function returns the maximum +// number of PCRs supported per bank, plus the size needed for the new allocation and the the +// size available. +func (t *TPMContext) PCRAllocate(authContext ResourceContext, pcrAllocation PCRSelectionList, authContextAuthSession SessionContext, sessions ...SessionContext) (allocationSuccess bool, maxPCR uint32, sizeNeeded uint32, sizeAvailable uint32, err error) { + if err := t.StartCommand(CommandPCRAllocate). + AddHandles(UseResourceContextWithAuth(authContext, authContextAuthSession)). + AddParams(pcrAllocation). + AddExtraSessions(sessions...). + Run(nil, &allocationSuccess, &maxPCR, &sizeNeeded, &sizeAvailable); err != nil { + return false, 0, 0, 0, err + } + + return allocationSuccess, maxPCR, sizeNeeded, sizeAvailable, nil +} + // PCRReset executes the TPM2_PCR_Reset command to reset the PCR associated with pcrContext in all // banks. This command requires authorization with the user auth role for pcrContext, with session // based authorization provided via pcrContextAuthSession. diff --git a/cmds_pcr_test.go b/cmds_pcr_test.go index 5c7cbcf..5094a15 100644 --- a/cmds_pcr_test.go +++ b/cmds_pcr_test.go @@ -8,8 +8,11 @@ import ( "bytes" "testing" + "github.com/canonical/go-tpm2" . "github.com/canonical/go-tpm2" + internal_testutil "github.com/canonical/go-tpm2/internal/testutil" "github.com/canonical/go-tpm2/testutil" + . "gopkg.in/check.v1" ) func TestPCRExtend(t *testing.T) { @@ -380,3 +383,85 @@ func TestPCRReset(t *testing.T) { }) } } + +type pcrSuite struct { + testutil.TPMSimulatorTest +} + +var _ = Suite(&pcrSuite{}) + +type testPCRAllocationParams struct { + allocation PCRSelectionList + authContextAuthSession SessionContext +} + +func (s *pcrSuite) testPCRAllocation(c *C, params *testPCRAllocationParams) error { + sessionHandles := []Handle{authSessionHandle(params.authContextAuthSession)} + + success, _, sizeNeeded, _, err := s.TPM.PCRAllocate(s.TPM.PlatformHandleContext(), params.allocation, params.authContextAuthSession) + if err != nil { + c.Check(success, internal_testutil.IsFalse) + return err + } + + c.Check(success, internal_testutil.IsTrue) + + var expectedSizeNeeded uint32 + for _, selection := range params.allocation { + digestSize := selection.Hash.Size() + expectedSizeNeeded += uint32(len(selection.Select) * digestSize) + } + c.Check(sizeNeeded, internal_testutil.IntEqual, expectedSizeNeeded) + + authArea := s.LastCommand(c).CmdAuthArea + c.Assert(authArea, internal_testutil.LenEquals, 1) + c.Check(authArea[0].SessionHandle, Equals, sessionHandles[0]) + + s.ResetTPMSimulator(c) + + current, err := s.TPM.GetCapabilityPCRs() + c.Check(err, IsNil) + c.Check(current, testutil.TPMValueDeepEquals, params.allocation) + + return nil +} + +func (s *pcrSuite) TestPCRAllocation1(c *C) { + current, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + + for i := range current { + current[i].Select = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22} + } + err = s.testPCRAllocation(c, &testPCRAllocationParams{ + allocation: current, + }) + c.Check(err, IsNil) +} + +func (s *pcrSuite) TestPCRAllocation2(c *C) { + current, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + c.Assert(current, internal_testutil.LenGreater, 1) + + current[0].Select = nil + + err = s.testPCRAllocation(c, &testPCRAllocationParams{ + allocation: current, + }) + c.Check(err, IsNil) +} + +func (s *pcrSuite) TestPCRAllocationWithSession(c *C) { + current, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + + for i := range current { + current[i].Select = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22} + } + err = s.testPCRAllocation(c, &testPCRAllocationParams{ + allocation: current, + authContextAuthSession: s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256), + }) + c.Check(err, IsNil) +} diff --git a/internal/testutil/checkers.go b/internal/testutil/checkers.go index 79d0dda..d3cef5d 100644 --- a/internal/testutil/checkers.go +++ b/internal/testutil/checkers.go @@ -352,6 +352,14 @@ func (checker *hasLenChecker) Check(params []interface{}, names []string) (resul var LenEquals Checker = &hasLenChecker{ &CheckerInfo{Name: "LenEquals", Params: []string{"value", "n"}}, IntEqual} +// LenGreater checks that the value has a length that is greater than n. +// +// For example: +// +// c.Check(value, LenGreater, 5) +var LenGreater Checker = &hasLenChecker{ + &CheckerInfo{Name: "LenGreater", Params: []string{"value", "n"}}, IntGreater} + // LenGreaterEquals checks that the value has a length that is greater than or equal // to n. // diff --git a/testutil/export_test.go b/testutil/export_test.go index 95341b0..8042314 100644 --- a/testutil/export_test.go +++ b/testutil/export_test.go @@ -8,7 +8,11 @@ import ( "github.com/canonical/go-tpm2" ) -func MockWrapMssimTransport(fn func(tpm2.Transport) (*Transport, error)) (restore func()) { +const ( + TpmFeatureSimulatorOnlyPCRAllocation = tpmFeatureSimulatorOnlyPCRAllocation +) + +func MockWrapMssimTransport(fn func(tpm2.Transport, TPMFeatureFlags) (*Transport, error)) (restore func()) { origWrapMssimTransport := wrapMssimTransport wrapMssimTransport = fn return func() { diff --git a/testutil/suites.go b/testutil/suites.go index d2ea09d..9388dd6 100644 --- a/testutil/suites.go +++ b/testutil/suites.go @@ -149,7 +149,7 @@ func (b *TPMTest) initTPMContextIfNeeded(c *C) { case b.Transport != nil: // A transport has been provided by the test using the new field. // Create a TPMContext from the supplied transport - b.TPM, _ = OpenTPMDevice(c, newTransportPassthroughDevice(b.Transport)) + b.TPM, _ = OpenTPMDevice(c, NewTransportPassthroughDevice(b.Transport)) b.TCTI = b.Transport // populate the deprecated field case b.Device != nil: // A device has been provided by the test. @@ -456,6 +456,20 @@ func (b *TPMSimulatorTest) SetUpTest(c *C) { return } b.ResetAndClearTPMSimulatorUsingPlatformHierarchy(c) + c.Check(b.TPM.Close(), IsNil) + b.TPM = nil + + if b.Transport.didUpdatePcrAllocation { + // We need to give the TPM one more reset + c.Assert(b.Device, NotNil) + tpm, transport := OpenTPMDevice(c, b.Device) + b.TPM = tpm + b.Transport = transport + b.TCTI = transport + b.ResetTPMSimulator(c) + c.Check(b.TPM.Close(), IsNil) + b.TPM = nil + } }) } diff --git a/testutil/tpm.go b/testutil/tpm.go index ff106d3..6bed20a 100644 --- a/testutil/tpm.go +++ b/testutil/tpm.go @@ -104,6 +104,8 @@ const ( // were not created by the test, such as writing to or undefining NV indices or evicting // persistent objects. TPMFeaturePersistent + + tpmFeatureSimulatorOnlyPCRAllocation = 1 << 31 ) func (f TPMFeatureFlags) String() string { @@ -168,9 +170,7 @@ var ( // MssimPort defines the port number of the TPM simulator command port where TPMBackend is TPMBackendMssim. MssimPort uint = 2321 - wrapMssimTransport = func(transport tpm2.Transport) (*Transport, error) { - return WrapTransport(transport, TPMFeatureFlags(math.MaxUint32)) - } + wrapMssimTransport = WrapTransport ErrSkipNoTPM = errors.New("no TPM configured for the test") ) @@ -634,8 +634,10 @@ func NewTransportT(t *testing.T, features TPMFeatureFlags) *Transport { } // TransportBackedDevice is a TPMDevice that is backed by an already -// opened transport, and just returns the same transport on each open -// call. It keeps track of how many transports are currently open. +// opened transport, and just returns a new transport that wraps the same +// supplied transport on each open call. It keeps track of how many transports +// are currently open. +// // Consider that whilst [tpm2.TPMDevice] implementations can generally be // used by more than one goroutine, each opened [tpm2.Transport] is only // safe to use from a single goroutine, and this device returns multiple @@ -647,10 +649,10 @@ type TransportBackedDevice struct { } // NewTransportBackedDevice returns a new TPMDevice from the supplied -// transport that just returns the same transport on each call to Open. -// It's useful in tests where it is necessary to create multiple -// [tpm2.TPMContext] instances from the same underlying transport, although -// this isn't something one would do in normal production use. An example +// transport that just returns a new transport that wraps the same transport +// on each call to Open. It's useful in tests where it is necessary to create +// multiple [tpm2.TPMContext] instances from the same underlying transport, +// although this isn't something one would do in normal production use. An example // here is creating a [tpm2.TPMContext] in the code under test that shares // a transport with the unit test code (which has its own [tpm2.TPMContext]). // @@ -659,12 +661,14 @@ type TransportBackedDevice struct { // calls to Open will return a transport that is already closed. If it is // false, the returned device will keep track of how many transports are open, // but calling Close on any returned transport will not actually close the -// underlying transport. +// underlying transport - it will mark that specific one as closed. // // Consider that whilst [tpm2.TPMDevice] implementations can generally be // used by more than one goroutine, each opened [tpm2.Transport] is only // safe to use from a single goroutine, and this device returns multiple // pointers to the same transport. +// +// Note that this device does not work with [OpenTPMDevice] or [OpenTPMDeviceT]. func NewTransportBackedDevice(transport *Transport, closable bool) *TransportBackedDevice { return &TransportBackedDevice{ transport: transport, @@ -704,8 +708,10 @@ func (t *duplicateTransport) Unwrap() tpm2.Transport { } // Open implements [tpm2.TPMDevice.Open]. Whilst most implementations of this -// return a new transport, this repeatedly returns the same transport which means each -// call to this returns transports that generally have to be used on the same gorountine. +// return a new transport, and this does return a new transport structure, it is just +// a wrapper for the transport that was supplied to [NewTransportBackedDevice], which +// means each call to this returns transports that generally have to be used on the +// same gorountine. func (d *TransportBackedDevice) Open() (tpm2.Transport, error) { d.opened += 1 return &duplicateTransport{ @@ -722,12 +728,20 @@ type transportPassthroughDevice struct { transport *Transport } -func newTransportPassthroughDevice(transport *Transport) tpm2.TPMDevice { +// NewTransportPassthroughDevice returns a device that returns the supplied transport +// on the first and only call to its Open method. On subsequent calls, it returns the +// [ErrSkipNoTPM] error, which makes it suitable for [OpenTPMDevice] and [OpenTPMDeviceT]. +func NewTransportPassthroughDevice(transport *Transport) tpm2.TPMDevice { return &transportPassthroughDevice{transport: transport} } func (d *transportPassthroughDevice) Open() (tpm2.Transport, error) { - return d.transport, nil + transport := d.transport + d.transport = nil + if transport == nil { + return nil, ErrSkipNoTPM + } + return transport, nil } func (d *transportPassthroughDevice) String() string { @@ -972,7 +986,7 @@ func (d *simulatorDevice) Open() (tpm2.Transport, error) { return nil, err } - return wrapMssimTransport(transport) + return wrapMssimTransport(transport, TPMFeatureFlags(math.MaxUint32)) } func (d *simulatorDevice) String() string { diff --git a/testutil/transport.go b/testutil/transport.go index c930a28..096ef58 100644 --- a/testutil/transport.go +++ b/testutil/transport.go @@ -75,6 +75,7 @@ var commandInfoMap = map[tpm2.CommandCode]commandInfo{ tpm2.CommandClearControl: commandInfo{1, 1, false, true}, tpm2.CommandHierarchyChangeAuth: commandInfo{1, 1, false, true}, tpm2.CommandNVDefineSpace: commandInfo{1, 1, false, true}, + tpm2.CommandPCRAllocate: commandInfo{1, 1, false, true}, tpm2.CommandCreatePrimary: commandInfo{1, 1, true, false}, tpm2.CommandNVGlobalWriteLock: commandInfo{1, 1, false, true}, tpm2.CommandGetCommandAuditDigest: commandInfo{2, 2, false, true}, @@ -283,20 +284,22 @@ type Transport struct { r io.Reader w io.Writer - restorePermanentAttrs tpm2.PermanentAttributes - restoreStClearAttrs tpm2.StartupClearAttributes - restoreDaParams daParams - restoreCmdAuditStatus cmdAuditStatus + restorePermanentAttrs tpm2.PermanentAttributes + restoreStClearAttrs tpm2.StartupClearAttributes + restoreDaParams daParams + restoreCmdAuditStatus cmdAuditStatus + restorePcrAllocationParams tpm2.PCRSelectionList currentCmd *cmdContext hierarchyAuths map[tpm2.Handle]tpm2.Auth handles map[tpm2.Handle]*handleInfo - didClearControl bool - didHierarchyControl bool - didSetDaParams bool - didSetCmdAuditStatus bool + didClearControl bool + didHierarchyControl bool + didSetDaParams bool + didSetCmdAuditStatus bool + didUpdatePcrAllocation bool // CommandLog keeps a record of all of the commands executed via // this interface @@ -562,6 +565,8 @@ func (t *Transport) readResponse() ([]byte, error) { } t.handles[object].pub = outPublic } + case tpm2.CommandPCRAllocate: + t.didUpdatePcrAllocation = true } if t.ResponseIntercept != nil { @@ -648,13 +653,13 @@ func (t *Transport) sendCommand(data []byte) (int, error) { return 0, fmt.Errorf("cannot unmarshal parameters: %w", err) } switch { - case t.permittedFeatures&TPMFeaturePlatformHierarchy == 0: - // We can't reenable hierarchies so this change will require a restart or reset. - commandFeatures |= TPMFeatureStClearChange case enable == tpm2.HandlePlatform: // We won't be able to reenable hierarchies because the platform hierarchy is // being disabled. This change will require a restart or reset. commandFeatures |= TPMFeatureStClearChange + case t.permittedFeatures&TPMFeaturePlatformHierarchy == 0: + // We can't reenable hierarchies so this change will require a restart or reset. + commandFeatures |= TPMFeatureStClearChange } case tpm2.CommandNVUndefineSpace: nvIndex := handles[1] @@ -674,6 +679,10 @@ func (t *Transport) sendCommand(data []byte) (int, error) { // Permitting TPMFeatureClearControl should imply TPMFeatureNV is permitted for this command. commandFeatures &^= TPMFeatureNV } + case tpm2.CommandPCRAllocate: + if t.permittedFeatures&tpmFeatureSimulatorOnlyPCRAllocation == 0 { + return 0, errors.New("TPM2_PCR_Allocate can only be used with the simulator") + } case tpm2.CommandNVGlobalWriteLock: commandFeatures |= TPMFeatureNVGlobalWriteLock // Permitting TPMFeatureNVGlobalWriteLock should imply TPMFeatureNV is permitted for this command. @@ -794,7 +803,7 @@ func (t *Transport) restorePlatformHierarchyAuth(tpm *tpm2.TPMContext) error { if tpm2.IsTPMHandleError(err, tpm2.ErrorHierarchy, tpm2.CommandHierarchyChangeAuth, 1) { // Platform hierarchy was disabled which was already checked to be permitted via // TPMFeatureStClearChange. The auth value will be restored on the next - // TPM2_Startup(CLEAR). + // TPM2_Startup(CLEAR) after a reset. return nil } return fmt.Errorf("cannot clear auth value for %v: %w", tpm2.HandlePlatform, err) @@ -809,7 +818,7 @@ func (t *Transport) restoreHierarchies(errs []error, tpm *tpm2.TPMContext) []err if t.permittedFeatures&TPMFeaturePlatformHierarchy == 0 { // TPM2_HierarchyControl was already checked to be permitted via TPMFeatureStClearChange. - // The hierarchies will be restored on the next TPM2_Startup(CLEAR). + // The hierarchies will be restored on the next TPM2_Startup(CLEAR) after a reset. return errs } @@ -828,7 +837,7 @@ func (t *Transport) restoreHierarchies(errs []error, tpm *tpm2.TPMContext) []err if tpm2.IsTPMHandleError(err, tpm2.ErrorHierarchy, tpm2.CommandHierarchyControl, 1) { // The platform hierarchy was disabled which already checked to be permitted via // TPMFeatureStClearChange. The hierarchies will be restored on the next - // TPM2_Startup(CLEAR). + // TPM2_Startup(CLEAR) after a reset. break } errs = append(errs, fmt.Errorf("cannot restore hierarchy %v: %w", hierarchy, err)) @@ -850,6 +859,23 @@ func (t *Transport) restoreHierarchyAuths(errs []error, tpm *tpm2.TPMContext) [] return errs } +func (t *Transport) restorePcrAllocation(tpm *tpm2.TPMContext) error { + if !t.didUpdatePcrAllocation { + return nil + } + + // We already check that TPMFeaturePlatformHierarchy is permitted before changing the allocation, + // so we're ok to use the platform hierarchy here. + success, _, _, _, err := tpm.PCRAllocate(tpm.PlatformHandleContext(), t.restorePcrAllocationParams, nil) + if err != nil { + return fmt.Errorf("cannot restore PCR allocation: %w", err) + } + if !success { + return errors.New("cannot restore PCR allocation for unknown reason") + } + return nil +} + func (t *Transport) restoreDisableClear(tpm *tpm2.TPMContext) error { if !t.didClearControl { return nil @@ -1053,6 +1079,10 @@ func (t *Transport) Close() error { errs = t.restoreHierarchyAuths(errs, tpm) + if err := t.restorePcrAllocation(tpm); err != nil { + errs = append(errs, err) + } + if err := t.restoreDisableClear(tpm); err != nil { errs = append(errs, err) } @@ -1142,17 +1172,25 @@ func WrapTransport(transport tpm2.Transport, permittedFeatures TPMFeatureFlags) cmdAuditStatus.commands = commands } + var pcrAllocation tpm2.PCRSelectionList + if permittedFeatures&tpmFeatureSimulatorOnlyPCRAllocation > 0 { + pcrAllocation, err = tpm.GetCapabilityPCRs() + if err != nil { + return nil, fmt.Errorf("cannot obtain current PCR allocation: %w", err) + } + } + out := &Transport{ - transport: transport, - permittedFeatures: permittedFeatures, - restorePermanentAttrs: permanentAttrs, - restoreStClearAttrs: stClearAttrs, - restoreDaParams: daParams, - restoreCmdAuditStatus: cmdAuditStatus, - hierarchyAuths: make(map[tpm2.Handle]tpm2.Auth), - handles: make(map[tpm2.Handle]*handleInfo)} + transport: transport, + permittedFeatures: permittedFeatures, + restorePermanentAttrs: permanentAttrs, + restoreStClearAttrs: stClearAttrs, + restoreDaParams: daParams, + restoreCmdAuditStatus: cmdAuditStatus, + restorePcrAllocationParams: pcrAllocation, + hierarchyAuths: make(map[tpm2.Handle]tpm2.Auth), + handles: make(map[tpm2.Handle]*handleInfo)} out.r = transportutil.BufferResponses(&responseReader{t: out}, maxResponseSize) out.w = transportutil.BufferCommands(&commandSender{t: out}, maxCommandSize) return out, nil - } diff --git a/testutil/transport_test.go b/testutil/transport_test.go index 72cdf01..5548546 100644 --- a/testutil/transport_test.go +++ b/testutil/transport_test.go @@ -64,7 +64,7 @@ func (s *transportSuite) SetUpTest(c *C) { } func (s *transportSuite) initTPMContext(c *C, permittedFeatures TPMFeatureFlags) { - restore := MockWrapMssimTransport(func(transport tpm2.Transport) (*Transport, error) { + restore := MockWrapMssimTransport(func(transport tpm2.Transport, _ TPMFeatureFlags) (*Transport, error) { return WrapTransport(&ignoreCloseTransport{transport: transport}, permittedFeatures) }) defer restore() @@ -2114,3 +2114,57 @@ func (s *transportSuite) TestDontEvictExistingIndex(c *C) { c.Check(props, internal_testutil.LenEquals, 1) c.Check(props[0], Equals, nvPublic.Index) } + +func (s *transportSuite) TestBlockPCRAllocateWithSimulator(c *C) { + s.initTPMContext(c, TPMFeaturePlatformHierarchy|TPMFeatureNV) + s.deferCloseTpm(c) + + current, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + + _, _, _, _, err = s.TPM.PCRAllocate(s.TPM.PlatformHandleContext(), current, nil) + c.Check(err, ErrorMatches, `cannot complete write operation on Transport: TPM2_PCR_Allocate can only be used with the simulator`) +} + +func (s *transportSuite) TestRestorePCRAllocateWithSimulator(c *C) { + s.initTPMContext(c, TPMFeaturePlatformHierarchy|TPMFeatureNV|TPMFeatureShutdown|TpmFeatureSimulatorOnlyPCRAllocation) + + orig, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + c.Assert(orig, internal_testutil.LenGreater, 1) + + var allocation tpm2.PCRSelectionList + mu.MustCopyValue(&allocation, orig) + allocation[0].Select = nil + success, _, _, _, err := s.TPM.PCRAllocate(s.TPM.PlatformHandleContext(), allocation, nil) + c.Check(err, IsNil) + c.Check(success, internal_testutil.IsTrue) + + s.ResetTPMSimulator(c) + + current, err := s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + c.Check(current, DeepEquals, allocation) + + c.Check(s.TPM.Close(), IsNil) + + origTransport := s.Transport + + // The intermediate transport is closed now, so wrap a new one to + // create a new passthrough device and context so we can reset the + // simulator again and make sure that the revert was saved. + newTransport, err := WrapTransport(&ignoreCloseTransport{transport: s.Transport.Unwrap().(TransportWrapper).Unwrap()}, TPMFeatureShutdown|TPMFeatureNV) + c.Assert(err, IsNil) + device := NewTransportPassthroughDevice(newTransport) + s.TPM, s.Transport = OpenTPMDevice(c, device) + + s.ResetTPMSimulator(c) + + current, err = s.TPM.GetCapabilityPCRs() + c.Assert(err, IsNil) + c.Check(current, DeepEquals, orig) + + c.Check(s.TPM.Close(), IsNil) + + s.Transport = origTransport +}