diff --git a/examples/platform/esp32/common/CHIPDeviceManager.cpp b/examples/platform/esp32/common/CHIPDeviceManager.cpp index 6ad588cdc49ebd..037ca40f2c0c3b 100644 --- a/examples/platform/esp32/common/CHIPDeviceManager.cpp +++ b/examples/platform/esp32/common/CHIPDeviceManager.cpp @@ -79,6 +79,9 @@ CHIP_ERROR CHIPDeviceManager::Init(CHIPDeviceManagerCallbacks * cb) // this function will happen on the CHIP event loop thread, not the app_main thread. PlatformMgr().AddEventHandler(CHIPDeviceManager::CommonDeviceEventHandler, reinterpret_cast(cb)); + // Start a task to run the CHIP Device background event loop. + ReturnErrorOnFailure(PlatformMgr().StartBackgroundEventLoopTask()); + // Start a task to run the CHIP Device event loop. return PlatformMgr().StartEventLoopTask(); } diff --git a/src/credentials/FabricTable.cpp b/src/credentials/FabricTable.cpp index 52a866b4f16851..63f7afebd7d0ba 100644 --- a/src/credentials/FabricTable.cpp +++ b/src/credentials/FabricTable.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace chip { using namespace Credentials; @@ -342,6 +343,7 @@ CHIP_ERROR FabricTable::VerifyCredentials(FabricIndex fabricIndex, const ByteSpa FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey, Crypto::P256PublicKey * outRootPublicKey) const { + assertChipStackLockedByCurrentThread(); uint8_t rootCertBuf[kMaxCHIPCertLength]; MutableByteSpan rootCertSpan{ rootCertBuf }; ReturnErrorOnFailure(FetchRootCert(fabricIndex, rootCertSpan)); diff --git a/src/credentials/FabricTable.h b/src/credentials/FabricTable.h index 7389efe79c8fb5..42f88024b877c6 100644 --- a/src/credentials/FabricTable.h +++ b/src/credentials/FabricTable.h @@ -912,13 +912,17 @@ class DLL_EXPORT FabricTable */ void RevertPendingOpCertsExceptRoot(); - // Verifies credentials, with the fabric's root under fabricIndex, and extract critical bits. - // This call is used for CASE. + // Verifies credentials, using the root certificate of the provided fabric index. CHIP_ERROR VerifyCredentials(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac, Credentials::ValidationContext & context, CompressedFabricId & outCompressedFabricId, FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey, Crypto::P256PublicKey * outRootPublicKey = nullptr) const; + // Verifies credentials, using the provided root certificate. + static CHIP_ERROR VerifyCredentials(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac, + Credentials::ValidationContext & context, CompressedFabricId & outCompressedFabricId, + FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey, + Crypto::P256PublicKey * outRootPublicKey = nullptr); /** * @brief Enables FabricInfo instances to collide and reference the same logical fabric (i.e Root Public Key + FabricId). * @@ -1093,13 +1097,6 @@ class DLL_EXPORT FabricTable mStateFlags.HasAll(StateFlags::kIsPendingFabricDataPresent, StateFlags::kIsUpdatePending); } - // Verifies credentials, using the provided root certificate. - // This call is done whenever a fabric is "directly" added - static CHIP_ERROR VerifyCredentials(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac, - Credentials::ValidationContext & context, CompressedFabricId & outCompressedFabricId, - FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey, - Crypto::P256PublicKey * outRootPublicKey); - // Validate an NOC chain at time of adding/updating a fabric (uses VerifyCredentials with additional checks). // The `existingFabricId` is passed for UpdateNOC, and must match the Fabric, to make sure that we are // not trying to change FabricID with UpdateNOC. If set to kUndefinedFabricId, we are doing AddNOC and diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h old mode 100755 new mode 100644 index 72cdf6e36c620d..7e87ee7615c360 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -79,7 +79,7 @@ * The priority of the chip task. */ #ifndef CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY -#define CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY 1 +#define CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY 2 #endif /** @@ -91,6 +91,51 @@ #define CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE 100 #endif +/** + * CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + * + * Enable support for background event processing. + */ +#ifndef CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING +#define CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING 0 +#endif + +/** + * CHIP_DEVICE_CONFIG_BG_TASK_NAME + * + * The name of the background task. + */ +#ifndef CHIP_DEVICE_CONFIG_BG_TASK_NAME +#define CHIP_DEVICE_CONFIG_BG_TASK_NAME "BG" +#endif + +/** + * CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE + * + * The size (in bytes) of the background task stack. + */ +#ifndef CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE +#define CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE (6 * 1024) +#endif + +/** + * CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY + * + * The priority of the background task. + */ +#ifndef CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY +#define CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY 1 +#endif + +/** + * CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE + * + * The maximum number of events that can be held in the chip background event queue. + */ +#ifndef CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE +#define CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE 1 +#endif + /** * CHIP_DEVICE_CONFIG_ENABLE_SED * @@ -648,7 +693,7 @@ * The priority of the OpenThread task. */ #ifndef CHIP_DEVICE_CONFIG_THREAD_TASK_PRIORITY -#define CHIP_DEVICE_CONFIG_THREAD_TASK_PRIORITY 2 +#define CHIP_DEVICE_CONFIG_THREAD_TASK_PRIORITY 3 #endif /** diff --git a/src/include/platform/CHIPDeviceLayer.h b/src/include/platform/CHIPDeviceLayer.h index 4e2eef9b558df1..5d4348cc269948 100644 --- a/src/include/platform/CHIPDeviceLayer.h +++ b/src/include/platform/CHIPDeviceLayer.h @@ -41,7 +41,7 @@ namespace chip { namespace DeviceLayer { -void SetSystemLayerForTesting(System::LayerImpl * layer); +void SetSystemLayerForTesting(System::Layer * layer); // These functions are defined in src/platform/Globals.cpp chip::System::Layer & SystemLayer(); diff --git a/src/include/platform/PlatformManager.h b/src/include/platform/PlatformManager.h index 40d74c432b77b6..cc788f353be5ec 100644 --- a/src/include/platform/PlatformManager.h +++ b/src/include/platform/PlatformManager.h @@ -109,6 +109,7 @@ class PlatformManager * other. */ CHIP_ERROR InitChipStack(); + CHIP_ERROR AddEventHandler(EventHandlerFunct handler, intptr_t arg = 0); void RemoveEventHandler(EventHandlerFunct handler, intptr_t arg = 0); void SetDelegate(PlatformManagerDelegate * delegate) { mDelegate = delegate; } @@ -137,7 +138,7 @@ class PlatformManager * processing, the callback function may be called (on the work item * processing thread) before ScheduleWork returns. */ - void ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg = 0); + CHIP_ERROR ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg = 0); /** * Process work items until StopEventLoopTask is called. RunEventLoop will @@ -183,6 +184,7 @@ class PlatformManager * returns. */ CHIP_ERROR StopEventLoopTask(); + void LockChipStack(); bool TryLockChipStack(); void UnlockChipStack(); @@ -201,6 +203,47 @@ class PlatformManager [[nodiscard]] CHIP_ERROR PostEvent(const ChipDeviceEvent * event); void PostEventOrDie(const ChipDeviceEvent * event); + /** + * Generally this function has the same semantics as ScheduleWork + * except it applies to background processing. + * + * Delegates to PostBackgroundEvent (which will delegate to PostEvent if + * CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING is not true). + */ + CHIP_ERROR ScheduleBackgroundWork(AsyncWorkFunct workFunct, intptr_t arg = 0); + + /** + * Generally this function has the same semantics as PostEvent + * except it applies to background processing. + * + * If CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING is not true, will delegate + * to PostEvent. + * + * Only accepts events of type kCallWorkFunct or kNoOp. + * + * Returns CHIP_ERROR_INVALID_ARGUMENT if the event type is not acceptable. + * Returns CHIP_ERROR_NO_MEMORY if resources are exhausted. + */ + CHIP_ERROR PostBackgroundEvent(const ChipDeviceEvent * event); + + /** + * Generally this function has the same semantics as RunEventLoop + * except it applies to background processing. + */ + void RunBackgroundEventLoop(); + + /** + * Generally this function has the same semantics as StartEventLoopTask + * except it applies to background processing. + */ + CHIP_ERROR StartBackgroundEventLoopTask(); + + /** + * Generally this function has the same semantics as StopEventLoopTask + * except it applies to background processing. + */ + CHIP_ERROR StopBackgroundEventLoopTask(); + private: bool mInitialized = false; PlatformManagerDelegate * mDelegate = nullptr; @@ -352,9 +395,9 @@ inline void PlatformManager::HandleServerShuttingDown() static_cast(this)->_HandleServerShuttingDown(); } -inline void PlatformManager::ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg) +inline CHIP_ERROR PlatformManager::ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg) { - static_cast(this)->_ScheduleWork(workFunct, arg); + return static_cast(this)->_ScheduleWork(workFunct, arg); } inline void PlatformManager::RunEventLoop() @@ -432,6 +475,31 @@ inline void PlatformManager::PostEventOrDie(const ChipDeviceEvent * event) static_cast(event->Type), status.Format()); } +inline CHIP_ERROR PlatformManager::ScheduleBackgroundWork(AsyncWorkFunct workFunct, intptr_t arg) +{ + return static_cast(this)->_ScheduleBackgroundWork(workFunct, arg); +} + +inline CHIP_ERROR PlatformManager::PostBackgroundEvent(const ChipDeviceEvent * event) +{ + return static_cast(this)->_PostBackgroundEvent(event); +} + +inline void PlatformManager::RunBackgroundEventLoop() +{ + static_cast(this)->_RunBackgroundEventLoop(); +} + +inline CHIP_ERROR PlatformManager::StartBackgroundEventLoopTask() +{ + return static_cast(this)->_StartBackgroundEventLoopTask(); +} + +inline CHIP_ERROR PlatformManager::StopBackgroundEventLoopTask() +{ + return static_cast(this)->_StopBackgroundEventLoopTask(); +} + inline void PlatformManager::DispatchEvent(const ChipDeviceEvent * event) { static_cast(this)->_DispatchEvent(event); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl.h b/src/include/platform/internal/GenericPlatformManagerImpl.h index 73151747d9a347..ce98438820d1af 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl.h +++ b/src/include/platform/internal/GenericPlatformManagerImpl.h @@ -57,7 +57,12 @@ class GenericPlatformManagerImpl void _RemoveEventHandler(PlatformManager::EventHandlerFunct handler, intptr_t arg); void _HandleServerStarted(); void _HandleServerShuttingDown(); - void _ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg); + CHIP_ERROR _ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg); + CHIP_ERROR _ScheduleBackgroundWork(AsyncWorkFunct workFunct, intptr_t arg); + CHIP_ERROR _PostBackgroundEvent(const ChipDeviceEvent * event); + void _RunBackgroundEventLoop(void); + CHIP_ERROR _StartBackgroundEventLoopTask(void); + CHIP_ERROR _StopBackgroundEventLoopTask(); void _DispatchEvent(const ChipDeviceEvent * event); // ===== Support methods that can be overridden by the implementation subclass. diff --git a/src/include/platform/internal/GenericPlatformManagerImpl.ipp b/src/include/platform/internal/GenericPlatformManagerImpl.ipp index 4cee5dab0b5cd8..d6f29ed515e034 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl.ipp @@ -216,18 +216,56 @@ void GenericPlatformManagerImpl::_HandleServerShuttingDown() } template -void GenericPlatformManagerImpl::_ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg) +CHIP_ERROR GenericPlatformManagerImpl::_ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg) { - ChipDeviceEvent event; - event.Type = DeviceEventType::kCallWorkFunct; - event.CallWorkFunct.WorkFunct = workFunct; - event.CallWorkFunct.Arg = arg; + ChipDeviceEvent event{ .Type = DeviceEventType::kCallWorkFunct }; + event.CallWorkFunct = { .WorkFunct = workFunct, .Arg = arg }; + CHIP_ERROR err = Impl()->PostEvent(&event); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to schedule work: %" CHIP_ERROR_FORMAT, err.Format()); + } + return err; +} - CHIP_ERROR status = Impl()->PostEvent(&event); - if (status != CHIP_NO_ERROR) +template +CHIP_ERROR GenericPlatformManagerImpl::_ScheduleBackgroundWork(AsyncWorkFunct workFunct, intptr_t arg) +{ + ChipDeviceEvent event{ .Type = DeviceEventType::kCallWorkFunct }; + event.CallWorkFunct = { .WorkFunct = workFunct, .Arg = arg }; + CHIP_ERROR err = Impl()->PostBackgroundEvent(&event); + if (err != CHIP_NO_ERROR) { - ChipLogError(DeviceLayer, "Failed to schedule work: %" CHIP_ERROR_FORMAT, status.Format()); + ChipLogError(DeviceLayer, "Failed to schedule background work: %" CHIP_ERROR_FORMAT, err.Format()); } + return err; +} + +template +CHIP_ERROR GenericPlatformManagerImpl::_PostBackgroundEvent(const ChipDeviceEvent * event) +{ + // Impl class must override to implement background event processing + return Impl()->PostEvent(event); +} + +template +void GenericPlatformManagerImpl::_RunBackgroundEventLoop(void) +{ + // Impl class must override to implement background event processing +} + +template +CHIP_ERROR GenericPlatformManagerImpl::_StartBackgroundEventLoopTask(void) +{ + // Impl class must override to implement background event processing + return CHIP_NO_ERROR; +} + +template +CHIP_ERROR GenericPlatformManagerImpl::_StopBackgroundEventLoopTask(void) +{ + // Impl class must override to implement background event processing + return CHIP_NO_ERROR; } template diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.h b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.h index f1a400fc884efd..db9ec6f33a7d16 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.h +++ b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.h @@ -65,12 +65,19 @@ class GenericPlatformManagerImpl_FreeRTOS : public GenericPlatformManagerImpl mShouldRunEventLoop; + +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + static void BackgroundEventLoopTaskMain(void * arg); + +#if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE) && CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE + uint8_t mBackgroundQueueBuffer[CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE * sizeof(ChipDeviceEvent)]; + StaticQueue_t mBackgroundQueueStruct; +#endif #if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_TASK) && CHIP_CONFIG_FREERTOS_USE_STATIC_TASK - StackType_t mEventLoopStack[CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE / sizeof(StackType_t)]; - StaticTask_t mventLoopTaskStruct; + StackType_t mBackgroundEventLoopStack[CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE / sizeof(StackType_t)]; + StaticTask_t mBackgroundEventLoopTaskStruct; +#endif + + std::atomic mShouldRunBackgroundEventLoop; #endif #if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE) && CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.ipp b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.ipp index 710d1c63fe3e24..383e14fed3a680 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.ipp @@ -48,7 +48,10 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_InitChipStack(void) mNextTimerDurationTicks = 0; // TODO: This nulling out of mEventLoopTask should happen when we shut down // the task, not here! - mEventLoopTask = NULL; + mEventLoopTask = NULL; +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + mBackgroundEventLoopTask = NULL; +#endif mChipTimerActive = false; // We support calling Shutdown followed by InitChipStack, because some tests @@ -62,7 +65,7 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_InitChipStack(void) mChipStackLock = xSemaphoreCreateMutexStatic(&mChipStackLockMutex); #else mChipStackLock = xSemaphoreCreateMutex(); -#endif // CHIP_CONFIG_FREERTOS_USE_STATIC_SEMAPHORE +#endif if (mChipStackLock == NULL) { @@ -81,7 +84,7 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_InitChipStack(void) #endif if (mChipEventQueue == NULL) { - ChipLogError(DeviceLayer, "Failed to allocate CHIP event queue"); + ChipLogError(DeviceLayer, "Failed to allocate CHIP main event queue"); ExitNow(err = CHIP_ERROR_NO_MEMORY); } } @@ -94,6 +97,29 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_InitChipStack(void) mShouldRunEventLoop.store(false); +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + if (mBackgroundEventQueue == NULL) + { +#if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE) && CHIP_CONFIG_FREERTOS_USE_STATIC_QUEUE + mBackgroundEventQueue = xQueueCreateStatic(CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent), + mBackgroundQueueBuffer, &mBackgroundQueueStruct); +#else + mBackgroundEventQueue = xQueueCreate(CHIP_DEVICE_CONFIG_BG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent)); +#endif + if (mBackgroundEventQueue == NULL) + { + ChipLogError(DeviceLayer, "Failed to allocate CHIP background event queue"); + ExitNow(err = CHIP_ERROR_NO_MEMORY); + } + } + else + { + xQueueReset(mBackgroundEventQueue); + } + + mShouldRunBackgroundEventLoop.store(false); +#endif + // Call up to the base class _InitChipStack() to perform the bulk of the initialization. err = GenericPlatformManagerImpl::_InitChipStack(); SuccessOrExit(err); @@ -225,12 +251,10 @@ void GenericPlatformManagerImpl_FreeRTOS::_RunEventLoop(void) eventReceived = xQueueReceive(mChipEventQueue, &event, waitTime); } - // If an event was received, dispatch it. Continue receiving events from the queue and - // dispatching them until the queue is empty. + // If an event was received, dispatch it and continue until the queue is empty. while (eventReceived == pdTRUE) { Impl()->DispatchEvent(&event); - eventReceived = xQueueReceive(mChipEventQueue, &event, 0); } } @@ -241,25 +265,121 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_StartEventLoopTask(v { #if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_TASK) && CHIP_CONFIG_FREERTOS_USE_STATIC_TASK mEventLoopTask = xTaskCreateStatic(EventLoopTaskMain, CHIP_DEVICE_CONFIG_CHIP_TASK_NAME, ArraySize(mEventLoopStack), this, - CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY, mEventLoopStack, &mventLoopTaskStruct); + CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY, mEventLoopStack, &mEventLoopTaskStruct); #else xTaskCreate(EventLoopTaskMain, CHIP_DEVICE_CONFIG_CHIP_TASK_NAME, CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE / sizeof(StackType_t), this, CHIP_DEVICE_CONFIG_CHIP_TASK_PRIORITY, &mEventLoopTask); #endif - return (mEventLoopTask != NULL) ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY; } template void GenericPlatformManagerImpl_FreeRTOS::EventLoopTaskMain(void * arg) { - ChipLogDetail(DeviceLayer, "CHIP task running"); + ChipLogDetail(DeviceLayer, "CHIP event task running"); static_cast *>(arg)->Impl()->RunEventLoop(); // TODO: At this point, should we not // vTaskDelete(static_cast *>(arg)->mEventLoopTask)? // Or somehow get our caller to do it once this thread is joined? } +template +CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_PostBackgroundEvent(const ChipDeviceEvent * event) +{ +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + if (mBackgroundEventQueue == NULL) + { + return CHIP_ERROR_INTERNAL; + } + if (!(event->Type == DeviceEventType::kCallWorkFunct || event->Type == DeviceEventType::kNoOp)) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + auto status = xQueueSendToBack(mBackgroundEventQueue, event, 1); + if (status != pdTRUE) + { + ChipLogError(DeviceLayer, "Failed to post event to CHIP background event queue"); + return CHIP_ERROR_NO_MEMORY; + } + return CHIP_NO_ERROR; +#else + // Use foreground event loop for background events + return _PostEvent(event); +#endif +} + +template +void GenericPlatformManagerImpl_FreeRTOS::_RunBackgroundEventLoop(void) +{ +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + bool oldShouldRunBackgroundEventLoop = false; + if (!mShouldRunBackgroundEventLoop.compare_exchange_strong(oldShouldRunBackgroundEventLoop /* expected */, true /* desired */)) + { + ChipLogError(DeviceLayer, "Error trying to run the background event loop while it is already running"); + return; + } + + while (mShouldRunBackgroundEventLoop.load()) + { + ChipDeviceEvent event; + auto eventReceived = xQueueReceive(mBackgroundEventQueue, &event, portMAX_DELAY) == pdTRUE; + while (eventReceived) + { + Impl()->DispatchEvent(&event); + eventReceived = xQueueReceive(mBackgroundEventQueue, &event, portMAX_DELAY) == pdTRUE; + } + } +#else + // Use foreground event loop for background events +#endif +} + +template +CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_StartBackgroundEventLoopTask(void) +{ +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING +#if defined(CHIP_CONFIG_FREERTOS_USE_STATIC_TASK) && CHIP_CONFIG_FREERTOS_USE_STATIC_TASK + mBackgroundEventLoopTask = + xTaskCreateStatic(BackgroundEventLoopTaskMain, CHIP_DEVICE_CONFIG_BG_TASK_NAME, ArraySize(mBackgroundEventLoopStack), this, + CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY, mBackgroundEventLoopStack, &mBackgroundEventLoopTaskStruct); +#else + xTaskCreate(BackgroundEventLoopTaskMain, CHIP_DEVICE_CONFIG_BG_TASK_NAME, + CHIP_DEVICE_CONFIG_BG_TASK_STACK_SIZE / sizeof(StackType_t), this, CHIP_DEVICE_CONFIG_BG_TASK_PRIORITY, + &mBackgroundEventLoopTask); +#endif + return (mBackgroundEventLoopTask != NULL) ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY; +#else + // Use foreground event loop for background events + return CHIP_NO_ERROR; +#endif +} + +template +CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_StopBackgroundEventLoopTask(void) +{ +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING + bool oldShouldRunBackgroundEventLoop = true; + if (mShouldRunBackgroundEventLoop.compare_exchange_strong(oldShouldRunBackgroundEventLoop /* expected */, false /* desired */)) + { + ChipDeviceEvent noop{ .Type = DeviceEventType::kNoOp }; + xQueueSendToBack(mBackgroundEventQueue, &noop, 0); + } + return CHIP_NO_ERROR; +#else + // Use foreground event loop for background events + return CHIP_NO_ERROR; +#endif +} + +#if defined(CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING) && CHIP_DEVICE_CONFIG_ENABLE_BG_EVENT_PROCESSING +template +void GenericPlatformManagerImpl_FreeRTOS::BackgroundEventLoopTaskMain(void * arg) +{ + ChipLogDetail(DeviceLayer, "CHIP background task running"); + static_cast *>(arg)->Impl()->RunBackgroundEventLoop(); +} +#endif + template CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_StartChipTimer(System::Clock::Timeout delay) { @@ -272,9 +392,8 @@ CHIP_ERROR GenericPlatformManagerImpl_FreeRTOS::_StartChipTimer(Syste // to the event queue. if (xTaskGetCurrentTaskHandle() != mEventLoopTask) { - ChipDeviceEvent event; - event.Type = DeviceEventType::kNoOp; - ReturnErrorOnFailure(Impl()->PostEvent(&event)); + ChipDeviceEvent noop{ .Type = DeviceEventType::kNoOp }; + ReturnErrorOnFailure(Impl()->PostEvent(&noop)); } return CHIP_NO_ERROR; diff --git a/src/inet/tests/TestInetCommonPosix.cpp b/src/inet/tests/TestInetCommonPosix.cpp index d7820303a9a995..8a187600375e07 100644 --- a/src/inet/tests/TestInetCommonPosix.cpp +++ b/src/inet/tests/TestInetCommonPosix.cpp @@ -450,7 +450,7 @@ void ServiceEvents(uint32_t aSleepTimeMilliseconds) gSystemLayer.PrepareEvents(); gSystemLayer.WaitForEvents(); gSystemLayer.HandleEvents(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif #if CHIP_SYSTEM_CONFIG_USE_LWIP if (gSystemLayer.IsInitialized()) diff --git a/src/platform/Globals.cpp b/src/platform/Globals.cpp index b49e08ad739773..04bd9ce3fe273b 100644 --- a/src/platform/Globals.cpp +++ b/src/platform/Globals.cpp @@ -26,9 +26,9 @@ namespace DeviceLayer { chip::System::LayerImpl * gMockedSystemLayer = nullptr; -void SetSystemLayerForTesting(System::LayerImpl * layer) +void SetSystemLayerForTesting(System::Layer * layer) { - gMockedSystemLayer = layer; + gMockedSystemLayer = static_cast(layer); } chip::System::LayerImpl & SystemLayerImpl() diff --git a/src/platform/fake/PlatformManagerImpl.h b/src/platform/fake/PlatformManagerImpl.h index 47904d4d5c0c74..cf454d3c807c05 100644 --- a/src/platform/fake/PlatformManagerImpl.h +++ b/src/platform/fake/PlatformManagerImpl.h @@ -51,7 +51,13 @@ class PlatformManagerImpl final : public PlatformManager void _RemoveEventHandler(EventHandlerFunct handler, intptr_t arg = 0) {} void _HandleServerStarted() {} void _HandleServerShuttingDown() {} - void _ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg = 0) {} + + CHIP_ERROR _ScheduleWork(AsyncWorkFunct workFunct, intptr_t arg = 0) + { + ChipDeviceEvent event{ .Type = DeviceEventType::kCallWorkFunct }; + event.CallWorkFunct = { .WorkFunct = workFunct, .Arg = arg }; + return _PostEvent(&event); + } void _RunEventLoop() { @@ -89,15 +95,30 @@ class PlatformManagerImpl final : public PlatformManager { switch (event->Type) { + case DeviceEventType::kNoOp: + // Do nothing for no-op events. + break; + case DeviceEventType::kChipLambdaEvent: event->LambdaEvent(); break; + case DeviceEventType::kCallWorkFunct: + // If the event is a "call work function" event, call the specified function. + event->CallWorkFunct.WorkFunct(event->CallWorkFunct.Arg); + break; + default: break; } } + CHIP_ERROR _ScheduleBackgroundWork(AsyncWorkFunct workFunct, intptr_t arg) { return _ScheduleWork(workFunct, arg); } + CHIP_ERROR _PostBackgroundEvent(const ChipDeviceEvent * event) { return CHIP_ERROR_NOT_IMPLEMENTED; } + void _RunBackgroundEventLoop(void) {} + CHIP_ERROR _StartBackgroundEventLoopTask(void) { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR _StopBackgroundEventLoopTask() { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR _StartChipTimer(System::Clock::Timeout duration) { return CHIP_ERROR_NOT_IMPLEMENTED; } void _LockChipStack() {} diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 005e12d3bddc4c..7c00272155624e 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,14 @@ enum kTag_TBEData_ResumptionID = 4, }; +enum +{ + kTag_TBSData_SenderNOC = 1, + kTag_TBSData_SenderICAC = 2, + kTag_TBSData_SenderPubKey = 3, + kTag_TBSData_ReceiverPubKey = 4, +}; + enum { kTag_Sigma1_InitiatorRandom = 1, @@ -210,6 +219,11 @@ CASESession::PrepareForSessionEstablishment(SessionManager & sessionManager, Fab ReturnErrorOnFailure(Init(sessionManager, policy, delegate, previouslyEstablishedPeer)); CHIP_ERROR err = CHIP_NO_ERROR; + + // Sequence number used to coordinate foreground/background work for a + // particular session establishment. + mSequence++; + SuccessOrExit(err = fabricTable->AddFabricDelegate(this)); mFabricsTable = fabricTable; @@ -958,11 +972,19 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) uint16_t responderSessionId; + ChipLogProgress(SecureChannel, "Received Sigma2 msg"); + + FabricId fabricId = kUndefinedFabricId; + { + VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex); + VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + fabricId = fabricInfo->GetFabricId(); + } + VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - ChipLogProgress(SecureChannel, "Received Sigma2 msg"); - tlvReader.Init(std::move(msg)); SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); SuccessOrExit(err = tlvReader.EnterContainer(containerType)); @@ -1038,11 +1060,17 @@ CHIP_ERROR CASESession::HandleSigma2(System::PacketBufferHandle && msg) // Validate responder identity located in msg_r2_encrypted // Constructing responder identity - SuccessOrExit(err = ValidatePeerIdentity(responderNOC, responderICAC, responderNodeId, responderPublicKey)); - - // Verify that responderNodeId (from responderNOC) matches one that was included - // in the computation of the Destination Identifier when generating Sigma1. - VerifyOrExit(mPeerNodeId == responderNodeId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); + { + CompressedFabricId unused; + FabricId responderFabricId; + SuccessOrExit(err = SetEffectiveTime()); + SuccessOrExit(err = mFabricsTable->VerifyCredentials(mFabricIndex, responderNOC, responderICAC, mValidContext, unused, + responderFabricId, responderNodeId, responderPublicKey)); + VerifyOrExit(fabricId == responderFabricId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); + // Verify that responderNodeId (from responderNOC) matches one that was included + // in the computation of the Destination Identifier when generating Sigma1. + VerifyOrExit(mPeerNodeId == responderNodeId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); + } // Construct msg_R2_Signed and validate the signature in msg_r2_encrypted msg_r2_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), responderNOC.size(), responderICAC.size(), @@ -1235,11 +1263,39 @@ CHIP_ERROR CASESession::SendSigma3() return err; } -CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) +struct CASESession::Sigma3Work +{ + // Status of background processing. + CHIP_ERROR status; + + // Session to use after background processing. + CASESession * session; + + // Sequence number used to coordinate foreground/background work for a + // particular session establishment. + int sequence; + + chip::Platform::ScopedMemoryBuffer msg_R3_Signed; + size_t msg_r3_signed_len; + + ByteSpan initiatorNOC; + ByteSpan initiatorICAC; + + uint8_t rootCertBuf[kMaxCHIPCertLength]; + ByteSpan fabricRCAC; + + P256ECDSASignature tbsData3Signature; + + FabricId fabricId; + NodeId initiatorNodeId; + + ValidationContext validContext; +}; + +CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) { MATTER_TRACE_EVENT_SCOPE("HandleSigma3", "CASESession"); CHIP_ERROR err = CHIP_NO_ERROR; - MutableByteSpan messageDigestSpan(mMessageDigest); System::PacketBufferTLVReader tlvReader; TLV::TLVReader decryptedDataTlvReader; TLV::TLVType containerType = TLV::kTLVType_Structure; @@ -1253,101 +1309,169 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) size_t msg_r3_encrypted_len = 0; size_t msg_r3_encrypted_len_with_tag = 0; size_t max_msg_r3_signed_enc_len; - chip::Platform::ScopedMemoryBuffer msg_R3_Signed; - size_t msg_r3_signed_len; uint8_t sr3k[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; - P256ECDSASignature tbsData3Signature; + uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; - NodeId initiatorNodeId; - P256PublicKey initiatorPublicKey; + ChipLogProgress(SecureChannel, "Received Sigma3 msg"); - ByteSpan initiatorNOC; - ByteSpan initiatorICAC; + auto * workPtr = Platform::New(); + VerifyOrExit(workPtr != nullptr, err = CHIP_ERROR_NO_MEMORY); + { + auto & work = *workPtr; - uint8_t msg_salt[kIPKSize + kSHA256_Hash_Length]; + // Used to call back into the session after background event processing. + // It happens that there's only one pairing session (in CASEServer) + // so it will still be available for use. Use a sequence number to + // coordinate. + work.session = this; + work.sequence = mSequence; - ChipLogProgress(SecureChannel, "Received Sigma3 msg"); + { + VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex); + VerifyOrExit(fabricInfo != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + work.fabricId = fabricInfo->GetFabricId(); + } - VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); + VerifyOrExit(mEphemeralKey != nullptr, err = CHIP_ERROR_INTERNAL); - tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); + tlvReader.Init(std::move(msg)); + SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag())); + SuccessOrExit(err = tlvReader.EnterContainer(containerType)); - // Fetch encrypted data - max_msg_r3_signed_enc_len = TLV::EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, - tbsData3Signature.Length(), kCaseOverheadForFutureTbeData); + // Fetch encrypted data + max_msg_r3_signed_enc_len = TLV::EstimateStructOverhead(Credentials::kMaxCHIPCertLength, Credentials::kMaxCHIPCertLength, + work.tbsData3Signature.Length(), kCaseOverheadForFutureTbeData); - SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_Sigma3_Encrypted3))); + SuccessOrExit(err = tlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_Sigma3_Encrypted3))); - msg_r3_encrypted_len_with_tag = tlvReader.GetLength(); + msg_r3_encrypted_len_with_tag = tlvReader.GetLength(); - // Validate we did not receive a buffer larger than legal - VerifyOrExit(msg_r3_encrypted_len_with_tag <= max_msg_r3_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrExit(msg_r3_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + // Validate we did not receive a buffer larger than legal + VerifyOrExit(msg_r3_encrypted_len_with_tag <= max_msg_r3_signed_enc_len, err = CHIP_ERROR_INVALID_TLV_ELEMENT); + VerifyOrExit(msg_r3_encrypted_len_with_tag > CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, err = CHIP_ERROR_INVALID_TLV_ELEMENT); - VerifyOrExit(msg_R3_Encrypted.Alloc(msg_r3_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); - SuccessOrExit(err = tlvReader.GetBytes(msg_R3_Encrypted.Get(), static_cast(msg_r3_encrypted_len_with_tag))); - msg_r3_encrypted_len = msg_r3_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; + VerifyOrExit(msg_R3_Encrypted.Alloc(msg_r3_encrypted_len_with_tag), err = CHIP_ERROR_NO_MEMORY); + SuccessOrExit(err = tlvReader.GetBytes(msg_R3_Encrypted.Get(), static_cast(msg_r3_encrypted_len_with_tag))); + msg_r3_encrypted_len = msg_r3_encrypted_len_with_tag - CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES; - // Step 1 - { - MutableByteSpan saltSpan(msg_salt); - err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan); - SuccessOrExit(err); + // Step 1 + { + MutableByteSpan saltSpan(msg_salt); + err = ConstructSaltSigma3(ByteSpan(mIPK), saltSpan); + SuccessOrExit(err); + + HKDF_sha_crypto mHKDF; + err = mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), + kKDFSR3Info, kKDFInfoLength, sr3k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); + SuccessOrExit(err); + } - HKDF_sha_crypto mHKDF; - err = mHKDF.HKDF_SHA256(mSharedSecret.ConstBytes(), mSharedSecret.Length(), saltSpan.data(), saltSpan.size(), kKDFSR3Info, - kKDFInfoLength, sr3k, CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES); - SuccessOrExit(err); - } + SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ buf, bufLen })); + // Step 2 - Decrypt data blob + SuccessOrExit(err = AES_CCM_decrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, + msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, sr3k, + CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, kTBEData3_Nonce, kTBEDataNonceLength, + msg_R3_Encrypted.Get())); - // Step 2 - Decrypt data blob - SuccessOrExit(err = AES_CCM_decrypt(msg_R3_Encrypted.Get(), msg_r3_encrypted_len, nullptr, 0, - msg_R3_Encrypted.Get() + msg_r3_encrypted_len, CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES, sr3k, - CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, kTBEData3_Nonce, kTBEDataNonceLength, - msg_R3_Encrypted.Get())); + decryptedDataTlvReader.Init(msg_R3_Encrypted.Get(), msg_r3_encrypted_len); + containerType = TLV::kTLVType_Structure; + SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); + SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); - decryptedDataTlvReader.Init(msg_R3_Encrypted.Get(), msg_r3_encrypted_len); - containerType = TLV::kTLVType_Structure; - SuccessOrExit(err = decryptedDataTlvReader.Next(containerType, TLV::AnonymousTag())); - SuccessOrExit(err = decryptedDataTlvReader.EnterContainer(containerType)); + SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_SenderNOC))); + SuccessOrExit(err = decryptedDataTlvReader.Get(work.initiatorNOC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_SenderNOC))); - SuccessOrExit(err = decryptedDataTlvReader.Get(initiatorNOC)); + SuccessOrExit(err = decryptedDataTlvReader.Next()); + if (TLV::TagNumFromTag(decryptedDataTlvReader.GetTag()) == kTag_TBEData_SenderICAC) + { + VerifyOrExit(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); + SuccessOrExit(err = decryptedDataTlvReader.Get(work.initiatorICAC)); + SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_Signature))); + } - SuccessOrExit(err = decryptedDataTlvReader.Next()); - if (TLV::TagNumFromTag(decryptedDataTlvReader.GetTag()) == kTag_TBEData_SenderICAC) - { - VerifyOrExit(decryptedDataTlvReader.GetType() == TLV::kTLVType_ByteString, err = CHIP_ERROR_WRONG_TLV_TYPE); - SuccessOrExit(err = decryptedDataTlvReader.Get(initiatorICAC)); - SuccessOrExit(err = decryptedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBEData_Signature))); + // Step 4 - Construct Sigma3 TBS Data + work.msg_r3_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), work.initiatorNOC.size(), work.initiatorICAC.size(), + kP256_PublicKey_Length, kP256_PublicKey_Length); + + VerifyOrExit(work.msg_R3_Signed.Alloc(work.msg_r3_signed_len), err = CHIP_ERROR_NO_MEMORY); + + SuccessOrExit(err = ConstructTBSData(work.initiatorNOC, work.initiatorICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), + ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), + work.msg_R3_Signed.Get(), work.msg_r3_signed_len)); + + VerifyOrExit(TLV::TagNumFromTag(decryptedDataTlvReader.GetTag()) == kTag_TBEData_Signature, + err = CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrExit(work.tbsData3Signature.Capacity() >= decryptedDataTlvReader.GetLength(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); + work.tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); + SuccessOrExit(err = decryptedDataTlvReader.GetBytes(work.tbsData3Signature.Bytes(), work.tbsData3Signature.Length())); + + // Prepare for Step 5/6 + { + MutableByteSpan fabricRCAC{ work.rootCertBuf }; + SuccessOrExit(err = mFabricsTable->FetchRootCert(mFabricIndex, fabricRCAC)); + work.fabricRCAC = fabricRCAC; + // TODO probably should make SetEffectiveTime static and call closer to VerifyCredentials + SuccessOrExit(err = SetEffectiveTime()); + } + + // Copy remaining needed data into work structure + { + work.validContext = mValidContext; + + // initiatorNOC and initiatorICAC are spans into msg_R3_Encrypted + // which is going away, so to save memory, redirect them to their + // copies in msg_R3_signed, which is staying around + TLV::TLVReader signedDataTlvReader; + signedDataTlvReader.Init(work.msg_R3_Signed.Get(), work.msg_r3_signed_len); + SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); + SuccessOrExit(err = signedDataTlvReader.EnterContainer(containerType)); + + SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBSData_SenderNOC))); + SuccessOrExit(err = signedDataTlvReader.Get(work.initiatorNOC)); + + if (!work.initiatorICAC.empty()) + { + SuccessOrExit(err = signedDataTlvReader.Next(TLV::kTLVType_ByteString, TLV::ContextTag(kTag_TBSData_SenderICAC))); + SuccessOrExit(err = signedDataTlvReader.Get(work.initiatorICAC)); + } + } + + SuccessOrExit( + err = DeviceLayer::PlatformMgr().ScheduleBackgroundWork( + [](intptr_t arg) { HandleSigma3b(*reinterpret_cast(arg)); }, reinterpret_cast(&work))); + workPtr = nullptr; // scheduling succeeded, so don't delete + mExchangeCtxt->WillSendMessage(); + mState = State::kBackgroundPending; } - // Step 5/6 - // Validate initiator identity located in msg->Start() - // Constructing responder identity - SuccessOrExit(err = ValidatePeerIdentity(initiatorNOC, initiatorICAC, initiatorNodeId, initiatorPublicKey)); - mPeerNodeId = initiatorNodeId; +exit: + Platform::Delete(workPtr); - // Step 4 - Construct Sigma3 TBS Data - msg_r3_signed_len = TLV::EstimateStructOverhead(sizeof(uint16_t), initiatorNOC.size(), initiatorICAC.size(), - kP256_PublicKey_Length, kP256_PublicKey_Length); + if (err != CHIP_NO_ERROR) + { + SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); + } - VerifyOrExit(msg_R3_Signed.Alloc(msg_r3_signed_len), err = CHIP_ERROR_NO_MEMORY); + return err; +} - SuccessOrExit(err = ConstructTBSData(initiatorNOC, initiatorICAC, ByteSpan(mRemotePubKey, mRemotePubKey.Length()), - ByteSpan(mEphemeralKey->Pubkey(), mEphemeralKey->Pubkey().Length()), msg_R3_Signed.Get(), - msg_r3_signed_len)); +void CASESession::HandleSigma3b(Sigma3Work & work) +{ + CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(TLV::TagNumFromTag(decryptedDataTlvReader.GetTag()) == kTag_TBEData_Signature, err = CHIP_ERROR_INVALID_TLV_TAG); - VerifyOrExit(tbsData3Signature.Capacity() >= decryptedDataTlvReader.GetLength(), err = CHIP_ERROR_INVALID_TLV_ELEMENT); - tbsData3Signature.SetLength(decryptedDataTlvReader.GetLength()); - SuccessOrExit(err = decryptedDataTlvReader.GetBytes(tbsData3Signature.Bytes(), tbsData3Signature.Length())); + // Step 5/6 + // Validate initiator identity located in msg->Start() + // Constructing responder identity + CompressedFabricId unused; + FabricId initiatorFabricId; + P256PublicKey initiatorPublicKey; + SuccessOrExit(err = FabricTable::VerifyCredentials(work.initiatorNOC, work.initiatorICAC, work.fabricRCAC, work.validContext, + unused, initiatorFabricId, work.initiatorNodeId, initiatorPublicKey)); + VerifyOrExit(work.fabricId == initiatorFabricId, err = CHIP_ERROR_INVALID_CASE_PARAMETER); // TODO - Validate message signature prior to validating the received operational credentials. // The op cert check requires traversal of cert chain, that is a more expensive operation. @@ -1359,25 +1483,64 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) { P256PublicKeyHSM initiatorPublicKeyHSM; memcpy(Uint8::to_uchar(initiatorPublicKeyHSM), initiatorPublicKey.Bytes(), initiatorPublicKey.Length()); - SuccessOrExit( - err = initiatorPublicKeyHSM.ECDSA_validate_msg_signature(msg_R3_Signed.Get(), msg_r3_signed_len, tbsData3Signature)); + SuccessOrExit(err = initiatorPublicKeyHSM.ECDSA_validate_msg_signature(work.msg_R3_Signed.Get(), work.msg_r3_signed_len, + work.tbsData3Signature)); } #else - SuccessOrExit(err = initiatorPublicKey.ECDSA_validate_msg_signature(msg_R3_Signed.Get(), msg_r3_signed_len, tbsData3Signature)); + SuccessOrExit(err = initiatorPublicKey.ECDSA_validate_msg_signature(work.msg_R3_Signed.Get(), work.msg_r3_signed_len, + work.tbsData3Signature)); #endif - SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan)); +exit: + work.status = err; + + auto err2 = DeviceLayer::PlatformMgr().ScheduleWork( + [](intptr_t arg) { + auto & work2 = *reinterpret_cast(arg); + work2.session->HandleSigma3c(work2); + }, + reinterpret_cast(&work)); + + if (err2 != CHIP_NO_ERROR) + { + Platform::Delete(&work); // scheduling failed, so delete + } +} + +CHIP_ERROR CASESession::HandleSigma3c(Sigma3Work & work) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + bool ignoreFailure = true; + + // Special case: if for whatever reason not in expected state or sequence, + // don't do anything, including sending a status report or aborting the + // pending establish. + VerifyOrExit(mState == State::kBackgroundPending, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mSequence == work.sequence, err = CHIP_ERROR_INCORRECT_STATE); + + ignoreFailure = false; + + SuccessOrExit(err = work.status); + + mPeerNodeId = work.initiatorNodeId; + + { + MutableByteSpan messageDigestSpan(mMessageDigest); + SuccessOrExit(err = mCommissioningHash.Finish(messageDigestSpan)); + } // Retrieve peer CASE Authenticated Tags (CATs) from peer's NOC. { - SuccessOrExit(err = ExtractCATsFromOpCert(initiatorNOC, mPeerCATs)); + SuccessOrExit(err = ExtractCATsFromOpCert(work.initiatorNOC, mPeerCATs)); } if (mSessionResumptionStorage != nullptr) { CHIP_ERROR err2 = mSessionResumptionStorage->Save(GetPeer(), mNewResumptionId, mSharedSecret, mPeerCATs); if (err2 != CHIP_NO_ERROR) + { ChipLogError(SecureChannel, "Unable to save session resumption state: %" CHIP_ERROR_FORMAT, err2.Format()); + } } SendStatusReport(mExchangeCtxt, kProtocolCodeSuccess); @@ -1386,10 +1549,17 @@ CHIP_ERROR CASESession::HandleSigma3(System::PacketBufferHandle && msg) Finish(); exit: - if (err != CHIP_NO_ERROR) + Platform::Delete(&work); + + if (err != CHIP_NO_ERROR && !ignoreFailure) { SendStatusReport(mExchangeCtxt, kProtocolCodeInvalidParam); + // Abort the pending establish, which is normally done by CASESession::OnMessageReceived, + // but in the background processing case must be done here. + DiscardExchange(); + AbortPendingEstablish(err); } + return err; } @@ -1489,38 +1659,12 @@ CHIP_ERROR CASESession::ValidateSigmaResumeMIC(const ByteSpan & resumeMIC, const return CHIP_NO_ERROR; } -CHIP_ERROR CASESession::ValidatePeerIdentity(const ByteSpan & peerNOC, const ByteSpan & peerICAC, NodeId & peerNodeId, - Crypto::P256PublicKey & peerPublicKey) -{ - ReturnErrorCodeIf(mFabricsTable == nullptr, CHIP_ERROR_INCORRECT_STATE); - const auto * fabricInfo = mFabricsTable->FindFabricWithIndex(mFabricIndex); - ReturnErrorCodeIf(fabricInfo == nullptr, CHIP_ERROR_INCORRECT_STATE); - - ReturnErrorOnFailure(SetEffectiveTime()); - - CompressedFabricId unused; - FabricId peerFabricId; - ReturnErrorOnFailure(mFabricsTable->VerifyCredentials(mFabricIndex, peerNOC, peerICAC, mValidContext, unused, peerFabricId, - peerNodeId, peerPublicKey)); - VerifyOrReturnError(fabricInfo->GetFabricId() == peerFabricId, CHIP_ERROR_INVALID_CASE_PARAMETER); - - return CHIP_NO_ERROR; -} - CHIP_ERROR CASESession::ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey, const ByteSpan & receiverPubKey, uint8_t * tbsData, size_t & tbsDataLen) { TLV::TLVWriter tlvWriter; TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified; - enum - { - kTag_TBSData_SenderNOC = 1, - kTag_TBSData_SenderICAC = 2, - kTag_TBSData_SenderPubKey = 3, - kTag_TBSData_ReceiverPubKey = 4, - }; - tlvWriter.Init(tbsData, tbsDataLen); ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType)); ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(kTag_TBSData_SenderNOC), senderNOC)); @@ -1820,7 +1964,7 @@ CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHea switch (static_cast(payloadHeader.GetMessageType())) { case Protocols::SecureChannel::MsgType::CASE_Sigma3: - err = HandleSigma3(std::move(msg)); + err = HandleSigma3a(std::move(msg)); break; case MsgType::StatusReport: diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index ff5248eb705e0c..844425d655f41b 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -192,6 +192,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, kSentSigma2Resume = 5, kFinished = 6, kFinishedViaResume = 7, + kBackgroundPending = 8, }; /* @@ -221,15 +222,17 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, CHIP_ERROR HandleSigma2_and_SendSigma3(System::PacketBufferHandle && msg); CHIP_ERROR HandleSigma2(System::PacketBufferHandle && msg); CHIP_ERROR HandleSigma2Resume(System::PacketBufferHandle && msg); + CHIP_ERROR SendSigma3(); - CHIP_ERROR HandleSigma3(System::PacketBufferHandle && msg); + struct Sigma3Work; + CHIP_ERROR HandleSigma3a(System::PacketBufferHandle && msg); + static void HandleSigma3b(Sigma3Work & work); + CHIP_ERROR HandleSigma3c(Sigma3Work & work); CHIP_ERROR SendSigma2Resume(); CHIP_ERROR ConstructSaltSigma2(const ByteSpan & rand, const Crypto::P256PublicKey & pubkey, const ByteSpan & ipk, MutableByteSpan & salt); - CHIP_ERROR ValidatePeerIdentity(const ByteSpan & peerNOC, const ByteSpan & peerICAC, NodeId & peerNodeId, - Crypto::P256PublicKey & peerPublicKey); CHIP_ERROR ConstructTBSData(const ByteSpan & senderNOC, const ByteSpan & senderICAC, const ByteSpan & senderPubKey, const ByteSpan & receiverPubKey, uint8_t * tbsData, size_t & tbsDataLen); CHIP_ERROR ConstructSaltSigma3(const ByteSpan & ipk, MutableByteSpan & salt); @@ -283,6 +286,10 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, // Sigma1 initiator random, maintained to be reused post-Sigma1, such as when generating Sigma2 S2RK key uint8_t mInitiatorRandom[kSigmaParamRandomNumberSize]; + // Sequence number used to coordinate foreground/background work for a + // particular session establishment. + int mSequence = 0; + State mState; #if CONFIG_BUILD_FOR_HOST_UNIT_TEST diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index c3628a707c9acb..fb881cb60579d9 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -57,6 +57,20 @@ using TestContext = Test::LoopbackMessagingContext; namespace chip { namespace { +void ServiceEvents(TestContext & ctx) +{ + // Service any messages + ctx.DrainAndServiceIO(); + + // Messages may have scheduled work, so service them + chip::DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t) -> void { chip::DeviceLayer::PlatformMgr().StopEventLoopTask(); }, + (intptr_t) nullptr); + chip::DeviceLayer::PlatformMgr().RunEventLoop(); + + // Work may have sent messages, so service them + ctx.DrainAndServiceIO(); +} + class TemporarySessionManager { public: @@ -351,19 +365,19 @@ void TestCASESession::SecurePairingStartTest(nlTestSuite * inSuite, void * inCon pairing.EstablishSession(sessionManager, nullptr, ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, nullptr, nullptr, nullptr, nullptr, Optional::Missing()) != CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, pairing.EstablishSession(sessionManager, &gCommissionerFabrics, ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, nullptr, nullptr, nullptr, nullptr, Optional::Missing()) != CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, pairing.EstablishSession(sessionManager, &gCommissionerFabrics, ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, context, nullptr, nullptr, &delegate, Optional::Missing()) == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); auto & loopback = ctx.GetLoopback(); // There should have been two message sent: Sigma1 and an ack. @@ -385,7 +399,7 @@ void TestCASESession::SecurePairingStartTest(nlTestSuite * inSuite, void * inCon pairing1.EstablishSession( sessionManager, &gCommissionerFabrics, ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, context1, nullptr, nullptr, &delegate, Optional::Missing()) == CHIP_ERROR_BAD_REQUEST); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); loopback.mMessageSendError = CHIP_NO_ERROR; } @@ -422,7 +436,7 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, S ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner, nullptr, nullptr, &delegateCommissioner, MakeOptional(nonSleepyCommissionerRmpConfig)) == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == sTestCaseMessageCount); NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingComplete == 1); @@ -490,7 +504,7 @@ void TestCASESession::SecurePairingHandshakeServerTest(nlTestSuite * inSuite, vo ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner, nullptr, nullptr, &delegateCommissioner, Optional::Missing()) == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == sTestCaseMessageCount); NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 1); @@ -510,7 +524,8 @@ void TestCASESession::SecurePairingHandshakeServerTest(nlTestSuite * inSuite, vo ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner1, nullptr, nullptr, &delegateCommissioner, Optional::Missing()) == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + + ServiceEvents(ctx); chip::Platform::Delete(pairingCommissioner); chip::Platform::Delete(pairingCommissioner1); @@ -916,7 +931,7 @@ void TestCASESession::SessionResumptionStorage(nlTestSuite * inSuite, void * inC ctx.GetSecureSessionManager(), &gCommissionerFabrics, ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner, &testVectors[i].initiatorStorage, nullptr, &delegateCommissioner, Optional::Missing()); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, establishmentReturnVal == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == testVectors[i].expectedSentMessageCount); NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == i + 1); @@ -960,7 +975,7 @@ void TestCASESession::SimulateUpdateNOCInvalidatePendingEstablishment(nlTestSuit Optional::Missing()) == CHIP_NO_ERROR); gDeviceFabrics.SendUpdateFabricNotificationForTest(gDeviceFabricIndex); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingErrors == 0); NL_TEST_ASSERT(inSuite, @@ -968,7 +983,7 @@ void TestCASESession::SimulateUpdateNOCInvalidatePendingEstablishment(nlTestSuit ScopedNodeId{ Node01_01, gCommissionerFabricIndex }, contextCommissioner, nullptr, nullptr, &delegateCommissioner, Optional::Missing()) == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); // At this point the CASESession is in the process of establishing. Confirm that there are no errors and there are session // has not been established. @@ -980,14 +995,14 @@ void TestCASESession::SimulateUpdateNOCInvalidatePendingEstablishment(nlTestSuit // Simulating an update to the Fabric NOC for gCommissionerFabrics fabric table. // Confirm that CASESession on commisioner side has reported an error. gCommissionerFabrics.SendUpdateFabricNotificationForTest(gCommissionerFabricIndex); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingErrors == 0); NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingErrors == 1); // Simulating an update to the Fabric NOC for gDeviceFabrics fabric table. // Confirm that CASESession on accessory side has reported an error. gDeviceFabrics.SendUpdateFabricNotificationForTest(gDeviceFabricIndex); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingErrors == 1); NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingErrors == 1); @@ -1070,7 +1085,7 @@ void TestCASESession::Sigma1BadDestinationIdTest(nlTestSuite * inSuite, void * i err = exchange->SendMessage(MsgType::CASE_Sigma1, std::move(data), SendMessageFlags::kExpectResponse); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - ctx.DrainAndServiceIO(); + ServiceEvents(ctx); NL_TEST_ASSERT(inSuite, caseDelegate.mNumPairingErrors == 1); NL_TEST_ASSERT(inSuite, caseDelegate.mNumPairingComplete == 0); @@ -1145,12 +1160,18 @@ int CASE_TestSecurePairing_Setup(void * inContext) { chip::Platform::MemoryInit(); + chip::DeviceLayer::PlatformMgr().InitChipStack(); + CHIP_ERROR err = CASETestSecurePairingSetup(inContext); if (err != CHIP_NO_ERROR) { ChipLogError(Support, "Failed to init tests %" CHIP_ERROR_FORMAT, err.Format()); return FAILURE; } + + TestContext & ctx = *reinterpret_cast(inContext); + chip::DeviceLayer::SetSystemLayerForTesting(&ctx.GetSystemLayer()); + return SUCCESS; } @@ -1159,12 +1180,15 @@ int CASE_TestSecurePairing_Setup(void * inContext) */ int CASE_TestSecurePairing_Teardown(void * inContext) { + chip::DeviceLayer::SetSystemLayerForTesting(nullptr); + gPairingServer.Shutdown(); gCommissionerStorageDelegate.ClearStorage(); gDeviceStorageDelegate.ClearStorage(); gCommissionerFabrics.DeleteAllFabrics(); gDeviceFabrics.DeleteAllFabrics(); static_cast(inContext)->Shutdown(); + chip::DeviceLayer::PlatformMgr().Shutdown(); return SUCCESS; }