blob: 272a4ed664a741e91c870227f954d3ebb5336101 [file] [log] [blame]
// Copyright 2020 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/test/nigori_test_utils.h"
#include "base/base64.h"
#include "base/check.h"
#include "base/ranges/algorithm.h"
#include "components/sync/base/time.h"
#include "components/sync/engine/nigori/cross_user_sharing_public_key.h"
#include "components/sync/engine/nigori/key_derivation_params.h"
#include "components/sync/engine/nigori/nigori.h"
#include "components/sync/nigori/cross_user_sharing_keys.h"
#include "components/sync/nigori/cryptographer_impl.h"
#include "components/sync/nigori/nigori_key_bag.h"
#include "components/sync/protocol/bookmark_specifics.pb.h"
#include "components/sync/protocol/encryption.pb.h"
#include "components/sync/protocol/entity_specifics.pb.h"
#include "components/sync/protocol/nigori_local_data.pb.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
sync_pb::CrossUserSharingPublicKey PublicKeyToProto(
const syncer::CrossUserSharingPublicKey& public_key,
uint32_t key_pair_version) {
sync_pb::CrossUserSharingPublicKey output;
const auto key = public_key.GetRawPublicKey();
output.set_x25519_public_key(std::string(key.begin(), key.end()));
output.set_version(key_pair_version);
return output;
}
} // namespace
namespace syncer {
KeyParamsForTesting KeystoreKeyParamsForTesting(
const std::vector<uint8_t>& raw_key) {
return Pbkdf2PassphraseKeyParamsForTesting(base::Base64Encode(raw_key));
}
KeyParamsForTesting TrustedVaultKeyParamsForTesting(
const std::vector<uint8_t>& raw_key) {
return Pbkdf2PassphraseKeyParamsForTesting(base::Base64Encode(raw_key));
}
KeyParamsForTesting Pbkdf2PassphraseKeyParamsForTesting(
const std::string& passphrase) {
return {KeyDerivationParams::CreateForPbkdf2(), passphrase};
}
KeyParamsForTesting ScryptPassphraseKeyParamsForTesting(
const std::string& passphrase) {
return {KeyDerivationParams::CreateForScrypt(passphrase), passphrase};
}
sync_pb::CrossUserSharingPublicKey CrossUserSharingKeysToPublicKeyProto(
const CrossUserSharingKeys& cross_user_sharing_keys,
size_t key_version) {
sync_pb::CrossUserSharingPublicKey public_key;
auto raw_public_key =
cross_user_sharing_keys.GetKeyPair(key_version).GetRawPublicKey();
public_key.set_x25519_public_key(
std::string(raw_public_key.begin(), raw_public_key.end()));
public_key.set_version(key_version);
return public_key;
}
sync_pb::NigoriSpecifics BuildKeystoreNigoriSpecifics(
const std::vector<KeyParamsForTesting>& keybag_keys_params,
const KeyParamsForTesting& keystore_decryptor_params,
const KeyParamsForTesting& keystore_key_params,
const CrossUserSharingKeys& cross_user_sharing_keys) {
DCHECK(!keybag_keys_params.empty());
sync_pb::NigoriSpecifics specifics;
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::FromSingleKeyForTesting(
keystore_decryptor_params.password,
keystore_decryptor_params.derivation_params);
NigoriKeyBag encryption_keybag = NigoriKeyBag::CreateEmpty();
for (const KeyParamsForTesting& key_params : keybag_keys_params) {
encryption_keybag.AddKey(Nigori::CreateByDerivation(
key_params.derivation_params, key_params.password));
}
sync_pb::EncryptionKeys keys_for_encryption;
keys_for_encryption.mutable_key()->CopyFrom(
encryption_keybag.ToProto().key());
keys_for_encryption.mutable_cross_user_sharing_private_key()->CopyFrom(
cross_user_sharing_keys.ToProto().private_key());
EXPECT_TRUE(cryptographer->Encrypt(keys_for_encryption,
specifics.mutable_encryption_keybag()));
std::string serialized_keystore_decryptor =
cryptographer->ExportDefaultKey().SerializeAsString();
std::unique_ptr<CryptographerImpl> keystore_cryptographer =
CryptographerImpl::FromSingleKeyForTesting(
keystore_key_params.password, keystore_key_params.derivation_params);
EXPECT_TRUE(keystore_cryptographer->EncryptString(
serialized_keystore_decryptor,
specifics.mutable_keystore_decryptor_token()));
if (cross_user_sharing_keys.HasKeyPair(/*key_version=*/0)) {
specifics.mutable_cross_user_sharing_public_key()->CopyFrom(
CrossUserSharingKeysToPublicKeyProto(cross_user_sharing_keys,
/*key_version=*/0));
}
specifics.set_passphrase_type(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
specifics.set_keystore_migration_time(TimeToProtoTime(base::Time::Now()));
return specifics;
}
sync_pb::NigoriSpecifics BuildKeystoreNigoriSpecificsWithCrossUserSharingKeys(
const std::vector<KeyParamsForTesting>& keybag_keys_params,
const KeyParamsForTesting& keystore_decryptor_params,
const KeyParamsForTesting& keystore_key_params,
const CrossUserSharingKeys& cross_user_sharing_keys,
const CrossUserSharingPublicKey& cross_user_sharing_public_key,
const uint32_t cross_user_sharing_public_key_version) {
sync_pb::NigoriSpecifics specifics = BuildKeystoreNigoriSpecifics(
keybag_keys_params, keystore_decryptor_params, keystore_key_params,
cross_user_sharing_keys);
*specifics.mutable_cross_user_sharing_public_key() = PublicKeyToProto(
cross_user_sharing_public_key, cross_user_sharing_public_key_version);
return specifics;
}
sync_pb::NigoriSpecifics BuildTrustedVaultNigoriSpecifics(
const std::vector<std::vector<uint8_t>>& trusted_vault_keys,
base::Time migration_time) {
sync_pb::NigoriSpecifics specifics;
specifics.set_passphrase_type(
sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE);
specifics.set_keybag_is_frozen(true);
std::unique_ptr<syncer::CryptographerImpl> cryptographer =
syncer::CryptographerImpl::CreateEmpty();
for (const std::vector<uint8_t>& trusted_vault_key : trusted_vault_keys) {
const std::string key_name = cryptographer->EmplaceKey(
base::Base64Encode(trusted_vault_key),
syncer::KeyDerivationParams::CreateForPbkdf2());
cryptographer->SelectDefaultEncryptionKey(key_name);
}
specifics.mutable_trusted_vault_debug_info()->set_migration_time(
TimeToProtoTime(migration_time));
specifics.mutable_trusted_vault_debug_info()->set_key_version(2);
EXPECT_TRUE(cryptographer->Encrypt(cryptographer->ToProto().key_bag(),
specifics.mutable_encryption_keybag()));
return specifics;
}
sync_pb::NigoriSpecifics BuildCustomPassphraseNigoriSpecifics(
const KeyParamsForTesting& passphrase_key_params,
const std::optional<KeyParamsForTesting>& old_key_params) {
KeyDerivationMethod method = passphrase_key_params.derivation_params.method();
sync_pb::NigoriSpecifics nigori;
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(1U);
nigori.set_custom_passphrase_time(2U);
nigori.set_encrypt_everything(true);
nigori.set_passphrase_type(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
nigori.set_custom_passphrase_key_derivation_method(
EnumKeyDerivationMethodToProto(method));
std::string encoded_salt;
switch (method) {
case KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
// Nothing to do; no further information needs to be extracted from
// Nigori.
break;
case KeyDerivationMethod::SCRYPT_8192_8_11:
encoded_salt = base::Base64Encode(
passphrase_key_params.derivation_params.scrypt_salt());
nigori.set_custom_passphrase_key_derivation_salt(encoded_salt);
break;
}
// Create the cryptographer, which encrypts with the key derived from
// |passphrase_key_params| and can decrypt with the key derived from
// |old_key_params| if given. |encryption_keybag| is a serialized version
// of this cryptographer |key_bag| encrypted with its encryption key.
auto cryptographer = CryptographerImpl::FromSingleKeyForTesting(
passphrase_key_params.password, passphrase_key_params.derivation_params);
if (old_key_params) {
cryptographer->EmplaceKey(old_key_params->password,
old_key_params->derivation_params);
}
sync_pb::CryptographerData proto = cryptographer->ToProto();
bool encrypt_result = cryptographer->Encrypt(
proto.key_bag(), nigori.mutable_encryption_keybag());
DCHECK(encrypt_result);
return nigori;
}
KeyDerivationParams InitCustomPassphraseKeyDerivationParamsFromNigori(
const sync_pb::NigoriSpecifics& nigori) {
std::optional<KeyDerivationMethod> key_derivation_method =
ProtoKeyDerivationMethodToEnum(
nigori.custom_passphrase_key_derivation_method());
if (!key_derivation_method.has_value()) {
// The test cannot pass since we wouldn't know how to decrypt data encrypted
// using an unsupported method.
ADD_FAILURE() << "Unsupported key derivation method encountered: "
<< nigori.custom_passphrase_key_derivation_method();
return KeyDerivationParams::CreateForPbkdf2();
}
switch (*key_derivation_method) {
case KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
return KeyDerivationParams::CreateForPbkdf2();
case KeyDerivationMethod::SCRYPT_8192_8_11:
std::string decoded_salt;
EXPECT_TRUE(base::Base64Decode(
nigori.custom_passphrase_key_derivation_salt(), &decoded_salt));
return KeyDerivationParams::CreateForScrypt(decoded_salt);
}
}
std::unique_ptr<Cryptographer> InitCustomPassphraseCryptographerFromNigori(
const sync_pb::NigoriSpecifics& nigori,
const std::string& passphrase) {
KeyDerivationParams key_derivation_params =
InitCustomPassphraseKeyDerivationParamsFromNigori(nigori);
auto cryptographer = CryptographerImpl::FromSingleKeyForTesting(
passphrase, key_derivation_params);
std::string decrypted_keys_str;
EXPECT_TRUE(cryptographer->DecryptToString(nigori.encryption_keybag(),
&decrypted_keys_str));
sync_pb::EncryptionKeys decrypted_keys;
EXPECT_TRUE(decrypted_keys.ParseFromString(decrypted_keys_str));
NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
base::ranges::for_each(
decrypted_keys.key(),
[&key_bag](sync_pb::NigoriKey key) { key_bag.AddKeyFromProto(key); });
cryptographer->EmplaceKeysFrom(key_bag);
return cryptographer;
}
sync_pb::EntitySpecifics GetEncryptedBookmarkEntitySpecifics(
const sync_pb::BookmarkSpecifics& bookmark_specifics,
const KeyParamsForTesting& key_params) {
sync_pb::EntitySpecifics new_specifics;
sync_pb::EntitySpecifics wrapped_entity_specifics;
*wrapped_entity_specifics.mutable_bookmark() = bookmark_specifics;
auto cryptographer = CryptographerImpl::FromSingleKeyForTesting(
key_params.password, key_params.derivation_params);
bool encrypt_result = cryptographer->Encrypt(
wrapped_entity_specifics, new_specifics.mutable_encrypted());
DCHECK(encrypt_result);
new_specifics.mutable_bookmark()->set_legacy_canonicalized_title("encrypted");
new_specifics.mutable_bookmark()->set_url("encrypted");
return new_specifics;
}
} // namespace syncer