| // 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 <string> |
| |
| #include "base/base64.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/tracked_objects.h" |
| #include "sync/internal_api/public/base/model_type_test_util.h" |
| #include "sync/internal_api/public/read_node.h" |
| #include "sync/internal_api/public/read_transaction.h" |
| #include "sync/internal_api/public/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 |
| }; |
| |
| google::protobuf::RepeatedPtrField<google::protobuf::string> |
| BuildEncryptionKeyProto(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() { |
| encryption_handler_.reset( |
| new SyncEncryptionHandlerImpl(user_share(), |
| &encryptor_, |
| std::string(), |
| std::string() /* bootstrap tokens */)); |
| 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() { |
| message_loop_.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 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 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 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()->EncryptEverythingEnabled()); |
| Mock::VerifyAndClearExpectations(observer()); |
| } |
| |
| // Build a migrated nigori node with the specified default passphrase |
| // as a custom passphrase. |
| void InitCustomPassMigratedNigori(int64 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| Mock::VerifyAndClearExpectations(observer()); |
| } |
| |
| 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 */); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| } |
| |
| // 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)); |
| JSONStringValueSerializer 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); |
| |
| { |
| 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()); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); |
| EXPECT_CALL(*observer(), |
| OnEncryptedTypesChanged(_, true)); |
| EXPECT_CALL(*observer(), |
| OnEncryptionComplete()).Times(2); |
| EXPECT_FALSE(encryption_handler()->MigratedToKeystore()); |
| encryption_handler()->SetDecryptionPassphrase(kOtherKey); |
| EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); |
| EXPECT_EQ(CUSTOM_PASSPHRASE, encryption_handler()->GetPassphraseType()); |
| VerifyMigratedNigori(CUSTOM_PASSPHRASE, kOtherKey); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| 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)); |
| 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(); |
| EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); |
| EXPECT_EQ(FROZEN_IMPLICIT_PASSPHRASE, |
| encryption_handler()->GetPassphraseType()); |
| EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled()); |
| VerifyMigratedNigori(FROZEN_IMPLICIT_PASSPHRASE, kCurKey); |
| } |
| |
| // 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, _)); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_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()); |
| |
| { |
| 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()); |
| } |
| // The actual migration gets posted, so run all pending tasks. |
| PumpLoop(); |
| EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); |
| EXPECT_EQ(CUSTOM_PASSPHRASE, |
| encryption_handler()->GetPassphraseType()); |
| EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled()); |
| VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCurKey); |
| } |
| |
| // 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, _)); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_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)); |
| EXPECT_CALL(*observer(), |
| OnEncryptionComplete()); |
| // The actual migration gets posted, so run all pending tasks. |
| PumpLoop(); |
| EXPECT_TRUE(encryption_handler()->MigratedToKeystore()); |
| EXPECT_EQ(CUSTOM_PASSPHRASE, |
| encryption_handler()->GetPassphraseType()); |
| EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled()); |
| VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCurKey); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| |
| 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()->EncryptEverythingEnabled()); |
| |
| 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. |
| { |
| 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()->EncryptEverythingEnabled()); |
| 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 unmigrated nigori node with old encrypted types. We should |
| // properly overwrite it with the migrated + encrypt everything state. |
| EXPECT_CALL(*observer(), |
| OnCryptographerStateChanged(_)).Times(AnyNumber()); |
| 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()->EncryptEverythingEnabled()); |
| VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCurKey); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| 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()); |
| 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(), CUSTOM_PASSPHRASE); |
| EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled()); |
| VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCurKey); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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. |
| { |
| 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(), |
| 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()->EncryptEverythingEnabled()); |
| Mock::VerifyAndClearExpectations(observer()); |
| |
| const char kNewKey[] = "new_key"; |
| EXPECT_CALL(*observer(), |
| OnCryptographerStateChanged(_)).Times(AnyNumber()); |
| EXPECT_CALL(*observer(), |
| OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_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()->EncryptEverythingEnabled()); |
| EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); |
| VerifyMigratedNigoriWithTimestamp(1, 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)); |
| } |
| |
| // 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. |
| { |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| Mock::VerifyAndClearExpectations(observer()); |
| |
| const char kNewKey[] = "new_key"; |
| EXPECT_CALL(*observer(), |
| OnCryptographerStateChanged(_)).Times(AnyNumber()); |
| EXPECT_CALL(*observer(), |
| OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_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()->EncryptEverythingEnabled()); |
| EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null()); |
| VerifyMigratedNigoriWithTimestamp(1, 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)); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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. |
| { |
| 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()->EncryptEverythingEnabled()); |
| 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(kCurKey); |
| Mock::VerifyAndClearExpectations(observer()); |
| |
| EXPECT_CALL(*observer(), |
| OnPassphraseTypeChanged(FROZEN_IMPLICIT_PASSPHRASE, _)); |
| EXPECT_CALL(*observer(), |
| OnEncryptionComplete()); |
| EXPECT_CALL(*observer(), |
| OnEncryptedTypesChanged(_, true)); |
| 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()->EncryptEverythingEnabled()); |
| 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)); |
| } |
| |
| // 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()->EncryptEverythingEnabled()); |
| |
| { |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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()->EncryptEverythingEnabled()); |
| 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)); |
| EXPECT_CALL(*observer(), |
| OnEncryptionComplete()).Times(AnyNumber()); |
| EXPECT_CALL(*observer(), |
| OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); |
| encryption_handler()->SetDecryptionPassphrase(kCustomPass); |
| Mock::VerifyAndClearExpectations(observer()); |
| |
| VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCustomPass); |
| } |
| |
| // 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); |
| |
| InitCustomPassMigratedNigori(1, kCustomPass); |
| VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCustomPass); |
| |
| { |
| // 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()); |
| encryption_handler()->SetKeystoreKeys(keys, |
| trans.GetWrappedTrans()); |
| } |
| PumpLoop(); |
| Mock::VerifyAndClearExpectations(observer()); |
| |
| VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCustomPass); |
| } |
| |
| } // namespace syncer |