Skip to content

Commit

Permalink
Add extra private type field to HandleContext
Browse files Browse the repository at this point in the history
There used to be something similar, but then it was removed and the
handle type was used to select the union contents, but then these
fields needed to be sized because they may or may not be empty depending
on the context. Instead, reintroduce a distinct context type that is
separate from the handle type, have this correspond to concrete go types,
and have some of these types map to nothing in the union. This way, we
can guarantee that ObjectContext.Public will always return something,
except after HandleContext.Dispose is called, and we can avoid returning
ObjectContext and NVIndexContext implementations where it really doesn't
make sense.
  • Loading branch information
chrisccoulson committed Apr 11, 2024
1 parent 3c41d9a commit 57a0ce4
Show file tree
Hide file tree
Showing 18 changed files with 522 additions and 310 deletions.
58 changes: 37 additions & 21 deletions cmds_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import (
// integrity protects the context with a key derived from the hierarchy proof. If saveContext does
// not correspond to a transient object or a session, then it will return an error.
//
// On successful completion, it returns a Context instance that can be passed to
// [TPMContext.ContextLoad]. Note that this function wraps the context data returned from the TPM
// On successful completion, it returns a Context structure that can be passed to
// [TPMContext.ContextLoad]. Note that this function wraps the context blob returned from the TPM
// with some host-side state associated with the resource, so that it can be restored fully in
// [TPMContext.ContextLoad]. If saveContext corresponds to a session, the host-side state that is
// added to the returned context blob includes the session key.
// [TPMContext.ContextLoad]. This means that the context structure cannot be passed directly to the
// TPM using the TPM2_ContextLoad command, but [TPMContext.ContextLoad] must be used instead. If
// saveContext corresponds to a session, the host-side state that is added to the returned context
// blob includes the session key.
//
// If saveContext corresponds to a session, then TPM2_ContextSave also removes resources associated
// with the session from the TPM (it becomes a saved session rather than a loaded session). In this
Expand Down Expand Up @@ -55,14 +57,21 @@ func (t *TPMContext) ContextSave(saveContext HandleContext) (context *Context, e
// ContextLoad executes the TPM2_ContextLoad command with the supplied Context, in order to restore
// a context previously saved from [TPMContext.ContextSave].
//
// If the size field of the integrity HMAC in the context blob is greater than the size of the
// largest digest algorithm, a *[TPMError] with an error code of [ErrorSize] is returned. If the
// context blob is shorter than the size indicated for the integrity HMAC, a *[TPMError] with an
// error code of [ErrorInsufficient] is returned.
// Note that the context blob returned from [TPMContext.ContextSave] wraps the context blob returned
// from the TPM with some host-side state that can be used by this function to reconstruct a
// HandleContext. This function expects a context structure created by [TPMContext.ContextSave] as
// opposed to one created directly by the TPM2_ContextSave command on the TPM.
//
// If the size of the context's integrity HMAC does not match the context integrity digest
// algorithm for the TPM, or the context blob is too short, a *[TPMParameterError] error with an
// error code of [ErrorSize] will be returned. If the integrity HMAC check fails, a
// If the size field of the integrity HMAC in the unwrapped context blob is greater than the size of
// the largest digest algorithm, a *[TPMError] with an error code of [ErrorSize] is returned. If the
// unwrapped context blob is shorter than the indicated size of the integrity HMAC, a *[TPMError] error
// with an error code of [ErrorInsufficient] is returned.
//
// If the size of the integrity HMAC in the unwrapped context blob does not match the size of the
// context integrity digest algorithm for the TPM, or the unwrapped context blob is too short, a
// *[TPMParameterError] error with an error code of [ErrorSize] is returned.
//
// If the integrity HMAC check for the context including the unwrapped blob fails, a
// *[TPMParameterError] with an error code of [ErrorIntegrity] will be returned.
//
// If the hierarchy that the context is part of is disabled, a *[TPMParameterError] error with an
Expand Down Expand Up @@ -127,15 +136,22 @@ func (t *TPMContext) ContextLoad(context *Context) (loadedContext HandleContext,
if loadedHandle.Type() != HandleTypeTransient {
return nil, &InvalidResponseError{CommandContextLoad, fmt.Errorf("handle %v returned from TPM is the wrong type", loadedHandle)}
}
hc = newObjectContext(loadedHandle, hc.Name(), hc.(ObjectContext).Public())
switch obj := hc.(type) {
case ObjectContext:
return newObjectContext(loadedHandle, hc.Name(), obj.Public()), nil
case ResourceContext:
return newResourceContext(loadedHandle, hc.Name()), nil
default:
return newHandleContext(loadedHandle), nil
}
case HandleTypeHMACSession, HandleTypePolicySession:
if loadedHandle != context.SavedHandle {
return nil, &InvalidResponseError{CommandContextLoad, fmt.Errorf("handle %v returned from TPM is incorrect", loadedHandle)}
}
return hc, nil
default:
panic("not reached")
}
return hc, nil
}

// FlushContext executes the TPM2_FlushContext command on the handle referenced by flushContext,
Expand Down Expand Up @@ -190,8 +206,8 @@ func (t *TPMContext) FlushContext(flushContext HandleContext) error {
// handle, a *[TPMError] error with an error code of [ErrorNVDefined] will be returned.
//
// On successful completion of persisting a transient object, it returns a ResourceContext that
// corresponds to the persistent object. If object was created with [NewLimitedResourceContext],
// then a similarly limited context will be returned for the new persistent object. On successful
// corresponds to the persistent object. If object can be type asserted to [ObjectContext], then
// the returned ResourceContext can also be type asserted to [ObjectContext]. On successful
// completion of evicting a persistent object, it returns a nil ResourceContext, and object will be
// invalidated.
func (t *TPMContext) EvictControl(auth, object ResourceContext, persistentHandle Handle, authAuthSession SessionContext, sessions ...SessionContext) (ResourceContext, error) {
Expand All @@ -218,13 +234,13 @@ func (t *TPMContext) EvictControl(auth, object ResourceContext, persistentHandle
name := make(Name, len(object.Name()))
copy(name, object.Name())

switch {
case object.Handle() == persistentHandle:
if object.Handle() == persistentHandle {
// we evicted an object
object.Dispose()
return nil, nil
case public != nil:
return newObjectContext(persistentHandle, name, public), nil
default:
return newLimitedResourceContext(persistentHandle, name), nil
}
if public == nil {
return newResourceContext(persistentHandle, name), nil
}
return newObjectContext(persistentHandle, name, public), nil
}
54 changes: 42 additions & 12 deletions cmds_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (s *contextSuiteBase) testEvictControl(c *C, data *testEvictControlData) {

c.Check(persist.Handle(), Equals, HandleUnassigned)

_, _, _, err = s.TPM.ReadPublic(NewLimitedHandleContext(data.handle))
_, _, _, err = s.TPM.ReadPublic(NewHandleContext(data.handle))
c.Assert(err, internal_testutil.ConvertibleTo, &TPMHandleError{})
c.Check(err.(*TPMHandleError), DeepEquals, &TPMHandleError{TPMError: &TPMError{Command: CommandReadPublic, Code: ErrorHandle}, Index: 1})
}
Expand Down Expand Up @@ -126,7 +126,7 @@ func (s *contextSuite) TestContextSaveSession(c *C) {
func (s *contextSuite) TestContextSaveLimitedResourceContext(c *C) {
object := s.CreateStoragePrimaryKeyRSA(c)

lr := NewLimitedResourceContext(object.Handle(), object.Name())
lr := NewResourceContext(object.Handle(), object.Name())

context, err := s.TPM.ContextSave(lr)
c.Assert(err, IsNil)
Expand All @@ -138,7 +138,7 @@ func (s *contextSuite) TestContextSaveLimitedResourceContext(c *C) {
func (s *contextSuite) TestContextSaveLimitedHandleContext(c *C) {
session := s.StartAuthSession(c, nil, nil, SessionTypeHMAC, nil, HashAlgorithmSHA256)

lh := NewLimitedHandleContext(session.Handle())
lh := NewHandleContext(session.Handle())

context, err := s.TPM.ContextSave(lh)
c.Assert(err, IsNil)
Expand Down Expand Up @@ -210,7 +210,7 @@ func (s *contextSuite) TestContextSaveAndLoadSession(c *C) {
func (s *contextSuite) TestContextSaveAndLoadSessionLimitedHandle(c *C) {
session := s.StartAuthSession(c, nil, nil, SessionTypePolicy, nil, HashAlgorithmSHA256)

lh := NewLimitedHandleContext(session.Handle())
lh := NewHandleContext(session.Handle())

context, err := s.TPM.ContextSave(lh)
c.Assert(err, IsNil)
Expand All @@ -219,20 +219,18 @@ func (s *contextSuite) TestContextSaveAndLoadSessionLimitedHandle(c *C) {
c.Assert(err, IsNil)

var sample SessionContext
c.Assert(restored, Implements, &sample)
c.Assert(restored, Not(Implements), &sample)

c.Check(restored.Handle(), Equals, lh.Handle())
c.Check(restored.Name(), DeepEquals, lh.Name())
c.Assert(restored, internal_testutil.ConvertibleTo, &SessionContextImpl{})
c.Check(restored.(*SessionContextImpl).Data(), IsNil)

c.Check(s.TPM.DoesHandleExist(restored.Handle()), internal_testutil.IsTrue)
}

func (s *contextSuite) TestContextSaveAndLoadTransientLimitedResource(c *C) {
object := s.CreateStoragePrimaryKeyRSA(c)

lr := NewLimitedResourceContext(object.Handle(), object.Name())
lr := NewResourceContext(object.Handle(), object.Name())

context, err := s.TPM.ContextSave(lr)
c.Assert(err, IsNil)
Expand All @@ -243,6 +241,9 @@ func (s *contextSuite) TestContextSaveAndLoadTransientLimitedResource(c *C) {
var sample ResourceContext
c.Check(restored, Implements, &sample)

var sample2 ObjectContext
c.Check(restored, Not(Implements), &sample2)

c.Check(restored.Handle().Type(), Equals, HandleTypeTransient)
c.Check(restored.Handle(), Not(Equals), lr.Handle())
c.Check(restored.Name(), DeepEquals, lr.Name())
Expand All @@ -255,7 +256,7 @@ func (s *contextSuite) TestContextSaveAndLoadTransientLimitedResource(c *C) {
func (s *contextSuite) TestContextSaveAndLoadTransientLimitedHandle(c *C) {
object := s.CreateStoragePrimaryKeyRSA(c)

lh := NewLimitedHandleContext(object.Handle())
lh := NewHandleContext(object.Handle())

context, err := s.TPM.ContextSave(lh)
c.Assert(err, IsNil)
Expand All @@ -264,11 +265,11 @@ func (s *contextSuite) TestContextSaveAndLoadTransientLimitedHandle(c *C) {
c.Assert(err, IsNil)

var sample ResourceContext
c.Check(restored, Implements, &sample)
c.Check(restored, Not(Implements), &sample)

c.Check(restored.Handle().Type(), Equals, HandleTypeTransient)
c.Check(restored.Handle(), Not(Equals), lh.Handle())
c.Check(restored.Name(), DeepEquals, lh.Name())
c.Check(restored.Name(), DeepEquals, Name(mu.MustMarshalToBytes(restored.Handle())))

c.Check(s.TPM.DoesHandleExist(restored.Handle()), internal_testutil.IsTrue)
}
Expand Down Expand Up @@ -300,7 +301,7 @@ func (s *contextSuite) TestFlushContextTransient(c *C) {

c.Check(object.Handle(), Equals, HandleUnassigned)

_, _, _, err := s.TPM.ReadPublic(NewLimitedHandleContext(handle))
_, _, _, err := s.TPM.ReadPublic(NewHandleContext(handle))
c.Assert(err, internal_testutil.ConvertibleTo, &TPMWarning{})
c.Check(err.(*TPMWarning), DeepEquals, &TPMWarning{Command: CommandReadPublic, Code: WarningReferenceH0})
}
Expand All @@ -317,3 +318,32 @@ func (s *contextSuite) TestFlushContextSession(c *C) {
c.Assert(err, IsNil)
c.Check(handle, Not(internal_testutil.IsOneOf(Equals)), handles)
}

func (s *contextSuite) TestEvictControlLimitedResource(c *C) {
object := s.CreatePrimary(c, HandleOwner, testutil.NewRSAStorageKeyTemplate())

lr := NewResourceContext(object.Handle(), object.Name())
handle := s.NextAvailableHandle(c, 0x81000000)

persist, err := s.TPM.EvictControl(s.TPM.OwnerHandleContext(), lr, handle, nil)
c.Assert(err, IsNil)
c.Check(persist.Handle(), Equals, handle)
c.Check(persist.Name(), DeepEquals, lr.Name())

var sample ObjectContext
c.Check(persist, Not(Implements), &sample)

_, name, _, err := s.TPM.ReadPublic(persist)
c.Assert(err, IsNil)
c.Check(name, DeepEquals, lr.Name())

persist2, err := s.TPM.EvictControl(s.TPM.OwnerHandleContext(), persist, handle, nil)
c.Check(err, IsNil)
c.Check(persist2, IsNil)

c.Check(persist.Handle(), Equals, HandleUnassigned)

_, _, _, err = s.TPM.ReadPublic(NewHandleContext(handle))
c.Assert(err, internal_testutil.ConvertibleTo, &TPMHandleError{})
c.Check(err.(*TPMHandleError), DeepEquals, &TPMHandleError{TPMError: &TPMError{Command: CommandReadPublic, Code: ErrorHandle}, Index: 1})
}
4 changes: 2 additions & 2 deletions cmds_hashhmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (t *TPMContext) HMACStart(context ResourceContext, auth Auth, hashAlg HashA
return nil, err
}

rc := newLimitedResourceContext(sequenceHandle, nil)
rc := newResourceContext(sequenceHandle, nil)
authValue := make([]byte, len(auth))
copy(authValue, auth)
rc.SetAuthValue(authValue)
Expand Down Expand Up @@ -71,7 +71,7 @@ func (t *TPMContext) HashSequenceStart(auth Auth, hashAlg HashAlgorithmId, sessi
return nil, err
}

rc := newLimitedResourceContext(sequenceHandle, nil)
rc := newResourceContext(sequenceHandle, nil)
authValue := make([]byte, len(auth))
copy(authValue, auth)
rc.SetAuthValue(authValue)
Expand Down
13 changes: 7 additions & 6 deletions cmds_hierarchy.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ import (
// On success, a ResourceContext instance will be returned that corresponds to the newly created
// object on the TPM. It will not be necessary to call [ResourceContext].SetAuthValue on it - this
// function sets the correct authorization value so that it can be used in subsequent commands that
// require knowledge of the authorization value. If the Type field of inPublic is
// [ObjectTypeKeyedHash] or [ObjectTypeSymCipher], then the returned *Public object will have a
// Unique field that is the digest of the sensitive data and the value of the object's seed in the
// sensitive area, computed using the object's name algorithm. If the Type field of inPublic is
// [ObjectTypeECC] or [ObjectTypeRSA], then the returned *Public object will have a Unique field
// containing details about the public part of the key, computed from the private part of the key.
// require knowledge of the authorization value. The returned ResourceContext can be type asserted
// to [ObjectContext]. If the Type field of inPublic is [ObjectTypeKeyedHash] or
// [ObjectTypeSymCipher], then the returned *Public object will have a Unique field that is the digest
// of the sensitive data and the value of the object's seed in the sensitive area, computed using the
// object's name algorithm. If the Type field of inPublic is [ObjectTypeECC] or [ObjectTypeRSA], then
// the returned *Public object will have a Unique field containing details about the public part of the
// key, computed from the private part of the key.
//
// The returned *CreationData will contain a digest computed from the values of PCRs selected by
// the creationPCR parameter at creation time in the PCRDigest field. It will also contain the
Expand Down
Loading

0 comments on commit 57a0ce4

Please sign in to comment.