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 <memory> 18 #include <optional> 19 20 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 21 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h" 22 #include "pw_bluetooth_sapphire/internal/host/gap/legacy_pairing_state.h" 23 #include "pw_bluetooth_sapphire/internal/host/gap/pairing_delegate.h" 24 #include "pw_bluetooth_sapphire/internal/host/gap/secure_simple_pairing_state.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/types.h" 29 #include "pw_bluetooth_sapphire/internal/host/transport/error.h" 30 31 namespace bt::gap { 32 33 // In order to support BR/EDR Legacy Pairing, each BrEdrConnection must manage 34 // either a LegacyPairingState or SecureSimplePairingState object since the two 35 // pairing processes differ. The PairingStateManager class's purpose is to 36 // abstract this management logic out of BrEdrConnection and 37 // BrEdrConnectionManager. 38 // 39 // PairingStateManager exists for each BrEdrConnection and routes the events 40 // received by the connection to either SecureSimplePairingState or 41 // LegacyPairingState. 42 // 43 // Sometimes we can receive pairing events before the L2CAP connection is 44 // complete (i.e. before interrogation can occur/is complete). In this case, we 45 // don’t know if the peer supports SSP, so we don’t know whether to use SSP or 46 // LP. PairingStateManager defaults to instantiating LegacyPairingState since LP 47 // can happen before L2CAP channel is open and handles the logic to switch to 48 // SSP if needed (i.e. on an HCI_IO_Capability* event). 49 // 50 // Only one of the two pairing states will ever be instantiated at a time. 51 class PairingStateManager final { 52 public: 53 // Used to report the status of each pairing procedure on this link. |status| 54 // will contain HostError::kNotSupported if the pairing procedure does not 55 // proceed in the order of events expected. 56 using StatusCallback = 57 fit::function<void(hci_spec::ConnectionHandle, hci::Result<>)>; 58 59 // Constructs a PairingStateManager for the ACL connection |link| to 60 // |peer_id|. |outgoing_connection| should be true if this device connected, 61 // and false if it was an incoming connection. This object will receive 62 // "encryption change" callbacks associate with |peer_id|. Successful pairing 63 // is reported through |status_cb| after encryption is enabled. When errors 64 // occur, this object will be put in a "failed" state and the owner shall 65 // disconnect the link and destroy its PairingStateManager. When destroyed, 66 // status callbacks for any waiting pairings are called. |status_cb| is not 67 // called on destruction. 68 // 69 // |auth_cb| will be called to indicate that the caller should send an 70 // Authentication Request for this peer. 71 // 72 // |link| must be valid for the lifetime of this object. 73 // 74 // If |legacy_pairing_state| is non-null, this means we were responding to 75 // a Legacy Pairing request before the ACL connection between the two devices 76 // was complete. |legacy_pairing_state| is transferred to the 77 // PairingStateManager. 78 PairingStateManager(Peer::WeakPtr peer, 79 WeakPtr<hci::BrEdrConnection> link, 80 std::unique_ptr<LegacyPairingState> legacy_pairing_state, 81 bool outgoing_connection, 82 fit::closure auth_cb, 83 StatusCallback status_cb, 84 hci::LocalAddressDelegate* low_energy_address_delegate, 85 bool controller_remote_public_key_validation_supported, 86 sm::BrEdrSecurityManagerFactory security_manager_factory, 87 pw::async::Dispatcher& dispatcher); 88 PairingStateManager(PairingStateManager&&) = default; 89 PairingStateManager& operator=(PairingStateManager&&) = default; 90 91 // Set a handler for user-interactive authentication challenges. If not set or 92 // set to nullptr, all pairing requests will be rejected, but this does not 93 // cause a fatal error and should not result in link disconnection. 94 // 95 // If the delegate indicates passkey display capabilities, then it will always 96 // be asked to confirm pairing, even when Core Spec v5.0, Vol 3, Part C, 97 // Section 5.2.2.6 indicates "automatic confirmation." SetPairingDelegate(const PairingDelegate::WeakPtr & pairing_delegate)98 void SetPairingDelegate(const PairingDelegate::WeakPtr& pairing_delegate) { 99 if (pairing_state_type_ == PairingStateType::kSecureSimplePairing) { 100 secure_simple_pairing_state_->SetPairingDelegate(pairing_delegate); 101 } else if (pairing_state_type_ == PairingStateType::kLegacyPairing) { 102 legacy_pairing_state_->SetPairingDelegate(pairing_delegate); 103 } 104 } 105 106 // Starts pairing against the peer, if pairing is not already in progress. 107 // If not, this device becomes the pairing initiator. If pairing is in 108 // progress, the request will be queued until the current pairing completes or 109 // an additional pairing that upgrades the link key succeeds or fails. 110 // 111 // If no PairingDelegate is available, |status_cb| is immediately called with 112 // HostError::kNotReady, but the PairingStateManager status callback (provided 113 // in the ctor) is not called. 114 // 115 // When pairing completes or errors out, the |status_cb| of each call to this 116 // function will be invoked with the result. 117 void InitiatePairing(BrEdrSecurityRequirements security_requirements, 118 StatusCallback status_cb); 119 120 // Event handlers. Caller must ensure that the event is addressed to the link 121 // for this SecureSimplePairingState. 122 123 // Returns value for IO Capability Request Reply, else std::nullopt for IO 124 // Capability Negative Reply. 125 // 126 // TODO(fxbug.dev/42138242): Indicate presence of out-of-band (OOB) data. 127 [[nodiscard]] std::optional<pw::bluetooth::emboss::IoCapability> 128 OnIoCapabilityRequest(); 129 130 // Caller is not expected to send a response. 131 void OnIoCapabilityResponse(pw::bluetooth::emboss::IoCapability peer_iocap); 132 133 // |cb| is called with: true to send User Confirmation Request Reply, else 134 // for to send User Confirmation Request Negative Reply. It may be called from 135 // a different thread than the one that called OnUserConfirmationRequest. 136 using UserConfirmationCallback = fit::callback<void(bool confirm)>; 137 void OnUserConfirmationRequest(uint32_t numeric_value, 138 UserConfirmationCallback cb); 139 140 // |cb| is called with: passkey value to send User Passkey Request Reply, else 141 // std::nullopt to send User Passkey Request Negative Reply. It may not be 142 // called from the same thread that called OnUserPasskeyRequest. 143 using UserPasskeyCallback = 144 fit::callback<void(std::optional<uint32_t> passkey)>; 145 void OnUserPasskeyRequest(UserPasskeyCallback cb); 146 147 // Caller is not expected to send a response. 148 void OnUserPasskeyNotification(uint32_t numeric_value); 149 150 // Caller is not expected to send a response. 151 void OnSimplePairingComplete(pw::bluetooth::emboss::StatusCode status_code); 152 153 // Caller should send the returned link key in a HCI_Link_Key_Request_Reply 154 // (or HCI_Link_Key_Request_Negative_Reply if the returned value is null). 155 [[nodiscard]] std::optional<hci_spec::LinkKey> OnLinkKeyRequest(); 156 157 // |cb| is called with the pin code value to send HCI_PIN_Code_Request_Reply 158 // or std::nullopt to send HCI_PIN_Code_Request_Negative_Reply. 159 using UserPinCodeCallback = 160 fit::callback<void(std::optional<uint16_t> passkey)>; 161 void OnPinCodeRequest(UserPinCodeCallback cb); 162 163 // Caller is not expected to send a response. 164 void OnLinkKeyNotification(const UInt128& link_key, 165 hci_spec::LinkKeyType key_type, 166 bool local_secure_connections_supported = false); 167 168 // Caller is not expected to send a response. 169 void OnAuthenticationComplete(pw::bluetooth::emboss::StatusCode status_code); 170 171 // Handler for hci::Connection::set_encryption_change_callback. 172 void OnEncryptionChange(hci::Result<bool> result); 173 174 // Create a SecureSimplePairingState or LegacyPairingState object based on 175 // |type|. If the object for corresponding |type| has already been created, 176 // this method does nothing. 177 void CreateOrUpdatePairingState(PairingStateType type, 178 PairingDelegate::WeakPtr pairing_delegate); 179 LogSspEventInLegacyPairing(const char * function)180 void LogSspEventInLegacyPairing(const char* function) { 181 bt_log(WARN, 182 "gap", 183 "Received an SSP event for a %u pairing type in %s", 184 static_cast<uint8_t>(pairing_state_type_), 185 function); 186 } 187 security_properties()188 sm::SecurityProperties& security_properties() { 189 if (pairing_state_type_ == PairingStateType::kSecureSimplePairing) { 190 return secure_simple_pairing_state_->security_properties(); 191 } 192 return legacy_pairing_state_->security_properties(); 193 } 194 195 // Sets the BR/EDR Security Mode of the pairing state - see enum definition 196 // for details of each mode. If a security upgrade is in-progress, only takes 197 // effect on the next security upgrade. set_security_mode(gap::BrEdrSecurityMode mode)198 void set_security_mode(gap::BrEdrSecurityMode mode) { 199 if (pairing_state_type_ == PairingStateType::kSecureSimplePairing) { 200 secure_simple_pairing_state_->set_security_mode(mode); 201 } 202 } 203 secure_simple_pairing_state()204 SecureSimplePairingState* secure_simple_pairing_state() { 205 return secure_simple_pairing_state_.get(); 206 } 207 legacy_pairing_state()208 LegacyPairingState* legacy_pairing_state() { 209 return legacy_pairing_state_.get(); 210 } 211 212 // Attach pairing state inspect node named |name| as a child of |parent|. 213 void AttachInspect(inspect::Node& parent, std::string name); 214 215 private: 216 PairingStateType pairing_state_type_ = PairingStateType::kUnknown; 217 std::unique_ptr<SecureSimplePairingState> secure_simple_pairing_state_; 218 std::unique_ptr<LegacyPairingState> legacy_pairing_state_; 219 220 Peer::WeakPtr peer_; 221 222 // The BR/EDR link whose pairing is being driven by this object. 223 WeakPtr<hci::BrEdrConnection> link_; 224 225 // True when the BR/EDR |link_| was initiated by local device. 226 bool outgoing_connection_; 227 228 // Stores the auth_cb and status_cb values passed in via the 229 // PairingStateManager constructor when the ACL connection is complete because 230 // before interrogation is complete, we do not know which type of pairing 231 // state to create. These are later used by CreateOrUpdatePairingState to 232 // create/update the appropriate pairing state once the type determined either 233 // via interrogation or encountering a pairing event specific to SSP or LP. 234 fit::closure auth_cb_; 235 StatusCallback status_cb_; 236 237 pw::async::Dispatcher* dispatcher_; 238 239 hci::LocalAddressDelegate* low_energy_address_delegate_; 240 241 bool controller_remote_public_key_validation_supported_; 242 243 sm::BrEdrSecurityManagerFactory security_manager_factory_; 244 245 struct InspectProperties { 246 inspect::StringProperty pairing_state_type; 247 }; 248 InspectProperties inspect_properties_; 249 inspect::Node inspect_node_; 250 251 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingStateManager); 252 }; 253 254 } // namespace bt::gap 255