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