Skip to content

Commit 3996167

Browse files
erjiaqingyunhanw-googlemkardous-silabs
authored andcommitted
[ICD] ICD registration in AutoCommissioner (#30260)
* [ICD] ICD registration in AutoCommissioner * Fix tests * Update according to comments * Fix * restyle * Fix Ci * Update * Regen * Update * Fix Test * Update * Update * Apply suggestions from code review Co-authored-by: mkardous-silabs <[email protected]> * Fix typo * Allow setting ICD keys during commissioning async * Update * Fix * Fix * Bypass darwin-tests for ICD Management Cluster * Add SendStayActive * Persist symmetric key * revert mICDClientStorage * revert mICDClientStorage * Address comments * Alter commissioning stage order * Update --------- Co-authored-by: yunhanw-google <[email protected]> Co-authored-by: mkardous-silabs <[email protected]>
1 parent 7d0629e commit 3996167

File tree

13 files changed

+306
-1028
lines changed

13 files changed

+306
-1028
lines changed

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

+46
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,30 @@ CommissioningParameters PairingCommand::GetCommissioningParameters()
132132
params.SetDSTOffsets(mDSTOffsetList);
133133
}
134134

135+
if (!mSkipICDRegistration.ValueOr(false))
136+
{
137+
params.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
138+
139+
if (!mICDSymmetricKey.HasValue())
140+
{
141+
chip::Crypto::DRBG_get_bytes(mRandomGeneratedICDSymmetricKey, sizeof(mRandomGeneratedICDSymmetricKey));
142+
mICDSymmetricKey.SetValue(ByteSpan(mRandomGeneratedICDSymmetricKey));
143+
}
144+
if (!mICDCheckInNodeId.HasValue())
145+
{
146+
mICDCheckInNodeId.SetValue(CurrentCommissioner().GetNodeId());
147+
}
148+
if (!mICDMonitoredSubject.HasValue())
149+
{
150+
mICDMonitoredSubject.SetValue(mICDCheckInNodeId.Value());
151+
}
152+
// These Optionals must have values now.
153+
// The commissioner will verify these values.
154+
params.SetICDSymmetricKey(mICDSymmetricKey.Value());
155+
params.SetICDCheckInNodeId(mICDCheckInNodeId.Value());
156+
params.SetICDMonitoredSubject(mICDMonitoredSubject.Value());
157+
}
158+
135159
return params;
136160
}
137161

@@ -367,6 +391,28 @@ void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
367391
SetCommandExitStatus(err);
368392
}
369393

394+
void PairingCommand::OnICDRegistrationInfoRequired()
395+
{
396+
// Since we compute our ICD Registration info up front, we can call ICDRegistrationInfoReady() directly.
397+
CurrentCommissioner().ICDRegistrationInfoReady();
398+
}
399+
400+
void PairingCommand::OnICDRegistrationComplete(NodeId nodeId, uint32_t icdCounter)
401+
{
402+
char icdSymmetricKeyHex[chip::Crypto::kAES_CCM128_Key_Length * 2 + 1];
403+
404+
chip::Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex,
405+
sizeof(icdSymmetricKeyHex), chip::Encoding::HexFlags::kNullTerminate);
406+
407+
// TODO: Persist symmetric key.
408+
409+
ChipLogProgress(chipTool,
410+
"ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
411+
" / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s",
412+
ChipLogValueX64(nodeId), ChipLogValueX64(mICDCheckInNodeId.Value()),
413+
ChipLogValueX64(mICDMonitoredSubject.Value()), icdSymmetricKeyHex);
414+
}
415+
370416
void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData)
371417
{
372418
// Ignore nodes with closed commissioning window

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

+16
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ class PairingCommand : public CHIPCommand,
6565
"Bypass the attestation verifier. If not provided or false, the attestation verifier is not bypassed."
6666
" If true, the commissioning will continue in case of attestation verification failure.");
6767
AddArgument("case-auth-tags", 1, UINT32_MAX, &mCASEAuthTags, "The CATs to be encoded in the NOC sent to the commissionee");
68+
AddArgument("skip-icd-registration", 0, 1, &mSkipICDRegistration,
69+
"Skip registering for check-ins from ICDs during commissioning. Default: false");
70+
AddArgument("icd-check-in-nodeid", 0, UINT64_MAX, &mICDCheckInNodeId,
71+
"The check-in node id for the ICD, default: node id of the commissioner.");
72+
AddArgument("icd-monitored-subject", 0, UINT64_MAX, &mICDMonitoredSubject,
73+
"The monitored subject of the ICD, default: The node id used for icd-check-in-nodeid.");
74+
AddArgument("icd-symmetric-key", &mICDSymmetricKey, "The 16 bytes ICD symmetric key, default: randomly generated.");
6875

6976
switch (networkType)
7077
{
@@ -188,6 +195,8 @@ class PairingCommand : public CHIPCommand,
188195
void OnPairingComplete(CHIP_ERROR error) override;
189196
void OnPairingDeleted(CHIP_ERROR error) override;
190197
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override;
198+
void OnICDRegistrationInfoRequired() override;
199+
void OnICDRegistrationComplete(NodeId deviceId, uint32_t icdCounter) override;
191200

192201
/////////// DeviceDiscoveryDelegate Interface /////////
193202
void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) override;
@@ -222,12 +231,17 @@ class PairingCommand : public CHIPCommand,
222231
chip::Optional<bool> mBypassAttestationVerifier;
223232
chip::Optional<std::vector<uint32_t>> mCASEAuthTags;
224233
chip::Optional<char *> mCountryCode;
234+
chip::Optional<bool> mSkipICDRegistration;
235+
chip::Optional<NodeId> mICDCheckInNodeId;
236+
chip::Optional<chip::ByteSpan> mICDSymmetricKey;
237+
chip::Optional<uint64_t> mICDMonitoredSubject;
225238
chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type> mTimeZoneList;
226239
TypedComplexArgument<chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>>
227240
mComplex_TimeZones;
228241
chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type> mDSTOffsetList;
229242
TypedComplexArgument<chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>>
230243
mComplex_DSTOffsets;
244+
231245
uint16_t mRemotePort;
232246
uint16_t mDiscriminator;
233247
uint32_t mSetupPINCode;
@@ -239,6 +253,8 @@ class PairingCommand : public CHIPCommand,
239253
uint64_t mDiscoveryFilterCode;
240254
char * mDiscoveryFilterInstanceName;
241255

256+
uint8_t mRandomGeneratedICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];
257+
242258
// For unpair
243259
chip::Platform::UniquePtr<chip::Controller::CurrentFabricRemover> mCurrentFabricRemover;
244260
chip::Callback::Callback<chip::Controller::OnCurrentFabricRemove> mCurrentFabricRemoveCallback;

examples/darwin-framework-tool/templates/tests/ciTests.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
"Disabled because darwin-framework-tool does not handle substraction in parameters",
6868
"Test_TC_S_2_3",
6969
"TestScenesMultiFabric",
70-
"TestScenesFabricSceneInfo"
70+
"TestScenesFabricSceneInfo",
71+
"#30759: Darwin chip-tool does not support ICD registration during commissioning",
72+
"TestIcdManagementCluster"
7173
]
7274
}

scripts/tests/chiptest/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def _GetChipReplUnsupportedTests() -> Set[str]:
175175
"Test_TC_ACE_1_6.yaml", # Test fails only in chip-repl: Refer--> https://github.com/project-chip/connectedhomeip/pull/27910#issuecomment-1632485584
176176
"Test_TC_IDM_1_2.yaml", # chip-repl does not support AnyCommands (19/07/2023)
177177
"TestGroupKeyManagementCluster.yaml", # chip-repl does not support EqualityCommands (2023-08-04)
178+
"TestIcdManagementCluster.yaml", # TODO(#30430): add ICD registration support in chip-repl
178179
"Test_TC_S_2_2.yaml", # chip-repl does not support EqualityCommands pseudo-cluster
179180
"Test_TC_MOD_3_1.yaml", # chip-repl does not support EqualityCommands pseudo-cluster
180181
"Test_TC_MOD_3_2.yaml", # chip-repl does not support EqualityCommands pseudo-cluster

src/app/tests/suites/TestIcdManagementCluster.yaml

+24-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ config:
2020
endpoint: 0
2121

2222
tests:
23+
- label: "Read the commissioner node ID"
24+
cluster: "CommissionerCommands"
25+
command: "GetCommissionerNodeId"
26+
response:
27+
values:
28+
- name: "nodeId"
29+
saveAs: commissionerNodeId
30+
2331
- label: "Wait for the commissioned device to be retrieved"
2432
cluster: "DelayCommands"
2533
command: "WaitForCommissionee"
@@ -94,11 +102,25 @@ tests:
94102
response:
95103
error: NOT_FOUND
96104

97-
- label: "Read RegisteredClients"
105+
# chip-tool will register itself with the ICD during the tests.
106+
- label: "Read RegisteredClients For Registration During Commissioning"
98107
command: "readAttribute"
99108
attribute: "RegisteredClients"
100109
response:
101-
value: []
110+
value:
111+
[
112+
{
113+
CheckInNodeID: commissionerNodeId,
114+
MonitoredSubject: commissionerNodeId,
115+
},
116+
]
117+
118+
- label: "Unregister Client Registered During Commissioning"
119+
command: "UnregisterClient"
120+
arguments:
121+
values:
122+
- name: "CheckInNodeID"
123+
value: commissionerNodeId
102124

103125
- label: "Register 1.0 (key too short)"
104126
command: "RegisterClient"

src/controller/AutoCommissioner.cpp

+63-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include <controller/AutoCommissioner.h>
2020

21+
#include <cstring>
22+
2123
#include <app/InteractionModelTimeout.h>
2224
#include <controller/CHIPDeviceController.h>
2325
#include <credentials/CHIPCert.h>
@@ -65,6 +67,32 @@ static bool IsUnsafeSpan(const Optional<SpanType> & maybeUnsafeSpan, const Optio
6567
return maybeUnsafeSpan.Value().data() != knownSafeSpan.Value().data();
6668
}
6769

70+
CHIP_ERROR AutoCommissioner::VerifyICDRegistrationInfo(const CommissioningParameters & params)
71+
{
72+
ChipLogProgress(Controller, "Checking ICD registration parameters");
73+
if (!params.GetICDSymmetricKey().HasValue())
74+
{
75+
ChipLogError(Controller, "Missing ICD symmetric key!");
76+
return CHIP_ERROR_INVALID_ARGUMENT;
77+
}
78+
if (params.GetICDSymmetricKey().Value().size() != sizeof(mICDSymmetricKey))
79+
{
80+
ChipLogError(Controller, "Invalid ICD symmetric key length!");
81+
return CHIP_ERROR_INVALID_ARGUMENT;
82+
}
83+
if (!params.GetICDCheckInNodeId().HasValue())
84+
{
85+
ChipLogError(Controller, "Missing ICD check-in node id!");
86+
return CHIP_ERROR_INVALID_ARGUMENT;
87+
}
88+
if (!params.GetICDMonitoredSubject().HasValue())
89+
{
90+
ChipLogError(Controller, "Missing ICD monitored subject!");
91+
return CHIP_ERROR_INVALID_ARGUMENT;
92+
}
93+
return CHIP_NO_ERROR;
94+
}
95+
6896
CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params)
6997
{
7098
// Make sure any members that point to buffers that we are not pointing to
@@ -90,6 +118,7 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
90118
IsUnsafeSpan(params.GetPAI(), mParams.GetPAI()) || IsUnsafeSpan(params.GetDAC(), mParams.GetDAC()) ||
91119
IsUnsafeSpan(params.GetTimeZone(), mParams.GetTimeZone()) ||
92120
IsUnsafeSpan(params.GetDSTOffsets(), mParams.GetDSTOffsets()) ||
121+
IsUnsafeSpan(params.GetICDSymmetricKey(), mParams.GetICDSymmetricKey()) ||
93122
(params.GetDefaultNTP().HasValue() && !params.GetDefaultNTP().Value().IsNull() &&
94123
params.GetDefaultNTP().Value().Value().data() != mDefaultNtp));
95124

@@ -229,6 +258,17 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
229258
}
230259
}
231260

261+
if (params.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore && params.GetICDSymmetricKey().HasValue())
262+
{
263+
ReturnErrorOnFailure(VerifyICDRegistrationInfo(params));
264+
265+
// The values must be valid now.
266+
memcpy(mICDSymmetricKey, params.GetICDSymmetricKey().Value().data(), params.GetICDSymmetricKey().Value().size());
267+
mParams.SetICDSymmetricKey(ByteSpan(mICDSymmetricKey));
268+
mParams.SetICDCheckInNodeId(params.GetICDCheckInNodeId().Value());
269+
mParams.SetICDMonitoredSubject(params.GetICDMonitoredSubject().Value());
270+
}
271+
232272
return CHIP_NO_ERROR;
233273
}
234274

@@ -367,6 +407,17 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
367407
return GetNextCommissioningStageInternal(CommissioningStage::kConfigureTrustedTimeSource, lastErr);
368408
}
369409
case CommissioningStage::kConfigureTrustedTimeSource:
410+
if (mNeedIcdRegistration)
411+
{
412+
return CommissioningStage::kICDGetRegistrationInfo;
413+
}
414+
return GetNextCommissioningStageInternal(CommissioningStage::kICDSendStayActive, lastErr);
415+
case CommissioningStage::kICDGetRegistrationInfo:
416+
return CommissioningStage::kICDRegistration;
417+
case CommissioningStage::kICDRegistration:
418+
// TODO(#24259): StayActiveRequest is not supported by server. We may want to SendStayActive after OpDiscovery.
419+
return CommissioningStage::kICDSendStayActive;
420+
case CommissioningStage::kICDSendStayActive:
370421
// TODO(cecille): device attestation casues operational cert provisioning to happen, This should be a separate stage.
371422
// For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the
372423
// operational network because the provisioning of certificates will trigger the device to start operational advertising.
@@ -709,10 +760,10 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
709760

710761
if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
711762
{
712-
if (commissioningInfo.isIcd)
763+
if (commissioningInfo.isIcd && commissioningInfo.checkInProtocolSupport)
713764
{
714-
mNeedIcdRegistraion = true;
715-
ChipLogDetail(Controller, "AutoCommissioner: Device is ICD");
765+
mNeedIcdRegistration = true;
766+
ChipLogDetail(Controller, "AutoCommissioner: ICD supports the check-in protocol.");
716767
}
717768
}
718769
break;
@@ -776,6 +827,15 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
776827
// storing the returned certs, so just return here without triggering the next stage.
777828
return NOCChainGenerated(report.Get<NocChain>().noc, report.Get<NocChain>().icac, report.Get<NocChain>().rcac,
778829
report.Get<NocChain>().ipk, report.Get<NocChain>().adminSubject);
830+
case CommissioningStage::kICDGetRegistrationInfo:
831+
// Noting to od. The ICD registation info is handled elsewhere.
832+
break;
833+
case CommissioningStage::kICDRegistration:
834+
// Noting to od. DevicePairingDelegate will handle this.
835+
break;
836+
case CommissioningStage::kICDSendStayActive:
837+
// Nothing to do.
838+
break;
779839
case CommissioningStage::kFindOperational:
780840
mOperationalDeviceProxy = report.Get<OperationalNodeFoundData>().operationalProxy;
781841
break;

src/controller/AutoCommissioner.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class AutoCommissioner : public CommissioningDelegate
7474
EndpointId GetEndpoint(const CommissioningStage & stage) const;
7575
CommissioningStage GetNextCommissioningStageInternal(CommissioningStage currentStage, CHIP_ERROR & lastErr);
7676

77+
CHIP_ERROR VerifyICDRegistrationInfo(const CommissioningParameters & params);
78+
7779
// Helper function to determine whether next stage should be kWiFiNetworkSetup,
7880
// kThreadNetworkSetup or kCleanup, depending whether network information has
7981
// been provided that matches the thread/wifi endpoint of the target.
@@ -120,7 +122,7 @@ class AutoCommissioner : public CommissioningDelegate
120122
ReadCommissioningInfo mDeviceCommissioningInfo;
121123
bool mNeedsDST = false;
122124

123-
bool mNeedIcdRegistraion = false;
125+
bool mNeedIcdRegistration = false;
124126

125127
// TODO: Why were the nonces statically allocated, but the certs dynamically allocated?
126128
uint8_t * mDAC = nullptr;
@@ -136,6 +138,8 @@ class AutoCommissioner : public CommissioningDelegate
136138
uint8_t mAttestationElements[Credentials::kMaxRspLen];
137139
uint16_t mAttestationSignatureLen = 0;
138140
uint8_t mAttestationSignature[Crypto::kMax_ECDSA_Signature_Length];
141+
142+
uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length];
139143
};
140144
} // namespace Controller
141145
} // namespace chip

src/controller/CHIPDeviceController.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,25 @@ void DeviceCommissioner::OnFailedToExtendedArmFailSafeDeviceAttestation(void * c
11731173
commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report);
11741174
}
11751175

1176+
void DeviceCommissioner::OnICDManagementRegisterClientResponse(
1177+
void * context, const app::Clusters::IcdManagement::Commands::RegisterClientResponse::DecodableType & data)
1178+
{
1179+
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
1180+
1181+
CommissioningDelegate::CommissioningReport report;
1182+
auto pairingDelegate = commissioner->GetPairingDelegate();
1183+
auto deviceBeingCommissioned = commissioner->mDeviceBeingCommissioned;
1184+
if (pairingDelegate != nullptr && deviceBeingCommissioned != nullptr)
1185+
{
1186+
pairingDelegate->OnICDRegistrationComplete(deviceBeingCommissioned->GetDeviceId(), data.ICDCounter);
1187+
commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report);
1188+
}
1189+
else
1190+
{
1191+
commissioner->CommissioningStageComplete(CHIP_ERROR_INCORRECT_STATE, report);
1192+
}
1193+
}
1194+
11761195
bool DeviceCommissioner::ExtendArmFailSafe(DeviceProxy * proxy, CommissioningStage step, uint16_t armFailSafeTimeout,
11771196
Optional<System::Clock::Timeout> commandTimeout, OnExtendFailsafeSuccess onSuccess,
11781197
OnExtendFailsafeFailure onFailure)
@@ -2362,6 +2381,16 @@ CHIP_ERROR DeviceCommissioner::NetworkCredentialsReady()
23622381
return CHIP_NO_ERROR;
23632382
}
23642383

2384+
CHIP_ERROR DeviceCommissioner::ICDRegistrationInfoReady()
2385+
{
2386+
ReturnErrorCodeIf(mCommissioningStage != CommissioningStage::kICDGetRegistrationInfo, CHIP_ERROR_INCORRECT_STATE);
2387+
2388+
// need to advance to next step
2389+
CommissioningStageComplete(CHIP_NO_ERROR);
2390+
2391+
return CHIP_NO_ERROR;
2392+
}
2393+
23652394
void DeviceCommissioner::OnNetworkConfigResponse(void * context,
23662395
const NetworkCommissioning::Commands::NetworkConfigResponse::DecodableType & data)
23672396
{
@@ -3007,6 +3036,41 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
30073036
}
30083037
}
30093038
break;
3039+
case CommissioningStage::kICDGetRegistrationInfo: {
3040+
GetPairingDelegate()->OnICDRegistrationInfoRequired();
3041+
return;
3042+
}
3043+
break;
3044+
case CommissioningStage::kICDRegistration: {
3045+
IcdManagement::Commands::RegisterClient::Type request;
3046+
3047+
if (!(params.GetICDCheckInNodeId().HasValue() && params.GetICDMonitoredSubject().HasValue() &&
3048+
params.GetICDSymmetricKey().HasValue()))
3049+
{
3050+
ChipLogError(Controller, "No ICD Registration information provided!");
3051+
CommissioningStageComplete(CHIP_ERROR_INCORRECT_STATE);
3052+
return;
3053+
}
3054+
3055+
request.checkInNodeID = params.GetICDCheckInNodeId().Value();
3056+
request.monitoredSubject = params.GetICDMonitoredSubject().Value();
3057+
request.key = params.GetICDSymmetricKey().Value();
3058+
3059+
CHIP_ERROR err = SendCommand(proxy, request, OnICDManagementRegisterClientResponse, OnBasicFailure, endpoint, timeout);
3060+
if (err != CHIP_NO_ERROR)
3061+
{
3062+
// We won't get any async callbacks here, so just complete our stage.
3063+
ChipLogError(Controller, "Failed to send IcdManagement.RegisterClient command: %" CHIP_ERROR_FORMAT, err.Format());
3064+
CommissioningStageComplete(err);
3065+
return;
3066+
}
3067+
}
3068+
break;
3069+
case CommissioningStage::kICDSendStayActive: {
3070+
// TODO(#24259): Send StayActiveRequest once server supports this.
3071+
CommissioningStageComplete(CHIP_NO_ERROR);
3072+
}
3073+
break;
30103074
case CommissioningStage::kFindOperational: {
30113075
// If there is an error, CommissioningStageComplete will be called from OnDeviceConnectionFailureFn.
30123076
auto scopedPeerId = GetPeerScopedId(proxy->GetDeviceId());

0 commit comments

Comments
 (0)