|  | // Copyright 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "sync/internal_api/sync_encryption_handler_impl.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/base64.h" | 
|  | #include "base/json/json_string_value_serializer.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "base/run_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/test/test_user_share.h" | 
|  | #include "sync/internal_api/public/write_node.h" | 
|  | #include "sync/internal_api/public/write_transaction.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/syncable_write_transaction.h" | 
|  | #include "sync/test/engine/test_id_factory.h" | 
|  | #include "sync/test/fake_encryptor.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::AnyNumber; | 
|  | using ::testing::AtLeast; | 
|  | using ::testing::Mock; | 
|  | using ::testing::SaveArg; | 
|  | using ::testing::StrictMock; | 
|  |  | 
|  | // The raw keystore key the server sends. | 
|  | static const char kRawKeystoreKey[] = "keystore_key"; | 
|  | // Base64 encoded version of |kRawKeystoreKey|. | 
|  | static const char kKeystoreKey[] = "a2V5c3RvcmVfa2V5"; | 
|  |  | 
|  | class SyncEncryptionHandlerObserverMock | 
|  | : public SyncEncryptionHandler::Observer { | 
|  | public: | 
|  | MOCK_METHOD2(OnPassphraseRequired, | 
|  | void(PassphraseRequiredReason, | 
|  | const sync_pb::EncryptedData&));  // NOLINT | 
|  | MOCK_METHOD0(OnPassphraseAccepted, void());  // NOLINT | 
|  | MOCK_METHOD2(OnBootstrapTokenUpdated, | 
|  | void(const std::string&, BootstrapTokenType type));  // NOLINT | 
|  | MOCK_METHOD2(OnEncryptedTypesChanged, | 
|  | void(ModelTypeSet, bool));  // NOLINT | 
|  | MOCK_METHOD0(OnEncryptionComplete, void());  // NOLINT | 
|  | MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*));  // NOLINT | 
|  | MOCK_METHOD2(OnPassphraseTypeChanged, void(PassphraseType, | 
|  | base::Time));  // NOLINT | 
|  | MOCK_METHOD1(OnLocalSetPassphraseEncryption, | 
|  | void(const SyncEncryptionHandler::NigoriState&));  // NOLINT | 
|  | }; | 
|  |  | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> | 
|  | BuildEncryptionKeyProto(const std::string& encryption_key) { | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(encryption_key); | 
|  | return keys; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class SyncEncryptionHandlerImplTest : public ::testing::Test { | 
|  | public: | 
|  | SyncEncryptionHandlerImplTest() {} | 
|  | virtual ~SyncEncryptionHandlerImplTest() {} | 
|  |  | 
|  | virtual void SetUp() { | 
|  | test_user_share_.SetUp(); | 
|  | SetUpEncryption(); | 
|  | CreateRootForType(NIGORI); | 
|  | } | 
|  |  | 
|  | virtual void TearDown() { | 
|  | PumpLoop(); | 
|  | test_user_share_.TearDown(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void SetUpEncryption() { | 
|  | SetUpEncryptionWithKeyForBootstrapping(std::string()); | 
|  | } | 
|  |  | 
|  | void SetUpEncryptionWithKeyForBootstrapping( | 
|  | const std::string& key_for_bootstrapping) { | 
|  | encryption_handler_.reset(new SyncEncryptionHandlerImpl( | 
|  | user_share(), &encryptor_, key_for_bootstrapping, | 
|  | std::string() /* keystore key for bootstrapping */, | 
|  | PASSPHRASE_TRANSITION_DO_NOT_CLEAR_DATA)); | 
|  | 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, | 
|  | model_type, | 
|  | wtrans.root_id(), | 
|  | tag_name); | 
|  | node.PutUniqueServerTag(tag_name); | 
|  | node.PutIsDir(true); | 
|  | node.PutServerIsDir(false); | 
|  | node.PutIsUnsynced(false); | 
|  | node.PutIsUnappliedUpdate(false); | 
|  | node.PutServerVersion(20); | 
|  | node.PutBaseVersion(20); | 
|  | node.PutIsDel(false); | 
|  | node.PutId(ids_.MakeServer(tag_name)); | 
|  | sync_pb::EntitySpecifics specifics; | 
|  | syncer::AddDefaultFieldValue(model_type, &specifics); | 
|  | node.PutSpecifics(specifics); | 
|  | } | 
|  |  | 
|  | void PumpLoop() { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Getters for tests. | 
|  | UserShare* user_share() { return test_user_share_.user_share(); } | 
|  | SyncEncryptionHandlerImpl* encryption_handler() { | 
|  | return encryption_handler_.get(); | 
|  | } | 
|  | SyncEncryptionHandlerObserverMock* observer() { return &observer_; } | 
|  | Cryptographer* GetCryptographer() { | 
|  | return encryption_handler_->GetCryptographerUnsafe(); | 
|  | } | 
|  |  | 
|  | void VerifyMigratedNigori(PassphraseType passphrase_type, | 
|  | const std::string& passphrase) { | 
|  | VerifyMigratedNigoriWithTimestamp(0, passphrase_type, passphrase); | 
|  | } | 
|  |  | 
|  | void VerifyMigratedNigoriWithTimestamp(int64_t migration_time, | 
|  | PassphraseType passphrase_type, | 
|  | const std::string& passphrase) { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | ReadNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | const sync_pb::NigoriSpecifics& nigori = nigori_node.GetNigoriSpecifics(); | 
|  | if (migration_time > 0) | 
|  | EXPECT_EQ(migration_time, nigori.keystore_migration_time()); | 
|  | else | 
|  | EXPECT_TRUE(nigori.has_keystore_migration_time()); | 
|  | EXPECT_TRUE(nigori.keybag_is_frozen()); | 
|  | if (passphrase_type == CUSTOM_PASSPHRASE || | 
|  | passphrase_type == FROZEN_IMPLICIT_PASSPHRASE) { | 
|  | EXPECT_TRUE(nigori.encrypt_everything()); | 
|  | EXPECT_TRUE(nigori.keystore_decryptor_token().blob().empty()); | 
|  | if (passphrase_type == CUSTOM_PASSPHRASE) { | 
|  | EXPECT_EQ(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE, | 
|  | nigori.passphrase_type()); | 
|  | if (!encryption_handler()->custom_passphrase_time().is_null()) { | 
|  | EXPECT_EQ(nigori.custom_passphrase_time(), | 
|  | TimeToProtoTime( | 
|  | encryption_handler()->custom_passphrase_time())); | 
|  | } | 
|  | } else { | 
|  | EXPECT_EQ(sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE, | 
|  | nigori.passphrase_type()); | 
|  | } | 
|  | } else { | 
|  | EXPECT_FALSE(nigori.encrypt_everything()); | 
|  | EXPECT_FALSE(nigori.keystore_decryptor_token().blob().empty()); | 
|  | EXPECT_EQ(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE, | 
|  | nigori.passphrase_type()); | 
|  | Cryptographer keystore_cryptographer(&encryptor_); | 
|  | KeyParams params = {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(params); | 
|  | EXPECT_TRUE(keystore_cryptographer.CanDecryptUsingDefaultKey( | 
|  | nigori.keystore_decryptor_token())); | 
|  | } | 
|  |  | 
|  | Cryptographer temp_cryptographer(&encryptor_); | 
|  | KeyParams params = {"localhost", "dummy", passphrase}; | 
|  | temp_cryptographer.AddKey(params); | 
|  | EXPECT_TRUE(temp_cryptographer.CanDecryptUsingDefaultKey( | 
|  | nigori.encryption_keybag())); | 
|  | } | 
|  |  | 
|  | sync_pb::NigoriSpecifics BuildMigratedNigori( | 
|  | PassphraseType passphrase_type, | 
|  | int64_t migration_time, | 
|  | const std::string& default_passphrase, | 
|  | const std::string& keystore_key) { | 
|  | DCHECK_NE(passphrase_type, IMPLICIT_PASSPHRASE); | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  |  | 
|  | std::string default_key = default_passphrase; | 
|  | if (default_key.empty()) { | 
|  | default_key = keystore_key; | 
|  | } else { | 
|  | KeyParams keystore_params = {"localhost", "dummy", keystore_key}; | 
|  | other_cryptographer.AddKey(keystore_params); | 
|  | } | 
|  | KeyParams params = {"localhost", "dummy", default_key}; | 
|  | other_cryptographer.AddKey(params); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  |  | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(migration_time); | 
|  |  | 
|  | if (passphrase_type == KEYSTORE_PASSPHRASE) { | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | keystore_key, | 
|  | &keystore_decryptor_token)); | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | } else { | 
|  | nigori.set_encrypt_everything(true); | 
|  | nigori.set_passphrase_type( | 
|  | passphrase_type == CUSTOM_PASSPHRASE ? | 
|  | sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE : | 
|  | sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE); | 
|  | } | 
|  | return nigori; | 
|  | } | 
|  |  | 
|  | // Build a migrated nigori node with the specified default passphrase | 
|  | // and keystore key and initialize the encryption handler with it. | 
|  | void InitKeystoreMigratedNigori(int64_t migration_time, | 
|  | const std::string& default_passphrase, | 
|  | const std::string& keystore_key) { | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori = BuildMigratedNigori( | 
|  | KEYSTORE_PASSPHRASE, | 
|  | migration_time, | 
|  | default_passphrase, | 
|  | keystore_key); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AtLeast(1)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(AtLeast(1)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | } | 
|  |  | 
|  | // Build a migrated nigori node with the specified default passphrase | 
|  | // as a custom passphrase. | 
|  | void InitCustomPassMigratedNigori(int64_t migration_time, | 
|  | const std::string& default_passphrase) { | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori = BuildMigratedNigori( | 
|  | CUSTOM_PASSPHRASE, | 
|  | migration_time, | 
|  | default_passphrase, | 
|  | kKeystoreKey); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AtLeast(1)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)).Times(AtLeast(1)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(AtLeast(1)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | } | 
|  |  | 
|  | // Build an unmigrated nigori node with the specified passphrase and type and | 
|  | // initialize the encryption handler with it. | 
|  | void InitUnmigratedNigori(const std::string& default_passphrase, | 
|  | PassphraseType passphrase_type) { | 
|  | DCHECK_NE(passphrase_type, FROZEN_IMPLICIT_PASSPHRASE); | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams default_key = {"localhost", "dummy", default_passphrase}; | 
|  | other_cryptographer.AddKey(default_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(passphrase_type == CUSTOM_PASSPHRASE); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | if (passphrase_type != IMPLICIT_PASSPHRASE) { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(passphrase_type, _)); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AtLeast(1)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), passphrase_type); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | } | 
|  |  | 
|  | // Verify we can restore the SyncEncryptionHandler state using a saved | 
|  | // |bootstrap_token| and |nigori_state|. | 
|  | // | 
|  | // |migration_time| is the time migration occurred. | 
|  | // | 
|  | // |passphrase| is the custom passphrase. | 
|  | void VerifyRestoreAfterCustomPassphrase( | 
|  | int64_t migration_time, | 
|  | const std::string& passphrase, | 
|  | const std::string& bootstrap_token, | 
|  | const SyncEncryptionHandler::NigoriState& nigori_state, | 
|  | PassphraseType passphrase_type) { | 
|  | TearDown(); | 
|  | test_user_share_.SetUp(); | 
|  | SetUpEncryptionWithKeyForBootstrapping(bootstrap_token); | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true)) | 
|  | .Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnPassphraseTypeChanged(passphrase_type, _)); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | encryption_handler()->RestoreNigori(nigori_state); | 
|  | encryption_handler()->Init(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, passphrase_type, | 
|  | passphrase); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | TestUserShare test_user_share_; | 
|  | FakeEncryptor encryptor_; | 
|  | scoped_ptr<SyncEncryptionHandlerImpl> encryption_handler_; | 
|  | StrictMock<SyncEncryptionHandlerObserverMock> observer_; | 
|  | TestIdFactory ids_; | 
|  | base::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(), &encryptor_, std::string(), | 
|  | std::string() /* bootstrap tokens */, | 
|  | PASSPHRASE_TRANSITION_DO_NOT_CLEAR_DATA); | 
|  | handler2.AddObserver(&observer2); | 
|  |  | 
|  | // Just set the sensitive types (shouldn't trigger any notifications). | 
|  | ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes()); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->MergeEncryptedTypes( | 
|  | encrypted_types, | 
|  | trans.GetWrappedTrans()); | 
|  | encryption_handler()->UpdateNigoriFromEncryptedTypes( | 
|  | &nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | handler2.UpdateEncryptedTypesFromNigori(nigori, trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | encryption_handler()->GetEncryptedTypesUnsafe())); | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | handler2.GetEncryptedTypesUnsafe())); | 
|  |  | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | Mock::VerifyAndClearExpectations(&observer2); | 
|  |  | 
|  | ModelTypeSet encrypted_user_types = EncryptableUserTypes(); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged( | 
|  | HasModelTypes(encrypted_user_types), false)); | 
|  | EXPECT_CALL(observer2, | 
|  | OnEncryptedTypesChanged( | 
|  | HasModelTypes(encrypted_user_types), false)); | 
|  |  | 
|  | // Set all encrypted types | 
|  | encrypted_types = EncryptableUserTypes(); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->MergeEncryptedTypes( | 
|  | encrypted_types, | 
|  | trans.GetWrappedTrans()); | 
|  | encryption_handler()->UpdateNigoriFromEncryptedTypes( | 
|  | &nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | handler2.UpdateEncryptedTypesFromNigori(nigori, trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | encryption_handler()->GetEncryptedTypesUnsafe())); | 
|  | EXPECT_TRUE(encrypted_types.Equals(handler2.GetEncryptedTypesUnsafe())); | 
|  |  | 
|  | // 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(); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | handler2.UpdateEncryptedTypesFromNigori(nigori, trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | encryption_handler()->GetEncryptedTypesUnsafe())); | 
|  | } | 
|  |  | 
|  | // Verify the encryption handler processes the encrypt everything field | 
|  | // properly. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingExplicit) { | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.set_encrypt_everything(true); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged( | 
|  | HasModelTypes(EncryptableUserTypes()), true)); | 
|  |  | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | ModelTypeSet encrypted_types = | 
|  | encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS))); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->UpdateEncryptedTypesFromNigori( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.HasAll(EncryptableUserTypes())); | 
|  |  | 
|  | // Receiving the nigori node again shouldn't trigger another notification. | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->UpdateEncryptedTypesFromNigori( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.set_encrypt_bookmarks(true);  // Non-passwords = encrypt everything | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged( | 
|  | HasModelTypes(EncryptableUserTypes()), true)); | 
|  |  | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | ModelTypeSet encrypted_types = | 
|  | encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS))); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->UpdateEncryptedTypesFromNigori( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.HasAll(EncryptableUserTypes())); | 
|  |  | 
|  | // Receiving a nigori node with encrypt everything explicitly set shouldn't | 
|  | // trigger another notification. | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | nigori.set_encrypt_everything(true); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->UpdateEncryptedTypesFromNigori( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.set_encrypt_everything(false); | 
|  | nigori.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()->IsEncryptEverythingEnabled()); | 
|  | ModelTypeSet encrypted_types = | 
|  | encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | ModelTypeSet(PASSWORDS, WIFI_CREDENTIALS))); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->UpdateEncryptedTypesFromNigori( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe(); | 
|  | EXPECT_TRUE(encrypted_types.Equals( | 
|  | ModelTypeSet(BOOKMARKS, PASSWORDS, WIFI_CREDENTIALS))); | 
|  | } | 
|  |  | 
|  | // 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(GetCryptographer()->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 = EncryptableUserTypes(); | 
|  |  | 
|  | // Set up the current encryption state (containing both keys and encrypt | 
|  | // everything). | 
|  | sync_pb::NigoriSpecifics current_nigori_specifics; | 
|  | GetCryptographer()->AddKey(old_key); | 
|  | GetCryptographer()->AddKey(current_key); | 
|  | GetCryptographer()->Encrypt( | 
|  | our_encrypted_specifics, | 
|  | our_encrypted_specifics.mutable_encrypted()); | 
|  | GetCryptographer()->GetKeys( | 
|  | current_nigori_specifics.mutable_encryption_keybag()); | 
|  | current_nigori_specifics.set_encrypt_everything(true); | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnEncryptedTypesChanged( | 
|  | HasModelTypes(EncryptableUserTypes()), 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_encryption_keybag()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | { | 
|  | // Update the encryption handler. | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->ApplyNigoriUpdate( | 
|  | old_nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_FALSE(GetCryptographer()->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.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | const sync_pb::NigoriSpecifics& nigori = nigori_node.GetNigoriSpecifics(); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey( | 
|  | our_encrypted_specifics.encrypted())); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt( | 
|  | other_encrypted_specifics.encrypted())); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(nigori.encryption_keybag())); | 
|  | EXPECT_TRUE(nigori.encrypt_everything()); | 
|  | EXPECT_TRUE( | 
|  | GetCryptographer()->CanDecryptUsingDefaultKey( | 
|  | nigori.encryption_keybag())); | 
|  | } | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | } | 
|  |  | 
|  | // Ensure setting the keystore key works, updates the bootstrap token, and | 
|  | // triggers a non-backwards compatible migration. Then verify that the | 
|  | // bootstrap token can be correctly parsed by the encryption handler at startup | 
|  | // time. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreMigratesAndUpdatesBootstrap) { | 
|  | // Passing no keys should do nothing. | 
|  | EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_FALSE(GetCryptographer()->is_initialized()); | 
|  | EXPECT_TRUE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans())); | 
|  | EXPECT_FALSE(encryption_handler()->SetKeystoreKeys( | 
|  | BuildEncryptionKeyProto(std::string()), trans.GetWrappedTrans())); | 
|  | EXPECT_TRUE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans())); | 
|  | } | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Build a set of keystore keys. | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  | std::string old_keystore_key; | 
|  | base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key); | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  |  | 
|  | // Pass them to the encryption handler, triggering a migration and bootstrap | 
|  | // token update. | 
|  | std::string encoded_key; | 
|  | std::string keystore_bootstrap; | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)); | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, | 
|  | KEYSTORE_BOOTSTRAP_TOKEN)). | 
|  | WillOnce(SaveArg<0>(&keystore_bootstrap)); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_TRUE( | 
|  | encryption_handler()->SetKeystoreKeys( | 
|  | keys, | 
|  | trans.GetWrappedTrans())); | 
|  | EXPECT_FALSE( | 
|  | encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans())); | 
|  | EXPECT_FALSE(GetCryptographer()->is_initialized()); | 
|  | } | 
|  | PumpLoop(); | 
|  | EXPECT_TRUE(GetCryptographer()->is_initialized()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  |  | 
|  | // Ensure the bootstrap is encoded properly (a base64 encoded encrypted blob | 
|  | // of list values containing the keystore keys). | 
|  | std::string decoded_bootstrap; | 
|  | ASSERT_TRUE(base::Base64Decode(keystore_bootstrap, &decoded_bootstrap)); | 
|  | std::string decrypted_bootstrap; | 
|  | ASSERT_TRUE( | 
|  | GetCryptographer()->encryptor()->DecryptString(decoded_bootstrap, | 
|  | &decrypted_bootstrap)); | 
|  | JSONStringValueDeserializer json(decrypted_bootstrap); | 
|  | scoped_ptr<base::Value> deserialized_keystore_keys( | 
|  | json.Deserialize(NULL, NULL)); | 
|  | ASSERT_TRUE(deserialized_keystore_keys.get()); | 
|  | base::ListValue* keystore_list = NULL; | 
|  | deserialized_keystore_keys->GetAsList(&keystore_list); | 
|  | ASSERT_TRUE(keystore_list); | 
|  | ASSERT_EQ(2U, keystore_list->GetSize()); | 
|  | std::string test_string; | 
|  | keystore_list->GetString(0, &test_string); | 
|  | ASSERT_EQ(old_keystore_key, test_string); | 
|  | keystore_list->GetString(1, &test_string); | 
|  | ASSERT_EQ(kKeystoreKey, test_string); | 
|  |  | 
|  |  | 
|  | // Now make sure a new encryption handler can correctly parse the bootstrap | 
|  | // token. | 
|  | SyncEncryptionHandlerImpl handler2(user_share(), &encryptor_, | 
|  | std::string(),  // Cryptographer bootstrap. | 
|  | keystore_bootstrap, | 
|  | PASSPHRASE_TRANSITION_DO_NOT_CLEAR_DATA); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_FALSE(handler2.NeedKeystoreKey(trans.GetWrappedTrans())); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ensure GetKeystoreDecryptor only updates the keystore decryptor token if it | 
|  | // wasn't already set properly. Otherwise, the decryptor should remain the | 
|  | // same. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, GetKeystoreDecryptor) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &encrypted)); | 
|  | std::string serialized = encrypted.SerializeAsString(); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &encrypted)); | 
|  | EXPECT_EQ(serialized, encrypted.SerializeAsString()); | 
|  | } | 
|  |  | 
|  | // Test that we don't attempt to migrate while an implicit passphrase is pending | 
|  | // and that once we do decrypt pending keys we migrate the nigori. Once | 
|  | // migrated, we should be in keystore passphrase state. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, MigrateOnDecryptImplicitPass) { | 
|  | const char kOtherKey[] = "other"; | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | } | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams other_key = {"localhost", "dummy", kOtherKey}; | 
|  | other_cryptographer.AddKey(other_key); | 
|  |  | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(false); | 
|  | nigori.set_encrypt_everything(false); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kOtherKey); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(KEYSTORE_PASSPHRASE, encryption_handler()->GetPassphraseType()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kOtherKey); | 
|  | } | 
|  |  | 
|  | // Test that we don't attempt to migrate while a custom passphrase is pending, | 
|  | // and that once we do decrypt pending keys we migrate the nigori. Once | 
|  | // migrated, we should be in custom passphrase state with encrypt everything. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, MigrateOnDecryptCustomPass) { | 
|  | const char kOtherKey[] = "other"; | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | } | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams other_key = {"localhost", "dummy", kOtherKey}; | 
|  | other_cryptographer.AddKey(other_key); | 
|  |  | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_encrypt_everything(false); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(2); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kOtherKey); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | const base::Time migration_time = encryption_handler()->migration_time(); | 
|  | EXPECT_EQ(CUSTOM_PASSPHRASE, encryption_handler()->GetPassphraseType()); | 
|  | VerifyMigratedNigori(CUSTOM_PASSPHRASE, kOtherKey); | 
|  |  | 
|  | VerifyRestoreAfterCustomPassphrase(TimeToProtoTime(migration_time), kOtherKey, | 
|  | captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that we trigger a migration when we set the keystore key, had an | 
|  | // implicit passphrase, and did not have encrypt everything. We should switch | 
|  | // to KEYSTORE_PASSPHRASE. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, MigrateOnKeystoreKeyAvailableImplicit) { | 
|  | const char kCurKey[] = "cur"; | 
|  | KeyParams current_key = {"localhost", "dummy", kCurKey}; | 
|  | GetCryptographer()->AddKey(current_key); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | // Once we provide a keystore key, we should perform the migration. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | // The actual migration gets posted, so run all pending tasks. | 
|  | PumpLoop(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(KEYSTORE_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kCurKey); | 
|  | } | 
|  |  | 
|  | // Test that we trigger a migration when we set the keystore key, had an | 
|  | // implicit passphrase, and encrypt everything enabled. We should switch to | 
|  | // FROZEN_IMPLICIT_PASSPHRASE. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | MigrateOnKeystoreKeyAvailableFrozenImplicit) { | 
|  | const char kCurKey[] = "cur"; | 
|  | KeyParams current_key = {"localhost", "dummy", kCurKey}; | 
|  | GetCryptographer()->AddKey(current_key); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->EnableEncryptEverything(); | 
|  |  | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | // Once we provide a keystore key, we should perform the migration. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(FROZEN_IMPLICIT_PASSPHRASE, _)); | 
|  |  | 
|  | // The actual migration gets posted, so run all pending tasks. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | const base::Time migration_time = encryption_handler()->migration_time(); | 
|  | EXPECT_EQ(FROZEN_IMPLICIT_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(FROZEN_IMPLICIT_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_, | 
|  | // PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked | 
|  | // during a previous instance) so get it from the Cryptographer. | 
|  | std::string passphrase_bootstrap_token; | 
|  | GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token); | 
|  | VerifyRestoreAfterCustomPassphrase( | 
|  | TimeToProtoTime(migration_time), kCurKey, passphrase_bootstrap_token, | 
|  | captured_nigori_state, FROZEN_IMPLICIT_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that we trigger a migration when we set the keystore key, had a | 
|  | // custom passphrase, and encrypt everything enabled. The passphrase state | 
|  | // should remain as CUSTOM_PASSPHRASE, and encrypt everything stay the same. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | MigrateOnKeystoreKeyAvailableCustomWithEncryption) { | 
|  | const char kCurKey[] = "cur"; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | encryption_handler()->Init(); | 
|  | encryption_handler()->SetEncryptionPassphrase(kCurKey, true); | 
|  | EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->EnableEncryptEverything(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | // Once we provide a keystore key, we should perform the migration. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | // The actual migration gets posted, so run all pending tasks. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | const base::Time migration_time = encryption_handler()->migration_time(); | 
|  | EXPECT_EQ(CUSTOM_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | VerifyRestoreAfterCustomPassphrase(TimeToProtoTime(migration_time), kCurKey, | 
|  | captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that we trigger a migration when we set the keystore key, had a | 
|  | // custom passphrase, and did not have encrypt everything. The passphrase state | 
|  | // should remain as CUSTOM_PASSPHRASE, and encrypt everything should be enabled. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | MigrateOnKeystoreKeyAvailableCustomNoEncryption) { | 
|  | const char kCurKey[] = "cur"; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | encryption_handler()->Init(); | 
|  | encryption_handler()->SetEncryptionPassphrase(kCurKey, true); | 
|  | EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | // Once we provide a keystore key, we should perform the migration. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | // The actual migration gets posted, so run all pending tasks. | 
|  | PumpLoop(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | const base::Time migration_time = encryption_handler()->migration_time(); | 
|  | EXPECT_EQ(CUSTOM_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | VerifyRestoreAfterCustomPassphrase(TimeToProtoTime(migration_time), kCurKey, | 
|  | captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that we can handle receiving a migrated nigori node in the | 
|  | // KEYSTORE_PASS state, and use the keystore decryptor token to decrypt the | 
|  | // keybag. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriKeystorePass) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_FALSE(GetCryptographer()->is_ready()); | 
|  | EXPECT_NE(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  |  | 
|  | // Now build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer should be | 
|  | // initialized properly to decrypt both kCurKey and kKeystoreKey. | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, KEYSTORE_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Check that the cryptographer still encrypts with the current key. | 
|  | sync_pb::EncryptedData current_encrypted; | 
|  | other_cryptographer.EncryptString("string", ¤t_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(current_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can decrypt keystore key based encryption. | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams keystore_key =  {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  | } | 
|  |  | 
|  | // Test that we handle receiving migrated nigori's with | 
|  | // FROZEN_IMPLICIT_PASSPHRASE state. We should be in a pending key state until | 
|  | // we supply the pending frozen implicit passphrase key. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriFrozenImplicitPass) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(FROZEN_IMPLICIT_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_passphrase_type( | 
|  | sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_encrypt_everything(true); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(FROZEN_IMPLICIT_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kCurKey); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, FROZEN_IMPLICIT_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Check that the cryptographer still encrypts with the current key. | 
|  | sync_pb::EncryptedData current_encrypted; | 
|  | other_cryptographer.EncryptString("string", ¤t_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(current_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can decrypt keystore key based encryption. | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams keystore_key =  {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  | } | 
|  |  | 
|  | // Test that we handle receiving migrated nigori's with | 
|  | // CUSTOM_PASSPHRASE state. We should be in a pending key state until we | 
|  | // provide the custom passphrase key. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriCustomPass) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_encrypt_everything(true); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(CUSTOM_PASSPHRASE, encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kCurKey); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Check that the cryptographer still encrypts with the current key. | 
|  | sync_pb::EncryptedData current_encrypted; | 
|  | other_cryptographer.EncryptString("string", ¤t_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(current_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can decrypt keystore key based encryption. | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams keystore_key =  {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  | } | 
|  |  | 
|  | // Test that if we have a migrated nigori with a custom passphrase, then receive | 
|  | // and old implicit passphrase nigori, we properly overwrite it with the current | 
|  | // state. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, ReceiveUnmigratedNigoriAfterMigration) { | 
|  | const char kOldKey[] = "old"; | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | KeyParams old_key = {"localhost", "dummy", kOldKey}; | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | GetCryptographer()->AddKey(old_key); | 
|  | GetCryptographer()->AddKey(cur_key); | 
|  |  | 
|  | // Build a migrated nigori with full encryption. | 
|  | const int64_t migration_time = 1; | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | GetCryptographer()->GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE); | 
|  | nigori.set_encrypt_everything(true); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)).Times(2); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Now build an old unmigrated nigori node with old encrypted types. We should | 
|  | // properly overwrite it with the migrated + encrypt everything state. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | { | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | other_cryptographer.AddKey(old_key); | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(false); | 
|  | nigori.set_encrypt_everything(false); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | PumpLoop(); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_, | 
|  | // PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked | 
|  | // during a previous instance) so get it from the Cryptographer. | 
|  | std::string passphrase_bootstrap_token; | 
|  | GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token); | 
|  | VerifyRestoreAfterCustomPassphrase(migration_time, kCurKey, | 
|  | passphrase_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that if we have a migrated nigori with a custom passphrase, then receive | 
|  | // a migrated nigori with a keystore passphrase, we properly overwrite it with | 
|  | // the current state. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, ReceiveOldMigratedNigori) { | 
|  | const char kOldKey[] = "old"; | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | KeyParams old_key = {"localhost", "dummy", kOldKey}; | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | GetCryptographer()->AddKey(old_key); | 
|  | GetCryptographer()->AddKey(cur_key); | 
|  |  | 
|  | // Build a migrated nigori with full encryption. | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | GetCryptographer()->GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE); | 
|  | nigori.set_encrypt_everything(true); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)).Times(2); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Now build an old keystore nigori node with old encrypted types. We should | 
|  | // properly overwrite it with the migrated + encrypt everything state. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | const int64_t migration_time = 1; | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | other_cryptographer.AddKey(old_key); | 
|  | encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | nigori.mutable_keystore_decryptor_token()); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_encrypt_everything(false); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori.set_keystore_migration_time(migration_time); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | PumpLoop(); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_, | 
|  | // PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked | 
|  | // during a previous instance) so get it from the Cryptographer. | 
|  | std::string passphrase_bootstrap_token; | 
|  | GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token); | 
|  | VerifyRestoreAfterCustomPassphrase(migration_time, kCurKey, | 
|  | passphrase_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that if we receive the keystore key after receiving a migrated nigori | 
|  | // node, we properly use the keystore decryptor token to decrypt the keybag. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreAfterReceivingMigratedNigori) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_FALSE(GetCryptographer()->is_ready()); | 
|  | EXPECT_NE(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  |  | 
|  | // Now build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer should be | 
|  | // initialized properly to decrypt both kCurKey and kKeystoreKey. | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | // Run any tasks posted via AppplyNigoriUpdate. | 
|  | PumpLoop(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, KEYSTORE_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Check that the cryptographer still encrypts with the current key. | 
|  | sync_pb::EncryptedData current_encrypted; | 
|  | other_cryptographer.EncryptString("string", ¤t_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(current_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can decrypt keystore key based encryption. | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams keystore_key =  {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  | } | 
|  |  | 
|  | // Test that after receiving a migrated nigori and decrypting it using the | 
|  | // keystore key, we can then switch to a custom passphrase. The nigori should | 
|  | // remain migrated and encrypt everything should be enabled. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, SetCustomPassAfterMigration) { | 
|  | const char kOldKey[] = "old"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kOldKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  |  | 
|  | // Build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer should be | 
|  | // initialized properly to decrypt both kOldKey and kKeystoreKey. | 
|  | const int64_t migration_time = 1; | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(migration_time); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | const char kNewKey[] = "new_key"; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(2); | 
|  | encryption_handler()->SetEncryptionPassphrase(kNewKey, true); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_FALSE(captured_bootstrap_token.empty()); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, kNewKey); | 
|  |  | 
|  | // Check that the cryptographer can decrypt the old key. | 
|  | sync_pb::EncryptedData old_encrypted; | 
|  | other_cryptographer.EncryptString("string", &old_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(old_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can decrypt keystore key based encryption. | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams keystore_key =  {"localhost", "dummy", kKeystoreKey}; | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  |  | 
|  | // Check the the cryptographer is encrypting with the new key. | 
|  | KeyParams new_key = {"localhost", "dummy", kNewKey}; | 
|  | Cryptographer new_cryptographer(GetCryptographer()->encryptor()); | 
|  | new_cryptographer.AddKey(new_key); | 
|  | sync_pb::EncryptedData new_encrypted; | 
|  | new_cryptographer.EncryptString("string", &new_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(new_encrypted)); | 
|  |  | 
|  | // Now verify that we can restore the current state using the captured | 
|  | // bootstrap token and nigori state. | 
|  | VerifyRestoreAfterCustomPassphrase(migration_time, kNewKey, | 
|  | captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that if a client without a keystore key (e.g. one without keystore | 
|  | // encryption enabled) receives a migrated nigori and then attempts to set a | 
|  | // custom passphrase, it also enables encrypt everything. The nigori node | 
|  | // should remain migrated. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | SetCustomPassAfterMigrationNoKeystoreKey) { | 
|  | const char kOldKey[] = "old"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kOldKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | KeyParams keystore_key = {"localhost", "dummy", kKeystoreKey}; | 
|  | other_cryptographer.AddNonDefaultKey(keystore_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  |  | 
|  | // Build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer will have | 
|  | // pending keys until we provide the decryption passphrase. | 
|  | const int64_t migration_time = 1; | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(migration_time); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kOldKey); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | const char kNewKey[] = "new_key"; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(2); | 
|  | encryption_handler()->SetEncryptionPassphrase(kNewKey, true); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, kNewKey); | 
|  |  | 
|  | // Check that the cryptographer can decrypt the old key. | 
|  | sync_pb::EncryptedData old_encrypted; | 
|  | other_cryptographer.EncryptString("string", &old_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(old_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can still decrypt keystore key based | 
|  | // encryption (should have been extracted from the encryption keybag). | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  |  | 
|  | // Check the the cryptographer is encrypting with the new key. | 
|  | KeyParams new_key = {"localhost", "dummy", kNewKey}; | 
|  | Cryptographer new_cryptographer(GetCryptographer()->encryptor()); | 
|  | new_cryptographer.AddKey(new_key); | 
|  | sync_pb::EncryptedData new_encrypted; | 
|  | new_cryptographer.EncryptString("string", &new_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(new_encrypted)); | 
|  |  | 
|  | // Now verify that we can restore the current state using the captured | 
|  | // bootstrap token and nigori state. | 
|  | VerifyRestoreAfterCustomPassphrase(migration_time, kNewKey, | 
|  | captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Test that if a client without a keystore key (e.g. one without keystore | 
|  | // encryption enabled) receives a migrated nigori and then attempts to set a | 
|  | // new implicit passphrase, we do not modify the nigori node (the implicit | 
|  | // passphrase is dropped). | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | SetImplicitPassAfterMigrationNoKeystoreKey) { | 
|  | const char kOldKey[] = "old"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kOldKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | KeyParams keystore_key = {"localhost", "dummy", kKeystoreKey}; | 
|  | other_cryptographer.AddNonDefaultKey(keystore_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  |  | 
|  | // Build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer will have | 
|  | // pending keys until we provide the decryption passphrase. | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kOldKey); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Should get dropped on the floor silently. | 
|  | const char kNewKey[] = "new_key"; | 
|  | encryption_handler()->SetEncryptionPassphrase(kNewKey, false); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, KEYSTORE_PASSPHRASE, kOldKey); | 
|  |  | 
|  | // Check that the cryptographer can decrypt the old key. | 
|  | sync_pb::EncryptedData old_encrypted; | 
|  | other_cryptographer.EncryptString("string", &old_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(old_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can still decrypt keystore key based | 
|  | // encryption (due to extracting the keystore key from the encryption keybag). | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  |  | 
|  | // Check the the cryptographer does not have the new key. | 
|  | KeyParams new_key = {"localhost", "dummy", kNewKey}; | 
|  | Cryptographer new_cryptographer(GetCryptographer()->encryptor()); | 
|  | new_cryptographer.AddKey(new_key); | 
|  | sync_pb::EncryptedData new_encrypted; | 
|  | new_cryptographer.EncryptString("string", &new_encrypted); | 
|  | EXPECT_FALSE(GetCryptographer()->CanDecryptUsingDefaultKey(new_encrypted)); | 
|  | } | 
|  |  | 
|  | // Test that if a client without a keystore key (e.g. one without keystore | 
|  | // encryption enabled) receives a migrated nigori in keystore passphrase state | 
|  | // and then attempts to enable encrypt everything, we switch to a custom | 
|  | // passphrase. The nigori should remain migrated. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | MigrateOnEncryptEverythingKeystorePassphrase) { | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData keystore_decryptor_token; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | other_cryptographer.AddKey(cur_key); | 
|  | KeyParams keystore_key = {"localhost", "dummy", kKeystoreKey}; | 
|  | other_cryptographer.AddNonDefaultKey(keystore_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  | EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | &keystore_decryptor_token)); | 
|  |  | 
|  | // Build a nigori node with the generated keystore decryptor token and | 
|  | // initialize the encryption handler with it. The cryptographer will have | 
|  | // pending keys until we provide the decryption passphrase. | 
|  | const int64_t migration_time = 1; | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | nigori.mutable_keystore_decryptor_token()->CopyFrom( | 
|  | keystore_decryptor_token); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_keystore_migration_time(migration_time); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->has_pending_keys()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kCurKey); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(FROZEN_IMPLICIT_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | encryption_handler()->EnableEncryptEverything(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(FROZEN_IMPLICIT_PASSPHRASE, | 
|  | encryption_handler()->GetPassphraseType()); | 
|  | EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigoriWithTimestamp(1, FROZEN_IMPLICIT_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Check that the cryptographer is encrypting using the frozen current key. | 
|  | sync_pb::EncryptedData current_encrypted; | 
|  | other_cryptographer.EncryptString("string", ¤t_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(current_encrypted)); | 
|  |  | 
|  | // Check that the cryptographer can still decrypt keystore key based | 
|  | // encryption (due to extracting the keystore key from the encryption keybag). | 
|  | Cryptographer keystore_cryptographer(GetCryptographer()->encryptor()); | 
|  | keystore_cryptographer.AddKey(keystore_key); | 
|  | sync_pb::EncryptedData keystore_encrypted; | 
|  | keystore_cryptographer.EncryptString("string", &keystore_encrypted); | 
|  | EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted)); | 
|  |  | 
|  | VerifyRestoreAfterCustomPassphrase( | 
|  | migration_time, kCurKey, captured_bootstrap_token, captured_nigori_state, | 
|  | FROZEN_IMPLICIT_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // If we receive a nigori migrated and with a KEYSTORE_PASSPHRASE type, but | 
|  | // using an old default key (i.e. old GAIA password), we should overwrite the | 
|  | // nigori, updating the keybag and keystore decryptor. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, | 
|  | ReceiveMigratedNigoriWithOldPassphrase) { | 
|  | const char kOldKey[] = "old"; | 
|  | const char kCurKey[] = "cur"; | 
|  | sync_pb::EncryptedData encrypted; | 
|  | KeyParams old_key = {"localhost", "dummy", kOldKey}; | 
|  | KeyParams cur_key = {"localhost", "dummy", kCurKey}; | 
|  | GetCryptographer()->AddKey(old_key); | 
|  | GetCryptographer()->AddKey(cur_key); | 
|  |  | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | other_cryptographer.AddKey(old_key); | 
|  | EXPECT_TRUE(other_cryptographer.is_ready()); | 
|  |  | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()); | 
|  | encryption_handler()->Init(); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  |  | 
|  | { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kCurKey); | 
|  |  | 
|  | // Now build an old keystore passphrase nigori node. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | { | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | WriteNode nigori_node(&trans); | 
|  | ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK); | 
|  | sync_pb::NigoriSpecifics nigori; | 
|  | Cryptographer other_cryptographer(GetCryptographer()->encryptor()); | 
|  | other_cryptographer.AddKey(old_key); | 
|  | encryption_handler()->GetKeystoreDecryptor( | 
|  | other_cryptographer, | 
|  | kKeystoreKey, | 
|  | nigori.mutable_keystore_decryptor_token()); | 
|  | other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); | 
|  | nigori.set_keybag_is_frozen(true); | 
|  | nigori.set_encrypt_everything(false); | 
|  | nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE); | 
|  | nigori.set_keystore_migration_time(1); | 
|  | encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans()); | 
|  | nigori_node.SetNigoriSpecifics(nigori); | 
|  | } | 
|  | PumpLoop(); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kCurKey); | 
|  | } | 
|  |  | 
|  | // Trigger a key rotation upon receiving new keys if we already had a keystore | 
|  | // migrated nigori with the gaia key as the default (still in backwards | 
|  | // compatible mode). | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysGaiaDefault) { | 
|  | // Destroy the existing nigori node so we init without a nigori node. | 
|  | TearDown(); | 
|  | test_user_share_.SetUp(); | 
|  | SetUpEncryption(); | 
|  |  | 
|  | const char kOldGaiaKey[] = "old_gaia_key"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  | std::string old_keystore_key; | 
|  | base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key); | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawOldKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Then init the nigori node with a backwards compatible set of keys. | 
|  | CreateRootForType(NIGORI); | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | InitKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key); | 
|  |  | 
|  | // Now set some new keystore keys. | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | { | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | // Pump for any posted tasks. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. We should | 
|  | // have rotated the keybag so that it's now encrypted with the newest keystore | 
|  | // key (instead of the old gaia key). | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  | } | 
|  |  | 
|  | // Trigger a key rotation upon receiving new keys if we already had a keystore | 
|  | // migrated nigori with the keystore key as the default. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysKeystoreDefault) { | 
|  | // Destroy the existing nigori node so we init without a nigori node. | 
|  | TearDown(); | 
|  | test_user_share_.SetUp(); | 
|  | SetUpEncryption(); | 
|  |  | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  | std::string old_keystore_key; | 
|  | base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key); | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawOldKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Then init the nigori node with a non-backwards compatible set of keys. | 
|  | CreateRootForType(NIGORI); | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | InitKeystoreMigratedNigori(1, old_keystore_key, old_keystore_key); | 
|  |  | 
|  | // Now set some new keystore keys. | 
|  | EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnEncryptionComplete()); | 
|  | { | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | // Pump for any posted tasks. | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. We should | 
|  | // have rotated the keybag so that it's now encrypted with the newest keystore | 
|  | // key (instead of the old gaia key). | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  | } | 
|  |  | 
|  | // Trigger a key rotation upon when a pending gaia passphrase is resolved. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysAfterPendingGaiaResolved) { | 
|  | const char kOldGaiaKey[] = "old_gaia_key"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseRequired(_, _)); | 
|  | InitUnmigratedNigori(kOldGaiaKey, IMPLICIT_PASSPHRASE); | 
|  |  | 
|  | { | 
|  | // Pass multiple keystore keys, signaling a rotation has happened. | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Resolve the pending keys. This should trigger the key rotation. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(AtLeast(1)); | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | encryption_handler()->SetDecryptionPassphrase(kOldGaiaKey); | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(KEYSTORE_PASSPHRASE, encryption_handler()->GetPassphraseType()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  | } | 
|  |  | 
|  | // When signing in for the first time, make sure we can rotate keys if we | 
|  | // already have a keystore migrated nigori. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysGaiaDefaultOnInit) { | 
|  | // Destroy the existing nigori node so we init without a nigori node. | 
|  | TearDown(); | 
|  | test_user_share_.SetUp(); | 
|  | SetUpEncryption(); | 
|  |  | 
|  | const char kOldGaiaKey[] = "old_gaia_key"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  | std::string old_keystore_key; | 
|  | base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key); | 
|  |  | 
|  | // Set two keys, signaling that a rotation has been performed. No nigori | 
|  | // node is present yet, so we can't rotate. | 
|  | { | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  |  | 
|  | // Then init the nigori node with an old set of keys. | 
|  | CreateRootForType(NIGORI); | 
|  | EXPECT_CALL(*observer(), OnPassphraseAccepted()); | 
|  | InitKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key); | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Verify we're still migrated and have proper encryption state. We should | 
|  | // have rotated the keybag so that it's now encrypted with the newest keystore | 
|  | // key (instead of the old gaia key). | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_TRUE(GetCryptographer()->is_ready()); | 
|  | EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE); | 
|  | EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  | } | 
|  |  | 
|  | // Trigger a key rotation when a migrated nigori (with an old keystore key) is | 
|  | // applied. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysWhenMigratedNigoriArrives) { | 
|  | const char kOldGaiaKey[] = "old_gaia_key"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  | std::string old_keystore_key; | 
|  | base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key); | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseRequired(_, _)); | 
|  | InitUnmigratedNigori(kOldGaiaKey, IMPLICIT_PASSPHRASE); | 
|  |  | 
|  | { | 
|  | // Pass multiple keystore keys, signaling a rotation has happened. | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Now simulate downloading a nigori node that was migrated before the | 
|  | // keys were rotated, and hence still encrypt with the old gaia key. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(AtLeast(1)); | 
|  | { | 
|  | sync_pb::NigoriSpecifics nigori = BuildMigratedNigori( | 
|  | KEYSTORE_PASSPHRASE, | 
|  | 1, | 
|  | kOldGaiaKey, | 
|  | old_keystore_key); | 
|  | // Update the encryption handler. | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->ApplyNigoriUpdate( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); | 
|  | PumpLoop(); | 
|  |  | 
|  | EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); | 
|  | EXPECT_EQ(KEYSTORE_PASSPHRASE, encryption_handler()->GetPassphraseType()); | 
|  | VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey); | 
|  | } | 
|  |  | 
|  | // Verify that performing a migration while having more than one keystore key | 
|  | // preserves a custom passphrase. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysUnmigratedCustomPassphrase) { | 
|  | const char kCustomPass[] = "custom_passphrase"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  |  | 
|  | EXPECT_CALL(*observer(), OnPassphraseRequired(_, _)); | 
|  | InitUnmigratedNigori(kCustomPass, CUSTOM_PASSPHRASE); | 
|  |  | 
|  | { | 
|  | // Pass multiple keystore keys, signaling a rotation has happened. | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Pass the decryption passphrase. This will also trigger the migration, | 
|  | // but should not overwrite the default key. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseAccepted()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, true)); | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptionComplete()).Times(AnyNumber()); | 
|  | std::string captured_bootstrap_token; | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_bootstrap_token)); | 
|  | encryption_handler()->SetDecryptionPassphrase(kCustomPass); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCustomPass); | 
|  |  | 
|  | const base::Time migration_time = encryption_handler()->migration_time(); | 
|  | VerifyRestoreAfterCustomPassphrase(TimeToProtoTime(migration_time), | 
|  | kCustomPass, captured_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Verify that a key rotation done after we've migrated a custom passphrase | 
|  | // nigori node preserves the custom passphrase. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, RotateKeysMigratedCustomPassphrase) { | 
|  | const char kCustomPass[] = "custom_passphrase"; | 
|  | const char kRawOldKeystoreKey[] = "old_keystore_key"; | 
|  |  | 
|  | KeyParams custom_key = {"localhost", "dummy", kCustomPass}; | 
|  | GetCryptographer()->AddKey(custom_key); | 
|  |  | 
|  | const int64_t migration_time = 1; | 
|  | InitCustomPassMigratedNigori(migration_time, kCustomPass); | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, | 
|  | kCustomPass); | 
|  |  | 
|  | SyncEncryptionHandler::NigoriState captured_nigori_state; | 
|  | { | 
|  | // Pass multiple keystore keys, signaling a rotation has happened. | 
|  | google::protobuf::RepeatedPtrField<google::protobuf::string> keys; | 
|  | keys.Add()->assign(kRawOldKeystoreKey); | 
|  | keys.Add()->assign(kRawKeystoreKey); | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_)) | 
|  | .WillOnce(testing::SaveArg<0>(&captured_nigori_state)); | 
|  | encryption_handler()->SetKeystoreKeys(keys, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | PumpLoop(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | VerifyMigratedNigoriWithTimestamp(migration_time, CUSTOM_PASSPHRASE, | 
|  | kCustomPass); | 
|  |  | 
|  | // We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_, | 
|  | // PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked | 
|  | // during a previous instance) so get it from the Cryptographer. | 
|  | std::string passphrase_bootstrap_token; | 
|  | GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token); | 
|  | VerifyRestoreAfterCustomPassphrase(migration_time, kCustomPass, | 
|  | passphrase_bootstrap_token, | 
|  | captured_nigori_state, CUSTOM_PASSPHRASE); | 
|  | } | 
|  |  | 
|  | // Verify that the client can gracefully handle a nigori node that is missing | 
|  | // the keystore migration time field. | 
|  | TEST_F(SyncEncryptionHandlerImplTest, MissingKeystoreMigrationTime) { | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnEncryptedTypesChanged(_, false)); | 
|  | encryption_handler()->Init(); | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Now simulate downloading a nigori node that that is missing the keystore | 
|  | // migration time. It should be interpreted properly, and the passphrase type | 
|  | // should switch to keystore passphrase. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseRequired(_, _)); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _)); | 
|  | { | 
|  | sync_pb::NigoriSpecifics nigori = BuildMigratedNigori( | 
|  | KEYSTORE_PASSPHRASE, | 
|  | 1, | 
|  | kKeystoreKey, | 
|  | kKeystoreKey); | 
|  | nigori.clear_keystore_migration_time(); | 
|  | // Update the encryption handler. | 
|  | WriteTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->ApplyNigoriUpdate( | 
|  | nigori, | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | Mock::VerifyAndClearExpectations(observer()); | 
|  |  | 
|  | // Now provide the keystore key to fully initialize the cryptographer. | 
|  | EXPECT_CALL(*observer(), | 
|  | OnCryptographerStateChanged(_)).Times(AnyNumber()); | 
|  | EXPECT_CALL(*observer(), | 
|  | OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN)); | 
|  | { | 
|  | ReadTransaction trans(FROM_HERE, user_share()); | 
|  | encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto( | 
|  | kRawKeystoreKey), | 
|  | trans.GetWrappedTrans()); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace syncer |