From f205ff2757e2544d4d9019b44d3406269bbd17b8 Mon Sep 17 00:00:00 2001 From: Abdul Samad Date: Wed, 19 Jun 2024 03:03:18 -0500 Subject: [PATCH] Update RPC and Add verifier to CommissioningWindowOpener --- examples/common/pigweed/BUILD.gn | 1 + .../protos/fabric_admin_service.options | 2 ++ .../pigweed/protos/fabric_admin_service.proto | 10 +++++-- .../common/pigweed/rpc_services/FabricAdmin.h | 2 +- .../OpenCommissioningWindowCommand.cpp | 14 ++++------ .../pairing/OpenCommissioningWindowCommand.h | 4 +++ examples/fabric-admin/rpc/RpcServer.cpp | 18 +++++++++--- .../fabric-bridge-app/linux/RpcClient.cpp | 28 +++++++++++++++++-- .../linux/include/RpcClient.h | 4 ++- examples/fabric-bridge-app/linux/main.cpp | 9 +++--- src/controller/CommissioningWindowOpener.cpp | 23 ++++++++++----- src/controller/CommissioningWindowOpener.h | 9 ++++-- 12 files changed, 90 insertions(+), 34 deletions(-) create mode 100644 examples/common/pigweed/protos/fabric_admin_service.options diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn index e523bea380c03f..6252f86e637890 100644 --- a/examples/common/pigweed/BUILD.gn +++ b/examples/common/pigweed/BUILD.gn @@ -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" diff --git a/examples/common/pigweed/protos/fabric_admin_service.options b/examples/common/pigweed/protos/fabric_admin_service.options new file mode 100644 index 00000000000000..9a65ae8a2b61d1 --- /dev/null +++ b/examples/common/pigweed/protos/fabric_admin_service.options @@ -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 diff --git a/examples/common/pigweed/protos/fabric_admin_service.proto b/examples/common/pigweed/protos/fabric_admin_service.proto index e52fd2951ac0d7..7f6ec4f4995b12 100644 --- a/examples/common/pigweed/protos/fabric_admin_service.proto +++ b/examples/common/pigweed/protos/fabric_admin_service.proto @@ -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 @@ -15,6 +20,5 @@ message OperationStatus { } service FabricAdmin { - rpc OpenCommissioningWindow(DeviceInfo) returns (OperationStatus){} + rpc OpenCommissioningWindow(DeviceCommissioningWindowInfo) returns (OperationStatus){} } - diff --git a/examples/common/pigweed/rpc_services/FabricAdmin.h b/examples/common/pigweed/rpc_services/FabricAdmin.h index 5254b9e9054a0c..38465d0b687b10 100644 --- a/examples/common/pigweed/rpc_services/FabricAdmin.h +++ b/examples/common/pigweed/rpc_services/FabricAdmin.h @@ -34,7 +34,7 @@ class FabricAdmin : public pw_rpc::nanopb::FabricAdmin::Service 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(); } diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp index b8c9a7f34938e0..4bc0632fe5b18d 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp @@ -35,18 +35,13 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand() { CHIP_ERROR err = CHIP_NO_ERROR; SetupPayload ignored; - const uint8_t payload[] = { 0x15, 0x36, 0x01, 0x15, 0x35, 0x01, 0x26, 0x00, 0x72, 0x4D, 0xDB, 0xCB, - 0x37, 0x01, 0x24, 0x02, 0x00, 0x24, 0x03, 0x1F, 0x24, 0x04, 0x00 }; - chip::ByteSpan payloadSpan(payload); - chip::Optional setupPIN(20202021); - chip::Optional salt(payloadSpan); if (mEndpointId == kRootEndpointId) { err = mWindowOpener->OpenCommissioningWindow(mNodeId, System::Clock::Seconds16(mCommissioningWindowTimeout), mIteration, - mDiscriminator, setupPIN, salt, &mOnOpenCommissioningWindowCallback, ignored, - /* readVIDPIDAttributes */ true); + mDiscriminator, chip::NullOptional, mVerifier, mSalt, + &mOnOpenCommissioningWindowCallback, ignored, /* readVIDPIDAttributes */ true); } else { @@ -56,8 +51,9 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand() .timeout = System::Clock::Seconds16(mCommissioningWindowTimeout), .iteration = mIteration, .discriminator = mDiscriminator, - .setupPIN = setupPIN, - .salt = salt, + .setupPIN = chip::NullOptional, + .verifier = mVerifier, + .salt = mSalt, .callback = &mOnOpenCommissioningWindowCallback, }; diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h index 86e1b68c3d5f3f..4db8842e6e0988 100644 --- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h +++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h @@ -47,6 +47,8 @@ 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"); + AddArgument("verifier", &mVerifier, "PAKE Passcode verifier encoded in hexadecimal format"); } void RegisterDelegate(CommissioningWindowDelegate * delegate) { mDelegate = delegate; } @@ -67,6 +69,8 @@ class OpenCommissioningWindowCommand : public CHIPCommand uint16_t mCommissioningWindowTimeout; uint32_t mIteration; uint16_t mDiscriminator; + chip::Optional mSalt; + chip::Optional mVerifier; chip::Optional mTimeout; diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp index 51ecb1ff6c13c5..e79651c73cb198 100644 --- a/examples/fabric-admin/rpc/RpcServer.cpp +++ b/examples/fabric-admin/rpc/RpcServer.cpp @@ -37,14 +37,24 @@ 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; + uint32_t commissioningTimeout = request.commissioning_timeout; + uint32_t iterations = request.iterations; + uint32_t discriminator = request.discriminator; + + char saltHex[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length * 2 + 1]; + chip::Encoding::BytesToHex(request.salt.bytes, request.salt.size, saltHex, sizeof(saltHex), chip::Encoding::HexFlags::kNullTerminate); + + char verifierHex[chip::Crypto::kSpake2p_VerifierSerialized_Length * 2 + 1]; + chip::Encoding::BytesToHex(request.verifier.bytes, request.verifier.size, verifierHex, sizeof(verifierHex), chip::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); diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp index d344c919668f68..769fb72e0c7316 100644 --- a/examples/fabric-bridge-app/linux/RpcClient.cpp +++ b/examples/fabric-bridge-app/linux/RpcClient.cpp @@ -91,7 +91,8 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) return rpc::client::StartPacketProcessing(); } -CHIP_ERROR OpenCommissioningWindow(NodeId nodeId) +CHIP_ERROR OpenCommissioningWindow(NodeId nodeId, uint16_t commissioningTimeout, uint16_t discriminator, uint32_t iterations, + chip::Optional salt, chip::Optional verifier) { ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId)); @@ -101,9 +102,32 @@ CHIP_ERROR OpenCommissioningWindow(NodeId nodeId) return CHIP_ERROR_BUSY; } - chip_rpc_DeviceInfo device; + chip_rpc_DeviceCommissioningWindowInfo device; device.node_id = nodeId; + device.commissioning_timeout = commissioningTimeout; + device.discriminator = discriminator; + device.iterations = iterations; + if (salt.HasValue()) + { + if (salt.Value().size() > sizeof(device.salt.bytes)) + { + return CHIP_ERROR_INTERNAL; + } + memcpy(device.salt.bytes, salt.Value().data(), salt.Value().size()); + device.salt.size = static_cast(salt.Value().size()); + } + + if (salt.HasValue()) + { + if (verifier.Value().size() > sizeof(device.verifier.bytes)) + { + return CHIP_ERROR_INTERNAL; + } + memcpy(device.verifier.bytes, verifier.Value().data(), verifier.Value().size()); + device.verifier.size = static_cast(verifier.Value().size()); + } + // The RPC will remain active as long as `openCommissioningWindowCall` is alive. openCommissioningWindowCall = fabricAdminClient.OpenCommissioningWindow(device, OnOpenCommissioningWindowCompleted); diff --git a/examples/fabric-bridge-app/linux/include/RpcClient.h b/examples/fabric-bridge-app/linux/include/RpcClient.h index bd424e9d275910..1529d0dae7c3fc 100644 --- a/examples/fabric-bridge-app/linux/include/RpcClient.h +++ b/examples/fabric-bridge-app/linux/include/RpcClient.h @@ -41,4 +41,6 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort); * - 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::NodeId nodeId, uint16_t commissioningTimeout, + uint16_t discriminator, uint32_t iterations, chip::Optional salt, + chip::Optional verifier); diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index b9cad2f2630fb0..fff6e1e9782b6a 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -71,7 +71,8 @@ void BridgePollingThread() #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE else if (ch == 'o') { - CHIP_ERROR err = OpenCommissioningWindow(0x1234); + commissioningTimeout, discriminator, iterations, salt, pakeVerifier + CHIP_ERROR err = OpenCommissioningWindow(0x1234, 300, 3840, 1000, chip::NullOptional, chip::NullOptional); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Failed to call OpenCommissioningWindow RPC: %" CHIP_ERROR_FORMAT, err.Format()); @@ -125,7 +126,7 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha return; } - chip::System::Clock::Seconds16 commissioningTimeout; + uint16_t commissioningTimeout; chip::ByteSpan pakeVerifier; uint16_t discriminator; uint32_t iterations; @@ -138,7 +139,7 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha CHIP_ERROR tlvError = DataModel::Decode(handlerContext.mPayload, commandData); SuccessOrExit(tlvError); - commissioningTimeout = System::Clock::Seconds16(commandData.commissioningTimeout); + commissioningTimeout = commandData.commissioningTimeout; pakeVerifier = commandData.PAKEPasscodeVerifier; discriminator = commandData.discriminator; iterations = commandData.iterations; @@ -149,7 +150,7 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE device = DeviceMgr().GetDevice(endpointId); - if (device != nullptr && OpenCommissioningWindow(device->GetNodeId()) == CHIP_NO_ERROR) + if (device != nullptr && OpenCommissioningWindow(device->GetNodeId(), commissioningTimeout, discriminator, iterations, salt, pakeVerifier) == CHIP_NO_ERROR) { ChipLogProgress(NotSpecified, "Commissioning window is now open"); status = Status::Success; diff --git a/src/controller/CommissioningWindowOpener.cpp b/src/controller/CommissioningWindowOpener.cpp index dbd305ffe61918..04a1b3e6a2b695 100644 --- a/src/controller/CommissioningWindowOpener.cpp +++ b/src/controller/CommissioningWindowOpener.cpp @@ -55,8 +55,8 @@ CHIP_ERROR CommissioningWindowOpener::OpenBasicCommissioningWindow(NodeId device } CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, Seconds16 timeout, uint32_t iteration, - uint16_t discriminator, Optional setupPIN, - Optional salt, + uint16_t discriminator, Optional setupPIN, + Optional verifier, Optional salt, Callback::Callback * callback, SetupPayload & payload, bool readVIDPIDAttributes) { @@ -67,6 +67,7 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, S .iteration = iteration, .discriminator = discriminator, .setupPIN = setupPIN, + .verifier = verifier, .salt = salt, .callback = callback, }; @@ -130,9 +131,17 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowImpl(const Commissi mCommissioningWindowTimeout = params.timeout; mPBKDFIterations = params.iteration; - bool randomSetupPIN = !params.setupPIN.HasValue(); - ReturnErrorOnFailure( - PASESession::GeneratePASEVerifier(mVerifier, mPBKDFIterations, mPBKDFSalt, randomSetupPIN, mSetupPayload.setUpPINCode)); + if (params.verifier.HasValue()) + { + ChipLogProgress(NotSpecified, "Verifier size is %ld", params.verifier.Value().size()); + ReturnErrorOnFailure(mVerifier.Deserialize(params.verifier.Value())); + } + else + { + bool randomSetupPIN = !params.setupPIN.HasValue(); + ReturnErrorOnFailure( + PASESession::GeneratePASEVerifier(mVerifier, mPBKDFIterations, mPBKDFSalt, randomSetupPIN, mSetupPayload.setUpPINCode)); + } payload = mSetupPayload; @@ -376,8 +385,8 @@ CHIP_ERROR AutoCommissioningWindowOpener::OpenCommissioningWindow(DeviceControll } CHIP_ERROR err = opener->CommissioningWindowOpener::OpenCommissioningWindow( - deviceId, timeout, iteration, discriminator, setupPIN, salt, &opener->mOnOpenCommissioningWindowCallback, payload, - readVIDPIDAttributes); + deviceId, timeout, iteration, discriminator, setupPIN, chip::NullOptional, salt, &opener->mOnOpenCommissioningWindowCallback, + payload, readVIDPIDAttributes); if (err != CHIP_NO_ERROR) { delete opener; diff --git a/src/controller/CommissioningWindowOpener.h b/src/controller/CommissioningWindowOpener.h index 744c9615365cf4..79abd08d401400 100644 --- a/src/controller/CommissioningWindowOpener.h +++ b/src/controller/CommissioningWindowOpener.h @@ -51,6 +51,7 @@ class CommissioningWindowOpener // verifier to be used for this commissioning. uint16_t discriminator; // The long discriminator for the DNS-SD advertisement. Optional setupPIN; // The setup PIN to use, or NullOptional to use a randomly-generated one. + Optional verifier; // The PAKE passcode verifier, if provided, will be used instead of generating one from setup PIN code Optional salt; // The salt to use, or NullOptional to use a randomly-generated one. If provided, must be at least // kSpake2p_Min_PBKDF_Salt_Length bytes and at most kSpake2p_Max_PBKDF_Salt_Length bytes in length. Callback::Callback * @@ -99,6 +100,8 @@ class CommissioningWindowOpener * PAKE passcode verifier to be used for this commissioning. * @param[in] discriminator The long discriminator for the DNS-SD advertisement. * @param[in] setupPIN The setup PIN to use, or NullOptional to use a randomly-generated one. + * @param[in] verifier The PAKE passcode verifier to use, or NullOptional to + * generate verifier based on PIN and other parameters. * @param[in] salt The salt to use, or NullOptional to use a * randomly-generated one. If provided, must be at * least kSpake2p_Min_PBKDF_Salt_Length bytes and @@ -118,9 +121,9 @@ class CommissioningWindowOpener * callback. */ CHIP_ERROR OpenCommissioningWindow(NodeId deviceId, System::Clock::Seconds16 timeout, uint32_t iteration, - uint16_t discriminator, Optional setupPIN, Optional salt, - Callback::Callback * callback, SetupPayload & payload, - bool readVIDPIDAttributes = false); + uint16_t discriminator, Optional setupPIN, Optional verifier, + Optional salt, Callback::Callback * callback, + SetupPayload & payload, bool readVIDPIDAttributes = false); /** * @brief