From 402197699ec459075cf3333c9d718e72bb2b9bb0 Mon Sep 17 00:00:00 2001 From: Marc Lepage <67919234+mlepage-google@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:01:31 -0500 Subject: [PATCH] Add background event handling for CASE establish (#24099) * Add background event handling for CASE establish CASE session establishment has operations which are costly, such as checking certificate chains. The handshake messages are processed in the event thread, so while these operations occur, other events cannot be processed. This delays responses, and can cause the event queue to fill entirely, which is fatal. This commit adds support for background event processing, and uses it to process the most costly operations during CASESesssion::HandleSigma3. - add platform support for background event processing: ScheduleBackgroundWork, RunBackgroundEventLoop, etc. - add device config flags for enabling/disabling and configuration - add implementation for FreeRTOS platform - refactor some CASESession operations so they can be static, avoiding use of member variables - break HandlSigma3 into 3 parts A/B/C: - HandleSigma3a (foreground, processes incoming message) - HandleSigma3b (background, performs most costly operations) - HandleSigma3c (foreground, sends status report) This breakup of HandleSigma3 was done in a fairly straightforward manner so it could be clearer, during review, that behaviour has not substantially changed. A subsequent commit should clean it up further by introducing helper code for managing the foreground/background work, lifetime of work object, when to send status report and/or abort pending establish, etc. Also still to do is implementation for other platforms, and for other messages in CASESession (e.g. Sigma2), and for other costly operations (e.g. PASESession). Currently, CASE session establishment is simplified: - only one pairing session is active at a time - it's always the same CASESession object in CASEServer - the two classes are higly coupled (e.g. CASEServer relies upon CASESession aborting the pending establish if an error occurs) Therefore, HandleSigma3b can rely upon the lifetime of the CASESession object, use an additional state and sequence number to synchronize work across foreground/background, and avoid use of member variables. If and when CASE session establishment becomes more complex, assumptions should be revisited. TESTING Testing was performed on M5Stack (ESP32) by commissioning using the Google Home app on Android. First, baseline behaviour with background events disabled: - If no errors, commissioning succeeds as before - If HandleSigma3a fails and sends a status report, pairing retries promptly and succeeds - If HandleSigma3a fails and cannot send a status report, pairing retries after about a minute and succeeds - If HandleSigma3c succeeds but cannot send a status report, pairing retries after about a minute and succeeds Next, improved behaviour with background events enabled: - If no errors, commissioning succeeds as before - If HandleSigma3a fails and sends a status report, pairing retries promptly and succeeds - (this includes failure to schedule HandleSigma3b) - If HandleSigma3b fails and sends a status report, pairing retries promptly and succeeds - If HandleSigma3c fails and sends a status report, pairing retries promptly and succeeds - If HandleSigma3c succeeds but cannot send a status report, pairing retries after about a minute and succeeds - If HandleSigma3b is starved (scheduled but does not complete), after several minutes the failsafe timer fires, then Home app allows try again, which then succeeds - If HandleSigma3b is delayed (completes late), the sequence number is unexpected, so no status report is sent, then after several minutes the failsafe timer fires, then Home app allows try again, which then succeeds * Remove WIP code * Address some comments from code review * Remove cruft from testing. * Remove some conditional compilation * Remove some conditional compilation * Move function back where it was Had more related changes, but they're all removed, so remove this change also. * Add some documentation Change error code also. * Use platform new/delete * Undo changes that are merely reordering Cleanup can occur in a subsequent commit. * Undo changes that are merely reordering Cleanup can occur in a subsequent commit. * Remove include file fix (C/C++) * Add documentation to background processing API * Use alternate fabrics table API * Improve documentation * Add assertion * Undo some unrelated cleanup * Update src/protocols/secure_channel/CASESession.cpp Co-authored-by: Boris Zbarsky * Ensure root cert buf keeps span * Restyled by whitespace * Restyled by clang-format * Add new functions to GenericPlatformManagerImpl So all platforms build and work, even if they don't use the new feature. * Attempt to fix build errors on some platforms Apparently initializing structs with anonymous unions is challenging. * Improving host test environment This commit has a bunch of extra logging etc. to flush out any more CI issues. * Remove log statements and clean up * Update fake PlatformManagerImpl * Increase timeout on fake linux CI * Redo changes to make tests work Undo previous changes to test/app contexts, and go back to just fixing the tests more surgically and contained. Passes Linux host tests and Linux fake platform tests now. * Undo SetSystemLayerForTesting nRF/Zephyr tests don't like this not being cleaned up. May fix Darwin too? * Change fake linux tests timeout back to 15 mins * Restyle * Init/shutdown platform mgr in TestCASESession Seems needed on Darwin. --------- Co-authored-by: Boris Zbarsky Co-authored-by: Restyled.io --- .../esp32/common/CHIPDeviceManager.cpp | 3 + src/credentials/FabricTable.cpp | 2 + src/credentials/FabricTable.h | 15 +- src/include/platform/CHIPDeviceConfig.h | 49 ++- src/include/platform/CHIPDeviceLayer.h | 2 +- src/include/platform/PlatformManager.h | 74 +++- .../internal/GenericPlatformManagerImpl.h | 7 +- .../internal/GenericPlatformManagerImpl.ipp | 54 ++- .../GenericPlatformManagerImpl_FreeRTOS.h | 33 +- .../GenericPlatformManagerImpl_FreeRTOS.ipp | 143 ++++++- src/inet/tests/TestInetCommonPosix.cpp | 2 +- src/platform/Globals.cpp | 4 +- src/platform/fake/PlatformManagerImpl.h | 23 +- src/protocols/secure_channel/CASESession.cpp | 364 ++++++++++++------ src/protocols/secure_channel/CASESession.h | 13 +- .../secure_channel/tests/TestCASESession.cpp | 50 ++- 16 files changed, 669 insertions(+), 169 deletions(-) mode change 100755 => 100644 src/include/platform/CHIPDeviceConfig.h 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; }