Skip to content

Commit f5fdddf

Browse files
[Bugfix] make RevokeCommissioning tear down PASESession (project-chip#41715) (project-chip#42018)
* Adding callback to FailSafeContext that is called after the failsafe timer is disarmed * rregister a callback to close commissioning window after fail-safe is disarmed when RevokeCommissioning is invoked * Adding Integration Test: Ensure PASE is cleared when Revoke Commissioning is invoked * Adding Unit Test to make sure RevokeCommissioning properly clears PASESession * RevokeCommissioning: Stop calling ForceFailSafeTimerExpiry if FailSafe is not armed * changing fix approach: modify the locations of AddEventHAndler and RemoveEventHandler for CommissioningWindowManager * Restyled by whitespace * Restyled by clang-format * Fix Integration test * Fix integration test and add a case * Restyled by autopep8 * Integrating comments * integrating comments --------- Co-authored-by: Restyled.io <[email protected]>
1 parent 2960abc commit f5fdddf

File tree

4 files changed

+329
-6
lines changed

4 files changed

+329
-6
lines changed

src/app/server/CommissioningWindowManager.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ void CommissioningWindowManager::ResetState()
134134

135135
DeviceLayer::SystemLayer().CancelTimer(HandleCommissioningWindowTimeout, this);
136136
mCommissioningTimeoutTimerArmed = false;
137-
138-
DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
139137
}
140138

141139
void CommissioningWindowManager::Cleanup()
@@ -207,8 +205,6 @@ void CommissioningWindowManager::OnSessionEstablished(const SessionHandle & sess
207205
mAppDelegate->OnCommissioningSessionStarted();
208206
}
209207

210-
DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
211-
212208
StopAdvertisement(/* aShuttingDown = */ false);
213209

214210
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
@@ -239,6 +235,7 @@ void CommissioningWindowManager::OnSessionEstablished(const SessionHandle & sess
239235
// When the now-armed fail-safe is disarmed or expires it will handle
240236
// clearing out mPASESession.
241237
mPASESession.Grab(session);
238+
DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
242239
}
243240
}
244241

@@ -566,6 +563,8 @@ void CommissioningWindowManager::OnSessionReleased()
566563
// session, since we arm it when the PASE session is set up, and anything
567564
// that disarms the fail-safe would also tear down the PASE session.
568565
ExpireFailSafeIfArmed();
566+
567+
DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
569568
}
570569

571570
void CommissioningWindowManager::ExpireFailSafeIfArmed()

src/app/server/CommissioningWindowManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
121121
// commissioning window timeout.
122122
void OverrideMinCommissioningTimeout(System::Clock::Seconds32 timeout) { mMinCommissioningTimeoutOverride.SetValue(timeout); }
123123

124+
Optional<SessionHandle> GetPASESession() const { return mPASESession.Get(); }
125+
124126
private:
125127
//////////// SessionDelegate Implementation ///////////////
126128
void OnSessionReleased() override;

src/app/tests/TestCommissioningWindowManager.cpp

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,37 @@
3535
#include <lib/core/StringBuilderAdapters.h>
3636
#include <pw_unit_test/framework.h>
3737

38+
#include <lib/support/UnitTestUtils.h>
39+
#include <messaging/tests/MessagingContext.h>
40+
41+
using namespace chip;
3842
using namespace chip::Crypto;
43+
using namespace chip::Messaging;
44+
using namespace System::Clock::Literals;
3945

4046
using chip::CommissioningWindowAdvertisement;
4147
using chip::CommissioningWindowManager;
4248
using chip::Server;
4349

4450
namespace {
51+
52+
// Test Set #01 of Spake2p Parameters (PIN Code, Iteration Count, Salt, and matching Verifier):
53+
constexpr uint32_t sTestSpake2p01_PinCode = 20202021;
54+
constexpr uint32_t sTestSpake2p01_IterationCount = 1000;
55+
constexpr uint8_t sTestSpake2p01_Salt[] = { 0x53, 0x50, 0x41, 0x4B, 0x45, 0x32, 0x50, 0x20,
56+
0x4B, 0x65, 0x79, 0x20, 0x53, 0x61, 0x6C, 0x74 };
57+
Spake2pVerifier sTestSpake2p01_PASEVerifier = { .mW0 = {
58+
0xB9, 0x61, 0x70, 0xAA, 0xE8, 0x03, 0x34, 0x68, 0x84, 0x72, 0x4F, 0xE9, 0xA3, 0xB2, 0x87, 0xC3,
59+
0x03, 0x30, 0xC2, 0xA6, 0x60, 0x37, 0x5D, 0x17, 0xBB, 0x20, 0x5A, 0x8C, 0xF1, 0xAE, 0xCB, 0x35,
60+
},
61+
.mL = {
62+
0x04, 0x57, 0xF8, 0xAB, 0x79, 0xEE, 0x25, 0x3A, 0xB6, 0xA8, 0xE4, 0x6B, 0xB0, 0x9E, 0x54, 0x3A,
63+
0xE4, 0x22, 0x73, 0x6D, 0xE5, 0x01, 0xE3, 0xDB, 0x37, 0xD4, 0x41, 0xFE, 0x34, 0x49, 0x20, 0xD0,
64+
0x95, 0x48, 0xE4, 0xC1, 0x82, 0x40, 0x63, 0x0C, 0x4F, 0xF4, 0x91, 0x3C, 0x53, 0x51, 0x38, 0x39,
65+
0xB7, 0xC0, 0x7F, 0xCC, 0x06, 0x27, 0xA1, 0xB8, 0x57, 0x3A, 0x14, 0x9F, 0xCD, 0x1F, 0xA4, 0x66,
66+
0xCF
67+
} };
68+
4569
bool sAdminFabricIndexDirty = false;
4670
bool sAdminVendorIdDirty = false;
4771
bool sWindowStatusDirty = false;
@@ -110,11 +134,24 @@ static void StopEventLoop(intptr_t context)
110134
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
111135
}
112136

113-
class TestCommissioningWindowManager : public ::testing::Test
137+
class TestSecurePairingDelegate : public SessionEstablishmentDelegate
138+
{
139+
public:
140+
void OnSessionEstablishmentError(CHIP_ERROR error) override { mNumPairingErrors++; }
141+
142+
void OnSessionEstablished(const SessionHandle & session) override { mNumPairingComplete++; }
143+
144+
uint32_t mNumPairingErrors = 0;
145+
uint32_t mNumPairingComplete = 0;
146+
};
147+
148+
class TestCommissioningWindowManager : public chip::Test::LoopbackMessagingContext
114149
{
115150
public:
116151
static void SetUpTestSuite()
117152
{
153+
LoopbackMessagingContext::SetUpTestSuite();
154+
118155
ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
119156
ASSERT_EQ(chip::DeviceLayer::PlatformMgr().InitChipStack(), CHIP_NO_ERROR);
120157

@@ -151,7 +188,9 @@ class TestCommissioningWindowManager : public ::testing::Test
151188
mdnsAdvertiser.RemoveServices();
152189
mdnsAdvertiser.Shutdown();
153190

154-
// Server shudown will be called in TearDownTask
191+
LoopbackMessagingContext::TearDownTestSuite();
192+
193+
// Server shutdown will be called in TearDownTask
155194

156195
// TODO: At this point UDP endpoits still seem leaked and the sanitizer
157196
// builds will attempt a memory free. As a result, we keep Memory initialized
@@ -162,6 +201,83 @@ class TestCommissioningWindowManager : public ::testing::Test
162201
//
163202
// chip::Platform::MemoryShutdown();
164203
}
204+
205+
void SetUp() override
206+
{
207+
ConfigInitializeNodes(false);
208+
chip::Test::LoopbackMessagingContext::SetUp();
209+
}
210+
211+
void EstablishPASEHandshake(SessionManager & sessionManager, PASESession & pairingCommissioner,
212+
TestSecurePairingDelegate & delegateCommissioner);
213+
214+
void ServiceEvents();
215+
};
216+
217+
void TestCommissioningWindowManager::ServiceEvents()
218+
{
219+
DrainAndServiceIO();
220+
221+
chip::DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) -> void { chip::DeviceLayer::PlatformMgr().StopEventLoopTask(); },
222+
(intptr_t) nullptr);
223+
chip::DeviceLayer::PlatformMgr().RunEventLoop();
224+
}
225+
226+
void TestCommissioningWindowManager::EstablishPASEHandshake(SessionManager & sessionManager, PASESession & pairingCommissioner,
227+
TestSecurePairingDelegate & delegateCommissioner)
228+
{
229+
230+
auto & loopback = GetLoopback();
231+
loopback.mSentMessageCount = 0;
232+
233+
ExchangeContext * contextCommissioner = NewUnauthenticatedExchangeToBob(&pairingCommissioner);
234+
235+
EXPECT_EQ(GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::PBKDFParamRequest,
236+
&Server::GetInstance().GetCommissioningWindowManager()),
237+
CHIP_NO_ERROR);
238+
239+
DrainAndServiceIO();
240+
241+
EXPECT_EQ(pairingCommissioner.Pair(sessionManager, sTestSpake2p01_PinCode, Optional<ReliableMessageProtocolConfig>::Missing(),
242+
contextCommissioner, &delegateCommissioner),
243+
CHIP_NO_ERROR);
244+
DrainAndServiceIO();
245+
246+
EXPECT_EQ(delegateCommissioner.mNumPairingErrors, 0u);
247+
EXPECT_EQ(delegateCommissioner.mNumPairingComplete, 1u);
248+
}
249+
250+
class TemporarySessionManager
251+
{
252+
public:
253+
TemporarySessionManager(TestCommissioningWindowManager & ctx) : mCtx(ctx)
254+
{
255+
EXPECT_EQ(CHIP_NO_ERROR,
256+
mSessionManager.Init(&ctx.GetSystemLayer(), &ctx.GetTransportMgr(), &ctx.GetMessageCounterManager(), &mStorage,
257+
&ctx.GetFabricTable(), ctx.GetSessionKeystore()));
258+
// The setup here is really weird: we are using one session manager for
259+
// the actual messages we send (the PASE handshake, so the
260+
// unauthenticated sessions) and a different one for allocating the PASE
261+
// sessions. Since our Init() set us up as the thing to handle messages
262+
// on the transport manager, undo that.
263+
mCtx.GetTransportMgr().SetSessionManager(&mCtx.GetSecureSessionManager());
264+
}
265+
266+
~TemporarySessionManager()
267+
{
268+
mSessionManager.Shutdown();
269+
// Reset the session manager on the transport again, just in case
270+
// shutdown messed with it.
271+
mCtx.GetTransportMgr().SetSessionManager(&mCtx.GetSecureSessionManager());
272+
}
273+
274+
operator SessionManager &() { return mSessionManager; }
275+
276+
TestCommissioningWindowManager & mCtx;
277+
278+
private:
279+
TestPersistentStorageDelegate mStorage;
280+
SessionManager mSessionManager;
165281
};
166282

167283
void CheckCommissioningWindowManagerBasicWindowOpenCloseTask(intptr_t context)
@@ -406,4 +522,72 @@ TEST_F(TestCommissioningWindowManager, TestCheckCommissioningWindowManagerEnhanc
406522
chip::DeviceLayer::PlatformMgr().RunEventLoop();
407523
}
408524

525+
void RevokeCommissioningCommandEquivalent()
526+
{
527+
Server::GetInstance().GetFailSafeContext().ForceFailSafeTimerExpiry();
528+
529+
if (!Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen())
530+
{
531+
ChipLogError(Zcl, "Commissioning window is currently not open");
532+
return;
533+
}
534+
535+
Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow();
536+
ChipLogProgress(Zcl, "Commissioning window is now closed");
537+
}
538+
539+
TEST_F(TestCommissioningWindowManager, SecurePairingHandshakeTestECM)
540+
{
541+
TemporarySessionManager sessionManager(*this);
542+
543+
TestSecurePairingDelegate delegateCommissioner;
544+
PASESession pairingCommissioner;
545+
auto & loopback = GetLoopback();
546+
loopback.Reset();
547+
548+
// Open an Enhanced Commissioning Window
549+
uint16_t originDiscriminator;
550+
EXPECT_EQ(chip::DeviceLayer::GetCommissionableDataProvider()->GetSetupDiscriminator(originDiscriminator), CHIP_NO_ERROR);
551+
uint16_t newDiscriminator = static_cast<uint16_t>(originDiscriminator + 1);
552+
553+
constexpr auto fabricIndex = static_cast<chip::FabricIndex>(1);
554+
constexpr auto vendorId = static_cast<chip::VendorId>(0xFFF3);
555+
556+
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
557+
EXPECT_EQ(commissionMgr.OpenEnhancedCommissioningWindow(commissionMgr.MaxCommissioningTimeout(), newDiscriminator,
558+
sTestSpake2p01_PASEVerifier, sTestSpake2p01_IterationCount,
559+
ByteSpan(sTestSpake2p01_Salt), fabricIndex, vendorId),
560+
CHIP_NO_ERROR);
561+
562+
EXPECT_TRUE(commissionMgr.IsCommissioningWindowOpen());
563+
564+
// Establish PASE Handshake to the server's CommissioningWindowManager
565+
EstablishPASEHandshake(sessionManager, pairingCommissioner, delegateCommissioner);
566+
567+
// Ensure that a PASE Session exists for pairingCommissioner
568+
auto commissionerSession = pairingCommissioner.CopySecureSession();
569+
EXPECT_TRUE(commissionerSession.HasValue());
570+
EXPECT_TRUE(commissionerSession.Value()->AsSecureSession()->IsPASESession());
571+
572+
// Ensure that a PASE Session exists for the CommissioningWindowManager
573+
EXPECT_TRUE(commissionMgr.GetPASESession().HasValue());
574+
EXPECT_TRUE(commissionMgr.GetPASESession().Value()->AsSecureSession()->IsPASESession());
575+
576+
// This is the equivalent of AdministratorCommissioningLogic::RevokeCommissioning() in the AdministratorCommissioning Cluster
577+
RevokeCommissioningCommandEquivalent();
578+
579+
// We need to service events here to allow the Async Events to be processed and make sure that the CommissioningWindowManager
580+
// successfully shutdown the PASESession
581+
ServiceEvents();
582+
583+
EXPECT_FALSE(commissionMgr.IsCommissioningWindowOpen());
584+
585+
// This asserts that the CommissioningWindowManager has cleared the PASESession
586+
EXPECT_FALSE(commissionMgr.GetPASESession().HasValue());
587+
588+
// Asserting that PASESession is still present on the Commissioner side
589+
commissionerSession = pairingCommissioner.CopySecureSession();
590+
EXPECT_TRUE(commissionerSession.HasValue());
591+
}
592+
409593
} // namespace

0 commit comments

Comments
 (0)