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

Unified Diff: chrome/browser/sync/engine/syncapi.cc

Issue 6465005: [Sync] Initial support for encrypting any datatype (no UI hookup yet). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Self review Created 9 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/engine/syncapi.cc
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index a275ae90665cc3d5b28c761f2dab0c176f73b019..4eb4eff7be3c4fdb5be3818884a478e68cc67190 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -4,9 +4,11 @@
#include "chrome/browser/sync/engine/syncapi.h"
+#include <algorithm>
#include <bitset>
#include <iomanip>
#include <list>
+#include <queue>
#include <string>
#include <vector>
@@ -45,6 +47,7 @@
#include "chrome/browser/sync/sessions/sync_session_context.h"
#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/nigori_util.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/util/crypto_helpers.h"
#include "chrome/common/deprecated/event_sys.h"
@@ -199,15 +202,47 @@ bool BaseNode::DecryptIfNecessary(Entry* entry) {
const sync_pb::EntitySpecifics& specifics =
entry->Get(syncable::SPECIFICS);
if (specifics.HasExtension(sync_pb::password)) {
+ // Passwords have their own legacy encryption structure.
scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics(
specifics, GetTransaction()->GetCryptographer()));
if (!data.get())
return false;
password_data_.swap(data);
+ return true;
+ }
+
+ // We assume any node with the encrypted field set has encrypted data.
+ if (!specifics.has_encrypted())
+ return true;
+
+ const sync_pb::EncryptedData& encrypted =
+ specifics.encrypted();
+ std::string plaintext_data = GetTransaction()->GetCryptographer()->
+ DecryptToString(encrypted);
+ if (plaintext_data.length() == 0)
+ return false;
+ if (!unencrypted_data_.ParseFromString(plaintext_data)) {
+ LOG(ERROR) << "Failed to decrypt encrypted node of type " <<
+ syncable::ModelTypeToString(entry->GetModelType()) << ".";
+ return false;
}
return true;
}
+const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics(
+ const syncable::Entry* entry) const {
+ const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS);
+ if (specifics.has_encrypted()) {
+ DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) !=
+ syncable::UNSPECIFIED);
+ return unencrypted_data_;
+ } else {
+ DCHECK(syncable::GetModelTypeFromSpecifics(unencrypted_data_) ==
+ syncable::UNSPECIFIED);
+ return specifics;
+ }
+}
+
int64 BaseNode::GetParentId() const {
return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
GetEntry()->Get(syncable::PARENT_ID));
@@ -274,27 +309,37 @@ int64 BaseNode::GetExternalId() const {
const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const {
DCHECK(GetModelType() == syncable::APPS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::app);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::app);
}
const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const {
DCHECK(GetModelType() == syncable::AUTOFILL);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::autofill);
}
const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const {
DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill_profile);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::autofill_profile);
}
const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const {
DCHECK(GetModelType() == syncable::BOOKMARKS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::bookmark);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::bookmark);
}
const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
DCHECK(GetModelType() == syncable::NIGORI);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::nigori);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::nigori);
}
const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
@@ -305,27 +350,37 @@ const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
const sync_pb::PreferenceSpecifics& BaseNode::GetPreferenceSpecifics() const {
DCHECK(GetModelType() == syncable::PREFERENCES);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::preference);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::preference);
}
const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const {
DCHECK(GetModelType() == syncable::THEMES);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::theme);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::theme);
}
const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
DCHECK(GetModelType() == syncable::TYPED_URLS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::typed_url);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::typed_url);
}
const sync_pb::ExtensionSpecifics& BaseNode::GetExtensionSpecifics() const {
DCHECK(GetModelType() == syncable::EXTENSIONS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::extension);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::extension);
}
const sync_pb::SessionSpecifics& BaseNode::GetSessionSpecifics() const {
DCHECK(GetModelType() == syncable::SESSIONS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::session);
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::session);
}
syncable::ModelType BaseNode::GetModelType() const {
@@ -334,6 +389,40 @@ syncable::ModelType BaseNode::GetModelType() const {
////////////////////////////////////
// WriteNode member definitions
+void WriteNode::EncryptIfNecessary(sync_pb::EntitySpecifics* unencrypted) {
+ syncable::ModelType type = syncable::GetModelTypeFromSpecifics(*unencrypted);
+ DCHECK(type != syncable::UNSPECIFIED);
tim (not reviewing) 2011/02/11 06:52:31 DCHECK_NE
Nicolas Zea 2011/02/14 21:18:41 Done.
+ DCHECK(type != syncable::PASSWORDS); // Passwords use their own encryption.
+ DCHECK(type != syncable::NIGORI); // Nigori is encrypted separately.
+
+ syncable::ModelTypeSet encrypted_types = GetTransaction()->GetWrappedTrans()->
+ GetEncryptedDatatypes();
+ if (encrypted_types.count(type) == 0) {
+ // This datatype does not require encryption.
+ return;
+ }
+
+ if (unencrypted->has_encrypted()) {
+ // This specifics is already encrypted, our work is done.
+ LOG(WARNING) << "Attempted to encrypt an already encrypted entity"
+ << " specifics of type " << syncable::ModelTypeToString(type)
+ << ". Dropping.";
+ return;
+ }
+ sync_pb::EntitySpecifics encrypted;
+ syncable::AddDefaultExtensionValue(type, &encrypted);
+ VLOG(2) << "Encrypted specifics of type " << syncable::ModelTypeToString(type)
+ << " with content: " << unencrypted->SerializeAsString() << "\n";
+ if (!GetTransaction()->GetCryptographer()->Encrypt(
+ *unencrypted,
+ encrypted.mutable_encrypted())) {
+ LOG(ERROR) << "Could not encrypt data for node of type " <<
+ syncable::ModelTypeToString(type);
+ NOTREACHED();
+ }
+ unencrypted->CopyFrom(encrypted);
+}
+
void WriteNode::SetIsFolder(bool folder) {
if (entry_->Get(syncable::IS_DIR) == folder)
return; // Skip redundant changes.
@@ -377,6 +466,7 @@ void WriteNode::PutAutofillSpecificsAndMarkForSyncing(
const sync_pb::AutofillSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::autofill)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -391,6 +481,7 @@ void WriteNode::PutAutofillProfileSpecificsAndMarkForSyncing(
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::autofill_profile)->CopyFrom(
new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -404,6 +495,7 @@ void WriteNode::PutBookmarkSpecificsAndMarkForSyncing(
const sync_pb::BookmarkSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::bookmark)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -423,14 +515,12 @@ void WriteNode::PutNigoriSpecificsAndMarkForSyncing(
void WriteNode::SetPasswordSpecifics(
const sync_pb::PasswordSpecificsData& data) {
DCHECK(GetModelType() == syncable::PASSWORDS);
-
sync_pb::PasswordSpecifics new_value;
if (!GetTransaction()->GetCryptographer()->Encrypt(
data,
new_value.mutable_encrypted())) {
NOTREACHED();
}
-
PutPasswordSpecificsAndMarkForSyncing(new_value);
}
@@ -452,6 +542,12 @@ void WriteNode::SetSessionSpecifics(
PutSessionSpecificsAndMarkForSyncing(new_value);
}
+void WriteNode::ResetFromSpecifics() {
+ sync_pb::EntitySpecifics new_data;
+ new_data.CopyFrom(GetUnencryptedSpecifics(GetEntry()));
+ EncryptIfNecessary(&new_data);
+ PutSpecificsAndMarkForSyncing(new_data);
+}
void WriteNode::PutPasswordSpecificsAndMarkForSyncing(
const sync_pb::PasswordSpecifics& new_value) {
@@ -464,6 +560,7 @@ void WriteNode::PutPreferenceSpecificsAndMarkForSyncing(
const sync_pb::PreferenceSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::preference)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -483,6 +580,7 @@ void WriteNode::PutAppSpecificsAndMarkForSyncing(
const sync_pb::AppSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::app)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -490,6 +588,7 @@ void WriteNode::PutThemeSpecificsAndMarkForSyncing(
const sync_pb::ThemeSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::theme)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -497,6 +596,7 @@ void WriteNode::PutTypedUrlSpecificsAndMarkForSyncing(
const sync_pb::TypedUrlSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::typed_url)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
@@ -504,18 +604,18 @@ void WriteNode::PutExtensionSpecificsAndMarkForSyncing(
const sync_pb::ExtensionSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::extension)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
-
void WriteNode::PutSessionSpecificsAndMarkForSyncing(
const sync_pb::SessionSpecifics& new_value) {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::session)->CopyFrom(new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
-
void WriteNode::PutSpecificsAndMarkForSyncing(
const sync_pb::EntitySpecifics& specifics) {
// Skip redundant changes.
@@ -1004,6 +1104,9 @@ class SyncManager::SyncInternal
// Whether or not the Nigori node is encrypted using an explicit passphrase.
bool IsUsingExplicitPassphrase();
+ // Set the datatypes we want to encrypt and encrypt any nodes as necessary.
+ void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types);
+
// Try to set the current passphrase to |passphrase|, and record whether
// it is an explicit passphrase or implicitly using gaia in the Nigori
// node.
@@ -1076,6 +1179,9 @@ class SyncManager::SyncInternal
return initialized_;
}
+ // If this is a deletion for a password, sets the legacy
+ // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets
+ // |buffer|'s specifics field to contain the unencrypted data.
void SetExtraChangeRecordData(int64 id,
syncable::ModelType type,
ChangeReorderBuffer* buffer,
@@ -1192,7 +1298,8 @@ class SyncManager::SyncInternal
// differ between the versions of an entry stored in |a| and |b|. A return
// value of false means that it should be OK to ignore this change.
static bool VisiblePropertiesDiffer(const syncable::EntryKernel& a,
- const syncable::Entry& b) {
+ const syncable::Entry& b,
+ Cryptographer* cryptographer) {
syncable::ModelType model_type = b.GetModelType();
// Suppress updates to items that aren't tracked by any browser model.
if (model_type == syncable::UNSPECIFIED ||
@@ -1203,8 +1310,21 @@ class SyncManager::SyncInternal
return true;
if (a.ref(syncable::IS_DIR) != b.Get(syncable::IS_DIR))
return true;
- if (a.ref(SPECIFICS).SerializeAsString() !=
- b.Get(SPECIFICS).SerializeAsString()) {
+ // Check if data has changed (account for encryption).
+ std::string a_str, b_str;
+ if (a.ref(SPECIFICS).has_encrypted()) {
+ const sync_pb::EncryptedData& encrypted = a.ref(SPECIFICS).encrypted();
+ a_str = cryptographer->DecryptToString(encrypted);
+ } else {
+ a_str = a.ref(SPECIFICS).SerializeAsString();
+ }
+ if (b.Get(SPECIFICS).has_encrypted()) {
+ const sync_pb::EncryptedData& encrypted = b.Get(SPECIFICS).encrypted();
+ b_str = cryptographer->DecryptToString(encrypted);
+ } else {
+ b_str = b.Get(SPECIFICS).SerializeAsString();
+ }
+ if (a_str != b_str) {
return true;
}
if (VisiblePositionsDiffer(a, b))
@@ -1395,6 +1515,11 @@ void SyncManager::SetPassphrase(const std::string& passphrase,
data_->SetPassphrase(passphrase, is_explicit);
}
+void SyncManager::EncryptDataTypes(
+ const syncable::ModelTypeSet& encrypted_types) {
+ data_->EncryptDataTypes(encrypted_types);
+}
+
bool SyncManager::IsUsingExplicitPassphrase() {
return data_ && data_->IsUsingExplicitPassphrase();
}
@@ -1500,22 +1625,33 @@ void SyncManager::SyncInternal::BootstrapEncryption(
Cryptographer* cryptographer = share_.dir_manager->cryptographer();
cryptographer->Bootstrap(restored_key_for_bootstrapping);
- ReadTransaction trans(GetUserShare());
- ReadNode node(&trans);
- if (!node.InitByTagLookup(kNigoriTag)) {
- NOTREACHED();
- return;
- }
+ sync_pb::NigoriSpecifics nigori;
+ {
+ ReadTransaction trans(GetUserShare());
+ ReadNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ NOTREACHED();
+ return;
+ }
- const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics();
- if (!nigori.encrypted().blob().empty()) {
- if (cryptographer->CanDecrypt(nigori.encrypted())) {
- cryptographer->SetKeys(nigori.encrypted());
- } else {
- cryptographer->SetPendingKeys(nigori.encrypted());
- observer_->OnPassphraseRequired(true);
+ nigori.CopyFrom(node.GetNigoriSpecifics());
+ if (!nigori.encrypted().blob().empty()) {
+ if (cryptographer->CanDecrypt(nigori.encrypted())) {
+ cryptographer->SetKeys(nigori.encrypted());
+ } else {
+ cryptographer->SetPendingKeys(nigori.encrypted());
+ observer_->OnPassphraseRequired(true);
+ }
}
}
+
+ // Refresh list of encrypted datatypes.
+ syncable::ModelTypeSet encrypted_types =
+ syncable::GetEncryptedDatatypesFromNigori(nigori);
+ encrypted_types.insert(syncable::PASSWORDS); // Always on.
+
+ // Ensure any datatypes that need encryption are encrypted.
+ EncryptDataTypes(encrypted_types);
}
void SyncManager::SyncInternal::StartSyncing() {
@@ -1699,8 +1835,8 @@ void SyncManager::SyncInternal::SetPassphrase(
if (is_explicit)
SetUsingExplicitPassphrasePrefForMigration();
- // Nudge the syncer so that passwords updates that were waiting for this
- // passphrase get applied as soon as possible.
+ // Nudge the syncer so that encrypted datatype updates that were waiting for
+ // this passphrase get applied as soon as possible.
sync_manager_->RequestNudge();
} else {
WriteTransaction trans(GetUserShare());
@@ -1746,13 +1882,88 @@ bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
return node.GetNigoriSpecifics().using_explicit_passphrase();
}
+void SyncManager::SyncInternal::EncryptDataTypes(
+ const syncable::ModelTypeSet& encrypted_types) {
+ // Verify the encrypted types are all enabled.
+ ModelSafeRoutingInfo routes;
+ registrar_->GetModelSafeRoutingInfo(&routes);
+ size_t count = 0;
+ for (ModelSafeRoutingInfo::iterator iter = routes.begin();
+ iter != routes.end(); ++iter, ++count) {
+ if (iter->first == syncable::PASSWORDS &&
+ encrypted_types.count(syncable::PASSWORDS) == 0) {
+ LOG(ERROR) << "Attempted to set PASSWORDS as unencrypted.";
+ NOTREACHED();
+ return;
+ }
+ }
+ WriteTransaction trans(GetUserShare());
+ WriteNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ LOG(ERROR) << "Unable to set encrypted datatypes because Nigori node not "
+ "found.";
+ NOTREACHED();
+ return;
+ }
+
+ // Update the Nigori node set of encrypted datatypes so other machines notice.
+ sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics();
+ syncable::FillNigoriEncryptedTypes(encrypted_types, &nigori);
+ node.SetNigoriSpecifics(nigori);
+
+ // Update syncable::directory's cache of encrypted datatypes.
+ trans.GetWrappedTrans()->SetEncryptedDataTypes(encrypted_types);
+
+ // TODO(zea): only reencrypt this datatype? ReEncrypting everything is a
+ // safer approach, and should not impact anything that is already encrypted
+ // (redundant changes are ignored).
+ ReEncryptEverything(&trans);
+ return;
+}
+
+// TODO(zea): unit tests.
void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
- // TODO(tim): bug 59242. We shouldn't lookup by data type and instead use
- // a protocol flag or existence of an EncryptedData message, but for now,
- // encryption is on if-and-only-if the type is passwords, and we haven't
- // ironed out the protocol for generic encryption.
- static const char* passwords_tag = "google_chrome_passwords";
+ syncable::ModelTypeSet encrypted_types = trans->GetWrappedTrans()->
+ GetEncryptedDatatypes();
+ std::string tag;
+ for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin();
+ iter != encrypted_types.end(); ++iter) {
+ if (*iter == syncable::PASSWORDS)
+ continue; // Has special implementation below.
+ ReadNode type_root(trans);
+ tag = syncable::ModelTypeToRootTag(*iter);
+ if (!type_root.InitByTagLookup(tag)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Iterate through all children of this datatype.
+ std::queue<int64> to_visit;
+ int64 child_id = type_root.GetFirstChildId();
+ to_visit.push(child_id);
+ while (!to_visit.empty()) {
+ child_id = to_visit.front();
+ to_visit.pop();
+ if (child_id == kInvalidId)
+ continue;
+
+ WriteNode child(trans);
+ if (!child.InitByIdLookup(child_id)) {
+ NOTREACHED();
+ return;
+ }
+ if (child.GetIsFolder()) {
+ to_visit.push(child.GetFirstChildId());
+ } else {
+ // Rewrite the specifics of the node with encrypted data if necessary.
+ child.ResetFromSpecifics();
+ }
+ to_visit.push(child.GetSuccessorId());
+ }
+ }
+
ReadNode passwords_root(trans);
+ std::string passwords_tag = syncable::ModelTypeToRootTag(syncable::PASSWORDS);
if (!passwords_root.InitByTagLookup(passwords_tag)) {
LOG(WARNING) << "No passwords to reencrypt.";
return;
@@ -1768,6 +1979,7 @@ void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
child.SetPasswordSpecifics(child.GetPasswordSpecifics());
child_id = child.GetSuccessorId();
}
+ observer_->OnEncryptionComplete(encrypted_types);
}
SyncManager::~SyncManager() {
@@ -2003,20 +2215,29 @@ void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id,
syncable::ModelType type, ChangeReorderBuffer* buffer,
Cryptographer* cryptographer, const syncable::EntryKernel& original,
bool existed_before, bool exists_now) {
- // If this is a deletion, attach the entity specifics as extra data
- // so that the delete can be processed.
+ // If this is a deletion and the datatype was encrypted, we need to decrypt it
+ // and attach it to the buffer.
if (!exists_now && existed_before) {
- buffer->SetSpecificsForId(id, original.ref(SPECIFICS));
+ sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
if (type == syncable::PASSWORDS) {
- // Need to dig a bit deeper as passwords are encrypted.
+ // Passwords must use their own legacy ExtraPasswordChangeRecordData.
scoped_ptr<sync_pb::PasswordSpecificsData> data(
- DecryptPasswordSpecifics(original.ref(SPECIFICS), cryptographer));
+ DecryptPasswordSpecifics(original_specifics, cryptographer));
if (!data.get()) {
NOTREACHED();
return;
}
buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data));
+ } else if (original_specifics.has_encrypted()) {
+ // All other datatypes can just create a new unencrypted specifics and
+ // attach it.
+ const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
+ if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
+ NOTREACHED();
+ return;
+ }
}
+ buffer->SetSpecificsForId(id, original_specifics);
}
}
@@ -2047,8 +2268,10 @@ void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
change_buffers_[type].PushAddedItem(id);
else if (!exists_now && existed_before)
change_buffers_[type].PushDeletedItem(id);
- else if (exists_now && existed_before && VisiblePropertiesDiffer(*i, e))
+ else if (exists_now && existed_before &&
+ VisiblePropertiesDiffer(*i, e, dir_manager()->cryptographer())) {
change_buffers_[type].PushUpdatedItem(id, VisiblePositionsDiffer(*i, e));
+ }
SetExtraChangeRecordData(id, type, &change_buffers_[type],
dir_manager()->cryptographer(), *i,
@@ -2075,29 +2298,54 @@ void SyncManager::SyncInternal::OnSyncEngineEvent(
if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
ModelSafeRoutingInfo enabled_types;
registrar_->GetModelSafeRoutingInfo(&enabled_types);
- if (enabled_types.count(syncable::PASSWORDS) > 0) {
- Cryptographer* cryptographer =
- GetUserShare()->dir_manager->cryptographer();
- if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) {
- sync_api::ReadTransaction trans(GetUserShare());
- sync_api::ReadNode node(&trans);
- if (!node.InitByTagLookup(kNigoriTag)) {
- DCHECK(!event.snapshot->is_share_usable);
- return;
+ {
+ // Check to see if we need to notify the frontend that we have newly
+ // encrypted types or that we require a passphrase.
+ sync_api::ReadTransaction trans(GetUserShare());
+ sync_api::ReadNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ DCHECK(!event.snapshot->is_share_usable);
+ return;
+ }
+ const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics();
+ syncable::ModelTypeSet encrypted_types =
+ syncable::GetEncryptedDatatypesFromNigori(nigori);
+ // If this is a first time sync with encryption, it's possible Passwords
+ // hasn't been added to the encryption types list.
+ if (enabled_types.count(syncable::PASSWORDS) > 0)
+ encrypted_types.insert(syncable::PASSWORDS);
+ if (encrypted_types.size() > 0) {
+ syncable::ModelTypeSet old_types =
+ trans.GetWrappedTrans()->GetEncryptedDatatypes();
+ if (encrypted_types != old_types) {
+ if (!includes(encrypted_types.begin(), encrypted_types.end(),
+ old_types.begin(), old_types.end())) {
+ // The set of encrypted datatypes should only ever increase.
+ NOTREACHED();
+ encrypted_types = old_types;
+ } else {
+ // We have some newly encrypted types. Notify the frontend.
+ trans.GetWrappedTrans()->SetEncryptedDataTypes(encrypted_types);
+ observer_->OnEncryptionComplete(encrypted_types);
+ }
}
- const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics();
- if (!nigori.encrypted().blob().empty()) {
- DCHECK(!cryptographer->CanDecrypt(nigori.encrypted()));
- cryptographer->SetPendingKeys(nigori.encrypted());
+
+ Cryptographer* cryptographer =
+ GetUserShare()->dir_manager->cryptographer();
+ if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) {
+ if (!nigori.encrypted().blob().empty()) {
+ DCHECK(!cryptographer->CanDecrypt(nigori.encrypted()));
+ cryptographer->SetPendingKeys(nigori.encrypted());
+ }
}
- }
- // If we've completed a sync cycle and the cryptographer isn't ready yet,
- // prompt the user for a passphrase.
- if (cryptographer->has_pending_keys()) {
- observer_->OnPassphraseRequired(true);
- } else if (!cryptographer->is_ready()) {
- observer_->OnPassphraseRequired(false);
+ // If we've completed a sync cycle and the cryptographer isn't ready
+ // yet, prompt the user for a passphrase.
+ if (cryptographer->has_pending_keys()) {
+ observer_->OnPassphraseRequired(true);
+ } else if (!cryptographer->is_ready()) {
+ observer_->OnPassphraseRequired(false);
+ }
}
}

Powered by Google App Engine
This is Rietveld 408576698