OLD | NEW |
---|---|
(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 } | |
OLD | NEW |