Commit 729eb451 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

device/fido: add PIN-setting and reset flows

This change adds request handlers for a future Settings UI to call into
in order to drive set/change PIN and reset operations.

Change-Id: I6df99d79807e4f87f20d4344a26eef7b896fd254
Reviewed-on: https://chromium-review.googlesource.com/c/1460412
Commit-Queue: Adam Langley <agl@chromium.org>
Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#632776}
parent 971c1bdd
......@@ -118,8 +118,12 @@ component("fido") {
"public_key_credential_rp_entity.h",
"public_key_credential_user_entity.cc",
"public_key_credential_user_entity.h",
"reset_request_handler.cc",
"reset_request_handler.h",
"response_data.cc",
"response_data.h",
"set_pin_request_handler.cc",
"set_pin_request_handler.h",
"u2f_command_constructor.cc",
"u2f_command_constructor.h",
"u2f_register_operation.cc",
......
......@@ -244,6 +244,11 @@ void FidoRequestHandlerBase::InitiatePairingWithDevice(
std::move(success_callback), std::move(error_callback));
}
void FidoRequestHandlerBase::ProvidePIN(const std::string& old_pin,
const std::string& pin) {
NOTREACHED();
}
base::WeakPtr<FidoRequestHandlerBase> FidoRequestHandlerBase::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
......
......@@ -175,6 +175,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
base::OnceClosure success_callback,
base::OnceClosure error_callback);
virtual void ProvidePIN(const std::string& old_pin, const std::string& pin);
base::WeakPtr<FidoRequestHandlerBase> GetWeakPtr();
void set_observer(TransportAvailabilityObserver* observer) {
......@@ -220,6 +222,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
}
TransportAvailabilityObserver* observer() const { return observer_; }
// FidoDiscoveryBase::Observer
void AuthenticatorAdded(FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) override;
void AuthenticatorRemoved(FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) override;
void AuthenticatorIdChanged(FidoDiscoveryBase* discovery,
const std::string& previous_id,
std::string new_id) override;
void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery,
const std::string& device_id,
bool is_in_pairing_mode) override;
private:
friend class FidoRequestHandlerTest;
......@@ -230,18 +244,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
const base::flat_set<FidoTransportProtocol>& available_transports);
#endif
// FidoDiscoveryBase::Observer
void AuthenticatorAdded(FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) final;
void AuthenticatorRemoved(FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) final;
void AuthenticatorIdChanged(FidoDiscoveryBase* discovery,
const std::string& previous_id,
std::string new_id) final;
void AuthenticatorPairingModeChanged(FidoDiscoveryBase* discovery,
const std::string& device_id,
bool is_in_pairing_mode) final;
void AddAuthenticator(FidoAuthenticator* authenticator);
void NotifyObserverTransportAvailability();
......
// Copyright 2019 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 <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_constants.h"
#include "device/fido/pin.h"
#include "device/fido/reset_request_handler.h"
namespace device {
ResetRequestHandler::ResetRequestHandler(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& supported_transports,
ResetSentCallback reset_sent_callback,
FinishedCallback finished_callback)
: FidoRequestHandlerBase(connector, supported_transports),
reset_sent_callback_(std::move(reset_sent_callback)),
finished_callback_(std::move(finished_callback)),
weak_factory_(this) {
Start();
}
ResetRequestHandler::~ResetRequestHandler() {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
CancelActiveAuthenticators();
}
void ResetRequestHandler::DispatchRequest(FidoAuthenticator* authenticator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
authenticator->GetTouch(base::BindOnce(&ResetRequestHandler::OnTouch,
weak_factory_.GetWeakPtr(),
authenticator));
}
void ResetRequestHandler::OnTouch(FidoAuthenticator* authenticator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
if (processed_touch_) {
return;
}
processed_touch_ = true;
CancelActiveAuthenticators();
if (authenticator->SupportedProtocol() != ProtocolVersion::kCtap) {
std::move(finished_callback_)
.Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand);
return;
}
authenticator->Reset(base::BindOnce(&ResetRequestHandler::OnResetComplete,
weak_factory_.GetWeakPtr()));
std::move(reset_sent_callback_).Run();
}
void ResetRequestHandler::OnResetComplete(
CtapDeviceResponseCode status,
base::Optional<pin::EmptyResponse> response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
DCHECK(processed_touch_);
if (status == CtapDeviceResponseCode::kSuccess && !response) {
status = CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
}
std::move(finished_callback_).Run(status);
}
} // namespace device
// Copyright 2019 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.
#ifndef DEVICE_FIDO_RESET_REQUEST_HANDLER_H_
#define DEVICE_FIDO_RESET_REQUEST_HANDLER_H_
#include <memory>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_transport_protocol.h"
namespace service_manager {
class Connector;
}; // namespace service_manager
namespace device {
class FidoAuthenticator;
namespace pin {
struct EmptyResponse;
}
// ResetRequestHandler is a simple state machine that gets a touch from an
// authenticator and then sends a CTAP2 reset request. This is expected to be
// driven by Settings UI for users to manually reset authenticators.
class COMPONENT_EXPORT(DEVICE_FIDO) ResetRequestHandler
: public FidoRequestHandlerBase {
public:
// ResetSentCallback will be run once an authenticator has been touched and a
// reset command has been sent to it. This will always occur before
// |FinishedCallback|.
using ResetSentCallback = base::OnceCallback<void()>;
// FinishedCallback will be called once this process has completed. If the
// status is |kCtap1ErrInvalidCommand| then the user may have selected a non-
// CTAP2 authenticator, in which case no reset command was ever sent.
// Otherwise the status is the result of the reset command.
using FinishedCallback = base::OnceCallback<void(CtapDeviceResponseCode)>;
ResetRequestHandler(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& supported_transports,
ResetSentCallback reset_sent_callback,
FinishedCallback finished_callback);
~ResetRequestHandler() override;
private:
// FidoRequestHandlerBase:
void DispatchRequest(FidoAuthenticator* authenticator) override;
void OnTouch(FidoAuthenticator* authenticator);
void OnResetComplete(CtapDeviceResponseCode status,
base::Optional<pin::EmptyResponse> response);
ResetSentCallback reset_sent_callback_;
FinishedCallback finished_callback_;
bool processed_touch_ = false;
SEQUENCE_CHECKER(my_sequence_checker_);
base::WeakPtrFactory<ResetRequestHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ResetRequestHandler);
};
} // namespace device
#endif // DEVICE_FIDO_RESET_REQUEST_HANDLER_H_
// Copyright 2019 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 <string>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_constants.h"
#include "device/fido/pin.h"
#include "device/fido/set_pin_request_handler.h"
namespace device {
SetPINRequestHandler::SetPINRequestHandler(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& supported_transports,
GetPINCallback get_pin_callback,
FinishedCallback finished_callback)
: FidoRequestHandlerBase(connector, supported_transports),
get_pin_callback_(std::move(get_pin_callback)),
finished_callback_(std::move(finished_callback)),
weak_factory_(this) {
Start();
}
SetPINRequestHandler::~SetPINRequestHandler() {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
CancelActiveAuthenticators();
}
void SetPINRequestHandler::ProvidePIN(const std::string& old_pin,
const std::string& new_pin) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
DCHECK_EQ(State::kWaitingForPIN, state_);
DCHECK(pin::IsValid(new_pin));
if (authenticator_ == nullptr) {
// Authenticator was detached.
state_ = State::kFinished;
finished_callback_.Run(CtapDeviceResponseCode::kCtap1ErrInvalidChannel);
return;
}
state_ = State::kGetEphemeralKey;
authenticator_->GetEphemeralKey(base::BindOnce(
&SetPINRequestHandler::OnHaveEphemeralKey, weak_factory_.GetWeakPtr(),
std::move(old_pin), std::move(new_pin)));
}
void SetPINRequestHandler::DispatchRequest(FidoAuthenticator* authenticator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
authenticator->GetTouch(base::BindOnce(&SetPINRequestHandler::OnTouch,
weak_factory_.GetWeakPtr(),
authenticator));
}
void SetPINRequestHandler::AuthenticatorRemoved(
FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
if (authenticator == authenticator_) {
authenticator_ = nullptr;
}
FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
}
void SetPINRequestHandler::OnTouch(FidoAuthenticator* authenticator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
if (state_ != State::kWaitingForTouch) {
return;
}
authenticator_ = authenticator;
CancelActiveAuthenticators();
switch (authenticator_->Options()->client_pin_availability) {
case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
state_ = State::kFinished;
finished_callback_.Run(CtapDeviceResponseCode::kCtap1ErrInvalidCommand);
return;
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedAndPinSet:
state_ = State::kGettingRetries;
authenticator_->GetRetries(
base::BindOnce(&SetPINRequestHandler::OnRetriesResponse,
weak_factory_.GetWeakPtr()));
break;
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedButPinNotSet:
state_ = State::kWaitingForPIN;
std::move(get_pin_callback_).Run(base::nullopt);
break;
}
}
void SetPINRequestHandler::OnRetriesResponse(
CtapDeviceResponseCode status,
base::Optional<pin::RetriesResponse> response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
DCHECK_EQ(state_, State::kGettingRetries);
if (status == CtapDeviceResponseCode::kSuccess && !response) {
status = CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
}
if (status != CtapDeviceResponseCode::kSuccess) {
state_ = State::kFinished;
finished_callback_.Run(status);
return;
}
state_ = State::kWaitingForPIN;
std::move(get_pin_callback_).Run(response->retries);
}
void SetPINRequestHandler::OnHaveEphemeralKey(
std::string old_pin,
std::string new_pin,
CtapDeviceResponseCode status,
base::Optional<pin::KeyAgreementResponse> response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
DCHECK_EQ(state_, State::kGetEphemeralKey);
if (status == CtapDeviceResponseCode::kSuccess && !response) {
status = CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
}
if (status != CtapDeviceResponseCode::kSuccess) {
state_ = State::kFinished;
finished_callback_.Run(status);
return;
}
state_ = State::kSettingPIN;
if (old_pin.empty()) {
authenticator_->SetPIN(
new_pin, *response,
base::BindOnce(&SetPINRequestHandler::OnSetPINComplete,
weak_factory_.GetWeakPtr()));
} else {
authenticator_->ChangePIN(
old_pin, new_pin, *response,
base::BindOnce(&SetPINRequestHandler::OnSetPINComplete,
weak_factory_.GetWeakPtr()));
}
}
void SetPINRequestHandler::OnSetPINComplete(
CtapDeviceResponseCode status,
base::Optional<pin::EmptyResponse> response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
DCHECK_EQ(state_, State::kSettingPIN);
if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
// The caller may try again.
state_ = State::kWaitingForPIN;
} else {
state_ = State::kFinished;
}
finished_callback_.Run(status);
}
} // namespace device
// Copyright 2019 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.
#ifndef DEVICE_FIDO_SET_PIN_REQUEST_HANDLER_H_
#define DEVICE_FIDO_SET_PIN_REQUEST_HANDLER_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_transport_protocol.h"
namespace service_manager {
class Connector;
}; // namespace service_manager
namespace device {
class FidoAuthenticator;
namespace pin {
struct RetriesResponse;
struct KeyAgreementResponse;
struct EmptyResponse;
} // namespace pin
// SetPINRequestHandler handles the Settings UI-based PIN setting flow. It
// flashes all authenticators so that the user can indicate which they want to
// set a PIN on, and then handles (potentially multiple) attempts at setting or
// changing the PIN.
class COMPONENT_EXPORT(DEVICE_FIDO) SetPINRequestHandler
: public FidoRequestHandlerBase {
public:
// GetPINCallback is called once, after the user has touched an authenticator,
// to request that the user enter a PIN. If the argument is |nullopt| then the
// authenticator has no PIN currently set. Otherwise it indicates the number
// of attempts remaining.
using GetPINCallback = base::OnceCallback<void(base::Optional<int64_t>)>;
// FinishedCallback is called multiple times once an attempt has completed.
// This can be called prior to |GetPINCallback| if the touched authenticator
// doesn't support setting a PIN. (In which case the error code will be
// |kCtap1ErrInvalidCommand|.) Otherwise it's called after |ProvidePIN| to
// report the outcome of an attempt at setting the PIN.
//
// Interesting status codes:
// |kCtap1ErrInvalidChannel|: authenticator was removed during the process.
// |kCtap1ErrInvalidCommand|: touched authenticator does not support PINs.
// |kCtap2ErrPinInvalid|: when changing a PIN, the old PIN was incorrect.
// In this case only, |ProvidePIN| may be called again to retry.
using FinishedCallback =
base::RepeatingCallback<void(CtapDeviceResponseCode)>;
SetPINRequestHandler(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& supported_transports,
GetPINCallback get_pin_callback,
FinishedCallback finished_callback);
~SetPINRequestHandler() override;
// ProvidePIN may be called after |get_pin_callback| has been used to indicate
// that an attempt at setting the PIN can be made. If the authenticator
// doesn't currently have a PIN set, then |old_pin| must be the empty string.
// pin::IsValid(new_pin) must be true when calling.
void ProvidePIN(const std::string& old_pin,
const std::string& new_pin) override;
private:
enum class State {
kWaitingForTouch,
kGettingRetries,
kWaitingForPIN,
kGetEphemeralKey,
kSettingPIN,
kFinished,
};
// FidoRequestHandlerBase:
void DispatchRequest(FidoAuthenticator* authenticator) override;
void AuthenticatorRemoved(FidoDiscoveryBase* discovery,
FidoAuthenticator* authenticator) override;
void OnTouch(FidoAuthenticator* authenticator);
void RequestRetries();
void OnRetriesResponse(CtapDeviceResponseCode status,
base::Optional<pin::RetriesResponse> response);
void OnHaveEphemeralKey(std::string old_pin,
std::string new_pin,
CtapDeviceResponseCode status,
base::Optional<pin::KeyAgreementResponse> response);
void OnSetPINComplete(CtapDeviceResponseCode status,
base::Optional<pin::EmptyResponse> response);
State state_ = State::kWaitingForTouch;
GetPINCallback get_pin_callback_;
FinishedCallback finished_callback_;
// authenticator_ is the authenticator that was selected by the initial touch.
// The pointed-at object is owned by the |FidoRequestHandlerBase| superclass
// of this class.
FidoAuthenticator* authenticator_ = nullptr;
SEQUENCE_CHECKER(my_sequence_checker_);
base::WeakPtrFactory<SetPINRequestHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SetPINRequestHandler);
};
} // namespace device
#endif // DEVICE_FIDO_SET_PIN_REQUEST_HANDLER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment