blob: 5717d4db0da4d62500cf95ad7beb14a6dbbc019d [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 <memory>
#include <optional>
#include <vector>
#include "base/functional/bind.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "chrome/browser/browser_process.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/os_crypt/async/browser/test_utils.h"
#include "components/os_crypt/async/common/encryptor.h"
#include "components/os_crypt/sync/os_crypt.h"
#include "content/public/browser/service_process_host.h"
#include "content/public/test/browser_test.h"
#include "services/test/echo/public/mojom/echo.mojom.h"
namespace os_crypt_async {
namespace {
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 instance, bool result) {
EXPECT_TRUE(result);
encryptor.emplace(std::move(instance));
run_loop.Quit();
}),
option);
run_loop.Run();
return std::move(*encryptor);
}
} // namespace
class OSCryptAsyncBrowserTest : public InProcessBrowserTest {
protected:
base::HistogramTester histogram_tester_;
};
// Test the basic browser interface to Encrypt and Decrypt data.
IN_PROC_BROWSER_TEST_F(OSCryptAsyncBrowserTest, EncryptDecrypt) {
auto encryptor = GetInstanceSync(*g_browser_process->os_crypt_async());
// These histograms should always have been recorded by the time the
// GetInstance callback above has happened, since the browser registers its
// metrics callback before anything else gets a chance to.
histogram_tester_.ExpectTotalCount("OSCrypt.AsyncInitialization.Time", 1u);
histogram_tester_.ExpectUniqueSample("OSCrypt.AsyncInitialization.Result",
true, 1u);
auto ciphertext = encryptor.EncryptString("plaintext");
ASSERT_TRUE(ciphertext);
auto decrypted = encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "plaintext");
}
// This test verifies that an Encryptor works inside a fully sandboxed process.
IN_PROC_BROWSER_TEST_F(OSCryptAsyncBrowserTest, SandboxedEncryptionTest) {
// Use a testing instance, otherwise fallback to os_crypt sync does not work
// on all platforms when inside a sandbox without transferring the key over
// manually.
auto os_crypt_async = GetTestOSCryptAsyncForTesting();
Encryptor encryptor = GetInstanceSync(*os_crypt_async);
constexpr char kTestData[] = "testdatatest";
// First, encrypt the data.
const auto encrypted_data = encryptor.EncryptString(kTestData);
ASSERT_TRUE(encrypted_data.has_value());
// Launch sandboxed echo service.
auto echo_service =
content::ServiceProcessHost::Launch<echo::mojom::EchoService>();
base::test::TestFuture<std::optional<std::vector<uint8_t>>> future;
// This performs a decrypt, then an encrypt.
echo_service->DecryptEncrypt(
std::move(encryptor), *encrypted_data,
future.GetCallback<const std::optional<std::vector<uint8_t>>&>());
auto& result = future.Get();
ASSERT_TRUE(result.has_value());
// Obtain a second encryptor, since the first one was consumed in the mojo
// call above.
Encryptor encryptor2 = GetInstanceSync(*os_crypt_async);
// Finally, decrypt the data again.
const auto plaintext = encryptor2.DecryptData(*result);
ASSERT_TRUE(plaintext.has_value());
EXPECT_EQ(*plaintext, kTestData);
}
// This test verifies that an Encryptor obtained with the kEncryptSyncCompat
// option encrypts data that can be decrypted by OSCrypt, as well as ensuring
// that kEncryptSyncCompat and kNone options are interoperable with each other.
IN_PROC_BROWSER_TEST_F(OSCryptAsyncBrowserTest, OSCryptBackwardsCompatTest) {
auto encryptor = GetInstanceSync(*g_browser_process->os_crypt_async(),
Encryptor::Option::kEncryptSyncCompat);
auto ciphertext = encryptor.EncryptString("plaintext");
ASSERT_TRUE(ciphertext);
{
const auto decrypted = encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(*decrypted, "plaintext");
}
{
std::string decrypted;
ASSERT_TRUE(OSCrypt::DecryptString(
std::string(ciphertext->begin(), ciphertext->end()), &decrypted));
EXPECT_EQ(decrypted, "plaintext");
}
{
// Verify that data encrypted from a kEncryptSyncCompat encryptor can be
// decrypted with a kNone encryptor.
auto full_encryptor = GetInstanceSync(*g_browser_process->os_crypt_async(),
Encryptor::Option::kNone);
const auto decrypted = full_encryptor.DecryptData(*ciphertext);
ASSERT_TRUE(decrypted);
EXPECT_EQ(decrypted, "plaintext");
// Verify that data encrypted from a kNone encryptor can be decrypted with a
// kEncryptSyncCompat encryptor.
const auto ciphertext2 = full_encryptor.EncryptString("more_plaintext");
ASSERT_TRUE(ciphertext2);
const auto decrypted2 = encryptor.DecryptData(*ciphertext2);
ASSERT_TRUE(decrypted2);
EXPECT_EQ(*decrypted2, "more_plaintext");
}
}
} // namespace os_crypt_async