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

Unified Diff: chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc

Issue 729803002: Easy Sign-in: Use TPM RSA key to sign nonce in sign-in protocol (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 6 years 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/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..570bef8c2a6b3e7552f49630c349adb6c83e1020
--- /dev/null
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.cc
@@ -0,0 +1,355 @@
+// 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 <cryptohi.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/common/pref_names.h"
+#include "content/public/browser/browser_thread.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;
+
+// Relays |GetSystemSlotOnIOThread| callback to |response_task_runner|.
+void RunCallbackOnThreadRunner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback,
+ crypto::ScopedPK11Slot slot) {
+ response_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, base::Passed(&slot)));
+}
+
+// Gets TPM system slot. Must be called on IO thread.
+// The callback wil be relayed to |response_task_runner|.
+void GetSystemSlotOnIOThread(
+ const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
+ const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
+ base::Callback<void(crypto::ScopedPK11Slot)> callback_on_origin_thread =
+ base::Bind(&RunCallbackOnThreadRunner, response_task_runner, callback);
+
+ crypto::ScopedPK11Slot system_slot =
+ crypto::GetSystemNSSKeySlot(callback_on_origin_thread);
+ if (system_slot)
+ callback_on_origin_thread.Run(system_slot.Pass());
+}
+
+// Checks if a private RSA key associated with |public_key| can be found in
+// |slot|.
+// Must be called on a worker thread.
+scoped_ptr<crypto::RSAPrivateKey> GetPrivateKeyOnWorkerThread(
+ 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));
+ if (!rsa_key || rsa_key->key()->pkcs11Slot != slot)
+ return scoped_ptr<crypto::RSAPrivateKey>();
+ return rsa_key.Pass();
+}
+
+// Signs |data| using a private key associated with |public_key| and stored in
+// |slot|. Once the data is signed, callback is run on |response_task_runner|.
+// In case of an error, the callback will be passed an empty string.
+void SignDataOnWorkerThread(
+ crypto::ScopedPK11Slot slot,
+ const std::string& public_key,
+ const std::string& data,
+ const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
+ const base::Callback<void(const std::string&)>& callback) {
+ scoped_ptr<crypto::RSAPrivateKey> private_key(
+ GetPrivateKeyOnWorkerThread(slot.get(), public_key));
+ if (!private_key) {
+ LOG(ERROR) << "Private key for signing data not found";
+ response_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, std::string()));
+ return;
+ }
+
+ SECItem sign_result = {siBuffer, NULL, 0};
+ if (SEC_SignData(&sign_result,
+ reinterpret_cast<const unsigned char*>(data.data()),
+ data.size(),
+ private_key->key(),
+ SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION) != SECSuccess) {
+ LOG(ERROR) << "Failed to sign data";
+ response_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, std::string()));
+ return;
+ }
+
+ std::string signature(reinterpret_cast<const char*>(sign_result.data),
+ sign_result.len);
+ response_task_runner->PostTask(FROM_HERE, base::Bind(callback, signature));
+}
+
+// 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() &&
+ GetPrivateKeyOnWorkerThread(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::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,
+ PrefService* local_state)
+ : user_id_(user_id),
+ local_state_(local_state),
+ create_tpm_key_state_(CREATE_TPM_KEY_NOT_STARTED),
+ get_tpm_slot_weak_ptr_factory_(this),
+ weak_ptr_factory_(this) {
+}
+
+EasyUnlockTpmKeyManager::~EasyUnlockTpmKeyManager() {
+}
+
+bool EasyUnlockTpmKeyManager::PrepareTpmKey(
+ bool check_private_key,
+ const base::Closure& callback) {
+ CHECK(!user_id_.empty());
+
+ if (create_tpm_key_state_ == CREATE_TPM_KEY_DONE)
+ return true;
+
+ std::string key = GetPublicTpmKey(user_id_);
+ if (!check_private_key && !key.empty() &&
+ create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED) {
+ return true;
+ }
+
+ prepare_tpm_key_callbacks_.push_back(callback);
+
+ if (create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED) {
+ create_tpm_key_state_ = CREATE_TPM_KEY_WAITING_FOR_SYSTEM_SLOT;
+
+ base::Callback<void(crypto::ScopedPK11Slot)> create_key_with_system_slot =
+ base::Bind(&EasyUnlockTpmKeyManager::CreateKeyInSystemSlot,
+ get_tpm_slot_weak_ptr_factory_.GetWeakPtr(),
+ key);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GetSystemSlotOnIOThread,
+ base::ThreadTaskRunnerHandle::Get(),
+ create_key_with_system_slot));
+ }
+
+ return false;
+}
+
+bool EasyUnlockTpmKeyManager::StartGetSystemSlotTimeoutMs(size_t timeout_ms) {
+ if (create_tpm_key_state_ == CREATE_TPM_KEY_DONE ||
+ create_tpm_key_state_ == CREATE_TPM_KEY_GOT_SYSTEM_SLOT) {
+ return false;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated,
+ get_tpm_slot_weak_ptr_factory_.GetWeakPtr(),
+ std::string()),
+ base::TimeDelta::FromMilliseconds(timeout_ms));
+ return true;
+}
+
+std::string EasyUnlockTpmKeyManager::GetPublicTpmKey(
+ 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::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;
+ }
+
+ base::Callback<void(crypto::ScopedPK11Slot)> sign_with_system_slot =
+ base::Bind(&EasyUnlockTpmKeyManager::SignDataWithSystemSlot,
+ weak_ptr_factory_.GetWeakPtr(),
+ key, data, callback);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GetSystemSlotOnIOThread,
+ base::ThreadTaskRunnerHandle::Get(),
+ sign_with_system_slot));
+}
+
+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::CreateKeyInSystemSlot(
+ const std::string& public_key,
+ crypto::ScopedPK11Slot system_slot) {
+ CHECK(system_slot);
+
+ create_tpm_key_state_ = CREATE_TPM_KEY_GOT_SYSTEM_SLOT;
+
+ // If there are any delayed tasks posted using |StartGetSystemSlotTimeoutMs|,
+ // this will cancel them.
+ // Note that this would cancel other pending |CreateKeyInSystemSlot| tasks,
+ // but there should be at most one such task at a time.
+ 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())),
+ true /* long task */);
+}
+
+void EasyUnlockTpmKeyManager::SignDataWithSystemSlot(
+ const std::string& public_key,
+ const std::string& data,
+ const base::Callback<void(const std::string& data)> callback,
+ crypto::ScopedPK11Slot system_slot) {
+ CHECK(system_slot);
+
+ base::WorkerPool::PostTask(
+ FROM_HERE,
+ base::Bind(&SignDataOnWorkerThread,
+ base::Passed(&system_slot),
+ public_key,
+ data,
+ base::ThreadTaskRunnerHandle::Get(),
+ base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback)),
+ true /* long task */);
+}
+
+void EasyUnlockTpmKeyManager::OnTpmKeyCreated(const std::string& public_key) {
+ // |OnTpmKeyCreated| is called by a timeout task posted by
+ // |StartGetSystemSlotTimeoutMs|. Invalidating the factory will have
+ // an effect of canceling any pending |GetSystemSlotOnIOThread| callbacks,
+ // as well as other pending timeouts.
+ // Note that in the case |OnTpmKeyCreated| was called as a result of
+ // |CreateKeyInSystemSlot|, this should have no effect as no weak ptrs from
+ // this factory should be in use in this case.
+ get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs();
+
+ if (!public_key.empty())
+ SetKeyInLocalState(user_id_, public_key);
+
+ for (size_t i = 0; i < prepare_tpm_key_callbacks_.size(); ++i) {
+ if (!prepare_tpm_key_callbacks_[i].is_null())
+ prepare_tpm_key_callbacks_[i].Run();
+ }
+
+ prepare_tpm_key_callbacks_.clear();
+
+ // If key creation failed, reset the state machine.
+ create_tpm_key_state_ =
+ public_key.empty() ? CREATE_TPM_KEY_NOT_STARTED : CREATE_TPM_KEY_DONE;
+}
+
+void EasyUnlockTpmKeyManager::OnDataSigned(
+ const base::Callback<void(const std::string&)>& callback,
+ const std::string& signature) {
+ callback.Run(signature);
+}

Powered by Google App Engine
This is Rietveld 408576698