blob: da7d6c3a64e899ad836e719d7382a3b51a6aeaeb [file] [log] [blame]
// 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/cryptographer_impl.h"
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "components/sync/engine/nigori/cross_user_sharing_public_private_key_pair.h"
#include "components/sync/engine/nigori/key_derivation_params.h"
#include "components/sync/engine/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"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
namespace {
using testing::Eq;
using testing::Ne;
using testing::NotNull;
} // namespace
TEST(CryptographerImplTest, ShouldCreateEmpty) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
EXPECT_FALSE(cryptographer->CanEncrypt());
sync_pb::EncryptedData encrypted;
encrypted.set_key_name("foo");
encrypted.set_blob("bar");
EXPECT_FALSE(cryptographer->CanDecrypt(encrypted));
std::string output;
EXPECT_FALSE(cryptographer->DecryptToString(encrypted, &output));
}
TEST(CryptographerImplTest, ShouldEmplaceKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
ASSERT_FALSE(cryptographer->CanEncrypt());
const std::string key_name = cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
EXPECT_THAT(key_name, Ne(std::string()));
sync_pb::EncryptedData encrypted;
encrypted.set_key_name(key_name);
encrypted.set_blob("fakeblob");
EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
EXPECT_FALSE(cryptographer->CanEncrypt());
}
TEST(CryptographerImplTest, ShouldEmplaceExistingKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
const std::string key_name = cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
ASSERT_THAT(key_name, Ne(std::string()));
EXPECT_THAT(cryptographer->EmplaceKey("password1",
KeyDerivationParams::CreateForPbkdf2()),
Eq(key_name));
}
TEST(CryptographerImplTest, ShouldEmplaceSecondKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
const std::string key_name1 = cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
const std::string key_name2 = cryptographer->EmplaceKey(
"password2", KeyDerivationParams::CreateForPbkdf2());
EXPECT_THAT(key_name1, Ne(std::string()));
EXPECT_THAT(key_name2, Ne(std::string()));
EXPECT_THAT(key_name1, Ne(key_name2));
}
TEST(CryptographerImplTest, ShouldSelectDefaultEncryptionKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
ASSERT_FALSE(cryptographer->CanEncrypt());
const std::string key_name = cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
ASSERT_THAT(key_name, Ne(std::string()));
cryptographer->SelectDefaultEncryptionKey(key_name);
ASSERT_TRUE(cryptographer->CanEncrypt());
sync_pb::EncryptedData encrypted;
EXPECT_TRUE(cryptographer->EncryptString("foo", &encrypted));
EXPECT_THAT(encrypted.key_name(), Eq(key_name));
}
TEST(CryptographerImplTest, ShouldSelectDefaultCrossUserSharingKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 4);
cryptographer->SelectDefaultCrossUserSharingKey(4);
const std::string plaintext = "Sharing is caring";
std::optional<std::vector<uint8_t>> encrypted_message =
cryptographer->AuthEncryptForCrossUserSharing(
base::as_byte_span(plaintext),
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair()
.GetRawPublicKey());
EXPECT_TRUE(encrypted_message.has_value());
}
TEST(CryptographerImplTest, ShouldFailOnNonSetEncryptionKeyPair) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 4);
const std::string plaintext = "Sharing is caring";
std::optional<std::vector<uint8_t>> encrypted_message =
cryptographer->AuthEncryptForCrossUserSharing(
base::as_byte_span(plaintext),
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair()
.GetRawPublicKey());
EXPECT_FALSE(encrypted_message.has_value());
}
TEST(CryptographerImplTest, ShouldFailOnNonExistentDefaultEncryptionKeyPair) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 4);
cryptographer->SelectDefaultCrossUserSharingKey(3);
const std::string plaintext = "Sharing is caring";
std::optional<std::vector<uint8_t>> encrypted_message =
cryptographer->AuthEncryptForCrossUserSharing(
base::as_byte_span(plaintext),
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair()
.GetRawPublicKey());
EXPECT_FALSE(encrypted_message.has_value());
}
TEST(CryptographerImplTest, ShouldSerializeToAndFromProto) {
const std::string kText1 = "foo";
const std::string kText2 = "bar";
std::unique_ptr<CryptographerImpl> original_cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(original_cryptographer, NotNull());
const std::string key_name1 = original_cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
const std::string key_name2 = original_cryptographer->EmplaceKey(
"password2", KeyDerivationParams::CreateForPbkdf2());
original_cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
original_cryptographer->SelectDefaultEncryptionKey(key_name1);
sync_pb::EncryptedData encrypted1;
EXPECT_TRUE(original_cryptographer->EncryptString(kText1, &encrypted1));
original_cryptographer->SelectDefaultEncryptionKey(key_name2);
sync_pb::EncryptedData encrypted2;
EXPECT_TRUE(original_cryptographer->EncryptString(kText2, &encrypted2));
// Restore a new cryptographer from proto.
std::unique_ptr<CryptographerImpl> restored_cryptographer =
CryptographerImpl::FromProto(original_cryptographer->ToProto());
ASSERT_THAT(restored_cryptographer, NotNull());
EXPECT_TRUE(restored_cryptographer->CanEncrypt());
EXPECT_TRUE(restored_cryptographer->HasKeyPair(0));
std::string decrypted;
EXPECT_TRUE(restored_cryptographer->DecryptToString(encrypted1, &decrypted));
EXPECT_THAT(decrypted, Eq(kText1));
EXPECT_TRUE(restored_cryptographer->DecryptToString(encrypted2, &decrypted));
EXPECT_THAT(decrypted, Eq(kText2));
}
TEST(CryptographerImplTest, ShouldExportDefaultKey) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
const std::string key_name = cryptographer->EmplaceKey(
"password1", KeyDerivationParams::CreateForPbkdf2());
ASSERT_THAT(key_name, Ne(std::string()));
cryptographer->SelectDefaultEncryptionKey(key_name);
ASSERT_TRUE(cryptographer->CanEncrypt());
sync_pb::NigoriKey exported_key = cryptographer->ExportDefaultKey();
EXPECT_FALSE(exported_key.has_deprecated_name());
// The exported key, even without name, should be importable, and the
// resulting key name should match the original.
EXPECT_THAT(NigoriKeyBag::CreateEmpty().AddKeyFromProto(exported_key),
Eq(key_name));
}
TEST(CryptographerImplTest, ShouldSetKeyPair) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
std::optional<CrossUserSharingPublicPrivateKeyPair> key_pair =
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair();
ASSERT_TRUE(key_pair.has_value());
ASSERT_FALSE(cryptographer->HasKeyPair(0));
cryptographer->SetKeyPair(std::move(key_pair.value()), 0);
EXPECT_TRUE(cryptographer->HasKeyPair(0));
}
TEST(CryptographerImplTest, ShouldEmplaceKeysFrom) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
const std::string key_name_1 = key_bag.AddKey(Nigori::CreateByDerivation(
KeyDerivationParams::CreateForPbkdf2(), "password1"));
const std::string key_name_2 = key_bag.AddKey(Nigori::CreateByDerivation(
KeyDerivationParams::CreateForPbkdf2(), "password2"));
ASSERT_FALSE(cryptographer->HasKey(key_name_1));
ASSERT_FALSE(cryptographer->HasKey(key_name_2));
cryptographer->EmplaceKeysFrom(key_bag);
EXPECT_TRUE(cryptographer->HasKey(key_name_1));
EXPECT_TRUE(cryptographer->HasKey(key_name_2));
}
TEST(CryptographerImplTest, ShouldEmplaceExistingKeyPair) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
ASSERT_FALSE(cryptographer->HasKeyPair(0));
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
ASSERT_TRUE(cryptographer->HasKeyPair(0));
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
EXPECT_TRUE(cryptographer->HasKeyPair(0));
}
TEST(CryptographerImplTest, ShouldReplaceCrossUserSharingKeys) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
ASSERT_FALSE(cryptographer->HasKeyPair(0));
CrossUserSharingKeys keys = CrossUserSharingKeys::CreateEmpty();
keys.SetKeyPair(CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(),
0);
keys.SetKeyPair(CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(),
1);
cryptographer->ReplaceCrossUserSharingKeys(std::move(keys));
EXPECT_TRUE(cryptographer->HasKeyPair(0));
EXPECT_TRUE(cryptographer->HasKeyPair(1));
}
TEST(CryptographerImplTest, ShouldOverwritePreexistingKeys) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
CrossUserSharingKeys old_keys = CrossUserSharingKeys::CreateEmpty();
old_keys.SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(),
/*version=*/0);
old_keys.SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(),
/*version=*/1);
cryptographer->ReplaceCrossUserSharingKeys(old_keys.Clone());
ASSERT_TRUE(cryptographer->HasKeyPair(/*key_pair_version=*/0));
ASSERT_TRUE(cryptographer->HasKeyPair(/*key_pair_version=*/1));
// Generate a new key pair and replace the pre-existing one with the same
// version. The version 1 should also disappear.
CrossUserSharingKeys new_keys = CrossUserSharingKeys::CreateEmpty();
new_keys.SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(),
/*version=*/0);
cryptographer->ReplaceCrossUserSharingKeys(new_keys.Clone());
ASSERT_TRUE(cryptographer->HasKeyPair(/*key_pair_version=*/0));
ASSERT_FALSE(cryptographer->HasKeyPair(/*key_pair_version=*/1));
EXPECT_EQ(cryptographer->GetCrossUserSharingKeyPair(/*version=*/0)
.GetRawPrivateKey(),
new_keys.GetKeyPair(/*version=*/0).GetRawPrivateKey());
EXPECT_NE(cryptographer->GetCrossUserSharingKeyPair(/*version=*/0)
.GetRawPrivateKey(),
old_keys.GetKeyPair(/*version=*/0).GetRawPrivateKey());
}
TEST(CryptographerImplTest, ShouldOverwriteOnlyOneKeyPair) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 1);
// Replace only one key with a new value.
auto raw_existing_private_key0 =
cryptographer->GetCrossUserSharingKeyPair(/*version=*/0)
.GetRawPrivateKey();
auto raw_existing_private_key1 =
cryptographer->GetCrossUserSharingKeyPair(/*version=*/1)
.GetRawPrivateKey();
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
EXPECT_NE(raw_existing_private_key0,
cryptographer->GetCrossUserSharingKeyPair(/*version=*/0)
.GetRawPrivateKey());
EXPECT_EQ(raw_existing_private_key1,
cryptographer->GetCrossUserSharingKeyPair(/*version=*/1)
.GetRawPrivateKey());
}
TEST(CryptographerImplTest, ShouldEncryptAndDecryptForCrossUserSharing) {
std::unique_ptr<CryptographerImpl> cryptographer_sender =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer_sender, NotNull());
cryptographer_sender->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
cryptographer_sender->SelectDefaultCrossUserSharingKey(0);
std::unique_ptr<CryptographerImpl> cryptographer_recipient =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer_recipient, NotNull());
cryptographer_recipient->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
cryptographer_recipient->SelectDefaultCrossUserSharingKey(0);
const std::string plaintext = "Sharing is caring";
std::optional<std::vector<uint8_t>> encrypted_message =
cryptographer_sender->AuthEncryptForCrossUserSharing(
base::as_byte_span(plaintext),
cryptographer_recipient->GetCrossUserSharingKeyPair(0)
.GetRawPublicKey());
EXPECT_TRUE(encrypted_message.has_value());
std::optional<std::vector<uint8_t>> decrypted_message =
cryptographer_recipient->AuthDecryptForCrossUserSharing(
encrypted_message.value(),
cryptographer_sender->GetCrossUserSharingKeyPair(0).GetRawPublicKey(),
0);
EXPECT_TRUE(decrypted_message.has_value());
EXPECT_THAT(decrypted_message.value(), testing::ElementsAreArray(plaintext));
}
TEST(CryptographerImplTest, ShouldClearCrossUserSharingKeys) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 0);
cryptographer->SetKeyPair(
CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair(), 1);
cryptographer->ClearAllKeys();
EXPECT_FALSE(cryptographer->HasKeyPair(0));
EXPECT_FALSE(cryptographer->HasKeyPair(1));
}
TEST(CryptographerImplTest, ShouldEmplaceAllNigoriKeysFrom) {
std::unique_ptr<CryptographerImpl> cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(cryptographer, NotNull());
ASSERT_FALSE(cryptographer->CanEncrypt());
const std::string key_name = cryptographer->EmplaceKey(
"password_local", KeyDerivationParams::CreateForPbkdf2());
ASSERT_THAT(key_name, Ne(std::string()));
cryptographer->SelectDefaultEncryptionKey(key_name);
ASSERT_TRUE(cryptographer->CanEncrypt());
std::unique_ptr<CryptographerImpl> other_cryptographer =
CryptographerImpl::CreateEmpty();
ASSERT_THAT(other_cryptographer, NotNull());
ASSERT_FALSE(other_cryptographer->CanEncrypt());
const std::string key_name_other = other_cryptographer->EmplaceKey(
"password_other", KeyDerivationParams::CreateForPbkdf2());
ASSERT_THAT(key_name_other, Ne(std::string()));
other_cryptographer->SelectDefaultEncryptionKey(key_name_other);
ASSERT_TRUE(other_cryptographer->CanEncrypt());
cryptographer->EmplaceAllNigoriKeysFrom(*other_cryptographer);
EXPECT_TRUE(cryptographer->HasKey(key_name));
EXPECT_TRUE(cryptographer->HasKey(key_name_other));
}
} // namespace syncer