| // 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. |
| |
| #ifndef DEVICE_FIDO_FIDO_REQUEST_HANDLER_BASE_H_ |
| #define DEVICE_FIDO_FIDO_REQUEST_HANDLER_BASE_H_ |
| |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/component_export.h" |
| #include "base/containers/flat_set.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/strings/string_piece_forward.h" |
| #include "build/build_config.h" |
| #include "device/fido/fido_device_authenticator.h" |
| #include "device/fido/fido_discovery_base.h" |
| #include "device/fido/fido_transport_protocol.h" |
| |
| namespace service_manager { |
| class Connector; |
| } // namespace service_manager |
| |
| namespace device { |
| |
| class BleAdapterManager; |
| class FidoAuthenticator; |
| |
| struct COMPONENT_EXPORT(DEVICE_FIDO) PlatformAuthenticatorInfo { |
| PlatformAuthenticatorInfo(std::unique_ptr<FidoAuthenticator> authenticator, |
| bool has_recognized_mac_touch_id_credential); |
| PlatformAuthenticatorInfo(PlatformAuthenticatorInfo&&); |
| PlatformAuthenticatorInfo& operator=(PlatformAuthenticatorInfo&& other); |
| ~PlatformAuthenticatorInfo(); |
| |
| std::unique_ptr<FidoAuthenticator> authenticator; |
| bool has_recognized_mac_touch_id_credential; |
| }; |
| |
| // Base class that handles authenticator discovery/removal. Its lifetime is |
| // equivalent to that of a single WebAuthn request. For each authenticator, the |
| // per-device work is carried out by one FidoAuthenticator instance, which is |
| // constructed in a FidoDiscoveryBase and passed to the request handler via its |
| // Observer interface. |
| class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase |
| : public FidoDiscoveryBase::Observer { |
| public: |
| using AuthenticatorMap = |
| std::map<std::string, FidoAuthenticator*, std::less<>>; |
| using RequestCallback = base::RepeatingCallback<void(const std::string&)>; |
| using BlePairingCallback = |
| base::RepeatingCallback<void(std::string authenticator_id, |
| std::string pin_code, |
| base::OnceClosure success_callback, |
| base::OnceClosure error_callback)>; |
| |
| enum class RequestType { kMakeCredential, kGetAssertion }; |
| |
| // Encapsulates data required to initiate WebAuthN UX dialog. Once all |
| // components of TransportAvailabilityInfo is set, |
| // AuthenticatorRequestClientDelegate should be notified. |
| // TODO(hongjunchoi): Add async calls to notify embedder when Bluetooth is |
| // powered on/off. |
| struct COMPONENT_EXPORT(DEVICE_FIDO) TransportAvailabilityInfo { |
| TransportAvailabilityInfo(); |
| TransportAvailabilityInfo(const TransportAvailabilityInfo& other); |
| TransportAvailabilityInfo& operator=( |
| const TransportAvailabilityInfo& other); |
| ~TransportAvailabilityInfo(); |
| |
| // TODO(hongjunchoi): Factor |rp_id| and |request_type| from |
| // TransportAvailabilityInfo. |
| // See: https://crbug.com/875011 |
| std::string rp_id; |
| RequestType request_type = RequestType::kMakeCredential; |
| |
| // The intersection of transports supported by the client and allowed by the |
| // relying party. |
| base::flat_set<FidoTransportProtocol> available_transports; |
| |
| bool has_recognized_mac_touch_id_credential = false; |
| bool is_ble_powered = false; |
| bool can_power_on_ble_adapter = false; |
| |
| // Indicates whether the native Windows WebAuthn API is available. |
| // Dispatching to it should be controlled by the embedder. |
| // |
| // The embedder: |
| // - may choose not to dispatch immediately if caBLE is available |
| // - should dispatch immediately if no other transport is available |
| bool has_win_native_api_authenticator = false; |
| |
| // Contains the authenticator ID of the native Windows |
| // authenticator if |has_win_native_api_authenticator| is true. |
| // This allows the observer to distinguish it from other |
| // authenticators. |
| std::string win_native_api_authenticator_id; |
| |
| // If true, dispatch of the request cannot be controlled by |
| // the embedder. The embedder must not display a UI for this |
| // request and must ignore all subsequent invocations of the |
| // Observer interface methods. |
| bool disable_embedder_ui = false; |
| }; |
| |
| class COMPONENT_EXPORT(DEVICE_FIDO) Observer { |
| public: |
| virtual ~Observer(); |
| |
| // This method will not be invoked until the observer is set. |
| virtual void OnTransportAvailabilityEnumerated( |
| TransportAvailabilityInfo data) = 0; |
| |
| // If true, the request handler will defer dispatch of its request onto the |
| // given authenticator to the embedder. The embedder needs to call |
| // |StartAuthenticatorRequest| when it wants to initiate request dispatch. |
| // |
| // This method is invoked before |FidoAuthenticatorAdded|, and may be |
| // invoked multiple times for the same authenticator. Depending on the |
| // result, the request handler might decide not to make the authenticator |
| // available, in which case it never gets passed to |
| // |FidoAuthenticatorAdded|. |
| virtual bool EmbedderControlsAuthenticatorDispatch( |
| const FidoAuthenticator& authenticator) = 0; |
| |
| virtual void BluetoothAdapterPowerChanged(bool is_powered_on) = 0; |
| virtual void FidoAuthenticatorAdded( |
| const FidoAuthenticator& authenticator) = 0; |
| virtual void FidoAuthenticatorRemoved(base::StringPiece device_id) = 0; |
| virtual void FidoAuthenticatorIdChanged( |
| base::StringPiece old_authenticator_id, |
| std::string new_authenticator_id) = 0; |
| virtual void FidoAuthenticatorPairingModeChanged( |
| base::StringPiece authenticator_id, |
| bool is_in_pairing_mode) = 0; |
| |
| // CollectPIN is called when a PIN is needed to complete a request. The |
| // |retries| parameter is either |nullopt| to indicate that the user needs |
| // to set a PIN, or contains the number of PIN attempts remaining before a |
| // hard lock. |
| virtual void CollectPIN( |
| base::Optional<int> attempts, |
| base::OnceCallback<void(std::string)> provide_pin_cb) = 0; |
| |
| // CollectClientPin is guaranteed to have been called previously. |
| virtual void FinishCollectPIN() = 0; |
| }; |
| |
| // TODO(https://crbug.com/769631): Remove the dependency on Connector once |
| // device/fido is servicified. The |available_transports| should be the |
| // intersection of transports supported by the client and allowed by the |
| // relying party. |
| FidoRequestHandlerBase( |
| service_manager::Connector* connector, |
| const base::flat_set<FidoTransportProtocol>& available_transports); |
| ~FidoRequestHandlerBase() override; |
| |
| // Triggers DispatchRequest() if |active_authenticators_| hold |
| // FidoAuthenticator with given |authenticator_id|. |
| void StartAuthenticatorRequest(const std::string& authenticator_id); |
| |
| // Invokes |FidoAuthenticator::Cancel| on all authenticators, except if |
| // matching |exclude_id|, if one is provided. Cancelled authenticators are |
| // immediately removed from |active_authenticators_|. |
| // |
| // This function is invoked either when: (a) the entire WebAuthn API request |
| // is canceled or, (b) a successful response or "invalid state error" is |
| // received from the any one of the connected authenticators, in which case |
| // all other authenticators are cancelled. |
| // https://w3c.github.io/webauthn/#iface-pkcredential |
| void CancelActiveAuthenticators(base::StringPiece exclude_id = nullptr); |
| void OnBluetoothAdapterEnumerated(bool is_present, |
| bool is_powered_on, |
| bool can_power_on); |
| void OnBluetoothAdapterPowerChanged(bool is_powered_on); |
| void PowerOnBluetoothAdapter(); |
| void InitiatePairingWithDevice(std::string authenticator_id, |
| std::string pin_code, |
| 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(Observer* observer) { |
| DCHECK(!observer_) << "Only one observer is supported."; |
| observer_ = observer; |
| |
| DCHECK(notify_observer_callback_); |
| notify_observer_callback_.Run(); |
| } |
| |
| // Set the platform authenticator for this request, if one is available. |
| // |AuthenticatorImpl| must call this method after invoking |set_observer| |
| // even if no platform authenticator is available, in which case it passes |
| // nullptr. |
| virtual void SetPlatformAuthenticatorOrMarkUnavailable( |
| base::Optional<PlatformAuthenticatorInfo> platform_authenticator_info); |
| |
| // Returns whether FidoAuthenticator with id equal to |authenticator_id| |
| // exists. Fake FidoRequestHandler objects used in testing overrides this |
| // function to simulate scenarios where authenticator with |authenticator_id| |
| // is known to the system. |
| virtual bool HasAuthenticator(const std::string& authentiator_id) const; |
| |
| TransportAvailabilityInfo& transport_availability_info() { |
| return transport_availability_info_; |
| } |
| |
| const AuthenticatorMap& AuthenticatorsForTesting() { |
| return active_authenticators_; |
| } |
| |
| protected: |
| // Subclasses implement this method to dispatch their request onto the given |
| // FidoAuthenticator. The FidoAuthenticator is owned by this |
| // FidoRequestHandler and stored in active_authenticators(). |
| virtual void DispatchRequest(FidoAuthenticator*) = 0; |
| |
| void Start(); |
| |
| AuthenticatorMap& active_authenticators() { return active_authenticators_; } |
| std::vector<std::unique_ptr<FidoDiscoveryBase>>& discoveries() { |
| return discoveries_; |
| } |
| Observer* 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; |
| |
| void InitDiscoveries( |
| const base::flat_set<FidoTransportProtocol>& available_transports); |
| #if defined(OS_WIN) |
| void InitDiscoveriesWin( |
| const base::flat_set<FidoTransportProtocol>& available_transports); |
| #endif |
| |
| void AddAuthenticator(FidoAuthenticator* authenticator); |
| void NotifyObserverTransportAvailability(); |
| |
| // Invokes FidoAuthenticator::InitializeAuthenticator(), followed by |
| // DispatchRequest(). InitializeAuthenticator() sends a GetInfo command |
| // to FidoDeviceAuthenticator instances in order to determine their protocol |
| // versions before a request can be dispatched. |
| void InitializeAuthenticatorAndDispatchRequest(FidoAuthenticator*); |
| void ConstructBleAdapterPowerManager(); |
| |
| AuthenticatorMap active_authenticators_; |
| std::vector<std::unique_ptr<FidoDiscoveryBase>> discoveries_; |
| Observer* observer_ = nullptr; |
| TransportAvailabilityInfo transport_availability_info_; |
| base::RepeatingClosure notify_observer_callback_; |
| std::unique_ptr<BleAdapterManager> bluetooth_adapter_manager_; |
| // TODO(martinkr): Inject platform authenticators through a new |
| // FidoDiscoveryBase specialization and hold ownership there. |
| std::unique_ptr<FidoAuthenticator> platform_authenticator_; |
| service_manager::Connector* const connector_; |
| |
| base::WeakPtrFactory<FidoRequestHandlerBase> weak_factory_; |
| DISALLOW_COPY_AND_ASSIGN(FidoRequestHandlerBase); |
| }; |
| |
| } // namespace device |
| |
| #endif // DEVICE_FIDO_FIDO_REQUEST_HANDLER_BASE_H_ |