chromium/chromeos/ash/services/device_sync/cryptauth_device_syncer_impl.h

// 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.

#ifndef CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_SYNCER_IMPL_H_
#define CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_SYNCER_IMPL_H_

#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_registry.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_sync_result.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_syncer.h"
#include "chromeos/ash/services/device_sync/cryptauth_ecies_encryptor.h"
#include "chromeos/ash/services/device_sync/cryptauth_feature_status_getter.h"
#include "chromeos/ash/services/device_sync/cryptauth_group_private_key_sharer.h"
#include "chromeos/ash/services/device_sync/cryptauth_key.h"
#include "chromeos/ash/services/device_sync/cryptauth_key_bundle.h"
#include "chromeos/ash/services/device_sync/cryptauth_metadata_syncer.h"
#include "chromeos/ash/services/device_sync/network_request_error.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_directive.pb.h"

class PrefService;

namespace cryptauthv2 {
class ClientAppMetadata;
class ClientMetadata;
}  // namespace cryptauthv2

namespace ash {

namespace device_sync {

class AttestationCertificatesSyncer;
class CryptAuthClient;
class CryptAuthClientFactory;
class CryptAuthKeyRegistry;
class SyncedBluetoothAddressTracker;

// An implementation of CryptAuthDeviceSyncer, using instances of
// CryptAuthClient to make the API calls to CryptAuth. This implementation
// handles timeouts internally, so the callback passed to
// CryptAuthDeviceSyncer::Sync() is always guaranteed to be invoked.
//
// When the DeviceSync flow finishes, the device registry is updated with all
// devices that have a valid device ID, device name, device public key, and
// feature states. If device metadata cannot be decrypted due to an error or
// because the group private key was not returned by CryptAuth, the device is
// still added to the registry without the new decrypted metadata. Any existing
// decrypted metadata from the device registry will remain there until the new
// metadata can be decrypted.
class CryptAuthDeviceSyncerImpl : public CryptAuthDeviceSyncer {
 public:
  class Factory {
   public:
    static std::unique_ptr<CryptAuthDeviceSyncer> Create(
        CryptAuthDeviceRegistry* device_registry,
        CryptAuthKeyRegistry* key_registry,
        CryptAuthClientFactory* client_factory,
        SyncedBluetoothAddressTracker* synced_bluetooth_address_tracker,
        AttestationCertificatesSyncer* attestation_certificates_syncer,
        PrefService* pref_service,
        std::unique_ptr<base::OneShotTimer> timer =
            std::make_unique<base::OneShotTimer>());
    static void SetFactoryForTesting(Factory* test_factory);

   protected:
    virtual ~Factory();
    virtual std::unique_ptr<CryptAuthDeviceSyncer> CreateInstance(
        CryptAuthDeviceRegistry* device_registry,
        CryptAuthKeyRegistry* key_registry,
        CryptAuthClientFactory* client_factory,
        SyncedBluetoothAddressTracker* synced_bluetooth_address_tracker,
        AttestationCertificatesSyncer* attestation_certificates_syncer,
        PrefService* pref_service,
        std::unique_ptr<base::OneShotTimer> timer) = 0;

   private:
    static Factory* test_factory_;
  };

  CryptAuthDeviceSyncerImpl(const CryptAuthDeviceSyncerImpl&) = delete;
  CryptAuthDeviceSyncerImpl& operator=(const CryptAuthDeviceSyncerImpl&) =
      delete;

  ~CryptAuthDeviceSyncerImpl() override;

 private:
  enum class State {
    kNotStarted,
    kWaitingForBluetoothAddress,
    kWaitingForAttestationCertificates,
    kWaitingForMetadataSync,
    kWaitingForFeatureStatuses,
    kWaitingForEncryptedGroupPrivateKeyProcessing,
    kWaitingForEncryptedDeviceMetadataProcessing,
    kWaitingForGroupPrivateKeySharing,
    kFinished
  };

  friend std::ostream& operator<<(std::ostream& stream, const State& state);

  static std::optional<base::TimeDelta> GetTimeoutForState(State state);
  static std::optional<CryptAuthDeviceSyncResult::ResultCode>
  ResultCodeErrorFromTimeoutDuringState(State state);

  // |device_registry|: At the end of a DeviceSync flow, the devices in the
  //     registry are replaced with the devices received from CryptAuth.
  // |key_registry|: The syncer will read and possibly write the group key pair,
  //     and it will read the user key pair and the key used for decrypting the
  //     group private key.
  // |client_factory|: Creates CryptAuthClient instances for making API calls.
  // |synced_bluetooth_address_tracker|: Used to fetch Bluetooth address and
  //     track address used for successful syncs.
  // |timer|: Handles timeouts for asynchronous operations.
  CryptAuthDeviceSyncerImpl(
      CryptAuthDeviceRegistry* device_registry,
      CryptAuthKeyRegistry* key_registry,
      CryptAuthClientFactory* client_factory,
      SyncedBluetoothAddressTracker* synced_bluetooth_address_tracker,
      AttestationCertificatesSyncer* attestation_certificates_syncer,
      PrefService* pref_service,
      std::unique_ptr<base::OneShotTimer> timer);

  // CryptAuthDeviceSyncer:
  void OnAttemptStarted(
      const cryptauthv2::ClientMetadata& client_metadata,
      const cryptauthv2::ClientAppMetadata& client_app_metadata) override;

  void SetState(State state);
  void OnTimeout();

  // Controls the logical flow of the class.
  void AttemptNextStep();

  void GetBluetoothAddress();
  void OnBluetoothAddress(const std::string& bluetooth_address);

  void GetAttestationCertificates();
  void OnAttestationCertificates(const std::vector<std::string>& cert_chain,
                                 bool valid);

  void SyncMetadata();
  void OnSyncMetadataFinished(
      const CryptAuthMetadataSyncer::IdToDeviceMetadataPacketMap&
          id_to_device_metadata_packet_map,
      std::unique_ptr<CryptAuthKey> new_group_key,
      const std::optional<cryptauthv2::EncryptedGroupPrivateKey>&
          encrypted_group_private_key,
      const std::optional<cryptauthv2::ClientDirective>& new_client_directive,
      CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);

  void SetGroupKey(const CryptAuthKey& new_group_key);

  void GetFeatureStatuses();
  void OnGetFeatureStatusesFinished(
      const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
          id_to_device_software_feature_info_map,
      CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);

  // Builds a new device registry map with all device information except
  // decrypted BetterTogetherDeviceMetadata for remote devices.
  void BuildNewDeviceRegistry(
      const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
          id_to_device_software_feature_info_map);

  // If an encrypted group private key was sent by CryptAuth, decrypt it. Even
  // if we already have the unencrypted group private key in the key registry,
  // we verify that they agree.
  void ProcessEncryptedGroupPrivateKey();
  void OnGroupPrivateKeyDecrypted(
      const std::optional<std::string>& group_private_key_from_cryptauth);

  void ProcessEncryptedDeviceMetadata();
  void OnDeviceMetadataDecrypted(const CryptAuthEciesEncryptor::IdToOutputMap&
                                     id_to_decrypted_metadata_map);

  // Adds decrypted BetterTogetherDeviceMetadata to the new device registry
  // constructed in BuildNewDeviceRegistry().
  void AddDecryptedMetadataToNewDeviceRegistry(
      const CryptAuthEciesEncryptor::IdToOutputMap&
          id_to_decrypted_metadata_map);

  void ShareGroupPrivateKey();
  void OnShareGroupPrivateKeyFinished(
      CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);

  // Replaces the current device registry if devices were able to be extracted
  // from the DeviceSync attempt. Finishes the DeviceSync attempt, sending back
  // the relevant CryptAuthDeviceSyncResult.
  void FinishAttempt(CryptAuthDeviceSyncResult::ResultCode result_code);

  bool did_non_fatal_error_occur_ = false;
  // Field reflects the certificate validity state only after local_better_together_device_metadata_
  // has been updated and before the sync attempt has completed. It is otherwise always false.
  bool are_attestation_certs_valid_ = false;

  // Set in OnAttemptStarted() and not modified during the rest of the flow.
  cryptauthv2::RequestContext request_context_;
  cryptauthv2::BetterTogetherDeviceMetadata
      local_better_together_device_metadata_;

  // Output from CryptAuthMetadataSyncer.
  CryptAuthMetadataSyncer::IdToDeviceMetadataPacketMap
      id_to_device_metadata_packet_map_;
  std::optional<cryptauthv2::EncryptedGroupPrivateKey>
      encrypted_group_private_key_;
  std::optional<cryptauthv2::ClientDirective> new_client_directive_;

  // Populated after a successful BatchGetFeatureStatuses call. Device metadata
  // is added if device metadata decryption is successful. Replaces the contents
  // of the device registry if non-null when the DeviceSync attempt ends,
  // successfully or not.
  std::optional<CryptAuthDeviceRegistry::InstanceIdToDeviceMap>
      new_device_registry_map_;

  // The time of the last state change. Used for execution time metrics.
  base::TimeTicks last_state_change_timestamp_;

  std::unique_ptr<CryptAuthMetadataSyncer> metadata_syncer_;
  std::unique_ptr<CryptAuthFeatureStatusGetter> feature_status_getter_;
  std::unique_ptr<CryptAuthEciesEncryptor> encryptor_;
  std::unique_ptr<CryptAuthGroupPrivateKeySharer> group_private_key_sharer_;

  State state_ = State::kNotStarted;
  raw_ptr<CryptAuthDeviceRegistry> device_registry_ = nullptr;
  raw_ptr<CryptAuthKeyRegistry> key_registry_ = nullptr;
  raw_ptr<CryptAuthClientFactory> client_factory_ = nullptr;
  raw_ptr<SyncedBluetoothAddressTracker> synced_bluetooth_address_tracker_ =
      nullptr;
  raw_ptr<AttestationCertificatesSyncer> attestation_certificates_syncer_ =
      nullptr;
  raw_ptr<PrefService> pref_service_ = nullptr;
  std::unique_ptr<base::OneShotTimer> timer_;

  base::WeakPtrFactory<CryptAuthDeviceSyncerImpl> weak_ptr_factory_{this};
};

}  // namespace device_sync

}  // namespace ash

#endif  // CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_SYNCER_IMPL_H_