Index: chrome/browser/sync/syncable/nigori_util.cc |
diff --git a/chrome/browser/sync/syncable/nigori_util.cc b/chrome/browser/sync/syncable/nigori_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..308d7bf5e6fed841f55026a24bf9138685672302 |
--- /dev/null |
+++ b/chrome/browser/sync/syncable/nigori_util.cc |
@@ -0,0 +1,170 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/sync/syncable/nigori_util.h" |
+ |
+#include <queue> |
+#include <vector> |
+ |
+#include "chrome/browser/sync/engine/syncer_util.h" |
+#include "chrome/browser/sync/syncable/syncable.h" |
+#include "chrome/browser/sync/util/cryptographer.h" |
+ |
+namespace syncable { |
+ |
+ModelTypeSet GetEncryptedDatatypesFromNigori( |
+ const sync_pb::NigoriSpecifics& nigori) { |
+ // We don't check NIGORI and PASSWORDS datatypes, they use their own |
+ // encryption scheme. |
+ ModelTypeSet encrypted_types; |
+ if(nigori.encrypt_bookmarks()) |
+ encrypted_types.insert(BOOKMARKS); |
+ if(nigori.encrypt_preferences()) |
+ encrypted_types.insert(PREFERENCES); |
+ if(nigori.encrypt_passwords()) |
+ encrypted_types.insert(PASSWORDS); |
+ if(nigori.encrypt_autofill_profile()) |
+ encrypted_types.insert(AUTOFILL_PROFILE); |
+ if(nigori.encrypt_autofill()) |
+ encrypted_types.insert(AUTOFILL); |
+ if(nigori.encrypt_themes()) |
+ encrypted_types.insert(THEMES); |
+ if(nigori.encrypt_typed_urls()) |
+ encrypted_types.insert(TYPED_URLS); |
+ if(nigori.encrypt_extensions()) |
+ encrypted_types.insert(EXTENSIONS); |
+ if(nigori.encrypt_sessions()) |
+ encrypted_types.insert(SESSIONS); |
+ if(nigori.encrypt_apps()) |
+ encrypted_types.insert(APPS); |
+ return encrypted_types; |
+} |
+ |
+void FillNigoriEncryptedTypes(const ModelTypeSet& types, |
+ sync_pb::NigoriSpecifics* nigori) { |
+ DCHECK(nigori); |
+ nigori->set_encrypt_bookmarks(types.count(BOOKMARKS) > 0); |
+ nigori->set_encrypt_preferences(types.count(PREFERENCES) > 0); |
+ nigori->set_encrypt_passwords(types.count(PASSWORDS) > 0); |
+ nigori->set_encrypt_autofill_profile(types.count(AUTOFILL_PROFILE) > 0); |
+ nigori->set_encrypt_autofill(types.count(AUTOFILL) > 0); |
+ nigori->set_encrypt_themes(types.count(THEMES) > 0); |
+ nigori->set_encrypt_typed_urls(types.count(TYPED_URLS) > 0); |
+ nigori->set_encrypt_extensions(types.count(EXTENSIONS) > 0); |
+ nigori->set_encrypt_sessions(types.count(SESSIONS) > 0); |
+ nigori->set_encrypt_apps(types.count(APPS) > 0); |
+} |
+ |
+bool ProcessUnsyncedChangesForEncryption( |
+ WriteTransaction* const trans, |
+ const ModelTypeSet& encrypted_types, |
+ browser_sync::Cryptographer* cryptographer) { |
+ // Get list of all datatypes with unsynced changes. It's possible that our |
+ // local changes need to be encrypted if encryption for that datatype was |
+ // just turned on (and vice versa). This should never affect passwords. |
+ std::vector<int64> handles; |
+ browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); |
+ for (size_t i = 0; i < handles.size(); ++i) { |
+ MutableEntry entry(trans, GET_BY_HANDLE, handles[i]); |
+ sync_pb::EntitySpecifics new_specifics; |
+ const sync_pb::EntitySpecifics& entry_specifics = entry.Get(SPECIFICS); |
+ ModelType type = entry.GetModelType(); |
+ if (type == PASSWORDS) |
+ continue; |
+ if (encrypted_types.count(type) > 0 && |
+ !entry_specifics.has_encrypted()) { |
+ // This entry now requires encryption. |
+ AddDefaultExtensionValue(type, &new_specifics); |
+ if (!cryptographer->Encrypt( |
+ entry_specifics, |
+ new_specifics.mutable_encrypted())) { |
+ LOG(ERROR) << "Could not encrypt data for newly encrypted type " << |
+ ModelTypeToString(type); |
+ NOTREACHED(); |
+ return false; |
+ } else { |
+ VLOG(1) << "Encrypted change for newly encrypted type " << |
+ ModelTypeToString(type); |
+ entry.Put(SPECIFICS, new_specifics); |
+ } |
+ } else if (encrypted_types.count(type) == 0 && |
+ entry_specifics.has_encrypted()) { |
+ // This entry no longer requires encryption. |
+ if (!cryptographer->Decrypt(entry_specifics.encrypted(), |
+ &new_specifics)) { |
+ LOG(ERROR) << "Could not decrypt data for newly unencrypted type " << |
+ ModelTypeToString(type); |
+ NOTREACHED(); |
+ return false; |
+ } else { |
+ VLOG(1) << "Decrypted change for newly unencrypted type " << |
+ ModelTypeToString(type); |
+ entry.Put(SPECIFICS, new_specifics); |
+ } |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool VerifyUnsyncedChangesAreEncrypted( |
+ BaseTransaction* const trans, |
+ const ModelTypeSet& encrypted_types) { |
+ std::vector<int64> handles; |
+ browser_sync::SyncerUtil::GetUnsyncedEntries(trans, &handles); |
+ for (size_t i = 0; i < handles.size(); ++i) { |
+ Entry entry(trans, GET_BY_HANDLE, handles[i]); |
+ if (!entry.good()) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ const sync_pb::EntitySpecifics& entry_specifics = entry.Get(SPECIFICS); |
+ ModelType type = entry.GetModelType(); |
+ if (type == PASSWORDS) |
+ continue; |
+ if (encrypted_types.count(type) > 0 && |
+ !entry_specifics.has_encrypted()) { |
+ // This datatype requires encryption but this data is not encrypted. |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Mainly for testing. |
+bool VerifyDataTypeEncryption(BaseTransaction* const trans, |
+ ModelType type, |
+ bool is_encrypted) { |
+ if(type == PASSWORDS) { |
+ NOTREACHED(); |
+ return true; |
+ } |
+ std::string type_tag = ModelTypeToRootTag(type); |
+ Entry root(trans, GET_BY_SERVER_TAG, type_tag); |
+ if (!root.good()) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+ Directory::ChildHandles handles; |
+ trans->directory()->GetChildHandles(trans, root.Get(ID), &handles); |
+ Directory::ChildHandles::iterator i = handles.begin(); |
+ while (i != handles.end()) { |
+ Entry child(trans, GET_BY_HANDLE, *i++); |
+ if (!child.good()) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ // TODO(zea): traverse into directories as well. |
+ if (child.Get(IS_DIR)) { |
+ NOTREACHED(); |
+ continue; |
+ } |
+ const sync_pb::EntitySpecifics& specifics = child.Get(SPECIFICS); |
+ if (specifics.has_encrypted() != is_encrypted) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // namespace syncable |