summaryrefslogtreecommitdiffstats
path: root/sync
diff options
context:
space:
mode:
Diffstat (limited to 'sync')
-rw-r--r--sync/engine/apply_updates_command_unittest.cc31
-rw-r--r--sync/engine/conflict_resolver.cc2
-rw-r--r--sync/engine/syncer_unittest.cc156
-rw-r--r--sync/engine/syncer_util.cc2
-rw-r--r--sync/internal_api/debug_info_event_listener.cc16
-rw-r--r--sync/internal_api/debug_info_event_listener.h18
-rw-r--r--sync/internal_api/js_sync_encryption_handler_observer.cc109
-rw-r--r--sync/internal_api/js_sync_encryption_handler_observer.h58
-rw-r--r--sync/internal_api/js_sync_encryption_handler_observer_unittest.cc158
-rw-r--r--sync/internal_api/js_sync_manager_observer.cc52
-rw-r--r--sync/internal_api/js_sync_manager_observer.h10
-rw-r--r--sync/internal_api/js_sync_manager_observer_unittest.cc71
-rw-r--r--sync/internal_api/public/sync_encryption_handler.cc25
-rw-r--r--sync/internal_api/public/sync_encryption_handler.h151
-rw-r--r--sync/internal_api/public/sync_manager.h117
-rw-r--r--sync/internal_api/public/test/fake_sync_manager.h12
-rw-r--r--sync/internal_api/public/util/sync_string_conversions.h1
-rw-r--r--sync/internal_api/sync_encryption_handler_impl.cc678
-rw-r--r--sync/internal_api/sync_encryption_handler_impl.h161
-rw-r--r--sync/internal_api/sync_encryption_handler_impl_unittest.cc380
-rw-r--r--sync/internal_api/sync_manager_impl.cc703
-rw-r--r--sync/internal_api/sync_manager_impl.h95
-rw-r--r--sync/internal_api/sync_manager_impl_unittest.cc295
-rw-r--r--sync/internal_api/test/fake_sync_manager.cc33
-rw-r--r--sync/sync.gyp12
-rw-r--r--sync/syncable/nigori_handler.cc14
-rw-r--r--sync/syncable/nigori_handler.h46
-rw-r--r--sync/syncable/nigori_util.cc65
-rw-r--r--sync/syncable/nigori_util.h10
-rw-r--r--sync/test/fake_sync_encryption_handler.cc106
-rw-r--r--sync/test/fake_sync_encryption_handler.h66
-rw-r--r--sync/util/DEPS4
-rw-r--r--sync/util/cryptographer.cc178
-rw-r--r--sync/util/cryptographer.h124
-rw-r--r--sync/util/cryptographer_unittest.cc172
35 files changed, 2496 insertions, 1635 deletions
diff --git a/sync/engine/apply_updates_command_unittest.cc b/sync/engine/apply_updates_command_unittest.cc
index 852bfc0..91fa259 100644
--- a/sync/engine/apply_updates_command_unittest.cc
+++ b/sync/engine/apply_updates_command_unittest.cc
@@ -24,6 +24,7 @@
#include "sync/test/engine/syncer_command_test.h"
#include "sync/test/engine/test_id_factory.h"
#include "sync/test/fake_encryptor.h"
+#include "sync/test/fake_sync_encryption_handler.h"
#include "sync/util/cryptographer.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -64,14 +65,22 @@ class ApplyUpdatesCommandTest : public SyncerCommandTest {
SyncerCommandTest::SetUp();
entry_factory_.reset(new TestEntryFactory(directory()));
ExpectNoGroupsToChange(apply_updates_command_);
+
+ syncable::ReadTransaction trans(FROM_HERE, directory());
+ directory()->GetCryptographer(&trans)->SetNigoriHandler(
+ &fake_encryption_handler_);
+ fake_encryption_handler_.set_cryptographer(
+ directory()->GetCryptographer(&trans));
}
+ protected:
+ DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
+
ApplyUpdatesCommand apply_updates_command_;
FakeEncryptor encryptor_;
TestIdFactory id_factory_;
scoped_ptr<TestEntryFactory> entry_factory_;
- private:
- DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
+ FakeSyncEncryptionHandler fake_encryption_handler_;
};
TEST_F(ApplyUpdatesCommandTest, Simple) {
@@ -534,8 +543,7 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
- encrypted_types.Put(BOOKMARKS);
+ nigori->set_encrypt_everything(true);
entry_factory_->CreateUnappliedNewItem(
ModelTypeToRootTag(NIGORI), specifics, true);
EXPECT_FALSE(cryptographer->has_pending_keys());
@@ -556,8 +564,7 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
EXPECT_FALSE(cryptographer->is_ready());
EXPECT_TRUE(cryptographer->has_pending_keys());
- EXPECT_TRUE(
- cryptographer->GetEncryptedTypes().Equals(ModelTypeSet::All()));
+ EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(ModelTypeSet::All()));
}
TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
@@ -581,10 +588,7 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_sessions(true);
- nigori->set_encrypt_themes(true);
- encrypted_types.Put(SESSIONS);
- encrypted_types.Put(THEMES);
+ nigori->set_encrypt_everything(true);
entry_factory_->CreateUnappliedNewItem(
ModelTypeToRootTag(NIGORI), specifics, true);
EXPECT_FALSE(cryptographer->has_pending_keys());
@@ -605,8 +609,7 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
EXPECT_FALSE(cryptographer->is_ready());
EXPECT_TRUE(cryptographer->has_pending_keys());
- EXPECT_TRUE(
- cryptographer->GetEncryptedTypes().Equals(ModelTypeSet::All()));
+ EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(ModelTypeSet::All()));
}
// Create some local unsynced and unencrypted data. Apply a nigori update that
@@ -660,7 +663,7 @@ TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
cryptographer->GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
+ nigori->set_encrypt_everything(true);
encrypted_types.Put(BOOKMARKS);
entry_factory_->CreateUnappliedNewItem(
ModelTypeToRootTag(NIGORI), specifics, true);
@@ -801,7 +804,7 @@ TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
+ nigori->set_encrypt_everything(true);
encrypted_types.Put(BOOKMARKS);
entry_factory_->CreateUnappliedNewItem(
ModelTypeToRootTag(NIGORI), specifics, true);
diff --git a/sync/engine/conflict_resolver.cc b/sync/engine/conflict_resolver.cc
index 2a3c62d..5912e46 100644
--- a/sync/engine/conflict_resolver.cc
+++ b/sync/engine/conflict_resolver.cc
@@ -229,7 +229,7 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori();
// Store the merged set of encrypted types (cryptographer->Update(..) will
// have merged the local types already).
- cryptographer->UpdateNigoriFromEncryptedTypes(server_nigori);
+ cryptographer->UpdateNigoriFromEncryptedTypes(server_nigori, trans);
// The cryptographer has the both the local and remote encryption keys
// (added at cryptographer->Update(..) time).
// If the cryptographer is ready, then it already merged both sets of keys
diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc
index 7372f68..d82197f 100644
--- a/sync/engine/syncer_unittest.cc
+++ b/sync/engine/syncer_unittest.cc
@@ -50,6 +50,7 @@
#include "sync/test/engine/test_syncable_utils.h"
#include "sync/test/fake_encryptor.h"
#include "sync/test/fake_extensions_activity_monitor.h"
+#include "sync/test/fake_sync_encryption_handler.h"
#include "sync/util/cryptographer.h"
#include "sync/util/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -255,6 +256,7 @@ class SyncerTest : public testing::Test,
child_id_ = ids_.MakeServer("child id");
directory()->set_store_birthday(mock_server_->store_birthday());
mock_server_->SetKeystoreKey("encryption_key");
+ GetCryptographer(&trans)->SetNigoriHandler(&fake_encryption_handler_);
}
virtual void TearDown() {
@@ -531,7 +533,7 @@ class SyncerTest : public testing::Test,
return GetField(metahandle, field, false);
}
- Cryptographer* cryptographer(syncable::BaseTransaction* trans) {
+ Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
return directory()->GetCryptographer(trans);
}
@@ -566,6 +568,8 @@ class SyncerTest : public testing::Test,
ModelTypeSet enabled_datatypes_;
TrafficRecorder traffic_recorder_;
+ FakeSyncEncryptionHandler fake_encryption_handler_;
+
DISALLOW_COPY_AND_ASSIGN(SyncerTest);
};
@@ -723,12 +727,12 @@ TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
+ fake_encryption_handler_.EnableEncryptEverything();
// Set up with an old passphrase, but have pending keys
- cryptographer(&wtrans)->AddKey(key_params);
- cryptographer(&wtrans)->Encrypt(bookmark,
+ GetCryptographer(&wtrans)->AddKey(key_params);
+ GetCryptographer(&wtrans)->Encrypt(bookmark,
encrypted_bookmark.mutable_encrypted());
- cryptographer(&wtrans)->Update(*nigori);
+ GetCryptographer(&wtrans)->SetPendingKeys(nigori->encrypted());
// In conflict but properly encrypted.
MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
@@ -766,7 +770,7 @@ TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
// Resolve the pending keys.
- cryptographer(&rtrans)->DecryptPendingKeys(other_params);
+ GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
}
SyncShareNudge();
{
@@ -836,10 +840,9 @@ TEST_F(SyncerTest, EncryptionAwareConflicts) {
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
- nigori->set_encrypt_preferences(true);
- cryptographer(&wtrans)->Update(*nigori);
- EXPECT_TRUE(cryptographer(&wtrans)->has_pending_keys());
+ fake_encryption_handler_.EnableEncryptEverything();
+ GetCryptographer(&wtrans)->SetPendingKeys(nigori->encrypted());
+ EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
}
mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark);
@@ -953,7 +956,7 @@ TEST_F(SyncerTest, EncryptionAwareConflicts) {
{
syncable::ReadTransaction rtrans(FROM_HERE, directory());
// Resolve the pending keys.
- cryptographer(&rtrans)->DecryptPendingKeys(key_params);
+ GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
}
// First cycle resolves conflicts, second cycle commits changes.
SyncShareNudge();
@@ -978,89 +981,6 @@ TEST_F(SyncerTest, EncryptionAwareConflicts) {
#undef VERIFY_ENTRY
-// Receive an old nigori with old encryption keys and encrypted types. We should
-// not revert our default key or encrypted types.
-TEST_F(SyncerTest, ReceiveOldNigori) {
- KeyParams old_key = {"localhost", "dummy", "old"};
- KeyParams current_key = {"localhost", "dummy", "cur"};
-
- // Data for testing encryption/decryption.
- Cryptographer other_cryptographer(&encryptor_);
- other_cryptographer.AddKey(old_key);
- sync_pb::EntitySpecifics other_encrypted_specifics;
- other_encrypted_specifics.mutable_bookmark()->set_title("title");
- other_cryptographer.Encrypt(
- other_encrypted_specifics,
- other_encrypted_specifics.mutable_encrypted());
- sync_pb::EntitySpecifics our_encrypted_specifics;
- our_encrypted_specifics.mutable_bookmark()->set_title("title2");
- ModelTypeSet encrypted_types = ModelTypeSet::All();
-
-
- // Receive the initial nigori node.
- sync_pb::EntitySpecifics initial_nigori_specifics;
- initial_nigori_specifics.mutable_nigori();
- mock_server_->SetNigori(1, 10, 10, initial_nigori_specifics);
- SyncShareNudge();
-
- {
- // Set up the current nigori node (containing both keys and encrypt
- // everything).
- WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
- sync_pb::EntitySpecifics specifics;
- sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- cryptographer(&wtrans)->AddKey(old_key);
- cryptographer(&wtrans)->AddKey(current_key);
- cryptographer(&wtrans)->Encrypt(
- our_encrypted_specifics,
- our_encrypted_specifics.mutable_encrypted());
- cryptographer(&wtrans)->GetKeys(
- nigori->mutable_encrypted());
- cryptographer(&wtrans)->set_encrypt_everything();
- cryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori);
- MutableEntry nigori_entry(&wtrans, GET_BY_SERVER_TAG,
- ModelTypeToRootTag(NIGORI));
- ASSERT_TRUE(nigori_entry.good());
- nigori_entry.Put(SPECIFICS, specifics);
- nigori_entry.Put(IS_UNSYNCED, true);
- EXPECT_FALSE(cryptographer(&wtrans)->has_pending_keys());
- EXPECT_TRUE(encrypted_types.Equals(
- cryptographer(&wtrans)->GetEncryptedTypes()));
- }
-
- SyncShareNudge(); // Commit it.
-
- // Now set up the old nigori node and add it as a server update.
- sync_pb::EntitySpecifics old_nigori_specifics;
- sync_pb::NigoriSpecifics *old_nigori = old_nigori_specifics.mutable_nigori();
- other_cryptographer.GetKeys(old_nigori->mutable_encrypted());
- other_cryptographer.UpdateNigoriFromEncryptedTypes(old_nigori);
- mock_server_->SetNigori(1, 30, 30, old_nigori_specifics);
-
- SyncShareNudge(); // Download the old nigori and apply it.
-
- {
- // Ensure everything is committed and stable now. The cryptographer
- // should be able to decrypt both sets of keys and still be encrypting with
- // the newest, and the encrypted types should be the most recent
- syncable::ReadTransaction trans(FROM_HERE, directory());
- Entry nigori_entry(&trans, GET_BY_SERVER_TAG,
- ModelTypeToRootTag(NIGORI));
- ASSERT_TRUE(nigori_entry.good());
- EXPECT_FALSE(nigori_entry.Get(IS_UNAPPLIED_UPDATE));
- EXPECT_FALSE(nigori_entry.Get(IS_UNSYNCED));
- const sync_pb::NigoriSpecifics& nigori =
- nigori_entry.Get(SPECIFICS).nigori();
- EXPECT_TRUE(cryptographer(&trans)->CanDecryptUsingDefaultKey(
- our_encrypted_specifics.encrypted()));
- EXPECT_TRUE(cryptographer(&trans)->CanDecrypt(
- other_encrypted_specifics.encrypted()));
- EXPECT_TRUE(cryptographer(&trans)->CanDecrypt(
- nigori.encrypted()));
- EXPECT_TRUE(cryptographer(&trans)->encrypt_everything());
- }
-}
-
// Resolve a confict between two nigori's with different encrypted types,
// and encryption keys (remote is explicit). Afterwards, the encrypted types
// should be unioned and the cryptographer should have both keys and be
@@ -1092,22 +1012,25 @@ TEST_F(SyncerTest, NigoriConflicts) {
WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
sync_pb::EntitySpecifics specifics;
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- cryptographer(&wtrans)->AddKey(local_key_params);
- cryptographer(&wtrans)->Encrypt(
+ GetCryptographer(&wtrans)->AddKey(local_key_params);
+ GetCryptographer(&wtrans)->Encrypt(
our_encrypted_specifics,
our_encrypted_specifics.mutable_encrypted());
- cryptographer(&wtrans)->GetKeys(
+ GetCryptographer(&wtrans)->GetKeys(
nigori->mutable_encrypted());
- cryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori);
- cryptographer(&wtrans)->set_encrypt_everything();
+ fake_encryption_handler_.EnableEncryptEverything();
+ GetCryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(
+ nigori,
+ &wtrans);
MutableEntry nigori_entry(&wtrans, GET_BY_SERVER_TAG,
ModelTypeToRootTag(NIGORI));
ASSERT_TRUE(nigori_entry.good());
nigori_entry.Put(SPECIFICS, specifics);
nigori_entry.Put(IS_UNSYNCED, true);
- EXPECT_FALSE(cryptographer(&wtrans)->has_pending_keys());
+ EXPECT_FALSE(GetCryptographer(&wtrans)->has_pending_keys());
EXPECT_TRUE(encrypted_types.Equals(
- cryptographer(&wtrans)->GetEncryptedTypes()));
+ GetCryptographer(&wtrans)->GetEncryptedTypes()));
+ fake_encryption_handler_.set_cryptographer(GetCryptographer(&wtrans));
}
{
sync_pb::EntitySpecifics specifics;
@@ -1136,19 +1059,20 @@ TEST_F(SyncerTest, NigoriConflicts) {
EXPECT_FALSE(nigori_entry.Get(IS_UNAPPLIED_UPDATE));
EXPECT_FALSE(nigori_entry.Get(IS_UNSYNCED));
sync_pb::EntitySpecifics specifics = nigori_entry.Get(SPECIFICS);
- EXPECT_TRUE(cryptographer(&wtrans)->has_pending_keys());
+ ASSERT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
EXPECT_TRUE(encrypted_types.Equals(
- cryptographer(&wtrans)->GetEncryptedTypes()));
- EXPECT_TRUE(cryptographer(&wtrans)->encrypt_everything());
+ GetCryptographer(&wtrans)->GetEncryptedTypes()));
+ EXPECT_TRUE(fake_encryption_handler_.EncryptEverythingEnabled());
EXPECT_TRUE(specifics.nigori().using_explicit_passphrase());
// Supply the pending keys. Afterwards, we should be able to decrypt both
// our own encrypted data and data encrypted by the other cryptographer,
// but the key provided by the other cryptographer should be the default.
- EXPECT_TRUE(cryptographer(&wtrans)->DecryptPendingKeys(other_key_params));
- EXPECT_FALSE(cryptographer(&wtrans)->has_pending_keys());
+ EXPECT_TRUE(
+ GetCryptographer(&wtrans)->DecryptPendingKeys(other_key_params));
+ EXPECT_FALSE(GetCryptographer(&wtrans)->has_pending_keys());
sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- cryptographer(&wtrans)->GetKeys(nigori->mutable_encrypted());
- cryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori);
+ GetCryptographer(&wtrans)->GetKeys(nigori->mutable_encrypted());
+ GetCryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori, &wtrans);
// Normally this would be written as part of SetPassphrase, but we do it
// manually for the test.
nigori_entry.Put(SPECIFICS, specifics);
@@ -1166,13 +1090,13 @@ TEST_F(SyncerTest, NigoriConflicts) {
ASSERT_TRUE(nigori_entry.good());
EXPECT_FALSE(nigori_entry.Get(IS_UNAPPLIED_UPDATE));
EXPECT_FALSE(nigori_entry.Get(IS_UNSYNCED));
- EXPECT_TRUE(cryptographer(&wtrans)->CanDecrypt(
+ EXPECT_TRUE(GetCryptographer(&wtrans)->CanDecrypt(
our_encrypted_specifics.encrypted()));
- EXPECT_FALSE(cryptographer(&wtrans)->
+ EXPECT_FALSE(GetCryptographer(&wtrans)->
CanDecryptUsingDefaultKey(our_encrypted_specifics.encrypted()));
- EXPECT_TRUE(cryptographer(&wtrans)->CanDecrypt(
+ EXPECT_TRUE(GetCryptographer(&wtrans)->CanDecrypt(
other_encrypted_specifics.encrypted()));
- EXPECT_TRUE(cryptographer(&wtrans)->
+ EXPECT_TRUE(GetCryptographer(&wtrans)->
CanDecryptUsingDefaultKey(other_encrypted_specifics.encrypted()));
EXPECT_TRUE(nigori_entry.Get(SPECIFICS).nigori().
using_explicit_passphrase());
@@ -4172,7 +4096,7 @@ TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
TEST_F(SyncerTest, GetKeySuccess) {
{
syncable::ReadTransaction rtrans(FROM_HERE, directory());
- EXPECT_FALSE(cryptographer(&rtrans)->HasKeystoreKey());
+ EXPECT_FALSE(GetCryptographer(&rtrans)->HasKeystoreKey());
}
SyncShareConfigure();
@@ -4180,14 +4104,14 @@ TEST_F(SyncerTest, GetKeySuccess) {
EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
{
syncable::ReadTransaction rtrans(FROM_HERE, directory());
- EXPECT_TRUE(cryptographer(&rtrans)->HasKeystoreKey());
+ EXPECT_TRUE(GetCryptographer(&rtrans)->HasKeystoreKey());
}
}
TEST_F(SyncerTest, GetKeyEmpty) {
{
syncable::ReadTransaction rtrans(FROM_HERE, directory());
- EXPECT_FALSE(cryptographer(&rtrans)->HasKeystoreKey());
+ EXPECT_FALSE(GetCryptographer(&rtrans)->HasKeystoreKey());
}
mock_server_->SetKeystoreKey("");
@@ -4196,7 +4120,7 @@ TEST_F(SyncerTest, GetKeyEmpty) {
EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
{
syncable::ReadTransaction rtrans(FROM_HERE, directory());
- EXPECT_FALSE(cryptographer(&rtrans)->HasKeystoreKey());
+ EXPECT_FALSE(GetCryptographer(&rtrans)->HasKeystoreKey());
}
}
diff --git a/sync/engine/syncer_util.cc b/sync/engine/syncer_util.cc
index fedf4d8..ca21f2e 100644
--- a/sync/engine/syncer_util.cc
+++ b/sync/engine/syncer_util.cc
@@ -217,7 +217,7 @@ UpdateAttemptResponse AttemptToUpdateEntry(
// the nigori node (e.g. on restart), they will commit without issue.
if (specifics.has_nigori()) {
const sync_pb::NigoriSpecifics& nigori = specifics.nigori();
- cryptographer->Update(nigori);
+ cryptographer->ApplyNigoriUpdate(nigori, trans);
// Make sure any unsynced changes are properly encrypted as necessary.
// We only perform this if the cryptographer is ready. If not, these are
diff --git a/sync/internal_api/debug_info_event_listener.cc b/sync/internal_api/debug_info_event_listener.cc
index b7e3cda..d88981a 100644
--- a/sync/internal_api/debug_info_event_listener.cc
+++ b/sync/internal_api/debug_info_event_listener.cc
@@ -4,6 +4,8 @@
#include "sync/internal_api/debug_info_event_listener.h"
+#include "sync/util/cryptographer.h"
+
namespace syncer {
using sessions::SyncSessionSnapshot;
@@ -88,19 +90,17 @@ void DebugInfoEventListener::OnEncryptionComplete() {
CreateAndAddEvent(sync_pb::DebugEventInfo::ENCRYPTION_COMPLETE);
}
+void DebugInfoEventListener::OnCryptographerStateChanged(
+ Cryptographer* cryptographer) {
+ cryptographer_has_pending_keys_ = cryptographer->has_pending_keys();
+ cryptographer_ready_ = cryptographer->is_ready();
+}
+
void DebugInfoEventListener::OnActionableError(
const SyncProtocolError& sync_error) {
CreateAndAddEvent(sync_pb::DebugEventInfo::ACTIONABLE_ERROR);
}
-void DebugInfoEventListener::SetCrytographerHasPendingKeys(bool pending_keys) {
- cryptographer_has_pending_keys_ = pending_keys;
-}
-
-void DebugInfoEventListener::SetCryptographerReady(bool ready) {
- cryptographer_ready_ = ready;
-}
-
void DebugInfoEventListener::OnNudgeFromDatatype(ModelType datatype) {
sync_pb::DebugEventInfo event_info;
event_info.set_nudging_datatype(
diff --git a/sync/internal_api/debug_info_event_listener.h b/sync/internal_api/debug_info_event_listener.h
index 67017b9..158801d 100644
--- a/sync/internal_api/debug_info_event_listener.h
+++ b/sync/internal_api/debug_info_event_listener.h
@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "sync/internal_api/public/sessions/sync_session_snapshot.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
#include "sync/internal_api/public/sync_manager.h"
#include "sync/internal_api/public/util/weak_handle.h"
#include "sync/js/js_backend.h"
@@ -24,6 +25,7 @@ const unsigned int kMaxEntries = 6;
// Listens to events and records them in a queue. And passes the events to
// syncer when requested.
class DebugInfoEventListener : public SyncManager::Observer,
+ public SyncEncryptionHandler::Observer,
public sessions::DebugInfoGetter {
public:
DebugInfoEventListener();
@@ -37,20 +39,24 @@ class DebugInfoEventListener : public SyncManager::Observer,
bool success, ModelTypeSet restored_types) OVERRIDE;
virtual void OnConnectionStatusChange(
ConnectionStatus connection_status) OVERRIDE;
+ virtual void OnStopSyncingPermanently() OVERRIDE;
+ virtual void OnUpdatedToken(const std::string& token) OVERRIDE;
+ virtual void OnActionableError(
+ const SyncProtocolError& sync_error) OVERRIDE;
+
+ // SyncEncryptionHandler::Observer implementation.
virtual void OnPassphraseRequired(
PassphraseRequiredReason reason,
const sync_pb::EncryptedData& pending_keys) OVERRIDE;
virtual void OnPassphraseAccepted() OVERRIDE;
virtual void OnBootstrapTokenUpdated(
const std::string& bootstrap_token) OVERRIDE;
- virtual void OnStopSyncingPermanently() OVERRIDE;
- virtual void OnUpdatedToken(const std::string& token) OVERRIDE;
virtual void OnEncryptedTypesChanged(
ModelTypeSet encrypted_types,
bool encrypt_everything) OVERRIDE;
virtual void OnEncryptionComplete() OVERRIDE;
- virtual void OnActionableError(
- const SyncProtocolError& sync_error) OVERRIDE;
+ virtual void OnCryptographerStateChanged(
+ Cryptographer* cryptographer) OVERRIDE;
// Sync manager events.
void OnNudgeFromDatatype(ModelType datatype);
@@ -59,10 +65,6 @@ class DebugInfoEventListener : public SyncManager::Observer,
// DebugInfoGetter Implementation.
virtual void GetAndClearDebugInfo(sync_pb::DebugInfo* debug_info) OVERRIDE;
- // Functions to set cryptographer state.
- void SetCrytographerHasPendingKeys(bool pending_keys);
- void SetCryptographerReady(bool ready);
-
private:
FRIEND_TEST_ALL_PREFIXES(DebugInfoEventListenerTest, VerifyEventsAdded);
FRIEND_TEST_ALL_PREFIXES(DebugInfoEventListenerTest, VerifyQueueSize);
diff --git a/sync/internal_api/js_sync_encryption_handler_observer.cc b/sync/internal_api/js_sync_encryption_handler_observer.cc
new file mode 100644
index 0000000..1e76575
--- /dev/null
+++ b/sync/internal_api/js_sync_encryption_handler_observer.cc
@@ -0,0 +1,109 @@
+// 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/internal_api/js_sync_encryption_handler_observer.h"
+
+#include <cstddef>
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "sync/internal_api/public/base/model_type.h"
+#include "sync/internal_api/public/util/sync_string_conversions.h"
+#include "sync/js/js_arg_list.h"
+#include "sync/js/js_event_details.h"
+#include "sync/js/js_event_handler.h"
+#include "sync/util/cryptographer.h"
+
+namespace syncer {
+
+JsSyncEncryptionHandlerObserver::JsSyncEncryptionHandlerObserver() {}
+
+JsSyncEncryptionHandlerObserver::~JsSyncEncryptionHandlerObserver() {}
+
+void JsSyncEncryptionHandlerObserver::SetJsEventHandler(
+ const WeakHandle<JsEventHandler>& event_handler) {
+ event_handler_ = event_handler;
+}
+
+void JsSyncEncryptionHandlerObserver::OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ details.SetString("reason",
+ PassphraseRequiredReasonToString(reason));
+ HandleJsEvent(FROM_HERE, "onPassphraseRequired", JsEventDetails(&details));
+}
+
+void JsSyncEncryptionHandlerObserver::OnPassphraseAccepted() {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ HandleJsEvent(FROM_HERE, "onPassphraseAccepted", JsEventDetails(&details));
+}
+
+void JsSyncEncryptionHandlerObserver::OnBootstrapTokenUpdated(
+ const std::string& boostrap_token) {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ details.SetString("bootstrapToken", "<redacted>");
+ HandleJsEvent(FROM_HERE, "OnBootstrapTokenUpdated", JsEventDetails(&details));
+}
+
+void JsSyncEncryptionHandlerObserver::OnEncryptedTypesChanged(
+ ModelTypeSet encrypted_types,
+ bool encrypt_everything) {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ details.Set("encryptedTypes",
+ ModelTypeSetToValue(encrypted_types));
+ details.SetBoolean("encryptEverything", encrypt_everything);
+ HandleJsEvent(FROM_HERE,
+ "onEncryptedTypesChanged", JsEventDetails(&details));
+}
+
+void JsSyncEncryptionHandlerObserver::OnEncryptionComplete() {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ HandleJsEvent(FROM_HERE, "onEncryptionComplete", JsEventDetails());
+}
+
+void JsSyncEncryptionHandlerObserver::OnCryptographerStateChanged(
+ Cryptographer* cryptographer) {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ details.SetBoolean("ready",
+ cryptographer->is_ready());
+ details.SetBoolean("hasPendingKeys",
+ cryptographer->has_pending_keys());
+ HandleJsEvent(FROM_HERE,
+ "onCryptographerStateChanged",
+ JsEventDetails(&details));
+}
+
+void JsSyncEncryptionHandlerObserver::HandleJsEvent(
+ const tracked_objects::Location& from_here,
+ const std::string& name, const JsEventDetails& details) {
+ if (!event_handler_.IsInitialized()) {
+ NOTREACHED();
+ return;
+ }
+ event_handler_.Call(from_here,
+ &JsEventHandler::HandleJsEvent, name, details);
+}
+
+} // namespace syncer
diff --git a/sync/internal_api/js_sync_encryption_handler_observer.h b/sync/internal_api/js_sync_encryption_handler_observer.h
new file mode 100644
index 0000000..4562e5f
--- /dev/null
+++ b/sync/internal_api/js_sync_encryption_handler_observer.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef SYNC_INTERNAL_API_JS_SYNC_ENCRYPTION_HANDLER_OBSERVER_H_
+#define SYNC_INTERNAL_API_JS_SYNC_ENCRYPTION_HANDLER_OBSERVER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
+#include "sync/internal_api/public/util/weak_handle.h"
+#include "sync/protocol/sync_protocol_error.h"
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace syncer {
+
+class JsEventDetails;
+class JsEventHandler;
+
+// Routes SyncEncryptionHandler events to a JsEventHandler.
+class JsSyncEncryptionHandlerObserver : public SyncEncryptionHandler::Observer {
+ public:
+ JsSyncEncryptionHandlerObserver();
+ virtual ~JsSyncEncryptionHandlerObserver();
+
+ void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler);
+
+ // SyncEncryptionHandlerObserver::Observer implementation.
+ virtual void OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) OVERRIDE;
+ virtual void OnPassphraseAccepted() OVERRIDE;
+ virtual void OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) OVERRIDE;
+ virtual void OnEncryptedTypesChanged(
+ ModelTypeSet encrypted_types,
+ bool encrypt_everything) OVERRIDE;
+ virtual void OnEncryptionComplete() OVERRIDE;
+ virtual void OnCryptographerStateChanged(
+ Cryptographer* cryptographer) OVERRIDE;
+
+ private:
+ void HandleJsEvent(const tracked_objects::Location& from_here,
+ const std::string& name, const JsEventDetails& details);
+
+ WeakHandle<JsEventHandler> event_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsSyncEncryptionHandlerObserver);
+};
+
+} // namespace syncer
+
+#endif // SYNC_INTERNAL_API_JS_SYNC_ENCRYPTION_HANDLER_OBSERVER_H_
diff --git a/sync/internal_api/js_sync_encryption_handler_observer_unittest.cc b/sync/internal_api/js_sync_encryption_handler_observer_unittest.cc
new file mode 100644
index 0000000..70ebab7
--- /dev/null
+++ b/sync/internal_api/js_sync_encryption_handler_observer_unittest.cc
@@ -0,0 +1,158 @@
+// 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/internal_api/js_sync_encryption_handler_observer.h"
+
+#include "base/basictypes.h"
+#include "base/location.h"
+#include "base/message_loop.h"
+#include "base/values.h"
+#include "sync/internal_api/public/base/model_type.h"
+#include "sync/internal_api/public/util/sync_string_conversions.h"
+#include "sync/internal_api/public/util/weak_handle.h"
+#include "sync/js/js_event_details.h"
+#include "sync/js/js_test_util.h"
+#include "sync/util/cryptographer.h"
+#include "sync/test/fake_encryptor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+namespace {
+
+using ::testing::InSequence;
+using ::testing::StrictMock;
+
+class JsSyncEncryptionHandlerObserverTest : public testing::Test {
+ protected:
+ JsSyncEncryptionHandlerObserverTest() {
+ js_sync_encryption_handler_observer_.SetJsEventHandler(
+ mock_js_event_handler_.AsWeakHandle());
+ }
+
+ private:
+ // This must be destroyed after the member variables below in order
+ // for WeakHandles to be destroyed properly.
+ MessageLoop message_loop_;
+
+ protected:
+ StrictMock<MockJsEventHandler> mock_js_event_handler_;
+ JsSyncEncryptionHandlerObserver js_sync_encryption_handler_observer_;
+
+ void PumpLoop() {
+ message_loop_.RunAllPending();
+ }
+};
+
+TEST_F(JsSyncEncryptionHandlerObserverTest, NoArgNotifiations) {
+ InSequence dummy;
+
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onEncryptionComplete",
+ HasDetails(JsEventDetails())));
+
+ js_sync_encryption_handler_observer_.OnEncryptionComplete();
+ PumpLoop();
+}
+
+TEST_F(JsSyncEncryptionHandlerObserverTest, OnPassphraseRequired) {
+ InSequence dummy;
+
+ DictionaryValue reason_passphrase_not_required_details;
+ DictionaryValue reason_encryption_details;
+ DictionaryValue reason_decryption_details;
+
+ reason_passphrase_not_required_details.SetString(
+ "reason",
+ PassphraseRequiredReasonToString(REASON_PASSPHRASE_NOT_REQUIRED));
+ reason_encryption_details.SetString(
+ "reason",
+ PassphraseRequiredReasonToString(REASON_ENCRYPTION));
+ reason_decryption_details.SetString(
+ "reason",
+ PassphraseRequiredReasonToString(REASON_DECRYPTION));
+
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onPassphraseRequired",
+ HasDetailsAsDictionary(
+ reason_passphrase_not_required_details)));
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onPassphraseRequired",
+ HasDetailsAsDictionary(reason_encryption_details)));
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onPassphraseRequired",
+ HasDetailsAsDictionary(reason_decryption_details)));
+
+ js_sync_encryption_handler_observer_.OnPassphraseRequired(
+ REASON_PASSPHRASE_NOT_REQUIRED,
+ sync_pb::EncryptedData());
+ js_sync_encryption_handler_observer_.OnPassphraseRequired(REASON_ENCRYPTION,
+ sync_pb::EncryptedData());
+ js_sync_encryption_handler_observer_.OnPassphraseRequired(REASON_DECRYPTION,
+ sync_pb::EncryptedData());
+ PumpLoop();
+}
+
+TEST_F(JsSyncEncryptionHandlerObserverTest, SensitiveNotifiations) {
+ DictionaryValue redacted_token_details;
+ redacted_token_details.SetString("token", "<redacted>");
+ DictionaryValue redacted_bootstrap_token_details;
+ redacted_bootstrap_token_details.SetString("bootstrapToken", "<redacted>");
+
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent(
+ "OnBootstrapTokenUpdated",
+ HasDetailsAsDictionary(redacted_bootstrap_token_details)));
+
+ js_sync_encryption_handler_observer_.OnBootstrapTokenUpdated(
+ "sensitive_token");
+ PumpLoop();
+}
+
+TEST_F(JsSyncEncryptionHandlerObserverTest, OnEncryptedTypesChanged) {
+ DictionaryValue expected_details;
+ ListValue* encrypted_type_values = new ListValue();
+ const bool encrypt_everything = false;
+ expected_details.Set("encryptedTypes", encrypted_type_values);
+ expected_details.SetBoolean("encryptEverything", encrypt_everything);
+ ModelTypeSet encrypted_types;
+
+ for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
+ ModelType type = ModelTypeFromInt(i);
+ encrypted_types.Put(type);
+ encrypted_type_values->Append(Value::CreateStringValue(
+ ModelTypeToString(type)));
+ }
+
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onEncryptedTypesChanged",
+ HasDetailsAsDictionary(expected_details)));
+
+ js_sync_encryption_handler_observer_.OnEncryptedTypesChanged(
+ encrypted_types, encrypt_everything);
+ PumpLoop();
+}
+
+
+TEST_F(JsSyncEncryptionHandlerObserverTest, OnCryptographerStateChanged) {
+ DictionaryValue expected_details;
+ bool expected_ready = false;
+ bool expected_pending = false;
+ expected_details.SetBoolean("ready", expected_ready);
+ expected_details.SetBoolean("hasPendingKeys", expected_pending);
+ ModelTypeSet encrypted_types;
+
+ EXPECT_CALL(mock_js_event_handler_,
+ HandleJsEvent("onCryptographerStateChanged",
+ HasDetailsAsDictionary(expected_details)));
+
+ FakeEncryptor encryptor;
+ Cryptographer cryptographer(&encryptor);
+
+ js_sync_encryption_handler_observer_.OnCryptographerStateChanged(
+ &cryptographer);
+ PumpLoop();
+}
+
+} // namespace
+} // namespace syncer
diff --git a/sync/internal_api/js_sync_manager_observer.cc b/sync/internal_api/js_sync_manager_observer.cc
index f017107..dd7c7a9 100644
--- a/sync/internal_api/js_sync_manager_observer.cc
+++ b/sync/internal_api/js_sync_manager_observer.cc
@@ -59,58 +59,6 @@ void JsSyncManagerObserver::OnUpdatedToken(const std::string& token) {
HandleJsEvent(FROM_HERE, "onUpdatedToken", JsEventDetails(&details));
}
-void JsSyncManagerObserver::OnPassphraseRequired(
- PassphraseRequiredReason reason,
- const sync_pb::EncryptedData& pending_keys) {
- if (!event_handler_.IsInitialized()) {
- return;
- }
- DictionaryValue details;
- details.SetString("reason",
- PassphraseRequiredReasonToString(reason));
- HandleJsEvent(FROM_HERE, "onPassphraseRequired", JsEventDetails(&details));
-}
-
-void JsSyncManagerObserver::OnPassphraseAccepted() {
- if (!event_handler_.IsInitialized()) {
- return;
- }
- DictionaryValue details;
- HandleJsEvent(FROM_HERE, "onPassphraseAccepted", JsEventDetails(&details));
-}
-
-void JsSyncManagerObserver::OnBootstrapTokenUpdated(
- const std::string& boostrap_token) {
- if (!event_handler_.IsInitialized()) {
- return;
- }
- DictionaryValue details;
- details.SetString("bootstrapToken", "<redacted>");
- HandleJsEvent(FROM_HERE, "OnBootstrapTokenUpdated", JsEventDetails(&details));
-}
-
-void JsSyncManagerObserver::OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) {
- if (!event_handler_.IsInitialized()) {
- return;
- }
- DictionaryValue details;
- details.Set("encryptedTypes",
- ModelTypeSetToValue(encrypted_types));
- details.SetBoolean("encryptEverything", encrypt_everything);
- HandleJsEvent(FROM_HERE,
- "onEncryptedTypesChanged", JsEventDetails(&details));
-}
-
-void JsSyncManagerObserver::OnEncryptionComplete() {
- if (!event_handler_.IsInitialized()) {
- return;
- }
- DictionaryValue details;
- HandleJsEvent(FROM_HERE, "onEncryptionComplete", JsEventDetails());
-}
-
void JsSyncManagerObserver::OnActionableError(
const SyncProtocolError& sync_error) {
if (!event_handler_.IsInitialized()) {
diff --git a/sync/internal_api/js_sync_manager_observer.h b/sync/internal_api/js_sync_manager_observer.h
index 8d0622c..be58f4a 100644
--- a/sync/internal_api/js_sync_manager_observer.h
+++ b/sync/internal_api/js_sync_manager_observer.h
@@ -35,16 +35,6 @@ class JsSyncManagerObserver : public SyncManager::Observer {
const sessions::SyncSessionSnapshot& snapshot) OVERRIDE;
virtual void OnConnectionStatusChange(ConnectionStatus status) OVERRIDE;
virtual void OnUpdatedToken(const std::string& token) OVERRIDE;
- virtual void OnPassphraseRequired(
- PassphraseRequiredReason reason,
- const sync_pb::EncryptedData& pending_keys) OVERRIDE;
- virtual void OnPassphraseAccepted() OVERRIDE;
- virtual void OnBootstrapTokenUpdated(
- const std::string& bootstrap_token) OVERRIDE;
- virtual void OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) OVERRIDE;
- virtual void OnEncryptionComplete() OVERRIDE;
virtual void OnInitializationComplete(
const WeakHandle<JsBackend>& js_backend, bool success,
syncer::ModelTypeSet restored_types) OVERRIDE;
diff --git a/sync/internal_api/js_sync_manager_observer_unittest.cc b/sync/internal_api/js_sync_manager_observer_unittest.cc
index 5e51bf4..4cde1f6 100644
--- a/sync/internal_api/js_sync_manager_observer_unittest.cc
+++ b/sync/internal_api/js_sync_manager_observer_unittest.cc
@@ -50,12 +50,8 @@ TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) {
EXPECT_CALL(mock_js_event_handler_,
HandleJsEvent("onStopSyncingPermanently",
HasDetails(JsEventDetails())));
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent("onEncryptionComplete",
- HasDetails(JsEventDetails())));
js_sync_manager_observer_.OnStopSyncingPermanently();
- js_sync_manager_observer_.OnEncryptionComplete();
PumpLoop();
}
@@ -133,44 +129,6 @@ TEST_F(JsSyncManagerObserverTest, OnConnectionStatusChange) {
PumpLoop();
}
-TEST_F(JsSyncManagerObserverTest, OnPassphraseRequired) {
- InSequence dummy;
-
- DictionaryValue reason_passphrase_not_required_details;
- DictionaryValue reason_encryption_details;
- DictionaryValue reason_decryption_details;
-
- reason_passphrase_not_required_details.SetString(
- "reason",
- PassphraseRequiredReasonToString(REASON_PASSPHRASE_NOT_REQUIRED));
- reason_encryption_details.SetString(
- "reason",
- PassphraseRequiredReasonToString(REASON_ENCRYPTION));
- reason_decryption_details.SetString(
- "reason",
- PassphraseRequiredReasonToString(REASON_DECRYPTION));
-
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent("onPassphraseRequired",
- HasDetailsAsDictionary(
- reason_passphrase_not_required_details)));
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent("onPassphraseRequired",
- HasDetailsAsDictionary(reason_encryption_details)));
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent("onPassphraseRequired",
- HasDetailsAsDictionary(reason_decryption_details)));
-
- js_sync_manager_observer_.OnPassphraseRequired(
- REASON_PASSPHRASE_NOT_REQUIRED,
- sync_pb::EncryptedData());
- js_sync_manager_observer_.OnPassphraseRequired(REASON_ENCRYPTION,
- sync_pb::EncryptedData());
- js_sync_manager_observer_.OnPassphraseRequired(REASON_DECRYPTION,
- sync_pb::EncryptedData());
- PumpLoop();
-}
-
TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) {
DictionaryValue redacted_token_details;
redacted_token_details.SetString("token", "<redacted>");
@@ -180,37 +138,8 @@ TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) {
EXPECT_CALL(mock_js_event_handler_,
HandleJsEvent("onUpdatedToken",
HasDetailsAsDictionary(redacted_token_details)));
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent(
- "OnBootstrapTokenUpdated",
- HasDetailsAsDictionary(redacted_bootstrap_token_details)));
js_sync_manager_observer_.OnUpdatedToken("sensitive_token");
- js_sync_manager_observer_.OnBootstrapTokenUpdated("sensitive_token");
- PumpLoop();
-}
-
-TEST_F(JsSyncManagerObserverTest, OnEncryptedTypesChanged) {
- DictionaryValue expected_details;
- ListValue* encrypted_type_values = new ListValue();
- const bool encrypt_everything = false;
- expected_details.Set("encryptedTypes", encrypted_type_values);
- expected_details.SetBoolean("encryptEverything", encrypt_everything);
- ModelTypeSet encrypted_types;
-
- for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
- ModelType type = ModelTypeFromInt(i);
- encrypted_types.Put(type);
- encrypted_type_values->Append(Value::CreateStringValue(
- ModelTypeToString(type)));
- }
-
- EXPECT_CALL(mock_js_event_handler_,
- HandleJsEvent("onEncryptedTypesChanged",
- HasDetailsAsDictionary(expected_details)));
-
- js_sync_manager_observer_.OnEncryptedTypesChanged(
- encrypted_types, encrypt_everything);
PumpLoop();
}
diff --git a/sync/internal_api/public/sync_encryption_handler.cc b/sync/internal_api/public/sync_encryption_handler.cc
new file mode 100644
index 0000000..d2b1ca2
--- /dev/null
+++ b/sync/internal_api/public/sync_encryption_handler.cc
@@ -0,0 +1,25 @@
+// 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/internal_api/public/sync_encryption_handler.h"
+
+namespace syncer {
+
+SyncEncryptionHandler::Observer::Observer() {}
+SyncEncryptionHandler::Observer::~Observer() {}
+
+SyncEncryptionHandler::SyncEncryptionHandler() {}
+SyncEncryptionHandler::~SyncEncryptionHandler() {}
+
+// Static.
+ModelTypeSet SyncEncryptionHandler::SensitiveTypes() {
+ // Both of these have their own encryption schemes, but we include them
+ // anyways.
+ ModelTypeSet types;
+ types.Put(PASSWORDS);
+ types.Put(NIGORI);
+ return types;
+}
+
+} // namespace syncer
diff --git a/sync/internal_api/public/sync_encryption_handler.h b/sync/internal_api/public/sync_encryption_handler.h
new file mode 100644
index 0000000..52e2ca3
--- /dev/null
+++ b/sync/internal_api/public/sync_encryption_handler.h
@@ -0,0 +1,151 @@
+// 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.
+
+#ifndef SYNC_INTERNAL_API_PUBLIC_SYNC_ENCRYPTION_HANDLER_H_
+#define SYNC_INTERNAL_API_PUBLIC_SYNC_ENCRYPTION_HANDLER_H_
+
+#include <string>
+
+#include "sync/internal_api/public/base/model_type.h"
+
+namespace sync_pb {
+class EncryptedData;
+}
+
+namespace syncer {
+
+class Cryptographer;
+
+// Reasons due to which Cryptographer might require a passphrase.
+enum PassphraseRequiredReason {
+ REASON_PASSPHRASE_NOT_REQUIRED = 0, // Initial value.
+ REASON_ENCRYPTION = 1, // The cryptographer requires a
+ // passphrase for its first attempt at
+ // encryption. Happens only during
+ // migration or upgrade.
+ REASON_DECRYPTION = 2, // The cryptographer requires a
+ // passphrase for its first attempt at
+ // decryption.
+};
+
+// Sync's encryption handler. Handles tracking encrypted types, ensuring the
+// cryptographer encrypts with the proper key and has the most recent keybag,
+// and keeps the nigori node up to date.
+class SyncEncryptionHandler {
+ public:
+ // All Observer methods are done synchronously from within a transaction and
+ // on the sync thread.
+ class Observer {
+ public:
+ Observer();
+
+ // Called when user interaction is required to obtain a valid passphrase.
+ // - If the passphrase is required for encryption, |reason| will be
+ // REASON_ENCRYPTION.
+ // - If the passphrase is required for the decryption of data that has
+ // already been encrypted, |reason| will be REASON_DECRYPTION.
+ // - If the passphrase is required because decryption failed, and a new
+ // passphrase is required, |reason| will be REASON_SET_PASSPHRASE_FAILED.
+ //
+ // |pending_keys| is a copy of the cryptographer's pending keys, that may be
+ // cached by the frontend for subsequent use by the UI.
+ virtual void OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) = 0;
+ // Called when the passphrase provided by the user has been accepted and is
+ // now used to encrypt sync data.
+
+ virtual void OnPassphraseAccepted() = 0;
+ // |bootstrap_token| is an opaque base64 encoded representation of the key
+ // generated by the current passphrase, and is provided to the observer for
+ // persistence purposes and use in a future initialization of sync (e.g.
+ // after restart). The boostrap token will always be derived from the most
+ // recent GAIA password (for accounts with implicit passphrases), even if
+ // the data is still encrypted with an older GAIA password. For accounts
+ // with explicit passphrases, it will be the most recently seen custom
+ // passphrase.
+ virtual void OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) = 0;
+
+ // Called when the set of encrypted types or the encrypt
+ // everything flag has been changed. Note that encryption isn't
+ // complete until the OnEncryptionComplete() notification has been
+ // sent (see below).
+ //
+ // |encrypted_types| will always be a superset of
+ // Cryptographer::SensitiveTypes(). If |encrypt_everything| is
+ // true, |encrypted_types| will be the set of all known types.
+ //
+ // Until this function is called, observers can assume that the
+ // set of encrypted types is Cryptographer::SensitiveTypes() and
+ // that the encrypt everything flag is false.
+ virtual void OnEncryptedTypesChanged(
+ ModelTypeSet encrypted_types,
+ bool encrypt_everything) = 0;
+
+ // Called after we finish encrypting the current set of encrypted
+ // types.
+ virtual void OnEncryptionComplete() = 0;
+
+ // The cryptographer has been updated. Listeners should check that their
+ // own state matches the cryptographer.
+ // Used primarily for debugging.
+ virtual void OnCryptographerStateChanged(Cryptographer* cryptographer) = 0;
+
+ protected:
+ virtual ~Observer();
+ };
+
+ SyncEncryptionHandler();
+ virtual ~SyncEncryptionHandler();
+
+ // Add/Remove SyncEncryptionHandler::Observer's.
+ // Must be called from sync thread.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
+ // Reads the nigori node, updates internal state as needed, and, if an
+ // empty/stale nigori node is detected, overwrites the existing
+ // nigori node. Upon completion, if the cryptographer is still ready
+ // attempts to re-encrypt all sync data.
+ // Note: This method is expensive (it iterates through all encrypted types),
+ // so should only be used sparingly (e.g. on startup).
+ virtual void Init() = 0;
+
+ // Attempts to re-encrypt encrypted data types using the passphrase provided.
+ // Notifies observers of the result of the operation via OnPassphraseAccepted
+ // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
+ // appropriate. If an explicit password has been set previously, we drop
+ // subsequent requests to set a passphrase. If the cryptographer has pending
+ // keys, and a new implicit passphrase is provided, we try decrypting the
+ // pending keys with it, and if that fails, we cache the passphrase for
+ // re-encryption once the pending keys are decrypted.
+ virtual void SetEncryptionPassphrase(const std::string& passphrase,
+ bool is_explicit) = 0;
+
+ // Provides a passphrase for decrypting the user's existing sync data.
+ // Notifies observers of the result of the operation via OnPassphraseAccepted
+ // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
+ // appropriate if there is a previously cached encryption passphrase. It is an
+ // error to call this when we don't have pending keys.
+ virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0;
+
+ // Enables encryption of all datatypes.
+ virtual void EnableEncryptEverything() = 0;
+
+ // Whether encryption of all datatypes is enabled. If false, only sensitive
+ // types are encrypted.
+ virtual bool EncryptEverythingEnabled() const = 0;
+
+ // Whether the account requires a user-provided passphrase to decrypt
+ // encrypted data.
+ virtual bool IsUsingExplicitPassphrase() const = 0;
+
+ // The set of types that are always encrypted.
+ static ModelTypeSet SensitiveTypes();
+};
+
+} // namespace syncer
+
+#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_ENCRYPTION_HANDLER_H_
diff --git a/sync/internal_api/public/sync_manager.h b/sync/internal_api/public/sync_manager.h
index 8e64f32..5facc1e 100644
--- a/sync/internal_api/public/sync_manager.h
+++ b/sync/internal_api/public/sync_manager.h
@@ -20,6 +20,7 @@
#include "sync/internal_api/public/configure_reason.h"
#include "sync/internal_api/public/engine/model_safe_worker.h"
#include "sync/internal_api/public/engine/sync_status.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
#include "sync/internal_api/public/util/report_unrecoverable_error_function.h"
#include "sync/internal_api/public/util/weak_handle.h"
#include "sync/notifier/invalidation_util.h"
@@ -39,6 +40,7 @@ class HttpPostProviderFactory;
class InternalComponentsFactory;
class JsBackend;
class JsEventHandler;
+class SyncEncryptionHandler;
class SyncNotifier;
class SyncNotifierObserver;
class SyncScheduler;
@@ -56,19 +58,6 @@ enum ConnectionStatus {
CONNECTION_SERVER_ERROR
};
-// Reasons due to which Cryptographer might require a passphrase.
-enum PassphraseRequiredReason {
- REASON_PASSPHRASE_NOT_REQUIRED = 0, // Initial value.
- REASON_ENCRYPTION = 1, // The cryptographer requires a
- // passphrase for its first attempt at
- // encryption. Happens only during
- // migration or upgrade.
- REASON_DECRYPTION = 2, // The cryptographer requires a
- // passphrase for its first attempt at
- // decryption.
-};
-
-
// Contains everything needed to talk to and identify a user account.
struct SyncCredentials {
std::string email;
@@ -187,35 +176,6 @@ class SyncManager {
// Called when a new auth token is provided by the sync server.
virtual void OnUpdatedToken(const std::string& token) = 0;
- // Called when user interaction is required to obtain a valid passphrase.
- // - If the passphrase is required for encryption, |reason| will be
- // REASON_ENCRYPTION.
- // - If the passphrase is required for the decryption of data that has
- // already been encrypted, |reason| will be REASON_DECRYPTION.
- // - If the passphrase is required because decryption failed, and a new
- // passphrase is required, |reason| will be REASON_SET_PASSPHRASE_FAILED.
- //
- // |pending_keys| is a copy of the cryptographer's pending keys, that may be
- // cached by the frontend for subsequent use by the UI.
- virtual void OnPassphraseRequired(
- PassphraseRequiredReason reason,
- const sync_pb::EncryptedData& pending_keys) = 0;
-
- // Called when the passphrase provided by the user has been accepted and is
- // now used to encrypt sync data.
- virtual void OnPassphraseAccepted() = 0;
-
- // |bootstrap_token| is an opaque base64 encoded representation of the key
- // generated by the current passphrase, and is provided to the observer for
- // persistence purposes and use in a future initialization of sync (e.g.
- // after restart). The boostrap token will always be derived from the most
- // recent GAIA password (for accounts with implicit passphrases), even if
- // the data is still encrypted with an older GAIA password. For accounts
- // with explicit passphrases, it will be the most recently seen custom
- // passphrase.
- virtual void OnBootstrapTokenUpdated(
- const std::string& bootstrap_token) = 0;
-
// Called when initialization is complete to the point that SyncManager can
// process changes. This does not necessarily mean authentication succeeded
// or that the SyncManager is online.
@@ -304,30 +264,6 @@ class SyncManager {
// global stop syncing operation has wiped the store.
virtual void OnStopSyncingPermanently() = 0;
- // Called when the set of encrypted types or the encrypt
- // everything flag has been changed. Note that encryption isn't
- // complete until the OnEncryptionComplete() notification has been
- // sent (see below).
- //
- // |encrypted_types| will always be a superset of
- // Cryptographer::SensitiveTypes(). If |encrypt_everything| is
- // true, |encrypted_types| will be the set of all known types.
- //
- // Until this function is called, observers can assume that the
- // set of encrypted types is Cryptographer::SensitiveTypes() and
- // that the encrypt everything flag is false.
- //
- // Called from within a transaction.
- virtual void OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) = 0;
-
- // Called after we finish encrypting the current set of encrypted
- // types.
- //
- // Called from within a transaction.
- virtual void OnEncryptionComplete() = 0;
-
virtual void OnActionableError(
const SyncProtocolError& sync_protocol_error) = 0;
@@ -423,24 +359,6 @@ class SyncManager {
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) = 0;
- // Attempts to re-encrypt encrypted data types using the passphrase provided.
- // Notifies observers of the result of the operation via OnPassphraseAccepted
- // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
- // appropriate. If an explicit password has been set previously, we drop
- // subsequent requests to set a passphrase. If the cryptographer has pending
- // keys, and a new implicit passphrase is provided, we try decrypting the
- // pending keys with it, and if that fails, we cache the passphrase for
- // re-encryption once the pending keys are decrypted.
- virtual void SetEncryptionPassphrase(const std::string& passphrase,
- bool is_explicit) = 0;
-
- // Provides a passphrase for decrypting the user's existing sync data.
- // Notifies observers of the result of the operation via OnPassphraseAccepted
- // or OnPassphraseRequired, updates the nigori node, and does re-encryption as
- // appropriate if there is a previously cached encryption passphrase. It is an
- // error to call this when we don't have pending keys.
- virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0;
-
// Switches the mode of operation to CONFIGURATION_MODE and performs
// any configuration tasks needed as determined by the params. Once complete,
// syncer will remain in CONFIGURATION_MODE until StartSyncingNormally is
@@ -469,10 +387,6 @@ class SyncManager {
// Status-related getter. May be called on any thread.
virtual SyncStatus GetDetailedStatus() const = 0;
- // Whether or not the Nigori node is encrypted using an explicit passphrase.
- // May be called on any thread.
- virtual bool IsUsingExplicitPassphrase() = 0;
-
// Extracts the keystore encryption bootstrap token if a keystore key existed.
// Returns true if bootstrap token successfully extracted, false otherwise.
virtual bool GetKeystoreKeyBootstrapToken(std::string* token) = 0;
@@ -498,30 +412,6 @@ class SyncManager {
// May be called from any thread.
virtual UserShare* GetUserShare() = 0;
- // Inform the cryptographer of the most recent passphrase and set of
- // encrypted types (from nigori node), then ensure all data that
- // needs encryption is encrypted with the appropriate passphrase.
- //
- // May trigger OnPassphraseRequired(). Otherwise, it will trigger
- // OnEncryptedTypesChanged() if necessary (see comments for
- // OnEncryptedTypesChanged()), and then OnEncryptionComplete().
- //
- // Also updates or adds device information to the nigori node.
- //
- // Note: opens a transaction, so must only be called after syncapi
- // has been initialized.
- virtual void RefreshNigori(const std::string& chrome_version,
- const base::Closure& done_callback) = 0;
-
- // Enable encryption of all sync data. Once enabled, it can never be
- // disabled without clearing the server data.
- //
- // This will trigger OnEncryptedTypesChanged() if necessary (see
- // comments for OnEncryptedTypesChanged()). It then may trigger
- // OnPassphraseRequired(), but otherwise it will trigger
- // OnEncryptionComplete().
- virtual void EnableEncryptEverything() = 0;
-
// Reads the nigori node to determine if any experimental features should
// be enabled.
// Note: opens a transaction. May be called on any thread.
@@ -530,6 +420,9 @@ class SyncManager {
// Uses a read-only transaction to determine if the directory being synced has
// any remaining unsynced items. May be called on any thread.
virtual bool HasUnsyncedItems() = 0;
+
+ // Returns the SyncManager's encryption handler.
+ virtual SyncEncryptionHandler* GetEncryptionHandler() = 0;
};
} // namespace syncer
diff --git a/sync/internal_api/public/test/fake_sync_manager.h b/sync/internal_api/public/test/fake_sync_manager.h
index dcd877a..91a25d1 100644
--- a/sync/internal_api/public/test/fake_sync_manager.h
+++ b/sync/internal_api/public/test/fake_sync_manager.h
@@ -18,6 +18,8 @@ class SequencedTaskRunner;
namespace syncer {
+class FakeSyncEncryptionHandler;
+
class FakeSyncManager : public SyncManager {
public:
// |initial_sync_ended_types|: The set of types that have initial_sync_ended
@@ -103,9 +105,6 @@ class FakeSyncManager : public SyncManager {
SyncNotifierObserver* handler) OVERRIDE;
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) OVERRIDE;
- virtual void SetEncryptionPassphrase(const std::string& passphrase,
- bool is_explicit) OVERRIDE;
- virtual void SetDecryptionPassphrase(const std::string& passphrase) OVERRIDE;
virtual void ConfigureSyncer(
ConfigureReason reason,
const ModelTypeSet& types_to_config,
@@ -115,17 +114,14 @@ class FakeSyncManager : public SyncManager {
virtual void AddObserver(Observer* observer) OVERRIDE;
virtual void RemoveObserver(Observer* observer) OVERRIDE;
virtual SyncStatus GetDetailedStatus() const OVERRIDE;
- virtual bool IsUsingExplicitPassphrase() OVERRIDE;
virtual bool GetKeystoreKeyBootstrapToken(std::string* token) OVERRIDE;
virtual void SaveChanges() OVERRIDE;
virtual void StopSyncingForShutdown(const base::Closure& callback) OVERRIDE;
virtual void ShutdownOnSyncThread() OVERRIDE;
virtual UserShare* GetUserShare() OVERRIDE;
- virtual void RefreshNigori(const std::string& chrome_version,
- const base::Closure& done_callback) OVERRIDE;
- virtual void EnableEncryptEverything() OVERRIDE;
virtual bool ReceivedExperiment(Experiments* experiments) OVERRIDE;
virtual bool HasUnsyncedItems() OVERRIDE;
+ virtual SyncEncryptionHandler* GetEncryptionHandler() OVERRIDE;
private:
void InvalidateOnSyncThread(
@@ -156,6 +152,8 @@ class FakeSyncManager : public SyncManager {
// Faked notifier state.
SyncNotifierRegistrar registrar_;
+ scoped_ptr<FakeSyncEncryptionHandler> fake_encryption_handler_;
+
DISALLOW_COPY_AND_ASSIGN(FakeSyncManager);
};
diff --git a/sync/internal_api/public/util/sync_string_conversions.h b/sync/internal_api/public/util/sync_string_conversions.h
index 45286ca..1c55898 100644
--- a/sync/internal_api/public/util/sync_string_conversions.h
+++ b/sync/internal_api/public/util/sync_string_conversions.h
@@ -5,6 +5,7 @@
#ifndef SYNC_INTERNAL_API_PUBLIC_UTIL_SYNC_STRING_CONVERSIONS_H_
#define SYNC_INTERNAL_API_PUBLIC_UTIL_SYNC_STRING_CONVERSIONS_H_
+#include "sync/internal_api/public/sync_encryption_handler.h"
#include "sync/internal_api/public/sync_manager.h"
namespace syncer {
diff --git a/sync/internal_api/sync_encryption_handler_impl.cc b/sync/internal_api/sync_encryption_handler_impl.cc
new file mode 100644
index 0000000..6ebe4a9
--- /dev/null
+++ b/sync/internal_api/sync_encryption_handler_impl.cc
@@ -0,0 +1,678 @@
+// 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/internal_api/sync_encryption_handler_impl.h"
+
+#include <queue>
+#include <string>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/tracked_objects.h"
+#include "base/metrics/histogram.h"
+#include "sync/internal_api/public/read_node.h"
+#include "sync/internal_api/public/read_transaction.h"
+#include "sync/internal_api/public/user_share.h"
+#include "sync/internal_api/public/util/experiments.h"
+#include "sync/internal_api/public/write_node.h"
+#include "sync/internal_api/public/write_transaction.h"
+#include "sync/protocol/encryption.pb.h"
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/syncable/base_transaction.h"
+#include "sync/syncable/directory.h"
+#include "sync/syncable/entry.h"
+#include "sync/syncable/nigori_util.h"
+#include "sync/util/cryptographer.h"
+
+namespace syncer {
+
+namespace {
+// The maximum number of times we will automatically overwrite the nigori node
+// because the encryption keys don't match (per chrome instantiation).
+// We protect ourselves against nigori rollbacks, but it's possible two
+// different clients might have contrasting view of what the nigori node state
+// should be, in which case they might ping pong (see crbug.com/119207).
+static const int kNigoriOverwriteLimit = 10;
+}
+
+SyncEncryptionHandlerImpl::SyncEncryptionHandlerImpl(
+ UserShare* user_share,
+ Cryptographer* cryptographer)
+ : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ user_share_(user_share),
+ cryptographer_(cryptographer),
+ encrypted_types_(SensitiveTypes()),
+ encrypt_everything_(false),
+ explicit_passphrase_(false),
+ nigori_overwrite_count_(0) {
+}
+
+SyncEncryptionHandlerImpl::~SyncEncryptionHandlerImpl() {}
+
+void SyncEncryptionHandlerImpl::AddObserver(Observer* observer) {
+ DCHECK(!observers_.HasObserver(observer));
+ observers_.AddObserver(observer);
+}
+
+void SyncEncryptionHandlerImpl::RemoveObserver(Observer* observer) {
+ DCHECK(observers_.HasObserver(observer));
+ observers_.RemoveObserver(observer);
+}
+
+void SyncEncryptionHandlerImpl::Init() {
+ WriteTransaction trans(FROM_HERE, user_share_);
+ WriteNode node(&trans);
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ cryptographer_ = cryptographer;
+
+ if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK)
+ return;
+ if (!ApplyNigoriUpdateImpl(node.GetNigoriSpecifics(),
+ trans.GetWrappedTrans())) {
+ WriteEncryptionStateToNigori(&trans);
+ }
+
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnCryptographerStateChanged(cryptographer));
+
+ // If the cryptographer is not ready (either it has pending keys or we
+ // failed to initialize it), we don't want to try and re-encrypt the data.
+ // If we had encrypted types, the DataTypeManager will block, preventing
+ // sync from happening until the the passphrase is provided.
+ if (cryptographer->is_ready())
+ ReEncryptEverything(&trans);
+}
+
+// Note: this is called from within a syncable transaction, so we need to post
+// tasks if we want to do any work that creates a new sync_api transaction.
+void SyncEncryptionHandlerImpl::ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) {
+ DCHECK(trans);
+ if (!ApplyNigoriUpdateImpl(nigori, trans)) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SyncEncryptionHandlerImpl::RewriteNigori,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnCryptographerStateChanged(cryptographer_));
+}
+
+// Note: this is always called via the Cryptographer interface right now,
+// so a transaction is already held. Once we remove that interface, we'll
+// need to enforce holding a transaction when calling this method.
+ModelTypeSet SyncEncryptionHandlerImpl::GetEncryptedTypes() const {
+ return encrypted_types_;
+}
+
+void SyncEncryptionHandlerImpl::SetEncryptionPassphrase(
+ const std::string& passphrase,
+ bool is_explicit) {
+ // We do not accept empty passphrases.
+ if (passphrase.empty()) {
+ NOTREACHED() << "Cannot encrypt with an empty passphrase.";
+ return;
+ }
+
+ // All accesses to the cryptographer are protected by a transaction.
+ WriteTransaction trans(FROM_HERE, user_share_);
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ KeyParams key_params = {"localhost", "dummy", passphrase};
+ WriteNode node(&trans);
+ if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
+ NOTREACHED();
+ return;
+ }
+
+ bool nigori_has_explicit_passphrase =
+ node.GetNigoriSpecifics().using_explicit_passphrase();
+ std::string bootstrap_token;
+ sync_pb::EncryptedData pending_keys;
+ if (cryptographer->has_pending_keys())
+ pending_keys = cryptographer->GetPendingKeys();
+ bool success = false;
+
+
+ // There are six cases to handle here:
+ // 1. The user has no pending keys and is setting their current GAIA password
+ // as the encryption passphrase. This happens either during first time sync
+ // with a clean profile, or after re-authenticating on a profile that was
+ // already signed in with the cryptographer ready.
+ // 2. The user has no pending keys, and is overwriting an (already provided)
+ // implicit passphrase with an explicit (custom) passphrase.
+ // 3. The user has pending keys for an explicit passphrase that is somehow set
+ // to their current GAIA passphrase.
+ // 4. The user has pending keys encrypted with their current GAIA passphrase
+ // and the caller passes in the current GAIA passphrase.
+ // 5. The user has pending keys encrypted with an older GAIA passphrase
+ // and the caller passes in the current GAIA passphrase.
+ // 6. The user has previously done encryption with an explicit passphrase.
+ // Furthermore, we enforce the fact that the bootstrap encryption token will
+ // always be derived from the newest GAIA password if the account is using
+ // an implicit passphrase (even if the data is encrypted with an old GAIA
+ // password). If the account is using an explicit (custom) passphrase, the
+ // bootstrap token will be derived from the most recently provided explicit
+ // passphrase (that was able to decrypt the data).
+ if (!nigori_has_explicit_passphrase) {
+ if (!cryptographer->has_pending_keys()) {
+ if (cryptographer->AddKey(key_params)) {
+ // Case 1 and 2. We set a new GAIA passphrase when there are no pending
+ // keys (1), or overwriting an implicit passphrase with a new explicit
+ // one (2) when there are no pending keys.
+ DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" )
+ << " passphrase for encryption.";
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ success = true;
+ } else {
+ NOTREACHED() << "Failed to add key to cryptographer.";
+ success = false;
+ }
+ } else { // cryptographer->has_pending_keys() == true
+ if (is_explicit) {
+ // This can only happen if the nigori node is updated with a new
+ // implicit passphrase while a client is attempting to set a new custom
+ // passphrase (race condition).
+ DVLOG(1) << "Failing because an implicit passphrase is already set.";
+ success = false;
+ } else { // is_explicit == false
+ if (cryptographer->DecryptPendingKeys(key_params)) {
+ // Case 4. We successfully decrypted with the implicit GAIA passphrase
+ // passed in.
+ DVLOG(1) << "Implicit internal passphrase accepted for decryption.";
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ success = true;
+ } else {
+ // Case 5. Encryption was done with an old GAIA password, but we were
+ // provided with the current GAIA password. We need to generate a new
+ // bootstrap token to preserve it. We build a temporary cryptographer
+ // to allow us to extract these params without polluting our current
+ // cryptographer.
+ DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding "
+ << "anyways as default passphrase and persisting via "
+ << "bootstrap token.";
+ Cryptographer temp_cryptographer(cryptographer->encryptor());
+ temp_cryptographer.AddKey(key_params);
+ temp_cryptographer.GetBootstrapToken(&bootstrap_token);
+ // We then set the new passphrase as the default passphrase of the
+ // real cryptographer, even though we have pending keys. This is safe,
+ // as although Cryptographer::is_initialized() will now be true,
+ // is_ready() will remain false due to having pending keys.
+ cryptographer->AddKey(key_params);
+ success = false;
+ }
+ } // is_explicit
+ } // cryptographer->has_pending_keys()
+ } else { // nigori_has_explicit_passphrase == true
+ // Case 6. We do not want to override a previously set explicit passphrase,
+ // so we return a failure.
+ DVLOG(1) << "Failing because an explicit passphrase is already set.";
+ success = false;
+ }
+
+ DVLOG_IF(1, !success)
+ << "Failure in SetEncryptionPassphrase; notifying and returning.";
+ DVLOG_IF(1, success)
+ << "Successfully set encryption passphrase; updating nigori and "
+ "reencrypting.";
+
+ FinishSetPassphrase(
+ success, bootstrap_token, is_explicit, &trans, &node);
+}
+
+void SyncEncryptionHandlerImpl::SetDecryptionPassphrase(
+ const std::string& passphrase) {
+ // We do not accept empty passphrases.
+ if (passphrase.empty()) {
+ NOTREACHED() << "Cannot decrypt with an empty passphrase.";
+ return;
+ }
+
+ // All accesses to the cryptographer are protected by a transaction.
+ WriteTransaction trans(FROM_HERE, user_share_);
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ KeyParams key_params = {"localhost", "dummy", passphrase};
+ WriteNode node(&trans);
+ if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
+ NOTREACHED();
+ return;
+ }
+
+ if (!cryptographer->has_pending_keys()) {
+ // Note that this *can* happen in a rare situation where data is
+ // re-encrypted on another client while a SetDecryptionPassphrase() call is
+ // in-flight on this client. It is rare enough that we choose to do nothing.
+ NOTREACHED() << "Attempt to set decryption passphrase failed because there "
+ << "were no pending keys.";
+ return;
+ }
+
+ bool nigori_has_explicit_passphrase =
+ node.GetNigoriSpecifics().using_explicit_passphrase();
+ std::string bootstrap_token;
+ sync_pb::EncryptedData pending_keys;
+ pending_keys = cryptographer->GetPendingKeys();
+ bool success = false;
+
+ // There are three cases to handle here:
+ // 7. We're using the current GAIA password to decrypt the pending keys. This
+ // happens when signing in to an account with a previously set implicit
+ // passphrase, where the data is already encrypted with the newest GAIA
+ // password.
+ // 8. The user is providing an old GAIA password to decrypt the pending keys.
+ // In this case, the user is using an implicit passphrase, but has changed
+ // their password since they last encrypted their data, and therefore
+ // their current GAIA password was unable to decrypt the data. This will
+ // happen when the user is setting up a new profile with a previously
+ // encrypted account (after changing passwords).
+ // 9. The user is providing a previously set explicit passphrase to decrypt
+ // the pending keys.
+ if (!nigori_has_explicit_passphrase) {
+ if (cryptographer->is_initialized()) {
+ // We only want to change the default encryption key to the pending
+ // one if the pending keybag already contains the current default.
+ // This covers the case where a different client re-encrypted
+ // everything with a newer gaia passphrase (and hence the keybag
+ // contains keys from all previously used gaia passphrases).
+ // Otherwise, we're in a situation where the pending keys are
+ // encrypted with an old gaia passphrase, while the default is the
+ // current gaia passphrase. In that case, we preserve the default.
+ Cryptographer temp_cryptographer(cryptographer->encryptor());
+ temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys());
+ if (temp_cryptographer.DecryptPendingKeys(key_params)) {
+ // Check to see if the pending bag of keys contains the current
+ // default key.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ if (temp_cryptographer.CanDecrypt(encrypted)) {
+ DVLOG(1) << "Implicit user provided passphrase accepted for "
+ << "decryption, overwriting default.";
+ // Case 7. The pending keybag contains the current default. Go ahead
+ // and update the cryptographer, letting the default change.
+ cryptographer->DecryptPendingKeys(key_params);
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ success = true;
+ } else {
+ // Case 8. The pending keybag does not contain the current default
+ // encryption key. We decrypt the pending keys here, and in
+ // FinishSetPassphrase, re-encrypt everything with the current GAIA
+ // passphrase instead of the passphrase just provided by the user.
+ DVLOG(1) << "Implicit user provided passphrase accepted for "
+ << "decryption, restoring implicit internal passphrase "
+ << "as default.";
+ std::string bootstrap_token_from_current_key;
+ cryptographer->GetBootstrapToken(
+ &bootstrap_token_from_current_key);
+ cryptographer->DecryptPendingKeys(key_params);
+ // Overwrite the default from the pending keys.
+ cryptographer->AddKeyFromBootstrapToken(
+ bootstrap_token_from_current_key);
+ success = true;
+ }
+ } else { // !temp_cryptographer.DecryptPendingKeys(..)
+ DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
+ success = false;
+ } // temp_cryptographer.DecryptPendingKeys(...)
+ } else { // cryptographer->is_initialized() == false
+ if (cryptographer->DecryptPendingKeys(key_params)) {
+ // This can happpen in two cases:
+ // - First time sync on android, where we'll never have a
+ // !user_provided passphrase.
+ // - This is a restart for a client that lost their bootstrap token.
+ // In both cases, we should go ahead and initialize the cryptographer
+ // and persist the new bootstrap token.
+ //
+ // Note: at this point, we cannot distinguish between cases 7 and 8
+ // above. This user provided passphrase could be the current or the
+ // old. But, as long as we persist the token, there's nothing more
+ // we can do.
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ DVLOG(1) << "Implicit user provided passphrase accepted, initializing"
+ << " cryptographer.";
+ success = true;
+ } else {
+ DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
+ success = false;
+ }
+ } // cryptographer->is_initialized()
+ } else { // nigori_has_explicit_passphrase == true
+ // Case 9. Encryption was done with an explicit passphrase, and we decrypt
+ // with the passphrase provided by the user.
+ if (cryptographer->DecryptPendingKeys(key_params)) {
+ DVLOG(1) << "Explicit passphrase accepted for decryption.";
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ success = true;
+ } else {
+ DVLOG(1) << "Explicit passphrase failed to decrypt.";
+ success = false;
+ }
+ } // nigori_has_explicit_passphrase
+
+ DVLOG_IF(1, !success)
+ << "Failure in SetDecryptionPassphrase; notifying and returning.";
+ DVLOG_IF(1, success)
+ << "Successfully set decryption passphrase; updating nigori and "
+ "reencrypting.";
+
+ FinishSetPassphrase(success,
+ bootstrap_token,
+ nigori_has_explicit_passphrase,
+ &trans,
+ &node);
+}
+
+void SyncEncryptionHandlerImpl::EnableEncryptEverything() {
+ if (encrypt_everything_) {
+ DCHECK(encrypted_types_.Equals(ModelTypeSet::All()));
+ return;
+ }
+ WriteTransaction trans(FROM_HERE, user_share_);
+ encrypt_everything_ = true;
+ // Change |encrypted_types_| directly to avoid sending more than one
+ // notification.
+ encrypted_types_ = ModelTypeSet::All();
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
+ WriteEncryptionStateToNigori(&trans);
+ ReEncryptEverything(&trans);
+}
+
+bool SyncEncryptionHandlerImpl::EncryptEverythingEnabled() const {
+ ReadTransaction trans(FROM_HERE, user_share_);
+ return encrypt_everything_;
+}
+
+bool SyncEncryptionHandlerImpl::IsUsingExplicitPassphrase() const {
+ ReadTransaction trans(FROM_HERE, user_share_);
+ return explicit_passphrase_;
+}
+
+// This function iterates over all encrypted types. There are many scenarios in
+// which data for some or all types is not currently available. In that case,
+// the lookup of the root node will fail and we will skip encryption for that
+// type.
+void SyncEncryptionHandlerImpl::ReEncryptEverything(
+ WriteTransaction* trans) {
+ Cryptographer* cryptographer = trans->GetCryptographer();
+ if (!cryptographer->is_ready())
+ return;
+ ModelTypeSet encrypted_types = GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = encrypted_types.First();
+ iter.Good(); iter.Inc()) {
+ if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
+ continue; // These types handle encryption differently.
+
+ ReadNode type_root(trans);
+ std::string tag = ModelTypeToRootTag(iter.Get());
+ if (type_root.InitByTagLookup(tag) != BaseNode::INIT_OK)
+ continue; // Don't try to reencrypt if the type's data is unavailable.
+
+ // 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) != BaseNode::INIT_OK) {
+ NOTREACHED();
+ continue;
+ }
+ if (child.GetIsFolder()) {
+ to_visit.push(child.GetFirstChildId());
+ }
+ if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) {
+ // Rewrite the specifics of the node with encrypted data if necessary
+ // (only rewrite the non-unique folders).
+ child.ResetFromSpecifics();
+ }
+ to_visit.push(child.GetSuccessorId());
+ }
+ }
+
+ // Passwords are encrypted with their own legacy scheme. Passwords are always
+ // encrypted so we don't need to check GetEncryptedTypes() here.
+ ReadNode passwords_root(trans);
+ std::string passwords_tag = ModelTypeToRootTag(PASSWORDS);
+ if (passwords_root.InitByTagLookup(passwords_tag) ==
+ BaseNode::INIT_OK) {
+ int64 child_id = passwords_root.GetFirstChildId();
+ while (child_id != kInvalidId) {
+ WriteNode child(trans);
+ if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK) {
+ NOTREACHED();
+ return;
+ }
+ child.SetPasswordSpecifics(child.GetPasswordSpecifics());
+ child_id = child.GetSuccessorId();
+ }
+ }
+
+ // NOTE: We notify from within a transaction.
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnEncryptionComplete());
+}
+
+bool SyncEncryptionHandlerImpl::ApplyNigoriUpdateImpl(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) {
+ Cryptographer* cryptographer = trans->directory()->GetCryptographer(trans);
+ bool nigori_types_need_update = !UpdateEncryptedTypesFromNigori(nigori);
+ if (nigori.using_explicit_passphrase())
+ explicit_passphrase_ = true;
+
+ bool nigori_needs_new_keys = false;
+ if (!nigori.encrypted().blob().empty()) {
+ if (cryptographer->CanDecrypt(nigori.encrypted())) {
+ cryptographer->InstallKeys(nigori.encrypted());
+ // We only update the default passphrase if this was a new explicit
+ // passphrase. Else, since it was decryptable, it must not have been a new
+ // key.
+ if (nigori.using_explicit_passphrase())
+ cryptographer->SetDefaultKey(nigori.encrypted().key_name());
+
+ // Check if the cryptographer's keybag is newer than the nigori's
+ // keybag. If so, we need to overwrite the nigori node.
+ sync_pb::EncryptedData new_keys = nigori.encrypted();
+ if (!cryptographer->GetKeys(&new_keys))
+ NOTREACHED();
+ if (nigori.encrypted().SerializeAsString() !=
+ new_keys.SerializeAsString())
+ nigori_needs_new_keys = true;
+ } else {
+ cryptographer->SetPendingKeys(nigori.encrypted());
+ }
+ } else {
+ nigori_needs_new_keys = true;
+ }
+
+ // If we've completed a sync cycle and the cryptographer isn't ready
+ // yet or has pending keys, prompt the user for a passphrase.
+ if (cryptographer->has_pending_keys()) {
+ DVLOG(1) << "OnPassphraseRequired Sent";
+ sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys();
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_DECRYPTION,
+ pending_keys));
+ } else if (!cryptographer->is_ready()) {
+ DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
+ << "ready";
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_ENCRYPTION,
+ sync_pb::EncryptedData()));
+ }
+
+ // Check if the current local encryption state is stricter/newer than the
+ // nigori state. If so, we need to overwrite the nigori node with the local
+ // state.
+ if (nigori.using_explicit_passphrase() != explicit_passphrase_ ||
+ nigori.encrypt_everything() != encrypt_everything_ ||
+ nigori_types_need_update ||
+ nigori_needs_new_keys) {
+ return false;
+ }
+ return true;
+}
+
+void SyncEncryptionHandlerImpl::RewriteNigori() {
+ WriteTransaction trans(FROM_HERE, user_share_);
+ WriteEncryptionStateToNigori(&trans);
+}
+
+void SyncEncryptionHandlerImpl::WriteEncryptionStateToNigori(
+ WriteTransaction* trans) {
+ WriteNode nigori_node(trans);
+ // This can happen in tests that don't have nigori nodes.
+ if (!nigori_node.InitByTagLookup(kNigoriTag) == BaseNode::INIT_OK)
+ return;
+ sync_pb::NigoriSpecifics nigori = nigori_node.GetNigoriSpecifics();
+ Cryptographer* cryptographer = trans->GetCryptographer();
+ if (cryptographer->is_ready() &&
+ nigori_overwrite_count_ < kNigoriOverwriteLimit) {
+ // Does not modify the encrypted blob if the unencrypted data already
+ // matches what is about to be written.
+ sync_pb::EncryptedData original_keys = nigori.encrypted();
+ if (!cryptographer->GetKeys(nigori.mutable_encrypted()))
+ NOTREACHED();
+
+ if (nigori.encrypted().SerializeAsString() !=
+ original_keys.SerializeAsString()) {
+ // We've updated the nigori node's encryption keys. In order to prevent
+ // a possible looping of two clients constantly overwriting each other,
+ // we limit the absolute number of overwrites per client instantiation.
+ nigori_overwrite_count_++;
+ UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites",
+ nigori_overwrite_count_);
+ }
+
+ // Note: we don't try to set using_explicit_passphrase here since if that
+ // is lost the user can always set it again. The main point is to preserve
+ // the encryption keys so all data remains decryptable.
+ }
+ syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_,
+ encrypt_everything_,
+ &nigori);
+
+ // If nothing has changed, this is a no-op.
+ nigori_node.SetNigoriSpecifics(nigori);
+}
+
+bool SyncEncryptionHandlerImpl::UpdateEncryptedTypesFromNigori(
+ const sync_pb::NigoriSpecifics& nigori) {
+ if (nigori.encrypt_everything()) {
+ if (!encrypt_everything_) {
+ encrypt_everything_ = true;
+ encrypted_types_ = ModelTypeSet::All();
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
+ }
+ DCHECK(encrypted_types_.Equals(ModelTypeSet::All()));
+ return true;
+ }
+
+ ModelTypeSet encrypted_types;
+ encrypted_types = syncable::GetEncryptedTypesFromNigori(nigori);
+ encrypted_types.PutAll(SensitiveTypes());
+
+ // If anything more than the sensitive types were encrypted, and
+ // encrypt_everything is not explicitly set to false, we assume it means
+ // a client intended to enable encrypt everything.
+ if (!nigori.has_encrypt_everything() &&
+ !Difference(encrypted_types, SensitiveTypes()).Empty()) {
+ if (!encrypt_everything_) {
+ encrypt_everything_ = true;
+ encrypted_types_ = ModelTypeSet::All();
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
+ }
+ DCHECK(encrypted_types_.Equals(ModelTypeSet::All()));
+ return false;
+ }
+
+ MergeEncryptedTypes(encrypted_types);
+ return encrypted_types_.Equals(encrypted_types);
+}
+
+void SyncEncryptionHandlerImpl::UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const {
+ syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_,
+ encrypt_everything_,
+ nigori);
+}
+
+void SyncEncryptionHandlerImpl::FinishSetPassphrase(
+ bool success,
+ const std::string& bootstrap_token,
+ bool is_explicit,
+ WriteTransaction* trans,
+ WriteNode* nigori_node) {
+ Cryptographer* cryptographer = trans->GetCryptographer();
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnCryptographerStateChanged(cryptographer));
+
+ // It's possible we need to change the bootstrap token even if we failed to
+ // set the passphrase (for example if we need to preserve the new GAIA
+ // passphrase).
+ if (!bootstrap_token.empty()) {
+ DVLOG(1) << "Bootstrap token updated.";
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnBootstrapTokenUpdated(bootstrap_token));
+ }
+
+ if (!success) {
+ if (cryptographer->is_ready()) {
+ LOG(ERROR) << "Attempt to change passphrase failed while cryptographer "
+ << "was ready.";
+ } else if (cryptographer->has_pending_keys()) {
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_DECRYPTION,
+ cryptographer->GetPendingKeys()));
+ } else {
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_ENCRYPTION,
+ sync_pb::EncryptedData()));
+ }
+ return;
+ }
+
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseAccepted());
+ DCHECK(cryptographer->is_ready());
+
+ sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics());
+ // Does not modify specifics.encrypted() if the original decrypted data was
+ // the same.
+ if (!cryptographer->GetKeys(specifics.mutable_encrypted())) {
+ NOTREACHED();
+ return;
+ }
+ explicit_passphrase_ = is_explicit;
+ specifics.set_using_explicit_passphrase(is_explicit);
+ nigori_node->SetNigoriSpecifics(specifics);
+
+ // Does nothing if everything is already encrypted or the cryptographer has
+ // pending keys.
+ ReEncryptEverything(trans);
+}
+
+void SyncEncryptionHandlerImpl::MergeEncryptedTypes(
+ ModelTypeSet encrypted_types) {
+ if (!encrypted_types_.HasAll(encrypted_types)) {
+ encrypted_types_ = encrypted_types;
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
+ }
+}
+
+} // namespace browser_sync
diff --git a/sync/internal_api/sync_encryption_handler_impl.h b/sync/internal_api/sync_encryption_handler_impl.h
new file mode 100644
index 0000000..6c605a8
--- /dev/null
+++ b/sync/internal_api/sync_encryption_handler_impl.h
@@ -0,0 +1,161 @@
+// 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.
+
+#ifndef SYNC_INTERNAL_API_SYNC_ENCRYPTION_HANDLER_IMPL_H_
+#define SYNC_INTERNAL_API_SYNC_ENCRYPTION_HANDLER_IMPL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
+#include "sync/syncable/nigori_handler.h"
+
+namespace syncer {
+
+struct UserShare;
+class WriteNode;
+class WriteTransaction;
+
+// Sync encryption handler implementation.
+//
+// This class acts as the respository of all sync encryption state, and handles
+// encryption related changes/queries coming from both the chrome side and
+// the sync side (via NigoriHandler). It is capable of modifying all sync data
+// (re-encryption), updating the encrypted types, changing the encryption keys,
+// and creating/receiving nigori node updates.
+//
+// The class should live as long as the directory itself in order to ensure
+// any data read/written is properly decrypted/encrypted.
+//
+// Note: See sync_encryption_handler.h for a description of the chrome visible
+// methods and what they do, and nigori_handler.h for a description of the
+// sync methods.
+//
+// TODO(zea): Make this class explicitly non-thread safe and ensure its only
+// accessed from the sync thread, with the possible exception of
+// GetEncryptedTypes. Need to cache explicit passphrase state on the UI thread.
+class SyncEncryptionHandlerImpl
+ : public SyncEncryptionHandler,
+ public syncable::NigoriHandler {
+ public:
+ SyncEncryptionHandlerImpl(UserShare* user_share,
+ Cryptographer* cryptographer);
+ virtual ~SyncEncryptionHandlerImpl();
+
+ // SyncEncryptionHandler implementation.
+ virtual void AddObserver(Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(Observer* observer) OVERRIDE;
+ virtual void Init() OVERRIDE;
+ virtual void SetEncryptionPassphrase(const std::string& passphrase,
+ bool is_explicit) OVERRIDE;
+ virtual void SetDecryptionPassphrase(const std::string& passphrase) OVERRIDE;
+ virtual void EnableEncryptEverything() OVERRIDE;
+ virtual bool EncryptEverythingEnabled() const OVERRIDE;
+ virtual bool IsUsingExplicitPassphrase() const OVERRIDE;
+
+ // NigoriHandler implementation.
+ // Note: all methods are invoked while the caller holds a transaction.
+ virtual void ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) OVERRIDE;
+ virtual void UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const OVERRIDE;
+ virtual ModelTypeSet GetEncryptedTypes() const OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SyncEncryptionHandlerImplTest,
+ NigoriEncryptionTypes);
+ FRIEND_TEST_ALL_PREFIXES(SyncEncryptionHandlerImplTest,
+ EncryptEverythingExplicit);
+ FRIEND_TEST_ALL_PREFIXES(SyncEncryptionHandlerImplTest,
+ EncryptEverythingImplicit);
+ FRIEND_TEST_ALL_PREFIXES(SyncEncryptionHandlerImplTest,
+ UnknownSensitiveTypes);
+
+ // Iterate over all encrypted types ensuring each entry is properly encrypted.
+ void ReEncryptEverything(WriteTransaction* trans);
+
+ // Apply a nigori update. Updates internal and cryptographer state.
+ // Returns true on success, false if |nigori| was incompatible, and the
+ // nigori node must be corrected.
+ // Note: must be called from within a transaction.
+ bool ApplyNigoriUpdateImpl(const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans);
+
+ // Wrapper around WriteEncryptionStateToNigori that creates a new write
+ // transaction.
+ void RewriteNigori();
+
+ // Write the current encryption state into the nigori node. This includes
+ // the encrypted types/encrypt everything state, as well as the keybag/
+ // explicit passphrase state (if the cryptographer is ready).
+ void WriteEncryptionStateToNigori(WriteTransaction* trans);
+
+ // Updates local encrypted types from |nigori|.
+ // Returns true if the local set of encrypted types either matched or was
+ // a subset of that in |nigori|. Returns false if the local state already
+ // had stricter encryption than |nigori|, and the nigori node needs to be
+ // updated with the newer encryption state.
+ // Note: must be called from within a transaction.
+ bool UpdateEncryptedTypesFromNigori(const sync_pb::NigoriSpecifics& nigori);
+
+ // The final step of SetEncryptionPassphrase and SetDecryptionPassphrase that
+ // notifies observers of the result of the set passphrase operation, updates
+ // the nigori node, and does re-encryption.
+ // |success|: true if the operation was successful and false otherwise. If
+ // success == false, we send an OnPassphraseRequired notification.
+ // |bootstrap_token|: used to inform observers if the cryptographer's
+ // bootstrap token was updated.
+ // |is_explicit|: used to differentiate between a custom passphrase (true) and
+ // a GAIA passphrase that is implicitly used for encryption
+ // (false).
+ // |trans| and |nigori_node|: used to access data in the cryptographer.
+ void FinishSetPassphrase(bool success,
+ const std::string& bootstrap_token,
+ bool is_explicit,
+ WriteTransaction* trans,
+ WriteNode* nigori_node);
+
+ // Merges the given set of encrypted types with the existing set and emits a
+ // notification if necessary.
+ // Note: must be called from within a transaction.
+ void MergeEncryptedTypes(ModelTypeSet encrypted_types);
+
+ base::WeakPtrFactory<SyncEncryptionHandlerImpl> weak_ptr_factory_;
+
+ ObserverList<SyncEncryptionHandler::Observer> observers_;
+
+ // The current user share (for creating transactions).
+ UserShare* user_share_;
+
+ // TODO(zea): have the sync encryption handler own the cryptographer, and live
+ // in the directory.
+ Cryptographer* cryptographer_;
+
+ // The set of types that require encryption. This is accessed on all sync
+ // datatype threads when we write to a node, so we must hold a transaction
+ // whenever we touch/read it.
+ ModelTypeSet encrypted_types_;
+
+ // Sync encryption state. These are only modified and accessed from the sync
+ // thread.
+ bool encrypt_everything_;
+ bool explicit_passphrase_;
+
+ // The number of times we've automatically (i.e. not via SetPassphrase or
+ // conflict resolver) updated the nigori's encryption keys in this chrome
+ // instantiation.
+ int nigori_overwrite_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncEncryptionHandlerImpl);
+};
+
+} // namespace syncer
+
+#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_ENCRYPTION_HANDLER_IMPL_H_
diff --git a/sync/internal_api/sync_encryption_handler_impl_unittest.cc b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
new file mode 100644
index 0000000..af58d54
--- /dev/null
+++ b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
@@ -0,0 +1,380 @@
+// 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/internal_api/sync_encryption_handler_impl.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/tracked_objects.h"
+#include "sync/internal_api/public/base/model_type_test_util.h"
+#include "sync/internal_api/public/read_node.h"
+#include "sync/internal_api/public/read_transaction.h"
+#include "sync/internal_api/public/write_transaction.h"
+#include "sync/internal_api/public/test/test_user_share.h"
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
+#include "sync/syncable/entry.h"
+#include "sync/syncable/mutable_entry.h"
+#include "sync/syncable/write_transaction.h"
+#include "sync/test/engine/test_id_factory.h"
+#include "sync/util/cryptographer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::StrictMock;
+
+class SyncEncryptionHandlerObserverMock
+ : public SyncEncryptionHandler::Observer {
+ public:
+ MOCK_METHOD2(OnPassphraseRequired,
+ void(PassphraseRequiredReason,
+ const sync_pb::EncryptedData&)); // NOLINT
+ MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT
+ MOCK_METHOD1(OnBootstrapTokenUpdated, void(const std::string&)); // NOLINT
+ MOCK_METHOD2(OnEncryptedTypesChanged,
+ void(ModelTypeSet, bool)); // NOLINT
+ MOCK_METHOD0(OnEncryptionComplete, void()); // NOLINT
+ MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*)); // NOLINT
+};
+
+} // namespace
+
+class SyncEncryptionHandlerImplTest : public ::testing::Test {
+ public:
+ SyncEncryptionHandlerImplTest() : cryptographer_(NULL) {}
+ virtual ~SyncEncryptionHandlerImplTest() {}
+
+ virtual void SetUp() {
+ test_user_share_.SetUp();
+ SetUpEncryption();
+ CreateRootForType(NIGORI);
+ }
+
+ virtual void TearDown() {
+ test_user_share_.TearDown();
+ }
+
+ protected:
+ void SetUpEncryption() {
+ ReadTransaction trans(FROM_HERE, user_share());
+ cryptographer_ = trans.GetCryptographer();
+ encryption_handler_.reset(
+ new SyncEncryptionHandlerImpl(user_share(),
+ cryptographer_));
+ cryptographer_->SetNigoriHandler(
+ encryption_handler_.get());
+ encryption_handler_->AddObserver(&observer_);
+ }
+
+ void CreateRootForType(ModelType model_type) {
+ syncer::syncable::Directory* directory = user_share()->directory.get();
+
+ std::string tag_name = ModelTypeToRootTag(model_type);
+
+ syncable::WriteTransaction wtrans(FROM_HERE, syncable::UNITTEST, directory);
+ syncable::MutableEntry node(&wtrans,
+ syncable::CREATE,
+ wtrans.root_id(),
+ tag_name);
+ node.Put(syncable::UNIQUE_SERVER_TAG, tag_name);
+ node.Put(syncable::IS_DIR, true);
+ node.Put(syncable::SERVER_IS_DIR, false);
+ node.Put(syncable::IS_UNSYNCED, false);
+ node.Put(syncable::IS_UNAPPLIED_UPDATE, false);
+ node.Put(syncable::SERVER_VERSION, 20);
+ node.Put(syncable::BASE_VERSION, 20);
+ node.Put(syncable::IS_DEL, false);
+ node.Put(syncable::ID, ids_.MakeServer(tag_name));
+ sync_pb::EntitySpecifics specifics;
+ syncer::AddDefaultFieldValue(model_type, &specifics);
+ node.Put(syncable::SPECIFICS, specifics);
+ }
+
+ void PumpLoop() {
+ message_loop_.RunAllPending();
+ }
+
+ // Getters for tests.
+ UserShare* user_share() { return test_user_share_.user_share(); }
+ SyncEncryptionHandlerImpl* encryption_handler() {
+ return encryption_handler_.get();
+ }
+ SyncEncryptionHandlerObserverMock* observer() { return &observer_; }
+ Cryptographer* cryptographer() { return cryptographer_; }
+
+ private:
+ TestUserShare test_user_share_;
+ scoped_ptr<SyncEncryptionHandlerImpl> encryption_handler_;
+ StrictMock<SyncEncryptionHandlerObserverMock> observer_;
+ Cryptographer* cryptographer_;
+ TestIdFactory ids_;
+ MessageLoop message_loop_;
+};
+
+// Verify that the encrypted types are being written to and read from the
+// nigori node properly.
+TEST_F(SyncEncryptionHandlerImplTest, NigoriEncryptionTypes) {
+ sync_pb::NigoriSpecifics nigori;
+
+ StrictMock<SyncEncryptionHandlerObserverMock> observer2;
+ SyncEncryptionHandlerImpl handler2(user_share(),
+ cryptographer());
+ handler2.AddObserver(&observer2);
+
+ // Just set the sensitive types (shouldn't trigger any notifications).
+ ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
+ encryption_handler()->MergeEncryptedTypes(encrypted_types);
+ {
+ WriteTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->UpdateNigoriFromEncryptedTypes(
+ &nigori,
+ trans.GetWrappedTrans());
+ }
+ handler2.UpdateEncryptedTypesFromNigori(nigori);
+ EXPECT_TRUE(encrypted_types.Equals(
+ encryption_handler()->GetEncryptedTypes()));
+ EXPECT_TRUE(encrypted_types.Equals(
+ handler2.GetEncryptedTypes()));
+
+ Mock::VerifyAndClearExpectations(observer());
+ Mock::VerifyAndClearExpectations(&observer2);
+
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(
+ HasModelTypes(ModelTypeSet::All()), false));
+ EXPECT_CALL(observer2,
+ OnEncryptedTypesChanged(
+ HasModelTypes(ModelTypeSet::All()), false));
+
+ // Set all encrypted types
+ encrypted_types = ModelTypeSet::All();
+ encryption_handler()->MergeEncryptedTypes(encrypted_types);
+ {
+ WriteTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->UpdateNigoriFromEncryptedTypes(
+ &nigori,
+ trans.GetWrappedTrans());
+ }
+ handler2.UpdateEncryptedTypesFromNigori(nigori);
+ EXPECT_TRUE(encrypted_types.Equals(
+ encryption_handler()->GetEncryptedTypes()));
+ EXPECT_TRUE(encrypted_types.Equals(handler2.GetEncryptedTypes()));
+
+ // Receiving an empty nigori should not reset any encrypted types or trigger
+ // an observer notification.
+ Mock::VerifyAndClearExpectations(observer());
+ Mock::VerifyAndClearExpectations(&observer2);
+ nigori = sync_pb::NigoriSpecifics();
+ encryption_handler()->UpdateEncryptedTypesFromNigori(nigori);
+ EXPECT_TRUE(encrypted_types.Equals(
+ encryption_handler()->GetEncryptedTypes()));
+}
+
+// Verify the encryption handler processes the encrypt everything field
+// properly.
+TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingExplicit) {
+ ModelTypeSet real_types = ModelTypeSet::All();
+ sync_pb::NigoriSpecifics specifics;
+ specifics.set_encrypt_everything(true);
+
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(
+ HasModelTypes(ModelTypeSet::All()), true));
+
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ ModelTypeSet encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ else
+ EXPECT_FALSE(encrypted_types.Has(iter.Get()));
+ }
+
+ encryption_handler()->UpdateEncryptedTypesFromNigori(specifics);
+
+ EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled());
+ encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ }
+
+ // Receiving the nigori node again shouldn't trigger another notification.
+ Mock::VerifyAndClearExpectations(observer());
+ encryption_handler()->UpdateEncryptedTypesFromNigori(specifics);
+}
+
+// Verify the encryption handler can detect an implicit encrypt everything state
+// (from clients that failed to write the encrypt everything field).
+TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingImplicit) {
+ ModelTypeSet real_types = ModelTypeSet::All();
+ sync_pb::NigoriSpecifics specifics;
+ specifics.set_encrypt_bookmarks(true); // Non-passwords = encrypt everything
+
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(
+ HasModelTypes(ModelTypeSet::All()), true));
+
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ ModelTypeSet encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ else
+ EXPECT_FALSE(encrypted_types.Has(iter.Get()));
+ }
+
+ encryption_handler()->UpdateEncryptedTypesFromNigori(specifics);
+
+ EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled());
+ encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ }
+
+ // Receiving a nigori node with encrypt everything explicitly set shouldn't
+ // trigger another notification.
+ Mock::VerifyAndClearExpectations(observer());
+ specifics.set_encrypt_everything(true);
+ encryption_handler()->UpdateEncryptedTypesFromNigori(specifics);
+}
+
+// Verify the encryption handler can deal with new versions treating new types
+// as Sensitive, and that it does not consider this an implicit encrypt
+// everything case.
+TEST_F(SyncEncryptionHandlerImplTest, UnknownSensitiveTypes) {
+ ModelTypeSet real_types = ModelTypeSet::All();
+ sync_pb::NigoriSpecifics specifics;
+ specifics.set_encrypt_everything(false);
+ specifics.set_encrypt_bookmarks(true);
+
+ ModelTypeSet expected_encrypted_types =
+ SyncEncryptionHandler::SensitiveTypes();
+ expected_encrypted_types.Put(BOOKMARKS);
+
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(
+ HasModelTypes(expected_encrypted_types), false));
+
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ ModelTypeSet encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ else
+ EXPECT_FALSE(encrypted_types.Has(iter.Get()));
+ }
+
+ encryption_handler()->UpdateEncryptedTypesFromNigori(specifics);
+
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ encrypted_types = encryption_handler()->GetEncryptedTypes();
+ for (ModelTypeSet::Iterator iter = real_types.First();
+ iter.Good(); iter.Inc()) {
+ if (iter.Get() == PASSWORDS ||
+ iter.Get() == NIGORI ||
+ iter.Get() == BOOKMARKS)
+ EXPECT_TRUE(encrypted_types.Has(iter.Get()));
+ else
+ EXPECT_FALSE(encrypted_types.Has(iter.Get()));
+ }
+}
+
+// Receive an old nigori with old encryption keys and encrypted types. We should
+// not revert our default key or encrypted types, and should post a task to
+// overwrite the existing nigori with the correct data.
+TEST_F(SyncEncryptionHandlerImplTest, ReceiveOldNigori) {
+ KeyParams old_key = {"localhost", "dummy", "old"};
+ KeyParams current_key = {"localhost", "dummy", "cur"};
+
+ // Data for testing encryption/decryption.
+ Cryptographer other_cryptographer(cryptographer()->encryptor());
+ other_cryptographer.AddKey(old_key);
+ sync_pb::EntitySpecifics other_encrypted_specifics;
+ other_encrypted_specifics.mutable_bookmark()->set_title("title");
+ other_cryptographer.Encrypt(
+ other_encrypted_specifics,
+ other_encrypted_specifics.mutable_encrypted());
+ sync_pb::EntitySpecifics our_encrypted_specifics;
+ our_encrypted_specifics.mutable_bookmark()->set_title("title2");
+ ModelTypeSet encrypted_types = ModelTypeSet::All();
+
+ // Set up the current encryption state (containing both keys and encrypt
+ // everything).
+ sync_pb::NigoriSpecifics current_nigori_specifics;
+ cryptographer()->AddKey(old_key);
+ cryptographer()->AddKey(current_key);
+ cryptographer()->Encrypt(
+ our_encrypted_specifics,
+ our_encrypted_specifics.mutable_encrypted());
+ cryptographer()->GetKeys(
+ current_nigori_specifics.mutable_encrypted());
+ current_nigori_specifics.set_encrypt_everything(true);
+
+ EXPECT_CALL(*observer(), OnCryptographerStateChanged(_));
+ EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
+ HasModelTypes(ModelTypeSet::All()), true));
+ {
+ // Update the encryption handler.
+ WriteTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->ApplyNigoriUpdate(
+ current_nigori_specifics,
+ trans.GetWrappedTrans());
+ }
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Now set up the old nigori specifics and apply it on top.
+ // Has an old set of keys, and no encrypted types.
+ sync_pb::NigoriSpecifics old_nigori;
+ other_cryptographer.GetKeys(old_nigori.mutable_encrypted());
+
+ EXPECT_CALL(*observer(), OnCryptographerStateChanged(_));
+ {
+ // Update the encryption handler.
+ WriteTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->ApplyNigoriUpdate(
+ old_nigori,
+ trans.GetWrappedTrans());
+ }
+ EXPECT_TRUE(cryptographer()->is_ready());
+ EXPECT_FALSE(cryptographer()->has_pending_keys());
+
+ // Encryption handler should have posted a task to overwrite the old
+ // specifics.
+ PumpLoop();
+
+ {
+ // The cryptographer should be able to decrypt both sets of keys and still
+ // be encrypting with the newest, and the encrypted types should be the
+ // most recent.
+ // In addition, the nigori node should match the current encryption state.
+ ReadTransaction trans(FROM_HERE, user_share());
+ ReadNode nigori_node(&trans);
+ ASSERT_EQ(nigori_node.InitByTagLookup(ModelTypeToRootTag(NIGORI)),
+ BaseNode::INIT_OK);
+ const sync_pb::NigoriSpecifics& nigori = nigori_node.GetNigoriSpecifics();
+ EXPECT_TRUE(cryptographer()->CanDecryptUsingDefaultKey(
+ our_encrypted_specifics.encrypted()));
+ EXPECT_TRUE(cryptographer()->CanDecrypt(
+ other_encrypted_specifics.encrypted()));
+ EXPECT_TRUE(cryptographer()->CanDecrypt(nigori.encrypted()));
+ EXPECT_TRUE(nigori.encrypt_everything());
+ EXPECT_TRUE(cryptographer()->CanDecryptUsingDefaultKey(nigori.encrypted()));
+ }
+ EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled());
+}
+
+} // namespace syncer
diff --git a/sync/internal_api/sync_manager_impl.cc b/sync/internal_api/sync_manager_impl.cc
index 95d650c..4335fe1 100644
--- a/sync/internal_api/sync_manager_impl.cc
+++ b/sync/internal_api/sync_manager_impl.cc
@@ -41,13 +41,11 @@
#include "sync/js/js_reply_handler.h"
#include "sync/notifier/invalidation_util.h"
#include "sync/notifier/sync_notifier.h"
-#include "sync/protocol/encryption.pb.h"
#include "sync/protocol/proto_value_conversions.h"
#include "sync/protocol/sync.pb.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/entry.h"
#include "sync/syncable/in_memory_directory_backing_store.h"
-#include "sync/syncable/nigori_util.h"
#include "sync/syncable/on_disk_directory_backing_store.h"
#include "sync/util/get_session_name.h"
@@ -68,10 +66,6 @@ static const int kPreferencesNudgeDelayMilliseconds = 2000;
static const int kSyncRefreshDelayMsec = 500;
static const int kSyncSchedulerDelayMsec = 250;
-// The maximum number of times we will automatically overwrite the nigori node
-// because the encryption keys don't match (per chrome instantiation).
-static const int kNigoriOverwriteLimit = 10;
-
// Maximum count and size for traffic recorder.
static const unsigned int kMaxMessagesToRecord = 10;
static const unsigned int kMaxMessageSizeToRecord = 5 * 1024;
@@ -179,8 +173,7 @@ SyncManagerImpl::SyncManagerImpl(const std::string& name)
traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord),
encryptor_(NULL),
unrecoverable_error_handler_(NULL),
- report_unrecoverable_error_function_(NULL),
- nigori_overwrite_count_(0) {
+ report_unrecoverable_error_function_(NULL) {
// Pre-fill |notification_info_map_|.
for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
notification_info_map_.insert(
@@ -310,24 +303,6 @@ ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken(
return result;
}
-void SyncManagerImpl::EnableEncryptEverything() {
- DCHECK(thread_checker_.CalledOnValidThread());
- {
- // Update the cryptographer to know we're now encrypting everything.
- WriteTransaction trans(FROM_HERE, GetUserShare());
- Cryptographer* cryptographer = trans.GetCryptographer();
- // Only set encrypt everything if we know we can encrypt. This allows the
- // user to cancel encryption if they have forgotten their passphrase.
- if (cryptographer->is_ready())
- cryptographer->set_encrypt_everything();
- }
-
- // Reads from cryptographer so will automatically encrypt all
- // datatypes and update the nigori node as necessary. Will trigger
- // OnPassphraseRequired if necessary.
- RefreshEncryption();
-}
-
void SyncManagerImpl::ConfigureSyncer(
ConfigureReason reason,
const ModelTypeSet& types_to_config,
@@ -489,7 +464,15 @@ void SyncManagerImpl::Init(
trans.GetCryptographer()->Bootstrap(restored_key_for_bootstrapping);
trans.GetCryptographer()->BootstrapKeystoreKey(
restored_keystore_key_for_bootstrapping);
- trans.GetCryptographer()->AddObserver(this);
+
+ sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl(
+ &share_,
+ trans.GetCryptographer()));
+ sync_encryption_handler_->AddObserver(this);
+ sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
+ sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
+ trans.GetCryptographer()->SetNigoriHandler(
+ sync_encryption_handler_.get());
FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
OnInitializationComplete(
@@ -497,141 +480,82 @@ void SyncManagerImpl::Init(
true, InitialSyncEndedTypes()));
}
-void SyncManagerImpl::RefreshNigori(const std::string& chrome_version,
- const base::Closure& done_callback) {
- DCHECK(initialized_);
- DCHECK(thread_checker_.CalledOnValidThread());
- GetSessionName(
- blocking_task_runner_,
- base::Bind(
- &SyncManagerImpl::UpdateCryptographerAndNigoriCallback,
- weak_ptr_factory_.GetWeakPtr(),
- chrome_version,
- done_callback));
-}
-
-void SyncManagerImpl::UpdateNigoriEncryptionState(
- Cryptographer* cryptographer,
- WriteNode* nigori_node) {
- DCHECK(nigori_node);
- sync_pb::NigoriSpecifics nigori = nigori_node->GetNigoriSpecifics();
-
- if (cryptographer->is_ready() &&
- nigori_overwrite_count_ < kNigoriOverwriteLimit) {
- // Does not modify the encrypted blob if the unencrypted data already
- // matches what is about to be written.
- sync_pb::EncryptedData original_keys = nigori.encrypted();
- if (!cryptographer->GetKeys(nigori.mutable_encrypted()))
- NOTREACHED();
-
- if (nigori.encrypted().SerializeAsString() !=
- original_keys.SerializeAsString()) {
- // We've updated the nigori node's encryption keys. In order to prevent
- // a possible looping of two clients constantly overwriting each other,
- // we limit the absolute number of overwrites per client instantiation.
- nigori_overwrite_count_++;
- UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites",
- nigori_overwrite_count_);
- }
-
- // Note: we don't try to set using_explicit_passphrase here since if that
- // is lost the user can always set it again. The main point is to preserve
- // the encryption keys so all data remains decryptable.
- }
- cryptographer->UpdateNigoriFromEncryptedTypes(&nigori);
-
- // If nothing has changed, this is a no-op.
- nigori_node->SetNigoriSpecifics(nigori);
-}
-
-void SyncManagerImpl::UpdateCryptographerAndNigoriCallback(
+void SyncManagerImpl::UpdateSessionNameCallback(
const std::string& chrome_version,
- const base::Closure& done_callback,
const std::string& session_name) {
- if (!directory()->initial_sync_ended_for_type(NIGORI)) {
- done_callback.Run(); // Should only happen during first time sync.
+ WriteTransaction trans(FROM_HERE, GetUserShare());
+ WriteNode node(&trans);
+ // TODO(rlarocque): switch to device info node.
+ if (node.InitByTagLookup(syncer::kNigoriTag) != syncer::BaseNode::INIT_OK) {
return;
}
- bool success = false;
- {
- WriteTransaction trans(FROM_HERE, GetUserShare());
- Cryptographer* cryptographer = trans.GetCryptographer();
- WriteNode node(&trans);
-
- if (node.InitByTagLookup(kNigoriTag) == BaseNode::INIT_OK) {
- sync_pb::NigoriSpecifics nigori(node.GetNigoriSpecifics());
- Cryptographer::UpdateResult result = cryptographer->Update(nigori);
- if (result == Cryptographer::NEEDS_PASSPHRASE) {
- sync_pb::EncryptedData pending_keys;
- if (cryptographer->has_pending_keys())
- pending_keys = cryptographer->GetPendingKeys();
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_DECRYPTION,
- pending_keys));
- }
-
- // Add or update device information.
- bool contains_this_device = false;
- for (int i = 0; i < nigori.device_information_size(); ++i) {
- const sync_pb::DeviceInformation& device_information =
- nigori.device_information(i);
- if (device_information.cache_guid() == directory()->cache_guid()) {
- // Update the version number in case it changed due to an update.
- if (device_information.chrome_version() != chrome_version) {
- sync_pb::DeviceInformation* mutable_device_information =
- nigori.mutable_device_information(i);
- mutable_device_information->set_chrome_version(
- chrome_version);
- }
- contains_this_device = true;
- }
+ sync_pb::NigoriSpecifics nigori(node.GetNigoriSpecifics());
+ // Add or update device information.
+ bool contains_this_device = false;
+ for (int i = 0; i < nigori.device_information_size(); ++i) {
+ const sync_pb::DeviceInformation& device_information =
+ nigori.device_information(i);
+ if (device_information.cache_guid() == directory()->cache_guid()) {
+ // Update the version number in case it changed due to an update.
+ if (device_information.chrome_version() != chrome_version) {
+ sync_pb::DeviceInformation* mutable_device_information =
+ nigori.mutable_device_information(i);
+ mutable_device_information->set_chrome_version(
+ chrome_version);
}
+ contains_this_device = true;
+ }
+ }
- if (!contains_this_device) {
- sync_pb::DeviceInformation* device_information =
- nigori.add_device_information();
- device_information->set_cache_guid(directory()->cache_guid());
+ if (!contains_this_device) {
+ sync_pb::DeviceInformation* device_information =
+ nigori.add_device_information();
+ device_information->set_cache_guid(directory()->cache_guid());
#if defined(OS_CHROMEOS)
- device_information->set_platform("ChromeOS");
+ device_information->set_platform("ChromeOS");
#elif defined(OS_LINUX)
- device_information->set_platform("Linux");
+ device_information->set_platform("Linux");
#elif defined(OS_MACOSX)
- device_information->set_platform("Mac");
+ device_information->set_platform("Mac");
#elif defined(OS_WIN)
- device_information->set_platform("Windows");
+ device_information->set_platform("Windows");
#endif
- device_information->set_name(session_name);
- device_information->set_chrome_version(chrome_version);
- }
- // Disabled to avoid nigori races. TODO(zea): re-enable. crbug.com/122837
- // node.SetNigoriSpecifics(nigori);
+ device_information->set_name(session_name);
+ device_information->set_chrome_version(chrome_version);
+ }
+ node.SetNigoriSpecifics(nigori);
+}
- // Make sure the nigori node has the up to date encryption info.
- UpdateNigoriEncryptionState(cryptographer, &node);
- NotifyCryptographerState(cryptographer);
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes());
+void SyncManagerImpl::OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) {
+ // Does nothing.
+}
- success = cryptographer->is_ready();
- } else {
- NOTREACHED();
- }
- }
+void SyncManagerImpl::OnPassphraseAccepted() {
+ // Does nothing.
+}
- if (success)
- RefreshEncryption();
- done_callback.Run();
+void SyncManagerImpl::OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) {
+ // Does nothing.
}
-void SyncManagerImpl::NotifyCryptographerState(Cryptographer * cryptographer) {
- // TODO(lipalani): Explore the possibility of hooking this up to
- // SyncManager::Observer and making |AllStatus| a listener for that.
+void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
+ bool encrypt_everything) {
+ allstatus_.SetEncryptedTypes(encrypted_types);
+}
+
+void SyncManagerImpl::OnEncryptionComplete() {
+ // Does nothing.
+}
+
+void SyncManagerImpl::OnCryptographerStateChanged(
+ Cryptographer* cryptographer) {
allstatus_.SetCryptographerReady(cryptographer->is_ready());
allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
- debug_info_event_listener_.SetCryptographerReady(cryptographer->is_ready());
- debug_info_event_listener_.SetCrytographerHasPendingKeys(
- cryptographer->has_pending_keys());
}
void SyncManagerImpl::StartSyncingNormally(
@@ -760,444 +684,11 @@ void SyncManagerImpl::UnregisterInvalidationHandler(
sync_notifier_->UnregisterHandler(handler);
}
-void SyncManagerImpl::SetEncryptionPassphrase(
- const std::string& passphrase,
- bool is_explicit) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // We do not accept empty passphrases.
- if (passphrase.empty()) {
- NOTREACHED() << "Cannot encrypt with an empty passphrase.";
- return;
- }
-
- // All accesses to the cryptographer are protected by a transaction.
- WriteTransaction trans(FROM_HERE, GetUserShare());
- Cryptographer* cryptographer = trans.GetCryptographer();
- KeyParams key_params = {"localhost", "dummy", passphrase};
- WriteNode node(&trans);
- if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
- NOTREACHED();
- return;
- }
-
- bool nigori_has_explicit_passphrase =
- node.GetNigoriSpecifics().using_explicit_passphrase();
- std::string bootstrap_token;
- sync_pb::EncryptedData pending_keys;
- if (cryptographer->has_pending_keys())
- pending_keys = cryptographer->GetPendingKeys();
- bool success = false;
-
-
- // There are six cases to handle here:
- // 1. The user has no pending keys and is setting their current GAIA password
- // as the encryption passphrase. This happens either during first time sync
- // with a clean profile, or after re-authenticating on a profile that was
- // already signed in with the cryptographer ready.
- // 2. The user has no pending keys, and is overwriting an (already provided)
- // implicit passphrase with an explicit (custom) passphrase.
- // 3. The user has pending keys for an explicit passphrase that is somehow set
- // to their current GAIA passphrase.
- // 4. The user has pending keys encrypted with their current GAIA passphrase
- // and the caller passes in the current GAIA passphrase.
- // 5. The user has pending keys encrypted with an older GAIA passphrase
- // and the caller passes in the current GAIA passphrase.
- // 6. The user has previously done encryption with an explicit passphrase.
- // Furthermore, we enforce the fact that the bootstrap encryption token will
- // always be derived from the newest GAIA password if the account is using
- // an implicit passphrase (even if the data is encrypted with an old GAIA
- // password). If the account is using an explicit (custom) passphrase, the
- // bootstrap token will be derived from the most recently provided explicit
- // passphrase (that was able to decrypt the data).
- if (!nigori_has_explicit_passphrase) {
- if (!cryptographer->has_pending_keys()) {
- if (cryptographer->AddKey(key_params)) {
- // Case 1 and 2. We set a new GAIA passphrase when there are no pending
- // keys (1), or overwriting an implicit passphrase with a new explicit
- // one (2) when there are no pending keys.
- DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" )
- << " passphrase for encryption.";
- cryptographer->GetBootstrapToken(&bootstrap_token);
- success = true;
- } else {
- NOTREACHED() << "Failed to add key to cryptographer.";
- success = false;
- }
- } else { // cryptographer->has_pending_keys() == true
- if (is_explicit) {
- // This can only happen if the nigori node is updated with a new
- // implicit passphrase while a client is attempting to set a new custom
- // passphrase (race condition).
- DVLOG(1) << "Failing because an implicit passphrase is already set.";
- success = false;
- } else { // is_explicit == false
- if (cryptographer->DecryptPendingKeys(key_params)) {
- // Case 4. We successfully decrypted with the implicit GAIA passphrase
- // passed in.
- DVLOG(1) << "Implicit internal passphrase accepted for decryption.";
- cryptographer->GetBootstrapToken(&bootstrap_token);
- success = true;
- } else {
- // Case 5. Encryption was done with an old GAIA password, but we were
- // provided with the current GAIA password. We need to generate a new
- // bootstrap token to preserve it. We build a temporary cryptographer
- // to allow us to extract these params without polluting our current
- // cryptographer.
- DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding "
- << "anyways as default passphrase and persisting via "
- << "bootstrap token.";
- Cryptographer temp_cryptographer(encryptor_);
- temp_cryptographer.AddKey(key_params);
- temp_cryptographer.GetBootstrapToken(&bootstrap_token);
- // We then set the new passphrase as the default passphrase of the
- // real cryptographer, even though we have pending keys. This is safe,
- // as although Cryptographer::is_initialized() will now be true,
- // is_ready() will remain false due to having pending keys.
- cryptographer->AddKey(key_params);
- success = false;
- }
- } // is_explicit
- } // cryptographer->has_pending_keys()
- } else { // nigori_has_explicit_passphrase == true
- // Case 6. We do not want to override a previously set explicit passphrase,
- // so we return a failure.
- DVLOG(1) << "Failing because an explicit passphrase is already set.";
- success = false;
- }
-
- DVLOG_IF(1, !success)
- << "Failure in SetEncryptionPassphrase; notifying and returning.";
- DVLOG_IF(1, success)
- << "Successfully set encryption passphrase; updating nigori and "
- "reencrypting.";
-
- FinishSetPassphrase(
- success, bootstrap_token, is_explicit, &trans, &node);
-}
-
-void SyncManagerImpl::SetDecryptionPassphrase(
- const std::string& passphrase) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // We do not accept empty passphrases.
- if (passphrase.empty()) {
- NOTREACHED() << "Cannot decrypt with an empty passphrase.";
- return;
- }
-
- // All accesses to the cryptographer are protected by a transaction.
- WriteTransaction trans(FROM_HERE, GetUserShare());
- Cryptographer* cryptographer = trans.GetCryptographer();
- KeyParams key_params = {"localhost", "dummy", passphrase};
- WriteNode node(&trans);
- if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
- NOTREACHED();
- return;
- }
-
- if (!cryptographer->has_pending_keys()) {
- // Note that this *can* happen in a rare situation where data is
- // re-encrypted on another client while a SetDecryptionPassphrase() call is
- // in-flight on this client. It is rare enough that we choose to do nothing.
- NOTREACHED() << "Attempt to set decryption passphrase failed because there "
- << "were no pending keys.";
- return;
- }
-
- bool nigori_has_explicit_passphrase =
- node.GetNigoriSpecifics().using_explicit_passphrase();
- std::string bootstrap_token;
- sync_pb::EncryptedData pending_keys;
- pending_keys = cryptographer->GetPendingKeys();
- bool success = false;
-
- // There are three cases to handle here:
- // 7. We're using the current GAIA password to decrypt the pending keys. This
- // happens when signing in to an account with a previously set implicit
- // passphrase, where the data is already encrypted with the newest GAIA
- // password.
- // 8. The user is providing an old GAIA password to decrypt the pending keys.
- // In this case, the user is using an implicit passphrase, but has changed
- // their password since they last encrypted their data, and therefore
- // their current GAIA password was unable to decrypt the data. This will
- // happen when the user is setting up a new profile with a previously
- // encrypted account (after changing passwords).
- // 9. The user is providing a previously set explicit passphrase to decrypt
- // the pending keys.
- if (!nigori_has_explicit_passphrase) {
- if (cryptographer->is_initialized()) {
- // We only want to change the default encryption key to the pending
- // one if the pending keybag already contains the current default.
- // This covers the case where a different client re-encrypted
- // everything with a newer gaia passphrase (and hence the keybag
- // contains keys from all previously used gaia passphrases).
- // Otherwise, we're in a situation where the pending keys are
- // encrypted with an old gaia passphrase, while the default is the
- // current gaia passphrase. In that case, we preserve the default.
- Cryptographer temp_cryptographer(encryptor_);
- temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys());
- if (temp_cryptographer.DecryptPendingKeys(key_params)) {
- // Check to see if the pending bag of keys contains the current
- // default key.
- sync_pb::EncryptedData encrypted;
- cryptographer->GetKeys(&encrypted);
- if (temp_cryptographer.CanDecrypt(encrypted)) {
- DVLOG(1) << "Implicit user provided passphrase accepted for "
- << "decryption, overwriting default.";
- // Case 7. The pending keybag contains the current default. Go ahead
- // and update the cryptographer, letting the default change.
- cryptographer->DecryptPendingKeys(key_params);
- cryptographer->GetBootstrapToken(&bootstrap_token);
- success = true;
- } else {
- // Case 8. The pending keybag does not contain the current default
- // encryption key. We decrypt the pending keys here, and in
- // FinishSetPassphrase, re-encrypt everything with the current GAIA
- // passphrase instead of the passphrase just provided by the user.
- DVLOG(1) << "Implicit user provided passphrase accepted for "
- << "decryption, restoring implicit internal passphrase "
- << "as default.";
- std::string bootstrap_token_from_current_key;
- cryptographer->GetBootstrapToken(
- &bootstrap_token_from_current_key);
- cryptographer->DecryptPendingKeys(key_params);
- // Overwrite the default from the pending keys.
- cryptographer->AddKeyFromBootstrapToken(
- bootstrap_token_from_current_key);
- success = true;
- }
- } else { // !temp_cryptographer.DecryptPendingKeys(..)
- DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
- success = false;
- } // temp_cryptographer.DecryptPendingKeys(...)
- } else { // cryptographer->is_initialized() == false
- if (cryptographer->DecryptPendingKeys(key_params)) {
- // This can happpen in two cases:
- // - First time sync on android, where we'll never have a
- // !user_provided passphrase.
- // - This is a restart for a client that lost their bootstrap token.
- // In both cases, we should go ahead and initialize the cryptographer
- // and persist the new bootstrap token.
- //
- // Note: at this point, we cannot distinguish between cases 7 and 8
- // above. This user provided passphrase could be the current or the
- // old. But, as long as we persist the token, there's nothing more
- // we can do.
- cryptographer->GetBootstrapToken(&bootstrap_token);
- DVLOG(1) << "Implicit user provided passphrase accepted, initializing"
- << " cryptographer.";
- success = true;
- } else {
- DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
- success = false;
- }
- } // cryptographer->is_initialized()
- } else { // nigori_has_explicit_passphrase == true
- // Case 9. Encryption was done with an explicit passphrase, and we decrypt
- // with the passphrase provided by the user.
- if (cryptographer->DecryptPendingKeys(key_params)) {
- DVLOG(1) << "Explicit passphrase accepted for decryption.";
- cryptographer->GetBootstrapToken(&bootstrap_token);
- success = true;
- } else {
- DVLOG(1) << "Explicit passphrase failed to decrypt.";
- success = false;
- }
- } // nigori_has_explicit_passphrase
-
- DVLOG_IF(1, !success)
- << "Failure in SetDecryptionPassphrase; notifying and returning.";
- DVLOG_IF(1, success)
- << "Successfully set decryption passphrase; updating nigori and "
- "reencrypting.";
-
- FinishSetPassphrase(success,
- bootstrap_token,
- nigori_has_explicit_passphrase,
- &trans,
- &node);
-}
-
-void SyncManagerImpl::FinishSetPassphrase(
- bool success,
- const std::string& bootstrap_token,
- bool is_explicit,
- WriteTransaction* trans,
- WriteNode* nigori_node) {
- Cryptographer* cryptographer = trans->GetCryptographer();
- NotifyCryptographerState(cryptographer);
-
- // It's possible we need to change the bootstrap token even if we failed to
- // set the passphrase (for example if we need to preserve the new GAIA
- // passphrase).
- if (!bootstrap_token.empty()) {
- DVLOG(1) << "Bootstrap token updated.";
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnBootstrapTokenUpdated(bootstrap_token));
- }
-
- if (!success) {
- if (cryptographer->is_ready()) {
- LOG(ERROR) << "Attempt to change passphrase failed while cryptographer "
- << "was ready.";
- } else if (cryptographer->has_pending_keys()) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_DECRYPTION,
- cryptographer->GetPendingKeys()));
- } else {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_ENCRYPTION,
- sync_pb::EncryptedData()));
- }
- return;
- }
-
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseAccepted());
- DCHECK(cryptographer->is_ready());
-
- // TODO(tim): Bug 58231. It would be nice if setting a passphrase didn't
- // require messing with the Nigori node, because we can't set a passphrase
- // until download conditions are met vs Cryptographer init. It seems like
- // it's safe to defer this work.
- sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics());
- // Does not modify specifics.encrypted() if the original decrypted data was
- // the same.
- if (!cryptographer->GetKeys(specifics.mutable_encrypted())) {
- NOTREACHED();
- return;
- }
- specifics.set_using_explicit_passphrase(is_explicit);
- nigori_node->SetNigoriSpecifics(specifics);
-
- // Does nothing if everything is already encrypted or the cryptographer has
- // pending keys.
- ReEncryptEverything(trans);
-}
-
-bool SyncManagerImpl::IsUsingExplicitPassphrase() {
- ReadTransaction trans(FROM_HERE, &share_);
- ReadNode node(&trans);
- if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
- // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
- NOTREACHED();
- return false;
- }
-
- return node.GetNigoriSpecifics().using_explicit_passphrase();
-}
-
bool SyncManagerImpl::GetKeystoreKeyBootstrapToken(std::string* token) {
ReadTransaction trans(FROM_HERE, GetUserShare());
return trans.GetCryptographer()->GetKeystoreKeyBootstrapToken(token);
}
-void SyncManagerImpl::RefreshEncryption() {
- DCHECK(initialized_);
-
- WriteTransaction trans(FROM_HERE, GetUserShare());
- WriteNode node(&trans);
- if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
- NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not "
- << "found.";
- return;
- }
-
- Cryptographer* cryptographer = trans.GetCryptographer();
-
- if (!cryptographer->is_ready()) {
- DVLOG(1) << "Attempting to encrypt datatypes when cryptographer not "
- << "initialized, prompting for passphrase.";
- // TODO(zea): this isn't really decryption, but that's the only way we have
- // to prompt the user for a passsphrase. See http://crbug.com/91379.
- sync_pb::EncryptedData pending_keys;
- if (cryptographer->has_pending_keys())
- pending_keys = cryptographer->GetPendingKeys();
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_DECRYPTION,
- pending_keys));
- return;
- }
-
- UpdateNigoriEncryptionState(cryptographer, &node);
-
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes());
-
- // We reencrypt everything regardless of whether the set of encrypted
- // types changed to ensure that any stray unencrypted entries are overwritten.
- ReEncryptEverything(&trans);
-}
-
-// This function iterates over all encrypted types. There are many scenarios in
-// which data for some or all types is not currently available. In that case,
-// the lookup of the root node will fail and we will skip encryption for that
-// type.
-void SyncManagerImpl::ReEncryptEverything(
- WriteTransaction* trans) {
- Cryptographer* cryptographer = trans->GetCryptographer();
- if (!cryptographer || !cryptographer->is_ready())
- return;
- ModelTypeSet encrypted_types = GetEncryptedTypes(trans);
- for (ModelTypeSet::Iterator iter = encrypted_types.First();
- iter.Good(); iter.Inc()) {
- if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
- continue; // These types handle encryption differently.
-
- ReadNode type_root(trans);
- std::string tag = ModelTypeToRootTag(iter.Get());
- if (type_root.InitByTagLookup(tag) != BaseNode::INIT_OK)
- continue; // Don't try to reencrypt if the type's data is unavailable.
-
- // 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) != BaseNode::INIT_OK) {
- NOTREACHED();
- continue;
- }
- if (child.GetIsFolder()) {
- to_visit.push(child.GetFirstChildId());
- }
- if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) {
- // Rewrite the specifics of the node with encrypted data if necessary
- // (only rewrite the non-unique folders).
- child.ResetFromSpecifics();
- }
- to_visit.push(child.GetSuccessorId());
- }
- }
-
- // Passwords are encrypted with their own legacy scheme. Passwords are always
- // encrypted so we don't need to check GetEncryptedTypes() here.
- ReadNode passwords_root(trans);
- std::string passwords_tag = ModelTypeToRootTag(PASSWORDS);
- if (passwords_root.InitByTagLookup(passwords_tag) == BaseNode::INIT_OK) {
- int64 child_id = passwords_root.GetFirstChildId();
- while (child_id != kInvalidId) {
- WriteNode child(trans);
- if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK) {
- NOTREACHED();
- return;
- }
- child.SetPasswordSpecifics(child.GetPasswordSpecifics());
- child_id = child.GetSuccessorId();
- }
- }
-
- // NOTE: We notify from within a transaction.
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnEncryptionComplete());
-}
-
void SyncManagerImpl::AddObserver(SyncManager::Observer* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
observers_.AddObserver(observer);
@@ -1226,6 +717,11 @@ void SyncManagerImpl::ShutdownOnSyncThread() {
scheduler_.reset();
session_context_.reset();
+ if (sync_encryption_handler_.get()) {
+ sync_encryption_handler_->RemoveObserver(&debug_info_event_listener_);
+ sync_encryption_handler_->RemoveObserver(this);
+ }
+
SetJsEventHandler(WeakHandle<JsEventHandler>());
RemoveObserver(&js_sync_manager_observer_);
@@ -1248,12 +744,6 @@ void SyncManagerImpl::ShutdownOnSyncThread() {
observing_ip_address_changes_ = false;
if (initialized_ && directory()) {
- {
- // Cryptographer should only be accessed while holding a
- // transaction.
- ReadTransaction trans(FROM_HERE, GetUserShare());
- trans.GetCryptographer()->RemoveObserver(this);
- }
directory()->SaveChanges();
}
@@ -1506,32 +996,6 @@ void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) {
// Notifications are sent at the end of every sync cycle, regardless of
// whether we should sync again.
if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
- {
- // Check to see if we need to notify the frontend that we have newly
- // encrypted types or that we require a passphrase.
- ReadTransaction trans(FROM_HERE, GetUserShare());
- Cryptographer* cryptographer = trans.GetCryptographer();
- // 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()) {
- DVLOG(1) << "OnPassPhraseRequired Sent";
- sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys();
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_DECRYPTION,
- pending_keys));
- } else if (!cryptographer->is_ready() &&
- event.snapshot.initial_sync_ended().Has(NIGORI)) {
- DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
- << "ready";
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(REASON_ENCRYPTION,
- sync_pb::EncryptedData()));
- }
-
- NotifyCryptographerState(cryptographer);
- allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes());
- }
-
if (!initialized_) {
LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not "
<< "initialized";
@@ -1539,17 +1003,6 @@ void SyncManagerImpl::OnSyncEngineEvent(const SyncEngineEvent& event) {
}
if (!event.snapshot.has_more_to_sync()) {
- {
- // To account for a nigori node arriving with stale/bad data, we ensure
- // that the nigori node is up to date at the end of each cycle.
- WriteTransaction trans(FROM_HERE, GetUserShare());
- WriteNode nigori_node(&trans);
- if (nigori_node.InitByTagLookup(kNigoriTag) == BaseNode::INIT_OK) {
- Cryptographer* cryptographer = trans.GetCryptographer();
- UpdateNigoriEncryptionState(cryptographer, &nigori_node);
- }
- }
-
DVLOG(1) << "Sending OnSyncCycleCompleted";
FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
OnSyncCycleCompleted(event.snapshot));
@@ -1599,6 +1052,7 @@ void SyncManagerImpl::SetJsEventHandler(
js_event_handler_ = event_handler;
js_sync_manager_observer_.SetJsEventHandler(js_event_handler_);
js_mutation_event_observer_.SetJsEventHandler(js_event_handler_);
+ js_sync_encryption_handler_observer_.SetJsEventHandler(js_event_handler_);
}
void SyncManagerImpl::ProcessJsMessage(
@@ -1804,15 +1258,6 @@ JsArgList SyncManagerImpl::GetChildNodeIds(const JsArgList& args) {
return JsArgList(&return_args);
}
-void SyncManagerImpl::OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) {
- // NOTE: We're in a transaction.
- FOR_EACH_OBSERVER(
- SyncManager::Observer, observers_,
- OnEncryptedTypesChanged(encrypted_types, encrypt_everything));
-}
-
void SyncManagerImpl::UpdateNotificationInfo(
const ModelTypePayloadMap& type_payloads) {
for (ModelTypePayloadMap::const_iterator it = type_payloads.begin();
@@ -1911,6 +1356,10 @@ bool SyncManagerImpl::HasUnsyncedItems() {
return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
}
+SyncEncryptionHandler* SyncManagerImpl::GetEncryptionHandler() {
+ return sync_encryption_handler_.get();
+}
+
// static.
int SyncManagerImpl::GetDefaultNudgeDelay() {
return kDefaultNudgeDelayMilliseconds;
diff --git a/sync/internal_api/sync_manager_impl.h b/sync/internal_api/sync_manager_impl.h
index 19bad68..7b2a6c4 100644
--- a/sync/internal_api/sync_manager_impl.h
+++ b/sync/internal_api/sync_manager_impl.h
@@ -17,8 +17,10 @@
#include "sync/internal_api/change_reorder_buffer.h"
#include "sync/internal_api/debug_info_event_listener.h"
#include "sync/internal_api/js_mutation_event_observer.h"
+#include "sync/internal_api/js_sync_encryption_handler_observer.h"
#include "sync/internal_api/js_sync_manager_observer.h"
#include "sync/internal_api/public/sync_manager.h"
+#include "sync/internal_api/sync_encryption_handler_impl.h"
#include "sync/js/js_backend.h"
#include "sync/notifier/notifications_disabled_reason.h"
#include "sync/notifier/sync_notifier_observer.h"
@@ -46,12 +48,12 @@ class SyncSessionContext;
// same thread.
class SyncManagerImpl : public SyncManager,
public net::NetworkChangeNotifier::IPAddressObserver,
- public Cryptographer::Observer,
public SyncNotifierObserver,
public JsBackend,
public SyncEngineEventListener,
public ServerConnectionEventListener,
- public syncable::DirectoryChangeDelegate {
+ public syncable::DirectoryChangeDelegate,
+ public SyncEncryptionHandler::Observer {
public:
// Create an uninitialized SyncManager. Callers must Init() before using.
explicit SyncManagerImpl(const std::string& name);
@@ -95,9 +97,6 @@ class SyncManagerImpl : public SyncManager,
SyncNotifierObserver* handler) OVERRIDE;
virtual void StartSyncingNormally(
const ModelSafeRoutingInfo& routing_info) OVERRIDE;
- virtual void SetEncryptionPassphrase(const std::string& passphrase,
- bool is_explicit) OVERRIDE;
- virtual void SetDecryptionPassphrase(const std::string& passphrase) OVERRIDE;
virtual void ConfigureSyncer(
ConfigureReason reason,
const ModelTypeSet& types_to_config,
@@ -107,27 +106,28 @@ class SyncManagerImpl : public SyncManager,
virtual void AddObserver(SyncManager::Observer* observer) OVERRIDE;
virtual void RemoveObserver(SyncManager::Observer* observer) OVERRIDE;
virtual SyncStatus GetDetailedStatus() const OVERRIDE;
- virtual bool IsUsingExplicitPassphrase() OVERRIDE;
virtual bool GetKeystoreKeyBootstrapToken(std::string* token) OVERRIDE;
virtual void SaveChanges() OVERRIDE;
virtual void StopSyncingForShutdown(const base::Closure& callback) OVERRIDE;
virtual void ShutdownOnSyncThread() OVERRIDE;
virtual UserShare* GetUserShare() OVERRIDE;
-
- // Update the Cryptographer from the current nigori node and write back any
- // necessary changes to the nigori node. We also detect missing encryption
- // keys and write them into the nigori node.
- // Also updates or adds the device information into the nigori node.
- // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so
- // should only be called after syncapi is fully initialized.
- // Calls the callback argument with true if cryptographer is ready, false
- // otherwise.
- virtual void RefreshNigori(const std::string& chrome_version,
- const base::Closure& done_callback) OVERRIDE;
-
- virtual void EnableEncryptEverything() OVERRIDE;
virtual bool ReceivedExperiment(Experiments* experiments) OVERRIDE;
virtual bool HasUnsyncedItems() OVERRIDE;
+ virtual SyncEncryptionHandler* GetEncryptionHandler() OVERRIDE;
+
+ // SyncEncryptionHandler::Observer implementation.
+ virtual void OnPassphraseRequired(
+ PassphraseRequiredReason reason,
+ const sync_pb::EncryptedData& pending_keys) OVERRIDE;
+ virtual void OnPassphraseAccepted() OVERRIDE;
+ virtual void OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) OVERRIDE;
+ virtual void OnEncryptedTypesChanged(
+ ModelTypeSet encrypted_types,
+ bool encrypt_everything) OVERRIDE;
+ virtual void OnEncryptionComplete() OVERRIDE;
+ virtual void OnCryptographerStateChanged(
+ Cryptographer* cryptographer) OVERRIDE;
// Return the currently active (validated) username for use with syncable
// types.
@@ -166,11 +166,6 @@ class SyncManagerImpl : public SyncManager,
const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
syncable::BaseTransaction* trans) OVERRIDE;
- // Cryptographer::Observer implementation.
- virtual void OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) OVERRIDE;
-
// SyncNotifierObserver implementation.
virtual void OnNotificationsEnabled() OVERRIDE;
virtual void OnNotificationsDisabled(
@@ -240,8 +235,6 @@ class SyncManagerImpl : public SyncManager,
const tracked_objects::Location& nudge_location,
ModelTypeSet type);
- void NotifyCryptographerState(Cryptographer* cryptographer);
-
// 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.
@@ -253,42 +246,11 @@ class SyncManagerImpl : public SyncManager,
bool existed_before,
bool exists_now);
- // Stores the current set of encryption keys (if the cryptographer is ready)
- // and encrypted types into the nigori node.
- void UpdateNigoriEncryptionState(Cryptographer* cryptographer,
- WriteNode* nigori_node);
-
- // Internal callback of UpdateCryptographerAndNigoriCallback.
- void UpdateCryptographerAndNigoriCallback(
- const std::string& chrome_version,
- const base::Closure& done_callback,
- const std::string& session_name);
-
- // Updates the nigori node with any new encrypted types and then
- // encrypts the nodes for those new data types as well as other
- // nodes that should be encrypted but aren't. Triggers
- // OnPassphraseRequired if the cryptographer isn't ready.
- void RefreshEncryption();
-
- void ReEncryptEverything(WriteTransaction* trans);
-
- // The final step of SetEncryptionPassphrase and SetDecryptionPassphrase that
- // notifies observers of the result of the set passphrase operation, updates
- // the nigori node, and does re-encryption.
- // |success|: true if the operation was successful and false otherwise. If
- // success == false, we send an OnPassphraseRequired notification.
- // |bootstrap_token|: used to inform observers if the cryptographer's
- // bootstrap token was updated.
- // |is_explicit|: used to differentiate between a custom passphrase (true) and
- // a GAIA passphrase that is implicitly used for encryption
- // (false).
- // |trans| and |nigori_node|: used to access data in the cryptographer.
- void FinishSetPassphrase(
- bool success,
- const std::string& bootstrap_token,
- bool is_explicit,
- WriteTransaction* trans,
- WriteNode* nigori_node);
+ // Internal callback used by GetSessionName.
+ // TODO(rlarocque): not currently called from anywhere. This should be
+ // hooked up to something once we start preserving device information again.
+ void UpdateSessionNameCallback(const std::string& chrome_version,
+ const std::string& session_name);
// Called for every notification. This updates the notification statistics
// to be displayed in about:sync.
@@ -402,6 +364,7 @@ class SyncManagerImpl : public SyncManager,
WeakHandle<JsEventHandler> js_event_handler_;
JsSyncManagerObserver js_sync_manager_observer_;
JsMutationEventObserver js_mutation_event_observer_;
+ JsSyncEncryptionHandlerObserver js_sync_encryption_handler_observer_;
ThrottledDataTypeTracker throttled_data_type_tracker_;
@@ -414,10 +377,10 @@ class SyncManagerImpl : public SyncManager,
UnrecoverableErrorHandler* unrecoverable_error_handler_;
ReportUnrecoverableErrorFunction report_unrecoverable_error_function_;
- // The number of times we've automatically (i.e. not via SetPassphrase or
- // conflict resolver) updated the nigori's encryption keys in this chrome
- // instantiation.
- int nigori_overwrite_count_;
+ // Sync's encryption handler. It tracks the set of encrypted types, manages
+ // changing passphrases, and in general handles sync-specific interactions
+ // with the cryptographer.
+ scoped_ptr<SyncEncryptionHandlerImpl> sync_encryption_handler_;
DISALLOW_COPY_AND_ASSIGN(SyncManagerImpl);
};
diff --git a/sync/internal_api/sync_manager_impl_unittest.cc b/sync/internal_api/sync_manager_impl_unittest.cc
index c41215c..48b81c1 100644
--- a/sync/internal_api/sync_manager_impl_unittest.cc
+++ b/sync/internal_api/sync_manager_impl_unittest.cc
@@ -36,6 +36,7 @@
#include "sync/internal_api/public/test/test_user_share.h"
#include "sync/internal_api/public/write_node.h"
#include "sync/internal_api/public/write_transaction.h"
+#include "sync/internal_api/sync_encryption_handler_impl.h"
#include "sync/internal_api/sync_manager_impl.h"
#include "sync/internal_api/syncapi_internal.h"
#include "sync/js/js_arg_list.h"
@@ -77,6 +78,7 @@ using testing::AtLeast;
using testing::DoAll;
using testing::InSequence;
using testing::Invoke;
+using testing::NiceMock;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
@@ -225,15 +227,25 @@ class SyncApiTest : public testing::Test {
public:
virtual void SetUp() {
test_user_share_.SetUp();
+ SetUpEncryption();
}
virtual void TearDown() {
test_user_share_.TearDown();
}
+ void SetUpEncryption() {
+ ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
+ encryption_handler_.reset(
+ new SyncEncryptionHandlerImpl(test_user_share_.user_share(),
+ trans.GetCryptographer()));
+ trans.GetCryptographer()->SetNigoriHandler(encryption_handler_.get());
+ }
+
protected:
MessageLoop message_loop_;
TestUserShare test_user_share_;
+ scoped_ptr<SyncEncryptionHandlerImpl> encryption_handler_;
};
TEST_F(SyncApiTest, SanityCheckTest) {
@@ -472,8 +484,8 @@ TEST_F(SyncApiTest, WriteEncryptedTitle) {
{
ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
trans.GetCryptographer()->AddKey(params);
- trans.GetCryptographer()->set_encrypt_everything();
}
+ encryption_handler_->EnableEncryptEverything();
{
WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
ReadNode root_node(&trans);
@@ -680,18 +692,24 @@ class SyncManagerObserverMock : public SyncManager::Observer {
void(const WeakHandle<JsBackend>&, bool,
syncer::ModelTypeSet)); // NOLINT
MOCK_METHOD1(OnConnectionStatusChange, void(ConnectionStatus)); // NOLINT
+ MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
+ MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
+ MOCK_METHOD1(OnActionableError,
+ void(const SyncProtocolError&)); // NOLINT
+};
+
+class SyncEncryptionHandlerObserverMock
+ : public SyncEncryptionHandler::Observer {
+ public:
MOCK_METHOD2(OnPassphraseRequired,
void(PassphraseRequiredReason,
const sync_pb::EncryptedData&)); // NOLINT
MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT
MOCK_METHOD1(OnBootstrapTokenUpdated, void(const std::string&)); // NOLINT
- MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
- MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
MOCK_METHOD2(OnEncryptedTypesChanged,
void(ModelTypeSet, bool)); // NOLINT
MOCK_METHOD0(OnEncryptionComplete, void()); // NOLINT
- MOCK_METHOD1(OnActionableError,
- void(const SyncProtocolError&)); // NOLINT
+ MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*)); // NOLINT
};
class SyncNotifierMock : public SyncNotifier {
@@ -752,8 +770,8 @@ class SyncManagerTest : public testing::Test,
// Called by ShutdownOnSyncThread().
EXPECT_CALL(*sync_notifier_mock_, UnregisterHandler(_));
- sync_manager_.AddObserver(&observer_);
- EXPECT_CALL(observer_, OnInitializationComplete(_, _, _)).
+ sync_manager_.AddObserver(&manager_observer_);
+ EXPECT_CALL(manager_observer_, OnInitializationComplete(_, _, _)).
WillOnce(SaveArg<0>(&js_backend_));
EXPECT_FALSE(js_backend_.IsInitialized());
@@ -778,6 +796,8 @@ class SyncManagerTest : public testing::Test,
&handler_,
NULL);
+ sync_manager_.GetEncryptionHandler()->AddObserver(&encryption_observer_);
+
EXPECT_TRUE(js_backend_.IsInitialized());
for (ModelSafeRoutingInfo::iterator i = routing_info.begin();
@@ -789,7 +809,7 @@ class SyncManagerTest : public testing::Test,
}
void TearDown() {
- sync_manager_.RemoveObserver(&observer_);
+ sync_manager_.RemoveObserver(&manager_observer_);
// |sync_notifier_mock_| is strict, which ensures we don't do anything but
// unregister |sync_manager_| as a handler on shutdown.
sync_manager_.ShutdownOnSyncThread();
@@ -825,6 +845,9 @@ class SyncManagerTest : public testing::Test,
return false;
// Set the nigori cryptographer information.
+ if (encryption_status == FULL_ENCRYPTION)
+ sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
+
WriteTransaction trans(FROM_HERE, share);
Cryptographer* cryptographer = trans.GetCryptographer();
if (!cryptographer)
@@ -835,12 +858,12 @@ class SyncManagerTest : public testing::Test,
} else {
DCHECK_NE(nigori_status, WRITE_TO_NIGORI);
}
- if (encryption_status == FULL_ENCRYPTION)
- cryptographer->set_encrypt_everything();
if (nigori_status == WRITE_TO_NIGORI) {
sync_pb::NigoriSpecifics nigori;
cryptographer->GetKeys(nigori.mutable_encrypted());
- cryptographer->UpdateNigoriFromEncryptedTypes(&nigori);
+ cryptographer->UpdateNigoriFromEncryptedTypes(
+ &nigori,
+ trans.GetWrappedTrans());
WriteNode node(&trans);
EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(nigori_id));
node.SetNigoriSpecifics(nigori);
@@ -896,8 +919,7 @@ class SyncManagerTest : public testing::Test,
// Returns true if we are currently encrypting all sync data. May
// be called on any thread.
bool EncryptEverythingEnabledForTest() {
- ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
- return trans.GetCryptographer()->encrypt_everything();
+ return sync_manager_.GetEncryptionHandler()->EncryptEverythingEnabled();
}
// Gets the set of encrypted types from the cryptographer
@@ -962,7 +984,8 @@ class SyncManagerTest : public testing::Test,
StrictMock<SyncNotifierMock>* sync_notifier_mock_;
SyncManagerImpl sync_manager_;
WeakHandle<JsBackend> js_backend_;
- StrictMock<SyncManagerObserverMock> observer_;
+ StrictMock<SyncManagerObserverMock> manager_observer_;
+ StrictMock<SyncEncryptionHandlerObserverMock> encryption_observer_;
InternalComponentsFactory::Switches switches_;
};
@@ -1354,9 +1377,10 @@ TEST_F(SyncManagerTest, OnIncomingNotification) {
TEST_F(SyncManagerTest, RefreshEncryptionReady) {
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
const ModelTypeSet encrypted_types = GetEncryptedDataTypesForTest();
@@ -1380,8 +1404,11 @@ TEST_F(SyncManagerTest, RefreshEncryptionReady) {
TEST_F(SyncManagerTest, RefreshEncryptionNotReady) {
// Don't set up encryption (no nigori node created).
- // Should fail.
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ // Should fail. Triggers an OnPassphraseRequired because the cryptographer
+ // is not ready.
+ EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_, _)).Times(1);
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
const ModelTypeSet encrypted_types = GetEncryptedDataTypesForTest();
@@ -1392,10 +1419,11 @@ TEST_F(SyncManagerTest, RefreshEncryptionNotReady) {
// Attempt to refresh encryption when nigori is empty.
TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) {
EXPECT_TRUE(SetUpEncryption(DONT_WRITE_NIGORI, DEFAULT_ENCRYPTION));
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(1);
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
// Should write to nigori.
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
const ModelTypeSet encrypted_types = GetEncryptedDataTypesForTest();
@@ -1417,11 +1445,11 @@ TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) {
TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.EnableEncryptEverything();
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
EXPECT_TRUE(EncryptEverythingEnabledForTest());
}
@@ -1456,7 +1484,7 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
EXPECT_TRUE(GetEncryptedTypes(&trans).Equals(
- Cryptographer::SensitiveTypes()));
+ SyncEncryptionHandler::SensitiveTypes()));
EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
trans.GetWrappedTrans(),
trans.GetCryptographer(),
@@ -1474,11 +1502,11 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
false /* not encrypted */));
}
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.EnableEncryptEverything();
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
EXPECT_TRUE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1502,11 +1530,13 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
}
// Trigger's a ReEncryptEverything with new passphrase.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", true);
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase", true);
EXPECT_TRUE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1529,12 +1559,11 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
}
// Calling EncryptDataTypes with an empty encrypted types should not trigger
// a reencryption and should just notify immediately.
- // TODO(zea): add logic to ensure nothing was written.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_)).Times(0);
- EXPECT_CALL(observer_, OnPassphraseAccepted()).Times(0);
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.EnableEncryptEverything();
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_)).Times(0);
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()).Times(0);
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(0);
+ sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
}
// Test that when there are no pending keys and the cryptographer is not
@@ -1542,10 +1571,15 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
// (case 1 in SyncManager::SyncInternal::SetEncryptionPassphrase)
TEST_F(SyncManagerTest, SetInitialGaiaPass) {
EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED));
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", false);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ false);
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1571,10 +1605,15 @@ TEST_F(SyncManagerTest, UpdateGaiaPass) {
cryptographer->GetBootstrapToken(&bootstrap_token);
verifier.Bootstrap(bootstrap_token);
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", false);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ false);
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1613,10 +1652,15 @@ TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
data.set_password_value("secret");
password_node.SetPasswordSpecifics(data);
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", true);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ true);
+ EXPECT_TRUE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1659,14 +1703,17 @@ TEST_F(SyncManagerTest, SupplyPendingGAIAPass) {
EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
sync_pb::NigoriSpecifics nigori;
other_cryptographer.GetKeys(nigori.mutable_encrypted());
- cryptographer->Update(nigori);
+ cryptographer->SetPendingKeys(nigori.encrypted());
EXPECT_TRUE(cryptographer->has_pending_keys());
node.SetNigoriSpecifics(nigori);
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetDecryptionPassphrase("passphrase2");
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("passphrase2");
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1704,7 +1751,7 @@ TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) {
sync_pb::NigoriSpecifics nigori;
other_cryptographer.GetKeys(nigori.mutable_encrypted());
node.SetNigoriSpecifics(nigori);
- cryptographer->Update(nigori);
+ cryptographer->SetPendingKeys(nigori.encrypted());
// other_cryptographer now contains all encryption keys, and is encrypting
// with the newest gaia.
@@ -1714,12 +1761,17 @@ TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) {
// The bootstrap token should have been updated. Save it to ensure it's based
// on the new GAIA password.
std::string bootstrap_token;
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_))
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_))
.WillOnce(SaveArg<0>(&bootstrap_token));
- EXPECT_CALL(observer_, OnPassphraseRequired(_,_));
- sync_manager_.SetEncryptionPassphrase("new_gaia", false);
+ EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_,_));
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_gaia",
+ false);
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
- testing::Mock::VerifyAndClearExpectations(&observer_);
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
Cryptographer* cryptographer = trans.GetCryptographer();
@@ -1731,10 +1783,15 @@ TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) {
other_cryptographer.GetKeys(&encrypted);
EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("old_gaia", false);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "old_gaia",
+ false);
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
Cryptographer* cryptographer = trans.GetCryptographer();
@@ -1774,15 +1831,18 @@ TEST_F(SyncManagerTest, SupplyPendingExplicitPass) {
EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
sync_pb::NigoriSpecifics nigori;
other_cryptographer.GetKeys(nigori.mutable_encrypted());
- cryptographer->Update(nigori);
+ cryptographer->SetPendingKeys(nigori.encrypted());
EXPECT_TRUE(cryptographer->has_pending_keys());
nigori.set_using_explicit_passphrase(true);
node.SetNigoriSpecifics(nigori);
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetDecryptionPassphrase("explicit");
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("explicit");
+ EXPECT_TRUE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1814,13 +1874,18 @@ TEST_F(SyncManagerTest, SupplyPendingGAIAPassUserProvided) {
sync_pb::NigoriSpecifics nigori;
other_cryptographer.GetKeys(nigori.mutable_encrypted());
node.SetNigoriSpecifics(nigori);
- cryptographer->Update(nigori);
+ cryptographer->SetPendingKeys(nigori.encrypted());
EXPECT_FALSE(cryptographer->is_ready());
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("passphrase", false);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "passphrase",
+ false);
+ EXPECT_FALSE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1844,10 +1909,15 @@ TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
node_id = password_node.GetId();
}
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", true);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ true);
+ EXPECT_TRUE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_FALSE(EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1949,11 +2019,11 @@ TEST_F(SyncManagerTest, EncryptBookmarksWithLegacyData) {
false /* not encrypted */));
}
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.EnableEncryptEverything();
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
EXPECT_TRUE(EncryptEverythingEnabledForTest());
{
@@ -2037,13 +2107,14 @@ TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
// Encrypt the datatatype, should set is_unsynced.
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -2062,11 +2133,14 @@ TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
// Set a new passphrase. Should set is_unsynced.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", true);
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ true);
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
ReadNode node(&trans);
@@ -2084,10 +2158,11 @@ TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
// Force a re-encrypt everything. Should not set is_unsynced.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
{
@@ -2253,11 +2328,16 @@ TEST_F(SyncManagerTest, UpdatePasswordNewPassphrase) {
EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
// Set a new passphrase. Should set is_unsynced.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
- EXPECT_CALL(observer_, OnPassphraseAccepted());
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetEncryptionPassphrase("new_passphrase", true);
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+ "new_passphrase",
+ true);
+ EXPECT_TRUE(
+ sync_manager_.GetEncryptionHandler()->IsUsingExplicitPassphrase());
EXPECT_TRUE(ResetUnsyncedEntry(PASSWORDS, client_tag));
}
@@ -2284,9 +2364,10 @@ TEST_F(SyncManagerTest, UpdatePasswordReencryptEverything) {
EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
// Force a re-encrypt everything. Should not set is_unsynced.
- testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
}
@@ -2342,12 +2423,13 @@ TEST_F(SyncManagerTest, SetBookmarkTitleWithEncryption) {
EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
// Encrypt the datatatype, should set is_unsynced.
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
@@ -2437,12 +2519,13 @@ TEST_F(SyncManagerTest, SetNonBookmarkTitleWithEncryption) {
EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
// Encrypt the datatatype, should set is_unsynced.
- EXPECT_CALL(observer_,
+ EXPECT_CALL(encryption_observer_,
OnEncryptedTypesChanged(
HasModelTypes(ModelTypeSet::All()), true));
- EXPECT_CALL(observer_, OnEncryptionComplete());
+ EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
- sync_manager_.RefreshNigori(kTestChromeVersion, base::Bind(&DoNothing));
+ EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+ sync_manager_.GetEncryptionHandler()->Init();
PumpLoop();
EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag));
diff --git a/sync/internal_api/test/fake_sync_manager.cc b/sync/internal_api/test/fake_sync_manager.cc
index 0e1e24b..97e729e 100644
--- a/sync/internal_api/test/fake_sync_manager.cc
+++ b/sync/internal_api/test/fake_sync_manager.cc
@@ -19,6 +19,7 @@
#include "sync/notifier/notifications_disabled_reason.h"
#include "sync/notifier/object_id_payload_map.h"
#include "sync/notifier/sync_notifier.h"
+#include "sync/test/fake_sync_encryption_handler.h"
namespace syncer {
@@ -27,7 +28,9 @@ FakeSyncManager::FakeSyncManager(ModelTypeSet initial_sync_ended_types,
ModelTypeSet configure_fail_types) :
initial_sync_ended_types_(initial_sync_ended_types),
progress_marker_types_(progress_marker_types),
- configure_fail_types_(configure_fail_types) {}
+ configure_fail_types_(configure_fail_types) {
+ fake_encryption_handler_.reset(new FakeSyncEncryptionHandler());
+}
FakeSyncManager::~FakeSyncManager() {}
@@ -180,15 +183,6 @@ void FakeSyncManager::StartSyncingNormally(
// Do nothing.
}
-void FakeSyncManager::SetEncryptionPassphrase(const std::string& passphrase,
- bool is_explicit) {
- NOTIMPLEMENTED();
-}
-
-void FakeSyncManager::SetDecryptionPassphrase(const std::string& passphrase) {
- NOTIMPLEMENTED();
-}
-
void FakeSyncManager::ConfigureSyncer(
ConfigureReason reason,
const ModelTypeSet& types_to_config,
@@ -235,11 +229,6 @@ SyncStatus FakeSyncManager::GetDetailedStatus() const {
return SyncStatus();
}
-bool FakeSyncManager::IsUsingExplicitPassphrase() {
- NOTIMPLEMENTED();
- return false;
-}
-
bool FakeSyncManager::GetKeystoreKeyBootstrapToken(std::string* token) {
return false;
}
@@ -259,19 +248,9 @@ void FakeSyncManager::ShutdownOnSyncThread() {
}
UserShare* FakeSyncManager::GetUserShare() {
- NOTIMPLEMENTED();
return NULL;
}
-void FakeSyncManager::RefreshNigori(const std::string& chrome_version,
- const base::Closure& done_callback) {
- done_callback.Run();
-}
-
-void FakeSyncManager::EnableEncryptEverything() {
- NOTIMPLEMENTED();
-}
-
bool FakeSyncManager::ReceivedExperiment(Experiments* experiments) {
return false;
}
@@ -281,6 +260,10 @@ bool FakeSyncManager::HasUnsyncedItems() {
return false;
}
+SyncEncryptionHandler* FakeSyncManager::GetEncryptionHandler() {
+ return fake_encryption_handler_.get();
+}
+
void FakeSyncManager::InvalidateOnSyncThread(
const ObjectIdPayloadMap& id_payloads,
IncomingNotificationSource source) {
diff --git a/sync/sync.gyp b/sync/sync.gyp
index ea2caac..720a28c 100644
--- a/sync/sync.gyp
+++ b/sync/sync.gyp
@@ -175,6 +175,8 @@
'syncable/model_type.cc',
'syncable/mutable_entry.cc',
'syncable/mutable_entry.h',
+ 'syncable/nigori_handler.h',
+ 'syncable/nigori_handler.cc',
'syncable/nigori_util.cc',
'syncable/nigori_util.h',
'syncable/on_disk_directory_backing_store.cc',
@@ -322,6 +324,8 @@
'internal_api/public/read_node.h',
'internal_api/public/read_transaction.h',
'internal_api/public/sync_manager.h',
+ 'internal_api/public/sync_encryption_handler.cc',
+ 'internal_api/public/sync_encryption_handler.h',
'internal_api/public/sync_manager.cc',
'internal_api/public/sync_manager_factory.h',
'internal_api/public/user_share.h',
@@ -338,6 +342,8 @@
'internal_api/internal_components_factory_impl.cc',
'internal_api/js_mutation_event_observer.cc',
'internal_api/js_mutation_event_observer.h',
+ 'internal_api/js_sync_encryption_handler_observer.cc',
+ 'internal_api/js_sync_encryption_handler_observer.h',
'internal_api/js_sync_manager_observer.cc',
'internal_api/js_sync_manager_observer.h',
'internal_api/read_node.cc',
@@ -346,6 +352,8 @@
'internal_api/syncapi_internal.h',
'internal_api/syncapi_server_connection_manager.cc',
'internal_api/syncapi_server_connection_manager.h',
+ 'internal_api/sync_encryption_handler_impl.cc',
+ 'internal_api/sync_encryption_handler_impl.h',
'internal_api/sync_manager_factory.cc',
'internal_api/sync_manager_impl.cc',
'internal_api/sync_manager_impl.h',
@@ -432,6 +440,8 @@
'test/engine/test_syncable_utils.h',
'test/fake_encryptor.cc',
'test/fake_encryptor.h',
+ 'test/fake_sync_encryption_handler.h',
+ 'test/fake_sync_encryption_handler.cc',
'test/fake_extensions_activity_monitor.cc',
'test/fake_extensions_activity_monitor.h',
'test/null_directory_change_delegate.cc',
@@ -709,8 +719,10 @@
'internal_api/debug_info_event_listener_unittest.cc',
'internal_api/http_bridge_unittest.cc',
'internal_api/js_mutation_event_observer_unittest.cc',
+ 'internal_api/js_sync_encryption_handler_observer_unittest.cc',
'internal_api/js_sync_manager_observer_unittest.cc',
'internal_api/syncapi_server_connection_manager_unittest.cc',
+ 'internal_api/sync_encryption_handler_impl_unittest.cc',
'internal_api/sync_manager_impl_unittest.cc',
],
},
diff --git a/sync/syncable/nigori_handler.cc b/sync/syncable/nigori_handler.cc
new file mode 100644
index 0000000..e193c893
--- /dev/null
+++ b/sync/syncable/nigori_handler.cc
@@ -0,0 +1,14 @@
+// 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/syncable/nigori_handler.h"
+
+namespace syncer {
+namespace syncable {
+
+NigoriHandler::NigoriHandler() {}
+NigoriHandler::~NigoriHandler() {}
+
+} // namespace syncer
+} // namespace syncable
diff --git a/sync/syncable/nigori_handler.h b/sync/syncable/nigori_handler.h
new file mode 100644
index 0000000..94bb644
--- /dev/null
+++ b/sync/syncable/nigori_handler.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef SYNC_SYNCABLE_NIGORI_HANDLER_H_
+#define SYNC_SYNCABLE_NIGORI_HANDLER_H_
+
+#include "sync/internal_api/public/base/model_type.h"
+
+namespace sync_pb {
+class NigoriSpecifics;
+}
+
+namespace syncer {
+namespace syncable {
+
+class BaseTransaction;
+
+// Sync internal interface for dealing with nigori node and querying
+// the current set of encrypted types. Not thread safe, so a sync transaction
+// must be held by a caller whenever invoking methods.
+class NigoriHandler {
+ public:
+ NigoriHandler();
+ virtual ~NigoriHandler();
+
+ // Apply a nigori node update, updating the internal encryption state
+ // accordingly.
+ virtual void ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) = 0;
+
+ // Store the current encrypt everything/encrypted types state into |nigori|.
+ virtual void UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const = 0;
+
+ // Returns the set of currently encrypted types.
+ // TODO(zea): force callers to pass their syncable trans here.
+ virtual ModelTypeSet GetEncryptedTypes() const = 0;
+};
+
+} // namespace syncable
+} // namespace syncer
+
+#endif // SYNC_SYNCABLE_NIGORI_HANDLER_H_
diff --git a/sync/syncable/nigori_util.cc b/sync/syncable/nigori_util.cc
index 32a5fcf..72d9281 100644
--- a/sync/syncable/nigori_util.cc
+++ b/sync/syncable/nigori_util.cc
@@ -246,5 +246,70 @@ bool UpdateEntryWithEncryption(
return true;
}
+void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types,
+ bool encrypt_everything,
+ sync_pb::NigoriSpecifics* nigori) {
+ nigori->set_encrypt_everything(encrypt_everything);
+ COMPILE_ASSERT(17, MODEL_TYPE_COUNT);
+ 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_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));
+}
+
+ModelTypeSet GetEncryptedTypesFromNigori(
+ const sync_pb::NigoriSpecifics& nigori) {
+ if (nigori.encrypt_everything())
+ return ModelTypeSet::All();
+
+ ModelTypeSet encrypted_types;
+ COMPILE_ASSERT(17, MODEL_TYPE_COUNT);
+ 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_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);
+ return encrypted_types;
+}
+
} // namespace syncable
} // namespace syncer
diff --git a/sync/syncable/nigori_util.h b/sync/syncable/nigori_util.h
index 2edc9cd..0c020b4 100644
--- a/sync/syncable/nigori_util.h
+++ b/sync/syncable/nigori_util.h
@@ -68,6 +68,16 @@ bool UpdateEntryWithEncryption(
const sync_pb::EntitySpecifics& new_specifics,
MutableEntry* entry);
+// Updates |nigori| to match the encryption state specified by |encrypted_types|
+// and |encrypt_everything|.
+void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types,
+ bool encrypt_everything,
+ sync_pb::NigoriSpecifics* nigori);
+
+// Extracts the set of encrypted types from a nigori node.
+ModelTypeSet GetEncryptedTypesFromNigori(
+ const sync_pb::NigoriSpecifics& nigori);
+
} // namespace syncable
} // namespace syncer
diff --git a/sync/test/fake_sync_encryption_handler.cc b/sync/test/fake_sync_encryption_handler.cc
new file mode 100644
index 0000000..b490862
--- /dev/null
+++ b/sync/test/fake_sync_encryption_handler.cc
@@ -0,0 +1,106 @@
+// 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/test/fake_sync_encryption_handler.h"
+
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/syncable/nigori_util.h"
+#include "sync/util/cryptographer.h"
+
+namespace syncer {
+
+FakeSyncEncryptionHandler::FakeSyncEncryptionHandler()
+ : encrypted_types_(SensitiveTypes()),
+ encrypt_everything_(false),
+ explicit_passphrase_(false),
+ cryptographer_(NULL) {
+}
+FakeSyncEncryptionHandler::~FakeSyncEncryptionHandler() {}
+
+void FakeSyncEncryptionHandler::Init() {
+ // Do nothing.
+}
+
+void FakeSyncEncryptionHandler::ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) {
+ if (nigori.encrypt_everything())
+ EnableEncryptEverything();
+ if (nigori.using_explicit_passphrase())
+ explicit_passphrase_ = true;
+
+ if (!cryptographer_)
+ return;
+
+ if (cryptographer_->CanDecrypt(nigori.encrypted()))
+ cryptographer_->InstallKeys(nigori.encrypted());
+ else
+ cryptographer_->SetPendingKeys(nigori.encrypted());
+
+ if (cryptographer_->has_pending_keys()) {
+ DVLOG(1) << "OnPassPhraseRequired Sent";
+ sync_pb::EncryptedData pending_keys = cryptographer_->GetPendingKeys();
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_DECRYPTION,
+ pending_keys));
+ } else if (!cryptographer_->is_ready()) {
+ DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
+ << "ready";
+ FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
+ OnPassphraseRequired(REASON_ENCRYPTION,
+ sync_pb::EncryptedData()));
+ }
+}
+
+void FakeSyncEncryptionHandler::UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const {
+ syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_,
+ encrypt_everything_,
+ nigori);
+}
+
+void FakeSyncEncryptionHandler::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FakeSyncEncryptionHandler::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void FakeSyncEncryptionHandler::SetEncryptionPassphrase(
+ const std::string& passphrase,
+ bool is_explicit) {
+ if (is_explicit)
+ explicit_passphrase_ = true;
+}
+
+void FakeSyncEncryptionHandler::SetDecryptionPassphrase(
+ const std::string& passphrase) {
+ // Do nothing.
+}
+
+void FakeSyncEncryptionHandler::EnableEncryptEverything() {
+ if (encrypt_everything_)
+ return;
+ encrypt_everything_ = true;
+ encrypted_types_ = ModelTypeSet::All();
+ FOR_EACH_OBSERVER(
+ Observer, observers_,
+ OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
+}
+
+bool FakeSyncEncryptionHandler::EncryptEverythingEnabled() const {
+ return encrypt_everything_;
+}
+
+ModelTypeSet FakeSyncEncryptionHandler::GetEncryptedTypes() const {
+ return encrypted_types_;
+}
+
+bool FakeSyncEncryptionHandler::IsUsingExplicitPassphrase() const {
+ return explicit_passphrase_;
+}
+
+} // namespace syncer
diff --git a/sync/test/fake_sync_encryption_handler.h b/sync/test/fake_sync_encryption_handler.h
new file mode 100644
index 0000000..83a2e63
--- /dev/null
+++ b/sync/test/fake_sync_encryption_handler.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef SYNC_SYNCABLE_TEST_FAKE_SYNC_ENCRYPTION_HANDLER_H_
+#define SYNC_SYNCABLE_TEST_FAKE_SYNC_ENCRYPTION_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
+#include "sync/syncable/nigori_handler.h"
+
+namespace syncer {
+
+class Cryptographer;
+
+// A fake sync encryption handler capable of keeping track of the encryption
+// state without opening any transactions or interacting with the nigori node.
+// Note that this only performs basic interactions with the cryptographer
+// (setting pending keys, installing keys).
+// Note: NOT thread safe. If threads attempt to check encryption state
+// while another thread is modifying it, races can occur.
+class FakeSyncEncryptionHandler : public SyncEncryptionHandler,
+ public syncable::NigoriHandler {
+ public:
+ FakeSyncEncryptionHandler();
+ virtual ~FakeSyncEncryptionHandler();
+
+ void set_cryptographer(Cryptographer* cryptographer) {
+ cryptographer_ = cryptographer;
+ }
+
+ // SyncEncryptionHandler implementation.
+ virtual void AddObserver(Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(Observer* observer) OVERRIDE;
+ virtual void Init() OVERRIDE;
+ virtual void SetEncryptionPassphrase(const std::string& passphrase,
+ bool is_explicit) OVERRIDE;
+ virtual void SetDecryptionPassphrase(const std::string& passphrase) OVERRIDE;
+ virtual void EnableEncryptEverything() OVERRIDE;
+ virtual bool EncryptEverythingEnabled() const OVERRIDE;
+ virtual bool IsUsingExplicitPassphrase() const OVERRIDE;
+
+ // NigoriHandler implemenation.
+ virtual void ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) OVERRIDE;
+ virtual ModelTypeSet GetEncryptedTypes() const OVERRIDE;
+ virtual void UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const OVERRIDE;
+
+ private:
+ ObserverList<SyncEncryptionHandler::Observer> observers_;
+ ModelTypeSet encrypted_types_;
+ bool encrypt_everything_;
+ bool explicit_passphrase_;
+
+ Cryptographer* cryptographer_;
+};
+
+} // namespace syncer
+
+#endif // SYNC_INTERNAL_API_PUBLIC_TEST_FAKE_SYNC_ENCRYPTION_HANDLER_H_
diff --git a/sync/util/DEPS b/sync/util/DEPS
index 0662b96..23fe0bd 100644
--- a/sync/util/DEPS
+++ b/sync/util/DEPS
@@ -8,4 +8,8 @@ include_rules = [
# TODO(kochi): Remove this hack after "Chromebox" hack in get_session_name.cc
# is gone.
"+chrome/browser/chromeos/system",
+
+ # TODO(zea): remove this once we don't need the cryptographer to get the set
+ # of encrypted types.
+ "+sync/syncable/nigori_handler.h"
]
diff --git a/sync/util/cryptographer.cc b/sync/util/cryptographer.cc
index c512ab6..0cdb389 100644
--- a/sync/util/cryptographer.cc
+++ b/sync/util/cryptographer.cc
@@ -8,6 +8,8 @@
#include "base/base64.h"
#include "base/logging.h"
+#include "sync/protocol/nigori_specifics.pb.h"
+#include "sync/syncable/nigori_handler.h"
#include "sync/util/encryptor.h"
namespace syncer {
@@ -20,27 +22,37 @@ const char kNigoriTag[] = "google_chrome_nigori";
// assign the same name to a particular triplet.
const char kNigoriKeyName[] = "nigori-key";
-Cryptographer::Observer::~Observer() {}
-
Cryptographer::Cryptographer(Encryptor* encryptor)
: encryptor_(encryptor),
default_nigori_(NULL),
keystore_nigori_(NULL),
- encrypted_types_(SensitiveTypes()),
- encrypt_everything_(false) {
+ nigori_node_handler_(NULL) {
DCHECK(encryptor);
}
Cryptographer::~Cryptographer() {}
-void Cryptographer::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
+void Cryptographer::SetNigoriHandler(syncable::NigoriHandler* delegate) {
+ nigori_node_handler_ = delegate;
+}
+
+void Cryptographer::ApplyNigoriUpdate(
+ const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans) {
+ nigori_node_handler_->ApplyNigoriUpdate(nigori, trans);
+}
+
+ModelTypeSet Cryptographer::GetEncryptedTypes() const {
+ return nigori_node_handler_->GetEncryptedTypes();
}
-void Cryptographer::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
+void Cryptographer::UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const {
+ nigori_node_handler_->UpdateNigoriFromEncryptedTypes(nigori, trans);
}
+
void Cryptographer::Bootstrap(const std::string& restored_bootstrap_token) {
if (is_initialized()) {
NOTREACHED();
@@ -194,6 +206,11 @@ void Cryptographer::InstallKeys(const sync_pb::EncryptedData& encrypted) {
InstallKeyBag(bag);
}
+void Cryptographer::SetDefaultKey(const std::string& key_name) {
+ DCHECK(nigoris_.end() != nigoris_.find(key_name));
+ default_nigori_ = &*nigoris_.find(key_name);
+}
+
void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) {
DCHECK(!CanDecrypt(encrypted));
pending_keys_.reset(new sync_pb::EncryptedData(encrypted));
@@ -311,29 +328,6 @@ Nigori* Cryptographer::UnpackBootstrapToken(const std::string& token) const {
return nigori.release();
}
-Cryptographer::UpdateResult Cryptographer::Update(
- const sync_pb::NigoriSpecifics& nigori) {
- UpdateEncryptedTypesFromNigori(nigori);
- if (!nigori.encrypted().blob().empty()) {
- if (CanDecrypt(nigori.encrypted())) {
- InstallKeys(nigori.encrypted());
- // We only update the default passphrase if this was a new explicit
- // passphrase. Else, since it was decryptable, it must not have been a new
- // key.
- if (nigori.using_explicit_passphrase()) {
- std::string new_default_key_name = nigori.encrypted().key_name();
- DCHECK(nigoris_.end() != nigoris_.find(new_default_key_name));
- default_nigori_ = &*nigoris_.find(new_default_key_name);
- }
- return Cryptographer::SUCCESS;
- } else {
- SetPendingKeys(nigori.encrypted());
- return Cryptographer::NEEDS_PASSPHRASE;
- }
- }
- return Cryptographer::SUCCESS;
-}
-
bool Cryptographer::SetKeystoreKey(const std::string& keystore_key) {
if (keystore_key.empty())
return false;
@@ -355,128 +349,6 @@ bool Cryptographer::HasKeystoreKey() const {
return keystore_nigori_ != NULL;
}
-// Static
-ModelTypeSet Cryptographer::SensitiveTypes() {
- // Both of these have their own encryption schemes, but we include them
- // anyways.
- ModelTypeSet types;
- types.Put(PASSWORDS);
- types.Put(NIGORI);
- return types;
-}
-
-void Cryptographer::UpdateEncryptedTypesFromNigori(
- const sync_pb::NigoriSpecifics& nigori) {
- if (nigori.encrypt_everything()) {
- set_encrypt_everything();
- return;
- }
-
- ModelTypeSet encrypted_types(SensitiveTypes());
- 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_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);
-
- // Note: the initial version with encryption did not support the
- // encrypt_everything field. If anything more than the sensitive types were
- // encrypted, it meant we were encrypting everything.
- if (!nigori.has_encrypt_everything() &&
- !Difference(encrypted_types, SensitiveTypes()).Empty()) {
- set_encrypt_everything();
- return;
- }
-
- MergeEncryptedTypes(encrypted_types);
-}
-
-void Cryptographer::UpdateNigoriFromEncryptedTypes(
- sync_pb::NigoriSpecifics* nigori) const {
- nigori->set_encrypt_everything(encrypt_everything_);
- 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_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));
-}
-
-void Cryptographer::set_encrypt_everything() {
- if (encrypt_everything_) {
- DCHECK(encrypted_types_.Equals(ModelTypeSet::All()));
- return;
- }
- encrypt_everything_ = true;
- // Change |encrypted_types_| directly to avoid sending more than one
- // notification.
- encrypted_types_ = ModelTypeSet::All();
- EmitEncryptedTypesChangedNotification();
-}
-
-bool Cryptographer::encrypt_everything() const {
- return encrypt_everything_;
-}
-
-ModelTypeSet Cryptographer::GetEncryptedTypes() const {
- return encrypted_types_;
-}
-
-void Cryptographer::MergeEncryptedTypesForTest(ModelTypeSet encrypted_types) {
- MergeEncryptedTypes(encrypted_types);
-}
-
-void Cryptographer::MergeEncryptedTypes(ModelTypeSet encrypted_types) {
- if (encrypted_types_.HasAll(encrypted_types)) {
- return;
- }
- encrypted_types_ = encrypted_types;
- EmitEncryptedTypesChangedNotification();
-}
-
-void Cryptographer::EmitEncryptedTypesChangedNotification() {
- FOR_EACH_OBSERVER(
- Observer, observers_,
- OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_));
-}
-
void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) {
int key_size = bag.key_size();
for (int i = 0; i < key_size; ++i) {
diff --git a/sync/util/cryptographer.h b/sync/util/cryptographer.h
index b9c0001..6f9ab5c 100644
--- a/sync/util/cryptographer.h
+++ b/sync/util/cryptographer.h
@@ -11,16 +11,24 @@
#include "base/gtest_prod_util.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
-#include "base/observer_list.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/protocol/encryption.pb.h"
-#include "sync/protocol/nigori_specifics.pb.h"
#include "sync/util/nigori.h"
+namespace sync_pb {
+class NigoriKeyBag;
+class NigoriSpecifics;
+}
+
namespace syncer {
class Encryptor;
+namespace syncable {
+class BaseTransaction;
+class NigoriHandler;
+}
+
extern const char kNigoriTag[];
// The parameters used to initialize a Nigori instance.
@@ -46,46 +54,22 @@ struct KeyParams {
// delayed until after it can be decrypted.
class Cryptographer {
public:
- // All Observer methods are done synchronously, so they're called
- // under a transaction (since all Cryptographer operations are done
- // under a transaction).
- class Observer {
- public:
- // Called when the set of encrypted types or the encrypt
- // everything flag has been changed. Note that this doesn't
- // necessarily mean that encryption has completed for the given
- // types.
- //
- // |encrypted_types| will always be a superset of
- // SensitiveTypes(). If |encrypt_everything| is true,
- // |encrypted_types| will be the set of all known types.
- //
- // Until this function is called, observers can assume that the
- // set of encrypted types is SensitiveTypes() and that the encrypt
- // everything flag is false.
- virtual void OnEncryptedTypesChanged(
- ModelTypeSet encrypted_types,
- bool encrypt_everything) = 0;
-
- protected:
- virtual ~Observer();
- };
-
// Does not take ownership of |encryptor|.
explicit Cryptographer(Encryptor* encryptor);
~Cryptographer();
- // When update on cryptographer is called this enum tells if the
- // cryptographer was succesfully able to update using the nigori node or if
- // it needs a key to decrypt the nigori node.
- enum UpdateResult {
- SUCCESS,
- NEEDS_PASSPHRASE
- };
-
- // Manage observers.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
+ // Set the sync nigori node handler.
+ // TODO(zea): refactor so that Cryptographer doesn't need any connection
+ // to a NigoriHandler. crbug.com/139848
+ void SetNigoriHandler(syncable::NigoriHandler* delegate);
+
+ // NigoriHandler delegator methods (passes through to delegate).
+ void ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
+ syncable::BaseTransaction* const trans);
+ void UpdateNigoriFromEncryptedTypes(
+ sync_pb::NigoriSpecifics* nigori,
+ syncable::BaseTransaction* const trans) const;
+ ModelTypeSet GetEncryptedTypes() const;
// |restored_bootstrap_token| can be provided via this method to bootstrap
// Cryptographer instance into the ready state (is_ready will be true).
@@ -142,6 +126,13 @@ class Cryptographer {
// with a cryptographer that has already been initialized.
bool AddKeyFromBootstrapToken(const std::string restored_bootstrap_token);
+ // Decrypts |encrypted| and uses its contents to initialize Nigori instances.
+ // Returns true unless decryption of |encrypted| fails. The caller is
+ // responsible for checking that CanDecrypt(encrypted) == true.
+ // Does not update the default nigori.
+ void InstallKeys(const sync_pb::EncryptedData& encrypted);
+
+
// Makes a local copy of |encrypted| to later be decrypted by
// DecryptPendingKeys. This should only be used if CanDecrypt(encrypted) ==
// false.
@@ -159,6 +150,10 @@ class Cryptographer {
// is updated.
bool DecryptPendingKeys(const KeyParams& params);
+ // Sets the default key to the nigori with name |key_name|. |key_name| must
+ // correspond to a nigori that has already been installed into the keybag.
+ void SetDefaultKey(const std::string& key_name);
+
bool is_initialized() const { return !nigoris_.empty() && default_nigori_; }
// Returns whether this Cryptographer is ready to encrypt and decrypt data.
@@ -176,16 +171,6 @@ class Cryptographer {
// Obtain the bootstrap token based on the keystore encryption key.
bool GetKeystoreKeyBootstrapToken(std::string* token) const;
- // Update the cryptographer based on the contents of the nigori specifics.
- // This updates both the encryption keys and the set of encrypted types.
- // Returns NEEDS_PASSPHRASE if was unable to decrypt the pending keys,
- // SUCCESS otherwise.
- // Note: will not change the default key. If the nigori's keybag
- // is decryptable, all keys are added to the local keybag and the current
- // default is preserved. If the nigori's keybag is not decryptable, it is
- // stored in the |pending_keys_|.
- UpdateResult Update(const sync_pb::NigoriSpecifics& nigori);
-
// Set the keystore-derived nigori from the provided key.
// Returns true if we succesfully create the keystore derived nigori from the
// provided key, false otherwise.
@@ -195,44 +180,12 @@ class Cryptographer {
// otherwise.
bool HasKeystoreKey() const;
- // The set of types that are always encrypted.
- static ModelTypeSet SensitiveTypes();
-
- // Reset our set of encrypted types based on the contents of the nigori
- // specifics.
- void UpdateEncryptedTypesFromNigori(const sync_pb::NigoriSpecifics& nigori);
-
- // Update the nigori to reflect the current set of encrypted types.
- void UpdateNigoriFromEncryptedTypes(sync_pb::NigoriSpecifics* nigori) const;
-
- // Setter/getter for whether all current and future datatypes should
- // be encrypted. Once set you cannot unset without reading from a
- // new nigori node. set_encrypt_everything() emits a notification
- // the first time it's called.
- void set_encrypt_everything();
- bool encrypt_everything() const;
-
- // Return the set of encrypted types.
- ModelTypeSet GetEncryptedTypes() const;
-
- // Forwards to MergeEncryptedTypes.
- void MergeEncryptedTypesForTest(ModelTypeSet encrypted_types);
+ Encryptor* encryptor() const { return encryptor_; }
private:
FRIEND_TEST_ALL_PREFIXES(SyncCryptographerTest, PackUnpack);
- typedef std::map<std::string, linked_ptr<const Nigori> > NigoriMap;
-
- // Merges the given set of encrypted types with the existing set and emits a
- // notification if necessary.
- void MergeEncryptedTypes(ModelTypeSet encrypted_types);
-
- void EmitEncryptedTypesChangedNotification();
- // Decrypts |encrypted| and uses its contents to initialize Nigori instances.
- // Returns true unless decryption of |encrypted| fails. The caller is
- // responsible for checking that CanDecrypt(encrypted) == true.
- // Does not update the default nigori.
- void InstallKeys(const sync_pb::EncryptedData& encrypted);
+ typedef std::map<std::string, linked_ptr<const Nigori> > NigoriMap;
// Helper method to instantiate Nigori instances for each set of key
// parameters in |bag|.
@@ -250,16 +203,15 @@ class Cryptographer {
Encryptor* const encryptor_;
- ObserverList<Observer> observers_;
-
NigoriMap nigoris_; // The Nigoris we know about, mapped by key name.
NigoriMap::value_type* default_nigori_; // The Nigori used for encryption.
NigoriMap::value_type* keystore_nigori_; // Nigori generated from keystore.
scoped_ptr<sync_pb::EncryptedData> pending_keys_;
- ModelTypeSet encrypted_types_;
- bool encrypt_everything_;
+ // The sync nigori node handler. Necessary until we decouple the encrypted
+ // types from the cryptographer.
+ syncable::NigoriHandler* nigori_node_handler_;
DISALLOW_COPY_AND_ASSIGN(Cryptographer);
};
diff --git a/sync/util/cryptographer_unittest.cc b/sync/util/cryptographer_unittest.cc
index 01190e9..09fb63f 100644
--- a/sync/util/cryptographer_unittest.cc
+++ b/sync/util/cryptographer_unittest.cc
@@ -8,8 +8,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
-#include "sync/internal_api/public/base/model_type_test_util.h"
-#include "sync/protocol/nigori_specifics.pb.h"
#include "sync/protocol/password_specifics.pb.h"
#include "sync/test/fake_encryptor.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -20,13 +18,6 @@ namespace syncer {
namespace {
using ::testing::_;
-using ::testing::Mock;
-using ::testing::StrictMock;
-
-class MockObserver : public Cryptographer::Observer {
- public:
- MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool));
-};
} // namespace
@@ -251,167 +242,4 @@ TEST_F(SyncCryptographerTest, BootstrapKeystore) {
EXPECT_FALSE(cryptographer2.is_initialized());
}
-TEST_F(SyncCryptographerTest, NigoriEncryptionTypes) {
- Cryptographer cryptographer2(&encryptor_);
- sync_pb::NigoriSpecifics nigori;
-
- StrictMock<MockObserver> observer;
- cryptographer_.AddObserver(&observer);
- StrictMock<MockObserver> observer2;
- cryptographer2.AddObserver(&observer2);
-
- // Just set the sensitive types (shouldn't trigger any
- // notifications).
- ModelTypeSet encrypted_types(Cryptographer::SensitiveTypes());
- cryptographer_.MergeEncryptedTypesForTest(encrypted_types);
- cryptographer_.UpdateNigoriFromEncryptedTypes(&nigori);
- cryptographer2.UpdateEncryptedTypesFromNigori(nigori);
- EXPECT_TRUE(encrypted_types.Equals(cryptographer_.GetEncryptedTypes()));
- EXPECT_TRUE(encrypted_types.Equals(cryptographer2.GetEncryptedTypes()));
-
- Mock::VerifyAndClearExpectations(&observer);
- Mock::VerifyAndClearExpectations(&observer2);
-
- EXPECT_CALL(observer,
- OnEncryptedTypesChanged(
- HasModelTypes(ModelTypeSet::All()), false));
- EXPECT_CALL(observer2,
- OnEncryptedTypesChanged(
- HasModelTypes(ModelTypeSet::All()), false));
-
- // Set all encrypted types
- encrypted_types = ModelTypeSet::All();
- cryptographer_.MergeEncryptedTypesForTest(encrypted_types);
- cryptographer_.UpdateNigoriFromEncryptedTypes(&nigori);
- cryptographer2.UpdateEncryptedTypesFromNigori(nigori);
- EXPECT_TRUE(encrypted_types.Equals(cryptographer_.GetEncryptedTypes()));
- EXPECT_TRUE(encrypted_types.Equals(cryptographer2.GetEncryptedTypes()));
-
- // Receiving an empty nigori should not reset any encrypted types or trigger
- // an observer notification.
- Mock::VerifyAndClearExpectations(&observer);
- nigori = sync_pb::NigoriSpecifics();
- cryptographer_.UpdateEncryptedTypesFromNigori(nigori);
- EXPECT_TRUE(encrypted_types.Equals(cryptographer_.GetEncryptedTypes()));
-}
-
-TEST_F(SyncCryptographerTest, EncryptEverythingExplicit) {
- ModelTypeSet real_types = ModelTypeSet::All();
- sync_pb::NigoriSpecifics specifics;
- specifics.set_encrypt_everything(true);
-
- StrictMock<MockObserver> observer;
- cryptographer_.AddObserver(&observer);
-
- EXPECT_CALL(observer,
- OnEncryptedTypesChanged(
- HasModelTypes(ModelTypeSet::All()), true));
-
- EXPECT_FALSE(cryptographer_.encrypt_everything());
- ModelTypeSet encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- else
- EXPECT_FALSE(encrypted_types.Has(iter.Get()));
- }
-
- cryptographer_.UpdateEncryptedTypesFromNigori(specifics);
-
- EXPECT_TRUE(cryptographer_.encrypt_everything());
- encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- }
-
- // Shouldn't trigger another notification.
- specifics.set_encrypt_everything(true);
-
- cryptographer_.RemoveObserver(&observer);
-}
-
-TEST_F(SyncCryptographerTest, EncryptEverythingImplicit) {
- ModelTypeSet real_types = ModelTypeSet::All();
- sync_pb::NigoriSpecifics specifics;
- specifics.set_encrypt_bookmarks(true); // Non-passwords = encrypt everything
-
- StrictMock<MockObserver> observer;
- cryptographer_.AddObserver(&observer);
-
- EXPECT_CALL(observer,
- OnEncryptedTypesChanged(
- HasModelTypes(ModelTypeSet::All()), true));
-
- EXPECT_FALSE(cryptographer_.encrypt_everything());
- ModelTypeSet encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- else
- EXPECT_FALSE(encrypted_types.Has(iter.Get()));
- }
-
- cryptographer_.UpdateEncryptedTypesFromNigori(specifics);
-
- EXPECT_TRUE(cryptographer_.encrypt_everything());
- encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- }
-
- // Shouldn't trigger another notification.
- specifics.set_encrypt_everything(true);
-
- cryptographer_.RemoveObserver(&observer);
-}
-
-TEST_F(SyncCryptographerTest, UnknownSensitiveTypes) {
- ModelTypeSet real_types = ModelTypeSet::All();
- sync_pb::NigoriSpecifics specifics;
- // Explicitly setting encrypt everything should override logic for implicit
- // encrypt everything.
- specifics.set_encrypt_everything(false);
- specifics.set_encrypt_bookmarks(true);
-
- StrictMock<MockObserver> observer;
- cryptographer_.AddObserver(&observer);
-
- ModelTypeSet expected_encrypted_types = Cryptographer::SensitiveTypes();
- expected_encrypted_types.Put(BOOKMARKS);
-
- EXPECT_CALL(observer,
- OnEncryptedTypesChanged(
- HasModelTypes(expected_encrypted_types), false));
-
- EXPECT_FALSE(cryptographer_.encrypt_everything());
- ModelTypeSet encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- if (iter.Get() == PASSWORDS || iter.Get() == NIGORI)
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- else
- EXPECT_FALSE(encrypted_types.Has(iter.Get()));
- }
-
- cryptographer_.UpdateEncryptedTypesFromNigori(specifics);
-
- EXPECT_FALSE(cryptographer_.encrypt_everything());
- encrypted_types = cryptographer_.GetEncryptedTypes();
- for (ModelTypeSet::Iterator iter = real_types.First();
- iter.Good(); iter.Inc()) {
- if (iter.Get() == PASSWORDS ||
- iter.Get() == NIGORI ||
- iter.Get() == BOOKMARKS)
- EXPECT_TRUE(encrypted_types.Has(iter.Get()));
- else
- EXPECT_FALSE(encrypted_types.Has(iter.Get()));
- }
-
- cryptographer_.RemoveObserver(&observer);
-}
-
} // namespace syncer