From 4806c073f87d8f965985acd24da79aad34e6b78f Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 5 Jul 2019 12:48:46 +0100 Subject: [PATCH 1/9] Add support for clearing TPM2 devices This adds an API for calling TPM2_Clear with password authentication --- tpm2/constants.go | 1 + tpm2/tpm2.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tpm2/constants.go b/tpm2/constants.go index 3a11cd04..d797935b 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -306,6 +306,7 @@ var toGoCurve = map[EllipticCurve]elliptic.Curve{ const ( cmdEvictControl tpmutil.Command = 0x00000120 cmdUndefineSpace tpmutil.Command = 0x00000122 + cmdClear tpmutil.Command = 0x00000126 cmdClockSet tpmutil.Command = 0x00000128 cmdDefineSpace tpmutil.Command = 0x0000012A cmdPCRAllocate tpmutil.Command = 0x0000012B diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index a3d089c8..d43e5daa 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -507,6 +507,27 @@ 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) +} + +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 decodeReadPublic(in []byte) (Public, []byte, []byte, error) { var resp struct { Public tpmutil.U16Bytes From 67499b40886d4c51867e2bb9ae5da78424b6238f Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 5 Jul 2019 16:07:40 +0100 Subject: [PATCH 2/9] Add support for setting dictionary attack parameters This adds a simple API that wraps the TPM2_DictionaryAttackParameters command. --- tpm2/constants.go | 35 ++++++++++++++++++----------------- tpm2/tpm2.go | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/tpm2/constants.go b/tpm2/constants.go index d797935b..301723f8 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -304,23 +304,24 @@ var toGoCurve = map[EllipticCurve]elliptic.Curve{ // Supported TPM operations. const ( - cmdEvictControl tpmutil.Command = 0x00000120 - cmdUndefineSpace tpmutil.Command = 0x00000122 - cmdClear tpmutil.Command = 0x00000126 - 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 + 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 + cmdDictionaryAttackParameters tpmutil.Command = 0x0000013A + 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 diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index d43e5daa..7c04936a 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -528,6 +528,31 @@ func Clear(rw io.ReadWriter, hierarchy tpmutil.Handle, password string) error { 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) +} + +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 decodeReadPublic(in []byte) (Public, []byte, []byte, error) { var resp struct { Public tpmutil.U16Bytes From c5ced971e085b7d2bcccc8d580295e16370e9021 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 5 Jul 2019 16:24:13 +0100 Subject: [PATCH 3/9] Add support for changing TPM2 hierarchy authorizations This adds an API that wraps around the TPM2_HierarchyChangeAuth command so that authorization values for the various hierarchies can be set. --- tpm2/constants.go | 1 + tpm2/tpm2.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tpm2/constants.go b/tpm2/constants.go index 301723f8..fe0f1d6b 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -308,6 +308,7 @@ const ( cmdUndefineSpace tpmutil.Command = 0x00000122 cmdClear tpmutil.Command = 0x00000126 cmdClockSet tpmutil.Command = 0x00000128 + cmdHierarchyChangeAuth tpmutil.Command = 0x00000129 cmdDefineSpace tpmutil.Command = 0x0000012A cmdPCRAllocate tpmutil.Command = 0x0000012B cmdCreatePrimary tpmutil.Command = 0x00000131 diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 7c04936a..14e6cc94 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -553,6 +553,31 @@ func SetDictionaryAttackParameters(rw io.ReadWriter, maxTries, recoveryTime, loc 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) +} + +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 From 736fbab99bf16295b6b37d46e9cc14f443f6af2b Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Fri, 5 Jul 2019 16:45:26 +0100 Subject: [PATCH 4/9] Add support for disabling owner clear on TPM2 devices This adds an API that wraps around TPM2_ClearControl to disable the ability to clear the TPM with owner authorization. As re-enabling owner clear requires platform authorization, the API only supports disabling owner clear (ie, TPM2_ClearControl called with disable=YES") --- tpm2/constants.go | 1 + tpm2/tpm2.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tpm2/constants.go b/tpm2/constants.go index fe0f1d6b..e1a09431 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -307,6 +307,7 @@ const ( 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 diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 14e6cc94..e816d71d 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -528,6 +528,31 @@ func Clear(rw io.ReadWriter, hierarchy tpmutil.Handle, password string) error { return err } +func encodeDisableOwnerClear(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 + } + param, err := tpmutil.Pack(true) + if err != nil { + return nil, err + } + return concat(lockout, auth, param) +} + +func DisableOwnerClear(rw io.ReadWriter, password string) error { + cmd, err := encodeDisableOwnerClear(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 { From e66d7692f6c387d4375b9cab21ee679a9fb5529d Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 8 Jul 2019 14:30:28 +0100 Subject: [PATCH 5/9] Add tests for Clear and HierarchyChangeAuth --- tpm2/tpm2_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tpm2/tpm2_test.go b/tpm2/tpm2_test.go index 9800dfde..6250018a 100644 --- a/tpm2/tpm2_test.go +++ b/tpm2/tpm2_test.go @@ -1363,3 +1363,57 @@ 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)) + t.Logf("len(vals)=%d", len(vals)) + 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) +} From c6c7cb7465ae50e13263f2f8ff33f57d1f9859bc Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Mon, 8 Jul 2019 14:36:22 +0100 Subject: [PATCH 6/9] Remove a spurious Logf --- tpm2/tpm2_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tpm2/tpm2_test.go b/tpm2/tpm2_test.go index 6250018a..341d6c75 100644 --- a/tpm2/tpm2_test.go +++ b/tpm2/tpm2_test.go @@ -1392,7 +1392,6 @@ func TestClear(t *testing.T) { } vals, _, err := GetCapability(rw, CapabilityHandles, 1, uint32(persistentHandle)) - t.Logf("len(vals)=%d", len(vals)) if err != nil { t.Fatalf("GetCapability failed: %v", err) } From a3814f48fd64c65cd46972d28dc0d6980b6d4e76 Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Thu, 11 Jul 2019 11:00:18 +0100 Subject: [PATCH 7/9] Address review comments --- tpm2/constants.go | 2 +- tpm2/tpm2.go | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tpm2/constants.go b/tpm2/constants.go index e1a09431..036d2049 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -315,8 +315,8 @@ const ( cmdCreatePrimary tpmutil.Command = 0x00000131 cmdIncrementNVCounter tpmutil.Command = 0x00000134 cmdWriteNV tpmutil.Command = 0x00000137 - cmdPCREvent tpmutil.Command = 0x0000013C cmdDictionaryAttackParameters tpmutil.Command = 0x0000013A + cmdPCREvent tpmutil.Command = 0x0000013C cmdStartup tpmutil.Command = 0x00000144 cmdShutdown tpmutil.Command = 0x00000145 cmdStirRandom tpmutil.Command = 0x00000146 diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index e816d71d..3cdac65a 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -519,6 +519,9 @@ func encodeClear(hierarchy tpmutil.Handle, password string) ([]byte, error) { 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 { @@ -528,8 +531,8 @@ func Clear(rw io.ReadWriter, hierarchy tpmutil.Handle, password string) error { return err } -func encodeDisableOwnerClear(password string) ([]byte, error) { - lockout, err := tpmutil.Pack(HandleLockout) +func encodeClearControl(hierarchy tpmutil.Handle, disable bool, password string) ([]byte, error) { + handle, err := tpmutil.Pack(hierarchy) if err != nil { return nil, err } @@ -537,15 +540,21 @@ func encodeDisableOwnerClear(password string) ([]byte, error) { if err != nil { return nil, err } - param, err := tpmutil.Pack(true) + param, err := tpmutil.Pack(disable) if err != nil { return nil, err } - return concat(lockout, auth, param) + return concat(handle, auth, param) } -func DisableOwnerClear(rw io.ReadWriter, password string) error { - cmd, err := encodeDisableOwnerClear(password) +// Control 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 } From 9f5e4eb491faa0e349919e0ab888f9607101834c Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Thu, 11 Jul 2019 11:07:55 +0100 Subject: [PATCH 8/9] Add a couple more doc comments --- tpm2/tpm2.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 3cdac65a..9f7b6a60 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -578,6 +578,13 @@ func encodeSetDAParameters(maxTries, recoveryTime, lockoutRecovery uint32, passw return concat(lockout, auth, params) } +// Change 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 { @@ -603,6 +610,8 @@ func encodeHierarchyChangeAuthParameters(hierarchy tpmutil.Handle, oldPassword, 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 { From 6cd31e7b4a499f8ab6cfe6e58adf030c28ee9f6e Mon Sep 17 00:00:00 2001 From: Chris Coulson Date: Thu, 18 Jul 2019 13:32:34 +0100 Subject: [PATCH 9/9] Adjust doc comments --- tpm2/tpm2.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 9f7b6a60..63fa6d9e 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -547,9 +547,9 @@ func encodeClearControl(hierarchy tpmutil.Handle, disable bool, password string) return concat(handle, auth, param) } -// Control 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. +// 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. @@ -578,11 +578,12 @@ func encodeSetDAParameters(maxTries, recoveryTime, lockoutRecovery uint32, passw return concat(lockout, auth, params) } -// Change 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. +// 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 {