summaryrefslogtreecommitdiffstats
path: root/sync/internal_api/sync_encryption_handler_impl_unittest.cc
diff options
context:
space:
mode:
authorzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-19 01:56:52 +0000
committerzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-19 01:56:52 +0000
commit9a3072c62ef8896a3e089b50efff8f2eb6794fcd (patch)
tree123be8e004986b76bc322e37b3222ec2712313b4 /sync/internal_api/sync_encryption_handler_impl_unittest.cc
parent270f2cf4ed76055f185ca9c59d3e4b9316b758fc (diff)
downloadchromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.zip
chromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.tar.gz
chromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.tar.bz2
[Sync] Add support for keystore key rotation.
Key rotation will trigger a full re-encryption of all sync data with the newest keystore key. Previous keys will be added to the keybag as well. We detect key rotation by checking whether we have multiple keystore keys and the nigori's keybag is not encrypted with the current keystore key. In that case, we no longer support backwards compatibility with non-keystore supporting versions, and re-encrypt using the newest keystore key. This change also fixes two latent issues: lack of encryption of the keystore bootstrap and not properly posting OnPassphraseAccepted when we resolve pending keys. BUG=163744 Review URL: https://chromiumcodereview.appspot.com/11434070 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173830 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/internal_api/sync_encryption_handler_impl_unittest.cc')
-rw-r--r--sync/internal_api/sync_encryption_handler_impl_unittest.cc606
1 files changed, 566 insertions, 40 deletions
diff --git a/sync/internal_api/sync_encryption_handler_impl_unittest.cc b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
index 65fc964..74bf0e42 100644
--- a/sync/internal_api/sync_encryption_handler_impl_unittest.cc
+++ b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/base64.h"
+#include "base/json/json_string_value_serializer.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/tracked_objects.h"
@@ -33,6 +34,7 @@ namespace {
using ::testing::_;
using ::testing::AnyNumber;
+using ::testing::AtLeast;
using ::testing::Mock;
using ::testing::SaveArg;
using ::testing::StrictMock;
@@ -59,6 +61,13 @@ class SyncEncryptionHandlerObserverMock
base::Time)); // NOLINT
};
+google::protobuf::RepeatedPtrField<google::protobuf::string>
+BuildEncryptionKeyProto(std::string encryption_key) {
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(encryption_key);
+ return keys;
+}
+
} // namespace
class SyncEncryptionHandlerImplTest : public ::testing::Test {
@@ -177,6 +186,149 @@ class SyncEncryptionHandlerImplTest : public ::testing::Test {
nigori.encryption_keybag()));
}
+ sync_pb::NigoriSpecifics BuildMigratedNigori(
+ PassphraseType passphrase_type,
+ int64 migration_time,
+ const std::string& default_passphrase,
+ const std::string& keystore_key) {
+ DCHECK_NE(passphrase_type, IMPLICIT_PASSPHRASE);
+ Cryptographer other_cryptographer(GetCryptographer()->encryptor());
+
+ std::string default_key = default_passphrase;
+ if (default_key.empty()) {
+ default_key = keystore_key;
+ } else {
+ KeyParams keystore_params = {"localhost", "dummy", keystore_key};
+ other_cryptographer.AddKey(keystore_params);
+ }
+ KeyParams params = {"localhost", "dummy", default_key};
+ other_cryptographer.AddKey(params);
+ EXPECT_TRUE(other_cryptographer.is_ready());
+
+ sync_pb::NigoriSpecifics nigori;
+ other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
+ nigori.set_keybag_is_frozen(true);
+ nigori.set_keystore_migration_time(migration_time);
+
+ if (passphrase_type == KEYSTORE_PASSPHRASE) {
+ sync_pb::EncryptedData keystore_decryptor_token;
+ EXPECT_TRUE(encryption_handler()->GetKeystoreDecryptor(
+ other_cryptographer,
+ keystore_key,
+ &keystore_decryptor_token));
+ nigori.mutable_keystore_decryptor_token()->CopyFrom(
+ keystore_decryptor_token);
+ nigori.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
+ } else {
+ nigori.set_encrypt_everything(true);
+ nigori.set_passphrase_type(
+ passphrase_type == CUSTOM_PASSPHRASE ?
+ sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE :
+ sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE);
+ }
+ return nigori;
+ }
+
+ // Build a migrated nigori node with the specified default passphrase
+ // and keystore key and initialize the encryption handler with it.
+ void InitKeystoreMigratedNigori(int64 migration_time,
+ const std::string& default_passphrase,
+ const std::string& keystore_key) {
+ {
+ WriteTransaction trans(FROM_HERE, user_share());
+ WriteNode nigori_node(&trans);
+ ASSERT_EQ(nigori_node.InitByTagLookup(kNigoriTag), BaseNode::INIT_OK);
+ sync_pb::NigoriSpecifics nigori = BuildMigratedNigori(
+ KEYSTORE_PASSPHRASE,
+ migration_time,
+ default_passphrase,
+ keystore_key);
+ nigori_node.SetNigoriSpecifics(nigori);
+ }
+
+ EXPECT_CALL(*observer(),
+ OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AtLeast(1));
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(_, false));
+ EXPECT_CALL(*observer(),
+ OnEncryptionComplete()).Times(AtLeast(1));
+ encryption_handler()->Init();
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE);
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ Mock::VerifyAndClearExpectations(observer());
+ }
+
+ // Build a migrated nigori node with the specified default passphrase
+ // as a custom passphrase.
+ void InitCustomPassMigratedNigori(int64 migration_time,
+ const std::string& default_passphrase) {
+ {
+ WriteTransaction trans(FROM_HERE, user_share());
+ WriteNode nigori_node(&trans);
+ ASSERT_EQ(nigori_node.InitByTagLookup(kNigoriTag), BaseNode::INIT_OK);
+ sync_pb::NigoriSpecifics nigori = BuildMigratedNigori(
+ CUSTOM_PASSPHRASE,
+ migration_time,
+ default_passphrase,
+ kKeystoreKey);
+ nigori_node.SetNigoriSpecifics(nigori);
+ }
+
+ EXPECT_CALL(*observer(),
+ OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AtLeast(1));
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(_, true)).Times(AtLeast(1));
+ EXPECT_CALL(*observer(),
+ OnEncryptionComplete()).Times(AtLeast(1));
+ encryption_handler()->Init();
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), CUSTOM_PASSPHRASE);
+ EXPECT_TRUE(encryption_handler()->EncryptEverythingEnabled());
+ Mock::VerifyAndClearExpectations(observer());
+ }
+
+ // Build an unmigrated nigori node with the specified passphrase and type and
+ // initialize the encryption handler with it.
+ void InitUnmigratedNigori(const std::string& default_passphrase,
+ PassphraseType passphrase_type) {
+ DCHECK_NE(passphrase_type, FROZEN_IMPLICIT_PASSPHRASE);
+ Cryptographer other_cryptographer(GetCryptographer()->encryptor());
+ KeyParams default_key = {"localhost", "dummy", default_passphrase};
+ other_cryptographer.AddKey(default_key);
+ EXPECT_TRUE(other_cryptographer.is_ready());
+
+ {
+ WriteTransaction trans(FROM_HERE, user_share());
+ WriteNode nigori_node(&trans);
+ ASSERT_EQ(nigori_node.InitByTagLookup(kNigoriTag), BaseNode::INIT_OK);
+ sync_pb::NigoriSpecifics nigori;
+ other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
+ nigori.set_keybag_is_frozen(passphrase_type == CUSTOM_PASSPHRASE);
+ nigori_node.SetNigoriSpecifics(nigori);
+ }
+
+ if (passphrase_type != IMPLICIT_PASSPHRASE) {
+ EXPECT_CALL(*observer(),
+ OnPassphraseTypeChanged(passphrase_type, _));
+ }
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AtLeast(1));
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(_, false));
+ encryption_handler()->Init();
+ EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), passphrase_type);
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ Mock::VerifyAndClearExpectations(observer());
+ }
+
protected:
TestUserShare test_user_share_;
FakeEncryptor encryptor_;
@@ -450,27 +602,73 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveOldNigori) {
}
// Ensure setting the keystore key works, updates the bootstrap token, and
-// doesn't modify the cryptographer.
-TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreUpdatedBoostrapToken) {
+// doesn't modify the cryptographer. Then verify that the bootstrap token
+// can be correctly parsed by the encryption handler at startup time.
+TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreUpdatesBootstrap) {
WriteTransaction trans(FROM_HERE, user_share());
+
+ // Passing no keys should do nothing.
EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0);
EXPECT_FALSE(GetCryptographer()->is_initialized());
EXPECT_TRUE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans()));
- EXPECT_FALSE(encryption_handler()->SetKeystoreKey("",
- trans.GetWrappedTrans()));
+ EXPECT_FALSE(
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(""),
+ 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 bootstrap token update.
std::string encoded_key;
- ASSERT_TRUE(base::Base64Encode(kRawKeystoreKey, &encoded_key));
- ASSERT_EQ(kKeystoreKey, encoded_key);
- EXPECT_CALL(*observer(),
- OnBootstrapTokenUpdated(kKeystoreKey,
- KEYSTORE_BOOTSTRAP_TOKEN));
- EXPECT_TRUE(encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans()));
+ std::string keystore_bootstrap;
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_,
+ KEYSTORE_BOOTSTRAP_TOKEN)).
+ WillOnce(SaveArg<0>(&keystore_bootstrap));
+ EXPECT_TRUE(
+ encryption_handler()->SetKeystoreKeys(
+ keys,
+ trans.GetWrappedTrans()));
EXPECT_FALSE(encryption_handler()->NeedKeystoreKey(trans.GetWrappedTrans()));
EXPECT_FALSE(GetCryptographer()->is_initialized());
+
+ // Ensure the bootstrap is encoded properly (a base64 encoded encrypted blob
+ // of list values containing the keystore keys).
+ std::string decoded_bootstrap;
+ ASSERT_TRUE(base::Base64Decode(keystore_bootstrap, &decoded_bootstrap));
+ std::string decrypted_bootstrap;
+ ASSERT_TRUE(
+ GetCryptographer()->encryptor()->DecryptString(decoded_bootstrap,
+ &decrypted_bootstrap));
+ JSONStringValueSerializer json(decrypted_bootstrap);
+ scoped_ptr<base::Value> deserialized_keystore_keys(
+ json.Deserialize(NULL, NULL));
+ ASSERT_TRUE(deserialized_keystore_keys.get());
+ base::ListValue* keystore_list = NULL;
+ deserialized_keystore_keys->GetAsList(&keystore_list);
+ ASSERT_TRUE(keystore_list);
+ ASSERT_EQ(2U, keystore_list->GetSize());
+ std::string test_string;
+ keystore_list->GetString(0, &test_string);
+ ASSERT_EQ(old_keystore_key, test_string);
+ keystore_list->GetString(1, &test_string);
+ ASSERT_EQ(kKeystoreKey, test_string);
+
+
+ // Now make sure a new encryption handler can correctly parse the bootstrap
+ // token.
+ SyncEncryptionHandlerImpl handler2(user_share(),
+ &encryptor_,
+ "", // Cryptographer bootstrap.
+ keystore_bootstrap);
+ EXPECT_FALSE(handler2.NeedKeystoreKey(trans.GetWrappedTrans()));
}
// Ensure GetKeystoreDecryptor only updates the keystore decryptor token if it
@@ -504,8 +702,9 @@ TEST_F(SyncEncryptionHandlerImplTest, MigrateOnDecryptImplicitPass) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
Mock::VerifyAndClearExpectations(observer());
}
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
@@ -560,8 +759,9 @@ TEST_F(SyncEncryptionHandlerImplTest, MigrateOnDecryptCustomPass) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
Mock::VerifyAndClearExpectations(observer());
}
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
@@ -632,8 +832,9 @@ TEST_F(SyncEncryptionHandlerImplTest, MigrateOnKeystoreKeyAvailableImplicit) {
OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
@@ -676,8 +877,9 @@ TEST_F(SyncEncryptionHandlerImplTest,
OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(FROZEN_IMPLICIT_PASSPHRASE, _));
@@ -729,8 +931,9 @@ TEST_F(SyncEncryptionHandlerImplTest,
OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
// The actual migration gets posted, so run all pending tasks.
PumpLoop();
@@ -773,8 +976,9 @@ TEST_F(SyncEncryptionHandlerImplTest,
OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(),
OnEncryptedTypesChanged(_, true));
@@ -822,6 +1026,7 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriKeystorePass) {
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(),
@@ -830,8 +1035,9 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriKeystorePass) {
OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
EXPECT_CALL(*observer(),
OnCryptographerStateChanged(_)).Times(AnyNumber());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
nigori_node.SetNigoriSpecifics(nigori);
}
@@ -874,8 +1080,9 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriFrozenImplicitPass) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
@@ -953,8 +1160,9 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveMigratedNigoriCustomPass) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
@@ -1060,8 +1268,9 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveUnmigratedNigoriAfterMigration) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
Mock::VerifyAndClearExpectations(observer());
@@ -1069,6 +1278,7 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveUnmigratedNigoriAfterMigration) {
// properly overwrite it with the migrated + encrypt everything state.
EXPECT_CALL(*observer(),
OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(), OnEncryptionComplete());
{
Cryptographer other_cryptographer(GetCryptographer()->encryptor());
other_cryptographer.AddKey(old_key);
@@ -1138,8 +1348,9 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveOldMigratedNigori) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
Mock::VerifyAndClearExpectations(observer());
@@ -1147,6 +1358,7 @@ TEST_F(SyncEncryptionHandlerImplTest, ReceiveOldMigratedNigori) {
// properly overwrite it with the migrated + encrypt everything state.
EXPECT_CALL(*observer(),
OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(), OnEncryptionComplete());
{
WriteTransaction trans(FROM_HERE, user_share());
WriteNode nigori_node(&trans);
@@ -1225,6 +1437,7 @@ TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreAfterReceivingMigratedNigori) {
EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
Mock::VerifyAndClearExpectations(observer());
+ EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(),
OnCryptographerStateChanged(_)).Times(AnyNumber());
EXPECT_CALL(*observer(),
@@ -1233,8 +1446,9 @@ TEST_F(SyncEncryptionHandlerImplTest, SetKeystoreAfterReceivingMigratedNigori) {
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
PumpLoop();
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
@@ -1289,10 +1503,12 @@ TEST_F(SyncEncryptionHandlerImplTest, SetCustomPassAfterMigration) {
nigori_node.SetNigoriSpecifics(nigori);
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
+ EXPECT_CALL(*observer(), OnPassphraseAccepted());
EXPECT_CALL(*observer(),
OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
EXPECT_CALL(*observer(),
@@ -1680,11 +1896,12 @@ TEST_F(SyncEncryptionHandlerImplTest,
EXPECT_CALL(*observer(),
OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
ReadTransaction trans(FROM_HERE, user_share());
- encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
- trans.GetWrappedTrans());
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawKeystoreKey),
+ trans.GetWrappedTrans());
}
EXPECT_CALL(*observer(),
- OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
+ OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
PumpLoop();
Mock::VerifyAndClearExpectations(observer());
EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
@@ -1694,6 +1911,7 @@ TEST_F(SyncEncryptionHandlerImplTest,
// 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);
@@ -1723,4 +1941,312 @@ TEST_F(SyncEncryptionHandlerImplTest,
VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kCurKey);
}
+// Trigger a key rotation upon receiving new keys if we already had a keystore
+// migrated nigori with the gaia key as the default (still in backwards
+// compatible mode).
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysGaiaDefault) {
+ const char kOldGaiaKey[] = "old_gaia_key";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+ std::string old_keystore_key;
+ base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key);
+ {
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawOldKeystoreKey),
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ EXPECT_CALL(*observer(), OnPassphraseAccepted());
+ InitKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key);
+
+ // Now set some new keystore keys.
+ EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(), OnEncryptionComplete());
+ {
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ ReadTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ // Pump for any posted tasks.
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Verify we're still migrated and have proper encryption state. We should
+ // have rotated the keybag so that it's now encrypted with the newest keystore
+ // key (instead of the old gaia key).
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_TRUE(GetCryptographer()->is_ready());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE);
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey);
+}
+
+// Trigger a key rotation upon receiving new keys if we already had a keystore
+// migrated nigori with the keystore key as the default.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysKeystoreDefault) {
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+ std::string old_keystore_key;
+ base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key);
+ {
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetKeystoreKeys(BuildEncryptionKeyProto(
+ kRawOldKeystoreKey),
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ EXPECT_CALL(*observer(), OnPassphraseAccepted());
+ InitKeystoreMigratedNigori(1, old_keystore_key, old_keystore_key);
+
+ // Now set some new keystore keys.
+ EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(), OnEncryptionComplete());
+ {
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ ReadTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ // Pump for any posted tasks.
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Verify we're still migrated and have proper encryption state. We should
+ // have rotated the keybag so that it's now encrypted with the newest keystore
+ // key (instead of the old gaia key).
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_TRUE(GetCryptographer()->is_ready());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE);
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey);
+}
+
+// Trigger a key rotation upon when a pending gaia passphrase is resolved.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysAfterPendingGaiaResolved) {
+ const char kOldGaiaKey[] = "old_gaia_key";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+
+ EXPECT_CALL(*observer(), OnPassphraseRequired(_, _));
+ InitUnmigratedNigori(kOldGaiaKey, IMPLICIT_PASSPHRASE);
+
+ {
+ // Pass multiple keystore keys, signaling a rotation has happened.
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Resolve the pending keys. This should trigger the key rotation.
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(),
+ OnPassphraseAccepted());
+ EXPECT_CALL(*observer(),
+ OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
+ EXPECT_CALL(*observer(),
+ OnEncryptionComplete()).Times(AtLeast(1));
+ EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
+ encryption_handler()->SetDecryptionPassphrase(kOldGaiaKey);
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_EQ(KEYSTORE_PASSPHRASE, encryption_handler()->GetPassphraseType());
+ VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey);
+}
+
+// When signing in for the first time, make sure we can rotate keys if we
+// already have a keystore migrated nigori.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysGaiaDefaultOnInit) {
+ // Destroy the existing nigori node so we init without a nigori node.
+ TearDown();
+ test_user_share_.SetUp();
+ SetUpEncryption();
+
+ const char kOldGaiaKey[] = "old_gaia_key";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+ std::string old_keystore_key;
+ base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key);
+
+ // Set two keys, signaling that a rotation has been performed. No nigori
+ // node is present yet, so we can't rotate.
+ {
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ ReadTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+
+ // Then init the nigori node with an old set of keys.
+ CreateRootForType(NIGORI);
+ EXPECT_CALL(*observer(), OnPassphraseAccepted());
+ InitKeystoreMigratedNigori(1, kOldGaiaKey, old_keystore_key);
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Verify we're still migrated and have proper encryption state. We should
+ // have rotated the keybag so that it's now encrypted with the newest keystore
+ // key (instead of the old gaia key).
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_TRUE(GetCryptographer()->is_ready());
+ EXPECT_EQ(encryption_handler()->GetPassphraseType(), KEYSTORE_PASSPHRASE);
+ EXPECT_FALSE(encryption_handler()->EncryptEverythingEnabled());
+ VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey);
+}
+
+// Trigger a key rotation when a migrated nigori (with an old keystore key) is
+// applied.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysWhenMigratedNigoriArrives) {
+ const char kOldGaiaKey[] = "old_gaia_key";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+ std::string old_keystore_key;
+ base::Base64Encode(kRawOldKeystoreKey, &old_keystore_key);
+
+ EXPECT_CALL(*observer(), OnPassphraseRequired(_, _));
+ InitUnmigratedNigori(kOldGaiaKey, IMPLICIT_PASSPHRASE);
+
+ {
+ // Pass multiple keystore keys, signaling a rotation has happened.
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Now simulate downloading a nigori node that was migrated before the
+ // keys were rotated, and hence still encrypt with the old gaia key.
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(),
+ OnPassphraseAccepted());
+ EXPECT_CALL(*observer(),
+ OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE, _));
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
+ EXPECT_CALL(*observer(),
+ OnEncryptionComplete()).Times(AtLeast(1));
+ {
+ sync_pb::NigoriSpecifics nigori = BuildMigratedNigori(
+ KEYSTORE_PASSPHRASE,
+ 1,
+ kOldGaiaKey,
+ old_keystore_key);
+ // Update the encryption handler.
+ WriteTransaction trans(FROM_HERE, user_share());
+ encryption_handler()->ApplyNigoriUpdate(
+ nigori,
+ trans.GetWrappedTrans());
+ }
+ EXPECT_FALSE(encryption_handler()->MigratedToKeystore());
+ PumpLoop();
+
+ EXPECT_TRUE(encryption_handler()->MigratedToKeystore());
+ EXPECT_EQ(KEYSTORE_PASSPHRASE, encryption_handler()->GetPassphraseType());
+ VerifyMigratedNigori(KEYSTORE_PASSPHRASE, kKeystoreKey);
+}
+
+// Verify that performing a migration while having more than one keystore key
+// preserves a custom passphrase.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysUnmigratedCustomPassphrase) {
+ const char kCustomPass[] = "custom_passphrase";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+
+ EXPECT_CALL(*observer(), OnPassphraseRequired(_, _));
+ InitUnmigratedNigori(kCustomPass, CUSTOM_PASSPHRASE);
+
+ {
+ // Pass multiple keystore keys, signaling a rotation has happened.
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Pass the decryption passphrase. This will also trigger the migration,
+ // but should not overwrite the default key.
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*observer(),
+ OnPassphraseAccepted());
+ EXPECT_CALL(*observer(),
+ OnEncryptedTypesChanged(_, true));
+ EXPECT_CALL(*observer(),
+ OnEncryptionComplete()).Times(AnyNumber());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
+ encryption_handler()->SetDecryptionPassphrase(kCustomPass);
+ Mock::VerifyAndClearExpectations(observer());
+
+ VerifyMigratedNigori(CUSTOM_PASSPHRASE, kCustomPass);
+}
+
+// Verify that a key rotation done after we've migrated a custom passphrase
+// nigori node preserves the custom passphrase.
+TEST_F(SyncEncryptionHandlerImplTest, RotateKeysMigratedCustomPassphrase) {
+ const char kCustomPass[] = "custom_passphrase";
+ const char kRawOldKeystoreKey[] = "old_keystore_key";
+
+ KeyParams custom_key = {"localhost", "dummy", kCustomPass};
+ GetCryptographer()->AddKey(custom_key);
+
+ InitCustomPassMigratedNigori(1, kCustomPass);
+ VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCustomPass);
+
+ {
+ // Pass multiple keystore keys, signaling a rotation has happened.
+ google::protobuf::RepeatedPtrField<google::protobuf::string> keys;
+ keys.Add()->assign(kRawOldKeystoreKey);
+ keys.Add()->assign(kRawKeystoreKey);
+ ReadTransaction trans(FROM_HERE, user_share());
+ EXPECT_CALL(*observer(),
+ OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
+ EXPECT_CALL(*observer(),
+ OnCryptographerStateChanged(_)).Times(AnyNumber());
+ encryption_handler()->SetKeystoreKeys(keys,
+ trans.GetWrappedTrans());
+ }
+ PumpLoop();
+ Mock::VerifyAndClearExpectations(observer());
+
+ VerifyMigratedNigoriWithTimestamp(1, CUSTOM_PASSPHRASE, kCustomPass);
+}
+
} // namespace syncer