• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/pairing_phase.h"
16 
17 #include <memory>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel_test.h"
22 #include "pw_bluetooth_sapphire/internal/host/sm/fake_phase_listener.h"
23 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
24 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
25 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
26 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
27 #include "pw_unit_test/framework.h"
28 
29 namespace bt::sm {
30 namespace {
31 using Listener = PairingPhase::Listener;
32 using PairingChannelHandler = PairingChannel::Handler;
33 
34 class ConcretePairingPhase : public PairingPhase, public PairingChannelHandler {
35  public:
ConcretePairingPhase(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,Role role,size_t max_packet_size=sizeof (PairingPublicKeyParams))36   ConcretePairingPhase(PairingChannel::WeakPtr chan,
37                        Listener::WeakPtr listener,
38                        Role role,
39                        size_t max_packet_size = sizeof(PairingPublicKeyParams))
40       : PairingPhase(std::move(chan), std::move(listener), role),
41         weak_handler_(this) {
42     // All concrete pairing phases should set themselves as the pairing channel
43     // handler.
44     SetPairingChannelHandler(*this);
45     last_rx_packet_ = DynamicByteBuffer(max_packet_size);
46   }
47 
48   // All concrete pairing phases should invalidate the channel handler in their
49   // destructor.
~ConcretePairingPhase()50   ~ConcretePairingPhase() override { InvalidatePairingChannelHandler(); }
51 
52   // PairingPhase overrides.
ToStringInternal()53   std::string ToStringInternal() override { return ""; }
54 
55   // PairingPhase override, not tested as PairingPhase does not implement this
56   // pure virtual function.
Start()57   void Start() override {}
58 
59   // PairingChannelHandler override
OnChannelClosed()60   void OnChannelClosed() override { PairingPhase::HandleChannelClosed(); }
OnRxBFrame(ByteBufferPtr sdu)61   void OnRxBFrame(ByteBufferPtr sdu) override { sdu->Copy(&last_rx_packet_); }
62 
last_rx_packet()63   const ByteBuffer& last_rx_packet() { return last_rx_packet_; }
64 
65  private:
66   WeakSelf<PairingChannelHandler> weak_handler_;
67   DynamicByteBuffer last_rx_packet_;
68 };
69 
70 class PairingPhaseTest : public l2cap::testing::FakeChannelTest {
71  public:
72   PairingPhaseTest() = default;
73   ~PairingPhaseTest() override = default;
74 
75  protected:
SetUp()76   void SetUp() override { NewPairingPhase(); }
77 
TearDown()78   void TearDown() override { pairing_phase_ = nullptr; }
79 
NewPairingPhase(Role role=Role::kInitiator,bt::LinkType ll_type=bt::LinkType::kLE)80   void NewPairingPhase(Role role = Role::kInitiator,
81                        bt::LinkType ll_type = bt::LinkType::kLE) {
82     l2cap::ChannelId cid = ll_type == bt::LinkType::kLE ? l2cap::kLESMPChannelId
83                                                         : l2cap::kSMPChannelId;
84     ChannelOptions options(cid);
85     options.link_type = ll_type;
86 
87     listener_ = std::make_unique<FakeListener>();
88     fake_chan_ = CreateFakeChannel(options);
89     sm_chan_ = std::make_unique<PairingChannel>(fake_chan_->GetWeakPtr());
90     pairing_phase_ = std::make_unique<ConcretePairingPhase>(
91         sm_chan_->GetWeakPtr(), listener_->as_weak_ptr(), role);
92   }
93 
fake_chan() const94   l2cap::testing::FakeChannel* fake_chan() const { return fake_chan_.get(); }
listener()95   FakeListener* listener() { return listener_.get(); }
pairing_phase()96   ConcretePairingPhase* pairing_phase() { return pairing_phase_.get(); }
97 
98  private:
99   std::unique_ptr<FakeListener> listener_;
100   std::unique_ptr<l2cap::testing::FakeChannel> fake_chan_;
101   std::unique_ptr<PairingChannel> sm_chan_;
102   std::unique_ptr<ConcretePairingPhase> pairing_phase_;
103 
104   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingPhaseTest);
105 };
106 
107 using PairingPhaseDeathTest = PairingPhaseTest;
108 
TEST_F(PairingPhaseDeathTest,CallMethodOnFailedPhaseDies)109 TEST_F(PairingPhaseDeathTest, CallMethodOnFailedPhaseDies) {
110   pairing_phase()->Abort(ErrorCode::kUnspecifiedReason);
111   ASSERT_DEATH_IF_SUPPORTED(
112       pairing_phase()->OnFailure(Error(HostError::kFailed)), ".*failed.*");
113 }
114 
TEST_F(PairingPhaseTest,ChannelClosedNotifiesListener)115 TEST_F(PairingPhaseTest, ChannelClosedNotifiesListener) {
116   ASSERT_EQ(listener()->pairing_error_count(), 0);
117   fake_chan()->Close();
118   RunUntilIdle();
119   ASSERT_EQ(listener()->pairing_error_count(), 1);
120   EXPECT_EQ(Error(HostError::kLinkDisconnected), listener()->last_error());
121 }
122 
TEST_F(PairingPhaseTest,OnFailureNotifiesListener)123 TEST_F(PairingPhaseTest, OnFailureNotifiesListener) {
124   auto ecode = ErrorCode::kDHKeyCheckFailed;
125   ASSERT_EQ(listener()->pairing_error_count(), 0);
126   pairing_phase()->OnFailure(Error(ecode));
127   RunUntilIdle();
128   ASSERT_EQ(listener()->pairing_error_count(), 1);
129   EXPECT_EQ(Error(ecode), listener()->last_error());
130 }
131 
TEST_F(PairingPhaseTest,AbortSendsFailureMessageAndNotifiesListener)132 TEST_F(PairingPhaseTest, AbortSendsFailureMessageAndNotifiesListener) {
133   ByteBufferPtr msg_sent = nullptr;
134   fake_chan()->SetSendCallback(
135       [&msg_sent](ByteBufferPtr sdu) { msg_sent = std::move(sdu); },
136       dispatcher());
137   ASSERT_EQ(0, listener()->pairing_error_count());
138 
139   pairing_phase()->Abort(ErrorCode::kDHKeyCheckFailed);
140   RunUntilIdle();
141 
142   // Check the PairingFailed message was sent to the channel
143   ASSERT_TRUE(msg_sent);
144   auto reader = PacketReader(msg_sent.get());
145   ASSERT_EQ(ErrorCode::kDHKeyCheckFailed, reader.payload<ErrorCode>());
146 
147   // Check the listener PairingFailed callback was made.
148   ASSERT_EQ(1, listener()->pairing_error_count());
149   EXPECT_EQ(Error(ErrorCode::kDHKeyCheckFailed), listener()->last_error());
150 }
151 
152 }  // namespace
153 }  // namespace bt::sm
154