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 ;
3842using namespace chip ::Crypto;
43+ using namespace chip ::Messaging;
44+ using namespace System ::Clock::Literals;
3945
4046using chip::CommissioningWindowAdvertisement;
4147using chip::CommissioningWindowManager;
4248using chip::Server;
4349
4450namespace {
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+
4569bool sAdminFabricIndexDirty = false ;
4670bool sAdminVendorIdDirty = false ;
4771bool 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{
115150public:
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
167283void 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