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