Skip to content

Commit d38175b

Browse files
committed
Implemented the JCM mDNS advertisement and Open Commissioning Window
1 parent feba8fd commit d38175b

28 files changed

+349
-43
lines changed

examples/chip-tool/commands/pairing/PairingCommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
368368
filter.code = 0;
369369
filter.instanceName = mDiscoveryFilterInstanceName;
370370
break;
371+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
372+
// TODO
373+
break;
371374
}
372375

373376
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this);

examples/chip-tool/commands/pairing/PairingCommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class PairingCommand : public CHIPCommand,
180180
break;
181181
case chip::Dnssd::DiscoveryFilterType::kCommissioningMode:
182182
case chip::Dnssd::DiscoveryFilterType::kCommissioner:
183+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
183184
break;
184185
case chip::Dnssd::DiscoveryFilterType::kDeviceType:
185186
AddArgument("device-type", 0, UINT16_MAX, &mDiscoveryFilterCode);

examples/fabric-admin/commands/pairing/PairingCommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
331331
filter.code = 0;
332332
filter.instanceName = mDiscoveryFilterInstanceName;
333333
break;
334+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
335+
// TODO
336+
break;
334337
}
335338

336339
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this);

examples/fabric-admin/commands/pairing/PairingCommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class PairingCommand : public CHIPCommand,
158158
break;
159159
case chip::Dnssd::DiscoveryFilterType::kCommissioningMode:
160160
case chip::Dnssd::DiscoveryFilterType::kCommissioner:
161+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
161162
break;
162163
case chip::Dnssd::DiscoveryFilterType::kDeviceType:
163164
AddArgument("device-type", 0, UINT16_MAX, &mDiscoveryFilterCode);

examples/jf-control-app/BUILD.gn

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ config("config") {
6666

6767
static_library("jfc-utils") {
6868
sources = [
69-
"${chip_root}/examples/chip-tool/CHIPCommand.h",
7069
"${chip_root}/examples/chip-tool/commands/clusters/ModelCommand.cpp",
7170
"${chip_root}/examples/chip-tool/commands/clusters/ModelCommand.h",
7271
"${chip_root}/examples/chip-tool/commands/common/BDXDiagnosticLogsServerDelegate.cpp",
72+
"${chip_root}/examples/chip-tool/commands/common/CHIPCommand.h",
7373
"${chip_root}/examples/chip-tool/commands/common/Command.cpp",
7474
"${chip_root}/examples/chip-tool/commands/common/Command.h",
7575
"${chip_root}/examples/chip-tool/commands/common/Commands.cpp",
@@ -93,6 +93,8 @@ static_library("jfc-utils") {
9393
"commands/common/CHIPCommand.cpp",
9494
"commands/example/ExampleOperationalCredentialsIssuer.cpp",
9595
"commands/example/ExampleOperationalCredentialsIssuer.h",
96+
"commands/pairing/OpenJointCommissioningWindowCommand.cpp",
97+
"commands/pairing/OpenJointCommissioningWindowCommand.h",
9698
"commands/pairing/PairingCommand.cpp",
9799
"commands/pairing/PairingCommand.h",
98100
]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#include "OpenJointCommissioningWindowCommand.h"
20+
21+
#include <system/SystemClock.h>
22+
23+
using namespace ::chip;
24+
25+
CHIP_ERROR OpenJointCommissioningWindowCommand::RunCommand()
26+
{
27+
mWindowOpener = Platform::MakeUnique<Controller::CommissioningWindowOpener>(&CurrentCommissioner());
28+
29+
SetupPayload ignored;
30+
return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
31+
.SetNodeId(mNodeId)
32+
.SetTimeout(mCommissioningWindowTimeout)
33+
.SetIteration(mIteration)
34+
.SetDiscriminator(mDiscriminator)
35+
.SetReadVIDPIDAttributes(true)
36+
.SetCallback(&mOnOpenCommissioningWindowCallback),
37+
ignored, true);
38+
}
39+
40+
void OpenJointCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * context, NodeId remoteId, CHIP_ERROR err,
41+
SetupPayload payload)
42+
{
43+
LogErrorOnFailure(err);
44+
45+
OpenJointCommissioningWindowCommand * command = reinterpret_cast<OpenJointCommissioningWindowCommand *>(context);
46+
VerifyOrReturn(command != nullptr, ChipLogError(chipTool, "OnOpenJointCommissioningWindowCommand: context is null"));
47+
command->SetCommandExitStatus(err);
48+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#pragma once
20+
21+
#include "CHIPCommand.h"
22+
23+
#include <controller/CommissioningWindowOpener.h>
24+
#include <lib/support/CHIPMem.h>
25+
26+
class OpenJointCommissioningWindowCommand : public CHIPCommand
27+
{
28+
public:
29+
OpenJointCommissioningWindowCommand(CredentialIssuerCommands * credIssuerCommands) :
30+
CHIPCommand("open-joint-commissioning-window", credIssuerCommands),
31+
mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this)
32+
{
33+
AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to send command to.");
34+
AddArgument("window-timeout", 0, UINT16_MAX, &mCommissioningWindowTimeout,
35+
"Time, in seconds, before the commissioning window closes.");
36+
AddArgument("iteration", chip::Crypto::kSpake2p_Min_PBKDF_Iterations, chip::Crypto::kSpake2p_Max_PBKDF_Iterations,
37+
&mIteration, "Number of PBKDF iterations to use to derive the verifier. Ignored if 'option' is 0.");
38+
AddArgument("discriminator", 0, 4095, &mDiscriminator, "Discriminator to use for advertising. Ignored if 'option' is 0.");
39+
AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out.");
40+
}
41+
42+
/////////// CHIPCommand Interface /////////
43+
CHIP_ERROR RunCommand() override;
44+
// We issue multiple data model operations for this command, and the default
45+
// timeout for those is 10 seconds, so default to 20 seconds.
46+
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(mTimeout.ValueOr(20)); }
47+
48+
private:
49+
NodeId mNodeId;
50+
uint16_t mCommissioningWindowTimeout;
51+
uint32_t mIteration;
52+
uint16_t mDiscriminator;
53+
54+
chip::Optional<uint16_t> mTimeout;
55+
56+
chip::Platform::UniquePtr<chip::Controller::CommissioningWindowOpener> mWindowOpener;
57+
58+
static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
59+
60+
chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
61+
};

examples/jf-control-app/commands/pairing/PairingCommand.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
398398
case Dnssd::DiscoveryFilterType::kCommissioner:
399399
filter.code = 1;
400400
break;
401+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
402+
// TODO
403+
break;
401404
case Dnssd::DiscoveryFilterType::kInstanceName:
402405
filter.code = 0;
403406
filter.instanceName = mDiscoveryFilterInstanceName;

examples/jf-control-app/commands/pairing/PairingCommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class PairingCommand : public CHIPCommand,
180180
break;
181181
case chip::Dnssd::DiscoveryFilterType::kCommissioningMode:
182182
case chip::Dnssd::DiscoveryFilterType::kCommissioner:
183+
case chip::Dnssd::DiscoveryFilterType::kJointFabricMode:
183184
break;
184185
case chip::Dnssd::DiscoveryFilterType::kDeviceType:
185186
AddArgument("device-type", 0, UINT16_MAX, &mDiscoveryFilterCode);

examples/jf-control-app/main.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "commands/icd/ICDCommand.h"
2828
#include "commands/interactive/Commands.h"
2929
#include "commands/pairing/Commands.h"
30+
#include "commands/pairing/OpenJointCommissioningWindowCommand.h"
3031

3132
#include "RpcClientProcessor.h"
3233

@@ -46,6 +47,17 @@ CHIP_ERROR RpcConnect(void)
4647
return chip::rpc::client::StartPacketProcessing();
4748
}
4849

50+
void registerJCMCommands(Commands & commands, CredentialIssuerCommands * credsIssuerConfig)
51+
{
52+
const char * clusterName = "JCM";
53+
54+
commands_list clusterCommands = {
55+
make_unique<OpenJointCommissioningWindowCommand>(credsIssuerConfig),
56+
};
57+
58+
commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for joint commissioning devices.");
59+
}
60+
4961
// ================================================================================
5062
// Main Code
5163
// ================================================================================

src/app/clusters/joint-fabric-administrator-server/joint-fabric-administrator-server.cpp

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class JointFabricAdministratorAttrAccess : public AttributeAccessInterface
7070
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
7171

7272
private:
73-
CHIP_ERROR ReadAdministratorFabricIndex(AttributeValueEncoder & aEncoder);
7473
};
7574

7675
JointFabricAdministratorAttrAccess gJointFabricAdministratorAttrAccess;
@@ -81,21 +80,13 @@ CHIP_ERROR JointFabricAdministratorAttrAccess::Read(const ConcreteReadAttributeP
8180

8281
switch (aPath.mAttributeId)
8382
{
84-
case JointFabricAdministrator::Attributes::AdministratorFabricIndex::Id: {
85-
return ReadAdministratorFabricIndex(aEncoder);
86-
}
8783
default:
8884
break;
8985
}
9086

9187
return CHIP_NO_ERROR;
9288
}
9389

94-
CHIP_ERROR JointFabricAdministratorAttrAccess::ReadAdministratorFabricIndex(AttributeValueEncoder & aEncoder)
95-
{
96-
return aEncoder.Encode(Server::GetInstance().GetJointFabricDatastore().GetAdministratorFabricIndex());
97-
}
98-
9990
void MatterJointFabricAdministratorPluginServerInitCallback()
10091
{
10192
ChipLogProgress(DataManagement, "JointFabricAdministrator: initializing");
@@ -126,15 +117,67 @@ bool emberAfJointFabricAdministratorClusterAddICACCallback(
126117
return true;
127118
}
128119

129-
// TODO
130120
bool emberAfJointFabricAdministratorClusterOpenJointCommissioningWindowCallback(
131121
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
132122
const chip::app::Clusters::JointFabricAdministrator::Commands::OpenJointCommissioningWindow::DecodableType & commandData)
133123
{
134124
MATTER_TRACE_SCOPE("OpenJointCommissioningWindow", "JointFabricAdministrator");
125+
auto commissioningTimeout = System::Clock::Seconds16(commandData.commissioningTimeout);
126+
auto & pakeVerifier = commandData.PAKEPasscodeVerifier;
127+
auto & discriminator = commandData.discriminator;
128+
auto & iterations = commandData.iterations;
129+
auto & salt = commandData.salt;
130+
131+
Optional<AdministratorCommissioning::StatusCode> status = Optional<AdministratorCommissioning::StatusCode>::Missing();
132+
Status globalStatus = Status::Success;
133+
Spake2pVerifier verifier;
135134

136135
ChipLogProgress(Zcl, "Received command to open joint commissioning window");
137136

137+
FabricIndex fabricIndex = commandObj->GetAccessingFabricIndex();
138+
const FabricInfo * fabricInfo = Server::GetInstance().GetFabricTable().FindFabricWithIndex(fabricIndex);
139+
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
140+
auto & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
141+
142+
VerifyOrExit(fabricInfo != nullptr, status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
143+
VerifyOrExit(failSafeContext.IsFailSafeFullyDisarmed(), status.Emplace(AdministratorCommissioning::StatusCode::kBusy));
144+
145+
VerifyOrExit(!commissionMgr.IsCommissioningWindowOpen(), status.Emplace(AdministratorCommissioning::StatusCode::kBusy));
146+
VerifyOrExit(iterations >= kSpake2p_Min_PBKDF_Iterations,
147+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
148+
VerifyOrExit(iterations <= kSpake2p_Max_PBKDF_Iterations,
149+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
150+
VerifyOrExit(salt.size() >= kSpake2p_Min_PBKDF_Salt_Length,
151+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
152+
VerifyOrExit(salt.size() <= kSpake2p_Max_PBKDF_Salt_Length,
153+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
154+
VerifyOrExit(commissioningTimeout <= commissionMgr.MaxCommissioningTimeout(), globalStatus = Status::InvalidCommand);
155+
VerifyOrExit(commissioningTimeout >= commissionMgr.MinCommissioningTimeout(), globalStatus = Status::InvalidCommand);
156+
VerifyOrExit(discriminator <= kMaxDiscriminatorValue, globalStatus = Status::InvalidCommand);
157+
158+
VerifyOrExit(verifier.Deserialize(pakeVerifier) == CHIP_NO_ERROR,
159+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
160+
VerifyOrExit(commissionMgr.OpenJointCommissioningWindow(commissioningTimeout, discriminator, verifier, iterations, salt,
161+
fabricIndex, fabricInfo->GetVendorId()) == CHIP_NO_ERROR,
162+
status.Emplace(AdministratorCommissioning::StatusCode::kPAKEParameterError));
163+
ChipLogProgress(Zcl, "Commissioning window is now open");
164+
165+
exit:
166+
if (status.HasValue())
167+
{
168+
ChipLogError(Zcl, "Failed to open joint commissioning window. Cluster status 0x%02x", to_underlying(status.Value()));
169+
commandObj->AddClusterSpecificFailure(commandPath, to_underlying(status.Value()));
170+
}
171+
else
172+
{
173+
if (globalStatus != Status::Success)
174+
{
175+
ChipLogError(Zcl, "Failed to open joint commissioning window. Global status " ChipLogFormatIMStatus,
176+
ChipLogValueIMStatus(globalStatus));
177+
}
178+
commandObj->AddStatus(commandPath, globalStatus);
179+
}
180+
138181
return true;
139182
}
140183

src/app/server/CommissioningModeProvider.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DLL_EXPORT CommissioningModeProvider
3131
{
3232
public:
3333
virtual Dnssd::CommissioningMode GetCommissioningMode() const = 0;
34+
virtual bool IsJointFabricEnabled() = 0;
3435

3536
virtual ~CommissioningModeProvider() {}
3637
};

src/app/server/CommissioningWindowManager.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ void CommissioningWindowManager::Shutdown()
119119
void CommissioningWindowManager::ResetState()
120120
{
121121
mUseECM = false;
122+
mJCM = false;
122123

123124
mECMDiscriminator = 0;
124125
mECMIterations = 0;
@@ -390,6 +391,14 @@ CHIP_ERROR CommissioningWindowManager::OpenEnhancedCommissioningWindow(Seconds32
390391
return err;
391392
}
392393

394+
CHIP_ERROR CommissioningWindowManager::OpenJointCommissioningWindow(Seconds32 commissioningTimeout, uint16_t discriminator,
395+
Spake2pVerifier & verifier, uint32_t iterations, ByteSpan salt,
396+
FabricIndex fabricIndex, VendorId vendorId)
397+
{
398+
SetJCM(true);
399+
return OpenEnhancedCommissioningWindow(commissioningTimeout, discriminator, verifier, iterations, salt, fabricIndex, vendorId);
400+
}
401+
393402
void CommissioningWindowManager::CloseCommissioningWindow()
394403
{
395404
if (IsCommissioningWindowOpen())

src/app/server/CommissioningWindowManager.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
8888
Crypto::Spake2pVerifier & verifier, uint32_t iterations, chip::ByteSpan salt,
8989
FabricIndex fabricIndex, VendorId vendorId);
9090

91+
CHIP_ERROR OpenJointCommissioningWindow(System::Clock::Seconds32 commissioningTimeout, uint16_t discriminator,
92+
Crypto::Spake2pVerifier & verifier, uint32_t iterations, chip::ByteSpan salt,
93+
FabricIndex fabricIndex, VendorId vendorId);
94+
9195
void CloseCommissioningWindow();
9296

9397
app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum CommissioningWindowStatusForCluster() const;
@@ -102,6 +106,7 @@ class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
102106

103107
// CommissioningModeProvider implementation.
104108
Dnssd::CommissioningMode GetCommissioningMode() const override;
109+
bool IsJointFabricEnabled() override { return mJCM; }
105110

106111
//// UnsolicitedMessageHandler Implementation ////
107112
CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader,
@@ -127,6 +132,8 @@ class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
127132

128133
void SetBLE(bool ble) { mIsBLE = ble; }
129134

135+
void SetJCM(bool jcm) { mJCM = jcm; }
136+
130137
CHIP_ERROR SetTemporaryDiscriminator(uint16_t discriminator);
131138

132139
CHIP_ERROR RestoreDiscriminator();
@@ -190,6 +197,7 @@ class CommissioningWindowManager : public Messaging::UnsolicitedMessageHandler,
190197
app::Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen;
191198

192199
bool mIsBLE = true;
200+
bool mJCM = false;
193201

194202
PASESession mPairingSession;
195203

0 commit comments

Comments
 (0)