// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.h"
#include <windows.h>
#include <memory>
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/command_line.h"
#include "base/notreached.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/win_key_rotation_command.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/mock_key_network_delegate.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/key_persistence_delegate.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/shared_command_constants.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service/rotate_util.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/util/util_constants.h"
#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::Invoke;
using testing::StrictMock;
using BPKUR = enterprise_management::BrowserPublicKeyUploadRequest;
namespace enterprise_connectors {
using test::MockKeyNetworkDelegate;
using HttpResponseCode = KeyNetworkDelegate::HttpResponseCode;
constexpr HttpResponseCode kSuccessCode = 200;
HRESULT MockRunGoogleUpdateElevatedCommandFn(
HttpResponseCode upload_response_code,
std::string expected_dm_token,
std::string expected_client_id,
const wchar_t* command,
const std::vector<std::string>& args,
std::optional<DWORD>* return_code) {
base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
CHECK(args.size() == 3);
cmd_line.AppendSwitchASCII(switches::kRotateDTKey, args[0]);
cmd_line.AppendSwitchASCII(switches::kDmServerUrl, args[1]);
cmd_line.AppendSwitchASCII(switches::kNonce, args[2]);
auto mock_network_delegate =
std::make_unique<StrictMock<MockKeyNetworkDelegate>>();
EXPECT_CALL(*mock_network_delegate, SendPublicKeyToDmServer(_, _, _, _))
.WillOnce(Invoke(
[upload_response_code, expected_dm_token, expected_client_id, args](
const GURL& url, const std::string& dm_token,
const std::string& body, base::OnceCallback<void(int)> callback) {
// Check if the DM Server URL contains the correct Client ID
CHECK(url.spec().find(expected_client_id) != std::string::npos);
// Check if the correct DM Token is being uploaded
CHECK_EQ(dm_token, expected_dm_token);
// TODO(b/269746642): add a check for the 'body' parameter above
std::move(callback).Run(upload_response_code);
}));
const auto result = enterprise_connectors::RotateDeviceTrustKey(
enterprise_connectors::KeyRotationManager::Create(
std::move(mock_network_delegate)),
cmd_line, install_static::GetChromeChannel());
switch (result) {
case enterprise_connectors::KeyRotationResult::kSucceeded:
*return_code = installer::ROTATE_DTKEY_SUCCESS;
break;
case enterprise_connectors::KeyRotationResult::kInsufficientPermissions:
*return_code = installer::ROTATE_DTKEY_FAILED_PERMISSIONS;
break;
case enterprise_connectors::KeyRotationResult::kFailedKeyConflict:
*return_code = installer::ROTATE_DTKEY_FAILED_CONFLICT;
break;
case enterprise_connectors::KeyRotationResult::kFailed:
default:
*return_code = installer::ROTATE_DTKEY_FAILED;
break;
}
return S_OK;
}
DeviceTrustTestEnvironmentWin::DeviceTrustTestEnvironmentWin()
: DeviceTrustTestEnvironment("device_trust_test_environment_win",
kSuccessCode),
install_details_(true) {
KeyRotationCommandFactory::SetFactoryInstanceForTesting(this);
}
DeviceTrustTestEnvironmentWin::~DeviceTrustTestEnvironmentWin() {
KeyRotationCommandFactory::ClearFactoryInstanceForTesting();
}
std::unique_ptr<KeyRotationCommand>
DeviceTrustTestEnvironmentWin::CreateCommand(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
policy::BrowserDMTokenStorage* dm_token_storage,
policy::DeviceManagementService* device_management_service) {
if (!worker_thread_.IsRunning()) {
// Make sure the worker thread is running. Its task runner can be reused for
// all created commands, and its destruction will be handled automatically.
bool started = worker_thread_.StartAndWaitForTesting();
CHECK(started);
}
return std::make_unique<WinKeyRotationCommand>(
base::BindRepeating(&MockRunGoogleUpdateElevatedCommandFn,
upload_response_code_, expected_dm_token_,
expected_client_id_),
worker_thread_.task_runner());
}
void DeviceTrustTestEnvironmentWin::SetUpExistingKey() {
auto trust_level = BPKUR::CHROME_BROWSER_HW_KEY;
auto key_pair = key_persistence_delegate_->CreateKeyPair();
EXPECT_TRUE(key_persistence_delegate_->StoreKeyPair(
trust_level, key_pair->key()->GetWrappedKey()));
}
void DeviceTrustTestEnvironmentWin::ClearExistingKey() {
EXPECT_TRUE(key_persistence_delegate_->StoreKeyPair(
BPKUR::KEY_TRUST_LEVEL_UNSPECIFIED, std::vector<uint8_t>()));
EXPECT_FALSE(KeyExists());
}
std::vector<uint8_t> DeviceTrustTestEnvironmentWin::GetWrappedKey() {
std::vector<uint8_t> wrapped_key;
auto loaded_key_pair = key_persistence_delegate_->LoadKeyPair(
KeyStorageType::kPermanent, nullptr);
if (loaded_key_pair) {
auto* key_pointer = loaded_key_pair->key();
if (key_pointer) {
wrapped_key = key_pointer->GetWrappedKey();
}
}
return wrapped_key;
}
} // namespace enterprise_connectors