// 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 #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> 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> 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: case apdu::ApduResponse::Status::SW_WRONG_LENGTH: // 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