summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/syncable/nigori_util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/sync/syncable/nigori_util.cc')
-rw-r--r--chrome/browser/sync/syncable/nigori_util.cc196
1 files changed, 196 insertions, 0 deletions
diff --git a/chrome/browser/sync/syncable/nigori_util.cc b/chrome/browser/sync/syncable/nigori_util.cc
new file mode 100644
index 0000000..71af205
--- /dev/null
+++ b/chrome/browser/sync/syncable/nigori_util.cc
@@ -0,0 +1,196 @@
+// 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 <string>
+#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 GetEncryptedDataTypes(BaseTransaction* const trans) {
+ std::string nigori_tag = ModelTypeToRootTag(syncable::NIGORI);
+ Entry entry(trans, GET_BY_SERVER_TAG, nigori_tag);
+ if (!entry.good()) {
+ VLOG(1) << "Nigori node not found, assuming no encrypted datatypes.";
+ return ModelTypeSet();
+ }
+ if (NIGORI != entry.GetModelType()) {
+ // Can happen if we fail to apply the nigori node due to a conflict.
+ VLOG(1) << "Nigori node does not have nigori extension. Assuming no"
+ << " encrypted datatypes.";
+ return ModelTypeSet();
+ }
+ const sync_pb::EntitySpecifics& specifics = entry.Get(SPECIFICS);
+ return GetEncryptedDataTypesFromNigori(
+ specifics.GetExtension(sync_pb::nigori));
+}
+
+ModelTypeSet GetEncryptedDataTypesFromNigori(
+ const sync_pb::NigoriSpecifics& nigori) {
+ // We don't check NIGORI datatype, it uses its 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_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_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 || type == NIGORI) {
+ NOTREACHED();
+ return true;
+ }
+ std::string type_tag = ModelTypeToRootTag(type);
+ Entry type_root(trans, GET_BY_SERVER_TAG, type_tag);
+ if (!type_root.good()) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::queue<Id> to_visit;
+ Id id_string =
+ trans->directory()->GetFirstChildId(trans, type_root.Get(ID));
+ to_visit.push(id_string);
+ while (!to_visit.empty()) {
+ id_string = to_visit.front();
+ to_visit.pop();
+ if (id_string.IsRoot())
+ continue;
+
+ Entry child(trans, GET_BY_ID, id_string);
+ if (!child.good()) {
+ NOTREACHED();
+ return false;
+ }
+ if (child.Get(IS_DIR)) {
+ // Traverse the children.
+ to_visit.push(
+ trans->directory()->GetFirstChildId(trans, child.Get(ID)));
+ } else {
+ const sync_pb::EntitySpecifics& specifics = child.Get(SPECIFICS);
+ DCHECK_EQ(type, child.GetModelType());
+ DCHECK_EQ(type, GetModelTypeFromSpecifics(specifics));
+ if (specifics.has_encrypted() != is_encrypted)
+ return false;
+ }
+ // Push the successor.
+ to_visit.push(child.Get(NEXT_ID));
+ }
+ return true;
+}
+
+} // namespace syncable