blob: e80655a5f90f207ee97b8ceb58c173c20938ab45 [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/u2f_register_operation.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/apdu/apdu_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/device_response_converter.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/u2f_command_constructor.h"
namespace device {
U2fRegisterOperation::U2fRegisterOperation(
FidoDevice* device,
const CtapMakeCredentialRequest& request,
DeviceResponseCallback callback)
: DeviceOperation(device, request, std::move(callback)),
weak_factory_(this) {}
U2fRegisterOperation::~U2fRegisterOperation() = default;
void U2fRegisterOperation::Start() {
DCHECK(IsConvertibleToU2fRegisterCommand(request()));
const auto& exclude_list = request().exclude_list();
if (!exclude_list || exclude_list->empty()) {
TryRegistration(false /* is_duplicate_registration */);
} else {
auto it = request().exclude_list()->cbegin();
DispatchDeviceRequest(
ConvertToU2fCheckOnlySignCommand(request(), *it),
base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
weak_factory_.GetWeakPtr(), it));
}
}
void U2fRegisterOperation::TryRegistration(bool is_duplicate_registration) {
auto command = is_duplicate_registration
? ConstructBogusU2fRegistrationCommand()
: ConvertToU2fRegisterCommand(request());
DispatchDeviceRequest(
std::move(command),
base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
weak_factory_.GetWeakPtr(), is_duplicate_registration));
}
void U2fRegisterOperation::OnRegisterResponseReceived(
bool is_duplicate_registration,
base::Optional<std::vector<uint8_t>> device_response) {
const auto& apdu_response =
device_response
? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
: base::nullopt;
auto return_code = apdu_response ? apdu_response->status()
: apdu::ApduResponse::Status::SW_WRONG_DATA;
switch (return_code) {
case apdu::ApduResponse::Status::SW_NO_ERROR: {
if (is_duplicate_registration) {
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
base::nullopt);
break;
}
auto response =
AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
device()->DeviceTransport(),
fido_parsing_utils::CreateSHA256Hash(request().rp().rp_id()),
apdu_response->data());
std::move(callback())
.Run(CtapDeviceResponseCode::kSuccess, std::move(response));
break;
}
case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
// Waiting for user touch, retry after delay.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&U2fRegisterOperation::TryRegistration,
weak_factory_.GetWeakPtr(), is_duplicate_registration),
kU2fRetryDelay);
break;
default:
// An error has occurred, quit trying this device.
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
break;
}
}
void U2fRegisterOperation::OnCheckForExcludedKeyHandle(
ExcludeListIterator it,
base::Optional<std::vector<uint8_t>> device_response) {
const auto& apdu_response =
device_response
? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
: base::nullopt;
auto return_code = apdu_response ? apdu_response->status()
: apdu::ApduResponse::Status::SW_WRONG_DATA;
switch (return_code) {
case apdu::ApduResponse::Status::SW_NO_ERROR:
case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
// Duplicate registration found. Call bogus registration to check for
// user presence (touch) and terminate the registration process.
DispatchDeviceRequest(
ConstructBogusU2fRegistrationCommand(),
base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
weak_factory_.GetWeakPtr(),
true /* is_duplicate_registration */));
break;
}
case apdu::ApduResponse::Status::SW_WRONG_DATA:
// Continue to iterate through the provided key handles in the exclude
// list and check for already registered keys.
if (++it != request().exclude_list()->cend()) {
DispatchDeviceRequest(
ConvertToU2fCheckOnlySignCommand(request(), *it),
base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
weak_factory_.GetWeakPtr(), it));
} else {
// Reached the end of exclude list with no duplicate credential.
// Proceed with registration.
DispatchDeviceRequest(
ConvertToU2fRegisterCommand(request()),
base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
weak_factory_.GetWeakPtr(),
false /* is_duplicate_registration */));
}
break;
default:
// Some sort of failure occurred. Silently drop device request.
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
break;
}
}
} // namespace device