1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
19 #include "pw_bluetooth_sapphire/internal/host/gap/pairing_delegate.h"
20 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
21 #include "pw_bluetooth_sapphire/internal/host/gap/secure_simple_pairing_state.h"
22 #include "pw_bluetooth_sapphire/internal/host/gap/types.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci-spec/link_key.h"
25 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
26 #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection.h"
27 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
28 
29 namespace bt::gap {
30 
31 // Implements event handlers and tracks the state of a peer's BR/EDR link
32 // throughout the Legacy Pairing process in order to drive HCI and user
33 // transactions.
34 //
35 // Each class instance occurs on a per-connection basis and is destroyed when
36 // the connection is destroyed.
37 //
38 // This class handles both the peer bonded case (both Hosts furnish their Link
39 // Keys to their Controllers) and the unbonded case (both Controllers perform
40 // Legacy Pairing and deliver the resulting Link Keys to their Hosts).
41 //
42 // Pairing is considered complete when the Link Keys have been used to
43 // successfully encrypt the link, at which time pairing may be restarted
44 // (possibly with different capabilities).
45 class LegacyPairingState final {
46  public:
47   // Used to report the status of each pairing procedure on this link. The
48   // callback's result will contain |HostError::kFailed| if the pairing
49   // procedure does not proceed in the order of events expected.
50   using StatusCallback =
51       fit::function<void(hci_spec::ConnectionHandle, hci::Result<>)>;
52 
53   // Constructs a LegacyPairingState to handle pairing protocols, commands, and
54   // events to the |peer|, prior to the ACL connection being established. We
55   // cannot populate |link_|, |send_auth_request_callback_|, and
56   // |status_callback_| until the ACL connection is complete.
57   LegacyPairingState(Peer::WeakPtr peer,
58                      PairingDelegate::WeakPtr pairing_delegate,
59                      bool outgoing_connection);
60 
61   // Constructs a LegacyPairingState for the ACL connection |link| to |peer| to
62   // handle pairing protocols, commands, and events. |link| must be valid for
63   // the lifetime of this object.
64   //
65   // |outgoing_connection| is true if this device initiated the connection, and
66   // false if it was an incoming connection.
67   //
68   // |auth_cb| will be called to indicate that the device should initiate an
69   // HCI_Authentication_Requested command for this |peer|. This should only
70   // occur when |outgoing_connection| is true.
71   //
72   // Successful pairing is reported through |status_cb| after encryption is
73   // enabled.
74   //
75   // This object will be put in a |kFailed| state upon any errors and the owner
76   // shall disconnect the link and destroy the LegacyPairingState. When
77   // destroyed, status callbacks for any queued pairing requests are called.
78   // |status_cb| is not called on destruction.
79   LegacyPairingState(Peer::WeakPtr peer,
80                      PairingDelegate::WeakPtr pairing_delegate,
81                      WeakPtr<hci::BrEdrConnection> link,
82                      bool outgoing_connection,
83                      fit::closure auth_cb,
84                      StatusCallback status_cb);
85   ~LegacyPairingState();
86 
87   // Sets the |link|'s callbacks fields when the ACL connection is complete
88   // (i.e. after HCI_Connection_Complete event). |auth_cb| and |status_cb| are
89   // passed in from either the LegacyPairingState or PairingStateManager
90   // constructor, depending on which object is first created due to the ordering
91   // of legacy pairing events (which may occur before or after the ACL
92   // connection is created).
93   void BuildEstablishedLink(WeakPtr<hci::BrEdrConnection> link,
94                             fit::closure auth_cb,
95                             StatusCallback status_cb);
96 
97   // Set a handler for user-interactive authentication challenges. If not set or
98   // set to nullptr, all pairing requests will be rejected. This does not cause
99   // a fatal error and should not result in link disconnection.
100   //
101   // If the delegate indicates passkey display capabilities, then it will always
102   // be asked to confirm pairing, even when Core Spec v5.4, Vol 3, Part C,
103   // Section 5.2.2.6 indicates "automatic confirmation".
SetPairingDelegate(PairingDelegate::WeakPtr pairing_delegate)104   void SetPairingDelegate(PairingDelegate::WeakPtr pairing_delegate) {
105     pairing_delegate_ = std::move(pairing_delegate);
106   }
107 
108   // If pairing is not already in progress, this device starts pairing against
109   // the peer and becomes the pairing initiator. If pairing is in progress, the
110   // request will be queued until the current pairing completes or an additional
111   // pairing that upgrades the link key succeeds or fails.
112   //
113   // If no PairingDelegate is available, |status_cb| is immediately called with
114   // |HostError::kNotReady|. The LegacyPairingState status callback (provided in
115   // the constructor) is not called.
116   //
117   // When pairing completes or errors out, the |status_cb| of each call to this
118   // function will be invoked with the result.
119   void InitiatePairing(StatusCallback status_cb);
120 
121   // Caller should send the returned link key in a HCI_Link_Key_Request_Reply
122   // (or HCI_Link_Key_Request_Negative_Reply if the returned value is null).
123   [[nodiscard]] std::optional<hci_spec::LinkKey> OnLinkKeyRequest();
124 
125   // |cb| is called with the pin code value to send HCI_PIN_Code_Request_Reply
126   // or std::nullopt to send HCI_PIN_Code_Request_Negative_Reply.
127   using UserPinCodeCallback =
128       fit::callback<void(std::optional<uint16_t> passkey)>;
129   void OnPinCodeRequest(UserPinCodeCallback cb);
130 
131   // If the connection is complete, store |link_key| in the connection.
132   // Otherwise store temporarily in |link_key_|.
133   void OnLinkKeyNotification(const UInt128& link_key,
134                              hci_spec::LinkKeyType key_type);
135 
136   // Retry pairing if the peer is missing a PIN or link key. Otherwise enable
137   // encryption.
138   void OnAuthenticationComplete(pw::bluetooth::emboss::StatusCode status_code);
139 
140   // Handler for hci::Connection::set_encryption_change_callback.
141   void OnEncryptionChange(hci::Result<bool> result);
142 
143   // Attach inspect node named |name| as a child of |parent|.
144   void AttachInspect(inspect::Node& parent, std::string name);
145 
146   // True if there is currently a pairing procedure in progress that the local
147   // device initiated.
initiator()148   bool initiator() const {
149     return is_pairing() ? current_pairing_->initiator : false;
150   }
151 
peer()152   Peer::WeakPtr peer() { return peer_; }
153 
outgoing_connection()154   bool outgoing_connection() const { return outgoing_connection_; }
155 
security_properties()156   sm::SecurityProperties& security_properties() { return bredr_security_; }
157 
link_key()158   std::optional<hci_spec::LinkKey> link_key() { return link_key_; }
159 
set_link_ltk()160   void set_link_ltk() {
161     link_->set_link_key(link_key_.value(), hci_spec::LinkKeyType::kCombination);
162   }
link_ltk()163   std::optional<hci_spec::LinkKey> link_ltk() const { return link_->ltk(); }
164 
165  private:
166   enum class State : uint8_t {
167     // Either waiting to locally initiate pairing, or for the pairing
168     // initiator's HCI_Link_Key_Request or HCI_PIN_Code_Request_Reply event
169     // (depending if the pairing initiator has a valid link key or not
170     // respectively).
171     kIdle,
172 
173     // Wait for HCI_Link_Key_Request event (only when pairing initiator)
174     kInitiatorWaitLinkKeyRequest,
175 
176     // Wait for HCI_PIN_Code_Request event
177     kWaitPinCodeRequest,
178 
179     // Wait for HCI_Link_Key_Notification
180     kWaitLinkKey,
181 
182     // Wait for HCI_Authentication_Complete event (nly when pairing initiator)
183     kInitiatorWaitAuthComplete,
184 
185     // Wait for HCI_Encryption_Change event
186     kWaitEncryption,
187 
188     // Wait for link closure and ignore events due to an error
189     kFailed,
190   };
191 
192   // Extra information for pairing constructed when a pairing procedure begins
193   // and destroyed when the pairing procedure is reset or errors out.
194   //
195   // Instances must be heap allocated so that they can be moved without
196   // destruction, preserving their WeakPtr holders. WeakPtrs are vended to
197   // PairingDelegate callbacks to uniquely identify each attempt to pair because
198   // |current_pairing_| is not synchronized to the user's actions through
199   // PairingDelegate.
200   class Pairing final {
201    public:
202     static std::unique_ptr<Pairing> MakeInitiator(
203         BrEdrSecurityRequirements security_requirements,
204         bool outgoing_connection);
205     static std::unique_ptr<Pairing> MakeResponder(
206         bool outgoing_connection,
207         std::optional<pw::bluetooth::emboss::IoCapability> peer_iocap =
208             std::nullopt);
209     // Make a responder for a peer that has initiated pairing.
210     static std::unique_ptr<Pairing> MakeResponderForBonded();
211 
212     // Used to prevent PairingDelegate callbacks from using captured stale
213     // pointers.
214     using WeakPtr = WeakSelf<Pairing>::WeakPtr;
GetWeakPtr()215     Pairing::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
216 
217     // True if the local device initiated pairing.
218     bool initiator;
219 
220     // True if we allow automatic pairing (i.e. not re-pairing and
221     // |outgoing_connection_| is true).
222     bool allow_automatic;
223 
224     // Device's IO capabilities obtained from the pairing delegate.
225     pw::bluetooth::emboss::IoCapability local_iocap;
226 
227     // Peer's IO capabilities obtained through HCI_IO_Capability_Response.
228     pw::bluetooth::emboss::IoCapability peer_iocap;
229 
230     // User interaction to perform after receiving HCI user event.
231     bt::gap::PairingAction action;
232 
233     // HCI event to respond to in order to complete or reject pairing.
234     hci_spec::EventCode expected_event;
235 
236     // True if this pairing is expected to be resistant to on-path attacks.
237     bool authenticated;
238 
239     // Security properties of the link key received from the controller.
240     std::optional<sm::SecurityProperties> security_properties;
241 
242     // If the preferred security is greater than the existing link key, a new
243     // link key will be negotiated (which may still have insufficient security
244     // properties).
245     BrEdrSecurityRequirements preferred_security;
246 
247    private:
Pairing(bool automatic)248     explicit Pairing(bool automatic)
249         : allow_automatic(automatic), weak_self_(this) {}
250 
251     WeakSelf<Pairing> weak_self_;
252   };
253 
is_pairing()254   bool is_pairing() const { return current_pairing_ != nullptr; }
255 
handle()256   hci_spec::ConnectionHandle handle() const { return link_->handle(); }
257 
258   // Called to enable encryption on the link for this peer. Sets |state_| to
259   // |kWaitEncryption|.
260   void EnableEncryption();
261 
262   // Call the permanent status callback this object was created with as well as
263   // any completed request callbacks from local initiators. Resets the current
264   // pairing and may initiate a new pairing if any requests have not been
265   // completed. |caller| is used for logging.
266   void SignalStatus(hci::Result<> status, const char* caller);
267 
268   // Starts the pairing procedure for the next queued pairing request, if any.
269   void InitiateNextPairingRequest();
270 
271   // Determines which pairing requests have been completed by the current link
272   // key and/or status and removes them from the queue. If any pairing requests
273   // were not completed, starts a new pairing procedure. Returns a list of
274   // closures that call the status callbacks of completed pairing requests.
275   std::vector<fit::closure> CompletePairingRequests(hci::Result<> status);
276 
277   // Called when an event is received while in a state that doesn't expect that
278   // event. Invokes |status_callback_| with |HostError::kFailed| and sets
279   // |state_| to |kFailed|. Logs an error using |handler_name| for
280   // identification.
281   void FailWithUnexpectedEvent(const char* handler_name);
282 
283   static const char* ToString(State state);
284 
285   PeerId peer_id_;
286   Peer::WeakPtr peer_;
287 
288   // The BR/EDR link whose pairing is being driven by this object.
289   WeakPtr<hci::BrEdrConnection> link_;
290 
291   // True when the BR/EDR |link_| was initiated by local device.
292   bool outgoing_connection_;
293 
294   // Current security properties of the ACL-U link.
295   sm::SecurityProperties bredr_security_;
296 
297   std::unique_ptr<Pairing> current_pairing_;
298 
299   PairingDelegate::WeakPtr pairing_delegate_;
300 
301   // Before the ACL connection is complete, we can temporarily store the link
302   // key in LegacyPairingState. Once the connection is complete, this value will
303   // be stored into the created connection.
304   std::optional<hci_spec::LinkKey> link_key_;
305 
306   // True when the peer has reported it doesn't have a link key.
307   bool peer_missing_key_ = false;
308 
309   // State machine representation to track transition between pairing events.
310   State state_ = State::kIdle;
311 
312   struct PairingRequest {
313     // Security properties required by the pairing initiator for pairing to be
314     // considered a success.
315     BrEdrSecurityRequirements security_requirements;
316 
317     // Callback called when the pairing procedure is complete.
318     StatusCallback status_callback;
319   };
320   // Represents ongoing and queued pairing requests. Will contain a value when
321   // the state isn't |kIdle| or |kFailed|. Requests may be completed
322   // out-of-order as their security requirements are satisfied.
323   std::list<PairingRequest> request_queue_;
324 
325   // Callback used to indicate an HCI_Authentication_Requested for this peer
326   // should be sent.
327   fit::closure send_auth_request_callback_;
328 
329   // Callback that status of this pairing is reported back through.
330   StatusCallback status_callback_;
331 
332   struct InspectProperties {
333     inspect::StringProperty encryption_status;
334   };
335   InspectProperties inspect_properties_;
336   inspect::Node inspect_node_;
337 
338   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LegacyPairingState);
339 };
340 
341 }  // namespace bt::gap
342