summaryrefslogtreecommitdiffstats
path: root/sync/engine/nigori_util.cc
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:35:42 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-15 09:35:42 +0000
commitc1c32c85357f14756247b04b8b5ae41b05bf2e16 (patch)
tree58f25f64e1fa592e8daf276ef69901cd2218f929 /sync/engine/nigori_util.cc
parent63ee33bde2ec8471a70f0f0ec6a1962dd07fc8ab (diff)
downloadchromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.zip
chromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.tar.gz
chromium_src-c1c32c85357f14756247b04b8b5ae41b05bf2e16.tar.bz2
[Sync] Move 'sync' target to sync/
Also move related test files. Move WriteNode::UpdateEntryWithEncryption to nigori_util.h. Clean up defines and dependencies. In particular, get rid of SYNC_ENGINE_VERSION_STRING and hard-code the string in the single place it's used. Rename data_encryption.* to data_encryption_win.* and add a pragma for crypt32.lib. Clean up exit-time constructor warnings in sync{able,er}_unittest.cc. Remove some unused files. BUG=117585 TEST= TBR=jhawkins@chromium.org Review URL: https://chromiumcodereview.appspot.com/9699057 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126872 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/engine/nigori_util.cc')
-rw-r--r--sync/engine/nigori_util.cc244
1 files changed, 244 insertions, 0 deletions
diff --git a/sync/engine/nigori_util.cc b/sync/engine/nigori_util.cc
new file mode 100644
index 0000000..1b6d42a
--- /dev/null
+++ b/sync/engine/nigori_util.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2012 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 "sync/engine/nigori_util.h"
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/json/json_writer.h"
+#include "sync/engine/syncer_util.h"
+#include "sync/syncable/syncable.h"
+#include "sync/util/cryptographer.h"
+
+namespace syncable {
+
+bool ProcessUnsyncedChangesForEncryption(
+ WriteTransaction* const trans,
+ browser_sync::Cryptographer* cryptographer) {
+ DCHECK(cryptographer->is_ready());
+ // 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).
+ // Note: we do not attempt to re-encrypt data with a new key here as key
+ // changes in this code path are likely due to consistency issues (we have
+ // to be updated to a key we already have, e.g. an old key).
+ 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]);
+ const sync_pb::EntitySpecifics& specifics = entry.Get(SPECIFICS);
+ // Ignore types that don't need encryption or entries that are already
+ // encrypted.
+ if (!SpecificsNeedsEncryption(cryptographer->GetEncryptedTypes(),
+ specifics)) {
+ continue;
+ }
+ if (!UpdateEntryWithEncryption(cryptographer, specifics, &entry)) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifyUnsyncedChangesAreEncrypted(
+ BaseTransaction* const trans,
+ 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;
+ }
+ if (EntryNeedsEncryption(encrypted_types, entry))
+ return false;
+ }
+ return true;
+}
+
+bool EntryNeedsEncryption(ModelTypeSet encrypted_types,
+ const Entry& entry) {
+ if (!entry.Get(UNIQUE_SERVER_TAG).empty())
+ return false; // We don't encrypt unique server nodes.
+ syncable::ModelType type = entry.GetModelType();
+ if (type == PASSWORDS || type == NIGORI)
+ return false;
+ // Checking NON_UNIQUE_NAME is not necessary for the correctness of encrypting
+ // the data, nor for determining if data is encrypted. We simply ensure it has
+ // been overwritten to avoid any possible leaks of sensitive data.
+ return SpecificsNeedsEncryption(encrypted_types, entry.Get(SPECIFICS)) ||
+ (encrypted_types.Has(type) &&
+ entry.Get(NON_UNIQUE_NAME) != kEncryptedString);
+}
+
+bool SpecificsNeedsEncryption(ModelTypeSet encrypted_types,
+ const sync_pb::EntitySpecifics& specifics) {
+ const ModelType type = GetModelTypeFromSpecifics(specifics);
+ if (type == PASSWORDS || type == NIGORI)
+ return false; // These types have their own encryption schemes.
+ if (!encrypted_types.Has(type))
+ return false; // This type does not require encryption
+ return !specifics.has_encrypted();
+}
+
+// Mainly for testing.
+bool VerifyDataTypeEncryptionForTest(
+ BaseTransaction* const trans,
+ browser_sync::Cryptographer* cryptographer,
+ 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;
+ if (!trans->directory()->GetFirstChildId(
+ trans, type_root.Get(ID), &id_string)) {
+ NOTREACHED();
+ return false;
+ }
+ 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)) {
+ Id child_id_string;
+ if (!trans->directory()->GetFirstChildId(
+ trans, child.Get(ID), &child_id_string)) {
+ NOTREACHED();
+ return false;
+ }
+ // Traverse the children.
+ to_visit.push(child_id_string);
+ }
+ const sync_pb::EntitySpecifics& specifics = child.Get(SPECIFICS);
+ DCHECK_EQ(type, child.GetModelType());
+ DCHECK_EQ(type, GetModelTypeFromSpecifics(specifics));
+ // We don't encrypt the server's permanent items.
+ if (child.Get(UNIQUE_SERVER_TAG).empty()) {
+ if (specifics.has_encrypted() != is_encrypted)
+ return false;
+ if (specifics.has_encrypted()) {
+ if (child.Get(NON_UNIQUE_NAME) != kEncryptedString)
+ return false;
+ if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted()))
+ return false;
+ }
+ }
+ // Push the successor.
+ to_visit.push(child.Get(NEXT_ID));
+ }
+ return true;
+}
+
+bool UpdateEntryWithEncryption(
+ browser_sync::Cryptographer* cryptographer,
+ const sync_pb::EntitySpecifics& new_specifics,
+ syncable::MutableEntry* entry) {
+ syncable::ModelType type = syncable::GetModelTypeFromSpecifics(new_specifics);
+ DCHECK_GE(type, syncable::FIRST_REAL_MODEL_TYPE);
+ const sync_pb::EntitySpecifics& old_specifics = entry->Get(SPECIFICS);
+ const syncable::ModelTypeSet encrypted_types =
+ cryptographer->GetEncryptedTypes();
+ // It's possible the nigori lost the set of encrypted types. If the current
+ // specifics are already encrypted, we want to ensure we continue encrypting.
+ bool was_encrypted = old_specifics.has_encrypted();
+ sync_pb::EntitySpecifics generated_specifics;
+ if (new_specifics.has_encrypted()) {
+ NOTREACHED() << "New specifics already has an encrypted blob.";
+ return false;
+ }
+ if ((!SpecificsNeedsEncryption(encrypted_types, new_specifics) &&
+ !was_encrypted) ||
+ !cryptographer->is_initialized()) {
+ // No encryption required or we are unable to encrypt.
+ generated_specifics.CopyFrom(new_specifics);
+ } else {
+ // Encrypt new_specifics into generated_specifics.
+ if (VLOG_IS_ON(2)) {
+ scoped_ptr<DictionaryValue> value(entry->ToValue());
+ std::string info;
+ base::JSONWriter::Write(value.get(), true, &info);
+ DVLOG(2) << "Encrypting specifics of type "
+ << syncable::ModelTypeToString(type)
+ << " with content: "
+ << info;
+ }
+ // Only copy over the old specifics if it is of the right type and already
+ // encrypted. The first time we encrypt a node we start from scratch, hence
+ // removing all the unencrypted data, but from then on we only want to
+ // update the node if the data changes or the encryption key changes.
+ if (syncable::GetModelTypeFromSpecifics(old_specifics) == type &&
+ was_encrypted) {
+ generated_specifics.CopyFrom(old_specifics);
+ } else {
+ syncable::AddDefaultFieldValue(type, &generated_specifics);
+ }
+ // Does not change anything if underlying encrypted blob was already up
+ // to date and encrypted with the default key.
+ if (!cryptographer->Encrypt(new_specifics,
+ generated_specifics.mutable_encrypted())) {
+ NOTREACHED() << "Could not encrypt data for node of type "
+ << syncable::ModelTypeToString(type);
+ return false;
+ }
+ }
+
+ // It's possible this entry was encrypted but didn't properly overwrite the
+ // non_unique_name (see crbug.com/96314).
+ bool encrypted_without_overwriting_name = (was_encrypted &&
+ entry->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString);
+
+ // If we're encrypted but the name wasn't overwritten properly we still want
+ // to rewrite the entry, irrespective of whether the specifics match.
+ if (!encrypted_without_overwriting_name &&
+ old_specifics.SerializeAsString() ==
+ generated_specifics.SerializeAsString()) {
+ DVLOG(2) << "Specifics of type " << syncable::ModelTypeToString(type)
+ << " already match, dropping change.";
+ return true;
+ }
+
+ if (generated_specifics.has_encrypted()) {
+ // Overwrite the possibly sensitive non-specifics data.
+ entry->Put(syncable::NON_UNIQUE_NAME, kEncryptedString);
+ // For bookmarks we actually put bogus data into the unencrypted specifics,
+ // else the server will try to do it for us.
+ if (type == syncable::BOOKMARKS) {
+ sync_pb::BookmarkSpecifics* bookmark_specifics =
+ generated_specifics.mutable_bookmark();
+ if (!entry->Get(syncable::IS_DIR))
+ bookmark_specifics->set_url(kEncryptedString);
+ bookmark_specifics->set_title(kEncryptedString);
+ }
+ }
+ entry->Put(syncable::SPECIFICS, generated_specifics);
+ DVLOG(1) << "Overwriting specifics of type "
+ << syncable::ModelTypeToString(type)
+ << " and marking for syncing.";
+ syncable::MarkForSyncing(entry);
+ return true;
+}
+
+} // namespace syncable