Commit 59635dd0 authored by Maksim Moskvitin's avatar Maksim Moskvitin Committed by Commit Bot

[Sync:USS] Support pending keys in keystore mode

In theory, keystore keys might not arrive together with
NigoriSpecifics, for example, in case of throttling. To make USS
implementation more robust in this situation, we allow pending keys in
keystore mode. Cryptographer should be properly initialized once we
receive keystore key, which was used for encryption of
keystore_decryptor_token.

This CL extends pending keys concept to keystore_decryptor_token,
because we need to store it until decryption. Note: Directory
implementation implicitly had this concept, since it stored
NigoriSpecifics itself as the local state.

Note: this CL doesn't add support for initialization of default Nigori
in case bridge has no keystore keys.

Bug: 922900
Change-Id: I9a138d2190e28b617789bb0632d3751fa226d8d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1840694
Commit-Queue: Maksim Moskvitin <mmoskvitin@google.com>
Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#705063}
parent 8ce74234
...@@ -75,6 +75,10 @@ NigoriState NigoriState::CreateFromProto(const sync_pb::NigoriModel& proto) { ...@@ -75,6 +75,10 @@ NigoriState NigoriState::CreateFromProto(const sync_pb::NigoriModel& proto) {
for (int i = 0; i < proto.keystore_key_size(); ++i) { for (int i = 0; i < proto.keystore_key_size(); ++i) {
state.keystore_keys.push_back(proto.keystore_key(i)); state.keystore_keys.push_back(proto.keystore_key(i));
} }
if (proto.has_pending_keystore_decryptor_token()) {
state.pending_keystore_decryptor_token =
proto.pending_keystore_decryptor_token();
}
return state; return state;
} }
...@@ -127,6 +131,10 @@ sync_pb::NigoriModel NigoriState::ToProto() const { ...@@ -127,6 +131,10 @@ sync_pb::NigoriModel NigoriState::ToProto() const {
for (const std::string& keystore_key : keystore_keys) { for (const std::string& keystore_key : keystore_keys) {
proto.add_keystore_key(keystore_key); proto.add_keystore_key(keystore_key);
} }
if (pending_keystore_decryptor_token.has_value()) {
*proto.mutable_pending_keystore_decryptor_token() =
*pending_keystore_decryptor_token;
}
return proto; return proto;
} }
......
...@@ -68,6 +68,11 @@ struct NigoriState { ...@@ -68,6 +68,11 @@ struct NigoriState {
// key. These keys are not a part of Nigori node and are persisted // key. These keys are not a part of Nigori node and are persisted
// separately. Must be encrypted with OSCrypt before persisting. // separately. Must be encrypted with OSCrypt before persisting.
std::vector<std::string> keystore_keys; std::vector<std::string> keystore_keys;
// Represents |keystore_decryptor_token| from NigoriSpecifics in case it
// can't be decrypted right after remote update arrival due to lack of
// keystore keys. May be set only for keystore Nigori.
base::Optional<sync_pb::EncryptedData> pending_keystore_decryptor_token;
}; };
} // namespace syncer } // namespace syncer
......
...@@ -567,6 +567,33 @@ TEST_F(NigoriSyncBridgeImplTest, ...@@ -567,6 +567,33 @@ TEST_F(NigoriSyncBridgeImplTest,
EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams)); EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
} }
// 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) {
const std::string kRawKeystoreKey = "raw_keystore_key";
const KeyParams kKeystoreKeyParams = KeystoreKeyParams(kRawKeystoreKey);
EntityData entity_data;
*entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics(
/*keybag_keys_params=*/{kKeystoreKeyParams},
/*keystore_decryptor_params=*/kKeystoreKeyParams,
/*keystore_key_params=*/kKeystoreKeyParams);
EXPECT_CALL(*observer(), OnCryptographerStateChanged(
NotNull(), /*has_pending_keys=*/true));
EXPECT_THAT(bridge()->MergeSyncData(std::move(entity_data)),
Eq(base::nullopt));
const Cryptographer& cryptographer = bridge()->GetCryptographerForTesting();
EXPECT_FALSE(cryptographer.CanEncrypt());
EXPECT_CALL(*observer(), OnCryptographerStateChanged(
NotNull(), /*has_pending_keys=*/false));
EXPECT_TRUE(bridge()->SetKeystoreKeys({kRawKeystoreKey}));
EXPECT_THAT(cryptographer, CanDecryptWith(kKeystoreKeyParams));
EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
}
// Tests that we can perform initial sync with custom passphrase Nigori. // Tests that we can perform initial sync with custom passphrase Nigori.
// We should notify observers about encryption state changes and cryptographer // We should notify observers about encryption state changes and cryptographer
// shouldn't be ready (by having pending keys) until user provides the // shouldn't be ready (by having pending keys) until user provides the
......
...@@ -86,6 +86,10 @@ message NigoriModel { ...@@ -86,6 +86,10 @@ message NigoriModel {
// browser startup. Due to backward compatibility requirements keys are // browser startup. Due to backward compatibility requirements keys are
// always Base64 encoded. // always Base64 encoded.
repeated string keystore_key = 10; repeated string keystore_key = 10;
// Encryptor keystore decryptor token. Used for decryption of keystore Nigori
// in case keystore keys arrived after NigoriSpecifics.
optional EncryptedData pending_keystore_decryptor_token = 11;
} }
// Sync proto to store Nigori data in storage. Proto should be encrypted with // Sync proto to store Nigori data in storage. Proto should be encrypted with
......
...@@ -607,6 +607,7 @@ VISIT_PROTO_FIELDS(const sync_pb::NigoriModel& proto) { ...@@ -607,6 +607,7 @@ VISIT_PROTO_FIELDS(const sync_pb::NigoriModel& proto) {
VISIT(encrypt_everything); VISIT(encrypt_everything);
VISIT_REP(encrypted_types_specifics_field_number); VISIT_REP(encrypted_types_specifics_field_number);
VISIT_REP(keystore_key); VISIT_REP(keystore_key);
VISIT(pending_keystore_decryptor_token);
} }
VISIT_PROTO_FIELDS(const sync_pb::NigoriLocalData& proto) { VISIT_PROTO_FIELDS(const sync_pb::NigoriLocalData& proto) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment