blob: 074e5d44586cc7c3aef2aef1344fd9a0041c5f99 [file] [log] [blame]
// Copyright 2020 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/ash/attestation/tpm_challenge_key_subtle.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/time/time.h"
#include "chrome/browser/ash/attestation/mock_machine_certificate_uploader.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key_result.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/fake_user_private_token_kpm_service.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/mock_key_permissions_manager.h"
#include "chrome/browser/chromeos/platform_keys/key_permissions/user_private_token_kpm_service_factory.h"
#include "chrome/browser/platform_keys/platform_keys.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/attestation/mock_attestation_flow.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/attestation/fake_attestation_client.h"
#include "chromeos/dbus/attestation/interface.pb.h"
#include "chromeos/dbus/constants/attestation_constants.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using base::test::RunOnceCallback;
using testing::_;
using testing::Invoke;
using testing::StrictMock;
namespace ash {
namespace attestation {
namespace {
constexpr char kTestUserEmail[] = "test@google.com";
constexpr char kTestUserGaiaId[] = "test_gaia_id";
constexpr char kEmptyKeyName[] = "";
constexpr char kNonDefaultKeyName[] = "key_name_123";
constexpr char kFakeCertificate[] = "fake_cert";
const char* GetDefaultKeyName(AttestationKeyType type) {
switch (type) {
case KEY_DEVICE:
return kEnterpriseMachineKey;
case KEY_USER:
return kEnterpriseUserKey;
}
}
std::string GetChallenge() {
constexpr uint8_t kBuffer[] = {0x0, 0x1, 0x2, 'c', 'h',
'a', 'l', 0xfd, 0xfe, 0xff};
return std::string(reinterpret_cast<const char*>(kBuffer), sizeof(kBuffer));
}
std::string GetChallengeResponse(bool include_spkac) {
return AttestationClient::Get()
->GetTestInterface()
->GetEnterpriseChallengeFakeSignature(GetChallenge(), include_spkac);
}
std::string GetPublicKey() {
constexpr uint8_t kBuffer[] = {0x0, 0x1, 0x2, 'p', 'u',
'b', 'k', 0xfd, 0xfe, 0xff};
return std::string(reinterpret_cast<const char*>(kBuffer), sizeof(kBuffer));
}
class CallbackObserver {
public:
TpmChallengeKeyCallback GetCallback() {
return base::BindOnce(&CallbackObserver::Callback, base::Unretained(this));
}
bool IsResultReceived() const { return result_.has_value(); }
const TpmChallengeKeyResult& GetResult() const {
CHECK(result_.has_value()) << "Callback was never called";
return result_.value();
}
void WaitForCallback() { loop_.Run(); }
private:
void Callback(const TpmChallengeKeyResult& result) {
CHECK(!result_.has_value()) << "Callback was called more than once";
result_ = result;
loop_.Quit();
}
base::RunLoop loop_;
absl::optional<TpmChallengeKeyResult> result_;
};
template <typename T>
struct CallbackHolder {
void SaveCallback(T new_callback) {
CHECK(callback.is_null());
callback = std::move(new_callback);
}
T callback;
};
//================= MockableFakeAttestationFlow ================================
class MockableFakeAttestationFlow : public MockAttestationFlow {
public:
MockableFakeAttestationFlow() {
ON_CALL(*this, GetCertificate(_, _, _, _, _, _))
.WillByDefault(
Invoke(this, &MockableFakeAttestationFlow::GetCertificateInternal));
}
~MockableFakeAttestationFlow() override = default;
void set_status(AttestationStatus status) { status_ = status; }
private:
void GetCertificateInternal(
AttestationCertificateProfile /*certificate_profile*/,
const AccountId& account_id,
const std::string& /*request_origin*/,
bool /*force_new_key*/,
const std::string& key_name,
CertificateCallback callback) {
std::string certificate;
if (status_ == ATTESTATION_SUCCESS) {
certificate = certificate_;
AttestationClient::Get()
->GetTestInterface()
->GetMutableKeyInfoReply(cryptohome::Identification(account_id).id(),
key_name)
->set_public_key(public_key_);
}
std::move(callback).Run(status_, certificate);
}
AttestationStatus status_ = ATTESTATION_SUCCESS;
const std::string certificate_ = kFakeCertificate;
const std::string public_key_ = GetPublicKey();
};
//==================== TpmChallengeKeySubtleTestBase ===========================
// Describes which kind of profile will be used with TpmChallengeKeySubtle.
enum class TestProfileChoice {
// Pass nullptr as Profile.
kNoProfile,
// Pass the sign-in Profile.
kSigninProfile,
// Pass the Profile of an unaffiliated user.
kUnaffiliatedProfile,
// Pass the Profile of an affiliated user.
kAffiliatedProfile
};
class TpmChallengeKeySubtleTestBase : public ::testing::Test {
public:
explicit TpmChallengeKeySubtleTestBase(TestProfileChoice test_profile_choice);
~TpmChallengeKeySubtleTestBase();
protected:
// ::testing::Test:
void SetUp() override;
TestingProfile* CreateUserProfile(bool is_affiliated);
TestingProfile* GetProfile();
ScopedCrosSettingsTestHelper* GetCrosSettingsHelper();
chromeos::StubInstallAttributes* GetInstallAttributes();
// Runs StartPrepareKeyStep and checks that the result is equal to
// |public_key|.
void RunOneStepAndExpect(AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& public_key);
// Runs StartPrepareKeyStep and checks that the result is success. Then runs
// StartSignChallengeStep and checks that the result is equal to
// |challenge_response|.
void RunTwoStepsAndExpect(AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& challenge_response);
// Runs first two steps and checks that results are success. Then runs
// StartRegisterKeyStep and checks that the result is equal to
// |register_result|.
void RunThreeStepsAndExpect(AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& register_result);
protected:
const TestProfileChoice test_profile_choice_;
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
StrictMock<MockableFakeAttestationFlow> mock_attestation_flow_;
std::unique_ptr<platform_keys::MockKeyPermissionsManager>
system_token_key_permissions_manager_;
std::unique_ptr<platform_keys::MockKeyPermissionsManager>
user_private_token_key_permissions_manager_;
MockMachineCertificateUploader mock_cert_uploader_;
std::unique_ptr<TpmChallengeKeySubtle> challenge_key_subtle_;
TestingProfileManager testing_profile_manager_;
FakeChromeUserManager fake_user_manager_;
// A sign-in Profile is always created in SetUp().
TestingProfile* signin_profile_ = nullptr;
// The profile that will be passed to TpmChallengeKeySubtle - can be nullptr.
TestingProfile* testing_profile_ = nullptr;
};
TpmChallengeKeySubtleTestBase::TpmChallengeKeySubtleTestBase(
TestProfileChoice test_profile_choice)
: test_profile_choice_(test_profile_choice),
testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {
::chromeos::TpmManagerClient::InitializeFake();
::chromeos::AttestationClient::InitializeFake();
CHECK(testing_profile_manager_.SetUp());
challenge_key_subtle_ = std::make_unique<TpmChallengeKeySubtleImpl>(
&mock_attestation_flow_, &mock_cert_uploader_);
// By default make it reply that the certificate is already uploaded.
ON_CALL(mock_cert_uploader_, WaitForUploadComplete)
.WillByDefault(RunOnceCallback<0>(true));
}
TpmChallengeKeySubtleTestBase::~TpmChallengeKeySubtleTestBase() {
::chromeos::AttestationClient::Shutdown();
::chromeos::TpmManagerClient::Shutdown();
}
void TpmChallengeKeySubtleTestBase::SetUp() {
signin_profile_ =
testing_profile_manager_.CreateTestingProfile(chrome::kInitialProfile);
switch (test_profile_choice_) {
case TestProfileChoice::kNoProfile:
testing_profile_ = nullptr;
break;
case TestProfileChoice::kSigninProfile:
testing_profile_ = signin_profile_;
break;
case TestProfileChoice::kUnaffiliatedProfile:
testing_profile_ = CreateUserProfile(/*is_affiliated=*/false);
break;
case TestProfileChoice::kAffiliatedProfile:
testing_profile_ = CreateUserProfile(/*is_affiliated=*/true);
testing_profile_->GetTestingPrefService()->SetManagedPref(
prefs::kAttestationEnabled, std::make_unique<base::Value>(true));
break;
}
GetInstallAttributes()->SetCloudManaged("google.com", "device_id");
GetCrosSettingsHelper()->ReplaceDeviceSettingsProviderWithStub();
GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled,
true);
system_token_key_permissions_manager_ =
std::make_unique<platform_keys::MockKeyPermissionsManager>();
platform_keys::KeyPermissionsManagerImpl::
SetSystemTokenKeyPermissionsManagerForTesting(
system_token_key_permissions_manager_.get());
if (!testing_profile_) {
return;
}
user_private_token_key_permissions_manager_ =
std::make_unique<platform_keys::MockKeyPermissionsManager>();
platform_keys::UserPrivateTokenKeyPermissionsManagerServiceFactory::
GetInstance()
->SetTestingFactory(
testing_profile_,
base::BindRepeating(
&platform_keys::
BuildFakeUserPrivateTokenKeyPermissionsManagerService,
user_private_token_key_permissions_manager_.get()));
}
TestingProfile* TpmChallengeKeySubtleTestBase::CreateUserProfile(
bool is_affiliated) {
TestingProfile* testing_profile =
testing_profile_manager_.CreateTestingProfile(kTestUserEmail);
CHECK(testing_profile);
auto test_account =
AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId);
fake_user_manager_.AddUserWithAffiliation(test_account, is_affiliated);
ProfileHelper::Get()->SetUserToProfileMappingForTesting(
fake_user_manager_.GetPrimaryUser(), testing_profile);
return testing_profile;
}
TestingProfile* TpmChallengeKeySubtleTestBase::GetProfile() {
return testing_profile_;
}
ScopedCrosSettingsTestHelper*
TpmChallengeKeySubtleTestBase::GetCrosSettingsHelper() {
return signin_profile_->ScopedCrosSettingsTestHelper();
}
chromeos::StubInstallAttributes*
TpmChallengeKeySubtleTestBase::GetInstallAttributes() {
return GetCrosSettingsHelper()->InstallAttributes();
}
void TpmChallengeKeySubtleTestBase::RunOneStepAndExpect(
AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& public_key) {
CallbackObserver callback_observer;
challenge_key_subtle_->StartPrepareKeyStep(key_type, will_register_key,
key_name, GetProfile(),
callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(), public_key);
}
void TpmChallengeKeySubtleTestBase::RunTwoStepsAndExpect(
AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& challenge_response) {
RunOneStepAndExpect(key_type, will_register_key, key_name,
TpmChallengeKeyResult::MakePublicKey(GetPublicKey()));
CallbackObserver callback_observer;
challenge_key_subtle_->StartSignChallengeStep(
GetChallenge(), callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(), challenge_response);
}
void TpmChallengeKeySubtleTestBase::RunThreeStepsAndExpect(
AttestationKeyType key_type,
bool will_register_key,
const std::string& key_name,
const TpmChallengeKeyResult& register_result) {
RunTwoStepsAndExpect(key_type, will_register_key, key_name,
TpmChallengeKeyResult::MakeChallengeResponse(
GetChallengeResponse(will_register_key)));
CallbackObserver callback_observer;
challenge_key_subtle_->StartRegisterKeyStep(callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(), register_result);
}
//==============================================================================
// Tests all Profile* options where TpmChallengeKeySubtle can be used with
// device-wide keys, including passing profile=nullptr.
class DeviceKeysAccessTpmChallengeKeySubtleTest
: public TpmChallengeKeySubtleTestBase,
public ::testing::WithParamInterface<TestProfileChoice> {
public:
DeviceKeysAccessTpmChallengeKeySubtleTest()
: TpmChallengeKeySubtleTestBase(GetParam()) {}
~DeviceKeysAccessTpmChallengeKeySubtleTest() = default;
};
INSTANTIATE_TEST_SUITE_P(
DeviceKeysAccessTpmChallengeKeySubtleTest,
DeviceKeysAccessTpmChallengeKeySubtleTest,
testing::Values(TestProfileChoice::kNoProfile,
TestProfileChoice::kSigninProfile,
TestProfileChoice::kAffiliatedProfile));
// Tests TpmChallengeKeySubtle with the sign-in Profile only.
class SigninProfileTpmChallengeKeySubtleTest
: public TpmChallengeKeySubtleTestBase {
public:
SigninProfileTpmChallengeKeySubtleTest()
: TpmChallengeKeySubtleTestBase(TestProfileChoice::kSigninProfile) {}
~SigninProfileTpmChallengeKeySubtleTest() override = default;
};
// Tests TpmChallengeKeySubtle with an affiliated user profile only.
class AffiliatedUserTpmChallengeKeySubtleTest
: public TpmChallengeKeySubtleTestBase {
public:
AffiliatedUserTpmChallengeKeySubtleTest()
: TpmChallengeKeySubtleTestBase(TestProfileChoice::kAffiliatedProfile) {}
~AffiliatedUserTpmChallengeKeySubtleTest() override = default;
};
// Tests TpmChallengeKeySubtle with an unaffiliated user profile only.
class UnaffiliatedUserTpmChallengeKeySubtleTest
: public TpmChallengeKeySubtleTestBase {
public:
UnaffiliatedUserTpmChallengeKeySubtleTest()
: TpmChallengeKeySubtleTestBase(TestProfileChoice::kUnaffiliatedProfile) {
}
~UnaffiliatedUserTpmChallengeKeySubtleTest() override = default;
};
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest,
DeviceKeyNonEnterpriseDevice) {
GetInstallAttributes()->SetConsumerOwned();
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kNonEnterpriseDeviceError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest,
DeviceKeyDeviceAttestationDisabled) {
GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled,
false);
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kDevicePolicyDisabledError));
}
TEST_F(UnaffiliatedUserTpmChallengeKeySubtleTest, DeviceKeyUserNotManaged) {
RunOneStepAndExpect(KEY_DEVICE,
/*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kUserNotManagedError));
}
TEST_F(SigninProfileTpmChallengeKeySubtleTest, UserKeyUserKeyNotAvailable) {
RunOneStepAndExpect(
KEY_USER, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kUserKeyNotAvailableError));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, UserKeyUserPolicyDisabled) {
GetProfile()->GetTestingPrefService()->SetManagedPref(
prefs::kAttestationEnabled, std::make_unique<base::Value>(false));
RunOneStepAndExpect(KEY_USER,
/*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kUserPolicyDisabledError));
}
// Checks that a user should be affiliated with a device
TEST_F(UnaffiliatedUserTpmChallengeKeySubtleTest, UserKeyUserNotAffiliated) {
GetProfile()->GetTestingPrefService()->SetManagedPref(
prefs::kAttestationEnabled, std::make_unique<base::Value>(true));
RunOneStepAndExpect(KEY_USER,
/*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kUserNotManagedError));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest,
UserKeyDeviceAttestationDisabled) {
GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled,
false);
RunOneStepAndExpect(
KEY_USER, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kDevicePolicyDisabledError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, DoesKeyExistDbusFailed) {
AttestationClient::Get()
->GetTestInterface()
->GetMutableKeyInfoReply(/*username=*/"", kEnterpriseMachineKey)
->set_status(::attestation::STATUS_DBUS_ERROR);
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(TpmChallengeKeyResultCode::kDbusError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, GetCertificateFailed) {
const AttestationKeyType key_type = KEY_DEVICE;
mock_attestation_flow_.set_status(ATTESTATION_UNSPECIFIED_FAILURE);
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _));
RunOneStepAndExpect(
key_type, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kGetCertificateFailedError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, KeyExists) {
const AttestationKeyType key_type = KEY_DEVICE;
AttestationClient::Get()
->GetTestInterface()
->GetMutableKeyInfoReply(/*username=*/"", kEnterpriseMachineKey)
->set_public_key(GetPublicKey());
RunOneStepAndExpect(key_type, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakePublicKey(GetPublicKey()));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, AttestationNotPrepared) {
chromeos::AttestationClient::Get()
->GetTestInterface()
->ConfigureEnrollmentPreparations(false);
RunOneStepAndExpect(KEY_DEVICE,
/*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kResetRequiredError));
}
// Test that we get a proper error message in case we don't have a TPM.
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, AttestationUnsupported) {
chromeos::AttestationClient::Get()
->GetTestInterface()
->ConfigureEnrollmentPreparations(false);
chromeos::TpmManagerClient::Get()
->GetTestInterface()
->mutable_nonsensitive_status_reply()
->set_is_enabled(false);
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kAttestationUnsupportedError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest,
AttestationPreparedDbusFailed) {
chromeos::AttestationClient::Get()
->GetTestInterface()
->ConfigureEnrollmentPreparationsStatus(::attestation::STATUS_DBUS_ERROR);
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(TpmChallengeKeyResultCode::kDbusError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest,
AttestationPreparedServiceInternalError) {
chromeos::AttestationClient::Get()
->GetTestInterface()
->ConfigureEnrollmentPreparationsStatus(
::attestation::STATUS_NOT_AVAILABLE);
RunOneStepAndExpect(
KEY_DEVICE, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kAttestationServiceInternalError));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest,
DeviceKeyNotRegisteredSuccess) {
const AttestationKeyType key_type = KEY_DEVICE;
const char* const key_name = GetDefaultKeyName(key_type);
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
::attestation::SignEnterpriseChallengeRequest expected_request;
expected_request.set_key_label(key_name);
expected_request.set_domain(std::string());
expected_request.set_device_id(GetInstallAttributes()->GetDeviceId());
AttestationClient::Get()
->GetTestInterface()
->AllowlistSignEnterpriseChallengeKey(expected_request);
RunTwoStepsAndExpect(key_type, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeChallengeResponse(
GetChallengeResponse(/*include_spkac=*/false)));
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, DeviceKeyRegisteredSuccess) {
const AttestationKeyType key_type = KEY_DEVICE;
const char* const key_name = kNonDefaultKeyName;
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
::attestation::SignEnterpriseChallengeRequest expected_request;
expected_request.set_key_label(GetDefaultKeyName(key_type));
expected_request.set_key_name_for_spkac(key_name);
expected_request.set_domain(std::string());
expected_request.set_device_id(GetInstallAttributes()->GetDeviceId());
AttestationClient::Get()
->GetTestInterface()
->AllowlistSignEnterpriseChallengeKey(expected_request);
AttestationClient::Get()->GetTestInterface()->AllowlistRegisterKey(
/*username=*/"", key_name);
EXPECT_CALL(
*system_token_key_permissions_manager_,
AllowKeyForUsage(/*callback=*/_, platform_keys::KeyUsage::kCorporate,
GetPublicKey()))
.WillOnce(RunOnceCallback<0>(chromeos::platform_keys::Status::kSuccess));
RunThreeStepsAndExpect(key_type, /*will_register_key=*/true, key_name,
TpmChallengeKeyResult::MakeSuccess());
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, UserKeyNotRegisteredSuccess) {
const AttestationKeyType key_type = KEY_USER;
const char* const key_name = GetDefaultKeyName(key_type);
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
::attestation::SignEnterpriseChallengeRequest expected_request;
expected_request.set_username(kTestUserEmail);
expected_request.set_key_label(GetDefaultKeyName(key_type));
expected_request.set_domain(kTestUserEmail);
expected_request.set_device_id(GetInstallAttributes()->GetDeviceId());
AttestationClient::Get()
->GetTestInterface()
->AllowlistSignEnterpriseChallengeKey(expected_request);
RunTwoStepsAndExpect(key_type, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeChallengeResponse(
GetChallengeResponse(/*include_spkac=*/false)));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, UserKeyRegisteredSuccess) {
const AttestationKeyType key_type = KEY_USER;
const char* const key_name = kNonDefaultKeyName;
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
::attestation::SignEnterpriseChallengeRequest expected_request;
expected_request.set_username(kTestUserEmail);
expected_request.set_key_label(kNonDefaultKeyName);
expected_request.set_domain(kTestUserEmail);
expected_request.set_device_id(GetInstallAttributes()->GetDeviceId());
AttestationClient::Get()
->GetTestInterface()
->AllowlistSignEnterpriseChallengeKey(expected_request);
AttestationClient::Get()->GetTestInterface()->AllowlistRegisterKey(
kTestUserEmail, key_name);
EXPECT_CALL(*user_private_token_key_permissions_manager_,
AllowKeyForUsage(/*callback=*/_,
chromeos::platform_keys::KeyUsage::kCorporate,
GetPublicKey()))
.WillOnce(RunOnceCallback<0>(chromeos::platform_keys::Status::kSuccess));
RunThreeStepsAndExpect(key_type, /*will_register_key=*/true, key_name,
TpmChallengeKeyResult::MakeSuccess());
}
TEST_P(DeviceKeysAccessTpmChallengeKeySubtleTest, SignChallengeFailed) {
const AttestationKeyType key_type = KEY_DEVICE;
EXPECT_CALL(mock_attestation_flow_,
GetCertificate(_, _, _, _, GetDefaultKeyName(key_type), _));
// The signing operations fails because we don't allowlist any key.
RunTwoStepsAndExpect(
key_type, /*will_register_key=*/false, kEmptyKeyName,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kSignChallengeFailedError));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, RestorePreparedKeyState) {
const AttestationKeyType key_type = KEY_USER;
const char* const key_name = kNonDefaultKeyName;
// Inject mocks into the next result of CreateForPreparedKey.
TpmChallengeKeySubtleFactory::SetForTesting(
std::make_unique<TpmChallengeKeySubtleImpl>(&mock_attestation_flow_,
&mock_cert_uploader_));
challenge_key_subtle_ = TpmChallengeKeySubtleFactory::CreateForPreparedKey(
key_type, /*will_register_key=*/true, key_name, GetPublicKey(),
GetProfile());
::attestation::SignEnterpriseChallengeRequest expected_request;
expected_request.set_username(kTestUserEmail);
expected_request.set_key_label(kNonDefaultKeyName);
expected_request.set_domain(kTestUserEmail);
expected_request.set_device_id(GetInstallAttributes()->GetDeviceId());
AttestationClient::Get()
->GetTestInterface()
->AllowlistSignEnterpriseChallengeKey(expected_request);
{
CallbackObserver callback_observer;
challenge_key_subtle_->StartSignChallengeStep(
GetChallenge(), callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(),
TpmChallengeKeyResult::MakeChallengeResponse(
GetChallengeResponse(/*include_spkac=*/true)));
}
AttestationClient::Get()->GetTestInterface()->AllowlistRegisterKey(
kTestUserEmail, key_name);
EXPECT_CALL(
*user_private_token_key_permissions_manager_,
AllowKeyForUsage(/*callback=*/_, platform_keys::KeyUsage::kCorporate,
GetPublicKey()))
.WillOnce(RunOnceCallback<0>(chromeos::platform_keys::Status::kSuccess));
{
CallbackObserver callback_observer;
challenge_key_subtle_->StartRegisterKeyStep(
callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(),
TpmChallengeKeyResult::MakeSuccess());
}
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, KeyRegistrationFailed) {
const AttestationKeyType key_type = KEY_USER;
const char* const key_name = kNonDefaultKeyName;
// Inject mocks into the next result of CreateForPreparedKey.
TpmChallengeKeySubtleFactory::SetForTesting(
std::make_unique<TpmChallengeKeySubtleImpl>(&mock_attestation_flow_,
&mock_cert_uploader_));
challenge_key_subtle_ = TpmChallengeKeySubtleFactory::CreateForPreparedKey(
key_type, /*will_register_key=*/true, key_name, GetPublicKey(),
GetProfile());
CallbackObserver callback_observer;
challenge_key_subtle_->StartRegisterKeyStep(callback_observer.GetCallback());
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(),
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kKeyRegistrationFailedError));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, GetPublicKeyFailed) {
const char* const key_name = kNonDefaultKeyName;
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
// Force the attestation client to report absence even after successful
// attestation flow.
AttestationClient::Get()
->GetTestInterface()
->GetMutableKeyInfoReply(/*username=*/"", key_name)
->set_status(::attestation::STATUS_INVALID_PARAMETER);
RunOneStepAndExpect(KEY_DEVICE,
/*will_register_key=*/true, key_name,
TpmChallengeKeyResult::MakeError(
TpmChallengeKeyResultCode::kGetPublicKeyFailedError));
}
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, WaitForCertificateUploaded) {
const char* const key_name = kNonDefaultKeyName;
using CallbackHolderT =
CallbackHolder<MachineCertificateUploader::UploadCallback>;
CallbackHolderT callback_holder;
EXPECT_CALL(mock_cert_uploader_, WaitForUploadComplete)
.WillOnce(
testing::Invoke(&callback_holder, &CallbackHolderT::SaveCallback));
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
CallbackObserver callback_observer;
challenge_key_subtle_->StartPrepareKeyStep(
KEY_DEVICE, /*will_register_key=*/true, key_name, GetProfile(),
callback_observer.GetCallback());
// |challenge_key_subtle_| should wait until the certificate is uploaded.
task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(10));
EXPECT_FALSE(callback_observer.IsResultReceived());
// Emulate callback from the certificate uploader, |challenge_key_subtle_|
// should be able to continue now.
std::move(callback_holder.callback).Run(true);
callback_observer.WaitForCallback();
EXPECT_EQ(callback_observer.GetResult(),
TpmChallengeKeyResult::MakePublicKey(GetPublicKey()));
}
// Check that the class works when MachineCertificateUploader is not provided
// (e.g. if device is managed by Active Directory).
TEST_F(AffiliatedUserTpmChallengeKeySubtleTest, NoCertificateUploaderSuccess) {
const char* const key_name = kNonDefaultKeyName;
challenge_key_subtle_ = std::make_unique<TpmChallengeKeySubtleImpl>(
&mock_attestation_flow_, /*machine_certificate_uploader=*/nullptr);
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, key_name, _));
RunOneStepAndExpect(KEY_USER,
/*will_register_key=*/true, key_name,
TpmChallengeKeyResult::MakePublicKey(GetPublicKey()));
}
} // namespace
} // namespace attestation
} // namespace ash