diff --git a/go.mod b/go.mod index f031dd6d..9770f393 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/canonical/go-kbkdf v0.0.0-20250104172618-3b1308f9acf9 github.com/canonical/go-password-validator v0.0.0-20250617132709-1b205303ca54 github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 - github.com/canonical/go-tpm2 v1.15.0 + github.com/canonical/go-tpm2 v1.16.0 github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981 github.com/jessevdk/go-flags v1.5.0 github.com/snapcore/snapd v0.0.0-20220714152900-4a1f4c93fc85 diff --git a/go.sum b/go.sum index 6622f560..6e12c2c3 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/canonical/go-tpm2 v1.13.0 h1:Ka9VmUVwoz9pJef5JXP6Gd4CIhxFE70X26K8x3Le github.com/canonical/go-tpm2 v1.13.0/go.mod h1:P50xMwC7y5/uxPikzWdK4d9pW9orKi8+ZL5sBifxoBQ= github.com/canonical/go-tpm2 v1.15.0 h1:T4dVCO8qCs76vDDs4vWNpvPdh7UHuSORPH4Scq7N2gw= github.com/canonical/go-tpm2 v1.15.0/go.mod h1:P50xMwC7y5/uxPikzWdK4d9pW9orKi8+ZL5sBifxoBQ= +github.com/canonical/go-tpm2 v1.16.0 h1:AX+hpmdPgR8i3VFe3DVgKO46S5EpnumKP0yS5ND/Tz8= +github.com/canonical/go-tpm2 v1.16.0/go.mod h1:P50xMwC7y5/uxPikzWdK4d9pW9orKi8+ZL5sBifxoBQ= github.com/canonical/tcglog-parser v0.0.0-20210824131805-69fa1e9f0ad2/go.mod h1:QoW2apR2tBl6T/4czdND/EHjL1Ia9cCmQnIj9Xe0Kt8= github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981 h1:vrUzSfbhl8mzdXPzjxq4jXZPCCNLv18jy6S7aVTS2tI= github.com/canonical/tcglog-parser v0.0.0-20240924110432-d15eaf652981/go.mod h1:ywdPBqUGkuuiitPpVWCfilf2/gq+frhq4CNiNs9KyHU= diff --git a/internal/compattest/v0_test.go b/internal/compattest/v0_test.go index 3fba4318..0bd9b707 100644 --- a/internal/compattest/v0_test.go +++ b/internal/compattest/v0_test.go @@ -62,7 +62,7 @@ func (s *compatTestV0Suite) TestUnseal2(c *C) { func (s *compatTestV0Suite) TestUnsealAfterReprovision(c *C) { // Test that reprovisioning doesn't touch the legacy lock NV index if it is valid - c.Assert(s.TPM().EnsureProvisioned(secboot_tpm2.ProvisionWithoutLockout()), IsNil) + c.Assert(s.TPM().EnsureProvisioned(), IsNil) s.testUnseal(c, s.absPath("pcrSequence.1")) } diff --git a/internal/compattest/v1_test.go b/internal/compattest/v1_test.go index 5838a78f..36e3b3e2 100644 --- a/internal/compattest/v1_test.go +++ b/internal/compattest/v1_test.go @@ -51,7 +51,7 @@ func (s *compatTestV1Suite) TestUnseal2(c *C) { func (s *compatTestV1Suite) TestUnsealAfterReprovision(c *C) { // This should still work because the primary key doesn't change. - c.Assert(s.TPM().EnsureProvisioned(secboot_tpm2.ProvisionWithoutLockout()), IsNil) + c.Assert(s.TPM().EnsureProvisioned(), IsNil) s.testUnseal(c, s.absPath("pcrSequence.1")) } diff --git a/tpm2/export_test.go b/tpm2/export_test.go index 2a0a9272..7a2dae3d 100644 --- a/tpm2/export_test.go +++ b/tpm2/export_test.go @@ -150,16 +150,21 @@ func NewPcrPolicyData_v3(v2 *PcrPolicyData_v2) *PcrPolicyData_v3 { type PlatformKeyDataHandler = platformKeyDataHandler +// TODO: Remove this and ProvisionMode.Option in favour of just updating the tests +// to use the correct options instead. type ProvisionMode = provisionMode -func (m ProvisionMode) Option() EnsureProvisionedOption { +func (m ProvisionMode) Option(lockoutAuthValue []byte) EnsureProvisionedOption { switch m { case provisionModeWithoutLockout: - return ProvisionWithoutLockout() + return func(_ *ensureProvisionedParams) {} case provisionModeClear: - return WithClearBeforeProvision() + return func(p *ensureProvisionedParams) { + WithLockoutAuthValue(lockoutAuthValue)(p) + WithClearBeforeProvision()(p) + } default: - return func(_ *ensureProvisionedParams) {} + return WithLockoutAuthValue(lockoutAuthValue) } } diff --git a/tpm2/key_sealer_test.go b/tpm2/key_sealer_test.go index cff059fa..a4e4ed8a 100644 --- a/tpm2/key_sealer_test.go +++ b/tpm2/key_sealer_test.go @@ -50,7 +50,7 @@ func (s *sealedObjectKeySealerSuite) SetUpSuite(c *C) { func (s *sealedObjectKeySealerSuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) } diff --git a/tpm2/lockoutauth.go b/tpm2/lockoutauth.go index 6fb4e70f..00738836 100644 --- a/tpm2/lockoutauth.go +++ b/tpm2/lockoutauth.go @@ -115,46 +115,78 @@ func (p *lockoutAuthParams) UnmarshalJSON(data []byte) error { return nil } -func (t *Connection) resetDictionaryAttackLockImpl(params *lockoutAuthParams) error { - if len(params.NewAuthValue) > 0 || params.NewAuthPolicy != nil { - return errors.New("lockout hierarchy auth value change not supported yet") +// authorizeLockout authorizes the use of the lockout hierarchy using the supplied parameters for the +// specified command code. On success, a session is returned that can be used to authorize the specified +// command. The session is either a newly created policy session or the HMAC session returned from +// Connection.HmacSession. +// +// After using the authorization, the caller must execute the returned callback. +func (t *Connection) authorizeLockout(authParams *lockoutAuthParams, command tpm2.CommandCode) (session tpm2.SessionContext, lockoutAuthSet bool, done func(), err error) { + if len(authParams.NewAuthValue) > 0 || authParams.NewAuthPolicy != nil { + return nil, false, nil, errors.New("lockout hierarchy auth value change not supported yet") } var authValue []byte val, err := t.GetCapabilityTPMProperty(tpm2.PropertyPermanent) if err != nil { - return fmt.Errorf("cannot obtain value of TPM_PT_PERMANENT: %w", err) + return nil, false, nil, fmt.Errorf("cannot obtain value of TPM_PT_PERMANENT: %w", err) } - lockoutAuthSet := tpm2.PermanentAttributes(val)&tpm2.AttrLockoutAuthSet > 0 + lockoutAuthSet = tpm2.PermanentAttributes(val)&tpm2.AttrLockoutAuthSet > 0 if lockoutAuthSet { - authValue = params.AuthValue + authValue = authParams.AuthValue } - var session tpm2.SessionContext switch { - case params.AuthPolicy == nil: + case authParams.AuthPolicy == nil: session = t.HmacSession() default: session, err = t.StartAuthSession(nil, nil, tpm2.SessionTypePolicy, nil, defaultSessionHashAlgorithm) if err != nil { - return fmt.Errorf("cannot start policy session: %w", err) + return nil, false, nil, fmt.Errorf("cannot start policy session: %w", err) } - defer t.FlushContext(session) + sessionInternal := session + defer func() { + if err == nil { + return + } + t.FlushContext(sessionInternal) + }() // Execute policy session, constraining the use to the TPM2_DictionaryAttackLockReset command so // that the correct branch executes. - _, err := params.AuthPolicy.Execute( + _, err := authParams.AuthPolicy.Execute( policyutil.NewPolicyExecuteSession(t.TPMContext, session), - policyutil.WithSessionUsageCommandConstraint(tpm2.CommandDictionaryAttackLockReset, []policyutil.NamedHandle{t.LockoutHandleContext()}), + policyutil.WithSessionUsageCommandCodeConstraint(command), ) if err != nil { - return ErrInvalidLockoutAuthPolicy + return nil, false, nil, ErrInvalidLockoutAuthPolicy } } + origAuthValue := t.LockoutHandleContext().AuthValue() t.LockoutHandleContext().SetAuthValue(authValue) - defer t.LockoutHandleContext().SetAuthValue(nil) + defer func() { + if err == nil { + return + } + t.LockoutHandleContext().SetAuthValue(origAuthValue) + }() + + return session, lockoutAuthSet, func() { + if authParams.AuthPolicy != nil { + t.FlushContext(session) + } + t.LockoutHandleContext().SetAuthValue(origAuthValue) + }, nil +} + +func (t *Connection) resetDictionaryAttackLockImpl(params *lockoutAuthParams) error { + session, lockoutAuthSet, done, err := t.authorizeLockout(params, tpm2.CommandDictionaryAttackLockReset) + if err != nil { + return err + } + defer done() switch err := t.DictionaryAttackLockReset(t.LockoutHandleContext(), session); { case isAuthFailError(err, tpm2.CommandDictionaryAttackLockReset, 1): diff --git a/tpm2/lockoutauth_test.go b/tpm2/lockoutauth_test.go index af8455a3..8123a78f 100644 --- a/tpm2/lockoutauth_test.go +++ b/tpm2/lockoutauth_test.go @@ -56,6 +56,12 @@ func (*lockoutauthSuiteMixin) newDefaultLockoutAuthPolicy(c *C, alg tpm2.HashAlg n.AddBranch("", func(b *policyutil.PolicyBuilderBranch) { b.PolicyCommandCode(tpm2.CommandClear) }) + + // XXX: This is here temporarily to make provisioningSuite.TestProvisionWithLockoutAuthData + // pass and will be removed in the next PR. + n.AddBranch("", func(b *policyutil.PolicyBuilderBranch) { + b.PolicyCommandCode(tpm2.CommandHierarchyChangeAuth) + }) }) b.PolicyAuthValue() }) @@ -103,6 +109,20 @@ func (*lockoutauthSuiteMixin) newRotateAuthValueLockoutAuthPolicy(c *C, alg tpm2 return digest, policy } +func (*lockoutauthSuiteMixin) makeLockoutAuthData(c *C, params *LockoutAuthParams) []byte { + data, err := json.Marshal(params) + c.Assert(err, IsNil) + return data +} + +func (m *lockoutauthSuiteMixin) makeDefaultLockoutAuthData(c *C, alg tpm2.HashAlgorithmId, val []byte) (tpm2.Digest, []byte) { + digest, policy := m.newDefaultLockoutAuthPolicy(c, alg) + return digest, m.makeLockoutAuthData(c, &LockoutAuthParams{ + AuthValue: val, + AuthPolicy: policy, + }) +} + type lockoutauthSuiteNoTPM struct { lockoutauthSuiteMixin } @@ -135,12 +155,6 @@ func (s *lockoutauthSuite) SetUpTest(c *C) { c.Assert(s.TPM().DictionaryAttackParameters(s.TPM().LockoutHandleContext(), 32, 7200, 86400, nil), IsNil) } -func (s *lockoutauthSuite) makeLockoutAuthData(c *C, params *LockoutAuthParams) []byte { - data, err := json.Marshal(params) - c.Assert(err, IsNil) - return data -} - var _ = Suite(&lockoutauthSuiteNoTPM{}) var _ = Suite(&lockoutauthSuite{}) @@ -152,7 +166,7 @@ func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsMarshalJSON(c *C) { data, err := json.Marshal(params) c.Check(err, IsNil) - c.Check(data, DeepEquals, []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC5xRENPNjPxvymnylptEkkmB67kMJSALrpC4PA2joYWCAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABaw=="}`)) + c.Check(data, DeepEquals, []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC/cbf1/nUkzaClt4ysmVL5cqWE67D7Brmd7cgdwi7ztVAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQALufUnMhfDMA5sLu0OUIPoKx2NNK4laaj7SfVnqdZFDjYAAAACIAEBcQAAAAUAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAAAAAABAAu+aY2L0UknzX7Xvdk75B8n/yYmvS2KFUDi+URfLuWrLgAAAAEAAAFsAAABKQAAAWs="}`)) } func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsMarshalJSONNoPolicy(c *C) { @@ -176,12 +190,11 @@ func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsMarshalJSONForChangeAuth(c data, err := json.Marshal(params) c.Check(err, IsNil) - c.Check(data, DeepEquals, []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC5xRENPNjPxvymnylptEkkmB67kMJSALrpC4PA2joYWCAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABaw==","new-auth-value":"24LL69EOvYMbSP+K5ydaIwKQdLpiLAQW2XzTTdONgYY=","new-auth-policy":"AAAAAAAAAAEAC8iuOzJsfCEvz5HdnLSO98fhopBFpLgo9fX7/1TF/6KqAAAAAAAAAAEgAQFxAAAAAgAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABawAAAAAAAQALDDnMvDFtHshfTn3M6F3KHOta8q5u4GWsqsqB8JnLJCYAAAACAAABbAAAASkAAAFgACMACwAEAAAAAAAQABAAAwAQACC2BaF5zNUOUWsO9Vxdw5PNDslawcvHjS3x54a1VHxZfAAgaOCKN2rpEFpajypuc/XSGSr0LnK/e8W9IyZMM8DufpUAC0NIQU5HRS1BVVRIAAAAAAAA"}`)) + c.Check(data, DeepEquals, []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC/cbf1/nUkzaClt4ysmVL5cqWE67D7Brmd7cgdwi7ztVAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQALufUnMhfDMA5sLu0OUIPoKx2NNK4laaj7SfVnqdZFDjYAAAACIAEBcQAAAAUAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAAAAAABAAu+aY2L0UknzX7Xvdk75B8n/yYmvS2KFUDi+URfLuWrLgAAAAEAAAFsAAABKQAAAWs=","new-auth-value":"24LL69EOvYMbSP+K5ydaIwKQdLpiLAQW2XzTTdONgYY=","new-auth-policy":"AAAAAAAAAAEAC8iuOzJsfCEvz5HdnLSO98fhopBFpLgo9fX7/1TF/6KqAAAAAAAAAAEgAQFxAAAAAgAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABawAAAAAAAQALDDnMvDFtHshfTn3M6F3KHOta8q5u4GWsqsqB8JnLJCYAAAACAAABbAAAASkAAAFgACMACwAEAAAAAAAQABAAAwAQACC2BaF5zNUOUWsO9Vxdw5PNDslawcvHjS3x54a1VHxZfAAgaOCKN2rpEFpajypuc/XSGSr0LnK/e8W9IyZMM8DufpUAC0NIQU5HRS1BVVRIAAAAAAAA"}`)) } func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsUnmarshalJSON(c *C) { - data := []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC5xRENPNjPxvymnylptEkkmB67kMJSALrpC4PA2joYWCAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABaw=="}`) - + data := []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC/cbf1/nUkzaClt4ysmVL5cqWE67D7Brmd7cgdwi7ztVAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQALufUnMhfDMA5sLu0OUIPoKx2NNK4laaj7SfVnqdZFDjYAAAACIAEBcQAAAAUAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAAAAAABAAu+aY2L0UknzX7Xvdk75B8n/yYmvS2KFUDi+URfLuWrLgAAAAEAAAFsAAABKQAAAWs="}`) expected := &LockoutAuthParams{ AuthValue: testutil.DecodeHexString(c, "c7da0ed6f6ba3f3ea741e7863a0a1748138b6eccb0e084132b04a9c976f0d0b1"), AuthPolicy: s.newDefaultLockoutAuthPolicy(c, tpm2.HashAlgorithmSHA256), @@ -193,7 +206,7 @@ func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsUnmarshalJSON(c *C) { } func (s *lockoutauthSuiteNoTPM) TestLockoutAuthParamsUnmarshalJSONForChangeAuth(c *C) { - data := []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC5xRENPNjPxvymnylptEkkmB67kMJSALrpC4PA2joYWCAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABaw==","new-auth-value":"24LL69EOvYMbSP+K5ydaIwKQdLpiLAQW2XzTTdONgYY=","new-auth-policy":"AAAAAAAAAAEAC8iuOzJsfCEvz5HdnLSO98fhopBFpLgo9fX7/1TF/6KqAAAAAAAAAAEgAQFxAAAAAgAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABawAAAAAAAQALDDnMvDFtHshfTn3M6F3KHOta8q5u4GWsqsqB8JnLJCYAAAACAAABbAAAASkAAAFgACMACwAEAAAAAAAQABAAAwAQACC2BaF5zNUOUWsO9Vxdw5PNDslawcvHjS3x54a1VHxZfAAgaOCKN2rpEFpajypuc/XSGSr0LnK/e8W9IyZMM8DufpUAC0NIQU5HRS1BVVRIAAAAAAAA"}`) + data := []byte(`{"auth-value":"x9oO1va6Pz6nQeeGOgoXSBOLbsyw4IQTKwSpyXbw0LE=","auth-policy":"AAAAAAAAAAEAC/cbf1/nUkzaClt4ysmVL5cqWE67D7Brmd7cgdwi7ztVAAAAAAAAAAEgAQFxAAAAAQAAAAAAAQALufUnMhfDMA5sLu0OUIPoKx2NNK4laaj7SfVnqdZFDjYAAAACIAEBcQAAAAUAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAAAAAABAAu+aY2L0UknzX7Xvdk75B8n/yYmvS2KFUDi+URfLuWrLgAAAAEAAAFsAAABKQAAAWs=","new-auth-value":"24LL69EOvYMbSP+K5ydaIwKQdLpiLAQW2XzTTdONgYY=","new-auth-policy":"AAAAAAAAAAEAC8iuOzJsfCEvz5HdnLSO98fhopBFpLgo9fX7/1TF/6KqAAAAAAAAAAEgAQFxAAAAAgAAAAAAAQAL+21OPQovgBAFA+/1biwvpZu8ItTlnZBiGL/DKXTgoIIAAAACIAEBcQAAAAQAAAAAAAEAC7bFwF5YGQnN6n33pfkcDy7tN/128VUi7uW1X4lvLVY/AAAAAQAAAWwAAAE5AAAAAAABAAscaCd8nWVk3YG8z35Wuj7cqziPxgzpWzpEK9JyWPYN/AAAAAEAAAFsAAABOgAAAAAAAQALlAz7Qhe7Htz3+0GTfKl0qmjmmKt4uBJLBwET4hH9RvwAAAABAAABbAAAAScAAAAAAAEAC8Tfq87ajeg2yVZhlSiSsd73IDr7Rv7+xD/8/JO+VAcwAAAAAQAAAWwAAAEmAAABawAAAAAAAQALDDnMvDFtHshfTn3M6F3KHOta8q5u4GWsqsqB8JnLJCYAAAACAAABbAAAASkAAAFgACMACwAEAAAAAAAQABAAAwAQACC2BaF5zNUOUWsO9Vxdw5PNDslawcvHjS3x54a1VHxZfAAgaOCKN2rpEFpajypuc/XSGSr0LnK/e8W9IyZMM8DufpUAC0NIQU5HRS1BVVRIAAAAAAAA"}`) authValue := testutil.DecodeHexString(c, "c7da0ed6f6ba3f3ea741e7863a0a1748138b6eccb0e084132b04a9c976f0d0b1") expected := &LockoutAuthParams{ @@ -348,6 +361,9 @@ func (s *lockoutauthSuite) TestResetDictionaryAttackLockWithAuthValue(c *C) { c.Check(s.TPM().LockoutHandleContext().AuthValue(), DeepEquals, []byte(nil)) cmds := s.CommandLog() + for _, cmd := range cmds { + c.Logf("%v", cmd.CmdCode) + } c.Assert(len(cmds) > 1, testutil.IsTrue) cmd := cmds[len(cmds)-2] c.Check(cmd.CmdCode, Equals, tpm2.CommandDictionaryAttackLockReset) diff --git a/tpm2/platform_legacy_test.go b/tpm2/platform_legacy_test.go index 9d9e3b3f..01adcbab 100644 --- a/tpm2/platform_legacy_test.go +++ b/tpm2/platform_legacy_test.go @@ -49,7 +49,7 @@ func (s *platformLegacySuite) SetUpSuite(c *C) { func (s *platformLegacySuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), Equals, ErrTPMProvisioningRequiresLockout) + c.Check(s.TPM().EnsureProvisioned(), Equals, ErrTPMProvisioningRequiresLockout) } var _ = Suite(&platformLegacySuite{}) diff --git a/tpm2/platform_test.go b/tpm2/platform_test.go index b9760269..e93a46b5 100644 --- a/tpm2/platform_test.go +++ b/tpm2/platform_test.go @@ -59,7 +59,7 @@ func (s *platformSuite) SetUpSuite(c *C) { func (s *platformSuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), Equals, ErrTPMProvisioningRequiresLockout) + c.Check(s.TPM().EnsureProvisioned(), Equals, ErrTPMProvisioningRequiresLockout) s.lastEncryptedPayload = nil s.AddCleanup(MockSecbootNewKeyData(func(params *secboot.KeyParams) (*secboot.KeyData, error) { diff --git a/tpm2/provisioning.go b/tpm2/provisioning.go index f9121438..33f45e92 100644 --- a/tpm2/provisioning.go +++ b/tpm2/provisioning.go @@ -20,6 +20,7 @@ package tpm2 import ( + "encoding/json" "errors" "fmt" "os" @@ -195,6 +196,9 @@ func removeStoredSrkTemplate(tpm *tpm2.TPMContext, session tpm2.SessionContext) } type ensureProvisionedParams struct { + lockoutAuthParams *lockoutAuthParams + lockoutAuthParamsErr error + mode provisionMode newLockoutAuthValue []byte srkTemplate *tpm2.Public @@ -203,19 +207,36 @@ type ensureProvisionedParams struct { type EnsureProvisionedOption func(*ensureProvisionedParams) -// ProvisionModeWithoutLockout tells [Connection.EnsureProvisioned] to not perform any actions -// that require use of the TPM's lockout hierarchy. If the TPM indicates that use of the lockout -// hierarchy is required to fully provision the TPM (eg, to disable owner clear, set the lockout -// hierarchy authorization value or configure the DA lockout parameters), then a -// [ErrTPMProvisioningRequiresLockout] error will be returned. In this scenario, -// [Connection.EnsureProvisioned] will complete all operations that can be completed without using -// the lockout hierarchy, but it should be called again without this option. -func ProvisionWithoutLockout() EnsureProvisionedOption { +// WithLockoutAuthValue tells [Connection.EnsureProvisioned] that it can use the TPM's lockout hierarchy +// with the supplied authorization value. This option is for systems that were configured with an older +// version of [Connection.EnsureProvisioned] (XXX: not yet) where an authorization value was chosen and +// supplied by the caller. +// +// If the wrong value is supplied, the lockout hierarchy will become unavailable for the pre-programmed +// recovery time. +func WithLockoutAuthValue(authValue []byte) EnsureProvisionedOption { + return func(p *ensureProvisionedParams) { + if p.lockoutAuthParams != nil || p.lockoutAuthParamsErr != nil { + panic("WithLockoutAuthValue incompatible with WithLockoutAuthData") + } + p.lockoutAuthParams = &lockoutAuthParams{ + AuthValue: authValue, + } + } +} + +// WithLockoutAuthData tells [Connection.EnsureProvisioned] that it can use the TPM's lockout hierarchy +// with the supplied authorization data. The authorization data will have been supplied by a previous call +// to [Connection.EnsureProvisioned] (XXX: in a future PR). +// +// If the data contains the wrong authorization value, the lockout hierarchy will become unavailable for +// the pre-programmed recovery time. +func WithLockoutAuthData(data []byte) EnsureProvisionedOption { return func(p *ensureProvisionedParams) { - if p.mode == provisionModeClear { - panic("ProvisionWithoutLockout conflicts with WithClearBeforeProvision") + if p.lockoutAuthParams != nil { + panic("WithLockoutAuthData incompatible with WithLockoutAuthValue") } - p.mode = provisionModeWithoutLockout + p.lockoutAuthParamsErr = json.Unmarshal(data, &p.lockoutAuthParams) } } @@ -273,21 +294,18 @@ func WithCustomSRKTemplate(template *tpm2.Public) EnsureProvisionedOption { // only way to recover from this is to clear the TPM either by calling this function with the [WithClearBeforeProvision] // option (and providing the correct authorization value for the lockout hierarchy), or by using the physical presence interface. // -// If the [ProvisionWithoutLockout] option is not supplied, then owner clear will be disabled, and the parameters of the TPM's -// dictionary attack logic will be configured to appropriate values. The authorization value for the lockout hierarchy will -// be set to the value supplied to [WithProvisionNewLockoutAuthValue], or the empty value if not supplied. +// If the [WithLockoutAuthValue] or [WithLockoutAuthData] option is supplied, then owner clear will be disabled, and the +// parameters of the TPM's dictionary attack logic will be configured to appropriate values. The authorization value for the +// lockout hierarchy will be set to the value supplied to [WithProvisionNewLockoutAuthValue], or the empty value if not supplied. // -// If the [ProvisionWithoutLockout] option is not supplied, this function performs operations that require the use of the lockout -// hierarchy (detailed above), and knowledge of the lockout hierarchy's authorization value. This must be provided by calling -// Connection.LockoutHandleContext().SetAuthValue() prior to this call. If the wrong lockout hierarchy authorization value is -// provided, then a [AuthFailError] error will be returned. If this happens, the TPM will have entered dictionary attack lockout -// mode for the lockout hierarchy. Further calls will result in a [ErrTPMLockout] error being returned. The only way to recover -// from this is to either wait for the pre-programmed recovery time to expire, or to clear the TPM via the physical presence -// interface by calling [RequestTPMClearUsingPPI]. If the lockout hierarchy authorization value is not known then the -// [ProvisionWithoutLockout] option should be supplied, with the caveat that this mode cannot fully provision the TPM. +// If the [WithLockoutAuthValue] or [WithLockoutAuthData] option is supplied with the wrong value, then a [AuthFailError] error +// may be returned. If this happens, the TPM will have entered dictionary attack lockout mode for the lockout hierarchy. Further +// calls will result in a [ErrTPMLockout] error being returned. The only way to recover from this is to either wait for the +// pre-programmed recovery time to expire, or to clear the TPM via the physical presence interface by calling +// [RequestTPMClearUsingPPI]. // // If [WithClearBeforeProvision] is not supplied, this function will not affect the ability to recover sealed keys that -// can currently be recovered. +// can currently be recovered. If it is supplied, then one of [WithLockoutAuthValue] or [WithLockoutAuthData] must be supplied. func (t *Connection) EnsureProvisioned(options ...EnsureProvisionedOption) error { params := &ensureProvisionedParams{ mode: provisionModeFull, @@ -296,8 +314,36 @@ func (t *Connection) EnsureProvisioned(options ...EnsureProvisionedOption) error for _, opt := range options { opt(params) } - if params.srkTemplate != nil && !params.srkTemplate.IsStorageParent() { + switch { + case params.srkTemplate != nil && !params.srkTemplate.IsStorageParent(): return errors.New("supplied SRK template is not valid for a parent key") + case params.lockoutAuthParamsErr != nil: + return &InvalidLockoutAuthDataError{err: params.lockoutAuthParamsErr} + case params.mode == provisionModeClear && params.lockoutAuthParams == nil: + return errors.New("WithClearBeforeProvision requires WithLockoutAuthParams or WithLockoutAuthData") + case params.lockoutAuthParams == nil: + params.mode = provisionModeWithoutLockout + } + + authorizeAndUseLockoutHierarchy := func(command tpm2.CommandCode, fn func(tpm2.SessionContext) error, msg string) error { + session, _, done, err := t.authorizeLockout(params.lockoutAuthParams, command) + if err != nil { + return err + } + defer done() + + switch err := fn(session); { + case isAuthFailError(err, command, 1): + return AuthFailError{tpm2.HandleLockout} + case tpm2.IsTPMWarning(err, tpm2.WarningLockout, command): + return ErrTPMLockout + case tpm2.IsTPMSessionError(err, tpm2.ErrorPolicyFail, command, 1): + return ErrInvalidLockoutAuthPolicy + case err != nil: + return fmt.Errorf("%s: %w", msg, err) + } + + return nil } session := t.HmacSession() @@ -306,20 +352,16 @@ func (t *Connection) EnsureProvisioned(options ...EnsureProvisionedOption) error if err != nil { return fmt.Errorf("cannot fetch permanent properties: %w", err) } + if params.mode == provisionModeClear { if tpm2.PermanentAttributes(val)&tpm2.AttrDisableClear > 0 { return ErrTPMClearRequiresPPI } - // Use HMAC session to authenticate with lockout hierarchy. - if err := t.Clear(t.LockoutHandleContext(), session); err != nil { - switch { - case isAuthFailError(err, tpm2.CommandClear, 1): - return AuthFailError{tpm2.HandleLockout} - case tpm2.IsTPMWarning(err, tpm2.WarningLockout, tpm2.CommandClear): - return ErrTPMLockout - } - return fmt.Errorf("cannot clear the TPM: %w", err) + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandClear, func(session tpm2.SessionContext) error { + return t.Clear(t.LockoutHandleContext(), session) + }, "cannot clear the TPM"); err != nil { + return err } } @@ -396,46 +438,47 @@ func (t *Connection) EnsureProvisioned(options ...EnsureProvisionedOption) error // Perform actions that require the lockout hierarchy authorization. - // Set the DA parameters. Pass the HMAC session here so we don't supply the cleartext auth - // value for the lockout hierarchy. - if err := t.DictionaryAttackParameters(t.LockoutHandleContext(), maxTries, recoveryTime, lockoutRecovery, session); err != nil { - switch { - case isAuthFailError(err, tpm2.CommandDictionaryAttackParameters, 1): - return AuthFailError{tpm2.HandleLockout} - case tpm2.IsTPMWarning(err, tpm2.WarningLockout, tpm2.CommandDictionaryAttackParameters): - return ErrTPMLockout - } - return fmt.Errorf("cannot configure dictionary attack parameters: %w", err) + // Set the DA parameters. + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandDictionaryAttackParameters, func(session tpm2.SessionContext) error { + return t.DictionaryAttackParameters(t.LockoutHandleContext(), maxTries, recoveryTime, lockoutRecovery, session) + }, "cannot configure dictionary attack parameters"); err != nil { + return err } // Clear any lockout if there is one. This has to happen after setting the DA parameters // because we can't clear a lockout if maxTries is 0. - if err := t.DictionaryAttackLockReset(t.LockoutHandleContext(), session); err != nil { - switch { - case isAuthFailError(err, tpm2.CommandDictionaryAttackLockReset, 1): - return AuthFailError{tpm2.HandleLockout} - case tpm2.IsTPMWarning(err, tpm2.WarningLockout, tpm2.CommandDictionaryAttackLockReset): - return ErrTPMLockout - } - return fmt.Errorf("cannot reset dictionary attack protection: %w", err) + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandDictionaryAttackLockReset, func(session tpm2.SessionContext) error { + return t.DictionaryAttackLockReset(t.LockoutHandleContext(), session) + }, "cannot reset dictionary attack protection"); err != nil { + return err } - // Disable owner clear. Pass the HMAC session here so we don't supply the cleartext auth - // value for the lockout hierarchy. - if err := t.ClearControl(t.LockoutHandleContext(), true, session); err != nil { - // Lockout auth failure or lockout mode would have been caught by DictionaryAttackParameters - return fmt.Errorf("cannot disable owner clear: %w", err) + // Disable owner clear. + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandClearControl, func(session tpm2.SessionContext) error { + return t.ClearControl(t.LockoutHandleContext(), true, session) + }, "cannot disable owner clear"); err != nil { + return err } - // Set the lockout hierarchy authorization. Use command parameter encryption here for the new value. - // Note that this only offers protections against passive interposers. // XXX: Clear any policy for the lockout hierarchy first. A future PR will initialize this to something // sensible. - if err := t.SetPrimaryPolicy(t.LockoutHandleContext(), nil, tpm2.HashAlgorithmNull, session); err != nil { - return fmt.Errorf("cannot clear the lockout hierarchy authorization policy: %w", err) + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandSetPrimaryPolicy, func(session tpm2.SessionContext) error { + return t.SetPrimaryPolicy(t.LockoutHandleContext(), nil, tpm2.HashAlgorithmNull, session) + }, "cannot clear the lockout hierarchy authorization policy"); err != nil { + return err } - if err := t.HierarchyChangeAuth(t.LockoutHandleContext(), params.newLockoutAuthValue, session.IncludeAttrs(tpm2.AttrCommandEncrypt)); err != nil { - return xerrors.Errorf("cannot set the lockout hierarchy authorization value: %w", err) + if err := authorizeAndUseLockoutHierarchy(tpm2.CommandHierarchyChangeAuth, func(authSession tpm2.SessionContext) error { + switch { + case authSession.Handle().Type() == tpm2.HandleTypePolicySession: + // We're using policy auth so need to supply the HMAC session as an extra + // session for parameter encryption. + return t.HierarchyChangeAuth(t.LockoutHandleContext(), params.newLockoutAuthValue, authSession, session.IncludeAttrs(tpm2.AttrCommandEncrypt)) + default: + // We're using HMAC auth + return t.HierarchyChangeAuth(t.LockoutHandleContext(), params.newLockoutAuthValue, authSession.IncludeAttrs(tpm2.AttrCommandEncrypt)) + } + }, "cannot set the lockout hierarchy authorization value"); err != nil { + return err } return nil diff --git a/tpm2/provisioning_test.go b/tpm2/provisioning_test.go index 9134a752..ee6207b9 100644 --- a/tpm2/provisioning_test.go +++ b/tpm2/provisioning_test.go @@ -63,6 +63,7 @@ func (m *primaryKeyMixin) validateEK(c *C) { type provisioningSuite struct { tpm2test.TPMTest primaryKeyMixin + lockoutauthSuiteMixin } func (s *provisioningSuite) SetUpSuite(c *C) { @@ -102,7 +103,7 @@ type testProvisionNewTPMData struct { func (s *provisioningSimulatorSuite) testProvisionNewTPM(c *C, data *testProvisionNewTPMData) { origHmacSession := s.TPM().HmacSession() - c.Check(s.TPM().EnsureProvisioned(data.mode.Option(), WithProvisionNewLockoutAuthValue(data.lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(data.mode.Option(data.lockoutAuth), WithProvisionNewLockoutAuthValue(data.lockoutAuth)), IsNil) s.AddCleanup(func() { // github.com/canonical/go-tpm2/testutil cannot restore this because // EnsureProvisioned uses command parameter encryption. We have to do @@ -170,6 +171,90 @@ func (s *provisioningSimulatorSuite) TestProvisionNewTPMDifferentLockoutAuth(c * lockoutAuth: []byte("foo")}) } +func (s *provisioningSuite) TestProvisionWithLockoutAuthValue(c *C) { + origValue := []byte("1234") + newValue := []byte("5678") + s.HierarchyChangeAuth(c, tpm2.HandleLockout, origValue) + + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(origValue), WithProvisionNewLockoutAuthValue(newValue)), IsNil) + s.AddCleanup(func() { + // github.com/canonical/go-tpm2/testutil cannot restore this because + // EnsureProvisioned uses command parameter encryption. We have to do + // this manually else the test fixture fails the test. + s.TPM().LockoutHandleContext().SetAuthValue(newValue) + s.HierarchyChangeAuth(c, tpm2.HandleLockout, nil) + }) + + // Validate the DA parameters + value, err := s.TPM().GetCapabilityTPMProperty(tpm2.PropertyMaxAuthFail) + c.Check(err, IsNil) + c.Check(value, Equals, uint32(32)) + value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyLockoutInterval) + c.Check(err, IsNil) + c.Check(value, Equals, uint32(7200)) + value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyLockoutRecovery) + c.Check(err, IsNil) + c.Check(value, Equals, uint32(86400)) + + // Verify that owner control is disabled, that the lockout hierarchy auth is set, no + // other hierarchy auth is set, and there is no lockout. + value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyPermanent) + c.Check(err, IsNil) + c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrLockoutAuthSet, Equals, tpm2.AttrLockoutAuthSet) + c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrDisableClear, Equals, tpm2.AttrDisableClear) + c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrOwnerAuthSet, Equals, tpm2.PermanentAttributes(0)) + c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrEndorsementAuthSet, Equals, tpm2.PermanentAttributes(0)) + c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrInLockout, Equals, tpm2.PermanentAttributes(0)) + + // Test the lockout hierarchy auth + s.TPM().LockoutHandleContext().SetAuthValue(newValue) + c.Check(s.TPM().DictionaryAttackLockReset(s.TPM().LockoutHandleContext(), nil), IsNil) +} + +// XXX: This is temporarily disabled because EnsureProvisioned clears the policy +//func (s *provisioningSuite) TestProvisionWithLockoutAuthData(c *C) { +// origValue := []byte("1234") +// newValue := []byte("5678") +// +// policyDigest, data := s.makeDefaultLockoutAuthData(c, tpm2.HashAlgorithmSHA256, origValue) +// s.HierarchyChangeAuth(c, tpm2.HandleLockout, origValue) +// c.Assert(s.TPM().SetPrimaryPolicy(s.TPM().LockoutHandleContext(), policyDigest, tpm2.HashAlgorithmSHA256, nil), IsNil) +// +// c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthData(data), WithProvisionNewLockoutAuthValue(newValue)), IsNil) +// s.AddCleanup(func() { +// // github.com/canonical/go-tpm2/testutil cannot restore this because +// // EnsureProvisioned uses command parameter encryption. We have to do +// // this manually else the test fixture fails the test. +// s.TPM().LockoutHandleContext().SetAuthValue(newValue) +// s.HierarchyChangeAuth(c, tpm2.HandleLockout, nil) +// }) +// +// // Validate the DA parameters +// value, err := s.TPM().GetCapabilityTPMProperty(tpm2.PropertyMaxAuthFail) +// c.Check(err, IsNil) +// c.Check(value, Equals, uint32(32)) +// value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyLockoutInterval) +// c.Check(err, IsNil) +// c.Check(value, Equals, uint32(7200)) +// value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyLockoutRecovery) +// c.Check(err, IsNil) +// c.Check(value, Equals, uint32(86400)) +// +// // Verify that owner control is disabled, that the lockout hierarchy auth is set, no +// // other hierarchy auth is set, and there is no lockout. +// value, err = s.TPM().GetCapabilityTPMProperty(tpm2.PropertyPermanent) +// c.Check(err, IsNil) +// c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrLockoutAuthSet, Equals, tpm2.AttrLockoutAuthSet) +// c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrDisableClear, Equals, tpm2.AttrDisableClear) +// c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrOwnerAuthSet, Equals, tpm2.PermanentAttributes(0)) +// c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrEndorsementAuthSet, Equals, tpm2.PermanentAttributes(0)) +// c.Check(tpm2.PermanentAttributes(value)&tpm2.AttrInLockout, Equals, tpm2.PermanentAttributes(0)) +// +// // Test the lockout hierarchy auth +// s.TPM().LockoutHandleContext().SetAuthValue(newValue) +// c.Check(s.TPM().DictionaryAttackLockReset(s.TPM().LockoutHandleContext(), nil), IsNil) +//} + func (s *provisioningSimulatorSuite) TestProvisionTPMInLockout(c *C) { // Trip the DA logic by triggering an auth failure with a DA protected // resource. @@ -196,7 +281,7 @@ func (s *provisioningSimulatorSuite) testProvisionErrorHandling(c *C, mode Provi // else the test fixture fails the test. s.ClearTPMUsingPlatformHierarchy(c) }() - return s.TPM().EnsureProvisioned(mode.Option()) + return s.TPM().EnsureProvisioned(mode.Option(nil)) } func (s *provisioningSuite) testProvisionErrorHandling(c *C, mode ProvisionMode) error { @@ -206,7 +291,7 @@ func (s *provisioningSuite) testProvisionErrorHandling(c *C, mode ProvisionMode) // else the test fixture fails the test. s.ClearTPMUsingPlatformHierarchy(c) }() - return s.TPM().EnsureProvisioned(mode.Option()) + return s.TPM().EnsureProvisioned(mode.Option(nil)) } func (s *provisioningSuite) TestProvisionErrorHandlingClearRequiresPPI(c *C) { @@ -328,11 +413,12 @@ func (s *provisioningSimulatorSuite) TestProvisionErrorHandlingRequiresLockout5( func (s *provisioningSuite) testProvisionRecreateEK(c *C, mode ProvisionMode) { lockoutAuth := []byte("1234") - c.Check(s.TPM().EnsureProvisioned(WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(nil), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) s.AddCleanup(func() { // github.com/canonical/go-tpm2/testutil cannot restore this because // EnsureProvisioned uses command parameter encryption. We have to do // this manually else the test fixture fails the test. + s.TPM().LockoutHandleContext().SetAuthValue(lockoutAuth) s.HierarchyChangeAuth(c, tpm2.HandleLockout, nil) }) @@ -342,7 +428,7 @@ func (s *provisioningSuite) testProvisionRecreateEK(c *C, mode ProvisionMode) { c.Assert(err, IsNil) s.EvictControl(c, tpm2.HandleOwner, ek, ek.Handle()) - c.Check(s.TPM().EnsureProvisioned(mode.Option(), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(mode.Option(lockoutAuth), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) s.validateEK(c) s.validateSRK(c) @@ -364,11 +450,12 @@ func (s *provisioningSuite) TestRecreateEKWithoutLockout(c *C) { func (s *provisioningSuite) testProvisionRecreateSRK(c *C, mode ProvisionMode) { lockoutAuth := []byte("1234") - c.Check(s.TPM().EnsureProvisioned(WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(lockoutAuth), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) s.AddCleanup(func() { // github.com/canonical/go-tpm2/testutil cannot restore this because // EnsureProvisioned uses command parameter encryption. We have to do // this manually else the test fixture fails the test. + s.TPM().LockoutHandleContext().SetAuthValue(lockoutAuth) s.HierarchyChangeAuth(c, tpm2.HandleLockout, nil) }) @@ -377,7 +464,7 @@ func (s *provisioningSuite) testProvisionRecreateSRK(c *C, mode ProvisionMode) { expectedName := srk.Name() s.EvictControl(c, tpm2.HandleOwner, srk, srk.Handle()) - c.Check(s.TPM().EnsureProvisioned(mode.Option(), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(mode.Option(lockoutAuth), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) s.validateEK(c) s.validateSRK(c) @@ -398,8 +485,7 @@ func (s *provisioningSuite) TestProvisionRecreateSRKWithoutLockout(c *C) { func (s *provisioningSuite) TestProvisionWithEndorsementAuth(c *C) { s.HierarchyChangeAuth(c, tpm2.HandleEndorsement, []byte("1234")) - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Check(s.TPM().EnsureProvisioned(), Equals, ErrTPMProvisioningRequiresLockout) s.validateEK(c) s.validateSRK(c) @@ -408,8 +494,7 @@ func (s *provisioningSuite) TestProvisionWithEndorsementAuth(c *C) { func (s *provisioningSuite) TestProvisionWithOwnerAuth(c *C) { s.HierarchyChangeAuth(c, tpm2.HandleOwner, []byte("1234")) - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Check(s.TPM().EnsureProvisioned(), Equals, ErrTPMProvisioningRequiresLockout) s.validateEK(c) s.validateSRK(c) @@ -430,7 +515,7 @@ func (s *provisioningSuite) testProvisionWithCustomSRKTemplate(c *C, mode Provis Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull}, KeyBits: 2048, Exponent: 0}}} - c.Check(s.TPM().EnsureProvisioned(mode.Option(), WithCustomSRKTemplate(&template)), IsNil) + c.Check(s.TPM().EnsureProvisioned(mode.Option(nil), WithCustomSRKTemplate(&template)), IsNil) s.validatePrimaryKeyAgainstTemplate(c, tpm2.HandleOwner, tcg.SRKHandle, &template) @@ -490,11 +575,12 @@ func (s *provisioningSuite) testProvisionDefaultPreservesCustomSRKTemplate(c *C, Exponent: 0}}} lockoutAuth := []byte("1234") - c.Check(s.TPM().EnsureProvisioned(WithProvisionNewLockoutAuthValue(lockoutAuth), WithCustomSRKTemplate(&template)), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(nil), WithProvisionNewLockoutAuthValue(lockoutAuth), WithCustomSRKTemplate(&template)), IsNil) s.AddCleanup(func() { // github.com/canonical/go-tpm2/testutil cannot restore this because // EnsureProvisioned uses command parameter encryption. We have to do // this manually else the test fixture fails the test. + s.TPM().LockoutHandleContext().SetAuthValue(lockoutAuth) s.HierarchyChangeAuth(c, tpm2.HandleLockout, nil) }) @@ -502,7 +588,7 @@ func (s *provisioningSuite) testProvisionDefaultPreservesCustomSRKTemplate(c *C, c.Assert(err, IsNil) s.EvictControl(c, tpm2.HandleOwner, srk, srk.Handle()) - c.Check(s.TPM().EnsureProvisioned(mode.Option(), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) + c.Check(s.TPM().EnsureProvisioned(mode.Option(lockoutAuth), WithProvisionNewLockoutAuthValue(lockoutAuth)), IsNil) s.validatePrimaryKeyAgainstTemplate(c, tpm2.HandleOwner, tcg.SRKHandle, &template) } @@ -530,11 +616,10 @@ func (s *provisioningSuite) TestProvisionDefaultClearRemovesCustomSRKTemplate(c Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull}, KeyBits: 2048, Exponent: 0}}} - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout(), WithCustomSRKTemplate(&template)), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Check(s.TPM().EnsureProvisioned(WithCustomSRKTemplate(&template)), Equals, ErrTPMProvisioningRequiresLockout) s.validatePrimaryKeyAgainstTemplate(c, tpm2.HandleOwner, tcg.SRKHandle, &template) - c.Check(s.TPM().EnsureProvisioned(WithClearBeforeProvision()), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(nil), WithClearBeforeProvision()), IsNil) s.validateSRK(c) } @@ -553,7 +638,7 @@ func (s *provisioningSuite) TestProvisionWithCustomSRKTemplateOverwritesExisting Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull}, KeyBits: 2048, Exponent: 0}}} - c.Check(s.TPM().EnsureProvisioned(WithCustomSRKTemplate(&template1)), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithCustomSRKTemplate(&template1)), Equals, ErrTPMProvisioningRequiresLockout) s.validatePrimaryKeyAgainstTemplate(c, tpm2.HandleOwner, tcg.SRKHandle, &template1) template2 := tpm2.Public{ @@ -570,7 +655,7 @@ func (s *provisioningSuite) TestProvisionWithCustomSRKTemplateOverwritesExisting Scheme: tpm2.RSAScheme{Scheme: tpm2.RSASchemeNull}, KeyBits: 2048, Exponent: 0}}} - c.Check(s.TPM().EnsureProvisioned(WithCustomSRKTemplate(&template2)), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithCustomSRKTemplate(&template2)), Equals, ErrTPMProvisioningRequiresLockout) s.validatePrimaryKeyAgainstTemplate(c, tpm2.HandleOwner, tcg.SRKHandle, &template2) nv, err := s.TPM().NewResourceContext(0x01810001) diff --git a/tpm2/seal_legacy_test.go b/tpm2/seal_legacy_test.go index d4489a0e..003164e1 100644 --- a/tpm2/seal_legacy_test.go +++ b/tpm2/seal_legacy_test.go @@ -56,8 +56,7 @@ func (s *sealLegacySuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) s.primaryKeyMixin.tpmTest = &s.TPMTest.TPMTest - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) } var _ = Suite(&sealLegacySuite{}) diff --git a/tpm2/seal_test.go b/tpm2/seal_test.go index 8555fa38..9cbb203b 100644 --- a/tpm2/seal_test.go +++ b/tpm2/seal_test.go @@ -83,8 +83,7 @@ func (s *sealSuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) s.primaryKeyMixin.tpmTest = &s.TPMTest.TPMTest - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) origKdf := secboot.SetArgon2KDF(&testutil.MockArgon2KDF{}) s.AddCleanup(func() { secboot.SetArgon2KDF(origKdf) }) } diff --git a/tpm2/tpm_test.go b/tpm2/tpm_test.go index b19c08b6..5be75687 100644 --- a/tpm2/tpm_test.go +++ b/tpm2/tpm_test.go @@ -104,8 +104,9 @@ func (s *tpmSuitePlatform) TestConnectionLockoutAuthSet(c *C) { c.Check(s.TPM().LockoutAuthSet(), testutil.IsFalse) // FullProvising of the TPM puts it in DA lockout mode - c.Check(s.TPM().EnsureProvisioned(WithProvisionNewLockoutAuthValue([]byte("1234"))), IsNil) + c.Check(s.TPM().EnsureProvisioned(WithLockoutAuthValue(nil), WithProvisionNewLockoutAuthValue([]byte("1234"))), IsNil) s.AddCleanup(func() { + s.TPM().LockoutHandleContext().SetAuthValue([]byte("1234")) c.Check(s.TPM().HierarchyChangeAuth(s.TPM().LockoutHandleContext(), nil, nil), IsNil) }) c.Check(s.TPM().LockoutAuthSet(), testutil.IsTrue) @@ -139,8 +140,7 @@ func (s *tpmSuiteSimulator) TestConnectToDefaultTPMUnprovisioned(c *C) { } func (s *tpmSuite) TestConnectToDefaultTPMProvisioned(c *C) { - c.Check(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Check(s.TPM().EnsureProvisioned(), Equals, ErrTPMProvisioningRequiresLockout) s.AddCleanup(s.CloseMockConnection(c)) s.testConnectToDefaultTPM(c, true) } diff --git a/tpm2/unseal_legacy_test.go b/tpm2/unseal_legacy_test.go index 6723600d..9877fd0a 100644 --- a/tpm2/unseal_legacy_test.go +++ b/tpm2/unseal_legacy_test.go @@ -48,8 +48,7 @@ func (s *unsealSuite) SetUpSuite(c *C) { func (s *unsealSuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) } var _ = Suite(&unsealSuite{}) diff --git a/tpm2/update_legacy_test.go b/tpm2/update_legacy_test.go index 3e31d59b..a8f1aea8 100644 --- a/tpm2/update_legacy_test.go +++ b/tpm2/update_legacy_test.go @@ -53,8 +53,7 @@ func (s *updateLegacySuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) s.primaryKeyMixin.tpmTest = &s.TPMTest.TPMTest - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) } var _ = Suite(&updateLegacySuite{}) diff --git a/tpm2/update_test.go b/tpm2/update_test.go index 1f08551f..361d9a1a 100644 --- a/tpm2/update_test.go +++ b/tpm2/update_test.go @@ -49,8 +49,7 @@ func (s *updateSuite) SetUpTest(c *C) { s.TPMTest.SetUpTest(c) s.primaryKeyMixin.tpmTest = &s.TPMTest.TPMTest - c.Assert(s.TPM().EnsureProvisioned(ProvisionWithoutLockout()), - testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) + c.Assert(s.TPM().EnsureProvisioned(), testutil.InSlice(Equals), []error{ErrTPMProvisioningRequiresLockout, nil}) } var _ = Suite(&updateSuite{})