chromium/chrome/browser/ash/crosapi/content_protection_ash.cc

// Copyright 2021 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/ash/crosapi/content_protection_ash.h"

#include "ash/display/output_protection_delegate.h"
#include "chrome/browser/ash/crosapi/window_util.h"
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
#include "chromeos/ash/components/settings/cros_settings.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "components/user_manager/user_manager.h"

namespace crosapi {

ContentProtectionAsh::ContentProtectionAsh() = default;
ContentProtectionAsh::~ContentProtectionAsh() {
  for (auto& pair : output_protection_delegates_) {
    pair.first->RemoveObserver(this);
  }
}

void ContentProtectionAsh::BindReceiver(
    mojo::PendingReceiver<mojom::ContentProtection> pending_receiver) {
  receivers_.Add(this, std::move(pending_receiver));
}

ash::OutputProtectionDelegate*
ContentProtectionAsh::FindOrCreateOutputProtectionDelegate(
    aura::Window* window) {
  auto it = output_protection_delegates_.find(window);
  if (it != output_protection_delegates_.end())
    return it->second.get();

  auto delegate = std::make_unique<ash::OutputProtectionDelegate>(window);
  ash::OutputProtectionDelegate* ptr = delegate.get();
  output_protection_delegates_[window] = std::move(delegate);
  window->AddObserver(this);
  return ptr;
}

void ContentProtectionAsh::EnableWindowProtection(
    const std::string& window_id,
    uint32_t desired_protection_mask,
    EnableWindowProtectionCallback callback) {
  aura::Window* window = crosapi::GetShellSurfaceWindow(window_id);
  if (!window) {
    std::move(callback).Run(/*success=*/false);
    return;
  }

  ash::OutputProtectionDelegate* delegate =
      FindOrCreateOutputProtectionDelegate(window);
  delegate->SetProtection(desired_protection_mask, std::move(callback));
}

void ContentProtectionAsh::QueryWindowStatus(
    const std::string& window_id,
    QueryWindowStatusCallback callback) {
  aura::Window* window = crosapi::GetShellSurfaceWindow(window_id);
  if (!window) {
    ExecuteWindowStatusCallback(std::move(callback), /*success=*/false,
                                /*link_mask=*/0,
                                /*protection_mask=*/0);
    return;
  }

  ash::OutputProtectionDelegate* delegate =
      FindOrCreateOutputProtectionDelegate(window);
  delegate->QueryStatus(
      base::BindOnce(&ContentProtectionAsh::ExecuteWindowStatusCallback,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ContentProtectionAsh::GetSystemSalt(GetSystemSaltCallback callback) {
  ash::SystemSaltGetter::Get()->GetSystemSalt(std::move(callback));
}

void ContentProtectionAsh::ChallengePlatform(
    const std::string& service_id,
    const std::string& challenge,
    ChallengePlatformCallback callback) {
  // Remote attestation requires a cryptohome user. Since Lacros knows nothing
  // about cryptohome, we always choose to use the primary logged in user.
  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
  const user_manager::User* user = user_manager->GetPrimaryUser();
  if (!user) {
    std::move(callback).Run(nullptr);
    return;
  }

  if (!platform_verification_flow_) {
    platform_verification_flow_ =
        base::MakeRefCounted<ash::attestation::PlatformVerificationFlow>();
  }

  platform_verification_flow_->ChallengePlatformKey(
      user, service_id, challenge,
      base::BindOnce(&ContentProtectionAsh::OnChallengePlatform,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ContentProtectionAsh::IsVerifiedAccessEnabled(
    IsVerifiedAccessEnabledCallback callback) {
  bool enabled_for_device = false;
  ash::CrosSettings::Get()->GetBoolean(
      ash::kAttestationForContentProtectionEnabled, &enabled_for_device);
  std::move(callback).Run(enabled_for_device);
}

void ContentProtectionAsh::OnWindowDestroyed(aura::Window* window) {
  output_protection_delegates_.erase(window);
  // No need to call window->RemoveObserver() since Window* handles that before
  // calling this method.
}

void ContentProtectionAsh::ExecuteWindowStatusCallback(
    QueryWindowStatusCallback callback,
    bool success,
    uint32_t link_mask,
    uint32_t protection_mask) {
  if (success) {
    mojom::ContentProtectionWindowStatusPtr status =
        mojom::ContentProtectionWindowStatus::New();
    status->link_mask = link_mask;
    status->protection_mask = protection_mask;
    std::move(callback).Run(std::move(status));
  } else {
    std::move(callback).Run(nullptr);
  }
}

void ContentProtectionAsh::OnChallengePlatform(
    ChallengePlatformCallback callback,
    ash::attestation::PlatformVerificationFlow::Result result,
    const std::string& signed_data,
    const std::string& signature,
    const std::string& platform_key_certificate) {
  if (result != ash::attestation::PlatformVerificationFlow::SUCCESS) {
    std::move(callback).Run(nullptr);
    return;
  }

  mojom::ChallengePlatformResultPtr output =
      mojom::ChallengePlatformResult::New();
  output->signed_data = signed_data;
  output->signed_data_signature = signature;
  output->platform_key_certificate = platform_key_certificate;
  std::move(callback).Run(std::move(output));
}

}  // namespace crosapi