Last active
August 3, 2021 23:31
-
-
Save chrisccoulson/9d3a7e6e4d2d330e60dbd6174985becd to your computer and use it in GitHub Desktop.
test_tpmauth.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This tests behaviour of authorization value handling. Some things to note | |
// from the reference TPM implementation: | |
// - Authorization values for objects are stored padded with trailing zeroes to | |
// the size of the object's name algorithm. | |
// - Authorization values for hierarchies and NV indices are stored with trailing | |
// zeroes removed. | |
// - EntityGetAuthValue removes trailing zeroes from an entity's authorization | |
// value, prior to its use in the front end code when comparing comparing it | |
// against a password session or using it to compute a HMAC key for a HMAC | |
// session, or in the TPM2_StartAuthSession when computing the session key. | |
// - The front end code removes trailing zeroes from a password session. | |
// - TPM2_LoadExternal doesn't modify the authorization value of external | |
// objects. | |
// - TPM2_Import pads the authorization value of imported objects to the size | |
// of the object's name algorithm. | |
package main | |
import ( | |
"bytes" | |
"crypto" | |
"crypto/rand" | |
_ "crypto/sha256" | |
"encoding/binary" | |
"errors" | |
"fmt" | |
"os" | |
"github.com/canonical/go-tpm2" | |
"github.com/canonical/go-tpm2/testutil" | |
"github.com/jessevdk/go-flags" | |
"gopkg.in/check.v1" | |
) | |
type options struct { | |
TPM string `long:"tpm" description:"Test the TPM at the specified character device" default:"/dev/tpm0" optional:"true" optional-value:"/dev/tpm0"` | |
Mssim *uint `long:"mssim" description:"Test the TPM simulator on the specified local port" optional:"true" optional-value:"2321"` | |
Verbose []bool `short:"v" long:"verbose" description:"Enable verbose output"` | |
} | |
var opts options | |
type authSuite struct { | |
testutil.TPMTest | |
} | |
func (s *authSuite) SetUpSuite(c *check.C) { | |
s.TPMFeatures = testutil.TPMFeatureOwnerHierarchy|testutil.TPMFeatureNV | |
} | |
var _ = check.Suite(&authSuite{}) | |
func (s *authSuite) createSealedObject(c *check.C, parent tpm2.ResourceContext, userAuth tpm2.Auth) (tpm2.Private, *tpm2.Public) { | |
sensitive := tpm2.SensitiveCreate{ | |
UserAuth: userAuth, | |
Data: make([]byte, 32)} | |
template := tpm2.Public{ | |
Type: tpm2.ObjectTypeKeyedHash, | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.AttrUserWithAuth | tpm2.AttrNoDA, | |
Params: &tpm2.PublicParamsU{KeyedHashDetail: &tpm2.KeyedHashParams{Scheme: tpm2.KeyedHashScheme{Scheme: tpm2.KeyedHashSchemeNull}}}} | |
priv, pub, _, _, _, err := s.TPM.Create(parent, &sensitive, &template, nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
return priv, pub | |
} | |
func (s *authSuite) createAndLoadSealedObject(c *check.C, parent tpm2.ResourceContext, userAuth tpm2.Auth) tpm2.ResourceContext { | |
priv, pub := s.createSealedObject(c, parent, userAuth) | |
object, err := s.TPM.Load(parent, priv, pub, nil) | |
c.Assert(err, check.IsNil) | |
return object | |
} | |
func (s *authSuite) TestObject(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
object := s.createAndLoadSealedObject(c, primary, []byte("password")) | |
object.SetAuthValue([]byte("password")) | |
_, err := s.TPM.Unseal(object, nil) | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestObjectAuthValueWithTrailingZeroes(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
object := s.createAndLoadSealedObject(c, primary, []byte("password\x00\x00")) | |
object.SetAuthValue([]byte("password")) | |
_, err := s.TPM.Unseal(object, nil) | |
// We created an object with an auth value that has trailing zeroes. | |
// The reference TPM implementation pads the auth value to the size of | |
// the name algorithm anyway, and EntityGetAuthValue removes the trailing | |
// zeroes when checking the auth value. The frontend removes trailing zeroes | |
// from the password session, so this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestObjectAuthValueWithTrailingZeroesAndUnboundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
object := s.createAndLoadSealedObject(c, primary, []byte("password\x00\x00")) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
object.SetAuthValue([]byte("password")) | |
_, err := s.TPM.Unseal(object, session) | |
// We created an object with an auth value that has trailing zeroes. | |
// The reference TPM implementation pads the auth value to the size of | |
// the name algorithm anyway, EntityGetAuthValue removes the trailing | |
// zeroes when computing the HMAC key, and so do we, so this should | |
// work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestObjectAuthValueWithTrailingZeroesAndBoundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
object := s.createAndLoadSealedObject(c, primary, []byte("password\x00\x00")) | |
object.SetAuthValue([]byte("password")) | |
session := s.StartAuthSession(c, nil, object, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err := s.TPM.Unseal(object, session) | |
// We created an object with an auth value that has trailing zeroes. | |
// The reference TPM implementation pads the auth value to the size of | |
// the name algorithm anyway, EntityGetAuthValue removes the trailing | |
// zeroes when computing the session key, and so do we, so this should | |
// work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestObjectSize(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
priv, _ := s.createSealedObject(c, primary, []byte("password\x00\x00")) | |
// The reference TPM implementation pads the auth value of objects to the size | |
// of the name algorithm, and EntityGetAuthValue removes the trailing zeroes | |
// when the auth value is used later. This is to avoid leaking the size of | |
// the authorization value from the TPM. Other tests check the sensitive | |
// area from duplicated objects, but check that the size isn't leaked in | |
// an object currently protected by the TPM by checking that the private | |
// area has the expected size. | |
// | |
// The private area consists of the following fields (the sensitive | |
// area is encrypted): | |
// ============================================ | |
// = size (2 bytes) | outer HMAC (32 bytes) = | |
// = size (2 bytes) | symmetric IV (16 bytes) = | |
// = size of sensitive area (2 bytes) = | |
// = sensitiveType (2 bytes) = | |
// = size (2 bytes) | authValue (32 bytes) = | |
// = size (2 bytes) | seedValue (32 bytes) = | |
// = size (2 bytes) | sensitive (32 bytes) = | |
// ============================================ | |
// The size of the private area might be 134 bytes on A TPM that behaves | |
// incorrectly | |
c.Check(priv, testutil.LenEquals, 158) | |
} | |
func (s *authSuite) TestHierarchy(c *check.C) { | |
s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("password\x00\x00")) | |
_, _, _, _, _, err := s.TPM.CreatePrimary(s.TPM.OwnerHandleContext(), nil, testutil.NewRSAStorageKeyTemplate(), nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
} | |
func (s *authSuite) TestHierarchyAuthValueWithTrailingZeroes(c *check.C) { | |
s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("password\x00\x00")) | |
_, _, _, _, _, err := s.TPM.CreatePrimary(s.TPM.OwnerHandleContext(), nil, testutil.NewRSAStorageKeyTemplate(), nil, nil, nil) | |
// We changed the storage hierarchy auth value to one that has trailing | |
// zeroes. The reference TPM implementation removes the trailing zeroes | |
// before storing it to NV, and the frontend removes trailing zeroes | |
// from the password session, so this should work fine. | |
c.Assert(err, check.IsNil) | |
} | |
func (s *authSuite) TestHierarchyAuthValueWithTrailingZeroesAndUnboundHMACSession(c *check.C) { | |
s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("password\x00\x00")) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, _, _, _, _, err := s.TPM.CreatePrimary(s.TPM.OwnerHandleContext(), nil, testutil.NewRSAStorageKeyTemplate(), nil, nil, session) | |
// We changed the storage hierarchy auth value to one that has trailing | |
// zeroes. The reference TPM implementation removes the trailing zeroes | |
// before storing it to NV, and we remove trailing zeroes when computing | |
// the HMAC key, so this should work fine. | |
c.Assert(err, check.IsNil) | |
} | |
func (s *authSuite) TestHierarchyAuthValueWithTrailingZeroesAndBoundHMACSession(c *check.C) { | |
s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("password\x00\x00")) | |
session := s.StartAuthSession(c, nil, s.TPM.OwnerHandleContext(), tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, _, _, _, _, err := s.TPM.CreatePrimary(s.TPM.OwnerHandleContext(), nil, testutil.NewRSAStorageKeyTemplate(), nil, nil, session) | |
// We changed the storage hierarchy auth value to one that has trailing | |
// zeroes. The reference TPM implementation removes the trailing zeroes | |
// before storing it to NV, and we remove trailing zeroes when computing | |
// the session key, so this should work fine. | |
c.Assert(err, check.IsNil) | |
} | |
func (s *authSuite) TestHierarchyAuthValueAllZeroes(c *check.C) { | |
s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("\x00\x00\x00\x00")) | |
props, err := s.TPM.GetCapabilityTPMProperties(tpm2.PropertyPermanent, 1) | |
c.Assert(err, check.IsNil) | |
c.Assert(props, testutil.LenEquals, 1) | |
c.Check(props[0].Property, check.Equals, tpm2.PropertyPermanent) | |
// We changed the storage hierarchy auth value to all zeroes. As the | |
// reference TPM implementation removes trailing zeroes before storing it | |
// to NV, the TPM should indicate that the auth value isn't set. | |
c.Check(tpm2.PermanentAttributes(props[0].Value)&tpm2.AttrOwnerAuthSet, check.Equals, tpm2.PermanentAttributes(0)) | |
} | |
func (s *authSuite) makeSealedObjectExternal() (*tpm2.Public, *tpm2.Sensitive) { | |
key := make([]byte, 32) | |
seed := make([]byte, crypto.SHA256.Size()) | |
rand.Read(seed) | |
h := crypto.SHA256.New() | |
h.Write(seed) | |
h.Write(key) | |
unique := h.Sum(nil) | |
authValue := make(tpm2.Auth, crypto.SHA256.Size()) | |
copy(authValue, []byte("password")) | |
sensitive := tpm2.Sensitive{ | |
Type: tpm2.ObjectTypeKeyedHash, | |
AuthValue: authValue, | |
SeedValue: seed, | |
Sensitive: &tpm2.SensitiveCompositeU{Bits: key}} | |
public := tpm2.Public{ | |
Type: tpm2.ObjectTypeKeyedHash, | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.AttrUserWithAuth | tpm2.AttrNoDA, | |
Params: &tpm2.PublicParamsU{KeyedHashDetail: &tpm2.KeyedHashParams{Scheme: tpm2.KeyedHashScheme{Scheme: tpm2.KeyedHashSchemeNull}}}, | |
Unique: &tpm2.PublicIDU{KeyedHash: unique}} | |
return &public, &sensitive | |
} | |
func (s *authSuite) TestLoadExternal(c *check.C) { | |
public, sensitive := s.makeSealedObjectExternal() | |
object, err := s.TPM.LoadExternal(sensitive, public, tpm2.HandleNull) | |
c.Check(err, check.IsNil) | |
_, err = s.TPM.Unseal(object, nil) | |
// We loaded an external object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when checking the auth value and the frontend removes | |
// trailing zeroes from the password session, this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestLoadExternalWithUnboundHMACSession(c *check.C) { | |
public, sensitive := s.makeSealedObjectExternal() | |
object, err := s.TPM.LoadExternal(sensitive, public, tpm2.HandleNull) | |
c.Check(err, check.IsNil) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err = s.TPM.Unseal(object, session) | |
// We loaded an external object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when computing the HMAC key, and so do we, this | |
// should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestLoadExternalWithBoundHMACSession(c *check.C) { | |
public, sensitive := s.makeSealedObjectExternal() | |
object, err := s.TPM.LoadExternal(sensitive, public, tpm2.HandleNull) | |
c.Check(err, check.IsNil) | |
session := s.StartAuthSession(c, nil, object, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err = s.TPM.Unseal(object, session) | |
// We loaded an external object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when computing the session key, and so do we, this | |
// should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) importObjectFromSensitive(c *check.C, parent tpm2.ResourceContext, public *tpm2.Public, sensitive *tpm2.Sensitive) tpm2.Private { | |
parentPub, _, _, err := s.TPM.ReadPublic(parent) | |
c.Assert(err, check.IsNil) | |
_, duplicate, symSeed, err := tpm2.CreateDuplicationObjectFromSensitive(sensitive, public, parentPub, nil, nil) | |
c.Assert(err, check.IsNil) | |
priv, err := s.TPM.Import(parent, nil, public, duplicate, symSeed, nil, nil) | |
c.Assert(err, check.IsNil) | |
return priv | |
} | |
func (s *authSuite) importObjectFromSensitiveAndLoad(c *check.C, parent tpm2.ResourceContext, public *tpm2.Public, sensitive *tpm2.Sensitive) tpm2.ResourceContext { | |
priv := s.importObjectFromSensitive(c, parent, public, sensitive) | |
object, err := s.TPM.Load(parent, priv, public, nil) | |
c.Assert(err, check.IsNil) | |
return object | |
} | |
func (s *authSuite) TestImport(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
_, err := s.TPM.Unseal(object, nil) | |
// We imported an object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when checking the auth value and the frontend removes | |
// trailing zeroes from the password session, this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestImportWithUnboundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err := s.TPM.Unseal(object, session) | |
// We imported an object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when computing the HMAC key, and so do we, this | |
// should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestImportWithBoundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
session := s.StartAuthSession(c, nil, object, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err := s.TPM.Unseal(object, session) | |
// We imported an object with an authorization value padded to the | |
// size of the object's name algorithm. As EntityGetAuthValue removes the | |
// trailing zeroes when computing the session key, and so do we, this | |
// should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestImportWithShortAuthValue(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
sensitive.AuthValue = []byte("password\x00\x00") | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
_, err := s.TPM.Unseal(object, nil) | |
// We imported an object with an authorization value with some trailing | |
// zeroes but not padded to the size of the object's name algorithm. The | |
// reference TPM implementation fixes this, so this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestImportWithShortAuthValueAndUnboundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
sensitive.AuthValue = []byte("password\x00\x00") | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err := s.TPM.Unseal(object, session) | |
// We imported an object with an authorization value with some trailing | |
// zeroes but not padded to the size of the object's name algorithm. The | |
// reference TPM implementation fixes this, so this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestImportWithShortAuthValueAndBoundHMACSession(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
pub, sensitive := s.makeSealedObjectExternal() | |
sensitive.AuthValue = []byte("password\x00\x00") | |
object := s.importObjectFromSensitiveAndLoad(c, primary, pub, sensitive) | |
object.SetAuthValue([]byte("password")) | |
session := s.StartAuthSession(c, nil, object, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
_, err := s.TPM.Unseal(object, session) | |
// We imported an object with an authorization value with some trailing | |
// zeroes but not padded to the size of the object's name algorithm. The | |
// reference TPM implementation fixes this, so this should work fine. | |
c.Check(err, check.IsNil) | |
} | |
func (s *authSuite) TestDuplicateObject(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
template := testutil.NewRSAStorageKeyTemplate() | |
template.Attrs &^= tpm2.AttrFixedTPM|tpm2.AttrFixedParent | |
trial, _ := tpm2.ComputeAuthPolicy(tpm2.HashAlgorithmSHA256) | |
trial.PolicyCommandCode(tpm2.CommandDuplicate) | |
template.AuthPolicy = trial.GetDigest() | |
inSensitive := tpm2.SensitiveCreate{UserAuth: []byte("password")} | |
priv, pub, _, _, _, err := s.TPM.Create(primary, &inSensitive, template, nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
object, err := s.TPM.Load(primary, priv, pub, nil) | |
c.Assert(err, check.IsNil) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypePolicy, nil, tpm2.HashAlgorithmSHA256) | |
c.Check(s.TPM.PolicyCommandCode(session, tpm2.CommandDuplicate), check.IsNil) | |
_, duplicate, _, err := s.TPM.Duplicate(object, nil, nil, nil, session) | |
c.Assert(err, check.IsNil) | |
sensitive, err := tpm2.UnwrapDuplicationObjectToSensitive(duplicate, pub, nil, tpm2.HashAlgorithmNull, nil, nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
// We duplicated an object created by the TPM. The reference TPM implementation | |
// pads the auth value of objects to the size of the name algorithm, and we should | |
// see this in the duplicated sensitive area. | |
c.Check(sensitive.AuthValue, testutil.LenEquals, crypto.SHA256.Size()) | |
} | |
func (s *authSuite) TestDuplicateImported(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
public, sensitive := s.makeSealedObjectExternal() | |
trial, _ := tpm2.ComputeAuthPolicy(tpm2.HashAlgorithmSHA256) | |
trial.PolicyCommandCode(tpm2.CommandDuplicate) | |
public.AuthPolicy = trial.GetDigest() | |
object := s.importObjectFromSensitiveAndLoad(c, primary, public, sensitive) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypePolicy, nil, tpm2.HashAlgorithmSHA256) | |
c.Check(s.TPM.PolicyCommandCode(session, tpm2.CommandDuplicate), check.IsNil) | |
_, duplicate, _, err := s.TPM.Duplicate(object, nil, nil, nil, session) | |
c.Assert(err, check.IsNil) | |
sensitive2, err := tpm2.UnwrapDuplicationObjectToSensitive(duplicate, public, nil, tpm2.HashAlgorithmNull, nil, nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
// We duplicated an object imported into the TPM. As the object already | |
// had an authorization value padded to the size of the name algorithm, | |
// we should see the unmodified value in the duplicated sensitive area. | |
c.Check(sensitive2.AuthValue, check.DeepEquals, sensitive.AuthValue) | |
} | |
func (s *authSuite) TestDuplicateImportedWithShortAuthValue1(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
public, sensitive := s.makeSealedObjectExternal() | |
trial, _ := tpm2.ComputeAuthPolicy(tpm2.HashAlgorithmSHA256) | |
trial.PolicyCommandCode(tpm2.CommandDuplicate) | |
public.AuthPolicy = trial.GetDigest() | |
sensitive.AuthValue = []byte("password\x00\x00") | |
object := s.importObjectFromSensitiveAndLoad(c, primary, public, sensitive) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypePolicy, nil, tpm2.HashAlgorithmSHA256) | |
c.Check(s.TPM.PolicyCommandCode(session, tpm2.CommandDuplicate), check.IsNil) | |
_, duplicate, _, err := s.TPM.Duplicate(object, nil, nil, nil, session) | |
c.Assert(err, check.IsNil) | |
sensitive2, err := tpm2.UnwrapDuplicationObjectToSensitive(duplicate, public, nil, tpm2.HashAlgorithmNull, nil, nil, nil, nil) | |
c.Assert(err, check.IsNil) | |
expectedAuthValue := make(tpm2.Auth, public.NameAlg.Size()) | |
copy(expectedAuthValue, sensitive.AuthValue) | |
// We duplicated an object imported into the TPM. The object had an authorization | |
// value that wasn't padded to the size of the name algorithm. The reference TPM | |
// implementation corrects this, so we should see the corrected version in the | |
// duplicated sensitive area. | |
c.Check(sensitive2.AuthValue, check.DeepEquals, expectedAuthValue) | |
} | |
func (s *authSuite) TestObjectSizeImported(c *check.C) { | |
primary := s.CreateStoragePrimaryKeyRSA(c) | |
public, sensitive := s.makeSealedObjectExternal() | |
priv := s.importObjectFromSensitive(c, primary, public, sensitive) | |
// The reference TPM implementation pads the auth value of objects to the size | |
// of the name algorithm, and EntityGetAuthValue removes the trailing zeroes | |
// when the auth value is used later. This is to avoid leaking the size of | |
// the authorization value from the TPM. Other tests check the sensitive | |
// area from duplicated objects, but check that the size isn't leaked in | |
// an object currently protected by the TPM by checking that the private | |
// area has the expected size. | |
// | |
// The private area consists of the following fields (the sensitive | |
// area is encrypted): | |
// ============================================ | |
// = size (2 bytes) | outer HMAC (32 bytes) = | |
// = size (2 bytes) | symmetric IV (16 bytes) = | |
// = size of sensitive area (2 bytes) = | |
// = sensitiveType (2 bytes) = | |
// = size (2 bytes) | authValue (32 bytes) = | |
// = size (2 bytes) | seedValue (32 bytes) = | |
// = size (2 bytes) | sensitive (32 bytes) = | |
// ============================================ | |
// The size of the private area might be 134 bytes on A TPM that behaves | |
// incorrectly | |
c.Check(priv, testutil.LenEquals, 158) | |
} | |
func (s *authSuite) TestNVIndex(c *check.C) { | |
pub := tpm2.NVPublic{ | |
Index: s.NextAvailableHandle(c, 0x01800000), | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.NVTypeOrdinary.WithAttrs(tpm2.AttrNVAuthRead|tpm2.AttrNVAuthWrite|tpm2.AttrNVNoDA), | |
Size: 8} | |
index, err := s.TPM.NVDefineSpace(s.TPM.OwnerHandleContext(), []byte("password"), &pub, nil) | |
c.Assert(err, check.IsNil) | |
c.Check(s.TPM.NVWrite(index, index, nil, 0, nil), check.IsNil) | |
} | |
func (s *authSuite) TestNVIndexAuthValueWithTrailingZeroes(c *check.C) { | |
pub := tpm2.NVPublic{ | |
Index: s.NextAvailableHandle(c, 0x01800000), | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.NVTypeOrdinary.WithAttrs(tpm2.AttrNVAuthRead|tpm2.AttrNVAuthWrite|tpm2.AttrNVNoDA), | |
Size: 8} | |
index, err := s.TPM.NVDefineSpace(s.TPM.OwnerHandleContext(), []byte("password\x00\x00"), &pub, nil) | |
c.Assert(err, check.IsNil) | |
// We created a NV index with an auth value that has trailing zeroes. | |
// The reference TPM implementation removes the trailing zeroes before | |
// storing it to NV, and the frontend removes trailing zeroes | |
// from the password session, so this should work fine. | |
c.Check(s.TPM.NVWrite(index, index, nil, 0, nil), check.IsNil) | |
} | |
func (s *authSuite) TestNVIndexAuthValueWithTrailingZeroesAndUnboundHMACSession(c *check.C) { | |
pub := tpm2.NVPublic{ | |
Index: s.NextAvailableHandle(c, 0x01800000), | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.NVTypeOrdinary.WithAttrs(tpm2.AttrNVAuthRead|tpm2.AttrNVAuthWrite|tpm2.AttrNVNoDA), | |
Size: 8} | |
index, err := s.TPM.NVDefineSpace(s.TPM.OwnerHandleContext(), []byte("password\x00\x00"), &pub, nil) | |
c.Assert(err, check.IsNil) | |
session := s.StartAuthSession(c, nil, nil, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
// We created a NV index with an auth value that has trailing zeroes. | |
// The reference TPM implementation removes the trailing zeroes before | |
// storing it to NV, and we remove the trailing zeroes when computing | |
// the HMAC key, so this should work fine. | |
c.Check(s.TPM.NVWrite(index, index, nil, 0, session), check.IsNil) | |
} | |
func (s *authSuite) TestNVIndexAuthValueWithTrailingZeroesAndBoundHMACSession(c *check.C) { | |
pub := tpm2.NVPublic{ | |
Index: s.NextAvailableHandle(c, 0x01800000), | |
NameAlg: tpm2.HashAlgorithmSHA256, | |
Attrs: tpm2.NVTypeOrdinary.WithAttrs(tpm2.AttrNVAuthRead|tpm2.AttrNVAuthWrite|tpm2.AttrNVNoDA), | |
Size: 8} | |
index, err := s.TPM.NVDefineSpace(s.TPM.OwnerHandleContext(), []byte("password\x00\x00"), &pub, nil) | |
c.Assert(err, check.IsNil) | |
session := s.StartAuthSession(c, nil, index, tpm2.SessionTypeHMAC, nil, tpm2.HashAlgorithmSHA256) | |
// We created a NV index with an auth value that has trailing zeroes. | |
// The reference TPM implementation removes the trailing zeroes before | |
// storing it to NV, and we remove the trailing zeroes when computing | |
// the session key, so this should work fine. | |
c.Check(s.TPM.NVWrite(index, index, nil, 0, session), check.IsNil) | |
} | |
func printTPMInfo() { | |
var tcti tpm2.TCTI | |
var err error | |
if opts.Mssim != nil { | |
tcti, err = tpm2.OpenMssim("", *opts.Mssim) | |
} else { | |
tcti, err = tpm2.OpenTPMDevice(opts.TPM) | |
} | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "cannot open TPM device: %v", err) | |
return | |
} | |
tpm, _ := tpm2.NewTPMContext(tcti) | |
defer tpm.Close() | |
manufacturer, err := tpm.GetManufacturer() | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "cannot determine TPM manufacturer: %v", err) | |
return | |
} | |
fmt.Printf("TPM manufacturer: %v\n", manufacturer) | |
props, err := tpm.GetCapabilityTPMProperties(tpm2.PropertyVendorString1, 7) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "cannot obtain TPM properties: %v", err) | |
return | |
} | |
vendorString := new(bytes.Buffer) | |
var firmwareVersion [8]byte | |
for _, prop := range props { | |
switch prop.Property { | |
case tpm2.PropertyVendorString1, tpm2.PropertyVendorString2, tpm2.PropertyVendorString3, tpm2.PropertyVendorString4: | |
binary.Write(vendorString, binary.BigEndian, prop.Value) | |
case tpm2.PropertyFirmwareVersion1: | |
binary.BigEndian.PutUint32(firmwareVersion[0:], prop.Value) | |
case tpm2.PropertyFirmwareVersion2: | |
binary.BigEndian.PutUint32(firmwareVersion[4:], prop.Value) | |
} | |
} | |
vendorBytes := vendorString.Bytes() | |
vendor, _ := vendorString.ReadString(0) | |
fmt.Printf("TPM vendor string: %s\n", vendor) | |
fmt.Printf("TPM vendor string (bytes): 0x%x\n", vendorBytes) | |
fmt.Printf("TPM firmware version: 0x%x\n", firmwareVersion) | |
} | |
func run() error { | |
if _, err := flags.Parse(&opts); err != nil { | |
return err | |
} | |
if opts.Mssim != nil { | |
testutil.TPMBackend = testutil.TPMBackendMssim | |
testutil.MssimPort = *opts.Mssim | |
shutdown, err := testutil.LaunchTPMSimulator(nil) | |
if err != nil { | |
return fmt.Errorf("cannot start simulator: %v", err) | |
} | |
defer shutdown() | |
} else { | |
testutil.TPMBackend = testutil.TPMBackendDevice | |
testutil.TPMDevicePath = opts.TPM | |
} | |
testutil.PermittedTPMFeatures = testutil.TPMFeatureOwnerHierarchy|testutil.TPMFeatureNV | |
printTPMInfo() | |
if opts.Mssim == nil { | |
fmt.Println("") | |
fmt.Println("This may take a few minutes to run on a real TPM device.") | |
fmt.Println("Interrupting this test will prevent the fixture from restoring ") | |
fmt.Println("the TPM to the state it was at the start of the test!") | |
} | |
conf := new(check.RunConf) | |
if len(opts.Verbose) >= 1 { | |
conf.Verbose = true | |
} | |
if len(opts.Verbose) >= 2 { | |
conf.Stream = true | |
} | |
results := check.RunAll(conf) | |
println(results.String()) | |
if !results.Passed() { | |
return errors.New("not all tests passed") | |
} | |
return nil | |
} | |
func main() { | |
if err := run(); err != nil { | |
switch e := err.(type) { | |
case *flags.Error: | |
// flags already prints this | |
if e.Type != flags.ErrHelp { | |
os.Exit(1) | |
} | |
default: | |
fmt.Fprintln(os.Stderr, err) | |
os.Exit(1) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment