blob: 24cc98701ae662e14eb838e94be6646914af9efd [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 <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/ctap_make_credential_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/make_credential_task.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 {
constexpr uint8_t kClientDataHash[] = {0x01, 0x02, 0x03};
constexpr uint8_t kUserId[] = {0x01, 0x02, 0x03};
constexpr char kRpId[] = "acme.com";
using TestMakeCredentialTaskCallback =
::device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<AuthenticatorMakeCredentialResponse>>;
} // namespace
class FidoMakeCredentialTaskTest : public testing::Test {
public:
FidoMakeCredentialTaskTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
std::unique_ptr<MakeCredentialTask> CreateMakeCredentialTask(
FidoDevice* device) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(
fido_parsing_utils::Materialize(kUserId));
return std::make_unique<MakeCredentialTask>(
device,
CtapMakeCredentialRequest(
fido_parsing_utils::Materialize(kClientDataHash), std::move(rp),
std::move(user),
PublicKeyCredentialParams(
std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
AuthenticatorSelectionCriteria(), callback_receiver_.callback());
}
std::unique_ptr<MakeCredentialTask>
CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
FidoDevice* device,
AuthenticatorSelectionCriteria criteria) {
PublicKeyCredentialRpEntity rp(kRpId);
PublicKeyCredentialUserEntity user(
fido_parsing_utils::Materialize(kUserId));
return std::make_unique<MakeCredentialTask>(
device,
CtapMakeCredentialRequest(
fido_parsing_utils::Materialize(kClientDataHash), std::move(rp),
std::move(user),
PublicKeyCredentialParams(
std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
std::move(criteria), callback_receiver_.callback());
}
TestMakeCredentialTaskCallback& make_credential_callback_receiver() {
return callback_receiver_;
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
TestMakeCredentialTaskCallback callback_receiver_;
};
TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialSuccess) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponse);
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
EXPECT_TRUE(make_credential_callback_receiver().value());
EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
EXPECT_TRUE(device->device_info());
}
TEST_F(FidoMakeCredentialTaskTest, TestIncorrectAuthenticatorGetInfoResponse) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
EXPECT_FALSE(device->device_info());
}
TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialWithIncorrectRpIdHash) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponseWithIncorrectRpIdHash);
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
}
TEST_F(FidoMakeCredentialTaskTest,
TestUserVerificationAuthenticatorSelectionCriteria) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestGetInfoResponseWithoutUvSupport);
const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
device.get(),
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
false /* require_resident_key */,
UserVerificationRequirement::kRequired));
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_TRUE(device->device_info());
EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
kSupportedButNotConfigured,
device->device_info()->options().user_verification_availability());
}
TEST_F(FidoMakeCredentialTaskTest,
TestPlatformDeviceAuthenticatorSelectionCriteria) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestGetInfoResponseCrossPlatformDevice);
const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
device.get(),
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
false /* require_resident_key */,
UserVerificationRequirement::kPreferred));
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_TRUE(device->device_info());
EXPECT_FALSE(device->device_info()->options().is_platform_device());
}
TEST_F(FidoMakeCredentialTaskTest,
TestResidentKeyAuthenticatorSelectionCriteria) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestGetInfoResponseWithoutResidentKeySupport);
const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
device.get(),
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
true /* require_resident_key */,
UserVerificationRequirement::kPreferred));
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_TRUE(device->device_info());
EXPECT_FALSE(device->device_info()->options().supports_resident_key());
}
TEST_F(FidoMakeCredentialTaskTest,
TestSatisfyAllAuthenticatorSelectionCriteria) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponse);
const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
device.get(),
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
true /* require_resident_key */,
UserVerificationRequirement::kRequired));
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
EXPECT_TRUE(make_credential_callback_receiver().value());
EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_TRUE(device->device_info());
const auto& device_options = device->device_info()->options();
EXPECT_TRUE(device_options.is_platform_device());
EXPECT_TRUE(device_options.supports_resident_key());
EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
kSupportedAndConfigured,
device_options.user_verification_availability());
}
TEST_F(FidoMakeCredentialTaskTest, TestIncompatibleUserVerificationSetting) {
auto device = std::make_unique<MockFidoDevice>();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo,
test_data::kTestGetInfoResponseWithoutUvSupport);
const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
device.get(),
AuthenticatorSelectionCriteria(
AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
false /* require_resident_key */,
UserVerificationRequirement::kRequired));
make_credential_callback_receiver().WaitForCallback();
EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_FALSE(make_credential_callback_receiver().value());
}
} // namespace device