blob: 5ac7d81d01226830040c2d985f6e236a78a080fe [file] [log] [blame]
<
// 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 "components/sync/engine_impl/sync_encryption_handler_impl.h"
#include <stdint.h>
#include <cstring>
#include <memory>
#include "base/base64.h"
#include "base/bind.h"
#include "base/json/json_string_value_serializer.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "components/sync/base/fake_encryptor.h"
#include "components/sync/base/model_type_test_util.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/base/sync_base_switches.h"
#include "components/sync/engine/sync_engine_switches.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/syncable/entry.h"
#include "components/sync/syncable/mutable_entry.h"
#include "components/sync/syncable/read_node.h"
#include "components/sync/syncable/read_transaction.h"
#include "components/sync/syncable/syncable_write_transaction.h"
#include "components/sync/syncable/test_user_share.h"
#include "components/sync/syncable/write_node.h"
#include "components/sync/syncable/write_transaction.h"
#include "components/sync/test/engine/test_id_factory.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";
static const char kCustomPassphrase[] = "custom_passphrase";
// Denotes a value of custom_passphrase_key_derivation_method in NigoriSpecifics
// that is not a valid value from NigoriSpecifics::KeyDerivationMethod.
static const ::google::protobuf::int32 kUnsupportedKeyDerivationMethod = 12345;
static const char kScryptSalt[] = "Salt string used for scrypt";
enum class ExpectedKeyDerivationMethodStateForMetrics {
NOT_SET = 0,
UNSUPPORTED = 1,
PBKDF2_HMAC_SHA1_1003 = 2,
SCRYPT_8192_8_11 = 3
};
class SyncEncryptionHandlerObserverMock
: public SyncEncryptionHandler::Observer {
public:
MOCK_METHOD3(OnPassphraseRequired,
void(PassphraseRequiredReason,
const KeyDerivationParams&,
const sync_pb::EncryptedData&)); // NOLINT
MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT
MOCK_METHOD2(OnBootstrapTokenUpdated,
void(const std::string&, BootstrapTokenType type)); // NOLINT
MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool)); // NOLINT
MOCK_METHOD0(OnEncryptionComplete, void()); // NOLINT
MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*)); // NOLINT
MOCK_METHOD2(OnPassphraseTypeChanged,
void(PassphraseType,
base::Time)); // NOLINT
MOCK_METHOD1(OnLocalSetPassphraseEncryption,
void(const SyncEncryptionHandler::NigoriState&)); // NOLINT
};
google::protobuf::RepeatedPtrField<google::protobuf::string>
BuildEncryptionKeyProto(const std::string& encryption_key) {
google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
keys.Add()->assign(encryption_key);
return keys;
}
} // namespace
class SyncEncryptionHandlerImplTest : public ::testing::Test {
public:
SyncEncryptionHandlerImplTest() {}
~SyncEncryptionHandlerImplTest() override {}
void SetUp() override {
fake_random_salt_generator_ =
base::BindRepeating([]() { return std::string(kScryptSalt); });
test_user_share_.SetUp();
SetUpEncryption();
CreateRootForType(NIGORI);
}
void TearDown() override {
PumpLoop();
test_user_share_.TearDown();
}
protected:
void SetScryptFeaturesState(bool force_disabled,
bool use_for_new_passphrases) {
std::vector<base::Feature> enabled_features;
std::vector<base::Feature> disabled_features;
if (force_disabled) {
enabled_features.push_back(
switches::kSyncForceDisableScryptForCustomPassphrase);
} else {
disabled_features.push_back(
switches::kSyncForceDisableScryptForCustomPassphrase);
}
if (use_for_new_passphrases) {
enabled_features.push_back(
switches::kSyncUseScryptForNewCustomPassphrases);
} else {
disabled_features.push_back(
switches::kSyncUseScryptForNewCustomPassphrases);
}
feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
void SetUpEncryption() {
SetUpEncryptionWithKeyForBootstrapping(std::string());
}
void SetUpEncryptionWithKeyForBootstrapping(
const std::string& key_for_bootstrapping) {
encryption_handler_ = std::make_unique<SyncEncryptionHandlerImpl>(
user_share(), &encryptor_, key_for_bootstrapping,
std::string() /* keystore key for bootstrapping */,
fake_random_salt_generator_);
encryption_handler_->AddObserver(&observer_);
}
void CreateRootForType(ModelType model_type) {
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;
AddDefaultFieldValue(model_type, &specifics);
node.PutSpecifics(specifics);
}
void PumpLoop() { base::RunLoop().RunUntilIdle(); }
// Getters for tests.
UserShare* user_share() { return test_user_share_.user_share(); }
SyncEncryptionHandlerImpl* encryption_handler() {
return encryption_handler_.get();
}
SyncEncryptionHandlerObserverMock* observer() { return &observer_; }
Cryptographer* GetCryptographer() {
return encryption_handler_->GetCryptographerUnsafe();
}
sync_pb::NigoriSpecifics ReadNigoriSpecifics() {
ReadTransaction trans(FROM_HERE, user_share());
ReadNode nigori_node(&trans);
BaseNode::InitByLookupResult result = nigori_node.InitTypeRoot(NIGORI);
DCHECK_EQ(result, BaseNode::INIT_OK);
return nigori_node.GetNigoriSpecifics();
}
void VerifyMigratedNigori(
PassphraseType passphrase_type,
const std::string& passphrase,
const base::Optional<KeyDerivationParams>& key_derivation_params) {
VerifyMigratedNigoriWithTimestamp(0, passphrase_type, passphrase,
key_derivation_params);
}
void VerifyMigratedNigoriWithTimestamp(
int64_t migration_time,
PassphraseType passphrase_type,
const std::string& passphrase,
const base::Optional<KeyDerivationParams>& key_derivation_params) {
const sync_pb::NigoriSpecifics& nigori = ReadNigoriSpecifics();
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 == PassphraseType::CUSTOM_PASSPHRASE ||
passphrase_type == PassphraseType::FROZEN_IMPLICIT_PASSPHRASE) {
EXPECT_TRUE(nigori.encrypt_everything());
EXPECT_TRUE(nigori.keystore_decryptor_token().blob().empty());
if (passphrase_type == PassphraseType::CUSTOM_PASSPHRASE) {
EXPECT_EQ(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE,
nigori.passphrase_type());
if (key_derivation_params.has_value()) {
EXPECT_EQ(ProtoKeyDerivationMethodToEnum(
nigori.custom_passphrase_key_derivation_method()),
key_derivation_params.value().method());
if (key_derivation_params.value().method() ==
KeyDerivationMethod::SCRYPT_8192_8_11) {
std::string decoded_salt;
base::Base64Decode(nigori.custom_passphrase_key_derivation_salt(),
&decoded_salt);
EXPECT_EQ(key_derivation_params.value().scrypt_salt(),
decoded_salt);
}
}
if (!encryption_handler()->custom_passphrase_time().is_null()) {
EXPECT_EQ(
nigori.custom_passphrase_time(),
TimeToProtoTime(encryption_handler()->custom_passphrase_time()));
}
} else {
DCHECK(!key_derivation_params.has_value());
EXPECT_EQ(sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE,
nigori.passphrase_type());
EXPECT_FALSE(nigori.has_custom_passphrase_key_derivation_method());
}
} else {
DCHECK(!key_derivation_params.has_value());
EXPECT_FALSE(nigori.encrypt_everything());
EXPECT_FALSE(nigori.keystore_decryptor_token().blob().empty());
EXPECT_EQ(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE,
nigori.passphrase_type());
EXPECT_FALSE(nigori.has_custom_passphrase_key_derivation_method());
Cryptographer keystore_cryptographer(&encryptor_);
KeyParams params = {KeyDerivationParams::CreateForPbkdf2(), kKeystoreKey};
keystore_cryptographer.AddKey(params);
EXPECT_TRUE(keystore_cryptographer.CanDecryptUsingDefaultKey(
nigori.keystore_decryptor_token()));
}
Cryptographer temp_cryptographer(&encryptor_);
if (key_derivation_params.has_value() &&
passphrase_type == PassphraseType::CUSTOM_PASSPHRASE) {
temp_cryptographer.AddKey({key_derivation_params.value(), passphrase});
} else {
temp_cryptographer.AddKey(
{KeyDerivationParams::CreateForPbkdf2(), passphrase});
}
EXPECT_TRUE(temp_cryptographer.CanDecryptUsingDefaultKey(
nigori.encryption_keybag()));
}
sync_pb::NigoriSpecifics BuildMigratedNigori(
PassphraseType passphrase_type,
int64_t migration_time,
::google::protobuf::int32 proto_key_derivation_method,
const std::string& default_passphrase,
const std::string& keystore_key,
const base::Optional<std::string>& key_derivation_salt) {
DCHECK_NE(passphrase_type, PassphraseType::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 = {KeyDerivationParams::CreateForPbkdf2(),
keystore_key};
other_cryptographer.AddKey(keystore_params);
}
KeyDerivationMethod key_derivation_method =
ProtoKeyDerivationMethodToEnum(proto_key_derivation_method);
if (key_derivation_method == KeyDerivationMethod::UNSUPPORTED) {
// Since this is an unsupported method, we need to simulate an
// undecryptable keybag. To do this, we will use an arbitrary passphrase
// that SyncEncryptionHandlerImpl does not know about, therefore ensuring
// that the keybag cannot be decrypted in tests.
KeyParams underivable_key_params = {
KeyDerivationParams::CreateForPbkdf2(),
"SyncEncryptionHandlerImpl does not know this passphrase!"};
other_cryptographer.AddKey(underivable_key_params);
} else {
// Since scrypt might be forcibly disabled, we want to temporarily
// re-enable it so that we have no problems when we try to derive a key
// using it.
base::test::ScopedFeatureList scrypt_enabled_feature_list;
scrypt_enabled_feature_list.InitWithFeatureState(
switches::kSyncForceDisableScryptForCustomPassphrase, false);
switch (key_derivation_method) {
case KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
DCHECK(!key_derivation_salt.has_value());
other_cryptographer.AddKey(
{KeyDerivationParams::CreateForPbkdf2(), default_key});
break;
case KeyDerivationMethod::SCRYPT_8192_8_11:
DCHECK(key_derivation_salt.has_value());
other_cryptographer.AddKey({KeyDerivationParams::CreateForScrypt(
key_derivation_salt.value()),
default_key});
break;
case KeyDerivationMethod::UNSUPPORTED:
NOTREACHED();
break;
}
}
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 == PassphraseType::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 == PassphraseType::CUSTOM_PASSPHRASE
? sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE
: sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE);
nigori.set_custom_passphrase_key_derivation_method(
proto_key_derivation_method);
if (key_derivation_method == KeyDerivationMethod::SCRYPT_8192_8_11) {
// Also persist the salt in Nigori.
DCHECK(key_derivation_salt.has_value());
std::string encoded_salt;
base::Base64Encode(key_derivation_salt.value(), &encoded_salt);
nigori.set_custom_passphrase_key_derivation_salt(encoded_salt);
}
}
return nigori;
}
sync_pb::NigoriSpecifics BuildKeystoreMigratedNigori(
int64_t migration_time,
const std::string& default_passphrase,
const std::string& keystore_key) {
return BuildMigratedNigori(
PassphraseType::KEYSTORE_PASSPHRASE, migration_time,
sync_pb::NigoriSpecifics::UNSPECIFIED, default_passphrase, keystore_key,
/* key_derivation_salt = */ base::nullopt);
}
sync_pb::NigoriSpecifics BuildCustomPassMigratedNigori(
int64_t migration_time,
::google::protobuf::int32 key_derivation_method,
const std::string& default_passphrase,
const base::Optional<std::string>& key_derivation_salt) {
return BuildMigratedNigori(PassphraseType::CUSTOM_PASSPHRASE,
migration_time, key_derivation_method,
default_passphrase, kKeystoreKey,
key_derivation_salt);
}
void VerifyPassphraseType(PassphraseType passphrase_type) {
ReadTransaction trans(FROM_HERE, user_share());
EXPECT_EQ(passphrase_type,
encryption_handler()->GetPassphraseType(trans.GetWrappedTrans()));
}
// Builds a list of keys and call SyncEncryptionHandlerImpl::SetKeystoreKeys.
void SetupKeystoreKeys(const std::vector<std::string> keystore_keys) {
google::protobuf::RepeatedPtrField<google::protobuf::string>
proto_keystore_keys;
for (const std::string& keystore_key : keystore_keys) {
proto_keystore_keys.Add()->assign(keystore_key);
}
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
{
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(proto_keystore_keys,
trans.GetWrappedTrans());
}
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
}
void WriteNigori(const sync_pb::NigoriSpecifics& nigori) {
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
nigori_node.SetNigoriSpecifics(nigori);
}
// Build a migrated nigori node with the specified default passphrase
// and keystore key and initialize the encryption handler with it, verifying
// that the initialization was performed correctly.
void InitAndVerifyKeystoreMigratedNigori(
int64_t migration_time,
const std::string& default_passphrase,
const std::string& keystore_key) {
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(
PassphraseType::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));
InitKeystoreMigratedNigori(migration_time, default_passphrase,
keystore_key);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
}
// Build a migrated nigori node with the specified default passphrase
// and keystore key and initialize the encryption handler with it.
void InitKeystoreMigratedNigori(int64_t migration_time,
const std::string& default_passphrase,
const std::string& keystore_key) {
CreateRootForType(NIGORI);
sync_pb::NigoriSpecifics nigori = BuildKeystoreMigratedNigori(
migration_time, default_passphrase, keystore_key);
WriteNigori(nigori);
encryption_handler()->Init();
PumpLoop();
}
// Build a migrated nigori node with the specified default passphrase as a
// custom passphrase and initialize the encryption handler with it, verifying
// that the initialization was performed correctly.
void InitAndVerifyCustomPassphraseMigratedNigori(
int64_t migration_time,
::google::protobuf::int32 key_derivation_method,
const std::string& default_passphrase,
const base::Optional<std::string>& key_derivation_salt) {
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::CUSTOM_PASSPHRASE, _));
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AtLeast(1));
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true))
.Times(AtLeast(1));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(AnyNumber());
// OnPassphraseRequired might be called if the cryptographer is not
// populated with keys before we Init().
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _)).Times(AnyNumber());
InitCustomPassMigratedNigori(migration_time, key_derivation_method,
default_passphrase, key_derivation_salt);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
}
// Build a migrated nigori node with the specified default passphrase as a
// custom passphrase and initialize the encryption handler with it.
void InitCustomPassMigratedNigori(
int64_t migration_time,
::google::protobuf::int32 key_derivation_method,
const std::string& default_passphrase,
const base::Optional<std::string>& key_derivation_salt) {
sync_pb::NigoriSpecifics nigori =
BuildCustomPassMigratedNigori(migration_time, key_derivation_method,
default_passphrase, key_derivation_salt);
WriteNigori(nigori);
encryption_handler()->Init();
}
// Returns the serialized key that should have been derived for the given key
// derivation method and given custom passphrase. Can be compared with the
// return value of Cryptographer::GetDefaultNigoriKeyData.
std::string GetSerializedNigoriKeyForCustomPassphrase(
const KeyDerivationParams& key_derivation_params,
const std::string& passphrase) {
sync_pb::NigoriKey key;
Nigori nigori;
nigori.InitByDerivation(key_derivation_params, passphrase);
nigori.ExportKeys(key.mutable_user_key(), key.mutable_encryption_key(),
key.mutable_mac_key());
return key.SerializeAsString();
}
// Build an unmigrated nigori node with the specified passphrase and type and
// initialize the encryption handler with it, verifying that the
// initialization was performed correctly.
void InitAndVerifyUnmigratedNigori(const std::string& default_passphrase,
PassphraseType passphrase_type) {
if (passphrase_type != PassphraseType::IMPLICIT_PASSPHRASE) {
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(passphrase_type, _));
}
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AtLeast(1));
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, false));
InitUnmigratedNigori(default_passphrase, passphrase_type);
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
VerifyPassphraseType(passphrase_type);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
}
// Build an unmigrated nigori node with the specified passphrase and type and
// initialize the encryption handler with it.
void InitUnmigratedNigori(const std::string& default_passphrase,
PassphraseType passphrase_type) {
DCHECK_NE(passphrase_type, PassphraseType::FROZEN_IMPLICIT_PASSPHRASE);
Cryptographer other_cryptographer(GetCryptographer()->encryptor());
KeyParams default_key = {KeyDerivationParams::CreateForPbkdf2(),
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 ==
PassphraseType::CUSTOM_PASSPHRASE);
nigori_node.SetNigoriSpecifics(nigori);
}
encryption_handler()->Init();
}
// Verify we can restore the SyncEncryptionHandler state using a saved
// |bootstrap_token| and |nigori_state|.
//
// |migration_time| is the time migration occurred.
//
// |passphrase| is the explicit passphrase.
//
// |passphrase_type| is either CUSTOM_PASSPHRASE or
// FROZEN_IMPLICIT_PASSPHRASE.
void VerifyRestoreAfterExplicitPaspshrase(
int64_t migration_time,
const std::string& passphrase,
const std::string& bootstrap_token,
const SyncEncryptionHandler::NigoriState& nigori_state,
PassphraseType passphrase_type,
const base::Optional<KeyDerivationParams>& key_derivation_params) {
TearDown();
test_user_share_.SetUp();
SetUpEncryptionWithKeyForBootstrapping(bootstrap_token);
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true))
.Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(passphrase_type, _));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->RestoreNigori(nigori_state);
encryption_handler()->Init();
Mock::VerifyAndClearExpectations(observer());
VerifyMigratedNigoriWithTimestamp(migration_time, passphrase_type,
passphrase, key_derivation_params);
}
// Sets up the observer mocks so that any calls to its methods would satisfy
// them.
// TODO(davidovic): Use a nice mock instead. However, that's not trivial
// because it is not obvious which of existing EXPECT_CALLs are testing some
// behavior and which have just been added to fulfill the strict mock.
void IgnoreAllObserverCalls() {
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseAccepted()).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(AnyNumber());
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(_, _)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _)).Times(AnyNumber());
}
protected:
TestUserShare test_user_share_;
FakeEncryptor encryptor_;
std::unique_ptr<SyncEncryptionHandlerImpl> encryption_handler_;
StrictMock<SyncEncryptionHandlerObserverMock> observer_;
TestIdFactory ids_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::test::ScopedFeatureList feature_list_;
base::RepeatingCallback<std::string()> fake_random_salt_generator_;
};
// 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 */,
fake_random_salt_generator_);
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_EQ(encrypted_types, encryption_handler()->GetEncryptedTypesUnsafe());
EXPECT_EQ(encrypted_types, 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_EQ(encrypted_types, encryption_handler()->GetEncryptedTypesUnsafe());
EXPECT_EQ(encrypted_types, 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_EQ(encrypted_types, encryption_handler()->GetEncryptedTypesUnsafe());
}
// Verify the encryption handler processes the encrypt everything field
// properly.
TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingExplicit) {
sync_pb::NigoriSpecifics nigori;
nigori.set_encrypt_everything(true);
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
HasModelTypes(EncryptableUserTypes()), true));
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_EQ(ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS),
encrypted_types);
{
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->UpdateEncryptedTypesFromNigori(
nigori, trans.GetWrappedTrans());
}
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_TRUE(encrypted_types.HasAll(EncryptableUserTypes()));
// Receiving the nigori node again shouldn't trigger another notification.
Mock::VerifyAndClearExpectations(observer());
{
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->UpdateEncryptedTypesFromNigori(
nigori, trans.GetWrappedTrans());
}
}
// Verify the encryption handler can detect an implicit encrypt everything state
// (from clients that failed to write the encrypt everything field).
TEST_F(SyncEncryptionHandlerImplTest, EncryptEverythingImplicit) {
sync_pb::NigoriSpecifics nigori;
nigori.set_encrypt_bookmarks(true); // Non-passwords = encrypt everything
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
HasModelTypes(EncryptableUserTypes()), true));
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_EQ(ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS),
encrypted_types);
{
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->UpdateEncryptedTypesFromNigori(
nigori, trans.GetWrappedTrans());
}
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_TRUE(encrypted_types.HasAll(EncryptableUserTypes()));
// Receiving a nigori node with encrypt everything explicitly set shouldn't
// trigger another notification.
Mock::VerifyAndClearExpectations(observer());
nigori.set_encrypt_everything(true);
{
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->UpdateEncryptedTypesFromNigori(
nigori, trans.GetWrappedTrans());
}
}
// Verify the encryption handler can deal with new versions treating new types
// as Sensitive, and that it does not consider this an implicit encrypt
// everything case.
TEST_F(SyncEncryptionHandlerImplTest, UnknownSensitiveTypes) {
sync_pb::NigoriSpecifics nigori;
nigori.set_encrypt_everything(false);
nigori.set_encrypt_bookmarks(true);
ModelTypeSet expected_encrypted_types =
SyncEncryptionHandler::SensitiveTypes();
expected_encrypted_types.Put(BOOKMARKS);
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
HasModelTypes(expected_encrypted_types), false));
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
ModelTypeSet encrypted_types =
encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_EQ(ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS),
encrypted_types);
{
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->UpdateEncryptedTypesFromNigori(
nigori, trans.GetWrappedTrans());
}
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe();
EXPECT_EQ(ModelTypeSet(BOOKMARKS, PASSWORDS, DEPRECATED_WIFI_CREDENTIALS),
encrypted_types);
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), "old"};
KeyParams current_key = {KeyDerivationParams::CreateForPbkdf2(), "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");
// 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.
const sync_pb::NigoriSpecifics nigori = ReadNigoriSpecifics();
EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(
our_encrypted_specifics.encrypted()));
EXPECT_TRUE(
GetCryptographer()->CanDecrypt(other_encrypted_specifics.encrypted()));
EXPECT_TRUE(GetCryptographer()->CanDecrypt(nigori.encryption_keybag()));
EXPECT_TRUE(nigori.encrypt_everything());
EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(
nigori.encryption_keybag()));
}
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
}
// Ensure setting the keystore key works, updates the bootstrap token, and
// triggers a non-backwards compatible migration. Then verify that the
// bootstrap token can be correctly parsed by the encryption handler at startup
// time.
TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreMigratesAndUpdatesBootstrap) {
// Passing no keys should do nothing.
EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0);
{
WriteTransaction trans(FROM_HERE, user_share());
EXPECT_FALSE(GetCryptographer()->is_initialized());
EXPECT_TRUE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans()));
EXPECT_FALSE(encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(std::string()), trans.GetWrappedTrans()));
EXPECT_TRUE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans()));
}
Mock::VerifyAndClearExpectations(observer());
// Build a set of keystore keys.
const char kRawOldKeystoreKey[] = "old_keystore_key";
std::string old_keystore_key;
base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key);
google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
keys.Add()->assign(kRawOldKeystoreKey);
keys.Add()->assign(kRawKeystoreKey);
// Pass them to the encryption handler, triggering a migration and bootstrap
// token update.
std::string encoded_key;
std::string keystore_bootstrap;
EXPECT_CALL(*observer(), OnEncryptionComplete());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_));
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::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(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_params=*/base::nullopt);
// Ensure the bootstrap is encoded properly (a base64 encoded encrypted blob
// of list values containing the keystore keys).
std::string decoded_bootstrap;
ASSERT_TRUE(base::Base64Decode(keystore_bootstrap, &decoded_bootstrap));
std::string decrypted_bootstrap;
ASSERT_TRUE(GetCryptographer()->encryptor()->DecryptString(
decoded_bootstrap, &decrypted_bootstrap));
JSONStringValueDeserializer json(decrypted_bootstrap);
std::unique_ptr<base::Value> deserialized_keystore_keys(
json.Deserialize(nullptr, nullptr));
ASSERT_TRUE(deserialized_keystore_keys.get());
base::ListValue* keystore_list = nullptr;
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,
fake_random_salt_generator_);
{
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 = {KeyDerivationParams::CreateForPbkdf2(), 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 = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kOtherKey,
/*key_derivation_params=*/base::nullopt);
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::CUSTOM_PASSPHRASE, _));
encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
nigori_node.SetNigoriSpecifics(nigori);
}
// Run any tasks posted via AppplyNigoriUpdate.
PumpLoop();
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(2);
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
encryption_handler()->SetDecryptionPassphrase(kOtherKey);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
const base::Time migration_time = encryption_handler()->migration_time();
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
VerifyMigratedNigori(PassphraseType::CUSTOM_PASSPHRASE, kOtherKey,
{KeyDerivationParams::CreateForPbkdf2()});
VerifyRestoreAfterExplicitPaspshrase(
TimeToProtoTime(migration_time), kOtherKey, captured_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 PassphraseType::KEYSTORE_PASSPHRASE.
TEST_F(SyncEncryptionHandlerImplTest, MigrateOnKeystoreKeyAvailableImplicit) {
const char kCurKey[] = "cur";
KeyParams current_key = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::KEYSTORE_PASSPHRASE, _));
// The actual migration gets posted, so run all pending tasks.
PumpLoop();
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kCurKey,
/*key_derivation_params=*/base::nullopt);
}
// Test that we trigger a migration when we set the keystore key, had an
// implicit passphrase, and encrypt everything enabled. We should switch to
// PassphraseType::FROZEN_IMPLICIT_PASSPHRASE.
TEST_F(SyncEncryptionHandlerImplTest,
MigrateOnKeystoreKeyAvailableFrozenImplicit) {
const char kCurKey[] = "cur";
KeyParams current_key = {KeyDerivationParams::CreateForPbkdf2(), kCurKey};
GetCryptographer()->AddKey(current_key);
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, false));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->Init();
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->EnableEncryptEverything();
{
ReadTransaction trans(FROM_HERE, user_share());
// Once we provide a keystore key, we should perform the migration.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(
PassphraseType::FROZEN_IMPLICIT_PASSPHRASE, _));
// The actual migration gets posted, so run all pending tasks.
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
const base::Time migration_time = encryption_handler()->migration_time();
VerifyPassphraseType(PassphraseType::FROZEN_IMPLICIT_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::FROZEN_IMPLICIT_PASSPHRASE, kCurKey,
/*key_derivation_params=*/base::nullopt);
// We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_,
// PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked
// during a previous instance) so get it from the Cryptographer.
std::string passphrase_bootstrap_token;
GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token);
VerifyRestoreAfterExplicitPaspshrase(
TimeToProtoTime(migration_time), kCurKey, passphrase_bootstrap_token,
captured_nigori_state, PassphraseType::FROZEN_IMPLICIT_PASSPHRASE,
/*key_derivation_method=*/base::nullopt);
}
// 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(PassphraseType::CUSTOM_PASSPHRASE, _));
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
encryption_handler()->Init();
encryption_handler()->SetEncryptionPassphrase(kCurKey);
EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->EnableEncryptEverything();
Mock::VerifyAndClearExpectations(observer());
SyncEncryptionHandler::NigoriState captured_nigori_state;
{
ReadTransaction trans(FROM_HERE, user_share());
// Once we provide a keystore key, we should perform the migration.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
// The actual migration gets posted, so run all pending tasks.
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
const base::Time migration_time = encryption_handler()->migration_time();
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::CUSTOM_PASSPHRASE, kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
VerifyRestoreAfterExplicitPaspshrase(
TimeToProtoTime(migration_time), kCurKey, captured_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 PassphraseType::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(PassphraseType::CUSTOM_PASSPHRASE, _));
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
encryption_handler()->Init();
encryption_handler()->SetEncryptionPassphrase(kCurKey);
EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
Mock::VerifyAndClearExpectations(observer());
{
ReadTransaction trans(FROM_HERE, user_share());
// Once we provide a keystore key, we should perform the migration.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete());
// The actual migration gets posted, so run all pending tasks.
PumpLoop();
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
const base::Time migration_time = encryption_handler()->migration_time();
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::CUSTOM_PASSPHRASE, kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
VerifyRestoreAfterExplicitPaspshrase(
TimeToProtoTime(migration_time), kCurKey, captured_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), 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());
{
ReadTransaction trans(FROM_HERE, user_share());
EXPECT_NE(encryption_handler()->GetPassphraseType(trans.GetWrappedTrans()),
PassphraseType::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(
PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(1, PassphraseType::KEYSTORE_PASSPHRASE,
kCurKey,
/*key_derivation_method=*/base::nullopt);
// Check that the cryptographer still encrypts with the current key.
sync_pb::EncryptedData current_encrypted;
other_cryptographer.EncryptString("string", &current_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 = {KeyDerivationParams::CreateForPbkdf2(),
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
// PassphraseType::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 = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::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());
VerifyPassphraseType(PassphraseType::FROZEN_IMPLICIT_PASSPHRASE);
EXPECT_TRUE(GetCryptographer()->has_pending_keys());
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptionComplete());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
encryption_handler()->SetDecryptionPassphrase(kCurKey);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyMigratedNigoriWithTimestamp(
1, PassphraseType::FROZEN_IMPLICIT_PASSPHRASE, kCurKey,
/*key_derivation_method=*/base::nullopt);
// Check that the cryptographer still encrypts with the current key.
sync_pb::EncryptedData current_encrypted;
other_cryptographer.EncryptString("string", &current_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 = {KeyDerivationParams::CreateForPbkdf2(),
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
// PassphraseType::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 = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::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());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(GetCryptographer()->has_pending_keys());
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptionComplete());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
encryption_handler()->SetDecryptionPassphrase(kCurKey);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyMigratedNigoriWithTimestamp(1, PassphraseType::CUSTOM_PASSPHRASE,
kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
// Check that the cryptographer still encrypts with the current key.
sync_pb::EncryptedData current_encrypted;
other_cryptographer.EncryptString("string", &current_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 = {KeyDerivationParams::CreateForPbkdf2(),
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 = {KeyDerivationParams::CreateForPbkdf2(), kOldKey};
KeyParams cur_key = {KeyDerivationParams::CreateForPbkdf2(), kCurKey};
GetCryptographer()->AddKey(old_key);
GetCryptographer()->AddKey(cur_key);
// Build a migrated nigori with full encryption.
const int64_t migration_time = 1;
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
GetCryptographer()->GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(1);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
nigori.set_encrypt_everything(true);
nigori_node.SetNigoriSpecifics(nigori);
}
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::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());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(migration_time,
PassphraseType::CUSTOM_PASSPHRASE, kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
{
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
Mock::VerifyAndClearExpectations(observer());
// Now build an old unmigrated nigori node with old encrypted types. We should
// properly overwrite it with the migrated + encrypt everything state.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete());
{
Cryptographer other_cryptographer(GetCryptographer()->encryptor());
other_cryptographer.AddKey(old_key);
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(false);
nigori.set_encrypt_everything(false);
encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
nigori_node.SetNigoriSpecifics(nigori);
}
PumpLoop();
// Verify we're still migrated and have proper encryption state.
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(1, PassphraseType::CUSTOM_PASSPHRASE,
kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
// We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_,
// PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked
// during a previous instance) so get it from the Cryptographer.
std::string passphrase_bootstrap_token;
GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token);
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kCurKey, passphrase_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kOldKey};
KeyParams cur_key = {KeyDerivationParams::CreateForPbkdf2(), 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(PassphraseType::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());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(1, PassphraseType::CUSTOM_PASSPHRASE,
kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
{
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
Mock::VerifyAndClearExpectations(observer());
// Now build an old keystore nigori node with old encrypted types. We should
// properly overwrite it with the migrated + encrypt everything state.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete());
const int64_t migration_time = 1;
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
Cryptographer other_cryptographer(GetCryptographer()->encryptor());
other_cryptographer.AddKey(old_key);
encryption_handler()->GetKeystoreDecryptor(
other_cryptographer, kKeystoreKey,
nigori.mutable_keystore_decryptor_token());
other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(true);
nigori.set_encrypt_everything(false);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
nigori.set_keystore_migration_time(migration_time);
encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
nigori_node.SetNigoriSpecifics(nigori);
}
PumpLoop();
// Verify we're still migrated and have proper encryption state.
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(migration_time,
PassphraseType::CUSTOM_PASSPHRASE, kCurKey,
{KeyDerivationParams::CreateForPbkdf2()});
// We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_,
// PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked
// during a previous instance) so get it from the Cryptographer.
std::string passphrase_bootstrap_token;
GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token);
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kCurKey, passphrase_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), 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());
{
ReadTransaction trans(FROM_HERE, user_share());
EXPECT_NE(encryption_handler()->GetPassphraseType(trans.GetWrappedTrans()),
PassphraseType::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(
PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
{
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
PumpLoop();
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(1, PassphraseType::KEYSTORE_PASSPHRASE,
kCurKey,
/*key_derivation_method=*/base::nullopt);
// Check that the cryptographer still encrypts with the current key.
sync_pb::EncryptedData current_encrypted;
other_cryptographer.EncryptString("string", &current_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 = {KeyDerivationParams::CreateForPbkdf2(),
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 = {KeyDerivationParams::CreateForPbkdf2(), kOldKey};
other_cryptographer.AddKey(cur_key);
EXPECT_TRUE(other_cryptographer.is_ready());
EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor(
other_cryptographer, kKeystoreKey, &keystore_decryptor_token));
// Build a nigori node with the generated keystore decryptor token and
// initialize the encryption handler with it. The cryptographer should be
// initialized properly to decrypt both kOldKey and kKeystoreKey.
const int64_t migration_time = 1;
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
nigori.mutable_keystore_decryptor_token()->CopyFrom(
keystore_decryptor_token);
other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(migration_time);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
nigori_node.SetNigoriSpecifics(nigori);
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
const char kNewKey[] = "new_key";
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::CUSTOM_PASSPHRASE, _));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(2);
encryption_handler()->SetEncryptionPassphrase(kNewKey);
Mock::VerifyAndClearExpectations(observer());
EXPECT_FALSE(captured_bootstrap_token.empty());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
VerifyMigratedNigoriWithTimestamp(migration_time,
PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
{KeyDerivationParams::CreateForPbkdf2()});
// 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 = {KeyDerivationParams::CreateForPbkdf2(),
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 = {KeyDerivationParams::CreateForPbkdf2(), kNewKey};
Cryptographer new_cryptographer(GetCryptographer()->encryptor());
new_cryptographer.AddKey(new_key);
sync_pb::EncryptedData new_encrypted;
new_cryptographer.EncryptString("string", &new_encrypted);
EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(new_encrypted));
// Now verify that we can restore the current state using the captured
// bootstrap token and nigori state.
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kNewKey, captured_bootstrap_token, captured_nigori_state,
PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kOldKey};
other_cryptographer.AddKey(cur_key);
KeyParams keystore_key = {KeyDerivationParams::CreateForPbkdf2(),
kKeystoreKey};
other_cryptographer.AddNonDefaultKey(keystore_key);
EXPECT_TRUE(other_cryptographer.is_ready());
EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor(
other_cryptographer, kKeystoreKey, &keystore_decryptor_token));
// Build a nigori node with the generated keystore decryptor token and
// initialize the encryption handler with it. The cryptographer will have
// pending keys until we provide the decryption passphrase.
const int64_t migration_time = 1;
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
nigori.mutable_keystore_decryptor_token()->CopyFrom(
keystore_decryptor_token);
other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(migration_time);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
nigori_node.SetNigoriSpecifics(nigori);
}
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _));
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->SetDecryptionPassphrase(kOldKey);
EXPECT_TRUE(GetCryptographer()->is_ready());
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
const char kNewKey[] = "new_key";
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::CUSTOM_PASSPHRASE, _));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(2);
encryption_handler()->SetEncryptionPassphrase(kNewKey);
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
VerifyMigratedNigoriWithTimestamp(migration_time,
PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
{KeyDerivationParams::CreateForPbkdf2()});
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kNewKey};
Cryptographer new_cryptographer(GetCryptographer()->encryptor());
new_cryptographer.AddKey(new_key);
sync_pb::EncryptedData new_encrypted;
new_cryptographer.EncryptString("string", &new_encrypted);
EXPECT_TRUE(GetCryptographer()->CanDecryptUsingDefaultKey(new_encrypted));
// Now verify that we can restore the current state using the captured
// bootstrap token and nigori state.
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kNewKey, captured_bootstrap_token, captured_nigori_state,
PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kCurKey};
other_cryptographer.AddKey(cur_key);
KeyParams keystore_key = {KeyDerivationParams::CreateForPbkdf2(),
kKeystoreKey};
other_cryptographer.AddNonDefaultKey(keystore_key);
EXPECT_TRUE(other_cryptographer.is_ready());
EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor(
other_cryptographer, kKeystoreKey, &keystore_decryptor_token));
// Build a nigori node with the generated keystore decryptor token and
// initialize the encryption handler with it. The cryptographer will have
// pending keys until we provide the decryption passphrase.
const int64_t migration_time = 1;
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
ASSERT_EQ(nigori_node.InitTypeRoot(NIGORI), BaseNode::INIT_OK);
sync_pb::NigoriSpecifics nigori;
nigori.mutable_keystore_decryptor_token()->CopyFrom(
keystore_decryptor_token);
other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(migration_time);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
nigori_node.SetNigoriSpecifics(nigori);
}
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _));
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->SetDecryptionPassphrase(kCurKey);
Mock::VerifyAndClearExpectations(observer());
EXPECT_CALL(*observer(), OnPassphraseTypeChanged(
PassphraseType::FROZEN_IMPLICIT_PASSPHRASE, _));
EXPECT_CALL(*observer(), OnEncryptionComplete());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
encryption_handler()->EnableEncryptEverything();
Mock::VerifyAndClearExpectations(observer());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
EXPECT_TRUE(GetCryptographer()->is_ready());
VerifyPassphraseType(PassphraseType::FROZEN_IMPLICIT_PASSPHRASE);
EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigoriWithTimestamp(
1, PassphraseType::FROZEN_IMPLICIT_PASSPHRASE, kCurKey,
/*key_derivation_method=*/base::nullopt);
// Check that the cryptographer is encrypting using the frozen current key.
sync_pb::EncryptedData current_encrypted;
other_cryptographer.EncryptString("string", &current_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));
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kCurKey, captured_bootstrap_token, captured_nigori_state,
PassphraseType::FROZEN_IMPLICIT_PASSPHRASE,
/*key_derivation_method=*/base::nullopt);
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kOldKey};
KeyParams cur_key = {KeyDerivationParams::CreateForPbkdf2(), kCurKey};
GetCryptographer()->AddKey(old_key);
GetCryptographer()->AddKey(cur_key);
Cryptographer other_cryptographer(GetCryptographer()->encryptor());
other_cryptographer.AddKey(old_key);
EXPECT_TRUE(other_cryptographer.is_ready());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, false));
EXPECT_CALL(*observer(), OnEncryptionComplete());
encryption_handler()->Init();
EXPECT_TRUE(GetCryptographer()->is_ready());
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
{
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::KEYSTORE_PASSPHRASE, _));
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kCurKey,
/*key_derivation_params=*/base::nullopt);
// 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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kCurKey,
/*key_derivation_params=*/base::nullopt);
}
// 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);
SetupKeystoreKeys({kRawOldKeystoreKey});
// Then init the nigori node with a backwards compatible set of keys.
InitAndVerifyKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key);
// Now set some new keystore keys.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptionComplete());
SetupKeystoreKeys({kRawOldKeystoreKey, kRawKeystoreKey});
// 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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_method=*/base::nullopt);
}
// 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);
SetupKeystoreKeys({kRawOldKeystoreKey});
// Then init the nigori node with a non-backwards compatible set of keys.
InitAndVerifyKeystoreMigratedNigori(1, old_keystore_key, old_keystore_key);
// Now set some new keystore keys.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnEncryptionComplete());
SetupKeystoreKeys({kRawOldKeystoreKey, kRawKeystoreKey});
// 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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_method=*/base::nullopt);
}
// 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(_, _, _));
InitAndVerifyUnmigratedNigori(kOldGaiaKey,
PassphraseType::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(PassphraseType::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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_method=*/base::nullopt);
}
// 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.
SetupKeystoreKeys({kRawOldKeystoreKey, kRawKeystoreKey});
InitAndVerifyKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key);
// 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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_method=*/base::nullopt);
}
// 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(_, _, _));
InitAndVerifyUnmigratedNigori(kOldGaiaKey,
PassphraseType::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(PassphraseType::KEYSTORE_PASSPHRASE, _));
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(AtLeast(1));
{
sync_pb::NigoriSpecifics nigori = BuildMigratedNigori(
PassphraseType::KEYSTORE_PASSPHRASE, 1,
sync_pb::NigoriSpecifics::UNSPECIFIED, kOldGaiaKey, old_keystore_key,
/* key_derivation_salt = */ base::nullopt);
// 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());
VerifyPassphraseType(PassphraseType::KEYSTORE_PASSPHRASE);
VerifyMigratedNigori(PassphraseType::KEYSTORE_PASSPHRASE, kKeystoreKey,
/*key_derivation_method=*/base::nullopt);
}
// 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(_, _, _));
InitAndVerifyUnmigratedNigori(kCustomPass, PassphraseType::CUSTOM_PASSPHRASE);
{
// Pass multiple keystore keys, signaling a rotation has happened.
google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
keys.Add()->assign(kRawOldKeystoreKey);
keys.Add()->assign(kRawKeystoreKey);
ReadTransaction trans(FROM_HERE, user_share());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
encryption_handler()->SetKeystoreKeys(keys, trans.GetWrappedTrans());
}
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
// Pass the decryption passphrase. This will also trigger the migration,
// but should not overwrite the default key.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, true));
SyncEncryptionHandler::NigoriState captured_nigori_state;
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
EXPECT_CALL(*observer(), OnEncryptionComplete()).Times(AnyNumber());
std::string captured_bootstrap_token;
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
.WillOnce(testing::SaveArg<0>(&captured_bootstrap_token));
encryption_handler()->SetDecryptionPassphrase(kCustomPass);
Mock::VerifyAndClearExpectations(observer());
VerifyMigratedNigori(PassphraseType::CUSTOM_PASSPHRASE, kCustomPass,
{KeyDerivationParams::CreateForPbkdf2()});
const base::Time migration_time = encryption_handler()->migration_time();
VerifyRestoreAfterExplicitPaspshrase(
TimeToProtoTime(migration_time), kCustomPass, captured_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// 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 = {KeyDerivationParams::CreateForPbkdf2(), kCustomPass};
GetCryptographer()->AddKey(custom_key);
const int64_t migration_time = 1;
InitAndVerifyCustomPassphraseMigratedNigori(
migration_time, sync_pb::NigoriSpecifics::PBKDF2_HMAC_SHA1_1003,
kCustomPass, /* key_derivation_salt = */ base::nullopt);
VerifyMigratedNigoriWithTimestamp(
migration_time, PassphraseType::CUSTOM_PASSPHRASE, kCustomPass,
{KeyDerivationParams::CreateForPbkdf2()});
SyncEncryptionHandler::NigoriState captured_nigori_state;
{
// Pass multiple keystore keys, signaling a rotation has happened.
google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
keys.Add()->assign(kRawOldKeystoreKey);
keys.Add()->assign(kRawKeystoreKey);
ReadTransaction trans(FROM_HERE, user_share());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnLocalSetPassphraseEncryption(_))
.WillOnce(testing::SaveArg<0>(&captured_nigori_state));
encryption_handler()->SetKeystoreKeys(keys, trans.GetWrappedTrans());
}
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
VerifyMigratedNigoriWithTimestamp(
migration_time, PassphraseType::CUSTOM_PASSPHRASE, kCustomPass,
{KeyDerivationParams::CreateForPbkdf2()});
// We need the passphrase bootstrap token, but OnBootstrapTokenUpdated(_,
// PASSPHRASE_BOOTSTRAP_TOKEN) has not been invoked (because it was invoked
// during a previous instance) so get it from the Cryptographer.
std::string passphrase_bootstrap_token;
GetCryptographer()->GetBootstrapToken(&passphrase_bootstrap_token);
VerifyRestoreAfterExplicitPaspshrase(
migration_time, kCustomPass, passphrase_bootstrap_token,
captured_nigori_state, PassphraseType::CUSTOM_PASSPHRASE,
{KeyDerivationParams::CreateForPbkdf2()});
}
// Verify that the client can gracefully handle a nigori node that is missing
// the keystore migration time field.
TEST_F(SyncEncryptionHandlerImplTest, MissingKeystoreMigrationTime) {
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _));
EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, false));
encryption_handler()->Init();
Mock::VerifyAndClearExpectations(observer());
// Now simulate downloading a nigori node that that is missing the keystore
// migration time. It should be interpreted properly, and the passphrase type
// should switch to keystore passphrase.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(), OnPassphraseRequired(_, _, _));
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(PassphraseType::KEYSTORE_PASSPHRASE, _));
{
sync_pb::NigoriSpecifics nigori = BuildMigratedNigori(
PassphraseType::KEYSTORE_PASSPHRASE, 1,
sync_pb::NigoriSpecifics::UNSPECIFIED, kKeystoreKey, kKeystoreKey,
/* key_derivation_salt = */ base::nullopt);
nigori.clear_keystore_migration_time();
// Update the encryption handler.
WriteTransaction trans(FROM_HERE, user_share());
encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
}
Mock::VerifyAndClearExpectations(observer());
// Now provide the keystore key to fully initialize the cryptographer.
EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
{
ReadTransaction trans(FROM_HERE, user_share());
encryption_handler()->SetKeystoreKeys(
BuildEncryptionKeyProto(kRawKeystoreKey), trans.GetWrappedTrans());
}
}
// When we receive a remote Nigori with UNSPECIFIED as the key derivation
// method, it implies data encrypted using an old version (<M70) which does not
// know about the key derivation method field but always uses PBKDF2.
// Initializing the encryption handler should set it to PBKDF2 explicitly.
TEST_F(SyncEncryptionHandlerImplTest,
InitShouldSetPbkdf2WithCustomPassphraseWhenUnspecified) {
KeyParams custom_key = {KeyDerivationParams::CreateForPbkdf2(),
kCustomPassphrase};
GetCryptographer()->AddKey(custom_key);
IgnoreAllObserverCalls();
InitCustomPassMigratedNigori(
/*migration_time=*/1, sync_pb::NigoriSpecifics::UNSPECIFIED,
kCustomPassphrase, /*key_derivation_salt=*/base::nullopt);
sync_pb::NigoriSpecifics nigori = ReadNigoriSpecifics();
ASSERT_TRUE(nigori.has_custom_passphrase_key_derivation_method());
EXPECT_EQ(sync_pb::NigoriSpecifics::PBKDF2_HMAC_SHA1_1003,
nigori.custom_passphrase_key_derivation_method());
}
// This tests behavior that happens when Init calls ApplyNigoriUpdateImpl.
// Generally, in order not to repeat the tests, we are testing only this case.
// As long as this test passes (implying Init calls ApplyNigoriUpdateImpl) and
// ApplyNigoriUpdate tests pass, Init behaves correctly.
TEST_F(SyncEncryptionHandlerImplTest,
InitShouldPassPbkdf2ToObserverWhenUnspecified) {
IgnoreAllObserverCalls();
EXPECT_CALL(*observer(), OnPassphraseRequired(
_, KeyDerivationParams::CreateForPbkdf2(), _));
InitCustomPassMigratedNigori(
/*migration_time=*/1, sync_pb::NigoriSpecifics::UNSPECIFIED,
kCustomPassphrase, /*key_derivation_salt=*/base::nullopt);
Mock::VerifyAndClearExpectations(observer());
}
TEST_F(SyncEncryptionHandlerImplTest,
InitShouldReportPbkdf2InHistogramWhenPbkdf2Persisted) {
KeyParams custom_key = {KeyDerivationParams::CreateForPbkdf2(),
kCustomPassphrase};
GetCryptographer()->AddKey(custom_key);
IgnoreAllObserverCalls();
base::HistogramTester histogram_tester;
InitCustomPassMigratedNigori(
/*migration_time=*/1, sync_pb::NigoriSpecifics::PBKDF2_HMAC_SHA1_1003,
kCustomPassphrase, /*key_derivation_salt=*/base::nullopt);
histogram_tester.ExpectUniqueSample(
"Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup",
/*sample=*/
ExpectedKeyDerivationMethodStateForMetrics::PBKDF2_HMAC_SHA1_1003,
/*count=*/1);
}
TEST_F(SyncEncryptionHandlerImplTest,
InitShouldReportPbkdf2InHistogramWhenUnspecifiedKeyMethodPersisted) {
KeyParams custom_key = {KeyDerivationParams::CreateForPbkdf2(),
kCustomPassphrase};
GetCryptographer()->AddKey(custom_key);
IgnoreAllObserverCalls();
base::HistogramTester histogram_tester;
InitCustomPassMigratedNigori(
/*migration_time=*/1, sync_pb::NigoriSpecifics::UNSPECIFIED,
kCustomPassphrase, /*key_derivation_salt=*/base::nullopt);
histogram_tester.ExpectUniqueSample(
"Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup",
/*sample=*/
ExpectedKeyDerivationMethodStateForMetrics::PBKDF2_HMAC_SHA1_1003,
/*count=*/1);
}
TEST_F(SyncEncryptionHandlerImplTest,
InitShouldReportScryptInHistogramWhenScryptPersisted) {
KeyParams custom_key = {KeyDerivationParams::CreateForScrypt(kScryptSalt),
kCustomPassphrase};
GetCryptographer()->AddKey(custom_key);
IgnoreAllObserverCalls();
base::HistogramTester histogram_tester;
InitCustomPassMigratedNigori(
/*migration_time=*/1, sync_pb::NigoriSpecifics::SCRYPT_8192_8_11,
kCustomPassphrase, /*key_derivation_salt=*/kScryptSalt);
histogram_tester.ExpectUniqueSample(
"Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup",