chromium/device/fido/fido_request_handler_unittest.cc

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

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

#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_device_authenticator.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_task.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "device/fido/mock_fido_device.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_WIN)
#include "device/fido/win/fake_webauthn_api.h"
#include "device/fido/win/webauthn_api.h"
#endif  // BUILDFLAG(IS_WIN)

_;

namespace device {

namespace {

FakeTaskCallback;
FakeHandlerFuture;

enum class FakeTaskResponse : uint8_t {};

// FidoRequestHandler that automatically starts discovery but does nothing on
// DispatchRequest().
class EmptyRequestHandler : public FidoRequestHandlerBase {};

class TestObserver : public FidoRequestHandlerBase::Observer {};

// Fake FidoTask implementation that sends an empty byte array to the device
// when StartTask() is invoked.
class FakeFidoTask : public FidoTask {};

class FakeFidoRequestHandler : public FidoRequestHandlerBase {};

std::vector<uint8_t> CreateFakeSuccessDeviceResponse() {}

std::vector<uint8_t> CreateFakeUserPresenceVerifiedError() {}

std::vector<uint8_t> CreateFakeDeviceProcesssingError() {}

std::vector<uint8_t> CreateFakeOperationDeniedError() {}

}  // namespace

class FidoRequestHandlerTest : public ::testing::Test {};

TEST_F(FidoRequestHandlerTest, TestSingleDeviceSuccess) {}

// Tests a scenario where two unresponsive authenticators are connected and
// cancel request has been sent either from the user or from the relying party
// (i.e. FidoRequestHandler object is destroyed.) Upon destruction, cancel
// command must be invoked to all connected authenticators.
TEST_F(FidoRequestHandlerTest, TestAuthenticatorHandlerReset) {}

// Test a scenario where 2 devices are connected and a response is received
// from only a single device(device1) and the remaining device hangs.
TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {}

// Test a scenario where 2 devices respond successfully with small time
// delay. Only the first received response should be passed on to the relying
// party, and cancel request should be sent to the other authenticator.
TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {}

// Test a scenario where 3 devices respond with a processing error, an UP(user
// presence) verified failure response with small time delay, and an UP
// verified failure response with big time delay, respectively. Request for
// device with processing error should be immediately dropped. Also, for UP
// verified failures, the first received response should be passed on to the
// relying party and cancel command should be sent to the remaining device.
TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {}

// If a device with transport type kInternal returns a
// CTAP2_ERR_OPERATION_DENIED error, the request should be cancelled on all
// pending authenticators.
TEST_F(FidoRequestHandlerTest,
       TestRequestWithOperationDeniedErrorInternalTransport) {}

// Like |TestRequestWithOperationDeniedErrorInternalTransport|, but with a
// cross-platform authenticator.
TEST_F(FidoRequestHandlerTest,
       TestRequestWithOperationDeniedErrorCrossPlatform) {}

// Requests should be dispatched to the platform authenticator.
TEST_F(FidoRequestHandlerTest, TestWithPlatformAuthenticator) {}

TEST_F(FidoRequestHandlerTest, InternalTransportDisallowedIfMarkedUnavailable) {}

TEST_F(FidoRequestHandlerTest,
       TransportAvailabilityNotificationOnObserverSetLate) {}

// This tests sets up a scenario where there are two platform authenticators
// and the first one returns a failure for discovery, to verify that the
// second is still used.
TEST_F(FidoRequestHandlerTest, TestWithMultiplePlatformAuthenticators) {}

#if BUILDFLAG(IS_WIN)

TEST_F(FidoRequestHandlerTest, TransportAvailabilityOfWindowsAuthenticator) {
  static const struct {
    bool api_available = false;
    bool is_uvpaa = false;
  } kTestCases[] = {
      /* clang-format off */
      /* api_available is_uvpaa */
      {true,           true},
      {true,           false},
      {false,          false},
      /* clang-format on */
  };
  FakeWinWebAuthnApi api;
  device::WinWebAuthnApi::ScopedOverride win_webauthn_api_override(&api);
  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(::testing::Message()
                 << "api_available=" << test_case.api_available);
    SCOPED_TRACE(::testing::Message() << "is_uvpaa=" << test_case.is_uvpaa);
    api.set_available(test_case.api_available);
    api.set_is_uvpaa(test_case.is_uvpaa);

    TestObserver observer;
    ForgeNextHidDiscovery();
    fake_discovery_factory_.set_discover_win_webauthn_api_authenticator(true);
    EmptyRequestHandler request_handler(
        {FidoTransportProtocol::kUsbHumanInterfaceDevice},
        &fake_discovery_factory_);
    request_handler.set_observer(&observer);

    // If the windows API is not enabled, the request is dispatched to the USB
    // discovery. Simulate a success to fill the transport availability info.
    if (!test_case.api_available) {
      discovery()->WaitForCallToStartAndSimulateSuccess();
    }

    auto transport_availability_info =
        observer.WaitForTransportAvailabilityInfo();
    EXPECT_EQ(transport_availability_info.available_transports.empty(),
              test_case.api_available);
    EXPECT_EQ(transport_availability_info.has_win_native_api_authenticator,
              test_case.api_available);
    EXPECT_EQ(transport_availability_info.win_is_uvpaa, test_case.is_uvpaa);
  }
}
#endif  // BUILDFLAG(IS_WIN)

}  // namespace device