Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tpm2: Update the policy for the PCR policy counter index #370

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion internal/tpm2test/suites.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,23 @@ func (m *tpmTestMixin) setUpTest(c *C, suite *tpm2_testutil.TPMTest, open func()
// an open connection, and open their own connection instead, the test should call this
// API to temporarily close the internal mock connection.
//
// It returns a callback to re-open the mock connection again.
// This also context saves the HMAC session associated with the connection accessible
// via the TPM() accessor.
//
// It returns a callback to re-open the mock connection again, and restore the HMAC
// session that was context saved.
func (m *tpmTestMixin) CloseMockConnection(c *C) (restore func()) {
c.Assert(m.mockConnection, NotNil)
c.Check(m.mockConnection.Close(), IsNil)
m.mockConnection = nil

hmacSessionContext, err := m.TPM.ContextSave(m.TPM.HmacSession())
c.Assert(err, IsNil)

return func() {
_, err := m.TPM.ContextLoad(hmacSessionContext)
c.Assert(err, IsNil)

mockConn, err := secboot_tpm2.ConnectToDefaultTPM()
c.Assert(err, IsNil)
m.mockConnection = mockConn
Expand Down
4 changes: 4 additions & 0 deletions tpm2/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ func NewPcrPolicyParams(key secboot.PrimaryKey, pcrs tpm2.PCRSelectionList, pcrD
}
}

func NewPcrPolicyData_v3(v2 *PcrPolicyData_v2) *PcrPolicyData_v3 {
return &PcrPolicyData_v3{pcrPolicyData_v2: *v2}
}

type PlatformKeyDataHandler = platformKeyDataHandler
type SealedKeyDataBase = sealedKeyDataBase
type SnapModelHasher = snapModelHasher
Expand Down
46 changes: 24 additions & 22 deletions tpm2/keydata_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,17 @@ func (s *keyDataV3Suite) newMockKeyData(c *C, pcrPolicyCounterHandle tpm2.Handle

template.AuthPolicy = policyDigest

policyData.(*KeyDataPolicy_v3).PCRData = &PcrPolicyData_v3{
PolicySequence: policyCount,
AuthorizedPolicy: make(tpm2.Digest, 32),
AuthorizedPolicySignature: &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgECDSA,
Signature: &tpm2.SignatureU{
ECDSA: &tpm2.SignatureECDSA{
Hash: tpm2.HashAlgorithmSHA256,
SignatureR: make(tpm2.ECCParameter, 32),
SignatureS: make(tpm2.ECCParameter, 32)}}}}
policyData.(*KeyDataPolicy_v3).PCRData = NewPcrPolicyData_v3(
&PcrPolicyData_v2{
PolicySequence: policyCount,
AuthorizedPolicy: make(tpm2.Digest, 32),
AuthorizedPolicySignature: &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgECDSA,
Signature: &tpm2.SignatureU{
ECDSA: &tpm2.SignatureECDSA{
Hash: tpm2.HashAlgorithmSHA256,
SignatureR: make(tpm2.ECCParameter, 32),
SignatureS: make(tpm2.ECCParameter, 32)}}}})

sensitive := tpm2.SensitiveCreate{Data: secret}

Expand Down Expand Up @@ -130,18 +131,19 @@ func (s *keyDataV3Suite) newMockImportableKeyData(c *C, role string, requireAuth

pub.AuthPolicy = policy

policyData.(*KeyDataPolicy_v3).PCRData = &PcrPolicyData_v3{
Selection: tpm2.PCRSelectionList{},
OrData: PolicyOrData_v0{},
PolicySequence: 0,
AuthorizedPolicy: make(tpm2.Digest, 32),
AuthorizedPolicySignature: &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgECDSA,
Signature: &tpm2.SignatureU{
ECDSA: &tpm2.SignatureECDSA{
Hash: tpm2.HashAlgorithmSHA256,
SignatureR: make(tpm2.ECCParameter, 32),
SignatureS: make(tpm2.ECCParameter, 32)}}}}
policyData.(*KeyDataPolicy_v3).PCRData = NewPcrPolicyData_v3(
&PcrPolicyData_v2{
Selection: tpm2.PCRSelectionList{},
OrData: PolicyOrData_v0{},
PolicySequence: 0,
AuthorizedPolicy: make(tpm2.Digest, 32),
AuthorizedPolicySignature: &tpm2.Signature{
SigAlg: tpm2.SigSchemeAlgECDSA,
Signature: &tpm2.SignatureU{
ECDSA: &tpm2.SignatureECDSA{
Hash: tpm2.HashAlgorithmSHA256,
SignatureR: make(tpm2.ECCParameter, 32),
SignatureS: make(tpm2.ECCParameter, 32)}}}})

srkPub, _, _, err := s.TPM().ReadPublic(s.primary)
c.Assert(err, IsNil)
Expand Down
32 changes: 20 additions & 12 deletions tpm2/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,9 +564,11 @@ func (s *platformSuite) TestRecoverKeysWithAuthKey(c *C) {
PCRPolicyRef: pcrPolicyRef,
PCRPolicyCounterHandle: index,
RequireAuthValue: true},
PCRData: &PcrPolicyData_v3{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}),
}

mockPolicyDigest, err := builder.Digest()
c.Assert(err, IsNil)
Expand Down Expand Up @@ -652,9 +654,11 @@ func (s *platformSuite) TestRecoverKeysWithIncorrectAuthKey(c *C) {
PCRPolicyRef: pcrPolicyRef,
PCRPolicyCounterHandle: index,
RequireAuthValue: true},
PCRData: &PcrPolicyData_v3{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}),
}

mockPolicyDigest, err := builder.Digest()
c.Assert(err, IsNil)
Expand Down Expand Up @@ -733,9 +737,11 @@ func (s *platformSuite) TestChangeAuthKeyWithIncorrectAuthKey(c *C) {
PCRPolicyRef: pcrPolicyRef,
PCRPolicyCounterHandle: index,
RequireAuthValue: true},
PCRData: &PcrPolicyData_v3{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}),
}

mockPolicyDigest, err := builder.Digest()
c.Assert(err, IsNil)
Expand Down Expand Up @@ -816,9 +822,11 @@ func (s *platformSuite) TestRecoverKeysWithAuthKeyTPMLockout(c *C) {
PCRPolicyRef: pcrPolicyRef,
PCRPolicyCounterHandle: index,
RequireAuthValue: true},
PCRData: &PcrPolicyData_v3{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull},
}),
}

mockPolicyDigest, err := builder.Digest()
c.Assert(err, IsNil)
Expand Down
7 changes: 4 additions & 3 deletions tpm2/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,10 @@ var newKeyDataPolicy = func(alg tpm2.HashAlgorithmId, key *tpm2.Public, role str
PCRPolicyCounterHandle: pcrPolicyCounterHandle,
RequireAuthValue: requireAuthValue},
PCRData: &pcrPolicyData_v3{
// Set AuthorizedPolicySignature here because this object needs to be
// serializable before the initial signature is created.
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull}}}, policyDigest, nil
pcrPolicyData_v2: pcrPolicyData_v2{
// Set AuthorizedPolicySignature here because this object needs to be
// serializable before the initial signature is created.
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull}}}}, policyDigest, nil
}

// newKeyDataPolicyLegacy creates a keyDataPolicy for legacy sealed key files containing a static
Expand Down
68 changes: 65 additions & 3 deletions tpm2/policy_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"hash"
Expand Down Expand Up @@ -97,6 +98,7 @@ func computeV3PcrPolicyCounterAuthPolicies(alg tpm2.HashAlgorithmId, updateKey *

// The NV index requires 3 policies:
// - A policy to read the index with no authorization.
// - A policy to use the index with TPM2_PolicyNV with no authorization.
// - A policy to initialize the index with no authorization.
// - A policy for updating the index to revoke old PCR policies using a signed assertion.
var authPolicies tpm2.DigestList
Expand All @@ -109,6 +111,14 @@ func computeV3PcrPolicyCounterAuthPolicies(alg tpm2.HashAlgorithmId, updateKey *
}
authPolicies = append(authPolicies, digest)

builder = policyutil.NewPolicyBuilder(alg)
builder.RootBranch().PolicyCommandCode(tpm2.CommandPolicyNV)
digest, err = builder.Digest()
if err != nil {
return nil, err
}
authPolicies = append(authPolicies, digest)

builder = policyutil.NewPolicyBuilder(alg)
builder.RootBranch().PolicyNvWritten(false)
builder.RootBranch().PolicyCommandCode(tpm2.CommandNVIncrement)
Expand Down Expand Up @@ -148,10 +158,61 @@ type staticPolicyData_v3 struct {
RequireAuthValue bool
}

// policyOrData_v3 is the version 3 on-disk representation of policyOrTree.
// It is a flattened tree which suitable for serializing - the tree is just
// a slice of nodes, with each node specifying an offset to its parent node.
type policyOrData_v3 = policyOrData_v0

// pcrPolicyData_v3 represents version 3 of the PCR policy metadata for
// executing a policy session, and can be updated. It has the same format
// as version 2.
type pcrPolicyData_v3 = pcrPolicyData_v2
type pcrPolicyData_v3 struct {
// This is mostly the same as v0-v2, with the only difference being
// how the call to executeRevocationCheck works.
pcrPolicyData_v2
}

func (d *pcrPolicyData_v3) executeRevocationCheck(tpm *tpm2.TPMContext, counter tpm2.ResourceContext, updateKey *tpm2.Public, policySession tpm2.SessionContext) error {
operandB := make([]byte, 8)
binary.BigEndian.PutUint64(operandB, d.PolicySequence)

// XXX(chrisccoulson): This is the 3rd session we open here, with all 3 loaded
// on the TPM. PC Client TPMs are only guaranteed to support 3 loaded sessions,
// so we're pushing things a bit close to the wire here. This code does execute
// during early boot, but it might be better to make the HMAC session associated
// with the TPM connection unloaded by default (context saved), and only context
// load it when needed.
revocationCheckSession, err := tpm.StartAuthSession(nil, nil, tpm2.SessionTypePolicy, nil, counter.Name().Algorithm())
if err != nil {
return fmt.Errorf("cannot create policy session for revocation check: %w", err)
}
defer tpm.FlushContext(revocationCheckSession)

authPolicies, err := computeV3PcrPolicyCounterAuthPolicies(counter.Name().Algorithm(), updateKey)
if err != nil {
return policyDataError{fmt.Errorf("cannot compute auth policies for PCR policy counter: %w", err)}
}
if err := tpm.PolicyCommandCode(revocationCheckSession, tpm2.CommandPolicyNV); err != nil {
return err
}
if err := tpm.PolicyOR(revocationCheckSession, authPolicies); err != nil {
return err
}
Comment on lines +191 to +212
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be good to have a comment here that explain why we create a new session and what we do with it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment now.


if err := tpm.PolicyNV(counter, counter, policySession, operandB, 0, tpm2.OpUnsignedLE, revocationCheckSession); err != nil {
switch {
case tpm2.IsTPMError(err, tpm2.ErrorPolicy, tpm2.CommandPolicyNV):
// The PCR policy has been revoked.
return policyDataError{errors.New("the PCR policy has been revoked")}
case tpm2.IsTPMSessionError(err, tpm2.ErrorPolicyFail, tpm2.CommandPolicyNV, 1):
// Either StaticData.PCRPolicyCounterAuthPolicies is invalid or the NV index isn't what's expected, so the key file is invalid.
return policyDataError{errors.New("invalid PCR policy counter or associated authorization policy metadata")}
}
return xerrors.Errorf("cannot complete PCR policy revocation check: %w", err)
}

return nil
}

// keyDataPolicy_v3 represents version 3 of the metadata for executing a
// policy session.
Expand Down Expand Up @@ -223,6 +284,8 @@ func (p *keyDataPolicy_v3) ExecutePCRPolicy(tpm *tpm2.TPMContext, policySession,
return policyDataError{fmt.Errorf("invalid handle %v for PCR policy counter", pcrPolicyCounterHandle)}
}

authPublicKey := p.StaticData.AuthPublicKey

var pcrPolicyCounter tpm2.ResourceContext
if pcrPolicyCounterHandle != tpm2.HandleNull {
var err error
Expand All @@ -235,12 +298,11 @@ func (p *keyDataPolicy_v3) ExecutePCRPolicy(tpm *tpm2.TPMContext, policySession,
return err
}

if err := p.PCRData.executeRevocationCheck(tpm, pcrPolicyCounter, policySession, nil); err != nil {
if err := p.PCRData.executeRevocationCheck(tpm, pcrPolicyCounter, authPublicKey, policySession); err != nil {
return err
}
}

authPublicKey := p.StaticData.AuthPublicKey
authorizeKey, err := tpm.LoadExternal(nil, authPublicKey, tpm2.HandleOwner)
if err != nil {
if tpm2.IsTPMParameterError(err, tpm2.AnyErrorCode, tpm2.CommandLoadExternal, 2) {
Expand Down
13 changes: 7 additions & 6 deletions tpm2/policy_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,15 @@ func (s *policyV3SuiteNoTPM) TestPCRPolicyCounterHandle(c *C) {

func (s *policyV3SuiteNoTPM) TestPCRPolicySequence(c *C) {
var data KeyDataPolicy = &KeyDataPolicy_v3{
PCRData: &PcrPolicyData_v3{
PolicySequence: 10}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
PolicySequence: 10})}
c.Check(data.PCRPolicySequence(), Equals, uint64(10))

data = &KeyDataPolicy_v3{
PCRData: &PcrPolicyData_v3{
PolicySequence: 500}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
PolicySequence: 500})}
c.Check(data.PCRPolicySequence(), Equals, uint64(500))
}

Expand Down Expand Up @@ -1330,8 +1332,7 @@ func (s *policyV3Suite) TestExecutePCRPolicyErrorHandlingInvalidAuthPublicKey(c
},
})
c.Check(IsPolicyDataError(err), testutil.IsTrue)
c.Check(err, ErrorMatches, "public area of dynamic authorization policy signing key is invalid: TPM returned an error for parameter 2 whilst executing command TPM_CC_LoadExternal: "+
"TPM_RC_HASH \\(hash algorithm not supported or not appropriate\\)")
c.Check(err, ErrorMatches, "cannot compute auth policies for PCR policy counter: could not build policy: encountered an error when calling PolicySigned: invalid authKey")
}

func (s *policyV3Suite) TestExecutePCRPolicyErrorHandlingInvalidAuthorizedPolicySignature(c *C) {
Expand Down
5 changes: 3 additions & 2 deletions tpm2/seal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,9 @@ func (s *sealSuiteNoTPM) testMakeSealedKeyData(c *C, data *testMakeSealedKeyData
StaticData: &StaticPolicyData_v3{
AuthPublicKey: key,
PCRPolicyCounterHandle: index},
PCRData: &PcrPolicyData_v3{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull}}}
PCRData: NewPcrPolicyData_v3(
&PcrPolicyData_v2{
AuthorizedPolicySignature: &tpm2.Signature{SigAlg: tpm2.SigSchemeAlgNull}})}

mockPolicyDigest = make([]byte, alg.Size())
rand.Read(mockPolicyDigest)
Expand Down
Loading