chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc

// Copyright 2013 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/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h"

#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/values.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/enterprise_platform_keys_private.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/dbus/attestation/attestation_ca.pb.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/manifest.h"

namespace extensions {

namespace api_epkp = api::enterprise_platform_keys_private;

EPKPChallengeKey::EPKPChallengeKey() = default;
EPKPChallengeKey::~EPKPChallengeKey() = default;

// Check if the extension is allowisted in the user policy.
bool EPKPChallengeKey::IsExtensionAllowed(
    Profile* profile,
    scoped_refptr<const Extension> extension) {
  if (!ash::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());
  }
  return platform_keys::IsExtensionAllowed(profile, extension.get());
}

void EPKPChallengeKey::Run(::attestation::VerifiedAccessFlow type,
                           scoped_refptr<ExtensionFunction> caller,
                           ash::attestation::TpmChallengeKeyCallback callback,
                           const std::string& challenge,
                           bool register_key) {
  Profile* profile = Profile::FromBrowserContext(caller->browser_context());

  if (!IsExtensionAllowed(profile, caller->extension())) {
    std::move(callback).Run(ash::attestation::TpmChallengeKeyResult::MakeError(
        ash::attestation::TpmChallengeKeyResultCode::
            kExtensionNotAllowedError));
    return;
  }

  std::string key_name_for_spkac;
  if (register_key && (type == ::attestation::ENTERPRISE_MACHINE)) {
    key_name_for_spkac = ash::attestation::kEnterpriseMachineKeyForSpkacPrefix +
                         caller->extension()->id();
  }

  impl_ = ash::attestation::TpmChallengeKeyFactory::Create();
  impl_->BuildResponse(type, profile, std::move(callback), challenge,
                       register_key, attestation::KEY_TYPE_RSA,
                       key_name_for_spkac, /*signals=*/std::nullopt);
}

EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
    EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() = default;

EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
    ~EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() = default;

ExtensionFunction::ResponseAction
EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::Run() {
  std::optional<api_epkp::ChallengeMachineKey::Params> params =
      api_epkp::ChallengeMachineKey::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);
  ash::attestation::TpmChallengeKeyCallback callback =
      base::BindOnce(&EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::
                         OnChallengedKey,
                     this);

  std::string challenge;
  if (!base::Base64Decode(params->challenge, &challenge)) {
    auto result = ash::attestation::TpmChallengeKeyResult::MakeError(
        ash::attestation::TpmChallengeKeyResultCode::kChallengeBadBase64Error);
    return RespondNow(Error(result.GetErrorMessage()));
  }

  // base::Unretained is safe on impl_ since its life-cycle matches |this| and
  // |callback| holds a reference to |this|.
  base::OnceClosure task = base::BindOnce(
      &EPKPChallengeKey::Run, base::Unretained(&impl_),
      ::attestation::ENTERPRISE_MACHINE, scoped_refptr<ExtensionFunction>(this),
      std::move(callback), challenge,
      /*register_key=*/false);
  content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));
  return RespondLater();
}

void EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::OnChallengedKey(
    const ash::attestation::TpmChallengeKeyResult& result) {
  if (result.IsSuccess()) {
    Respond(ArgumentList(api_epkp::ChallengeMachineKey::Results::Create(
        base::Base64Encode(result.challenge_response))));
  } else {
    Respond(Error(result.GetErrorMessage()));
  }
}

EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
    EnterprisePlatformKeysPrivateChallengeUserKeyFunction() = default;

EnterprisePlatformKeysPrivateChallengeUserKeyFunction::
    ~EnterprisePlatformKeysPrivateChallengeUserKeyFunction() = default;

ExtensionFunction::ResponseAction
EnterprisePlatformKeysPrivateChallengeUserKeyFunction::Run() {
  std::optional<api_epkp::ChallengeUserKey::Params> params =
      api_epkp::ChallengeUserKey::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);
  ash::attestation::TpmChallengeKeyCallback callback = base::BindOnce(
      &EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey,
      this);

  std::string challenge;
  if (!base::Base64Decode(params->challenge, &challenge)) {
    auto result = ash::attestation::TpmChallengeKeyResult::MakeError(
        ash::attestation::TpmChallengeKeyResultCode::kChallengeBadBase64Error);
    return RespondNow(Error(result.GetErrorMessage()));
  }

  // base::Unretained is safe on impl_ since its life-cycle matches |this| and
  // |callback| holds a reference to |this|.
  base::OnceClosure task = base::BindOnce(
      &EPKPChallengeKey::Run, base::Unretained(&impl_),
      ::attestation::ENTERPRISE_USER, scoped_refptr<ExtensionFunction>(this),
      std::move(callback), challenge, params->register_key);
  content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));
  return RespondLater();
}

void EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey(
    const ash::attestation::TpmChallengeKeyResult& result) {
  if (result.IsSuccess()) {
    Respond(ArgumentList(api_epkp::ChallengeUserKey::Results::Create(
        base::Base64Encode(result.challenge_response))));
  } else {
    Respond(Error(result.GetErrorMessage()));
  }
}

}  // namespace extensions