Commit d66b8119 authored by Maksim Moskvitin's avatar Maksim Moskvitin Committed by Commit Bot

[Sync:USS] Implement SerializeAsNigoriLocalData()

SerializeAsNigoriLocalData() converts state of the Nigori's bridge and
processor (i.e. sync metadata) to NigoriLocalData proto, that will be
used for persistence.

Side changes: Cryptographer interface is extended by adding ToProto()
method, NigoriLocalData |keystore_keys| field renamed to
|keystore_key| (singular form fits better for repeated proto fields).

Bug: 922900
Change-Id: I9535008f221b755a389a6ca74c6d0593304ee3a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1768325Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Commit-Queue: Maksim Moskvitin <mmoskvitin@google.com>
Cr-Commit-Position: refs/heads/master@{#692609}
parent 26ace7fb
......@@ -24,6 +24,11 @@ KeyParams::KeyParams(const KeyParams& other) = default;
KeyParams::KeyParams(KeyParams&& other) = default;
KeyParams::~KeyParams() = default;
CryptographerDataWithPendingKeys::CryptographerDataWithPendingKeys() = default;
CryptographerDataWithPendingKeys::CryptographerDataWithPendingKeys(
CryptographerDataWithPendingKeys&& other) = default;
CryptographerDataWithPendingKeys::~CryptographerDataWithPendingKeys() = default;
Cryptographer::Cryptographer() : key_bag_(NigoriKeyBag::CreateEmpty()) {}
Cryptographer::Cryptographer(const Cryptographer& other)
......@@ -46,6 +51,17 @@ void Cryptographer::CopyFrom(const Cryptographer& other) {
}
}
CryptographerDataWithPendingKeys
Cryptographer::ToCryptographerDataWithPendingKeys() const {
CryptographerDataWithPendingKeys output;
*output.cryptographer_data.mutable_key_bag() = key_bag_.ToProto();
output.cryptographer_data.set_default_key_name(default_nigori_name_);
if (pending_keys_) {
output.pending_keys = *pending_keys_;
}
return output;
}
void Cryptographer::Bootstrap(const Encryptor& encryptor,
const std::string& restored_bootstrap_token) {
if (is_initialized()) {
......
......@@ -10,10 +10,12 @@
#include <string>
#include "base/macros.h"
#include "base/optional.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/nigori/nigori.h"
#include "components/sync/nigori/nigori_key_bag.h"
#include "components/sync/protocol/encryption.pb.h"
#include "components/sync/protocol/nigori_local_data.pb.h"
namespace sync_pb {
class NigoriKeyBag;
......@@ -36,6 +38,18 @@ struct KeyParams {
std::string password;
};
struct CryptographerDataWithPendingKeys {
CryptographerDataWithPendingKeys();
CryptographerDataWithPendingKeys(CryptographerDataWithPendingKeys&& other);
~CryptographerDataWithPendingKeys();
sync_pb::CryptographerData cryptographer_data;
base::Optional<sync_pb::EncryptedData> pending_keys;
private:
DISALLOW_COPY_AND_ASSIGN(CryptographerDataWithPendingKeys);
};
// This class manages the Nigori objects used to encrypt and decrypt sensitive
// sync data (eg. passwords). Each Nigori object knows how to handle data
// protected with a particular passphrase.
......@@ -58,6 +72,9 @@ class Cryptographer {
void CopyFrom(const Cryptographer& other);
// Serialization.
CryptographerDataWithPendingKeys ToCryptographerDataWithPendingKeys() const;
// |restored_bootstrap_token| can be provided via this method to bootstrap
// Cryptographer instance into the ready state (is_ready will be true).
// It must be a string that was previously built by the
......
......@@ -6,6 +6,7 @@
#include "base/strings/string_util.h"
#include "components/sync/base/fake_encryptor.h"
#include "components/sync/protocol/nigori_local_data.pb.h"
#include "components/sync/protocol/password_specifics.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -14,7 +15,10 @@ namespace syncer {
namespace {
using ::testing::_;
using testing::_;
using testing::Eq;
using testing::Ne;
using testing::SizeIs;
} // namespace
......@@ -301,4 +305,27 @@ TEST_F(CryptographerTest, GetKeysThenInstall) {
EXPECT_TRUE(another_cryptographer.CanDecrypt(encrypted_k2));
}
TEST_F(CryptographerTest, ShouldConvertToCryptographerDataWithPendingKeys) {
const KeyParams kKeyParams = {KeyDerivationParams::CreateForPbkdf2(),
"password1"};
ASSERT_TRUE(cryptographer_.AddKey(kKeyParams));
CryptographerDataWithPendingKeys serialized =
cryptographer_.ToCryptographerDataWithPendingKeys();
EXPECT_THAT(serialized.cryptographer_data.key_bag().key(), SizeIs(1));
std::string expected_key_name;
Nigori::CreateByDerivation(kKeyParams.derivation_params, kKeyParams.password)
->Permute(Nigori::Password, kNigoriKeyName, &expected_key_name);
EXPECT_THAT(serialized.cryptographer_data.default_key_name(),
Eq(expected_key_name));
EXPECT_THAT(serialized.cryptographer_data.key_bag().key(0).name(),
Eq(expected_key_name));
EXPECT_THAT(serialized.cryptographer_data.key_bag().key(0).user_key(),
Ne(""));
EXPECT_THAT(serialized.cryptographer_data.key_bag().key(0).encryption_key(),
Ne(""));
EXPECT_THAT(serialized.cryptographer_data.key_bag().key(0).mac_key(), Ne(""));
}
} // namespace syncer
......@@ -15,6 +15,7 @@
#include "components/sync/model/entity_data.h"
#include "components/sync/nigori/nigori.h"
#include "components/sync/protocol/encryption.pb.h"
#include "components/sync/protocol/nigori_local_data.pb.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
namespace syncer {
......@@ -396,6 +397,31 @@ bool CanDecryptWithSerializedNigoriKey(
return cryptographer.CanDecrypt(encrypted_data);
}
std::string ComputePbkdf2KeyName(const std::string& password) {
std::string key_name;
Nigori::CreateByDerivation(KeyDerivationParams::CreateForPbkdf2(), password)
->Permute(Nigori::Password, kNigoriKeyName, &key_name);
return key_name;
}
sync_pb::CustomPassphraseKeyDerivationParams
CustomPassphraseKeyDerivationParamsToProto(const KeyDerivationParams& params) {
sync_pb::CustomPassphraseKeyDerivationParams output;
output.set_custom_passphrase_key_derivation_method(
EnumKeyDerivationMethodToProto(params.method()));
if (params.method() == KeyDerivationMethod::SCRYPT_8192_8_11) {
output.set_custom_passphrase_key_derivation_salt(params.scrypt_salt());
}
return output;
}
ModelTypeSet GetEncryptedTypes(bool encrypt_everything) {
if (encrypt_everything) {
return EncryptableUserTypes();
}
return SyncEncryptionHandler::SensitiveTypes();
}
} // namespace
NigoriSyncBridgeImpl::NigoriSyncBridgeImpl(
......@@ -434,14 +460,9 @@ bool NigoriSyncBridgeImpl::Init() {
// TODO(crbug.com/922900): try to avoid double notification. Currently it
// happens iff we received explicit passphrase Nigori during the first
// sync cycle, and more complicated once we persist the local state.
ModelTypeSet encrypted_types;
if (encrypt_everything_) {
encrypted_types = EncryptableUserTypes();
} else {
encrypted_types = SensitiveTypes();
}
for (auto& observer : observers_) {
observer.OnEncryptedTypesChanged(encrypted_types, encrypt_everything_);
observer.OnEncryptedTypesChanged(GetEncryptedTypes(encrypt_everything_),
encrypt_everything_);
}
NOTIMPLEMENTED();
// TODO(crbug.com/922900): notify observers about cryptographer change in
......@@ -923,4 +944,59 @@ void NigoriSyncBridgeImpl::MaybeNotifyBootstrapTokenUpdated() const {
}
}
sync_pb::NigoriLocalData NigoriSyncBridgeImpl::SerializeAsNigoriLocalData()
const {
sync_pb::NigoriLocalData output;
// Serialize the metadata.
const NigoriMetadataBatch metadata_batch = processor_->GetMetadata();
*output.mutable_model_type_state() = metadata_batch.model_type_state;
if (metadata_batch.entity_metadata) {
*output.mutable_entity_metadata() = *metadata_batch.entity_metadata;
}
// Serialize the data.
sync_pb::NigoriModel* nigori_model = output.mutable_nigori_model();
CryptographerDataWithPendingKeys serialized_cryptographer =
cryptographer_.ToCryptographerDataWithPendingKeys();
*nigori_model->mutable_cryptographer_data() =
serialized_cryptographer.cryptographer_data;
if (serialized_cryptographer.pending_keys.has_value()) {
*nigori_model->mutable_pending_keys() =
*serialized_cryptographer.pending_keys;
}
if (!keystore_keys_.empty()) {
nigori_model->set_current_keystore_key_name(
ComputePbkdf2KeyName(keystore_keys_.back()));
}
nigori_model->set_passphrase_type(passphrase_type_);
if (!keystore_migration_time_.is_null()) {
nigori_model->set_keystore_migration_time(
TimeToProtoTime(keystore_migration_time_));
}
if (!custom_passphrase_time_.is_null()) {
nigori_model->set_custom_passphrase_time(
TimeToProtoTime(custom_passphrase_time_));
}
if (custom_passphrase_key_derivation_params_) {
*nigori_model->mutable_custom_passphrase_key_derivation_params() =
CustomPassphraseKeyDerivationParamsToProto(
*custom_passphrase_key_derivation_params_);
}
nigori_model->set_encrypt_everything(encrypt_everything_);
for (ModelType model_type : GetEncryptedTypes(encrypt_everything_)) {
nigori_model->add_encrypted_types_specifics_field_number(
GetSpecificsFieldNumberFromModelType(model_type));
}
// TODO(crbug.com/970213): we currently store keystore keys in proto only to
// allow rollback of USS Nigori. Having keybag with all keystore keys and
// |current_keystore_key_name| is enough to support all bridge logic. We
// should remove them few milestones after USS migration completed.
for (const std::string& keystore_key : keystore_keys_) {
nigori_model->add_keystore_key(keystore_key);
}
return output;
}
} // namespace syncer
......@@ -22,6 +22,10 @@
#include "components/sync/nigori/nigori_local_change_processor.h"
#include "components/sync/nigori/nigori_sync_bridge.h"
namespace sync_pb {
class NigoriLocalData;
} // namespace sync_pb
namespace syncer {
class Encryptor;
......@@ -100,6 +104,9 @@ class NigoriSyncBridgeImpl : public KeystoreKeysHandler,
// just won't be updated.
void MaybeNotifyBootstrapTokenUpdated() const;
// Serializes state of the bridge and sync metadata into the proto.
sync_pb::NigoriLocalData SerializeAsNigoriLocalData() const;
const Encryptor* const encryptor_;
const std::unique_ptr<NigoriLocalChangeProcessor> processor_;
......
......@@ -79,13 +79,13 @@ message NigoriModel {
// The list of encrypted UserEncryptableTypes, represented by their specifics
// field number.
repeated int32 encrypted_types_specifics_field_numbers = 9;
repeated int32 encrypted_types_specifics_field_number = 9;
// Keystore keys are used to decrypt keystore-based Nigori. Should be
// persisted in order to not ask the keystore server for them during every
// browser startup. Due to backward compatibility requirements keys are
// always Base64 encoded.
repeated string keystore_keys = 10;
repeated string keystore_key = 10;
}
// Sync proto to store Nigori data in storage. Proto should be encrypted with
......
......@@ -640,8 +640,8 @@ VISIT_PROTO_FIELDS(const sync_pb::NigoriModel& proto) {
VISIT(custom_passphrase_time);
VISIT(custom_passphrase_key_derivation_params);
VISIT(encrypt_everything);
VISIT_REP(encrypted_types_specifics_field_numbers);
VISIT_REP(keystore_keys);
VISIT_REP(encrypted_types_specifics_field_number);
VISIT_REP(keystore_key);
}
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