blob: 2e14f1e128b1679dd58e626dd829716077061743 [file] [log] [blame]
// Copyright 2023 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/os_crypt/async/browser/os_crypt_async.h"
#include <optional>
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "components/os_crypt/async/browser/key_provider.h"
#include "components/os_crypt/async/browser/test_utils.h"
#include "components/os_crypt/async/common/algorithm.mojom.h"
#include "components/os_crypt/async/common/encryptor.h"
#include "components/os_crypt/sync/os_crypt.h"
#include "components/os_crypt/sync/os_crypt_mocker.h"
#include "crypto/hkdf.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_LINUX)
#include "components/os_crypt/sync/key_storage_linux.h"
#endif
namespace os_crypt_async {
class OSCryptAsyncTest : public ::testing::Test {
protected:
using ProviderList =
std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>>;
Encryptor GetInstanceSync(
OSCryptAsync& factory,
Encryptor::Option option = Encryptor::Option::kNone) {
base::RunLoop run_loop;
std::optional<Encryptor> encryptor;
auto sub =
factory.GetInstance(base::BindLambdaForTesting(
[&](Encryptor encryptor_param, bool success) {
EXPECT_TRUE(success);
encryptor.emplace(std::move(encryptor_param));
run_loop.Quit();
}),
option);
run_loop.Run();
return std::move(*encryptor);
}
// Simulate a 'locked' OSCrypt keychain on platforms that need it, which makes
// OSCrypt::IsEncryptionAvailable return false, without hitting a CHECK on
// Linux. Note this is different from using the full OSCryptMocker, because in
// this state, no key is available for encryption. Returns a
// ScopedClosureRunner that will reset the behavior back to default when it
// goes out of scope.
[[nodiscard]] static std::optional<base::ScopedClosureRunner>
MaybeSimulateLockedKeyChain() {
#if BUILDFLAG(IS_LINUX)
OSCrypt::UseMockKeyStorageForTesting(base::BindOnce(
[]() -> std::unique_ptr<KeyStorageLinux> { return nullptr; }));
return std::nullopt;
#elif BUILDFLAG(IS_APPLE)
OSCrypt::UseLockedMockKeychainForTesting(/*use_locked=*/true);
return base::ScopedClosureRunner(base::BindOnce([]() {
OSCrypt::UseLockedMockKeychainForTesting(/*use_locked=*/false);
}));
#else
return std::nullopt;
#endif
}
base::test::TaskEnvironment task_environment_;
};
class TestKeyProvider : public KeyProvider {
public:
TestKeyProvider(const std::string& name,
bool use_for_encryption,
bool compatible_with_os_crypt_sync = false)
: name_(name),
use_for_encryption_(use_for_encryption),
compatible_with_os_crypt_sync_(compatible_with_os_crypt_sync) {}
protected:
TestKeyProvider()
: name_("TEST"),
use_for_encryption_(true),
compatible_with_os_crypt_sync_(false) {}
Encryptor::Key GenerateKey() {
// Make the key derive from the name to ensure different providers have
// different keys.
std::string key = crypto::HkdfSha256(name_, "salt", "info",
Encryptor::Key::kAES256GCMKeySize);
return Encryptor::Key(std::vector<uint8_t>(key.begin(), key.end()),
mojom::Algorithm::kAES256GCM);
}
const std::string name_;
private:
void GetKey(KeyCallback callback) override {
std::move(callback).Run(name_, GenerateKey());
}
bool UseForEncryption() override { return use_for_encryption_; }
bool IsCompatibleWithOsCryptSync() override {
return compatible_with_os_crypt_sync_;
}
const bool use_for_encryption_;
const bool compatible_with_os_crypt_sync_;
};
TEST_F(OSCryptAsyncTest, EncryptHeader) {
const std::string kTestProviderName("TEST");
ProviderList providers;
providers.emplace_back(
std::make_pair(10u, std::make_unique<TestKeyProvider>(
kTestProviderName, /*use_for_encryption=*/true)));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
auto ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(std::equal(kTestProviderName.cbegin(), kTestProviderName.cend(),
ciphertext->cbegin()));
}
TEST_F(OSCryptAsyncTest, TwoProvidersBothEnabled) {
std::optional<std::vector<uint8_t>> ciphertext;
{
const std::string kFooProviderName("FOO");
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u, std::make_unique<TestKeyProvider>(
kFooProviderName, /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(ciphertext);
// The higher of the two providers should have been picked for data
// encryption.
EXPECT_TRUE(std::equal(kFooProviderName.cbegin(), kFooProviderName.cend(),
ciphertext->cbegin()));
}
// Check that provider precedence does not matter for decrypt.
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
// BAR is the preferred provider since it has the higher precedence.
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
auto plaintext = encryptor.DecryptData(*ciphertext);
// The correct provider based on the encrypted data header should have been
// picked for data decryption.
ASSERT_TRUE(plaintext);
EXPECT_EQ("secrets", *plaintext);
}
// Check that order of providers does not affect which one is chosen for
// encrypt operations.
{
const std::string kFooProviderName("FOO");
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/10u, std::make_unique<TestKeyProvider>(
kFooProviderName, /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(ciphertext);
// The higher of the two providers should have been picked for data
// encryption.
EXPECT_TRUE(std::equal(kFooProviderName.cbegin(), kFooProviderName.cend(),
ciphertext->cbegin()));
}
}
TEST_F(OSCryptAsyncTest, TwoProvidersOneEnabled) {
std::optional<std::vector<uint8_t>> ciphertext;
{
const std::string kBarProviderName("BAR");
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/false));
providers.emplace_back(
/*precedence=*/5u, std::make_unique<TestKeyProvider>(
kBarProviderName, /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(ciphertext);
// Despite FOO being higher than BAR, BAR is chosen for encryption because
// FOO is not enabled for encryption.
EXPECT_TRUE(std::equal(kBarProviderName.cbegin(), kBarProviderName.cend(),
ciphertext->cbegin()));
}
// Check that even with no enabled providers, data can still be decrypted by
// any registered provider.
{
ProviderList providers;
// Neither is enabled for encrypt.
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/false));
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/false));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
auto plaintext = encryptor.DecryptData(*ciphertext);
// The correct provider based on the encrypted data header should have been
// picked for data decryption.
ASSERT_TRUE(plaintext);
EXPECT_EQ("secrets", *plaintext);
}
}
TEST_F(OSCryptAsyncTest, EncryptorOption) {
const std::string kTESTProviderName("TEST");
const std::string kBLAHProviderName("BLAH");
std::optional<std::vector<uint8_t>> blah_ciphertext, test_ciphertext;
{
ProviderList providers;
providers.emplace_back(/*precedence=*/10u,
std::make_unique<TestKeyProvider>(
kTESTProviderName, /*use_for_encryption=*/true));
providers.emplace_back(/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
kBLAHProviderName, /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
test_ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(test_ciphertext);
// TEST should be picked, because it has a higher precedence than BLAH.
EXPECT_TRUE(std::equal(kTESTProviderName.cbegin(), kTESTProviderName.cend(),
test_ciphertext->cbegin()));
// Now obtain an encryptor with a compatibility option.
Encryptor encryptor_compat =
GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
blah_ciphertext = encryptor_compat.EncryptString("secrets");
ASSERT_TRUE(blah_ciphertext);
// Should be encrypted with BLAH now.
EXPECT_TRUE(std::equal(kBLAHProviderName.cbegin(), kBLAHProviderName.cend(),
blah_ciphertext->cbegin()));
}
// Check that with just BLAH provider, data can still be decrypted.
{
auto cleanup = MaybeSimulateLockedKeyChain();
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>(kBLAHProviderName,
/*use_for_encryption=*/false));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
// The only provider has indicated that it is not to be used for encryption,
// so encryption should not be available, as OSCrypt fallback is not
// available.
ASSERT_FALSE(encryptor.IsEncryptionAvailable());
// Decryption is possible, as long as the data is encrypted with the BLAH
// key.
ASSERT_TRUE(encryptor.IsDecryptionAvailable());
auto plaintext = encryptor.DecryptData(*blah_ciphertext);
// The correct provider based on the encrypted data header should have been
// picked for data decryption.
ASSERT_TRUE(plaintext);
EXPECT_EQ("secrets", *plaintext);
// The first data that was encrypted with v20 cannot be decrypted.
auto failing_plaintext = encryptor.DecryptData(*test_ciphertext);
EXPECT_FALSE(failing_plaintext);
}
}
class SlowTestKeyProvider : public TestKeyProvider {
public:
explicit SlowTestKeyProvider(base::TimeDelta sleep_time) {}
private:
void GetKey(KeyCallback callback) override {
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](KeyCallback callback, Encryptor::Key key,
const std::string& name) {
std::move(callback).Run(name, std::move(key));
},
std::move(callback), GenerateKey(), name_),
sleep_time_);
}
const base::TimeDelta sleep_time_;
};
// This test verifies that GetInstanceAsync can correctly handle multiple queued
// requests for an instance for a slow init.
TEST_F(OSCryptAsyncTest, MultipleCalls) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<SlowTestKeyProvider>(base::Seconds(1)));
OSCryptAsync factory(std::move(providers));
size_t calls = 0;
const size_t kExpectedCalls = 10;
base::RunLoop run_loop;
std::list<base::CallbackListSubscription> subs;
for (size_t call = 0; call < kExpectedCalls; call++) {
subs.push_back(factory.GetInstance(base::BindLambdaForTesting(
[&calls, &run_loop](Encryptor encryptor, bool success) {
calls++;
if (calls == kExpectedCalls) {
run_loop.Quit();
}
})));
}
run_loop.Run();
EXPECT_EQ(calls, kExpectedCalls);
}
// This test verifies that if the subscription from CallbackList moves out of
// scope, then the callback never occurs.
TEST_F(OSCryptAsyncTest, SubscriptionCancelled) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<SlowTestKeyProvider>(base::Seconds(1)));
OSCryptAsync factory(std::move(providers));
{
auto sub = factory.GetInstance(
base::BindOnce([](Encryptor encryptor, bool success) {
// This should not be called, as the subscription went out of scope.
NOTREACHED();
}));
}
// Complete the init on a nested RunLoop.
base::RunLoop run_loop;
auto sub = factory.GetInstance(
base::BindLambdaForTesting([&](Encryptor encryptor_param, bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(OSCryptAsyncTest, TestOSCryptAsyncInterface) {
auto os_crypt = GetTestOSCryptAsyncForTesting();
auto encryptor = GetInstanceSync(*os_crypt);
auto ciphertext = encryptor.EncryptString("testsecrets");
ASSERT_TRUE(ciphertext);
{
auto decrypted = encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "testsecrets");
}
{
// Verify that all encryptors returned by the test OSCryptAsync instance use
// the same keys.
auto second_encryptor = GetInstanceSync(*os_crypt);
auto decrypted = second_encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "testsecrets");
}
{
// Verify that the key used by the encryptor returned from the testing
// instance indicates compatibility with OSCrypt Sync. This avoids all tests
// having to install the OSCrypt mocker in every fixture.
const auto os_crypt_compat_encryptor =
GetInstanceSync(*os_crypt, Encryptor::Option::kEncryptSyncCompat);
{
Encryptor::DecryptFlags flags;
const auto decrypted =
os_crypt_compat_encryptor.DecryptData(*ciphertext, &flags);
ASSERT_TRUE(decrypted);
// Switching from kNone to kEncryptSyncCompat should result in the data
// needing to be re-encrypted.
EXPECT_TRUE(flags.should_reencrypt);
EXPECT_EQ(*decrypted, "testsecrets");
}
{
// Encrypt with the OSCrypt Sync compat one, then decrypt with the new
// one, which should signal again that the data needs to be re-encrypted.
const auto os_crypt_compat_ciphertext =
os_crypt_compat_encryptor.EncryptString("moresecret");
ASSERT_TRUE(os_crypt_compat_ciphertext);
Encryptor::DecryptFlags flags;
const auto decrypted =
encryptor.DecryptData(*os_crypt_compat_ciphertext, &flags);
ASSERT_TRUE(decrypted);
EXPECT_TRUE(flags.should_reencrypt);
EXPECT_EQ(*decrypted, "moresecret");
}
}
{
auto another_encryptor =
GetInstanceSync(*os_crypt, Encryptor::Option::kEncryptSyncCompat);
auto decrypted = another_encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "testsecrets");
}
}
TEST_F(OSCryptAsyncTest, TestEncryptorInterface) {
auto encryptor = GetTestEncryptorForTesting();
auto ciphertext = encryptor.EncryptString("testsecrets");
ASSERT_TRUE(ciphertext);
auto decrypted = encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "testsecrets");
}
TEST_F(OSCryptAsyncTest, TestEncryptorIsEncryptionAvailable) {
auto encryptor = GetTestEncryptorForTesting();
EXPECT_TRUE(encryptor.IsDecryptionAvailable());
encryptor.set_decryption_available_for_testing(false);
EXPECT_FALSE(encryptor.IsDecryptionAvailable());
encryptor.set_decryption_available_for_testing(std::nullopt);
EXPECT_TRUE(encryptor.IsDecryptionAvailable());
EXPECT_TRUE(encryptor.IsEncryptionAvailable());
encryptor.set_encryption_available_for_testing(false);
EXPECT_FALSE(encryptor.IsEncryptionAvailable());
encryptor.set_encryption_available_for_testing(std::nullopt);
EXPECT_TRUE(encryptor.IsEncryptionAvailable());
}
class FailingKeyProvider : public TestKeyProvider {
private:
void GetKey(KeyCallback callback) override {
std::move(callback).Run("BLAH", std::nullopt);
}
};
// Some tests require a working OSCrypt.
class OSCryptAsyncTestWithOSCrypt : public OSCryptAsyncTest {
protected:
void SetUp() override { OSCryptMocker::SetUp(); }
void TearDown() override {
OSCryptMocker::TearDown();
#if BUILDFLAG(IS_WIN)
OSCrypt::ResetStateForTesting();
#endif // BUILDFLAG(IS_WIN)
}
};
// This test merely verifies that OSCryptAsync can operate with no key providers
// and return a valid Encryptor with no keys, and that it can interop with
// OSCrypt. The rest of the encryption tests for this mode are located in
// encryptor_unittest.cc.
TEST_F(OSCryptAsyncTestWithOSCrypt, Empty) {
ProviderList providers;
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
{
std::string ciphertext;
EXPECT_TRUE(OSCrypt::EncryptString("secrets", &ciphertext));
std::string plaintext;
EXPECT_TRUE(encryptor.DecryptString(ciphertext, &plaintext));
EXPECT_EQ("secrets", plaintext);
}
{
const auto ciphertext = encryptor.EncryptString("moresecrets");
ASSERT_TRUE(ciphertext.has_value());
std::string plaintext;
EXPECT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext->begin(), ciphertext->end()), &plaintext));
EXPECT_EQ("moresecrets", plaintext);
}
}
TEST_F(OSCryptAsyncTestWithOSCrypt, FailingKeyProvider) {
ProviderList providers;
providers.emplace_back(/*precedence=*/10u,
std::make_unique<FailingKeyProvider>());
OSCryptAsync factory(std::move(providers));
// TODO: Work out how best to handle provider failures.
Encryptor encryptor = GetInstanceSync(factory);
{
// Encryption should still work, because an empty Encryptor is made which
// falls back to OSCrypt.
auto ciphertext = encryptor.EncryptString("secrets");
EXPECT_TRUE(ciphertext);
std::string plaintext;
EXPECT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext->cbegin(), ciphertext->cend()), &plaintext));
EXPECT_EQ("secrets", plaintext);
}
{
std::string ciphertext;
EXPECT_TRUE(OSCrypt::EncryptString("secrets", &ciphertext));
std::string plaintext;
// Decryption falls back to OSCrypt if there are no matching providers. In
// this case, there are no providers at all.
EXPECT_TRUE(encryptor.DecryptString(ciphertext, &plaintext));
EXPECT_EQ("secrets", plaintext);
}
}
TEST_F(OSCryptAsyncTest, ShouldReencrypt) {
std::string ciphertext;
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/8u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ASSERT_TRUE(encryptor.EncryptString("secrets", &ciphertext));
// FOO should be used, as it's the higher precedence.
EXPECT_THAT(base::span(ciphertext).first<3>(),
::testing::ElementsAreArray(base::span_from_cstring("FOO")));
std::string plaintext;
Encryptor::DecryptFlags flags;
ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
EXPECT_EQ(plaintext, "secrets");
EXPECT_FALSE(flags.should_reencrypt);
}
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/8u,
std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
Encryptor::DecryptFlags flags;
std::string plaintext;
ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
EXPECT_EQ(plaintext, "secrets");
EXPECT_TRUE(flags.should_reencrypt);
}
}
TEST_F(OSCryptAsyncTestWithOSCrypt, OSCryptShouldReencrypt) {
std::string ciphertext;
ASSERT_TRUE(OSCrypt::EncryptString("secrets", &ciphertext));
{
ProviderList providers;
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
std::string plaintext;
Encryptor::DecryptFlags flags;
// This encryptor has no providers, so falls back to OSCrypt Sync.
ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
EXPECT_EQ(plaintext, "secrets");
EXPECT_FALSE(flags.should_reencrypt);
}
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
Encryptor::DecryptFlags flags;
std::string plaintext;
ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
EXPECT_EQ(plaintext, "secrets");
EXPECT_TRUE(flags.should_reencrypt);
}
{
ProviderList providers;
// Specify not to encrypt data with this provider.
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/false));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
Encryptor::DecryptFlags flags;
std::string plaintext;
ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
EXPECT_EQ(plaintext, "secrets");
EXPECT_FALSE(flags.should_reencrypt);
}
}
// Test also that if no key providers have OSCrypt sync compatibility then
// encryption simply falls back to OSCrypt, and that OSCrypt can decrypt it
// fine.
TEST_F(OSCryptAsyncTestWithOSCrypt, EncryptorOption) {
ProviderList providers;
providers.emplace_back(/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
"BAR", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/false));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor =
GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
const auto ciphertext = encryptor.EncryptString("os_crypt_secrets");
ASSERT_TRUE(ciphertext);
std::string plaintext;
ASSERT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext->begin(), ciphertext->end()), &plaintext));
EXPECT_EQ("os_crypt_secrets", plaintext);
}
using OSCryptAsyncDeathTest = OSCryptAsyncTest;
TEST_F(OSCryptAsyncDeathTest, SamePrecedence) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("A", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/20u,
std::make_unique<TestKeyProvider>("B", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("C", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("D", /*use_for_encryption=*/true));
EXPECT_DCHECK_DEATH_WITH({ OSCryptAsync factory(std::move(providers)); },
"Cannot have two providers with same precedence.");
}
TEST_F(OSCryptAsyncDeathTest, SameName) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("TEST", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("TEST", /*use_for_encryption=*/true));
EXPECT_DCHECK_DEATH_WITH(
{
OSCryptAsync factory(std::move(providers));
std::ignore = GetInstanceSync(factory);
},
"Tags must not overlap.");
}
TEST_F(OSCryptAsyncDeathTest, OverlappingNames) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("TEST", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("TEST2", /*use_for_encryption=*/true));
EXPECT_DCHECK_DEATH_WITH(
{
OSCryptAsync factory(std::move(providers));
std::ignore = GetInstanceSync(factory);
},
"Tags must not overlap.");
}
TEST_F(OSCryptAsyncDeathTest, OverlappingNamesBackwards) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>("TEST2", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("TEST", /*use_for_encryption=*/true));
EXPECT_DCHECK_DEATH_WITH(
{
OSCryptAsync factory(std::move(providers));
std::ignore = GetInstanceSync(factory);
},
"Tags must not overlap.");
}
TEST_F(OSCryptAsyncDeathTest, TwoOsCryptCompatibleKeyProviders) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u, std::make_unique<TestKeyProvider>(
"ABC", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
providers.emplace_back(
/*precedence=*/15u, std::make_unique<TestKeyProvider>(
"DEF", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
EXPECT_DCHECK_DEATH_WITH(
{
OSCryptAsync factory(std::move(providers));
std::ignore = GetInstanceSync(factory);
},
"Cannot have more than one key marked OSCrypt sync compatible.");
}
TEST_F(OSCryptAsyncDeathTest, EmptyProviderName) {
ProviderList providers;
providers.emplace_back(/*precedence=*/10u,
std::make_unique<TestKeyProvider>(
std::string(), /*use_for_encryption=*/true));
EXPECT_DCHECK_DEATH_WITH(
{
OSCryptAsync factory(std::move(providers));
std::ignore = GetInstanceSync(factory);
},
"Tag cannot be empty.");
}
TEST_F(OSCryptAsyncTest, NoCrashWithLongNames) {
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("ABC", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
"TEST_REALLY_LOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG_NAME",
/*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/15u,
std::make_unique<TestKeyProvider>("XYZ", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
GetInstanceSync(factory);
}
} // namespace os_crypt_async