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 ;
3943using namespace chip ::Crypto;
44+ using namespace chip ::Messaging;
45+ using namespace System ::Clock::Literals;
4046
4147using chip::CommissioningWindowAdvertisement;
4248using chip::CommissioningWindowManager;
4349using chip::Server;
4450
4551namespace {
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+
4670bool sAdminFabricIndexDirty = false ;
4771bool sAdminVendorIdDirty = false ;
4872bool 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{
116151public:
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
168284void 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