| Index: sync/syncable/nigori_util.cc
|
| diff --git a/sync/syncable/nigori_util.cc b/sync/syncable/nigori_util.cc
|
| deleted file mode 100644
|
| index 2016a7ac9896401620d3471128cd9e43aae91eb0..0000000000000000000000000000000000000000
|
| --- a/sync/syncable/nigori_util.cc
|
| +++ /dev/null
|
| @@ -1,332 +0,0 @@
|
| -// Copyright 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/syncable/nigori_util.h"
|
| -
|
| -#include <stddef.h>
|
| -#include <stdint.h>
|
| -
|
| -#include <queue>
|
| -#include <string>
|
| -#include <vector>
|
| -
|
| -#include "base/json/json_writer.h"
|
| -#include "sync/syncable/directory.h"
|
| -#include "sync/syncable/entry.h"
|
| -#include "sync/syncable/mutable_entry.h"
|
| -#include "sync/syncable/nigori_handler.h"
|
| -#include "sync/syncable/syncable_util.h"
|
| -#include "sync/syncable/syncable_write_transaction.h"
|
| -#include "sync/util/cryptographer.h"
|
| -
|
| -namespace syncer {
|
| -namespace syncable {
|
| -
|
| -bool ProcessUnsyncedChangesForEncryption(
|
| - WriteTransaction* const trans) {
|
| - NigoriHandler* nigori_handler = trans->directory()->GetNigoriHandler();
|
| - ModelTypeSet encrypted_types = nigori_handler->GetEncryptedTypes(trans);
|
| - Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
|
| - 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_t> handles;
|
| - 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.GetSpecifics();
|
| - // Ignore types that don't need encryption or entries that are already
|
| - // encrypted.
|
| - if (!SpecificsNeedsEncryption(encrypted_types, specifics))
|
| - continue;
|
| - if (!UpdateEntryWithEncryption(trans, specifics, &entry))
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool VerifyUnsyncedChangesAreEncrypted(
|
| - BaseTransaction* const trans,
|
| - ModelTypeSet encrypted_types) {
|
| - std::vector<int64_t> handles;
|
| - 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.GetUniqueServerTag().empty())
|
| - return false; // We don't encrypt unique server nodes.
|
| - ModelType type = entry.GetModelType();
|
| - if (type == PASSWORDS || IsControlType(type))
|
| - 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.GetSpecifics()) ||
|
| - (encrypted_types.Has(type) &&
|
| - entry.GetNonUniqueName() != kEncryptedString);
|
| -}
|
| -
|
| -bool SpecificsNeedsEncryption(ModelTypeSet encrypted_types,
|
| - const sync_pb::EntitySpecifics& specifics) {
|
| - const ModelType type = GetModelTypeFromSpecifics(specifics);
|
| - if (type == PASSWORDS || IsControlType(type))
|
| - 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,
|
| - ModelType type,
|
| - bool is_encrypted) {
|
| - Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
|
| - if (type == PASSWORDS || IsControlType(type)) {
|
| - NOTREACHED();
|
| - return true;
|
| - }
|
| - Entry type_root(trans, GET_TYPE_ROOT, type);
|
| - if (!type_root.good()) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| -
|
| - std::queue<Id> to_visit;
|
| - Id id_string = type_root.GetFirstChildId();
|
| - to_visit.push(id_string);
|
| - while (!to_visit.empty()) {
|
| - id_string = to_visit.front();
|
| - to_visit.pop();
|
| - if (id_string.IsNull())
|
| - continue;
|
| -
|
| - Entry child(trans, GET_BY_ID, id_string);
|
| - if (!child.good()) {
|
| - NOTREACHED();
|
| - return false;
|
| - }
|
| - if (child.GetIsDir()) {
|
| - Id child_id_string = child.GetFirstChildId();
|
| - // Traverse the children.
|
| - to_visit.push(child_id_string);
|
| - }
|
| - const sync_pb::EntitySpecifics& specifics = child.GetSpecifics();
|
| - DCHECK_EQ(type, child.GetModelType());
|
| - DCHECK_EQ(type, GetModelTypeFromSpecifics(specifics));
|
| - // We don't encrypt the server's permanent items.
|
| - if (child.GetUniqueServerTag().empty()) {
|
| - if (specifics.has_encrypted() != is_encrypted)
|
| - return false;
|
| - if (specifics.has_encrypted()) {
|
| - if (child.GetNonUniqueName() != kEncryptedString)
|
| - return false;
|
| - if (!cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted()))
|
| - return false;
|
| - }
|
| - }
|
| - // Push the successor.
|
| - to_visit.push(child.GetSuccessorId());
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool UpdateEntryWithEncryption(
|
| - BaseTransaction* const trans,
|
| - const sync_pb::EntitySpecifics& new_specifics,
|
| - syncable::MutableEntry* entry) {
|
| - NigoriHandler* nigori_handler = trans->directory()->GetNigoriHandler();
|
| - Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
|
| - ModelType type = GetModelTypeFromSpecifics(new_specifics);
|
| - DCHECK_GE(type, FIRST_REAL_MODEL_TYPE);
|
| - const sync_pb::EntitySpecifics& old_specifics = entry->GetSpecifics();
|
| - const ModelTypeSet encrypted_types =
|
| - nigori_handler?
|
| - nigori_handler->GetEncryptedTypes(trans) : ModelTypeSet();
|
| - // 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 || !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)) {
|
| - std::unique_ptr<base::DictionaryValue> value(entry->ToValue(NULL));
|
| - std::string info;
|
| - base::JSONWriter::WriteWithOptions(
|
| - *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &info);
|
| - DVLOG(2) << "Encrypting specifics of type "
|
| - << 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 (GetModelTypeFromSpecifics(old_specifics) == type &&
|
| - was_encrypted) {
|
| - generated_specifics.CopyFrom(old_specifics);
|
| - } else {
|
| - 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 "
|
| - << 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->GetNonUniqueName() != 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 " << ModelTypeToString(type)
|
| - << " already match, dropping change.";
|
| - return true;
|
| - }
|
| -
|
| - if (generated_specifics.has_encrypted()) {
|
| - // Overwrite the possibly sensitive non-specifics data.
|
| - entry->PutNonUniqueName(kEncryptedString);
|
| - // For bookmarks we actually put bogus data into the unencrypted specifics,
|
| - // else the server will try to do it for us.
|
| - if (type == BOOKMARKS) {
|
| - sync_pb::BookmarkSpecifics* bookmark_specifics =
|
| - generated_specifics.mutable_bookmark();
|
| - if (!entry->GetIsDir())
|
| - bookmark_specifics->set_url(kEncryptedString);
|
| - bookmark_specifics->set_title(kEncryptedString);
|
| - }
|
| - }
|
| - entry->PutSpecifics(generated_specifics);
|
| - DVLOG(1) << "Overwriting specifics of type "
|
| - << ModelTypeToString(type)
|
| - << " and marking for syncing.";
|
| - syncable::MarkForSyncing(entry);
|
| - return true;
|
| -}
|
| -
|
| -void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types,
|
| - bool encrypt_everything,
|
| - sync_pb::NigoriSpecifics* nigori) {
|
| - nigori->set_encrypt_everything(encrypt_everything);
|
| - static_assert(37 == MODEL_TYPE_COUNT, "update encrypted types");
|
| - nigori->set_encrypt_bookmarks(
|
| - encrypted_types.Has(BOOKMARKS));
|
| - nigori->set_encrypt_preferences(
|
| - encrypted_types.Has(PREFERENCES));
|
| - nigori->set_encrypt_autofill_profile(
|
| - encrypted_types.Has(AUTOFILL_PROFILE));
|
| - nigori->set_encrypt_autofill(encrypted_types.Has(AUTOFILL));
|
| - nigori->set_encrypt_autofill_wallet_metadata(
|
| - encrypted_types.Has(AUTOFILL_WALLET_METADATA));
|
| - nigori->set_encrypt_themes(encrypted_types.Has(THEMES));
|
| - nigori->set_encrypt_typed_urls(
|
| - encrypted_types.Has(TYPED_URLS));
|
| - nigori->set_encrypt_extension_settings(
|
| - encrypted_types.Has(EXTENSION_SETTINGS));
|
| - nigori->set_encrypt_extensions(
|
| - encrypted_types.Has(EXTENSIONS));
|
| - nigori->set_encrypt_search_engines(
|
| - encrypted_types.Has(SEARCH_ENGINES));
|
| - nigori->set_encrypt_sessions(encrypted_types.Has(SESSIONS));
|
| - nigori->set_encrypt_app_settings(
|
| - encrypted_types.Has(APP_SETTINGS));
|
| - nigori->set_encrypt_apps(encrypted_types.Has(APPS));
|
| - nigori->set_encrypt_app_notifications(
|
| - encrypted_types.Has(APP_NOTIFICATIONS));
|
| - nigori->set_encrypt_dictionary(encrypted_types.Has(DICTIONARY));
|
| - nigori->set_encrypt_favicon_images(encrypted_types.Has(FAVICON_IMAGES));
|
| - nigori->set_encrypt_favicon_tracking(encrypted_types.Has(FAVICON_TRACKING));
|
| - nigori->set_encrypt_articles(encrypted_types.Has(ARTICLES));
|
| - nigori->set_encrypt_app_list(encrypted_types.Has(APP_LIST));
|
| - nigori->set_encrypt_arc_package(encrypted_types.Has(ARC_PACKAGE));
|
| -}
|
| -
|
| -ModelTypeSet GetEncryptedTypesFromNigori(
|
| - const sync_pb::NigoriSpecifics& nigori) {
|
| - if (nigori.encrypt_everything())
|
| - return ModelTypeSet::All();
|
| -
|
| - ModelTypeSet encrypted_types;
|
| - static_assert(37 == MODEL_TYPE_COUNT, "update encrypted types");
|
| - if (nigori.encrypt_bookmarks())
|
| - encrypted_types.Put(BOOKMARKS);
|
| - if (nigori.encrypt_preferences())
|
| - encrypted_types.Put(PREFERENCES);
|
| - if (nigori.encrypt_autofill_profile())
|
| - encrypted_types.Put(AUTOFILL_PROFILE);
|
| - if (nigori.encrypt_autofill())
|
| - encrypted_types.Put(AUTOFILL);
|
| - if (nigori.encrypt_autofill_wallet_metadata())
|
| - encrypted_types.Put(AUTOFILL_WALLET_METADATA);
|
| - if (nigori.encrypt_themes())
|
| - encrypted_types.Put(THEMES);
|
| - if (nigori.encrypt_typed_urls())
|
| - encrypted_types.Put(TYPED_URLS);
|
| - if (nigori.encrypt_extension_settings())
|
| - encrypted_types.Put(EXTENSION_SETTINGS);
|
| - if (nigori.encrypt_extensions())
|
| - encrypted_types.Put(EXTENSIONS);
|
| - if (nigori.encrypt_search_engines())
|
| - encrypted_types.Put(SEARCH_ENGINES);
|
| - if (nigori.encrypt_sessions())
|
| - encrypted_types.Put(SESSIONS);
|
| - if (nigori.encrypt_app_settings())
|
| - encrypted_types.Put(APP_SETTINGS);
|
| - if (nigori.encrypt_apps())
|
| - encrypted_types.Put(APPS);
|
| - if (nigori.encrypt_app_notifications())
|
| - encrypted_types.Put(APP_NOTIFICATIONS);
|
| - if (nigori.encrypt_dictionary())
|
| - encrypted_types.Put(DICTIONARY);
|
| - if (nigori.encrypt_favicon_images())
|
| - encrypted_types.Put(FAVICON_IMAGES);
|
| - if (nigori.encrypt_favicon_tracking())
|
| - encrypted_types.Put(FAVICON_TRACKING);
|
| - if (nigori.encrypt_articles())
|
| - encrypted_types.Put(ARTICLES);
|
| - if (nigori.encrypt_app_list())
|
| - encrypted_types.Put(APP_LIST);
|
| - if (nigori.encrypt_arc_package())
|
| - encrypted_types.Put(ARC_PACKAGE);
|
| - return encrypted_types;
|
| -}
|
| -
|
| -} // namespace syncable
|
| -} // namespace syncer
|
|
|