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