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

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: Rebase + small fix 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
« no previous file with comments | « chrome/browser/sync/engine/syncapi.h ('k') | chrome/browser/sync/engine/syncapi_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/sync/engine/syncapi.cc
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 018c5cf75475d29a1df2bdc2c5c8bd278ede4ad3..a90b658d76316493604492bd1e6913e82802be8e 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>
@@ -52,6 +54,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"
@@ -192,8 +195,11 @@ sync_pb::PasswordSpecificsData* DecryptPasswordSpecifics(
const sync_pb::EntitySpecifics& specifics, Cryptographer* crypto) {
if (!specifics.HasExtension(sync_pb::password))
return NULL;
- const sync_pb::EncryptedData& encrypted =
- specifics.GetExtension(sync_pb::password).encrypted();
+ const sync_pb::PasswordSpecifics& password_specifics =
+ specifics.GetExtension(sync_pb::password);
+ if (!password_specifics.has_encrypted())
+ return NULL;
+ const sync_pb::EncryptedData& encrypted = password_specifics.encrypted();
scoped_ptr<sync_pb::PasswordSpecificsData> data(
new sync_pb::PasswordSpecificsData);
if (!crypto->Decrypt(encrypted, data.get()))
@@ -202,19 +208,51 @@ sync_pb::PasswordSpecificsData* DecryptPasswordSpecifics(
}
bool BaseNode::DecryptIfNecessary(Entry* entry) {
- if (GetIsFolder()) return true; // Ignore the top-level password folder.
+ if (GetIsFolder()) return true; // Ignore the top-level datatype folder.
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));
@@ -316,59 +354,79 @@ int64 BaseNode::GetExternalId() const {
}
const sync_pb::AppSpecifics& BaseNode::GetAppSpecifics() const {
- DCHECK(GetModelType() == syncable::APPS);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::app);
+ DCHECK_EQ(syncable::APPS, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::AUTOFILL, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::BOOKMARKS, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::NIGORI, GetModelType());
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::nigori);
}
const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
- DCHECK(GetModelType() == syncable::PASSWORDS);
+ DCHECK_EQ(syncable::PASSWORDS, GetModelType());
DCHECK(password_data_.get());
return *password_data_;
}
const sync_pb::PreferenceSpecifics& BaseNode::GetPreferenceSpecifics() const {
- DCHECK(GetModelType() == syncable::PREFERENCES);
- return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::preference);
+ DCHECK_EQ(syncable::PREFERENCES, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::THEMES, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::TYPED_URLS, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::EXTENSIONS, GetModelType());
+ 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);
+ DCHECK_EQ(syncable::SESSIONS, GetModelType());
+ const sync_pb::EntitySpecifics& unencrypted =
+ GetUnencryptedSpecifics(GetEntry());
+ return unencrypted.GetExtension(sync_pb::session);
}
syncable::ModelType BaseNode::GetModelType() const {
@@ -377,6 +435,40 @@ syncable::ModelType BaseNode::GetModelType() const {
////////////////////////////////////
// WriteNode member definitions
+void WriteNode::EncryptIfNecessary(sync_pb::EntitySpecifics* unencrypted) {
+ syncable::ModelType type = syncable::GetModelTypeFromSpecifics(*unencrypted);
+ DCHECK_NE(type, syncable::UNSPECIFIED);
+ DCHECK_NE(type, syncable::PASSWORDS); // Passwords use their own encryption.
+ DCHECK_NE(type, syncable::NIGORI); // Nigori is encrypted separately.
+
+ syncable::ModelTypeSet encrypted_types =
+ GetEncryptedDataTypes(GetTransaction()->GetWrappedTrans());
+ 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.
@@ -406,13 +498,13 @@ void WriteNode::SetURL(const GURL& url) {
void WriteNode::SetAppSpecifics(
const sync_pb::AppSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::APPS);
+ DCHECK_EQ(syncable::APPS, GetModelType());
PutAppSpecificsAndMarkForSyncing(new_value);
}
void WriteNode::SetAutofillSpecifics(
const sync_pb::AutofillSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::AUTOFILL);
+ DCHECK_EQ(syncable::AUTOFILL, GetModelType());
PutAutofillSpecificsAndMarkForSyncing(new_value);
}
@@ -420,6 +512,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);
}
@@ -434,12 +527,13 @@ void WriteNode::PutAutofillProfileSpecificsAndMarkForSyncing(
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::autofill_profile)->CopyFrom(
new_value);
+ EncryptIfNecessary(&entity_specifics);
PutSpecificsAndMarkForSyncing(entity_specifics);
}
void WriteNode::SetBookmarkSpecifics(
const sync_pb::BookmarkSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::BOOKMARKS);
+ DCHECK_EQ(syncable::BOOKMARKS, GetModelType());
PutBookmarkSpecificsAndMarkForSyncing(new_value);
}
@@ -447,12 +541,13 @@ 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);
}
void WriteNode::SetNigoriSpecifics(
const sync_pb::NigoriSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::NIGORI);
+ DCHECK_EQ(syncable::NIGORI, GetModelType());
PutNigoriSpecificsAndMarkForSyncing(new_value);
}
@@ -465,36 +560,40 @@ void WriteNode::PutNigoriSpecificsAndMarkForSyncing(
void WriteNode::SetPasswordSpecifics(
const sync_pb::PasswordSpecificsData& data) {
- DCHECK(GetModelType() == syncable::PASSWORDS);
-
+ DCHECK_EQ(syncable::PASSWORDS, GetModelType());
sync_pb::PasswordSpecifics new_value;
if (!GetTransaction()->GetCryptographer()->Encrypt(
data,
new_value.mutable_encrypted())) {
NOTREACHED();
}
-
PutPasswordSpecificsAndMarkForSyncing(new_value);
}
void WriteNode::SetPreferenceSpecifics(
const sync_pb::PreferenceSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::PREFERENCES);
+ DCHECK_EQ(syncable::PREFERENCES, GetModelType());
PutPreferenceSpecificsAndMarkForSyncing(new_value);
}
void WriteNode::SetThemeSpecifics(
const sync_pb::ThemeSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::THEMES);
+ DCHECK_EQ(syncable::THEMES, GetModelType());
PutThemeSpecificsAndMarkForSyncing(new_value);
}
void WriteNode::SetSessionSpecifics(
const sync_pb::SessionSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::SESSIONS);
+ DCHECK_EQ(syncable::SESSIONS, GetModelType());
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) {
@@ -507,18 +606,19 @@ 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);
}
void WriteNode::SetTypedUrlSpecifics(
const sync_pb::TypedUrlSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::TYPED_URLS);
+ DCHECK_EQ(syncable::TYPED_URLS, GetModelType());
PutTypedUrlSpecificsAndMarkForSyncing(new_value);
}
void WriteNode::SetExtensionSpecifics(
const sync_pb::ExtensionSpecifics& new_value) {
- DCHECK(GetModelType() == syncable::EXTENSIONS);
+ DCHECK_EQ(syncable::EXTENSIONS, GetModelType());
PutExtensionSpecificsAndMarkForSyncing(new_value);
}
@@ -526,6 +626,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);
}
@@ -533,6 +634,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);
}
@@ -540,6 +642,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);
}
@@ -547,18 +650,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.
@@ -623,7 +726,7 @@ bool WriteNode::InitByTagLookup(const std::string& tag) {
if (entry_->Get(syncable::IS_DEL))
return false;
syncable::ModelType model_type = GetModelType();
- DCHECK(model_type == syncable::NIGORI);
+ DCHECK_EQ(syncable::NIGORI, model_type);
return true;
}
@@ -636,7 +739,7 @@ void WriteNode::PutModelType(syncable::ModelType model_type) {
sync_pb::EntitySpecifics specifics;
syncable::AddDefaultExtensionValue(model_type, &specifics);
PutSpecificsAndMarkForSyncing(specifics);
- DCHECK(GetModelType() == model_type);
+ DCHECK_EQ(model_type, GetModelType());
}
// Create a new node with default properties, and bind this WriteNode to it.
@@ -934,8 +1037,6 @@ syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const {
return transaction_;
}
-SyncManager::ExtraChangeRecordData::~ExtraChangeRecordData() {}
-
SyncManager::ChangeRecord::ChangeRecord()
: id(kInvalidId), action(ACTION_ADD) {}
@@ -985,9 +1086,12 @@ DictionaryValue* SyncManager::ChangeRecord::ToValue(
return value;
}
+SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {}
+
SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData(
const sync_pb::PasswordSpecificsData& data)
- : unencrypted_(data) {}
+ : unencrypted_(data) {
+}
SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {}
@@ -1060,6 +1164,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.
@@ -1135,6 +1242,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,
@@ -1260,7 +1370,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 ||
@@ -1271,8 +1382,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))
@@ -1477,6 +1601,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();
}
@@ -1582,23 +1711,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());
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- 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());
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnPassphraseRequired(true));
+ }
}
}
+
+ // Refresh list of encrypted datatypes.
+ syncable::ModelTypeSet encrypted_types =
+ syncable::GetEncryptedDataTypesFromNigori(nigori);
+
+ // Ensure any datatypes that need encryption are encrypted.
+ EncryptDataTypes(encrypted_types);
}
void SyncManager::SyncInternal::StartSyncing() {
@@ -1709,7 +1848,7 @@ bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) {
void SyncManager::SyncInternal::UpdateCredentials(
const SyncCredentials& credentials) {
DCHECK_EQ(MessageLoop::current(), core_message_loop_);
- DCHECK(share_.name == credentials.email);
+ DCHECK_EQ(credentials.email, share_.name);
connection_manager()->set_auth_token(credentials.sync_token);
TalkMediatorLogin(credentials.email, credentials.sync_token);
CheckServerReachable();
@@ -1803,8 +1942,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());
@@ -1826,7 +1965,8 @@ void SyncManager::SyncInternal::SetPassphrase(
// messing with the Nigori node, because we can't call SetPassphrase until
// download conditions are met vs Cryptographer init. It seems like it's
// safe to defer this work.
- sync_pb::NigoriSpecifics specifics;
+ sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics());
+ specifics.clear_encrypted();
cryptographer->GetKeys(specifics.mutable_encrypted());
specifics.set_using_explicit_passphrase(is_explicit);
node.SetNigoriSpecifics(specifics);
@@ -1851,28 +1991,109 @@ bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
return node.GetNigoriSpecifics().using_explicit_passphrase();
}
-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";
- ReadNode passwords_root(trans);
- if (!passwords_root.InitByTagLookup(passwords_tag)) {
- LOG(WARNING) << "No passwords to reencrypt.";
+void SyncManager::SyncInternal::EncryptDataTypes(
+ const syncable::ModelTypeSet& encrypted_types) {
+ // Verify the encrypted types are all enabled.
+ ModelSafeRoutingInfo routes;
+ registrar_->GetModelSafeRoutingInfo(&routes);
+ for (syncable::ModelTypeSet::const_iterator iter = encrypted_types.begin();
+ iter != encrypted_types.end(); ++iter) {
+ if (routes.count(*iter) == 0) {
+ LOG(WARNING) << "Attempted to encrypt non-enabled datatype "
+ << syncable::ModelTypeToString(*iter) << ", dropping type.";
+ routes.erase(*iter);
+ }
+ }
+
+ WriteTransaction trans(GetUserShare());
+ WriteNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ LOG(ERROR) << "Unable to set encrypted datatypes because Nigori node not "
+ << "found.";
+ NOTREACHED();
return;
}
- int64 child_id = passwords_root.GetFirstChildId();
- while (child_id != kInvalidId) {
- WriteNode child(trans);
- if (!child.InitByIdLookup(child_id)) {
+ // Update the Nigori node set of encrypted datatypes so other machines notice.
+ sync_pb::NigoriSpecifics nigori;
+ nigori.CopyFrom(node.GetNigoriSpecifics());
+ syncable::FillNigoriEncryptedTypes(encrypted_types, &nigori);
+ node.SetNigoriSpecifics(nigori);
+
+ // 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;
+}
+
+void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
+ syncable::ModelTypeSet encrypted_types =
+ GetEncryptedDataTypes(trans->GetWrappedTrans());
+ ModelSafeRoutingInfo routes;
+ registrar_->GetModelSafeRoutingInfo(&routes);
+ std::string tag;
+ for (syncable::ModelTypeSet::iterator iter = encrypted_types.begin();
+ iter != encrypted_types.end(); ++iter) {
+ if (*iter == syncable::PASSWORDS || routes.count(*iter) == 0)
+ continue;
+ ReadNode type_root(trans);
+ tag = syncable::ModelTypeToRootTag(*iter);
+ if (!type_root.InitByTagLookup(tag)) {
NOTREACHED();
return;
}
- child.SetPasswordSpecifics(child.GetPasswordSpecifics());
- child_id = child.GetSuccessorId();
+
+ // 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());
+ }
}
+
+ if (routes.count(syncable::PASSWORDS) > 0) {
+ // Passwords are encrypted with their own legacy scheme.
+ encrypted_types.insert(syncable::PASSWORDS);
+ 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;
+ }
+
+ int64 child_id = passwords_root.GetFirstChildId();
+ while (child_id != kInvalidId) {
+ WriteNode child(trans);
+ if (!child.InitByIdLookup(child_id)) {
+ NOTREACHED();
+ return;
+ }
+ child.SetPasswordSpecifics(child.GetPasswordSpecifics());
+ child_id = child.GetSuccessorId();
+ }
+ }
+
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnEncryptionComplete(encrypted_types));
}
SyncManager::~SyncManager() {
@@ -2120,20 +2341,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);
}
}
@@ -2164,8 +2394,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,
@@ -2192,31 +2424,43 @@ 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;
- }
- const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics();
- if (!nigori.encrypted().blob().empty()) {
- DCHECK(!cryptographer->CanDecrypt(nigori.encrypted()));
- cryptographer->SetPendingKeys(nigori.encrypted());
- }
+ {
+ // 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 passwords are enabled, they're automatically considered encrypted.
+ if (enabled_types.count(syncable::PASSWORDS) > 0)
+ encrypted_types.insert(syncable::PASSWORDS);
+ if (encrypted_types.size() > 0) {
+ 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()) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(true));
- } else if (!cryptographer->is_ready()) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- 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()) {
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnPassphraseRequired(true));
+ } else if (!cryptographer->is_ready()) {
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnPassphraseRequired(false));
+ } else {
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnEncryptionComplete(encrypted_types));
+ }
}
}
« no previous file with comments | « chrome/browser/sync/engine/syncapi.h ('k') | chrome/browser/sync/engine/syncapi_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698