blob: c24efb37df3c266b379c15048683155905ec8b74 [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/make_credential_task.h"
#include <utility>
#include "base/bind.h"
#include "device/base/features.h"
#include "device/fido/ctap2_device_operation.h"
#include "device/fido/u2f_command_constructor.h"
#include "device/fido/u2f_register_operation.h"
namespace device {
namespace {
// Checks whether the incoming MakeCredential request has ClientPin option that
// is compatible with the Chrome's CTAP2 implementation. According to the CTAP
// spec, CTAP2 authenticators that have client pin set will always error out on
// MakeCredential request when "pinAuth" parameter in the request is not set. As
// ClientPin is not supported on Chrome yet, this check allows Chrome to avoid
// such failures if possible by defaulting to U2F request when user verification
// is not required and the device supports U2F protocol.
// TODO(hongjunchoi): Remove this once ClientPin command is implemented.
// See: https://crbug.com/870892
bool ShouldUseU2fBecauseCtapRequiresClientPin(
const FidoDevice* device,
const CtapMakeCredentialRequest& request) {
if (request.user_verification() == UserVerificationRequirement::kRequired)
return false;
DCHECK(device && device->device_info());
bool client_pin_set =
device->device_info()->options().client_pin_availability ==
AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet;
bool supports_u2f = base::ContainsKey(device->device_info()->versions(),
ProtocolVersion::kU2f);
return client_pin_set && supports_u2f;
}
} // namespace
MakeCredentialTask::MakeCredentialTask(
FidoDevice* device,
CtapMakeCredentialRequest request_parameter,
MakeCredentialTaskCallback callback)
: FidoTask(device),
request_parameter_(std::move(request_parameter)),
callback_(std::move(callback)),
weak_factory_(this) {}
MakeCredentialTask::~MakeCredentialTask() = default;
void MakeCredentialTask::StartTask() {
if (device()->supported_protocol() == ProtocolVersion::kCtap &&
!request_parameter_.is_u2f_only() &&
!ShouldUseU2fBecauseCtapRequiresClientPin(device(), request_parameter_)) {
MakeCredential();
} else {
// |device_info| should be present iff the device is CTAP2. This will be
// used in |MaybeRevertU2fFallback| to restore the protocol of CTAP2 devices
// once this task is complete.
DCHECK((device()->supported_protocol() == ProtocolVersion::kCtap) ==
static_cast<bool>(device()->device_info()));
device()->set_supported_protocol(ProtocolVersion::kU2f);
U2fRegister();
}
}
void MakeCredentialTask::MakeCredential() {
register_operation_ = std::make_unique<Ctap2DeviceOperation<
CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
device(), request_parameter_, std::move(callback_),
base::BindOnce(&ReadCTAPMakeCredentialResponse,
device()->DeviceTransport()));
register_operation_->Start();
}
void MakeCredentialTask::U2fRegister() {
if (!IsConvertibleToU2fRegisterCommand(request_parameter_)) {
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
register_operation_ = std::make_unique<U2fRegisterOperation>(
device(), request_parameter_,
base::BindOnce(&MakeCredentialTask::MaybeRevertU2fFallback,
weak_factory_.GetWeakPtr()));
register_operation_->Start();
}
void MakeCredentialTask::MaybeRevertU2fFallback(
CtapDeviceResponseCode status,
base::Optional<AuthenticatorMakeCredentialResponse> response) {
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
if (device()->device_info()) {
// This was actually a CTAP2 device, but the protocol version was set to U2F
// because it had a PIN set and so, in order to make a credential, the U2F
// interface was used.
device()->set_supported_protocol(ProtocolVersion::kCtap);
}
std::move(callback_).Run(status, std::move(response));
}
} // namespace device