Index: chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc |
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cd3ed9be54161163d99223a43580f45ab7a59426 |
--- /dev/null |
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc |
@@ -0,0 +1,307 @@ |
+// Copyright 2014 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/easy_unlock/easy_unlock_tpm_key_manager.h" |
+ |
+#include "base/base64.h" |
+#include "base/bind.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/prefs/pref_registry_simple.h" |
+#include "base/prefs/pref_service.h" |
+#include "base/prefs/scoped_user_pref_update.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/threading/worker_pool.h" |
+#include "base/time/time.h" |
+#include "base/values.h" |
+#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/chromeos/platform_keys/platform_keys.h" |
+#include "chrome/common/pref_names.h" |
+#include "components/pref_registry/pref_registry_syncable.h" |
+#include "crypto/nss_util_internal.h" |
+#include "crypto/rsa_private_key.h" |
+#include "crypto/scoped_nss_types.h" |
+ |
+namespace { |
+ |
+// The modulus length for RSA keys used by easy sign-in. |
+const int kKeyModulusLength = 2048; |
+ |
+// Checks if a private RSA key associated with |public_key| can be found in |
+// |slot|. |
+// Must be called on a worker thread. |
+bool PrivateKeyPresentOnWorkerThread(PK11SlotInfo* slot, |
+ const std::string& public_key) { |
+ const uint8* public_key_uint8 = |
+ reinterpret_cast<const uint8*>(public_key.data()); |
+ std::vector<uint8> public_key_vector( |
+ public_key_uint8, public_key_uint8 + public_key.size()); |
+ |
+ scoped_ptr<crypto::RSAPrivateKey> rsa_key( |
+ crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector)); |
+ return rsa_key && rsa_key->key()->pkcs11Slot == slot; |
+} |
+ |
+// Creates a RSA key pair in |slot|. When done, it runs |callback| with the |
+// created public key on |response_task_runner|. |
+// If |public_key| is not empty, a key pair will be created only if the private |
+// key associated with |public_key| does not exist in |slot|. Otherwise the |
+// callback will be run with |public_key|. |
+void CreateTpmKeyPairOnWorkerThread( |
+ crypto::ScopedPK11Slot slot, |
+ const std::string& public_key, |
+ const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner, |
+ const base::Callback<void(const std::string&)>& callback) { |
+ if (!public_key.empty() && |
+ PrivateKeyPresentOnWorkerThread(slot.get(), public_key)) { |
+ response_task_runner->PostTask(FROM_HERE, base::Bind(callback, public_key)); |
+ return; |
+ } |
+ |
+ scoped_ptr<crypto::RSAPrivateKey> rsa_key( |
+ crypto::RSAPrivateKey::CreateSensitive(slot.get(), kKeyModulusLength)); |
+ if (!rsa_key) { |
+ LOG(ERROR) << "Failed to create an RSA key."; |
+ response_task_runner->PostTask(FROM_HERE, |
+ base::Bind(callback, std::string())); |
+ return; |
+ } |
+ |
+ std::vector<uint8> created_public_key; |
+ if (!rsa_key->ExportPublicKey(&created_public_key)) { |
+ LOG(ERROR) << "Failed to export public key."; |
+ response_task_runner->PostTask(FROM_HERE, |
+ base::Bind(callback, std::string())); |
+ return; |
+ } |
+ |
+ response_task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind(callback, |
+ std::string(created_public_key.begin(), |
+ created_public_key.end()))); |
+} |
+ |
+} // namespace |
+ |
+// static |
+void EasyUnlockTpmKeyManager::RegisterProfilePrefs( |
+ user_prefs::PrefRegistrySyncable* registry) { |
+ registry->RegisterStringPref( |
+ prefs::kEasyUnlockUserTpmKey, |
+ std::string(), |
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
+} |
+ |
+// static |
+void EasyUnlockTpmKeyManager::RegisterLocalStatePrefs( |
+ PrefRegistrySimple* registry) { |
+ registry->RegisterDictionaryPref(prefs::kEasyUnlockLocalStateTpmKeys); |
+} |
+ |
+// static |
+void EasyUnlockTpmKeyManager::ResetLocalStateForUser( |
+ const std::string& user_id) { |
+ if (!g_browser_process) |
+ return; |
+ PrefService* local_state = g_browser_process->local_state(); |
+ if (!local_state) |
+ return; |
+ |
+ DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockLocalStateTpmKeys); |
+ update->RemoveWithoutPathExpansion(user_id, NULL); |
+} |
+ |
+EasyUnlockTpmKeyManager::EasyUnlockTpmKeyManager( |
+ const std::string& user_id, |
+ content::BrowserContext* context, |
+ PrefService* user_prefs, |
+ PrefService* local_state) |
+ : user_id_(user_id), |
+ browser_context_(context), |
+ user_prefs_(user_prefs), |
+ local_state_(local_state), |
+ creating_tpm_key_pair_(false), |
+ got_tpm_slot_(false), |
+ get_tpm_slot_weak_ptr_factory_(this), |
+ weak_ptr_factory_(this) { |
+ CHECK_EQ(!user_prefs, user_id.empty()); |
+} |
+ |
+EasyUnlockTpmKeyManager::~EasyUnlockTpmKeyManager() { |
+} |
+ |
+bool EasyUnlockTpmKeyManager::IsTpmKeyPresent( |
+ const std::string& user_id, |
+ bool check_private_key, |
+ const base::Closure& callback) { |
+ CHECK(user_prefs_); |
+ CHECK_EQ(user_id_, user_id); |
+ |
+ std::string key = GetKeyFromUserPrefs(); |
+ if (!check_private_key && !creating_tpm_key_pair_ && !key.empty()) { |
+ SetKeyInLocalState(user_id, key); |
+ return true; |
+ } |
+ |
+ tpm_key_present_callbacks_.push_back(callback); |
+ if (!creating_tpm_key_pair_) { |
+ creating_tpm_key_pair_ = true; |
+ |
+ base::Callback<void(crypto::ScopedPK11Slot)> create_key_with_system_slot = |
+ base::Bind(&EasyUnlockTpmKeyManager::CreateKeyInSystemSlot, |
+ get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), |
+ user_id, |
+ key); |
+ crypto::ScopedPK11Slot system_slot = crypto::GetSystemNSSKeySlot( |
+ create_key_with_system_slot); |
+ if (system_slot) |
+ create_key_with_system_slot.Run(system_slot.Pass()); |
+ } |
+ |
+ return false; |
+} |
+ |
+bool EasyUnlockTpmKeyManager::SetGetSystemSlotTimeoutMs(size_t timeout_ms) { |
+ if (got_tpm_slot_) |
+ return false; |
+ |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated, |
+ get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), |
+ user_id_, |
+ std::string()), |
+ base::TimeDelta::FromMilliseconds(timeout_ms)); |
+ return true; |
+} |
+ |
+std::string EasyUnlockTpmKeyManager::GetPublicTpmKey( |
+ const std::string& user_id) { |
+ if (user_prefs_) { |
+ CHECK_EQ(user_id_, user_id); |
+ return GetKeyFromUserPrefs(); |
+ } |
+ return GetKeyFromLocalState(user_id); |
+} |
+ |
+void EasyUnlockTpmKeyManager::SignUsingTpmKey( |
+ const std::string& user_id, |
+ const std::string& data, |
+ const base::Callback<void(const std::string& data)> callback) { |
+ std::string key = GetPublicTpmKey(user_id); |
+ if (key.empty()) { |
+ callback.Run(std::string()); |
+ return; |
+ } |
+ |
+ chromeos::platform_keys::subtle::Sign( |
+ chromeos::platform_keys::kTokenIdSystem, |
+ key, |
+ chromeos::platform_keys::HASH_ALGORITHM_SHA256, |
+ data, |
+ base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ callback), |
+ browser_context_); |
+} |
+ |
+std::string EasyUnlockTpmKeyManager::GetKeyFromUserPrefs() { |
+ if (!user_prefs_) |
+ return std::string(); |
+ std::string key = user_prefs_->GetString(prefs::kEasyUnlockUserTpmKey); |
+ std::string decoded; |
+ base::Base64Decode(key, &decoded); |
+ return decoded; |
+} |
+ |
+std::string EasyUnlockTpmKeyManager::GetKeyFromLocalState( |
+ const std::string& user_id) { |
+ if (!local_state_) |
+ return std::string(); |
+ const base::DictionaryValue* dict = |
+ local_state_->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys); |
+ std::string key; |
+ if (dict) |
+ dict->GetStringWithoutPathExpansion(user_id, &key); |
+ std::string decoded; |
+ base::Base64Decode(key, &decoded); |
+ return decoded; |
+} |
+ |
+void EasyUnlockTpmKeyManager::SetKeyInLocalState(const std::string& user_id, |
+ const std::string& value) { |
+ if (!local_state_) |
+ return; |
+ |
+ std::string encoded; |
+ base::Base64Encode(value, &encoded); |
+ DictionaryPrefUpdate update(local_state_, |
+ prefs::kEasyUnlockLocalStateTpmKeys); |
+ update->SetStringWithoutPathExpansion(user_id, encoded); |
+} |
+ |
+void EasyUnlockTpmKeyManager::SetKeyInUserPrefs(const std::string& value) { |
+ if (!user_prefs_) |
+ return; |
+ |
+ std::string encoded; |
+ base::Base64Encode(value, &encoded); |
+ user_prefs_->SetString(prefs::kEasyUnlockUserTpmKey, encoded); |
+} |
+ |
+void EasyUnlockTpmKeyManager::CreateKeyInSystemSlot( |
+ const std::string& user_id, |
+ const std::string& public_key, |
+ crypto::ScopedPK11Slot system_slot) { |
+ CHECK(system_slot); |
+ |
+ got_tpm_slot_ = true; |
+ get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs(); |
+ |
+ base::WorkerPool::PostTask( |
+ FROM_HERE, |
+ base::Bind(&CreateTpmKeyPairOnWorkerThread, |
+ base::Passed(&system_slot), |
+ public_key, |
+ base::ThreadTaskRunnerHandle::Get(), |
+ base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ user_id)), |
+ true /* long task */); |
+} |
+ |
+void EasyUnlockTpmKeyManager::OnTpmKeyCreated( |
+ const std::string& user_id, |
+ const std::string& public_key) { |
+ creating_tpm_key_pair_ = false; |
+ |
+ get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs(); |
+ |
+ if (!public_key.empty()) { |
+ SetKeyInUserPrefs(public_key); |
+ SetKeyInLocalState(user_id, public_key); |
xiyuan
2014/12/02 23:15:58
Since the private key is protected in TPM, can we
tbarzic
2014/12/03 19:10:28
yeah, sounds reasonable.. Done.
|
+ } |
+ |
+ for (size_t i = 0; i < tpm_key_present_callbacks_.size(); ++i) { |
+ if (!tpm_key_present_callbacks_[i].is_null()) |
+ tpm_key_present_callbacks_[i].Run(); |
+ } |
+ |
+ tpm_key_present_callbacks_.clear(); |
+} |
+ |
+void EasyUnlockTpmKeyManager::OnDataSigned( |
+ const base::Callback<void(const std::string&)>& callback, |
+ const std::string& signature, |
+ const std::string& error_message) { |
+ if (!error_message.empty()) { |
+ callback.Run(std::string()); |
+ return; |
+ } |
+ |
+ callback.Run(signature); |
+} |