diff --git a/tpm2/constants.go b/tpm2/constants.go index 8c048e7e..edb7b59e 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -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 + 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 diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 9615e7c9..3adb3d1e 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -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 { + 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 diff --git a/tpm2/tpm2_test.go b/tpm2/tpm2_test.go index 4bf09815..166b3b50 100644 --- a/tpm2/tpm2_test.go +++ b/tpm2/tpm2_test.go @@ -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) + // 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) +}