| // Copyright 2018 The Fuchsia 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 GARNET_DRIVERS_BLUETOOTH_LIB_SM_PAIRING_STATE_H_ |
| #define GARNET_DRIVERS_BLUETOOTH_LIB_SM_PAIRING_STATE_H_ |
| |
| #include <memory> |
| #include <queue> |
| |
| #include "garnet/drivers/bluetooth/lib/hci/link_key.h" |
| #include "garnet/drivers/bluetooth/lib/l2cap/channel.h" |
| #include "garnet/drivers/bluetooth/lib/sm/bearer.h" |
| #include "garnet/drivers/bluetooth/lib/sm/status.h" |
| #include "garnet/drivers/bluetooth/lib/sm/types.h" |
| #include "lib/fxl/macros.h" |
| |
| namespace btlib { |
| namespace sm { |
| |
| // Represents the pairing state of a connected peer. The peer device must be a |
| // LE or a BR/EDR/LE device. |
| class PairingState final : public Bearer::Listener { |
| public: |
| // Delegate interface for pairing and bonding events. |
| class Delegate { |
| public: |
| virtual ~Delegate() = default; |
| |
| // Called to obtain a Temporary Key during legacy pairing. This should |
| // return a TK by invoking the |tk_response| parameter. |
| // |
| // If the delegate fails to obtain the TK, it should signal this by setting |
| // the |success| parameter to false. This will abort the pairing procedure. |
| // |
| // The delegate should use the following algorithm to provide a temporary |
| // key: |
| // |
| // 1. If |method| is kJustWorks, the |tk| should be set to 0. Depending on |
| // the I/O capabilities the user should be asked to confirm or reject the |
| // pairing. |
| // |
| // 2. If |method| is kPasskeyEntryDisplay, the |tk| should be set to a |
| // random integer between 0 and 999,999. This should be displayed to the |
| // user until the user has finished entering the passkey on the peer |
| // device. |
| // TODO(armansito): Notify the delegate on SMP keypress events to |
| // automatically dismiss the dialog. |
| // |
| // 3. If |method| is kPasskeyEntryInput, the user should be prompted to |
| // enter a 6-digit passkey. The |tk| should be set to this passkey. |
| using TkResponse = fit::function<void(bool success, uint32_t tk)>; |
| virtual void OnTemporaryKeyRequest(PairingMethod method, |
| TkResponse response) = 0; |
| |
| // Called when an ongoing pairing is completed with the given |status|. |
| virtual void OnPairingComplete(Status status) = 0; |
| |
| // Called when new pairing data has been obtained for this peer. |
| virtual void OnNewPairingData(const sm::PairingData& data) = 0; |
| |
| // Called when the link layer authentication procedure fails. This likely |
| // indicates that the LTK or STK used to encrypt the connection was rejected |
| // by the peer device. |
| // |
| // The underlying link will disconnect after this callback runs. |
| virtual void OnAuthenticationFailure(hci::Status status) = 0; |
| }; |
| |
| // |link|: The LE logical link over which pairing procedures occur. |
| // |smp|: The L2CAP LE SMP fixed channel that operates over |link|. |
| // |ioc|: The initial I/O capability. |
| // |delegate|: Delegate responsible for handling authentication challenges and |
| // storing pairing information. |
| PairingState(fxl::WeakPtr<hci::Connection> link, |
| fbl::RefPtr<l2cap::Channel> smp, IOCapability io_capability, |
| fxl::WeakPtr<Delegate> delegate); |
| ~PairingState() override; |
| |
| // Returns the current security properties of the LE link. |
| const SecurityProperties& security() const { return le_sec_; } |
| |
| // Assigns the requested |ltk| to this connection, adopting the security |
| // properties of |ltk|. If the local device is the master of the underlying |
| // link, then the link layer authentication procedure will be initiated. |
| // |
| // Returns false if a pairing procedure is in progress when this method is |
| // called. If the link layer authentication procedure fails, then the link |
| // will be disconnected by the controller (Vol 2, Part E, 7.8.24; |
| // hci::Connection guarantees this by severing the link directly). |
| // |
| // TODO(armansito): The failure path is less obvious when the local device is |
| // the slave since there is no way to immediately tell whether |ltk| is still |
| // valid or not, since the master initiates the authentication procedure. Make |
| // sure that we disconnect the link in that case? |
| bool SetCurrentSecurity(const LTK& ltk); |
| |
| // TODO(armansito): Add function to register a BR/EDR link and SMP channel. |
| |
| // Attempt to raise the security level of a the connection to the desired |
| // |level| and notify the result in |callback|. |
| // |
| // If the desired security properties are already satisfied, this procedure |
| // will succeed immediately (|callback| will be run with the current security |
| // properties). |
| // |
| // If a pairing procedure has already been initiated (either by us or the |
| // peer), the request will be queued and |callback| will be notified when the |
| // procedure completes. If the resulting security level does not satisfy |
| // |level|, pairing will be re-initiated. |
| // |
| // If no pairing is in progress then the local device will initiate pairing. |
| // |
| // If pairing fails |callback| will be called with a |status| that represents |
| // the error. |
| using PairingCallback = |
| fit::function<void(Status status, const SecurityProperties& sec_props)>; |
| void UpgradeSecurity(SecurityLevel level, PairingCallback callback); |
| |
| // Assign I/O capabilities. This aborts any ongoing pairing procedure and sets |
| // up the I/O capabilities to use for future requests. |
| void Reset(IOCapability io_capability); |
| |
| // Abort all ongoing pairing procedures and notify pairing callbacks with an |
| // error. |
| // TODO(armansito): Add a "pairing canceled" callback to notify the pairing |
| // delegate so that it can dismiss any user challenge. |
| void Abort(); |
| |
| private: |
| static constexpr size_t kPairingRequestSize = |
| sizeof(Header) + sizeof(PairingRequestParams); |
| |
| // Represents the state for LE legacy pairing. |
| struct LegacyState final { |
| // |id| uniquely identifies the pairing procedure that this state object is |
| // tied to. This is generated by PairingState. |
| explicit LegacyState(uint64_t id); |
| |
| // We are in Phase 1 if pairing features have not been obtained. |
| bool InPhase1() const; |
| |
| // We are in Phase 2 if we have obtained the TK and waiting for STK |
| // encryption. |
| bool InPhase2() const; |
| |
| // We are in Phase 3 if the link is encrypted with the STK but not all |
| // requested keys have been obtained. |
| bool InPhase3() const; |
| |
| bool IsComplete() const; |
| |
| // True if we are in the beginning of Phase 2 where we have not obtained the |
| // TK yet. |
| bool WaitingForTK() const; |
| |
| // True if all keys that are expected from the remote have been received. |
| bool RequestedKeysObtained() const; |
| |
| bool ShouldReceiveLTK() const; // True if peer should send the LTK |
| bool ShouldReceiveIdentity() const; // True if peer should send identity |
| bool ShouldSendLTK() const; // True if we should send the LTK |
| bool ShouldSendIdentity() const; // True if we should send identity info |
| |
| // True if LTK will be exchanged and the link is yet to be encrypted with |
| // the LTK. |
| bool WaitingForEncryptionWithLTK() const; |
| |
| // A unique token for this pairing state. |
| uint64_t id; |
| |
| // The pairing features obtained during Phase 1. If invalid, we're in |
| // Phase 1. Otherwise, we're in Phase 2. |
| std::optional<PairingFeatures> features; |
| |
| // True if the link has been encrypted with the STK. This means that we're |
| // in Phase 3. Otherwise we're in Phase 1 or 2. |
| bool stk_encrypted; |
| |
| // True if the link has been encrypted with the LTK. If the LTK should be |
| // exchanged, then pairing is considered complete when the link is |
| // encrypted with the LTK. |
| bool ltk_encrypted; |
| |
| // The remote keys that have been obtained so far. |
| KeyDistGenField obtained_remote_keys; |
| |
| // Data used to generate STK and confirm values in Phase 2. |
| bool has_tk; |
| bool has_peer_confirm; |
| bool has_peer_rand; |
| bool sent_local_confirm; |
| bool sent_local_rand; |
| common::UInt128 tk; |
| common::UInt128 local_confirm; |
| common::UInt128 peer_confirm; |
| common::UInt128 local_rand; |
| common::UInt128 peer_rand; |
| common::StaticByteBuffer<kPairingRequestSize> preq; |
| common::StaticByteBuffer<kPairingRequestSize> pres; |
| |
| // Data from the peer tracked during Phase 3. Parts of LTK are received in |
| // separate events. |
| bool has_ltk; |
| bool has_irk; |
| common::UInt128 ltk_bytes; // LTK without ediv/rand |
| common::UInt128 irk; |
| common::DeviceAddress identity_address; |
| |
| // Keys obtained during pairing. |
| std::optional<hci::LinkKey> ltk; // LTK with ediv/rand |
| }; |
| |
| // Represents a pending request to update the security level. |
| struct PendingRequest { |
| PendingRequest(SecurityLevel level, PairingCallback callback); |
| PendingRequest(PendingRequest&&) = default; |
| PendingRequest& operator=(PendingRequest&&) = default; |
| |
| SecurityLevel level; |
| PairingCallback callback; |
| }; |
| |
| // Aborts an ongoing legacy pairing procedure. |
| void AbortLegacyPairing(ErrorCode error_code); |
| |
| // Begin Phase 1 of LE legacy pairing with the given |level|. |
| void BeginLegacyPairingPhase1(SecurityLevel level); |
| |
| // Begin Phase 2 of LE legacy pairing. This is called after LE pairing |
| // features have been exchanged and results (asynchronously) in the generation |
| // and encryption of a link using the STK. This follows (roughly) the |
| // following steps: |
| // 1. Asynchronously obtain the TK. |
| // 2. Generate the local confirm/rand values. |
| // 3. If initiator, start the exchange, otherwise wait for the peer to send |
| // its confirm value. |
| void BeginLegacyPairingPhase2(const common::ByteBuffer& preq, |
| const common::ByteBuffer& pres); |
| void LegacySendConfirmValue(); |
| void LegacySendRandomValue(); |
| |
| // Called when the link is encrypted with the STK at the end of Legacy |
| // Pairing Phase 2. |
| void EndLegacyPairingPhase2(); |
| |
| // Completes the legacy pairing process by cleaning up pairing state, updates |
| // the current security level, and notifies parties that have requested |
| // security. |
| void CompleteLegacyPairing(); |
| |
| // Bearer::Listener overrides: |
| void OnPairingFailed(Status status) override; |
| void OnFeatureExchange(const PairingFeatures& features, |
| const common::ByteBuffer& preq, |
| const common::ByteBuffer& pres) override; |
| void OnPairingConfirm(const common::UInt128& confirm) override; |
| void OnPairingRandom(const common::UInt128& random) override; |
| void OnLongTermKey(const common::UInt128& ltk) override; |
| void OnMasterIdentification(uint16_t ediv, uint64_t random) override; |
| void OnIdentityResolvingKey(const common::UInt128& irk) override; |
| void OnIdentityAddress(const common::DeviceAddress& address) override; |
| |
| // Called when the encryption state of the LE link changes. |
| void OnEncryptionChange(hci::Status status, bool enabled); |
| |
| // Called when an expected key was received from the peer during Phase 3 of |
| // legacy pairing. Completes the ongoing pairing procedure if all expected |
| // keys have been received. If a LTK was obtained then the link is encrypted |
| // before pairing is complete. |
| void OnExpectedKeyReceived(); |
| |
| // Returns pointers to the initiator and responder addresses. This can be |
| // called after pairing Phase 1. |
| void LEPairingAddresses(const common::DeviceAddress** out_initiator, |
| const common::DeviceAddress** out_responder); |
| |
| // The ID that will be assigned to the next pairing state. |
| unsigned int next_pairing_id_; |
| |
| fxl::WeakPtr<Delegate> delegate_; |
| |
| // Data for the currently registered LE-U link, if any. |
| fxl::WeakPtr<hci::Connection> le_link_; |
| std::unique_ptr<Bearer> le_smp_; // SMP data bearer for the LE-U link. |
| SecurityProperties le_sec_; // Current security properties of the LE-U link. |
| |
| // The state of the LE legacy pairing procedure, if any. |
| std::unique_ptr<LegacyState> legacy_state_; |
| |
| // The pending security requests added via UpgradeSecurity(). |
| std::queue<PendingRequest> request_queue_; |
| |
| // TODO(armansito): Support SMP over ACL-U for LE Secure Connections. |
| |
| fxl::WeakPtrFactory<PairingState> weak_ptr_factory_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(PairingState); |
| }; |
| |
| } // namespace sm |
| } // namespace btlib |
| |
| #endif // GARNET_DRIVERS_BLUETOOTH_LIB_SM_PAIRING_STATE_H_ |