Repository: google/go-tpm
Pull request #359 (2024-06-12 20:01:29)

add PolicyAuthValue
Adds support for `PolicyAuthVaue`

salrashid123: @chrisfenner for review per discussion

note the test cases are end-to-end vs comparing just the policy values

Patch:
diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go
index 23157ff..1d1799c 100644
--- a/tpm2/tpm2.go
+++ b/tpm2/tpm2.go
@@ -1216,6 +1216,34 @@ func (cmd PolicyPCR) Update(policy *PolicyCalculator) error {
 // PolicyPCRResponse is the response from TPM2_PolicyPCR.
 type PolicyPCRResponse struct{}
 
+// PolicyAuthValue is the input to TPM2_PolicyAuthValue.
+// See definition in Part 3, Commands, section 23.17.
+type PolicyAuthValue struct {
+	// handle for the policy session being extended
+	PolicySession handle `gotpm:"handle"`
+}
+
+// Command implements the Command interface.
+func (PolicyAuthValue) Command() TPMCC { return TPMCCPolicyAuthValue }
+
+// Execute executes the command and returns the response.
+func (cmd PolicyAuthValue) Execute(t transport.TPM, s ...Session) (*PolicyAuthValueResponse, error) {
+	var rsp PolicyAuthValueResponse
+	err := execute[PolicyAuthValueResponse](t, cmd, &rsp, s...)
+	if err != nil {
+		return nil, err
+	}
+	return &rsp, nil
+}
+
+// Update implements the PolicyAuthValue interface.
+func (cmd PolicyAuthValue) Update(policy *PolicyCalculator) error {
+	return policy.Update(TPMCCPolicyAuthValue)
+}
+
+// PolicyAuthValueResponse is the response from TPM2_PolicyAuthValue.
+type PolicyAuthValueResponse struct{}
+
 // PolicyNV is the input to TPM2_PolicyNV.
 // See definition in Part 3, Commands, section 23.9.
 type PolicyNV struct {


Test patch:
diff --git a/tpm2/test/policy_test.go b/tpm2/test/policy_test.go
index 9720f45..2a0d11a 100644
--- a/tpm2/test/policy_test.go
+++ b/tpm2/test/policy_test.go
@@ -3,6 +3,7 @@ package tpm2test
 import (
 	"bytes"
 	"crypto/sha1"
+	"crypto/sha256"
 	"testing"
 
 	. "github.com/google/go-tpm/tpm2"
@@ -161,6 +162,31 @@ func nvIndex(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) {
 	}, cleanup
 }
 
+func primaryRSASRK(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) {
+	t.Helper()
+	createPrimary := CreatePrimary{
+		PrimaryHandle: TPMRHOwner,
+		InPublic:      New2B(RSASRKTemplate),
+	}
+	rsp, err := createPrimary.Execute(thetpm)
+	if err != nil {
+		t.Fatalf("could not create primary key: %v", err)
+	}
+	cleanup := func() {
+		t.Helper()
+		flush := FlushContext{
+			FlushHandle: rsp.ObjectHandle,
+		}
+		if _, err := flush.Execute(thetpm); err != nil {
+			t.Errorf("could not flush primary key: %v", err)
+		}
+	}
+	return NamedHandle{
+		Handle: rsp.ObjectHandle,
+		Name:   rsp.Name,
+	}, cleanup
+}
+
 func TestPolicySignedUpdate(t *testing.T) {
 	thetpm, err := simulator.OpenSimulator()
 	if err != nil {
@@ -771,3 +797,187 @@ func TestPolicyCommandCodeUpdate(t *testing.T) {
 		t.Errorf("PolicyCommandCode.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer)
 	}
 }
+
+func TestPolicyAuthValue(t *testing.T) {
+	thetpm, err := simulator.OpenSimulator()
+	if err != nil {
+		t.Fatalf("could not connect to TPM simulator: %v", err)
+	}
+	defer thetpm.Close()
+
+	password := []byte("foo")
+	wrongPassword := []byte("bar")
+
+	tests := []struct {
+		name              string
+		password          []byte
+		authOption        []AuthOption
+		callShouldSucceed bool
+	}{
+		{"PasswordCorrect", password, []AuthOption{Auth(password)}, true},
+		{"PasswordIncorrect", wrongPassword, []AuthOption{Auth(password)}, false},
+		{"PasswordEmpty", nil, []AuthOption{Auth(password)}, false},
+		{"AuthOptionEmpty", password, []AuthOption{}, false},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+
+			pk, pkcleanup := primaryRSASRK(t, thetpm)
+			defer pkcleanup()
+
+			// create a trial policy with PolicyAuthValue
+			sess, cleanup1, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial())
+			if err != nil {
+				t.Fatalf("setting up trial session: %v", err)
+			}
+			defer func() {
+				t.Helper()
+				if err := cleanup1(); err != nil {
+					t.Errorf("cleaning up trial session: %v", err)
+				}
+			}()
+
+			pav := PolicyAuthValue{
+				PolicySession: sess.Handle(),
+			}
+			_, err = pav.Execute(thetpm)
+			if err != nil {
+				t.Fatalf("error executing policyAuthValue: %v", err)
+			}
+
+			// verify the digest
+			pgd, err := PolicyGetDigest{
+				PolicySession: sess.Handle(),
+			}.Execute(thetpm)
+			if err != nil {
+				t.Fatalf("error executing PolicyGetDigest: %v", pgd)
+			}
+
+			// Use the policy helper to calculate the same policy
+			pol, err := NewPolicyCalculator(TPMAlgSHA256)
+			if err != nil {
+				t.Fatalf("creating policy calculator: %v", err)
+			}
+			pav.Update(pol)
+			got := pol.Hash()
+
+			if !bytes.Equal(got.Digest, pgd.PolicyDigest.Buffer) {
+				t.Errorf("PolicyAuthValue.Hash() = %x,\nwant %x", got.Digest, pgd.PolicyDigest.Buffer)
+			}
+
+			// now apply the policy to a new key
+			rsaTemplate := TPMTPublic{
+				Type:    TPMAlgRSA,
+				NameAlg: TPMAlgSHA256,
+				ObjectAttributes: TPMAObject{
+					SignEncrypt:         true,
+					FixedTPM:            true,
+					FixedParent:         true,
+					SensitiveDataOrigin: true,
+					UserWithAuth:        true,
+				},
+				AuthPolicy: pgd.PolicyDigest,
+				Parameters: NewTPMUPublicParms(
+					TPMAlgRSA,
+					&TPMSRSAParms{
+						Scheme: TPMTRSAScheme{
+							Scheme: TPMAlgRSASSA,
+							Details: NewTPMUAsymScheme(
+								TPMAlgRSASSA,
+								&TPMSSigSchemeRSASSA{
+									HashAlg: TPMAlgSHA256,
+								},
+							),
+						},
+						KeyBits: 2048,
+					},
+				),
+			}
+
+			k, err := CreateLoaded{
+				ParentHandle: AuthHandle{
+					Handle: pk.Handle,
+					Name:   pk.Name,
+					Auth:   PasswordAuth(nil),
+				},
+				InPublic: New2BTemplate(&rsaTemplate),
+				InSensitive: TPM2BSensitiveCreate{
+					Sensitive: &TPMSSensitiveCreate{
+						UserAuth: TPM2BAuth{
+							Buffer: tt.password,
+						},
+					},
+				},
+			}.Execute(thetpm)
+			if err != nil {
+				t.Fatalf("error creating key %v", err)
+			}
+			defer func() {
+				t.Helper()
+				_, err := FlushContext{
+					FlushHandle: k.ObjectHandle,
+				}.Execute(thetpm)
+				if err != nil {
+					t.Errorf("error cleaning up key: %v", err)
+				}
+			}()
+
+			// create a real policy session and use the password through the authOption
+			sess2, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, tt.authOption...)
+			if err != nil {
+				t.Fatalf("setting up policy session: %v", err)
+			}
+			defer cleanup2()
+
+			policyAuthValue2 := PolicyAuthValue{
+				PolicySession: sess2.Handle(),
+			}
+
+			_, err = policyAuthValue2.Execute(thetpm)
+			if err != nil {
+				t.Fatalf("executing policyAuthValue: %v", err)
+			}
+
+			// sign some data with the key using the session
+			data := []byte("somedata")
+			digest := sha256.Sum256(data)
+
+			sign := Sign{
+				KeyHandle: AuthHandle{
+					Handle: k.ObjectHandle,
+					Name:   k.Name,
+					Auth:   sess2,
+				},
+				Digest: TPM2BDigest{
+					Buffer: digest[:],
+				},
+				InScheme: TPMTSigScheme{
+					Scheme: TPMAlgRSASSA,
+					Details: NewTPMUSigScheme(
+						TPMAlgRSASSA,
+						&TPMSSchemeHash{
+							HashAlg: TPMAlgSHA256,
+						},
+					),
+				},
+				Validation: TPMTTKHashCheck{
+					Tag: TPMSTHashCheck,
+				},
+			}
+
+			_, err = sign.Execute(thetpm)
+			if tt.callShouldSucceed {
+				if err != nil {
+					t.Fatalf("expected no error for PolicyAuthValue but got: %v", err)
+				}
+			} else {
+				if err == nil {
+					t.Fatalf("expected error for PolicyAuthValue, got nil")
+				}
+				return
+			}
+		})
+	}
+
+}