Skip to content

Commit

Permalink
[Fabric-Sync] Port sync-device command from fabric-admin (project-chi…
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca authored Nov 15, 2024
1 parent e09abae commit 57489d1
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 2 deletions.
72 changes: 72 additions & 0 deletions examples/fabric-sync/admin/DeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <LinuxCommissionableDataProvider.h>
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
#include <bridge/include/FabricBridge.h>
#include <crypto/RandUtils.h>
#include <lib/support/StringBuilder.h>

Expand All @@ -33,6 +34,9 @@ namespace admin {
namespace {

constexpr EndpointId kAggregatorEndpointId = 1;
constexpr uint16_t kWindowTimeout = 300;
constexpr uint16_t kIteration = 1000;
constexpr uint16_t kMaxDiscriminatorLength = 4095;

} // namespace

Expand Down Expand Up @@ -75,6 +79,13 @@ void DeviceManager::SetRemoteBridgeNodeId(chip::NodeId nodeId)
}
}

void DeviceManager::AddSyncedDevice(const SyncedDevice & device)
{
mSyncedDevices.insert(device);
ChipLogProgress(NotSpecified, "Added synced device: NodeId:" ChipLogFormatX64 ", EndpointId %u",
ChipLogValueX64(device.GetNodeId()), device.GetEndpointId());
}

SyncedDevice * DeviceManager::FindDeviceByEndpoint(EndpointId endpointId)
{
for (auto & device : mSyncedDevices)
Expand All @@ -99,6 +110,27 @@ SyncedDevice * DeviceManager::FindDeviceByNode(NodeId nodeId)
return nullptr;
}

void DeviceManager::RemoveSyncedDevice(chip::ScopedNodeId scopedNodeId)
{
NodeId nodeId = scopedNodeId.GetNodeId();

if (bridge::FabricBridge::Instance().RemoveSynchronizedDevice(scopedNodeId) != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to remove Node ID:" ChipLogFormatX64, ChipLogValueX64(nodeId));
}

SyncedDevice * device = FindDeviceByNode(nodeId);
if (device == nullptr)
{
ChipLogProgress(NotSpecified, "No device found with NodeId:" ChipLogFormatX64, ChipLogValueX64(nodeId));
return;
}

mSyncedDevices.erase(*device);
ChipLogProgress(NotSpecified, "Removed synced device: NodeId:" ChipLogFormatX64 ", EndpointId %u",
ChipLogValueX64(device->GetNodeId()), device->GetEndpointId());
}

void DeviceManager::OpenLocalBridgeCommissioningWindow(uint32_t iterations, uint16_t commissioningTimeoutSec,
uint16_t discriminator, const ByteSpan & salt, const ByteSpan & verifier)
{
Expand Down Expand Up @@ -132,6 +164,46 @@ void DeviceManager::OpenLocalBridgeCommissioningWindow(uint32_t iterations, uint
}
}

void DeviceManager::OpenDeviceCommissioningWindow(ScopedNodeId scopedNodeId, uint32_t iterations, uint16_t commissioningTimeoutSec,
uint16_t discriminator, const ByteSpan & salt, const ByteSpan & verifier)
{
// PairingManager isn't currently capable of OpenCommissioningWindow on a device of a fabric that it doesn't have
// the controller for. Currently no implementation need this functionality, but should they need it they will hit
// the verify or die below and it will be the responsiblity of whoever requires that functionality to implement.
VerifyOrDie(PairingManager::Instance().CurrentCommissioner().GetFabricIndex() == scopedNodeId.GetFabricIndex());
ChipLogProgress(NotSpecified, "Opening commissioning window for Node ID: " ChipLogFormatX64,
ChipLogValueX64(scopedNodeId.GetNodeId()));

// Open the commissioning window of a device within its own fabric.
CHIP_ERROR err = PairingManager::Instance().OpenCommissioningWindow(
scopedNodeId.GetNodeId(), kRootEndpointId, commissioningTimeoutSec, iterations, discriminator, salt, verifier);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to open commissioning window: %s", ErrorStr(err));
}
}

void DeviceManager::OpenRemoteDeviceCommissioningWindow(EndpointId remoteEndpointId)
{
// Open the commissioning window of a device from another fabric via its fabric bridge.
// This method constructs and sends a command to open the commissioning window for a device
// that is part of a different fabric, accessed through a fabric bridge.

// Use random discriminator to have less chance of collision.
uint16_t discriminator =
Crypto::GetRandU16() % (kMaxDiscriminatorLength + 1); // Include the upper limit kMaxDiscriminatorLength

ByteSpan emptySalt;
ByteSpan emptyVerifier;

CHIP_ERROR err = PairingManager::Instance().OpenCommissioningWindow(mRemoteBridgeNodeId, remoteEndpointId, kWindowTimeout,
kIteration, discriminator, emptySalt, emptyVerifier);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to open commissioning window: %s", ErrorStr(err));
}
}

CHIP_ERROR DeviceManager::PairRemoteFabricBridge(NodeId nodeId, uint32_t setupPINCode, const char * deviceRemoteIp,
uint16_t deviceRemotePort)
{
Expand Down
38 changes: 38 additions & 0 deletions examples/fabric-sync/admin/DeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class DeviceManager

bool IsFabricSyncReady() const { return mRemoteBridgeNodeId != chip::kUndefinedNodeId; }

void AddSyncedDevice(const SyncedDevice & device);

void RemoveSyncedDevice(chip::ScopedNodeId scopedNodeId);

/**
* @brief Determines whether a given nodeId corresponds to the remote bridge device.
*
Expand All @@ -97,6 +101,40 @@ class DeviceManager
void OpenLocalBridgeCommissioningWindow(uint32_t iterations, uint16_t commissioningTimeoutSec, uint16_t discriminator,
const chip::ByteSpan & salt, const chip::ByteSpan & verifier);

/**
* @brief Open the commissioning window for a specific device within its own fabric.
*
* This function initiates the process to open the commissioning window for a device identified by the given node ID.
*
* @param scopedNodeId The scoped node ID of the device that should open the commissioning window.
* @param iterations The number of PBKDF (Password-Based Key Derivation Function) iterations to use
* for deriving the PAKE (Password Authenticated Key Exchange) verifier.
* @param commissioningTimeoutSec The time in seconds before the commissioning window closes. This value determines
* how long the commissioning window remains open for incoming connections.
* @param discriminator The device-specific discriminator, determined during commissioning, which helps
* to uniquely identify the device among others.
* @param salt The salt used in the cryptographic operations for commissioning.
* @param verifier The PAKE verifier used to authenticate the commissioning process.
*
*/
void OpenDeviceCommissioningWindow(chip::ScopedNodeId scopedNodeId, uint32_t iterations, uint16_t commissioningTimeoutSec,
uint16_t discriminator, const chip::ByteSpan & salt, const chip::ByteSpan & verifier);

/**
* @brief Open the commissioning window of a device from another fabric via its fabric bridge.
*
* This function initiates the process to open the commissioning window for a device that belongs to another
* fabric, accessed through a fabric bridge.
*
* @param remoteEndpointId The endpoint ID of the remote device that should open the commissioning window.
* This endpoint is associated with the device in the other fabric, accessed via the
* fabric bridge.
*
* @note This function is used when the device to be commissioned is part of a different fabric and must be
* accessed through an intermediary fabric bridge.
*/
void OpenRemoteDeviceCommissioningWindow(chip::EndpointId remoteEndpointId);

/**
* @brief Pair a remote fabric bridge with a given node ID.
*
Expand Down
21 changes: 21 additions & 0 deletions examples/fabric-sync/admin/FabricAdmin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ FabricAdmin & FabricAdmin::Instance()
return sInstance;
}

CHIP_ERROR FabricAdmin::OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams params, FabricIndex fabricIndex)
{
ScopedNodeId scopedNodeId(params.GetNodeId(), fabricIndex);
uint32_t iterations = params.GetIteration();
uint16_t discriminator = params.GetDiscriminator();
uint16_t commissioningTimeoutSec = static_cast<uint16_t>(params.GetTimeout().count());

// Log request details for debugging purposes
ChipLogProgress(NotSpecified,
"Received OpenCommissioningWindow request: NodeId " ChipLogFormatX64
", Timeout: %u, Iterations: %u, Discriminator: %u",
ChipLogValueX64(scopedNodeId.GetNodeId()), commissioningTimeoutSec, iterations, discriminator);

// Open the device commissioning window with provided salt and verifier data
DeviceManager::Instance().OpenDeviceCommissioningWindow(scopedNodeId, iterations, commissioningTimeoutSec, discriminator,
ByteSpan(params.GetSalt().data(), params.GetSalt().size()),
ByteSpan(params.GetVerifier().data(), params.GetVerifier().size()));

return CHIP_NO_ERROR;
}

CHIP_ERROR
FabricAdmin::CommissionRemoteBridge(Controller::CommissioningWindowPasscodeParams params, VendorId vendorId, uint16_t productId)
{
Expand Down
3 changes: 3 additions & 0 deletions examples/fabric-sync/admin/FabricAdmin.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class FabricAdmin final : public bridge::FabricAdminDelegate
public:
static FabricAdmin & Instance();

CHIP_ERROR OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params,
chip::FabricIndex fabricIndex) override;

CHIP_ERROR
CommissionRemoteBridge(chip::Controller::CommissioningWindowPasscodeParams params, chip::VendorId vendorId,
uint16_t productId) override;
Expand Down
12 changes: 12 additions & 0 deletions examples/fabric-sync/bridge/include/FabricAdminDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ class FabricAdminDelegate
public:
virtual ~FabricAdminDelegate() = default;

/**
* Opens a commissioning window for a specified node using pre-computed PAKE passcode verifier.
*
* @param params Params for opening the commissioning window using verifier.
* @return CHIP_ERROR An error code indicating the success or failure of the operation.
* - CHIP_NO_ERROR: The RPC command was successfully sent.
* - CHIP_ERROR_BUSY: Another commissioning window is currently in progress.
* - CHIP_ERROR_INTERNAL: An internal error occurred.
*/
virtual CHIP_ERROR OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params,
chip::FabricIndex fabricIndex) = 0;

/**
* Reverse commission a bridge using the specified parameters.
*
Expand Down
2 changes: 2 additions & 0 deletions examples/fabric-sync/shell/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ source_set("shell") {
"RemoveDeviceCommand.h",
"ShellCommands.cpp",
"ShellCommands.h",
"SyncDeviceCommand.cpp",
"SyncDeviceCommand.h",
]

deps = [
Expand Down
38 changes: 36 additions & 2 deletions examples/fabric-sync/shell/ShellCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "AddDeviceCommand.h"
#include "RemoveBridgeCommand.h"
#include "RemoveDeviceCommand.h"
#include "SyncDeviceCommand.h"

#include <admin/DeviceManager.h>
#include <inttypes.h>
Expand Down Expand Up @@ -129,11 +130,11 @@ static CHIP_ERROR HandleAddDeviceCommand(int argc, char ** argv)

// Parse arguments
chip::NodeId nodeId = static_cast<chip::NodeId>(strtoull(argv[1], nullptr, 10));
uint32_t setupPINCode = static_cast<uint32_t>(strtoul(argv[2], nullptr, 10));
uint32_t payload = static_cast<uint32_t>(strtoul(argv[2], nullptr, 10));
const char * remoteAddr = argv[3];
uint16_t remotePort = static_cast<uint16_t>(strtoul(argv[4], nullptr, 10));

auto command = std::make_unique<commands::AddDeviceCommand>(nodeId, setupPINCode, remoteAddr, remotePort);
auto command = std::make_unique<commands::AddDeviceCommand>(nodeId, payload, remoteAddr, remotePort);

CHIP_ERROR result = command->RunCommand();
if (result == CHIP_NO_ERROR)
Expand Down Expand Up @@ -173,6 +174,35 @@ static CHIP_ERROR HandleRemoveDeviceCommand(int argc, char ** argv)
return result;
}

static CHIP_ERROR HandleSyncDeviceCommand(int argc, char ** argv)
{
if (argc != 2)
{
fprintf(stderr, "Invalid arguments. Usage: app sync-device\n");
return CHIP_ERROR_INVALID_ARGUMENT;
}

// Check if there is already an active command
if (commands::CommandRegistry::Instance().IsCommandActive())
{
fprintf(stderr, "Another command is currently active. Please wait until it completes.\n");
return CHIP_ERROR_BUSY;
}

// Parse arguments
chip::EndpointId endpointId = static_cast<chip::EndpointId>(strtoul(argv[1], nullptr, 10));

auto command = std::make_unique<commands::SyncDeviceCommand>(endpointId);

CHIP_ERROR result = command->RunCommand();
if (result == CHIP_NO_ERROR)
{
commands::CommandRegistry::Instance().SetActiveCommand(std::move(command));
}

return result;
}

static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)
{
CHIP_ERROR error = CHIP_NO_ERROR;
Expand Down Expand Up @@ -201,6 +231,10 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)
{
return HandleRemoveDeviceCommand(argc, argv);
}
else if (strcmp(argv[0], "sync-device") == 0)
{
return HandleSyncDeviceCommand(argc, argv);
}
else
{
return CHIP_ERROR_INVALID_ARGUMENT;
Expand Down
Loading

0 comments on commit 57489d1

Please sign in to comment.