• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 #include <list>
17 #include <optional>
18 #include <vector>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
22 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
23 #include "pw_bluetooth_sapphire/internal/host/gap/pairing_delegate.h"
24 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h"
25 #include "pw_bluetooth_sapphire/internal/host/gap/types.h"
26 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
27 #include "pw_bluetooth_sapphire/internal/host/hci/bredr_connection.h"
28 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
29 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
30 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
31 
32 namespace bt::gap {
33 
34 // Represents the local user interaction that will occur, as inferred from Core
35 // Spec v5.0 Vol 3, Part C, Sec 5.2.2.6 (Table 5.7). This is not directly
36 // coupled to the reply action for the HCI "User" event for pairing; e.g.
37 // kDisplayPasskey may mean automatically confirming User Confirmation Request
38 // or displaying the value from User Passkey Notification.
39 enum class PairingAction {
40   // Don't involve the user.
41   kAutomatic,
42 
43   // Request yes/no consent.
44   kGetConsent,
45 
46   // Display 6-digit value with "cancel."
47   kDisplayPasskey,
48 
49   // Display 6-digit value with "yes/no."
50   kComparePasskey,
51 
52   // Request a 6-digit value entry.
53   kRequestPasskey,
54 };
55 
56 // Tracks the pairing state of a peer's BR/EDR link. This drives HCI
57 // transactions and user interactions for pairing in order to obtain the highest
58 // possible level of link security given the capabilities of the controllers
59 // and hosts participating in the pairing.
60 //
61 // This implements Core Spec v5.0 Vol 2, Part F, Sec 4.2 through Sec 4.4, per
62 // logic requirements in Vol 3, Part C, Sec 5.2.2.
63 //
64 // This tracks both the bonded case (both hosts furnish their Link Keys to their
65 // controllers) and the unbonded case (both controllers perform Secure Simple
66 // Pairing and deliver the resulting Link Keys to their hosts).
67 //
68 // Pairing is considered complete when the Link Keys have been used to
69 // successfully encrypt the link, at which time pairing may be restarted (e.g.
70 // with different capabilities).
71 //
72 // This state machine navigates the following HCI message sequences, in which
73 // both the host subsystem and the Link Manager use knowledge of both peers' IO
74 // Capabilities and Authentication Requirements to decide on the same
75 // association model.
76 // ▶ means command.
77 // ◀ means event.
78 //
79 // Initiator flow
80 // --------------
81 // Authentication Requested▶
82 // (◀ Authentication Complete with an error is possible at any time after this)
83 //     ◀ Link Key Request
84 // Link Key Request Reply▶ (skip to "Authentication Complete")
85 //     or
86 // Link Key Request Negative Reply▶ (continue with pairing)
87 //     ◀ Command Complete
88 //     ◀ IO Capability Request
89 // (◀ Simple Pairing Complete with an error is possible at any time after this)
90 // IO Capability Request Reply▶
91 //     or
92 // IO Capability Request Negative Reply▶ (reject pairing)
93 //     ◀ Command Complete
94 //     ◀ IO Capability Response
95 //     ◀ User Confirmation Request
96 //         or
97 //     ◀ User Passkey Request
98 //         or
99 //     ◀ User Passkey Notification
100 //         or
101 //     ◀ Remote OOB Data Request
102 // User Confirmation Request Reply▶
103 //     or
104 // User Confirmation Request Negative Reply▶ (reject pairing)
105 //     or
106 // User Passkey Request Reply▶
107 //     or
108 // User Passkey Request Negative Reply▶ (reject pairing)
109 //     or
110 // Remote OOB Data Request Reply▶
111 //     or
112 // Remote OOB Extended Data Request Reply▶
113 //     or
114 // Remote OOB Data Request Negative Reply▶ (reject pairing)
115 //     ◀ Simple Pairing Complete (status may be error)
116 //     ◀ Link Key Notification (key may be insufficient)
117 //     ◀ Authentication Complete (status may be error)
118 //       If status is PIN or Key missing, return to:
119 //         Authentication Requested▶ (use Link Key Request Negative Reply)
120 // Set Connection Encryption▶
121 //     ◀ Command Status
122 //     ◀ Encryption Change (status may be error or encryption may be disabled)
123 //
124 // Responder flow
125 // --------------
126 // If initiator has key:
127 //     ◀ Link Key Request
128 // Link Key Request Reply▶ (skip to "Encryption Change")
129 //     or
130 // Link Key Request Negative Reply▶ (Authentication failed, skip pairing)
131 //
132 // If initiator doesn't have key:
133 //     ◀ IO Capability Response
134 //     ◀ IO Capability Request
135 // (◀ Simple Pairing Complete with an error is possible at any time after this)
136 // IO Capability Request Reply▶
137 //     or
138 // IO Capability Request Negative Reply▶ (reject pairing)
139 //     ◀ Command Complete
140 // Pairing
141 //     ◀ User Confirmation Request
142 //         or
143 //     ◀ User Passkey Request
144 //         or
145 //     ◀ User Passkey Notification
146 //         or
147 //     ◀ Remote OOB Data Request
148 // User Confirmation Request Reply▶
149 //     or
150 // User Confirmation Request Negative Reply▶ (reject pairing)
151 //     or
152 // User Passkey Request Reply▶
153 //     or
154 // User Passkey Request Negative Reply▶ (reject pairing)
155 //     or
156 // Remote OOB Data Request Reply▶
157 //     or
158 // Remote OOB Extended Data Request Reply▶
159 //     or
160 // Remote OOB Data Request Negative Reply▶ (reject pairing)
161 //     ◀ Simple Pairing Complete (status may contain error)
162 //     ◀ Link Key Notification (key may be insufficient)
163 // Set Connection Encryption▶
164 //     ◀ Command Status
165 //     ◀ Encryption Change (status may be error or encryption may be disabled)
166 //
167 // This class is not thread-safe and should only be called on the thread on
168 // which it was created.
169 class PairingState final {
170  public:
171   // Used to report the status of each pairing procedure on this link. |status|
172   // will contain HostError::kNotSupported if the pairing procedure does not
173   // proceed in the order of events expected.
174   using StatusCallback =
175       fit::function<void(hci_spec::ConnectionHandle, hci::Result<>)>;
176 
177   // Constructs a PairingState for the ACL connection |link| to |peer_id|.
178   // |link_initiated| should be true if this device connected, and false if it
179   // was an incoming connection.
180   // This object will receive "encryption change" callbacks associate with
181   // |peer_id|. Successful pairing is reported through |status_cb| after
182   // encryption is enabled. When errors occur, this object will be put in a
183   // "failed" state and the owner shall disconnect the link and destroy its
184   // PairingState.  When destroyed, status callbacks for any waiting pairings
185   // are called. |status_cb| is not called on destruction.
186   //
187   // |auth_cb| will be called to indicate that the caller should send an
188   // Authentication Request for this peer.
189   //
190   // |link| must be valid for the lifetime of this object.
191   PairingState(Peer::WeakPtr peer,
192                hci::BrEdrConnection* link,
193                bool link_initiated,
194                fit::closure auth_cb,
195                StatusCallback status_cb);
196   PairingState(PairingState&&) = default;
197   PairingState& operator=(PairingState&&) = default;
198   ~PairingState();
199 
200   // True if there is currently a pairing procedure in progress that the local
201   // device initiated.
initiator()202   bool initiator() const {
203     return is_pairing() ? current_pairing_->initiator : false;
204   }
205 
206   // Set a handler for user-interactive authentication challenges. If not set or
207   // set to nullptr, all pairing requests will be rejected, but this does not
208   // cause a fatal error and should not result in link disconnection.
209   //
210   // If the delegate indicates passkey display capabilities, then it will always
211   // be asked to confirm pairing, even when Core Spec v5.0, Vol 3, Part C,
212   // Section 5.2.2.6 indicates "automatic confirmation."
SetPairingDelegate(PairingDelegate::WeakPtr pairing_delegate)213   void SetPairingDelegate(PairingDelegate::WeakPtr pairing_delegate) {
214     pairing_delegate_ = std::move(pairing_delegate);
215   }
216 
217   // Starts pairing against the peer, if pairing is not already in progress.
218   // If not, this device becomes the pairing initiator. If pairing is in
219   // progress, the request will be queued until the current pairing completes or
220   // an additional pairing that upgrades the link key succeeds or fails.
221   //
222   // If no PairingDelegate is available, |status_cb| is immediately called with
223   // HostError::kNotReady, but the PairingState status callback (provided in the
224   // ctor) is not called.
225   //
226   // When pairing completes or errors out, the |status_cb| of each call to this
227   // function will be invoked with the result.
228   void InitiatePairing(BrEdrSecurityRequirements security_requirements,
229                        StatusCallback status_cb);
230 
231   // Event handlers. Caller must ensure that the event is addressed to the link
232   // for this PairingState.
233 
234   // Returns value for IO Capability Request Reply, else std::nullopt for IO
235   // Capability Negative Reply.
236   //
237   // TODO(fxbug.dev/42138242): Indicate presence of out-of-band (OOB) data.
238   [[nodiscard]] std::optional<pw::bluetooth::emboss::IoCapability>
239   OnIoCapabilityRequest();
240 
241   // Caller is not expected to send a response.
242   void OnIoCapabilityResponse(pw::bluetooth::emboss::IoCapability peer_iocap);
243 
244   // |cb| is called with: true to send User Confirmation Request Reply, else
245   // for to send User Confirmation Request Negative Reply. It may be called from
246   // a different thread than the one that called OnUserConfirmationRequest.
247   using UserConfirmationCallback = fit::callback<void(bool confirm)>;
248   void OnUserConfirmationRequest(uint32_t numeric_value,
249                                  UserConfirmationCallback cb);
250 
251   // |cb| is called with: passkey value to send User Passkey Request Reply, else
252   // std::nullopt to send User Passkey Request Negative Reply. It may not be
253   // called from the same thread that called OnUserPasskeyRequest.
254   using UserPasskeyCallback =
255       fit::callback<void(std::optional<uint32_t> passkey)>;
256   void OnUserPasskeyRequest(UserPasskeyCallback cb);
257 
258   // Caller is not expected to send a response.
259   void OnUserPasskeyNotification(uint32_t numeric_value);
260 
261   // Caller is not expected to send a response.
262   void OnSimplePairingComplete(pw::bluetooth::emboss::StatusCode status_code);
263 
264   // Caller should send the returned link key in a Link Key Request Reply (or
265   // Link Key Request Negative Reply if the returned value is null).
266   [[nodiscard]] std::optional<hci_spec::LinkKey> OnLinkKeyRequest();
267 
268   // Caller is not expected to send a response.
269   void OnLinkKeyNotification(const UInt128& link_key,
270                              hci_spec::LinkKeyType key_type,
271                              bool local_secure_connections_supported = false);
272 
273   // Caller is not expected to send a response.
274   void OnAuthenticationComplete(pw::bluetooth::emboss::StatusCode status_code);
275 
276   // Handler for hci::Connection::set_encryption_change_callback.
277   void OnEncryptionChange(hci::Result<bool> result);
278 
set_security_properties(sm::SecurityProperties & security)279   void set_security_properties(sm::SecurityProperties& security) {
280     bredr_security_ = security;
281   }
security_properties()282   sm::SecurityProperties& security_properties() { return bredr_security_; }
283 
284   // Sets the BR/EDR Security Mode of the pairing state - see enum definition
285   // for details of each mode. If a security upgrade is in-progress, only takes
286   // effect on the next security upgrade.
set_security_mode(gap::BrEdrSecurityMode mode)287   void set_security_mode(gap::BrEdrSecurityMode mode) { security_mode_ = mode; }
security_mode()288   gap::BrEdrSecurityMode security_mode() const { return security_mode_; }
289 
290   // Attach pairing state inspect node named |name| as a child of |parent|.
291   void AttachInspect(inspect::Node& parent, std::string name);
292 
293  private:
294   // Current security properties of the ACL-U link.
295   sm::SecurityProperties bredr_security_;
296 
297   enum class State {
298     // Wait for initiator's IO Capability Response, Link Key Request, or for
299     // locally-initiated
300     // pairing.
301     kIdle,
302 
303     // As initiator, wait for Link Key Request.
304     kInitiatorWaitLinkKeyRequest,
305 
306     // As initiator, wait for IO Capability Request.
307     kInitiatorWaitIoCapRequest,
308 
309     // As initiator, wait for IO Capability Response.
310     kInitiatorWaitIoCapResponse,
311 
312     // As responder, wait for IO Capability Request.
313     kResponderWaitIoCapRequest,
314 
315     // Wait for controller event for pairing action. Only one of these will
316     // occur in a given pairing
317     // (see class documentation for pairing flow).
318     kWaitUserConfirmationRequest,
319     kWaitUserPasskeyRequest,
320     kWaitUserPasskeyNotification,
321 
322     // Wait for Simple Pairing Complete.
323     kWaitPairingComplete,
324 
325     // Wait for Link Key Notification.
326     kWaitLinkKey,
327 
328     // As initiator, wait for Authentication Complete.
329     kInitiatorWaitAuthComplete,
330 
331     // Wait for Encryption Change.
332     kWaitEncryption,
333 
334     // Error occurred; wait for link closure and ignore events.
335     kFailed,
336   };
337 
338   // Extra information for pairing constructed when a pairing procedure begins
339   // and destroyed when the pairing procedure is reset or errors out.
340   //
341   // Instances must be heap allocated so that they can be moved without
342   // destruction, preserving their WeakPtr holders. WeakPtrs are vended to
343   // PairingDelegate callbacks to uniquely identify each attempt to pair because
344   // |current_pairing_| is not synchronized to the user's actions through
345   // PairingDelegate.
346   class Pairing final {
347    public:
348     static std::unique_ptr<Pairing> MakeInitiator(
349         BrEdrSecurityRequirements security_requirements, bool link_initiated);
350     static std::unique_ptr<Pairing> MakeResponder(
351         pw::bluetooth::emboss::IoCapability peer_iocap, bool link_inititated);
352     // Make a responder for a peer that has initiated a pairing (asked for our
353     // key while in idle)
354     static std::unique_ptr<Pairing> MakeResponderForBonded();
355 
356     // For a Pairing whose |initiator|, |local_iocap|, and |peer_iocap| are
357     // already set, compute and set |action|, |expected_event|, |authenticated|,
358     // and |security_properties| for the pairing procedure and bonding data that
359     // we expect.
360     void ComputePairingData();
361 
362     // Used to prevent PairingDelegate callbacks from using captured stale
363     // pointers.
364     using WeakPtr = WeakSelf<Pairing>::WeakPtr;
GetWeakPtr()365     Pairing::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
366 
367     // True if the local device initiated pairing.
368     bool initiator;
369 
370     // True if we allow automatic pairing. (when outgoing connection and not
371     // re-pairing)
372     bool allow_automatic;
373 
374     // IO Capability obtained from the pairing delegate.
375     pw::bluetooth::emboss::IoCapability local_iocap;
376 
377     // IO Capability from peer through IO Capability Response.
378     pw::bluetooth::emboss::IoCapability peer_iocap;
379 
380     // User interaction to perform after receiving HCI user event.
381     PairingAction action;
382 
383     // HCI event to respond to in order to complete or reject pairing.
384     hci_spec::EventCode expected_event;
385 
386     // inclusive-language: ignore
387     // True if this pairing is expected to be resistant to MITM attacks.
388     bool authenticated;
389 
390     // Security properties of the link key received from the controller.
391     std::optional<sm::SecurityProperties> security_properties;
392 
393     // If the preferred security is greater than the existing link key, a new
394     // link key will be negotiated (which may still have insufficient security
395     // properties).
396     BrEdrSecurityRequirements preferred_security;
397 
398    private:
Pairing(bool automatic)399     explicit Pairing(bool automatic)
400         : allow_automatic(automatic), weak_self_(this) {}
401 
402     WeakSelf<Pairing> weak_self_;
403   };
404 
405   static const char* ToString(State state);
406 
407   // Returns state for the three pairing action events, kFailed otherwise.
408   static State GetStateForPairingEvent(hci_spec::EventCode event_code);
409 
410   // Peer for this pairing.
peer_id()411   PeerId peer_id() const { return peer_id_; }
412 
state()413   State state() const { return state_; }
414 
is_pairing()415   bool is_pairing() const { return current_pairing_ != nullptr; }
416 
handle()417   hci_spec::ConnectionHandle handle() const { return link_->handle(); }
418 
419   // Returns nullptr if the delegate is not set or no longer alive.
pairing_delegate()420   const PairingDelegate::WeakPtr& pairing_delegate() const {
421     return pairing_delegate_;
422   }
423 
424   // Call the permanent status callback this object was created with as well as
425   // any completed request callbacks from local initiators. Resets the current
426   // pairing and may initiate a new pairing if any requests have not been
427   // completed. |caller| is used for logging.
428   void SignalStatus(hci::Result<> status, const char* caller);
429 
430   // Determines which pairing requests have been completed by the current link
431   // key and/or status and removes them from the queue. If any pairing requests
432   // were not completed, starts a new pairing procedure. Returns a list of
433   // closures that call the status callbacks of completed pairing requests.
434   std::vector<fit::closure> CompletePairingRequests(hci::Result<> status);
435 
436   // Starts the pairing procedure for the next queued pairing request, if any.
437   void InitiateNextPairingRequest();
438 
439   // Called to enable encryption on the link for this peer. Sets |state_| to
440   // kWaitEncryption.
441   void EnableEncryption();
442 
443   // Called when an event is received while in a state that doesn't expect that
444   // event. Invokes |status_callback_| with HostError::kNotSupported and sets
445   // |state_| to kFailed. Logs an error using |handler_name| for identification.
446   void FailWithUnexpectedEvent(const char* handler_name);
447 
448   // Compute the expected pairing event and state to occur after receiving the
449   // peer IO Capability and write it to |current_pairing_| (which must exist).
450   void WritePairingData();
451 
452   // Returns true when the peer's host and peer's controller support Secure
453   // Connections
454   bool IsPeerSecureConnectionsSupported() const;
455 
456   PeerId peer_id_;
457   Peer::WeakPtr peer_;
458 
459   // The current GAP security mode of the device (v5.2 Vol. 3 Part C
460   // Section 5.2.2)
461   gap::BrEdrSecurityMode security_mode_;
462 
463   // The BR/EDR link whose pairing is being driven by this object.
464   hci::BrEdrConnection* link_;
465 
466   // True when the BR/EDR |link_| was locally requested.
467   bool outgoing_connection_;
468 
469   // True when the remote device has reported it doesn't have a link key.
470   bool peer_missing_key_;
471 
472   PairingDelegate::WeakPtr pairing_delegate_;
473 
474   // State machine representation.
475   State state_;
476 
477   std::unique_ptr<Pairing> current_pairing_;
478 
479   struct PairingRequest {
480     // Security properties required by the pairing initiator for pairing to be
481     // considered a success.
482     BrEdrSecurityRequirements security_requirements;
483 
484     // Callback called when the pairing procedure is complete.
485     StatusCallback status_callback;
486   };
487   // Represents ongoing and queued pairing requests. Will contain a value when
488   // the state isn't kIdle or kFailed. Requests may be completed out-of-order as
489   // their security requirements are satisfied.
490   std::list<PairingRequest> request_queue_;
491 
492   // Callback used to indicate an Authentication Request for this peer should be
493   // sent.
494   fit::closure send_auth_request_callback_;
495 
496   // Callback that status of this pairing is reported back through.
497   StatusCallback status_callback_;
498 
499   // Cleanup work that should occur only once per connection; uniqueness is
500   // guaranteed by being moved with PairingState. |self| shall be a pointer to
501   // the moved-to instance being cleaned up.
502   fit::callback<void(PairingState* self)> cleanup_cb_;
503 
504   struct InspectProperties {
505     inspect::StringProperty encryption_status;
506   };
507   InspectProperties inspect_properties_;
508   inspect::Node inspect_node_;
509 
510   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingState);
511 };
512 
513 PairingAction GetInitiatorPairingAction(
514     pw::bluetooth::emboss::IoCapability initiator_cap,
515     pw::bluetooth::emboss::IoCapability responder_cap);
516 PairingAction GetResponderPairingAction(
517     pw::bluetooth::emboss::IoCapability initiator_cap,
518     pw::bluetooth::emboss::IoCapability responder_cap);
519 hci_spec::EventCode GetExpectedEvent(
520     pw::bluetooth::emboss::IoCapability local_cap,
521     pw::bluetooth::emboss::IoCapability peer_cap);
522 bool IsPairingAuthenticated(pw::bluetooth::emboss::IoCapability local_cap,
523                             pw::bluetooth::emboss::IoCapability peer_cap);
524 
525 // Get the Authentication Requirements for a locally-initiated pairing according
526 // to Core Spec v5.0, Vol 2, Part E, Sec 7.1.29.
527 //
528 // Non-Bondable Mode and Dedicated Bonding over BR/EDR are not supported and
529 // this always returns kMITMGeneralBonding if |local_cap| is not
530 // kNoInputNoOutput, kGeneralBonding otherwise. This requests authentication
531 // when possible (based on IO Capabilities), as we don't know the peer's
532 // authentication requirements yet.
533 pw::bluetooth::emboss::AuthenticationRequirements
534 GetInitiatorAuthenticationRequirements(
535     pw::bluetooth::emboss::IoCapability local_cap);
536 
537 // Get the Authentication Requirements for a peer-initiated pairing. This will
538 // inclusive-language: ignore
539 // request MITM protection whenever possible to obtain an "authenticated" link
540 // encryption key.
541 //
542 // Local service requirements and peer authentication bonding type should be
543 // available by the time this is called, but Non-Bondable Mode and Dedicated
544 // Bonding over BR/EDR are not supported, so this always returns
545 // kMITMGeneralBonding if this pairing can result in an authenticated link key,
546 // kGeneralBonding otherwise.
547 pw::bluetooth::emboss::AuthenticationRequirements
548 GetResponderAuthenticationRequirements(
549     pw::bluetooth::emboss::IoCapability local_cap,
550     pw::bluetooth::emboss::IoCapability remote_cap);
551 
552 }  // namespace bt::gap
553