diff --git a/src/app/clusters/groups-server/groups-server.cpp b/src/app/clusters/groups-server/groups-server.cpp index 42cc5ee8bab2b4..452813c919f8ce 100644 --- a/src/app/clusters/groups-server/groups-server.cpp +++ b/src/app/clusters/groups-server/groups-server.cpp @@ -85,28 +85,6 @@ static bool GroupExists(FabricIndex fabricIndex, EndpointId endpointId, GroupId return provider->GroupMappingExists(fabricIndex, mapping); } -static bool GroupFind(FabricIndex fabricIndex, EndpointId endpointId, GroupId groupId, CharSpan & name) -{ - GroupDataProvider * provider = GetGroupDataProvider(); - VerifyOrReturnError(nullptr != provider, false); - - auto * groupIt = provider->IterateGroupMappings(fabricIndex, endpointId); - VerifyOrReturnError(nullptr != groupIt, false); - - GroupDataProvider::GroupMapping mapping; - bool found = false; - while (!found && groupIt->Next(mapping)) - { - if (mapping.group == groupId) - { - name = mapping.name; - found = true; - } - } - groupIt->Release(); - return found; -} - static EmberAfStatus GroupAdd(FabricIndex fabricIndex, EndpointId endpointId, GroupId groupId, const CharSpan & groupName) { VerifyOrReturnError(IsFabricGroupId(groupId), EMBER_ZCL_STATUS_INVALID_VALUE); @@ -217,22 +195,38 @@ bool emberAfGroupsClusterViewGroupCallback(app::CommandHandler * commandObj, con auto groupId = commandData.groupId; auto endpointId = commandPath.mEndpointId; + GroupDataProvider::GroupMapping mapping; EmberAfStatus status = EMBER_ZCL_STATUS_NOT_FOUND; CHIP_ERROR err = CHIP_NO_ERROR; - CharSpan groupName; + size_t nameSize = 0; if (emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST && emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST_REPLY) { return true; } - if (!IsFabricGroupId(groupId)) + if (IsFabricGroupId(groupId)) { - status = EMBER_ZCL_STATUS_INVALID_VALUE; + GroupDataProvider * provider = GetGroupDataProvider(); + VerifyOrReturnError(nullptr != provider, false); + + auto * groupIt = provider->IterateGroupMappings(fabricIndex, endpointId); + VerifyOrReturnError(nullptr != groupIt, false); + + while (groupIt->Next(mapping)) + { + if (mapping.group == groupId) + { + nameSize = strnlen(mapping.name, GroupDataProvider::GroupMapping::kGroupNameMax); + status = EMBER_ZCL_STATUS_SUCCESS; + break; + } + } + groupIt->Release(); } - else if (GroupFind(fabricIndex, endpointId, groupId, groupName)) + else { - status = EMBER_ZCL_STATUS_SUCCESS; + status = EMBER_ZCL_STATUS_INVALID_VALUE; } { @@ -243,7 +237,7 @@ bool emberAfGroupsClusterViewGroupCallback(app::CommandHandler * commandObj, con VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = writer->PutString(TLV::ContextTag(2), groupName.data(), static_cast(groupName.size()))); + SuccessOrExit(err = writer->PutString(TLV::ContextTag(2), mapping.name, static_cast(nameSize))); SuccessOrExit(err = commandObj->FinishCommand()); } exit: diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 5bfa9e18114b88..35175deb1669e6 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -93,6 +93,11 @@ CHIP_ERROR Server::Init(AppDelegate * delegate, uint16_t secureServicePort, uint err = mFabrics.Init(&mServerStorage); SuccessOrExit(err); + // Group data provider must be initialized after mServerStorage + err = mGroupsProvider.Init(); + SuccessOrExit(err); + SetGroupDataProvider(&mGroupsProvider); + // Init transport before operations with secure session mgr. err = mTransports.Init( UdpListenParameters(&DeviceLayer::InetLayer()).SetAddressType(IPAddressType::kIPv6).SetListenPort(mSecuredServicePort) diff --git a/src/app/server/Server.h b/src/app/server/Server.h index a4fedca91fd440..f432832128fc21 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -90,7 +92,7 @@ class Server static Server & GetInstance() { return sServer; } private: - Server() : mCommissioningWindowManager(this) {} + Server() : mCommissioningWindowManager(this), mGroupsProvider(mGroupsStorage) {} static Server sServer; @@ -145,9 +147,15 @@ class Server chip::Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient gUDCClient; #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT SecurePairingUsingTestSecret mTestPairing; + CommissioningWindowManager mCommissioningWindowManager; + // Both PersistentStorageDelegate, and GroupDataProvider should be injected by the applications + // See: https://github.com/project-chip/connectedhomeip/issues/12276 ServerStorageDelegate mServerStorage; - CommissioningWindowManager mCommissioningWindowManager; + // Currently, the GroupDataProvider cannot use KeyValueStoreMgr() due to + // (https://github.com/project-chip/connectedhomeip/issues/12174) + TestPersistentStorageDelegate mGroupsStorage; + Credentials::GroupDataProviderImpl mGroupsProvider; chip::OperationalDeviceProxy * mOperationalDeviceProxy = nullptr; diff --git a/src/app/tests/suites/TestGroupsCluster.yaml b/src/app/tests/suites/TestGroupsCluster.yaml index 3293eb68806366..992986f10dbaec 100644 --- a/src/app/tests/suites/TestGroupsCluster.yaml +++ b/src/app/tests/suites/TestGroupsCluster.yaml @@ -92,9 +92,20 @@ tests: - name: "groupId" value: 0x1111 + - label: "Get Group Membership 1 (all)" + command: "GetGroupMembership" + arguments: + values: + - name: "groupList" + value: [] + response: + values: + - name: "capacity" + value: 0xff + - name: "groupList" + value: [0x01] + - label: "Add Group 2 (new)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "AddGroup" arguments: values: @@ -110,8 +121,6 @@ tests: value: 0x1111 - label: "View Group 2 (new)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "ViewGroup" arguments: values: @@ -140,8 +149,6 @@ tests: value: 0x7fff - label: "Add Group 3 (new)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "AddGroup" arguments: values: @@ -172,8 +179,6 @@ tests: value: "Group #1" - label: "View Group 2 (existing)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "ViewGroup" arguments: values: @@ -188,39 +193,20 @@ tests: - name: "groupName" value: "Group #2" - - label: "Get Group Membership 1" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true - command: "GetGroupMembership" - arguments: - values: - - name: "groupList" - value: [0x01, 0x02, 0x03, 0x7fff] - response: - values: - - name: "capacity" - value: 0xff - - name: "groupList" - value: [0x01, 0x7fff] - - label: "Get Group Membership 2" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "GetGroupMembership" arguments: values: - name: "groupList" - value: [] + value: [0x02, 0x03, 0x7fff] response: values: - name: "capacity" value: 0xff - name: "groupList" - value: [0x01, 0x1111, 0x7fff] + value: [0x7fff] - label: "View Group 3 (new)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "ViewGroup" arguments: values: @@ -262,8 +248,6 @@ tests: value: 0x04 - label: "Remove Group 2 (existing)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "RemoveGroup" arguments: values: @@ -305,8 +289,6 @@ tests: value: 0x1111 - label: "View Group 3 (not removed)" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "ViewGroup" arguments: values: @@ -322,19 +304,17 @@ tests: value: "Group #3" - label: "Get Group Membership 3" - # https://github.com/project-chip/connectedhomeip/issues/11312 - disabled: true command: "GetGroupMembership" arguments: values: - name: "groupList" - value: [0x01, 0x02, 0x1111, 0x03, 0x7fff] + value: [0x01, 0x02, 0x1111, 0x03] response: values: - name: "capacity" value: 0xff - name: "groupList" - value: [0x01, 0x7fff] + value: [0x01] - label: "Remove All" command: "RemoveAllGroups" @@ -379,8 +359,6 @@ tests: value: 0x7fff - label: "Get Group Membership 4" - # https://github.com/project-chip/connectedhomeip/issues/11495 - disabled: true command: "GetGroupMembership" arguments: values: diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index fd43190c147d59..e0d5f418bae3b3 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -37,11 +37,11 @@ static_library("credentials") { "FabricTable.h", "GenerateChipX509Cert.cpp", "GroupDataProvider.h", + "GroupDataProviderImpl.cpp", "examples/DefaultDeviceAttestationVerifier.cpp", "examples/DefaultDeviceAttestationVerifier.h", "examples/DeviceAttestationCredsExample.cpp", "examples/DeviceAttestationCredsExample.h", - "examples/GroupDataProviderExample.cpp", ] if (chip_with_se05x == 1) { diff --git a/src/credentials/GroupDataProvider.h b/src/credentials/GroupDataProvider.h index 16812421d7a430..51e2de1b83c273 100644 --- a/src/credentials/GroupDataProvider.h +++ b/src/credentials/GroupDataProvider.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include #include @@ -31,7 +32,7 @@ class GroupDataProvider // An EpochKey is a single key usable to determine an operational group key struct EpochKey { - static constexpr size_t kLengthBytes = (128 / 8); + static constexpr size_t kLengthBytes = Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES; // Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch") uint64_t start_time; // Actual key bits. Depending on context, it may be a raw epoch key (as seen within `SetKeySet` calls) @@ -44,32 +45,68 @@ class GroupDataProvider // `endpoint` value. struct GroupMapping { + static constexpr size_t kGroupNameMax = CHIP_CONFIG_MAX_GROUP_NAME_LENGTH; + // The endpoint to which a GroupId is mapped. - EndpointId endpoint = 0; + EndpointId endpoint = kInvalidEndpointId; // The GroupId, which, when received in a message will map to the `endpoint`. - GroupId group = 0; + GroupId group = kUndefinedGroupId; // Group name - CharSpan name; + char name[kGroupNameMax + 1] = { 0 }; GroupMapping() = default; - GroupMapping(EndpointId eid, GroupId gid) : endpoint(eid), group(gid) {} - GroupMapping(EndpointId eid, GroupId gid, const CharSpan & groupName) : endpoint(eid), group(gid), name(groupName) {} - GroupMapping(EndpointId eid, GroupId gid, const char * groupName) : - endpoint(eid), group(gid), name(CharSpan(groupName, strnlen(groupName, CHIP_CONFIG_MAX_GROUP_NAME_LENGTH))) - {} - - bool operator==(const GroupMapping & other) { return this->endpoint == other.endpoint && this->group == other.group; } + GroupMapping(EndpointId eid, GroupId gid) : GroupMapping(eid, gid, nullptr) {} + GroupMapping(EndpointId eid, GroupId gid, const char * groupName) : endpoint(eid), group(gid) + { + if (nullptr == groupName) + { + name[0] = 0; + } + else + { + size_t size = strnlen(groupName, kGroupNameMax); + strncpy(name, groupName, size); + name[size] = 0; + } + } + GroupMapping(EndpointId eid, GroupId gid, const CharSpan & groupName) : endpoint(eid), group(gid) + { + if (nullptr == groupName.data()) + { + name[0] = 0; + } + else + { + size_t size = std::min(groupName.size(), kGroupNameMax); + strncpy(name, groupName.data(), size); + name[size] = 0; + } + } + bool operator==(const GroupMapping & other) + { + return (this->endpoint == other.endpoint) && (this->group == other.group) && + strncmp(this->name, other.name, kGroupNameMax); + } }; // A group state maps the group key set to use for encryption/decryption for a given group ID. struct GroupState { + GroupState() = default; + GroupState(chip::FabricIndex fabric, chip::GroupId group_id, uint16_t key_set) : + fabric_index(fabric), group(group_id), keyset_index(key_set) + {} // Fabric Index associated with the group state entry's fabric scoping - chip::FabricIndex fabric_index; + chip::FabricIndex fabric_index = kUndefinedFabricIndex; // Identifies the group within the scope of the given fabric - chip::GroupId group; + chip::GroupId group = kUndefinedGroupId; // References the set of group keys that generate operationa group keys for use with the given group - uint16_t key_set_index; + uint16_t keyset_index = 0; + bool operator==(const GroupState & other) + { + return this->fabric_index == other.fabric_index && this->group == other.group && + this->keyset_index == other.keyset_index; + } }; // A operational group key set, usable by many GroupState mappings @@ -81,74 +118,68 @@ class GroupDataProvider kLowLatency = 1 }; + KeySet() = default; + KeySet(uint16_t id) : keyset_id(id) {} + KeySet(uint16_t id, SecurityPolicy policy_id, uint8_t num_keys) : keyset_id(id), policy(policy_id), num_keys_used(num_keys) + {} + KeySet(SecurityPolicy policy_id, uint8_t num_keys) : keyset_id(0), policy(policy_id), num_keys_used(num_keys) {} + // The actual keys for the group key set EpochKey epoch_keys[3]; - // Logical index provided by the Administrator that configured the entry - uint16_t key_set_index; + // Logical id provided by the Administrator that configured the entry + uint16_t keyset_id = 0; // Security policy to use for groups that use this keyset - SecurityPolicy policy; + SecurityPolicy policy = SecurityPolicy::kStandard; // Number of keys present - uint8_t num_keys_used; - }; + uint8_t num_keys_used = 0; - // Iterator for group mappings under a given endpoint. Associated with - // Groups cluster logic. - class GroupMappingIterator - { - public: - virtual ~GroupMappingIterator() = default; - // Returns the number of entries in total that will be iterated. - virtual size_t Count() = 0; - // Returns true if a groupID is found in the iteration. - virtual bool Next(GroupMapping & mapping) = 0; - // Release the memory allocated by this iterator, if any. Must be called before - // losing scope of a `GroupMappingIterator *` - virtual void Release() = 0; - - protected: - GroupMappingIterator() = default; + bool operator==(const KeySet & other) + { + if (this->policy == other.policy && this->num_keys_used == other.num_keys_used) + { + return !memcmp(this->epoch_keys, other.epoch_keys, this->num_keys_used * sizeof(EpochKey)); + } + return false; + } }; - // Iterator for group state information mapping a Group ID to a Group Key Set index, - // such as reflected by the Groups attribute of the Group Key Management cluster. - class GroupStateIterator + /** + * Template used to iterate the stored group data + */ + template + class Iterator { public: - virtual ~GroupStateIterator() = default; - // Returns the number of entries in total that will be iterated. + virtual ~Iterator() = default; + /** + * @retval The number of entries in total that will be iterated. + */ virtual size_t Count() = 0; - // Returns true if a GroupState is found in the iteration. - virtual bool Next(GroupState & outEntry) = 0; - // Release the memory allocated by this iterator, if any. Must be called before - // losing scope of a `GroupStateIterator *` + /** + * @param[out] item Value associated with the next element in the iteration. + * @retval true if the next entry is successfully retrieved. + * @retval false if no more entries can be found. + */ + virtual bool Next(T & item) = 0; + /** + * Release the memory allocated by this iterator. + * Must be called before the pointer goes out of scope. + */ virtual void Release() = 0; protected: - GroupStateIterator() = default; + Iterator() = default; }; - // Iterator for the Group Key Sets related under a given Fabric. - // TODO: Refactor to allow trial decryption and encryption directly, rather than accessing raw keys. - class KeySetIterator - { - public: - virtual ~KeySetIterator() = default; - // Returns the number of entries in total that will be iterated. - virtual size_t Count() = 0; - // Returns true if a KeySet is found in the iteration. - virtual bool Next(KeySet & outSet) = 0; - // Release the memory allocated by this iterator, if any. Must be called before - // losing scope of a `KeySetIterator *` - virtual void Release() = 0; + using GroupMappingIterator = Iterator; + using GroupStateIterator = Iterator; + using KeySetIterator = Iterator; - protected: - KeySetIterator() = default; - }; - - // Interface for a listener for changes in any Group configuration. Necessary - // to implement attribute subscription for Group Key Management cluster, and - // to react to configuration changes that may impact in-progress functional - // work. + /** + * Interface for a listener of changes in any Group configuration. Necessary + * to implement attribute subscription for Group Key Management cluster, and + * to react to configuration changes that may impact in-progress functional work. + */ class GroupListener { public: @@ -185,46 +216,59 @@ class GroupDataProvider virtual CHIP_ERROR Init() = 0; virtual void Finish() = 0; - // Endpoints + // + // Group Mappings + // + virtual bool HasGroupNamesSupport() = 0; virtual bool GroupMappingExists(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0; virtual CHIP_ERROR AddGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0; virtual CHIP_ERROR RemoveGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) = 0; virtual CHIP_ERROR RemoveAllGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) = 0; /** - * Returns an iterator that may be used to obtain the GroupMappings for the given endpoint. The number - * of concurrent iterator instances is limited, and must be released using their own Release() method. + * Creates an iterator that may be used to obtain the groups associated with the given fabric and endpoint. + * The number of concurrent instances of this iterator is limited. In order to release the allocated memory, + * the iterator's Release() method must be called after the iteration is finished. * @retval An instance of GroupMappingIterator on success * @retval nullptr if no iterator instances are available. */ virtual GroupMappingIterator * IterateGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) = 0; - // States + // + // Group States + // + virtual CHIP_ERROR SetGroupState(size_t state_index, const GroupState & state) = 0; virtual CHIP_ERROR GetGroupState(size_t state_index, GroupState & state) = 0; virtual CHIP_ERROR RemoveGroupState(size_t state_index) = 0; /** - * Returns an iterator that may be used to obtain the GroupStates for all fabrics. The number - * of concurrent iterator instances is limited, and must be released using their own Release() method. + * Creates an iterator that may be used to obtain the list of group states. + * The number of concurrent instances of this iterator is limited. In order to release the allocated memory, + * the iterator's Release() method must be called after the iteration is finished. * @retval An instance of GroupStateIterator on success * @retval nullptr if no iterator instances are available. */ virtual GroupStateIterator * IterateGroupStates() = 0; /** - * Returns an iterator that may be used to obtain the GroupStates for the given fabric. The number - * of concurrent iterator instances is limited, and must be released using their own Release() method. + * Creates an iterator that may be used to obtain the list of group states associated with the given fabric. + * The number of concurrent instances of this iterator is limited. In order to release the allocated memory, + * the iterator's Release() method must be called after the iteration is finished. * @retval An instance of GroupStateIterator on success * @retval nullptr if no iterator instances are available. */ virtual GroupStateIterator * IterateGroupStates(chip::FabricIndex fabric_index) = 0; - // Keys - virtual CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index, const KeySet & keys) = 0; - virtual CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index, KeySet & keys) = 0; - virtual CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index) = 0; + // + // Key Sets + // + + virtual CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, const KeySet & keys) = 0; + virtual CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, KeySet & keys) = 0; + virtual CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id) = 0; /** - * Returns an iterator that may be used to obtain the KeySets associated with the given fabric. The number - * of concurrent iterator instances is limited, and must be released using their own Release() method. + * Creates an iterator that may be used to obtain the list of key sets associated with the given fabric. + * The number of concurrent instances of this iterator is limited. In order to release the allocated memory, + * the iterator's Release() method must be called after the iteration is finished. * @retval An instance of KeySetIterator on success * @retval nullptr if no iterator instances are available. */ @@ -234,7 +278,7 @@ class GroupDataProvider virtual CHIP_ERROR RemoveFabric(chip::FabricIndex fabric_index) = 0; // General - virtual CHIP_ERROR Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, System::PacketBufferHandle && msg) = 0; + virtual CHIP_ERROR Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, System::PacketBufferHandle & msg) = 0; // Listener void SetListener(GroupListener * listener) { mListener = listener; }; diff --git a/src/credentials/GroupDataProviderImpl.cpp b/src/credentials/GroupDataProviderImpl.cpp new file mode 100644 index 00000000000000..cc95cd935316f3 --- /dev/null +++ b/src/credentials/GroupDataProviderImpl.cpp @@ -0,0 +1,1490 @@ +/* + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Credentials { + +static constexpr size_t kPersistentBufferMax = 128; + +template +struct PersistentData +{ +public: + virtual ~PersistentData() = default; + + virtual void UpdateKey(DefaultStorageKeyAllocator & key) = 0; + virtual void Clear() = 0; + virtual CHIP_ERROR Serialize(TLV::TLVWriter & writer) const = 0; + virtual CHIP_ERROR Deserialize(TLV::TLVReader & reader) = 0; + + CHIP_ERROR Save(chip::PersistentStorageDelegate & storage) + { + uint8_t buffer[kMaxSerializedSize] = { 0 }; + DefaultStorageKeyAllocator key; + // Update storage key + UpdateKey(key); + + // Serialize the data + TLV::TLVWriter writer; + writer.Init(buffer, sizeof(buffer)); + ReturnErrorOnFailure(Serialize(writer)); + + // Save serialized data + ReturnErrorOnFailure(storage.SyncSetKeyValue(key.KeyName(), buffer, static_cast(writer.GetLengthWritten()))); + + // WARNING: This read-back is a walkaround for a bug in the Darwing implementation of KeyValueStoreMgr + // See https://github.com/project-chip/connectedhomeip/issues/12174. + uint16_t size = static_cast(sizeof(buffer)); + return storage.SyncGetKeyValue(key.KeyName(), buffer, size); + } + + CHIP_ERROR Load(chip::PersistentStorageDelegate & storage) + { + uint8_t buffer[kMaxSerializedSize] = { 0 }; + DefaultStorageKeyAllocator key; + + // Set data to defaults + Clear(); + + // Update storage key + UpdateKey(key); + + // Load the serialized data + uint16_t size = static_cast(sizeof(buffer)); + ReturnErrorOnFailure(storage.SyncGetKeyValue(key.KeyName(), buffer, size)); + + // Decode serialized data + TLV::TLVReader reader; + reader.Init(buffer, size); + + return Deserialize(reader); + } + + CHIP_ERROR Delete(chip::PersistentStorageDelegate & storage) + { + DefaultStorageKeyAllocator key; + + // Update storage key + UpdateKey(key); + // Delete stored data + return storage.SyncDeleteKeyValue(key.KeyName()); + } +}; + +struct FabricData : public PersistentData +{ + static const TLV::Tag kTagFirstEndpoint = TLV::ContextTag(1); + static const TLV::Tag kTagEndpointCount = TLV::ContextTag(2); + static const TLV::Tag kTagFirstKeySet = TLV::ContextTag(3); + static const TLV::Tag kTagKeySetCount = TLV::ContextTag(4); + + chip::FabricIndex fabric = kUndefinedFabricIndex; + chip::EndpointId first_endpoint = kInvalidEndpointId; + uint16_t endpoint_count = 0; + uint16_t first_keyset = 0; + uint16_t keyset_count = 0; + + FabricData(chip::FabricIndex fabric_index) : fabric(fabric_index) {} + + void UpdateKey(DefaultStorageKeyAllocator & key) override { key.FabricGroups(this->fabric); } + + void Clear() override + { + first_endpoint = kInvalidEndpointId; + endpoint_count = 0; + first_keyset = 0; + keyset_count = 0; + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + ReturnErrorOnFailure(writer.Put(kTagFirstEndpoint, static_cast(first_endpoint))); + ReturnErrorOnFailure(writer.Put(kTagEndpointCount, static_cast(endpoint_count))); + ReturnErrorOnFailure(writer.Put(kTagFirstKeySet, static_cast(first_keyset))); + ReturnErrorOnFailure(writer.Put(kTagKeySetCount, static_cast(keyset_count))); + + return writer.EndContainer(container); + } + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // first_endpoint + ReturnErrorOnFailure(reader.Next(kTagFirstEndpoint)); + ReturnErrorOnFailure(reader.Get(first_endpoint)); + // endpoint_count + ReturnErrorOnFailure(reader.Next(kTagEndpointCount)); + ReturnErrorOnFailure(reader.Get(endpoint_count)); + // first_keyset + ReturnErrorOnFailure(reader.Next(kTagFirstKeySet)); + ReturnErrorOnFailure(reader.Get(first_keyset)); + // keyset_count + ReturnErrorOnFailure(reader.Next(kTagKeySetCount)); + ReturnErrorOnFailure(reader.Get(keyset_count)); + + return reader.ExitContainer(container); + } +}; + +struct EndpointData : public PersistentData +{ + static const TLV::Tag kTagFirstGroup = TLV::ContextTag(1); + static const TLV::Tag kTagNextEndpoint = TLV::ContextTag(2); + + chip::FabricIndex fabric = kUndefinedFabricIndex; + chip::EndpointId endpoint_id = 0; + chip::GroupId first_group = kUndefinedGroupId; + chip::EndpointId next = kInvalidEndpointId; + + EndpointData() = default; + + EndpointData(chip::FabricIndex fabric_index, chip::EndpointId endpoint = kInvalidEndpointId, + chip::GroupId first = kUndefinedGroupId, chip::EndpointId next_endpoint = kInvalidEndpointId) : + fabric(fabric_index), + endpoint_id(endpoint), first_group(first), next(next_endpoint) + {} + + void UpdateKey(DefaultStorageKeyAllocator & key) override { key.FabricEndpoint(this->fabric, this->endpoint_id); } + + void Clear() override + { + first_group = kUndefinedGroupId; + next = kInvalidEndpointId; + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + ReturnErrorOnFailure(writer.Put(kTagFirstGroup, static_cast(first_group))); + ReturnErrorOnFailure(writer.Put(kTagNextEndpoint, static_cast(next))); + + return writer.EndContainer(container); + } + + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // first_group + ReturnErrorOnFailure(reader.Next(kTagFirstGroup)); + ReturnErrorOnFailure(reader.Get(first_group)); + // next + ReturnErrorOnFailure(reader.Next(kTagNextEndpoint)); + ReturnErrorOnFailure(reader.Get(next)); + + return reader.ExitContainer(container); + } +}; + +struct GroupData : public GroupDataProvider::GroupMapping, PersistentData +{ + static const TLV::Tag kTagName = TLV::ContextTag(1); + static const TLV::Tag kTagNext = TLV::ContextTag(2); + + chip::FabricIndex fabric = kUndefinedFabricIndex; + chip::GroupId next = 0; + + GroupData(chip::FabricIndex fabric_index, chip::EndpointId endpoint_id, chip::GroupId group_id, + chip::GroupId next_group = kUndefinedGroupId) : + GroupDataProvider::GroupMapping(endpoint_id, group_id), + fabric(fabric_index), next(next_group) + {} + + GroupData(chip::FabricIndex fabric_index, chip::EndpointId endpoint_id, chip::GroupId group_id, const char * group_name, + chip::GroupId next_group = kUndefinedGroupId) : + GroupDataProvider::GroupMapping(endpoint_id, group_id, group_name), + fabric(fabric_index), next(next_group) + {} + + void UpdateKey(DefaultStorageKeyAllocator & key) override + { + key.FabricEndpointGroup(this->fabric, this->endpoint, this->group); + } + + void Clear() override + { + name[0] = 0; + next = kUndefinedGroupId; + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + size_t name_size = strnlen(this->name, GroupDataProvider::GroupMapping::kGroupNameMax); + ReturnErrorOnFailure(writer.PutString(kTagName, this->name, static_cast(name_size))); + ReturnErrorOnFailure(writer.Put(kTagNext, static_cast(this->next))); + + return writer.EndContainer(container); + } + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // name + ReturnErrorOnFailure(reader.Next(kTagName)); + ReturnErrorOnFailure(reader.GetString(this->name, sizeof(this->name))); + size_t size = strnlen(this->name, kGroupNameMax); + this->name[size] = 0; + // next + ReturnErrorOnFailure(reader.Next(kTagNext)); + ReturnErrorOnFailure(reader.Get(this->next)); + + return reader.ExitContainer(container); + } +}; + +struct StateListData : public PersistentData +{ + static const TLV::Tag kTagFirst = TLV::ContextTag(1); + static const TLV::Tag kTagCount = TLV::ContextTag(2); + + uint16_t first = 0; + uint16_t count = 0; + + void UpdateKey(DefaultStorageKeyAllocator & key) override { key.GroupStates(); } + + void Clear() override + { + first = 0; + count = 0; + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + ReturnErrorOnFailure(writer.Put(kTagFirst, static_cast(first))); + ReturnErrorOnFailure(writer.Put(kTagCount, static_cast(count))); + + return writer.EndContainer(container); + } + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // first + ReturnErrorOnFailure(reader.Next(kTagFirst)); + ReturnErrorOnFailure(reader.Get(first)); + // count + ReturnErrorOnFailure(reader.Next(kTagCount)); + ReturnErrorOnFailure(reader.Get(count)); + + return reader.ExitContainer(container); + } +}; + +struct StateData : public GroupDataProvider::GroupState, PersistentData +{ + static const TLV::Tag kTagFabric = TLV::ContextTag(1); + static const TLV::Tag kTagGroup = TLV::ContextTag(2); + static const TLV::Tag kTagKeySet = TLV::ContextTag(3); + static const TLV::Tag kTagNext = TLV::ContextTag(4); + + // Linked-list entry id. May be different to state_index + uint16_t id = 0; + // Index of the next element of the linked-list + uint16_t next = 0; + + StateData() = default; + StateData(uint16_t state_id, chip::FabricIndex fabric = kUndefinedFabricIndex, chip::GroupId group_id = kUndefinedGroupId, + uint16_t key_set = 0) : + GroupState(fabric, group_id, key_set), + id(state_id) + {} + + void UpdateKey(DefaultStorageKeyAllocator & key) override { key.GroupState(this->id); } + + void Clear() override + { + fabric_index = 0; + group = kUndefinedGroupId; + keyset_index = 0; + next = 0; + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + ReturnErrorOnFailure(writer.Put(kTagFabric, static_cast(fabric_index))); + ReturnErrorOnFailure(writer.Put(kTagGroup, static_cast(group))); + ReturnErrorOnFailure(writer.Put(kTagKeySet, static_cast(keyset_index))); + ReturnErrorOnFailure(writer.Put(kTagNext, static_cast(next))); + + return writer.EndContainer(container); + } + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // fabric_index + ReturnErrorOnFailure(reader.Next(kTagFabric)); + ReturnErrorOnFailure(reader.Get(fabric_index)); + // group + ReturnErrorOnFailure(reader.Next(kTagGroup)); + ReturnErrorOnFailure(reader.Get(group)); + // keyset_index + ReturnErrorOnFailure(reader.Next(kTagKeySet)); + ReturnErrorOnFailure(reader.Get(keyset_index)); + // next + ReturnErrorOnFailure(reader.Next(kTagNext)); + ReturnErrorOnFailure(reader.Get(next)); + + return reader.ExitContainer(container); + } +}; + +struct KeySetData : public GroupDataProvider::KeySet, PersistentData +{ + static const TLV::Tag kTagKeySetId = TLV::ContextTag(1); + static const TLV::Tag kTagPolicy = TLV::ContextTag(2); + static const TLV::Tag kTagNumKeys = TLV::ContextTag(3); + static const TLV::Tag kTagEpochKeys = TLV::ContextTag(4); + static const TLV::Tag kTagStartTime = TLV::ContextTag(5); + static const TLV::Tag kTagKey = TLV::ContextTag(6); + static const TLV::Tag kTagNext = TLV::ContextTag(7); + + chip::FabricIndex fabric = kUndefinedFabricIndex; + uint16_t next = 0; + + KeySetData() = default; + KeySetData(chip::FabricIndex fabric_index, uint16_t id) : KeySet(id), fabric(fabric_index) {} + KeySetData(chip::FabricIndex fabric_index, uint16_t id, SecurityPolicy policy_id, uint8_t num_keys) : + KeySet(id, policy_id, num_keys), fabric(fabric_index) + {} + + void UpdateKey(DefaultStorageKeyAllocator & key) override { key.FabricKeySet(this->fabric, this->keyset_id); } + + void Clear() override + { + policy = KeySet::SecurityPolicy::kStandard; + num_keys_used = 0; + next = 0; + memset(epoch_keys, 0x00, sizeof(epoch_keys)); + } + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, container)); + + // keyset_id + ReturnErrorOnFailure(writer.Put(kTagKeySetId, static_cast(keyset_id))); + // policy + ReturnErrorOnFailure(writer.Put(kTagPolicy, static_cast(policy))); + // num_keys_used + ReturnErrorOnFailure(writer.Put(kTagNumKeys, static_cast(num_keys_used))); + // epoch_keys + { + TLV::TLVType array, item; + ReturnErrorOnFailure(writer.StartContainer(kTagEpochKeys, TLV::kTLVType_Array, array)); + for (auto & epoch : this->epoch_keys) + { + ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, item)); + ReturnErrorOnFailure(writer.Put(kTagStartTime, static_cast(epoch.start_time))); + ReturnErrorOnFailure(writer.Put(kTagKey, ByteSpan(epoch.key, GroupDataProvider::EpochKey::kLengthBytes))); + ReturnErrorOnFailure(writer.EndContainer(item)); + } + ReturnErrorOnFailure(writer.EndContainer(array)); + } + // next keyset + ReturnErrorOnFailure(writer.Put(kTagNext, static_cast(next))); + + return writer.EndContainer(container); + } + + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType container; + ReturnErrorOnFailure(reader.EnterContainer(container)); + + // keyset_id + ReturnErrorOnFailure(reader.Next(kTagKeySetId)); + ReturnErrorOnFailure(reader.Get(keyset_id)); + // policy + ReturnErrorOnFailure(reader.Next(kTagPolicy)); + ReturnErrorOnFailure(reader.Get(policy)); + // num_keys_used + ReturnErrorOnFailure(reader.Next(kTagNumKeys)); + ReturnErrorOnFailure(reader.Get(num_keys_used)); + { + // epoch_keys + ReturnErrorOnFailure(reader.Next(kTagEpochKeys)); + VerifyOrReturnError(TLV::kTLVType_Array == reader.GetType(), CHIP_ERROR_INTERNAL); + + TLV::TLVType array, item; + ReturnErrorOnFailure(reader.EnterContainer(array)); + for (auto & epoch : this->epoch_keys) + { + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag)); + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_INTERNAL); + + ReturnErrorOnFailure(reader.EnterContainer(item)); + // start_time + ReturnErrorOnFailure(reader.Next(kTagStartTime)); + ReturnErrorOnFailure(reader.Get(epoch.start_time)); + // key + ByteSpan key; // epoch.key, + ReturnErrorOnFailure(reader.Next(kTagKey)); + ReturnErrorOnFailure(reader.Get(key)); + VerifyOrReturnError(GroupDataProvider::EpochKey::kLengthBytes == key.size(), CHIP_ERROR_INTERNAL); + memcpy(epoch.key, key.data(), GroupDataProvider::EpochKey::kLengthBytes); + ReturnErrorOnFailure(reader.ExitContainer(item)); + } + ReturnErrorOnFailure(reader.ExitContainer(array)); + } + // next keyset + ReturnErrorOnFailure(reader.Next(kTagNext)); + ReturnErrorOnFailure(reader.Get(next)); + + return reader.ExitContainer(container); + } +}; + +// +// General +// + +CHIP_ERROR GroupDataProviderImpl::Init() +{ + mInitialized = true; + return CHIP_NO_ERROR; +} + +void GroupDataProviderImpl::Finish() +{ + mInitialized = false; +} + +// +// Group Mappings +// + +constexpr size_t GroupDataProvider::GroupMapping::kGroupNameMax; + +bool GroupDataProviderImpl::HasGroupNamesSupport() +{ + return true; +} + +bool GroupDataProviderImpl::GroupMappingExists(chip::FabricIndex fabric_index, const GroupMapping & mapping) +{ + VerifyOrReturnError(mInitialized, false); + + FabricData fabric(fabric_index); + + VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), false); + VerifyOrReturnError(kInvalidEndpointId != fabric.first_endpoint, false); + + // Existing fabric + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + size_t endpoint_count = 0; + + // Loop through the fabric's endpoints + do + { + VerifyOrReturnError(CHIP_NO_ERROR == endpoint.Load(mStorage), false); + if (endpoint.endpoint_id == mapping.endpoint) + { + // Target endpoint found + break; + } + endpoint.endpoint_id = endpoint.next; + } while ((++endpoint_count < fabric.endpoint_count) && (kInvalidEndpointId != endpoint.endpoint_id)); + VerifyOrReturnError(endpoint.endpoint_id == mapping.endpoint, false); + + // Target endpoint found + + GroupData group(fabric_index, mapping.endpoint, endpoint.first_group); + + // Loop through the endpoint's groups + do + { + // Load next group + VerifyOrReturnError(CHIP_NO_ERROR == group.Load(mStorage), false); + if (group.group == mapping.group) + { + // Target group found + return true; + } + group.group = group.next; + } while (kUndefinedGroupId != group.group); + + // Not found + return false; +} + +CHIP_ERROR GroupDataProviderImpl::AddGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(kInvalidEndpointId != mapping.endpoint, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsFabricGroupId(mapping.group), CHIP_ERROR_INVALID_ARGUMENT); + + CHIP_ERROR err = CHIP_NO_ERROR; + FabricData fabric(fabric_index); + + if (CHIP_NO_ERROR != fabric.Load(mStorage) || kInvalidEndpointId == fabric.first_endpoint) + { + // New fabric, or new endpoint + GroupData group(fabric_index, mapping.endpoint, mapping.group, mapping.name); + ReturnErrorOnFailure(group.Save(mStorage)); + + EndpointData endpoint(fabric_index, mapping.endpoint, mapping.group); + err = endpoint.Save(mStorage); + if (CHIP_NO_ERROR != err) + { + // Clean-up the orphan group + group.Delete(mStorage); + return err; + } + + fabric.first_endpoint = mapping.endpoint; + fabric.endpoint_count = 1; + err = fabric.Save(mStorage); + if (CHIP_NO_ERROR != err) + { + // Clean-up the orphan entries + group.Delete(mStorage); + endpoint.Delete(mStorage); + } + return err; + } + + // Existing fabric + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + size_t endpoint_count = 0; + + // Loop through the fabric's endpoints + do + { + if (CHIP_NO_ERROR != endpoint.Load(mStorage)) + { + // Endpoint info not found + break; + } + if (endpoint.endpoint_id == mapping.endpoint) + { + // Target endpoint info found + break; + } + endpoint.endpoint_id = endpoint.next; + } while ((++endpoint_count < fabric.endpoint_count) && (kInvalidEndpointId != endpoint.endpoint_id)); + + if (endpoint.endpoint_id != mapping.endpoint) + { + // Endpoint not found, create new, and insert first + GroupData group(fabric_index, mapping.endpoint, mapping.group, mapping.name); + ReturnErrorOnFailure(group.Save(mStorage)); + + endpoint.endpoint_id = mapping.endpoint; + endpoint.first_group = mapping.group; + endpoint.next = fabric.first_endpoint; + err = endpoint.Save(mStorage); + if (CHIP_NO_ERROR != err) + { + // Clean-up the orphan group + group.Delete(mStorage); + return err; + } + + fabric.first_endpoint = mapping.endpoint; + fabric.endpoint_count++; + return fabric.Save(mStorage); + } + + // Endpoint info found, loop endpoints + + GroupData group(fabric_index, endpoint.endpoint_id, endpoint.first_group); + + do + { + if (CHIP_NO_ERROR != group.Load(mStorage)) + { + break; + } + if (group.group == mapping.group) + { + // Duplicated group + return CHIP_NO_ERROR; + } + group.group = group.next; + } while (kUndefinedGroupId != group.group); + + // Insert group first, update endpoint + ReturnErrorOnFailure( + GroupData(fabric_index, mapping.endpoint, mapping.group, mapping.name, endpoint.first_group).Save(mStorage)); + endpoint.first_group = mapping.group; + return endpoint.Save(mStorage); +} + +CHIP_ERROR GroupDataProviderImpl::RemoveGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(kInvalidEndpointId != mapping.endpoint, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsFabricGroupId(mapping.group), CHIP_ERROR_INVALID_ARGUMENT); + + FabricData fabric(fabric_index); + + VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_INVALID_FABRIC_ID); + VerifyOrReturnError(kInvalidEndpointId != fabric.first_endpoint, CHIP_ERROR_KEY_NOT_FOUND); + + // Existing fabric + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + size_t endpoint_count = 0; + + // Loop through the fabric's endpoints + do + { + VerifyOrReturnError(CHIP_NO_ERROR == endpoint.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + if (endpoint.endpoint_id == mapping.endpoint) + { + // Target endpoint info found + break; + } + endpoint.endpoint_id = endpoint.next; + } while ((++endpoint_count < fabric.endpoint_count) && (kInvalidEndpointId != endpoint.endpoint_id)); + VerifyOrReturnError(endpoint.endpoint_id == mapping.endpoint, CHIP_ERROR_KEY_NOT_FOUND); + + // Target endpoint found + + GroupData group(fabric_index, endpoint.endpoint_id, endpoint.first_group); + chip::GroupId prev_gid = kUndefinedGroupId; + + do + { + VerifyOrReturnError(CHIP_NO_ERROR == group.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + if (group.group == mapping.group) + { + // Target group found + break; + } + prev_gid = group.group; + group.group = group.next; + } while (kUndefinedGroupId != group.group); + + VerifyOrReturnError(group.group == mapping.group, CHIP_ERROR_KEY_NOT_FOUND); + + // Target group found, remove + ReturnErrorOnFailure(group.Delete(mStorage)); + + if (prev_gid == kUndefinedGroupId) + { + // Removing first group, update endpoint + endpoint.first_group = group.next; + return endpoint.Save(mStorage); + } + + // Removing intermediate group, update previous + + GroupData prev_group(fabric_index, endpoint.endpoint_id, prev_gid); + ReturnErrorOnFailure(prev_group.Load(mStorage)); + + prev_group.next = group.next; + return prev_group.Save(mStorage); +} + +CHIP_ERROR GroupDataProviderImpl::RemoveAllGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint_id) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT); + + FabricData fabric(fabric_index); + + VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_INVALID_FABRIC_ID); + VerifyOrReturnError(kInvalidEndpointId != fabric.first_endpoint, CHIP_ERROR_KEY_NOT_FOUND); + + // Existing fabric + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + chip::EndpointId prev_eid = kInvalidEndpointId; + size_t endpoint_count = 0; + + // Loop through the fabric's endpoints + do + { + VerifyOrReturnError(CHIP_NO_ERROR == endpoint.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + if (endpoint.endpoint_id == endpoint_id) + { + // Target endpoint info found + break; + } + prev_eid = endpoint.endpoint_id; + endpoint.endpoint_id = endpoint.next; + } while ((++endpoint_count < fabric.endpoint_count) && (kInvalidEndpointId != endpoint.endpoint_id)); + VerifyOrReturnError(endpoint.endpoint_id == endpoint_id, CHIP_ERROR_KEY_NOT_FOUND); + + // Target endpoint found + GroupData group(fabric_index, endpoint.endpoint_id, endpoint.first_group); + + // Remove endpoint's groups + do + { + ReturnErrorOnFailure(group.Load(mStorage)); + group.Delete(mStorage); + group.group = group.next; + } while (kUndefinedGroupId != group.group); + + // Remove endpoint + ReturnErrorOnFailure(endpoint.Delete(mStorage)); + + if (kInvalidEndpointId == prev_eid) + { + // First endpoint, update fabric info + fabric.first_endpoint = endpoint.next; + return fabric.Save(mStorage); + } + else + { + // Mid endpoint, update previous endpoint's info + EndpointData prev_endpoint(fabric_index, prev_eid); + ReturnErrorOnFailure(prev_endpoint.Load(mStorage)); + prev_endpoint.next = endpoint.next; + return prev_endpoint.Save(mStorage); + } +} + +GroupDataProvider::GroupMappingIterator * GroupDataProviderImpl::IterateGroupMappings(chip::FabricIndex fabric_index, + EndpointId endpoint_id) +{ + VerifyOrReturnError(mInitialized, nullptr); + return mEndpointIterators.CreateObject(*this, fabric_index, endpoint_id); +} + +GroupDataProviderImpl::GroupMappingIteratorImpl::GroupMappingIteratorImpl(GroupDataProviderImpl & provider, + chip::FabricIndex fabric_index, + chip::EndpointId endpoint_id) : + mProvider(provider), + mFabric(fabric_index), mEndpoint(endpoint_id) +{ + FabricData fabric(fabric_index); + ReturnOnFailure(fabric.Load(provider.mStorage)); + + // Existing fabric + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + uint16_t count = 0; + + // Loop through the fabric's endpoints + do + { + ReturnOnFailure(endpoint.Load(provider.mStorage)); + if (endpoint.endpoint_id == endpoint_id) + { + // Target endpoint found + mGroup = endpoint.first_group; + break; + } + endpoint.endpoint_id = endpoint.next; + } while (++count < fabric.endpoint_count); +} + +size_t GroupDataProviderImpl::GroupMappingIteratorImpl::Count() +{ + size_t count = 0; + + GroupData group(mFabric, mEndpoint, mGroup); + chip::GroupId prev_gid = kUndefinedGroupId; + + while ((kUndefinedGroupId != group.group) && (prev_gid != group.group)) + { + if (CHIP_NO_ERROR != group.Load(mProvider.mStorage)) + { + break; + } + prev_gid = group.group; + group.group = group.next; + count++; + } + return count; +} + +bool GroupDataProviderImpl::GroupMappingIteratorImpl::Next(GroupMapping & item) +{ + GroupData group(mFabric, mEndpoint, mGroup); + CHIP_ERROR err = group.Load(mProvider.mStorage); + if ((kUndefinedGroupId == mGroup) || (CHIP_NO_ERROR != err)) + { + return false; + } + item.endpoint = mEndpoint; + item.group = mGroup; + size_t size = strnlen(group.name, GroupData::kGroupNameMax); + strncpy(item.name, group.name, size); + item.name[size] = 0; + mGroup = group.next; + return true; +} + +void GroupDataProviderImpl::GroupMappingIteratorImpl::Release() +{ + mProvider.mEndpointIterators.ReleaseObject(this); +} + +// +// Group States +// + +CHIP_ERROR GroupDataProviderImpl::SetGroupState(size_t state_index, const GroupState & in_state) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(IsFabricGroupId(in_state.group), CHIP_ERROR_INVALID_ARGUMENT); + + StateListData states; + + if (CHIP_NO_ERROR != states.Load(mStorage)) + { + // First state + VerifyOrReturnError(0 == state_index, CHIP_ERROR_INVALID_ARGUMENT); + states.first = 0; + states.count = 1; + ReturnLogErrorOnFailure( + StateData(states.first, in_state.fabric_index, in_state.group, in_state.keyset_index).Save(mStorage)); + return states.Save(mStorage); + } + + StateData state(states.first); + StateData prev_state; + uint16_t index = 0; + uint16_t new_id = 0; + bool found = false; + bool previous = false; + + // Loop until the desired index + do + { + if (CHIP_NO_ERROR != state.Load(mStorage)) + { + break; + } + if (new_id == state.id) + { + // Used index, keep looking + new_id++; + } + if (index == state_index) + { + // Target index found + found = true; + break; + } + previous = true; + prev_state = state; + state.id = state.next; + } while (++index < states.count); + + VerifyOrReturnError(state_index <= index, CHIP_ERROR_INVALID_ARGUMENT); + + if (found) + { + // Update existing state, must be in the same fabric + VerifyOrReturnError(state.fabric_index == in_state.fabric_index, CHIP_ERROR_ACCESS_DENIED); + GroupState old_state = state; + state.group = in_state.group; + state.keyset_index = in_state.keyset_index; + ReturnErrorOnFailure(state.Save(mStorage)); + if (nullptr != mListener) + { + mListener->OnGroupStateChanged(&old_state, &state); + } + return CHIP_NO_ERROR; + } + + // New state + ReturnErrorOnFailure(StateData(new_id, in_state.fabric_index, in_state.group, in_state.keyset_index).Save(mStorage)); + + if (previous) + { + // New middle state, update previous + prev_state.next = new_id; + ReturnErrorOnFailure(prev_state.Save(mStorage)); + } + else + { + // New first state + states.first = new_id; + } + // Update main list + states.count = static_cast(index + 1); + return states.Save(mStorage); + + return CHIP_ERROR_INTERNAL; +} + +CHIP_ERROR GroupDataProviderImpl::GetGroupState(size_t state_index, GroupState & out_state) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + + StateListData states; + VerifyOrReturnError(CHIP_NO_ERROR == states.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + + // Fabric info found + + StateData state(states.first); + size_t index = 0; + + // Loop until the desired index + while (index < states.count) + { + if (CHIP_NO_ERROR != state.Load(mStorage)) + { + break; + } + if (index == state_index) + { + // Target index found + out_state.fabric_index = state.fabric_index; + out_state.group = state.group; + out_state.keyset_index = state.keyset_index; + return CHIP_NO_ERROR; + } + state.id = state.next; + index++; + } + return CHIP_ERROR_KEY_NOT_FOUND; +} + +CHIP_ERROR GroupDataProviderImpl::RemoveGroupState(size_t state_index) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + + StateListData states; + + VerifyOrReturnError(CHIP_NO_ERROR == states.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + + StateData state(states.first); + StateData prev_state; + size_t index = 0; + bool found = false; + bool previous = false; + + // Loop until the desired index + while (index < states.count) + { + if (CHIP_NO_ERROR != state.Load(mStorage)) + { + break; + } + if (index == state_index) + { + // Target index found + found = true; + break; + } + previous = true; + prev_state = state; + state.id = state.next; + index++; + } + + VerifyOrReturnError(found, CHIP_ERROR_KEY_NOT_FOUND); + + if (states.count > 0) + { + states.count--; + } + + ReturnErrorOnFailure(state.Delete(mStorage)); + if (previous) + { + // Remove intermediate state + prev_state.next = state.next; + ReturnErrorOnFailure(prev_state.Save(mStorage)); + } + else + { + // Remove first state + states.first = state.next; + } + ReturnErrorOnFailure(states.Save(mStorage)); + if (nullptr != mListener) + { + mListener->OnGroupStateRemoved(&state); + } + return CHIP_NO_ERROR; +} + +GroupDataProvider::GroupStateIterator * GroupDataProviderImpl::IterateGroupStates() +{ + VerifyOrReturnError(mInitialized, nullptr); + return mAllStatesIterators.CreateObject(*this); +} + +GroupDataProvider::GroupStateIterator * GroupDataProviderImpl::IterateGroupStates(chip::FabricIndex fabric_index) +{ + VerifyOrReturnError(mInitialized, nullptr); + return mFabricStatesIterators.CreateObject(*this, fabric_index); +} + +GroupDataProviderImpl::AllStatesIterator::AllStatesIterator(GroupDataProviderImpl & provider) : mProvider(provider) +{ + StateListData states; + if (CHIP_NO_ERROR == states.Load(provider.mStorage)) + { + mIndex = states.first; + mTotalCount = states.count; + } +} + +size_t GroupDataProviderImpl::AllStatesIterator::Count() +{ + return mTotalCount; +} + +bool GroupDataProviderImpl::AllStatesIterator::Next(GroupState & item) +{ + VerifyOrReturnError(mCount < mTotalCount, false); + + StateData state(mIndex); + VerifyOrReturnError(CHIP_NO_ERROR == state.Load(mProvider.mStorage), false); + + mCount++; + mIndex = state.next; + item.fabric_index = state.fabric_index; + item.group = state.group; + item.keyset_index = state.keyset_index; + return true; +} + +void GroupDataProviderImpl::AllStatesIterator::Release() +{ + mProvider.mAllStatesIterators.ReleaseObject(this); +} + +GroupDataProviderImpl::FabricStatesIterator::FabricStatesIterator(GroupDataProviderImpl & provider, + chip::FabricIndex fabric_index) : + mProvider(provider), + mFabric(fabric_index) +{ + StateListData states; + if (CHIP_NO_ERROR == states.Load(provider.mStorage)) + { + mIndex = states.first; + mTotalCount = states.count; + } +} + +size_t GroupDataProviderImpl::FabricStatesIterator::Count() +{ + size_t count = 0; + size_t index = 0; + StateData state(mIndex); + + while (index++ < mTotalCount) + { + if (CHIP_NO_ERROR != state.Load(mProvider.mStorage)) + { + break; + } + if (state.fabric_index == mFabric) + { + count++; + } + state.id = state.next; + } + return count; +} + +bool GroupDataProviderImpl::FabricStatesIterator::Next(GroupState & item) +{ + StateData state(mIndex); + + while (mCount++ < mTotalCount) + { + if (CHIP_NO_ERROR != state.Load(mProvider.mStorage)) + { + mCount = mTotalCount; + break; + } + state.id = state.next; + if (state.fabric_index == mFabric) + { + item.fabric_index = state.fabric_index; + item.group = state.group; + item.keyset_index = state.keyset_index; + mIndex = state.id; + return true; + } + } + return false; +} + +void GroupDataProviderImpl::FabricStatesIterator::Release() +{ + mProvider.mFabricStatesIterators.ReleaseObject(this); +} + +// +// Key Sets +// + +constexpr size_t GroupDataProvider::EpochKey::kLengthBytes; + +CHIP_ERROR GroupDataProviderImpl::SetKeySet(chip::FabricIndex fabric_index, uint16_t target_id, const KeySet & in_keyset) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + + FabricData fabric(fabric_index); + if (CHIP_NO_ERROR != fabric.Load(mStorage)) + { + // First in_keyset + fabric.keyset_count = 1; + fabric.first_keyset = target_id; + KeySetData keyset(fabric_index, target_id, in_keyset.policy, in_keyset.num_keys_used); + memcpy(keyset.epoch_keys, in_keyset.epoch_keys, sizeof(in_keyset.epoch_keys)); + ReturnLogErrorOnFailure(keyset.Save(mStorage)); + return fabric.Save(mStorage); + } + + KeySetData keyset(fabric_index, fabric.first_keyset); + size_t keyset_count = 0; + bool found = false; + + // Seach for the target in_keyset id + while (keyset_count < fabric.keyset_count) + { + if (CHIP_NO_ERROR != keyset.Load(mStorage)) + { + break; + } + if (keyset.keyset_id == target_id) + { + // Target id found + found = true; + break; + } + keyset.keyset_id = keyset.next; + keyset_count++; + } + + keyset.keyset_id = target_id; + keyset.policy = in_keyset.policy; + keyset.num_keys_used = in_keyset.num_keys_used; + memcpy(keyset.epoch_keys, in_keyset.epoch_keys, sizeof(in_keyset.epoch_keys)); + + if (found) + { + // Update existing in_keyset info, keep next + return keyset.Save(mStorage); + } + + // New in_keyset, insert first + keyset.next = fabric.first_keyset; + ReturnErrorOnFailure(keyset.Save(mStorage)); + // Update fabric info + fabric.keyset_count++; + fabric.first_keyset = target_id; + return fabric.Save(mStorage); +} + +CHIP_ERROR GroupDataProviderImpl::GetKeySet(chip::FabricIndex fabric_index, uint16_t target_id, KeySet & out_keyset) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + + FabricData fabric(fabric_index); + VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_INVALID_FABRIC_ID); + + KeySetData keyset(fabric_index, fabric.first_keyset); + size_t keyset_count = 0; + + // Loop until the desired index + while (keyset_count < fabric.keyset_count) + { + if (CHIP_NO_ERROR != keyset.Load(mStorage)) + { + break; + } + if (keyset.keyset_id == target_id) + { + // Target index found + out_keyset.keyset_id = keyset.keyset_id; + out_keyset.policy = keyset.policy; + out_keyset.num_keys_used = keyset.num_keys_used; + memcpy(out_keyset.epoch_keys, keyset.epoch_keys, sizeof(out_keyset.epoch_keys)); + return CHIP_NO_ERROR; + } + keyset.keyset_id = keyset.next; + keyset_count++; + } + + return CHIP_ERROR_KEY_NOT_FOUND; +} + +CHIP_ERROR GroupDataProviderImpl::RemoveKeySet(chip::FabricIndex fabric_index, uint16_t target_id) +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); + + FabricData fabric(fabric_index); + VerifyOrReturnError(CHIP_NO_ERROR == fabric.Load(mStorage), CHIP_ERROR_KEY_NOT_FOUND); + + KeySetData keyset(fabric_index, fabric.first_keyset); + uint16_t prev_id = 0; + size_t keyset_count = 0; + + // Loop until the desired index + while (keyset_count < fabric.keyset_count) + { + if (CHIP_NO_ERROR != keyset.Load(mStorage)) + { + break; + } + if (keyset.keyset_id == target_id) + { + // Target index found + break; + } + prev_id = keyset.keyset_id; + keyset.keyset_id = keyset.next; + keyset_count++; + } + + VerifyOrReturnError(keyset.keyset_id == target_id, CHIP_ERROR_KEY_NOT_FOUND); + + ReturnErrorOnFailure(keyset.Delete(mStorage)); + if (keyset_count > 0) + { + // Remove intermediate keyset, update previous + KeySetData prev_data(fabric_index, prev_id); + ReturnErrorOnFailure(prev_data.Load(mStorage)); + prev_data.next = keyset.next; + ReturnErrorOnFailure(prev_data.Save(mStorage)); + } + else + { + // Remove first keyset + fabric.first_keyset = keyset.next; + } + if (fabric.keyset_count > 0) + { + fabric.keyset_count--; + } + // Update fabric info + return fabric.Save(mStorage); +} + +GroupDataProvider::KeySetIterator * GroupDataProviderImpl::IterateKeySets(chip::FabricIndex fabric_index) +{ + VerifyOrReturnError(mInitialized, nullptr); + return mKeySetIterators.CreateObject(*this, fabric_index); +} + +GroupDataProviderImpl::KeySetIteratorImpl::KeySetIteratorImpl(GroupDataProviderImpl & provider, chip::FabricIndex fabric_index) : + mProvider(provider), mFabric(fabric_index) +{ + FabricData fabric(fabric_index); + if (CHIP_NO_ERROR == fabric.Load(provider.mStorage)) + { + mNextId = fabric.first_keyset; + mCount = fabric.keyset_count; + mIndex = 0; + } +} + +size_t GroupDataProviderImpl::KeySetIteratorImpl::Count() +{ + return mCount; +} + +bool GroupDataProviderImpl::KeySetIteratorImpl::Next(KeySet & item) +{ + VerifyOrReturnError(mIndex < mCount, false); + + KeySetData keyset(mFabric, mNextId); + VerifyOrReturnError(CHIP_NO_ERROR == keyset.Load(mProvider.mStorage), false); + + mIndex++; + mNextId = keyset.next; + item.keyset_id = keyset.keyset_id; + item.policy = keyset.policy; + item.num_keys_used = keyset.num_keys_used; + memcpy(item.epoch_keys, keyset.epoch_keys, sizeof(item.epoch_keys)); + return true; +} + +void GroupDataProviderImpl::KeySetIteratorImpl::Release() +{ + mProvider.mKeySetIterators.ReleaseObject(this); +} + +// +// Fabrics +// + +CHIP_ERROR GroupDataProviderImpl::RemoveFabric(chip::FabricIndex fabric_index) +{ + FabricData fabric(fabric_index); + + // Fabric data defaults to zero, so if not entry is found, no mappings, or keys are removed + // However, states has a separate list, and needs to be removed regardless + fabric.Load(mStorage); + + // Remove Group Mappings + + EndpointData endpoint(fabric_index, fabric.first_endpoint); + size_t endpoint_count = 0; + + do + { + if (CHIP_NO_ERROR != endpoint.Load(mStorage)) + { + break; + } + RemoveAllGroupMappings(fabric_index, endpoint.endpoint_id); + endpoint.endpoint_id = endpoint.next; + } while ((++endpoint_count < fabric.endpoint_count) && (kInvalidEndpointId != endpoint.endpoint_id)); + + // Remove States + + StateListData states; + + // Load state list info. No error check needed: If the list is not found, + // the number of states defaults to zero, so the following loop is skipped. + states.Load(mStorage); + + StateData state(states.first); + size_t index = 0; + + while (index < states.count) + { + if (CHIP_NO_ERROR != state.Load(mStorage)) + { + break; + } + if (state.fabric_index == fabric_index) + { + // Remove the N state from the array. State N+1 becomes N + RemoveGroupState(index); + } + else + { + index++; + } + state.id = state.next; + } + + // Remove Keysets + + KeySetData keyset(fabric_index, fabric.first_keyset); + size_t keyset_count = 0; + + // Loop the keysets associated with the target fabric + while (keyset_count < fabric.keyset_count) + { + if (CHIP_NO_ERROR != keyset.Load(mStorage)) + { + break; + } + RemoveKeySet(fabric_index, keyset.keyset_id); + keyset.keyset_id = keyset.next; + keyset_count++; + } + + // Remove fabric + return fabric.Delete(mStorage); +} + +// +// General +// + +CHIP_ERROR GroupDataProviderImpl::Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, + System::PacketBufferHandle & msg) +{ + (void) packetHeader; + (void) payloadHeader; + (void) msg; + return CHIP_NO_ERROR; +} + +namespace { + +GroupDataProvider * gGroupsProvider = nullptr; + +} // namespace + +/** + * Instance getter for the global GroupDataProvider. + * + * Callers have to externally synchronize usage of this function. + * + * @return The global device attestation credentials provider. Assume never null. + */ +GroupDataProvider * GetGroupDataProvider() +{ + return gGroupsProvider; +} + +/** + * Instance setter for the global GroupDataProvider. + * + * Callers have to externally synchronize usage of this function. + * + * If the `provider` is nullptr, no change is done. + * + * @param[in] provider the GroupDataProvider to start returning with the getter + */ +void SetGroupDataProvider(GroupDataProvider * provider) +{ + if (provider) + { + gGroupsProvider = provider; + } +} + +} // namespace Credentials +} // namespace chip diff --git a/src/credentials/GroupDataProviderImpl.h b/src/credentials/GroupDataProviderImpl.h new file mode 100644 index 00000000000000..638816fe8eeecf --- /dev/null +++ b/src/credentials/GroupDataProviderImpl.h @@ -0,0 +1,145 @@ +/* + * + * 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. + */ +#pragma once + +#include +#include +#include + +namespace chip { +namespace Credentials { + +static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_GROUP_CONCURRENT_ITERATORS; + +class GroupDataProviderImpl : public GroupDataProvider +{ +public: + GroupDataProviderImpl(chip::PersistentStorageDelegate & storage_delegate) : mStorage(storage_delegate) {} + virtual ~GroupDataProviderImpl() {} + + CHIP_ERROR Init() override; + void Finish() override; + + // + // Group Mappings + // + + bool HasGroupNamesSupport() override; + bool GroupMappingExists(chip::FabricIndex fabric_index, const GroupMapping & mapping) override; + CHIP_ERROR AddGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) override; + CHIP_ERROR RemoveGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) override; + CHIP_ERROR RemoveAllGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) override; + GroupMappingIterator * IterateGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) override; + + // + // Group States + // + + CHIP_ERROR SetGroupState(size_t state_index, const GroupState & state) override; + CHIP_ERROR GetGroupState(size_t state_index, GroupState & state) override; + CHIP_ERROR RemoveGroupState(size_t state_index) override; + GroupStateIterator * IterateGroupStates() override; + GroupStateIterator * IterateGroupStates(chip::FabricIndex fabric_index) override; + + // + // Key Sets + // + + CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, const KeySet & keys) override; + CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id, KeySet & keys) override; + CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, uint16_t keyset_id) override; + KeySetIterator * IterateKeySets(chip::FabricIndex fabric_index) override; + + // Fabrics + CHIP_ERROR RemoveFabric(chip::FabricIndex fabric_index) override; + + // General + CHIP_ERROR Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, System::PacketBufferHandle & msg) override; + +private: + class GroupMappingIteratorImpl : public GroupMappingIterator + { + public: + GroupMappingIteratorImpl(GroupDataProviderImpl & provider, chip::FabricIndex fabric, chip::EndpointId endpoint); + size_t Count() override; + bool Next(GroupMapping & item) override; + void Release() override; + + private: + GroupDataProviderImpl & mProvider; + chip::FabricIndex mFabric = 0; + chip::EndpointId mEndpoint = 0; + chip::GroupId mGroup = kUndefinedGroupId; + }; + + class AllStatesIterator : public GroupStateIterator + { + public: + AllStatesIterator(GroupDataProviderImpl & provider); + size_t Count() override; + bool Next(GroupState & item) override; + void Release() override; + + private: + GroupDataProviderImpl & mProvider; + uint16_t mIndex = 0; + size_t mCount = 0; + size_t mTotalCount = 0; + }; + + class FabricStatesIterator : public GroupStateIterator + { + public: + FabricStatesIterator(GroupDataProviderImpl & provider, chip::FabricIndex fabric_index); + size_t Count() override; + bool Next(GroupState & item) override; + void Release() override; + + private: + GroupDataProviderImpl & mProvider; + chip::FabricIndex mFabric = 0; + uint16_t mIndex = 0; + size_t mCount = 0; + size_t mTotalCount = 0; + }; + + class KeySetIteratorImpl : public KeySetIterator + { + public: + KeySetIteratorImpl(GroupDataProviderImpl & provider, chip::FabricIndex fabric_index); + size_t Count() override; + bool Next(KeySet & item) override; + void Release() override; + + private: + GroupDataProviderImpl & mProvider; + chip::FabricIndex mFabric = 0; + uint16_t mNextId = 0; + size_t mCount = 0; + size_t mIndex = 0; + }; + + chip::PersistentStorageDelegate & mStorage; + bool mInitialized = false; + BitMapObjectPool mEndpointIterators; + BitMapObjectPool mAllStatesIterators; + BitMapObjectPool mFabricStatesIterators; + BitMapObjectPool mKeySetIterators; +}; + +} // namespace Credentials +} // namespace chip diff --git a/src/credentials/examples/GroupDataProviderExample.cpp b/src/credentials/examples/GroupDataProviderExample.cpp deleted file mode 100644 index 190f4dcfba3565..00000000000000 --- a/src/credentials/examples/GroupDataProviderExample.cpp +++ /dev/null @@ -1,743 +0,0 @@ -/* - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include - -namespace chip { -namespace Credentials { -namespace { - -static constexpr size_t kNumFabrics = CHIP_CONFIG_MAX_DEVICE_ADMINS; -static constexpr size_t kEndpointEntriesMax = CHIP_CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC; -static constexpr size_t kStateEntriesMax = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC; -static constexpr size_t kKeyEntriesMax = CHIP_CONFIG_MAX_GROUPS_PER_FABRIC; -static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_GROUP_CONCURRENT_ITERATORS; -static constexpr size_t kGroupNameMax = CHIP_CONFIG_MAX_GROUP_NAME_LENGTH; - -class StaticGroupsProvider : public GroupDataProvider -{ -public: - StaticGroupsProvider() { Init(); } - -protected: - struct EndpointEntry : public GroupMapping - { - bool in_use = false; - char name[kGroupNameMax + 1]; - size_t nameLength; - - void Clear() - { - endpoint = 0; - group = 0; - in_use = false; - } - }; - - struct KeysEntry : public KeySet - { - bool in_use = false; - void Clear() - { - memset(epoch_keys, 0x00, sizeof(epoch_keys)); - key_set_index = 0; - num_keys_used = 0; - in_use = false; - } - }; - - struct Fabric - { - // Whether entry is allocated (true), or free to allocate (false) - bool in_use = false; - // Node-wide fabric index associated with the entry - chip::FabricIndex fabric_index; - // Group to Endpoint mapping for fabric - EndpointEntry endpoints[kEndpointEntriesMax]; - // Number of Group states for this fabric - size_t states_count; - // Group key sets for fabric - KeysEntry keys[kKeyEntriesMax]; - - void Clear() - { - in_use = false; - fabric_index = 0; - states_count = 0; - for (size_t i = 0; i < kEndpointEntriesMax; i++) - { - endpoints[i].Clear(); - } - for (size_t i = 0; i < kKeyEntriesMax; i++) - { - keys[i].Clear(); - } - } - }; - - struct StateEntry : public GroupState - { - Fabric * fabric = nullptr; - bool in_use = false; - void Clear() - { - in_use = false; - fabric_index = 0; - group = 0; - key_set_index = 0; - fabric = nullptr; - } - }; - - class EndpointIterator : public GroupMappingIterator - { - public: - EndpointIterator(StaticGroupsProvider & provider, Fabric * fabric, chip::EndpointId endpoint) : - mProvider(provider), mFabric(fabric), mEndpoint(endpoint) - {} - - size_t Count() override - { - size_t count = 0; - - for (size_t i = 0; this->mFabric && i < kEndpointEntriesMax; ++i) - { - const EndpointEntry & entry = this->mFabric->endpoints[i]; - if (entry.in_use && entry.endpoint == this->mEndpoint) - { - count++; - } - } - return count; - } - - bool Next(GroupMapping & mapping) override - { - while ((this->mFabric != nullptr) && (this->mIndex < kEndpointEntriesMax)) - { - const EndpointEntry & entry = this->mFabric->endpoints[this->mIndex++]; - - if (entry.in_use && (entry.endpoint == this->mEndpoint)) - { - mapping.endpoint = entry.endpoint; - mapping.group = entry.group; - mapping.name = CharSpan(entry.name, entry.nameLength); - return true; - } - } - - return false; - } - - void Release() override { mProvider.Release(this); } - - private: - StaticGroupsProvider & mProvider; - Fabric * mFabric = nullptr; - chip::EndpointId mEndpoint = 0; - uint16_t mIndex = 0; - }; - - class FabricGroupStateIterator : public GroupStateIterator - { - public: - FabricGroupStateIterator(StaticGroupsProvider & provider, Fabric * fabric) : mProvider(provider), mFabric(fabric) {} - - size_t Count() override - { - size_t count = 0; - for (size_t i = 0; i < kMaxNumGroupStates && this->mProvider.mGroupStatesCount; ++i) - { - const StateEntry & entry = this->mProvider.mGroupStates[i]; - if (entry.in_use && entry.fabric == mFabric) - { - count++; - } - } - return count; - } - - bool Next(GroupState & outEntry) override - { - while ((this->mFabric != nullptr) && (mIndex < kMaxNumGroupStates) && (mIndex < this->mProvider.mGroupStatesCount)) - { - const StateEntry & entry = this->mProvider.mGroupStates[mIndex++]; - if (entry.in_use && entry.fabric == mFabric) - { - // Iterator has data available, copy the contents of the entry to the output - outEntry = entry; - return true; - } - } - return false; - } - - void Release() override { mProvider.Release(this); } - - private: - StaticGroupsProvider & mProvider; - Fabric * mFabric = nullptr; - uint16_t mIndex = 0; - }; - - class AllGroupStateIterator : public GroupStateIterator - { - public: - AllGroupStateIterator(StaticGroupsProvider & provider) : mProvider(provider) {} - - size_t Count() override - { - size_t count = 0; - for (size_t i = 0; i < kMaxNumGroupStates && this->mProvider.mGroupStatesCount; ++i) - { - const StateEntry & entry = this->mProvider.mGroupStates[i]; - if (entry.in_use) - { - count++; - } - } - return count; - } - - bool Next(GroupState & outEntry) override - { - while ((mIndex < kMaxNumGroupStates) && (mIndex < this->mProvider.mGroupStatesCount)) - { - const StateEntry & entry = this->mProvider.mGroupStates[mIndex++]; - if (entry.in_use) - { - // Iterator has data available, copy the contents of the entry to the output - outEntry = entry; - return true; - } - } - return false; - } - - void Release() override { mProvider.Release(this); } - - private: - StaticGroupsProvider & mProvider; - uint16_t mIndex = 0; - }; - - class KeysIterator : public KeySetIterator - { - public: - KeysIterator(StaticGroupsProvider & provider, Fabric * fabric) : mProvider(provider), mFabric(fabric) {} - - size_t Count() override - { - size_t count = 0; - for (size_t i = 0; this->mFabric && i < kKeyEntriesMax; ++i) - { - const KeysEntry & entry = this->mFabric->keys[i]; - if (entry.in_use) - { - count++; - } - } - return count; - } - - bool Next(KeySet & outSet) override - { - while ((this->mFabric != nullptr) && (this->mIndex < kKeyEntriesMax)) - { - const KeysEntry & entry = this->mFabric->keys[this->mIndex++]; - if (entry.in_use) - { - outSet.key_set_index = entry.key_set_index; - outSet.policy = entry.policy; - outSet.num_keys_used = entry.num_keys_used; - memcpy(outSet.epoch_keys, entry.epoch_keys, sizeof(outSet.epoch_keys)); - return true; - } - } - return false; - } - - void Release() override { mProvider.Release(this); } - - private: - StaticGroupsProvider & mProvider; - Fabric * mFabric = nullptr; - uint16_t mIndex = 0; - }; - - // Get the fabric-scoped dataset slot for the given `fabric_index`. - // If `allow_allocate` is true, a fabric index not found will mark a slot as allocated - // to that given fabric_index, otherwise only lookup of existing fabric slots is possible. - // If no slot is found matching the `fabric_index`, nullptr is returned. - Fabric * GetFabric(chip::FabricIndex fabric_index, bool allow_allocate) - { - Fabric * fabric = nullptr; - Fabric * unused = nullptr; - - for (chip::FabricIndex fabric_slot_idx = 0; fabric_slot_idx < kNumFabrics; fabric_slot_idx++) - { - fabric = &mFabrics[fabric_slot_idx]; - if (fabric->in_use) - { - if (fabric->fabric_index == fabric_index) - { - // Fabric in use - return fabric; - } - } - else if (nullptr == unused) - { - // Remember the first unused entry - unused = fabric; - } - } - if (unused && allow_allocate) - { - // Use the first available entry - unused->fabric_index = fabric_index; - unused->in_use = true; - return unused; - } - // Fabric not found, and not allowed to allocate or out of entry - return nullptr; - } - - Fabric * GetExistingFabric(chip::FabricIndex fabric_index) { return GetFabric(fabric_index, /* allow_allocate= */ false); } - - Fabric * GetExistingFabricOrAllocateNew(chip::FabricIndex fabric_index) - { - return GetFabric(fabric_index, /* allow_allocate= */ true); - } - -public: - CHIP_ERROR Init() override - { - // Clear-out all fabric entries - for (size_t i = 0; i < kNumFabrics; ++i) - { - mFabrics[i].Clear(); - } - // Clear-out all entries of index mapping. - mGroupStatesCount = 0; - for (size_t i = 0; i < kMaxNumGroupStates; ++i) - { - mGroupStates[i].Clear(); - } - mInitialized = true; - return CHIP_NO_ERROR; - } - - void Finish() override { mInitialized = false; } - - // Endpoints - bool HasGroupNamesSupport() override { return true; } - - bool GroupMappingExists(chip::FabricIndex fabric_index, const GroupMapping & mapping) override - { - VerifyOrReturnError(mInitialized, false); - - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(nullptr != fabric, false); - - for (uint16_t i = 0; fabric && i < kEndpointEntriesMax; ++i) - { - EndpointEntry & entry = fabric->endpoints[i]; - if (entry.in_use && (entry == mapping)) - { - return true; - } - } - return false; - } - - CHIP_ERROR AddGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - - Fabric * fabric = GetExistingFabricOrAllocateNew(fabric_index); - VerifyOrReturnError(nullptr != fabric, CHIP_ERROR_NO_MEMORY); - - // Search for existing mapping - for (uint16_t i = 0; i < kEndpointEntriesMax; ++i) - { - EndpointEntry & entry = fabric->endpoints[i]; - if (entry.in_use && (entry == mapping)) - { - // Duplicated - return CHIP_NO_ERROR; - } - } - // New mapping - for (uint16_t i = 0; i < kEndpointEntriesMax; ++i) - { - EndpointEntry & entry = fabric->endpoints[i]; - - if (!entry.in_use) - { - entry.group = mapping.group; - entry.endpoint = mapping.endpoint; - if (mapping.name.empty()) - { - entry.nameLength = 0; - entry.name[0] = 0; - } - else - { - entry.nameLength = std::min(mapping.name.size(), kGroupNameMax); - strncpy(entry.name, mapping.name.data(), entry.nameLength); - entry.name[entry.nameLength] = 0; - } - - entry.in_use = true; - return CHIP_NO_ERROR; - } - } - return CHIP_ERROR_NO_MEMORY; - } - - CHIP_ERROR RemoveGroupMapping(chip::FabricIndex fabric_index, const GroupMapping & mapping) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - - // Search for existing mapping - for (uint16_t i = 0; fabric && i < kEndpointEntriesMax; ++i) - { - EndpointEntry & entry = fabric->endpoints[i]; - if (entry.in_use && (entry == mapping)) - { - // Found - entry.in_use = false; - return CHIP_NO_ERROR; - } - } - - return CHIP_ERROR_KEY_NOT_FOUND; - } - - CHIP_ERROR RemoveAllGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - - // Remove all mappings from fabric - for (uint16_t i = 0; fabric && i < kEndpointEntriesMax; ++i) - { - if (fabric->endpoints[i].endpoint == endpoint) - { - fabric->endpoints[i].in_use = false; - } - } - return CHIP_NO_ERROR; - } - - GroupMappingIterator * IterateGroupMappings(chip::FabricIndex fabric_index, EndpointId endpoint) override - { - VerifyOrReturnError(mInitialized, nullptr); - return mEndpointIterators.CreateObject(*this, GetExistingFabric(fabric_index), endpoint); - } - - void Release(EndpointIterator * iterator) { mEndpointIterators.ReleaseObject(iterator); } - - // States - - CHIP_ERROR SetGroupState(size_t state_index, const GroupState & state) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - - // Append is "add to one past the end". Further than that is not supported. - VerifyOrReturnError(static_cast(state_index) <= mGroupStatesCount, CHIP_ERROR_INVALID_ARGUMENT); - - Fabric * fabric = GetExistingFabricOrAllocateNew(state.fabric_index); - VerifyOrReturnError(nullptr != fabric, CHIP_ERROR_NO_MEMORY); - - StateEntry & entry = mGroupStates[state_index]; - bool appending = static_cast(state_index) == mGroupStatesCount; - - if (appending) - { - // New entry, append at the end, limiting the number of entries per fabric - VerifyOrReturnError(fabric->states_count < kStateEntriesMax, CHIP_ERROR_NO_MEMORY); - mGroupStatesCount++; - fabric->states_count++; - } - else - { - // Existing entry, avoid overwrite another fabric's entry - VerifyOrReturnError(mGroupStates[state_index].fabric_index == state.fabric_index, CHIP_ERROR_ACCESS_DENIED); - } - - GroupState old_entry = entry; - entry.fabric_index = state.fabric_index; - entry.group = state.group; - entry.key_set_index = state.key_set_index; - entry.fabric = fabric; - entry.in_use = true; - - if (mListener) - { - mListener->OnGroupStateChanged((appending ? nullptr : &old_entry), &entry); - } - - return CHIP_NO_ERROR; - } - - CHIP_ERROR GetGroupState(size_t state_index, GroupState & state) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - VerifyOrReturnError(static_cast(state_index) < mGroupStatesCount, CHIP_ERROR_KEY_NOT_FOUND); - - StateEntry & entry = mGroupStates[state_index]; - - // Should not happen that mapped fabric is not allocated! - VerifyOrReturnError(entry.fabric && entry.fabric->in_use && entry.in_use, CHIP_ERROR_INTERNAL); - - // Update output mapping entry - state = mGroupStates[state_index]; - - return CHIP_NO_ERROR; - } - - CHIP_ERROR RemoveGroupState(size_t state_index) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - VerifyOrReturnError(static_cast(state_index) < mGroupStatesCount, CHIP_ERROR_KEY_NOT_FOUND); - - StateEntry & entry = mGroupStates[state_index]; - - // Should not happen that mapped fabric is not allocated! - VerifyOrReturnError(entry.fabric && entry.fabric->in_use && entry.fabric->states_count > 0 && entry.in_use, - CHIP_ERROR_INTERNAL); - - GroupState old_entry = entry; - - // Shift rest of the list up - - // Mark entry unused in fabric - entry.fabric->states_count--; - entry.Clear(); - - --mGroupStatesCount; - - size_t num_entries_to_shift = mGroupStatesCount - state_index; - if (num_entries_to_shift > 0) - { - memmove(&mGroupStates[state_index], &mGroupStates[state_index + 1], num_entries_to_shift * sizeof(mGroupStates[0])); - // Invalidate entry one past the end of whole used space of table after shift, if any shift occured. - // This is because the shift left free space at the end of the table which is a copy of the - // very last entry previously there, and this would break free-space checking assumptions if not - // fixed-up that all unused entries at the end are invalid - mGroupStates[mGroupStatesCount].Clear(); - } - - if (mListener) - { - mListener->OnGroupStateRemoved(&old_entry); - } - return CHIP_NO_ERROR; - } - - GroupStateIterator * IterateGroupStates() override - { - VerifyOrReturnError(mInitialized, nullptr); - return mAllStateIterators.CreateObject(*this); - } - - GroupStateIterator * IterateGroupStates(chip::FabricIndex fabric_index) override - { - VerifyOrReturnError(mInitialized, nullptr); - return mFabricStateIterators.CreateObject(*this, GetExistingFabric(fabric_index)); - } - - void Release(FabricGroupStateIterator * iterator) { mFabricStateIterators.ReleaseObject(iterator); } - - void Release(AllGroupStateIterator * iterator) { mAllStateIterators.ReleaseObject(iterator); } - - // Keys - CHIP_ERROR SetKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index, const KeySet & keys) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - - Fabric * fabric = GetExistingFabricOrAllocateNew(fabric_index); - KeysEntry * entry = nullptr; - - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - - // Search for existing, or unused entry - for (uint16_t i = 0; fabric && i < kKeyEntriesMax; ++i) - { - if (fabric->keys[i].in_use) - { - if (fabric->keys[i].key_set_index == key_set_index) - { - // Reuse existing entry - entry = &fabric->keys[i]; - break; - } - } - else if (!entry) - { - // Unused entry - entry = &fabric->keys[i]; - } - } - if (entry) - { - entry->key_set_index = key_set_index; - entry->policy = keys.policy; - entry->num_keys_used = keys.num_keys_used; - memcpy(entry->epoch_keys, keys.epoch_keys, sizeof(keys.epoch_keys[0]) * keys.num_keys_used); - entry->in_use = true; - return CHIP_NO_ERROR; - } - return CHIP_ERROR_NO_MEMORY; - } - - CHIP_ERROR GetKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index, KeySet & keys) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - - // Search for existing keys - for (uint16_t i = 0; fabric && i < kKeyEntriesMax; ++i) - { - KeysEntry & entry = fabric->keys[i]; - if (entry.in_use && entry.key_set_index == key_set_index) - { - // Found - keys.policy = entry.policy; - keys.num_keys_used = entry.num_keys_used; - memcpy(keys.epoch_keys, entry.epoch_keys, sizeof(keys.epoch_keys[0]) * keys.num_keys_used); - return CHIP_NO_ERROR; - } - } - return CHIP_ERROR_KEY_NOT_FOUND; - } - - CHIP_ERROR RemoveKeySet(chip::FabricIndex fabric_index, uint16_t key_set_index) override - { - VerifyOrReturnError(mInitialized, CHIP_ERROR_INTERNAL); - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - - // Search for existing keys - for (uint16_t i = 0; fabric && i < kKeyEntriesMax; ++i) - { - KeysEntry & entry = fabric->keys[i]; - if (entry.in_use && entry.key_set_index == key_set_index) - { - // Found - entry.in_use = false; - return CHIP_NO_ERROR; - } - } - return CHIP_ERROR_KEY_NOT_FOUND; - } - - KeySetIterator * IterateKeySets(chip::FabricIndex fabric_index) override - { - VerifyOrReturnError(mInitialized, nullptr); - return mKeyIterators.CreateObject(*this, GetExistingFabric(fabric_index)); - } - - void Release(KeysIterator * iterator) { return mKeyIterators.ReleaseObject(iterator); } - - CHIP_ERROR RemoveFabric(chip::FabricIndex fabric_index) override - { - Fabric * fabric = GetExistingFabric(fabric_index); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_FABRIC_ID); - // Remove group states - for (size_t i = 0; i < kMaxNumGroupStates; ++i) - { - if (mGroupStates[i].fabric == fabric) - { - RemoveGroupState(i); - } - } - // Release other resources associated with the fabric entry, such as mapped - // endpoints (i.e. through the Groups cluster) and group key sets, both of which - // live in the `Fabric` data structure of this implementation. - fabric->Clear(); - return CHIP_NO_ERROR; - } - - CHIP_ERROR Decrypt(PacketHeader packetHeader, PayloadHeader & payloadHeader, System::PacketBufferHandle && msg) override - { - // TODO - - return CHIP_NO_ERROR; - } - -private: - bool mInitialized = false; - Fabric mFabrics[kNumFabrics]; - BitMapObjectPool mKeyIterators; - BitMapObjectPool mEndpointIterators; - BitMapObjectPool mFabricStateIterators; - BitMapObjectPool mAllStateIterators; - static constexpr size_t kMaxNumGroupStates = kNumFabrics * kStateEntriesMax; - StateEntry mGroupStates[kMaxNumGroupStates]; - size_t mGroupStatesCount = 0; -}; - -StaticGroupsProvider gDefaultGroupsProvider; - -GroupDataProvider * gGroupsProvider = &gDefaultGroupsProvider; - -} // namespace - -/** - * Instance getter for the global GroupDataProvider. - * - * Callers have to externally synchronize usage of this function. - * - * @return The global device attestation credentials provider. Assume never null. - */ -GroupDataProvider * GetGroupDataProvider() -{ - return gGroupsProvider; -} - -/** - * Instance setter for the global GroupDataProvider. - * - * Callers have to externally synchronize usage of this function. - * - * If the `provider` is nullptr, no change is done. - * - * @param[in] provider the GroupDataProvider to start returning with the getter - */ -void SetGroupDataProvider(GroupDataProvider * provider) -{ - if (provider) - { - gGroupsProvider = provider; - } -} - -} // namespace Credentials -} // namespace chip diff --git a/src/credentials/tests/TestGroupDataProvider.cpp b/src/credentials/tests/TestGroupDataProvider.cpp index f91cdfe235d310..946c533c43bca8 100644 --- a/src/credentials/tests/TestGroupDataProvider.cpp +++ b/src/credentials/tests/TestGroupDataProvider.cpp @@ -16,209 +16,910 @@ * limitations under the License. */ -#include +#include +#include #include +#include #include #include +#include +#include #include using namespace chip::Credentials; +using GroupMapping = GroupDataProvider::GroupMapping; +using KeySet = GroupDataProvider::KeySet; +using GroupState = GroupDataProvider::GroupState; +using EpochKey = GroupDataProvider::EpochKey; namespace chip { namespace app { namespace TestGroups { -using KeySet = chip::Credentials::GroupDataProvider::KeySet; +static const char * kKey1 = "abc/def"; +static const char * kValue1 = "abc/def"; +static const char * kValue2 = "abc/ghi/xyz"; +static const size_t kSize1 = strlen(kValue1) + 1; +static const size_t kSize2 = strlen(kValue2) + 1; -void TestEndpoints(nlTestSuite * apSuite, void * apContext) +void TestStorageDelegate(nlTestSuite * apSuite, void * apContext) +{ + chip::TestPersistentStorageDelegate delegate; + + char out[128]; + uint16_t size = static_cast(sizeof(out)); + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == delegate.SyncGetKeyValue(kKey1, out, size)); + + size = static_cast(kSize1); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == delegate.SyncSetKeyValue(kKey1, kValue1, size)); + + size = static_cast(sizeof(out)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == delegate.SyncGetKeyValue(kKey1, out, size)); + NL_TEST_ASSERT(apSuite, size == kSize1); + NL_TEST_ASSERT(apSuite, !memcmp(out, kValue1, kSize1)); + + size = static_cast(kSize2); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == delegate.SyncSetKeyValue(kKey1, kValue2, size)); + + size = static_cast(sizeof(out)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == delegate.SyncGetKeyValue(kKey1, out, size)); + NL_TEST_ASSERT(apSuite, size == kSize2); + NL_TEST_ASSERT(apSuite, !memcmp(out, kValue2, kSize2)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == delegate.SyncDeleteKeyValue(kKey1)); + + size = static_cast(sizeof(out)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == delegate.SyncGetKeyValue(kKey1, out, size)); +} + +constexpr chip::FabricIndex kFabric1 = 1; +constexpr chip::FabricIndex kFabric2 = 7; +constexpr chip::EndpointId kEndpoint1 = 1; +constexpr chip::EndpointId kEndpoint2 = 0xabcd; +constexpr chip::EndpointId kEndpoint3 = 0xfffe; +constexpr chip::GroupId kGroup1 = kMinFabricGroupId; +constexpr chip::GroupId kGroup2 = 0x2222; +constexpr chip::GroupId kGroup3 = kMaxFabricGroupId; + +static const GroupMapping endpoint1group1(kEndpoint1, kGroup1, "Group 1.1"); +static const GroupMapping endpoint1group2(kEndpoint1, kGroup2, "Group 1.2"); +static const GroupMapping endpoint1group3(kEndpoint1, kGroup3, "Group 1.3"); + +static const GroupMapping endpoint2group1(kEndpoint2, kGroup1, "Group 2.1"); +static const GroupMapping endpoint2group2(kEndpoint2, kGroup2, "Group 2.2"); +static const GroupMapping endpoint2group3(kEndpoint2, kGroup3, "Group 2.3"); + +static const GroupMapping endpoint3group1(kEndpoint3, kGroup1, "Group 3.1"); +static const GroupMapping endpoint3group2(kEndpoint3, kGroup2, "Group 3.2"); +static const GroupMapping endpoint3group3(kEndpoint3, kGroup3, "Group 3.3"); + +void TestGroupMappings(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + // Test initial conditions + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group3)); + + // Add Group (new) + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint2group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint1group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint1group1)); + // Keep these out to check for unexisting groups: + // NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group2)); + // NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group3)); + + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint2group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group3)); + + // Add Group (duplicated) + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint1group2)); + + // Remove Group (invalid) + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR != groups->RemoveGroupMapping(kFabric1, GroupMapping(kInvalidEndpointId, 1, nullptr))); + NL_TEST_ASSERT(apSuite, + CHIP_NO_ERROR != groups->RemoveGroupMapping(kFabric1, GroupMapping(kEndpoint1, kUndefinedGroupId, nullptr))); + + // Remove Group (existing) + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupMapping(kFabric1, endpoint1group1)); // First + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupMapping(kFabric2, endpoint2group1)); // Last + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupMapping(kFabric2, endpoint2group2)); // Middle + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint1group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint2group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group3)); + + // Remove Group (already removed) + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveGroupMapping(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveGroupMapping(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveGroupMapping(kFabric2, endpoint2group2)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group2)); + + // Remove All + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric2, kEndpoint1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric1, kEndpoint3)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint2group3)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, groups->GroupMappingExists(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group3)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric1, kEndpoint1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric2, kEndpoint2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric2, kEndpoint3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveAllGroupMappings(kFabric1, kEndpoint2)); + + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric1, endpoint3group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint1group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint2group3)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group1)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, !groups->GroupMappingExists(kFabric2, endpoint3group3)); +} + +void TestGroupMappingIterator(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + // Add Groups + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint1group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint2group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric1, endpoint3group3)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint1group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint2group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint2group1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->AddGroupMapping(kFabric2, endpoint3group1)); + + // Fabric 1 + + GroupMapping mapping; + + constexpr size_t endpoints_count = 3; + constexpr EndpointId endpoints[endpoints_count] = { kEndpoint1, kEndpoint2, kEndpoint3 }; + + std::set expected_f1{ kGroup1, kGroup2, kGroup3 }; + std::map group_map = { { kGroup1, 1 }, { kGroup2, 2 }, { kGroup3, 3 } }; + std::map end_map = { { kEndpoint1, 1 }, { kEndpoint2, 2 }, { kEndpoint3, 3 } }; + char expected_name[64]; + size_t j = 0; + + for (size_t i = 0; i < endpoints_count; i++) + { + auto it = groups->IterateGroupMappings(kFabric1, endpoints[i]); + NL_TEST_ASSERT(apSuite, it); + if (it) + { + size_t count = it->Count(); + NL_TEST_ASSERT(apSuite, expected_f1.size() == count); + j = 0; + while (it->Next(mapping) && j < expected_f1.size()) + { + sprintf(expected_name, "Group %d.%d", end_map[mapping.endpoint], group_map[mapping.group]); + NL_TEST_ASSERT(apSuite, expected_f1.count(mapping.group) > 0); + NL_TEST_ASSERT(apSuite, !strcmp(expected_name, mapping.name)); + j++; + } + NL_TEST_ASSERT(apSuite, j == expected_f1.size()); + it->Release(); + } + } + + // // Fabric 2 + + std::set expected_f2[3] = { { kGroup1, kUndefinedGroupId, kUndefinedGroupId }, + { kGroup2, kGroup1, kUndefinedGroupId }, + { kGroup3, kGroup2, kGroup1 } }; + constexpr size_t expected_f2_counts[endpoints_count] = { 1, 2, 3 }; + + for (size_t i = 0; i < endpoints_count; i++) + { + auto it = groups->IterateGroupMappings(kFabric2, endpoints[i]); + NL_TEST_ASSERT(apSuite, it); + if (it) + { + size_t expected_count = expected_f2_counts[i]; + size_t count = it->Count(); + + NL_TEST_ASSERT(apSuite, expected_count == count); + j = 0; + while (it->Next(mapping) && j < expected_count) + { + sprintf(expected_name, "Group %d.%d", end_map[mapping.endpoint], group_map[mapping.group]); + NL_TEST_ASSERT(apSuite, mapping.endpoint == endpoints[i]); + NL_TEST_ASSERT(apSuite, expected_f2[i].count(mapping.group) > 0); + NL_TEST_ASSERT(apSuite, !strcmp(expected_name, mapping.name)); + j++; + } + NL_TEST_ASSERT(apSuite, j == expected_count); + it->Release(); + } + } +} + +constexpr uint16_t kKeySet1 = 101; +constexpr uint16_t kKeySet2 = 102; +constexpr uint16_t kKeySet3 = 103; + +static const GroupState fabric0group0set0(0, 0, 0); + +static const GroupState fabric1group1set1(kFabric1, kGroup1, kKeySet1); +static const GroupState fabric1group1set2(kFabric1, kGroup1, kKeySet2); +static const GroupState fabric1group1set3(kFabric1, kGroup1, kKeySet3); +static const GroupState fabric1group2set1(kFabric1, kGroup2, kKeySet1); +static const GroupState fabric1group2set2(kFabric1, kGroup2, kKeySet2); +static const GroupState fabric1group2set3(kFabric1, kGroup2, kKeySet3); + +static const GroupState fabric2group1set1(kFabric2, kGroup1, kKeySet1); +static const GroupState fabric2group1set2(kFabric2, kGroup1, kKeySet2); +static const GroupState fabric2group1set3(kFabric2, kGroup1, kKeySet3); +static const GroupState fabric2group2set1(kFabric2, kGroup2, kKeySet1); +static const GroupState fabric2group2set2(kFabric2, kGroup2, kKeySet2); +static const GroupState fabric2group2set3(kFabric2, kGroup2, kKeySet3); + +void TestGroupStates(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + // Add States + + GroupState state(0, 0, 0); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(0, fabric1group1set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(1, fabric1group1set2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(2, fabric1group1set3)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set1); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(1, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set2); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(2, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set3); + + // Override + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(2, fabric1group2set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(1, fabric1group2set2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(0, fabric1group2set3)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set3); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(1, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set2); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(2, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set1); + // Invalid + NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT == groups->SetGroupState(4, fabric1group1set1)); + + // Remove States + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveGroupState(3)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupState(0)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set2); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(1, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set1); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupState(1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group2set2); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT != groups->GetGroupState(1, state)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveGroupState(0)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT != groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT != groups->GetGroupState(1, state)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT != groups->GetGroupState(3, state)); + + // Multiple fabrics + + // Invalid index + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR != groups->SetGroupState(1, fabric2group1set1)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(0, fabric1group1set3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(1, fabric2group1set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(2, fabric1group1set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(3, fabric2group1set2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(4, fabric2group1set3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(5, fabric1group1set2)); + // Incorrect fabric + NL_TEST_ASSERT(apSuite, CHIP_ERROR_ACCESS_DENIED == groups->SetGroupState(1, fabric1group1set1)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(2, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set1); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(5, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set2); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(0, state)); + NL_TEST_ASSERT(apSuite, state == fabric1group1set3); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(4, state)); + NL_TEST_ASSERT(apSuite, state == fabric2group1set3); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(1, state)); + NL_TEST_ASSERT(apSuite, state == fabric2group1set1); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetGroupState(3, state)); + NL_TEST_ASSERT(apSuite, state == fabric2group1set2); +} + +void TestGroupStateIterator(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + // Add data to iterate + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(0, fabric1group1set3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(1, fabric2group1set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(2, fabric1group1set1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(3, fabric2group1set2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(4, fabric2group1set3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetGroupState(5, fabric1group1set2)); - chip::FabricIndex kFabricIndex1 = 1; - chip::FabricIndex kFabricIndex2 = 7; + // Iterate All Fabrics + + constexpr size_t expected_count = 6; + const GroupState expected[expected_count] = { fabric1group1set3, fabric2group1set1, fabric1group1set1, + fabric2group1set2, fabric2group1set3, fabric1group1set2 }; + GroupState state(0, 0, 0); + + auto it_all = groups->IterateGroupStates(); + NL_TEST_ASSERT(apSuite, it_all); + if (it_all) + { + size_t i = 0; + NL_TEST_ASSERT(apSuite, expected_count == it_all->Count()); + + while (it_all->Next(state) && i < expected_count) + { + NL_TEST_ASSERT(apSuite, state == expected[i]); + i++; + } + NL_TEST_ASSERT(apSuite, i == expected_count); + it_all->Release(); + } + + // Iterate Fabric 1 only + + constexpr size_t expected_count_f1 = 3; + const GroupState expected_f1[expected_count_f1] = { fabric1group1set3, fabric1group1set1, fabric1group1set2 }; + + auto it_f1 = groups->IterateGroupStates(kFabric1); + NL_TEST_ASSERT(apSuite, it_f1); + if (it_f1) + { + size_t i = 0; + NL_TEST_ASSERT(apSuite, expected_count_f1 == it_f1->Count()); + + while (it_f1->Next(state) && i < expected_count_f1) + { + NL_TEST_ASSERT(apSuite, state == expected_f1[i]); + i++; + } + NL_TEST_ASSERT(apSuite, i == expected_count_f1); + it_f1->Release(); + } + + // Iterate Fabric 2 only + + constexpr size_t expected_count_f2 = 3; + const GroupState expected_f2[expected_count_f2] = { fabric2group1set1, fabric2group1set2, fabric2group1set3 }; + + auto it_f2 = groups->IterateGroupStates(kFabric2); + NL_TEST_ASSERT(apSuite, it_f2); + if (it_f2) + { + size_t i = 0; + NL_TEST_ASSERT(apSuite, expected_count_f2 == it_f2->Count()); + + while (it_f2->Next(state) && i < expected_count_f2) + { + NL_TEST_ASSERT(apSuite, state == expected_f2[i]); + i++; + } + NL_TEST_ASSERT(apSuite, i == expected_count_f2); + it_f2->Release(); + } +} + +static EpochKey epoch_keys0[3] = { + { 0x1111111111111111, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { 0x2222222222222222, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, + { 0x3333333333333333, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } +}; +static EpochKey epoch_keys1[3] = { + { 0xaaaaaaaaaaaaaaaa, { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } }, + { 0xbbbbbbbbbbbbbbbb, { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f } }, + { 0xcccccccccccccccc, { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f } }, +}; +static EpochKey epoch_keys2[2] = { + { 0xeeeeeeeeeeeeeeee, { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf } }, + { 0xffffffffffffffff, { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf } }, +}; + +constexpr uint16_t kKeySetId0 = 0x0; +constexpr uint16_t kKeySetId1 = 0x1111; +constexpr uint16_t kKeySetId2 = 0x2222; +constexpr uint16_t kKeySetId3 = 0x3333; + +void TestKeySets(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + KeySet keyset0(KeySet::SecurityPolicy::kStandard, 3); + KeySet keyset1(KeySet::SecurityPolicy::kStandard, 1); + KeySet keyset2(KeySet::SecurityPolicy::kLowLatency, 2); + KeySet keyset3(KeySet::SecurityPolicy::kStandard, 3); + KeySet keysets; + + memcpy(keyset0.epoch_keys, epoch_keys0, sizeof(epoch_keys1)); + memcpy(keyset1.epoch_keys, epoch_keys1, sizeof(epoch_keys1)); + memcpy(keyset2.epoch_keys, epoch_keys2, sizeof(epoch_keys2)); + memcpy(keyset3.epoch_keys, epoch_keys1, sizeof(epoch_keys1)); + + // Add KeySets + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId1, keyset1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId0, keyset0)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId2, keyset2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId3, keyset3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId1, keyset3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId0, keyset0)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId2, keyset1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId3, keyset2)); + + // Get KeySets + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset3); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset1); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId0, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset0); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset2); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset2); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset1); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset3); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId0, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset0); + + // Remove Keysets + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveKeySet(kFabric1, 0xffff)); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric1, kKeySetId1)); // First + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric1, kKeySetId3)); // Last + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric2, kKeySetId2)); // Middle + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId0, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset0); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric1, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset2); + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset2); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric2, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset3); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->GetKeySet(kFabric2, kKeySetId0, keysets)); + NL_TEST_ASSERT(apSuite, keysets == keyset0); + + // Remove all + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveKeySet(kFabric1, kKeySetId3)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveKeySet(kFabric1, kKeySetId1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric1, kKeySetId0)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric1, kKeySetId2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric2, kKeySetId3)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->RemoveKeySet(kFabric2, kKeySetId2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric2, kKeySetId1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->RemoveKeySet(kFabric2, kKeySetId0)); + + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId0, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric1, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric2, kKeySetId3, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric2, kKeySetId2, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric2, kKeySetId1, keysets)); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == groups->GetKeySet(kFabric2, kKeySetId0, keysets)); +} + +void TestKeySetIterator(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); + + KeySet keyset0(KeySet::SecurityPolicy::kStandard, 3); + KeySet keyset1(KeySet::SecurityPolicy::kStandard, 1); + KeySet keyset2(KeySet::SecurityPolicy::kLowLatency, 2); + KeySet keyset3(KeySet::SecurityPolicy::kStandard, 3); + KeySet keysets; + + memcpy(keyset0.epoch_keys, epoch_keys0, sizeof(epoch_keys1)); + memcpy(keyset1.epoch_keys, epoch_keys1, sizeof(epoch_keys1)); + memcpy(keyset2.epoch_keys, epoch_keys2, sizeof(epoch_keys2)); + memcpy(keyset3.epoch_keys, epoch_keys1, sizeof(epoch_keys1)); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + // Add data to iterate + + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId1, keyset1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId0, keyset0)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId2, keyset2)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric1, kKeySetId3, keyset3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId1, keyset3)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId2, keyset1)); + NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->SetKeySet(kFabric2, kKeySetId3, keyset2)); + + // Iterate Fabric 1 + + std::map expected_f1{ + { kKeySetId1, keyset1 }, { kKeySetId0, keyset0 }, { kKeySetId2, keyset2 }, { kKeySetId3, keyset3 } + }; + + auto it_f1 = groups->IterateKeySets(kFabric1); + NL_TEST_ASSERT(apSuite, it_f1); + if (it_f1) + { + size_t i = 0; + NL_TEST_ASSERT(apSuite, expected_f1.size() == it_f1->Count()); + + while (it_f1->Next(keysets) && i < expected_f1.size()) + { + NL_TEST_ASSERT(apSuite, expected_f1.count(keysets.keyset_id) > 0); + NL_TEST_ASSERT(apSuite, keysets == expected_f1[keysets.keyset_id]); + i++; + } + NL_TEST_ASSERT(apSuite, i == expected_f1.size()); + it_f1->Release(); + } + + // Iterate Fabric 2 + + std::map expected_f2{ { kKeySetId3, keyset2 }, { kKeySetId1, keyset3 }, { kKeySetId2, keyset1 } }; + + auto it_f2 = groups->IterateKeySets(kFabric2); + NL_TEST_ASSERT(apSuite, it_f2); + if (it_f2) + { + size_t i = 0; + NL_TEST_ASSERT(apSuite, expected_f2.size() == it_f2->Count()); + + while (it_f2->Next(keysets) && i < expected_f2.size()) + { + NL_TEST_ASSERT(apSuite, expected_f2.count(keysets.keyset_id) > 0); + NL_TEST_ASSERT(apSuite, keysets == expected_f2[keysets.keyset_id]); + i++; + } + NL_TEST_ASSERT(apSuite, i == expected_f2.size()); + it_f2->Release(); + } +} + +void TestEndpoints(nlTestSuite * apSuite, void * apContext) +{ + GroupDataProvider * groups = GetGroupDataProvider(); + NL_TEST_ASSERT(apSuite, groups); - GroupDataProvider::GroupMapping group1a(1, 1, "Group 1.1"); - GroupDataProvider::GroupMapping group1b(1, 2, "Group 1.2"); - GroupDataProvider::GroupMapping group1c(1, 3, "Group 1.3"); CHIP_ERROR err = CHIP_NO_ERROR; bool exists = false; + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + NL_TEST_ASSERT(apSuite, groups); - exists = groups->GroupMappingExists(0xff, group1a); + exists = groups->GroupMappingExists(0xff, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - err = groups->AddGroupMapping(kFabricIndex1, group1a); + err = groups->AddGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex1, group1c); + err = groups->AddGroupMapping(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->RemoveGroupMapping(kFabricIndex1, group1a); + err = groups->RemoveGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->AddGroupMapping(kFabricIndex1, group1a); + err = groups->AddGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex1, group1b); + err = groups->AddGroupMapping(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->RemoveAllGroupMappings(kFabricIndex1, 1); + err = groups->RemoveAllGroupMappings(kFabric1, 1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, !exists); // Test multiple fabrics NL_TEST_ASSERT(apSuite, groups); - exists = groups->GroupMappingExists(0xff, group1a); + exists = groups->GroupMappingExists(0xff, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - err = groups->AddGroupMapping(kFabricIndex1, group1a); + err = groups->AddGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex1, group1c); + err = groups->AddGroupMapping(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex2, group1a); + err = groups->AddGroupMapping(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex2, group1c); + err = groups->AddGroupMapping(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->RemoveGroupMapping(kFabricIndex2, group1a); + err = groups->RemoveGroupMapping(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->AddGroupMapping(kFabricIndex2, group1a); + err = groups->AddGroupMapping(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex2, group1b); + err = groups->AddGroupMapping(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - err = groups->RemoveAllGroupMappings(kFabricIndex2, 1); + err = groups->RemoveAllGroupMappings(kFabric2, 1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, !exists); - - groups->Finish(); } void TestEndpointIterator(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); - - chip::FabricIndex kFabricIndex = 1; - GroupDataProvider::GroupMapping group1a(1, 1, "Group 1.1"); - GroupDataProvider::GroupMapping group1b(1, 3, "Group 1.3"); - GroupDataProvider::GroupMapping group2a(2, 2, "Group 2.2"); - GroupDataProvider::GroupMapping group3a(3, 1, "Group 3.1"); - GroupDataProvider::GroupMapping group3b(3, 2, "Group 3.2"); - GroupDataProvider::GroupMapping group3c(3, 3, "Group 3.3"); - CHIP_ERROR err = CHIP_NO_ERROR; - NL_TEST_ASSERT(apSuite, groups); - err = groups->AddGroupMapping(kFabricIndex, group3b); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + CHIP_ERROR err = CHIP_NO_ERROR; - err = groups->AddGroupMapping(kFabricIndex, group2a); + err = groups->AddGroupMapping(kFabric1, endpoint3group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex, group1b); + err = groups->AddGroupMapping(kFabric1, endpoint2group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex, group3a); + err = groups->AddGroupMapping(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex, group1a); + err = groups->AddGroupMapping(kFabric1, endpoint3group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex, group3c); + err = groups->AddGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex, group3a); + err = groups->AddGroupMapping(kFabric1, endpoint3group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); // Endpoint 1 - EndpointId endpoint = 1; - GroupDataProvider::GroupMappingIterator * it = groups->IterateGroupMappings(kFabricIndex, endpoint); + + auto * it = groups->IterateGroupMappings(kFabric1, kEndpoint1); NL_TEST_ASSERT(apSuite, it); GroupDataProvider::GroupMapping mapping; @@ -228,15 +929,15 @@ void TestEndpointIterator(nlTestSuite * apSuite, void * apContext) while (it->Next(mapping)) { count2++; - NL_TEST_ASSERT(apSuite, 1 == mapping.group || 3 == mapping.group); + NL_TEST_ASSERT(apSuite, kGroup1 == mapping.group || kGroup2 == mapping.group); } NL_TEST_ASSERT(apSuite, count2 == count1); it->Release(); it = nullptr; // Endpoint 3 - endpoint = 3; - it = groups->IterateGroupMappings(kFabricIndex, endpoint); + + it = groups->IterateGroupMappings(kFabric1, kEndpoint3); NL_TEST_ASSERT(apSuite, it); count1 = it->Count(); @@ -245,35 +946,31 @@ void TestEndpointIterator(nlTestSuite * apSuite, void * apContext) while (it->Next(mapping)) { count2++; - NL_TEST_ASSERT(apSuite, mapping.group > 0 && mapping.group < 4); + NL_TEST_ASSERT(apSuite, kGroup1 == mapping.group || kGroup2 == mapping.group || kGroup3 == mapping.group); } NL_TEST_ASSERT(apSuite, count2 == count1); it->Release(); it = nullptr; - - groups->Finish(); } void TestStates(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); NL_TEST_ASSERT(apSuite, groups); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); - - GroupDataProvider::GroupState state0a = { .fabric_index = 1, .group = 1, .key_set_index = 1 }; - - GroupDataProvider::GroupState state0b = { .fabric_index = 0, .group = 10, .key_set_index = 11 }; - GroupDataProvider::GroupState state1a = { .fabric_index = 1, .group = 1, .key_set_index = 2 }; - GroupDataProvider::GroupState state1b = { .fabric_index = 0, .group = 10, .key_set_index = 12 }; + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); - GroupDataProvider::GroupState state3b = { .fabric_index = 0, .group = 10, .key_set_index = 13 }; + GroupDataProvider::GroupState state0a(1, 1, 1); + GroupDataProvider::GroupState state0b(0, 10, 11); + GroupDataProvider::GroupState state1a(1, 1, 2); + GroupDataProvider::GroupState state1b(0, 10, 12); + GroupDataProvider::GroupState state3b(0, 10, 13); + GroupDataProvider::GroupState state4a(1, 5, 3); + GroupDataProvider::GroupState state4b(0, 10, 14); - GroupDataProvider::GroupState state4a = { .fabric_index = 1, .group = 5, .key_set_index = 3 }; - GroupDataProvider::GroupState state4b = { .fabric_index = 0, .group = 10, .key_set_index = 14 }; - - chip::FabricIndex fabric_index = 1; - CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; // First append err = groups->SetGroupState(0, state0a); @@ -287,7 +984,7 @@ void TestStates(nlTestSuite * apSuite, void * apContext) err = groups->SetGroupState(3, state0a); NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_ARGUMENT == err); - auto * it = groups->IterateGroupStates(fabric_index); + auto * it = groups->IterateGroupStates(kFabric1); NL_TEST_ASSERT(apSuite, it != nullptr); NL_TEST_ASSERT(apSuite, 2 == it->Count()); it->Release(); @@ -296,14 +993,14 @@ void TestStates(nlTestSuite * apSuite, void * apContext) err = groups->GetGroupState(0, state0b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state0a.group == state0b.group); - NL_TEST_ASSERT(apSuite, state0a.key_set_index == state0b.key_set_index); - NL_TEST_ASSERT(apSuite, fabric_index == state0b.fabric_index); + NL_TEST_ASSERT(apSuite, state0a.keyset_index == state0b.keyset_index); + NL_TEST_ASSERT(apSuite, kFabric1 == state0b.fabric_index); err = groups->GetGroupState(1, state1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state1a.group == state1b.group); - NL_TEST_ASSERT(apSuite, state1a.key_set_index == state1b.key_set_index); - NL_TEST_ASSERT(apSuite, fabric_index == state1b.fabric_index); + NL_TEST_ASSERT(apSuite, state1a.keyset_index == state1b.keyset_index); + NL_TEST_ASSERT(apSuite, kFabric1 == state1b.fabric_index); err = groups->GetGroupState(2, state3b); NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); @@ -312,19 +1009,19 @@ void TestStates(nlTestSuite * apSuite, void * apContext) NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); // Entry 1 should remain, now at slot 0 - state1b.group = 10; - state1b.key_set_index = 12; - state1b.fabric_index = 14; - err = groups->GetGroupState(0, state1b); + state1b.group = 10; + state1b.keyset_index = 12; + state1b.fabric_index = 14; + err = groups->GetGroupState(0, state1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state1a.group == state1b.group); - NL_TEST_ASSERT(apSuite, state1a.key_set_index == state1b.key_set_index); - NL_TEST_ASSERT(apSuite, fabric_index == state1b.fabric_index); + NL_TEST_ASSERT(apSuite, state1a.keyset_index == state1b.keyset_index); + NL_TEST_ASSERT(apSuite, kFabric1 == state1b.fabric_index); - state1b.group = 10; - state1b.key_set_index = 12; - state1b.fabric_index = 14; - err = groups->GetGroupState(1, state1b); + state1b.group = 10; + state1b.keyset_index = 12; + state1b.fabric_index = 14; + err = groups->GetGroupState(1, state1b); NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); err = groups->RemoveGroupState(0); @@ -344,7 +1041,7 @@ void TestStates(nlTestSuite * apSuite, void * apContext) err = groups->GetGroupState(0, state4b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state4a.group == state4b.group); - NL_TEST_ASSERT(apSuite, state4a.key_set_index == state4b.key_set_index); + NL_TEST_ASSERT(apSuite, state4a.keyset_index == state4b.keyset_index); NL_TEST_ASSERT(apSuite, state4a.fabric_index == state4b.fabric_index); // Incorrect fabric @@ -355,22 +1052,18 @@ void TestStates(nlTestSuite * apSuite, void * apContext) err = groups->RemoveGroupState(0); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - - groups->Finish(); } void TestStateIterator(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); + NL_TEST_ASSERT(apSuite, groups); - chip::FabricIndex kFabricIndex1 = 1; - chip::FabricIndex kFabricIndex2 = 2; - GroupDataProvider::GroupState state0 = { .fabric_index = kFabricIndex1, .group = 1, .key_set_index = 1 }; // Fabric1 - GroupDataProvider::GroupState state1 = { .fabric_index = kFabricIndex1, .group = 2, .key_set_index = 1 }; // Fabric1 - GroupDataProvider::GroupState state2 = { .fabric_index = kFabricIndex2, .group = 2, .key_set_index = 2 }; // Fabric2 - GroupDataProvider::GroupState state3 = { .fabric_index = kFabricIndex1, .group = 3, .key_set_index = 1 }; // Fabric1 - CHIP_ERROR err = CHIP_NO_ERROR; + GroupDataProvider::GroupState state0(kFabric1, 1, 1); + GroupDataProvider::GroupState state1(kFabric1, 2, 1); + GroupDataProvider::GroupState state2(kFabric2, 2, 2); + GroupDataProvider::GroupState state3(kFabric1, 3, 1); + CHIP_ERROR err = CHIP_NO_ERROR; NL_TEST_ASSERT(apSuite, groups); @@ -388,7 +1081,7 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) { // Fabric Index 1 has 3 entries - GroupDataProvider::GroupStateIterator * it = groups->IterateGroupStates(kFabricIndex1); + auto * it = groups->IterateGroupStates(kFabric1); NL_TEST_ASSERT(apSuite, it != nullptr); size_t count1 = it->Count(); @@ -397,8 +1090,8 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) GroupDataProvider::GroupState state; while (it->Next(state)) { - NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.key_set_index == 1)); - NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabricIndex1)); + NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.keyset_index == 1)); + NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabric1)); count2++; } NL_TEST_ASSERT(apSuite, count2 == count1); @@ -408,7 +1101,7 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) { // Fabric Index 2 has 1 entry - GroupDataProvider::GroupStateIterator * it = groups->IterateGroupStates(kFabricIndex2); + auto * it = groups->IterateGroupStates(kFabric2); NL_TEST_ASSERT(apSuite, it != nullptr); size_t count1 = it->Count(); @@ -416,8 +1109,8 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) GroupDataProvider::GroupState state; NL_TEST_ASSERT(apSuite, it->Next(state)); - NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.key_set_index == 2)); - NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabricIndex2)); + NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.keyset_index == 2)); + NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabric2)); NL_TEST_ASSERT(apSuite, !it->Next(state)); @@ -427,7 +1120,7 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) { // Fabric Index 1 has 3 entries + Fabric Index 2 has 1 entry - GroupDataProvider::GroupStateIterator * it = groups->IterateGroupStates(); + auto * it = groups->IterateGroupStates(); NL_TEST_ASSERT(apSuite, it != nullptr); size_t count1 = it->Count(); @@ -436,47 +1129,33 @@ void TestStateIterator(nlTestSuite * apSuite, void * apContext) GroupDataProvider::GroupState state; while (it->Next(state)) { - NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabricIndex1 || state.fabric_index == kFabricIndex2)); - NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.key_set_index == 1 || state.key_set_index == 2)); + NL_TEST_ASSERT(apSuite, (state.fabric_index == kFabric1 || state.fabric_index == kFabric2)); + NL_TEST_ASSERT(apSuite, (state.group > 0 && state.group < 4) && (state.keyset_index == 1 || state.keyset_index == 2)); count2++; } NL_TEST_ASSERT(apSuite, count2 == count1); it->Release(); it = nullptr; } - - groups->Finish(); } -static GroupDataProvider::EpochKey epoch_keys0[3] = { - { 0x0000000000000000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, - { 0x0000000000000000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, - { 0x0000000000000000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } -}; -static GroupDataProvider::EpochKey epoch_keys1[3] = { - { 0x1000000000000000, { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } }, - { 0x2000000000000000, { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f } }, - { 0x3000000000000000, { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f } }, -}; -static GroupDataProvider::EpochKey epoch_keys2[2] = { - { 0xa000000000000000, { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf } }, - { 0xb000000000000000, { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf } }, -}; - void TestKeys(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); + NL_TEST_ASSERT(apSuite, groups); + + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); // Pairs keys0[a|b], keys1[a|b] have different values. [b] is used as Get target, so it // should get overwritten with the values from [a]. - KeySet keys0a = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 3 }; - KeySet keys0b = { .policy = KeySet::SecurityPolicy::kLowLatency, .num_keys_used = 2 }; - KeySet keys1a = { .policy = KeySet::SecurityPolicy::kLowLatency, .num_keys_used = 3 }; - KeySet keys1b = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 2 }; - KeySet keys3 = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 2 }; - chip::FabricIndex kFabricIndex = 1; - CHIP_ERROR err = CHIP_NO_ERROR; + KeySet keys0a(0, KeySet::SecurityPolicy::kStandard, 3); + KeySet keys0b(0, KeySet::SecurityPolicy::kStandard, 2); + KeySet keys1a(0, KeySet::SecurityPolicy::kStandard, 3); + KeySet keys1b(0, KeySet::SecurityPolicy::kStandard, 2); + KeySet keys3(0, KeySet::SecurityPolicy::kStandard, 2); + CHIP_ERROR err = CHIP_NO_ERROR; NL_TEST_ASSERT(apSuite, groups); @@ -486,53 +1165,54 @@ void TestKeys(nlTestSuite * apSuite, void * apContext) memcpy(keys1b.epoch_keys, epoch_keys0, sizeof(epoch_keys0)); memcpy(keys3.epoch_keys, epoch_keys2, sizeof(epoch_keys2)); - err = groups->SetKeySet(kFabricIndex, 0, keys0a); + err = groups->SetKeySet(kFabric1, kKeySetId0, keys0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex, 1, keys1a); + err = groups->SetKeySet(kFabric1, kKeySetId1, keys1a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - auto * it = groups->IterateKeySets(kFabricIndex); + auto * it = groups->IterateKeySets(kFabric1); NL_TEST_ASSERT(apSuite, it != nullptr); NL_TEST_ASSERT(apSuite, it->Count() == 2); - err = groups->GetKeySet(kFabricIndex, 0, keys0b); + err = groups->GetKeySet(kFabric1, kKeySetId0, keys0b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys0b.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys0b.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys0b.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex, 1, keys1b); + err = groups->GetKeySet(kFabric1, kKeySetId1, keys1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys1a.policy == keys1b.policy); NL_TEST_ASSERT(apSuite, keys1a.num_keys_used == keys1b.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys1a.epoch_keys, keys1b.epoch_keys, sizeof(keys1a.epoch_keys[0]) * keys1a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex, 3, keys3); + err = groups->GetKeySet(kFabric1, kKeySetId3, keys3); NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); - err = groups->RemoveKeySet(kFabricIndex, 0); + err = groups->RemoveKeySet(kFabric1, kKeySetId0); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->GetKeySet(kFabricIndex, 0, keys0b); - NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); - - err = groups->GetKeySet(kFabricIndex, 1, keys1b); + err = groups->GetKeySet(kFabric1, kKeySetId1, keys1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - groups->Finish(); + err = groups->GetKeySet(kFabric1, kKeySetId0, keys0b); + NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); } void TestKeysIterator(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); + NL_TEST_ASSERT(apSuite, groups); - KeySet keys0 = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 3 }; - KeySet keys1 = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 2 }; - KeySet keys2 = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 3 }; - chip::FabricIndex kFabricIndex = 1; - CHIP_ERROR err = CHIP_NO_ERROR; + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); + + KeySet keys0(0, KeySet::SecurityPolicy::kStandard, 3); + KeySet keys1(0, KeySet::SecurityPolicy::kStandard, 2); + KeySet keys2(0, KeySet::SecurityPolicy::kStandard, 3); + CHIP_ERROR err = CHIP_NO_ERROR; NL_TEST_ASSERT(apSuite, groups); @@ -542,16 +1222,16 @@ void TestKeysIterator(nlTestSuite * apSuite, void * apContext) NL_TEST_ASSERT(apSuite, groups); - err = groups->SetKeySet(kFabricIndex, 2, keys2); + err = groups->SetKeySet(kFabric1, kKeySetId2, keys2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex, 0, keys0); + err = groups->SetKeySet(kFabric1, kKeySetId0, keys0); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex, 1, keys1); + err = groups->SetKeySet(kFabric1, kKeySetId1, keys1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - GroupDataProvider::KeySetIterator * it = groups->IterateKeySets(kFabricIndex); + auto * it = groups->IterateKeySets(kFabric1); NL_TEST_ASSERT(apSuite, it); size_t count1 = it->Count(); @@ -559,89 +1239,80 @@ void TestKeysIterator(nlTestSuite * apSuite, void * apContext) NL_TEST_ASSERT(apSuite, 3 == count1); GroupDataProvider::KeySet keys; - uint16_t last_key_set_index = UINT16_MAX; + uint16_t last_keyset_id = UINT16_MAX; while (it->Next(keys)) { - NL_TEST_ASSERT(apSuite, keys.key_set_index >= 0 && keys.key_set_index <= 2); - NL_TEST_ASSERT(apSuite, keys.key_set_index != last_key_set_index); - last_key_set_index = keys.key_set_index; + NL_TEST_ASSERT(apSuite, keys.keyset_id == kKeySetId0 || keys.keyset_id == kKeySetId1 || keys.keyset_id == kKeySetId2); + NL_TEST_ASSERT(apSuite, keys.keyset_id != last_keyset_id); + last_keyset_id = keys.keyset_id; count2++; } NL_TEST_ASSERT(apSuite, count2 == count1); it->Release(); it = nullptr; - - groups->Finish(); } void TestPerFabricData(nlTestSuite * apSuite, void * apContext) { GroupDataProvider * groups = GetGroupDataProvider(); - NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == groups->Init()); + NL_TEST_ASSERT(apSuite, groups); - const chip::FabricIndex kFabricIndex1 = 33; - const chip::FabricIndex kFabricIndex2 = 77; - const chip::GroupId kGroupId1 = 111; - const chip::GroupId kGroupId2 = 222; - const chip::GroupId kGroupId3 = 333; + // Reset test + groups->RemoveFabric(kFabric1); + groups->RemoveFabric(kFabric2); - const GroupDataProvider::GroupMapping group1a(101, kGroupId1, "Group 1.1"); - const GroupDataProvider::GroupMapping group1b(101, kGroupId2, "Group 1.2"); - const GroupDataProvider::GroupMapping group1c(101, kGroupId3, "Group 1.3"); CHIP_ERROR err = CHIP_NO_ERROR; bool exists = false; - NL_TEST_ASSERT(apSuite, groups); - // Mappings - err = groups->AddGroupMapping(kFabricIndex1, group1a); + err = groups->AddGroupMapping(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex1, group1b); + err = groups->AddGroupMapping(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex1, group1c); + err = groups->AddGroupMapping(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex2, group1a); + err = groups->AddGroupMapping(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->AddGroupMapping(kFabricIndex2, group1c); + err = groups->AddGroupMapping(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); // States - const GroupDataProvider::GroupState state0a = { .fabric_index = kFabricIndex1, .group = kGroupId1, .key_set_index = 101 }; - GroupDataProvider::GroupState state0b = { .fabric_index = 0, .group = 0, .key_set_index = 0 }; + const GroupDataProvider::GroupState state0a(kFabric1, kGroup1, 101); + GroupDataProvider::GroupState state0b(0, 0, 0); - const GroupDataProvider::GroupState state1a = { .fabric_index = kFabricIndex2, .group = kGroupId1, .key_set_index = 102 }; - GroupDataProvider::GroupState state1b = { .fabric_index = 0, .group = 0, .key_set_index = 0 }; + const GroupDataProvider::GroupState state1a(kFabric2, kGroup1, 102); + GroupDataProvider::GroupState state1b(0, 0, 0); - const GroupDataProvider::GroupState state2a = { .fabric_index = kFabricIndex2, .group = kGroupId2, .key_set_index = 101 }; - GroupDataProvider::GroupState state2b = { .fabric_index = 0, .group = 0, .key_set_index = 0 }; + const GroupDataProvider::GroupState state2a(kFabric2, kGroup2, 101); + GroupDataProvider::GroupState state2b(0, 0, 0); - const GroupDataProvider::GroupState state3a = { .fabric_index = kFabricIndex1, .group = kGroupId2, .key_set_index = 102 }; - GroupDataProvider::GroupState state4b = { .fabric_index = 0, .group = 0, .key_set_index = 0 }; + const GroupDataProvider::GroupState state3a(kFabric1, kGroup2, 102); + GroupDataProvider::GroupState state4b(0, 0, 0); err = groups->SetGroupState(0, state0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); @@ -659,28 +1330,28 @@ void TestPerFabricData(nlTestSuite * apSuite, void * apContext) NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state0a.fabric_index == state0b.fabric_index); NL_TEST_ASSERT(apSuite, state0a.group == state0b.group); - NL_TEST_ASSERT(apSuite, state0a.key_set_index == state0b.key_set_index); + NL_TEST_ASSERT(apSuite, state0a.keyset_index == state0b.keyset_index); err = groups->GetGroupState(1, state1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state1a.fabric_index == state1b.fabric_index); NL_TEST_ASSERT(apSuite, state1a.group == state1b.group); - NL_TEST_ASSERT(apSuite, state1a.key_set_index == state1b.key_set_index); + NL_TEST_ASSERT(apSuite, state1a.keyset_index == state1b.keyset_index); err = groups->GetGroupState(2, state2b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state2a.fabric_index == state2b.fabric_index); NL_TEST_ASSERT(apSuite, state2a.group == state2b.group); - NL_TEST_ASSERT(apSuite, state2a.key_set_index == state2b.key_set_index); + NL_TEST_ASSERT(apSuite, state2a.keyset_index == state2b.keyset_index); err = groups->GetGroupState(4, state4b); NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); // Keys - KeySet keys0a = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 3 }; - KeySet keys1a = { .policy = KeySet::SecurityPolicy::kLowLatency, .num_keys_used = 3 }; - KeySet keys_out = { .policy = KeySet::SecurityPolicy::kStandard, .num_keys_used = 0 }; + KeySet keys0a(0, KeySet::SecurityPolicy::kStandard, 3); + KeySet keys1a(0, KeySet::SecurityPolicy::kLowLatency, 3); + KeySet keys_out(0, KeySet::SecurityPolicy::kStandard, 0); NL_TEST_ASSERT(apSuite, groups); @@ -688,88 +1359,91 @@ void TestPerFabricData(nlTestSuite * apSuite, void * apContext) memcpy(keys1a.epoch_keys, epoch_keys1, sizeof(epoch_keys1)); memcpy(keys_out.epoch_keys, epoch_keys0, sizeof(epoch_keys0)); - err = groups->SetKeySet(kFabricIndex2, 101, keys0a); + err = groups->SetKeySet(kFabric2, kKeySetId0, keys0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex1, 202, keys0a); + err = groups->SetKeySet(kFabric1, kKeySetId0, keys0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex2, 303, keys1a); + err = groups->SetKeySet(kFabric2, kKeySetId1, keys1a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex1, 404, keys1a); + err = groups->SetKeySet(kFabric1, kKeySetId1, keys1a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex2, 505, keys0a); + err = groups->SetKeySet(kFabric2, kKeySetId2, keys0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->SetKeySet(kFabricIndex1, 606, keys0a); + err = groups->SetKeySet(kFabric1, kKeySetId2, keys0a); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); - err = groups->GetKeySet(kFabricIndex2, 101, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId0, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex2, 303, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId1, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys1a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys1a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys1a.epoch_keys, keys_out.epoch_keys, sizeof(keys1a.epoch_keys[0]) * keys1a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex2, 505, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId2, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex1, 202, keys_out); + err = groups->GetKeySet(kFabric1, kKeySetId2, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex1, 404, keys_out); + err = groups->GetKeySet(kFabric1, kKeySetId1, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys1a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys1a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys1a.epoch_keys, keys_out.epoch_keys, sizeof(keys1a.epoch_keys[0]) * keys1a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex1, 606, keys_out); + err = groups->GetKeySet(kFabric1, kKeySetId0, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); + // // Remove Fabric - err = groups->RemoveFabric(kFabricIndex1); + // + + err = groups->RemoveFabric(kFabric1); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); // Mappings - exists = groups->GroupMappingExists(kFabricIndex1, group1a); + exists = groups->GroupMappingExists(kFabric1, endpoint1group1); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1b); + exists = groups->GroupMappingExists(kFabric1, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex1, group1c); + exists = groups->GroupMappingExists(kFabric1, endpoint1group3); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1a); + exists = groups->GroupMappingExists(kFabric2, endpoint1group1); NL_TEST_ASSERT(apSuite, exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1b); + exists = groups->GroupMappingExists(kFabric2, endpoint1group2); NL_TEST_ASSERT(apSuite, !exists); - exists = groups->GroupMappingExists(kFabricIndex2, group1c); + exists = groups->GroupMappingExists(kFabric2, endpoint1group3); NL_TEST_ASSERT(apSuite, exists); // States: Removing the fabric shift the remaining groups states to a lower index @@ -778,50 +1452,48 @@ void TestPerFabricData(nlTestSuite * apSuite, void * apContext) NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state1a.fabric_index == state0b.fabric_index); NL_TEST_ASSERT(apSuite, state1a.group == state0b.group); - NL_TEST_ASSERT(apSuite, state1a.key_set_index == state0b.key_set_index); + NL_TEST_ASSERT(apSuite, state1a.keyset_index == state0b.keyset_index); err = groups->GetGroupState(1, state1b); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, state2a.fabric_index == state1b.fabric_index); NL_TEST_ASSERT(apSuite, state2a.group == state1b.group); - NL_TEST_ASSERT(apSuite, state2a.key_set_index == state1b.key_set_index); + NL_TEST_ASSERT(apSuite, state2a.keyset_index == state1b.keyset_index); err = groups->GetGroupState(2, state2b); NL_TEST_ASSERT(apSuite, CHIP_ERROR_KEY_NOT_FOUND == err); // Keys - err = groups->GetKeySet(kFabricIndex2, 101, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId0, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex2, 303, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId1, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys1a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys1a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys1a.epoch_keys, keys_out.epoch_keys, sizeof(keys1a.epoch_keys[0]) * keys1a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex2, 505, keys_out); + err = groups->GetKeySet(kFabric2, kKeySetId0, keys_out); NL_TEST_ASSERT(apSuite, CHIP_NO_ERROR == err); NL_TEST_ASSERT(apSuite, keys0a.policy == keys_out.policy); NL_TEST_ASSERT(apSuite, keys0a.num_keys_used == keys_out.num_keys_used); NL_TEST_ASSERT(apSuite, 0 == memcmp(keys0a.epoch_keys, keys_out.epoch_keys, sizeof(keys0a.epoch_keys[0]) * keys0a.num_keys_used)); - err = groups->GetKeySet(kFabricIndex1, 202, keys_out); + err = groups->GetKeySet(kFabric1, 202, keys_out); NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_FABRIC_ID == err); - err = groups->GetKeySet(kFabricIndex1, 404, keys_out); + err = groups->GetKeySet(kFabric1, 404, keys_out); NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_FABRIC_ID == err); - err = groups->GetKeySet(kFabricIndex1, 606, keys_out); + err = groups->GetKeySet(kFabric1, 606, keys_out); NL_TEST_ASSERT(apSuite, CHIP_ERROR_INVALID_FABRIC_ID == err); - - groups->Finish(); } } // namespace TestGroups @@ -830,18 +1502,17 @@ void TestPerFabricData(nlTestSuite * apSuite, void * apContext) namespace { +static chip::TestPersistentStorageDelegate sDelegate; +static GroupDataProviderImpl sProvider(sDelegate); + /** * Set up the test suite. */ int Test_Setup(void * inContext) { - CHIP_ERROR error = chip::Platform::MemoryInit(); - - if (error != CHIP_NO_ERROR) - { - return FAILURE; - } - + SetGroupDataProvider(&sProvider); + VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); + VerifyOrReturnError(CHIP_NO_ERROR == sProvider.Init(), FAILURE); return SUCCESS; } @@ -851,26 +1522,36 @@ int Test_Setup(void * inContext) int Test_Teardown(void * inContext) { chip::Platform::MemoryShutdown(); + GroupDataProvider * groups = GetGroupDataProvider(); + if (nullptr != groups) + { + groups->Finish(); + } return SUCCESS; } -const nlTest sTests[] = { NL_TEST_DEF("TestEndpoints", chip::app::TestGroups::TestEndpoints), +const nlTest sTests[] = { NL_TEST_DEF("TestStorageDelegate", chip::app::TestGroups::TestStorageDelegate), + NL_TEST_DEF("TestGroupMappings", chip::app::TestGroups::TestGroupMappings), + NL_TEST_DEF("TestGroupMappingIterator", chip::app::TestGroups::TestGroupMappingIterator), + NL_TEST_DEF("TestGroupStates", chip::app::TestGroups::TestGroupStates), + NL_TEST_DEF("TestGroupStateIterator", chip::app::TestGroups::TestGroupStateIterator), + NL_TEST_DEF("TestKeySets", chip::app::TestGroups::TestKeySets), + NL_TEST_DEF("TestKeySetIterator", chip::app::TestGroups::TestKeySetIterator), + // Old Tests + NL_TEST_DEF("TestEndpoints", chip::app::TestGroups::TestEndpoints), NL_TEST_DEF("TestEndpointIterator", chip::app::TestGroups::TestEndpointIterator), NL_TEST_DEF("TestStates", chip::app::TestGroups::TestStates), NL_TEST_DEF("TestStateIterator", chip::app::TestGroups::TestStateIterator), NL_TEST_DEF("TestKeys", chip::app::TestGroups::TestKeys), NL_TEST_DEF("TestKeysIterator", chip::app::TestGroups::TestKeysIterator), - NL_TEST_DEF("TestPerFabricData", chip::app::TestGroups::TestPerFabricData), - NL_TEST_SENTINEL() }; + NL_TEST_DEF("TestPerFabricData", chip::app::TestGroups::TestPerFabricData), NL_TEST_SENTINEL() }; } // namespace int TestGroups() { nlTestSuite theSuite = { "GroupDataProvider", &sTests[0], &Test_Setup, &Test_Teardown }; -#if 0 // Tests disabled due to insuficient CHIP configuration (https://github.com/project-chip/connectedhomeip/issues/11312). nlTestRunner(&theSuite, nullptr); -#endif return (nlTestRunnerStats(&theSuite)); } diff --git a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m index d94a295c811eac..b6ef2e4f78f2f4 100644 --- a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m @@ -29656,7 +29656,110 @@ - (void)testSendClusterTestGroupsCluster_000005_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000006_ViewGroup +- (void)testSendClusterTestGroupsCluster_000006_GetGroupMembership +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Get Group Membership 1 (all)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterGetGroupMembershipParams alloc] init]; + { + NSMutableArray * temp_0 = [[NSMutableArray alloc] init]; + params.groupList = temp_0; + } + [cluster getGroupMembershipWithParams:params + completionHandler:^( + CHIPGroupsClusterGetGroupMembershipResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Get Group Membership 1 (all) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.capacity; + XCTAssertEqual([actualValue unsignedCharValue], 255); + } + { + id actualValue = values.groupList; + XCTAssertEqual([actualValue count], 1); + XCTAssertEqual([actualValue[0] unsignedShortValue], 1U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000007_AddGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Add Group 2 (new)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterAddGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:4369U]; + params.groupName = @"Group #2"; + [cluster addGroupWithParams:params + completionHandler:^(CHIPGroupsClusterAddGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Add Group 2 (new) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 4369U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000008_ViewGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 2 (new)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterViewGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:4369U]; + [cluster viewGroupWithParams:params + completionHandler:^(CHIPGroupsClusterViewGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"View Group 2 (new) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 4369U); + } + { + id actualValue = values.groupName; + XCTAssertTrue([actualValue isEqualToString:@"Group #2"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000009_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 3 (not found)"]; @@ -29687,7 +29790,39 @@ - (void)testSendClusterTestGroupsCluster_000006_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000007_ViewGroup +- (void)testSendClusterTestGroupsCluster_000010_AddGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Add Group 3 (new)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterAddGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:32767U]; + params.groupName = @"Group #3"; + [cluster addGroupWithParams:params + completionHandler:^(CHIPGroupsClusterAddGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Add Group 3 (new) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 32767U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000011_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 1 (existing)"]; @@ -29722,7 +29857,116 @@ - (void)testSendClusterTestGroupsCluster_000007_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000008_RemoveGroup +- (void)testSendClusterTestGroupsCluster_000012_ViewGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 2 (existing)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterViewGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:4369U]; + [cluster viewGroupWithParams:params + completionHandler:^(CHIPGroupsClusterViewGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"View Group 2 (existing) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 4369U); + } + { + id actualValue = values.groupName; + XCTAssertTrue([actualValue isEqualToString:@"Group #2"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000013_GetGroupMembership +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Get Group Membership 2"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterGetGroupMembershipParams alloc] init]; + { + NSMutableArray * temp_0 = [[NSMutableArray alloc] init]; + temp_0[0] = [NSNumber numberWithUnsignedShort:2U]; + temp_0[1] = [NSNumber numberWithUnsignedShort:3U]; + temp_0[2] = [NSNumber numberWithUnsignedShort:32767U]; + params.groupList = temp_0; + } + [cluster getGroupMembershipWithParams:params + completionHandler:^( + CHIPGroupsClusterGetGroupMembershipResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Get Group Membership 2 Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.capacity; + XCTAssertEqual([actualValue unsignedCharValue], 255); + } + { + id actualValue = values.groupList; + XCTAssertEqual([actualValue count], 1); + XCTAssertEqual([actualValue[0] unsignedShortValue], 32767U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000014_ViewGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 3 (new)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterViewGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:32767U]; + [cluster viewGroupWithParams:params + completionHandler:^(CHIPGroupsClusterViewGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"View Group 3 (new) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 32767U); + } + { + id actualValue = values.groupName; + XCTAssertTrue([actualValue isEqualToString:@"Group #3"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000015_RemoveGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"Remove Group 0 (invalid)"]; @@ -29753,7 +29997,7 @@ - (void)testSendClusterTestGroupsCluster_000008_RemoveGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000009_RemoveGroup +- (void)testSendClusterTestGroupsCluster_000016_RemoveGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"Remove Group 4 (not found)"]; @@ -29784,7 +30028,38 @@ - (void)testSendClusterTestGroupsCluster_000009_RemoveGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000010_ViewGroup +- (void)testSendClusterTestGroupsCluster_000017_RemoveGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Remove Group 2 (existing)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterRemoveGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:4369U]; + [cluster removeGroupWithParams:params + completionHandler:^(CHIPGroupsClusterRemoveGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Remove Group 2 (existing) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 4369U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000018_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 1 (not removed)"]; @@ -29819,7 +30094,7 @@ - (void)testSendClusterTestGroupsCluster_000010_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000011_ViewGroup +- (void)testSendClusterTestGroupsCluster_000019_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 2 (removed)"]; @@ -29850,7 +30125,82 @@ - (void)testSendClusterTestGroupsCluster_000011_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000012_RemoveAllGroups +- (void)testSendClusterTestGroupsCluster_000020_ViewGroup +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 3 (not removed)"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterViewGroupParams alloc] init]; + params.groupId = [NSNumber numberWithUnsignedShort:32767U]; + [cluster viewGroupWithParams:params + completionHandler:^(CHIPGroupsClusterViewGroupResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"View Group 3 (not removed) Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.status; + XCTAssertEqual([actualValue unsignedCharValue], 0); + } + { + id actualValue = values.groupId; + XCTAssertEqual([actualValue unsignedShortValue], 32767U); + } + { + id actualValue = values.groupName; + XCTAssertTrue([actualValue isEqualToString:@"Group #3"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000021_GetGroupMembership +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Get Group Membership 3"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterGetGroupMembershipParams alloc] init]; + { + NSMutableArray * temp_0 = [[NSMutableArray alloc] init]; + temp_0[0] = [NSNumber numberWithUnsignedShort:1U]; + temp_0[1] = [NSNumber numberWithUnsignedShort:2U]; + temp_0[2] = [NSNumber numberWithUnsignedShort:4369U]; + temp_0[3] = [NSNumber numberWithUnsignedShort:3U]; + params.groupList = temp_0; + } + [cluster getGroupMembershipWithParams:params + completionHandler:^( + CHIPGroupsClusterGetGroupMembershipResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Get Group Membership 3 Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.capacity; + XCTAssertEqual([actualValue unsignedCharValue], 255); + } + { + id actualValue = values.groupList; + XCTAssertEqual([actualValue count], 1); + XCTAssertEqual([actualValue[0] unsignedShortValue], 1U); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestGroupsCluster_000022_RemoveAllGroups { XCTestExpectation * expectation = [self expectationWithDescription:@"Remove All"]; @@ -29869,7 +30219,7 @@ - (void)testSendClusterTestGroupsCluster_000012_RemoveAllGroups [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000013_ViewGroup +- (void)testSendClusterTestGroupsCluster_000023_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 1 (removed)"]; @@ -29900,7 +30250,7 @@ - (void)testSendClusterTestGroupsCluster_000013_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000014_ViewGroup +- (void)testSendClusterTestGroupsCluster_000024_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 2 (still removed)"]; @@ -29931,7 +30281,7 @@ - (void)testSendClusterTestGroupsCluster_000014_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestGroupsCluster_000015_ViewGroup +- (void)testSendClusterTestGroupsCluster_000025_ViewGroup { XCTestExpectation * expectation = [self expectationWithDescription:@"View Group 3 (removed)"]; @@ -29962,6 +30312,46 @@ - (void)testSendClusterTestGroupsCluster_000015_ViewGroup [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } +- (void)testSendClusterTestGroupsCluster_000026_GetGroupMembership +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Get Group Membership 4"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestGroups * cluster = [[CHIPTestGroups alloc] initWithDevice:device endpoint:1 queue:queue]; + XCTAssertNotNil(cluster); + + __auto_type * params = [[CHIPGroupsClusterGetGroupMembershipParams alloc] init]; + { + NSMutableArray * temp_0 = [[NSMutableArray alloc] init]; + temp_0[0] = [NSNumber numberWithUnsignedShort:1U]; + temp_0[1] = [NSNumber numberWithUnsignedShort:2U]; + temp_0[2] = [NSNumber numberWithUnsignedShort:4369U]; + temp_0[3] = [NSNumber numberWithUnsignedShort:3U]; + temp_0[4] = [NSNumber numberWithUnsignedShort:32767U]; + params.groupList = temp_0; + } + [cluster getGroupMembershipWithParams:params + completionHandler:^( + CHIPGroupsClusterGetGroupMembershipResponseParams * _Nullable values, NSError * _Nullable err) { + NSLog(@"Get Group Membership 4 Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = values.capacity; + XCTAssertEqual([actualValue unsignedCharValue], 255); + } + { + id actualValue = values.groupList; + XCTAssertEqual([actualValue count], 0); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} - (void)testSendClusterTestIdentifyCluster_000000_WaitForCommissionee { diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h new file mode 100644 index 00000000000000..4eb8d5bf0cf4bf --- /dev/null +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -0,0 +1,67 @@ +/* + * + * 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. + */ +#pragma once + +#include +#include + +namespace chip { + +/** + * This is the common key allocation policy for all classes using PersistentStorageDelegate storage + */ +class DefaultStorageKeyAllocator +{ +public: + DefaultStorageKeyAllocator() = default; + + // Fabric Table + + const char * FabricTable(chip::FabricIndex fabric) { return Format("f/%x/t", fabric); } + + // Group Data Provider + + const char * FabricGroups(chip::FabricIndex fabric) { return Format("f/%x/g", fabric); } + const char * FabricEndpoint(chip::FabricIndex fabric, chip::EndpointId endpoint) + { + return Format("f/%x/e/%x", fabric, endpoint); + } + const char * FabricEndpointGroup(chip::FabricIndex fabric, chip::EndpointId endpoint, chip::GroupId group) + { + return Format("f/%x/e/%x/g/%x", fabric, endpoint, group); + } + const char * GroupStates() { return Format("g/s"); } + const char * GroupState(uint16_t state_id) { return Format("g/s/%x", state_id); } + const char * FabricKeySet(chip::FabricIndex fabric, uint16_t keyset_id) { return Format("f/%x/k/%x", fabric, keyset_id); } + const char * KeyName() { return mKeyName; } + +private: + static const size_t kKeyLengthMax = 32; + + const char * Format(const char * format...) + { + va_list args; + va_start(args, format); + vsnprintf(mKeyName, sizeof(mKeyName), format, args); + va_end(args); + return mKeyName; + } + + char mKeyName[kKeyLengthMax + 1] = { 0 }; +}; + +} // namespace chip diff --git a/src/lib/support/TestPersistentStorageDelegate.h b/src/lib/support/TestPersistentStorageDelegate.h new file mode 100644 index 00000000000000..e21159cb0c1581 --- /dev/null +++ b/src/lib/support/TestPersistentStorageDelegate.h @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace chip { + +class TestPersistentStorageDelegate : public PersistentStorageDelegate +{ +public: + TestPersistentStorageDelegate() {} + + CHIP_ERROR SyncGetKeyValue(const char * key, void * buffer, uint16_t & size) override + { + bool contains = mStorage.find(key) != mStorage.end(); + VerifyOrReturnError(contains, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); + + std::vector & value = mStorage[key]; + uint16_t value_size = static_cast(value.size()); + VerifyOrReturnError(value_size <= size, CHIP_ERROR_BUFFER_TOO_SMALL); + + size = std::min(value_size, size); + memcpy(buffer, value.data(), size); + return CHIP_NO_ERROR; + } + + CHIP_ERROR SyncSetKeyValue(const char * key, const void * value, uint16_t size) override + { + const uint8_t * bytes = static_cast(value); + mStorage[key] = std::vector(bytes, bytes + size); + return CHIP_NO_ERROR; + } + + CHIP_ERROR SyncDeleteKeyValue(const char * key) override + { + mStorage.erase(key); + return CHIP_NO_ERROR; + } + +protected: + std::map> mStorage; +}; + +} // namespace chip diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 928ed6f5b590db..d8e33aa0a151e9 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -46135,44 +46135,88 @@ class TestGroupsCluster : public TestCommand err = TestViewGroup2NotFound_5(); break; case 6: - ChipLogProgress(chipTool, " ***** Test Step 6 : View Group 3 (not found)\n"); - err = TestViewGroup3NotFound_6(); + ChipLogProgress(chipTool, " ***** Test Step 6 : Get Group Membership 1 (all)\n"); + err = TestGetGroupMembership1All_6(); break; case 7: - ChipLogProgress(chipTool, " ***** Test Step 7 : View Group 1 (existing)\n"); - err = TestViewGroup1Existing_7(); + ChipLogProgress(chipTool, " ***** Test Step 7 : Add Group 2 (new)\n"); + err = TestAddGroup2New_7(); break; case 8: - ChipLogProgress(chipTool, " ***** Test Step 8 : Remove Group 0 (invalid)\n"); - err = TestRemoveGroup0Invalid_8(); + ChipLogProgress(chipTool, " ***** Test Step 8 : View Group 2 (new)\n"); + err = TestViewGroup2New_8(); break; case 9: - ChipLogProgress(chipTool, " ***** Test Step 9 : Remove Group 4 (not found)\n"); - err = TestRemoveGroup4NotFound_9(); + ChipLogProgress(chipTool, " ***** Test Step 9 : View Group 3 (not found)\n"); + err = TestViewGroup3NotFound_9(); break; case 10: - ChipLogProgress(chipTool, " ***** Test Step 10 : View Group 1 (not removed)\n"); - err = TestViewGroup1NotRemoved_10(); + ChipLogProgress(chipTool, " ***** Test Step 10 : Add Group 3 (new)\n"); + err = TestAddGroup3New_10(); break; case 11: - ChipLogProgress(chipTool, " ***** Test Step 11 : View Group 2 (removed)\n"); - err = TestViewGroup2Removed_11(); + ChipLogProgress(chipTool, " ***** Test Step 11 : View Group 1 (existing)\n"); + err = TestViewGroup1Existing_11(); break; case 12: - ChipLogProgress(chipTool, " ***** Test Step 12 : Remove All\n"); - err = TestRemoveAll_12(); + ChipLogProgress(chipTool, " ***** Test Step 12 : View Group 2 (existing)\n"); + err = TestViewGroup2Existing_12(); break; case 13: - ChipLogProgress(chipTool, " ***** Test Step 13 : View Group 1 (removed)\n"); - err = TestViewGroup1Removed_13(); + ChipLogProgress(chipTool, " ***** Test Step 13 : Get Group Membership 2\n"); + err = TestGetGroupMembership2_13(); break; case 14: - ChipLogProgress(chipTool, " ***** Test Step 14 : View Group 2 (still removed)\n"); - err = TestViewGroup2StillRemoved_14(); + ChipLogProgress(chipTool, " ***** Test Step 14 : View Group 3 (new)\n"); + err = TestViewGroup3New_14(); break; case 15: - ChipLogProgress(chipTool, " ***** Test Step 15 : View Group 3 (removed)\n"); - err = TestViewGroup3Removed_15(); + ChipLogProgress(chipTool, " ***** Test Step 15 : Remove Group 0 (invalid)\n"); + err = TestRemoveGroup0Invalid_15(); + break; + case 16: + ChipLogProgress(chipTool, " ***** Test Step 16 : Remove Group 4 (not found)\n"); + err = TestRemoveGroup4NotFound_16(); + break; + case 17: + ChipLogProgress(chipTool, " ***** Test Step 17 : Remove Group 2 (existing)\n"); + err = TestRemoveGroup2Existing_17(); + break; + case 18: + ChipLogProgress(chipTool, " ***** Test Step 18 : View Group 1 (not removed)\n"); + err = TestViewGroup1NotRemoved_18(); + break; + case 19: + ChipLogProgress(chipTool, " ***** Test Step 19 : View Group 2 (removed)\n"); + err = TestViewGroup2Removed_19(); + break; + case 20: + ChipLogProgress(chipTool, " ***** Test Step 20 : View Group 3 (not removed)\n"); + err = TestViewGroup3NotRemoved_20(); + break; + case 21: + ChipLogProgress(chipTool, " ***** Test Step 21 : Get Group Membership 3\n"); + err = TestGetGroupMembership3_21(); + break; + case 22: + ChipLogProgress(chipTool, " ***** Test Step 22 : Remove All\n"); + err = TestRemoveAll_22(); + break; + case 23: + ChipLogProgress(chipTool, " ***** Test Step 23 : View Group 1 (removed)\n"); + err = TestViewGroup1Removed_23(); + break; + case 24: + ChipLogProgress(chipTool, " ***** Test Step 24 : View Group 2 (still removed)\n"); + err = TestViewGroup2StillRemoved_24(); + break; + case 25: + ChipLogProgress(chipTool, " ***** Test Step 25 : View Group 3 (removed)\n"); + err = TestViewGroup3Removed_25(); + break; + case 26: + ChipLogProgress(chipTool, " ***** Test Step 26 : Get Group Membership 4\n"); + err = TestGetGroupMembership4_26(); break; } @@ -46185,7 +46229,7 @@ class TestGroupsCluster : public TestCommand private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 16; + const uint16_t mTestCount = 27; // // Tests methods @@ -46355,16 +46399,17 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup3NotFound_6() + CHIP_ERROR TestGetGroupMembership1All_6() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; - using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + using RequestType = chip::app::Clusters::Groups::Commands::GetGroupMembership::Type; RequestType request; - request.groupId = 32767U; + + request.groupList = chip::app::DataModel::List(); auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_6(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_6(data.capacity, data.groupList); }; auto failure = [](void * context, EmberAfStatus status) { @@ -46377,7 +46422,106 @@ class TestGroupsCluster : public TestCommand void OnFailureResponse_6(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_6(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_6(uint8_t capacity, const chip::app::DataModel::DecodableList & groupList) + { + VerifyOrReturn(CheckValue("capacity", capacity, 255)); + + auto iter = groupList.begin(); + VerifyOrReturn(CheckNextListItemDecodes("groupList", iter, 0)); + VerifyOrReturn(CheckValue("groupList[0]", iter.GetValue(), 1U)); + VerifyOrReturn(CheckNoMoreListItems("groupList", iter, 1)); + + NextTest(); + } + + CHIP_ERROR TestAddGroup2New_7() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::AddGroup::Type; + + RequestType request; + request.groupId = 4369U; + request.groupName = chip::Span("Group #2garbage: not in length on purpose", 8); + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_7(data.status, data.groupId); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_7(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_7(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_7(uint8_t status, uint16_t groupId) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 4369U)); + + NextTest(); + } + + CHIP_ERROR TestViewGroup2New_8() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + + RequestType request; + request.groupId = 4369U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_8(data.status, data.groupId, data.groupName); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_8(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_8(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_8(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 4369U)); + + VerifyOrReturn(CheckValueAsString("groupName", groupName, chip::CharSpan("Group #2", 8))); + + NextTest(); + } + + CHIP_ERROR TestViewGroup3NotFound_9() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + + RequestType request; + request.groupId = 32767U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_9(data.status, data.groupId, data.groupName); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_9(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_9(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_9(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46386,7 +46530,39 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup1Existing_7() + CHIP_ERROR TestAddGroup3New_10() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::AddGroup::Type; + + RequestType request; + request.groupId = 32767U; + request.groupName = chip::Span("Group #3garbage: not in length on purpose", 8); + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_10(data.status, data.groupId); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_10(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_10(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_10(uint8_t status, uint16_t groupId) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 32767U)); + + NextTest(); + } + + CHIP_ERROR TestViewGroup1Existing_11() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46395,20 +46571,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 1U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_7(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_11(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_7(status); + (static_cast(context))->OnFailureResponse_11(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_7(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_11(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_7(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_11(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 0)); @@ -46419,7 +46595,112 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestRemoveGroup0Invalid_8() + CHIP_ERROR TestViewGroup2Existing_12() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + + RequestType request; + request.groupId = 4369U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_12(data.status, data.groupId, data.groupName); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_12(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_12(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_12(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 4369U)); + + VerifyOrReturn(CheckValueAsString("groupName", groupName, chip::CharSpan("Group #2", 8))); + + NextTest(); + } + + CHIP_ERROR TestGetGroupMembership2_13() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::GetGroupMembership::Type; + + RequestType request; + + uint16_t groupListList[3]; + groupListList[0] = 2U; + groupListList[1] = 3U; + groupListList[2] = 32767U; + request.groupList = groupListList; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_13(data.capacity, data.groupList); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_13(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_13(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_13(uint8_t capacity, const chip::app::DataModel::DecodableList & groupList) + { + VerifyOrReturn(CheckValue("capacity", capacity, 255)); + + auto iter = groupList.begin(); + VerifyOrReturn(CheckNextListItemDecodes("groupList", iter, 0)); + VerifyOrReturn(CheckValue("groupList[0]", iter.GetValue(), 32767U)); + VerifyOrReturn(CheckNoMoreListItems("groupList", iter, 1)); + + NextTest(); + } + + CHIP_ERROR TestViewGroup3New_14() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + + RequestType request; + request.groupId = 32767U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_14(data.status, data.groupId, data.groupName); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_14(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_14(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_14(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 32767U)); + + VerifyOrReturn(CheckValueAsString("groupName", groupName, chip::CharSpan("Group #3", 8))); + + NextTest(); + } + + CHIP_ERROR TestRemoveGroup0Invalid_15() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::RemoveGroup::Type; @@ -46428,20 +46709,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 0U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_8(data.status, data.groupId); + (static_cast(context))->OnSuccessResponse_15(data.status, data.groupId); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_8(status); + (static_cast(context))->OnFailureResponse_15(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_8(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_15(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_8(uint8_t status, uint16_t groupId) + void OnSuccessResponse_15(uint8_t status, uint16_t groupId) { VerifyOrReturn(CheckValue("status", status, 135)); @@ -46450,7 +46731,7 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestRemoveGroup4NotFound_9() + CHIP_ERROR TestRemoveGroup4NotFound_16() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::RemoveGroup::Type; @@ -46459,20 +46740,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 4U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_9(data.status, data.groupId); + (static_cast(context))->OnSuccessResponse_16(data.status, data.groupId); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_9(status); + (static_cast(context))->OnFailureResponse_16(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_9(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_16(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_9(uint8_t status, uint16_t groupId) + void OnSuccessResponse_16(uint8_t status, uint16_t groupId) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46481,7 +46762,38 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup1NotRemoved_10() + CHIP_ERROR TestRemoveGroup2Existing_17() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::RemoveGroup::Type; + + RequestType request; + request.groupId = 4369U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_17(data.status, data.groupId); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_17(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_17(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_17(uint8_t status, uint16_t groupId) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 4369U)); + + NextTest(); + } + + CHIP_ERROR TestViewGroup1NotRemoved_18() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46490,20 +46802,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 1U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_10(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_18(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_10(status); + (static_cast(context))->OnFailureResponse_18(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_10(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_18(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_10(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_18(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 0)); @@ -46514,7 +46826,7 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup2Removed_11() + CHIP_ERROR TestViewGroup2Removed_19() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46523,20 +46835,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 4369U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_11(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_19(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_11(status); + (static_cast(context))->OnFailureResponse_19(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_11(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_19(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_11(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_19(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46545,7 +46857,80 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestRemoveAll_12() + CHIP_ERROR TestViewGroup3NotRemoved_20() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; + + RequestType request; + request.groupId = 32767U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_20(data.status, data.groupId, data.groupName); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_20(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_20(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_20(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 32767U)); + + VerifyOrReturn(CheckValueAsString("groupName", groupName, chip::CharSpan("Group #3", 8))); + + NextTest(); + } + + CHIP_ERROR TestGetGroupMembership3_21() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::GetGroupMembership::Type; + + RequestType request; + + uint16_t groupListList[4]; + groupListList[0] = 1U; + groupListList[1] = 2U; + groupListList[2] = 4369U; + groupListList[3] = 3U; + request.groupList = groupListList; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_21(data.capacity, data.groupList); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_21(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_21(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_21(uint8_t capacity, const chip::app::DataModel::DecodableList & groupList) + { + VerifyOrReturn(CheckValue("capacity", capacity, 255)); + + auto iter = groupList.begin(); + VerifyOrReturn(CheckNextListItemDecodes("groupList", iter, 0)); + VerifyOrReturn(CheckValue("groupList[0]", iter.GetValue(), 1U)); + VerifyOrReturn(CheckNoMoreListItems("groupList", iter, 1)); + + NextTest(); + } + + CHIP_ERROR TestRemoveAll_22() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::RemoveAllGroups::Type; @@ -46553,22 +46938,22 @@ class TestGroupsCluster : public TestCommand RequestType request; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_12(); + (static_cast(context))->OnSuccessResponse_22(); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_12(status); + (static_cast(context))->OnFailureResponse_22(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_12(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_22(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_12() { NextTest(); } + void OnSuccessResponse_22() { NextTest(); } - CHIP_ERROR TestViewGroup1Removed_13() + CHIP_ERROR TestViewGroup1Removed_23() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46577,20 +46962,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 1U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_13(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_23(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_13(status); + (static_cast(context))->OnFailureResponse_23(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_13(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_23(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_13(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_23(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46599,7 +46984,7 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup2StillRemoved_14() + CHIP_ERROR TestViewGroup2StillRemoved_24() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46608,20 +46993,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 4369U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_14(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_24(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_14(status); + (static_cast(context))->OnFailureResponse_24(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_14(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_24(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_14(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_24(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46630,7 +47015,7 @@ class TestGroupsCluster : public TestCommand NextTest(); } - CHIP_ERROR TestViewGroup3Removed_15() + CHIP_ERROR TestViewGroup3Removed_25() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; using RequestType = chip::app::Clusters::Groups::Commands::ViewGroup::Type; @@ -46639,20 +47024,20 @@ class TestGroupsCluster : public TestCommand request.groupId = 32767U; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_15(data.status, data.groupId, data.groupName); + (static_cast(context))->OnSuccessResponse_25(data.status, data.groupId, data.groupName); }; auto failure = [](void * context, EmberAfStatus status) { - (static_cast(context))->OnFailureResponse_15(status); + (static_cast(context))->OnFailureResponse_25(status); }; ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_15(uint8_t status) { ThrowFailureResponse(); } + void OnFailureResponse_25(uint8_t status) { ThrowFailureResponse(); } - void OnSuccessResponse_15(uint8_t status, uint16_t groupId, chip::CharSpan groupName) + void OnSuccessResponse_25(uint8_t status, uint16_t groupId, chip::CharSpan groupName) { VerifyOrReturn(CheckValue("status", status, 139)); @@ -46660,6 +47045,45 @@ class TestGroupsCluster : public TestCommand NextTest(); } + + CHIP_ERROR TestGetGroupMembership4_26() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::GetGroupMembership::Type; + + RequestType request; + + uint16_t groupListList[5]; + groupListList[0] = 1U; + groupListList[1] = 2U; + groupListList[2] = 4369U; + groupListList[3] = 3U; + groupListList[4] = 32767U; + request.groupList = groupListList; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_26(data.capacity, data.groupList); + }; + + auto failure = [](void * context, EmberAfStatus status) { + (static_cast(context))->OnFailureResponse_26(status); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_26(uint8_t status) { ThrowFailureResponse(); } + + void OnSuccessResponse_26(uint8_t capacity, const chip::app::DataModel::DecodableList & groupList) + { + VerifyOrReturn(CheckValue("capacity", capacity, 255)); + + auto iter = groupList.begin(); + VerifyOrReturn(CheckNoMoreListItems("groupList", iter, 0)); + + NextTest(); + } }; class TestOperationalCredentialsCluster : public TestCommand