Repository: google/go-tpm
Pull request #394 (2025-04-17 06:08:43)

TPM2_PCR_Allocate support
Fix for #393 

chrisfenner: Thanks for the implementation!

Patch:
diff --git a/legacy/tpm2/constants.go b/legacy/tpm2/constants.go
index 2b0de54..1357370 100644
--- a/legacy/tpm2/constants.go
+++ b/legacy/tpm2/constants.go
@@ -447,6 +447,7 @@ const (
 	CmdClear                      tpmutil.Command = 0x00000126
 	CmdHierarchyChangeAuth        tpmutil.Command = 0x00000129
 	CmdDefineSpace                tpmutil.Command = 0x0000012A
+	CmdPCRAllocate                tpmutil.Command = 0x0000012B
 	CmdCreatePrimary              tpmutil.Command = 0x00000131
 	CmdIncrementNVCounter         tpmutil.Command = 0x00000134
 	CmdWriteNV                    tpmutil.Command = 0x00000137
diff --git a/legacy/tpm2/tpm2.go b/legacy/tpm2/tpm2.go
index 18d5a96..0105c37 100644
--- a/legacy/tpm2/tpm2.go
+++ b/legacy/tpm2/tpm2.go
@@ -51,6 +51,10 @@ func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) {
 		return tpmutil.Pack(uint32(0))
 	}
 
+	if len(sel) == 1 && len(sel[0].PCRs) == 0 && sel[0].Hash == 0 {
+		return tpmutil.Pack(uint32(0))
+	}
+
 	// PCR selection is a variable-size bitmask, where position of a set bit is
 	// the selected PCR index.
 	// Size of the bitmask in bytes is pre-pended. It should be at least
@@ -61,10 +65,6 @@ func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) {
 	// 00000011 00000000 00000001 00000100
 	var retBytes []byte
 	for _, s := range sel {
-		if len(s.PCRs) == 0 {
-			return tpmutil.Pack(uint32(0))
-		}
-
 		ts := tpmsPCRSelection{
 			Hash: s.Hash,
 			Size: sizeOfPCRSelect,
@@ -1153,6 +1153,34 @@ func Clear(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand) error {
 	return err
 }
 
+func encodePCRAllocate(handle tpmutil.Handle, auth AuthCommand, pcrSelection []PCRSelection) ([]byte, error) {
+	ah, err := tpmutil.Pack(handle)
+	if err != nil {
+		return nil, err
+	}
+	authEncoded, err := encodeAuthArea(auth)
+	if err != nil {
+		return nil, err
+	}
+
+	pcrEncoded, err := encodeTPMLPCRSelection(pcrSelection...)
+	if err != nil {
+		return nil, err
+	}
+	return concat(ah, authEncoded, pcrEncoded)
+}
+
+// PCRAllocate sets the desired PCR allocation of PCR and algorithms.
+// The changes take effect once the TPM is restarted.
+func PCRAllocate(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand, pcrSelection []PCRSelection) error {
+	Cmd, err := encodePCRAllocate(handle, auth, pcrSelection)
+	if err != nil {
+		return err
+	}
+	_, err = runCommand(rw, TagSessions, CmdPCRAllocate, tpmutil.RawBytes(Cmd))
+	return err
+}
+
 func encodeHierarchyChangeAuth(handle tpmutil.Handle, auth AuthCommand, newAuth string) ([]byte, error) {
 	ah, err := tpmutil.Pack(handle)
 	if err != nil {


Test patch:
diff --git a/legacy/tpm2/test/tpm2_test.go b/legacy/tpm2/test/tpm2_test.go
index abbb4b7..a5dd3b2 100644
--- a/legacy/tpm2/test/tpm2_test.go
+++ b/legacy/tpm2/test/tpm2_test.go
@@ -1135,6 +1135,21 @@ func TestPCRReset(t *testing.T) {
 	}
 }
 
+func TestPCRAllocate(t *testing.T) {
+	rw := openTPM(t)
+	defer rw.Close()
+
+	pcrSelectionRemoveSHA1 := []PCRSelection{
+		{Hash: AlgSHA1, PCRs: []int{}},
+		{Hash: AlgSHA256, PCRs: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}},
+	}
+
+	err := PCRAllocate(rw, HandlePlatform, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, pcrSelectionRemoveSHA1)
+	if err != nil {
+		t.Fatalf("PCRAllocate failed: %v", err)
+	}
+}
+
 func makeAttestationData() AttestationData {
 	signer := tpmutil.Handle(100)
 	return AttestationData{