chromium/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/sync/nigori/nigori_sync_bridge_impl.h"

#include <utility>

#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "components/os_crypt/sync/os_crypt_mocker.h"
#include "components/sync/base/time.h"
#include "components/sync/engine/nigori/key_derivation_params.h"
#include "components/sync/nigori/keystore_keys_cryptographer.h"
#include "components/sync/nigori/nigori_state.h"
#include "components/sync/nigori/nigori_storage.h"
#include "components/sync/protocol/entity_data.h"
#include "components/sync/protocol/nigori_local_data.pb.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
#include "components/sync/test/nigori_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace syncer {

namespace {

_;
Eq;
IsNull;
Ne;
Not;
NotNull;
Return;

NigoriMetadataBatch CreateFakeNigoriMetadataBatch(
    const std::string& progress_marker_token,
    int64_t entity_metadata_sequence_number);

MATCHER(NullTime, "") {}

MATCHER_P(HasDefaultKeyDerivedFrom, key_params, "") {}

MATCHER(HasKeystoreNigori, "") {}

MATCHER_P2(HasPublicKeyVersionAndValue, key_version, key_value, "") {}

MATCHER_P(HasPublicKeyVersion, key_version, "") {}

MATCHER(HasCustomPassphraseNigori, "") {}

MATCHER_P(CanDecryptWith, key_params, "") {}

MATCHER_P(EncryptedDataEq, expected, "") {}

MATCHER(IsEmptyMetadataBatch, "") {}

MATCHER_P2(IsFakeNigoriMetadataBatchWithTokenAndSequenceNumber,
           expected_token,
           expected_sequence_number,
           "") {}

CrossUserSharingKeys CreateNewCrossUserSharingKeys() {}

NigoriMetadataBatch CreateFakeNigoriMetadataBatch(
    const std::string& progress_marker_token,
    int64_t entity_metadata_sequence_number) {}

std::unique_ptr<Nigori> MakeNigoriKey(const KeyParamsForTesting& key_params) {}

KeyDerivationParams MakeCustomPassphraseKeyDerivationParams() {}

class MockNigoriLocalChangeProcessor : public NigoriLocalChangeProcessor {};

// This class is a helper to keep ownership of a mocked processor by providing
// ownership to the forwarding processor.
class ForwardingNigoriLocalChangeProcessor : public NigoriLocalChangeProcessor {};

class MockObserver : public SyncEncryptionHandler::Observer {};

class MockNigoriStorage : public NigoriStorage {};

class NigoriSyncBridgeImplTest : public testing::Test {};

class NigoriSyncBridgeImplTestWithOptionalScryptDerivation
    : public NigoriSyncBridgeImplTest,
      public testing::WithParamInterface<bool> {};

// During initialization bridge should expose encrypted types via observers
// notification.
TEST_F(NigoriSyncBridgeImplTest, ShouldNotifyObserversOnInit) {}

// Tests that bridge support Nigori with IMPLICIT_PASSPHRASE.
TEST_F(NigoriSyncBridgeImplTest, ShouldAcceptKeysFromImplicitPassphraseNigori) {}

// Simplest case of keystore Nigori: we have only one keystore key and no old
// keys. This keystore key is encrypted in both encryption_keybag and
// keystore_decryptor_token. Client receives such Nigori if initialization of
// Nigori node was done after keystore was introduced and no key rotations
// happened.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldAcceptKeysFromKeystoreNigoriAndNotifyObservers) {}

// Simplest case of keystore Nigori with CrossUserSharingKeys.
TEST_F(
    NigoriSyncBridgeImplTest,
    ShouldAcceptKeysFromKeystoreNigoriWithCrossUserSharingKeysAndNotifyObservers) {}

// Tests that client can properly process remote updates with rotated keystore
// nigori. Cryptographer should be able to decrypt any data encrypted with any
// keystore key and use current keystore key as default key.
TEST_F(NigoriSyncBridgeImplTest, ShouldAcceptKeysFromRotatedKeystoreNigori) {}

// In the backward compatible mode keystore Nigori's keystore_decryptor_token
// isn't a kestore key, however keystore_decryptor_token itself should be
// encrypted with the keystore key.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldAcceptKeysFromBackwardCompatibleKeystoreNigori) {}

// Tests that we can successfully use old keys from encryption_keybag in
// backward compatible mode.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldAcceptOldKeysFromBackwardCompatibleKeystoreNigori) {}

// Tests that we build keystore Nigori, put it to processor, initialize the
// cryptographer and expose a valid entity through GetDataForCommit() /
// GetDataForDebugging(), when the default Nigori is received.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldPutAndMakeCryptographerReadyOnDefaultNigori) {}

// Tests that upon receiving Nigori corrupted due to absence of
// |encryption_keybag|, bridge respect its passphrase type and doesn't attempt
// to trigger keystore initialization.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldNotTriggerKeystoreInitializationForCorruptedCustomPassphrase) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldRotateKeystoreKey) {}

// This test emulates late arrival of keystore keys, so neither
// |keystore_decryptor_token| or |encryption_keybag| could be decrypted at the
// moment NigoriSpecifics arrived. They should be decrypted right after
// keystore keys arrival.
TEST_F(NigoriSyncBridgeImplTest, ShouldDecryptPendingKeysInKeystoreMode) {}

// This test emulates late arrival of keystore keys in backward-compatible
// keystore mode, so neither |keystore_decryptor_token| or |encryption_keybag|
// could be decrypted at the moment NigoriSpecifics arrived. Since default key
// is derived from legacy implicit passphrase, pending keys should be decrypted
// once passphrase passed to SetExplicitPassphraseDecryptionKey().
// SetKeystoreKeys() intentionally not called in this test, to not allow
// decryption with |keystore_decryptor_token|.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldDecryptPendingKeysWithPassphraseInKeystoreMode) {}

// Tests that bridge is able to decrypt keystore nigori, when
// |keystore_decryptor_token| is corrupted, but |encryption_keybag| is
// decryptable using keystore keys.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldDecryptKeystoreNigoriWithCorruptedKeystoreDecryptor) {}

// Tests that unsuccessful attempt of |pending_keys| decryption ends up in
// additional OnPassphraseRequired() call. This is allowed because of possible
// change of |pending_keys| in keystore mode or due to transition from keystore
// to custom passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldNotifyWhenDecryptionWithPassphraseFailed) {}

// Tests that attempt to SetEncryptionPassphrase() has no effect (at least
// that bridge's Nigori is still keystore one) if it was called, while bridge
// has pending keys in keystore mode.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldNotSetEncryptionPassphraseWithPendingKeys) {}

// Tests that we can perform initial sync with custom passphrase Nigori.
// We should notify observers about encryption state changes and cryptographer
// shouldn't be ready (by having pending keys) until user provides the
// passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldNotifyWhenSyncedWithCustomPassphraseNigori) {}

// Tests that we can process remote update with custom passphrase Nigori, while
// we already have keystore Nigori locally.
// We should notify observers about encryption state changes and cryptographer
// shouldn't be ready (by having pending keys) until user provides the
// passphrase.
TEST_F(NigoriSyncBridgeImplTest, ShouldTransitToCustomPassphrase) {}

// Tests that bridge doesn't try to overwrite unknown passphrase type and
// report ModelError unless it received default Nigori node (which is
// determined by the size of encryption_keybag). It's a requirement because
// receiving unknown passphrase type might mean that some newer client switched
// to the new passphrase type.
TEST_F(NigoriSyncBridgeImplTest, ShouldFailOnUnknownPassprase) {}

// Test emulates remote update in custom passphrase mode, which contains
// |encryption_keybag| encrypted with known key, but without this key inside
// the |encryption_keybag|. This is a protocol violation and bridge should
// return ModelError on such updates.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnCustomPassphraseUpdateWithMissingKeybagDecryptionKey) {}

// Tests that bridge reports error when receiving corrupted NigoriSpecifics
// if decryption happens in SetKeystoreKeys().
TEST_F(NigoriSyncBridgeImplTest, ShouldFailOnInvalidKeystoreDecryption) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldClearDataWhenSyncDisabled) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldClearCrossUserSharingKeysWhenSyncDisabled) {}

// Tests decryption logic for explicit passphrase. In order to check that we're
// able to decrypt the data encrypted with old key (i.e. keystore keys or old
// GAIA passphrase) we add one extra key to the encryption keybag.
TEST_P(NigoriSyncBridgeImplTestWithOptionalScryptDerivation,
       ShouldDecryptWithCustomPassphraseAndUpdateDefaultKey) {}

INSTANTIATE_TEST_SUITE_P();

// Tests custom passphrase setup logic. Initially Nigori node will be
// initialized with keystore Nigori due to sync with default Nigori. After
// SetEncryptionPassphrase() call observers should be notified about state
// changes, custom passphrase Nigori should be put into the processor and
// exposed through GetDataForCommit(), cryptographer should encrypt data with
// custom passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldPutAndNotifyObserversWhenSetEncryptionPassphrase) {}

// Tests that pending local change with setting custom passphrase is applied,
// when there was a conflicting remote update and remote update is respected.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldSetCustomPassphraseAfterConflictingUpdates) {}

// Tests that SetEncryptionPassphrase() call doesn't lead to custom passphrase
// change in case we already have one.
TEST_F(NigoriSyncBridgeImplTest, ShouldNotAllowCustomPassphraseChange) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldRestoreMetadata) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldRestoreKeystoreNigori) {}

// Commit with keystore Nigori initialization might be not completed before
// the browser restart. This test emulates loading non-initialized Nigori
// after restart and expects that bridge will trigger initialization after
// loading.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldInitializeKeystoreNigoriWhenLoadedFromStorage) {}

// Tests the initial sync with a trusted vault Nigori. Observers should be
// notified about encryption state changes and cryptographer shouldn't be ready
// (by having pending keys) until the passphrase is received by means other than
// the sync protocol.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldRequireUserActionIfInitiallyUsingTrustedVault) {}

// Tests the processing of a remote incremental update that transitions from
// keystore to trusted vault passphrase, which requires receiving the new
// passphrase by means other than the sync protocol.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldProcessRemoteTransitionFromKeystoreToTrustedVault) {}

// Tests the processing of a remote incremental update that rotates the trusted
// vault passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldProcessRemoteKeyRotationForTrustedVault) {}

// Tests transitioning locally from trusted vault passphrase to custom
// passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldTransitionLocallyFromTrustedVaultToCustomPassphrase) {}

// Tests processing of remote incremental update that transits from trusted
// vault to keystore passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldProcessRemoteTransitionFromTrustedVaultToKeystore) {}

// Tests processing of remote incremental update that transits from trusted
// vault to custom passphrase.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldProcessRemoteTransitionFromTrustedVaultToCustomPassphrase) {}

// Tests processing of remote incremental update that transits from trusted
// vault to keystore passphrase, which doesn't contain trusted vault key. The
// bridge should report model error.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnInvalidRemoteTransitionFromTrustedVaultToKeystore) {}

// Tests processing of remote incremental update that transits from trusted
// vault to custom passphrase, which doesn't contain trusted vault key. The
// bridge should report model error.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnInvalidRemoteTransitionFromTrustedVaultToCustomPassphrase) {}

// Tests processing of remote incremental update that transits from trusted
// vault to custom passphrase, which doesn't contain trusted vault key. Mimics
// browser restart in between of receiving the remote update and providing
// custom passphrase. The bridge should report model error.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnInvalidRemoteTransitionFromTrustedVaultAfterRestart) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldNotAddDecryptionKeysToTrustedVaultCryptographer) {}

// Tests that upon startup bridge migrates the Nigori from backward compatible
// keystore mode to full keystore mode.
TEST_F(NigoriSyncBridgeImplTest, ShouldCompleteKeystoreMigration) {}

// Tests that upon startup bridge adds keystore keys into cryptographer, so it
// can later decrypt the data using them.
TEST_F(NigoriSyncBridgeImplTest, ShouldDecryptWithKeystoreKeysAfterRestart) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldRestoreTrustedVaultNigori) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldRestoreTrustedVaultNigoriWithPendingKeys) {}

// Tests that the initial built keystore Nigori, includes initialized
// Public-private key-pairs.
TEST_F(NigoriSyncBridgeImplTest, ShouldInitKeystoreNigoriWithKeyPair) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnDifferentKeyInitializingKeystoreNigoriWithKeyPair) {}

// Tests that an existing Nigori will be initialized with Public-private
// key-pairs.
TEST_F(NigoriSyncBridgeImplTest, ShouldInitKeyPairForExistingNigori) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldFailOnDifferentNigoriKeyInitializingKeyPairForExistingNigori) {}

TEST_F(NigoriSyncBridgeImplTest, ShouldRegenerateKeyPairIfCorrupted) {}

// Regression test for crbug.com/329164040: stored local data suggests that
// initial sync was not done (due to data corruption or missing migration),
// the bridge should drop local data and perform initial sync once again.
// Main expectation is absence of crash.
TEST_F(NigoriSyncBridgeImplTest, ShouldIgnoreLocalDataWithoutInitialSyncDone) {}

// The only legit scenario when UNKNOWN passphrase type gets persisted is when
// keystore keys are present, but commit wasn't completed before browser
// restart. Otherwise it indicates data corruption and it is safer to ignore
// such state.
TEST_F(NigoriSyncBridgeImplTest,
       ShouldIgnoreLocalDataWithUnknownPassphraseWithoutKeystoreKeys) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldIgnoreLocalDataWithCustomPassphraseWithoutKeyDerivationParams) {}

TEST_F(NigoriSyncBridgeImplTest,
       ShouldIgnoreLocalDataWithRealPassphraseTypeWithoutEncryptionKeys) {}

}  // namespace

}  // namespace syncer