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 #include "pw_bluetooth_sapphire/internal/host/sm/phase_2_legacy.h"
16
17 #include <optional>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
24 #include "pw_bluetooth_sapphire/internal/host/sm/delegate.h"
25 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
26 #include "pw_bluetooth_sapphire/internal/host/sm/pairing_phase.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 namespace {
33 // We do not support OOB pairing & Legacy pairing does not permit Numeric
34 // Comparison.
IsSupportedLegacyMethod(PairingMethod method)35 bool IsSupportedLegacyMethod(PairingMethod method) {
36 return (method == PairingMethod::kJustWorks ||
37 method == PairingMethod::kPasskeyEntryDisplay ||
38 method == PairingMethod::kPasskeyEntryInput);
39 }
40 } // namespace
41
Phase2Legacy(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,Role role,PairingFeatures features,const ByteBuffer & preq,const ByteBuffer & pres,const DeviceAddress & initiator_add,const DeviceAddress & responder_add,OnPhase2KeyGeneratedCallback cb)42 Phase2Legacy::Phase2Legacy(PairingChannel::WeakPtr chan,
43 Listener::WeakPtr listener,
44 Role role,
45 PairingFeatures features,
46 const ByteBuffer& preq,
47 const ByteBuffer& pres,
48 const DeviceAddress& initiator_add,
49 const DeviceAddress& responder_add,
50 OnPhase2KeyGeneratedCallback cb)
51 : PairingPhase(std::move(chan), std::move(listener), role),
52 sent_local_confirm_(false),
53 sent_local_rand_(false),
54 tk_(std::nullopt),
55 local_confirm_(std::nullopt),
56 peer_confirm_(std::nullopt),
57 local_rand_(std::nullopt),
58 peer_rand_(std::nullopt),
59 features_(features),
60 initiator_addr_(initiator_add),
61 responder_addr_(responder_add),
62 on_stk_ready_(std::move(cb)),
63 weak_self_(this) {
64 // Cache |preq| and |pres|. These are used for confirm value generation.
65 BT_ASSERT(preq.size() == preq_.size());
66 BT_ASSERT(pres.size() == pres_.size());
67 BT_ASSERT_MSG(IsSupportedLegacyMethod(features.method),
68 "unsupported legacy pairing method!");
69 preq.Copy(&preq_);
70 pres.Copy(&pres_);
71 SetPairingChannelHandler(*this);
72 }
73
Start()74 void Phase2Legacy::Start() {
75 BT_ASSERT(!has_failed());
76 BT_ASSERT(!features_.secure_connections);
77 BT_ASSERT(!tk_.has_value());
78 BT_ASSERT(!peer_confirm_.has_value());
79 BT_ASSERT(!peer_rand_.has_value());
80 BT_ASSERT(!sent_local_confirm_);
81 BT_ASSERT(!sent_local_rand_);
82 MakeTemporaryKeyRequest();
83 }
84
MakeTemporaryKeyRequest()85 void Phase2Legacy::MakeTemporaryKeyRequest() {
86 bt_log(DEBUG,
87 "sm",
88 "TK request - method: %s",
89 sm::util::PairingMethodToString(features_.method).c_str());
90 BT_ASSERT(listener().is_alive());
91 auto self = weak_self_.GetWeakPtr();
92 if (features_.method == sm::PairingMethod::kPasskeyEntryInput) {
93 // The TK will be provided by the user.
94 listener()->RequestPasskey([self](int64_t passkey) {
95 if (!self.is_alive()) {
96 return;
97 }
98 bool success = passkey >= 0;
99 self->HandleTemporaryKey(success ? std::optional<uint32_t>(passkey)
100 : std::nullopt);
101 });
102 return;
103 }
104
105 if (features_.method == sm::PairingMethod::kPasskeyEntryDisplay) {
106 // Randomly generate a 6 digit passkey.
107 uint32_t passkey;
108 random_generator()->GetInt<uint32_t>(passkey,
109 /*exclusive_upper_bound=*/1'000'000);
110 listener()->DisplayPasskey(
111 passkey,
112 Delegate::DisplayMethod::kPeerEntry,
113 [passkey, self](bool confirm) {
114 if (!self.is_alive()) {
115 return;
116 }
117 self->HandleTemporaryKey(confirm ? std::optional<uint32_t>(passkey)
118 : std::nullopt);
119 });
120 return;
121 }
122
123 // TODO(fxbug.dev/42138242): Support providing a TK out of band.
124 BT_ASSERT(features_.method == sm::PairingMethod::kJustWorks);
125 listener()->ConfirmPairing([self](bool confirm) {
126 if (!self.is_alive()) {
127 return;
128 }
129 self->HandleTemporaryKey(confirm ? std::optional<uint32_t>(0)
130 : std::nullopt);
131 });
132 }
133
HandleTemporaryKey(std::optional<uint32_t> maybe_tk)134 void Phase2Legacy::HandleTemporaryKey(std::optional<uint32_t> maybe_tk) {
135 if (!maybe_tk.has_value()) {
136 bt_log(INFO, "sm", "temporary key listener responded with error; aborting");
137 if (features_.method == PairingMethod::kPasskeyEntryInput) {
138 Abort(ErrorCode::kPasskeyEntryFailed);
139 } else {
140 Abort(ErrorCode::kUnspecifiedReason);
141 }
142 return;
143 }
144 uint32_t tk = *maybe_tk;
145 tk_ = UInt128{0};
146 // Set the lower bits to |tk|.
147 tk = htole32(tk);
148 std::memcpy(tk_.value().data(), &tk, sizeof(tk));
149
150 // We have TK so we can generate the confirm value now.
151 local_rand_ = Random<UInt128>();
152 local_confirm_ = UInt128();
153 util::C1(tk_.value(),
154 local_rand_.value(),
155 preq_,
156 pres_,
157 initiator_addr_,
158 responder_addr_,
159 &(local_confirm_.value()));
160
161 // If we are the initiator then we just generated the "Mconfirm" value. We
162 // start the exchange by sending this value to the peer. Otherwise this is the
163 // "Sconfirm" value and we either:
164 // a. send it now if the peer has sent us its confirm value while we were
165 // waiting for the TK.
166 // b. send it later when we receive Mconfirm.
167 if (role() == Role::kInitiator || peer_confirm_.has_value()) {
168 SendConfirmValue();
169 }
170 }
171
SendConfirmValue()172 void Phase2Legacy::SendConfirmValue() {
173 BT_ASSERT(!sent_local_confirm_);
174 BT_ASSERT(local_confirm_.has_value());
175 // Only allowed on the LE transport.
176 if (sm_chan().link_type() != bt::LinkType::kLE) {
177 bt_log(DEBUG,
178 "sm",
179 "attempted to send confirm value over BR/EDR, not sending");
180 return;
181 }
182
183 sm_chan().SendMessage(kPairingConfirm, *local_confirm_);
184 sent_local_confirm_ = true;
185 }
186
OnPairingConfirm(PairingConfirmValue confirm)187 void Phase2Legacy::OnPairingConfirm(PairingConfirmValue confirm) {
188 if (fit::result result = CanReceivePairingConfirm(); result.is_error()) {
189 Abort(result.error_value());
190 return;
191 }
192
193 peer_confirm_ = confirm;
194
195 if (role() == Role::kInitiator) {
196 // We MUST have a TK and have previously generated an Mconfirm - this was
197 // implicitly checked in CanReceivePairingConfirm by checking whether we've
198 // sent the confirm value.
199 BT_ASSERT(tk_.has_value());
200 BT_ASSERT(sent_local_confirm_);
201
202 // We have sent Mconfirm and just received Sconfirm. We now send Mrand for
203 // the peer to compare.
204 SendRandomValue();
205 } else if (tk_.has_value()) {
206 // We are the responder and have just received Mconfirm. If we already have
207 // a TK, we now send the local confirm to the peer. If not,
208 // HandleTemporaryKey will take care of that.
209 SendConfirmValue();
210 }
211 }
212
SendRandomValue()213 void Phase2Legacy::SendRandomValue() {
214 BT_ASSERT(!sent_local_rand_);
215 // This is always generated in the TK callback, which must have been called by
216 // now as the random are sent after the confirm values, and the TK must exist
217 // in order to send the confirm.
218 BT_ASSERT(local_rand_.has_value());
219
220 // Only allowed on the LE transport.
221 if (sm_chan().link_type() != bt::LinkType::kLE) {
222 bt_log(
223 WARN, "sm", "attempted to send confirm value over BR/EDR, not sending");
224 return;
225 }
226
227 sm_chan().SendMessage(kPairingRandom, *local_rand_);
228 sent_local_rand_ = true;
229 }
230
OnPairingRandom(PairingRandomValue rand)231 void Phase2Legacy::OnPairingRandom(PairingRandomValue rand) {
232 if (fit::result result = CanReceivePairingRandom(); result.is_error()) {
233 Abort(result.error_value());
234 return;
235 }
236 // These should have been checked in CanReceivePairingRandom
237 BT_ASSERT(local_rand_.has_value());
238 BT_ASSERT(tk_.has_value());
239 BT_ASSERT(peer_confirm_.has_value());
240
241 peer_rand_ = rand;
242
243 // We have the peer's confirm and rand. Verify the peer confirm to validate
244 // the authentication.
245 UInt128 peer_confirm_check;
246 util::C1(tk_.value(),
247 peer_rand_.value(),
248 preq_,
249 pres_,
250 initiator_addr_,
251 responder_addr_,
252 &peer_confirm_check);
253 if (peer_confirm_check != peer_confirm_) {
254 bt_log(WARN,
255 "sm",
256 "%sconfirm value does not match!",
257 role() == Role::kInitiator ? "S" : "M");
258 Abort(ErrorCode::kConfirmValueFailed);
259 return;
260 }
261
262 // Generate the STK.
263 UInt128 stk;
264 auto [initiator_rand, responder_rand] =
265 util::MapToRoles(*local_rand_, *peer_rand_, role());
266 util::S1(tk_.value(), responder_rand, initiator_rand, &stk);
267
268 // Mask the key based on the requested encryption key size.
269 uint8_t key_size = features_.encryption_key_size;
270 if (key_size < kMaxEncryptionKeySize) {
271 MutableBufferView view(stk.data() + key_size,
272 kMaxEncryptionKeySize - key_size);
273 view.SetToZeros();
274 }
275
276 // We've generated the STK, so Phase 2 is now over if we're the initiator.
277 on_stk_ready_(stk);
278
279 // As responder, we choose to notify the STK to the higher layer before
280 // sending our SRand. We expect the peer initiator to request encryption
281 // immediately after receiving SRand, and we want to ensure the STK is
282 // available at the hci::Connection layer when this occurs.
283 if (role() == Role::kResponder) {
284 SendRandomValue();
285 }
286 }
287
CanReceivePairingConfirm() const288 fit::result<ErrorCode> Phase2Legacy::CanReceivePairingConfirm() const {
289 // Only allowed on the LE transport.
290 if (sm_chan().link_type() != bt::LinkType::kLE) {
291 bt_log(DEBUG, "sm", "\"Confirm value\" over BR/EDR not supported!");
292 return fit::error(ErrorCode::kCommandNotSupported);
293 }
294
295 // Per the message sequence charts in V5.1 Vol. 3 Part H Appendix C.2.1,
296 // reject the pairing confirm value and abort if
297 // a. we are the initiator, and have not yet sent our confirm value.
298 // b. we are the responder, and have already sent our confirm value.
299 if ((role() == Role::kInitiator && !sent_local_confirm_) ||
300 (role() == Role::kResponder && sent_local_confirm_)) {
301 bt_log(
302 WARN, "sm", "abort pairing due to confirm value received out of order");
303 return fit::error(ErrorCode::kUnspecifiedReason);
304 }
305
306 // Legacy pairing only allows for one confirm/random exchange per pairing.
307 if (peer_confirm_.has_value()) {
308 bt_log(WARN, "sm", "already received confirm value! aborting");
309 return fit::error(ErrorCode::kUnspecifiedReason);
310 }
311
312 // The confirm value shouldn't be sent after the random value. (See spec V5.0
313 // Vol 3, Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of
314 // events).
315 if (peer_rand_.has_value() || sent_local_rand_) {
316 bt_log(
317 WARN, "sm", "\"Pairing Confirm\" must come before \"Pairing Random\"");
318 return fit::error(ErrorCode::kUnspecifiedReason);
319 }
320
321 return fit::ok();
322 }
323
CanReceivePairingRandom() const324 fit::result<ErrorCode> Phase2Legacy::CanReceivePairingRandom() const {
325 // Only allowed on the LE transport.
326 if (sm_chan().link_type() != bt::LinkType::kLE) {
327 bt_log(DEBUG, "sm", "\"Random value\" over BR/EDR not supported!");
328 return fit::error(ErrorCode::kCommandNotSupported);
329 }
330
331 if (!tk_.has_value()) {
332 bt_log(
333 WARN, "sm", "abort pairing, random value received before user input");
334 return fit::error(ErrorCode::kUnspecifiedReason);
335 }
336
337 // V5.0 Vol 3, Part H, 2.3.5.5 dictates that there should be exactly one
338 // pairing random value received by each peer in Legacy Pairing Phase 2.
339 if (peer_rand_.has_value()) {
340 bt_log(WARN, "sm", "already received random value! aborting");
341 return fit::error(ErrorCode::kUnspecifiedReason);
342 }
343
344 // The random value shouldn't be sent before the confirm value. See V5.0 Vol
345 // 3, Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of events.
346 if (!peer_confirm_.has_value()) {
347 bt_log(WARN, "sm", "\"Pairing Rand\" expected after \"Pairing Confirm\"");
348 return fit::error(ErrorCode::kUnspecifiedReason);
349 }
350
351 if (role() == Role::kInitiator) {
352 // The initiator distributes both values before the responder sends Srandom.
353 if (!sent_local_rand_ || !sent_local_confirm_) {
354 bt_log(WARN, "sm", "\"Pairing Random\" received in wrong order!");
355 return fit::error(ErrorCode::kUnspecifiedReason);
356 }
357 } else {
358 // We know we have not received Mrand, and should not have sent Srand
359 // without receiving Mrand.
360 BT_ASSERT(!sent_local_rand_);
361
362 // We need to send Sconfirm before the initiator sends Mrand.
363 if (!sent_local_confirm_) {
364 bt_log(WARN, "sm", "\"Pairing Random\" received in wrong order!");
365 return fit::error(ErrorCode::kUnspecifiedReason);
366 }
367 }
368
369 return fit::ok();
370 }
371
OnRxBFrame(ByteBufferPtr sdu)372 void Phase2Legacy::OnRxBFrame(ByteBufferPtr sdu) {
373 fit::result<ErrorCode, ValidPacketReader> maybe_reader =
374 ValidPacketReader::ParseSdu(sdu);
375 if (maybe_reader.is_error()) {
376 Abort(maybe_reader.error_value());
377 return;
378 }
379 ValidPacketReader reader = maybe_reader.value();
380 Code smp_code = reader.code();
381
382 if (smp_code == kPairingFailed) {
383 OnFailure(Error(reader.payload<ErrorCode>()));
384 } else if (smp_code == kPairingConfirm) {
385 OnPairingConfirm(reader.payload<PairingConfirmValue>());
386 } else if (smp_code == kPairingRandom) {
387 OnPairingRandom(reader.payload<PairingRandomValue>());
388 } else {
389 bt_log(INFO,
390 "sm",
391 "received unexpected code %#.2X when in Pairing Legacy Phase 2",
392 smp_code);
393 Abort(ErrorCode::kUnspecifiedReason);
394 }
395 }
396
397 } // namespace bt::sm
398