chromium/chromeos/ash/services/device_sync/remote_device_v2_loader_impl.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/device_sync/remote_device_v2_loader_impl.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/multidevice/beacon_seed.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/multidevice/secure_message_delegate_impl.h"
#include "chromeos/ash/services/device_sync/async_execution_time_metrics_logger.h"
#include "chromeos/ash/services/device_sync/cryptauth_task_metrics_logger.h"

namespace ash {

namespace device_sync {

// static
RemoteDeviceV2LoaderImpl::Factory*
    RemoteDeviceV2LoaderImpl::Factory::test_factory_ = nullptr;

// static
std::unique_ptr<RemoteDeviceV2Loader>
RemoteDeviceV2LoaderImpl::Factory::Create() {
  if (test_factory_)
    return test_factory_->CreateInstance();

  return base::WrapUnique(new RemoteDeviceV2LoaderImpl());
}

// static
void RemoteDeviceV2LoaderImpl::Factory::SetFactoryForTesting(
    Factory* test_factory) {
  test_factory_ = test_factory;
}

RemoteDeviceV2LoaderImpl::Factory::~Factory() = default;

RemoteDeviceV2LoaderImpl::RemoteDeviceV2LoaderImpl()
    : secure_message_delegate_(
          multidevice::SecureMessageDelegateImpl::Factory::Create()) {}

RemoteDeviceV2LoaderImpl::~RemoteDeviceV2LoaderImpl() = default;

void RemoteDeviceV2LoaderImpl::Load(
    const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& id_to_device_map,
    const std::string& user_email,
    const std::string& user_private_key,
    LoadCallback callback) {
  DCHECK(!user_email.empty());
  DCHECK(!user_private_key.empty());

  DCHECK(callback_.is_null());
  callback_ = std::move(callback);

  DCHECK(id_to_device_map_.empty());
  id_to_device_map_ = id_to_device_map;
  if (id_to_device_map_.empty()) {
    std::move(callback_).Run(remote_devices_);
    return;
  }

  DCHECK(remaining_ids_to_process_.empty());
  for (const auto& id_device_pair : id_to_device_map_)
    remaining_ids_to_process_.insert(id_device_pair.first);

  for (const auto& id_device_pair : id_to_device_map_) {
    if (!id_device_pair.second.better_together_device_metadata ||
        id_device_pair.second.better_together_device_metadata->public_key()
            .empty()) {
      AddRemoteDevice(id_device_pair.second, user_email,
                      std::string() /* psk */);
      continue;
    }

    // Performs ECDH key agreement to generate a shared secret between the local
    // device and the remote device of |id_device_pair|.
    secure_message_delegate_->DeriveKey(
        user_private_key,
        id_device_pair.second.better_together_device_metadata->public_key(),
        base::BindOnce(&RemoteDeviceV2LoaderImpl::OnPskDerived,
                       base::Unretained(this), id_device_pair.second,
                       user_email));
  }
}

void RemoteDeviceV2LoaderImpl::OnPskDerived(const CryptAuthDevice& device,
                                            const std::string& user_email,
                                            const std::string& psk) {
  if (psk.empty())
    PA_LOG(WARNING) << "Derived persistent symmetric key is empty.";

  AddRemoteDevice(device, user_email, psk);
}

void RemoteDeviceV2LoaderImpl::AddRemoteDevice(const CryptAuthDevice& device,
                                               const std::string& user_email,
                                               const std::string& psk) {
  const std::optional<cryptauthv2::BetterTogetherDeviceMetadata>&
      beto_metadata = device.better_together_device_metadata;
  remote_devices_.emplace_back(
      user_email, device.instance_id(), device.device_name,
      beto_metadata ? beto_metadata->no_pii_device_name() : std::string(),
      beto_metadata ? beto_metadata->public_key() : std::string(), psk,
      device.last_update_time.InMillisecondsSinceUnixEpoch(),
      device.feature_states,
      beto_metadata ? multidevice::FromCryptAuthV2SeedRepeatedPtrField(
                          beto_metadata->beacon_seeds())
                    : std::vector<multidevice::BeaconSeed>(),
      beto_metadata ? beto_metadata->bluetooth_public_address()
                    : std::string());

  remaining_ids_to_process_.erase(device.instance_id());

  if (remaining_ids_to_process_.empty())
    std::move(callback_).Run(remote_devices_);
}

}  // namespace device_sync

}  // namespace ash