Skip to content

Commit

Permalink
PolicyPCR (#289)
Browse files Browse the repository at this point in the history
* Implemented PolicyPCR and basic related test.

* Added table tests for real and trial cases.

* Fixed all outstanding nits.

Co-authored-by: Matthew Tsai <matthewtsai@google.com>
  • Loading branch information
matt-tsai and Matthew Tsai authored Jul 12, 2022
1 parent 8bd9361 commit d20ef6e
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 1 deletion.
131 changes: 131 additions & 0 deletions direct/tpm2/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tpm2

import (
"bytes"
"crypto/sha1"
"testing"

"github.com/google/go-tpm/direct/structures/tpm"
Expand Down Expand Up @@ -281,6 +282,136 @@ func TestPolicyOrUpdate(t *testing.T) {
}
}

func getExpectedPCRDigest(t *testing.T, thetpm transport.TPM, selection tpml.PCRSelection, hashAlg tpm.AlgID) []byte {
t.Helper()
pcrRead := PCRRead{
PCRSelectionIn: selection,
}

pcrReadRsp, err := pcrRead.Execute(thetpm)
if err != nil {
t.Fatalf("failed to read PCRs")
}

var expectedVal []byte
for _, digest := range pcrReadRsp.PCRValues.Digests {
expectedVal = append(expectedVal, digest.Buffer...)
}

cryptoHashAlg, err := hashAlg.Hash()
if err != nil {
t.Fatalf("failed to get crypto hash")
}

hash := cryptoHashAlg.New()
hash.Write(expectedVal)
return hash.Sum(nil)
}

func TestPolicyPCR(t *testing.T) {
thetpm, err := simulator.OpenSimulator()
if err != nil {
t.Fatalf("could not connect to TPM simulator: %v", err)
}
defer thetpm.Close()

PCRs, err := CreatePCRSelection([]int{0, 1, 2, 3, 7})
if err != nil {
t.Fatalf("Failed to create PCRSelection")
}

selection := tpml.PCRSelection{
PCRSelections: []tpms.PCRSelection{
{
Hash: tpm.AlgSHA1,
PCRSelect: PCRs,
},
},
}

expectedDigest := getExpectedPCRDigest(t, thetpm, selection, tpm.AlgSHA1)

wrongDigest := sha1.Sum(expectedDigest[:])

tests := []struct {
name string
authOption []AuthOption
pcrDigest []byte
callShouldSucceed bool
}{
{"TrialCorrect", []AuthOption{Trial()}, expectedDigest, true},
{"TrialIncorrect", []AuthOption{Trial()}, wrongDigest[:], true},
{"TrialEmpty", []AuthOption{Trial()}, nil, true},
{"RealCorrect", nil, expectedDigest, true},
{"RealIncorrect", nil, wrongDigest[:], false},
{"RealEmpty", nil, nil, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sess, cleanup2, err := PolicySession(thetpm, tpm.AlgSHA1, 16, tt.authOption...)

if err != nil {
t.Fatalf("setting up policy session: %v", err)
}

policyPCR := PolicyPCR{
PolicySession: sess.Handle(),
PcrDigest: tpm2b.Digest{
Buffer: tt.pcrDigest,
},
Pcrs: selection,
}

err = policyPCR.Execute(thetpm)
if tt.callShouldSucceed {
if err != nil {
t.Fatalf("executing PolicyPCR: %v", err)
}
} else {
if err == nil {
t.Fatalf("expected PolicyPCR to return error, got nil")
}
return
}

pgd := PolicyGetDigest{
PolicySession: sess.Handle(),
}
want, err := pgd.Execute(thetpm)
if err != nil {
t.Fatalf("executing PolicyGetDigest: %v", err)
}

// If the pcrDigest is empty: see TPM 2.0 Part 3, 23.7.
if tt.pcrDigest == nil {
expectedDigest := getExpectedPCRDigest(t, thetpm, selection, tpm.AlgSHA1)
t.Logf("expectedDigest=%x", expectedDigest)

// Create a populated policyPCR for the PolicyCalculator
policyPCR.PcrDigest.Buffer = expectedDigest[:]
}

// Use the policy helper to calculate the same policy
pol, err := NewPolicyCalculator(tpm.AlgSHA1)
if err != nil {
t.Fatalf("creating policy calculator: %v", err)
}
policyPCR.Update(pol)
got := pol.Hash()

if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) {
t.Errorf("policyPCR.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer)
}

if err := cleanup2(); err != nil {
t.Errorf("cleaning up policy session: %v", err)
}
})
}

}

func TestPolicyCpHashUpdate(t *testing.T) {
thetpm, err := simulator.OpenSimulator()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion direct/tpm2/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ func AuditExclusive() AuthOption {
}
}

// Trial indicates that the policy session should be in tral-mode.
// Trial indicates that the policy session should be in trial-mode.
// This allows using the TPM to calculate policy hashes.
// This option has no effect on non-Policy sessions.
func Trial() AuthOption {
Expand Down
32 changes: 32 additions & 0 deletions direct/tpm2/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,38 @@ type PolicyOrResponse struct{}
// Response implements the Response interface.
func (*PolicyOrResponse) Response() tpm.CC { return tpm.CCPolicyOR }

// PolicyPCR is the input to TPM2_PolicyPCR.
// See definition in Part 3, Commands, section 23.7.
type PolicyPCR struct {
// handle for the policy session being extended
PolicySession handle `gotpm:"handle"`
// expected digest value of the selected PCR using the
// hash algorithm of the session; may be zero length
PcrDigest tpm2b.Digest
// the PCR to include in the check digest
Pcrs tpml.PCRSelection
}

// Command implements the Command interface.
func (*PolicyPCR) Command() tpm.CC { return tpm.CCPolicyPCR }

// Execute executes the command and returns the response.
func (cmd *PolicyPCR) Execute(t transport.TPM, s ...Session) error {
var rsp PolicyPCRResponse
return execute(t, cmd, &rsp, s...)
}

// Update implements the PolicyCommand interface.
func (p *PolicyPCR) Update(policy *PolicyCalculator) error {
return policy.Update(tpm.CCPolicyPCR, p.Pcrs, p.PcrDigest.Buffer)
}

// PolicyPCRResponse is the response from TPM2_PolicyPCR.
type PolicyPCRResponse struct{}

// Response implements the Response interface.
func (*PolicyPCRResponse) Response() tpm.CC { return tpm.CCPolicyPCR }

// PolicyCommandCode is the input to TPM2_PolicyCommandCode.
// See definition in Part 3, Commands, section 23.11.
type PolicyCommandCode struct {
Expand Down

0 comments on commit d20ef6e

Please sign in to comment.