Repository: google/go-tpm
Pull request #113 (2019-07-19 07:50:31)

Add LoadUsingAuth and CreateKeyUsingAuth
Right now, we can only call `Load` or `CreateKey` with password based authorization, we have to add methods in order to use policy authorization.

This allows us to create child keys of the default EK, for example. We also did a little refactoring of the Create/Load methods to reduce duplicated code (essentially the old `Load` and `CreateKey` methods now call `LoadWithAuth` and `CreateKeyWithAuth`)

@stevenrutherford authored a good chunk of this code.


Patch:
diff --git a/tpm2/credactivation/credential_activation.go b/tpm2/credactivation/credential_activation.go
index 6ae3f5e..a8a1786 100644
--- a/tpm2/credactivation/credential_activation.go
+++ b/tpm2/credactivation/credential_activation.go
@@ -121,14 +121,19 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
 		IntegrityHMAC: integrityHMAC,
 		EncIdentity:   encIdentity,
 	}
-	id, err := idObject.Encode()
+	id, err := tpmutil.Pack(idObject)
 	if err != nil {
 		return nil, nil, fmt.Errorf("encoding IDObject: %v", err)
 	}
+
+	packedID, err := tpmutil.Pack(tpmutil.U16Bytes(id))
+	if err != nil {
+		return nil, nil, fmt.Errorf("packing id: %v", err)
+	}
 	packedEncSecret, err := tpmutil.Pack(tpmutil.U16Bytes(encSecret))
 	if err != nil {
 		return nil, nil, fmt.Errorf("packing encSecret: %v", err)
 	}
 
-	return id, packedEncSecret, nil
+	return packedID, packedEncSecret, nil
 }
diff --git a/tpm2/structures.go b/tpm2/structures.go
index cecab5f..a936e74 100644
--- a/tpm2/structures.go
+++ b/tpm2/structures.go
@@ -60,12 +60,12 @@ type Public struct {
 	Attributes KeyProp
 	AuthPolicy tpmutil.U16Bytes
 
-	// If Type is AlgKeyedHash, then do not set these.
-	// Otherwise, only one of the Parameters fields should be set. When encoding/decoding,
+	// Only one of the following fields should be set. When encoding/decoding,
 	// one will be picked based on Type.
 	RSAParameters       *RSAParams
 	ECCParameters       *ECCParams
 	SymCipherParameters *SymCipherParams
+	KeyedHashUnique     tpmutil.U16Bytes // Optional for AlgKeyedHash
 }
 
 // Encode serializes a Public structure in TPM wire format.
@@ -79,10 +79,7 @@ func (p Public) Encode() ([]byte, error) {
 	case AlgRSA:
 		params, err = p.RSAParameters.encode()
 	case AlgKeyedHash:
-		// We only support "keyedHash" objects for the purposes of
-		// creating "Sealed Data Blobs".
-		var unique uint16
-		params, err = tpmutil.Pack(AlgNull, unique)
+		params, err = tpmutil.Pack(AlgNull, p.KeyedHashUnique)
 	case AlgECC:
 		params, err = p.ECCParameters.encode()
 	case AlgSymCipher:
@@ -120,6 +117,26 @@ func (p Public) Key() (crypto.PublicKey, error) {
 	return pubKey, nil
 }
 
+// Name computes the Digest-based Name from the public area of an object.
+func (p Public) Name() (*Name, error) {
+	pubEncoded, err := p.Encode()
+	if err != nil {
+		return nil, err
+	}
+	hash, err := p.NameAlg.HashConstructor()
+	if err != nil {
+		return nil, err
+	}
+	nameHash := hash()
+	nameHash.Write(pubEncoded)
+	return &Name{
+		Digest: &HashValue{
+			Alg:   p.NameAlg,
+			Value: nameHash.Sum(nil),
+		},
+	}, nil
+}
+
 // DecodePublic decodes a TPMT_PUBLIC message. No error is returned if
 // the input has extra trailing data.
 func DecodePublic(buf []byte) (Public, error) {
@@ -137,6 +154,8 @@ func DecodePublic(buf []byte) (Public, error) {
 		pub.ECCParameters, err = decodeECCParams(in)
 	case AlgSymCipher:
 		pub.SymCipherParameters, err = decodeSymCipherParams(in)
+	case AlgKeyedHash:
+		err = tpmutil.UnpackBuf(in, &pub.KeyedHashUnique)
 	default:
 		err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", pub.Type)
 	}
@@ -341,6 +360,7 @@ func decodeSymCipherParams(in *bytes.Buffer) (*SymCipherParams, error) {
 }
 
 // SymScheme represents a symmetric encryption scheme.
+// Known in the specification by TPMT_SYM_DEF_OBJECT.
 type SymScheme struct {
 	Alg     Algorithm
 	KeyBits uint16
@@ -699,19 +719,9 @@ func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) {
 // IDObject represents an encrypted credential bound to a TPM object.
 type IDObject struct {
 	IntegrityHMAC tpmutil.U16Bytes
-	EncIdentity   tpmutil.RawBytes
-}
-
-// Encode packs the IDObject into a byte stream representing
-// a TPM2B_ID_OBJECT.
-func (o *IDObject) Encode() ([]byte, error) {
-	// encIdentity is packed raw, as the bytes representing the size
+	// EncIdentity is packed raw, as the bytes representing the size
 	// of the credential value are present within the encrypted blob.
-	d, err := tpmutil.Pack(o.IntegrityHMAC, o.EncIdentity)
-	if err != nil {
-		return nil, fmt.Errorf("encoding IntegrityHMAC, EncIdentity: %v", err)
-	}
-	return tpmutil.Pack(tpmutil.U16Bytes(d))
+	EncIdentity tpmutil.RawBytes
 }
 
 // CreationData describes the attributes and environment for an object created
@@ -836,23 +846,15 @@ func (n Name) encode() ([]byte, error) {
 // MatchesPublic compares Digest in Name against given Public structure. Note:
 // this only works for regular Names, not Qualified Names.
 func (n Name) MatchesPublic(p Public) (bool, error) {
-	buf, err := p.Encode()
-	if err != nil {
-		return false, err
-	}
 	if n.Digest == nil {
 		return false, errors.New("Name doesn't have a Digest, can't compare to Public")
 	}
-	hfn, ok := hashConstructors[n.Digest.Alg]
-	if !ok {
-		return false, fmt.Errorf("Name hash algorithm 0x%x not supported", n.Digest.Alg)
+	expected, err := p.Name()
+	if err != nil {
+		return false, err
 	}
-
-	h := hfn()
-	h.Write(buf)
-	digest := h.Sum(nil)
-
-	return bytes.Equal(digest, n.Digest.Value), nil
+	// No secrets, so no constant-time comparison
+	return bytes.Equal(expected.Digest.Value, n.Digest.Value), nil
 }
 
 // HashValue is an algorithm-specific hash value.
diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go
index 5f31916..9615e7c 100644
--- a/tpm2/tpm2.go
+++ b/tpm2/tpm2.go
@@ -370,20 +370,12 @@ func encodeSensitiveArea(s tpmsSensitiveCreate) ([]byte, error) {
 }
 
 // encodeCreate works for both TPM2_Create and TPM2_CreatePrimary.
-func encodeCreate(owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, sensitiveData []byte, pub Public) ([]byte, error) {
-	inPublic, err := pub.Encode()
-	if err != nil {
-		return nil, err
-	}
-	return encodeCreateRawTemplate(owner, sel, parentPassword, ownerPassword, sensitiveData, inPublic)
-}
-
-func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, sensitiveData, inPublic []byte) ([]byte, error) {
+func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, sensitiveData []byte, pub Public) ([]byte, error) {
 	parent, err := tpmutil.Pack(owner)
 	if err != nil {
 		return nil, err
 	}
-	auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)})
+	encodedAuth, err := encodeAuthArea(auth)
 	if err != nil {
 		return nil, err
 	}
@@ -394,6 +386,10 @@ func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassw
 	if err != nil {
 		return nil, err
 	}
+	inPublic, err := pub.Encode()
+	if err != nil {
+		return nil, err
+	}
 	publicBlob, err := tpmutil.Pack(tpmutil.U16Bytes(inPublic))
 	if err != nil {
 		return nil, err
@@ -408,7 +404,7 @@ func encodeCreateRawTemplate(owner tpmutil.Handle, sel PCRSelection, parentPassw
 	}
 	return concat(
 		parent,
-		auth,
+		encodedAuth,
 		inSensitive,
 		publicBlob,
 		outsideInfo,
@@ -459,7 +455,7 @@ func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, par
 
 	pubKey, err := pub.Key()
 	if err != nil {
-		return 0, nil, err
+		return 0, nil, fmt.Errorf("extracting cryto.PublicKey from Public part of primary key: %v", err)
 	}
 
 	return hnd, pubKey, err
@@ -469,7 +465,8 @@ func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, par
 // This function differs from CreatePrimary in that all response elements
 // are returned, and they are returned in relatively raw form.
 func CreatePrimaryEx(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (keyHandle tpmutil.Handle, public, creationData, creationHash []byte, ticket *Ticket, creationName []byte, err error) {
-	cmd, err := encodeCreate(owner, sel, parentPassword, ownerPassword, nil /*inSensitive*/, pub)
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)}
+	cmd, err := encodeCreate(owner, sel, auth, ownerPassword, nil /*inSensitive*/, pub)
 	if err != nil {
 		return 0, nil, nil, nil, nil, nil, err
 	}
@@ -485,26 +482,11 @@ func CreatePrimaryEx(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, p
 // (TPMT_PUBLIC) provided pre-encoded. This is commonly used with key templates
 // stored in NV RAM.
 func CreatePrimaryRawTemplate(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, public []byte) (tpmutil.Handle, crypto.PublicKey, error) {
-	cmd, err := encodeCreateRawTemplate(owner, sel, parentPassword, ownerPassword, nil /*inSensitive*/, public)
-	if err != nil {
-		return 0, nil, err
-	}
-	resp, err := runCommand(rw, TagSessions, cmdCreatePrimary, tpmutil.RawBytes(cmd))
-	if err != nil {
-		return 0, nil, err
-	}
-
-	hnd, public, _, _, _, _, err := decodeCreatePrimary(resp)
 	pub, err := DecodePublic(public)
 	if err != nil {
-		return 0, nil, fmt.Errorf("parsing public: %v", err)
+		return 0, nil, fmt.Errorf("parsing input template: %v", err)
 	}
-
-	pubKey, err := pub.Key()
-	if err != nil {
-		return 0, nil, fmt.Errorf("extracting cryto.PublicKey from Public part of primary key: %v", err)
-	}
-	return hnd, pubKey, nil
+	return CreatePrimary(rw, owner, sel, parentPassword, ownerPassword, pub)
 }
 
 func decodeReadPublic(in []byte) (Public, []byte, []byte, error) {
@@ -551,8 +533,8 @@ func decodeCreate(in []byte) (private, public, creationData, creationHash tpmuti
 	return private, public, creationData, creationHash, creationTicket, nil
 }
 
-func create(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) {
-	cmd, err := encodeCreate(parentHandle, pcrSelection, parentPassword, objectPassword, sensitiveData, pub)
+func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) {
+	cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub)
 	if err != nil {
 		return nil, nil, nil, nil, Ticket{}, err
 	}
@@ -567,7 +549,15 @@ func create(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objec
 // Returns private key and public key blobs as well as the
 // creation data, a hash of said data and the creation ticket.
 func CreateKey(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) {
-	return create(rw, owner, parentPassword, ownerPassword, nil /*inSensitive*/, pub, sel)
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)}
+	return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel)
+}
+
+// CreateKeyUsingAuth creates a new key pair under the owner handle using the
+// provided AuthCommand. Returns private key and public key blobs as well as
+// the creation data, a hash of said data, and the creation ticket.
+func CreateKeyUsingAuth(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) {
+	return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel)
 }
 
 // Seal creates a data blob object that seals the sensitive data under a parent and with a
@@ -580,19 +570,20 @@ func Seal(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objectP
 		Attributes: FlagFixedTPM | FlagFixedParent,
 		AuthPolicy: objectAuthPolicy,
 	}
-	private, public, _, _, _, err := create(rw, parentHandle, parentPassword, objectPassword, sensitiveData, inPublic, PCRSelection{})
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)}
+	private, public, _, _, _, err := create(rw, parentHandle, auth, objectPassword, sensitiveData, inPublic, PCRSelection{})
 	if err != nil {
 		return nil, nil, err
 	}
 	return private, public, nil
 }
 
-func encodeLoad(parentHandle tpmutil.Handle, parentAuth string, publicBlob, privateBlob tpmutil.U16Bytes) ([]byte, error) {
+func encodeLoad(parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob tpmutil.U16Bytes) ([]byte, error) {
 	ah, err := tpmutil.Pack(parentHandle)
 	if err != nil {
 		return nil, err
 	}
-	auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: tpmutil.U16Bytes(parentAuth)})
+	encodedAuth, err := encodeAuthArea(auth)
 	if err != nil {
 		return nil, err
 	}
@@ -600,7 +591,7 @@ func encodeLoad(parentHandle tpmutil.Handle, parentAuth string, publicBlob, priv
 	if err != nil {
 		return nil, err
 	}
-	return concat(ah, auth, params)
+	return concat(ah, encodedAuth, params)
 }
 
 func decodeLoad(in []byte) (tpmutil.Handle, []byte, error) {
@@ -617,7 +608,14 @@ func decodeLoad(in []byte) (tpmutil.Handle, []byte, error) {
 // Load loads public/private blobs into an object in the TPM.
 // Returns loaded object handle and its name.
 func Load(rw io.ReadWriter, parentHandle tpmutil.Handle, parentAuth string, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) {
-	cmd, err := encodeLoad(parentHandle, parentAuth, publicBlob, privateBlob)
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentAuth)}
+	return LoadUsingAuth(rw, parentHandle, auth, publicBlob, privateBlob)
+}
+
+// LoadUsingAuth loads public/private blobs into an object in the TPM using the
+// provided AuthCommand. Returns loaded object handle and its name.
+func LoadUsingAuth(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) {
+	cmd, err := encodeLoad(parentHandle, auth, publicBlob, privateBlob)
 	if err != nil {
 		return 0, nil, err
 	}


Test patch:
diff --git a/tpm2/encoding_test.go b/tpm2/encoding_test.go
index 1df74f5..3dc6bba 100644
--- a/tpm2/encoding_test.go
+++ b/tpm2/encoding_test.go
@@ -106,7 +106,8 @@ func TestEncodeLoad(t *testing.T) {
 	}
 	privateBlob := testCmdBytes[33:123]
 	publicBlob := testCmdBytes[125:]
-	cmdBytes, err := encodeLoad(tpmutil.Handle(0x80000000), defaultPassword, publicBlob, privateBlob)
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)}
+	cmdBytes, err := encodeLoad(tpmutil.Handle(0x80000000), auth, publicBlob, privateBlob)
 	if err != nil {
 		t.Fatalf("encodeLoad failed %s", err)
 	}
@@ -146,7 +147,8 @@ func TestEncodeCreate(t *testing.T) {
 			Modulus:  big.NewInt(0),
 		},
 	}
-	cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, "", defaultPassword, []byte{255} /*sensitiveData*/, params)
+	auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}
+	cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, auth, defaultPassword, []byte{255} /*sensitiveData*/, params)
 	if err != nil {
 		t.Fatal(err)
 	}