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_2_legacy.h"
18
19 #include <pw_assert/check.h>
20 #include <pw_bytes/endian.h>
21
22 #include <cstdint>
23 #include <memory>
24
25 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
26 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h"
29 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
30 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel_test.h"
31 #include "pw_bluetooth_sapphire/internal/host/sm/fake_phase_listener.h"
32 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
33 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
34 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
35 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
36 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
37 #include "pw_unit_test/framework.h"
38
39 namespace bt::sm {
40 namespace {
41
42 const PairingFeatures kDefaultFeatures = {
43 .initiator = true,
44 .secure_connections = false,
45 .will_bond = true,
46 .generate_ct_key = std::optional<CrossTransportKeyAlgo>{std::nullopt},
47 .method = PairingMethod::kJustWorks,
48 .encryption_key_size = kMaxEncryptionKeySize,
49 .local_key_distribution = KeyDistGen::kIdKey,
50 .remote_key_distribution = KeyDistGen::kIdKey | KeyDistGen::kEncKey};
51
52 const PairingRequestParams kDefaultPreq{
53 .io_capability = IOCapability::kNoInputNoOutput,
54 .oob_data_flag = OOBDataFlag::kNotPresent,
55 .auth_req = AuthReq::kBondingFlag,
56 .max_encryption_key_size = kMaxEncryptionKeySize,
57 .initiator_key_dist_gen = KeyDistGen::kIdKey,
58 .responder_key_dist_gen = KeyDistGen::kIdKey | KeyDistGen::kEncKey};
59
60 const PairingResponseParams kDefaultPres{
61 .io_capability = IOCapability::kNoInputNoOutput,
62 .oob_data_flag = OOBDataFlag::kNotPresent,
63 .auth_req = AuthReq::kBondingFlag,
64 .max_encryption_key_size = kMaxEncryptionKeySize,
65 .initiator_key_dist_gen = KeyDistGen::kIdKey,
66 .responder_key_dist_gen = KeyDistGen::kIdKey | KeyDistGen::kEncKey};
67
68 const DeviceAddress kAddr1(DeviceAddress::Type::kLEPublic,
69 {0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
70 const DeviceAddress kAddr2(DeviceAddress::Type::kLEPublic,
71 {0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
72 struct Phase2LegacyArgs {
73 PairingFeatures features = kDefaultFeatures;
74 PairingRequestParams preq = kDefaultPreq;
75 PairingResponseParams pres = kDefaultPres;
76 const DeviceAddress* initiator_addr = &kAddr1;
77 const DeviceAddress* responder_addr = &kAddr2;
78 };
79
80 using util::PacketSize;
81
82 class Phase2LegacyTest : public l2cap::testing::FakeChannelTest {
83 public:
84 Phase2LegacyTest() = default;
85 ~Phase2LegacyTest() override = default;
86
heap_dispatcher()87 pw::async::HeapDispatcher& heap_dispatcher() { return heap_dispatcher_; }
88
89 protected:
SetUp()90 void SetUp() override { NewPhase2Legacy(); }
91
TearDown()92 void TearDown() override { phase_2_legacy_ = nullptr; }
93
NewPhase2Legacy(Phase2LegacyArgs phase_args=Phase2LegacyArgs (),bt::LinkType ll_type=bt::LinkType::kLE)94 void NewPhase2Legacy(Phase2LegacyArgs phase_args = Phase2LegacyArgs(),
95 bt::LinkType ll_type = bt::LinkType::kLE) {
96 l2cap::ChannelId cid = ll_type == bt::LinkType::kLE ? l2cap::kLESMPChannelId
97 : l2cap::kSMPChannelId;
98 ChannelOptions options(cid);
99 options.link_type = ll_type;
100
101 phase_args_ = phase_args;
102
103 listener_ = std::make_unique<FakeListener>();
104 fake_chan_ = CreateFakeChannel(options);
105 sm_chan_ = std::make_unique<PairingChannel>(fake_chan_->GetWeakPtr());
106 auto role =
107 phase_args.features.initiator ? Role::kInitiator : Role::kResponder;
108 StaticByteBuffer<PacketSize<PairingRequestParams>()> preq, pres;
109 preq.WriteObj(phase_args.preq);
110 pres.WriteObj(phase_args.pres);
111 phase_2_legacy_ =
112 std::make_unique<Phase2Legacy>(sm_chan_->GetWeakPtr(),
113 listener_->as_weak_ptr(),
114 role,
115 phase_args.features,
116 preq,
117 pres,
118 *phase_args.initiator_addr,
119 *phase_args.responder_addr,
120 [this](const UInt128& stk) {
121 phase_2_complete_count_++;
122 stk_ = stk;
123 });
124 }
125
Receive128BitCmd(Code cmd_code,const UInt128 & value)126 void Receive128BitCmd(Code cmd_code, const UInt128& value) {
127 fake_chan()->Receive(Make128BitCmd(cmd_code, value));
128 }
129
Make128BitCmd(Code cmd_code,const UInt128 & value)130 DynamicByteBuffer Make128BitCmd(Code cmd_code, const UInt128& value) {
131 StaticByteBuffer<PacketSize<UInt128>()> buffer;
132 PacketWriter writer(cmd_code, &buffer);
133 *writer.mutable_payload<UInt128>() = value;
134 return DynamicByteBuffer(buffer);
135 }
136
GenerateConfirmValue(const UInt128 & random,uint32_t tk=0) const137 UInt128 GenerateConfirmValue(const UInt128& random, uint32_t tk = 0) const {
138 tk = pw::bytes::ConvertOrderTo(cpp20::endian::little, tk);
139 UInt128 tk128;
140 tk128.fill(0);
141 std::memcpy(tk128.data(), &tk, sizeof(tk));
142 StaticByteBuffer<PacketSize<PairingRequestParams>()> preq, pres;
143 preq.WriteObj(phase_args_.preq);
144 pres.WriteObj(phase_args_.pres);
145
146 UInt128 out_value;
147 util::C1(tk128,
148 random,
149 preq,
150 pres,
151 *phase_args_.initiator_addr,
152 *phase_args_.responder_addr,
153 &out_value);
154 return out_value;
155 }
156
157 struct MatchingPair {
158 UInt128 confirm;
159 UInt128 random;
160 };
GenerateMatchingConfirmAndRandom(uint32_t tk)161 MatchingPair GenerateMatchingConfirmAndRandom(uint32_t tk) {
162 MatchingPair pair;
163 random_generator()->Get(
164 {reinterpret_cast<std::byte*>(pair.random.data()), pair.random.size()});
165 pair.confirm = GenerateConfirmValue(pair.random, tk);
166 return pair;
167 }
168
ExtractCodeAnd128BitCmd(ByteBufferPtr sdu)169 static std::pair<Code, UInt128> ExtractCodeAnd128BitCmd(ByteBufferPtr sdu) {
170 PW_CHECK(sdu, "Tried to ExtractCodeAnd128BitCmd from nullptr in test");
171 auto maybe_reader = ValidPacketReader::ParseSdu(sdu);
172 PW_CHECK(maybe_reader.is_ok(),
173 "Tried to ExtractCodeAnd128BitCmd from invalid SMP packet");
174 return {maybe_reader.value().code(),
175 maybe_reader.value().payload<UInt128>()};
176 }
177
DestroyPhase2()178 void DestroyPhase2() { phase_2_legacy_.reset(nullptr); }
fake_chan() const179 l2cap::testing::FakeChannel* fake_chan() const { return fake_chan_.get(); }
phase_2_legacy()180 Phase2Legacy* phase_2_legacy() { return phase_2_legacy_.get(); }
listener()181 FakeListener* listener() { return listener_.get(); }
182
phase_2_complete_count() const183 int phase_2_complete_count() const { return phase_2_complete_count_; }
stk() const184 UInt128 stk() const { return stk_; }
185
186 private:
187 std::unique_ptr<FakeListener> listener_;
188 std::unique_ptr<l2cap::testing::FakeChannel> fake_chan_;
189 std::unique_ptr<PairingChannel> sm_chan_;
190 std::unique_ptr<Phase2Legacy> phase_2_legacy_;
191 Phase2LegacyArgs phase_args_;
192 int phase_2_complete_count_ = 0;
193 UInt128 stk_;
194 pw::async::HeapDispatcher heap_dispatcher_{dispatcher()};
195
196 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Phase2LegacyTest);
197 };
198
199 using Phase2LegacyDeathTest = Phase2LegacyTest;
200
TEST_F(Phase2LegacyDeathTest,InvalidPairingMethodDies)201 TEST_F(Phase2LegacyDeathTest, InvalidPairingMethodDies) {
202 Phase2LegacyArgs args;
203 // Legacy Pairing does not permit Numeric Comparison (V5.0, Vol. 3, Part H,
204 // Section 2.3.5.1)
205 args.features.method = PairingMethod::kNumericComparison;
206 ASSERT_DEATH_IF_SUPPORTED(NewPhase2Legacy(args), "method");
207 }
208
TEST_F(Phase2LegacyTest,InitiatorJustWorksStkSucceeds)209 TEST_F(Phase2LegacyTest, InitiatorJustWorksStkSucceeds) {
210 Phase2LegacyArgs args;
211 args.features.initiator = true;
212 args.features.method = PairingMethod::kJustWorks;
213 NewPhase2Legacy(args);
214 // Using Just Works, pairing should request user confirmation
215 FakeListener::ConfirmCallback confirm_cb = nullptr;
216 listener()->set_confirm_delegate(
217 [&](FakeListener::ConfirmCallback cb) { confirm_cb = std::move(cb); });
218
219 Code sent_code = kInvalidCode;
220 std::optional<UInt128> sent_payload = std::nullopt;
221 fake_chan()->SetSendCallback(
222 [&](ByteBufferPtr sdu) {
223 std::tie(sent_code, sent_payload) =
224 ExtractCodeAnd128BitCmd(std::move(sdu));
225 },
226 dispatcher());
227 phase_2_legacy()->Start();
228 // We should request user confirmation, but not send a message until we
229 // receive it.
230 ASSERT_EQ(kInvalidCode, sent_code);
231 ASSERT_TRUE(confirm_cb);
232 confirm_cb(true);
233 RunUntilIdle();
234 ASSERT_EQ(kPairingConfirm, sent_code);
235
236 // Reset |sent_payload| to be able to detect that the FakeChannel's
237 // |send_callback| is notified.
238 sent_payload = std::nullopt;
239 MatchingPair values =
240 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
241 Receive128BitCmd(kPairingConfirm, values.confirm);
242 RunUntilIdle();
243 ASSERT_EQ(kPairingRandom, sent_code);
244 ASSERT_TRUE(sent_payload.has_value());
245
246 // Receive the peer pairing random & verify pairing completes successfully
247 Receive128BitCmd(kPairingRandom, values.random);
248 RunUntilIdle();
249 ASSERT_EQ(1, phase_2_complete_count());
250 UInt128 generated_stk;
251 util::S1({0}, values.random, *sent_payload, &generated_stk);
252 ASSERT_EQ(generated_stk, stk());
253 }
254
TEST_F(Phase2LegacyTest,InitiatorPasskeyInputStkSucceeds)255 TEST_F(Phase2LegacyTest, InitiatorPasskeyInputStkSucceeds) {
256 Phase2LegacyArgs args;
257 args.features.initiator = true;
258 args.features.method = PairingMethod::kPasskeyEntryInput;
259 // preq & pres are set for consistency w/ args.features - not necessary for
260 // the test to pass.
261 args.preq.io_capability = IOCapability::kKeyboardOnly;
262 args.preq.auth_req |= AuthReq::kMITM;
263 args.pres.io_capability = IOCapability::kDisplayOnly;
264 NewPhase2Legacy(args);
265 FakeListener::PasskeyResponseCallback passkey_responder = nullptr;
266 listener()->set_request_passkey_delegate(
267 [&](FakeListener::PasskeyResponseCallback cb) {
268 passkey_responder = std::move(cb);
269 });
270
271 Code sent_code = kInvalidCode;
272 std::optional<UInt128> sent_payload = std::nullopt;
273 fake_chan()->SetSendCallback(
274 [&](ByteBufferPtr sdu) {
275 std::tie(sent_code, sent_payload) =
276 ExtractCodeAnd128BitCmd(std::move(sdu));
277 },
278 dispatcher());
279 phase_2_legacy()->Start();
280
281 // We should request user confirmation, but not send a message until we
282 // receive it.
283 ASSERT_EQ(kInvalidCode, sent_code);
284 ASSERT_TRUE(passkey_responder);
285 const int32_t kTk = 0x1234;
286 const UInt128 kTk128 = {0x34, 0x12};
287 passkey_responder(kTk);
288 RunUntilIdle();
289 ASSERT_EQ(kPairingConfirm, sent_code);
290
291 // Reset |sent_payload| to be able to detect that the FakeChannel's
292 // |send_callback| is notified.
293 sent_payload = std::nullopt;
294 MatchingPair values = GenerateMatchingConfirmAndRandom(kTk);
295 Receive128BitCmd(kPairingConfirm, values.confirm);
296 RunUntilIdle();
297 ASSERT_EQ(kPairingRandom, sent_code);
298 ASSERT_TRUE(sent_payload.has_value());
299
300 // Receive the peer pairing random & verify pairing completes successfully
301 Receive128BitCmd(kPairingRandom, values.random);
302 RunUntilIdle();
303 ASSERT_EQ(1, phase_2_complete_count());
304 UInt128 generated_stk;
305 util::S1(kTk128, values.random, *sent_payload, &generated_stk);
306 ASSERT_EQ(generated_stk, stk());
307 }
308
309 // This test is shorter than InitiatorPasskeyInputStkSucceeds because it only
310 // tests the code paths that differ for PasskeyDisplay, which all take place
311 // before sending the Confirm value.
TEST_F(Phase2LegacyTest,InitiatorPasskeyDisplaySucceeds)312 TEST_F(Phase2LegacyTest, InitiatorPasskeyDisplaySucceeds) {
313 Phase2LegacyArgs args;
314 args.features.initiator = true;
315 args.features.method = PairingMethod::kPasskeyEntryDisplay;
316 // preq & pres are set for consistency w/ args.features - not necessary for
317 // the test to pass.
318 args.preq.io_capability = IOCapability::kDisplayOnly;
319 args.preq.auth_req |= AuthReq::kMITM;
320 args.pres.io_capability = IOCapability::kKeyboardOnly;
321 NewPhase2Legacy(args);
322
323 FakeListener::ConfirmCallback display_confirmer = nullptr;
324 listener()->set_display_delegate(
325 [&](uint32_t, bool, FakeListener::ConfirmCallback cb) {
326 display_confirmer = std::move(cb);
327 });
328
329 Code sent_code = kInvalidCode;
330 std::optional<UInt128> sent_payload = std::nullopt;
331 fake_chan()->SetSendCallback(
332 [&](ByteBufferPtr sdu) {
333 std::tie(sent_code, sent_payload) =
334 ExtractCodeAnd128BitCmd(std::move(sdu));
335 },
336 dispatcher());
337 phase_2_legacy()->Start();
338 // We should request user confirmation, but not send a message until we
339 // receive it.
340 ASSERT_EQ(kInvalidCode, sent_code);
341 ASSERT_TRUE(display_confirmer);
342 display_confirmer(true);
343 RunUntilIdle();
344 ASSERT_EQ(kPairingConfirm, sent_code);
345 ASSERT_TRUE(sent_payload.has_value());
346 // After sending Pairing Confirm, the behavior is the same as
347 // InitiatorPasskeyInputStkSucceeds
348 }
349
TEST_F(Phase2LegacyTest,InitiatorReceivesConfirmBeforeTkFails)350 TEST_F(Phase2LegacyTest, InitiatorReceivesConfirmBeforeTkFails) {
351 Phase2LegacyArgs args;
352 args.features.initiator = true;
353 NewPhase2Legacy(args);
354 FakeListener::ConfirmCallback confirm_cb = nullptr;
355 listener()->set_confirm_delegate(
356 [&](FakeListener::ConfirmCallback cb) { confirm_cb = std::move(cb); });
357
358 ByteBufferPtr sent_sdu = nullptr;
359 fake_chan()->SetSendCallback(
360 [&](ByteBufferPtr sdu) { sent_sdu = std::move(sdu); }, dispatcher());
361 phase_2_legacy()->Start();
362 ASSERT_TRUE(confirm_cb);
363 ASSERT_FALSE(sent_sdu);
364
365 // Receive peer confirm (generated from arbitrary peer rand {0}) before
366 // |confirm_cb| is notified
367 const auto kPairingConfirmCmd =
368 Make128BitCmd(kPairingConfirm, GenerateConfirmValue({0}));
369 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
370 kPairingFailed, ErrorCode::kUnspecifiedReason};
371 ASSERT_TRUE(ReceiveAndExpect(kPairingConfirmCmd, kExpectedFailure));
372 }
373
TEST_F(Phase2LegacyTest,InvalidConfirmValueFails)374 TEST_F(Phase2LegacyTest, InvalidConfirmValueFails) {
375 Code sent_code = kInvalidCode;
376 std::optional<UInt128> sent_payload = std::nullopt;
377 fake_chan()->SetSendCallback(
378 [&](ByteBufferPtr sdu) {
379 std::tie(sent_code, sent_payload) =
380 ExtractCodeAnd128BitCmd(std::move(sdu));
381 },
382 dispatcher());
383 phase_2_legacy()->Start();
384 RunUntilIdle();
385 ASSERT_EQ(kPairingConfirm, sent_code);
386 // Reset |sent_payload| to be able to detect that the FakeChannel's
387 // |send_callback| is notified.
388 sent_payload = std::nullopt;
389 MatchingPair values =
390 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
391 Receive128BitCmd(kPairingConfirm, values.confirm);
392 RunUntilIdle();
393 ASSERT_EQ(kPairingRandom, sent_code);
394 // Change the peer random so that the confirm value we sent does not match the
395 // random value.
396 UInt128 mismatched_peer_rand = values.random;
397 mismatched_peer_rand[0] += 1;
398 const auto kPairingRandomCmd =
399 Make128BitCmd(kPairingRandom, mismatched_peer_rand);
400 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
401 kPairingFailed, ErrorCode::kConfirmValueFailed};
402 ASSERT_TRUE(ReceiveAndExpect(kPairingRandomCmd, kExpectedFailure));
403 ASSERT_EQ(1, listener()->pairing_error_count());
404 }
405
TEST_F(Phase2LegacyTest,JustWorksUserConfirmationRejectedPairingFails)406 TEST_F(Phase2LegacyTest, JustWorksUserConfirmationRejectedPairingFails) {
407 Phase2LegacyArgs args;
408 args.features.method = PairingMethod::kJustWorks;
409 NewPhase2Legacy(args);
410 // Reject the TK request
411 bool confirmation_requested = false;
412 listener()->set_confirm_delegate([&](FakeListener::ConfirmCallback cb) {
413 confirmation_requested = true;
414 cb(false);
415 });
416 (void)heap_dispatcher().Post(
417 [this](pw::async::Context /*ctx*/, pw::Status status) {
418 if (status.ok()) {
419 phase_2_legacy()->Start();
420 }
421 });
422 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
423 kPairingFailed, ErrorCode::kUnspecifiedReason};
424 ASSERT_TRUE(Expect(kExpectedFailure));
425 ASSERT_TRUE(confirmation_requested);
426 ASSERT_EQ(1, listener()->pairing_error_count());
427 }
428
TEST_F(Phase2LegacyTest,PasskeyInputRejectedPairingFails)429 TEST_F(Phase2LegacyTest, PasskeyInputRejectedPairingFails) {
430 Phase2LegacyArgs args;
431 args.features.method = PairingMethod::kPasskeyEntryInput;
432 NewPhase2Legacy(args);
433 // Reject the TK request
434 bool confirmation_requested = false;
435 listener()->set_request_passkey_delegate(
436 [&](FakeListener::PasskeyResponseCallback cb) {
437 confirmation_requested = true;
438 const int64_t kGenericNegativeInt = -12;
439 cb(kGenericNegativeInt);
440 });
441 (void)heap_dispatcher().Post(
442 [this](pw::async::Context /*ctx*/, pw::Status status) {
443 if (status.ok()) {
444 phase_2_legacy()->Start();
445 }
446 });
447 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
448 kPairingFailed, ErrorCode::kPasskeyEntryFailed};
449 ASSERT_TRUE(Expect(kExpectedFailure));
450 ASSERT_TRUE(confirmation_requested);
451 ASSERT_EQ(1, listener()->pairing_error_count());
452 }
453
TEST_F(Phase2LegacyTest,PasskeyDisplayRejectedPairingFails)454 TEST_F(Phase2LegacyTest, PasskeyDisplayRejectedPairingFails) {
455 Phase2LegacyArgs args;
456 args.features.method = PairingMethod::kPasskeyEntryDisplay;
457 NewPhase2Legacy(args);
458 // Reject the TK request
459 bool confirmation_requested = false;
460 listener()->set_display_delegate(
461 [&](uint32_t /*ignore*/, bool, FakeListener::ConfirmCallback cb) {
462 confirmation_requested = true;
463 cb(false);
464 });
465 (void)heap_dispatcher().Post(
466 [this](pw::async::Context /*ctx*/, pw::Status status) {
467 if (status.ok()) {
468 phase_2_legacy()->Start();
469 }
470 });
471 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
472 kPairingFailed, ErrorCode::kUnspecifiedReason};
473 ASSERT_TRUE(Expect(kExpectedFailure));
474 ASSERT_TRUE(confirmation_requested);
475 ASSERT_EQ(1, listener()->pairing_error_count());
476 }
477
478 // Each of the pairing methods has its own user input callback, and thus correct
479 // behavior under destruction of the Phase needs to be checked for each method.
TEST_F(Phase2LegacyTest,PhaseDestroyedWhileWaitingForJustWorksTk)480 TEST_F(Phase2LegacyTest, PhaseDestroyedWhileWaitingForJustWorksTk) {
481 Phase2LegacyArgs args;
482 args.features.method = PairingMethod::kJustWorks;
483 NewPhase2Legacy(args);
484 FakeListener::ConfirmCallback respond = nullptr;
485 listener()->set_confirm_delegate([&](auto rsp) { respond = std::move(rsp); });
486 phase_2_legacy()->Start();
487
488 ASSERT_TRUE(respond);
489
490 DestroyPhase2();
491 respond(true);
492 RunUntilIdle();
493 SUCCEED();
494 }
495
TEST_F(Phase2LegacyTest,PhaseDestroyedWhileWaitingForPasskeyInputTk)496 TEST_F(Phase2LegacyTest, PhaseDestroyedWhileWaitingForPasskeyInputTk) {
497 Phase2LegacyArgs args;
498 args.features.method = PairingMethod::kPasskeyEntryInput;
499 NewPhase2Legacy(args);
500 FakeListener::PasskeyResponseCallback respond = nullptr;
501 listener()->set_request_passkey_delegate(
502 [&](auto rsp) { respond = std::move(rsp); });
503 phase_2_legacy()->Start();
504
505 ASSERT_TRUE(respond);
506
507 DestroyPhase2();
508 respond(1234);
509 RunUntilIdle();
510 SUCCEED();
511 }
512
TEST_F(Phase2LegacyTest,PhaseDestroyedWaitingForPasskeyDisplayTk)513 TEST_F(Phase2LegacyTest, PhaseDestroyedWaitingForPasskeyDisplayTk) {
514 Phase2LegacyArgs args;
515 args.features.method = PairingMethod::kPasskeyEntryDisplay;
516 NewPhase2Legacy(args);
517 FakeListener::ConfirmCallback respond = nullptr;
518 listener()->set_display_delegate(
519 [&](uint32_t /*unused*/, bool, FakeListener::ConfirmCallback rsp) {
520 respond = std::move(rsp);
521 });
522 phase_2_legacy()->Start();
523
524 ASSERT_TRUE(respond);
525
526 DestroyPhase2();
527 respond(true);
528 RunUntilIdle();
529 SUCCEED();
530 }
531
TEST_F(Phase2LegacyTest,ReceiveRandomBeforeTkFails)532 TEST_F(Phase2LegacyTest, ReceiveRandomBeforeTkFails) {
533 // This test assumes initiator flow, but the behavior verified is the same for
534 // responder flow.
535 FakeListener::ConfirmCallback confirm_cb = nullptr;
536 listener()->set_confirm_delegate(
537 [&](FakeListener::ConfirmCallback cb) { confirm_cb = std::move(cb); });
538
539 phase_2_legacy()->Start();
540 // We should have made the pairing delegate request, which will not be
541 // responded to.
542 ASSERT_TRUE(confirm_cb);
543
544 MatchingPair values =
545 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
546 const auto kPairingRandomCmd = Make128BitCmd(kPairingRandom, values.random);
547 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
548 kPairingFailed, ErrorCode::kUnspecifiedReason};
549 ASSERT_TRUE(ReceiveAndExpect(kPairingRandomCmd, kExpectedFailure));
550 ASSERT_EQ(1, listener()->pairing_error_count());
551 }
552
TEST_F(Phase2LegacyTest,ReceiveRandomBeforeConfirmFails)553 TEST_F(Phase2LegacyTest, ReceiveRandomBeforeConfirmFails) {
554 // This test assumes initiator flow, but the behavior verified is the same for
555 // responder flow.
556 bool requested_confirmation = false;
557 // We automatically confirm the TK to check the case where we have a TK, but
558 // no peer confirm.
559 listener()->set_confirm_delegate([&](FakeListener::ConfirmCallback cb) {
560 requested_confirmation = true;
561 cb(true);
562 });
563 phase_2_legacy()->Start();
564 ASSERT_TRUE(requested_confirmation);
565 MatchingPair values =
566 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
567 const auto kPairingRandomCmd = Make128BitCmd(kPairingRandom, values.random);
568 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
569 kPairingFailed, ErrorCode::kUnspecifiedReason};
570 ASSERT_TRUE(ReceiveAndExpect(kPairingRandomCmd, kExpectedFailure));
571 ASSERT_EQ(1, listener()->pairing_error_count());
572 }
573
TEST_F(Phase2LegacyTest,ReceivePairingFailed)574 TEST_F(Phase2LegacyTest, ReceivePairingFailed) {
575 phase_2_legacy()->Start();
576 fake_chan()->Receive(StaticByteBuffer<PacketSize<ErrorCode>()>{
577 kPairingFailed, ErrorCode::kPairingNotSupported});
578 RunUntilIdle();
579
580 EXPECT_EQ(1, listener()->pairing_error_count());
581 EXPECT_EQ(Error(ErrorCode::kPairingNotSupported), listener()->last_error());
582 }
583
TEST_F(Phase2LegacyTest,UnsupportedCommandDuringPairing)584 TEST_F(Phase2LegacyTest, UnsupportedCommandDuringPairing) {
585 // Don't confirm the TK so that the confirm value is not sent;
586 listener()->set_confirm_delegate([](auto) {});
587 phase_2_legacy()->Start();
588
589 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpected{
590 kPairingFailed, ErrorCode::kCommandNotSupported};
591 ASSERT_TRUE(ReceiveAndExpect(StaticByteBuffer<1>(0xFF),
592 kExpected)); // 0xFF is not an SMP code.
593 EXPECT_EQ(1, listener()->pairing_error_count());
594 EXPECT_EQ(Error(ErrorCode::kCommandNotSupported), listener()->last_error());
595 }
596
TEST_F(Phase2LegacyTest,ReceiveMalformedPacket)597 TEST_F(Phase2LegacyTest, ReceiveMalformedPacket) {
598 phase_2_legacy()->Start();
599 // clang-format off
600 const StaticByteBuffer<PacketSize<PairingRandomValue>() - 1> kMalformedPairingRandom {
601 kPairingRandom,
602 // Random value (1 octet too short)
603 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
604 };
605 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure {
606 kPairingFailed, ErrorCode::kInvalidParameters
607 };
608 // clang-format on
609
610 EXPECT_TRUE(ReceiveAndExpect(kMalformedPairingRandom, kExpectedFailure));
611 }
612
TEST_F(Phase2LegacyTest,ResponderJustWorksStkSucceeds)613 TEST_F(Phase2LegacyTest, ResponderJustWorksStkSucceeds) {
614 Phase2LegacyArgs args;
615 args.features.initiator = false;
616 args.features.method = PairingMethod::kJustWorks;
617 NewPhase2Legacy(args);
618 // Using Just Works, pairing should request user confirmation
619 FakeListener::ConfirmCallback confirm_cb = nullptr;
620 listener()->set_confirm_delegate(
621 [&](FakeListener::ConfirmCallback cb) { confirm_cb = std::move(cb); });
622
623 Code sent_code = kInvalidCode;
624 std::optional<UInt128> sent_payload = std::nullopt;
625 fake_chan()->SetSendCallback(
626 [&](ByteBufferPtr sdu) {
627 std::tie(sent_code, sent_payload) =
628 ExtractCodeAnd128BitCmd(std::move(sdu));
629 },
630 dispatcher());
631 phase_2_legacy()->Start();
632 // We should not send a message until we receive the requested user input AND
633 // the peer confirm.
634 ASSERT_TRUE(confirm_cb);
635 ASSERT_EQ(kInvalidCode, sent_code);
636 confirm_cb(true);
637 RunUntilIdle();
638 ASSERT_EQ(kInvalidCode, sent_code);
639
640 // Now we receive the peer confirm & should send ours.
641 MatchingPair values =
642 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
643 Receive128BitCmd(kPairingConfirm, values.confirm);
644 RunUntilIdle();
645 ASSERT_EQ(kPairingConfirm, sent_code);
646
647 // Reset |sent_payload| to be able to detect that the FakeChannel's
648 // |send_callback| is notified.
649 sent_payload = std::nullopt;
650 // Receive the peer pairing random & verify we send our random & pairing
651 // completes.
652 Receive128BitCmd(kPairingRandom, values.random);
653 RunUntilIdle();
654 ASSERT_EQ(kPairingRandom, sent_code);
655 ASSERT_TRUE(sent_payload.has_value());
656 ASSERT_EQ(1, phase_2_complete_count());
657 UInt128 generated_stk;
658 util::S1({0}, *sent_payload, values.random, &generated_stk);
659 ASSERT_EQ(generated_stk, stk());
660 }
661
TEST_F(Phase2LegacyTest,ResponderPasskeyInputStkSucceeds)662 TEST_F(Phase2LegacyTest, ResponderPasskeyInputStkSucceeds) {
663 Phase2LegacyArgs args;
664 args.features.initiator = false;
665 args.features.method = PairingMethod::kPasskeyEntryInput;
666 // preq & pres are set for consistency w/ args.features - not necessary for
667 // the test to pass.
668 args.preq.io_capability = IOCapability::kDisplayOnly;
669 args.preq.auth_req |= AuthReq::kMITM;
670 args.pres.io_capability = IOCapability::kKeyboardOnly;
671 NewPhase2Legacy(args);
672 FakeListener::PasskeyResponseCallback passkey_responder = nullptr;
673 listener()->set_request_passkey_delegate(
674 [&](FakeListener::PasskeyResponseCallback cb) {
675 passkey_responder = std::move(cb);
676 });
677
678 Code sent_code = kInvalidCode;
679 std::optional<UInt128> sent_payload = std::nullopt;
680 fake_chan()->SetSendCallback(
681 [&](ByteBufferPtr sdu) {
682 std::tie(sent_code, sent_payload) =
683 ExtractCodeAnd128BitCmd(std::move(sdu));
684 },
685 dispatcher());
686
687 phase_2_legacy()->Start();
688
689 // We should not send a message until we receive the requested user input AND
690 // the peer confirm.
691 ASSERT_TRUE(passkey_responder);
692 ASSERT_EQ(kInvalidCode, sent_code);
693 const int32_t kTk = 0x1234;
694 const UInt128 kTk128 = {0x34, 0x12};
695 passkey_responder(kTk);
696 RunUntilIdle();
697 ASSERT_EQ(kInvalidCode, sent_code);
698 // Now we receive the peer confirm & should send ours.
699 MatchingPair values = GenerateMatchingConfirmAndRandom(kTk);
700 Receive128BitCmd(kPairingConfirm, values.confirm);
701 RunUntilIdle();
702 ASSERT_EQ(kPairingConfirm, sent_code);
703
704 // Reset |sent_payload| to be able to detect that the FakeChannel's
705 // |send_callback| is notified.
706 sent_payload = std::nullopt;
707 // Receive the peer pairing random & verify we send our random & pairing
708 // completes.
709 Receive128BitCmd(kPairingRandom, values.random);
710 RunUntilIdle();
711 ASSERT_EQ(kPairingRandom, sent_code);
712 ASSERT_TRUE(sent_payload.has_value());
713 ASSERT_EQ(1, phase_2_complete_count());
714 UInt128 generated_stk;
715 util::S1(kTk128, *sent_payload, values.random, &generated_stk);
716 ASSERT_EQ(generated_stk, stk());
717 }
718
719 // This test is shorter than ResponderPasskeyInputStkSucceeds because it only
720 // tests the code paths that differ for PasskeyDisplay, which all take place
721 // before sending the Confirm value.
TEST_F(Phase2LegacyTest,ResponderPasskeyDisplaySucceeds)722 TEST_F(Phase2LegacyTest, ResponderPasskeyDisplaySucceeds) {
723 Phase2LegacyArgs args;
724 args.features.initiator = false;
725 args.features.method = PairingMethod::kPasskeyEntryDisplay;
726 // preq & pres are set for consistency w/ args.features - not necessary for
727 // the test to pass.
728 args.preq.io_capability = IOCapability::kKeyboardOnly;
729 args.preq.auth_req |= AuthReq::kMITM;
730 args.pres.io_capability = IOCapability::kDisplayOnly;
731 NewPhase2Legacy(args);
732
733 FakeListener::ConfirmCallback display_confirmer = nullptr;
734 uint32_t passkey = 0;
735 listener()->set_display_delegate(
736 [&](uint32_t key, bool, FakeListener::ConfirmCallback cb) {
737 passkey = key;
738 display_confirmer = std::move(cb);
739 });
740
741 Code sent_code = kInvalidCode;
742 std::optional<UInt128> sent_payload = std::nullopt;
743 fake_chan()->SetSendCallback(
744 [&](ByteBufferPtr sdu) {
745 std::tie(sent_code, sent_payload) =
746 ExtractCodeAnd128BitCmd(std::move(sdu));
747 },
748 dispatcher());
749
750 phase_2_legacy()->Start();
751 // We should not send a message until we receive the requested user input AND
752 // the peer confirm.
753 ASSERT_TRUE(display_confirmer);
754 ASSERT_EQ(kInvalidCode, sent_code);
755 display_confirmer(true);
756 RunUntilIdle();
757 ASSERT_EQ(kInvalidCode, sent_code);
758
759 // Now we receive the peer confirm & should send ours.
760 MatchingPair values = GenerateMatchingConfirmAndRandom(passkey);
761 Receive128BitCmd(kPairingConfirm, values.confirm);
762 RunUntilIdle();
763 ASSERT_EQ(kPairingConfirm, sent_code);
764 ASSERT_TRUE(sent_payload.has_value());
765 }
766
TEST_F(Phase2LegacyTest,ResponderReceivesConfirmBeforeTkSucceeds)767 TEST_F(Phase2LegacyTest, ResponderReceivesConfirmBeforeTkSucceeds) {
768 Phase2LegacyArgs args;
769 args.features.initiator = false;
770 NewPhase2Legacy(args);
771 // Using Just Works, pairing should request user confirmation
772 FakeListener::ConfirmCallback confirm_cb = nullptr;
773 listener()->set_confirm_delegate(
774 [&](FakeListener::ConfirmCallback cb) { confirm_cb = std::move(cb); });
775
776 Code sent_code = kInvalidCode;
777 std::optional<UInt128> sent_payload = std::nullopt;
778 fake_chan()->SetSendCallback(
779 [&](ByteBufferPtr sdu) {
780 std::tie(sent_code, sent_payload) =
781 ExtractCodeAnd128BitCmd(std::move(sdu));
782 },
783 dispatcher());
784 phase_2_legacy()->Start();
785
786 // We should not send a message until we receive the requested user input AND
787 // the peer confirm.
788 ASSERT_TRUE(confirm_cb);
789 ASSERT_EQ(kInvalidCode, sent_code);
790 MatchingPair values =
791 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
792 Receive128BitCmd(kPairingConfirm, values.confirm);
793 RunUntilIdle();
794 ASSERT_EQ(kInvalidCode, sent_code);
795
796 // Now we received the user input & should send the peer our confirmation.
797 confirm_cb(true);
798 RunUntilIdle();
799 ASSERT_EQ(kPairingConfirm, sent_code);
800 // Reset |sent_payload| to be able to detect that the FakeChannel's
801 // |send_callback| is notified.
802 sent_payload = std::nullopt;
803 // Receive the peer pairing random & verify we send our random & pairing
804 // completes.
805 Receive128BitCmd(kPairingRandom, values.random);
806 RunUntilIdle();
807 ASSERT_EQ(kPairingRandom, sent_code);
808 ASSERT_TRUE(sent_payload.has_value());
809 ASSERT_EQ(1, phase_2_complete_count());
810 UInt128 generated_stk;
811 util::S1({0}, *sent_payload, values.random, &generated_stk);
812 ASSERT_EQ(generated_stk, stk());
813 }
814
TEST_F(Phase2LegacyTest,ReceiveConfirmValueTwiceFails)815 TEST_F(Phase2LegacyTest, ReceiveConfirmValueTwiceFails) {
816 // This test uses the responder flow, but the behavior verified is the same
817 // for initiator flow.
818 Phase2LegacyArgs args;
819 args.features.initiator = false;
820 NewPhase2Legacy(args);
821
822 Code code = kInvalidCode;
823 fake_chan()->SetSendCallback(
824 [&](ByteBufferPtr sdu) {
825 std::tie(code, std::ignore) = ExtractCodeAnd128BitCmd(std::move(sdu));
826 },
827 dispatcher());
828 phase_2_legacy()->Start();
829
830 MatchingPair values =
831 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
832 Receive128BitCmd(kPairingConfirm, values.confirm);
833 RunUntilIdle();
834 ASSERT_EQ(kPairingConfirm, code);
835 const auto kPairingConfirmCmd =
836 Make128BitCmd(kPairingConfirm, values.confirm);
837 // Pairing should fail after receiving 2 confirm values with
838 // kUnspecifiedReason
839 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
840 kPairingFailed, ErrorCode::kUnspecifiedReason};
841 ASSERT_TRUE(ReceiveAndExpect(kPairingConfirmCmd, kExpectedFailure));
842 ASSERT_EQ(1, listener()->pairing_error_count());
843 }
844
845 // Phase 2 ends after receiving the second random value & subsequently sending
846 // its own, but if the Phase 2 object is kept around and receives a second
847 // random value, pairing will fail.
TEST_F(Phase2LegacyTest,ReceiveRandomValueTwiceFails)848 TEST_F(Phase2LegacyTest, ReceiveRandomValueTwiceFails) {
849 // This test uses the responder flow, but the behavior verified is the same
850 // for initiator flow.
851 Phase2LegacyArgs args;
852 args.features.initiator = false;
853 NewPhase2Legacy(args);
854
855 phase_2_legacy()->Start();
856
857 MatchingPair values =
858 GenerateMatchingConfirmAndRandom(0); // Just Works TK is 0
859 Receive128BitCmd(kPairingConfirm, values.confirm);
860 RunUntilIdle();
861 Receive128BitCmd(kPairingRandom, values.random);
862 RunUntilIdle();
863 // We've completed Phase 2, and should've notified the callback
864 ASSERT_EQ(1, phase_2_complete_count());
865 const auto kPairingRandomCmd = Make128BitCmd(kPairingRandom, values.random);
866 // Pairing should fail after receiving a second random value with
867 // kUnspecifiedReason
868 const StaticByteBuffer<PacketSize<ErrorCode>()> kExpectedFailure{
869 kPairingFailed, ErrorCode::kUnspecifiedReason};
870 ASSERT_TRUE(ReceiveAndExpect(kPairingRandomCmd, kExpectedFailure));
871 ASSERT_EQ(1, listener()->pairing_error_count());
872 }
873 } // namespace
874 } // namespace bt::sm
875