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 and link key when the ACL connection is 88 // complete (i.e. after HCI_Connection_Complete event). |auth_cb| and 89 // |status_cb| are passed in from either the LegacyPairingState or 90 // PairingStateManager constructor, depending on which object is first created 91 // due to the ordering of legacy pairing events (which may occur before or 92 // after the ACL 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 link_ltk()160 std::optional<hci_spec::LinkKey> link_ltk() const { return link_->ltk(); } 161 162 private: 163 enum class State : uint8_t { 164 // Either waiting to locally initiate pairing, or for the pairing 165 // initiator's HCI_Link_Key_Request or HCI_PIN_Code_Request_Reply event 166 // (depending if the pairing initiator has a valid link key or not 167 // respectively). 168 kIdle, 169 170 // Wait for HCI_Link_Key_Request event (only when pairing initiator) 171 kInitiatorWaitLinkKeyRequest, 172 173 // Wait for HCI_PIN_Code_Request event 174 kWaitPinCodeRequest, 175 176 // Wait for HCI_Link_Key_Notification 177 kWaitLinkKey, 178 179 // Wait for HCI_Authentication_Complete event (nly when pairing initiator) 180 kInitiatorWaitAuthComplete, 181 182 // Wait for HCI_Encryption_Change event 183 kWaitEncryption, 184 185 // Wait for link closure and ignore events due to an error 186 kFailed, 187 }; 188 189 // Extra information for pairing constructed when a pairing procedure begins 190 // and destroyed when the pairing procedure is reset or errors out. 191 // 192 // Instances must be heap allocated so that they can be moved without 193 // destruction, preserving their WeakPtr holders. WeakPtrs are vended to 194 // PairingDelegate callbacks to uniquely identify each attempt to pair because 195 // |current_pairing_| is not synchronized to the user's actions through 196 // PairingDelegate. 197 class Pairing final { 198 public: 199 static std::unique_ptr<Pairing> MakeInitiator( 200 BrEdrSecurityRequirements security_requirements, 201 bool outgoing_connection, 202 Peer::PairingToken&& token); 203 static std::unique_ptr<Pairing> MakeResponder( 204 bool outgoing_connection, 205 Peer::PairingToken&& token, 206 std::optional<pw::bluetooth::emboss::IoCapability> peer_iocap = 207 std::nullopt); 208 // Make a responder for a peer that has initiated pairing. 209 static std::unique_ptr<Pairing> MakeResponderForBonded( 210 Peer::PairingToken&& token); 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 Peer::PairingToken pairing_token; 248 249 private: Pairing(bool automatic,Peer::PairingToken && token)250 explicit Pairing(bool automatic, Peer::PairingToken&& token) 251 : allow_automatic(automatic), 252 pairing_token(std::move(token)), 253 weak_self_(this) {} 254 255 WeakSelf<Pairing> weak_self_; 256 }; 257 is_pairing()258 bool is_pairing() const { return current_pairing_ != nullptr; } 259 handle()260 hci_spec::ConnectionHandle handle() const { return link_->handle(); } 261 262 // Called to enable encryption on the link for this peer. Sets |state_| to 263 // |kWaitEncryption|. 264 void EnableEncryption(); 265 266 // Call the permanent status callback this object was created with as well as 267 // any completed request callbacks from local initiators. Resets the current 268 // pairing and may initiate a new pairing if any requests have not been 269 // completed. |caller| is used for logging. 270 void SignalStatus(hci::Result<> status, const char* caller); 271 272 // Starts the pairing procedure for the next queued pairing request, if any. 273 void InitiateNextPairingRequest(); 274 275 // Determines which pairing requests have been completed by the current link 276 // key and/or status and removes them from the queue. If any pairing requests 277 // were not completed, starts a new pairing procedure. Returns a list of 278 // closures that call the status callbacks of completed pairing requests. 279 std::vector<fit::closure> CompletePairingRequests(hci::Result<> status); 280 281 // Called when an event is received while in a state that doesn't expect that 282 // event. Invokes |status_callback_| with |HostError::kFailed| and sets 283 // |state_| to |kFailed|. Logs an error using |handler_name| for 284 // identification. 285 void FailWithUnexpectedEvent(const char* handler_name); 286 287 static const char* ToString(State state); 288 289 PeerId peer_id_; 290 Peer::WeakPtr peer_; 291 292 // The BR/EDR link whose pairing is being driven by this object. 293 WeakPtr<hci::BrEdrConnection> link_; 294 295 // True when the BR/EDR |link_| was initiated by local device. 296 bool outgoing_connection_; 297 298 // Current security properties of the ACL-U link. 299 sm::SecurityProperties bredr_security_; 300 301 std::unique_ptr<Pairing> current_pairing_; 302 303 PairingDelegate::WeakPtr pairing_delegate_; 304 305 // Before the ACL connection is complete, we can temporarily store the link 306 // key in LegacyPairingState. Once the connection is complete, this value will 307 // be stored into the created connection. 308 std::optional<hci_spec::LinkKey> link_key_; 309 310 // True when the peer has reported it doesn't have a link key. 311 bool peer_missing_key_ = false; 312 313 // State machine representation to track transition between pairing events. 314 State state_ = State::kIdle; 315 316 struct PairingRequest { 317 // Security properties required by the pairing initiator for pairing to be 318 // considered a success. 319 BrEdrSecurityRequirements security_requirements; 320 321 // Callback called when the pairing procedure is complete. 322 StatusCallback status_callback; 323 }; 324 // Represents ongoing and queued pairing requests. Will contain a value when 325 // the state isn't |kIdle| or |kFailed|. Requests may be completed 326 // out-of-order as their security requirements are satisfied. 327 std::list<PairingRequest> request_queue_; 328 329 // Callback used to indicate an HCI_Authentication_Requested for this peer 330 // should be sent. 331 fit::closure send_auth_request_callback_; 332 333 // Callback that status of this pairing is reported back through. 334 StatusCallback status_callback_; 335 336 struct InspectProperties { 337 inspect::StringProperty encryption_status; 338 }; 339 InspectProperties inspect_properties_; 340 inspect::Node inspect_node_; 341 342 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LegacyPairingState); 343 }; 344 345 } // namespace bt::gap 346