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