blob: e5686fc938f71c80d42db139a0b871dd4ecd6f37 [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.
#ifndef DEVICE_FIDO_FIDO_DEVICE_H_
#define DEVICE_FIDO_FIDO_DEVICE_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/memory/weak_ptr.h"
#include "device/fido/authenticator_get_info_response.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_transport_protocol.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace device {
// Device abstraction for an individual CTAP1.0/CTAP2.0 device.
//
// Devices are instantiated with an unknown protocol version. Users should call
// |DiscoverSupportedProtocolAndDeviceInfo| to determine a device's
// capabilities and initialize the instance accordingly. Instances returned by
// |FidoDeviceDiscovery| are not fully initialized.
class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
public:
// CancelToken is an opaque value that can be used to cancel submitted
// requests.
typedef uint32_t CancelToken;
// kInvalidCancelToken is a |CancelToken| value that will not be returned as
// the result of |DeviceTransact| and thus can be used as a placeholder.
static constexpr CancelToken kInvalidCancelToken = 0;
using DeviceCallback =
base::OnceCallback<void(absl::optional<std::vector<uint8_t>>)>;
// Internal state machine states.
enum class State {
kInit,
// kConnecting occurs when the device is performing some initialisation. For
// example, HID devices need to allocate a channel ID before sending
// requests.
kConnecting,
kBusy,
kReady,
// kMsgError occurs when the the device responds with an error indicating an
// invalid command, parameter, or length. This is used within |FidoDevice|
// to handle the case of a device rejecting a CTAP2 GetInfo command. It is
// otherwise a fatal, terminal state.
kMsgError,
// kDeviceError indicates some error other than those covered by
// |kMsgError|. This is a terminal state.
kDeviceError,
};
FidoDevice();
FidoDevice(const FidoDevice&) = delete;
FidoDevice& operator=(const FidoDevice&) = delete;
virtual ~FidoDevice();
// Pure virtual function defined by each device type, implementing
// the device communication transaction. The function must not immediately
// call (i.e. hairpin) |callback|.
virtual CancelToken DeviceTransact(std::vector<uint8_t> command,
DeviceCallback callback) = 0;
// Attempt to make the device "wink", i.e. grab the attention of the user
// usually by flashing a light. |callback| is run after a successful wink or
// if the device does not support winking, in which case it may run
// immediately.
virtual void TryWink(base::OnceClosure callback);
// Cancel attempts to cancel an enqueued request. If the request is currently
// active it will be aborted if possible, which is expected to cause it to
// complete with |kCtap2ErrKeepAliveCancel|. If the request is still enqueued
// it will be deleted and the callback called with
// |kCtap2ErrKeepAliveCancel| immediately. It is possible that a request to
// cancel may be unsuccessful and that the request may complete normally.
// It is safe to attempt to cancel an operation that has already completed.
virtual void Cancel(CancelToken token) = 0;
// GetId returns a unique string representing this device. This string should
// be distinct from all other devices concurrently discovered.
virtual std::string GetId() const = 0;
// GetDisplayName returns a string identifying a device to a human, which
// might not be unique. For example, |GetDisplayName| could return the VID:PID
// of a HID device, but |GetId| could not because two devices can share the
// same VID:PID. It defaults to returning the value of |GetId|.
virtual std::string GetDisplayName() const;
virtual FidoTransportProtocol DeviceTransport() const = 0;
// These must only be called on Bluetooth devices.
virtual bool IsInPairingMode() const;
virtual bool IsPaired() const;
// Returns whether the service bit is set to require a PIN or passkey to pair
// for a FIDO Bluetooth device.
virtual bool RequiresBlePairingPin() const;
// NoSilentRequests returns true if this device does not support up=false
// requests.
bool NoSilentRequests() const;
virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
// Sends a speculative AuthenticatorGetInfo request to determine whether the
// device supports the CTAP2 protocol, and initializes supported_protocol_
// and device_info_ according to the result.
virtual void DiscoverSupportedProtocolAndDeviceInfo(base::OnceClosure done);
// Returns whether supported_protocol has been correctly initialized (usually
// by calling DiscoverSupportedProtocolAndDeviceInfo).
bool SupportedProtocolIsInitialized();
// TODO(martinkr): Rename to "SetSupportedProtocolForTesting".
void set_supported_protocol(ProtocolVersion supported_protocol) {
supported_protocol_ = supported_protocol;
}
ProtocolVersion supported_protocol() const { return supported_protocol_; }
const absl::optional<AuthenticatorGetInfoResponse>& device_info() const {
return device_info_;
}
bool is_in_error_state() const {
return state_ == State::kMsgError || state_ == State::kDeviceError;
}
// IsStatusForUnrecognisedCredentialID returns true iff the given |status|, in
// response to a CTAP2 GetAssertion command, indicates that none of the
// credential IDs was recognised by the authenticator.
static bool IsStatusForUnrecognisedCredentialID(
CtapDeviceResponseCode status);
State state_for_testing() const { return state_; }
void SetStateForTesting(State state) { state_ = state; }
protected:
void OnDeviceInfoReceived(base::OnceClosure done,
absl::optional<std::vector<uint8_t>> response);
void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
State state_ = State::kInit;
ProtocolVersion supported_protocol_ = ProtocolVersion::kUnknown;
absl::optional<AuthenticatorGetInfoResponse> device_info_;
// If `true`, the device needs to be sent a specific wink command to flash
// when user presence is required.
bool needs_explicit_wink_ = false;
// next_cancel_token_ is the value of the next |CancelToken| returned by this
// device. It starts at one so that zero can be used as an invalid value where
// needed.
CancelToken next_cancel_token_ = kInvalidCancelToken + 1;
};
} // namespace device
#endif // DEVICE_FIDO_FIDO_DEVICE_H_