diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index 3245626bebaa79..4b3359cc8684a4 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -300,6 +300,7 @@ cxx CY CYW DAC +dadbdcdddedf DAP DAPLINK DataFrame @@ -358,6 +359,7 @@ DevKitC DevKitM devtype df +dfe dfu DgDxsfHx dhclient @@ -542,6 +544,7 @@ Gradle gradlew GroupId GroupKeyManagement +groupsettings gtk GUA Gv @@ -646,6 +649,7 @@ kBusy kCase Kconfig KeypadInput +keyset kGroup kInvalidCommandId KitProg @@ -1184,6 +1188,7 @@ TestEmptyString TestGenExample TestGroupDemoConfig TestMultiRead +TestName TESTPASSWD TestPICS TESTSSID @@ -1326,6 +1331,7 @@ WS WSL WSTK xa +xAAAA xab xaver xb @@ -1358,6 +1364,8 @@ xfffff xFFFFFFFD xffffffffe xfffffffff +xffffffffffff +xffffffffffffXXXX xtensa xwayland XXXX diff --git a/examples/chip-tool/README.md b/examples/chip-tool/README.md index e90072970abc15..c44e830e299595 100644 --- a/examples/chip-tool/README.md +++ b/examples/chip-tool/README.md @@ -120,6 +120,45 @@ The endpoint id must be between 1 and 240. The client will send a single command packet and then exit. +## Configuring the client for Group Commands + +Prior to sending a Group command, both the end device and the Client (Chip-tool) +must be configured appropriately. + +To configure the client please use the groupsettings option + + $ chip-tool groupsettings + +A group with a valid encryption key needs to be set. The groupid and the +encryption key must match the one configured on the end device. + +To add a group + + $ chip-tool groupsettings add-group TestName 0x1010 + +To add a keyset + + $ chip-tool groupsettings add-keyset 0xAAAA 0 0x000000000021dfe0 hex:d0d1d2d3d4d5d6d7d8d9dadbdcdddedf + +Take note that the epoch key must be in hex form with the 'hex:' prefix + +Finally to bind the keyset to the group + + $ chip-tool groupsettings bind-keyset 0x1010 0xAAAA + +## Using the Client to Send Group (Multicast) Matter Commands + +To use the Client to send Matter commands, run the built executable and pass it +the target cluster name, the target command name, the Group Id in Node Id form +(0xffffffffffffXXXX) and an unused endpoint Id. Take note that Only commands and +attributes write can be send with Group Id. + +E.G. sending to group Id 0x0025 + + $ chip-tool onoff on 0xffffffffffff0025 1 + +The client will send a single multicast command packet and then exit. + ### How to get the list of supported clusters To get the list of supported clusters, run the built executable without any diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index bb34584bf2ec72..856dbbeee6ed16 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -61,7 +61,7 @@ CHIP_ERROR CHIPCommand::Run() ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityGamma, kIdentityGammaFabricId, trustStore)); // Initialize Group Data - ReturnLogErrorOnFailure(chip::GroupTesting::InitProvider()); + ReturnLogErrorOnFailure(chip::GroupTesting::InitProvider(mDefaultStorage)); for (auto it = mCommissioners.begin(); it != mCommissioners.end(); it++) { chip::FabricInfo * fabric = it->second->GetFabricInfo(); diff --git a/examples/chip-tool/commands/group/Commands.h b/examples/chip-tool/commands/group/Commands.h new file mode 100644 index 00000000000000..7f5b838566c145 --- /dev/null +++ b/examples/chip-tool/commands/group/Commands.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2022 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 "../common/CHIPCommand.h" +#include "../common/Command.h" + +#include + +class ShowControllerGroups : public CHIPCommand +{ +public: + ShowControllerGroups(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("show-groups", credsIssuerConfig) {} + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + bool FindKeySetId(chip::FabricIndex fabricIndex, chip::GroupId groupId, chip::KeysetId & keysetId) + { + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + auto iter = groupDataProvider->IterateGroupKeys(fabricIndex); + chip::Credentials::GroupDataProvider::GroupKey groupKey; + while (iter->Next(groupKey)) + { + if (groupKey.group_id == groupId) + { + keysetId = groupKey.keyset_id; + iter->Release(); + return true; + } + } + iter->Release(); + return false; + } + + CHIP_ERROR RunCommand() override + { + fprintf(stderr, "\n"); + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | Available Groups : |\n"); + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | Group Id | KeySet Id | Group Name |\n"); + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + auto it = groupDataProvider->IterateGroupInfo(fabricIndex); + chip::Credentials::GroupDataProvider::GroupInfo group; + if (it) + { + while (it->Next(group)) + { + chip::KeysetId keysetId; + if (FindKeySetId(fabricIndex, group.group_id, keysetId)) + { + fprintf(stderr, " | 0x%-12x 0x%-13x %-50s |\n", group.group_id, keysetId, group.name); + } + else + { + fprintf(stderr, " | 0x%-12x %-15s %-50s |\n", group.group_id, "None", group.name); + } + } + it->Release(); + } + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } +}; + +class AddGroup : public CHIPCommand +{ +public: + AddGroup(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("add-group", credsIssuerConfig) + { + AddArgument("groupName", &groupName); + AddArgument("groupId", chip::kUndefinedGroupId, UINT16_MAX, &groupId); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(20); } + + CHIP_ERROR RunCommand() override + { + if (strlen(groupName) > CHIP_CONFIG_MAX_GROUP_NAME_LENGTH || groupId == chip::kUndefinedGroupId) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + chip::Credentials::GroupDataProvider::GroupInfo group; + + group.SetName(groupName); + group.group_id = groupId; + ReturnErrorOnFailure(groupDataProvider->SetGroupInfo(fabricIndex, group)); + + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + char * groupName; + chip::GroupId groupId; +}; + +class RemoveGroup : public CHIPCommand +{ +public: + RemoveGroup(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("remove-group", credsIssuerConfig) + { + AddArgument("groupId", chip::kUndefinedGroupId, UINT16_MAX, &groupId); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + if (groupId == chip::kUndefinedGroupId) + { + ChipLogError(chipTool, "Invalid group Id : 0x%x", groupId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + ReturnErrorOnFailure(groupDataProvider->RemoveGroupInfo(fabricIndex, groupId)); + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + chip::GroupId groupId; +}; + +class ShowKeySets : public CHIPCommand +{ +public: + ShowKeySets(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("show-keysets", credsIssuerConfig) {} + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + chip::Credentials::GroupDataProvider::KeySet keySet; + + fprintf(stderr, "\n"); + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | Available KeySets : |\n"); + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + fprintf(stderr, " | KeySet Id | Key Policy |\n"); + + auto it = groupDataProvider->IterateKeySets(fabricIndex); + if (it) + { + while (it->Next(keySet)) + { + fprintf(stderr, " | 0x%-12x %-66s |\n", keySet.keyset_id, + (keySet.policy == chip::Credentials::GroupDataProvider::SecurityPolicy::kCacheAndSync) ? "Cache and Sync" + : "Trust First"); + } + it->Release(); + } + fprintf(stderr, " +-------------------------------------------------------------------------------------+\n"); + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } +}; + +class BindKeySet : public CHIPCommand +{ +public: + BindKeySet(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("bind-keyset", credsIssuerConfig) + { + AddArgument("groupId", chip::kUndefinedGroupId, UINT16_MAX, &groupId); + AddArgument("keysetId", 0, UINT16_MAX, &keysetId); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + size_t current_count = 0; + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + + auto iter = groupDataProvider->IterateGroupKeys(fabricIndex); + current_count = iter->Count(); + iter->Release(); + + ReturnErrorOnFailure(groupDataProvider->SetGroupKeyAt(fabricIndex, current_count, + chip::Credentials::GroupDataProvider::GroupKey(groupId, keysetId))); + + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + chip::GroupId groupId; + chip::KeysetId keysetId; +}; + +class UnbindKeySet : public CHIPCommand +{ +public: + UnbindKeySet(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("unbind-keyset", credsIssuerConfig) + { + AddArgument("groupId", chip::kUndefinedGroupId, UINT16_MAX, &groupId); + AddArgument("keysetId", 0, UINT16_MAX, &keysetId); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + size_t index = 0; + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + auto iter = groupDataProvider->IterateGroupKeys(fabricIndex); + size_t maxCount = iter->Count(); + chip::Credentials::GroupDataProvider::GroupKey groupKey; + while (iter->Next(groupKey)) + { + if (groupKey.group_id == groupId && groupKey.keyset_id == keysetId) + { + break; + } + index++; + } + iter->Release(); + if (index >= maxCount) + { + return CHIP_ERROR_INTERNAL; + } + + ReturnErrorOnFailure(groupDataProvider->RemoveGroupKeyAt(fabricIndex, index)); + + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + chip::GroupId groupId; + chip::KeysetId keysetId; +}; + +class AddKeySet : public CHIPCommand +{ +public: + AddKeySet(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("add-keysets", credsIssuerConfig) + { + AddArgument("keysetId", 0, UINT16_MAX, &keysetId); + AddArgument("keyPolicy", 0, UINT16_MAX, &keyPolicy); + AddArgument("validityTime", 0, UINT64_MAX, &validityTime); + AddArgument("EpochKey", &epochKey); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + uint8_t compressed_fabric_id[sizeof(uint64_t)]; + chip::MutableByteSpan compressed_fabric_id_span(compressed_fabric_id); + ReturnLogErrorOnFailure(CurrentCommissioner().GetFabricInfo()->GetCompressedId(compressed_fabric_id_span)); + + if ((keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kCacheAndSync && + keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kTrustFirst) || + (epochKey.size()) != chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + chip::Credentials::GroupDataProvider::KeySet keySet(keysetId, keyPolicy, 1); + chip::Credentials::GroupDataProvider::EpochKey epoch_key; + epoch_key.start_time = validityTime; + memcpy(epoch_key.key, epochKey.data(), chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes); + + memcpy(keySet.epoch_keys, &epoch_key, sizeof(chip::Credentials::GroupDataProvider::EpochKey)); + ReturnErrorOnFailure(groupDataProvider->SetKeySet(fabricIndex, compressed_fabric_id_span, keySet)); + + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + chip::KeysetId keysetId; + chip::Credentials::GroupDataProvider::SecurityPolicy keyPolicy; + uint64_t validityTime; + chip::ByteSpan epochKey; +}; + +class RemoveKeySet : public CHIPCommand +{ +public: + RemoveKeySet(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("remove-keyset", credsIssuerConfig) + { + AddArgument("keysetId", 0, UINT16_MAX, &keysetId); + } + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(5); } + + CHIP_ERROR RunCommand() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + chip::FabricIndex fabricIndex; + CurrentCommissioner().GetFabricIndex(&fabricIndex); + chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); + + // Unbind all group + size_t index = 0; + auto iter = groupDataProvider->IterateGroupKeys(fabricIndex); + chip::Credentials::GroupDataProvider::GroupKey groupKey; + while (iter->Next(groupKey)) + { + if (groupKey.keyset_id == keysetId) + { + err = groupDataProvider->RemoveGroupKeyAt(fabricIndex, index); + if (err != CHIP_NO_ERROR) + { + break; + } + } + index++; + } + iter->Release(); + + if (err == CHIP_NO_ERROR) + { + return err; + } + ReturnErrorOnFailure(groupDataProvider->RemoveKeySet(fabricIndex, keysetId)); + + SetCommandExitStatus(CHIP_NO_ERROR); + return CHIP_NO_ERROR; + } + +private: + chip::KeysetId keysetId; +}; + +void registerCommandsGroup(Commands & commands, CredentialIssuerCommands * credsIssuerConfig) +{ + const char * clusterName = "GroupSettings"; + + commands_list clusterCommands = { + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), + }; + + commands.Register(clusterName, clusterCommands); +} diff --git a/examples/chip-tool/main.cpp b/examples/chip-tool/main.cpp index 88d7adff4e800b..10e6b7440fb09c 100644 --- a/examples/chip-tool/main.cpp +++ b/examples/chip-tool/main.cpp @@ -20,6 +20,7 @@ #include "commands/example/ExampleCredentialIssuerCommands.h" #include "commands/discover/Commands.h" +#include "commands/group/Commands.h" #include "commands/pairing/Commands.h" #include "commands/payload/Commands.h" @@ -37,6 +38,7 @@ int main(int argc, char * argv[]) registerCommandsPayload(commands); registerCommandsPairing(commands, &credIssuerCommands); registerCommandsTests(commands, &credIssuerCommands); + registerCommandsGroup(commands, &credIssuerCommands); registerClusters(commands, &credIssuerCommands); return commands.Run(argc, argv); diff --git a/src/lib/support/TestGroupData.h b/src/lib/support/TestGroupData.h index bc80bac9ee84a8..dc611b4697609b 100644 --- a/src/lib/support/TestGroupData.h +++ b/src/lib/support/TestGroupData.h @@ -48,6 +48,14 @@ CHIP_ERROR InitProvider() return CHIP_NO_ERROR; } +CHIP_ERROR InitProvider(chip::PersistentStorageDelegate & storageDelegate) +{ + sGroupsProvider.SetStorageDelegate(&storageDelegate); + ReturnErrorOnFailure(sGroupsProvider.Init()); + chip::Credentials::SetGroupDataProvider(&sGroupsProvider); + return CHIP_NO_ERROR; +} + CHIP_ERROR InitData(chip::FabricIndex fabric_index, const ByteSpan & compressed_fabric_id) { // Groups