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 "base/base64.h" | |
8 #include "base/bind.h" | |
9 #include "base/location.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/prefs/pref_registry_simple.h" | |
13 #include "base/prefs/pref_service.h" | |
14 #include "base/prefs/scoped_user_pref_update.h" | |
15 #include "base/single_thread_task_runner.h" | |
16 #include "base/thread_task_runner_handle.h" | |
17 #include "base/threading/worker_pool.h" | |
18 #include "base/time/time.h" | |
19 #include "base/values.h" | |
20 #include "chrome/browser/browser_process.h" | |
21 #include "chrome/browser/chromeos/platform_keys/platform_keys.h" | |
22 #include "chrome/common/pref_names.h" | |
23 #include "components/pref_registry/pref_registry_syncable.h" | |
24 #include "crypto/nss_util_internal.h" | |
25 #include "crypto/rsa_private_key.h" | |
26 #include "crypto/scoped_nss_types.h" | |
27 | |
28 namespace { | |
29 | |
30 // The modulus length for RSA keys used by easy sign-in. | |
31 const int kKeyModulusLength = 2048; | |
32 | |
33 // Checks if a private RSA key associated with |public_key| can be found in | |
34 // |slot|. | |
35 // Must be called on a worker thread. | |
36 bool PrivateKeyPresentOnWorkerThread(PK11SlotInfo* slot, | |
37 const std::string& public_key) { | |
38 const uint8* public_key_uint8 = | |
39 reinterpret_cast<const uint8*>(public_key.data()); | |
40 std::vector<uint8> public_key_vector( | |
41 public_key_uint8, public_key_uint8 + public_key.size()); | |
42 | |
43 scoped_ptr<crypto::RSAPrivateKey> rsa_key( | |
44 crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key_vector)); | |
45 return rsa_key && rsa_key->key()->pkcs11Slot == slot; | |
46 } | |
47 | |
48 // Creates a RSA key pair in |slot|. When done, it runs |callback| with the | |
49 // created public key on |response_task_runner|. | |
50 // If |public_key| is not empty, a key pair will be created only if the private | |
51 // key associated with |public_key| does not exist in |slot|. Otherwise the | |
52 // callback will be run with |public_key|. | |
53 void CreateTpmKeyPairOnWorkerThread( | |
54 crypto::ScopedPK11Slot slot, | |
55 const std::string& public_key, | |
56 const scoped_refptr<base::SingleThreadTaskRunner>& response_task_runner, | |
57 const base::Callback<void(const std::string&)>& callback) { | |
58 if (!public_key.empty() && | |
59 PrivateKeyPresentOnWorkerThread(slot.get(), public_key)) { | |
60 response_task_runner->PostTask(FROM_HERE, base::Bind(callback, public_key)); | |
61 return; | |
62 } | |
63 | |
64 scoped_ptr<crypto::RSAPrivateKey> rsa_key( | |
65 crypto::RSAPrivateKey::CreateSensitive(slot.get(), kKeyModulusLength)); | |
66 if (!rsa_key) { | |
67 LOG(ERROR) << "Failed to create an RSA key."; | |
68 response_task_runner->PostTask(FROM_HERE, | |
69 base::Bind(callback, std::string())); | |
70 return; | |
71 } | |
72 | |
73 std::vector<uint8> created_public_key; | |
74 if (!rsa_key->ExportPublicKey(&created_public_key)) { | |
75 LOG(ERROR) << "Failed to export public key."; | |
76 response_task_runner->PostTask(FROM_HERE, | |
77 base::Bind(callback, std::string())); | |
78 return; | |
79 } | |
80 | |
81 response_task_runner->PostTask( | |
82 FROM_HERE, | |
83 base::Bind(callback, | |
84 std::string(created_public_key.begin(), | |
85 created_public_key.end()))); | |
86 } | |
87 | |
88 } // namespace | |
89 | |
90 // static | |
91 void EasyUnlockTpmKeyManager::RegisterProfilePrefs( | |
92 user_prefs::PrefRegistrySyncable* registry) { | |
93 registry->RegisterStringPref( | |
94 prefs::kEasyUnlockUserTpmKey, | |
95 std::string(), | |
96 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
97 } | |
98 | |
99 // static | |
100 void EasyUnlockTpmKeyManager::RegisterLocalStatePrefs( | |
101 PrefRegistrySimple* registry) { | |
102 registry->RegisterDictionaryPref(prefs::kEasyUnlockLocalStateTpmKeys); | |
103 } | |
104 | |
105 // static | |
106 void EasyUnlockTpmKeyManager::ResetLocalStateForUser( | |
107 const std::string& user_id) { | |
108 if (!g_browser_process) | |
109 return; | |
110 PrefService* local_state = g_browser_process->local_state(); | |
111 if (!local_state) | |
112 return; | |
113 | |
114 DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockLocalStateTpmKeys); | |
115 update->RemoveWithoutPathExpansion(user_id, NULL); | |
116 } | |
117 | |
118 EasyUnlockTpmKeyManager::EasyUnlockTpmKeyManager( | |
119 const std::string& user_id, | |
120 content::BrowserContext* context, | |
121 PrefService* user_prefs, | |
122 PrefService* local_state) | |
123 : user_id_(user_id), | |
124 browser_context_(context), | |
125 user_prefs_(user_prefs), | |
126 local_state_(local_state), | |
127 creating_tpm_key_pair_(false), | |
128 got_tpm_slot_(false), | |
129 get_tpm_slot_weak_ptr_factory_(this), | |
130 weak_ptr_factory_(this) { | |
131 CHECK_EQ(!user_prefs, user_id.empty()); | |
132 } | |
133 | |
134 EasyUnlockTpmKeyManager::~EasyUnlockTpmKeyManager() { | |
135 } | |
136 | |
137 bool EasyUnlockTpmKeyManager::IsTpmKeyPresent( | |
138 const std::string& user_id, | |
139 bool check_private_key, | |
140 const base::Closure& callback) { | |
141 CHECK(user_prefs_); | |
142 CHECK_EQ(user_id_, user_id); | |
143 | |
144 std::string key = GetKeyFromUserPrefs(); | |
145 if (!check_private_key && !creating_tpm_key_pair_ && !key.empty()) { | |
146 SetKeyInLocalState(user_id, key); | |
147 return true; | |
148 } | |
149 | |
150 tpm_key_present_callbacks_.push_back(callback); | |
151 if (!creating_tpm_key_pair_) { | |
152 creating_tpm_key_pair_ = true; | |
153 | |
154 base::Callback<void(crypto::ScopedPK11Slot)> create_key_with_system_slot = | |
155 base::Bind(&EasyUnlockTpmKeyManager::CreateKeyInSystemSlot, | |
156 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), | |
157 user_id, | |
158 key); | |
159 crypto::ScopedPK11Slot system_slot = crypto::GetSystemNSSKeySlot( | |
160 create_key_with_system_slot); | |
161 if (system_slot) | |
162 create_key_with_system_slot.Run(system_slot.Pass()); | |
163 } | |
164 | |
165 return false; | |
166 } | |
167 | |
168 bool EasyUnlockTpmKeyManager::SetGetSystemSlotTimeoutMs(size_t timeout_ms) { | |
169 if (got_tpm_slot_) | |
170 return false; | |
171 | |
172 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
173 FROM_HERE, | |
174 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated, | |
175 get_tpm_slot_weak_ptr_factory_.GetWeakPtr(), | |
176 user_id_, | |
177 std::string()), | |
178 base::TimeDelta::FromMilliseconds(timeout_ms)); | |
179 return true; | |
180 } | |
181 | |
182 std::string EasyUnlockTpmKeyManager::GetPublicTpmKey( | |
183 const std::string& user_id) { | |
184 if (user_prefs_) { | |
185 CHECK_EQ(user_id_, user_id); | |
186 return GetKeyFromUserPrefs(); | |
187 } | |
188 return GetKeyFromLocalState(user_id); | |
189 } | |
190 | |
191 void EasyUnlockTpmKeyManager::SignUsingTpmKey( | |
192 const std::string& user_id, | |
193 const std::string& data, | |
194 const base::Callback<void(const std::string& data)> callback) { | |
195 std::string key = GetPublicTpmKey(user_id); | |
196 if (key.empty()) { | |
197 callback.Run(std::string()); | |
198 return; | |
199 } | |
200 | |
201 chromeos::platform_keys::subtle::Sign( | |
202 chromeos::platform_keys::kTokenIdSystem, | |
203 key, | |
204 chromeos::platform_keys::HASH_ALGORITHM_SHA256, | |
205 data, | |
206 base::Bind(&EasyUnlockTpmKeyManager::OnDataSigned, | |
207 weak_ptr_factory_.GetWeakPtr(), | |
208 callback), | |
209 browser_context_); | |
210 } | |
211 | |
212 std::string EasyUnlockTpmKeyManager::GetKeyFromUserPrefs() { | |
213 if (!user_prefs_) | |
214 return std::string(); | |
215 std::string key = user_prefs_->GetString(prefs::kEasyUnlockUserTpmKey); | |
216 std::string decoded; | |
217 base::Base64Decode(key, &decoded); | |
218 return decoded; | |
219 } | |
220 | |
221 std::string EasyUnlockTpmKeyManager::GetKeyFromLocalState( | |
222 const std::string& user_id) { | |
223 if (!local_state_) | |
224 return std::string(); | |
225 const base::DictionaryValue* dict = | |
226 local_state_->GetDictionary(prefs::kEasyUnlockLocalStateTpmKeys); | |
227 std::string key; | |
228 if (dict) | |
229 dict->GetStringWithoutPathExpansion(user_id, &key); | |
230 std::string decoded; | |
231 base::Base64Decode(key, &decoded); | |
232 return decoded; | |
233 } | |
234 | |
235 void EasyUnlockTpmKeyManager::SetKeyInLocalState(const std::string& user_id, | |
236 const std::string& value) { | |
237 if (!local_state_) | |
238 return; | |
239 | |
240 std::string encoded; | |
241 base::Base64Encode(value, &encoded); | |
242 DictionaryPrefUpdate update(local_state_, | |
243 prefs::kEasyUnlockLocalStateTpmKeys); | |
244 update->SetStringWithoutPathExpansion(user_id, encoded); | |
245 } | |
246 | |
247 void EasyUnlockTpmKeyManager::SetKeyInUserPrefs(const std::string& value) { | |
248 if (!user_prefs_) | |
249 return; | |
250 | |
251 std::string encoded; | |
252 base::Base64Encode(value, &encoded); | |
253 user_prefs_->SetString(prefs::kEasyUnlockUserTpmKey, encoded); | |
254 } | |
255 | |
256 void EasyUnlockTpmKeyManager::CreateKeyInSystemSlot( | |
257 const std::string& user_id, | |
258 const std::string& public_key, | |
259 crypto::ScopedPK11Slot system_slot) { | |
260 CHECK(system_slot); | |
261 | |
262 got_tpm_slot_ = true; | |
263 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs(); | |
264 | |
265 base::WorkerPool::PostTask( | |
266 FROM_HERE, | |
267 base::Bind(&CreateTpmKeyPairOnWorkerThread, | |
268 base::Passed(&system_slot), | |
269 public_key, | |
270 base::ThreadTaskRunnerHandle::Get(), | |
271 base::Bind(&EasyUnlockTpmKeyManager::OnTpmKeyCreated, | |
272 weak_ptr_factory_.GetWeakPtr(), | |
273 user_id)), | |
274 true /* long task */); | |
275 } | |
276 | |
277 void EasyUnlockTpmKeyManager::OnTpmKeyCreated( | |
278 const std::string& user_id, | |
279 const std::string& public_key) { | |
280 creating_tpm_key_pair_ = false; | |
281 | |
282 get_tpm_slot_weak_ptr_factory_.InvalidateWeakPtrs(); | |
283 | |
284 if (!public_key.empty()) { | |
285 SetKeyInUserPrefs(public_key); | |
286 SetKeyInLocalState(user_id, public_key); | |
xiyuan
2014/12/02 23:15:58
Since the private key is protected in TPM, can we
tbarzic
2014/12/03 19:10:28
yeah, sounds reasonable.. Done.
| |
287 } | |
288 | |
289 for (size_t i = 0; i < tpm_key_present_callbacks_.size(); ++i) { | |
290 if (!tpm_key_present_callbacks_[i].is_null()) | |
291 tpm_key_present_callbacks_[i].Run(); | |
292 } | |
293 | |
294 tpm_key_present_callbacks_.clear(); | |
295 } | |
296 | |
297 void EasyUnlockTpmKeyManager::OnDataSigned( | |
298 const base::Callback<void(const std::string&)>& callback, | |
299 const std::string& signature, | |
300 const std::string& error_message) { | |
301 if (!error_message.empty()) { | |
302 callback.Run(std::string()); | |
303 return; | |
304 } | |
305 | |
306 callback.Run(signature); | |
307 } | |
OLD | NEW |