Skip to content

Commit

Permalink
policyutil: Reintroduce CpHash and NameHash interfaces and use them i…
Browse files Browse the repository at this point in the history
…n PolicyBuilder
  • Loading branch information
chrisccoulson committed Jan 15, 2025
1 parent d0e8651 commit 31e3df0
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 76 deletions.
12 changes: 6 additions & 6 deletions policyutil/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,20 +447,20 @@ func (b *PolicyBuilderBranch) PolicyCounterTimer(operandB tpm2.Operand, offset u
//
// As this binds the authorization to an object and and a policy has to have the same algorithm as
// this, policies with this assertion can only be computed for a single digest algorithm.
func (b *PolicyBuilderBranch) PolicyCpHash(code tpm2.CommandCode, handles []Named, params ...interface{}) (tpm2.Digest, error) {
func (b *PolicyBuilderBranch) PolicyCpHash(cpHash CpHash) (tpm2.Digest, error) {
if err := b.prepareToModifyBranch(); err != nil {
return nil, b.policy.fail("PolicyCpHash", err)
}

cpHash, err := ComputeCpHash(b.alg(), code, handles, params...)
cpHashA, err := cpHash.Digest(b.alg())
if err != nil {
return nil, b.policy.fail("PolicyCpHash", fmt.Errorf("cannot compute cpHashA: %w", err))
}

element := &policyElement{
Type: tpm2.CommandPolicyCpHash,
Details: &policyElementDetails{
CpHash: &policyCpHashElement{Digest: cpHash}}}
CpHash: &policyCpHashElement{Digest: cpHashA}}}
if err := element.runner().run(&b.runner); err != nil {
return nil, b.policy.fail("PolicyCpHash", fmt.Errorf("internal error: %w", err))
}
Expand All @@ -478,20 +478,20 @@ func (b *PolicyBuilderBranch) PolicyCpHash(code tpm2.CommandCode, handles []Name
//
// As this binds the authorization to an object and and a policy has to have the same algorithm as
// this, policies with this assertion can only be computed for a single digest algorithm.
func (b *PolicyBuilderBranch) PolicyNameHash(handles ...Named) (tpm2.Digest, error) {
func (b *PolicyBuilderBranch) PolicyNameHash(nameHash NameHash) (tpm2.Digest, error) {
if err := b.prepareToModifyBranch(); err != nil {
return nil, b.policy.fail("PolicyNameHash", err)
}

nameHash, err := ComputeNameHash(b.alg(), handles...)
nameHashA, err := nameHash.Digest(b.alg())
if err != nil {
return nil, b.policy.fail("PolicyNameHash", fmt.Errorf("cannot compute nameHash: %w", err))
}

element := &policyElement{
Type: tpm2.CommandPolicyNameHash,
Details: &policyElementDetails{
NameHash: &policyNameHashElement{Digest: nameHash}}}
NameHash: &policyNameHashElement{Digest: nameHashA}}}
if err := element.runner().run(&b.runner); err != nil {
return nil, b.policy.fail("PolicyNameHash", fmt.Errorf("internal error: %w", err))
}
Expand Down
55 changes: 14 additions & 41 deletions policyutil/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,16 +572,14 @@ func (s *builderSuite) TestPolicyCounterTimerDifferentOperation(c *C) {

type testBuildPolicyCpHashData struct {
alg tpm2.HashAlgorithmId
code tpm2.CommandCode
handles []Named
params []interface{}
cpHash CpHash
expectedCpHash tpm2.Digest
expectedDigest tpm2.Digest
}

func (s *builderSuite) testPolicyCpHash(c *C, data *testBuildPolicyCpHashData) {
builder := NewPolicyBuilder(data.alg)
digest, err := builder.RootBranch().PolicyCpHash(data.code, data.handles, data.params...)
digest, err := builder.RootBranch().PolicyCpHash(data.cpHash)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, data.expectedDigest)

Expand All @@ -605,55 +603,30 @@ Policy {
func (s *builderSuite) TestPolicyCpHash(c *C) {
s.testPolicyCpHash(c, &testBuildPolicyCpHashData{
alg: tpm2.HashAlgorithmSHA256,
code: tpm2.CommandLoad,
handles: []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}},
params: []interface{}{tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())},
cpHash: CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())),
expectedCpHash: internal_testutil.DecodeHexString(c, "0d5c70236d9181ea6b26fb203d8a45bbb3d982926d6cf4ba60ce0fe5d5717ac3"),
expectedDigest: internal_testutil.DecodeHexString(c, "79cefecd804486b13ac906b061a6d0faffacb46d7f387d91771b9455242de694")})
}

func (s *builderSuite) TestPolicyCpHashDifferentParams(c *C) {
func (s *builderSuite) TestPolicyCpHashDifferentDigest(c *C) {
s.testPolicyCpHash(c, &testBuildPolicyCpHashData{
alg: tpm2.HashAlgorithmSHA256,
code: tpm2.CommandLoad,
handles: []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}},
params: []interface{}{tpm2.Private{1, 2, 3, 4, 5}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())},
cpHash: CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4, 5}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())),
expectedCpHash: internal_testutil.DecodeHexString(c, "15fc1d7283e0f5f864651602c55f1d1dbebf7e573850bfae5235e94df0ac1fa1"),
expectedDigest: internal_testutil.DecodeHexString(c, "801e24b6989cfea7a0ec1d885d21aa9311331443d7f21e1bbcb51675b0927475")})
}

func (s *builderSuite) TestPolicyCpHashDifferentHandles(c *C) {
s.testPolicyCpHash(c, &testBuildPolicyCpHashData{
alg: tpm2.HashAlgorithmSHA256,
code: tpm2.CommandLoad,
handles: []Named{tpm2.Name{0x40, 0x00, 0x00, 0x0b}},
params: []interface{}{tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())},
expectedCpHash: internal_testutil.DecodeHexString(c, "4facb677c43722471af5c535353911e4882d26aa58f4859562b6861476f4aca3"),
expectedDigest: internal_testutil.DecodeHexString(c, "62d74f265639e887956694eb36a4106228a08879ce1ade983cf0b28c2415acbb")})
}

func (s *builderSuite) TestPolicyCpHashDifferentCommand(c *C) {
s.testPolicyCpHash(c, &testBuildPolicyCpHashData{
alg: tpm2.HashAlgorithmSHA256,
code: tpm2.CommandLoadExternal,
params: []interface{}{mu.Sized((*tpm2.Sensitive)(nil)), mu.Sized(objectutil.NewRSAStorageKeyTemplate()), tpm2.HandleOwner},
expectedCpHash: internal_testutil.DecodeHexString(c, "bcbfc6e1846a7f58ed0c05ddf8a0ce7e2b3a50ba3f04e3ac87ee8c940a360f46"),
expectedDigest: internal_testutil.DecodeHexString(c, "f3d3c11955dd8dc8b45c6b66961cd929bc62a0fd263f5d7336139f30a166f011")})
}

func (s *builderSuite) TestPolicyCpHashSHA1(c *C) {
s.testPolicyCpHash(c, &testBuildPolicyCpHashData{
alg: tpm2.HashAlgorithmSHA1,
code: tpm2.CommandLoad,
handles: []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}},
params: []interface{}{tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())},
cpHash: CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())),
expectedCpHash: internal_testutil.DecodeHexString(c, "d98ba8350f71c34132f62f50a6b9f21c4fa54f75"),
expectedDigest: internal_testutil.DecodeHexString(c, "a59f3e6a358dee7edfd733373d7c8a9851296d26")})
}

func (s *builderSuite) TestPolicyCpHashInvalidName(c *C) {
builder := NewPolicyBuilder(tpm2.HashAlgorithmSHA256)
_, err := builder.RootBranch().PolicyCpHash(tpm2.CommandLoad, []Named{tpm2.Name{0, 0}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
_, err := builder.RootBranch().PolicyCpHash(CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0, 0}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate())))
c.Check(err, ErrorMatches, `cannot compute cpHashA: invalid name for handle 0`)
_, _, err = builder.Policy()
c.Check(err, ErrorMatches,
Expand All @@ -662,14 +635,14 @@ func (s *builderSuite) TestPolicyCpHashInvalidName(c *C) {

type testBuildPolicyNameHashData struct {
alg tpm2.HashAlgorithmId
handles []Named
nameHash NameHash
expectedNameHash tpm2.Digest
expectedDigest tpm2.Digest
}

func (s *builderSuite) testPolicyNameHash(c *C, data *testBuildPolicyNameHashData) {
builder := NewPolicyBuilder(data.alg)
digest, err := builder.RootBranch().PolicyNameHash(data.handles...)
digest, err := builder.RootBranch().PolicyNameHash(data.nameHash)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, data.expectedDigest)

Expand All @@ -693,30 +666,30 @@ Policy {
func (s *builderSuite) TestPolicyNameHash(c *C) {
s.testPolicyNameHash(c, &testBuildPolicyNameHashData{
alg: tpm2.HashAlgorithmSHA256,
handles: []Named{tpm2.MakeHandleName(tpm2.HandleOwner)},
nameHash: CommandHandles(tpm2.MakeHandleName(tpm2.HandleOwner)),
expectedNameHash: internal_testutil.DecodeHexString(c, "16a3d3b482bb480394dfac704038a3708db2a77ccaa80ca419e91122406599ec"),
expectedDigest: internal_testutil.DecodeHexString(c, "f46ca197c159be2500db41866e2713bd5e25cda9bbd46e2a398550010d7e5e5b")})
}

func (s *builderSuite) TestPolicyNameHashDifferentHandles(c *C) {
s.testPolicyNameHash(c, &testBuildPolicyNameHashData{
alg: tpm2.HashAlgorithmSHA256,
handles: []Named{tpm2.MakeHandleName(tpm2.HandleEndorsement)},
nameHash: CommandHandles(tpm2.MakeHandleName(tpm2.HandleEndorsement)),
expectedNameHash: internal_testutil.DecodeHexString(c, "c791c5d6c902890a3b91af630b09bc5b04cbe7cc6385708771f25aa6cb334ae3"),
expectedDigest: internal_testutil.DecodeHexString(c, "3e3fbf3b3c59ba10ae0f02c691ceb60ba87fd7463c4100c1bb85c143e24e6eab")})
}

func (s *builderSuite) TestPolicyNameHashSHA1(c *C) {
s.testPolicyNameHash(c, &testBuildPolicyNameHashData{
alg: tpm2.HashAlgorithmSHA1,
handles: []Named{tpm2.MakeHandleName(tpm2.HandleOwner)},
nameHash: CommandHandles(tpm2.MakeHandleName(tpm2.HandleOwner)),
expectedNameHash: internal_testutil.DecodeHexString(c, "97d538cbfae3f530b934596ea99c19a9b5c06d03"),
expectedDigest: internal_testutil.DecodeHexString(c, "022794dd35419f458603c2c11808dced821078d2")})
}

func (s *builderSuite) TestPolicyNameHashInvalidName(c *C) {
builder := NewPolicyBuilder(tpm2.HashAlgorithmSHA256)
_, err := builder.RootBranch().PolicyNameHash(tpm2.Name{0, 0})
_, err := builder.RootBranch().PolicyNameHash(CommandHandles(tpm2.Name{0, 0}))
c.Check(err, ErrorMatches, `cannot compute nameHash: invalid name for handle 0`)
_, _, err = builder.Policy()
c.Check(err, ErrorMatches,
Expand Down Expand Up @@ -1289,7 +1262,7 @@ func (s *builderSuite) TestPolicyORSHA1(c *C) {
func (s *builderSuite) TestModifyFailedBranch(c *C) {
// XXX: Note that this only tests one method - this should be expanded to test all
builder := NewPolicyBuilder(tpm2.HashAlgorithmSHA256)
_, err := builder.RootBranch().PolicyNameHash(tpm2.Name{0, 0})
_, err := builder.RootBranch().PolicyNameHash(CommandHandles(tpm2.Name{0, 0}))
c.Check(err, ErrorMatches, `cannot compute nameHash: invalid name for handle 0`)
_, err = builder.RootBranch().PolicyAuthValue()
c.Check(err, ErrorMatches, `encountered an error when calling PolicyNameHash: cannot compute nameHash: invalid name for handle 0`)
Expand Down
68 changes: 55 additions & 13 deletions policyutil/cphash.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import (
"github.com/canonical/go-tpm2/mu"
)

// CpHash provides a way to obtain a command parameter digest.
type CpHash interface {
// Digest returns the command parameter digest for the specified algorithm.
Digest(alg tpm2.HashAlgorithmId) (tpm2.Digest, error)
}

func computeCpHash(alg tpm2.HashAlgorithmId, command tpm2.CommandCode, handles []tpm2.Name, cpBytes []byte) (tpm2.Digest, error) {
if !alg.Available() {
return nil, errors.New("algorithm is not available")
Expand All @@ -29,6 +35,53 @@ func computeCpHash(alg tpm2.HashAlgorithmId, command tpm2.CommandCode, handles [
return h.Sum(nil), nil
}

type commandParams struct {
command tpm2.CommandCode
handles []Named
params []interface{}
}

func (c *commandParams) Digest(alg tpm2.HashAlgorithmId) (tpm2.Digest, error) {
cpBytes, err := mu.MarshalToBytes(c.params...)
if err != nil {
return nil, err
}
var handles []tpm2.Name
for i, handle := range c.handles {
name := handle.Name()
if !name.IsValid() {
return nil, fmt.Errorf("invalid name for handle %d", i)
}
handles = append(handles, name)
}
return computeCpHash(alg, c.command, handles, cpBytes)
}

// CommandParameters returns a CpHash implementation for the specified command code, handles and
// parameters. The required parameters are defined in part 3 of the TPM 2.0 Library Specification
// for the specific command.
func CommandParameters(command tpm2.CommandCode, handles []Named, params ...interface{}) CpHash {
return &commandParams{
command: command,
handles: handles,
params: params}
}

type cpDigest tpm2.TaggedHash

func (d *cpDigest) Digest(alg tpm2.HashAlgorithmId) (tpm2.Digest, error) {
if alg != d.HashAlg {
return nil, errors.New("no digest for algorithm")
}
return tpm2.Digest((*tpm2.TaggedHash)(d).Digest()), nil
}

// CommandParameterDigest returns a CpHash implementation for the specified algorithm and digest.
func CommandParameterDigest(alg tpm2.HashAlgorithmId, digest tpm2.Digest) CpHash {
d := tpm2.MakeTaggedHash(alg, digest)
return (*cpDigest)(&d)
}

// ComputeCpHash computes a command parameter digest from the specified command code, the supplied
// handles, and parameters using the specified digest algorithm.
//
Expand All @@ -40,17 +93,6 @@ func computeCpHash(alg tpm2.HashAlgorithmId, command tpm2.CommandCode, handles [
// [tpm2.TPMContext.PolicySecret], [tpm2.TPMContext.PolicyTicket] and
// [tpm2.TPMContext.PolicyCpHash].
func ComputeCpHash(alg tpm2.HashAlgorithmId, command tpm2.CommandCode, handles []Named, params ...interface{}) (tpm2.Digest, error) {
cpBytes, err := mu.MarshalToBytes(params...)
if err != nil {
return nil, err
}
var handleNames []tpm2.Name
for i, handle := range handles {
name := handle.Name()
if !name.IsValid() {
return nil, fmt.Errorf("invalid name for handle %d", i)
}
handleNames = append(handleNames, name)
}
return computeCpHash(alg, command, handleNames, cpBytes)
d := CommandParameters(command, handles, params...)
return d.Digest(alg)
}
59 changes: 59 additions & 0 deletions policyutil/cphash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package policyutil_test

import (
"crypto"
_ "crypto/sha1"
_ "crypto/sha256"
"io"

. "gopkg.in/check.v1"

Expand All @@ -21,6 +23,63 @@ type cpHashSuite struct{}

var _ = Suite(&cpHashSuite{})

func (s *cpHashSuite) TestCommandParameters(c *C) {
cpHashA := CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA256)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(internal_testutil.DecodeHexString(c, "0d5c70236d9181ea6b26fb203d8a45bbb3d982926d6cf4ba60ce0fe5d5717ac3")))
}

func (s *cpHashSuite) TestCommandParametersDifferentParams(c *C) {
cpHashA := CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4, 5}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA256)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(internal_testutil.DecodeHexString(c, "15fc1d7283e0f5f864651602c55f1d1dbebf7e573850bfae5235e94df0ac1fa1")))
}

func (s *cpHashSuite) TestCommandParametersDifferentHandles(c *C) {
cpHashA := CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x0b}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA256)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(internal_testutil.DecodeHexString(c, "4facb677c43722471af5c535353911e4882d26aa58f4859562b6861476f4aca3")))
}

func (s *cpHashSuite) TestCommandParametersSHA1(c *C) {
cpHashA := CommandParameters(tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA1)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(internal_testutil.DecodeHexString(c, "d98ba8350f71c34132f62f50a6b9f21c4fa54f75")))
}

func (s *cpHashSuite) TestCommandParameterDigestSHA256(c *C) {
h := crypto.SHA256.New()
io.WriteString(h, "params")

cpHashA := CommandParameterDigest(tpm2.HashAlgorithmSHA256, h.Sum(nil))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA256)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(h.Sum(nil)))
}

func (s *cpHashSuite) TestCommandParameterDigestSHA1(c *C) {
h := crypto.SHA1.New()
io.WriteString(h, "params")

cpHashA := CommandParameterDigest(tpm2.HashAlgorithmSHA1, h.Sum(nil))
digest, err := cpHashA.Digest(tpm2.HashAlgorithmSHA1)
c.Check(err, IsNil)
c.Check(digest, DeepEquals, tpm2.Digest(h.Sum(nil)))
}

func (s *cpHashSuite) TestCommandParameterDigestError(c *C) {
h := crypto.SHA256.New()
io.WriteString(h, "params")

cpHashA := CommandParameterDigest(tpm2.HashAlgorithmSHA256, h.Sum(nil))
_, err := cpHashA.Digest(tpm2.HashAlgorithmSHA1)
c.Check(err, ErrorMatches, "no digest for algorithm")
}

func (s *cpHashSuite) TestComputeCpHash(c *C) {
cpHashA, err := ComputeCpHash(tpm2.HashAlgorithmSHA256, tpm2.CommandLoad, []Named{tpm2.Name{0x40, 0x00, 0x00, 0x01}}, tpm2.Private{1, 2, 3, 4}, mu.Sized(objectutil.NewRSAStorageKeyTemplate()))
c.Check(err, IsNil)
Expand Down
Loading

0 comments on commit 31e3df0

Please sign in to comment.