xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/sm/phase_1.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 // inclusive-language: disable
16 
17 #include "pw_bluetooth_sapphire/internal/host/sm/phase_1.h"
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
22 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
25 #include "pw_bluetooth_sapphire/internal/host/l2cap/scoped_channel.h"
26 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
27 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
28 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
29 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
30 
31 namespace bt::sm {
32 
CreatePhase1Initiator(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,IOCapability io_capability,BondableMode bondable_mode,SecurityLevel requested_level,CompleteCallback on_complete)33 std::unique_ptr<Phase1> Phase1::CreatePhase1Initiator(
34     PairingChannel::WeakPtr chan,
35     Listener::WeakPtr listener,
36     IOCapability io_capability,
37     BondableMode bondable_mode,
38     SecurityLevel requested_level,
39     CompleteCallback on_complete) {
40   // Use `new` & unique_ptr constructor here instead of `std::make_unique`
41   // because the private Phase1 constructor prevents std::make_unique from
42   // working (https://abseil.io/tips/134).
43   return std::unique_ptr<Phase1>(new Phase1(std::move(chan),
44                                             std::move(listener),
45                                             Role::kInitiator,
46                                             std::nullopt,
47                                             io_capability,
48                                             bondable_mode,
49                                             requested_level,
50                                             std::move(on_complete)));
51 }
52 
CreatePhase1Responder(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,PairingRequestParams preq,IOCapability io_capability,BondableMode bondable_mode,SecurityLevel minimum_allowed_level,CompleteCallback on_complete)53 std::unique_ptr<Phase1> Phase1::CreatePhase1Responder(
54     PairingChannel::WeakPtr chan,
55     Listener::WeakPtr listener,
56     PairingRequestParams preq,
57     IOCapability io_capability,
58     BondableMode bondable_mode,
59     SecurityLevel minimum_allowed_level,
60     CompleteCallback on_complete) {
61   // Use `new` & unique_ptr constructor here instead of `std::make_unique`
62   // because the private Phase1 constructor prevents std::make_unique from
63   // working (https://abseil.io/tips/134).
64   return std::unique_ptr<Phase1>(new Phase1(std::move(chan),
65                                             std::move(listener),
66                                             Role::kResponder,
67                                             preq,
68                                             io_capability,
69                                             bondable_mode,
70                                             minimum_allowed_level,
71                                             std::move(on_complete)));
72 }
73 
Phase1(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,Role role,std::optional<PairingRequestParams> preq,IOCapability io_capability,BondableMode bondable_mode,SecurityLevel requested_level,CompleteCallback on_complete)74 Phase1::Phase1(PairingChannel::WeakPtr chan,
75                Listener::WeakPtr listener,
76                Role role,
77                std::optional<PairingRequestParams> preq,
78                IOCapability io_capability,
79                BondableMode bondable_mode,
80                SecurityLevel requested_level,
81                CompleteCallback on_complete)
82     : PairingPhase(std::move(chan), std::move(listener), role),
83       preq_(preq),
84       requested_level_(requested_level),
85       oob_available_(false),
86       io_capability_(io_capability),
87       bondable_mode_(bondable_mode),
88       on_complete_(std::move(on_complete)) {
89   PW_CHECK(!(role == Role::kInitiator && preq_.has_value()));
90   PW_CHECK(!(role == Role::kResponder && !preq_.has_value()));
91   PW_CHECK(requested_level_ >= SecurityLevel::kEncrypted);
92   if (requested_level_ > SecurityLevel::kEncrypted) {
93     PW_CHECK(io_capability != IOCapability::kNoInputNoOutput);
94   }
95   SetPairingChannelHandler(*this);
96 }
97 
Start()98 void Phase1::Start() {
99   PW_CHECK(!has_failed());
100   if (role() == Role::kResponder) {
101     PW_CHECK(preq_.has_value());
102     RespondToPairingRequest(*preq_);
103     return;
104   }
105   PW_CHECK(!preq_.has_value());
106   InitiateFeatureExchange();
107 }
108 
InitiateFeatureExchange()109 void Phase1::InitiateFeatureExchange() {
110   // Only the initiator can initiate the feature exchange.
111   PW_CHECK(role() == Role::kInitiator);
112   LocalPairingParams preq_values = BuildPairingParameters();
113   preq_ = PairingRequestParams{
114       .io_capability = preq_values.io_capability,
115       .oob_data_flag = preq_values.oob_data_flag,
116       .auth_req = preq_values.auth_req,
117       .max_encryption_key_size = preq_values.max_encryption_key_size,
118       .initiator_key_dist_gen = preq_values.local_keys,
119       .responder_key_dist_gen = preq_values.remote_keys};
120   sm_chan().SendMessage(kPairingRequest, *preq_);
121 }
122 
RespondToPairingRequest(const PairingRequestParams & req_params)123 void Phase1::RespondToPairingRequest(const PairingRequestParams& req_params) {
124   // We should only be in this state when pairing is initiated by the remote
125   // i.e. we are the responder.
126   PW_CHECK(role() == Role::kResponder);
127 
128   LocalPairingParams pres_values = BuildPairingParameters();
129   pres_ = PairingResponseParams{
130       .io_capability = pres_values.io_capability,
131       .oob_data_flag = pres_values.oob_data_flag,
132       .auth_req = pres_values.auth_req,
133       .max_encryption_key_size = pres_values.max_encryption_key_size,
134       .initiator_key_dist_gen = 0,
135       .responder_key_dist_gen = 0};
136   // The keys that will be exchanged correspond to the intersection of what the
137   // initiator requests and we support.
138   pres_->initiator_key_dist_gen =
139       pres_values.remote_keys & req_params.initiator_key_dist_gen;
140   pres_->responder_key_dist_gen =
141       pres_values.local_keys & req_params.responder_key_dist_gen;
142 
143   fit::result<ErrorCode, PairingFeatures> maybe_features =
144       ResolveFeatures(/*local_initiator=*/false, req_params, *pres_);
145   if (maybe_features.is_error()) {
146     bt_log(DEBUG, "sm", "rejecting pairing features");
147     Abort(maybe_features.error_value());
148     return;
149   }
150   PairingFeatures features = maybe_features.value();
151   // If we've accepted a non-bondable pairing request in bondable mode as
152   // indicated by setting features.will_bond false, we should reflect that in
153   // the rsp_params we send to the peer.
154   if (!features.will_bond && bondable_mode_ == BondableMode::Bondable) {
155     pres_->auth_req &= ~AuthReq::kBondingFlag;
156   }
157 
158   sm_chan().SendMessage(kPairingResponse, *pres_);
159 
160   on_complete_(features, *preq_, *pres_);
161 }
162 
BuildPairingParameters()163 LocalPairingParams Phase1::BuildPairingParameters() {
164   // We build `local_params` to reflect the capabilities of this device over the
165   // LE transport.
166   LocalPairingParams local_params;
167   if (sm_chan().SupportsSecureConnections()) {
168     local_params.auth_req |= AuthReq::kSC;
169   }
170   if (requested_level_ >= SecurityLevel::kAuthenticated) {
171     local_params.auth_req |= AuthReq::kMITM;
172   }
173 
174   // If we are in non-bondable mode there will be no key distribution per V5.1
175   // Vol 3 Part C Section 9.4.2.2, so we use the default "no keys" value for
176   // LocalPairingParams.
177   if (bondable_mode_ == BondableMode::Bondable) {
178     local_params.auth_req |= AuthReq::kBondingFlag;
179     // We always request identity information from the remote.
180     local_params.remote_keys = KeyDistGen::kIdKey;
181 
182     PW_CHECK(listener().is_alive());
183     if (listener()->OnIdentityRequest().has_value()) {
184       local_params.local_keys |= KeyDistGen::kIdKey;
185     }
186 
187     // For the current connection, the responder-generated encryption keys (LTK)
188     // is always used. As device roles may change in future connections, Fuchsia
189     // supports distribution and generation of LTKs by both the local and remote
190     // device (V5.0 Vol. 3 Part H 2.4.2.3).
191     local_params.remote_keys |= KeyDistGen::kEncKey;
192     local_params.local_keys |= KeyDistGen::kEncKey;
193 
194     // If we support SC over LE, we always try to generate the cross-transport
195     // BR/EDR key by setting the link key bit (V5.0 Vol. 3 Part H 3.6.1).
196     if (local_params.auth_req & AuthReq::kSC) {
197       local_params.local_keys |= KeyDistGen::kLinkKey;
198       local_params.remote_keys |= KeyDistGen::kLinkKey;
199     }
200   }
201   // The CT2 bit indicates support for the 2nd Cross-Transport Key Derivation
202   // hashing function, a.k.a. H7 (v5.2 Vol. 3 Part H 3.5.1 and 2.4.2.4).
203   local_params.auth_req |= AuthReq::kCT2;
204   local_params.io_capability = io_capability_;
205   local_params.oob_data_flag =
206       oob_available_ ? OOBDataFlag::kPresent : OOBDataFlag::kNotPresent;
207   return local_params;
208 }
209 
ResolveFeatures(bool local_initiator,const PairingRequestParams & preq,const PairingResponseParams & pres)210 fit::result<ErrorCode, PairingFeatures> Phase1::ResolveFeatures(
211     bool local_initiator,
212     const PairingRequestParams& preq,
213     const PairingResponseParams& pres) {
214   // Select the smaller of the initiator and responder max. encryption key size
215   // values (Vol 3, Part H, 2.3.4).
216   uint8_t enc_key_size =
217       std::min(preq.max_encryption_key_size, pres.max_encryption_key_size);
218   uint8_t min_allowed_enc_key_size =
219       (requested_level_ == SecurityLevel::kSecureAuthenticated)
220           ? kMaxEncryptionKeySize
221           : kMinEncryptionKeySize;
222   if (enc_key_size < min_allowed_enc_key_size) {
223     bt_log(DEBUG, "sm", "encryption key size too small! (%u)", enc_key_size);
224     return fit::error(ErrorCode::kEncryptionKeySize);
225   }
226 
227   bool will_bond =
228       (preq.auth_req & kBondingFlag) && (pres.auth_req & kBondingFlag);
229   if (!will_bond) {
230     bt_log(
231         INFO,
232         "sm",
233         "negotiated non-bondable pairing (local mode: %s)",
234         bondable_mode_ == BondableMode::Bondable ? "bondable" : "non-bondable");
235   }
236   bool sc = (preq.auth_req & AuthReq::kSC) && (pres.auth_req & AuthReq::kSC);
237   bool mitm =
238       (preq.auth_req & AuthReq::kMITM) || (pres.auth_req & AuthReq::kMITM);
239   bool init_oob = preq.oob_data_flag == OOBDataFlag::kPresent;
240   bool rsp_oob = pres.oob_data_flag == OOBDataFlag::kPresent;
241 
242   IOCapability local_ioc, peer_ioc;
243   if (local_initiator) {
244     local_ioc = preq.io_capability;
245     peer_ioc = pres.io_capability;
246   } else {
247     local_ioc = pres.io_capability;
248     peer_ioc = preq.io_capability;
249   }
250 
251   PairingMethod method = util::SelectPairingMethod(
252       sc, init_oob, rsp_oob, mitm, local_ioc, peer_ioc, local_initiator);
253 
254   // If MITM protection is required but the pairing method cannot provide MITM,
255   // then reject the pairing.
256   if (mitm && method == PairingMethod::kJustWorks) {
257     return fit::error(ErrorCode::kAuthenticationRequirements);
258   }
259 
260   // The "Pairing Response" command (i.e. |pres|) determines the keys that shall
261   // be distributed. The keys that will be distributed by us and the peer
262   // depends on whichever one initiated the feature exchange by sending a
263   // "Pairing Request" command.
264   KeyDistGenField local_keys, remote_keys;
265   if (local_initiator) {
266     local_keys = pres.initiator_key_dist_gen;
267     remote_keys = pres.responder_key_dist_gen;
268 
269     // v5.1, Vol 3, Part H Section 3.6.1 requires that the responder shall not
270     // set to one any flag in the key dist gen fields that the initiator has set
271     // to zero. Hence we reject the pairing if the responder requests keys that
272     // we don't support.
273     if ((preq.initiator_key_dist_gen & local_keys) != local_keys ||
274         (preq.responder_key_dist_gen & remote_keys) != remote_keys) {
275       return fit::error(ErrorCode::kInvalidParameters);
276     }
277   } else {
278     local_keys = pres.responder_key_dist_gen;
279     remote_keys = pres.initiator_key_dist_gen;
280 
281     // When we are the responder we always respect the initiator's wishes.
282     PW_CHECK((preq.initiator_key_dist_gen & remote_keys) == remote_keys);
283     PW_CHECK((preq.responder_key_dist_gen & local_keys) == local_keys);
284   }
285   // v5.1 Vol 3 Part C Section 9.4.2.2 says that bonding information shall not
286   // be exchanged or stored in non-bondable mode. This check ensures that we
287   // avoid a situation where, if we were in bondable mode and a peer requested
288   // non-bondable mode with a non-zero keydistgen field, we pair in non-bondable
289   // mode but also attempt to distribute keys.
290   if (!will_bond && (local_keys || remote_keys)) {
291     return fit::error(ErrorCode::kInvalidParameters);
292   }
293 
294   // "If both [...] devices support [LE] Secure Connections [...] the devices
295   // may optionally generate the BR/EDR key [..] as part of the LE pairing
296   // procedure" (v5.2 Vol. 3 Part C 14.1).
297   std::optional<CrossTransportKeyAlgo> generate_ct_key = std::nullopt;
298   if (sc) {
299     // "In LE Secure Connections pairing, when SMP is running on the LE
300     // transport, then the EncKey field is ignored" (V5.0 Vol. 3 Part H 3.6.1).
301     // We ignore the Encryption Key bit here to allow for uniform handling of it
302     // later.
303     local_keys &= ~KeyDistGen::kEncKey;
304     remote_keys &= ~KeyDistGen::kEncKey;
305 
306     // "When hci_spec::LinkKey is set to 1 by both devices in the initiator and
307     // responder [KeyDistGen] fields, the procedures for calculating the BR/EDR
308     // link key from the LTK shall be used". The chosen procedure depends on the
309     // CT2 bit of the AuthReq (v5.2 Vol. 3 Part H 3.5.1 and 3.6.1).
310     if (local_keys & remote_keys & KeyDistGen::kLinkKey) {
311       generate_ct_key =
312           (preq.auth_req & AuthReq::kCT2) && (pres.auth_req & AuthReq::kCT2)
313               ? CrossTransportKeyAlgo::kUseH7
314               : CrossTransportKeyAlgo::kUseH6;
315     }
316 
317   } else if (requested_level_ == SecurityLevel::kSecureAuthenticated) {
318     // SecureAuthenticated means Secure Connections is required, so if this
319     // pairing would not use Secure Connections it does not meet the
320     // requirements of `requested_level_`
321     return fit::error(ErrorCode::kAuthenticationRequirements);
322   }
323 
324   return fit::ok(PairingFeatures{.initiator = local_initiator,
325                                  .secure_connections = sc,
326                                  .will_bond = will_bond,
327                                  .generate_ct_key = generate_ct_key,
328                                  .method = method,
329                                  .encryption_key_size = enc_key_size,
330                                  .local_key_distribution = local_keys,
331                                  .remote_key_distribution = remote_keys});
332 }
333 
OnPairingResponse(const PairingResponseParams & response_params)334 void Phase1::OnPairingResponse(const PairingResponseParams& response_params) {
335   // Support receiving a pairing response only as the initiator.
336   if (role() == Role::kResponder) {
337     bt_log(DEBUG, "sm", "received pairing response when acting as responder");
338     Abort(ErrorCode::kCommandNotSupported);
339     return;
340   }
341 
342   if (!preq_.has_value() || pres_.has_value()) {
343     bt_log(DEBUG, "sm", "ignoring unexpected \"Pairing Response\" packet");
344     Abort(ErrorCode::kUnspecifiedReason);
345     return;
346   }
347 
348   fit::result<ErrorCode, PairingFeatures> maybe_features =
349       ResolveFeatures(/*local_initiator=*/true, *preq_, response_params);
350 
351   if (maybe_features.is_error()) {
352     bt_log(DEBUG, "sm", "rejecting pairing features");
353     Abort(maybe_features.error_value());
354     return;
355   }
356   PairingFeatures features = maybe_features.value();
357   pres_ = response_params;
358   on_complete_(features, *preq_, *pres_);
359 }
360 
OnRxBFrame(ByteBufferPtr sdu)361 void Phase1::OnRxBFrame(ByteBufferPtr sdu) {
362   fit::result<ErrorCode, ValidPacketReader> maybe_reader =
363       ValidPacketReader::ParseSdu(sdu);
364   if (maybe_reader.is_error()) {
365     Abort(maybe_reader.error_value());
366     return;
367   }
368   ValidPacketReader reader = maybe_reader.value();
369   Code smp_code = reader.code();
370 
371   if (smp_code == kPairingFailed) {
372     OnFailure(Error(reader.payload<ErrorCode>()));
373   } else if (smp_code == kPairingResponse) {
374     OnPairingResponse(reader.payload<PairingResponseParams>());
375   } else {
376     bt_log(INFO,
377            "sm",
378            "received unexpected code %#.2X when in Pairing Phase 1",
379            smp_code);
380     Abort(ErrorCode::kUnspecifiedReason);
381   }
382 }
383 
384 }  // namespace bt::sm
385