Skip to content

Commit 0fb9893

Browse files
[Bugfix] make RevokeCommissioning tear down PASESession (project-chip#41715)
* 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 0ac3ca8 commit 0fb9893

File tree

4 files changed

+330
-6
lines changed

4 files changed

+330
-6
lines changed

src/app/server/CommissioningWindowManager.cpp

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

139139
DeviceLayer::SystemLayer().CancelTimer(HandleCommissioningWindowTimeout, this);
140140
mCommissioningTimeoutTimerArmed = false;
141-
142-
DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
143141
}
144142

145143
void CommissioningWindowManager::Cleanup()
@@ -212,8 +210,6 @@ void CommissioningWindowManager::OnSessionEstablished(const SessionHandle & sess
212210
mAppDelegate->OnCommissioningSessionStarted();
213211
}
214212

215-
TEMPORARY_RETURN_IGNORED DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
216-
217213
TEMPORARY_RETURN_IGNORED StopAdvertisement(/* aShuttingDown = */ false);
218214

219215
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
@@ -244,6 +240,8 @@ void CommissioningWindowManager::OnSessionEstablished(const SessionHandle & sess
244240
// When the now-armed fail-safe is disarmed or expires it will handle
245241
// clearing out mPASESession.
246242
mPASESession.Grab(session);
243+
TEMPORARY_RETURN_IGNORED DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEventWrapper,
244+
reinterpret_cast<intptr_t>(this));
247245
}
248246
}
249247

@@ -591,6 +589,8 @@ void CommissioningWindowManager::OnSessionReleased()
591589
// session, since we arm it when the PASE session is set up, and anything
592590
// that disarms the fail-safe would also tear down the PASE session.
593591
ExpireFailSafeIfArmed();
592+
593+
DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEventWrapper, reinterpret_cast<intptr_t>(this));
594594
}
595595

596596
void CommissioningWindowManager::ExpireFailSafeIfArmed()

src/app/server/CommissioningWindowManager.h

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

133+
Optional<SessionHandle> GetPASESession() const { return mPASESession.Get(); }
134+
133135
private:
134136
//////////// SessionDelegate Implementation ///////////////
135137
void OnSessionReleased() override;

src/app/tests/TestCommissioningWindowManager.cpp

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,37 @@
3636
#include <lib/support/tests/ExtraPwTestMacros.h>
3737
#include <pw_unit_test/framework.h>
3838

39+
#include <lib/support/UnitTestUtils.h>
40+
#include <messaging/tests/MessagingContext.h>
41+
42+
using namespace chip;
3943
using namespace chip::Crypto;
44+
using namespace chip::Messaging;
45+
using namespace System::Clock::Literals;
4046

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

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

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

@@ -152,7 +189,9 @@ class TestCommissioningWindowManager : public ::testing::Test
152189
RETURN_SAFELY_IGNORED mdnsAdvertiser.RemoveServices();
153190
mdnsAdvertiser.Shutdown();
154191

155-
// Server shudown will be called in TearDownTask
192+
LoopbackMessagingContext::TearDownTestSuite();
193+
194+
// Server shutdown will be called in TearDownTask
156195

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

168284
void CheckCommissioningWindowManagerBasicWindowOpenCloseTask(intptr_t context)
@@ -409,4 +525,72 @@ TEST_F(TestCommissioningWindowManager, TestCheckCommissioningWindowManagerEnhanc
409525
chip::DeviceLayer::PlatformMgr().RunEventLoop();
410526
}
411527

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

0 commit comments

Comments
 (0)