1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef DEVICE_FIDO_FIDO_DEVICE_H_
6 #define DEVICE_FIDO_FIDO_DEVICE_H_
7 
8 #include <stdint.h>
9 
10 #include <string>
11 #include <vector>
12 
13 #include "base/callback.h"
14 #include "base/component_export.h"
15 #include "base/macros.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/optional.h"
18 #include "base/strings/string16.h"
19 #include "device/fido/authenticator_get_info_response.h"
20 #include "device/fido/fido_constants.h"
21 #include "device/fido/fido_transport_protocol.h"
22 
23 namespace device {
24 
25 // Device abstraction for an individual CTAP1.0/CTAP2.0 device.
26 //
27 // Devices are instantiated with an unknown protocol version. Users should call
28 // |DiscoverSupportedProtocolAndDeviceInfo| to determine a device's
29 // capabilities and initialize the instance accordingly. Instances returned by
30 // |FidoDeviceDiscovery| are not fully initialized.
COMPONENT_EXPORT(DEVICE_FIDO)31 class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
32  public:
33   // CancelToken is an opaque value that can be used to cancel submitted
34   // requests.
35   typedef uint32_t CancelToken;
36   // kInvalidCancelToken is a |CancelToken| value that will not be returned as
37   // the result of |DeviceTransact| and thus can be used as a placeholder.
38   static constexpr CancelToken kInvalidCancelToken = 0;
39 
40   using DeviceCallback =
41       base::OnceCallback<void(base::Optional<std::vector<uint8_t>>)>;
42 
43   // Internal state machine states.
44   enum class State {
45     kInit,
46     // kConnecting occurs when the device is performing some initialisation. For
47     // example, HID devices need to allocate a channel ID before sending
48     // requests.
49     kConnecting,
50     kBusy,
51     kReady,
52     // kMsgError occurs when the the device responds with an error indicating an
53     // invalid command, parameter, or length. This is used within |FidoDevice|
54     // to handle the case of a device rejecting a CTAP2 GetInfo command. It is
55     // otherwise a fatal, terminal state.
56     kMsgError,
57     // kDeviceError indicates some error other than those covered by
58     // |kMsgError|. This is a terminal state.
59     kDeviceError,
60   };
61 
62   FidoDevice();
63   virtual ~FidoDevice();
64   // Pure virtual function defined by each device type, implementing
65   // the device communication transaction. The function must not immediately
66   // call (i.e. hairpin) |callback|.
67   virtual CancelToken DeviceTransact(std::vector<uint8_t> command,
68                                      DeviceCallback callback) = 0;
69   // Attempt to make the device "wink", i.e. grab the attention of the user
70   // usually by flashing a light. |callback| is run after a successful wink or
71   // if the device does not support winking, in which case it may run
72   // immediately.
73   virtual void TryWink(base::OnceClosure callback);
74   // Cancel attempts to cancel an enqueued request. If the request is currently
75   // active it will be aborted if possible, which is expected to cause it to
76   // complete with |kCtap2ErrKeepAliveCancel|. If the request is still enqueued
77   // it will be deleted and the callback called with
78   // |kCtap2ErrKeepAliveCancel| immediately. It is possible that a request to
79   // cancel may be unsuccessful and that the request may complete normally.
80   // It is safe to attempt to cancel an operation that has already completed.
81   virtual void Cancel(CancelToken token) = 0;
82   virtual std::string GetId() const = 0;
83   virtual base::string16 GetDisplayName() const;
84   virtual FidoTransportProtocol DeviceTransport() const = 0;
85 
86   // These must only be called on Bluetooth devices.
87   virtual bool IsInPairingMode() const;
88   virtual bool IsPaired() const;
89 
90   // Returns whether the service bit is set to require a PIN or passkey to pair
91   // for a FIDO Bluetooth device.
92   virtual bool RequiresBlePairingPin() const;
93 
94   virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
95 
96   // Sends a speculative AuthenticatorGetInfo request to determine whether the
97   // device supports the CTAP2 protocol, and initializes supported_protocol_
98   // and device_info_ according to the result.
99   virtual void DiscoverSupportedProtocolAndDeviceInfo(base::OnceClosure done);
100 
101   // Returns whether supported_protocol has been correctly initialized (usually
102   // by calling DiscoverSupportedProtocolAndDeviceInfo).
103   bool SupportedProtocolIsInitialized();
104   // TODO(martinkr): Rename to "SetSupportedProtocolForTesting".
105   void set_supported_protocol(ProtocolVersion supported_protocol) {
106     supported_protocol_ = supported_protocol;
107   }
108 
109   ProtocolVersion supported_protocol() const { return supported_protocol_; }
110   const base::Optional<AuthenticatorGetInfoResponse>& device_info() const {
111     return device_info_;
112   }
113   bool is_in_error_state() const {
114     return state_ == State::kMsgError || state_ == State::kDeviceError;
115   }
116 
117   State state_for_testing() const { return state_; }
118   void SetStateForTesting(State state) { state_ = state; }
119 
120  protected:
121   void OnDeviceInfoReceived(base::OnceClosure done,
122                             base::Optional<std::vector<uint8_t>> response);
123   void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
124 
125   State state_ = State::kInit;
126   ProtocolVersion supported_protocol_ = ProtocolVersion::kUnknown;
127   base::Optional<AuthenticatorGetInfoResponse> device_info_;
128   // If `true`, the device needs to be sent a specific wink command to flash
129   // when user presence is required.
130   bool needs_explicit_wink_ = false;
131   // next_cancel_token_ is the value of the next |CancelToken| returned by this
132   // device. It starts at one so that zero can be used as an invalid value where
133   // needed.
134   CancelToken next_cancel_token_ = kInvalidCancelToken + 1;
135 
136   DISALLOW_COPY_AND_ASSIGN(FidoDevice);
137 };
138 
139 }  // namespace device
140 
141 #endif  // DEVICE_FIDO_FIDO_DEVICE_H_
142