blob: c166c3e45676e4e1f31828541e9ad41e20f252ad [file] [log] [blame]
// Copyright (c) 2013 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/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/callback.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/common/extensions/api/enterprise_platform_keys_private.h"
#include "chrome/common/pref_names.h"
#include "chromeos/attestation/attestation_flow.h"
#include "chromeos/cryptohome/async_method_caller.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/attestation_constants.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/install_attributes.h"
#include "components/account_id/account_id.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/manifest.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace extensions {
namespace api_epkp = api::enterprise_platform_keys_private;
// Base class
const char EPKPChallengeKeyBase::kChallengeBadBase64Error[] =
"Challenge is not base64 encoded.";
const char EPKPChallengeKeyBase::kDevicePolicyDisabledError[] =
"Remote attestation is not enabled for your device.";
const char EPKPChallengeKeyBase::kExtensionNotWhitelistedError[] =
"The extension does not have permission to call this function.";
const char EPKPChallengeKeyBase::kResponseBadBase64Error[] =
"Response cannot be encoded in base64.";
const char EPKPChallengeKeyBase::kSignChallengeFailedError[] =
"Failed to sign the challenge.";
const char EPKPChallengeKeyBase::kUserNotManaged[] =
"The user account is not enterprise managed.";
EPKPChallengeKeyBase::PrepareKeyContext::PrepareKeyContext(
chromeos::attestation::AttestationKeyType key_type,
const AccountId& account_id,
const std::string& key_name,
chromeos::attestation::AttestationCertificateProfile certificate_profile,
bool require_user_consent,
const base::Callback<void(PrepareKeyResult)>& callback)
: key_type(key_type),
account_id(account_id),
key_name(key_name),
certificate_profile(certificate_profile),
require_user_consent(require_user_consent),
callback(callback) {}
EPKPChallengeKeyBase::PrepareKeyContext::PrepareKeyContext(
const PrepareKeyContext& other) = default;
EPKPChallengeKeyBase::PrepareKeyContext::~PrepareKeyContext() {
}
EPKPChallengeKeyBase::EPKPChallengeKeyBase()
: cryptohome_client_(
chromeos::DBusThreadManager::Get()->GetCryptohomeClient()),
async_caller_(cryptohome::AsyncMethodCaller::GetInstance()),
install_attributes_(g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetInstallAttributes()) {
std::unique_ptr<chromeos::attestation::ServerProxy> ca_client(
new chromeos::attestation::AttestationCAClient());
default_attestation_flow_.reset(new chromeos::attestation::AttestationFlow(
async_caller_, cryptohome_client_, std::move(ca_client)));
attestation_flow_ = default_attestation_flow_.get();
}
EPKPChallengeKeyBase::EPKPChallengeKeyBase(
chromeos::CryptohomeClient* cryptohome_client,
cryptohome::AsyncMethodCaller* async_caller,
chromeos::attestation::AttestationFlow* attestation_flow,
chromeos::InstallAttributes* install_attributes) :
cryptohome_client_(cryptohome_client),
async_caller_(async_caller),
attestation_flow_(attestation_flow),
install_attributes_(install_attributes) {
}
EPKPChallengeKeyBase::~EPKPChallengeKeyBase() {
}
void EPKPChallengeKeyBase::GetDeviceAttestationEnabled(
const base::Callback<void(bool)>& callback) const {
chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
chromeos::CrosSettingsProvider::TrustedStatus status =
settings->PrepareTrustedValues(
base::Bind(&EPKPChallengeKeyBase::GetDeviceAttestationEnabled,
base::Unretained(this), callback));
bool value = false;
switch (status) {
case chromeos::CrosSettingsProvider::TRUSTED:
if (!settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value))
value = false;
break;
case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
// Do nothing. This function will be called again when the values are
// ready.
return;
case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
// If the value cannot be trusted, we assume that the device attestation
// is false to be on the safe side.
break;
}
callback.Run(value);
}
bool EPKPChallengeKeyBase::IsEnterpriseDevice() const {
return install_attributes_->IsEnterpriseManaged();
}
bool EPKPChallengeKeyBase::IsExtensionWhitelisted() const {
if (!chromeos::ProfileHelper::Get()->GetUserByProfile(profile_)) {
// Only allow remote attestation for apps that were force-installed on the
// login/signin screen.
// TODO(drcrash): Use a separate device-wide policy for the API.
return Manifest::IsPolicyLocation(extension_->location());
}
if (Manifest::IsComponentLocation(extension_->location())) {
// Note: For this to even be called, the component extension must also be
// whitelisted in chrome/common/extensions/api/_permission_features.json
return true;
}
const base::ListValue* list =
profile_->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist);
base::Value value(extension_->id());
return list->Find(value) != list->end();
}
AccountId EPKPChallengeKeyBase::GetAccountId() const {
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
// Signin profile doesn't have associated user.
if (!user) {
return EmptyAccountId();
}
return user->GetAccountId();
}
bool EPKPChallengeKeyBase::IsUserAffiliated() const {
const user_manager::User* const user =
user_manager::UserManager::Get()->FindUser(GetAccountId());
if (user) {
return user->IsAffiliated();
}
return false;
}
std::string EPKPChallengeKeyBase::GetEnterpriseDomain() const {
return install_attributes_->GetDomain();
}
std::string EPKPChallengeKeyBase::GetUserEmail() const {
return GetAccountId().GetUserEmail();
}
std::string EPKPChallengeKeyBase::GetDeviceId() const {
return install_attributes_->GetDeviceId();
}
void EPKPChallengeKeyBase::PrepareKey(
chromeos::attestation::AttestationKeyType key_type,
const AccountId& account_id,
const std::string& key_name,
chromeos::attestation::AttestationCertificateProfile certificate_profile,
bool require_user_consent,
const base::Callback<void(PrepareKeyResult)>& callback) {
const PrepareKeyContext context = PrepareKeyContext(key_type,
account_id,
key_name,
certificate_profile,
require_user_consent,
callback);
cryptohome_client_->TpmAttestationIsPrepared(
base::BindOnce(&EPKPChallengeKeyBase::IsAttestationPreparedCallback,
base::Unretained(this), context));
}
void EPKPChallengeKeyBase::IsAttestationPreparedCallback(
const PrepareKeyContext& context,
base::Optional<bool> result) {
if (!result.has_value()) {
context.callback.Run(PREPARE_KEY_DBUS_ERROR);
return;
}
if (!result.value()) {
context.callback.Run(PREPARE_KEY_RESET_REQUIRED);
return;
}
// Attestation is available, see if the key we need already exists.
cryptohome_client_->TpmAttestationDoesKeyExist(
context.key_type,
cryptohome::CreateAccountIdentifierFromAccountId(context.account_id),
context.key_name,
base::BindOnce(&EPKPChallengeKeyBase::DoesKeyExistCallback,
base::Unretained(this), context));
}
void EPKPChallengeKeyBase::DoesKeyExistCallback(
const PrepareKeyContext& context,
base::Optional<bool> result) {
if (!result.has_value()) {
context.callback.Run(PREPARE_KEY_DBUS_ERROR);
return;
}
if (result.value()) {
// The key exists. Do nothing more.
context.callback.Run(PREPARE_KEY_OK);
} else {
// The key does not exist. Create a new key and have it signed by PCA.
if (context.require_user_consent) {
// We should ask the user explicitly before sending any private
// information to PCA.
AskForUserConsent(
base::Bind(&EPKPChallengeKeyBase::AskForUserConsentCallback,
base::Unretained(this), context));
} else {
// User consent is not required. Skip to the next step.
AskForUserConsentCallback(context, true);
}
}
}
void EPKPChallengeKeyBase::AskForUserConsent(
const base::Callback<void(bool)>& callback) const {
// TODO(davidyu): right now we just simply reject the request before we have
// a way to ask for user consent.
callback.Run(false);
}
void EPKPChallengeKeyBase::AskForUserConsentCallback(
const PrepareKeyContext& context,
bool result) {
if (!result) {
// The user rejects the request.
context.callback.Run(PREPARE_KEY_USER_REJECTED);
return;
}
// Generate a new key and have it signed by PCA.
attestation_flow_->GetCertificate(
context.certificate_profile, context.account_id,
std::string(), // Not used.
true, // Force a new key to be generated.
base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback,
base::Unretained(this), context.callback));
}
void EPKPChallengeKeyBase::GetCertificateCallback(
const base::Callback<void(PrepareKeyResult)>& callback,
chromeos::attestation::AttestationStatus status,
const std::string& pem_certificate_chain) {
if (status != chromeos::attestation::ATTESTATION_SUCCESS) {
callback.Run(PREPARE_KEY_GET_CERTIFICATE_FAILED);
return;
}
callback.Run(PREPARE_KEY_OK);
}
// Implementation of ChallengeMachineKey()
const char EPKPChallengeMachineKey::kGetCertificateFailedError[] =
"Failed to get Enterprise machine certificate. Error code = %d";
const char EPKPChallengeMachineKey::kKeyRegistrationFailedError[] =
"Machine key registration failed.";
const char EPKPChallengeMachineKey::kNonEnterpriseDeviceError[] =
"The device is not enterprise enrolled.";
const char EPKPChallengeMachineKey::kKeyName[] = "attest-ent-machine";
EPKPChallengeMachineKey::EPKPChallengeMachineKey() : EPKPChallengeKeyBase() {
}
EPKPChallengeMachineKey::EPKPChallengeMachineKey(
chromeos::CryptohomeClient* cryptohome_client,
cryptohome::AsyncMethodCaller* async_caller,
chromeos::attestation::AttestationFlow* attestation_flow,
chromeos::InstallAttributes* install_attributes) :
EPKPChallengeKeyBase(cryptohome_client,
async_caller,
attestation_flow,
install_attributes) {
}
EPKPChallengeMachineKey::~EPKPChallengeMachineKey() {
}
void EPKPChallengeMachineKey::Run(
scoped_refptr<UIThreadExtensionFunction> caller,
const ChallengeKeyCallback& callback,
const std::string& challenge,
bool register_key) {
callback_ = callback;
profile_ = ChromeExtensionFunctionDetails(caller.get()).GetProfile();
extension_ = scoped_refptr<const Extension>(caller->extension());
// Check if the device is enterprise enrolled.
if (!IsEnterpriseDevice()) {
callback_.Run(false, kNonEnterpriseDeviceError);
return;
}
// Check if the extension is whitelisted in the user policy.
if (!IsExtensionWhitelisted()) {
callback_.Run(false, kExtensionNotWhitelistedError);
return;
}
// Check whether the user is managed unless the signin profile is used.
if (chromeos::ProfileHelper::Get()->GetUserByProfile(profile_) &&
!IsUserAffiliated()) {
callback_.Run(false, kUserNotManaged);
return;
}
// Check if RA is enabled in the device policy.
GetDeviceAttestationEnabled(
base::Bind(&EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback,
base::Unretained(this), challenge, register_key));
}
void EPKPChallengeMachineKey::DecodeAndRun(
scoped_refptr<UIThreadExtensionFunction> caller,
const ChallengeKeyCallback& callback,
const std::string& encoded_challenge,
bool register_key) {
std::string challenge;
if (!base::Base64Decode(encoded_challenge, &challenge)) {
callback.Run(false, kChallengeBadBase64Error);
return;
}
Run(caller, callback, challenge, register_key);
}
void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback(
const std::string& challenge,
bool register_key,
bool enabled) {
if (!enabled) {
callback_.Run(false, kDevicePolicyDisabledError);
return;
}
PrepareKey(chromeos::attestation::KEY_DEVICE,
EmptyAccountId(), // Not used.
kKeyName,
chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE,
false, // user consent is not required.
base::Bind(&EPKPChallengeMachineKey::PrepareKeyCallback,
base::Unretained(this), challenge, register_key));
}
void EPKPChallengeMachineKey::PrepareKeyCallback(const std::string& challenge,
bool register_key,
PrepareKeyResult result) {
if (result != PREPARE_KEY_OK) {
callback_.Run(false,
base::StringPrintf(kGetCertificateFailedError, result));
return;
}
// Everything is checked. Sign the challenge.
async_caller_->TpmAttestationSignEnterpriseChallenge(
chromeos::attestation::KEY_DEVICE,
cryptohome::Identification(), // Not used.
kKeyName, GetEnterpriseDomain(), GetDeviceId(),
register_key ? chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY
: chromeos::attestation::CHALLENGE_OPTION_NONE,
challenge,
base::Bind(&EPKPChallengeMachineKey::SignChallengeCallback,
base::Unretained(this), register_key));
}
void EPKPChallengeMachineKey::SignChallengeCallback(
bool register_key,
bool success,
const std::string& response) {
if (!success) {
callback_.Run(false, kSignChallengeFailedError);
return;
}
if (register_key) {
async_caller_->TpmAttestationRegisterKey(
chromeos::attestation::KEY_DEVICE,
cryptohome::Identification(), // Not used.
kKeyName,
base::Bind(&EPKPChallengeMachineKey::RegisterKeyCallback,
base::Unretained(this), response));
} else {
RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE);
}
}
void EPKPChallengeMachineKey::RegisterKeyCallback(
const std::string& response,
bool success,
cryptohome::MountError return_code) {
if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
callback_.Run(false, kKeyRegistrationFailedError);
return;
}
callback_.Run(true, response);
}
// Implementation of ChallengeUserKey()
const char EPKPChallengeUserKey::kGetCertificateFailedError[] =
"Failed to get Enterprise user certificate. Error code = %d";
const char EPKPChallengeUserKey::kKeyRegistrationFailedError[] =
"Key registration failed.";
const char EPKPChallengeUserKey::kUserPolicyDisabledError[] =
"Remote attestation is not enabled for your account.";
const char EPKPChallengeUserKey::kUserKeyNotAvailable[] =
"User keys cannot be challenged in this profile.";
const char EPKPChallengeUserKey::kKeyName[] = "attest-ent-user";
EPKPChallengeUserKey::EPKPChallengeUserKey() : EPKPChallengeKeyBase() {
}
EPKPChallengeUserKey::EPKPChallengeUserKey(
chromeos::CryptohomeClient* cryptohome_client,
cryptohome::AsyncMethodCaller* async_caller,
chromeos::attestation::AttestationFlow* attestation_flow,
chromeos::InstallAttributes* install_attributes) :
EPKPChallengeKeyBase(cryptohome_client,
async_caller,
attestation_flow,
install_attributes) {
}
EPKPChallengeUserKey::~EPKPChallengeUserKey() {
}
void EPKPChallengeUserKey::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(prefs::kAttestationEnabled, false);
registry->RegisterListPref(prefs::kAttestationExtensionWhitelist);
}
void EPKPChallengeUserKey::Run(scoped_refptr<UIThreadExtensionFunction> caller,
const ChallengeKeyCallback& callback,
const std::string& challenge,
bool register_key) {
callback_ = callback;
profile_ = ChromeExtensionFunctionDetails(caller.get()).GetProfile();
extension_ = scoped_refptr<const Extension>(caller->extension());
// Check if user keys are available in this profile.
if (!chromeos::ProfileHelper::Get()->GetUserByProfile(profile_)) {
callback_.Run(false, EPKPChallengeUserKey::kUserKeyNotAvailable);
return;
}
// Check if RA is enabled in the user policy.
if (!IsRemoteAttestationEnabledForUser()) {
callback_.Run(false, kUserPolicyDisabledError);
return;
}
// Check if the extension is whitelisted in the user policy.
if (!IsExtensionWhitelisted()) {
callback_.Run(false, kExtensionNotWhitelistedError);
return;
}
if (IsEnterpriseDevice()) {
if (!IsUserAffiliated()) {
callback_.Run(false, kUserNotManaged);
return;
}
// Check if RA is enabled in the device policy.
GetDeviceAttestationEnabled(
base::Bind(&EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback,
base::Unretained(this), challenge, register_key,
false)); // user consent is not required.
} else {
// For personal devices, we don't need to check if RA is enabled in the
// device, but we need to ask for user consent if the key does not exist.
GetDeviceAttestationEnabledCallback(challenge, register_key,
true, // user consent is required.
true); // attestation is enabled.
}
}
void EPKPChallengeUserKey::DecodeAndRun(
scoped_refptr<UIThreadExtensionFunction> caller,
const ChallengeKeyCallback& callback,
const std::string& encoded_challenge,
bool register_key) {
std::string challenge;
if (!base::Base64Decode(encoded_challenge, &challenge)) {
callback.Run(false, kChallengeBadBase64Error);
return;
}
Run(caller, callback, challenge, register_key);
}
void EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback(
const std::string& challenge,
bool register_key,
bool require_user_consent,
bool enabled) {
if (!enabled) {
callback_.Run(false, kDevicePolicyDisabledError);
return;
}
PrepareKey(chromeos::attestation::KEY_USER, GetAccountId(), kKeyName,
chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE,
require_user_consent,
base::Bind(&EPKPChallengeUserKey::PrepareKeyCallback,
base::Unretained(this), challenge, register_key));
}
void EPKPChallengeUserKey::PrepareKeyCallback(const std::string& challenge,
bool register_key,
PrepareKeyResult result) {
if (result != PREPARE_KEY_OK) {
callback_.Run(false,
base::StringPrintf(kGetCertificateFailedError, result));
return;
}
// Everything is checked. Sign the challenge.
async_caller_->TpmAttestationSignEnterpriseChallenge(
chromeos::attestation::KEY_USER,
cryptohome::Identification(GetAccountId()), kKeyName, GetUserEmail(),
GetDeviceId(),
register_key ? chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY
: chromeos::attestation::CHALLENGE_OPTION_NONE,
challenge, base::Bind(&EPKPChallengeUserKey::SignChallengeCallback,
base::Unretained(this), register_key));
}
void EPKPChallengeUserKey::SignChallengeCallback(bool register_key,
bool success,
const std::string& response) {
if (!success) {
callback_.Run(false, kSignChallengeFailedError);
return;
}
if (register_key) {
async_caller_->TpmAttestationRegisterKey(
chromeos::attestation::KEY_USER,
cryptohome::Identification(GetAccountId()), kKeyName,
base::Bind(&EPKPChallengeUserKey::RegisterKeyCallback,
base::Unretained(this), response));
} else {
RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE);
}
}
void EPKPChallengeUserKey::RegisterKeyCallback(
const std::string& response,
bool success,
cryptohome::MountError return_code) {
if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
callback_.Run(false, kKeyRegistrationFailedError);
return;
}
callback_.Run(true, response);
}
bool EPKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const {
return profile_->GetPrefs()->GetBoolean(prefs::kAttestationEnabled);
}
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction()
: default_impl_(new EPKPChallengeMachineKey), impl_(default_impl_.get()) {}
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction(
EPKPChallengeMachineKey* impl_for_testing)
: impl_(impl_for_testing) {}
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
~EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() = default;
ExtensionFunction::ResponseAction
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::Run() {
std::unique_ptr<api_epkp::ChallengeMachineKey::Params> params(
api_epkp::ChallengeMachineKey::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
ChallengeKeyCallback callback =
base::Bind(&EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
OnChallengedKey,
this);
// base::Unretained is safe on impl_ since its life-cycle matches |this| and
// |callback| holds a reference to |this|.
base::Closure task = base::Bind(
&EPKPChallengeMachineKey::DecodeAndRun, base::Unretained(impl_),
scoped_refptr<UIThreadExtensionFunction>(AsUIThreadExtensionFunction()),
callback, params->challenge, false);
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, task);
return RespondLater();
}
void EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::OnChallengedKey(
bool success,
const std::string& data) {
if (success) {
std::string encoded_response;
base::Base64Encode(data, &encoded_response);
Respond(ArgumentList(
api_epkp::ChallengeMachineKey::Results::Create(encoded_response)));
} else {
Respond(Error(data));
}
}
EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
EnterprisePlatformKeysPrivateChallengeUserKeyFunction()
: default_impl_(new EPKPChallengeUserKey), impl_(default_impl_.get()) {}
EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
EnterprisePlatformKeysPrivateChallengeUserKeyFunction(
EPKPChallengeUserKey* impl_for_testing)
: impl_(impl_for_testing) {}
EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
~EnterprisePlatformKeysPrivateChallengeUserKeyFunction() = default;
ExtensionFunction::ResponseAction
EnterprisePlatformKeysPrivateChallengeUserKeyFunction::Run() {
std::unique_ptr<api_epkp::ChallengeUserKey::Params> params(
api_epkp::ChallengeUserKey::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
ChallengeKeyCallback callback = base::Bind(
&EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey,
this);
// base::Unretained is safe on impl_ since its life-cycle matches |this| and
// |callback| holds a reference to |this|.
base::Closure task = base::Bind(
&EPKPChallengeUserKey::DecodeAndRun, base::Unretained(impl_),
scoped_refptr<UIThreadExtensionFunction>(AsUIThreadExtensionFunction()),
callback, params->challenge, params->register_key);
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, task);
return RespondLater();
}
void EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey(
bool success,
const std::string& data) {
if (success) {
std::string encoded_response;
base::Base64Encode(data, &encoded_response);
Respond(ArgumentList(
api_epkp::ChallengeUserKey::Results::Create(encoded_response)));
} else {
Respond(Error(data));
}
}
} // namespace extensions