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"
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"
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
);
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
,
75 crypto::ScopedPK11Slot private_slot
= crypto::GetPrivateSlotForChromeOSUser(
76 username_hash
, callback_on_origin_thread
);
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(
86 const std::string
& public_key
) {
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
)
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
));
113 LOG(ERROR
) << "Private key for signing data not found";
114 response_task_runner
->PostTask(FROM_HERE
,
115 base::Bind(callback
, std::string()));
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()));
130 std::string
signature(reinterpret_cast<const char*>(sign_result
->data
),
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
));
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
,
156 LOG(ERROR
) << "Failed to create an RSA key.";
157 response_task_runner
->PostTask(FROM_HERE
,
158 base::Bind(callback
, std::string()));
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()));
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
)));
180 void EasyUnlockTpmKeyManager::RegisterLocalStatePrefs(
181 PrefRegistrySimple
* registry
) {
182 registry
->RegisterDictionaryPref(prefs::kEasyUnlockLocalStateTpmKeys
);
186 void EasyUnlockTpmKeyManager::ResetLocalStateForUser(
187 const std::string
& user_id
) {
188 if (!g_browser_process
)
190 PrefService
* local_state
= g_browser_process
->local_state();
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
)
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
)
222 std::string key
= GetPublicTpmKey(user_id_
);
223 if (!check_private_key
&& !key
.empty() &&
224 create_tpm_key_state_
== CREATE_TPM_KEY_NOT_STARTED
) {
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
));
246 bool EasyUnlockTpmKeyManager::StartGetSystemSlotTimeoutMs(size_t timeout_ms
) {
247 if (StartedCreatingTpmKeys())
250 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
252 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated
,
253 get_tpm_slot_weak_ptr_factory_
.GetWeakPtr(),
255 base::TimeDelta::FromMilliseconds(timeout_ms
));
259 std::string
EasyUnlockTpmKeyManager::GetPublicTpmKey(
260 const std::string
& user_id
) {
262 return std::string();
263 const base::DictionaryValue
* dict
=
264 local_state_
->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys
);
267 dict
->GetStringWithoutPathExpansion(user_id
, &key
);
269 base::Base64Decode(key
, &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
);
279 callback
.Run(std::string());
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
,
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
) {
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
) {
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(
341 base::Bind(&CreateTpmKeyPairOnWorkerThread
,
342 base::Passed(&system_slot
),
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
) {
357 base::WorkerPool::PostTask(
359 base::Bind(&SignDataOnWorkerThread
,
360 base::Passed(&system_slot
),
363 base::ThreadTaskRunnerHandle::Get(),
364 base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned
,
365 weak_ptr_factory_
.GetWeakPtr(),
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
);