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