Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / chromeos / login / easy_unlock / easy_unlock_tpm_key_manager.cc
blobcb389ed55e4673aad7b1bad6772e3bd82d9964d5
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.h"
7 #include <cryptohi.h>
8 #include <keyhi.h>
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/prefs/pref_registry_simple.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/prefs/scoped_user_pref_update.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/worker_pool.h"
21 #include "base/time/time.h"
22 #include "base/values.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "crypto/nss_key_util.h"
27 #include "crypto/nss_util_internal.h"
28 #include "crypto/scoped_nss_types.h"
30 namespace {
32 // The modulus length for RSA keys used by easy sign-in.
33 const int kKeyModulusLength = 2048;
35 // Relays |GetSystemSlotOnIOThread| callback to |response_task_runner|.
36 void RunCallbackOnTaskRunner(
37 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
38 const base::Callback<void(crypto::ScopedPK11Slot)>& callback,
39 crypto::ScopedPK11Slot slot) {
40 response_task_runner->PostTask(FROM_HERE,
41 base::Bind(callback, base::Passed(&slot)));
44 // Gets TPM system slot. Must be called on IO thread.
45 // The callback wil be relayed to |response_task_runner|.
46 void GetSystemSlotOnIOThread(
47 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
48 const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
49 base::Callback<void(crypto::ScopedPK11Slot)> callback_on_origin_thread =
50 base::Bind(&RunCallbackOnTaskRunner, response_task_runner, callback);
52 crypto::ScopedPK11Slot system_slot =
53 crypto::GetSystemNSSKeySlot(callback_on_origin_thread);
54 if (system_slot)
55 callback_on_origin_thread.Run(system_slot.Pass());
58 // Relays |EnsureUserTpmInitializedOnIOThread| callback to
59 // |response_task_runner|, ignoring |slot|.
60 void RunCallbackWithoutSlotOnTaskRunner(
61 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
62 const base::Closure& callback,
63 crypto::ScopedPK11Slot slot) {
64 response_task_runner->PostTask(FROM_HERE, callback);
67 void EnsureUserTPMInitializedOnIOThread(
68 const std::string& username_hash,
69 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
70 const base::Closure& callback) {
71 base::Callback<void(crypto::ScopedPK11Slot)> callback_on_origin_thread =
72 base::Bind(&RunCallbackWithoutSlotOnTaskRunner, response_task_runner,
73 callback);
75 crypto::ScopedPK11Slot private_slot = crypto::GetPrivateSlotForChromeOSUser(
76 username_hash, callback_on_origin_thread);
77 if (private_slot)
78 callback_on_origin_thread.Run(private_slot.Pass());
81 // Checks if a private RSA key associated with |public_key| can be found in
82 // |slot|. |slot| must be non-null.
83 // Must be called on a worker thread.
84 crypto::ScopedSECKEYPrivateKey GetPrivateKeyOnWorkerThread(
85 PK11SlotInfo* slot,
86 const std::string& public_key) {
87 CHECK(slot);
89 const uint8* public_key_uint8 =
90 reinterpret_cast<const uint8*>(public_key.data());
91 std::vector<uint8> public_key_vector(
92 public_key_uint8, public_key_uint8 + public_key.size());
94 crypto::ScopedSECKEYPrivateKey rsa_key(
95 crypto::FindNSSKeyFromPublicKeyInfoInSlot(public_key_vector, slot));
96 if (!rsa_key || SECKEY_GetPrivateKeyType(rsa_key.get()) != rsaKey)
97 return nullptr;
98 return rsa_key.Pass();
101 // Signs |data| using a private key associated with |public_key| and stored in
102 // |slot|. Once the data is signed, callback is run on |response_task_runner|.
103 // In case of an error, the callback will be passed an empty string.
104 void SignDataOnWorkerThread(
105 crypto::ScopedPK11Slot slot,
106 const std::string& public_key,
107 const std::string& data,
108 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
109 const base::Callback<void(const std::string&)>& callback) {
110 crypto::ScopedSECKEYPrivateKey private_key(
111 GetPrivateKeyOnWorkerThread(slot.get(), public_key));
112 if (!private_key) {
113 LOG(ERROR) << "Private key for signing data not found";
114 response_task_runner->PostTask(FROM_HERE,
115 base::Bind(callback, std::string()));
116 return;
119 crypto::ScopedSECItem sign_result(SECITEM_AllocItem(NULL, NULL, 0));
120 if (SEC_SignData(sign_result.get(),
121 reinterpret_cast<const unsigned char*>(data.data()),
122 data.size(), private_key.get(),
123 SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION) != SECSuccess) {
124 LOG(ERROR) << "Failed to sign data";
125 response_task_runner->PostTask(FROM_HERE,
126 base::Bind(callback, std::string()));
127 return;
130 std::string signature(reinterpret_cast<const char*>(sign_result->data),
131 sign_result->len);
132 response_task_runner->PostTask(FROM_HERE, base::Bind(callback, signature));
135 // Creates a RSA key pair in |slot|. When done, it runs |callback| with the
136 // created public key on |response_task_runner|.
137 // If |public_key| is not empty, a key pair will be created only if the private
138 // key associated with |public_key| does not exist in |slot|. Otherwise the
139 // callback will be run with |public_key|.
140 void CreateTpmKeyPairOnWorkerThread(
141 crypto::ScopedPK11Slot slot,
142 const std::string& public_key,
143 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
144 const base::Callback<void(const std::string&)>& callback) {
145 if (!public_key.empty() &&
146 GetPrivateKeyOnWorkerThread(slot.get(), public_key)) {
147 response_task_runner->PostTask(FROM_HERE, base::Bind(callback, public_key));
148 return;
151 crypto::ScopedSECKEYPublicKey public_key_obj;
152 crypto::ScopedSECKEYPrivateKey private_key_obj;
153 if (!crypto::GenerateRSAKeyPairNSS(slot.get(), kKeyModulusLength,
154 true /* permanent */, &public_key_obj,
155 &private_key_obj)) {
156 LOG(ERROR) << "Failed to create an RSA key.";
157 response_task_runner->PostTask(FROM_HERE,
158 base::Bind(callback, std::string()));
159 return;
162 crypto::ScopedSECItem public_key_der(
163 SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_obj.get()));
164 if (!public_key_der) {
165 LOG(ERROR) << "Failed to export public key.";
166 response_task_runner->PostTask(FROM_HERE,
167 base::Bind(callback, std::string()));
168 return;
171 response_task_runner->PostTask(
172 FROM_HERE, base::Bind(callback, std::string(reinterpret_cast<const char*>(
173 public_key_der->data),
174 public_key_der->len)));
177 } // namespace
179 // static
180 void EasyUnlockTpmKeyManager::RegisterLocalStatePrefs(
181 PrefRegistrySimple* registry) {
182 registry->RegisterDictionaryPref(prefs::kEasyUnlockLocalStateTpmKeys);
185 // static
186 void EasyUnlockTpmKeyManager::ResetLocalStateForUser(
187 const std::string& user_id) {
188 if (!g_browser_process)
189 return;
190 PrefService* local_state = g_browser_process->local_state();
191 if (!local_state)
192 return;
194 DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockLocalStateTpmKeys);
195 update->RemoveWithoutPathExpansion(user_id, NULL);
198 EasyUnlockTpmKeyManager::EasyUnlockTpmKeyManager(
199 const std::string& user_id,
200 const std::string& username_hash,
201 PrefService* local_state)
202 : user_id_(user_id),
203 username_hash_(username_hash),
204 local_state_(local_state),
205 create_tpm_key_state_(CREATE_TPM_KEY_NOT_STARTED),
206 get_tpm_slot_weak_ptr_factory_(this),
207 weak_ptr_factory_(this) {
210 EasyUnlockTpmKeyManager::~EasyUnlockTpmKeyManager() {
213 bool EasyUnlockTpmKeyManager::PrepareTpmKey(
214 bool check_private_key,
215 const base::Closure& callback) {
216 CHECK(!user_id_.empty());
217 CHECK(!username_hash_.empty());
219 if (create_tpm_key_state_ == CREATE_TPM_KEY_DONE)
220 return true;
222 std::string key = GetPublicTpmKey(user_id_);
223 if (!check_private_key && !key.empty() &&
224 create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED) {
225 return true;
228 prepare_tpm_key_callbacks_.push_back(callback);
230 if (create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED) {
231 create_tpm_key_state_ = CREATE_TPM_KEY_WAITING_FOR_USER_SLOT;
233 base::Closure on_user_tpm_ready =
234 base::Bind(&EasyUnlockTpmKeyManager::OnUserTPMInitialized,
235 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), key);
237 content::BrowserThread::PostTask(
238 content::BrowserThread::IO, FROM_HERE,
239 base::Bind(&EnsureUserTPMInitializedOnIOThread, username_hash_,
240 base::ThreadTaskRunnerHandle::Get(), on_user_tpm_ready));
243 return false;
246 bool EasyUnlockTpmKeyManager::StartGetSystemSlotTimeoutMs(size_t timeout_ms) {
247 if (StartedCreatingTpmKeys())
248 return false;
250 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
251 FROM_HERE,
252 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated,
253 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(),
254 std::string()),
255 base::TimeDelta::FromMilliseconds(timeout_ms));
256 return true;
259 std::string EasyUnlockTpmKeyManager::GetPublicTpmKey(
260 const std::string& user_id) {
261 if (!local_state_)
262 return std::string();
263 const base::DictionaryValue* dict =
264 local_state_->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys);
265 std::string key;
266 if (dict)
267 dict->GetStringWithoutPathExpansion(user_id, &key);
268 std::string decoded;
269 base::Base64Decode(key, &decoded);
270 return decoded;
273 void EasyUnlockTpmKeyManager::SignUsingTpmKey(
274 const std::string& user_id,
275 const std::string& data,
276 const base::Callback<void(const std::string& data)> callback) {
277 std::string key = GetPublicTpmKey(user_id);
278 if (key.empty()) {
279 callback.Run(std::string());
280 return;
283 base::Callback<void(crypto::ScopedPK11Slot)> sign_with_system_slot =
284 base::Bind(&EasyUnlockTpmKeyManager::SignDataWithSystemSlot,
285 weak_ptr_factory_.GetWeakPtr(),
286 key, data, callback);
288 content::BrowserThread::PostTask(
289 content::BrowserThread::IO,
290 FROM_HERE,
291 base::Bind(&GetSystemSlotOnIOThread,
292 base::ThreadTaskRunnerHandle::Get(),
293 sign_with_system_slot));
296 bool EasyUnlockTpmKeyManager::StartedCreatingTpmKeys() const {
297 return create_tpm_key_state_ == CREATE_TPM_KEY_GOT_SYSTEM_SLOT ||
298 create_tpm_key_state_ == CREATE_TPM_KEY_DONE;
301 void EasyUnlockTpmKeyManager::SetKeyInLocalState(const std::string& user_id,
302 const std::string& value) {
303 if (!local_state_)
304 return;
306 std::string encoded;
307 base::Base64Encode(value, &encoded);
308 DictionaryPrefUpdate update(local_state_,
309 prefs::kEasyUnlockLocalStateTpmKeys);
310 update->SetStringWithoutPathExpansion(user_id, encoded);
313 void EasyUnlockTpmKeyManager::OnUserTPMInitialized(
314 const std::string& public_key) {
315 create_tpm_key_state_ = CREATE_TPM_KEY_WAITING_FOR_SYSTEM_SLOT;
317 base::Callback<void(crypto::ScopedPK11Slot)> create_key_with_system_slot =
318 base::Bind(&EasyUnlockTpmKeyManager::CreateKeyInSystemSlot,
319 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), public_key);
321 content::BrowserThread::PostTask(
322 content::BrowserThread::IO, FROM_HERE,
323 base::Bind(&GetSystemSlotOnIOThread, base::ThreadTaskRunnerHandle::Get(),
324 create_key_with_system_slot));
327 void EasyUnlockTpmKeyManager::CreateKeyInSystemSlot(
328 const std::string& public_key,
329 crypto::ScopedPK11Slot system_slot) {
330 CHECK(system_slot);
331 create_tpm_key_state_ = CREATE_TPM_KEY_GOT_SYSTEM_SLOT;
333 // If there are any delayed tasks posted using |StartGetSystemSlotTimeoutMs|,
334 // this will cancel them.
335 // Note that this would cancel other pending |CreateKeyInSystemSlot| tasks,
336 // but there should be at most one such task at a time.
337 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs();
339 base::WorkerPool::PostTask(
340 FROM_HERE,
341 base::Bind(&CreateTpmKeyPairOnWorkerThread,
342 base::Passed(&system_slot),
343 public_key,
344 base::ThreadTaskRunnerHandle::Get(),
345 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated,
346 weak_ptr_factory_.GetWeakPtr())),
347 true /* long task */);
350 void EasyUnlockTpmKeyManager::SignDataWithSystemSlot(
351 const std::string& public_key,
352 const std::string& data,
353 const base::Callback<void(const std::string& data)> callback,
354 crypto::ScopedPK11Slot system_slot) {
355 CHECK(system_slot);
357 base::WorkerPool::PostTask(
358 FROM_HERE,
359 base::Bind(&SignDataOnWorkerThread,
360 base::Passed(&system_slot),
361 public_key,
362 data,
363 base::ThreadTaskRunnerHandle::Get(),
364 base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned,
365 weak_ptr_factory_.GetWeakPtr(),
366 callback)),
367 true /* long task */);
370 void EasyUnlockTpmKeyManager::OnTpmKeyCreated(const std::string& public_key) {
371 // |OnTpmKeyCreated| is called by a timeout task posted by
372 // |StartGetSystemSlotTimeoutMs|. Invalidating the factory will have
373 // an effect of canceling any pending |GetSystemSlotOnIOThread| callbacks,
374 // as well as other pending timeouts.
375 // Note that in the case |OnTpmKeyCreated| was called as a result of
376 // |CreateKeyInSystemSlot|, this should have no effect as no weak ptrs from
377 // this factory should be in use in this case.
378 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs();
380 if (!public_key.empty())
381 SetKeyInLocalState(user_id_, public_key);
383 for (size_t i = 0; i < prepare_tpm_key_callbacks_.size(); ++i) {
384 if (!prepare_tpm_key_callbacks_[i].is_null())
385 prepare_tpm_key_callbacks_[i].Run();
388 prepare_tpm_key_callbacks_.clear();
390 // If key creation failed, reset the state machine.
391 create_tpm_key_state_ =
392 public_key.empty() ? CREATE_TPM_KEY_NOT_STARTED : CREATE_TPM_KEY_DONE;
395 void EasyUnlockTpmKeyManager::OnDataSigned(
396 const base::Callback<void(const std::string&)>& callback,
397 const std::string& signature) {
398 callback.Run(signature);