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

Side by Side Diff: chrome/browser/sync/util/cryptographer.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 months 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 | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/sync/util/cryptographer.h ('k') | chrome/browser/sync/util/cryptographer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698