Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2024 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include <app/InteractionModelTimeout.h>
20 : #include <controller/AutoCommissioner.h>
21 : #include <controller/CHIPDeviceController.h>
22 : #include <credentials/CHIPCert.h>
23 : #include <lib/support/SafeInt.h>
24 :
25 : #include <cstring>
26 : #include <type_traits>
27 :
28 : namespace chip {
29 : namespace Controller {
30 :
31 : using namespace chip::app::Clusters;
32 : using namespace chip::Crypto;
33 : using chip::app::DataModel::MakeNullable;
34 : using chip::app::DataModel::NullNullable;
35 :
36 45 : AutoCommissioner::AutoCommissioner()
37 : {
38 15 : SetCommissioningParameters(CommissioningParameters());
39 15 : }
40 :
41 15 : AutoCommissioner::~AutoCommissioner() {}
42 :
43 0 : void AutoCommissioner::SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate)
44 : {
45 0 : mOperationalCredentialsDelegate = operationalCredentialsDelegate;
46 0 : }
47 :
48 1 : CHIP_ERROR AutoCommissioner::VerifyICDRegistrationInfo(const CommissioningParameters & params)
49 : {
50 1 : ChipLogProgress(Controller, "Checking ICD registration parameters");
51 1 : if (!params.GetICDSymmetricKey().HasValue())
52 : {
53 0 : ChipLogError(Controller, "Missing ICD symmetric key!");
54 0 : return CHIP_ERROR_INVALID_ARGUMENT;
55 : }
56 1 : if (params.GetICDSymmetricKey().Value().size() != sizeof(mICDSymmetricKey))
57 : {
58 0 : ChipLogError(Controller, "Invalid ICD symmetric key length!");
59 0 : return CHIP_ERROR_INVALID_ARGUMENT;
60 : }
61 1 : if (!params.GetICDCheckInNodeId().HasValue())
62 : {
63 0 : ChipLogError(Controller, "Missing ICD check-in node id!");
64 0 : return CHIP_ERROR_INVALID_ARGUMENT;
65 : }
66 1 : if (!params.GetICDMonitoredSubject().HasValue())
67 : {
68 0 : ChipLogError(Controller, "Missing ICD monitored subject!");
69 0 : return CHIP_ERROR_INVALID_ARGUMENT;
70 : }
71 1 : if (!params.GetICDClientType().HasValue())
72 : {
73 0 : ChipLogError(Controller, "Missing ICD Client Type!");
74 0 : return CHIP_ERROR_INVALID_ARGUMENT;
75 : }
76 1 : return CHIP_NO_ERROR;
77 : }
78 :
79 24 : CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params)
80 : {
81 : // Our logic below assumes that we can modify mParams without affecting params.
82 24 : VerifyOrReturnError(¶ms != &mParams, CHIP_NO_ERROR);
83 :
84 : // Copy the whole struct (scalars and pointers), but clear any members that might point to
85 : // external buffers. For those members we have to copy the data over into our own buffers below.
86 : // Note that all of the copy operations use memmove() instead of memcpy(), because the caller
87 : // may be passing a modified shallow copy of our CommissioningParmeters, i.e. where various spans
88 : // already point into the buffers we're copying into, and memcpy() with overlapping buffers is UB.
89 24 : mParams = params;
90 24 : mParams.ClearExternalBufferDependentValues();
91 :
92 24 : if (params.GetThreadOperationalDataset().HasValue())
93 : {
94 1 : ByteSpan dataset = params.GetThreadOperationalDataset().Value();
95 1 : if (dataset.size() > CommissioningParameters::kMaxThreadDatasetLen)
96 : {
97 1 : ChipLogError(Controller, "Thread operational data set is too large");
98 1 : return CHIP_ERROR_INVALID_ARGUMENT;
99 : }
100 0 : memmove(mThreadOperationalDataset, dataset.data(), dataset.size());
101 0 : ChipLogProgress(Controller, "Setting thread operational dataset from parameters");
102 0 : mParams.SetThreadOperationalDataset(ByteSpan(mThreadOperationalDataset, dataset.size()));
103 : }
104 :
105 23 : if (params.GetWiFiCredentials().HasValue())
106 : {
107 1 : WiFiCredentials creds = params.GetWiFiCredentials().Value();
108 1 : if (creds.ssid.size() > CommissioningParameters::kMaxSsidLen ||
109 0 : creds.credentials.size() > CommissioningParameters::kMaxCredentialsLen)
110 : {
111 1 : ChipLogError(Controller, "Wifi credentials are too large");
112 1 : return CHIP_ERROR_INVALID_ARGUMENT;
113 : }
114 0 : memmove(mSsid, creds.ssid.data(), creds.ssid.size());
115 0 : memmove(mCredentials, creds.credentials.data(), creds.credentials.size());
116 0 : ChipLogProgress(Controller, "Setting wifi credentials from parameters");
117 0 : mParams.SetWiFiCredentials(
118 0 : WiFiCredentials(ByteSpan(mSsid, creds.ssid.size()), ByteSpan(mCredentials, creds.credentials.size())));
119 : }
120 :
121 22 : if (params.GetCountryCode().HasValue())
122 : {
123 1 : auto code = params.GetCountryCode().Value();
124 1 : MutableCharSpan copiedCode(mCountryCode);
125 1 : if (CopyCharSpanToMutableCharSpan(code, copiedCode) == CHIP_NO_ERROR)
126 : {
127 0 : mParams.SetCountryCode(copiedCode);
128 : }
129 : else
130 : {
131 1 : ChipLogError(Controller, "Country code is too large: %u", static_cast<unsigned>(code.size()));
132 1 : return CHIP_ERROR_INVALID_ARGUMENT;
133 : }
134 : }
135 :
136 : // If the AttestationNonce is passed in, using that else using a random one..
137 21 : if (params.GetAttestationNonce().HasValue())
138 : {
139 1 : ChipLogProgress(Controller, "Setting attestation nonce from parameters");
140 1 : VerifyOrReturnError(params.GetAttestationNonce().Value().size() == sizeof(mAttestationNonce), CHIP_ERROR_INVALID_ARGUMENT);
141 0 : memmove(mAttestationNonce, params.GetAttestationNonce().Value().data(), params.GetAttestationNonce().Value().size());
142 : }
143 : else
144 : {
145 20 : ChipLogProgress(Controller, "Setting attestation nonce to random value");
146 20 : Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce));
147 : }
148 20 : mParams.SetAttestationNonce(ByteSpan(mAttestationNonce, sizeof(mAttestationNonce)));
149 :
150 20 : if (params.GetCSRNonce().HasValue())
151 : {
152 1 : ChipLogProgress(Controller, "Setting CSR nonce from parameters");
153 1 : VerifyOrReturnError(params.GetCSRNonce().Value().size() == sizeof(mCSRNonce), CHIP_ERROR_INVALID_ARGUMENT);
154 0 : memmove(mCSRNonce, params.GetCSRNonce().Value().data(), params.GetCSRNonce().Value().size());
155 : }
156 : else
157 : {
158 19 : ChipLogProgress(Controller, "Setting CSR nonce to random value");
159 19 : Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce));
160 : }
161 19 : mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));
162 :
163 19 : if (params.GetDSTOffsets().HasValue())
164 : {
165 1 : ChipLogProgress(Controller, "Setting DST offsets from parameters");
166 1 : size_t size = std::min(params.GetDSTOffsets().Value().size(), kMaxSupportedDstStructs);
167 2 : for (size_t i = 0; i < size; ++i)
168 : {
169 1 : mDstOffsetsBuf[i] = params.GetDSTOffsets().Value()[i];
170 : }
171 1 : auto list = app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>(mDstOffsetsBuf, size);
172 1 : mParams.SetDSTOffsets(list);
173 : }
174 19 : if (params.GetTimeZone().HasValue())
175 : {
176 1 : ChipLogProgress(Controller, "Setting Time Zone from parameters");
177 1 : size_t size = std::min(params.GetTimeZone().Value().size(), kMaxSupportedTimeZones);
178 2 : for (size_t i = 0; i < size; ++i)
179 : {
180 1 : mTimeZoneBuf[i] = params.GetTimeZone().Value()[i];
181 2 : if (params.GetTimeZone().Value()[i].name.HasValue() &&
182 2 : params.GetTimeZone().Value()[i].name.Value().size() <= kMaxTimeZoneNameLen)
183 : {
184 1 : auto span = MutableCharSpan(mTimeZoneNames[i], kMaxTimeZoneNameLen);
185 : // The buffer backing "span" is statically allocated and is of size kMaxSupportedTimeZones, so this should never
186 : // fail.
187 1 : CopyCharSpanToMutableCharSpan(params.GetTimeZone().Value()[i].name.Value(), span);
188 1 : mTimeZoneBuf[i].name.SetValue(span);
189 : }
190 : else
191 : {
192 0 : mTimeZoneBuf[i].name.ClearValue();
193 : }
194 : }
195 1 : auto list = app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>(mTimeZoneBuf, size);
196 1 : mParams.SetTimeZone(list);
197 : }
198 19 : if (params.GetDefaultNTP().HasValue())
199 : {
200 1 : ChipLogProgress(Controller, "Setting Default NTP from parameters");
201 : // This parameter is an optional nullable, so we need to go two levels deep here.
202 1 : if (!params.GetDefaultNTP().Value().IsNull() && params.GetDefaultNTP().Value().Value().size() <= kMaxDefaultNtpSize)
203 : {
204 : // The buffer backing "span" is statically allocated and is of size kMaxDefaultNtpSize.
205 1 : auto span = MutableCharSpan(mDefaultNtp, kMaxDefaultNtpSize);
206 1 : CopyCharSpanToMutableCharSpan(params.GetDefaultNTP().Value().Value(), span);
207 1 : auto default_ntp = MakeNullable(CharSpan(mDefaultNtp, params.GetDefaultNTP().Value().Value().size()));
208 1 : mParams.SetDefaultNTP(default_ntp);
209 : }
210 : }
211 :
212 19 : if (params.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore && params.GetICDSymmetricKey().HasValue())
213 : {
214 1 : ReturnErrorOnFailure(VerifyICDRegistrationInfo(params));
215 :
216 : // The values must be valid now.
217 1 : memmove(mICDSymmetricKey, params.GetICDSymmetricKey().Value().data(), params.GetICDSymmetricKey().Value().size());
218 1 : mParams.SetICDSymmetricKey(ByteSpan(mICDSymmetricKey));
219 1 : mParams.SetICDCheckInNodeId(params.GetICDCheckInNodeId().Value());
220 1 : mParams.SetICDMonitoredSubject(params.GetICDMonitoredSubject().Value());
221 1 : mParams.SetICDClientType(params.GetICDClientType().Value());
222 : }
223 :
224 19 : auto extraReadPaths = params.GetExtraReadPaths();
225 19 : if (extraReadPaths.size() > 0)
226 : {
227 : using ReadPath = std::remove_pointer_t<decltype(extraReadPaths.data())>;
228 : static_assert(std::is_trivially_copyable_v<ReadPath>, "can't use memmove / memcpy, not trivially copyable");
229 :
230 0 : if (mExtraReadPaths.AllocatedSize() == extraReadPaths.size())
231 : {
232 0 : memmove(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath));
233 : }
234 : else
235 : {
236 : // We can't reallocate mExtraReadPaths yet as this would free the old buffer,
237 : // and the caller might be passing a sub-span of the old paths.
238 0 : decltype(mExtraReadPaths) oldReadPaths(std::move(mExtraReadPaths));
239 0 : VerifyOrReturnError(mExtraReadPaths.Alloc(extraReadPaths.size()), CHIP_ERROR_NO_MEMORY);
240 0 : memcpy(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath));
241 0 : }
242 :
243 0 : mParams.SetExtraReadPaths(mExtraReadPaths.Span());
244 : }
245 : else
246 : {
247 19 : mExtraReadPaths.Free();
248 : }
249 :
250 19 : return CHIP_NO_ERROR;
251 : }
252 :
253 4 : const CommissioningParameters & AutoCommissioner::GetCommissioningParameters() const
254 : {
255 4 : return mParams;
256 : }
257 :
258 1 : CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR & lastErr)
259 : {
260 1 : auto nextStage = GetNextCommissioningStageInternal(currentStage, lastErr);
261 1 : if (lastErr == CHIP_NO_ERROR)
262 : {
263 1 : ChipLogProgress(Controller, "Commissioning stage next step: '%s' -> '%s'", StageToString(currentStage),
264 : StageToString(nextStage));
265 : }
266 : else
267 : {
268 0 : ChipLogProgress(Controller, "Going from commissioning step '%s' with lastErr = '%s' -> '%s'", StageToString(currentStage),
269 : lastErr.AsString(), StageToString(nextStage));
270 : }
271 1 : return nextStage;
272 : }
273 :
274 0 : CommissioningStage AutoCommissioner::GetNextCommissioningStageNetworkSetup(CommissioningStage currentStage, CHIP_ERROR & lastErr)
275 : {
276 0 : if (IsSecondaryNetworkSupported())
277 : {
278 0 : if (TryingSecondaryNetwork())
279 : {
280 : // Try secondary network interface.
281 0 : return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kThreadNetworkSetup
282 0 : : CommissioningStage::kWiFiNetworkSetup;
283 : }
284 : // Try primary network interface
285 0 : return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kWiFiNetworkSetup
286 0 : : CommissioningStage::kThreadNetworkSetup;
287 : }
288 :
289 0 : if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
290 : {
291 0 : return CommissioningStage::kWiFiNetworkSetup;
292 : }
293 0 : if (mParams.GetThreadOperationalDataset().HasValue() && mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
294 : {
295 0 : return CommissioningStage::kThreadNetworkSetup;
296 : }
297 :
298 0 : ChipLogError(Controller, "Required network information not provided in commissioning parameters");
299 0 : ChipLogError(Controller, "Parameters supplied: wifi (%s) thread (%s)", mParams.GetWiFiCredentials().HasValue() ? "yes" : "no",
300 : mParams.GetThreadOperationalDataset().HasValue() ? "yes" : "no");
301 0 : ChipLogError(Controller, "Device supports: wifi (%s) thread(%s)",
302 : mDeviceCommissioningInfo.network.wifi.endpoint == kInvalidEndpointId ? "no" : "yes",
303 : mDeviceCommissioningInfo.network.thread.endpoint == kInvalidEndpointId ? "no" : "yes");
304 0 : lastErr = CHIP_ERROR_INVALID_ARGUMENT;
305 0 : return CommissioningStage::kCleanup;
306 : }
307 :
308 39 : CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(CommissioningStage currentStage, CHIP_ERROR & lastErr)
309 : {
310 39 : if (mStopCommissioning)
311 : {
312 1 : return CommissioningStage::kCleanup;
313 : }
314 38 : if (lastErr != CHIP_NO_ERROR)
315 : {
316 1 : return CommissioningStage::kCleanup;
317 : }
318 :
319 37 : switch (currentStage)
320 : {
321 1 : case CommissioningStage::kSecurePairing:
322 1 : return CommissioningStage::kReadCommissioningInfo;
323 2 : case CommissioningStage::kReadCommissioningInfo:
324 2 : if (mDeviceCommissioningInfo.general.breadcrumb > 0)
325 : {
326 : // If the breadcrumb is 0, the failsafe was disarmed.
327 : // We failed on network setup or later, the node failsafe has not been re-armed and the breadcrumb has not been reset.
328 : // Per the spec, we restart from after adding the NOC.
329 1 : return GetNextCommissioningStage(CommissioningStage::kSendNOC, lastErr);
330 : }
331 1 : return CommissioningStage::kArmFailsafe;
332 1 : case CommissioningStage::kArmFailsafe:
333 1 : return CommissioningStage::kConfigRegulatory;
334 1 : case CommissioningStage::kConfigRegulatory:
335 1 : return CommissioningStage::kConfigureTCAcknowledgments;
336 2 : case CommissioningStage::kConfigureTCAcknowledgments:
337 2 : if (mDeviceCommissioningInfo.requiresUTC)
338 : {
339 1 : return CommissioningStage::kConfigureUTCTime;
340 : }
341 : else
342 : {
343 : // Time cluster is not supported, move right to DA
344 1 : return CommissioningStage::kSendPAICertificateRequest;
345 : }
346 0 : case CommissioningStage::kConfigureUTCTime:
347 0 : if (mDeviceCommissioningInfo.requiresTimeZone && mParams.GetTimeZone().HasValue())
348 : {
349 0 : return kConfigureTimeZone;
350 : }
351 : else
352 : {
353 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureTimeZone, lastErr);
354 : }
355 0 : case CommissioningStage::kConfigureTimeZone:
356 0 : if (mNeedsDST && mParams.GetDSTOffsets().HasValue())
357 : {
358 0 : return CommissioningStage::kConfigureDSTOffset;
359 : }
360 : else
361 : {
362 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureDSTOffset, lastErr);
363 : }
364 0 : case CommissioningStage::kConfigureDSTOffset:
365 0 : if (mDeviceCommissioningInfo.requiresDefaultNTP && mParams.GetDefaultNTP().HasValue())
366 : {
367 0 : return CommissioningStage::kConfigureDefaultNTP;
368 : }
369 : else
370 : {
371 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureDefaultNTP, lastErr);
372 : }
373 1 : case CommissioningStage::kConfigureDefaultNTP:
374 1 : return CommissioningStage::kSendPAICertificateRequest;
375 1 : case CommissioningStage::kSendPAICertificateRequest:
376 1 : return CommissioningStage::kSendDACCertificateRequest;
377 1 : case CommissioningStage::kSendDACCertificateRequest:
378 1 : return CommissioningStage::kSendAttestationRequest;
379 1 : case CommissioningStage::kSendAttestationRequest:
380 1 : return CommissioningStage::kAttestationVerification;
381 1 : case CommissioningStage::kAttestationVerification:
382 1 : return CommissioningStage::kAttestationRevocationCheck;
383 0 : case CommissioningStage::kAttestationRevocationCheck:
384 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
385 : if (mParams.GetUseJCM().ValueOr(false))
386 : {
387 : return CommissioningStage::kJCMTrustVerification;
388 : }
389 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
390 0 : return CommissioningStage::kSendOpCertSigningRequest;
391 1 : case CommissioningStage::kJCMTrustVerification:
392 1 : return CommissioningStage::kSendOpCertSigningRequest;
393 1 : case CommissioningStage::kSendOpCertSigningRequest:
394 1 : return CommissioningStage::kValidateCSR;
395 1 : case CommissioningStage::kValidateCSR:
396 1 : return CommissioningStage::kGenerateNOCChain;
397 1 : case CommissioningStage::kGenerateNOCChain:
398 1 : return CommissioningStage::kSendTrustedRootCert;
399 1 : case CommissioningStage::kSendTrustedRootCert:
400 1 : return CommissioningStage::kSendNOC;
401 2 : case CommissioningStage::kSendNOC:
402 2 : if (mDeviceCommissioningInfo.requiresTrustedTimeSource && mParams.GetTrustedTimeSource().HasValue())
403 : {
404 0 : return CommissioningStage::kConfigureTrustedTimeSource;
405 : }
406 : else
407 : {
408 2 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureTrustedTimeSource, lastErr);
409 : }
410 2 : case CommissioningStage::kConfigureTrustedTimeSource:
411 2 : if (mNeedIcdRegistration)
412 : {
413 0 : if (mParams.GetICDCheckInNodeId().HasValue() && mParams.GetICDMonitoredSubject().HasValue() &&
414 0 : mParams.GetICDSymmetricKey().HasValue())
415 : {
416 0 : return CommissioningStage::kICDRegistration;
417 : }
418 0 : return CommissioningStage::kICDGetRegistrationInfo;
419 : }
420 2 : return GetNextCommissioningStageInternal(CommissioningStage::kICDRegistration, lastErr);
421 1 : case CommissioningStage::kICDGetRegistrationInfo:
422 1 : return CommissioningStage::kICDRegistration;
423 2 : case CommissioningStage::kICDRegistration:
424 : // TODO(cecille): device attestation casues operational cert provisioning to happen, This should be a separate stage.
425 : // For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the
426 : // operational network because the provisioning of certificates will trigger the device to start operational advertising.
427 2 : if (mNeedsNetworkSetup)
428 : {
429 : // if there is a WiFi or a Thread endpoint, then perform scan
430 0 : if (IsScanNeeded())
431 : {
432 : // Perform Scan (kScanNetworks) and collect credentials (kNeedsNetworkCreds) right before configuring network.
433 : // This order of steps allows the workflow to return to collect credentials again if network enablement fails.
434 :
435 : // TODO: This is broken when we have multiple network commissioning endpoints
436 : // We always end up doing the scan on endpoint 0, even if we have disabled that
437 : // endpoint and are trying to use the secondary network commissioning endpoint.
438 : // We should probably have separate stages for "scan Thread" and "scan Wi-Fi", which
439 : // would allow GetEndpoint() to do the right thing. The IsScanNeeded() check should
440 : // also be changed to check the actual endpoint we are going to try to commission
441 : // here, not just "all the endpoints".
442 : //
443 : // See https://github.com/project-chip/connectedhomeip/issues/40755
444 0 : return CommissioningStage::kScanNetworks;
445 : }
446 0 : ChipLogProgress(Controller, "No NetworkScan enabled or WiFi/Thread endpoint not specified, skipping ScanNetworks");
447 :
448 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
449 : }
450 : else
451 : {
452 2 : SetCASEFailsafeTimerIfNeeded();
453 2 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
454 : {
455 0 : return CommissioningStage::kCleanup;
456 : }
457 2 : return CommissioningStage::kEvictPreviousCaseSessions;
458 : }
459 1 : case CommissioningStage::kScanNetworks:
460 1 : return CommissioningStage::kNeedsNetworkCreds;
461 0 : case CommissioningStage::kNeedsNetworkCreds:
462 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
463 1 : case CommissioningStage::kWiFiNetworkSetup:
464 1 : return CommissioningStage::kFailsafeBeforeWiFiEnable;
465 1 : case CommissioningStage::kThreadNetworkSetup:
466 1 : return CommissioningStage::kFailsafeBeforeThreadEnable;
467 1 : case CommissioningStage::kFailsafeBeforeWiFiEnable:
468 1 : return CommissioningStage::kWiFiNetworkEnable;
469 1 : case CommissioningStage::kFailsafeBeforeThreadEnable:
470 1 : return CommissioningStage::kThreadNetworkEnable;
471 0 : case CommissioningStage::kWiFiNetworkEnable:
472 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
473 : {
474 0 : SetCASEFailsafeTimerIfNeeded();
475 0 : return CommissioningStage::kCleanup;
476 : }
477 : else
478 : {
479 0 : SetCASEFailsafeTimerIfNeeded();
480 0 : return CommissioningStage::kEvictPreviousCaseSessions;
481 : }
482 0 : case CommissioningStage::kThreadNetworkEnable:
483 0 : SetCASEFailsafeTimerIfNeeded();
484 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
485 : {
486 0 : return CommissioningStage::kCleanup;
487 : }
488 0 : return CommissioningStage::kEvictPreviousCaseSessions;
489 1 : case CommissioningStage::kEvictPreviousCaseSessions:
490 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
491 : {
492 : // If there is no secure session, means commissioning has been continued after the unpowered phase was completed
493 : // In such case, move on setup the CASE session over operational network
494 : if (!mCommissioneeDeviceProxy->GetSecureSession().HasValue())
495 : {
496 : return CommissioningStage::kFindOperationalForStayActive;
497 : }
498 :
499 : // If the transport is NFC, move to unpowered phase complete to end the first phase of commissioning
500 : auto transportType =
501 : mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType();
502 : return (transportType == Transport::Type::kNfc && mDeviceCommissioningInfo.general.isCommissioningWithoutPower)
503 : ? CommissioningStage::kUnpoweredPhaseComplete
504 : : CommissioningStage::kFindOperationalForStayActive;
505 : }
506 : #else
507 1 : return CommissioningStage::kFindOperationalForStayActive;
508 : #endif
509 0 : case CommissioningStage::kPrimaryOperationalNetworkFailed:
510 0 : if (mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId)
511 : {
512 0 : return CommissioningStage::kRemoveWiFiNetworkConfig;
513 : }
514 : else
515 : {
516 0 : return CommissioningStage::kRemoveThreadNetworkConfig;
517 : }
518 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
519 : case CommissioningStage::kRemoveThreadNetworkConfig:
520 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
521 1 : case CommissioningStage::kFindOperationalForStayActive:
522 1 : return CommissioningStage::kICDSendStayActive;
523 1 : case CommissioningStage::kICDSendStayActive:
524 1 : return CommissioningStage::kFindOperationalForCommissioningComplete;
525 1 : case CommissioningStage::kFindOperationalForCommissioningComplete:
526 1 : return CommissioningStage::kSendComplete;
527 1 : case CommissioningStage::kSendComplete:
528 1 : return CommissioningStage::kCleanup;
529 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
530 : case CommissioningStage::kUnpoweredPhaseComplete:
531 : return CommissioningStage::kCleanup; // End commissioning (phase 1)
532 : #endif
533 :
534 : // Neither of these have a next stage so return kError;
535 2 : case CommissioningStage::kCleanup:
536 : case CommissioningStage::kError:
537 2 : return CommissioningStage::kError;
538 : }
539 1 : return CommissioningStage::kError;
540 : }
541 :
542 : // No specific actions to take when an error happens since this command can fail and commissioning can still succeed.
543 0 : static void OnFailsafeFailureForCASE(void * context, CHIP_ERROR error)
544 : {
545 0 : ChipLogProgress(Controller, "ExtendFailsafe received failure response: %" CHIP_ERROR_FORMAT, error.Format());
546 0 : }
547 :
548 : // No specific actions to take upon success.
549 : static void
550 0 : OnExtendFailsafeSuccessForCASE(void * context,
551 : const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
552 : {
553 0 : ChipLogProgress(Controller, "ExtendFailsafe received ArmFailSafe response errorCode=%u", to_underlying(data.errorCode));
554 0 : }
555 :
556 2 : void AutoCommissioner::SetCASEFailsafeTimerIfNeeded()
557 : {
558 : // if there is a final fail-safe timer configured then, send it
559 2 : if (mParams.GetCASEFailsafeTimerSeconds().HasValue() && mCommissioneeDeviceProxy != nullptr)
560 : {
561 : // send the command via the PASE session (mCommissioneeDeviceProxy) since the CASE portion of commissioning
562 : // might be done by a different service (ex. PASE is done by a phone app and CASE is done by a Hub).
563 : // Also, we want the CASE failsafe timer to apply for the time it takes the Hub to perform operational discovery,
564 : // CASE establishment, and receipt of the commissioning complete command.
565 : // We know that the mCommissioneeDeviceProxy is still valid at this point since it gets cleared during cleanup
566 : // and SetCASEFailsafeTimerIfNeeded is always called before that stage.
567 : //
568 : // A false return from ExtendArmFailSafe is fine; we don't want to make
569 : // the fail-safe shorter here.
570 0 : mCommissioner->ExtendArmFailSafe(mCommissioneeDeviceProxy, mCommissioner->GetCommissioningStage(),
571 0 : mParams.GetCASEFailsafeTimerSeconds().Value(),
572 0 : GetCommandTimeout(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe),
573 : OnExtendFailsafeSuccessForCASE, OnFailsafeFailureForCASE);
574 : }
575 2 : }
576 :
577 0 : EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage) const
578 : {
579 0 : switch (stage)
580 : {
581 0 : case CommissioningStage::kWiFiNetworkSetup:
582 : case CommissioningStage::kWiFiNetworkEnable:
583 0 : return mDeviceCommissioningInfo.network.wifi.endpoint;
584 0 : case CommissioningStage::kThreadNetworkSetup:
585 : case CommissioningStage::kThreadNetworkEnable:
586 0 : return mDeviceCommissioningInfo.network.thread.endpoint;
587 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
588 : case CommissioningStage::kRemoveThreadNetworkConfig:
589 0 : return kRootEndpointId;
590 0 : default:
591 0 : return kRootEndpointId;
592 : }
593 : }
594 :
595 0 : CHIP_ERROR AutoCommissioner::StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy)
596 : {
597 0 : if (commissioner == nullptr)
598 : {
599 0 : ChipLogError(Controller, "Invalid DeviceCommissioner");
600 0 : return CHIP_ERROR_INVALID_ARGUMENT;
601 : }
602 :
603 : // Proxy is expected to have a valid secure session before starting to commission. However, in case of continuing
604 : // commissioning post unpowered phase, allow proceeding if the stage is evict secure CASE sessions
605 0 : if (proxy == nullptr ||
606 0 : (!proxy->GetSecureSession().HasValue()
607 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
608 : && commissioner->GetCommissioningStage() != CommissioningStage::kEvictPreviousCaseSessions
609 : #endif
610 : ))
611 : {
612 0 : ChipLogError(Controller, "Device proxy secure session error");
613 0 : return CHIP_ERROR_INVALID_ARGUMENT;
614 : }
615 0 : mStopCommissioning = false;
616 0 : mCommissioner = commissioner;
617 0 : mCommissioneeDeviceProxy = proxy;
618 :
619 : // When commissioning is started after unpowered phase, there will be no secure session since the CASE session first needs to be
620 : // setup over operational network. Hence assume transport is UDP in such case.
621 0 : auto transportType = Transport::Type::kUdp;
622 0 : if (mCommissioneeDeviceProxy->GetSecureSession().HasValue())
623 : {
624 : transportType =
625 0 : mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType();
626 : }
627 :
628 0 : mNeedsNetworkSetup = (transportType == Transport::Type::kBle);
629 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
630 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kNfc);
631 : #endif
632 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
633 0 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kWiFiPAF);
634 : #endif
635 0 : CHIP_ERROR err = CHIP_NO_ERROR;
636 0 : CommissioningStage nextStage = GetNextCommissioningStage(commissioner->GetCommissioningStage(), err);
637 :
638 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, GetEndpoint(nextStage),
639 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
640 0 : return CHIP_NO_ERROR;
641 : }
642 :
643 0 : Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(DeviceProxy * device, CommissioningStage stage) const
644 : {
645 : // Network clusters can indicate the time required to connect, so if we are
646 : // connecting, use that time as our "how long it takes to process server
647 : // side" time. Otherwise pick a time that should be enough for the command
648 : // processing: 7s for slow steps that can involve crypto, the default IM
649 : // timeout otherwise.
650 : // TODO: is this a reasonable estimate for the slow-crypto cases?
651 0 : constexpr System::Clock::Timeout kSlowCryptoProcessingTime = System::Clock::Seconds16(7);
652 :
653 : System::Clock::Timeout timeout;
654 0 : switch (stage)
655 : {
656 0 : case CommissioningStage::kWiFiNetworkEnable:
657 0 : ChipLogProgress(Controller, "Setting wifi connection time min = %u",
658 : mDeviceCommissioningInfo.network.wifi.minConnectionTime);
659 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.wifi.minConnectionTime);
660 0 : break;
661 0 : case CommissioningStage::kThreadNetworkEnable:
662 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.thread.minConnectionTime);
663 0 : break;
664 0 : case CommissioningStage::kScanNetworks:
665 : // We're not sure which sort of scan we will do, so just use the larger
666 : // of the timeouts we might have. Note that anything we select here is
667 : // still clamped to be at least kMinimumCommissioningStepTimeout below.
668 0 : timeout = System::Clock::Seconds16(
669 0 : std::max(mDeviceCommissioningInfo.network.wifi.maxScanTime, mDeviceCommissioningInfo.network.thread.maxScanTime));
670 0 : break;
671 0 : case CommissioningStage::kSendNOC:
672 : case CommissioningStage::kSendOpCertSigningRequest:
673 0 : timeout = kSlowCryptoProcessingTime;
674 0 : break;
675 0 : default:
676 0 : timeout = app::kExpectedIMProcessingTime;
677 0 : break;
678 : }
679 :
680 : // Adjust the timeout for our session transport latency, if we have access
681 : // to a session.
682 0 : auto sessionHandle = device->GetSecureSession();
683 0 : if (sessionHandle.HasValue())
684 : {
685 0 : timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout, true /*isFirstMessageOnExchange*/);
686 : }
687 :
688 : // Enforce the spec minimal timeout. Maybe this enforcement should live in
689 : // the DeviceCommissioner?
690 0 : if (timeout < kMinimumCommissioningStepTimeout)
691 : {
692 0 : timeout = kMinimumCommissioningStepTimeout;
693 : }
694 :
695 0 : return MakeOptional(timeout);
696 0 : }
697 :
698 0 : CHIP_ERROR AutoCommissioner::NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac, IdentityProtectionKeySpan ipk,
699 : NodeId adminSubject)
700 : {
701 : // Reuse ICA Cert buffer for temporary store Root Cert.
702 0 : MutableByteSpan rootCert = MutableByteSpan(mICACertBuffer);
703 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcac, rootCert));
704 0 : mParams.SetRootCert(rootCert);
705 :
706 0 : MutableByteSpan noCert = MutableByteSpan(mNOCertBuffer);
707 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(noc, noCert));
708 0 : mParams.SetNoc(noCert);
709 :
710 0 : CommissioningStage nextStage = CommissioningStage::kSendTrustedRootCert;
711 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0,
712 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
713 :
714 : // Trusted root cert has been sent, so we can re-use the icac buffer for the icac.
715 0 : if (!icac.empty())
716 : {
717 0 : MutableByteSpan icaCert = MutableByteSpan(mICACertBuffer);
718 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icac, icaCert));
719 0 : mParams.SetIcac(icaCert);
720 : }
721 : else
722 : {
723 0 : mParams.SetIcac(ByteSpan());
724 : }
725 :
726 0 : mParams.SetIpk(ipk);
727 0 : mParams.SetAdminSubject(adminSubject);
728 :
729 0 : return CHIP_NO_ERROR;
730 : }
731 :
732 0 : void AutoCommissioner::CleanupCommissioning()
733 : {
734 0 : if (IsSecondaryNetworkSupported() && TryingSecondaryNetwork())
735 : {
736 0 : ResetTryingSecondaryNetwork();
737 : }
738 0 : mPAI.Free();
739 0 : mDAC.Free();
740 0 : mCommissioneeDeviceProxy = nullptr;
741 0 : mOperationalDeviceProxy = OperationalDeviceProxy();
742 0 : mDeviceCommissioningInfo = ReadCommissioningInfo();
743 0 : mNeedsDST = false;
744 0 : }
745 :
746 0 : CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report)
747 : {
748 0 : CompletionStatus completionStatus;
749 0 : completionStatus.err = err;
750 0 : if (err != CHIP_NO_ERROR)
751 : {
752 0 : ChipLogError(Controller, "Error on commissioning step '%s': '%s'", StageToString(report.stageCompleted), err.AsString());
753 0 : completionStatus.failedStage = MakeOptional(report.stageCompleted);
754 0 : if (report.Is<AttestationErrorInfo>())
755 : {
756 0 : completionStatus.attestationResult = MakeOptional(report.Get<AttestationErrorInfo>().attestationResult);
757 0 : if ((report.Get<AttestationErrorInfo>().attestationResult ==
758 0 : Credentials::AttestationVerificationResult::kDacProductIdMismatch) ||
759 0 : (report.Get<AttestationErrorInfo>().attestationResult ==
760 : Credentials::AttestationVerificationResult::kDacVendorIdMismatch))
761 : {
762 0 : ChipLogError(Controller,
763 : "Failed device attestation. Device vendor and/or product ID do not match the IDs expected. "
764 : "Verify DAC certificate chain and certification declaration to ensure spec rules followed.");
765 : }
766 :
767 0 : if (report.stageCompleted == CommissioningStage::kAttestationVerification)
768 : {
769 0 : ChipLogError(Controller, "Failed verifying attestation information. Now checking DAC chain revoked status.");
770 : // don't error out until we check for DAC chain revocation status
771 0 : err = CHIP_NO_ERROR;
772 : }
773 : }
774 0 : else if (report.Is<CommissioningErrorInfo>())
775 : {
776 0 : completionStatus.commissioningError = MakeOptional(report.Get<CommissioningErrorInfo>().commissioningError);
777 : }
778 0 : else if (report.Is<NetworkCommissioningStatusInfo>())
779 : {
780 : // This report type is used when an error happens in either NetworkConfig or ConnectNetwork commands
781 : completionStatus.networkCommissioningStatus =
782 0 : MakeOptional(report.Get<NetworkCommissioningStatusInfo>().networkCommissioningStatus);
783 :
784 : // If we are configured to scan networks, then don't error out.
785 : // Instead, allow the app to try another network.
786 0 : if (IsScanNeeded())
787 : {
788 0 : if (completionStatus.err == CHIP_NO_ERROR)
789 : {
790 0 : completionStatus.err = err;
791 : }
792 0 : err = CHIP_NO_ERROR;
793 : // Walk back the completed stage to kScanNetworks.
794 : // This will allow the app to try another network.
795 0 : report.stageCompleted = CommissioningStage::kScanNetworks;
796 : }
797 : }
798 :
799 0 : if (err != CHIP_NO_ERROR && IsSecondaryNetworkSupported() && !TryingSecondaryNetwork() &&
800 0 : completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup &&
801 0 : completionStatus.failedStage.Value() <= kICDSendStayActive)
802 : {
803 : // Primary network failed, disable primary network interface and try secondary network interface.
804 0 : TrySecondaryNetwork();
805 0 : err = CHIP_NO_ERROR;
806 0 : report.stageCompleted = CommissioningStage::kPrimaryOperationalNetworkFailed;
807 : }
808 : }
809 : else
810 : {
811 0 : ChipLogProgress(Controller, "Successfully finished commissioning step '%s'", StageToString(report.stageCompleted));
812 0 : switch (report.stageCompleted)
813 : {
814 0 : case CommissioningStage::kReadCommissioningInfo: {
815 0 : mDeviceCommissioningInfo = report.Get<ReadCommissioningInfo>();
816 :
817 0 : if (!mParams.GetFailsafeTimerSeconds().HasValue() && mDeviceCommissioningInfo.general.recommendedFailsafe > 0)
818 : {
819 0 : mParams.SetFailsafeTimerSeconds(mDeviceCommissioningInfo.general.recommendedFailsafe);
820 : }
821 0 : mParams.SetRemoteVendorId(mDeviceCommissioningInfo.basic.vendorId)
822 0 : .SetRemoteProductId(mDeviceCommissioningInfo.basic.productId)
823 0 : .SetDefaultRegulatoryLocation(mDeviceCommissioningInfo.general.currentRegulatoryLocation)
824 0 : .SetLocationCapability(mDeviceCommissioningInfo.general.locationCapability);
825 : // Don't send DST unless the device says it needs it
826 0 : mNeedsDST = false;
827 :
828 0 : mParams.SetSupportsConcurrentConnection(mDeviceCommissioningInfo.supportsConcurrentConnection);
829 :
830 0 : if (mParams.GetCheckForMatchingFabric())
831 : {
832 0 : NodeId nodeId = mDeviceCommissioningInfo.remoteNodeId;
833 0 : if (nodeId != kUndefinedNodeId)
834 : {
835 0 : mParams.SetRemoteNodeId(nodeId);
836 : }
837 : }
838 :
839 0 : mNeedIcdRegistration = false;
840 0 : if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
841 : {
842 0 : if (mDeviceCommissioningInfo.icd.isLIT && mDeviceCommissioningInfo.icd.checkInProtocolSupport)
843 : {
844 0 : mNeedIcdRegistration = true;
845 0 : ChipLogDetail(Controller, "AutoCommissioner: ICD supports the check-in protocol.");
846 : }
847 0 : else if (mParams.GetICDStayActiveDurationMsec().HasValue())
848 : {
849 0 : ChipLogDetail(Controller, "AutoCommissioner: Clear ICD StayActiveDurationMsec");
850 0 : mParams.ClearICDStayActiveDurationMsec();
851 : }
852 : }
853 :
854 0 : break;
855 : }
856 0 : case CommissioningStage::kConfigureTimeZone:
857 0 : mNeedsDST = report.Get<TimeZoneResponseInfo>().requiresDSTOffsets;
858 0 : break;
859 0 : case CommissioningStage::kSendPAICertificateRequest: {
860 0 : auto reportPAISpan = report.Get<RequestedCertificate>().certificate;
861 :
862 0 : mPAI.CopyFromSpan(reportPAISpan);
863 0 : mParams.SetPAI(mPAI.Span());
864 0 : break;
865 : }
866 0 : case CommissioningStage::kSendDACCertificateRequest: {
867 0 : auto reportDACSpan = report.Get<RequestedCertificate>().certificate;
868 :
869 0 : mDAC.CopyFromSpan(reportDACSpan);
870 0 : mParams.SetDAC(ByteSpan(mDAC.Span()));
871 0 : break;
872 : }
873 0 : case CommissioningStage::kSendAttestationRequest: {
874 0 : auto & elements = report.Get<AttestationResponse>().attestationElements;
875 0 : auto & signature = report.Get<AttestationResponse>().signature;
876 0 : if (elements.size() > sizeof(mAttestationElements))
877 : {
878 0 : ChipLogError(Controller, "AutoCommissioner attestationElements buffer size %u larger than cache size %u",
879 : static_cast<unsigned>(elements.size()), static_cast<unsigned>(sizeof(mAttestationElements)));
880 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
881 : }
882 0 : memcpy(mAttestationElements, elements.data(), elements.size());
883 0 : mAttestationElementsLen = static_cast<uint16_t>(elements.size());
884 0 : mParams.SetAttestationElements(ByteSpan(mAttestationElements, elements.size()));
885 0 : ChipLogDetail(Controller, "AutoCommissioner setting attestationElements buffer size %u/%u",
886 : static_cast<unsigned>(elements.size()),
887 : static_cast<unsigned>(mParams.GetAttestationElements().Value().size()));
888 :
889 0 : if (signature.size() > sizeof(mAttestationSignature))
890 : {
891 0 : ChipLogError(Controller,
892 : "AutoCommissioner attestationSignature buffer size %u larger than "
893 : "cache size %u",
894 : static_cast<unsigned>(signature.size()), static_cast<unsigned>(sizeof(mAttestationSignature)));
895 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
896 : }
897 0 : memcpy(mAttestationSignature, signature.data(), signature.size());
898 0 : mAttestationSignatureLen = static_cast<uint16_t>(signature.size());
899 0 : mParams.SetAttestationSignature(ByteSpan(mAttestationSignature, signature.size()));
900 :
901 : // TODO: Does this need to be done at runtime? Seems like this could be done earlier and we wouldn't need to hold a
902 : // reference to the operational credential delegate here
903 0 : if (mOperationalCredentialsDelegate != nullptr)
904 : {
905 0 : MutableByteSpan nonce(mCSRNonce);
906 0 : ReturnErrorOnFailure(mOperationalCredentialsDelegate->ObtainCsrNonce(nonce));
907 0 : mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));
908 : }
909 0 : break;
910 : }
911 0 : case CommissioningStage::kSendOpCertSigningRequest: {
912 0 : NOCChainGenerationParameters nocParams;
913 0 : nocParams.nocsrElements = report.Get<CSRResponse>().nocsrElements;
914 0 : nocParams.signature = report.Get<CSRResponse>().signature;
915 0 : mParams.SetNOCChainGenerationParameters(nocParams);
916 : }
917 0 : break;
918 0 : case CommissioningStage::kGenerateNOCChain:
919 : // For NOC chain generation, we re-use the buffers. NOCChainGenerated triggers the next stage before
920 : // storing the returned certs, so just return here without triggering the next stage.
921 0 : return NOCChainGenerated(report.Get<NocChain>().noc, report.Get<NocChain>().icac, report.Get<NocChain>().rcac,
922 0 : report.Get<NocChain>().ipk, report.Get<NocChain>().adminSubject);
923 0 : case CommissioningStage::kICDGetRegistrationInfo:
924 : // Noting to do. The ICD registation info is handled elsewhere.
925 0 : break;
926 0 : case CommissioningStage::kICDRegistration:
927 : // Noting to do. DevicePairingDelegate will handle this.
928 0 : break;
929 0 : case CommissioningStage::kFindOperationalForStayActive:
930 : case CommissioningStage::kFindOperationalForCommissioningComplete:
931 0 : mOperationalDeviceProxy = report.Get<OperationalNodeFoundData>().operationalProxy;
932 0 : break;
933 0 : case CommissioningStage::kCleanup:
934 0 : CleanupCommissioning();
935 0 : return CHIP_NO_ERROR;
936 0 : default:
937 0 : break;
938 : }
939 : }
940 :
941 0 : CommissioningStage nextStage = GetNextCommissioningStage(report.stageCompleted, err);
942 0 : if (nextStage == CommissioningStage::kError)
943 : {
944 0 : return CHIP_ERROR_INCORRECT_STATE;
945 : }
946 :
947 : // If GetNextCommissioningStage indicated a failure, don't lose track of
948 : // that. But don't overwrite any existing failures we had hanging
949 : // around.
950 0 : if (completionStatus.err == CHIP_NO_ERROR)
951 : {
952 0 : completionStatus.err = err;
953 : }
954 0 : mParams.SetCompletionStatus(completionStatus);
955 :
956 0 : return PerformStep(nextStage);
957 : }
958 :
959 0 : DeviceProxy * AutoCommissioner::GetDeviceProxyForStep(CommissioningStage nextStage)
960 : {
961 0 : if (nextStage == CommissioningStage::kSendComplete || nextStage == CommissioningStage::kICDSendStayActive ||
962 0 : (nextStage == CommissioningStage::kCleanup && mOperationalDeviceProxy.GetDeviceId() != kUndefinedNodeId))
963 : {
964 0 : return &mOperationalDeviceProxy;
965 : }
966 0 : return mCommissioneeDeviceProxy;
967 : }
968 :
969 0 : CHIP_ERROR AutoCommissioner::PerformStep(CommissioningStage nextStage)
970 : {
971 0 : DeviceProxy * proxy = GetDeviceProxyForStep(nextStage);
972 0 : if (proxy == nullptr)
973 : {
974 0 : ChipLogError(Controller, "Invalid device for commissioning");
975 0 : return CHIP_ERROR_INCORRECT_STATE;
976 : }
977 : // Perform any last minute parameter adjustments before calling the commissioner object
978 0 : switch (nextStage)
979 : {
980 0 : case CommissioningStage::kConfigureTimeZone:
981 0 : if (mParams.GetTimeZone().Value().size() > mDeviceCommissioningInfo.maxTimeZoneSize)
982 : {
983 0 : mParams.SetTimeZone(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>(
984 0 : mParams.GetTimeZone().Value().SubSpan(0, mDeviceCommissioningInfo.maxTimeZoneSize)));
985 : }
986 0 : break;
987 0 : case CommissioningStage::kConfigureDSTOffset:
988 0 : if (mParams.GetDSTOffsets().Value().size() > mDeviceCommissioningInfo.maxDSTSize)
989 : {
990 0 : mParams.SetDSTOffsets(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>(
991 0 : mParams.GetDSTOffsets().Value().SubSpan(0, mDeviceCommissioningInfo.maxDSTSize)));
992 : }
993 0 : break;
994 0 : default:
995 0 : break;
996 : }
997 :
998 0 : mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, GetEndpoint(nextStage),
999 0 : GetCommandTimeout(proxy, nextStage));
1000 0 : return CHIP_NO_ERROR;
1001 : }
1002 :
1003 : } // namespace Controller
1004 : } // namespace chip
|