OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "base/base64.h" | |
6 #include "chrome/browser/sync/util/cryptographer.h" | |
7 #include "chrome/browser/password_manager/encryptor.h" | |
8 | |
9 namespace browser_sync { | |
10 | |
11 const char kNigoriTag[] = "google_chrome_nigori"; | |
12 | |
13 // We name a particular Nigori instance (ie. a triplet consisting of a hostname, | |
14 // a username, and a password) by calling Permute on this string. Since the | |
15 // output of Permute is always the same for a given triplet, clients will always | |
16 // assign the same name to a particular triplet. | |
17 const char kNigoriKeyName[] = "nigori-key"; | |
18 | |
19 Cryptographer::Observer::~Observer() {} | |
20 | |
21 Cryptographer::Cryptographer() | |
22 : default_nigori_(NULL), | |
23 encrypt_everything_(false) { | |
24 syncable::ModelTypeSet sensitive_types = SensitiveTypes(); | |
25 encrypted_types_.insert(sensitive_types.begin(), sensitive_types.end()); | |
26 } | |
27 | |
28 Cryptographer::~Cryptographer() {} | |
29 | |
30 void Cryptographer::AddObserver(Observer* observer) { | |
31 observers_.AddObserver(observer); | |
32 } | |
33 | |
34 void Cryptographer::RemoveObserver(Observer* observer) { | |
35 observers_.RemoveObserver(observer); | |
36 } | |
37 | |
38 void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) { | |
39 if (is_initialized()) { | |
40 NOTREACHED(); | |
41 return; | |
42 } | |
43 | |
44 scoped_ptr<Nigori> nigori(UnpackBootstrapToken(restored_bootstrap_token)); | |
45 if (nigori.get()) | |
46 AddKeyImpl(nigori.release()); | |
47 } | |
48 | |
49 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const { | |
50 return nigoris_.end() != nigoris_.find(data.key_name()); | |
51 } | |
52 | |
53 bool Cryptographer::CanDecryptUsingDefaultKey( | |
54 const sync_pb::EncryptedData& data) const { | |
55 return default_nigori_ && (data.key_name() == default_nigori_->first); | |
56 } | |
57 | |
58 bool Cryptographer::Encrypt(const ::google::protobuf::MessageLite& message, | |
59 sync_pb::EncryptedData* encrypted) const { | |
60 if (!encrypted || !default_nigori_) { | |
61 LOG(ERROR) << "Cryptographer not ready, failed to encrypt."; | |
62 return false; | |
63 } | |
64 | |
65 std::string serialized; | |
66 if (!message.SerializeToString(&serialized)) { | |
67 LOG(ERROR) << "Message is invalid/missing a required field."; | |
68 return false; | |
69 } | |
70 | |
71 encrypted->set_key_name(default_nigori_->first); | |
72 if (!default_nigori_->second->Encrypt(serialized, | |
73 encrypted->mutable_blob())) { | |
74 LOG(ERROR) << "Failed to encrypt data."; | |
75 return false; | |
76 } | |
77 return true; | |
78 } | |
79 | |
80 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted, | |
81 ::google::protobuf::MessageLite* message) const { | |
82 DCHECK(message); | |
83 std::string plaintext = DecryptToString(encrypted); | |
84 return message->ParseFromString(plaintext); | |
85 } | |
86 | |
87 std::string Cryptographer::DecryptToString( | |
88 const sync_pb::EncryptedData& encrypted) const { | |
89 NigoriMap::const_iterator it = nigoris_.find(encrypted.key_name()); | |
90 if (nigoris_.end() == it) { | |
91 NOTREACHED() << "Cannot decrypt message"; | |
92 return std::string(""); // Caller should have called CanDecrypt(encrypt). | |
93 } | |
94 | |
95 std::string plaintext; | |
96 if (!it->second->Decrypt(encrypted.blob(), &plaintext)) { | |
97 return std::string(""); | |
98 } | |
99 | |
100 return plaintext; | |
101 } | |
102 | |
103 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const { | |
104 DCHECK(encrypted); | |
105 DCHECK(!nigoris_.empty()); | |
106 | |
107 // Create a bag of all the Nigori parameters we know about. | |
108 sync_pb::NigoriKeyBag bag; | |
109 for (NigoriMap::const_iterator it = nigoris_.begin(); it != nigoris_.end(); | |
110 ++it) { | |
111 const Nigori& nigori = *it->second; | |
112 sync_pb::NigoriKey* key = bag.add_key(); | |
113 key->set_name(it->first); | |
114 nigori.ExportKeys(key->mutable_user_key(), | |
115 key->mutable_encryption_key(), | |
116 key->mutable_mac_key()); | |
117 } | |
118 | |
119 // Encrypt the bag with the default Nigori. | |
120 return Encrypt(bag, encrypted); | |
121 } | |
122 | |
123 bool Cryptographer::AddKey(const KeyParams& params) { | |
124 DCHECK(NULL == pending_keys_.get()); | |
125 | |
126 // Create the new Nigori and make it the default encryptor. | |
127 scoped_ptr<Nigori> nigori(new Nigori); | |
128 if (!nigori->InitByDerivation(params.hostname, | |
129 params.username, | |
130 params.password)) { | |
131 NOTREACHED(); // Invalid username or password. | |
132 return false; | |
133 } | |
134 return AddKeyImpl(nigori.release()); | |
135 } | |
136 | |
137 bool Cryptographer::AddKeyImpl(Nigori* initialized_nigori) { | |
138 scoped_ptr<Nigori> nigori(initialized_nigori); | |
139 std::string name; | |
140 if (!nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) { | |
141 NOTREACHED(); | |
142 return false; | |
143 } | |
144 nigoris_[name] = make_linked_ptr(nigori.release()); | |
145 default_nigori_ = &*nigoris_.find(name); | |
146 return true; | |
147 } | |
148 | |
149 bool Cryptographer::SetKeys(const sync_pb::EncryptedData& encrypted) { | |
150 DCHECK(CanDecrypt(encrypted)); | |
151 | |
152 sync_pb::NigoriKeyBag bag; | |
153 if (!Decrypt(encrypted, &bag)) { | |
154 return false; | |
155 } | |
156 InstallKeys(encrypted.key_name(), bag); | |
157 return true; | |
158 } | |
159 | |
160 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) { | |
161 DCHECK(!CanDecrypt(encrypted)); | |
162 pending_keys_.reset(new sync_pb::EncryptedData(encrypted)); | |
163 } | |
164 | |
165 bool Cryptographer::DecryptPendingKeys(const KeyParams& params) { | |
166 Nigori nigori; | |
167 if (!nigori.InitByDerivation(params.hostname, | |
168 params.username, | |
169 params.password)) { | |
170 NOTREACHED(); | |
171 return false; | |
172 } | |
173 | |
174 std::string plaintext; | |
175 if (!nigori.Decrypt(pending_keys_->blob(), &plaintext)) | |
176 return false; | |
177 | |
178 sync_pb::NigoriKeyBag bag; | |
179 if (!bag.ParseFromString(plaintext)) { | |
180 NOTREACHED(); | |
181 return false; | |
182 } | |
183 InstallKeys(pending_keys_->key_name(), bag); | |
184 pending_keys_.reset(); | |
185 return true; | |
186 } | |
187 | |
188 bool Cryptographer::GetBootstrapToken(std::string* token) const { | |
189 DCHECK(token); | |
190 if (!is_initialized()) | |
191 return false; | |
192 | |
193 return PackBootstrapToken(default_nigori_->second.get(), token); | |
194 } | |
195 | |
196 bool Cryptographer::PackBootstrapToken(const Nigori* nigori, | |
197 std::string* pack_into) const { | |
198 DCHECK(pack_into); | |
199 DCHECK(nigori); | |
200 | |
201 sync_pb::NigoriKey key; | |
202 if (!nigori->ExportKeys(key.mutable_user_key(), | |
203 key.mutable_encryption_key(), | |
204 key.mutable_mac_key())) { | |
205 NOTREACHED(); | |
206 return false; | |
207 } | |
208 | |
209 std::string unencrypted_token; | |
210 if (!key.SerializeToString(&unencrypted_token)) { | |
211 NOTREACHED(); | |
212 return false; | |
213 } | |
214 | |
215 std::string encrypted_token; | |
216 if (!Encryptor::EncryptString(unencrypted_token, &encrypted_token)) { | |
217 NOTREACHED(); | |
218 return false; | |
219 } | |
220 | |
221 if (!base::Base64Encode(encrypted_token, pack_into)) { | |
222 NOTREACHED(); | |
223 return false; | |
224 } | |
225 return true; | |
226 } | |
227 | |
228 Nigori* Cryptographer::UnpackBootstrapToken(const std::string& token) const { | |
229 if (token.empty()) | |
230 return NULL; | |
231 | |
232 std::string encrypted_data; | |
233 if (!base::Base64Decode(token, &encrypted_data)) { | |
234 DLOG(WARNING) << "Could not decode token."; | |
235 return NULL; | |
236 } | |
237 | |
238 std::string unencrypted_token; | |
239 if (!Encryptor::DecryptString(encrypted_data, &unencrypted_token)) { | |
240 DLOG(WARNING) << "Decryption of bootstrap token failed."; | |
241 return NULL; | |
242 } | |
243 | |
244 sync_pb::NigoriKey key; | |
245 if (!key.ParseFromString(unencrypted_token)) { | |
246 DLOG(WARNING) << "Parsing of bootstrap token failed."; | |
247 return NULL; | |
248 } | |
249 | |
250 scoped_ptr<Nigori> nigori(new Nigori); | |
251 if (!nigori->InitByImport(key.user_key(), key.encryption_key(), | |
252 key.mac_key())) { | |
253 NOTREACHED(); | |
254 return NULL; | |
255 } | |
256 | |
257 return nigori.release(); | |
258 } | |
259 | |
260 Cryptographer::UpdateResult Cryptographer::Update( | |
261 const sync_pb::NigoriSpecifics& nigori) { | |
262 UpdateEncryptedTypesFromNigori(nigori); | |
263 if (!nigori.encrypted().blob().empty()) { | |
264 if (CanDecrypt(nigori.encrypted())) { | |
265 SetKeys(nigori.encrypted()); | |
266 return Cryptographer::SUCCESS; | |
267 } else { | |
268 SetPendingKeys(nigori.encrypted()); | |
269 return Cryptographer::NEEDS_PASSPHRASE; | |
270 } | |
271 } | |
272 return Cryptographer::SUCCESS; | |
273 } | |
274 | |
275 // Static | |
276 syncable::ModelTypeSet Cryptographer::SensitiveTypes() { | |
277 syncable::ModelTypeSet types; | |
278 // Both of these have their own encryption schemes, but we include them | |
279 // anyways. | |
280 types.insert(syncable::PASSWORDS); | |
281 types.insert(syncable::NIGORI); | |
282 return types; | |
283 } | |
284 | |
285 void Cryptographer::UpdateEncryptedTypesFromNigori( | |
286 const sync_pb::NigoriSpecifics& nigori) { | |
287 if (nigori.encrypt_everything()) { | |
288 set_encrypt_everything(); | |
289 return; | |
290 } | |
291 | |
292 syncable::ModelTypeSet encrypted_types(SensitiveTypes()); | |
293 if (nigori.encrypt_bookmarks()) | |
294 encrypted_types.insert(syncable::BOOKMARKS); | |
295 if (nigori.encrypt_preferences()) | |
296 encrypted_types.insert(syncable::PREFERENCES); | |
297 if (nigori.encrypt_autofill_profile()) | |
298 encrypted_types.insert(syncable::AUTOFILL_PROFILE); | |
299 if (nigori.encrypt_autofill()) | |
300 encrypted_types.insert(syncable::AUTOFILL); | |
301 if (nigori.encrypt_themes()) | |
302 encrypted_types.insert(syncable::THEMES); | |
303 if (nigori.encrypt_typed_urls()) | |
304 encrypted_types.insert(syncable::TYPED_URLS); | |
305 if (nigori.encrypt_extension_settings()) | |
306 encrypted_types.insert(syncable::EXTENSION_SETTINGS); | |
307 if (nigori.encrypt_extensions()) | |
308 encrypted_types.insert(syncable::EXTENSIONS); | |
309 if (nigori.encrypt_search_engines()) | |
310 encrypted_types.insert(syncable::SEARCH_ENGINES); | |
311 if (nigori.encrypt_sessions()) | |
312 encrypted_types.insert(syncable::SESSIONS); | |
313 if (nigori.encrypt_app_settings()) | |
314 encrypted_types.insert(syncable::APP_SETTINGS); | |
315 if (nigori.encrypt_apps()) | |
316 encrypted_types.insert(syncable::APPS); | |
317 if (nigori.encrypt_app_notifications()) | |
318 encrypted_types.insert(syncable::APP_NOTIFICATIONS); | |
319 | |
320 // Note: the initial version with encryption did not support the | |
321 // encrypt_everything field. If anything more than the sensitive types were | |
322 // encrypted, it meant we were encrypting everything. | |
323 if (!nigori.has_encrypt_everything() && | |
324 encrypted_types.size() > SensitiveTypes().size()) { | |
325 set_encrypt_everything(); | |
326 return; | |
327 } | |
328 | |
329 SetEncryptedTypes(encrypted_types); | |
330 } | |
331 | |
332 void Cryptographer::UpdateNigoriFromEncryptedTypes( | |
333 sync_pb::NigoriSpecifics* nigori) const { | |
334 nigori->set_encrypt_everything(encrypt_everything_); | |
335 nigori->set_encrypt_bookmarks( | |
336 encrypted_types_.count(syncable::BOOKMARKS) > 0); | |
337 nigori->set_encrypt_preferences( | |
338 encrypted_types_.count(syncable::PREFERENCES) > 0); | |
339 nigori->set_encrypt_autofill_profile( | |
340 encrypted_types_.count(syncable::AUTOFILL_PROFILE) > 0); | |
341 nigori->set_encrypt_autofill(encrypted_types_.count(syncable::AUTOFILL) > 0); | |
342 nigori->set_encrypt_themes(encrypted_types_.count(syncable::THEMES) > 0); | |
343 nigori->set_encrypt_typed_urls( | |
344 encrypted_types_.count(syncable::TYPED_URLS) > 0); | |
345 nigori->set_encrypt_extension_settings( | |
346 encrypted_types_.count(syncable::EXTENSION_SETTINGS) > 0); | |
347 nigori->set_encrypt_extensions( | |
348 encrypted_types_.count(syncable::EXTENSIONS) > 0); | |
349 nigori->set_encrypt_search_engines( | |
350 encrypted_types_.count(syncable::SEARCH_ENGINES) > 0); | |
351 nigori->set_encrypt_sessions(encrypted_types_.count(syncable::SESSIONS) > 0); | |
352 nigori->set_encrypt_app_settings( | |
353 encrypted_types_.count(syncable::APP_SETTINGS) > 0); | |
354 nigori->set_encrypt_apps(encrypted_types_.count(syncable::APPS) > 0); | |
355 nigori->set_encrypt_app_notifications( | |
356 encrypted_types_.count(syncable::APP_NOTIFICATIONS) > 0); | |
357 } | |
358 | |
359 void Cryptographer::set_encrypt_everything() { | |
360 if (encrypt_everything_) { | |
361 DCHECK(encrypted_types_ == syncable::GetAllRealModelTypes()); | |
362 return; | |
363 } | |
364 encrypt_everything_ = true; | |
365 // Change |encrypted_types_| directly to avoid sending more than one | |
366 // notification. | |
367 encrypted_types_ = syncable::GetAllRealModelTypes(); | |
368 EmitEncryptedTypesChangedNotification(); | |
369 } | |
370 | |
371 bool Cryptographer::encrypt_everything() const { | |
372 return encrypt_everything_; | |
373 } | |
374 | |
375 syncable::ModelTypeSet Cryptographer::GetEncryptedTypes() const { | |
376 return encrypted_types_; | |
377 } | |
378 | |
379 void Cryptographer::SetEncryptedTypesForTest( | |
380 const syncable::ModelTypeSet& encrypted_types) { | |
381 SetEncryptedTypes(encrypted_types); | |
382 } | |
383 | |
384 void Cryptographer::SetEncryptedTypes( | |
385 const syncable::ModelTypeSet& encrypted_types) { | |
386 if (encrypted_types_ == encrypted_types) { | |
387 return; | |
388 } | |
389 encrypted_types_ = encrypted_types; | |
390 EmitEncryptedTypesChangedNotification(); | |
391 } | |
392 | |
393 void Cryptographer::EmitEncryptedTypesChangedNotification() { | |
394 FOR_EACH_OBSERVER( | |
395 Observer, observers_, | |
396 OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_)); | |
397 } | |
398 | |
399 void Cryptographer::InstallKeys(const std::string& default_key_name, | |
400 const sync_pb::NigoriKeyBag& bag) { | |
401 int key_size = bag.key_size(); | |
402 for (int i = 0; i < key_size; ++i) { | |
403 const sync_pb::NigoriKey key = bag.key(i); | |
404 // Only use this key if we don't already know about it. | |
405 if (nigoris_.end() == nigoris_.find(key.name())) { | |
406 scoped_ptr<Nigori> new_nigori(new Nigori); | |
407 if (!new_nigori->InitByImport(key.user_key(), | |
408 key.encryption_key(), | |
409 key.mac_key())) { | |
410 NOTREACHED(); | |
411 continue; | |
412 } | |
413 nigoris_[key.name()] = make_linked_ptr(new_nigori.release()); | |
414 } | |
415 } | |
416 DCHECK(nigoris_.end() != nigoris_.find(default_key_name)); | |
417 default_nigori_ = &*nigoris_.find(default_key_name); | |
418 } | |
419 | |
420 } // namespace browser_sync | |
OLD | NEW |