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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager. h"
6
7 #include <cryptohi.h>
8
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/prefs/scoped_user_pref_update.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/threading/worker_pool.h"
20 #include "base/time/time.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/common/pref_names.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "crypto/nss_util_internal.h"
26 #include "crypto/rsa_private_key.h"
27 #include "crypto/scoped_nss_types.h"
28
29 namespace {
30
31 // The modulus length for RSA keys used by easy sign-in.
32 const int kKeyModulusLength = 2048;
33
34 // Relays |GetSystemSlotOnIOThread| callback to |response_task_runner|.
35 void RunCallbackOnThreadRunner(
36 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
37 const base::Callback<void(crypto::ScopedPK11Slot)>& callback,
38 crypto::ScopedPK11Slot slot) {
39 response_task_runner->PostTask(FROM_HERE,
40 base::Bind(callback, base::Passed(&slot)));
41 }
42
43 // Gets TPM system slot. Must be called on IO thread.
44 // The callback wil be relayed to |response_task_runner|.
45 void GetSystemSlotOnIOThread(
46 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
47 const base::Callback<void(crypto::ScopedPK11Slot)>& callback) {
48 base::Callback<void(crypto::ScopedPK11Slot)> callback_on_origin_thread =
49 base::Bind(&RunCallbackOnThreadRunner, response_task_runner, callback);
50
51 crypto::ScopedPK11Slot system_slot =
52 crypto::GetSystemNSSKeySlot(callback_on_origin_thread);
53 if (system_slot)
54 callback_on_origin_thread.Run(system_slot.Pass());
55 }
56
57 // Checks if a private RSA key associated with |public_key| can be found in
58 // |slot|.
59 // Must be called on a worker thread.
60 scoped_ptr<crypto::RSAPrivateKey> GetPrivateKeyOnWorkerThread(
61 PK11SlotInfo* slot,
62 const std::string& public_key) {
63 const uint8* public_key_uint8 =
64 reinterpret_cast<const uint8*>(public_key.data());
65 std::vector<uint8> public_key_vector(
66 public_key_uint8, public_key_uint8 + public_key.size());
67
68 scoped_ptr<crypto::RSAPrivateKey> rsa_key(
69 crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector));
70 if (!rsa_key || rsa_key->key()->pkcs11Slot != slot)
71 return scoped_ptr<crypto::RSAPrivateKey>();
72 return rsa_key.Pass();
73 }
74
75 // Signs |data| using a private key associated with |public_key| and stored in
76 // |slot|. Once the data is signed, callback is run on |response_task_runner|.
77 // In case of an error, the callback will be passed an empty string.
78 void SignDataOnWorkerThread(
79 crypto::ScopedPK11Slot slot,
80 const std::string& public_key,
81 const std::string& data,
82 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
83 const base::Callback<void(const std::string&)>& callback) {
84 scoped_ptr<crypto::RSAPrivateKey> private_key(
85 GetPrivateKeyOnWorkerThread(slot.get(), public_key));
86 if (!private_key) {
87 LOG(ERROR) << "Private key for signing data not found";
88 response_task_runner->PostTask(FROM_HERE,
89 base::Bind(callback, std::string()));
90 return;
91 }
92
93 SECItem sign_result = {siBuffer, NULL, 0};
94 if (SEC_SignData(&sign_result,
95 reinterpret_cast<const unsigned char*>(data.data()),
96 data.size(),
97 private_key->key(),
98 SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION) != SECSuccess) {
99 LOG(ERROR) << "Failed to sign data";
100 response_task_runner->PostTask(FROM_HERE,
101 base::Bind(callback, std::string()));
102 return;
103 }
104
105 std::string signature(reinterpret_cast<const char*>(sign_result.data),
106 sign_result.len);
107 response_task_runner->PostTask(FROM_HERE, base::Bind(callback, signature));
108 }
109
110 // Creates a RSA key pair in |slot|. When done, it runs |callback| with the
111 // created public key on |response_task_runner|.
112 // If |public_key| is not empty, a key pair will be created only if the private
113 // key associated with |public_key| does not exist in |slot|. Otherwise the
114 // callback will be run with |public_key|.
115 void CreateTpmKeyPairOnWorkerThread(
116 crypto::ScopedPK11Slot slot,
117 const std::string& public_key,
118 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner,
119 const base::Callback<void(const std::string&)>& callback) {
120 if (!public_key.empty() &&
121 GetPrivateKeyOnWorkerThread(slot.get(), public_key)) {
122 response_task_runner->PostTask(FROM_HERE, base::Bind(callback, public_key));
123 return;
124 }
125
126 scoped_ptr<crypto::RSAPrivateKey> rsa_key(
127 crypto::RSAPrivateKey::CreateSensitive(slot.get(), kKeyModulusLength));
128 if (!rsa_key) {
129 LOG(ERROR) << "Failed to create an RSA key.";
130 response_task_runner->PostTask(FROM_HERE,
131 base::Bind(callback, std::string()));
132 return;
133 }
134
135 std::vector<uint8> created_public_key;
136 if (!rsa_key->ExportPublicKey(&created_public_key)) {
137 LOG(ERROR) << "Failed to export public key.";
138 response_task_runner->PostTask(FROM_HERE,
139 base::Bind(callback, std::string()));
140 return;
141 }
142
143 response_task_runner->PostTask(
144 FROM_HERE,
145 base::Bind(callback,
146 std::string(created_public_key.begin(),
147 created_public_key.end())));
148 }
149
150 } // namespace
151
152 // static
153 void EasyUnlockTpmKeyManager::RegisterLocalStatePrefs(
154 PrefRegistrySimple* registry) {
155 registry->RegisterDictionaryPref(prefs::kEasyUnlockLocalStateTpmKeys);
156 }
157
158 // static
159 void EasyUnlockTpmKeyManager::ResetLocalStateForUser(
160 const std::string& user_id) {
161 if (!g_browser_process)
162 return;
163 PrefService* local_state = g_browser_process->local_state();
164 if (!local_state)
165 return;
166
167 DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockLocalStateTpmKeys);
168 update->RemoveWithoutPathExpansion(user_id, NULL);
169 }
170
171 EasyUnlockTpmKeyManager::EasyUnlockTpmKeyManager(const std::string& user_id,
172 PrefService* local_state)
173 : user_id_(user_id),
174 local_state_(local_state),
175 create_tpm_key_state_(CREATE_TPM_KEY_NOT_STARTED),
176 get_tpm_slot_weak_ptr_factory_(this),
177 weak_ptr_factory_(this) {
178 }
179
180 EasyUnlockTpmKeyManager::~EasyUnlockTpmKeyManager() {
181 }
182
183 bool EasyUnlockTpmKeyManager::PrepareTpmKey(
184 const std::string& user_id,
185 bool check_private_key,
186 const base::Closure& callback) {
187 CHECK(!user_id_.empty());
188 CHECK_EQ(user_id_, user_id);
pneubeck (no reviews) 2014/12/14 09:39:37 can't you just remove the |user_id| argument from
tbarzic 2014/12/15 20:05:47 Done.
189
190 std::string key = GetPublicTpmKey(user_id);
pneubeck (no reviews) 2014/12/14 09:39:37 nit: can be moved after the next if-block
tbarzic 2014/12/15 20:05:47 Done.
191 if (create_tpm_key_state_ == CREATE_TPM_KEY_DONE)
192 return true;
193
194 if (!check_private_key && !key.empty() &&
195 create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED)
pneubeck (no reviews) 2014/12/14 09:39:37 nit: missing braces
tbarzic 2014/12/15 20:05:47 Done.
196 return true;
197
198 prepare_tpm_key_callbacks_.push_back(callback);
199
200 if (create_tpm_key_state_ == CREATE_TPM_KEY_NOT_STARTED) {
201 create_tpm_key_state_ = CREATE_TPM_KEY_WAITING_FOR_SYSTEM_SLOT;
202
203 base::Callback<void(crypto::ScopedPK11Slot)> create_key_with_system_slot =
204 base::Bind(&EasyUnlockTpmKeyManager::CreateKeyInSystemSlot,
205 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(),
206 user_id,
207 key);
208
209 content::BrowserThread::PostTask(
210 content::BrowserThread::IO,
211 FROM_HERE,
212 base::Bind(&GetSystemSlotOnIOThread,
213 base::ThreadTaskRunnerHandle::Get(),
214 create_key_with_system_slot));
215 }
216
217 return false;
218 }
219
220 bool EasyUnlockTpmKeyManager::StartGetSystemSlotTimeoutMs(size_t timeout_ms) {
221 if (create_tpm_key_state_ == CREATE_TPM_KEY_DONE ||
222 create_tpm_key_state_ == CREATE_TPM_KEY_GOT_SYSTEM_SLOT)
pneubeck (no reviews) 2014/12/14 09:39:37 nit: missing braces
tbarzic 2014/12/15 20:05:47 Done.
223 return false;
224
225 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
226 FROM_HERE,
227 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated,
228 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(),
229 user_id_,
230 std::string()),
231 base::TimeDelta::FromMilliseconds(timeout_ms));
232 return true;
233 }
234
235 std::string EasyUnlockTpmKeyManager::GetPublicTpmKey(
236 const std::string& user_id) {
237 if (!local_state_)
238 return std::string();
239 const base::DictionaryValue* dict =
240 local_state_->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys);
241 std::string key;
242 if (dict)
243 dict->GetStringWithoutPathExpansion(user_id, &key);
244 std::string decoded;
245 base::Base64Decode(key, &decoded);
246 return decoded;
247 }
248
249 void EasyUnlockTpmKeyManager::SignUsingTpmKey(
250 const std::string& user_id,
251 const std::string& data,
252 const base::Callback<void(const std::string& data)> callback) {
253 std::string key = GetPublicTpmKey(user_id);
254 if (key.empty()) {
255 callback.Run(std::string());
256 return;
257 }
258
259 base::Callback<void(crypto::ScopedPK11Slot)> sign_with_system_slot =
260 base::Bind(&EasyUnlockTpmKeyManager::SignDataWithSystemSlot,
261 weak_ptr_factory_.GetWeakPtr(),
262 key, data, callback);
263
264 content::BrowserThread::PostTask(
265 content::BrowserThread::IO,
266 FROM_HERE,
267 base::Bind(&GetSystemSlotOnIOThread,
268 base::ThreadTaskRunnerHandle::Get(),
269 sign_with_system_slot));
270 }
271
272 void EasyUnlockTpmKeyManager::SetKeyInLocalState(const std::string& user_id,
273 const std::string& value) {
274 if (!local_state_)
275 return;
276
277 std::string encoded;
278 base::Base64Encode(value, &encoded);
279 DictionaryPrefUpdate update(local_state_,
280 prefs::kEasyUnlockLocalStateTpmKeys);
281 update->SetStringWithoutPathExpansion(user_id, encoded);
282 }
283
284 void EasyUnlockTpmKeyManager::CreateKeyInSystemSlot(
285 const std::string& user_id,
286 const std::string& public_key,
287 crypto::ScopedPK11Slot system_slot) {
288 CHECK(system_slot);
289
290 create_tpm_key_state_ = CREATE_TPM_KEY_GOT_SYSTEM_SLOT;
291
292 // If there are any delayed tasks posted using |StartGetSystemSlotTimeoutMs|,
293 // this will cancel them.
294 // Note that this would cancel other pending |CreateKeyInSystemSlot| tasks,
295 // but there should be at most one such task at a time.
296 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs();
297
298 base::WorkerPool::PostTask(
299 FROM_HERE,
300 base::Bind(&CreateTpmKeyPairOnWorkerThread,
301 base::Passed(&system_slot),
302 public_key,
303 base::ThreadTaskRunnerHandle::Get(),
304 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated,
305 weak_ptr_factory_.GetWeakPtr(),
306 user_id)),
307 true /* long task */);
308 }
309
310 void EasyUnlockTpmKeyManager::SignDataWithSystemSlot(
311 const std::string& public_key,
312 const std::string& data,
313 const base::Callback<void(const std::string& data)> callback,
314 crypto::ScopedPK11Slot system_slot) {
315 CHECK(system_slot);
316
317 base::WorkerPool::PostTask(
318 FROM_HERE,
319 base::Bind(&SignDataOnWorkerThread,
320 base::Passed(&system_slot),
321 public_key,
322 data,
323 base::ThreadTaskRunnerHandle::Get(),
324 base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned,
325 weak_ptr_factory_.GetWeakPtr(),
326 callback)),
327 true /* long task */);
328 }
329
330 void EasyUnlockTpmKeyManager::OnTpmKeyCreated(
331 const std::string& user_id,
332 const std::string& public_key) {
333
334 // |OnTpmKeyCreated| is called by a timeout task posted by
335 // |StartGetSystemSlotTimeoutMs|. Invalidating the factory will have
336 // an effect of canceling any pending |GetSystemSlotOnIOThread| callbacks,
337 // as well as other pending timeouts.
338 // Note that in the case |OnTpmKeyCreated| was called as a result of
339 // |CreateKeyInSystemSlot|, this should have no effect as no weak ptrs from
340 // this factory should be in use in this case.
341 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs();
342
343 if (!public_key.empty())
344 SetKeyInLocalState(user_id, public_key);
345
346 for (size_t i = 0; i < prepare_tpm_key_callbacks_.size(); ++i) {
347 if (!prepare_tpm_key_callbacks_[i].is_null())
348 prepare_tpm_key_callbacks_[i].Run();
349 }
350
351 prepare_tpm_key_callbacks_.clear();
352
353 // If key creation failed, reset the state machine.
354 create_tpm_key_state_ =
355 public_key.empty() ? CREATE_TPM_KEY_NOT_STARTED : CREATE_TPM_KEY_DONE;
356 }
357
358 void EasyUnlockTpmKeyManager::OnDataSigned(
359 const base::Callback<void(const std::string&)>& callback,
360 const std::string& signature) {
361 callback.Run(signature);
362 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698