| // 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 |