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

#ifndef DEVICE_FIDO_ENCLAVE_ENCLAVE_AUTHENTICATOR_H_
#define DEVICE_FIDO_ENCLAVE_ENCLAVE_AUTHENTICATOR_H_

#include <array>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <variant>
#include <vector>

#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/types/expected.h"
#include "components/sync/protocol/webauthn_credential_specifics.pb.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/cable/v2_constants.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/enclave/enclave_protocol_utils.h"
#include "device/fido/enclave/enclave_websocket_client.h"
#include "device/fido/enclave/transact.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/network_context_factory.h"
#include "device/fido/public/fido_constants.h"
#include "device/fido/public/fido_types.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/network/public/mojom/network_context.mojom.h"

namespace device::enclave {

struct CredentialRequest;

// This feature holds parameters that control the cert.xml & cert.sig.xml file
// locations, allowing us to roll out a new Vault cohort progressively.
// If URL parsing fails, the default is used instead.
COMPONENT_EXPORT(DEVICE_FIDO) BASE_DECLARE_FEATURE(kEnclaveTrustedVaultCohort);
COMPONENT_EXPORT(DEVICE_FIDO)
const extern base::FeatureParam<std::string> kCertXmlUrlFeature;
COMPONENT_EXPORT(DEVICE_FIDO)
const extern base::FeatureParam<std::string> kSigXmlUrlFeature;

class COMPONENT_EXPORT(DEVICE_FIDO) EnclaveAuthenticator
    : public FidoAuthenticator {
 public:
  EnclaveAuthenticator(
      std::unique_ptr<CredentialRequest> ui_request,
      NetworkContextFactory network_context_factory);
  ~EnclaveAuthenticator() override;

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

  void SetOauthToken(std::optional<std::string_view> token);

  // FidoAuthenticator:
  void GetAssertion(CtapGetAssertionRequest request,
                    CtapGetAssertionOptions options,
                    GetAssertionCallback callback) override;
  void MakeCredential(CtapMakeCredentialRequest request,
                      MakeCredentialOptions options,
                      MakeCredentialCallback callback) override;
  void InitializeAuthenticator(base::OnceClosure callback) override;
  void Cancel() override;
  AuthenticatorType GetType() const override;
  std::string GetId() const override;
  const AuthenticatorSupportedOptions& Options() const override;
  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
  base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;

 private:
  struct PendingGetAssertionRequest {
    PendingGetAssertionRequest(CtapGetAssertionRequest,
                               CtapGetAssertionOptions,
                               GetAssertionCallback);
    ~PendingGetAssertionRequest();
    PendingGetAssertionRequest(const PendingGetAssertionRequest&) = delete;
    PendingGetAssertionRequest& operator=(const PendingGetAssertionRequest&) =
        delete;

    CtapGetAssertionRequest request;
    CtapGetAssertionOptions options;
    GetAssertionCallback callback;
  };

  struct PendingMakeCredentialRequest {
    PendingMakeCredentialRequest(CtapMakeCredentialRequest,
                                 MakeCredentialOptions,
                                 MakeCredentialCallback);
    ~PendingMakeCredentialRequest();
    PendingMakeCredentialRequest(const PendingMakeCredentialRequest&) = delete;
    PendingMakeCredentialRequest& operator=(
        const PendingMakeCredentialRequest&) = delete;

    CtapMakeCredentialRequest request;
    MakeCredentialOptions options;
    MakeCredentialCallback callback;
  };

  void DispatchGetAssertion();
  void OnHaveReencodedLargeBlob(
      size_t original_size,
      base::expected<mojo_base::BigBuffer, std::string> maybe_deflated);
  void OnHaveInflatedLargeBlobForGetAssertion(
      AuthenticatorGetAssertionResponse,
      base::expected<mojo_base::BigBuffer, std::string>);
  void ReturnGetAssertionSuccess(AuthenticatorGetAssertionResponse);
  void DispatchMakeCredentialWithNewUVKey(
      base::span<const uint8_t> uv_public_key);
  void DispatchGetAssertionWithNewUVKey(
      base::span<const uint8_t> uv_public_key);
  void ProcessMakeCredentialResponse(
      base::expected<cbor::Value, TransactError> maybe_response);
  void ProcessGetAssertionResponse(
      base::expected<cbor::Value, TransactError> maybe_response);
  void ProcessErrorResponse(const ErrorResponse& error);

  // `Complete*` methods invoke callbacks that can result in `this` being
  // destroyed, and so should only be called immediately before a return.
  void CompleteRequestWithError(
      std::variant<GetAssertionStatus, MakeCredentialStatus> error);
  void CompleteMakeCredentialRequest(
      MakeCredentialStatus status,
      std::optional<AuthenticatorMakeCredentialResponse> response);
  void CompleteGetAssertionRequest(
      GetAssertionStatus status,
      std::vector<AuthenticatorGetAssertionResponse> responses);

  data_decoder::DataDecoder* data_decoder();

  const std::array<uint8_t, 8> id_;
  const NetworkContextFactory network_context_factory_;
  const std::unique_ptr<CredentialRequest> ui_request_;

  // Caches the request while waiting for the connection to be established.
  // At most one of these can be non-null at any given time.
  std::unique_ptr<PendingGetAssertionRequest> pending_get_assertion_request_;
  std::unique_ptr<PendingMakeCredentialRequest>
      pending_make_credential_request_;

  std::unique_ptr<EnclaveTransaction> pending_transaction_;

  // Set to true when the request included a deferred UV key creation.
  bool includes_new_uv_key_ = false;

  std::unique_ptr<data_decoder::DataDecoder> data_decoder_;

  base::WeakPtrFactory<EnclaveAuthenticator> weak_factory_{this};
};

}  // namespace device::enclave

#endif  // DEVICE_FIDO_ENCLAVE_ENCLAVE_AUTHENTICATOR_H_
