chromium/device/fido/get_assertion_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 <tuple>
#include <utility>
#include <vector>

#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/device_response_converter.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device_authenticator.h"
#include "device/fido/fido_discovery_base.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "device/fido/get_assertion_request_handler.h"
#include "device/fido/make_credential_task.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/u2f_command_constructor.h"
#include "device/fido/virtual_ctap2_device.h"
#include "device/fido/virtual_fido_device.h"
#include "device/fido/virtual_fido_device_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

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

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/dbus/u2f/u2f_client.h"
#endif

namespace device {

namespace {

constexpr uint8_t kBogusCredentialId[] =;
constexpr char kRequestTransportHistogram[] =;
constexpr char kResponseTransportHistogram[] =;

TestGetAssertionRequestFuture;

}  // namespace

_;

// FidoGetAssertionHandlerTest allows testing GetAssertionRequestHandler against
// MockFidoDevices injected via a FakeFidoDiscoveryFactory.
class FidoGetAssertionHandlerTest : public ::testing::Test {};

TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {}

TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {}

// Test a scenario where the connected authenticator is a U2F device.
TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {}

TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {}

TEST_F(FidoGetAssertionHandlerTest,
       TestU2fSignRequestWithUserVerificationRequired) {}

TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {}

// Tests a scenario where the authenticator responds with credential ID that
// is not included in the allowed list.
TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {}

// Tests a scenario where the authenticator responds with an empty credential.
// When GetAssertion request only has a single credential in the allow list,
// this is a valid response. Check that credential is set by the client before
// the response is returned to the relying party.
TEST_F(FidoGetAssertionHandlerTest, ValidEmptyCredential) {}

TEST_F(FidoGetAssertionHandlerTest, TruncatedUTF8) {}

TEST_F(FidoGetAssertionHandlerTest, TruncatedAndInvalidUTF8) {}

// Tests a scenario where authenticator responds without user entity in its
// response but client is expecting a resident key credential.
TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {}

TEST_F(FidoGetAssertionHandlerTest,
       AllTransportsAllowedIfAllowCredentialsListUndefined) {}

TEST_F(FidoGetAssertionHandlerTest,
       AllTransportsAllowedIfAllowCredentialsListIsEmpty) {}

TEST_F(FidoGetAssertionHandlerTest,
       AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {}

TEST_F(FidoGetAssertionHandlerTest,
       AllowedTransportsAreUnionOfTransportsLists) {}

TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyNfc) {}

TEST_F(FidoGetAssertionHandlerTest,
       SupportedTransportsAreOnlyCableAndInternal) {}

TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {}

TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {}

TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {}

// If a device with transport type kInternal returns a
// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
// GetAssertionStatus::kUserConsentDenied. Pending authenticators should be
// cancelled.
TEST_F(FidoGetAssertionHandlerTest,
       TestRequestWithOperationDeniedErrorPlatform) {}

// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
// cross-platform device.
TEST_F(FidoGetAssertionHandlerTest,
       TestRequestWithOperationDeniedErrorCrossPlatform) {}

// If a device returns CTAP2_ERR_PIN_AUTH_INVALID, the request should complete
// with GetAssertionStatus::kUserConsentDenied.
TEST_F(FidoGetAssertionHandlerTest, TestRequestWithPinAuthInvalid) {}

MATCHER_P(IsCtap2Command, expected_command, "") {}

TEST_F(FidoGetAssertionHandlerTest, DeviceFailsImmediately) {}

TEST_F(FidoGetAssertionHandlerTest, PinUvAuthTokenPreTouchFailure) {}

// Tests a scenario where authenticator of incorrect transport type was used to
// conduct CTAP GetAssertion call.
//
// TODO(engedy): This should not happen, instead |allowCredentials| should be
// filtered to only contain items compatible with the transport actually used to
// talk to the authenticator.
TEST(GetAssertionRequestHandlerTest, IncorrectTransportType) {}

TEST_F(FidoGetAssertionHandlerTest, ReportTransportMetric) {}

#if BUILDFLAG(IS_WIN)

// Verify that the request handler instantiates a HID device backed
// FidoDeviceAuthenticator or a WinNativeCrossPlatformAuthenticator, depending
// on API availability.
TEST(GetAssertionRequestHandlerWinTest, TestWinUsbDiscovery) {
  base::test::TaskEnvironment task_environment;
  for (const bool enable_api : {false, true}) {
    SCOPED_TRACE(::testing::Message() << "enable_api=" << enable_api);
    FakeWinWebAuthnApi api;
    api.set_available(enable_api);
    api.InjectNonDiscoverableCredential(
        test_data::kTestGetAssertionCredentialId, test_data::kRelyingPartyId);
    WinWebAuthnApi::ScopedOverride win_webauthn_api_override(&api);

    // Simulate a connected HID device.
    ScopedFakeFidoHidManager fake_hid_manager;
    fake_hid_manager.AddFidoHidDevice("guid");

    TestGetAssertionRequestFuture future;
    FidoDiscoveryFactory fido_discovery_factory;
    CtapGetAssertionRequest request(test_data::kRelyingPartyId,
                                    test_data::kClientDataJson);
    request.allow_list = {PublicKeyCredentialDescriptor(
        CredentialType::kPublicKey,
        fido_parsing_utils::Materialize(
            test_data::kTestGetAssertionCredentialId))};
    auto handler = std::make_unique<GetAssertionRequestHandler>(
        &fido_discovery_factory,
        std::vector<std::unique_ptr<FidoDiscoveryBase>>(),
        base::flat_set<FidoTransportProtocol>(
            {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
        std::move(request), CtapGetAssertionOptions(),
        /*allow_skipping_pin_touch=*/true, future.GetCallback());
    task_environment.RunUntilIdle();

    EXPECT_EQ(handler->AuthenticatorsForTesting().size(), 1u);
    EXPECT_EQ(handler->AuthenticatorsForTesting().begin()->second->GetType() ==
                  AuthenticatorType::kWinNative,
              enable_api);
  }
}

#endif  // BUILDFLAG(IS_WIN)

}  // namespace device