Chromium Code Reviews| 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); |
| +} |