Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3632)

Unified Diff: chrome/browser/chromeos/login/quick_unlock/pin_backend.cc

Issue 2809993004: cros: Implement cryptohome backend for pin.
Patch Set: Rebase, remove some extraneous LOG statements Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/login/quick_unlock/pin_backend.cc
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc b/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e9365fa6e37022ed4f12c2a0ca0b94cacf5f21b3
--- /dev/null
+++ b/chrome/browser/chromeos/login/quick_unlock/pin_backend.cc
@@ -0,0 +1,261 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
+#include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "chromeos/cryptohome/homedir_methods.h"
+#include "chromeos/cryptohome/system_salt_getter.h"
+#include "chromeos/login/auth/user_context.h"
+#include "components/signin/core/account_id/account_id.h"
+
+namespace chromeos {
+namespace quick_unlock {
+
+namespace {
+
+constexpr const char* kCryptohomePinLabel = "pin_label";
+
+void DoNothingCryptohome(bool success, cryptohome::MountError return_code) {}
+
+class CryptohomeBackend {
+ public:
+ CryptohomeBackend();
+ ~CryptohomeBackend();
+
+ void EnsurePinIsNotInCryptohome(const AccountId& account_id,
+ const UserContext& user_context) const;
+ void IsPinSetInCryptohome(const AccountId& account_id,
+ const PinBackend::BoolCallback& result) const;
+ void SetPin(const UserContext& user_context, const std::string& pin);
+ void RemovePin(const UserContext& user_context);
+ bool NeedsStrongAuth() const;
+
+ private:
+ void OnSystemSaltObtained(const std::string& system_salt);
+ // Called when we add or remove a key from cryptohome.
+ void OnCryptohomeKeyChange(bool success, cryptohome::MountError return_code);
+
+ void SetIsPinSet(bool is_set);
+
+ private:
+ bool salt_obtained_ = false;
+ std::string system_salt_;
+ std::vector<base::Closure> system_salt_callbacks_;
+
+ base::WeakPtrFactory<CryptohomeBackend> weak_factory_;
achuithb 2017/05/13 01:01:58 You can do weak_factory_{this}; https://cs.chromiu
jdufault 2017/06/06 18:17:06 Done - that's a nice pattern.
+
+ DISALLOW_COPY_AND_ASSIGN(CryptohomeBackend);
+};
+
+CryptohomeBackend::CryptohomeBackend() : weak_factory_(this) {
+ SystemSaltGetter::Get()->GetSystemSalt(base::Bind(
+ &CryptohomeBackend::OnSystemSaltObtained, weak_factory_.GetWeakPtr()));
+}
+
+CryptohomeBackend::~CryptohomeBackend() {}
+
+void CryptohomeBackend::IsPinSetInCryptohome(
+ const AccountId& account_id,
+ const PinBackend::BoolCallback& callback) const {
+ auto on_key_data =
achuithb 2017/05/13 01:01:58 Let's maybe pull this out into a named callback? I
jdufault 2017/06/06 18:17:06 Done.
+ [](const PinBackend::BoolCallback& callback, bool success,
+ cryptohome::MountError return_code,
+ const std::vector<cryptohome::KeyDefinition>& key_definitions) {
+
+ for (const cryptohome::KeyDefinition& definition : key_definitions) {
+ if (definition.label == kCryptohomePinLabel) {
+ callback.Run(true);
+ return;
+ }
+ }
+
+ callback.Run(false);
+ };
+
+ cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx(
+ cryptohome::Identification(account_id), kCryptohomePinLabel,
+ base::Bind(on_key_data, callback));
+}
+
+void CryptohomeBackend::SetPin(const UserContext& user_context,
+ const std::string& pin) {
+ // Rerun this method only after we have system salt.
+ if (!salt_obtained_) {
+ system_salt_callbacks_.push_back(base::Bind(&CryptohomeBackend::SetPin,
+ weak_factory_.GetWeakPtr(),
+ user_context, pin));
+ return;
+ }
+
+ const std::string pin_secret = PinBackend::ComputeSecret(pin, system_salt_);
+
+ cryptohome::Identification id(user_context.GetAccountId());
achuithb 2017/05/13 01:01:58 nit all const
jdufault 2017/06/06 18:17:06 Done.
+ cryptohome::Authorization auth(user_context.GetKey()->GetSecret(), "");
+ cryptohome::KeyDefinition key_def(pin_secret, kCryptohomePinLabel,
+ cryptohome::PRIV_DEFAULT);
+ cryptohome::HomedirMethods::GetInstance()->AddKeyEx(
+ id, auth, key_def, true /*replace_existing*/,
+ base::Bind(&DoNothingCryptohome));
+}
+
+void CryptohomeBackend::OnSystemSaltObtained(const std::string& system_salt) {
+ salt_obtained_ = true;
+ system_salt_ = system_salt;
+ for (std::vector<base::Closure>::const_iterator it =
achuithb 2017/05/13 01:01:58 Does range-based loop not work here?
jdufault 2017/06/06 18:17:06 Done - not sure why I wasn't using it.
+ system_salt_callbacks_.begin();
+ it != system_salt_callbacks_.end(); ++it) {
+ it->Run();
+ }
+ system_salt_callbacks_.clear();
+}
+
+void CryptohomeBackend::RemovePin(const UserContext& user_context) {
+ // Rerun this method only after we have system salt.
+ if (!salt_obtained_) {
+ system_salt_callbacks_.push_back(base::Bind(&CryptohomeBackend::RemovePin,
+ weak_factory_.GetWeakPtr(),
+ user_context));
+ return;
+ }
+
+ // Remove any PIN data from cryptohome.
+ cryptohome::Identification id(user_context.GetAccountId());
achuithb 2017/05/13 01:01:58 nit const
jdufault 2017/06/06 18:17:06 Done.
+ cryptohome::Authorization auth(user_context.GetKey()->GetSecret(), "");
+ cryptohome::HomedirMethods::GetInstance()->RemoveKeyEx(
+ id, auth, kCryptohomePinLabel, base::Bind(&DoNothingCryptohome));
+}
+
+CryptohomeBackend* g_cryptohome_backend_ = nullptr;
+
+CryptohomeBackend* GetCryptohomeBackend() {
+ if (!g_cryptohome_backend_)
+ g_cryptohome_backend_ = new CryptohomeBackend();
+ return g_cryptohome_backend_;
achuithb 2017/05/13 01:01:58 it's ok to leak this? Isn't there a singleton cl
jdufault 2017/06/06 18:17:06 I tried converting to a leaky base::Singleton inst
+}
+
+QuickUnlockStorage* GetPrefsBackend(const AccountId& account_id) {
+ return QuickUnlockFactory::GetForAccountId(account_id);
+}
+
+void SendResponse(const PinBackend::BoolCallback& result, bool value) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(result, value));
+}
+
+} // namespace
+
+// static
+void PinBackend::IsSet(const AccountId& account_id,
+ const BoolCallback& result) {
+ if (GetPinStorageType() == PinStorageType::kCryptohome) {
+ GetCryptohomeBackend()->IsPinSetInCryptohome(account_id, result);
+ } else {
+ QuickUnlockStorage* storage = GetPrefsBackend(account_id);
+ DCHECK(storage);
+ SendResponse(result, storage->pin_storage_prefs()->IsPinSet());
+ }
+}
+
+// static
+void PinBackend::Set(const UserContext& user_context, const std::string& pin) {
+ QuickUnlockStorage* storage = GetPrefsBackend(user_context.GetAccountId());
+ DCHECK(storage);
+
+ // Make sure to remove the other storage pin.
+ if (GetPinStorageType() == PinStorageType::kCryptohome) {
+ GetCryptohomeBackend()->SetPin(user_context, pin);
+ storage->pin_storage_prefs()->RemovePin();
+ } else {
+ storage->pin_storage_prefs()->SetPin(pin);
+ GetCryptohomeBackend()->RemovePin(user_context);
+ }
+}
+
+// static
+void PinBackend::Remove(const UserContext& user_context) {
+ GetCryptohomeBackend()->RemovePin(user_context);
+
+ QuickUnlockStorage* storage = GetPrefsBackend(user_context.GetAccountId());
+ DCHECK(storage);
+ storage->pin_storage_prefs()->RemovePin();
+}
+
+// static
+void PinBackend::CanAuthenticate(const AccountId& account_id,
+ const BoolCallback& result) {
+ if (GetPinStorageType() == PinStorageType::kCryptohome) {
+ GetCryptohomeBackend()->IsPinSetInCryptohome(account_id, result);
+ } else {
+ QuickUnlockStorage* storage = GetPrefsBackend(account_id);
+ if (!storage) {
+ SendResponse(result, false);
+ } else {
+ SendResponse(
+ result,
+ storage->HasStrongAuth() &&
+ storage->pin_storage_prefs()->IsPinAuthenticationAvailable());
+ }
+ }
+}
+
+// static
+void PinBackend::TryAuthenticate(const AccountId& account_id,
+ const std::string& pin,
+ const BoolCallback& result) {
+ if (GetPinStorageType() == PinStorageType::kCryptohome) {
+ // TODO(jdufalt): Refactor login auth such that typing a user password does
+ // not run crypthome check with wildcard key label. That means we will be
+ // forced to run an authentication check here.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(result, false));
+ } else {
+ QuickUnlockStorage* storage = GetPrefsBackend(account_id);
+ DCHECK(storage);
+
+ if (!storage->HasStrongAuth()) {
+ SendResponse(result, false);
+ } else {
+ SendResponse(result,
+ storage->pin_storage_prefs()->TryAuthenticatePin(pin));
+ }
+ }
+}
+
+// static
+void PinBackend::NotifyAuthentication(const AccountId& account_id) {
+ // Nothing to do for cryptohome backend.
+
+ if (GetPinStorageType() == PinStorageType::kPrefs) {
+ QuickUnlockStorage* storage = GetPrefsBackend(account_id);
+ if (!storage)
+ return;
+
+ storage->pin_storage_prefs()->ResetUnlockAttemptCount();
+ }
+}
+
+// static
+std::string PinBackend::ComputeSecret(const std::string& pin,
+ const std::string& salt) {
+ Key key(pin);
+ key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, salt);
+ return key.GetSecret();
+}
+
+// static
+void PinBackend::ResetForTesting() {
+ if (g_cryptohome_backend_)
+ delete g_cryptohome_backend_;
+ g_cryptohome_backend_ = nullptr;
+}
+
+} // namespace quick_unlock
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698