blob: 188f51aee6a6094eb58d5d0de483943a80ff05bc [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app/TestEventTriggerDelegate.h>
#include <app/TimerDelegates.h>
#include <app/reporting/ReportSchedulerImpl.h>
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
#include <crypto/RandUtils.h>
#include <lib/dnssd/Advertiser.h>
#include <lib/support/Span.h>
#include <lib/support/UnitTestRegistration.h>
#include <messaging/tests/echo/common.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/CommissionableDataProvider.h>
#include <platform/ConfigurationManager.h>
#include <platform/PlatformManager.h>
#include <platform/TestOnlyCommissionableDataProvider.h>
#include <protocols/secure_channel/PASESession.h>
#include <nlunit-test.h>
using namespace chip::Crypto;
using chip::CommissioningWindowAdvertisement;
using chip::CommissioningWindowManager;
using chip::Server;
// Mock function for linking
void InitDataModelHandler() {}
namespace {
bool sAdminFabricIndexDirty = false;
bool sAdminVendorIdDirty = false;
bool sWindowStatusDirty = false;
void ResetDirtyFlags()
{
sAdminFabricIndexDirty = false;
sAdminVendorIdDirty = false;
sWindowStatusDirty = false;
}
} // namespace
void MatterReportingAttributeChangeCallback(chip::EndpointId endpoint, chip::ClusterId clusterId, chip::AttributeId attributeId)
{
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::AdministratorCommissioning::Attributes;
if (endpoint != chip::kRootEndpointId || clusterId != AdministratorCommissioning::Id)
{
return;
}
switch (attributeId)
{
case WindowStatus::Id:
sWindowStatusDirty = true;
break;
case AdminVendorId::Id:
sAdminVendorIdDirty = true;
break;
case AdminFabricIndex::Id:
sAdminFabricIndexDirty = true;
break;
default:
break;
}
}
namespace {
static constexpr int kTestTaskWaitSeconds = 2;
void InitializeChip(nlTestSuite * suite)
{
CHIP_ERROR err = CHIP_NO_ERROR;
err = chip::Platform::MemoryInit();
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
err = chip::DeviceLayer::PlatformMgr().InitChipStack();
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
static chip::DeviceLayer::TestOnlyCommissionableDataProvider commissionableDataProvider;
chip::DeviceLayer::SetCommissionableDataProvider(&commissionableDataProvider);
static chip::CommonCaseDeviceServerInitParams initParams;
// Report scheduler and timer delegate instance
static chip::app::DefaultTimerDelegate sTimerDelegate;
static chip::app::reporting::ReportSchedulerImpl sReportScheduler(&sTimerDelegate);
initParams.reportScheduler = &sReportScheduler;
static chip::SimpleTestEventTriggerDelegate sSimpleTestEventTriggerDelegate;
initParams.testEventTriggerDelegate = &sSimpleTestEventTriggerDelegate;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
// Set a randomized server port(slightly shifted from CHIP_PORT) for testing
initParams.operationalServicePort = static_cast<uint16_t>(initParams.operationalServicePort + chip::Crypto::GetRandU16() % 20);
err = chip::Server::GetInstance().Init(initParams);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
Server::GetInstance().GetCommissioningWindowManager().CloseCommissioningWindow();
chip::DeviceLayer::PlatformMgr().StartEventLoopTask();
}
void ShutdownChipTest()
{
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
chip::DeviceLayer::PlatformMgr().Shutdown();
auto & mdnsAdvertiser = chip::Dnssd::ServiceAdvertiser::Instance();
mdnsAdvertiser.RemoveServices();
mdnsAdvertiser.Shutdown();
// Server shudown will be called in TearDownTask
// TODO: At this point UDP endpoits still seem leaked and the sanitizer
// builds will attempt a memory free. As a result, we keep Memory initialized
// so that the global UDPManager can still be destructed without a coredump.
//
// This is likely either a missing shutdown or an actual UDP endpoint leak
// which I have not been able to track down yet.
//
// chip::Platform::MemoryShutdown();
}
void CheckCommissioningWindowManagerBasicWindowOpenCloseTask(intptr_t context)
{
nlTestSuite * suite = reinterpret_cast<nlTestSuite *>(context);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(commissionMgr.MaxCommissioningTimeout(),
CommissioningWindowAdvertisement::kDnssdOnly);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerFabricIndex().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerVendorId().IsNull());
NL_TEST_ASSERT(suite, !chip::DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled());
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
commissionMgr.CloseCommissioningWindow();
NL_TEST_ASSERT(suite, !commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
}
void CheckCommissioningWindowManagerBasicWindowOpenClose(nlTestSuite * suite, void *)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(CheckCommissioningWindowManagerBasicWindowOpenCloseTask,
reinterpret_cast<intptr_t>(suite));
sleep(kTestTaskWaitSeconds);
}
void CheckCommissioningWindowManagerBasicWindowOpenCloseFromClusterTask(intptr_t context)
{
nlTestSuite * suite = reinterpret_cast<nlTestSuite *>(context);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
constexpr auto fabricIndex = static_cast<chip::FabricIndex>(1);
constexpr auto vendorId = static_cast<chip::VendorId>(0xFFF3);
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindowForAdministratorCommissioningCluster(
commissionMgr.MaxCommissioningTimeout(), fabricIndex, vendorId);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kBasicWindowOpen);
NL_TEST_ASSERT(suite, !commissionMgr.GetOpenerFabricIndex().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerFabricIndex().Value() == fabricIndex);
NL_TEST_ASSERT(suite, !commissionMgr.GetOpenerVendorId().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerVendorId().Value() == vendorId);
NL_TEST_ASSERT(suite, !chip::DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled());
NL_TEST_ASSERT(suite, sWindowStatusDirty);
NL_TEST_ASSERT(suite, sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, sAdminVendorIdDirty);
ResetDirtyFlags();
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
commissionMgr.CloseCommissioningWindow();
NL_TEST_ASSERT(suite, !commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerFabricIndex().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerVendorId().IsNull());
NL_TEST_ASSERT(suite, sWindowStatusDirty);
NL_TEST_ASSERT(suite, sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, sAdminVendorIdDirty);
ResetDirtyFlags();
}
void CheckCommissioningWindowManagerBasicWindowOpenCloseFromCluster(nlTestSuite * suite, void *)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(CheckCommissioningWindowManagerBasicWindowOpenCloseFromClusterTask,
reinterpret_cast<intptr_t>(suite));
sleep(kTestTaskWaitSeconds);
}
void CheckCommissioningWindowManagerWindowClosedTask(chip::System::Layer *, void * context)
{
nlTestSuite * suite = static_cast<nlTestSuite *>(context);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
NL_TEST_ASSERT(suite, !commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
}
void CheckCommissioningWindowManagerWindowTimeoutTask(intptr_t context)
{
nlTestSuite * suite = reinterpret_cast<nlTestSuite *>(context);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds32(1);
constexpr uint16_t kTimeoutMs = 1000;
constexpr unsigned kSleepPadding = 100;
commissionMgr.OverrideMinCommissioningTimeout(kTimeoutSeconds);
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds, CommissioningWindowAdvertisement::kDnssdOnly);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, !chip::DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled());
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(kTimeoutMs + kSleepPadding),
CheckCommissioningWindowManagerWindowClosedTask, suite);
}
void CheckCommissioningWindowManagerWindowTimeout(nlTestSuite * suite, void *)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(CheckCommissioningWindowManagerWindowTimeoutTask,
reinterpret_cast<intptr_t>(suite));
sleep(kTestTaskWaitSeconds);
}
void SimulateFailedSessionEstablishmentTask(chip::System::Layer *, void * context)
{
nlTestSuite * suite = static_cast<nlTestSuite *>(context);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
commissionMgr.OnSessionEstablishmentStarted();
commissionMgr.OnSessionEstablishmentError(CHIP_ERROR_INTERNAL);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
}
void CheckCommissioningWindowManagerWindowTimeoutWithSessionEstablishmentErrorsTask(intptr_t context)
{
nlTestSuite * suite = reinterpret_cast<nlTestSuite *>(context);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(1);
constexpr uint16_t kTimeoutMs = 1000;
constexpr unsigned kSleepPadding = 100;
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds, CommissioningWindowAdvertisement::kDnssdOnly);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, !chip::DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled());
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(kTimeoutMs + kSleepPadding),
CheckCommissioningWindowManagerWindowClosedTask, suite);
// Simulate a session establishment error during that window, such that the
// delay for the error plus the window size exceeds our "timeout + padding" above.
chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(kTimeoutMs / 4 * 3),
SimulateFailedSessionEstablishmentTask, suite);
}
void CheckCommissioningWindowManagerWindowTimeoutWithSessionEstablishmentErrors(nlTestSuite * suite, void *)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(CheckCommissioningWindowManagerWindowTimeoutWithSessionEstablishmentErrorsTask,
reinterpret_cast<intptr_t>(suite));
sleep(kTestTaskWaitSeconds);
}
void CheckCommissioningWindowManagerEnhancedWindowTask(intptr_t context)
{
nlTestSuite * suite = reinterpret_cast<nlTestSuite *>(context);
CommissioningWindowManager & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
uint16_t originDiscriminator;
CHIP_ERROR err = chip::DeviceLayer::GetCommissionableDataProvider()->GetSetupDiscriminator(originDiscriminator);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
uint16_t newDiscriminator = static_cast<uint16_t>(originDiscriminator + 1);
Spake2pVerifier verifier;
constexpr uint32_t kIterations = kSpake2p_Min_PBKDF_Iterations;
uint8_t salt[kSpake2p_Min_PBKDF_Salt_Length];
chip::ByteSpan saltData(salt);
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
constexpr auto fabricIndex = static_cast<chip::FabricIndex>(1);
constexpr auto vendorId = static_cast<chip::VendorId>(0xFFF3);
err = commissionMgr.OpenEnhancedCommissioningWindow(commissionMgr.MaxCommissioningTimeout(), newDiscriminator, verifier,
kIterations, saltData, fabricIndex, vendorId);
NL_TEST_ASSERT(suite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(suite, commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kEnhancedWindowOpen);
NL_TEST_ASSERT(suite, !chip::DeviceLayer::ConnectivityMgr().IsBLEAdvertisingEnabled());
NL_TEST_ASSERT(suite, !commissionMgr.GetOpenerFabricIndex().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerFabricIndex().Value() == fabricIndex);
NL_TEST_ASSERT(suite, !commissionMgr.GetOpenerVendorId().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerVendorId().Value() == vendorId);
NL_TEST_ASSERT(suite, sWindowStatusDirty);
NL_TEST_ASSERT(suite, sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, sAdminVendorIdDirty);
ResetDirtyFlags();
NL_TEST_ASSERT(suite, !sWindowStatusDirty);
NL_TEST_ASSERT(suite, !sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, !sAdminVendorIdDirty);
commissionMgr.CloseCommissioningWindow();
NL_TEST_ASSERT(suite, !commissionMgr.IsCommissioningWindowOpen());
NL_TEST_ASSERT(suite,
commissionMgr.CommissioningWindowStatusForCluster() ==
chip::app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerFabricIndex().IsNull());
NL_TEST_ASSERT(suite, commissionMgr.GetOpenerVendorId().IsNull());
NL_TEST_ASSERT(suite, sWindowStatusDirty);
NL_TEST_ASSERT(suite, sAdminFabricIndexDirty);
NL_TEST_ASSERT(suite, sAdminVendorIdDirty);
ResetDirtyFlags();
}
void CheckCommissioningWindowManagerEnhancedWindow(nlTestSuite * suite, void *)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(CheckCommissioningWindowManagerEnhancedWindowTask,
reinterpret_cast<intptr_t>(suite));
sleep(kTestTaskWaitSeconds);
}
void TearDownTask(intptr_t context)
{
chip::Server::GetInstance().Shutdown();
}
const nlTest sTests[] = {
NL_TEST_DEF("CheckCommissioningWindowManagerEnhancedWindow", CheckCommissioningWindowManagerEnhancedWindow),
NL_TEST_DEF("CheckCommissioningWindowManagerBasicWindowOpenClose", CheckCommissioningWindowManagerBasicWindowOpenClose),
NL_TEST_DEF("CheckCommissioningWindowManagerBasicWindowOpenCloseFromCluster",
CheckCommissioningWindowManagerBasicWindowOpenCloseFromCluster),
NL_TEST_DEF("CheckCommissioningWindowManagerWindowTimeout", CheckCommissioningWindowManagerWindowTimeout),
NL_TEST_DEF("CheckCommissioningWindowManagerWindowTimeoutWithSessionEstablishmentErrors",
CheckCommissioningWindowManagerWindowTimeoutWithSessionEstablishmentErrors),
NL_TEST_SENTINEL(),
};
} // namespace
int TestCommissioningWindowManager()
{
// clang-format off
nlTestSuite theSuite =
{
"CommissioningWindowManager",
&sTests[0],
nullptr,
nullptr
};
// clang-format on
InitializeChip(&theSuite);
nlTestRunner(&theSuite, nullptr);
// TODO: The platform memory was intentionally left not deinitialized so that minimal mdns can destruct
chip::DeviceLayer::PlatformMgr().ScheduleWork(TearDownTask, 0);
sleep(kTestTaskWaitSeconds);
ShutdownChipTest();
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestCommissioningWindowManager)