Skip to content

Commit

Permalink
[icd] integrate ICD management command into CHIP tool (project-chip#3…
Browse files Browse the repository at this point in the history
…0863)

* [icd] integrate ICD management command into CHIP tool

* Delete entry on failure

* Fix build
  • Loading branch information
erjiaqing authored Dec 12, 2023
1 parent bc746ae commit cce6c24
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 3 deletions.
3 changes: 3 additions & 0 deletions examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ static_library("chip-tool-utils") {
"commands/discover/DiscoverCommand.cpp",
"commands/discover/DiscoverCommissionablesCommand.cpp",
"commands/discover/DiscoverCommissionersCommand.cpp",
"commands/icd/ICDCommand.cpp",
"commands/icd/ICDCommand.h",
"commands/pairing/OpenCommissioningWindowCommand.cpp",
"commands/pairing/OpenCommissioningWindowCommand.h",
"commands/pairing/PairingCommand.cpp",
Expand Down Expand Up @@ -100,6 +102,7 @@ static_library("chip-tool-utils") {

public_deps = [
"${chip_root}/examples/common/tracing:commandline",
"${chip_root}/src/app/icd/client:manager",
"${chip_root}/src/app/server",
"${chip_root}/src/app/tests/suites/commands/interaction_model",
"${chip_root}/src/controller/data_model",
Expand Down
13 changes: 12 additions & 1 deletion examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ constexpr char kCDTrustStorePathVariable[] = "CHIPTOOL_CD_TRUST_STORE_PATH"

const chip::Credentials::AttestationTrustStore * CHIPCommand::sTrustStore = nullptr;
chip::Credentials::GroupDataProviderImpl CHIPCommand::sGroupDataProvider{ kMaxGroupsPerFabric, kMaxGroupKeysPerFabric };
// All fabrics share the same ICD client storage.
chip::app::DefaultICDClientStorage CHIPCommand::sICDClientStorage;
chip::Crypto::RawKeySessionKeystore CHIPCommand::sSessionKeystore;

namespace {

Expand Down Expand Up @@ -100,13 +103,19 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack()
ReturnLogErrorOnFailure(mOperationalKeystore.Init(&mDefaultStorage));
ReturnLogErrorOnFailure(mOpCertStore.Init(&mDefaultStorage));

// chip-tool uses a non-persistent keystore.
// ICD storage lifetime is currently tied to the chip-tool's lifetime. Since chip-tool interactive mode is currently used for
// ICD commissioning and check-in validation, this temporary storage meets the test requirements.
// TODO: Implement persistent ICD storage for the chip-tool.
ReturnLogErrorOnFailure(sICDClientStorage.Init(&mDefaultStorage, &sSessionKeystore));

chip::Controller::FactoryInitParams factoryInitParams;

factoryInitParams.fabricIndependentStorage = &mDefaultStorage;
factoryInitParams.operationalKeystore = &mOperationalKeystore;
factoryInitParams.opCertStore = &mOpCertStore;
factoryInitParams.enableServerInteractions = NeedsOperationalAdvertising();
factoryInitParams.sessionKeystore = &mSessionKeystore;
factoryInitParams.sessionKeystore = &sSessionKeystore;

// Init group data provider that will be used for all group keys and IPKs for the
// chip-tool-configured fabrics. This is OK to do once since the fabric tables
Expand Down Expand Up @@ -486,6 +495,8 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(CommissionerIdentity & identity,
chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, fabricIndex, defaultIpk, compressed_fabric_id_span));
}

CHIPCommand::sICDClientStorage.UpdateFabricList(commissioner->GetFabricIndex());

mCommissioners[identity] = std::move(commissioner);

return CHIP_NO_ERROR;
Expand Down
4 changes: 3 additions & 1 deletion examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "Command.h"

#include <TracingCommandLineArgument.h>
#include <app/icd/client/DefaultICDClientStorage.h>
#include <commands/common/CredentialIssuerCommands.h>
#include <commands/example/ExampleCredentialIssuerCommands.h>
#include <credentials/GroupDataProviderImpl.h>
Expand Down Expand Up @@ -155,9 +156,10 @@ class CHIPCommand : public Command
#endif // CONFIG_USE_LOCAL_STORAGE
chip::PersistentStorageOperationalKeystore mOperationalKeystore;
chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
chip::Crypto::RawKeySessionKeystore mSessionKeystore;
static chip::Crypto::RawKeySessionKeystore sSessionKeystore;

static chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
static chip::app::DefaultICDClientStorage sICDClientStorage;
CredentialIssuerCommands * mCredIssuerCmds;

std::string GetIdentity();
Expand Down
69 changes: 69 additions & 0 deletions examples/chip-tool/commands/icd/ICDCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2023 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.
*
*/

#include "ICDCommand.h"

#include <crypto/DefaultSessionKeystore.h>
#include <crypto/RawKeySessionKeystore.h>

using namespace ::chip;

CHIP_ERROR ICDListCommand::RunCommand()
{
app::ICDClientInfo info;
auto iter = CHIPCommand::sICDClientStorage.IterateICDClientInfo();
char icdSymmetricKeyHex[Crypto::kAES_CCM128_Key_Length * 2 + 1];

fprintf(stderr, " +-----------------------------------------------------------------------------+\n");
fprintf(stderr, " | %-75s |\n", "Known ICDs:");
fprintf(stderr, " +-----------------------------------------------------------------------------+\n");
fprintf(stderr, " | %20s | %15s | %15s | %16s |\n", "Fabric Index:Node ID", "Start Counter", "Counter Offset",
"MonitoredSubject");

while (iter->Next(info))
{
fprintf(stderr, " +-----------------------------------------------------------------------------+\n");
fprintf(stderr, " | %3" PRIu32 ":" ChipLogFormatX64 " | %15" PRIu32 " | %15" PRIu32 " | " ChipLogFormatX64 " |\n",
static_cast<uint32_t>(info.peer_node.GetFabricIndex()), ChipLogValueX64(info.peer_node.GetNodeId()),
info.start_icd_counter, info.offset, ChipLogValueX64(info.monitored_subject));

static_assert(std::is_same<decltype(CHIPCommand::sSessionKeystore), Crypto::RawKeySessionKeystore>::value,
"The following BytesToHex can copy/encode the key bytes from sharedKey to hexadecimal format, which only "
"works for RawKeySessionKeystore");
Encoding::BytesToHex(info.shared_key.As<Crypto::Symmetric128BitsKeyByteArray>(), Crypto::kAES_CCM128_Key_Length,
icdSymmetricKeyHex, sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate);
fprintf(stderr, " | Symmetric Key: %60s |\n", icdSymmetricKeyHex);
}

fprintf(stderr, " +-----------------------------------------------------------------------------+\n");

iter->Release();
SetCommandExitStatus(CHIP_NO_ERROR);
return CHIP_NO_ERROR;
}

void registerCommandsICD(Commands & commands, CredentialIssuerCommands * credsIssuerConfig)
{
const char * name = "ICD";

commands_list list = {
make_unique<ICDListCommand>(credsIssuerConfig),
};

commands.RegisterCommandSet(name, list, "Commands for client-side ICD management.");
}
45 changes: 45 additions & 0 deletions examples/chip-tool/commands/icd/ICDCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 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 "commands/common/Commands.h"

#include <lib/support/Span.h>

class ICDCommand : public CHIPCommand
{
public:
ICDCommand(const char * commandName, CredentialIssuerCommands * credIssuerCmds, const char * description) :
CHIPCommand(commandName, credIssuerCmds, description)
{}

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(10); }
};

class ICDListCommand : public ICDCommand
{
public:
ICDListCommand(CredentialIssuerCommands * credIssuerCmds) :
ICDCommand("list", credIssuerCmds, "List ICDs registed by this controller.")
{}
CHIP_ERROR RunCommand() override;
};

void registerCommandsICD(Commands & commands, CredentialIssuerCommands * credsIssuerConfig);
34 changes: 33 additions & 1 deletion examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ CHIP_ERROR PairingCommand::RunCommand()
// Clear the CATs in OperationalCredentialsIssuer
mCredIssuerCmds->SetCredentialIssuerCATValues(kUndefinedCATs);

mDeviceIsICD = false;

if (mCASEAuthTags.HasValue() && mCASEAuthTags.Value().size() <= kMaxSubjectCATAttributeCount)
{
CATValues cats = kUndefinedCATs;
Expand Down Expand Up @@ -385,6 +387,16 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
}
else
{
// When ICD device commissioning fails, the ICDClientInfo stored in OnICDRegistrationComplete needs to be removed.
if (mDeviceIsICD)
{
CHIP_ERROR deleteEntryError =
CHIPCommand::sICDClientStorage.DeleteEntry(ScopedNodeId(mNodeId, CurrentCommissioner().GetFabricIndex()));
if (deleteEntryError != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Failed to delete ICD entry: %s", ErrorStr(err));
}
}
ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err));
}

Expand All @@ -395,6 +407,7 @@ void PairingCommand::OnICDRegistrationInfoRequired()
{
// Since we compute our ICD Registration info up front, we can call ICDRegistrationInfoReady() directly.
CurrentCommissioner().ICDRegistrationInfoReady();
mDeviceIsICD = true;
}

void PairingCommand::OnICDRegistrationComplete(NodeId nodeId, uint32_t icdCounter)
Expand All @@ -404,8 +417,27 @@ void PairingCommand::OnICDRegistrationComplete(NodeId nodeId, uint32_t icdCounte
chip::Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex,
sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate);

// TODO: Persist symmetric key.
app::ICDClientInfo clientInfo;
clientInfo.peer_node = ScopedNodeId(nodeId, CurrentCommissioner().GetFabricIndex());
clientInfo.monitored_subject = mICDMonitoredSubject.Value();
clientInfo.start_icd_counter = icdCounter;

CHIP_ERROR err = CHIPCommand::sICDClientStorage.SetKey(clientInfo, mICDSymmetricKey.Value());
if (err == CHIP_NO_ERROR)
{
err = CHIPCommand::sICDClientStorage.StoreEntry(clientInfo);
}

if (err != CHIP_NO_ERROR)
{
CHIPCommand::sICDClientStorage.RemoveKey(clientInfo);
ChipLogError(chipTool, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId),
err.AsString());
SetCommandExitStatus(err);
return;
}

ChipLogProgress(chipTool, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId));
ChipLogProgress(chipTool,
"ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
" / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s",
Expand Down
2 changes: 2 additions & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,13 @@ class PairingCommand : public CHIPCommand,
uint64_t mDiscoveryFilterCode;
char * mDiscoveryFilterInstanceName;

bool mDeviceIsICD;
uint8_t mRandomGeneratedICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];

// For unpair
chip::Platform::UniquePtr<chip::Controller::CurrentFabricRemover> mCurrentFabricRemover;
chip::Callback::Callback<chip::Controller::OnCurrentFabricRemove> mCurrentFabricRemoveCallback;

static void OnCurrentFabricRemove(void * context, NodeId remoteNodeId, CHIP_ERROR status);
void PersistIcdInfo();
};
2 changes: 2 additions & 0 deletions examples/chip-tool/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "commands/delay/Commands.h"
#include "commands/discover/Commands.h"
#include "commands/group/Commands.h"
#include "commands/icd/ICDCommand.h"
#include "commands/interactive/Commands.h"
#include "commands/pairing/Commands.h"
#include "commands/payload/Commands.h"
Expand All @@ -40,6 +41,7 @@ int main(int argc, char * argv[])
Commands commands;
registerCommandsDelay(commands, &credIssuerCommands);
registerCommandsDiscover(commands, &credIssuerCommands);
registerCommandsICD(commands, &credIssuerCommands);
registerCommandsInteractive(commands, &credIssuerCommands);
registerCommandsPayload(commands);
registerCommandsPairing(commands, &credIssuerCommands);
Expand Down
1 change: 1 addition & 0 deletions examples/tv-casting-app/tv-casting-common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ chip_data_model("tv-casting-common") {

deps = [
"${chip_root}/examples/common/tracing:commandline",
"${chip_root}/src/app/icd/client:manager",
"${chip_root}/src/app/tests/suites/commands/interaction_model",
"${chip_root}/src/lib/support/jsontlv",
"${chip_root}/src/tracing",
Expand Down
15 changes: 15 additions & 0 deletions src/app/icd/client/DefaultICDClientStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
* limitations under the License.
*/

// Do not use the DefaultICDClientStorage class in settings where fabric indices are not stable.
// This class relies on the stability of fabric indices for efficient storage and retrieval of ICD client information.
// If fabric indices are not stable, the functionality of this class will be compromised and can lead to unexpected behavior.

#pragma once

#include "ICDClientStorage.h"
Expand Down Expand Up @@ -52,6 +56,17 @@ class DefaultICDClientStorage : public ICDClientStorage

ICDClientInfoIterator * IterateICDClientInfo() override;

/**
* When decrypting check-in messages, the system needs to iterate through all keys
* from all ICD clientInfos. In DefaultICDClientStorage, ICDClientInfos for the same fabric are stored in
* storage using the fabricIndex as the key. To retrieve all relevant ICDClientInfos
* from storage, the system needs to know all fabricIndices in advance. The
* `UpdateFabricList` function provides a way to inject newly created fabricIndices
* into a dedicated table. It is recommended to call this function whenever a controller is created
* with a new fabric index.
*
* @param[in] fabricIndex The newly created fabric index.
*/
CHIP_ERROR UpdateFabricList(FabricIndex fabricIndex);

CHIP_ERROR SetKey(ICDClientInfo & clientInfo, const ByteSpan keyData) override;
Expand Down

0 comments on commit cce6c24

Please sign in to comment.