blob: 66a1096539961293c3cf6f2fc421e9672125a139 [file] [log] [blame]
// Copyright 2018 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 <string>
#include <vector>
#include "base/base64.h"
#include "chrome/browser/sync/test/integration/encryption_helper.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/base/system_encryptor.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace encryption_helper {
bool GetServerNigori(fake_server::FakeServer* fake_server,
sync_pb::NigoriSpecifics* nigori) {
std::vector<sync_pb::SyncEntity> entity_list =
fake_server->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
if (entity_list.size() != 1U) {
return false;
}
*nigori = entity_list[0].specifics().nigori();
return true;
}
void InitCustomPassphraseCryptographerFromNigori(
const sync_pb::NigoriSpecifics& nigori,
syncer::Cryptographer* cryptographer,
const std::string& passphrase) {
sync_pb::EncryptedData keybag = nigori.encryption_keybag();
cryptographer->SetPendingKeys(keybag);
std::string decoded_salt;
switch (syncer::ProtoKeyDerivationMethodToEnum(
nigori.custom_passphrase_key_derivation_method())) {
case syncer::KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
ASSERT_TRUE(cryptographer->DecryptPendingKeys(
{syncer::KeyDerivationParams::CreateForPbkdf2(), passphrase}));
break;
case syncer::KeyDerivationMethod::SCRYPT_8192_8_11:
ASSERT_TRUE(base::Base64Decode(
nigori.custom_passphrase_key_derivation_salt(), &decoded_salt));
ASSERT_TRUE(cryptographer->DecryptPendingKeys(
{syncer::KeyDerivationParams::CreateForScrypt(decoded_salt),
passphrase}));
break;
case syncer::KeyDerivationMethod::UNSUPPORTED:
// This test cannot pass since we wouldn't know how to decrypt data
// encrypted using an unsupported method.
FAIL() << "Unsupported key derivation method encountered: "
<< nigori.custom_passphrase_key_derivation_method();
}
}
sync_pb::NigoriSpecifics CreateCustomPassphraseNigori(
const syncer::KeyParams& params) {
syncer::KeyDerivationMethod method = params.derivation_params.method();
sync_pb::NigoriSpecifics nigori;
nigori.set_keybag_is_frozen(true);
nigori.set_keystore_migration_time(1U);
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 syncer::KeyDerivationMethod::PBKDF2_HMAC_SHA1_1003:
// Nothing to do; no further information needs to be extracted from
// Nigori.
break;
case syncer::KeyDerivationMethod::SCRYPT_8192_8_11:
base::Base64Encode(params.derivation_params.scrypt_salt(), &encoded_salt);
nigori.set_custom_passphrase_key_derivation_salt(encoded_salt);
break;
case syncer::KeyDerivationMethod::UNSUPPORTED:
ADD_FAILURE()
<< "Unsupported method in KeyParams, cannot construct Nigori.";
break;
}
// Nigori also contains a keybag, which is an encrypted collection of all keys
// that the data might be encrypted with. To create it, we construct a
// cryptographer, add our key to it, and use GetKeys() to dump it to the
// keybag (in encrypted form). So, in our case, the keybag is simply the
// passphrase-derived key encrypted with itself. Note that this is usually
// also the case during normal Sync operation, and so the keybag from Nigori
// only helps the encryption machinery to know if a given key is correct (e.g.
// checking if a user's passphrase is correct is done by trying to decrypt the
// keybag using a key derived from that passphrase). However, in some migrated
// states, the keybag might also additionally contain an old, pre-migration
// key.
syncer::SystemEncryptor encryptor;
syncer::Cryptographer cryptographer(&encryptor);
bool add_key_result = cryptographer.AddKey(params);
DCHECK(add_key_result);
bool get_keys_result =
cryptographer.GetKeys(nigori.mutable_encryption_keybag());
DCHECK(get_keys_result);
return nigori;
}
sync_pb::EntitySpecifics GetEncryptedBookmarkEntitySpecifics(
const sync_pb::BookmarkSpecifics& bookmark_specifics,
const syncer::KeyParams& key_params) {
sync_pb::EntitySpecifics new_specifics;
sync_pb::EntitySpecifics wrapped_entity_specifics;
*wrapped_entity_specifics.mutable_bookmark() = bookmark_specifics;
syncer::SystemEncryptor encryptor;
syncer::Cryptographer cryptographer(&encryptor);
bool add_key_result = cryptographer.AddKey(key_params);
DCHECK(add_key_result);
bool encrypt_result = cryptographer.Encrypt(
wrapped_entity_specifics, new_specifics.mutable_encrypted());
DCHECK(encrypt_result);
new_specifics.mutable_bookmark()->set_title("encrypted");
new_specifics.mutable_bookmark()->set_url("encrypted");
return new_specifics;
}
void SetNigoriInFakeServer(fake_server::FakeServer* fake_server,
const sync_pb::NigoriSpecifics& nigori) {
std::string nigori_entity_id =
fake_server->GetTopLevelPermanentItemId(syncer::NIGORI);
ASSERT_NE(nigori_entity_id, "");
sync_pb::EntitySpecifics nigori_entity_specifics;
*nigori_entity_specifics.mutable_nigori() = nigori;
fake_server->ModifyEntitySpecifics(nigori_entity_id, nigori_entity_specifics);
}
} // namespace encryption_helper
ServerNigoriChecker::ServerNigoriChecker(
browser_sync::ProfileSyncService* service,
fake_server::FakeServer* fake_server,
syncer::PassphraseType expected_passphrase_type)
: SingleClientStatusChangeChecker(service),
fake_server_(fake_server),
expected_passphrase_type_(expected_passphrase_type) {}
bool ServerNigoriChecker::IsExitConditionSatisfied() {
std::vector<sync_pb::SyncEntity> nigori_entities =
fake_server_->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
EXPECT_LE(nigori_entities.size(), 1U);
return !nigori_entities.empty() &&
syncer::ProtoPassphraseTypeToEnum(
nigori_entities[0].specifics().nigori().passphrase_type()) ==
expected_passphrase_type_;
}
std::string ServerNigoriChecker::GetDebugMessage() const {
return "Waiting for a Nigori node with the proper passphrase type to become "
"available on the server.";
}
PassphraseRequiredStateChecker::PassphraseRequiredStateChecker(
browser_sync::ProfileSyncService* service,
bool desired_state)
: SingleClientStatusChangeChecker(service), desired_state_(desired_state) {}
bool PassphraseRequiredStateChecker::IsExitConditionSatisfied() {
return service()->IsPassphraseRequiredForDecryption() == desired_state_;
}
std::string PassphraseRequiredStateChecker::GetDebugMessage() const {
return "Waiting until decryption passphrase is " +
std::string(desired_state_ ? "required" : "not required");
}