123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371 |
- // Copyright 2019 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/driver/sync_service_crypto.h"
- #include <list>
- #include <map>
- #include <utility>
- #include "base/base64.h"
- #include "base/callback_helpers.h"
- #include "base/containers/contains.h"
- #include "base/memory/raw_ptr.h"
- #include "base/observer_list.h"
- #include "base/run_loop.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/mock_callback.h"
- #include "base/test/scoped_feature_list.h"
- #include "components/os_crypt/os_crypt.h"
- #include "components/os_crypt/os_crypt_mocker.h"
- #include "components/signin/public/identity_manager/account_info.h"
- #include "components/sync/base/features.h"
- #include "components/sync/base/sync_prefs.h"
- #include "components/sync/driver/trusted_vault_client.h"
- #include "components/sync/engine/nigori/key_derivation_params.h"
- #include "components/sync/engine/nigori/nigori.h"
- #include "components/sync/test/mock_sync_engine.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace syncer {
- namespace {
- using testing::_;
- using testing::Eq;
- using testing::IsEmpty;
- using testing::IsNull;
- using testing::Ne;
- using testing::Not;
- using testing::NotNull;
- using testing::Return;
- using testing::SaveArg;
- sync_pb::EncryptedData MakeEncryptedData(
- const std::string& passphrase,
- const KeyDerivationParams& derivation_params) {
- std::unique_ptr<Nigori> nigori =
- Nigori::CreateByDerivation(derivation_params, passphrase);
- std::string nigori_name;
- EXPECT_TRUE(
- nigori->Permute(Nigori::Type::Password, kNigoriKeyName, &nigori_name));
- const std::string unencrypted = "test";
- sync_pb::EncryptedData encrypted;
- encrypted.set_key_name(nigori_name);
- EXPECT_TRUE(nigori->Encrypt(unencrypted, encrypted.mutable_blob()));
- return encrypted;
- }
- CoreAccountInfo MakeAccountInfoWithGaia(const std::string& gaia) {
- CoreAccountInfo result;
- result.gaia = gaia;
- return result;
- }
- std::string CreateBootstrapToken(const std::string& passphrase,
- const KeyDerivationParams& derivation_params) {
- std::unique_ptr<Nigori> nigori =
- Nigori::CreateByDerivation(derivation_params, passphrase);
- sync_pb::NigoriKey proto;
- nigori->ExportKeys(proto.mutable_deprecated_user_key(),
- proto.mutable_encryption_key(), proto.mutable_mac_key());
- const std::string serialized_key = proto.SerializeAsString();
- EXPECT_FALSE(serialized_key.empty());
- std::string encrypted_key;
- EXPECT_TRUE(OSCrypt::EncryptString(serialized_key, &encrypted_key));
- std::string encoded_key;
- base::Base64Encode(encrypted_key, &encoded_key);
- return encoded_key;
- }
- MATCHER(IsScryptKeyDerivationParams, "") {
- const KeyDerivationParams& params = arg;
- return params.method() == KeyDerivationMethod::SCRYPT_8192_8_11 &&
- !params.scrypt_salt().empty();
- }
- MATCHER_P2(BootstrapTokenDerivedFrom,
- expected_passphrase,
- expected_derivation_params,
- "") {
- const std::string& given_bootstrap_token = arg;
- std::string decoded_key;
- if (!base::Base64Decode(given_bootstrap_token, &decoded_key)) {
- return false;
- }
- std::string decrypted_key;
- if (!OSCrypt::DecryptString(decoded_key, &decrypted_key)) {
- return false;
- }
- sync_pb::NigoriKey given_key;
- if (!given_key.ParseFromString(decrypted_key)) {
- return false;
- }
- std::unique_ptr<Nigori> expected_nigori = Nigori::CreateByDerivation(
- expected_derivation_params, expected_passphrase);
- sync_pb::NigoriKey expected_key;
- expected_nigori->ExportKeys(expected_key.mutable_deprecated_user_key(),
- expected_key.mutable_encryption_key(),
- expected_key.mutable_mac_key());
- return given_key.encryption_key() == expected_key.encryption_key() &&
- given_key.mac_key() == expected_key.mac_key();
- }
- class MockDelegate : public SyncServiceCrypto::Delegate {
- public:
- MockDelegate() = default;
- ~MockDelegate() override = default;
- MOCK_METHOD(void, CryptoStateChanged, (), (override));
- MOCK_METHOD(void, CryptoRequiredUserActionChanged, (), (override));
- MOCK_METHOD(void, ReconfigureDataTypesDueToCrypto, (), (override));
- MOCK_METHOD(void,
- SetEncryptionBootstrapToken,
- (const std::string&),
- (override));
- MOCK_METHOD(std::string, GetEncryptionBootstrapToken, (), (override));
- };
- // Object representing a server that contains the authoritative trusted vault
- // keys, and TestTrustedVaultClient reads from.
- class TestTrustedVaultServer {
- public:
- TestTrustedVaultServer() = default;
- ~TestTrustedVaultServer() = default;
- void StoreKeysOnServer(const std::string& gaia_id,
- const std::vector<std::vector<uint8_t>>& keys) {
- gaia_id_to_keys_[gaia_id] = keys;
- }
- // Mimics a user going through a key-retrieval flow (e.g. reauth) such that
- // keys are fetched from the server and cached in |client|.
- void MimicKeyRetrievalByUser(const std::string& gaia_id,
- TrustedVaultClient* client) {
- DCHECK(client);
- DCHECK_NE(0U, gaia_id_to_keys_.count(gaia_id))
- << "StoreKeysOnServer() should have been called for " << gaia_id;
- client->StoreKeys(gaia_id, gaia_id_to_keys_[gaia_id],
- /*last_key_version=*/
- static_cast<int>(gaia_id_to_keys_[gaia_id].size()) - 1);
- }
- // Mimics the server RPC endpoint that allows key rotation.
- std::vector<std::vector<uint8_t>> RequestRotatedKeysFromServer(
- const std::string& gaia_id,
- const std::vector<uint8_t>& key_known_by_client) const {
- auto it = gaia_id_to_keys_.find(gaia_id);
- if (it == gaia_id_to_keys_.end()) {
- return {};
- }
- const std::vector<std::vector<uint8_t>>& latest_keys = it->second;
- if (!base::Contains(latest_keys, key_known_by_client)) {
- // |key_known_by_client| is invalid or too old: cannot be used to follow
- // key rotation.
- return {};
- }
- return latest_keys;
- }
- private:
- std::map<std::string, std::vector<std::vector<uint8_t>>> gaia_id_to_keys_;
- };
- // Simple in-memory implementation of TrustedVaultClient.
- class TestTrustedVaultClient : public TrustedVaultClient {
- public:
- explicit TestTrustedVaultClient(const TestTrustedVaultServer* server)
- : server_(server) {}
- ~TestTrustedVaultClient() override = default;
- // Exposes the total number of calls to FetchKeys().
- int fetch_count() const { return fetch_count_; }
- // Exposes the total number of calls to MarkLocalKeysAsStale().
- bool keys_marked_as_stale_count() const {
- return keys_marked_as_stale_count_;
- }
- // Exposes the total number of calls to the server's RequestKeysFromServer().
- int server_request_count() const { return server_request_count_; }
- // Exposes the total number of calls to GetIsRecoverabilityDegraded().
- int get_is_recoverablity_degraded_call_count() const {
- return get_is_recoverablity_degraded_call_count_;
- }
- // Mimics the completion of the next (FIFO) FetchKeys() request.
- bool CompleteFetchKeysRequest() {
- if (pending_responses_.empty()) {
- return false;
- }
- base::OnceClosure cb = std::move(pending_responses_.front());
- pending_responses_.pop_front();
- std::move(cb).Run();
- return true;
- }
- void SetIsRecoverabilityDegraded(bool is_recoverability_degraded) {
- is_recoverability_degraded_ = is_recoverability_degraded;
- for (Observer& observer : observer_list_) {
- observer.OnTrustedVaultRecoverabilityChanged();
- }
- }
- // TrustedVaultClient implementation.
- void AddObserver(Observer* observer) override {
- observer_list_.AddObserver(observer);
- }
- void RemoveObserver(Observer* observer) override {
- observer_list_.RemoveObserver(observer);
- }
- void FetchKeys(
- const CoreAccountInfo& account_info,
- base::OnceCallback<void(const std::vector<std::vector<uint8_t>>&)> cb)
- override {
- const std::string& gaia_id = account_info.gaia;
- ++fetch_count_;
- CachedKeysPerUser& cached_keys = gaia_id_to_cached_keys_[gaia_id];
- // If there are no keys cached, the only way to bootstrap the client is by
- // going through a retrieval flow, see MimicKeyRetrievalByUser().
- if (cached_keys.keys.empty()) {
- pending_responses_.push_back(
- base::BindOnce(std::move(cb), std::vector<std::vector<uint8_t>>()));
- return;
- }
- // If the locally cached keys are not marked as stale, return them directly.
- if (!cached_keys.marked_as_stale) {
- pending_responses_.push_back(
- base::BindOnce(std::move(cb), cached_keys.keys));
- return;
- }
- // Fetch keys from the server and cache them.
- cached_keys.keys =
- server_->RequestRotatedKeysFromServer(gaia_id, cached_keys.keys.back());
- cached_keys.marked_as_stale = false;
- // Return the newly-cached keys.
- pending_responses_.push_back(
- base::BindOnce(std::move(cb), cached_keys.keys));
- }
- // Store keys in the client-side cache, usually retrieved from the server as
- // part of the key retrieval process, see MimicKeyRetrievalByUser().
- void StoreKeys(const std::string& gaia_id,
- const std::vector<std::vector<uint8_t>>& keys,
- int last_key_version) override {
- CachedKeysPerUser& cached_keys = gaia_id_to_cached_keys_[gaia_id];
- cached_keys.keys = keys;
- cached_keys.marked_as_stale = false;
- for (Observer& observer : observer_list_) {
- observer.OnTrustedVaultKeysChanged();
- }
- }
- void MarkLocalKeysAsStale(const CoreAccountInfo& account_info,
- base::OnceCallback<void(bool)> cb) override {
- const std::string& gaia_id = account_info.gaia;
- ++keys_marked_as_stale_count_;
- CachedKeysPerUser& cached_keys = gaia_id_to_cached_keys_[gaia_id];
- if (cached_keys.keys.empty() || cached_keys.marked_as_stale) {
- // Nothing changed so report |false|.
- std::move(cb).Run(false);
- return;
- }
- // The cache is stale and should be invalidated. Following calls to
- // FetchKeys() will read from the server.
- cached_keys.marked_as_stale = true;
- std::move(cb).Run(true);
- }
- void GetIsRecoverabilityDegraded(const CoreAccountInfo& account_info,
- base::OnceCallback<void(bool)> cb) override {
- ++get_is_recoverablity_degraded_call_count_;
- std::move(cb).Run(is_recoverability_degraded_);
- }
- void AddTrustedRecoveryMethod(const std::string& gaia_id,
- const std::vector<uint8_t>& public_key,
- int method_type_hint,
- base::OnceClosure cb) override {
- // Not relevant in these tests.
- std::move(cb).Run();
- }
- void ClearDataForAccount(const CoreAccountInfo& account_info) override {
- // Not relevant in these tests.
- }
- private:
- struct CachedKeysPerUser {
- bool marked_as_stale = false;
- std::vector<std::vector<uint8_t>> keys;
- };
- const raw_ptr<const TestTrustedVaultServer> server_;
- std::map<std::string, CachedKeysPerUser> gaia_id_to_cached_keys_;
- base::ObserverList<Observer> observer_list_;
- int fetch_count_ = 0;
- int keys_marked_as_stale_count_ = 0;
- int get_is_recoverablity_degraded_call_count_ = 0;
- int server_request_count_ = 0;
- std::list<base::OnceClosure> pending_responses_;
- bool is_recoverability_degraded_ = false;
- };
- class SyncServiceCryptoTest : public testing::Test {
- protected:
- // Account used in most tests.
- const CoreAccountInfo kSyncingAccount =
- MakeAccountInfoWithGaia("syncingaccount");
- // Initial trusted vault keys stored on the server |TestTrustedVaultServer|
- // for |kSyncingAccount|.
- const std::vector<std::vector<uint8_t>> kInitialTrustedVaultKeys = {
- {0, 1, 2, 3, 4}};
- SyncServiceCryptoTest()
- : trusted_vault_client_(&trusted_vault_server_),
- crypto_(&delegate_, &trusted_vault_client_) {
- trusted_vault_server_.StoreKeysOnServer(kSyncingAccount.gaia,
- kInitialTrustedVaultKeys);
- }
- ~SyncServiceCryptoTest() override = default;
- void SetUp() override { OSCryptMocker::SetUp(); }
- void TearDown() override { OSCryptMocker::TearDown(); }
- bool VerifyAndClearExpectations() {
- return testing::Mock::VerifyAndClearExpectations(&delegate_) &&
- testing::Mock::VerifyAndClearExpectations(&trusted_vault_client_) &&
- testing::Mock::VerifyAndClearExpectations(&engine_);
- }
- void MimicKeyRetrievalByUser() {
- trusted_vault_server_.MimicKeyRetrievalByUser(kSyncingAccount.gaia,
- &trusted_vault_client_);
- }
- testing::NiceMock<MockDelegate> delegate_;
- TestTrustedVaultServer trusted_vault_server_;
- TestTrustedVaultClient trusted_vault_client_;
- testing::NiceMock<MockSyncEngine> engine_;
- SyncServiceCrypto crypto_;
- };
- // Happy case where no user action is required upon startup.
- TEST_F(SyncServiceCryptoTest, ShouldRequireNoUserAction) {
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- }
- TEST_F(SyncServiceCryptoTest, ShouldSetUpNewCustomPassphrase) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- ASSERT_FALSE(crypto_.IsUsingExplicitPassphrase());
- ASSERT_FALSE(crypto_.IsEncryptEverythingEnabled());
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Ne(PassphraseType::kCustomPassphrase));
- EXPECT_CALL(delegate_, SetEncryptionBootstrapToken(Not(IsEmpty())));
- EXPECT_CALL(engine_, SetEncryptionPassphrase(kTestPassphrase,
- IsScryptKeyDerivationParams()));
- crypto_.SetEncryptionPassphrase(kTestPassphrase);
- // Mimic completion of the procedure in the sync engine.
- EXPECT_CALL(delegate_, CryptoStateChanged());
- crypto_.OnPassphraseTypeChanged(PassphraseType::kCustomPassphrase,
- base::Time::Now());
- // The current implementation notifies observers again upon
- // crypto_.OnEncryptedTypesChanged(). This may change in the future.
- EXPECT_CALL(delegate_, CryptoStateChanged());
- crypto_.OnEncryptedTypesChanged(syncer::EncryptableUserTypes(),
- /*encrypt_everything=*/true);
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnPassphraseAccepted();
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- EXPECT_TRUE(crypto_.IsEncryptEverythingEnabled());
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kCustomPassphrase));
- EXPECT_TRUE(crypto_.IsUsingExplicitPassphrase());
- }
- TEST_F(SyncServiceCryptoTest, ShouldExposePassphraseRequired) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(0));
- // Mimic the engine determining that a passphrase is required.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Entering the wrong passphrase should be rejected.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
- EXPECT_FALSE(crypto_.SetDecryptionPassphrase("wrongpassphrase"));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- // Entering the correct passphrase should be accepted.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- EXPECT_CALL(delegate_,
- SetEncryptionBootstrapToken(BootstrapTokenDerivedFrom(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- EXPECT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase));
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- // Regression test for crbug.com/1306831.
- TEST_F(SyncServiceCryptoTest,
- ShouldStoreBootstrapTokenBeforeReconfiguringDataTypes) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- ASSERT_TRUE(crypto_.IsPassphraseRequired());
- // Entering the correct passphrase should be accepted.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // Order of SetEncryptionBootstrapToken() and
- // ReconfigureDataTypesDueToCrypto() (assuming passphrase is not required upon
- // reconfiguration) is important as clients rely on this to detect whether
- // GetDecryptionNigoriKey() can be called.
- testing::InSequence seq;
- EXPECT_CALL(delegate_,
- SetEncryptionBootstrapToken(BootstrapTokenDerivedFrom(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- ASSERT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase));
- }
- TEST_F(SyncServiceCryptoTest, ShouldSetupDecryptionWithBootstrapToken) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic passphrase stored in bootstrap token.
- ON_CALL(delegate_, GetEncryptionBootstrapToken())
- .WillByDefault(Return(CreateBootstrapToken(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- // Expect setting decryption key without waiting till user enters the
- // passphrase.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // Mimic the engine determining that a passphrase is required.
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- // The passphrase-required state should have been automatically resolved via
- // the bootstrap token.
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldSetupDecryptionWithBootstrapTokenUponEngineInitialization) {
- const std::string kTestPassphrase = "somepassphrase";
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic passphrase stored in bootstrap token.
- ON_CALL(delegate_, GetEncryptionBootstrapToken())
- .WillByDefault(Return(CreateBootstrapToken(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- // Mimic the engine determining that a passphrase is required. Note that
- // |crypto_| isn't yet aware of engine initialization - this is a legitimate
- // scenario.
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- // Expect setting decryption key without waiting till user enters the
- // passphrase.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // Mimic completion of engine initialization, now decryption key from
- // bootstrap token should be populated to the engine.
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest, ShouldIgnoreNotMatchingBootstrapToken) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic wrong passphrase stored in bootstrap token.
- ON_CALL(delegate_, GetEncryptionBootstrapToken())
- .WillByDefault(Return(CreateBootstrapToken(
- "wrongpassphrase", KeyDerivationParams::CreateForPbkdf2())));
- // Mimic the engine determining that a passphrase is required.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- // There should be no attempt to populate wrong key to the |engine_|.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Entering the correct passphrase should be accepted.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- EXPECT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase));
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest, ShouldIgnoreCorruptedBootstrapToken) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic storing corrupted bootstrap token.
- ON_CALL(delegate_, GetEncryptionBootstrapToken())
- .WillByDefault(Return("corrupted_token"));
- // Mimic the engine determining that a passphrase is required.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- // There should be no attempt to populate wrong key to the |engine_|.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Entering the correct passphrase should be accepted.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- EXPECT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase));
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest, ShouldDecryptWithNigoriKey) {
- const std::string kTestPassphrase = "somepassphrase";
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic the engine determining that a passphrase is required.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Passing wrong decryption key should be ignored.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
- crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation(
- KeyDerivationParams::CreateForPbkdf2(), "wrongpassphrase"));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Passing correct decryption key should be accepted.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- EXPECT_CALL(delegate_,
- SetEncryptionBootstrapToken(BootstrapTokenDerivedFrom(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation(
- KeyDerivationParams::CreateForPbkdf2(), kTestPassphrase));
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldIgnoreDecryptionWithNigoriKeyWhenPassphraseNotRequired) {
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
- EXPECT_CALL(delegate_, SetEncryptionBootstrapToken).Times(0);
- crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation(
- KeyDerivationParams::CreateForPbkdf2(), "unexpected_passphrase"));
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- // Regression test for crbug.com/1322687: engine initialization may happen after
- // SetDecryptionNigoriKey() call, verify it doesn't crash and that decryption
- // key populated to the engine later, upon initialization.
- TEST_F(SyncServiceCryptoTest,
- ShouldDeferDecryptionWithNigoriKeyUntilEngineInitialization) {
- const std::string kTestPassphrase = "somepassphrase";
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- // Mimic the engine determining that a passphrase is required.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnPassphraseRequired(
- KeyDerivationParams::CreateForPbkdf2(),
- MakeEncryptedData(kTestPassphrase,
- KeyDerivationParams::CreateForPbkdf2()));
- ASSERT_TRUE(crypto_.IsPassphraseRequired());
- VerifyAndClearExpectations();
- // Pass decryption nigori key, it should be stored in the bootstrap token, but
- // shouldn't cause other changes, since engine isn't initialized.
- std::string bootstrap_token;
- ON_CALL(delegate_, SetEncryptionBootstrapToken(_))
- .WillByDefault(SaveArg<0>(&bootstrap_token));
- ON_CALL(delegate_, GetEncryptionBootstrapToken())
- .WillByDefault([&bootstrap_token]() { return bootstrap_token; });
- crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation(
- KeyDerivationParams::CreateForPbkdf2(), kTestPassphrase));
- EXPECT_TRUE(crypto_.IsPassphraseRequired());
- // Decryption key should be passed to the engine once it's initialized.
- EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
- .WillOnce(
- [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
- // The current implementation issues two reconfigurations: one immediately
- // after checking the passphrase in the UI thread and a second time later when
- // the engine confirms with OnPassphraseAccepted().
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- EXPECT_FALSE(crypto_.IsPassphraseRequired());
- }
- TEST_F(SyncServiceCryptoTest, ShouldGetDecryptionKeyFromBootstrapToken) {
- const std::string kTestPassphrase = "somepassphrase";
- // Mimic passphrase being stored in bootstrap token.
- ON_CALL(delegate_, GetEncryptionBootstrapToken)
- .WillByDefault(Return(CreateBootstrapToken(
- kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
- std::unique_ptr<Nigori> expected_nigori = Nigori::CreateByDerivation(
- KeyDerivationParams::CreateForPbkdf2(), kTestPassphrase);
- ASSERT_THAT(expected_nigori, NotNull());
- std::string deprecated_user_key;
- std::string expected_encryption_key;
- std::string expected_mac_key;
- expected_nigori->ExportKeys(&deprecated_user_key, &expected_encryption_key,
- &expected_mac_key);
- // Verify that GetDecryptionNigoriKey() result equals to |expected_nigori|.
- std::unique_ptr<Nigori> stored_nigori = crypto_.GetDecryptionNigoriKey();
- ASSERT_THAT(stored_nigori, NotNull());
- std::string stored_encryption_key;
- std::string stored_mac_key;
- stored_nigori->ExportKeys(&deprecated_user_key, &stored_encryption_key,
- &stored_mac_key);
- EXPECT_THAT(stored_encryption_key, Eq(expected_encryption_key));
- EXPECT_THAT(stored_mac_key, Eq(expected_mac_key));
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldGetNullDecryptionKeyFromEmptyBootstrapToken) {
- // GetEncryptionBootstrapToken() returns empty string by default.
- EXPECT_THAT(crypto_.GetDecryptionNigoriKey(), IsNull());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldGetNullDecryptionKeyFromCorruptedBootstrapToken) {
- // Mimic corrupted bootstrap token being stored.
- ON_CALL(delegate_, GetEncryptionBootstrapToken)
- .WillByDefault(Return("corrupted_token"));
- EXPECT_THAT(crypto_.GetDecryptionNigoriKey(), IsNull());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldReadValidTrustedVaultKeysFromClientBeforeInitialization) {
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization.
- MimicKeyRetrievalByUser();
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // OnTrustedVaultKeyRequired() called during initialization of the sync
- // engine (i.e. before SetSyncEngine()).
- crypto_.OnTrustedVaultKeyRequired();
- // Trusted vault keys should be fetched only after the engine initialization
- // is completed.
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(0));
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- // While there is an ongoing fetch, there should be no user action required.
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- base::OnceClosure add_keys_cb;
- EXPECT_CALL(engine_,
- AddTrustedVaultDecryptionKeys(kInitialTrustedVaultKeys, _))
- .WillOnce(
- [&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) { add_keys_cb = std::move(done_cb); });
- // Mimic completion of the fetch.
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(add_keys_cb);
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic completion of the engine.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnTrustedVaultKeyAccepted();
- std::move(add_keys_cb).Run();
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(0));
- EXPECT_THAT(trusted_vault_client_.server_request_count(), Eq(0));
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldReadValidTrustedVaultKeysFromClientAfterInitialization) {
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization.
- MimicKeyRetrievalByUser();
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic the initialization of the sync engine, without trusted vault keys
- // being required.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(0));
- // Later on, mimic trusted vault keys being required (e.g. remote Nigori
- // update), which should trigger a fetch.
- crypto_.OnTrustedVaultKeyRequired();
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- // While there is an ongoing fetch, there should be no user action required.
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- base::OnceClosure add_keys_cb;
- EXPECT_CALL(engine_,
- AddTrustedVaultDecryptionKeys(kInitialTrustedVaultKeys, _))
- .WillOnce(
- [&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) { add_keys_cb = std::move(done_cb); });
- // Mimic completion of the fetch.
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(add_keys_cb);
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic completion of the engine.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- crypto_.OnTrustedVaultKeyAccepted();
- std::move(add_keys_cb).Run();
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(0));
- EXPECT_THAT(trusted_vault_client_.server_request_count(), Eq(0));
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldReadNoTrustedVaultKeysFromClientAfterInitialization) {
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
- EXPECT_CALL(engine_, AddTrustedVaultDecryptionKeys).Times(0);
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic the initialization of the sync engine, without trusted vault keys
- // being required.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(0));
- ASSERT_THAT(trusted_vault_client_.server_request_count(), Eq(0));
- // Later on, mimic trusted vault keys being required (e.g. remote Nigori
- // update), which should trigger a fetch.
- crypto_.OnTrustedVaultKeyRequired();
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- // While there is an ongoing fetch, there should be no user action required.
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic completion of the fetch, which should lead to a reconfiguration.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_TRUE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.server_request_count(), Eq(0));
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(0));
- }
- TEST_F(SyncServiceCryptoTest, ShouldReadInvalidTrustedVaultKeysFromClient) {
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization. In this test, |kInitialTrustedVaultKeys| does not
- // match the Nigori keys (i.e. the engine continues to think trusted vault
- // keys are required).
- MimicKeyRetrievalByUser();
- base::OnceClosure add_keys_cb;
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault(
- [&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) { add_keys_cb = std::move(done_cb); });
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic the initialization of the sync engine, without trusted vault keys
- // being required.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(0));
- ASSERT_THAT(trusted_vault_client_.server_request_count(), Eq(0));
- // Later on, mimic trusted vault keys being required (e.g. remote Nigori
- // update), which should trigger a fetch.
- crypto_.OnTrustedVaultKeyRequired();
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- // While there is an ongoing fetch, there should be no user action required.
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic completion of the client.
- EXPECT_CALL(engine_,
- AddTrustedVaultDecryptionKeys(kInitialTrustedVaultKeys, _));
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(add_keys_cb);
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic completion of the engine, without OnTrustedVaultKeyAccepted().
- std::move(add_keys_cb).Run();
- // The keys should be marked as stale, and a second fetch attempt started.
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- // Mimic completion of the client for the second pass.
- EXPECT_CALL(engine_,
- AddTrustedVaultDecryptionKeys(kInitialTrustedVaultKeys, _));
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(add_keys_cb);
- // Mimic completion of the engine, without OnTrustedVaultKeyAccepted(), for
- // the second pass.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- std::move(add_keys_cb).Run();
- EXPECT_TRUE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- }
- // Similar to ShouldReadInvalidTrustedVaultKeysFromClient but in this case the
- // client is able to follow a key rotation as part of the second fetch attempt.
- TEST_F(SyncServiceCryptoTest, ShouldFollowKeyRotationDueToSecondFetch) {
- const std::vector<std::vector<uint8_t>> kRotatedKeys = {
- kInitialTrustedVaultKeys[0], {2, 3, 4, 5}};
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization. In this test, |kInitialTrustedVaultKeys| does not
- // match the Nigori keys (i.e. the engine continues to think trusted vault
- // keys are required until |kRotatedKeys| are provided).
- MimicKeyRetrievalByUser();
- // Mimic server-side key rotation which the keys, in a way that the rotated
- // keys are a continuation of kInitialTrustedVaultKeys, such that
- // TestTrustedVaultServer will allow the client to silently follow key
- // rotation.
- trusted_vault_server_.StoreKeysOnServer(kSyncingAccount.gaia, kRotatedKeys);
- // The engine replies with OnTrustedVaultKeyAccepted() only if |kRotatedKeys|
- // are provided.
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault([&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) {
- if (keys == kRotatedKeys) {
- crypto_.OnTrustedVaultKeyAccepted();
- }
- std::move(done_cb).Run();
- });
- // Mimic initialization of the engine where trusted vault keys are needed and
- // |kInitialTrustedVaultKeys| are fetched as part of the first fetch.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- crypto_.OnTrustedVaultKeyRequired();
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- // While there is an ongoing fetch (first attempt), there should be no user
- // action required.
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // The keys fetched in the first attempt (|kInitialTrustedVaultKeys|) are
- // insufficient and should be marked as stale. In addition, a second fetch
- // should be triggered.
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- // While there is an ongoing fetch (second attempt), there should be no user
- // action required.
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // Because of |kRotatedKeys| is a continuation of |kInitialTrustedVaultKeys|,
- // TrustedVaultServer should successfully deliver the new keys |kRotatedKeys|
- // to the client.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- ASSERT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- }
- // Similar to ShouldReadInvalidTrustedVaultKeysFromClient: the vault
- // initially has no valid keys, leading to IsTrustedVaultKeyRequired().
- // Later, the vault gets populated with the keys, which should trigger
- // a fetch and eventually resolve the encryption issue.
- TEST_F(SyncServiceCryptoTest, ShouldRefetchTrustedVaultKeysWhenChangeObserved) {
- const std::vector<std::vector<uint8_t>> kNewKeys = {{2, 3, 4, 5}};
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization. In this test, |kInitialTrustedVaultKeys| does not
- // match the Nigori keys (i.e. the engine continues to think trusted vault
- // keys are required until |kNewKeys| are provided).
- MimicKeyRetrievalByUser();
- // The engine replies with OnTrustedVaultKeyAccepted() only if |kNewKeys| are
- // provided.
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault([&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) {
- if (keys == kNewKeys) {
- crypto_.OnTrustedVaultKeyAccepted();
- }
- std::move(done_cb).Run();
- });
- // Mimic initialization of the engine where trusted vault keys are needed and
- // |kInitialTrustedVaultKeys| are fetched, which are insufficient, and hence
- // IsTrustedVaultKeyRequired() is exposed.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- crypto_.OnTrustedVaultKeyRequired();
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- // Note that this initial attempt involves two fetches, where both return
- // |kInitialTrustedVaultKeys|.
- ASSERT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic server-side key reset and a new retrieval.
- trusted_vault_server_.StoreKeysOnServer(kSyncingAccount.gaia, kNewKeys);
- MimicKeyRetrievalByUser();
- // Key retrieval should have initiated a third fetch.
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(3));
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- EXPECT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- }
- // Same as above but the new keys become available during an ongoing FetchKeys()
- // request.
- TEST_F(SyncServiceCryptoTest,
- ShouldDeferTrustedVaultKeyFetchingWhenChangeObservedWhileOngoingFetch) {
- const std::vector<std::vector<uint8_t>> kNewKeys = {{2, 3, 4, 5}};
- // Cache |kInitialTrustedVaultKeys| into |trusted_vault_client_| prior to
- // engine initialization. In this test, |kInitialTrustedVaultKeys| does not
- // match the Nigori keys (i.e. the engine continues to think trusted vault
- // keys are required until |kNewKeys| are provided).
- MimicKeyRetrievalByUser();
- // The engine replies with OnTrustedVaultKeyAccepted() only if |kNewKeys| are
- // provided.
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault([&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) {
- if (keys == kNewKeys) {
- crypto_.OnTrustedVaultKeyAccepted();
- }
- std::move(done_cb).Run();
- });
- // Mimic initialization of the engine where trusted vault keys are needed and
- // |kInitialTrustedVaultKeys| are in the process of being fetched.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- crypto_.OnTrustedVaultKeyRequired();
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // While there is an ongoing fetch, mimic server-side key reset and a new
- // retrieval.
- trusted_vault_server_.StoreKeysOnServer(kSyncingAccount.gaia, kNewKeys);
- MimicKeyRetrievalByUser();
- // Because there's already an ongoing fetch, a second one should not have been
- // triggered yet and should be deferred instead.
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- // As soon as the first fetch completes, the second one (deferred) should be
- // started.
- EXPECT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // The completion of the second fetch should resolve the encryption issue.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- EXPECT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- EXPECT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- }
- // The engine gets initialized and the vault initially has insufficient keys,
- // leading to IsTrustedVaultKeyRequired(). Later, keys are added to the vault
- // *twice*, where the later event should be handled as a deferred fetch.
- TEST_F(
- SyncServiceCryptoTest,
- ShouldDeferTrustedVaultKeyFetchingWhenChangeObservedWhileOngoingRefetch) {
- const std::vector<std::vector<uint8_t>> kLatestKeys = {{2, 2, 2, 2, 2}};
- // The engine replies with OnTrustedVaultKeyAccepted() only if |kLatestKeys|
- // are provided.
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault([&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) {
- if (keys == kLatestKeys) {
- crypto_.OnTrustedVaultKeyAccepted();
- }
- std::move(done_cb).Run();
- });
- // Mimic initialization of the engine where trusted vault keys are needed and
- // no keys are fetched from the client, hence IsTrustedVaultKeyRequired() is
- // exposed.
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- crypto_.OnTrustedVaultKeyRequired();
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_THAT(trusted_vault_client_.fetch_count(), Eq(1));
- ASSERT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(0));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequired());
- // Mimic retrieval of keys, leading to a second fetch that returns
- // |kInitialTrustedVaultKeys|, which are insufficient and should be marked as
- // stale as soon as the fetch completes (later below).
- MimicKeyRetrievalByUser();
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- // While the second fetch is ongoing, mimic additional keys being retrieved.
- // Because there's already an ongoing fetch, a third one should not have been
- // triggered yet and should be deferred instead.
- trusted_vault_server_.StoreKeysOnServer(kSyncingAccount.gaia, kLatestKeys);
- MimicKeyRetrievalByUser();
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(2));
- // As soon as the second fetch completes, the keys should be marked as stale
- // and a third fetch attempt triggered.
- EXPECT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_THAT(trusted_vault_client_.keys_marked_as_stale_count(), Eq(1));
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(3));
- // As soon as the third fetch completes, the fourth one (deferred) should be
- // started.
- EXPECT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- EXPECT_THAT(trusted_vault_client_.fetch_count(), Eq(3));
- }
- TEST_F(SyncServiceCryptoTest, ShouldNotGetRecoverabilityIfFeatureDisabled) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndDisableFeature(kSyncTrustedVaultPassphraseRecovery);
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.get_is_recoverablity_degraded_call_count(),
- Eq(0));
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldNotGetRecoverabilityIfKeystorePassphraseUsed) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kKeystorePassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kKeystorePassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_THAT(trusted_vault_client_.get_is_recoverablity_degraded_call_count(),
- Eq(0));
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldNotReportDegradedRecoverabilityUponInitialization) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- base::HistogramTester histogram_tester;
- trusted_vault_client_.SetIsRecoverabilityDegraded(false);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- histogram_tester.ExpectUniqueSample(
- "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
- /*sample=*/false, /*expected_bucket_count=*/1);
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldReportDegradedRecoverabilityUponInitialization) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- base::HistogramTester histogram_tester;
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- EXPECT_TRUE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- histogram_tester.ExpectUniqueSample(
- "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
- /*sample=*/true, /*expected_bucket_count=*/1);
- }
- TEST_F(SyncServiceCryptoTest, ShouldReportDegradedRecoverabilityUponChange) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- base::HistogramTester histogram_tester;
- trusted_vault_client_.SetIsRecoverabilityDegraded(false);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- ASSERT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // Changing the state notifies observers and should lead to a change in
- // IsTrustedVaultRecoverabilityDegraded().
- EXPECT_CALL(delegate_, CryptoStateChanged());
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- EXPECT_TRUE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // For UMA purposes, only the initial value counts (false).
- histogram_tester.ExpectUniqueSample(
- "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
- /*sample=*/false, /*expected_bucket_count=*/1);
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldStopReportingDegradedRecoverabilityUponChange) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- base::HistogramTester histogram_tester;
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- ASSERT_TRUE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // Changing the state notifies observers and should lead to a change in
- // IsTrustedVaultRecoverabilityDegraded().
- EXPECT_CALL(delegate_, CryptoStateChanged());
- trusted_vault_client_.SetIsRecoverabilityDegraded(false);
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // For UMA purposes, only the initial value counts (true).
- histogram_tester.ExpectUniqueSample(
- "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
- /*sample=*/true, /*expected_bucket_count=*/1);
- }
- TEST_F(SyncServiceCryptoTest, ShouldReportDegradedRecoverabilityUponRetrieval) {
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- base::HistogramTester histogram_tester;
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- // Mimic startup with trusted vault keys being required.
- crypto_.OnTrustedVaultKeyRequired();
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(kSyncingAccount, &engine_);
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // Complete the fetching of initial keys (no keys) from the client.
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequired());
- ASSERT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // Mimic a successful key retrieval.
- ON_CALL(engine_, AddTrustedVaultDecryptionKeys)
- .WillByDefault([&](const std::vector<std::vector<uint8_t>>& keys,
- base::OnceClosure done_cb) {
- crypto_.OnTrustedVaultKeyAccepted();
- std::move(done_cb).Run();
- });
- MimicKeyRetrievalByUser();
- ASSERT_TRUE(trusted_vault_client_.CompleteFetchKeysRequest());
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- // The recoverability state should be exposed.
- EXPECT_TRUE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- histogram_tester.ExpectUniqueSample(
- "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
- /*sample=*/true, /*expected_bucket_count=*/1);
- }
- TEST_F(SyncServiceCryptoTest,
- ShouldClearDegradedRecoverabilityIfCustomPassphraseIsSet) {
- const std::string kTestPassphrase = "somepassphrase";
- base::test::ScopedFeatureList override_features;
- override_features.InitAndEnableFeature(kSyncTrustedVaultPassphraseRecovery);
- // Mimic a browser startup in |kTrustedVaultPassphrase| with no additional
- // keys required and degraded recoverability state.
- trusted_vault_client_.SetIsRecoverabilityDegraded(true);
- crypto_.OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase,
- base::Time::Now());
- crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kTrustedVaultPassphrase));
- ASSERT_TRUE(crypto_.IsTrustedVaultKeyRequiredStateKnown());
- ASSERT_FALSE(crypto_.IsTrustedVaultKeyRequired());
- ASSERT_FALSE(crypto_.IsPassphraseRequired());
- ASSERT_TRUE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- // Mimic the user setting up a new custom passphrase.
- crypto_.SetEncryptionPassphrase(kTestPassphrase);
- // Mimic completion of the procedure in the sync engine.
- EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto());
- EXPECT_CALL(delegate_, CryptoStateChanged());
- crypto_.OnPassphraseTypeChanged(PassphraseType::kCustomPassphrase,
- base::Time::Now());
- crypto_.OnPassphraseAccepted();
- ASSERT_THAT(crypto_.GetPassphraseType(),
- Eq(PassphraseType::kCustomPassphrase));
- // Recoverability should no longer be considered degraded.
- EXPECT_FALSE(crypto_.IsTrustedVaultRecoverabilityDegraded());
- }
- } // namespace
- } // namespace syncer
|