Skip to content

Commit

Permalink
Add OCW verifier, pass params between fabric bridge & admin (#34209)
Browse files Browse the repository at this point in the history
* Add verifier arg to `CommissioningWindowOpener`

* Update RPC, pass OCW params between fabric bridge and admin

* Separate out OCW with passcode and verifier, bring back stable API

Co-authored-by: Boris Zbarsky <[email protected]>

* Use `CHIP_ERROR_BUFFER_TOO_SMALL`, Avoid SuccessOrExit, args for example

Co-authored-by: Andrei Litvin <[email protected]>

* Use fluent builder pattern for OCW params, and some fixes

Co-authored-by: Andrei Litvin <[email protected]>

* Add arg tests for new methods in `CommissioningWindowOpener`

* Move callback to params

Co-authored-by: Andrei Litvin <[email protected]>

* Enforce param values, Add mDiscriminator, Update docs

Co-authored-by: Boris Zbarsky <[email protected]>

* Use `HasDiscriminator()`/`HasNodeId()`, return `ERROR_INVALID_ARGUMENT`

---------

Co-authored-by: Boris Zbarsky <[email protected]>
Co-authored-by: Andrei Litvin <[email protected]>
  • Loading branch information
3 people authored and pull[bot] committed Jul 29, 2024
1 parent f3f7dd3 commit 1608964
Show file tree
Hide file tree
Showing 19 changed files with 679 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
if (mCommissioningWindowOption == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN)
{
SetupPayload ignored;
return mWindowOpener->OpenCommissioningWindow(mNodeId, System::Clock::Seconds16(mCommissioningWindowTimeout), mIteration,
mDiscriminator, NullOptional, NullOptional,
&mOnOpenCommissioningWindowCallback, ignored,
/* readVIDPIDAttributes */ true);
return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
.SetNodeId(mNodeId)
.SetTimeout(mCommissioningWindowTimeout)
.SetIteration(mIteration)
.SetDiscriminator(mDiscriminator)
.SetReadVIDPIDAttributes(true)
.SetCallback(&mOnOpenCommissioningWindowCallback),
ignored);
}

ChipLogError(chipTool, "Unknown commissioning window option: %d", to_underlying(mCommissioningWindowOption));
Expand Down
1 change: 1 addition & 0 deletions examples/common/pigweed/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pw_proto_library("button_service") {

pw_proto_library("fabric_admin_service") {
sources = [ "protos/fabric_admin_service.proto" ]
inputs = [ "protos/fabric_admin_service.options" ]
deps = [ "$dir_pw_protobuf:common_protos" ]
strip_prefix = "protos"
prefix = "fabric_admin_service"
Expand Down
2 changes: 2 additions & 0 deletions examples/common/pigweed/protos/fabric_admin_service.options
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
chip.rpc.DeviceCommissioningWindowInfo.verifier max_size:97 // kSpake2p_VerifierSerialized_Length
chip.rpc.DeviceCommissioningWindowInfo.salt max_size:32 // kSpake2p_Max_PBKDF_Salt_Length
10 changes: 7 additions & 3 deletions examples/common/pigweed/protos/fabric_admin_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import 'pw_protobuf_protos/common.proto';
package chip.rpc;

// Define the message for a synchronized end device with necessary fields
message DeviceInfo {
message DeviceCommissioningWindowInfo {
uint64 node_id = 1;
uint32 commissioning_timeout = 2;
uint32 discriminator = 3;
uint32 iterations = 4;
bytes salt = 5;
bytes verifier = 6;
}

// Define the response message to convey the status of the operation
Expand All @@ -15,6 +20,5 @@ message OperationStatus {
}

service FabricAdmin {
rpc OpenCommissioningWindow(DeviceInfo) returns (OperationStatus){}
rpc OpenCommissioningWindow(DeviceCommissioningWindowInfo) returns (OperationStatus){}
}

3 changes: 2 additions & 1 deletion examples/common/pigweed/rpc_services/FabricAdmin.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class FabricAdmin : public pw_rpc::nanopb::FabricAdmin::Service<FabricAdmin>
public:
virtual ~FabricAdmin() = default;

virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response)
virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceCommissioningWindowInfo & request,
chip_rpc_OperationStatus & response)
{
return pw::Status::Unimplemented();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,31 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()

if (mCommissioningWindowOption == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN)
{
SetupPayload ignored;
return mWindowOpener->OpenCommissioningWindow(mNodeId, System::Clock::Seconds16(mCommissioningWindowTimeout), mIteration,
mDiscriminator, NullOptional, NullOptional,
&mOnOpenCommissioningWindowCallback, ignored,
/* readVIDPIDAttributes */ true);
if (mVerifier.HasValue())
{
VerifyOrReturnError(mSalt.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
.SetNodeId(mNodeId)
.SetTimeout(mCommissioningWindowTimeout)
.SetIteration(mIteration)
.SetDiscriminator(mDiscriminator)
.SetVerifier(mVerifier.Value())
.SetSalt(mSalt.Value())
.SetCallback(&mOnOpenCommissioningWindowVerifierCallback));
}
else
{
SetupPayload ignored;
return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
.SetNodeId(mNodeId)
.SetTimeout(mCommissioningWindowTimeout)
.SetIteration(mIteration)
.SetDiscriminator(mDiscriminator)
.SetSalt(mSalt)
.SetReadVIDPIDAttributes(true)
.SetCallback(&mOnOpenCommissioningWindowCallback),
ignored);
}
}

ChipLogError(NotSpecified, "Unknown commissioning window option: %d", to_underlying(mCommissioningWindowOption));
Expand All @@ -58,6 +78,11 @@ void OpenCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * co
OnOpenBasicCommissioningWindowResponse(context, remoteId, err);
}

void OpenCommissioningWindowCommand::OnOpenCommissioningWindowVerifierResponse(void * context, NodeId remoteId, CHIP_ERROR err)
{
OnOpenBasicCommissioningWindowResponse(context, remoteId, err);
}

void OpenCommissioningWindowCommand::OnOpenBasicCommissioningWindowResponse(void * context, NodeId remoteId, CHIP_ERROR err)
{
LogErrorOnFailure(err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class OpenCommissioningWindowCommand : public CHIPCommand
OpenCommissioningWindowCommand(CredentialIssuerCommands * credIssuerCommands) :
CHIPCommand("open-commissioning-window", credIssuerCommands),
mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
mOnOpenCommissioningWindowVerifierCallback(OnOpenCommissioningWindowVerifierResponse, this),
mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
{
AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to send command to.");
Expand All @@ -47,6 +48,12 @@ class OpenCommissioningWindowCommand : public CHIPCommand
&mIteration, "Number of PBKDF iterations to use to derive the verifier. Ignored if 'option' is 0.");
AddArgument("discriminator", 0, 4096, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0.");
AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out.");
AddArgument("salt", &mSalt,
"Salt payload encoded in hexadecimal. Random salt will be generated if absent. "
"This needs to be present if verifier is provided, corresponding to salt used for generating verifier");
AddArgument("verifier", &mVerifier,
"PAKE Passcode verifier encoded in hexadecimal format. Will be generated from random setup pin and other "
"params if absent");
}

void RegisterDelegate(CommissioningWindowDelegate * delegate) { mDelegate = delegate; }
Expand All @@ -69,12 +76,16 @@ class OpenCommissioningWindowCommand : public CHIPCommand
uint16_t mDiscriminator;

chip::Optional<uint16_t> mTimeout;
chip::Optional<chip::ByteSpan> mSalt;
chip::Optional<chip::ByteSpan> mVerifier;

chip::Platform::UniquePtr<chip::Controller::CommissioningWindowOpener> mWindowOpener;

static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
static void OnOpenCommissioningWindowVerifierResponse(void * context, NodeId deviceId, CHIP_ERROR status);
static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);

chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindowWithVerifier> mOnOpenCommissioningWindowVerifierCallback;
chip::Callback::Callback<chip::Controller::OnOpenBasicCommissioningWindow> mOnOpenBasicCommissioningWindowCallback;
};
23 changes: 18 additions & 5 deletions examples/fabric-admin/rpc/RpcServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,27 @@ namespace {
class FabricAdmin final : public rpc::FabricAdmin
{
public:
pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response) override
pw::Status OpenCommissioningWindow(const chip_rpc_DeviceCommissioningWindowInfo & request,
chip_rpc_OperationStatus & response) override
{
NodeId nodeId = request.node_id;
NodeId nodeId = request.node_id;
uint32_t commissioningTimeout = request.commissioning_timeout;
uint32_t iterations = request.iterations;
uint32_t discriminator = request.discriminator;

char saltHex[Crypto::kSpake2p_Max_PBKDF_Salt_Length * 2 + 1];
Encoding::BytesToHex(request.salt.bytes, request.salt.size, saltHex, sizeof(saltHex), Encoding::HexFlags::kNullTerminate);

char verifierHex[Crypto::kSpake2p_VerifierSerialized_Length * 2 + 1];
Encoding::BytesToHex(request.verifier.bytes, request.verifier.size, verifierHex, sizeof(verifierHex),
Encoding::HexFlags::kNullTerminate);

ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId);

char command[64];
snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", nodeId, kRootEndpointId,
kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator);
char command[512];
snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d --salt hex:%s --verifier hex:%s",
nodeId, kRootEndpointId, kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex,
verifierHex);

PushCommand(command);

Expand Down
39 changes: 34 additions & 5 deletions examples/fabric-bridge-app/linux/RpcClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,9 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort)
return rpc::client::StartPacketProcessing();
}

CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
CHIP_ERROR OpenCommissioningWindow(chip_rpc_DeviceCommissioningWindowInfo device)
{
ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId));

chip_rpc_DeviceInfo device;
device.node_id = nodeId;
ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x" ChipLogFormatX64, ChipLogValueX64(device.node_id));

// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
Expand All @@ -114,3 +111,35 @@ CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)

return WaitForResponse(call);
}

CHIP_ERROR
OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams params)
{
chip_rpc_DeviceCommissioningWindowInfo device;
device.node_id = params.GetNodeId();
device.commissioning_timeout = params.GetTimeout().count();
device.discriminator = params.GetDiscriminator();
device.iterations = params.GetIteration();

return OpenCommissioningWindow(device);
}

CHIP_ERROR
OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params)
{
chip_rpc_DeviceCommissioningWindowInfo device;
device.node_id = params.GetNodeId();
device.commissioning_timeout = params.GetTimeout().count();
device.discriminator = params.GetDiscriminator();
device.iterations = params.GetIteration();

VerifyOrReturnError(params.GetSalt().size() <= sizeof(device.salt.bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(device.salt.bytes, params.GetSalt().data(), params.GetSalt().size());
device.salt.size = static_cast<size_t>(params.GetSalt().size());

VerifyOrReturnError(params.GetVerifier().size() <= sizeof(device.verifier.bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(device.verifier.bytes, params.GetVerifier().data(), params.GetVerifier().size());
device.verifier.size = static_cast<size_t>(params.GetVerifier().size());

return OpenCommissioningWindow(device);
}
20 changes: 17 additions & 3 deletions examples/fabric-bridge-app/linux/include/RpcClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include <controller/CommissioningWindowParams.h>
#include <platform/CHIPDeviceLayer.h>

constexpr uint16_t kFabricAdminServerPort = 33001;
Expand All @@ -33,12 +34,25 @@ constexpr uint16_t kFabricAdminServerPort = 33001;
CHIP_ERROR InitRpcClient(uint16_t rpcServerPort);

/**
* Opens a commissioning window for a specified node.
* Opens a commissioning window for a specified node using setup PIN (passcode).
*
* @param nodeId The identifier of the node for which the commissioning window should be opened.
* @param params Params for opening the commissioning window using passcode.
* @return CHIP_ERROR An error code indicating the success or failure of the operation.
* - CHIP_NO_ERROR: The RPC command was successfully processed.
* - CHIP_ERROR_BUSY: Another commissioning window is currently in progress.
* - CHIP_ERROR_INTERNAL: An internal error occurred.
*/
CHIP_ERROR OpenCommissioningWindow(chip::NodeId nodeId);
CHIP_ERROR
OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams params);

/**
* 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.
*/
CHIP_ERROR
OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params);
30 changes: 24 additions & 6 deletions examples/fabric-bridge-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ void BridgePollingThread()
#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
else if (ch == 'o')
{
CHIP_ERROR err = OpenCommissioningWindow(0x1234);
CHIP_ERROR err = OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams()
.SetNodeId(0x1234)
.SetTimeout(300)
.SetDiscriminator(3840)
.SetIteration(1000));
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to call OpenCommissioningWindow RPC: %" CHIP_ERROR_FORMAT, err.Format());
Expand Down Expand Up @@ -115,7 +119,7 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha
using Protocols::InteractionModel::Status;

EndpointId endpointId = handlerContext.mRequestPath.mEndpointId;
ChipLogProgress(NotSpecified, "Received command to open commissioning window on Endpoind: %d", endpointId);
ChipLogProgress(NotSpecified, "Received command to open commissioning window on Endpoint: %d", endpointId);

if (handlerContext.mRequestPath.mCommandId != Commands::OpenCommissioningWindow::Id || endpointId == kRootEndpointId)
{
Expand All @@ -124,23 +128,37 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha
}

handlerContext.SetCommandHandled();
Status status = Status::Success;

Commands::OpenCommissioningWindow::DecodableType commandData;
if (DataModel::Decode(handlerContext.mPayload, commandData) != CHIP_NO_ERROR)
{
handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::InvalidCommand);
return;
}

Status status = Status::Failure;

#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
Device * device = DeviceMgr().GetDevice(endpointId);

// TODO: issues:#33784, need to make OpenCommissioningWindow synchronous
if (device != nullptr && OpenCommissioningWindow(device->GetNodeId()) == CHIP_NO_ERROR)
if (device != nullptr &&
OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams()
.SetNodeId(device->GetNodeId())
.SetTimeout(commandData.commissioningTimeout)
.SetDiscriminator(commandData.discriminator)
.SetIteration(commandData.iterations)
.SetSalt(commandData.salt)
.SetVerifier(commandData.PAKEPasscodeVerifier)) == CHIP_NO_ERROR)
{
ChipLogProgress(NotSpecified, "Commissioning window is now open");
status = Status::Success;
}
else
{
status = Status::Failure;
ChipLogProgress(NotSpecified, "Commissioning window is failed to open");
}
#else
status = Status::Failure;
ChipLogProgress(NotSpecified, "Commissioning window failed to open: PW_RPC_FABRIC_BRIDGE_SERVICE not defined");
#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE

Expand Down
1 change: 1 addition & 0 deletions src/controller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ static_library("controller") {
"CommandSenderAllocator.h",
"CommissioneeDeviceProxy.h",
"CommissioningDelegate.h",
"CommissioningWindowParams.h",
"DeviceDiscoveryDelegate.h",
"DevicePairingDelegate.h",
"InvokeInteraction.h",
Expand Down
7 changes: 4 additions & 3 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,10 @@ class DLL_EXPORT DeviceController : public AbstractDnssdDiscoveryController
* An error return from this function means that neither callback has been
* called yet, and neither callback will be called in the future.
*/
CHIP_ERROR GetConnectedDevice(NodeId peerNodeId, Callback::Callback<OnDeviceConnected> * onConnection,
chip::Callback::Callback<OnDeviceConnectionFailure> * onFailure,
TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload)
virtual CHIP_ERROR
GetConnectedDevice(NodeId peerNodeId, Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure,
TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload)
{
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
mSystemState->CASESessionMgr()->FindOrEstablishSession(ScopedNodeId(peerNodeId, GetFabricIndex()), onConnection, onFailure,
Expand Down
Loading

0 comments on commit 1608964

Please sign in to comment.