// 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 "content/browser/devtools/protocol/webauthn_handler.h"

#include <map>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/devtools/protocol/web_authn.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/webauth/authenticator_environment.h"
#include "content/browser/webauth/virtual_authenticator.h"
#include "content/browser/webauth/virtual_authenticator_manager_impl.h"
#include "device/fido/public/fido_constants.h"
#include "device/fido/public/fido_transport_protocol.h"
#include "device/fido/virtual_fido_device.h"
#include "device/fido/virtual_u2f_device.h"

// This should be after all other #includes.
#if defined(_WINDOWS_)  // Detect whether windows.h was included.
#include "base/win/windows_h_disallowed.h"
#endif  // defined(_WINDOWS_)

namespace content::protocol {

namespace {
static constexpr char kActiveCmtgKeyIndexOutOfBounds[] =
    "activeCmtgKeyIndex out of bounds";
static constexpr char kAlreadyHasInternalAuthenticator[] =
    "Chrome only supports one internal authenticator per environment";
static constexpr char kAuthenticatorNotFound[] =
    "Could not find a Virtual Authenticator matching the ID";
static constexpr char kCableNotSupportedOnU2f[] =
    "U2F only supports the \"usb\", \"ble\" and \"nfc\" transports";
static constexpr char kCmtgNotSupported[] =
    "CMTG is not supported by this authenticator";
static constexpr char kCouldNotCreateCredential[] =
    "An error occurred trying to create the credential";
static constexpr char kCouldNotStoreLargeBlob[] =
    "An error occurred trying to store the large blob";
static constexpr char kCredentialNotFound[] =
    "Could not find a credential matching the ID";
static constexpr char kDevToolsNotAttached[] =
    "The DevTools session is not attached to a frame";
static constexpr char kErrorCreatingAuthenticator[] =
    "An error occurred when trying to create the authenticator";
static constexpr char kHandleRequiredForResidentCredential[] =
    "The User Handle is required for Resident Credentials";
static constexpr char kInvalidCmtgKey[] = "Invalid CMTG key";
static constexpr char kInvalidCtapVersion[] = "Invalid CTAP version.";
static constexpr char kInvalidProtocol[] = "The protocol is not valid";
static constexpr char kInvalidSignatureCounter[] =
    "Signature counter must be greater than or equal to -1";
static constexpr char kInvalidTransport[] = "The transport is not valid";
static constexpr char kInvalidUserHandle[] =
    "The User Handle must have a maximum size of ";
static constexpr char kRequiresCtap2[] =
    "Specified options require a CTAP 2 authenticator";
static constexpr char kRequiresCtap2_1[] =
    "Specified options require a CTAP 2.1 authenticator";
static constexpr char kRequiresResidentKey[] =
    "Specified options require an authenticator with resident key support";
static constexpr char kResidentCredentialNotSupported[] =
    "The Authenticator does not support Resident Credentials.";
static constexpr char kRpIdRequired[] =
    "The Relying Party ID is a required parameter";
static constexpr char kVirtualEnvironmentNotEnabled[] =
    "The Virtual Authenticator Environment has not been enabled for this "
    "session";

class GetCredentialCallbackAggregator
    : public base::RefCounted<GetCredentialCallbackAggregator> {
 public:
  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();

  explicit GetCredentialCallbackAggregator(
      std::unique_ptr<WebAuthn::Backend::GetCredentialsCallback> callback)
      : callback_(std::move(callback)) {}
  GetCredentialCallbackAggregator(const GetCredentialCallbackAggregator&) =
      delete;
  GetCredentialCallbackAggregator operator=(
      const GetCredentialCallbackAggregator&) = delete;

  void OnLargeBlob(std::unique_ptr<WebAuthn::Credential> credential,
                   std::optional<std::vector<uint8_t>> blob) {
    if (blob) {
      credential->SetLargeBlob(Binary::fromVector(std::move(*blob)));
    }
    credentials_->emplace_back(std::move(credential));
  }

 private:
  friend class base::RefCounted<GetCredentialCallbackAggregator>;
  ~GetCredentialCallbackAggregator() {
    callback_->sendSuccess(std::move(credentials_));
  }

  std::unique_ptr<WebAuthn::Backend::GetCredentialsCallback> callback_;
  std::unique_ptr<Array<WebAuthn::Credential>> credentials_ =
      std::make_unique<Array<WebAuthn::Credential>>();
};

device::ProtocolVersion ConvertToProtocolVersion(std::string_view protocol) {
  if (protocol == WebAuthn::AuthenticatorProtocolEnum::Ctap2)
    return device::ProtocolVersion::kCtap2;
  if (protocol == WebAuthn::AuthenticatorProtocolEnum::U2f)
    return device::ProtocolVersion::kU2f;
  return device::ProtocolVersion::kUnknown;
}

std::optional<device::Ctap2Version> ConvertToCtap2Version(
    std::string_view version) {
  if (version == WebAuthn::Ctap2VersionEnum::Ctap2_0)
    return device::Ctap2Version::kCtap2_0;
  if (version == WebAuthn::Ctap2VersionEnum::Ctap2_1)
    return device::Ctap2Version::kCtap2_1;
  if (version == WebAuthn::Ctap2VersionEnum::Ctap2_2) {
    return device::Ctap2Version::kCtap2_2;
  }
  return std::nullopt;
}

// LINT.IfChange(ConvertToFidoTransportProtocol)
std::optional<device::FidoTransportProtocol> ConvertToFidoTransportProtocol(
    std::string_view transport) {
  if (transport == WebAuthn::AuthenticatorTransportEnum::Usb) {
    return device::FidoTransportProtocol::kUsbHumanInterfaceDevice;
  }
  if (transport == WebAuthn::AuthenticatorTransportEnum::Nfc) {
    return device::FidoTransportProtocol::kNearFieldCommunication;
  }
  if (transport == WebAuthn::AuthenticatorTransportEnum::Ble) {
    return device::FidoTransportProtocol::kBluetoothLowEnergy;
  }
  if (transport == WebAuthn::AuthenticatorTransportEnum::Cable ||
      transport == WebAuthn::AuthenticatorTransportEnum::Hybrid) {
    return device::FidoTransportProtocol::kHybrid;
  }
  if (transport == WebAuthn::AuthenticatorTransportEnum::Internal) {
    return device::FidoTransportProtocol::kInternal;
  }
  if (transport == WebAuthn::AuthenticatorTransportEnum::SmartCard) {
    return device::FidoTransportProtocol::kSmartCard;
  }
  return std::nullopt;
}
// LINT.ThenChange(//third_party/blink/public/devtools_protocol/domains/WebAuthn.pdl:AuthenticatorTransport)

std::vector<uint8_t> CopyBinaryToVector(const Binary& binary) {
  return std::vector<uint8_t>(binary.begin(), binary.end());
}

std::unique_ptr<WebAuthn::Credential> BuildCredentialFromRegistration(
    const VirtualAuthenticator& authenticator,
    base::span<const uint8_t> credential_id,
    const device::VirtualFidoDevice::RegistrationData& registration) {
  int sign_count = -1;
  if (registration.counter.has_value()) {
    sign_count = base::saturated_cast<int>(*registration.counter);
  }
  auto credential = WebAuthn::Credential::Create()
                        .SetCredentialId(Binary::fromSpan(credential_id))
                        .SetPrivateKey(Binary::fromVector(
                            registration.private_key->GetPKCS8PrivateKey()))
                        .SetSignCount(sign_count)
                        .SetIsResidentCredential(registration.is_resident)
                        .SetBackupEligibility(registration.backup_eligible)
                        .SetBackupState(registration.backup_state)
                        .Build();

  if (registration.rp) {
    credential->SetRpId(registration.rp->id);
  }
  if (registration.user) {
    credential->SetUserHandle(Binary::fromVector(registration.user->id));
    if (registration.user->name) {
      credential->SetUserName(*registration.user->name);
    }
    if (registration.user->display_name) {
      credential->SetUserDisplayName(*registration.user->display_name);
    }
  }

  if (authenticator.has_cmtg_key()) {
    auto cmtg_keys = std::make_unique<Array<Binary>>();
    for (const auto& key : registration.cmtg_keys) {
      cmtg_keys->emplace_back(Binary::fromVector(key->GetPKCS8PrivateKey()));
    }
    credential->SetCmtgKeys(std::move(cmtg_keys));
    credential->SetActiveCmtgKeyIndex(
        base::saturated_cast<int>(registration.selected_cmtg_key_index));
    credential->SetGenerateCmtgKeyOnNextOperation(
        registration.generate_cmtg_key_on_next_operation);
  }

  return credential;
}

}  // namespace

WebAuthnHandler::WebAuthnHandler()
    : DevToolsDomainHandler(WebAuthn::Metainfo::domainName) {}

WebAuthnHandler::~WebAuthnHandler() = default;

void WebAuthnHandler::SetRenderer(int process_host_id,
                                  RenderFrameHostImpl* frame_host) {
  if (!frame_host) {
    Disable();
  }
  frame_host_ = frame_host;
}

void WebAuthnHandler::Wire(UberDispatcher* dispatcher) {
  frontend_ = std::make_unique<WebAuthn::Frontend>(dispatcher->channel());
  WebAuthn::Dispatcher::wire(dispatcher, this);
}

Response WebAuthnHandler::Enable(std::optional<bool> enable_ui) {
  if (!frame_host_)
    return Response::ServerError(kDevToolsNotAttached);

  AuthenticatorEnvironment::GetInstance()->EnableVirtualAuthenticatorFor(
      frame_host_->frame_tree_node(),
      enable_ui.value_or(/*default_value=*/false));
  return Response::Success();
}

Response WebAuthnHandler::Disable() {
  if (frame_host_) {
    AuthenticatorEnvironment::GetInstance()->DisableVirtualAuthenticatorFor(
        frame_host_->frame_tree_node());
  }
  return Response::Success();
}

Response WebAuthnHandler::AddVirtualAuthenticator(
    std::unique_ptr<WebAuthn::VirtualAuthenticatorOptions> options,
    String* out_authenticator_id) {
  VirtualAuthenticatorManagerImpl* authenticator_manager =
      AuthenticatorEnvironment::GetInstance()
          ->MaybeGetVirtualAuthenticatorManager(frame_host_->frame_tree_node());
  if (!authenticator_manager)
    return Response::ServerError(kVirtualEnvironmentNotEnabled);

  auto transport = ConvertToFidoTransportProtocol(options->GetTransport());
  if (!transport)
    return Response::InvalidParams(kInvalidTransport);

  auto protocol = ConvertToProtocolVersion(options->GetProtocol());
  if (protocol == device::ProtocolVersion::kUnknown)
    return Response::InvalidParams(kInvalidProtocol);

  if (protocol == device::ProtocolVersion::kU2f &&
      !device::VirtualU2fDevice::IsTransportSupported(*transport)) {
    return Response::InvalidParams(kCableNotSupportedOnU2f);
  }

  auto ctap2_version = ConvertToCtap2Version(
      options->GetCtap2Version(WebAuthn::Ctap2VersionEnum::Ctap2_0));
  if (!ctap2_version)
    return Response::InvalidParams(kInvalidCtapVersion);

  bool has_large_blob = options->GetHasLargeBlob(/*defaultValue=*/false);
  bool has_cred_blob = options->GetHasCredBlob(/*defaultValue=*/false);
  bool has_min_pin_length = options->GetHasMinPinLength(/*defaultValue=*/false);
  bool has_prf = options->GetHasPrf(/*defaultValue=*/false);
  bool has_hmac_secret = options->GetHasHmacSecret(/*defaultValue=*/false);
  bool has_hmac_secret_mc = options->GetHasHmacSecretMc(/*defaultValue=*/false);
  bool has_resident_key = options->GetHasResidentKey(/*defaultValue=*/false);
  bool has_cmtg_key = options->GetHasCmtgKey(/*defaultValue=*/false);

  if (!has_resident_key && (has_large_blob || has_cmtg_key)) {
    return Response::InvalidParams(kRequiresResidentKey);
  }

  if (protocol != device::ProtocolVersion::kCtap2 && has_cmtg_key) {
    return Response::InvalidParams(kRequiresCtap2);
  }

  if ((protocol != device::ProtocolVersion::kCtap2 ||
       ctap2_version < device::Ctap2Version::kCtap2_1) &&
      (has_large_blob || has_cred_blob || has_min_pin_length)) {
    return Response::InvalidParams(kRequiresCtap2_1);
  }

  if (transport == device::FidoTransportProtocol::kInternal &&
      std::ranges::any_of(authenticator_manager->GetAuthenticators(),
                          [](const auto& authenticator) {
                            return authenticator->transport() ==
                                   device::FidoTransportProtocol::kInternal;
                          })) {
    return Response::InvalidParams(kAlreadyHasInternalAuthenticator);
  }

  VirtualAuthenticator::Options virt_auth_options;
  virt_auth_options.protocol = protocol;
  virt_auth_options.transport = *transport;

  switch (protocol) {
    case device::ProtocolVersion::kU2f:
      virt_auth_options.attachment =
          device::AuthenticatorAttachment::kCrossPlatform;
      break;
    case device::ProtocolVersion::kCtap2:
      virt_auth_options.ctap2_version = *ctap2_version;
      virt_auth_options.attachment =
          transport == device::FidoTransportProtocol::kInternal
              ? device::AuthenticatorAttachment::kPlatform
              : device::AuthenticatorAttachment::kCrossPlatform;
      virt_auth_options.has_resident_key = has_resident_key;
      virt_auth_options.has_user_verification =
          options->GetHasUserVerification(/*defaultValue=*/false);
      virt_auth_options.has_large_blob = has_large_blob;
      virt_auth_options.has_cred_blob = has_cred_blob;
      virt_auth_options.has_min_pin_length = has_min_pin_length;
      virt_auth_options.has_prf = has_prf;
      virt_auth_options.has_hmac_secret = has_hmac_secret;
      virt_auth_options.has_hmac_secret_mc = has_hmac_secret_mc;
      virt_auth_options.has_cmtg_key = has_cmtg_key;
      virt_auth_options.default_backup_eligibility =
          options->GetDefaultBackupEligibility(/*defaultValue=*/false);
      virt_auth_options.default_backup_state =
          options->GetDefaultBackupState(/*defaultValue=*/false);
      break;
    case device::ProtocolVersion::kUnknown:
      NOTREACHED();
  }

  VirtualAuthenticator* const authenticator =
      authenticator_manager->AddAuthenticatorAndReturnNonOwningPointer(
          virt_auth_options);
  if (!authenticator)
    return Response::ServerError(kErrorCreatingAuthenticator);

  authenticator->SetUserPresence(
      options->GetAutomaticPresenceSimulation(/*defaultValue=*/true));
  authenticator->set_user_verified(
      options->GetIsUserVerified(/*defaultValue=*/false));
  observations_.AddObservation(authenticator);

  *out_authenticator_id = authenticator->unique_id();
  return Response::Success();
}

Response WebAuthnHandler::RemoveVirtualAuthenticator(
    const String& authenticator_id) {
  VirtualAuthenticatorManagerImpl* authenticator_manager =
      AuthenticatorEnvironment::GetInstance()
          ->MaybeGetVirtualAuthenticatorManager(frame_host_->frame_tree_node());
  if (!authenticator_manager)
    return Response::ServerError(kVirtualEnvironmentNotEnabled);

  if (!authenticator_manager->RemoveAuthenticator(authenticator_id))
    return Response::InvalidParams(kAuthenticatorNotFound);

  return Response::Success();
}

Response WebAuthnHandler::SetResponseOverrideBits(
    const String& authenticator_id,
    std::optional<bool> is_bogus_signature,
    std::optional<bool> is_bad_uv,
    std::optional<bool> is_bad_up) {
  VirtualAuthenticatorManagerImpl* authenticator_manager =
      AuthenticatorEnvironment::GetInstance()
          ->MaybeGetVirtualAuthenticatorManager(frame_host_->frame_tree_node());
  if (!authenticator_manager)
    return Response::ServerError(kVirtualEnvironmentNotEnabled);

  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess())
    return Response::InvalidParams(kAuthenticatorNotFound);

  authenticator->set_bogus_signature(
      is_bogus_signature.value_or(/*default_value=*/false));
  authenticator->set_bad_uv_bit(is_bad_uv.value_or(/*default_value=*/false));
  authenticator->set_bad_up_bit(is_bad_up.value_or(/*default_value=*/false));
  return Response::Success();
}

void WebAuthnHandler::AddCredential(
    const String& authenticator_id,
    std::unique_ptr<WebAuthn::Credential> credential,
    std::unique_ptr<AddCredentialCallback> callback) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess()) {
    callback->sendFailure(std::move(response));
    return;
  }

  std::vector<std::unique_ptr<device::VirtualFidoDevice::PrivateKey>> cmtg_keys;
  if (authenticator->has_cmtg_key()) {
    if (credential->HasCmtgKeys()) {
      for (const Binary& key : *credential->GetCmtgKeys()) {
        auto parsed_key = device::VirtualFidoDevice::PrivateKey::FromPKCS8(key);
        if (!parsed_key) {
          callback->sendFailure(Response::InvalidParams(kInvalidCmtgKey));
          return;
        }
        cmtg_keys.push_back(std::move(*parsed_key));
      }
    }
    if (credential->HasActiveCmtgKeyIndex()) {
      int index = *credential->GetActiveCmtgKeyIndex();
      if (index < 0 || static_cast<size_t>(index) >= cmtg_keys.size()) {
        callback->sendFailure(
            Response::InvalidParams(kActiveCmtgKeyIndexOutOfBounds));
        return;
      }
    }
  } else if (credential->HasCmtgKeys() || credential->HasActiveCmtgKeyIndex() ||
             credential->HasGenerateCmtgKeyOnNextOperation()) {
    callback->sendFailure(Response::InvalidParams(kCmtgNotSupported));
    return;
  }

  Binary user_handle = credential->GetUserHandle(Binary());
  if (credential->HasUserHandle() &&
      user_handle.size() > device::kUserHandleMaxLength) {
    callback->sendFailure(Response::InvalidParams(
        kInvalidUserHandle +
        base::NumberToString(device::kUserHandleMaxLength)));
    return;
  }

  if (!credential->HasRpId()) {
    callback->sendFailure(Response::InvalidParams(kRpIdRequired));
    return;
  }
  if (credential->HasLargeBlob() && !credential->GetIsResidentCredential()) {
    callback->sendFailure(Response::InvalidParams(kRequiresResidentKey));
    return;
  }

  bool credential_created;
  std::vector<uint8_t> credential_id =
      CopyBinaryToVector(credential->GetCredentialId());

  std::optional<uint32_t> counter;
  if (credential->GetSignCount() < -1) {
    callback->sendFailure(Response::InvalidParams(kInvalidSignatureCounter));
    return;
  }
  if (credential->GetSignCount() > -1) {
    // -1 is a special value to mean no signature counter.
    counter = static_cast<uint32_t>(credential->GetSignCount());
  }

  if (credential->GetIsResidentCredential()) {
    if (!authenticator->has_resident_key()) {
      callback->sendFailure(
          Response::InvalidParams(kResidentCredentialNotSupported));
      return;
    }

    if (!credential->HasUserHandle()) {
      callback->sendFailure(
          Response::InvalidParams(kHandleRequiredForResidentCredential));
      return;
    }

    credential_created = authenticator->AddResidentRegistration(
        credential_id, credential->GetRpId(""), credential->GetPrivateKey(),
        counter, CopyBinaryToVector(user_handle), credential->GetUserName(""),
        credential->GetUserDisplayName(""));
  } else {
    credential_created =
        authenticator->AddRegistration(credential_id, credential->GetRpId(""),
                                       credential->GetPrivateKey(), counter);
  }

  if (!credential_created) {
    callback->sendFailure(Response::ServerError(kCouldNotCreateCredential));
    return;
  }

  if (authenticator->has_cmtg_key()) {
    for (auto& key : cmtg_keys) {
      CHECK(authenticator->AddCmtgKey(credential_id, std::move(key)));
    }
    CHECK(authenticator->SetSelectedCmtgKeyIndex(
        credential_id, credential->GetActiveCmtgKeyIndex(0)));
    CHECK(authenticator->SetGenerateCmtgKeyOnNextOperation(
        credential_id, credential->GetGenerateCmtgKeyOnNextOperation(false)));
  }

  if (credential->HasLargeBlob()) {
    authenticator->SetLargeBlob(
        credential_id, CopyBinaryToVector(credential->GetLargeBlob({})),
        base::BindOnce(
            [](std::unique_ptr<AddCredentialCallback> callback, bool success) {
              if (!success) {
                callback->sendFailure(
                    Response::ServerError(kCouldNotStoreLargeBlob));
                return;
              }
              callback->sendSuccess();
            },
            std::move(callback)));
    return;
  }

  // VirtualFidoDevice takes care of setting BE & BS flags to the default
  // authenticator values whenever a new credential is created. Only override
  // the values if the client specified them.
  if (credential->HasBackupEligibility()) {
    authenticator->SetBackupEligibility(
        credential_id, credential->GetBackupEligibility(/*unused*/ false));
  }
  if (credential->HasBackupState()) {
    authenticator->SetBackupState(credential_id,
                                  credential->GetBackupState(/*unused*/ false));
  }

  callback->sendSuccess();
}

void WebAuthnHandler::GetCredential(
    const String& authenticator_id,
    const Binary& credential_id,
    std::unique_ptr<GetCredentialCallback> callback) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess()) {
    callback->sendFailure(response);
    return;
  }

  auto registration =
      authenticator->registrations().find(CopyBinaryToVector(credential_id));
  if (registration == authenticator->registrations().end()) {
    callback->sendFailure(Response::InvalidParams(kCredentialNotFound));
    return;
  }

  authenticator->GetLargeBlob(
      registration->first,
      base::BindOnce(
          [](std::unique_ptr<WebAuthn::Credential> registration,
             std::unique_ptr<GetCredentialCallback> callback,
             const std::optional<std::vector<uint8_t>> blob) {
            if (blob) {
              registration->SetLargeBlob(Binary::fromVector(std::move(*blob)));
            }
            callback->sendSuccess(std::move(registration));
          },
          BuildCredentialFromRegistration(*authenticator,
                                          base::span(registration->first),
                                          registration->second),
          std::move(callback)));
}

void WebAuthnHandler::GetCredentials(
    const String& authenticator_id,
    std::unique_ptr<GetCredentialsCallback> callback) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess()) {
    callback->sendFailure(response);
    return;
  }

  auto aggregator = base::MakeRefCounted<GetCredentialCallbackAggregator>(
      std::move(callback));
  for (const auto& registration : authenticator->registrations()) {
    authenticator->GetLargeBlob(
        registration.first,
        base::BindOnce(&GetCredentialCallbackAggregator::OnLargeBlob,
                       aggregator,
                       BuildCredentialFromRegistration(
                           *authenticator, base::span(registration.first),
                           registration.second)));
  }
}

Response WebAuthnHandler::RemoveCredential(const String& authenticator_id,
                                           const Binary& credential_id) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess())
    return response;

  if (!authenticator->RemoveRegistration(CopyBinaryToVector(credential_id)))
    return Response::InvalidParams(kCredentialNotFound);

  return Response::Success();
}

Response WebAuthnHandler::ClearCredentials(const String& authenticator_id) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess())
    return response;

  authenticator->ClearRegistrations();
  return Response::Success();
}

Response WebAuthnHandler::SetUserVerified(const String& authenticator_id,
                                          bool is_user_verified) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess())
    return response;

  authenticator->set_user_verified(is_user_verified);
  return Response::Success();
}

Response WebAuthnHandler::SetAutomaticPresenceSimulation(
    const String& authenticator_id,
    bool enabled) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess())
    return response;

  authenticator->SetUserPresence(enabled);
  return Response::Success();
}

Response WebAuthnHandler::SetCredentialProperties(
    const String& authenticator_id,
    const Binary& in_credential_id,
    std::optional<bool> backup_eligibility,
    std::optional<bool> backup_state,
    std::optional<int> active_cmtg_key_index,
    std::optional<bool> generate_cmtg_key_on_next_operation,
    std::optional<int> sign_count) {
  VirtualAuthenticator* authenticator;
  Response response = FindAuthenticator(authenticator_id, &authenticator);
  if (!response.IsSuccess()) {
    return response;
  }

  std::vector<uint8_t> credential_id = CopyBinaryToVector(in_credential_id);
  auto registration = authenticator->registrations().find(credential_id);
  if (registration == authenticator->registrations().end()) {
    return Response::InvalidParams(kCredentialNotFound);
  }

  if (backup_eligibility.has_value()) {
    authenticator->SetBackupEligibility(credential_id,
                                        backup_eligibility.value());
  }
  if (backup_state.has_value()) {
    authenticator->SetBackupState(credential_id, backup_state.value());
  }
  if (authenticator->has_cmtg_key()) {
    if (active_cmtg_key_index.has_value()) {
      if (*active_cmtg_key_index < 0 ||
          static_cast<size_t>(*active_cmtg_key_index) >=
              registration->second.cmtg_keys.size()) {
        return Response::InvalidParams(kActiveCmtgKeyIndexOutOfBounds);
      }
      CHECK(authenticator->SetSelectedCmtgKeyIndex(credential_id,
                                                   *active_cmtg_key_index));
    }
    if (generate_cmtg_key_on_next_operation.has_value()) {
      CHECK(authenticator->SetGenerateCmtgKeyOnNextOperation(
          credential_id, *generate_cmtg_key_on_next_operation));
    }
  } else {
    if (active_cmtg_key_index.has_value() ||
        generate_cmtg_key_on_next_operation.has_value()) {
      return Response::InvalidParams(kCmtgNotSupported);
    }
  }
  if (sign_count.has_value()) {
    if (*sign_count < -1) {
      return Response::InvalidParams(kInvalidSignatureCounter);
    }
    authenticator->SetSignatureCounter(
        credential_id,
        // -1 is used to represent no counter available.
        *sign_count == -1 ? std::nullopt
                          : std::make_optional<uint32_t>(*sign_count));
  }
  return Response::Success();
}

Response WebAuthnHandler::FindAuthenticator(
    const String& id,
    VirtualAuthenticator** out_authenticator) {
  *out_authenticator = nullptr;
  VirtualAuthenticatorManagerImpl* authenticator_manager =
      AuthenticatorEnvironment::GetInstance()
          ->MaybeGetVirtualAuthenticatorManager(frame_host_->frame_tree_node());
  if (!authenticator_manager)
    return Response::ServerError(kVirtualEnvironmentNotEnabled);

  *out_authenticator = authenticator_manager->GetAuthenticator(id);
  if (!*out_authenticator)
    return Response::InvalidParams(kAuthenticatorNotFound);

  return Response::Success();
}

void WebAuthnHandler::OnCredentialCreated(
    VirtualAuthenticator* authenticator,
    const device::VirtualFidoDevice::Credential& credential) {
  frontend_->CredentialAdded(
      authenticator->unique_id(),
      BuildCredentialFromRegistration(*authenticator, credential.first,
                                      *credential.second));
}

void WebAuthnHandler::OnCredentialDeleted(
    VirtualAuthenticator* authenticator,
    base::span<const uint8_t> credential_id) {
  frontend_->CredentialDeleted(authenticator->unique_id(),
                               Binary::fromSpan(credential_id));
}

void WebAuthnHandler::OnCredentialUpdated(
    VirtualAuthenticator* authenticator,
    const device::VirtualFidoDevice::Credential& credential) {
  frontend_->CredentialUpdated(
      authenticator->unique_id(),
      BuildCredentialFromRegistration(*authenticator, credential.first,
                                      *credential.second));
}

void WebAuthnHandler::OnAssertion(
    VirtualAuthenticator* authenticator,
    const device::VirtualFidoDevice::Credential& credential) {
  frontend_->CredentialAsserted(
      authenticator->unique_id(),
      BuildCredentialFromRegistration(*authenticator, credential.first,
                                      *credential.second));
}

void WebAuthnHandler::OnAuthenticatorWillBeDestroyed(
    VirtualAuthenticator* authenticator) {
  observations_.RemoveObservation(authenticator);
}

}  // namespace content::protocol
