blob: a05818eaffc92e62af04328424dd3f231a131432 [file] [log] [blame]
// Copyright 2017 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.h"
#include <utility>
#include "base/stl_util.h"
#include "components/apdu/apdu_command.h"
#include "components/apdu/apdu_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/u2f_command_constructor.h"
#include "services/service_manager/public/cpp/connector.h"
namespace device {
// static
std::unique_ptr<U2fRequest> U2fRegister::TryRegistration(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& transports,
std::vector<std::vector<uint8_t>> registered_keys,
std::vector<uint8_t> challenge_digest,
std::vector<uint8_t> application_parameter,
bool individual_attestation_ok,
RegisterResponseCallback completion_callback) {
std::unique_ptr<U2fRequest> request = std::make_unique<U2fRegister>(
connector, transports, std::move(registered_keys),
std::move(challenge_digest), std::move(application_parameter),
individual_attestation_ok, std::move(completion_callback));
request->Start();
return request;
}
U2fRegister::U2fRegister(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& transports,
std::vector<std::vector<uint8_t>> registered_keys,
std::vector<uint8_t> challenge_digest,
std::vector<uint8_t> application_parameter,
bool individual_attestation_ok,
RegisterResponseCallback completion_callback)
: U2fRequest(connector,
transports,
std::move(application_parameter),
std::move(challenge_digest),
std::move(registered_keys)),
individual_attestation_ok_(individual_attestation_ok),
completion_callback_(std::move(completion_callback)),
weak_factory_(this) {}
U2fRegister::~U2fRegister() = default;
void U2fRegister::TryDevice() {
DCHECK(current_device_);
if (!registered_keys_.empty() && !CheckedForDuplicateRegistration()) {
auto it = registered_keys_.cbegin();
InitiateDeviceTransaction(
GetU2fSignApduCommand(application_parameter_, *it,
true /* check_only */),
base::BindOnce(&U2fRegister::OnTryCheckRegistration,
weak_factory_.GetWeakPtr(), it));
} else {
InitiateDeviceTransaction(
GetU2fRegisterApduCommand(individual_attestation_ok_),
base::BindOnce(&U2fRegister::OnTryDevice, weak_factory_.GetWeakPtr(),
false /* is_duplicate_registration */));
}
}
void U2fRegister::OnTryCheckRegistration(
std::vector<std::vector<uint8_t>>::const_iterator it,
base::Optional<std::vector<uint8_t>> response) {
const auto apdu_response =
response ? apdu::ApduResponse::CreateFromMessage(std::move(*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.
InitiateDeviceTransaction(
ConstructBogusU2fRegistrationCommand(),
base::BindOnce(&U2fRegister::OnTryDevice, 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 != registered_keys_.end()) {
InitiateDeviceTransaction(
GetU2fSignApduCommand(application_parameter_, *it,
true /* check_only */),
base::BindOnce(&U2fRegister::OnTryCheckRegistration,
weak_factory_.GetWeakPtr(), it));
} else {
checked_device_id_list_.insert(current_device_->GetId());
if (devices_.empty()) {
// When all devices have been checked, proceed to registration.
CompleteNewDeviceRegistration();
} else {
state_ = State::IDLE;
Transition();
}
}
break;
default:
// Some sort of failure occurred. Abandon this device and move on.
AbandonCurrentDeviceAndTransition();
break;
}
}
void U2fRegister::CompleteNewDeviceRegistration() {
if (current_device_)
attempted_devices_.push_back(std::move(current_device_));
devices_.splice(devices_.end(), std::move(attempted_devices_));
state_ = State::IDLE;
Transition();
return;
}
bool U2fRegister::CheckedForDuplicateRegistration() {
return base::ContainsKey(checked_device_id_list_, current_device_->GetId());
}
void U2fRegister::OnTryDevice(bool is_duplicate_registration,
base::Optional<std::vector<uint8_t>> response) {
const auto apdu_response =
response ? apdu::ApduResponse::CreateFromMessage(std::move(*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: {
state_ = State::COMPLETE;
if (is_duplicate_registration) {
std::move(completion_callback_)
.Run(FidoReturnCode::kUserConsentButCredentialExcluded,
base::nullopt);
break;
}
auto response =
AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
application_parameter_, apdu_response->data());
if (!response) {
// The response data was corrupted / didn't parse properly.
std::move(completion_callback_)
.Run(FidoReturnCode::kAuthenticatorResponseInvalid, base::nullopt);
break;
}
std::move(completion_callback_)
.Run(FidoReturnCode::kSuccess, std::move(response));
break;
}
case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
// Waiting for user touch, move on and try this device later.
state_ = State::IDLE;
Transition();
break;
default:
// An error has occurred, quit trying this device.
AbandonCurrentDeviceAndTransition();
break;
}
}
} // namespace device