Add metrics for measuring the speed and reliability of app-based crypto.
This CL adds metrics to call into the elevation service to perform
app-based encryption and decryption operations similar to those
that would be performed to support app-based encryption in Chrome.
A task is posted at startup to measure a call to Encrypt for a new
local state, with the encrypted data persisted to local state.
On subsequent startups, the encrypted data is decrypted and the time
taken to decrypt is measured
This will give a good proxy for the performance of the code in
src/chrome if it is to be used in production, and help direct
further development on the threading model that will be used
for initializing the os_crypt key in future iterations.
A test was added that requires running as Admin to work correctly
as it installs the service, and verifies that it can be
successfully communicated with.
BUG=1333461
Change-Id: I9d735242a24bc8a29cfc3b8a14634578d6cf1c3e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3697028
Reviewed-by: Greg Thompson <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Commit-Queue: Will Harris <[email protected]>
Reviewed-by: Robert Kaplow <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1016956}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 44b91da..b0214ae 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5704,6 +5704,10 @@
"notifications/win/notification_template_builder.cc",
"notifications/win/notification_template_builder.h",
"obsolete_system/obsolete_system_win.cc",
+ "os_crypt/app_bound_encryption_metrics_win.cc",
+ "os_crypt/app_bound_encryption_metrics_win.h",
+ "os_crypt/app_bound_encryption_win.cc",
+ "os_crypt/app_bound_encryption_win.h",
"password_manager/password_manager_util_win.cc",
"password_manager/password_manager_util_win.h",
"performance_manager/mechanisms/working_set_trimmer_win.cc",
@@ -5806,7 +5810,7 @@
"//chrome/chrome_elf:third_party_shared_defines",
"//chrome/common:version_header",
"//chrome/credential_provider/common:common_constants",
- "//chrome/elevation_service:elevation_service_idl",
+ "//chrome/elevation_service:public_headers",
"//chrome/install_static:install_static_util",
"//chrome/installer/util:with_no_strings",
"//chrome/notification_helper:constants",
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc
index 77d5a8a8..9803fd1 100644
--- a/chrome/browser/browser_features.cc
+++ b/chrome/browser/browser_features.cc
@@ -27,7 +27,7 @@
// Destroy profiles when their last browser window is closed, instead of when
// the browser exits.
-const base::Feature kDestroyProfileOnBrowserClose{
+const base::Feature kDestroyProfileOnBrowserClose {
"DestroyProfileOnBrowserClose",
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
base::FEATURE_ENABLED_BY_DEFAULT
@@ -164,4 +164,10 @@
"RestartNetworkServiceUnsandboxedForFailedLaunch",
base::FEATURE_ENABLED_BY_DEFAULT};
+#if BUILDFLAG(IS_WIN)
+// When this feature is enabled, metrics are gathered regarding the performance
+// and reliability of app-bound encryption primitives on a background thread.
+const base::Feature kAppBoundEncryptionMetrics{
+ "AppBoundEncryptionMetrics", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
} // namespace features
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h
index 4f70115..f860c19 100644
--- a/chrome/browser/browser_features.h
+++ b/chrome/browser/browser_features.h
@@ -68,6 +68,10 @@
extern const base::Feature kKeyPinningComponentUpdater;
+#if BUILDFLAG(IS_WIN)
+extern const base::Feature kAppBoundEncryptionMetrics;
+#endif
+
} // namespace features
#endif // CHROME_BROWSER_BROWSER_FEATURES_H_
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 9f15dccc..d924bb5 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -47,10 +47,12 @@
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/enterprise/util/critical_policy_section_metrics_win.h"
#include "chrome/browser/first_run/first_run.h"
+#include "chrome/browser/os_crypt/app_bound_encryption_metrics_win.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_shortcut_manager.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/settings_resetter_win.h"
@@ -651,6 +653,12 @@
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, base::BindOnce(&DetectFaultTolerantHeap), base::Minutes(1));
+ // Query feature first, to include full population in field trial.
+ if (base::FeatureList::IsEnabled(features::kAppBoundEncryptionMetrics) &&
+ install_static::IsSystemInstall()) {
+ os_crypt::MeasureAppBoundEncryptionStatus(g_browser_process->local_state());
+ }
+
// Record Processor Metrics. This is very low priority, hence posting as
// BEST_EFFORT to start after Chrome startup has completed. This metric is
// only available starting Windows 10.
diff --git a/chrome/browser/os_crypt/DEPS b/chrome/browser/os_crypt/DEPS
new file mode 100644
index 0000000..1a0e207
--- /dev/null
+++ b/chrome/browser/os_crypt/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ '+chrome/elevation_service/elevation_service_idl.h',
+ '+chrome/elevation_service/elevator.h',
+]
diff --git a/chrome/browser/os_crypt/OWNERS b/chrome/browser/os_crypt/OWNERS
new file mode 100644
index 0000000..971354f1
--- /dev/null
+++ b/chrome/browser/os_crypt/OWNERS
@@ -0,0 +1,5 @@
+# Default sending any reviews to these owners:
[email protected]
+
+# As a backup, a review can also be obtained from a reviewer in this file.
+file://chrome/elevation_service/OWNERS
diff --git a/chrome/browser/os_crypt/README.md b/chrome/browser/os_crypt/README.md
new file mode 100644
index 0000000..803cb68b4
--- /dev/null
+++ b/chrome/browser/os_crypt/README.md
@@ -0,0 +1,27 @@
+This directory contains the interface to the application-bound encryption
+primitives that are implemented by the elevation service in
+[src/chrome/elevation_service].
+
+`EncryptAppBoundString` and `DecryptAppBoundString` act like
+`OSCrypt::EncryptString` and `OSCrypt::DecryptString` implemented by
+[src/components/os_crypt] except that, unlike `OSCrypt`, which binds encrypted
+data to the current user using DPAPI, this API will bind the encrypted data
+with a `ProtectionLevel` specified by the caller.
+
+`ProtectionLevels` are defined by chrome/elevation_service and are currently:
+
+ - `ProtectionLevel::NONE`
+
+ This acts identically to DPAPI in that the protection level is user-bound.
+ Only a `DecryptAppBoundString` call that comes from the same user principle
+ as the original `EncryptAppBoundString` call with succeed.
+
+ - `ProtectionLevel::PATH_VALIDATION`
+
+ This adds an additional protection that the path of the calling application
+ will be validated. Only a `DecryptAppBoundString` call that comes from the
+ same user principle, calling from the same Application (with the same file
+ path) as the original `EncryptAppBoundString` call with succeed. It is only
+ safe to call this from an application that is installed into a 'Trusted
+ Path' such as `C:\Program Files`, otherwise protection can be trivially
+ bypassed by renaming/placing a file into the required location.
diff --git a/chrome/browser/os_crypt/app_bound_encryption_metrics_win.cc b/chrome/browser/os_crypt/app_bound_encryption_metrics_win.cc
new file mode 100644
index 0000000..b5d29b5
--- /dev/null
+++ b/chrome/browser/os_crypt/app_bound_encryption_metrics_win.cc
@@ -0,0 +1,134 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/os_crypt/app_bound_encryption_metrics_win.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/win/com_init_util.h"
+#include "base/win/windows_types.h"
+#include "chrome/browser/os_crypt/app_bound_encryption_win.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace os_crypt {
+
+namespace prefs {
+
+const char kOsCryptAppBoundFixedDataPrefName[] =
+ "os_crypt.app_bound_fixed_data";
+
+} // namespace prefs
+
+namespace {
+
+// Rather than generate a random key here, use fixed data here for the purposes
+// of measuring the performance, as the content itself does not matter.
+const char kFixedData[] = "Fixed data used for metrics";
+
+void DecryptAndRecordMetricsOnCOMThread(const std::string& encrypted_data) {
+ base::win::AssertComInitialized();
+
+ std::string decrypted_data;
+ DWORD last_error;
+ HRESULT hr;
+ {
+ SCOPED_UMA_HISTOGRAM_TIMER("OSCrypt.AppBoundEncryption.Decrypt.Time");
+ hr = DecryptAppBoundString(encrypted_data, decrypted_data, last_error);
+ }
+
+ if (FAILED(hr)) {
+ base::UmaHistogramSparse(
+ "OSCrypt.AppBoundEncryption.Decrypt.ResultLastError", last_error);
+ } else {
+ // Check if it returned success but the data was invalid. This should never
+ // happen. If it does, log a unique HRESULT to track it.
+ if (decrypted_data != kFixedData) {
+ const HRESULT kErrorWrongData =
+ MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xA101);
+ hr = kErrorWrongData;
+ }
+ }
+
+ base::UmaHistogramSparse("OSCrypt.AppBoundEncryption.Decrypt.ResultCode", hr);
+}
+
+std::string EncryptAndRecordMetricsOnCOMThread() {
+ base::win::AssertComInitialized();
+
+ std::string encrypted_data;
+ DWORD last_error;
+ HRESULT hr;
+ {
+ SCOPED_UMA_HISTOGRAM_TIMER("OSCrypt.AppBoundEncryption.Encrypt.Time");
+ hr = EncryptAppBoundString(ProtectionLevel::PATH_VALIDATION, kFixedData,
+ encrypted_data, last_error);
+ }
+
+ base::UmaHistogramSparse("OSCrypt.AppBoundEncryption.Encrypt.ResultCode", hr);
+
+ if (FAILED(hr)) {
+ base::UmaHistogramSparse(
+ "OSCrypt.AppBoundEncryption.Encrypt.ResultLastError", last_error);
+ }
+
+ return encrypted_data;
+}
+
+void StorePrefOnUiThread(PrefService* local_state,
+ const std::string& encrypted_data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (encrypted_data.empty())
+ return;
+ std::string base64_data;
+ base::Base64Encode(encrypted_data, &base64_data);
+
+ local_state->SetString(prefs::kOsCryptAppBoundFixedDataPrefName, base64_data);
+}
+
+} // namespace
+
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kOsCryptAppBoundFixedDataPrefName, {});
+}
+
+bool MeasureAppBoundEncryptionStatus(PrefService* local_state) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ auto com_runner = base::ThreadPool::CreateCOMSTATaskRunner(
+ {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::SingleThreadTaskRunnerThreadMode::SHARED);
+
+ if (local_state->HasPrefPath(prefs::kOsCryptAppBoundFixedDataPrefName)) {
+ const std::string base64_encrypted_data =
+ local_state->GetString(prefs::kOsCryptAppBoundFixedDataPrefName);
+
+ std::string encrypted_data;
+ // If this fails it will be caught later when trying to decrypt and logged
+ // above..
+ std::ignore = base::Base64Decode(base64_encrypted_data, &encrypted_data);
+
+ // Gather metrics for decrypt.
+ return com_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&DecryptAndRecordMetricsOnCOMThread, encrypted_data));
+ }
+
+ return com_runner->PostTaskAndReplyWithResult(
+ FROM_HERE, base::BindOnce(&EncryptAndRecordMetricsOnCOMThread),
+ base::BindOnce(StorePrefOnUiThread, local_state));
+}
+
+} // namespace os_crypt
diff --git a/chrome/browser/os_crypt/app_bound_encryption_metrics_win.h b/chrome/browser/os_crypt/app_bound_encryption_metrics_win.h
new file mode 100644
index 0000000..66c6507
--- /dev/null
+++ b/chrome/browser/os_crypt/app_bound_encryption_metrics_win.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_METRICS_WIN_H_
+#define CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_METRICS_WIN_H_
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace os_crypt {
+
+// Register local state prefs required by the app bound encryption metrics.
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
+// Posts background tasks to measure the app bound encryption metrics. This
+// should be called on the UI thread.
+bool MeasureAppBoundEncryptionStatus(PrefService* local_state);
+
+} // namespace os_crypt
+
+#endif // CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_METRICS_WIN_H_
diff --git a/chrome/browser/os_crypt/app_bound_encryption_win.cc b/chrome/browser/os_crypt/app_bound_encryption_win.cc
new file mode 100644
index 0000000..79a680af
--- /dev/null
+++ b/chrome/browser/os_crypt/app_bound_encryption_win.cc
@@ -0,0 +1,98 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/os_crypt/app_bound_encryption_win.h"
+
+#include <objbase.h>
+#include <string.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "base/logging.h"
+#include "base/win/com_init_util.h"
+#include "base/win/scoped_bstr.h"
+#include "chrome/elevation_service/elevation_service_idl.h"
+#include "chrome/install_static/install_util.h"
+
+namespace os_crypt {
+
+HRESULT EncryptAppBoundString(ProtectionLevel protection_level,
+ const std::string& plaintext,
+ std::string& ciphertext,
+ DWORD& last_error) {
+ base::win::AssertComInitialized();
+ Microsoft::WRL::ComPtr<IElevator> elevator;
+ last_error = ERROR_GEN_FAILURE;
+ HRESULT hr = ::CoCreateInstance(
+ install_static::GetElevatorClsid(), nullptr, CLSCTX_LOCAL_SERVER,
+ install_static::GetElevatorIid(), IID_PPV_ARGS_Helper(&elevator));
+
+ if (FAILED(hr))
+ return hr;
+
+ hr = ::CoSetProxyBlanket(
+ elevator.Get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
+ COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+ RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING);
+ if (FAILED(hr))
+ return hr;
+
+ base::win::ScopedBstr plaintext_data;
+ ::memcpy(plaintext_data.AllocateBytes(plaintext.length()), plaintext.data(),
+ plaintext.length());
+
+ base::win::ScopedBstr encrypted_data;
+ hr = elevator->EncryptData(protection_level, plaintext_data.Get(),
+ encrypted_data.Receive(), &last_error);
+ if (FAILED(hr))
+ return hr;
+
+ ciphertext.assign(
+ reinterpret_cast<std::string::value_type*>(encrypted_data.Get()),
+ encrypted_data.ByteLength());
+
+ last_error = ERROR_SUCCESS;
+ return S_OK;
+}
+
+HRESULT DecryptAppBoundString(const std::string& ciphertext,
+ std::string& plaintext,
+ DWORD& last_error) {
+ DCHECK(!ciphertext.empty());
+ base::win::AssertComInitialized();
+ Microsoft::WRL::ComPtr<IElevator> elevator;
+ last_error = ERROR_GEN_FAILURE;
+ HRESULT hr = ::CoCreateInstance(
+ install_static::GetElevatorClsid(), nullptr, CLSCTX_LOCAL_SERVER,
+ install_static::GetElevatorIid(), IID_PPV_ARGS_Helper(&elevator));
+
+ if (FAILED(hr))
+ return hr;
+
+ hr = ::CoSetProxyBlanket(
+ elevator.Get(), RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
+ COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+ RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DYNAMIC_CLOAKING);
+ if (FAILED(hr))
+ return hr;
+
+ base::win::ScopedBstr ciphertext_data;
+ ::memcpy(ciphertext_data.AllocateBytes(ciphertext.length()),
+ ciphertext.data(), ciphertext.length());
+
+ base::win::ScopedBstr plaintext_data;
+ hr = elevator->DecryptData(ciphertext_data.Get(), plaintext_data.Receive(),
+ &last_error);
+ if (FAILED(hr))
+ return hr;
+
+ plaintext.assign(
+ reinterpret_cast<std::string::value_type*>(plaintext_data.Get()),
+ plaintext_data.ByteLength());
+
+ last_error = ERROR_SUCCESS;
+ return S_OK;
+}
+
+} // namespace os_crypt
diff --git a/chrome/browser/os_crypt/app_bound_encryption_win.h b/chrome/browser/os_crypt/app_bound_encryption_win.h
new file mode 100644
index 0000000..e8f1372
--- /dev/null
+++ b/chrome/browser/os_crypt/app_bound_encryption_win.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_WIN_H_
+#define CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_WIN_H_
+
+#include <string>
+
+#include "base/win/windows_types.h"
+#include "chrome/elevation_service/elevation_service_idl.h"
+
+namespace os_crypt {
+
+// Encrypts a string with a Protection level of `level`. See
+// `src/chrome/elevation_service/elevation-service_idl.idl` for the definition
+// of available protection levels.
+//
+// This returns an HRESULT as defined by src/chrome/elevation_service/elevator.h
+// or S_OK for success. If the call fails then `last_error` will be set to the
+// value returned from the most recent failing Windows API call or
+// ERROR_GEN_FAILURE.
+//
+// This should be called on a COM-enabled thread.
+HRESULT EncryptAppBoundString(ProtectionLevel level,
+ const std::string& plaintext,
+ std::string& ciphertext,
+ DWORD& last_error);
+
+// Decrypts a string previously encrypted by a call to EncryptAppBoundString.
+//
+// This returns an HRESULT as defined by src/chrome/elevation_service/elevator.h
+// or S_OK for success. If the call fails then `last_error` will be set to the
+// value returned from the most recent failing Windows API call or
+// ERROR_GEN_FAILURE.
+//
+// This should be called on a COM-enabled thread.
+HRESULT DecryptAppBoundString(const std::string& ciphertext,
+ std::string& plaintext,
+ DWORD& last_error);
+
+} // namespace os_crypt
+
+#endif // CHROME_BROWSER_OS_CRYPT_APP_BOUND_ENCRYPTION_WIN_H_
diff --git a/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc b/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc
new file mode 100644
index 0000000..c21144c
--- /dev/null
+++ b/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc
@@ -0,0 +1,221 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/os_crypt/app_bound_encryption_win.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/path_service.h"
+#include "base/process/process_info.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/elevation_service/elevator.h"
+#include "chrome/install_static/buildflags.h"
+#include "chrome/install_static/install_constants.h"
+#include "chrome/install_static/install_details.h"
+#include "chrome/install_static/install_util.h"
+#include "chrome/install_static/test/scoped_install_details.h"
+#include "chrome/installer/util/install_service_work_item.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/version_info/version_info_values.h"
+#include "content/public/test/browser_test.h"
+
+namespace {
+
+void WaitForHistogram(const std::string& histogram_name) {
+ // Continue if histogram was already recorded.
+ if (base::StatisticsRecorder::FindHistogram(histogram_name))
+ return;
+
+ // Else, wait until the histogram is recorded.
+ base::RunLoop run_loop;
+ auto histogram_observer =
+ std::make_unique<base::StatisticsRecorder::ScopedHistogramSampleObserver>(
+ histogram_name,
+ base::BindLambdaForTesting(
+ [&](const char* histogram_name, uint64_t name_hash,
+ base::HistogramBase::Sample sample) { run_loop.Quit(); }));
+ run_loop.Run();
+}
+
+} // namespace
+
+// This class allows system-level tests to be carried out that do not interfere
+// with an existing system-level install.
+class FakeInstallDetails : public install_static::PrimaryInstallDetails {
+ public:
+ // Copy template from first mode from install modes. Some of the values will
+ // then be overridden.
+ FakeInstallDetails() : constants_(install_static::kInstallModes[0]) {
+ // AppGuid determines registry locations, so use a test one.
+#if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
+ constants_.app_guid = L"testguid";
+#endif
+
+ // This is the CLSID of the test interface, used if
+ // kElevatorClsIdForTestingSwitch is supplied on the command line of the
+ // elevation service.
+ constants_.elevator_clsid = {elevation_service::kTestElevatorClsid};
+
+ // This is the IID of the non-channel specific IElevator Interface. See
+ // chrome/elevation_service/elevation_service_idl.idl.
+ constants_.elevator_iid = {
+ 0xA949CB4E,
+ 0xC4F9,
+ 0x44C4,
+ {0xB2, 0x13, 0x6B, 0xF8, 0xAA, 0x9A, 0xC6,
+ 0x9C}}; // IElevator IID and TypeLib
+ // {A949CB4E-C4F9-44C4-B213-6BF8AA9AC69C}
+
+ // These are used to generate the name of the service, so keep them
+ // different from any real installs.
+ constants_.base_app_name = L"testapp";
+ constants_.base_app_id = L"testapp";
+
+ // This is needed for shell_integration::GetDefaultBrowser which runs on
+ // startup.
+ constants_.prog_id_prefix = L"TestHTM";
+
+ set_mode(&constants_);
+ set_system_level(true);
+ }
+
+ FakeInstallDetails(const FakeInstallDetails&) = delete;
+ FakeInstallDetails& operator=(const FakeInstallDetails&) = delete;
+
+ private:
+ install_static::InstallConstants constants_;
+};
+
+class AppBoundEncryptionWinTest : public InProcessBrowserTest {
+ public:
+ AppBoundEncryptionWinTest()
+ : scoped_install_details_(std::make_unique<FakeInstallDetails>()) {}
+
+ protected:
+ void SetUp() override {
+ if (base::GetCurrentProcessIntegrityLevel() != base::HIGH_INTEGRITY)
+ GTEST_SKIP() << "Elevation is required for this test.";
+ enable_metrics_feature_.InitAndEnableFeature(
+ features::kAppBoundEncryptionMetrics);
+ InstallService();
+ InProcessBrowserTest::SetUp();
+ }
+
+ void TearDown() override {
+ if (base::GetCurrentProcessIntegrityLevel() != base::HIGH_INTEGRITY)
+ return;
+ InProcessBrowserTest::TearDown();
+ UnInstallService();
+ }
+
+ base::HistogramTester histogram_tester_;
+ base::test::ScopedFeatureList enable_metrics_feature_;
+
+ private:
+ static bool InstallService() {
+ base::FilePath exe_dir;
+ base::PathService::Get(base::DIR_EXE, &exe_dir);
+ base::CommandLine service_cmd(
+ exe_dir.Append(installer::kElevationServiceExe));
+ service_cmd.AppendSwitch(
+ elevation_service::switches::kElevatorClsIdForTestingSwitch);
+ installer::InstallServiceWorkItem install_service_work_item(
+ install_static::GetElevationServiceName(),
+ install_static::GetElevationServiceDisplayName(), SERVICE_DEMAND_START,
+ service_cmd, base::CommandLine(base::CommandLine::NO_PROGRAM),
+ install_static::GetClientStateKeyPath(),
+ {install_static::GetElevatorClsid()},
+ {install_static::GetElevatorIid()});
+ install_service_work_item.set_best_effort(true);
+ install_service_work_item.set_rollback_enabled(false);
+ return install_service_work_item.Do();
+ }
+
+ static bool UnInstallService() {
+ return installer::InstallServiceWorkItem::DeleteService(
+ install_static::GetElevationServiceName(),
+ install_static::GetClientStateKeyPath(),
+ {install_static::GetElevatorClsid()},
+ {install_static::GetElevatorIid()});
+ }
+
+ install_static::ScopedInstallDetails scoped_install_details_;
+};
+
+// Test the basic interface to Encrypt and Decrypt data.
+IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTest, EncryptDecrypt) {
+ ASSERT_TRUE(install_static::IsSystemInstall());
+ const std::string plaintext("plaintext");
+ std::string ciphertext;
+ DWORD last_error;
+
+ HRESULT hr = os_crypt::EncryptAppBoundString(
+ ProtectionLevel::PATH_VALIDATION, plaintext, ciphertext, last_error);
+
+ ASSERT_HRESULT_SUCCEEDED(hr);
+
+ std::string returned_plaintext;
+ hr = os_crypt::DecryptAppBoundString(ciphertext, returned_plaintext,
+ last_error);
+
+ ASSERT_HRESULT_SUCCEEDED(hr);
+ EXPECT_EQ(plaintext, returned_plaintext);
+}
+
+// These tests verify that the metrics are recorded correctly. The first load of
+// browser in the PRE_ test stores the "Test Key" with app-bound encryption and
+// the second stage of the test verifies it can be retrieved successfully.
+IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTest, PRE_MetricsTest) {
+ ASSERT_TRUE(install_static::IsSystemInstall());
+ // These histograms are recorded on a background worker thread, so the test
+ // needs to wait until this task completes and the histograms are recorded.
+ WaitForHistogram("OSCrypt.AppBoundEncryption.Encrypt.ResultCode");
+ histogram_tester_.ExpectBucketCount(
+ "OSCrypt.AppBoundEncryption.Encrypt.ResultCode", S_OK, 1);
+
+ WaitForHistogram("OSCrypt.AppBoundEncryption.Encrypt.Time");
+}
+
+IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTest, MetricsTest) {
+ ASSERT_TRUE(install_static::IsSystemInstall());
+ // These histograms are recorded on a background worker thread, so the test
+ // needs to wait until this task completes and the histograms are recorded.
+ WaitForHistogram("OSCrypt.AppBoundEncryption.Decrypt.ResultCode");
+ histogram_tester_.ExpectBucketCount(
+ "OSCrypt.AppBoundEncryption.Decrypt.ResultCode", S_OK, 1);
+
+ WaitForHistogram("OSCrypt.AppBoundEncryption.Decrypt.Time");
+}
+
+// Run this test manually to force uninstall the service.
+IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTest, MANUAL_Uninstall) {}
+
+class AppBoundEncryptionWinTestNoService : public InProcessBrowserTest {
+ public:
+ AppBoundEncryptionWinTestNoService()
+ : scoped_install_details_(std::make_unique<FakeInstallDetails>()) {}
+
+ private:
+ install_static::ScopedInstallDetails scoped_install_details_;
+};
+
+IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTestNoService, NoService) {
+ const std::string plaintext("plaintext");
+ std::string ciphertext;
+ DWORD last_error;
+
+ HRESULT hr = os_crypt::EncryptAppBoundString(
+ ProtectionLevel::PATH_VALIDATION, plaintext, ciphertext, last_error);
+
+ EXPECT_EQ(REGDB_E_CLASSNOTREG, hr);
+ EXPECT_EQ(DWORD{ERROR_GEN_FAILURE}, last_error);
+}
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 8bfc3599..badad2b 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -423,6 +423,7 @@
#include "chrome/browser/font_prewarmer_tab_helper.h"
#include "chrome/browser/media/cdm_pref_service_helper.h"
#include "chrome/browser/media/media_foundation_service_monitor.h"
+#include "chrome/browser/os_crypt/app_bound_encryption_metrics_win.h"
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chrome/browser/win/conflicts/incompatible_applications_updater.h"
#include "chrome/browser/win/conflicts/module_database.h"
@@ -1181,6 +1182,7 @@
component_updater::RegisterPrefsForSwReporter(registry);
safe_browsing::RegisterChromeCleanerScanCompletionTimePref(registry);
MediaFoundationServiceMonitor::RegisterPrefs(registry);
+ os_crypt::RegisterLocalStatePrefs(registry);
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
IncompatibleApplicationsUpdater::RegisterLocalStatePrefs(registry);
ModuleDatabase::RegisterLocalStatePrefs(registry);