• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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