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
30 changes: 17 additions & 13 deletions tpm2/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,19 +304,23 @@ var toGoCurve = map[EllipticCurve]elliptic.Curve{

// Supported TPM operations.
const (
cmdEvictControl tpmutil.Command = 0x00000120
cmdUndefineSpace tpmutil.Command = 0x00000122
cmdDefineSpace tpmutil.Command = 0x0000012A
cmdCreatePrimary tpmutil.Command = 0x00000131
cmdIncrementNVCounter tpmutil.Command = 0x00000134
cmdWriteNV tpmutil.Command = 0x00000137
cmdPCREvent tpmutil.Command = 0x0000013C
cmdStartup tpmutil.Command = 0x00000144
cmdShutdown tpmutil.Command = 0x00000145
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
cmdHierarchyChangeAuth tpmutil.Command = 0x00000129
cmdDefineSpace tpmutil.Command = 0x0000012A
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
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
115 changes: 115 additions & 0 deletions tpm2/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,121 @@ func CreatePrimaryRawTemplate(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSel
return CreatePrimary(rw, owner, sel, parentPassword, ownerPassword, pub)
}

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)
}

// ClearControl controls whether the TPM can be cleared with the Clear() API. Calling this with the third
// parameter set to 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)
}

// SetDictionaryAttackParameters changes the dictionary attack lockout parameters. The first parameter is the
// number of authorization failures 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)
}