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

Implement some additional hierarchy control and dictionary attack functions for TPM2 #109

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
36 changes: 20 additions & 16 deletions tpm2/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,22 +304,26 @@ var toGoCurve = map[EllipticCurve]elliptic.Curve{

// Supported TPM operations.
const (
cmdEvictControl tpmutil.Command = 0x00000120
cmdUndefineSpace tpmutil.Command = 0x00000122
cmdClockSet tpmutil.Command = 0x00000128
cmdDefineSpace tpmutil.Command = 0x0000012A
cmdPCRAllocate tpmutil.Command = 0x0000012B
cmdCreatePrimary tpmutil.Command = 0x00000131
cmdIncrementNVCounter tpmutil.Command = 0x00000134
cmdWriteNV tpmutil.Command = 0x00000137
cmdPCREvent tpmutil.Command = 0x0000013C
cmdStartup tpmutil.Command = 0x00000144
cmdShutdown tpmutil.Command = 0x00000145
cmdStirRandom tpmutil.Command = 0x00000146
cmdActivateCredential tpmutil.Command = 0x00000147
cmdCertify tpmutil.Command = 0x00000148
cmdCertifyCreation tpmutil.Command = 0x0000014A
cmdReadNV tpmutil.Command = 0x0000014E
cmdEvictControl tpmutil.Command = 0x00000120
cmdUndefineSpace tpmutil.Command = 0x00000122
cmdClear tpmutil.Command = 0x00000126
cmdClearControl tpmutil.Command = 0x00000127
cmdClockSet tpmutil.Command = 0x00000128
cmdHierarchyChangeAuth tpmutil.Command = 0x00000129
cmdDefineSpace tpmutil.Command = 0x0000012A
cmdPCRAllocate tpmutil.Command = 0x0000012B
cmdCreatePrimary tpmutil.Command = 0x00000131
cmdIncrementNVCounter tpmutil.Command = 0x00000134
cmdWriteNV tpmutil.Command = 0x00000137
cmdDictionaryAttackParameters tpmutil.Command = 0x0000013A
Copy link
Contributor

Choose a reason for hiding this comment

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

Move it one line higher, to keep sorted order

Copy link
Author

Choose a reason for hiding this comment

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

Thanks, I've done that now.

cmdPCREvent tpmutil.Command = 0x0000013C
cmdStartup tpmutil.Command = 0x00000144
cmdShutdown tpmutil.Command = 0x00000145
cmdStirRandom tpmutil.Command = 0x00000146
cmdActivateCredential tpmutil.Command = 0x00000147
cmdCertify tpmutil.Command = 0x00000148
cmdCertifyCreation tpmutil.Command = 0x0000014A
cmdReadNV tpmutil.Command = 0x0000014E
// CmdPolicySecret is a command code for TPM2_PolicySecret.
// It's exported for computing of default AuthPolicy value.
CmdPolicySecret tpmutil.Command = 0x00000151
Expand Down
114 changes: 114 additions & 0 deletions tpm2/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,120 @@ func CreatePrimaryRawTemplate(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSel
return hnd, pubKey, nil
}

func encodeClear(hierarchy tpmutil.Handle, password string) ([]byte, error) {
ha, err := tpmutil.Pack(hierarchy)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)})
if err != nil {
return nil, err
}
return concat(ha, auth)
}

// Clear removes all context associated with the current owner from the TPM.
// The handle provided must be for the platform or lockout hierarchy. This command will fail if the clear command
// has been disabled via the ClearControl() API.
func Clear(rw io.ReadWriter, hierarchy tpmutil.Handle, password string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add doc comments to all exported funcs

Copy link
Author

Choose a reason for hiding this comment

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

Done.

cmd, err := encodeClear(hierarchy, password)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, cmdClear, tpmutil.RawBytes(cmd))
return err
}

func encodeClearControl(hierarchy tpmutil.Handle, disable bool, password string) ([]byte, error) {
handle, err := tpmutil.Pack(hierarchy)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)})
if err != nil {
return nil, err
}
param, err := tpmutil.Pack(disable)
if err != nil {
return nil, err
}
return concat(handle, auth, param)
}

// Control whether the TPM can be cleared with the Clear() API. Calling this with the third parameter set to
Copy link
Contributor

Choose a reason for hiding this comment

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

Per https://golang.org/doc/effective_go.html#commentary, the comment must start with // ClearControl ...

Copy link
Author

Choose a reason for hiding this comment

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

Done

// true will disable the ability to clear the TPM. Calling it with the third parameter set to false will
// reenable the ability to clear the TPM.
//
// The handle provided must be for the platform or lockout hierarchy when disabling the ability to clear the
// TPM, but reenabling requires that it be the handle for the platform hierarchy.
func ClearControl(rw io.ReadWriter, hierarchy tpmutil.Handle, disable bool, password string) error {
cmd, err := encodeClearControl(hierarchy, disable, password)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, cmdClearControl, tpmutil.RawBytes(cmd))
return err
}

func encodeSetDAParameters(maxTries, recoveryTime, lockoutRecovery uint32, password string) ([]byte, error) {
lockout, err := tpmutil.Pack(HandleLockout)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)})
if err != nil {
return nil, err
}
params, err := tpmutil.Pack(maxTries, recoveryTime, lockoutRecovery)
if err != nil {
return nil, err
}
return concat(lockout, auth, params)
}

// Change the dictionary attack lockout parameters. The first parameter is the number of authorization failures
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above, start comment with // SetDictionaryAttackParameters ...

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the PR.
Please also sign the CLA per the above comment

I've hit this issue in the past, but I should be part of Canonical's corporate CLA with Google now.

Did you add your Canonical email address to the GitHub account? You can check it under https://github.com/settings/emails. It doesn't need to be primary, but needs to be added there.

My Canonical email address is registered with my github account. Note that I was only added to the CCLA after submitting this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I don't see anything wrong.
One last thing to check - did you change your github username since getting added to the CLA? Or perhaps there was a typo in it?

I've emailed our internal team responsible for this CLA github bot, will let you know when we figure out the problem.

// before the lockout is enabled. The second parameter is the number of seconds before the authorization failure
// count is decremented by one. Setting this to zero disables dictionary attack protection. The third parameter
// is the number of seconds after a lockout hierarchy authorization failure before the lockout hierarchy can
// be used again. Setting this to zero causes this lockout to be cleared after a power cycle.
//
// This command must be authorized using the lockout hierarchy.
func SetDictionaryAttackParameters(rw io.ReadWriter, maxTries, recoveryTime, lockoutRecovery uint32, password string) error {
cmd, err := encodeSetDAParameters(maxTries, recoveryTime, lockoutRecovery, password)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, cmdDictionaryAttackParameters, tpmutil.RawBytes(cmd))
return err
}

func encodeHierarchyChangeAuthParameters(hierarchy tpmutil.Handle, oldPassword, newPassword string) ([]byte, error) {
handle, err := tpmutil.Pack(hierarchy)
if err != nil {
return nil, err
}
auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(oldPassword)})
if err != nil {
return nil, err
}
params, err := tpmutil.Pack(tpmutil.U16Bytes(newPassword))
if err != nil {
return nil, err
}
return concat(handle, auth, params)
}

// Change the authorization value for the specified hierarchy. The command must be authorized with the
// hierarchy's current authorization value.
func HierarchyChangeAuth(rw io.ReadWriter, hierarchy tpmutil.Handle, oldPassword, newPassword string) error {
cmd, err := encodeHierarchyChangeAuthParameters(hierarchy, oldPassword, newPassword)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, cmdHierarchyChangeAuth, tpmutil.RawBytes(cmd))
return err
}

func decodeReadPublic(in []byte) (Public, []byte, []byte, error) {
var resp struct {
Public tpmutil.U16Bytes
Expand Down
53 changes: 53 additions & 0 deletions tpm2/tpm2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1363,3 +1363,56 @@ func TestCreatePrimaryRawTemplate(t *testing.T) {
t.Errorf("got key exponent %v, want %v", pubRSA.E, defaultKeyParams.RSAParameters.Exponent)
}
}

func TestClear(t *testing.T) {
rw := openTPM(t)
defer rw.Close()

rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams)
if err != nil {
t.Fatalf("CreatePrimary failed: %v", err)
}
defer FlushContext(rw, rootHandle)

persistentHandle := tpmutil.Handle(0x817FFFFF)
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment explaining where this value comes from

Copy link
Author

Choose a reason for hiding this comment

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

I just copied this value from other tests in tpm2_test.go.

// Evict persistent key, if there is one already (e.g. last test run failed).
if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil {
t.Logf("(expected) EvictControl failed: %v", err)
}
// Make key persistent.
if err := EvictControl(rw, emptyPassword, HandleOwner, rootHandle, persistentHandle); err != nil {
t.Fatalf("EvictControl failed: %v", err)
}

if err := Clear(rw, HandleLockout, emptyPassword); err != nil {
if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil {
t.Logf("EvictControl failed: %v", err)
}
t.Fatalf("Clear failed: %v", err)
}

vals, _, err := GetCapability(rw, CapabilityHandles, 1, uint32(persistentHandle))
if err != nil {
t.Fatalf("GetCapability failed: %v", err)
}

if len(vals) != 0 {
t.Errorf("Persistent handle wasn't cleared")
}
}

func TestHierarchyChangeAuth(t *testing.T) {
rw := openTPM(t)
defer rw.Close()

if err := HierarchyChangeAuth(rw, HandleOwner, emptyPassword, defaultPassword); err != nil {
t.Fatalf("HierarchyChangeAuth failed: %v", err)
}
defer HierarchyChangeAuth(rw, HandleOwner, defaultPassword, emptyPassword)

handle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, defaultPassword, emptyPassword, defaultKeyParams)
if err != nil {
t.Errorf("CreatePrimary failed: %v", err)
}
FlushContext(rw, handle)
}