chromium/chromeos/ash/services/device_sync/cryptauth_v2_enrollment_manager_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_V2_ENROLLMENT_MANAGER_IMPL_H_
#define CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_V2_ENROLLMENT_MANAGER_IMPL_H_

#include <memory>
#include <optional>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/default_clock.h"
#include "chromeos/ash/services/device_sync/cryptauth_enrollment_manager.h"
#include "chromeos/ash/services/device_sync/cryptauth_enrollment_result.h"
#include "chromeos/ash/services/device_sync/cryptauth_feature_type.h"
#include "chromeos/ash/services/device_sync/cryptauth_gcm_manager.h"
#include "chromeos/ash/services/device_sync/cryptauth_scheduler.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_common.pb.h"

class PrefService;
class PrefRegistrySimple;

namespace ash {

namespace device_sync {

class CryptAuthClientFactory;
class CryptAuthKeyRegistry;
class CryptAuthV2Enroller;

// Implementation of CryptAuthEnrollmentManager for CryptAuth v2 Enrollment.
//
// This implementation considers three sources of enrollment requests:
//  1) The scheduler requests periodic enrollments and handles any failed
//     attempts.
//  2) The enrollment manager listens to the GCM manager for re-enrollment
//     requests.
//  3) The ForceEnrollmentNow() method allows for immediate requests.
//  4) On start-up, if the client app metadata has changed since the last
//     enrollment, a re-enrollment is scheduled.
//
// All flavors of enrollment attempts are guarded by timeouts. For example, an
// enrollment attempt triggered by ForceEnrollmentNow() will always
// conclude--successfully or not--in an allotted period of time.
//
// The v2 Enrollment infrastructure stores keys in a CryptAuthKeyRegistry. In
// contrast, the v1 enrollment manager implementation directly controls a set of
// prefs that store the user key pair. However, on construction of
// CryptAuthV2EnrollmentManagerImpl, any existing v1 user key pair is added to
// the v2 key registry to ensure consistency across the v1 to v2 Enrollment
// migration. The converse is not true. The v1 prefs will never be modified by
// v2 Enrollment.
class CryptAuthV2EnrollmentManagerImpl
    : public CryptAuthEnrollmentManager,
      public CryptAuthScheduler::EnrollmentDelegate,
      public CryptAuthGCMManager::Observer {
 public:
  class Factory {
   public:
    static std::unique_ptr<CryptAuthEnrollmentManager> Create(
        const cryptauthv2::ClientAppMetadata& client_app_metadata,
        CryptAuthKeyRegistry* key_registry,
        CryptAuthClientFactory* client_factory,
        CryptAuthGCMManager* gcm_manager,
        CryptAuthScheduler* scheduler,
        PrefService* pref_service,
        base::Clock* clock = base::DefaultClock::GetInstance());
    static void SetFactoryForTesting(Factory* test_factory);

   protected:
    virtual ~Factory();
    virtual std::unique_ptr<CryptAuthEnrollmentManager> CreateInstance(
        const cryptauthv2::ClientAppMetadata& client_app_metadata,
        CryptAuthKeyRegistry* key_registry,
        CryptAuthClientFactory* client_factory,
        CryptAuthGCMManager* gcm_manager,
        CryptAuthScheduler* scheduler,
        PrefService* pref_service,
        base::Clock* clock) = 0;

   private:
    static Factory* test_factory_;
  };

  // Note: This should never be called with
  // CryptAuthEnrollmentManagerImpl::RegisterPrefs().
  static void RegisterPrefs(PrefRegistrySimple* registry);

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

  ~CryptAuthV2EnrollmentManagerImpl() override;

 protected:
  CryptAuthV2EnrollmentManagerImpl(
      const cryptauthv2::ClientAppMetadata& client_app_metadata,
      CryptAuthKeyRegistry* key_registry,
      CryptAuthClientFactory* client_factory,
      CryptAuthGCMManager* gcm_manager,
      CryptAuthScheduler* scheduler,
      PrefService* pref_service,
      base::Clock* clock);

 private:
  // CryptAuthEnrollmentManager:
  void Start() override;
  void ForceEnrollmentNow(
      cryptauth::InvocationReason invocation_reason,
      const std::optional<std::string>& session_id) override;
  bool IsEnrollmentValid() const override;
  base::Time GetLastEnrollmentTime() const override;
  base::TimeDelta GetTimeToNextAttempt() const override;
  bool IsEnrollmentInProgress() const override;
  bool IsRecoveringFromFailure() const override;
  std::string GetUserPublicKey() const override;
  std::string GetUserPrivateKey() const override;

  // CryptAuthScheduler::EnrollmentDelegate:
  void OnEnrollmentRequested(const cryptauthv2::ClientMetadata& client_metadata,
                             const std::optional<cryptauthv2::PolicyReference>&
                                 client_directive_policy_reference) override;

  // CryptAuthGCMManager::Observer:
  void OnReenrollMessage(
      const std::optional<std::string>& session_id,
      const std::optional<CryptAuthFeatureType>& feature_type) override;

  void Enroll();
  void OnEnrollmentFinished(const CryptAuthEnrollmentResult& enrollment_result);

  std::string GetClientAppMetadataHash() const;
  std::string GetV1UserPublicKey() const;
  std::string GetV1UserPrivateKey() const;

  // If a v1 user key-pair exists, add it to the registry as the active key in
  // the kUserKeyPair key bundle.
  void AddV1UserKeyPairToRegistryIfNecessary();

  cryptauthv2::ClientAppMetadata client_app_metadata_;
  raw_ptr<CryptAuthKeyRegistry> key_registry_;
  raw_ptr<CryptAuthClientFactory> client_factory_;
  raw_ptr<CryptAuthGCMManager> gcm_manager_;
  raw_ptr<CryptAuthScheduler> scheduler_;
  raw_ptr<PrefService> pref_service_;
  raw_ptr<base::Clock> clock_;

  bool initial_v1_and_v2_user_key_pairs_disagree_ = false;

  std::optional<cryptauthv2::ClientMetadata> current_client_metadata_;
  std::optional<cryptauthv2::PolicyReference>
      client_directive_policy_reference_;
  std::unique_ptr<CryptAuthV2Enroller> enroller_;

  // For weak pointers used in callbacks. These weak pointers are invalidated
  // when the current enrollment attempt finishes in order to cancel outstanding
  // callbacks.
  base::WeakPtrFactory<CryptAuthV2EnrollmentManagerImpl>
      callback_weak_ptr_factory_{this};

  // For sending a weak pointer to the scheduler, whose lifetime exceeds that of
  // CryptAuthV2EnrollmentManagerImpl.
  base::WeakPtrFactory<CryptAuthV2EnrollmentManagerImpl>
      scheduler_weak_ptr_factory_{this};
};

}  // namespace device_sync

}  // namespace ash

#endif  // CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_V2_ENROLLMENT_MANAGER_IMPL_H_