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

#ifndef CHROME_BROWSER_WEBAUTHN_GPM_ENCLAVE_TRANSACTION_H_
#define CHROME_BROWSER_WEBAUTHN_GPM_ENCLAVE_TRANSACTION_H_

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

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/webauthn/enclave_manager.h"

namespace device {
enum class FidoRequestType : uint8_t;
}

namespace device::enclave {
struct ClaimedPIN;
struct CredentialRequest;
enum class PINValidationResult : int;
}  // namespace device::enclave

namespace signin {
class PrimaryAccountAccessTokenFetcher;
}

namespace webauthn {
class PasskeyModel;
}

namespace sync_pb {
class WebauthnCredentialSpecifics;
}

// EnclaveUserVerificationMethod enumerates the possible ways that user
// verification will be performed for an enclave transaction.
enum class EnclaveUserVerificationMethod {
  // No user verification will be performed. The user acknowledged the operation
  // in browser UI.
  kUserPresenceOnly,
  // The user will enter a GPM PIN.
  kPIN,
  // User verification is satisfied because the user performed account recovery.
  kImplicit,
  // The operating system will perform user verification and allow signing
  // with the UV key.
  kUVKeyWithSystemUI,
  // The device is in a state waiting for an OS UV key to be created, which can
  // be done when a UV request is required.
  kDeferredUVKeyWithSystemUI,
  // Chrome will show user verification UI for the operating system, which will
  // then allow signing
  // with the UV key.
  kUVKeyWithChromeUI,
  // A 'silent' request. No user verification is performed, and the user did not
  // explicitly acknowledge the operation in browser UI either. This is only
  // used for passkey upgrades (i.e. registration with mediation=conditional).
  kNoUserVerificationAndNoUserPresence,
  // The request cannot be satisfied.
  kUnsatisfiable,
};

// GPMEnclaveTransaction encapsulates a single request (MakeCredential or
// GetAssertion) made to the enclave authenticator.
class GPMEnclaveTransaction {
 public:
  // Dispatches a CredentialRequest instance to the EnclaveAuthenticator.
  using EnclaveRequestCallback = base::RepeatingCallback<void(
      std::unique_ptr<device::enclave::CredentialRequest>)>;

  class Delegate {
   public:
    // Invoked when transaction encounters an error. This is useful for updating
    // UI.
    virtual void HandleEnclaveTransactionError() = 0;

    // Invoked to build UVKeyOptions for the request. This is only invoked for
    // requests that perform UV.
    virtual void BuildUVKeyOptions(EnclaveManager::UVKeyOptions& options) = 0;

    // Invoked with the result of the PIN claim of the transaction if PIN UV was
    // performed.
    virtual void HandlePINValidationResult(
        device::enclave::PINValidationResult result) = 0;

    // Invoked after a successful MakeCredential request.
    virtual void OnPasskeyCreated(
        const sync_pb::WebauthnCredentialSpecifics& passkey) = 0;

    // Returns the UV method to be used for the transaction.
    virtual EnclaveUserVerificationMethod GetUvMethod() = 0;
  };

  // `delegate`, `model` and `enclave_manager` must be non-null and outlive the
  // transaction. If `uv_method` is `kPIN` `pin` must be non-null.
  // `selected_credential_id` must be non-null iff `request_type` is
  // `kGetAssertion`.
  GPMEnclaveTransaction(
      Delegate* delegate,
      webauthn::PasskeyModel* model,
      device::FidoRequestType request_type,
      std::string rp_id,
      EnclaveManager* enclave_manager,
      std::optional<std::string> pin,
      std::optional<std::vector<uint8_t>> selected_credential_id,
      EnclaveRequestCallback enclave_request_callback,
      std::optional<std::vector<std::vector<uint8_t>>> cmtg_device_keys);

  ~GPMEnclaveTransaction();

  // Dispatches the request to the enclave authenticator.
  void Start();

 private:
  // Called when the UI has reached a state where it needs to do an enclave
  // operation, and an OAuth token for the enclave has been fetched.
  void MaybeHashPinAndStartEnclaveTransaction(std::optional<std::string> token);

  // Called when the UI has reached a state where it needs to do an enclave
  // operation, an OAuth token for the enclave has been fetched, and any PIN
  // hashing has been completed.
  void StartEnclaveTransaction(std::optional<std::string> token,
                               std::unique_ptr<device::enclave::ClaimedPIN>);

  void HandlePINValidationResult(device::enclave::PINValidationResult result);
  void OnPasskeyCreated(sync_pb::WebauthnCredentialSpecifics passkey);
  void OnPasskeyEncryptedBlobUpdated(const std::string& credential_id,
                                     const std::string& encrypted_data);

  raw_ptr<Delegate> delegate_;
  raw_ptr<webauthn::PasskeyModel> passkey_model_;
  device::FidoRequestType request_type_;
  std::string rp_id_;
  raw_ptr<EnclaveManager> enclave_manager_;
  std::optional<std::string> pin_;
  std::optional<std::vector<std::vector<uint8_t>>> cmtg_device_keys_;
  std::optional<std::vector<uint8_t>> selected_credential_id_;
  EnclaveRequestCallback enclave_request_callback_;

  // Taken when attempting to create a deferred UV key. Released on destruction.
  std::unique_ptr<EnclaveManager::UvKeyCreationLock> uv_key_lock_;

  // The pending request to fetch an OAuth token for the enclave request.
  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
      access_token_fetcher_;

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

#endif  // CHROME_BROWSER_WEBAUTHN_GPM_ENCLAVE_TRANSACTION_H_
