From d15a91f75ba039054e9b8225c11534e7a61d9f5c Mon Sep 17 00:00:00 2001 From: Jean-Francois Penven <67962328+jepenven-silabs@users.noreply.github.com> Date: Wed, 16 Feb 2022 09:59:07 -0500 Subject: [PATCH] [Group ]Group msg counter (#14996) * Add Group Message counters * Counter Integration --- src/app/tests/suites/TestGroupMessaging.yaml | 7 +- src/lib/core/CHIPConfig.h | 21 + src/lib/support/DefaultStorageKeyAllocator.h | 4 + src/platform/telink/CHIPPlatformConfig.h | 12 + src/transport/BUILD.gn | 2 + src/transport/GroupPeerMessageCounter.cpp | 325 ++++++++++++ src/transport/GroupPeerMessageCounter.h | 92 ++++ src/transport/PeerMessageCounter.h | 95 +++- src/transport/SessionManager.cpp | 65 ++- src/transport/SessionManager.h | 5 +- src/transport/tests/BUILD.gn | 1 + .../tests/TestGroupMessageCounter.cpp | 473 ++++++++++++++++++ .../chip-tool/zap-generated/test/Commands.h | 2 +- 13 files changed, 1075 insertions(+), 29 deletions(-) create mode 100644 src/transport/GroupPeerMessageCounter.cpp create mode 100644 src/transport/GroupPeerMessageCounter.h create mode 100644 src/transport/tests/TestGroupMessageCounter.cpp diff --git a/src/app/tests/suites/TestGroupMessaging.yaml b/src/app/tests/suites/TestGroupMessaging.yaml index 76be04d23bc7c7..f0c8f6f1ab74fb 100644 --- a/src/app/tests/suites/TestGroupMessaging.yaml +++ b/src/app/tests/suites/TestGroupMessaging.yaml @@ -74,10 +74,11 @@ tests: arguments: values: - name: "GroupKeySet" - value: - { + value: { GroupKeySetID: 0x01a1, - GroupKeySecurityPolicy: 0, + # TODO Revert this once MCSP is implemented + # GroupKeySecurityPolicy: 0, + GroupKeySecurityPolicy: 1, # 1 => LowLatency => TrustFirst EpochKey0: "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf", EpochStartTime0: 1110000, EpochKey1: "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index e2a8f5d97e7164..7e1d9079cbb5d2 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1272,6 +1272,27 @@ #define CHIP_CONFIG_MAX_FABRICS 16 #endif // CHIP_CONFIG_MAX_FABRICS +/** + * @def CHIP_CONFIG_MAX_GROUP_DATA_PEERS + * + * @brief + * Maximum number of Peer within a fabric that can send group data message to a device. + * + */ +#ifndef CHIP_CONFIG_MAX_GROUP_DATA_PEERS +#define CHIP_CONFIG_MAX_GROUP_DATA_PEERS 15 +#endif // CHIP_CONFIG_MAX_GROUP_DATA_PEERS + +/** + * @def CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS + * + * @brief + * Maximum number of Peer within a fabric that can send group control message to a device. + */ +#ifndef CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS +#define CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS 2 +#endif // CHIP_CONFIG_MAX_GROUP_CONTROL_PEER + /** * @def CHIP_NON_PRODUCTION_MARKER * diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index f21ab5db7080d8..2297b69dc0e55b 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -46,6 +46,10 @@ class DefaultStorageKeyAllocator return Format("acl/%x", static_cast(index)); } + // Group Message Counters + const char * GroupDataCounter() { return Format("gdc"); } + const char * GroupControlCounter() { return Format("gcc"); } + // Group Data Provider const char * FabricTable() { return Format("f/t"); } diff --git a/src/platform/telink/CHIPPlatformConfig.h b/src/platform/telink/CHIPPlatformConfig.h index affd965bd98064..790c7d54b2a07b 100644 --- a/src/platform/telink/CHIPPlatformConfig.h +++ b/src/platform/telink/CHIPPlatformConfig.h @@ -35,6 +35,18 @@ #define CHIP_CONFIG_ERROR_CLASS 1 +/** + * @def CHIP_CONFIG_MAX_FABRICS + * + * @brief + * Maximum number of fabrics the device can participate in. Each fabric can + * provision the device with its unique operational credentials and manage + * its own access control lists. + */ +#ifndef CHIP_CONFIG_MAX_FABRICS +#define CHIP_CONFIG_MAX_FABRICS 5 // 4 fabrics + 1 for rotation slack +#endif + // ==================== Security Adaptations ==================== // ==================== General Configuration Overrides ==================== diff --git a/src/transport/BUILD.gn b/src/transport/BUILD.gn index 7ea65702fe84dd..75828b62480068 100644 --- a/src/transport/BUILD.gn +++ b/src/transport/BUILD.gn @@ -24,6 +24,8 @@ static_library("transport") { sources = [ "CryptoContext.cpp", "CryptoContext.h", + "GroupPeerMessageCounter.cpp", + "GroupPeerMessageCounter.h", "GroupSession.h", "MessageCounter.cpp", "MessageCounter.h", diff --git a/src/transport/GroupPeerMessageCounter.cpp b/src/transport/GroupPeerMessageCounter.cpp new file mode 100644 index 00000000000000..f7b88ef972b3c9 --- /dev/null +++ b/src/transport/GroupPeerMessageCounter.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines the Matter Group message counters of remote nodes for groups. + * + */ + +#include +#include + +namespace chip { +namespace Transport { + +CHIP_ERROR GroupPeerTable::FindOrAddPeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl, + chip::Transport::PeerMessageCounter *& counter) +{ + if (fabricIndex == kUndefinedFabricIndex || nodeId == kUndefinedNodeId) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + for (auto & groupFabric : mGroupFabrics) + { + if (groupFabric.mFabricIndex == kUndefinedFabricIndex) + { + // Already iterated through all known fabricIndex + // Add the new peer to save some processing time + groupFabric.mFabricIndex = fabricIndex; + if (isControl) + { + groupFabric.mControlGroupSenders[0].mNodeId = nodeId; + counter = &(groupFabric.mControlGroupSenders[0].msgCounter); + groupFabric.mControlPeerCount++; + } + else + { + groupFabric.mDataGroupSenders[0].mNodeId = nodeId; + counter = &(groupFabric.mDataGroupSenders[0].msgCounter); + groupFabric.mDataPeerCount++; + } + return CHIP_NO_ERROR; + } + + if (fabricIndex == groupFabric.mFabricIndex) + { + if (isControl) + { + for (auto & node : groupFabric.mControlGroupSenders) + { + if (node.mNodeId == kUndefinedNodeId) + { + // Already iterated through all known NodeIds + // Add the new peer to save some processing time + node.mNodeId = nodeId; + counter = &(node.msgCounter); + groupFabric.mControlPeerCount++; + return CHIP_NO_ERROR; + } + + if (node.mNodeId == nodeId) + { + counter = &(node.msgCounter); + return CHIP_NO_ERROR; + } + } + } + else + { + for (auto & node : groupFabric.mDataGroupSenders) + { + if (node.mNodeId == kUndefinedNodeId) + { + // Already iterated through all known NodeIds + // Add the new peer to save some processing time + node.mNodeId = nodeId; + counter = &(node.msgCounter); + groupFabric.mDataPeerCount++; + return CHIP_NO_ERROR; + } + + if (node.mNodeId == nodeId) + { + counter = &(node.msgCounter); + return CHIP_NO_ERROR; + } + } + } + // Exceeded the Max number of Group peers + return CHIP_ERROR_TOO_MANY_PEER_NODES; + } + } + + // Exceeded the Max number of Group peers + return CHIP_ERROR_TOO_MANY_PEER_NODES; +} + +// Used in case of MCSP failure +CHIP_ERROR GroupPeerTable::RemovePeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl) +{ + CHIP_ERROR err = CHIP_ERROR_NOT_FOUND; + uint32_t fabricIt = CHIP_CONFIG_MAX_FABRICS; + + if (fabricIndex == kUndefinedFabricIndex || nodeId == kUndefinedNodeId) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + for (uint32_t it = 0; it < CHIP_CONFIG_MAX_FABRICS; it++) + { + if (fabricIndex == mGroupFabrics[it].mFabricIndex) + { + if (isControl) + { + if (RemoveSpecificPeer(mGroupFabrics[it].mControlGroupSenders, nodeId, CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS)) + { + fabricIt = it; + mGroupFabrics[it].mControlPeerCount--; + err = CHIP_NO_ERROR; + } + } + else + { + if (RemoveSpecificPeer(mGroupFabrics[it].mDataGroupSenders, nodeId, CHIP_CONFIG_MAX_GROUP_DATA_PEERS)) + { + fabricIt = it; + mGroupFabrics[it].mDataPeerCount--; + err = CHIP_NO_ERROR; + } + } + break; + } + } + + // Remove Fabric entry from PeerTable if empty + if (fabricIt < CHIP_CONFIG_MAX_FABRICS) + { + if (mGroupFabrics[fabricIt].mDataPeerCount == 0 && mGroupFabrics[fabricIt].mControlPeerCount == 0) + { + mGroupFabrics[fabricIt].mFabricIndex = kUndefinedFabricIndex; + // To maintain logic integrity Fabric array cannot have empty slot in between data + // Find the last non empty element + for (uint32_t i = CHIP_CONFIG_MAX_FABRICS - 1; i > fabricIt; i--) + { + if (mGroupFabrics[i].mFabricIndex != kUndefinedFabricIndex) + { + // Logic works since all buffer are static + // move it up front + new (&mGroupFabrics[fabricIt]) GroupFabric(mGroupFabrics[i]); + new (&mGroupFabrics[i]) GroupFabric(); + break; + } + } + } + } + + // Cannot find Peer to remove + return err; +} + +bool GroupPeerTable::RemoveSpecificPeer(GroupSender * list, NodeId nodeId, uint32_t size) +{ + bool removed = false; + for (uint32_t nodeIt = 0; nodeIt < size; nodeIt++) + { + if (list[nodeIt].mNodeId == nodeId) + { + list[nodeIt].mNodeId = kUndefinedNodeId; + list[nodeIt].msgCounter.Reset(); + removed = true; + break; + } + } + + if (removed) + { + CompactPeers(list, size); + } + + return removed; +} + +void GroupPeerTable::CompactPeers(GroupSender * list, uint32_t size) +{ + if (list == nullptr || size == 0) + { + return; + } + + for (uint32_t peerIndex = 0; peerIndex < size; peerIndex++) + { + if (list[peerIndex].mNodeId != kUndefinedNodeId) + { + continue; + } + + for (uint32_t i = (size - 1); i > peerIndex; i--) + { + if (list[i].mNodeId != kUndefinedNodeId) + { + // Logic works since all buffer are static + // move it up front + new (&list[peerIndex]) GroupSender(list[i]); + new (&list[i]) GroupSender(); + break; + } + } + } +} + +GroupOutgoingCounters::GroupOutgoingCounters(chip::PersistentStorageDelegate * storage_delegate) +{ + Init(storage_delegate); +} + +CHIP_ERROR GroupOutgoingCounters::Init(chip::PersistentStorageDelegate * storage_delegate) +{ + + if (storage_delegate == nullptr) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + // TODO Implement Logic for first time use / factory reset to be random + // Spec 4.5.1.3 + mStorage = storage_delegate; + uint16_t size = static_cast(sizeof(uint32_t)); + DefaultStorageKeyAllocator key; + uint32_t temp; + CHIP_ERROR err; + err = mStorage->SyncGetKeyValue(key.GroupControlCounter(), &temp, size); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + // might be the first time we retrieve the value + // TODO handle this case + mGroupControlCounter = 0; // TODO should be random + } + else if (err != CHIP_NO_ERROR) + { + return err; + } + else + { + mGroupControlCounter = temp; + } + + err = mStorage->SyncGetKeyValue(key.GroupDataCounter(), &temp, size); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + // might be the first time we retrieve the value + // TODO handle this case + mGroupDataCounter = 0; // TODO should be random + } + else if (err != CHIP_NO_ERROR) + { + return err; + } + else + { + mGroupDataCounter = temp; + } + + temp = mGroupControlCounter + GROUP_MSG_COUNTER_MIN_INCREMENT; + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key.GroupControlCounter(), &temp, size)); + + temp = mGroupDataCounter + GROUP_MSG_COUNTER_MIN_INCREMENT; + + return mStorage->SyncSetKeyValue(key.GroupDataCounter(), &temp, size); +} + +uint32_t GroupOutgoingCounters::GetCounter(bool isControl) +{ + return (isControl) ? mGroupControlCounter : mGroupDataCounter; +} + +CHIP_ERROR GroupOutgoingCounters::IncrementCounter(bool isControl) +{ + uint32_t temp = 0; + uint16_t size = static_cast(sizeof(uint32_t)); + uint32_t value = 0; + DefaultStorageKeyAllocator key; + + if (isControl) + { + mGroupControlCounter++; + key.GroupControlCounter(); + value = mGroupControlCounter; + } + else + { + mGroupDataCounter++; + key.GroupDataCounter(); + value = mGroupDataCounter; + } + + if (mStorage == nullptr) + { + return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; + } + + ReturnErrorOnFailure(mStorage->SyncGetKeyValue(key.KeyName(), &temp, size)); + if (temp == value) + { + temp = value + GROUP_MSG_COUNTER_MIN_INCREMENT; + return mStorage->SyncSetKeyValue(key.KeyName(), &temp, sizeof(uint32_t)); + } + return CHIP_NO_ERROR; +} + +} // namespace Transport +} // namespace chip diff --git a/src/transport/GroupPeerMessageCounter.h b/src/transport/GroupPeerMessageCounter.h new file mode 100644 index 00000000000000..a77dcaf41fd423 --- /dev/null +++ b/src/transport/GroupPeerMessageCounter.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines the Matter Group message counters of remote nodes for groups. + * + */ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define GROUP_MSG_COUNTER_MIN_INCREMENT 1000 + +namespace chip { +namespace Transport { + +class GroupSender +{ +public: + NodeId mNodeId = kUndefinedNodeId; + PeerMessageCounter msgCounter; +}; + +class GroupFabric +{ +public: + FabricIndex mFabricIndex = kUndefinedFabricIndex; + uint8_t mControlPeerCount = 0; + uint8_t mDataPeerCount = 0; + GroupSender mDataGroupSenders[CHIP_CONFIG_MAX_GROUP_DATA_PEERS]; + GroupSender mControlGroupSenders[CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS]; +}; + +class GroupPeerTable +{ +public: + CHIP_ERROR FindOrAddPeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl, + chip::Transport::PeerMessageCounter *& counter); + + // Used in case of MCSP failure + CHIP_ERROR RemovePeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl); + + // Protected for Unit Tests inheritance +protected: + bool RemoveSpecificPeer(GroupSender * list, NodeId nodeId, uint32_t size); + void CompactPeers(GroupSender * list, uint32_t size); + + GroupFabric mGroupFabrics[CHIP_CONFIG_MAX_FABRICS]; +}; + +// Might want to rename this so that it is explicitly the sending side of counters +class GroupOutgoingCounters +{ +public: + GroupOutgoingCounters(){}; + GroupOutgoingCounters(chip::PersistentStorageDelegate * storage_delegate); + CHIP_ERROR Init(chip::PersistentStorageDelegate * storage_delegate); + uint32_t GetCounter(bool isControl); + CHIP_ERROR IncrementCounter(bool isControl); + + // Protected for Unit Tests inheritance +protected: + // TODO Initialize those to random value + uint32_t mGroupDataCounter = 0; + uint32_t mGroupControlCounter = 0; + chip::PersistentStorageDelegate * mStorage = nullptr; +}; + +} // namespace Transport +} // namespace chip diff --git a/src/transport/PeerMessageCounter.h b/src/transport/PeerMessageCounter.h index 790b4498b96f3c..96afd069b17ef5 100644 --- a/src/transport/PeerMessageCounter.h +++ b/src/transport/PeerMessageCounter.h @@ -85,7 +85,50 @@ class PeerMessageCounter return CHIP_NO_ERROR; } - CHIP_ERROR Verify(uint32_t counter) const + /** + * @brief Implementation of spec 4.5.4.2 + * + * For encrypted messages of Group Session Type, any arriving message with a counter in the range + * [(max_message_counter + 1) to (max_message_counter + 2^31 - 1)] (modulo 2^32) SHALL be considered + * new, and cause the max_message_counter value to be updated. Messages with counters from + * [(max_message_counter - 2^31) to (max_message_counter - MSG_COUNTER_WINDOW_SIZE - 1)] (modulo 2^ + * 32) SHALL be considered duplicate. Message counters within the range of the bitmap SHALL be + * considered duplicate if the corresponding bit offset is set to true. + * + */ + CHIP_ERROR VerifyGroupCounter(uint32_t counter) + { + if (mStatus != Status::Synced) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + // 1. Check whether the new counter value falls in the spec's "valid future counter value" window. + uint32_t counterIncrease = counter - mSynced.mMaxCounter; + uint32_t groupFutureCounterWindow = (static_cast(1 << 31)) - 1; + if (counterIncrease >= 1 && counterIncrease <= (groupFutureCounterWindow)) + { + return CHIP_NO_ERROR; + } + + // 2. Counter Window check + uint32_t offset = mSynced.mMaxCounter - counter; + if (offset < CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE) + { + if ((offset == 0) || mSynced.mWindow.test(offset)) + { + return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED; // duplicated, in window + } + } + else + { + return CHIP_ERROR_MESSAGE_COUNTER_OUT_OF_WINDOW; + } + + return CHIP_NO_ERROR; + } + + CHIP_ERROR VerifyUnicast(uint32_t counter) const { if (mStatus != Status::Synced) { @@ -95,10 +138,12 @@ class PeerMessageCounter if (counter <= mSynced.mMaxCounter) { uint32_t offset = mSynced.mMaxCounter - counter; + if (offset >= CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE) { return CHIP_ERROR_MESSAGE_COUNTER_OUT_OF_WINDOW; // outside valid range } + if (mSynced.mWindow.test(offset)) { return CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED; // duplicated, in window @@ -108,7 +153,16 @@ class PeerMessageCounter return CHIP_NO_ERROR; } - CHIP_ERROR VerifyOrTrustFirst(uint32_t counter) + CHIP_ERROR Verify(uint32_t counter, bool useGroupAlgorithm = false) + { + if (useGroupAlgorithm) + { + return VerifyGroupCounter(counter); + } + return VerifyUnicast(counter); + } + + CHIP_ERROR VerifyOrTrustFirst(uint32_t counter, bool useGroupAlgorithm = false) { switch (mStatus) { @@ -117,8 +171,8 @@ class PeerMessageCounter SetCounter(counter); return CHIP_NO_ERROR; case Status::Synced: { - CHIP_ERROR err = Verify(counter); - if (err == CHIP_ERROR_MESSAGE_COUNTER_OUT_OF_WINDOW) + CHIP_ERROR err = Verify(counter, useGroupAlgorithm); + if (err == CHIP_ERROR_MESSAGE_COUNTER_OUT_OF_WINDOW && !useGroupAlgorithm) { // According to chip spec, when global unencrypted message // counter is out of window, the peer may have reset and is @@ -135,6 +189,37 @@ class PeerMessageCounter } } + /** + * @brief + * With the counter verified and the packet MIC also verified by the secure key, we can trust the packet and adjust + * counter states including possible Rollover for Groups communications. + * + * @pre Verify(counter) == CHIP_NO_ERROR + */ + void CommitWithRollOver(uint32_t counter) + { + uint32_t offset = mSynced.mMaxCounter - counter; + if (offset < CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE) + { + mSynced.mWindow.set(offset); + } + else + { + // Not a bit inside the window. Since we are committing, this is a new mMaxCounter value. + mSynced.mMaxCounter = counter; + uint32_t shift = -offset; + if (shift > CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE) + { + mSynced.mWindow.reset(); + } + else + { + mSynced.mWindow <<= shift; + } + mSynced.mWindow.set(0); + } + } + /** * @brief * With the counter verified and the packet MIC also verified by the secure key, we can trust the packet and adjust @@ -200,7 +285,7 @@ class PeerMessageCounter * | <-- mWindow -->| * |[n]| ... |[0]| */ - uint32_t mMaxCounter; // The most recent counter we have seen + uint32_t mMaxCounter = 0; // The most recent counter we have seen std::bitset mWindow; }; diff --git a/src/transport/SessionManager.cpp b/src/transport/SessionManager.cpp index 1c749344476477..d475535ce0b65c 100644 --- a/src/transport/SessionManager.cpp +++ b/src/transport/SessionManager.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,9 @@ #include +// Global object +chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + namespace chip { using System::PacketBufferHandle; @@ -83,12 +87,13 @@ CHIP_ERROR SessionManager::Init(System::Layer * systemLayer, TransportMgrBase * mSystemLayer = systemLayer; mTransportMgr = transportMgr; mMessageCounterManager = messageCounterManager; - mStorage = storageDelegate; // TODO: Handle error from mGlobalEncryptedMessageCounter! Unit tests currently crash if you do! (void) mGlobalEncryptedMessageCounter.Init(); mGlobalUnencryptedMessageCounter.Init(); + mGroupClientCounter.Init(storageDelegate); + ScheduleExpiryTimer(); mTransportMgr->SetSessionManager(this); @@ -114,7 +119,8 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P System::PacketBufferHandle && message, EncryptedPacketBufferHandle & preparedMessage) { PacketHeader packetHeader; - if (IsControlMessage(payloadHeader)) + bool isControlMsg = IsControlMessage(payloadHeader); + if (isControlMsg) { packetHeader.SetSecureSessionControlMsg(true); } @@ -131,9 +137,9 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P auto * groups = Credentials::GetGroupDataProvider(); VerifyOrReturnError(nullptr != groups, CHIP_ERROR_INTERNAL); - // TODO : #11911 - // For now, just set the packetHeader with the correct data. packetHeader.SetDestinationGroupId(groupSession->GetGroupId()); + packetHeader.SetMessageCounter(mGroupClientCounter.GetCounter(isControlMsg)); + mGroupClientCounter.IncrementCounter(isControlMsg); packetHeader.SetFlags(Header::SecFlagValues::kPrivacyFlag); packetHeader.SetSessionType(Header::SessionType::kGroupSession); packetHeader.SetSourceNodeId(groupSession->GetSourceNodeId()); @@ -157,7 +163,7 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P #if CHIP_PROGRESS_LOGGING destination = kUndefinedNodeId; - fabricIndex = kUndefinedFabricIndex; + fabricIndex = groupSession->GetFabricIndex(); #endif // CHIP_PROGRESS_LOGGING } break; @@ -601,9 +607,9 @@ void SessionManager::SecureGroupMessageDispatch(const PacketHeader & packetHeade System::PacketBufferHandle && msg) { PayloadHeader payloadHeader; - SessionMessageDelegate::DuplicateMessage isDuplicate = SessionMessageDelegate::DuplicateMessage::No; - Credentials::GroupDataProvider * groups = Credentials::GetGroupDataProvider(); + Credentials::GroupDataProvider * groups = Credentials::GetGroupDataProvider(); VerifyOrReturn(nullptr != groups); + CHIP_ERROR err = CHIP_NO_ERROR; if (!packetHeader.GetDestinationGroupId().HasValue()) { @@ -676,25 +682,49 @@ void SessionManager::SecureGroupMessageDispatch(const PacketHeader & packetHeade return; } - // TODO: Handle Group message counter here spec 4.7.3 + // Handle Group message counter here spec 4.7.3 // spec 4.5.1.2 for msg counter + chip::Transport::PeerMessageCounter * counter = nullptr; - if (isDuplicate == SessionMessageDelegate::DuplicateMessage::Yes) + if (CHIP_NO_ERROR == + mGroupPeerMsgCounter.FindOrAddPeer(groupContext.fabric_index, packetHeader.GetSourceNodeId().Value(), + packetHeader.IsSecureSessionControlMsg(), counter)) { - ChipLogDetail(Inet, - "Received a duplicate message with MessageCounter:" ChipLogFormatMessageCounter - " on exchange " ChipLogFormatExchangeId, - packetHeader.GetMessageCounter(), ChipLogValueExchangeIdFromReceivedHeader(payloadHeader)); - // If it's a duplicate message, let's drop it right here to save CPU cycles + if (Credentials::GroupDataProvider::SecurityPolicy::kLowLatency == groupContext.security_policy) + { + err = counter->VerifyOrTrustFirst(packetHeader.GetMessageCounter(), true); + } + else + { + + // TODO support cache and sync with MCSP. Issue #11689 + ChipLogError(Inet, "Received Group Msg with key policy Cache and Sync, but MCSP is not implemented"); + return; + + // cache and sync + // err = counter->Verify(packetHeader.GetMessageCounter(), true); + } + + if (err != CHIP_NO_ERROR) + { + // Exit now, since Group Messages don't have acks or responses of any kind. + ChipLogError(Inet, "Message counter verify failed, err = %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + } + else + { + ChipLogError(Inet, + "Group Counter Tables full or invalid NodeId/FabricIndex after decryption of message, dropping everything"); return; } - // TODO: Commit Group Message Counter + counter->CommitWithRollOver(packetHeader.GetMessageCounter()); if (mCB != nullptr) { - // TODO : When MCSP is done, clean up session creation logique + // TODO : When MCSP is done, clean up session creation logic Optional session = CreateGroupSession(groupContext.group_id, groupContext.fabric_index, packetHeader.GetSourceNodeId().Value()); @@ -702,7 +732,8 @@ void SessionManager::SecureGroupMessageDispatch(const PacketHeader & packetHeade Transport::GroupSession * groupSession = session.Value()->AsGroupSession(); CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, groupSession, peerAddress, msg->Start(), msg->TotalLength()); - mCB->OnMessageReceived(packetHeader, payloadHeader, session.Value(), peerAddress, isDuplicate, std::move(msg)); + mCB->OnMessageReceived(packetHeader, payloadHeader, session.Value(), peerAddress, + SessionMessageDelegate::DuplicateMessage::No, std::move(msg)); RemoveGroupSession(groupSession); } diff --git a/src/transport/SessionManager.h b/src/transport/SessionManager.h index c3db1b9849b4f6..6e21a99323d042 100644 --- a/src/transport/SessionManager.h +++ b/src/transport/SessionManager.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -257,6 +258,7 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate Transport::SecureSessionTable mSecureSessions; Transport::GroupSessionTable mGroupSessions; State mState; // < Initialization state of the object + chip::Transport::GroupOutgoingCounters mGroupClientCounter; SessionMessageDelegate * mCB = nullptr; @@ -266,9 +268,6 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate TransportMgrBase * mTransportMgr = nullptr; Transport::MessageCounterManagerInterface * mMessageCounterManager = nullptr; - // Will be use once PR 14996 is merged - chip::PersistentStorageDelegate * mStorage = nullptr; - GlobalUnencryptedMessageCounter mGlobalUnencryptedMessageCounter; GlobalEncryptedMessageCounter mGlobalEncryptedMessageCounter; diff --git a/src/transport/tests/BUILD.gn b/src/transport/tests/BUILD.gn index 532c701fe34e5a..68aa7a439a3d9e 100644 --- a/src/transport/tests/BUILD.gn +++ b/src/transport/tests/BUILD.gn @@ -23,6 +23,7 @@ chip_test_suite("tests") { output_name = "libTransportLayerTests" test_sources = [ + "TestGroupMessageCounter.cpp", "TestPairingSession.cpp", "TestPeerConnections.cpp", "TestSecureSession.cpp", diff --git a/src/transport/tests/TestGroupMessageCounter.cpp b/src/transport/tests/TestGroupMessageCounter.cpp new file mode 100644 index 00000000000000..e9037ca3d1fea4 --- /dev/null +++ b/src/transport/tests/TestGroupMessageCounter.cpp @@ -0,0 +1,473 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file implements unit tests for the SessionManager implementation. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace { + +using namespace chip; + +class TestGroupOutgoingCounters : public chip::Transport::GroupOutgoingCounters +{ +public: + TestGroupOutgoingCounters(){}; + TestGroupOutgoingCounters(chip::PersistentStorageDelegate * storage_delegate) : GroupOutgoingCounters(storage_delegate){}; + void SetCounter(bool isControl, uint32_t value) + { + uint32_t temp = 0; + + DefaultStorageKeyAllocator key; + + if (isControl) + { + mGroupControlCounter = value; + key.GroupControlCounter(); + } + else + { + mGroupDataCounter = value; + key.GroupDataCounter(); + } + + if (mStorage == nullptr) + { + return; + } + + // Always Update storage for Test purposes + temp = value + GROUP_MSG_COUNTER_MIN_INCREMENT; + mStorage->SyncSetKeyValue(key.KeyName(), &temp, sizeof(uint32_t)); + } +}; + +class TestGroupPeerTable : public chip::Transport::GroupPeerTable +{ +public: + TestGroupPeerTable(){}; + FabricIndex GetFabricIndexAt(uint8_t index) + { + if (index < CHIP_CONFIG_MAX_FABRICS) + { + return mGroupFabrics[index].mFabricIndex; + } + + return kUndefinedFabricIndex; + } + NodeId GetNodeIdAt(uint8_t fabricIndex, uint8_t index, bool isControl) + { + if (fabricIndex < CHIP_CONFIG_MAX_FABRICS) + { + if (isControl && index < CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS) + { + return mGroupFabrics[fabricIndex].mControlGroupSenders[index].mNodeId; + } + + if (!isControl && index < CHIP_CONFIG_MAX_GROUP_DATA_PEERS) + { + return mGroupFabrics[fabricIndex].mDataGroupSenders[index].mNodeId; + } + } + + return kUndefinedNodeId; + } +}; + +void AddPeerTest(nlTestSuite * inSuite, void * inContext) +{ + NodeId peerNodeId = 1234; + FabricIndex fabricIndex = 1; + uint32_t i = 0; + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + + do + { + err = mGroupPeerMsgCounter.FindOrAddPeer(fabricIndex, peerNodeId++, false, counter); + i++; + + } while (err != CHIP_ERROR_TOO_MANY_PEER_NODES); + + NL_TEST_ASSERT(inSuite, i == CHIP_CONFIG_MAX_GROUP_DATA_PEERS + 1); + + i = 1; + do + { + err = mGroupPeerMsgCounter.FindOrAddPeer(++fabricIndex, peerNodeId, false, counter); + i++; + } while (err != CHIP_ERROR_TOO_MANY_PEER_NODES); + NL_TEST_ASSERT(inSuite, i == CHIP_CONFIG_MAX_FABRICS + 1); +} + +void RemovePeerTest(nlTestSuite * inSuite, void * inContext) +{ + NodeId peerNodeId = 1234; + FabricIndex fabricIndex = 1; + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + + // Fill table up (max fabric and mac peer) + for (uint32_t it = 0; it < CHIP_CONFIG_MAX_FABRICS; it++) + { + for (uint32_t peerId = 0; peerId < CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS; peerId++) + { + err = mGroupPeerMsgCounter.FindOrAddPeer(fabricIndex, peerNodeId++, true, counter); + } + fabricIndex++; + } + // Verify that table is indeed full (for control Peer) + err = mGroupPeerMsgCounter.FindOrAddPeer(99, 99, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_TOO_MANY_PEER_NODES); + + // Clear all Peer + fabricIndex = 1; + peerNodeId = 1234; + for (uint32_t it = 0; it < CHIP_CONFIG_MAX_FABRICS; it++) + { + for (uint32_t peerId = 0; peerId < CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS; peerId++) + { + err = mGroupPeerMsgCounter.RemovePeer(fabricIndex, peerNodeId++, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + } + fabricIndex++; + } + + // Try re-adding the previous peer without any error + err = mGroupPeerMsgCounter.FindOrAddPeer(99, 99, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); +} + +void PeerRetrievalTest(nlTestSuite * inSuite, void * inContext) +{ + NodeId peerNodeId = 1234; + FabricIndex fabricIndex = 1; + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + chip::Transport::PeerMessageCounter * counter2 = nullptr; + chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + + err = mGroupPeerMsgCounter.FindOrAddPeer(fabricIndex, peerNodeId, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter != nullptr); + + err = mGroupPeerMsgCounter.FindOrAddPeer(99, 99, true, counter2); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter2 != nullptr); + NL_TEST_ASSERT(inSuite, counter2 != counter); + + err = mGroupPeerMsgCounter.FindOrAddPeer(fabricIndex, peerNodeId, true, counter2); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter2 == counter); +} + +void CounterCommitRolloverTest(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + + err = mGroupPeerMsgCounter.FindOrAddPeer(99, 99, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter != nullptr); + + err = counter->VerifyOrTrustFirst(UINT32_MAX, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + counter->CommitWithRollOver(UINT32_MAX); + + err = counter->VerifyOrTrustFirst(0, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + counter->CommitWithRollOver(0); + + err = counter->VerifyOrTrustFirst(1, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + counter->CommitWithRollOver(1); +} + +void CounterTrustFirstTest(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + chip::Transport::GroupPeerTable mGroupPeerMsgCounter; + + err = mGroupPeerMsgCounter.FindOrAddPeer(99, 99, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter != nullptr); + + err = counter->VerifyOrTrustFirst(5656, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + counter->CommitWithRollOver(5656); + + err = counter->VerifyOrTrustFirst(5756, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(5756); + err = counter->VerifyOrTrustFirst(4756, true); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + + // test sequential reception + err = counter->VerifyOrTrustFirst(5757, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(5757); + + err = counter->VerifyOrTrustFirst(5758, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(5758); + + err = counter->VerifyOrTrustFirst(5756, true); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + + // Test Roll over + err = mGroupPeerMsgCounter.FindOrAddPeer(1, 99, true, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter != nullptr); + + err = counter->VerifyOrTrustFirst(UINT32_MAX - 6, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(UINT32_MAX - 6); + + err = counter->VerifyOrTrustFirst(UINT32_MAX - 1, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(UINT32_MAX - 1); + + err = counter->VerifyOrTrustFirst(UINT32_MAX, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(0, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(1, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(2, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(3, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(4, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(5, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(6, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = counter->VerifyOrTrustFirst(7, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); +} + +void ReorderPeerRemovalTest(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + TestGroupPeerTable mGroupPeerMsgCounter; + + err = mGroupPeerMsgCounter.FindOrAddPeer(1, 1, true, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(1, 2, true, counter); + err = mGroupPeerMsgCounter.RemovePeer(1, 1, true); + + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetNodeIdAt(0, 0, true) == 2); + + // with other list + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 2, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 3, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 4, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 5, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 6, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 7, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 8, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 9, false, counter); + + err = mGroupPeerMsgCounter.RemovePeer(2, 7, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetNodeIdAt(1, 6, false) == 9); + + err = mGroupPeerMsgCounter.RemovePeer(2, 4, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetNodeIdAt(1, 3, false) == 8); + + err = mGroupPeerMsgCounter.RemovePeer(2, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetNodeIdAt(1, 0, false) == 9); +} + +#if !__ZEPHYR__ + +void ReorderFabricRemovalTest(nlTestSuite * inSuite, void * inContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::Transport::PeerMessageCounter * counter = nullptr; + TestGroupPeerTable mGroupPeerMsgCounter; + + err = mGroupPeerMsgCounter.FindOrAddPeer(1, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(2, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(3, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(4, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(5, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(6, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(7, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(8, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(9, 1, false, counter); + + err = counter->VerifyOrTrustFirst(5656, true); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + counter->CommitWithRollOver(5656); + + err = counter->VerifyOrTrustFirst(4756, true); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); + + err = mGroupPeerMsgCounter.FindOrAddPeer(10, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(11, 1, false, counter); + err = mGroupPeerMsgCounter.FindOrAddPeer(12, 1, false, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, counter != nullptr); + + err = mGroupPeerMsgCounter.RemovePeer(3, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetFabricIndexAt(2) == 12); + err = mGroupPeerMsgCounter.RemovePeer(8, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetFabricIndexAt(7) == 11); + err = mGroupPeerMsgCounter.RemovePeer(11, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetFabricIndexAt(7) == 10); + err = mGroupPeerMsgCounter.RemovePeer(1, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetFabricIndexAt(0) == 9); + err = mGroupPeerMsgCounter.RemovePeer(10, 1, false); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, mGroupPeerMsgCounter.GetFabricIndexAt(7) == 0); + + // Validate that counter value were moved around correctly + err = mGroupPeerMsgCounter.FindOrAddPeer(9, 1, false, counter); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = counter->VerifyOrTrustFirst(4756, true); + NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR); +} +#endif // !__ZEPHYR__ +void GroupMessageCounterTest(nlTestSuite * inSuite, void * inContext) +{ + + chip::TestPersistentStorageDelegate delegate; + TestGroupOutgoingCounters groupCientCounter; + CHIP_ERROR err = groupCientCounter.Init(&delegate); + + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Start Test with Control counter + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(true) == 0); + groupCientCounter.IncrementCounter(true); + + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(true) == 1); + + groupCientCounter.SetCounter(true, UINT32_MAX - GROUP_MSG_COUNTER_MIN_INCREMENT); + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(true) == UINT32_MAX - GROUP_MSG_COUNTER_MIN_INCREMENT); + + // Test Persistence + TestGroupOutgoingCounters groupCientCounter2(&delegate); + + NL_TEST_ASSERT(inSuite, groupCientCounter2.GetCounter(true) == UINT32_MAX); + NL_TEST_ASSERT(inSuite, groupCientCounter2.GetCounter(false) == GROUP_MSG_COUNTER_MIN_INCREMENT); + + // Test Roll over + groupCientCounter2.IncrementCounter(true); + NL_TEST_ASSERT(inSuite, groupCientCounter2.GetCounter(true) == 0); + + TestGroupOutgoingCounters groupCientCounter3(&delegate); + NL_TEST_ASSERT(inSuite, groupCientCounter3.GetCounter(true) == (UINT32_MAX + GROUP_MSG_COUNTER_MIN_INCREMENT)); + + // Redo the test with the second counter + + // Start Test with Control counter + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(false) == 0); + groupCientCounter.IncrementCounter(false); + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(false) == 1); + + groupCientCounter.SetCounter(false, UINT32_MAX - GROUP_MSG_COUNTER_MIN_INCREMENT); + NL_TEST_ASSERT(inSuite, groupCientCounter.GetCounter(false) == UINT32_MAX - GROUP_MSG_COUNTER_MIN_INCREMENT); + + // Test Persistence + TestGroupOutgoingCounters groupCientCounter4(&delegate); + + NL_TEST_ASSERT(inSuite, groupCientCounter4.GetCounter(false) == UINT32_MAX); + + // Test Roll over + groupCientCounter4.IncrementCounter(false); + NL_TEST_ASSERT(inSuite, groupCientCounter4.GetCounter(false) == 0); + + TestGroupOutgoingCounters groupCientCounter5(&delegate); + NL_TEST_ASSERT(inSuite, groupCientCounter5.GetCounter(false) == (UINT32_MAX + GROUP_MSG_COUNTER_MIN_INCREMENT)); +} + +} // namespace + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +const nlTest sTests[] = +{ + NL_TEST_DEF("Add Peer", AddPeerTest), + NL_TEST_DEF("Remove Peer", RemovePeerTest), + NL_TEST_DEF("Peer retrieval", PeerRetrievalTest), + NL_TEST_DEF("Counter Rollover", CounterCommitRolloverTest), + NL_TEST_DEF("Counter Trust first", CounterTrustFirstTest), + NL_TEST_DEF("Reorder Peer removal", ReorderPeerRemovalTest), + #if !__ZEPHYR__ + NL_TEST_DEF("Reorder Fabric Removal", ReorderFabricRemovalTest), + #endif + NL_TEST_DEF("Group Message Counter", GroupMessageCounterTest), + NL_TEST_SENTINEL() +}; +// clang-format on + +/** + * Main + */ +int TestGroupMessageCounter() +{ + // Run test suit against one context + + nlTestSuite theSuite = { "Transport-TestGroupMessageCounter", &sTests[0], nullptr, nullptr }; + nlTestRunner(&theSuite, nullptr); + + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestGroupMessageCounter); diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index c7e6aa27cf36e1..bb6ffc3e2a942b 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -91699,7 +91699,7 @@ class TestGroupMessaging : public TestCommand request.groupKeySet.groupKeySetID = 417U; request.groupKeySet.groupKeySecurityPolicy = - static_cast(0); + static_cast(1); request.groupKeySet.epochKey0.SetNonNull(); request.groupKeySet.epochKey0.Value() = chip::ByteSpan(chip::Uint8::from_const_char(