blob: 72a9aad0d97d08521dfef938e457133637b07c69 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/fido/get_assertion_task.h"
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "device/base/features.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace device {
namespace {
using TestGetAssertionTaskCallbackReceiver =
::device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<AuthenticatorGetAssertionResponse>>;
} // namespace
class FidoGetAssertionTaskTest : public testing::Test {
public:
FidoGetAssertionTaskTest() {
scoped_feature_list_.emplace();
scoped_feature_list_->InitAndEnableFeature(kNewCtap2Device);
}
TestGetAssertionTaskCallbackReceiver& get_assertion_callback_receiver() {
return cb_;
}
void RemoveCtapFlag() {
scoped_feature_list_.emplace();
scoped_feature_list_->InitAndDisableFeature(kNewCtap2Device);
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::Optional<base::test::ScopedFeatureList> scoped_feature_list_;
TestGetAssertionTaskCallbackReceiver cb_;
};
TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request_param.SetAllowList({{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(
test_data::kTestGetAssertionCredentialId)}});
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request_param),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
EXPECT_TRUE(get_assertion_callback_receiver().value());
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
}
TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
device->ExpectRequestAndRespondWith(
test_data::kU2fCheckOnlySignCommandApdu,
test_data::kApduEncodedNoErrorSignResponse);
device->ExpectRequestAndRespondWith(
test_data::kU2fSignCommandApdu,
test_data::kApduEncodedNoErrorSignResponse);
CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request_param.SetAllowList(
{{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request_param),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
EXPECT_TRUE(get_assertion_callback_receiver().value());
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
EXPECT_FALSE(device->device_info());
}
TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) {
RemoveCtapFlag();
auto device = std::make_unique<MockFidoDevice>();
device->ExpectRequestAndRespondWith(
test_data::kU2fCheckOnlySignCommandApdu,
test_data::kApduEncodedNoErrorSignResponse);
device->ExpectRequestAndRespondWith(
test_data::kU2fSignCommandApdu,
test_data::kApduEncodedNoErrorSignResponse);
CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request_param.SetAllowList(
{{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request_param),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
EXPECT_TRUE(get_assertion_callback_receiver().value());
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
EXPECT_FALSE(device->device_info());
}
// Tests a scenario where the authenticator responds with credential ID that
// is not included in the allowed list.
TEST_F(FidoGetAssertionTaskTest, TestGetAssertionInvalidCredential) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
}
// Tests a scenario where authenticator responds without user entity in its
// response but client is expecting a resident key credential.
TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectUserEntity) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectRpIdHash) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion, base::nullopt);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestIncompatibleUserVerificationSetting) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestGetInfoResponseWithoutUvSupport);
auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request.SetUserVerification(UserVerificationRequirement::kRequired);
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest,
TestU2fSignRequestWithUserVerificationRequired) {
auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash);
request.SetAllowList(
{{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
request.SetUserVerification(UserVerificationRequirement::kRequired);
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
test_data::kClientDataHash);
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
} // namespace device