#include "components/os_crypt/async/common/encryptor.h"
#include <algorithm>
#include <vector>
#include "base/containers/span.h"
#include "base/functional/callback_helpers.h"
#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "components/os_crypt/async/common/encryptor.h"
#include "components/os_crypt/async/common/encryptor.mojom.h"
#include "components/os_crypt/sync/os_crypt.h"
#include "components/os_crypt/sync/os_crypt_mocker.h"
#include "crypto/random.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_LINUX)
#include "components/os_crypt/sync/key_storage_linux.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <wincrypt.h>
#include "base/win/scoped_localalloc.h"
#endif
namespace os_crypt_async {
namespace {
#if BUILDFLAG(IS_WIN)
bool EncryptStringWithDPAPI(const std::string& plaintext,
std::string& ciphertext) {
DATA_BLOB input = {};
input.pbData =
const_cast<BYTE*>(reinterpret_cast<const BYTE*>(plaintext.data()));
input.cbData = static_cast<DWORD>(plaintext.length());
BOOL result = FALSE;
DATA_BLOB output = {};
result =
::CryptProtectData(&input, L"",
nullptr, nullptr,
nullptr, 0, &output);
if (!result) {
return false;
}
auto local_alloc = base::win::TakeLocalAlloc(output.pbData);
static_assert(sizeof(std::string::value_type) == 1);
ciphertext.assign(
reinterpret_cast<std::string::value_type*>(local_alloc.get()),
output.cbData);
return true;
}
#endif
[[nodiscard]] bool MaybeVerifyFailedDecryptOperation(
const std::optional<std::string>& decrypted,
base::span<const uint8_t> ciphertext) { … }
}
enum class TestType { … };
const auto kTestCases = …;
class EncryptorTestBase : public ::testing::Test { … };
class EncryptorTestWithOSCrypt : public EncryptorTestBase { … };
class EncryptorTest : public EncryptorTestWithOSCrypt,
public ::testing::WithParamInterface<TestType> { … };
TEST_P(EncryptorTest, StringInterface) { … }
TEST_P(EncryptorTest, SpanInterface) { … }
TEST_P(EncryptorTest, EncryptStringDecryptSpan) { … }
TEST_P(EncryptorTest, EncryptSpanDecryptString) { … }
TEST_P(EncryptorTest, EncryptDecryptString16) { … }
TEST_P(EncryptorTest, EncryptEmpty) { … }
TEST_P(EncryptorTest, DecryptEmpty) { … }
#if BUILDFLAG(IS_WIN)
TEST_P(EncryptorTest, DecryptInvalid) {
const Encryptor encryptor = GetTestEncryptor();
std::vector<uint8_t> invalid_cipher(100);
for (size_t c = 0u; c < invalid_cipher.size(); c++) {
invalid_cipher[c] = c;
}
auto plaintext = encryptor.DecryptData(invalid_cipher);
ASSERT_FALSE(plaintext);
}
#endif
TEST_P(EncryptorTest, DecryptFallback) { … }
TEST_P(EncryptorTest, Decrypt16Fallback) { … }
#if BUILDFLAG(IS_WIN)
TEST_P(EncryptorTest, AncientFallback) {
std::string ciphertext;
EXPECT_TRUE(EncryptStringWithDPAPI("secret", ciphertext));
std::string decrypted;
const Encryptor encryptor = GetTestEncryptor();
EXPECT_TRUE(encryptor.DecryptString(ciphertext, &decrypted));
EXPECT_EQ("secret", decrypted);
}
#endif
INSTANTIATE_TEST_SUITE_P(…);
TEST_F(EncryptorTestBase, MultipleKeys) { … }
TEST_F(EncryptorTestWithOSCrypt, EmptyandNonEmpty) { … }
TEST_F(EncryptorTestWithOSCrypt, ShortCiphertext) { … }
TEST_F(EncryptorTestBase, IsEncryptionAvailableFallback) { … }
TEST_F(EncryptorTestWithOSCrypt, IsEncryptionAvailableFallback) { … }
TEST_F(EncryptorTestBase, IsEncryptionAvailable) { … }
#if BUILDFLAG(IS_WIN)
TEST_F(EncryptorTestBase, AlgorithmDecryptCompatibility) {
std::string ciphertext;
std::string ciphertext16;
auto random_key = GenerateRandomTestKey(kKeyLength);
OSCrypt::SetRawEncryptionKey(
std::string(random_key.begin(), random_key.end()));
EXPECT_TRUE(OSCrypt::EncryptString("secret", &ciphertext));
EXPECT_TRUE(OSCrypt::EncryptString16(u"secret16", &ciphertext16));
OSCrypt::ResetStateForTesting();
OSCrypt::UseMockKeyForTesting(true);
std::string plaintext;
EXPECT_FALSE(OSCrypt::DecryptString(ciphertext, &plaintext));
Encryptor::KeyRing key_ring;
constexpr char kEncryptionVersionPrefix[] = "v10";
key_ring.emplace(kEncryptionVersionPrefix,
Encryptor::Key(random_key, mojom::Algorithm::kAES256GCM));
const Encryptor encryptor =
GetEncryptor(std::move(key_ring), kEncryptionVersionPrefix);
EXPECT_TRUE(encryptor.DecryptString(ciphertext, &plaintext));
std::u16string plaintext16;
EXPECT_TRUE(encryptor.DecryptString16(ciphertext16, &plaintext16));
EXPECT_EQ("secret", plaintext);
EXPECT_EQ(u"secret16", plaintext16);
OSCrypt::ResetStateForTesting();
}
TEST_F(EncryptorTestBase, AlgorithmEncryptCompatibility) {
auto random_key = GenerateRandomTestKey(kKeyLength);
Encryptor::KeyRing key_ring;
constexpr char kEncryptionVersionPrefix[] = "v10";
key_ring.emplace(kEncryptionVersionPrefix,
Encryptor::Key(random_key, mojom::Algorithm::kAES256GCM));
const Encryptor encryptor =
GetEncryptor(std::move(key_ring), kEncryptionVersionPrefix);
auto ciphertext = encryptor.EncryptString("secret");
EXPECT_TRUE(ciphertext);
std::string ciphertext16;
EXPECT_TRUE(encryptor.EncryptString16(u"secret16", &ciphertext16));
OSCrypt::UseMockKeyForTesting(true);
std::string plaintext;
std::u16string plaintext16;
EXPECT_FALSE(OSCrypt::DecryptString(
std::string(ciphertext->begin(), ciphertext->end()), &plaintext));
EXPECT_FALSE(OSCrypt::DecryptString16(ciphertext16, &plaintext16));
OSCrypt::ResetStateForTesting();
OSCrypt::SetRawEncryptionKey(
std::string(random_key.begin(), random_key.end()));
EXPECT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext->begin(), ciphertext->end()), &plaintext));
EXPECT_EQ("secret", plaintext);
EXPECT_TRUE(OSCrypt::DecryptString16(ciphertext16, &plaintext16));
EXPECT_EQ(u"secret16", plaintext16);
OSCrypt::ResetStateForTesting();
}
#endif
TEST_F(EncryptorTestBase, Clone) { … }
class EncryptorTraitsTest : public EncryptorTestBase { … };
TEST_F(EncryptorTraitsTest, TraitsRoundTrip) { … }
}