blob: 09fdbec3076fa4caa83a22f637bb732050a9d623 [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/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.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::test::TestFuture<Encryptor> future;
factory.GetInstance(future.GetCallback(), option);
return future.Take();
}
// 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);
}));
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
OSCrypt::SetEncryptionAvailableForTesting(/*available=*/false);
return base::ScopedClosureRunner(base::BindOnce([]() {
OSCrypt::SetEncryptionAvailableForTesting(/*available=*/std::nullopt);
}));
#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);
}
}
class OSCryptAsyncTestSwapped
: public OSCryptAsyncTest,
public ::testing::WithParamInterface</*switched=*/bool> {};
TEST_P(OSCryptAsyncTestSwapped, EncryptorOption) {
std::string first_provider_name("TEST");
std::string second_provider_name("BLAH");
// This tests std::map ordering does not matter.
if (GetParam()) {
first_provider_name = "BLAH";
second_provider_name = "TEST";
}
std::optional<std::vector<uint8_t>> first_ciphertext, second_ciphertext;
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>(first_provider_name,
/*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
second_provider_name, /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
first_ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(first_ciphertext);
// First provider should be picked, because it has a higher precedence than
// the second.
EXPECT_TRUE(std::equal(first_provider_name.cbegin(),
first_provider_name.cend(),
first_ciphertext->cbegin()));
// Now obtain an encryptor with a compatibility option.
Encryptor encryptor_compat =
GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
second_ciphertext = encryptor_compat.EncryptString("secrets");
ASSERT_TRUE(second_ciphertext);
// Should be encrypted with second key now.
EXPECT_TRUE(std::equal(second_provider_name.cbegin(),
second_provider_name.cend(),
second_ciphertext->cbegin()));
}
// Check that with just second provider, data can still be decrypted.
{
auto cleanup = MaybeSimulateLockedKeyChain();
ProviderList providers;
providers.emplace_back(
/*precedence=*/5u,
std::make_unique<TestKeyProvider>(second_provider_name,
/*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 second
// key.
ASSERT_TRUE(encryptor.IsDecryptionAvailable());
auto plaintext = encryptor.DecryptData(*second_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(*first_ciphertext);
EXPECT_FALSE(failing_plaintext);
}
// Test also that if there are multiple key providers with
// compatible_with_os_crypt_sync then the highest precedence is picked.
{
ProviderList providers;
providers.emplace_back(/*precedence=*/10u,
std::make_unique<TestKeyProvider>(
first_provider_name, /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
providers.emplace_back(/*precedence=*/8u,
std::make_unique<TestKeyProvider>(
"FOO", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/false));
providers.emplace_back(/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
"BAR", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor =
GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
const auto ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(ciphertext);
// Should be encrypted with first provider - it's the highest precedence
// provider that indicates it's compatible with OSCrypt sync.
EXPECT_TRUE(std::equal(first_provider_name.cbegin(),
first_provider_name.cend(), ciphertext->cbegin()));
}
// Just in case, test that order doesn't matter here, although this is also
// tested elsewhere.
{
ProviderList providers;
providers.emplace_back(/*precedence=*/5u,
std::make_unique<TestKeyProvider>(
"BAR", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
providers.emplace_back(/*precedence=*/8u,
std::make_unique<TestKeyProvider>(
"FOO", /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/false));
providers.emplace_back(/*precedence=*/10u,
std::make_unique<TestKeyProvider>(
first_provider_name, /*use_for_encryption=*/true,
/*compatible_with_os_crypt_sync=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor =
GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
const auto ciphertext = encryptor.EncryptString("secrets");
ASSERT_TRUE(ciphertext);
// Should be encrypted with first provider - it's the highest precedence
// provider that indicates it's compatible with OSCrypt sync.
EXPECT_TRUE(std::equal(first_provider_name.cbegin(),
first_provider_name.cend(), ciphertext->cbegin()));
}
}
INSTANTIATE_TEST_SUITE_P(, OSCryptAsyncTestSwapped, ::testing::Bool());
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;
for (size_t call = 0; call < kExpectedCalls; call++) {
factory.GetInstance(
base::BindLambdaForTesting([&calls, &run_loop](Encryptor encryptor) {
calls++;
if (calls == kExpectedCalls) {
run_loop.Quit();
}
}));
}
run_loop.Run();
EXPECT_EQ(calls, kExpectedCalls);
}
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 {
public:
FailingKeyProvider(KeyProvider::KeyError reason, const std::string& name)
: reason_(reason), name_(name) {}
private:
void GetKey(KeyCallback callback) override {
std::move(callback).Run(name_, base::unexpected(reason_));
}
const KeyProvider::KeyError reason_;
const std::string name_;
};
// 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) {
base::HistogramTester histograms;
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);
}
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount", 0, 1);
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount.Available", 0, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.TemporarilyUnavailable", 0, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.PermanentlyUnavailable", 0, 1);
}
TEST_F(OSCryptAsyncTestWithOSCrypt, FailingKeyProvider) {
base::HistogramTester histograms;
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kPermanentlyUnavailable, "BLAH"));
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);
}
// Permanently failing key providers never get emplaced into the keyring at
// all.
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount", 1, 1);
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount.Available", 0, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.TemporarilyUnavailable", 0, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.PermanentlyUnavailable", 1, 1);
}
TEST_F(OSCryptAsyncTestWithOSCrypt, TemporarilyFailingKeyProvider) {
std::optional<std::vector<uint8_t>> ciphertext;
// First, encrypt some data with the BLAH key provider.
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("BLAH", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ciphertext = encryptor.EncryptString("secrets");
EXPECT_TRUE(ciphertext);
}
// Next, cause this key provider to fail temporarily. This should cause
// decryption to fail but with kFailureKeyTemporarilyUnavailable.
{
base::HistogramTester histograms;
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kTemporarilyUnavailable, "BLAH"));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
Encryptor::DecryptFlags flags;
const auto plaintext = encryptor.DecryptData(*ciphertext, &flags);
EXPECT_FALSE(plaintext);
EXPECT_TRUE(flags.temporarily_unavailable);
// Encryption should still work, even with a temporarily failing key
// provider, but it will delegate to OSCrypt.
{
const auto ciphertext2 = encryptor.EncryptString("secret");
EXPECT_TRUE(ciphertext2);
std::string plaintext2;
EXPECT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext2->begin(), ciphertext2->end()), &plaintext2));
EXPECT_EQ(plaintext2, "secret");
}
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount", 1, 1);
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount.Available", 0, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.TemporarilyUnavailable", 1, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.PermanentlyUnavailable", 0, 1);
}
// Test permanently unavailable.
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kPermanentlyUnavailable, "BLAH"));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
Encryptor::DecryptFlags flags;
const auto plaintext = encryptor.DecryptData(*ciphertext, &flags);
// Since there is no key at all, this case has fallback to OSCrypt sync
// which cannot decrypt data encrypted with BLAH key.
EXPECT_FALSE(plaintext);
EXPECT_FALSE(flags.temporarily_unavailable);
// With no key provided at all (a permanent failure), encryption is
// delegated to OSCrypt.
{
const auto ciphertext2 = encryptor.EncryptString("secret");
EXPECT_TRUE(ciphertext2);
std::string plaintext2;
EXPECT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext2->begin(), ciphertext2->end()), &plaintext2));
EXPECT_EQ(plaintext2, "secret");
}
}
}
TEST_F(OSCryptAsyncTest, MultipleKeysSomeTemporarilyUnavailable) {
std::optional<std::vector<uint8_t>> ciphertext;
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("BLAH", /*use_for_encryption=*/true));
// Note: TEST is higher precedence so would normally be picked for
// encryption, were it not unavailable.
providers.emplace_back(
/*precedence=*/15u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kTemporarilyUnavailable, "TEST"));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
ciphertext = encryptor.EncryptString("secret data");
EXPECT_TRUE(ciphertext);
}
// Verify that BLAH is used by creating a new encryptor with only BLAH and
// decrypting.
{
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("BLAH", /*use_for_encryption=*/true));
OSCryptAsync factory(std::move(providers));
Encryptor encryptor = GetInstanceSync(factory);
const auto plaintext = encryptor.DecryptData(*ciphertext);
EXPECT_TRUE(plaintext);
EXPECT_EQ(*plaintext, "secret data");
}
}
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, 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);
}
TEST_F(OSCryptAsyncTest, Metrics) {
base::HistogramTester histograms;
ProviderList providers;
providers.emplace_back(
/*precedence=*/10u,
std::make_unique<TestKeyProvider>("ABC", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/15u,
std::make_unique<TestKeyProvider>("DEF", /*use_for_encryption=*/true));
providers.emplace_back(
/*precedence=*/20u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kPermanentlyUnavailable, "GHI"));
providers.emplace_back(
/*precedence=*/25u,
std::make_unique<FailingKeyProvider>(
KeyProvider::KeyError::kTemporarilyUnavailable, "JKL"));
OSCryptAsync factory(std::move(providers));
GetInstanceSync(factory);
// See TemporarilyFailingKeyProvider, FailingKeyProvider and Empty tests above
// for further testing of these counts.
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount", 4, 1);
histograms.ExpectBucketCount("OSCrypt.EncryptorKeyCount.Available", 2, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.TemporarilyUnavailable", 1, 1);
histograms.ExpectBucketCount(
"OSCrypt.EncryptorKeyCount.PermanentlyUnavailable", 1, 1);
}
} // namespace os_crypt_async