blob: 3ec9a9f1eae0c291c06d54f9a44a786f632f6a25 [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 <utility>
#include "base/bind.h"
#include "device/base/features.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap2_device_operation.h"
#include "device/fido/u2f_sign_operation.h"
namespace device {
namespace {
bool MayFallbackToU2fWithAppIdExtension(
const FidoDevice& device,
const CtapGetAssertionRequest& request) {
bool ctap2_device_supports_u2f =
device.device_info() &&
base::ContainsKey(device.device_info()->versions(),
ProtocolVersion::kU2f);
return request.alternative_application_parameter() &&
ctap2_device_supports_u2f;
}
} // namespace
GetAssertionTask::GetAssertionTask(FidoDevice* device,
CtapGetAssertionRequest request,
GetAssertionTaskCallback callback)
: FidoTask(device),
request_(std::move(request)),
callback_(std::move(callback)),
weak_factory_(this) {}
GetAssertionTask::~GetAssertionTask() = default;
void GetAssertionTask::StartTask() {
if (device()->supported_protocol() == ProtocolVersion::kCtap) {
GetAssertion();
} else {
U2fSign();
}
}
void GetAssertionTask::GetAssertion(bool enforce_user_presence) {
// If appId extension was used in the request and device is a hybrid U2F/CTAP2
// device, then first issue a silent GetAssertionRequest. If no credentials in
// allowed credential list are recognized, it's possible that the credential
// is registered via U2F. Under these circumstances, the request should be
// issued via the U2F protocol. Otherwise, proceed with a normal GetAssertion
// request.
auto uv_configuration = request_.user_verification();
bool is_silent_authentication = false;
if (!enforce_user_presence &&
MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
is_silent_authentication = true;
request_.SetUserPresenceRequired(false /* user_presence_required */);
request_.SetUserVerification(UserVerificationRequirement::kDiscouraged);
}
sign_operation_ =
std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
AuthenticatorGetAssertionResponse>>(
device(), request_,
base::BindOnce(&GetAssertionTask::GetAssertionCallbackWithU2fFallback,
weak_factory_.GetWeakPtr(), is_silent_authentication,
uv_configuration, std::move(callback_)),
base::BindOnce(&ReadCTAPGetAssertionResponse));
sign_operation_->Start();
}
void GetAssertionTask::U2fSign() {
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
sign_operation_ = std::make_unique<U2fSignOperation>(device(), request_,
std::move(callback_));
sign_operation_->Start();
}
void GetAssertionTask::GetAssertionCallbackWithU2fFallback(
bool is_silent_authentication,
UserVerificationRequirement user_verification_configuration,
GetAssertionTaskCallback callback,
CtapDeviceResponseCode response_code,
base::Optional<AuthenticatorGetAssertionResponse> response_data) {
DCHECK(device()->device_info());
if (!(is_silent_authentication &&
MayFallbackToU2fWithAppIdExtension(*device(), request_))) {
std::move(callback).Run(response_code, std::move(response_data));
return;
}
DCHECK(!callback_ && !request_.user_presence_required());
request_.SetUserPresenceRequired(true /* true */);
callback_ = std::move(callback);
// Credential was recognized by the device. As this authentication was
// silent authentication (i.e. user touch was not provided), try again with
// user presence enforced and with the original user verification
// configuration.
if (response_code == CtapDeviceResponseCode::kSuccess) {
DCHECK_EQ(UserVerificationRequirement::kDiscouraged,
request_.user_verification());
DCHECK_EQ(ProtocolVersion::kCtap, device()->supported_protocol());
request_.SetUserVerification(user_verification_configuration);
GetAssertion(true /* enforce_user_presence */);
} else {
// An error occurred or no credentials in the allowed list were recognized.
// However, as the relying party has provided appId extension, try again
// with U2F protocol to make sure that authentication via appID credential
// is also attempted.
device()->set_supported_protocol(ProtocolVersion::kU2f);
U2fSign();
}
}
} // namespace device