1 // Copyright 2023 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 #include <lib/fit/function.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
20 #include "pw_bluetooth_sapphire/internal/host/sm/pairing_phase.h"
21 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
22 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
23 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
24 
25 namespace bt::sm {
26 // Responsible for Phase 1 of SMP pairing, the feature exchange. Takes in the
27 // current SM settings and notifies a constructor-provided callback with the
28 // negotiated features upon completion.
29 //
30 // This class is not thread safe and is meant to be accessed on the thread it
31 // was created on. All callbacks will be run by the default dispatcher of a
32 // Phase1's creation thread.
33 class Phase1 final : public PairingPhase, public PairingChannelHandler {
34  public:
35   // Called when Phase 1 completes successfully. |features| are the negotiated
36   // features. |preq| and |pres| are the SMP "Pairing Request" and "Pairing
37   // Response" payloads exchanged by the devices, which are returned for use in
38   // Phase 2 crypto calculations.
39   using CompleteCallback = fit::function<void(PairingFeatures features,
40                                               PairingRequestParams preq,
41                                               PairingResponseParams pres)>;
42 
43   // Returns a Phase 1 that starts pairing to |requested_level|. Note the lack
44   // of a `preq` parameter: Phase 1 builds & sends the Pairing Request as
45   // initiator.
46   static std::unique_ptr<Phase1> CreatePhase1Initiator(
47       PairingChannel::WeakPtr chan,
48       Listener::WeakPtr listener,
49       IOCapability io_capability,
50       BondableMode bondable_mode,
51       SecurityLevel requested_level,
52       CompleteCallback on_complete);
53 
54   // Returns a Phase 1 in the responder role. Note the `preq` parameter: Phase 1
55   // is supplied the Pairing Request from the remote as responder. See private
56   // ctor for parameter descriptions.
57   static std::unique_ptr<Phase1> CreatePhase1Responder(
58       PairingChannel::WeakPtr chan,
59       Listener::WeakPtr listener,
60       PairingRequestParams preq,
61       IOCapability io_capability,
62       BondableMode bondable_mode,
63       SecurityLevel requested_level,
64       CompleteCallback on_complete);
65 
~Phase1()66   ~Phase1() override { InvalidatePairingChannelHandler(); }
67 
68   // Runs the Phase 1 state machine, performing the feature exchange.
69   void Start() final;
70 
71  private:
72   //   |chan|, |listener|, and |role|: used to construct the base PairingPhase
73   //   |preq|: If empty, the device is in the initiator role and starts the
74   //   pairing.
75   //           If present, the device is in the responder role, and will respond
76   //           to |preq|, the peer initiator's pairing request.
77   //   |bondable_mode|: the bondable mode of the local device (see V5.1 Vol. 3
78   //   Part C Section 9.4). |requested_level|: The minimum security level
79   //   required by the local device for this pairing.
80   //                      Must be at least SecurityLevel::kEncrypted. If
81   //                      authenticated, the ctor ASSERTs that |io_capabilities|
82   //                      can perform authenticated pairing.
83   //   |on_complete|: called at the end of Phase 1 with the resulting features.
84   Phase1(PairingChannel::WeakPtr chan,
85          Listener::WeakPtr listener,
86          Role role,
87          std::optional<PairingRequestParams> preq,
88          IOCapability io_capability,
89          BondableMode bondable_mode,
90          SecurityLevel requested_level,
91          CompleteCallback on_complete);
92 
93   // Start the feature exchange by sending the Pairing Request. The local device
94   // must be in the SMP initiator role (V5.1 Vol 3, Part H, 2.3).
95   void InitiateFeatureExchange();
96 
97   // Handle the peer-initiated feature exchange. The local device must be in the
98   // SMP responder role (V5.1 Vol 3, Part H, 2.3).
99   void RespondToPairingRequest(const PairingRequestParams& req_params);
100 
101   // The returned `LocalPairingParams` contains the locally-preferred pairing
102   // parameters for the LE transport. The caller is responsible for making any
103   // adjustments necessary (e.g. due to peer preferences) before converting the
104   // `LocalPairingParams` to SMP Pairing(Response|Request) PDUs.
105   LocalPairingParams BuildPairingParameters();
106 
107   // Called to complete a feature exchange. Returns the resulting
108   // PairingFeatures if the parameters should be accepted, or an error code if
109   // the parameters are rejected and pairing should abort.
110   fit::result<ErrorCode, PairingFeatures> ResolveFeatures(
111       bool local_initiator,
112       const PairingRequestParams& preq,
113       const PairingResponseParams& pres);
114 
115   // Called for SMP commands sent by the peer.
116   void OnPairingResponse(const PairingResponseParams& response_params);
117 
118   // PairingChannelHandler callbacks:
119   void OnRxBFrame(ByteBufferPtr sdu) final;
OnChannelClosed()120   void OnChannelClosed() final { PairingPhase::HandleChannelClosed(); }
121 
122   // PairingPhase overrides
ToStringInternal()123   std::string ToStringInternal() override {
124     return bt_lib_cpp_string::StringPrintf(
125         "Pairing Phase 1 (feature exchange) - pairing to %s security with "
126         "\"%s\" IOCapabilities",
127         LevelToString(requested_level_),
128         util::IOCapabilityToString(io_capability_).c_str());
129   }
130 
131   // If acting as the Responder to a peer-initiatied pairing, then |preq_| is
132   // the |preq| ctor parameter. Otherwise, these are generated and exchanged
133   // during Phase 1.
134   std::optional<PairingRequestParams> preq_;
135   std::optional<PairingResponseParams> pres_;
136 
137   // Phase 1 ensures that the feature exchange results in a pairing that
138   // supports this level of security. If this is impossible, pairing will abort.
139   SecurityLevel requested_level_;
140 
141   // Local feature flags that determine the feature exchange negotiation with
142   // the peer.
143   bool oob_available_;
144   IOCapability io_capability_;
145   BondableMode bondable_mode_;
146 
147   CompleteCallback on_complete_;
148 
149   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Phase1);
150 };
151 
152 }  // namespace bt::sm
153