Skip to content

Commit

Permalink
[icd] integrate ICD management command into CHIP tool
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing committed Dec 7, 2023
1 parent 6907eaa commit 4793823
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 9 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
22 changes: 22 additions & 0 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ constexpr char kCDTrustStorePathVariable[] = "CHIPTOOL_CD_TRUST_STORE_PATH"

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

namespace {

Expand Down Expand Up @@ -100,6 +102,12 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack()
ReturnLogErrorOnFailure(mOperationalKeystore.Init(&mDefaultStorage));
ReturnLogErrorOnFailure(mOpCertStore.Init(&mDefaultStorage));

if (sICDClientStorage == nullptr)
{
ReturnLogErrorOnFailure(mICDClientStorage.Init(&mDefaultStorage, &mSessionKeystore));
sICDClientStorage = &mICDClientStorage;
}

chip::Controller::FactoryInitParams factoryInitParams;

factoryInitParams.fabricIndependentStorage = &mDefaultStorage;
Expand Down Expand Up @@ -167,6 +175,11 @@ void CHIPCommand::MaybeTearDownStack()
return;
}

if (sICDClientStorage == &mICDClientStorage)
{
sICDClientStorage = nullptr;
}

//
// We can call DeviceController::Shutdown() safely without grabbing the stack lock
// since the CHIP thread and event queue have been stopped, preventing any thread
Expand Down Expand Up @@ -412,6 +425,13 @@ chip::Controller::DeviceCommissioner & CHIPCommand::GetCommissioner(std::string
return *item->second;
}

chip::app::DefaultICDClientStorage & CHIPCommand::GetICDClientStorage()
{
// This method should not be called before MaybeSetUpStack or after MaybeShutdownStack
VerifyOrDie(sICDClientStorage != nullptr);
return *sICDClientStorage;
}

void CHIPCommand::ShutdownCommissioner(const CommissionerIdentity & key)
{
mCommissioners[key].get()->Shutdown();
Expand Down Expand Up @@ -486,6 +506,8 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(CommissionerIdentity & identity,
chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, fabricIndex, defaultIpk, compressed_fabric_id_span));
}

GetICDClientStorage().UpdateFabricList(commissioner->GetFabricIndex());

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

return CHIP_NO_ERROR;
Expand Down
4 changes: 4 additions & 0 deletions 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 @@ -156,6 +157,7 @@ class CHIPCommand : public Command
chip::PersistentStorageOperationalKeystore mOperationalKeystore;
chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
chip::Crypto::RawKeySessionKeystore mSessionKeystore;
chip::app::DefaultICDClientStorage mICDClientStorage;

static chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
CredentialIssuerCommands * mCredIssuerCmds;
Expand All @@ -172,6 +174,8 @@ class CHIPCommand : public Command

ChipDeviceCommissioner & GetCommissioner(std::string identity);

chip::app::DefaultICDClientStorage & GetICDClientStorage();

private:
CHIP_ERROR MaybeSetUpStack();
void MaybeTearDownStack();
Expand Down
70 changes: 70 additions & 0 deletions examples/chip-tool/commands/icd/ICDCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 = GetICDClientStorage().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" PRIu8 ":" ChipLogFormatX64 " | %15" PRIu32 " | %15" PRIu32 " | " ChipLogFormatX64 " |\n",
info.peer_node.GetFabricIndex(), ChipLogValueX64(info.peer_node.GetNodeId()), info.start_icd_counter, info.offset,
ChipLogValueX64(info.monitored_subject));

if (std::is_same<Crypto::DefaultSessionKeystore, Crypto::RawKeySessionKeystore>::value)
{
// The following cast is valid only when `DefaultSessionKeystore` is `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 * clusterName = "ICD";

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

commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for 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);
53 changes: 44 additions & 9 deletions 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 @@ -381,6 +383,10 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
if (mDeviceIsIcd)
{
PersistIcdInfo();
}
ChipLogProgress(chipTool, "Device commissioning completed with success");
}
else
Expand All @@ -391,28 +397,57 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
SetCommandExitStatus(err);
}

void PairingCommand::OnICDRegistrationInfoRequired()
{
// Since we compute our ICD Registration info up front, we can call ICDRegistrationInfoReady() directly.
CurrentCommissioner().ICDRegistrationInfoReady();
}

void PairingCommand::OnICDRegistrationComplete(NodeId nodeId, uint32_t icdCounter)
void PairingCommand::PersistIcdInfo()
{
char icdSymmetricKeyHex[chip::Crypto::kAES_CCM128_Key_Length * 2 + 1];

chip::Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex,
sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate);

// TODO: Persist symmetric key.
chip::app::ICDClientInfo clientInfo;
clientInfo.peer_node = chip::ScopedNodeId(mNodeId, CurrentCommissioner().GetFabricIndex());
clientInfo.monitored_subject = mICDMonitoredSubject.Value();
CHIP_ERROR err = GetICDClientStorage().SetKey(clientInfo, mICDSymmetricKey.Value());
if (err == CHIP_NO_ERROR)
{
err = GetICDClientStorage().StoreEntry(clientInfo);
}

if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(mNodeId));
}
else
{
// StoreEntry is unlikely to fail since it is a local operation for CHIP Tool.
ChipLogError(chipTool, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(mNodeId),
err.AsString());
SetCommandExitStatus(err);
return;
}

ChipLogProgress(chipTool,
"ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
" / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s",
ChipLogValueX64(nodeId), ChipLogValueX64(mICDCheckInNodeId.Value()),
ChipLogValueX64(mNodeId), ChipLogValueX64(mICDCheckInNodeId.Value()),
ChipLogValueX64(mICDMonitoredSubject.Value()), icdSymmetricKeyHex);
}

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)
{
// Note: For chip-tool, it is OK to persist the information in `OnCommissioningComplete`.
// For some real world cases, we may want to save the information and revert it if further
// commissioning steps failed so we won't miss any check-in messages.
mIcdCounter = icdCounter;
}

void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData)
{
// Ignore nodes with closed commissioning window
Expand Down
3 changes: 3 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,14 @@ class PairingCommand : public CHIPCommand,
uint64_t mDiscoveryFilterCode;
char * mDiscoveryFilterInstanceName;

bool mDeviceIsIcd;
uint32_t mIcdCounter;
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

0 comments on commit 4793823

Please sign in to comment.