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 0 : return CommissioningStage::kScanNetworks;
435 : }
436 0 : ChipLogProgress(Controller, "No NetworkScan enabled or WiFi/Thread endpoint not specified, skipping ScanNetworks");
437 :
438 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
439 : }
440 : else
441 : {
442 2 : SetCASEFailsafeTimerIfNeeded();
443 2 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
444 : {
445 0 : return CommissioningStage::kCleanup;
446 : }
447 2 : return CommissioningStage::kEvictPreviousCaseSessions;
448 : }
449 1 : case CommissioningStage::kScanNetworks:
450 1 : return CommissioningStage::kNeedsNetworkCreds;
451 0 : case CommissioningStage::kNeedsNetworkCreds:
452 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
453 1 : case CommissioningStage::kWiFiNetworkSetup:
454 1 : return CommissioningStage::kFailsafeBeforeWiFiEnable;
455 1 : case CommissioningStage::kThreadNetworkSetup:
456 1 : return CommissioningStage::kFailsafeBeforeThreadEnable;
457 1 : case CommissioningStage::kFailsafeBeforeWiFiEnable:
458 1 : return CommissioningStage::kWiFiNetworkEnable;
459 1 : case CommissioningStage::kFailsafeBeforeThreadEnable:
460 1 : return CommissioningStage::kThreadNetworkEnable;
461 0 : case CommissioningStage::kWiFiNetworkEnable:
462 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
463 : {
464 0 : SetCASEFailsafeTimerIfNeeded();
465 0 : return CommissioningStage::kCleanup;
466 : }
467 : else
468 : {
469 0 : SetCASEFailsafeTimerIfNeeded();
470 0 : return CommissioningStage::kEvictPreviousCaseSessions;
471 : }
472 0 : case CommissioningStage::kThreadNetworkEnable:
473 0 : SetCASEFailsafeTimerIfNeeded();
474 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
475 : {
476 0 : return CommissioningStage::kCleanup;
477 : }
478 0 : return CommissioningStage::kEvictPreviousCaseSessions;
479 1 : case CommissioningStage::kEvictPreviousCaseSessions:
480 1 : return CommissioningStage::kFindOperationalForStayActive;
481 0 : case CommissioningStage::kPrimaryOperationalNetworkFailed:
482 0 : if (mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId)
483 : {
484 0 : return CommissioningStage::kRemoveWiFiNetworkConfig;
485 : }
486 : else
487 : {
488 0 : return CommissioningStage::kRemoveThreadNetworkConfig;
489 : }
490 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
491 : case CommissioningStage::kRemoveThreadNetworkConfig:
492 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
493 1 : case CommissioningStage::kFindOperationalForStayActive:
494 1 : return CommissioningStage::kICDSendStayActive;
495 1 : case CommissioningStage::kICDSendStayActive:
496 1 : return CommissioningStage::kFindOperationalForCommissioningComplete;
497 1 : case CommissioningStage::kFindOperationalForCommissioningComplete:
498 1 : return CommissioningStage::kSendComplete;
499 1 : case CommissioningStage::kSendComplete:
500 1 : return CommissioningStage::kCleanup;
501 :
502 : // Neither of these have a next stage so return kError;
503 2 : case CommissioningStage::kCleanup:
504 : case CommissioningStage::kError:
505 2 : return CommissioningStage::kError;
506 : }
507 1 : return CommissioningStage::kError;
508 : }
509 :
510 : // No specific actions to take when an error happens since this command can fail and commissioning can still succeed.
511 0 : static void OnFailsafeFailureForCASE(void * context, CHIP_ERROR error)
512 : {
513 0 : ChipLogProgress(Controller, "ExtendFailsafe received failure response: %" CHIP_ERROR_FORMAT, error.Format());
514 0 : }
515 :
516 : // No specific actions to take upon success.
517 : static void
518 0 : OnExtendFailsafeSuccessForCASE(void * context,
519 : const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
520 : {
521 0 : ChipLogProgress(Controller, "ExtendFailsafe received ArmFailSafe response errorCode=%u", to_underlying(data.errorCode));
522 0 : }
523 :
524 2 : void AutoCommissioner::SetCASEFailsafeTimerIfNeeded()
525 : {
526 : // if there is a final fail-safe timer configured then, send it
527 2 : if (mParams.GetCASEFailsafeTimerSeconds().HasValue() && mCommissioneeDeviceProxy != nullptr)
528 : {
529 : // send the command via the PASE session (mCommissioneeDeviceProxy) since the CASE portion of commissioning
530 : // might be done by a different service (ex. PASE is done by a phone app and CASE is done by a Hub).
531 : // Also, we want the CASE failsafe timer to apply for the time it takes the Hub to perform operational discovery,
532 : // CASE establishment, and receipt of the commissioning complete command.
533 : // We know that the mCommissioneeDeviceProxy is still valid at this point since it gets cleared during cleanup
534 : // and SetCASEFailsafeTimerIfNeeded is always called before that stage.
535 : //
536 : // A false return from ExtendArmFailSafe is fine; we don't want to make
537 : // the fail-safe shorter here.
538 0 : mCommissioner->ExtendArmFailSafe(mCommissioneeDeviceProxy, mCommissioner->GetCommissioningStage(),
539 0 : mParams.GetCASEFailsafeTimerSeconds().Value(),
540 0 : GetCommandTimeout(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe),
541 : OnExtendFailsafeSuccessForCASE, OnFailsafeFailureForCASE);
542 : }
543 2 : }
544 :
545 0 : EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage) const
546 : {
547 0 : switch (stage)
548 : {
549 0 : case CommissioningStage::kWiFiNetworkSetup:
550 : case CommissioningStage::kWiFiNetworkEnable:
551 0 : return mDeviceCommissioningInfo.network.wifi.endpoint;
552 0 : case CommissioningStage::kThreadNetworkSetup:
553 : case CommissioningStage::kThreadNetworkEnable:
554 0 : return mDeviceCommissioningInfo.network.thread.endpoint;
555 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
556 : case CommissioningStage::kRemoveThreadNetworkConfig:
557 0 : return kRootEndpointId;
558 0 : default:
559 0 : return kRootEndpointId;
560 : }
561 : }
562 :
563 0 : CHIP_ERROR AutoCommissioner::StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy)
564 : {
565 0 : if (commissioner == nullptr)
566 : {
567 0 : ChipLogError(Controller, "Invalid DeviceCommissioner");
568 0 : return CHIP_ERROR_INVALID_ARGUMENT;
569 : }
570 :
571 0 : if (proxy == nullptr || !proxy->GetSecureSession().HasValue())
572 : {
573 0 : ChipLogError(Controller, "Device proxy secure session error");
574 0 : return CHIP_ERROR_INVALID_ARGUMENT;
575 : }
576 0 : mStopCommissioning = false;
577 0 : mCommissioner = commissioner;
578 0 : mCommissioneeDeviceProxy = proxy;
579 :
580 : auto transportType =
581 0 : mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType();
582 0 : mNeedsNetworkSetup = (transportType == Transport::Type::kBle);
583 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
584 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kNfc);
585 : #endif
586 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
587 0 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kWiFiPAF);
588 : #endif
589 0 : CHIP_ERROR err = CHIP_NO_ERROR;
590 0 : CommissioningStage nextStage = GetNextCommissioningStage(CommissioningStage::kSecurePairing, err);
591 :
592 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, GetEndpoint(nextStage),
593 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
594 0 : return CHIP_NO_ERROR;
595 : }
596 :
597 0 : Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(DeviceProxy * device, CommissioningStage stage) const
598 : {
599 : // Network clusters can indicate the time required to connect, so if we are
600 : // connecting, use that time as our "how long it takes to process server
601 : // side" time. Otherwise pick a time that should be enough for the command
602 : // processing: 7s for slow steps that can involve crypto, the default IM
603 : // timeout otherwise.
604 : // TODO: is this a reasonable estimate for the slow-crypto cases?
605 0 : constexpr System::Clock::Timeout kSlowCryptoProcessingTime = System::Clock::Seconds16(7);
606 :
607 : System::Clock::Timeout timeout;
608 0 : switch (stage)
609 : {
610 0 : case CommissioningStage::kWiFiNetworkEnable:
611 0 : ChipLogProgress(Controller, "Setting wifi connection time min = %u",
612 : mDeviceCommissioningInfo.network.wifi.minConnectionTime);
613 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.wifi.minConnectionTime);
614 0 : break;
615 0 : case CommissioningStage::kThreadNetworkEnable:
616 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.thread.minConnectionTime);
617 0 : break;
618 0 : case CommissioningStage::kSendNOC:
619 : case CommissioningStage::kSendOpCertSigningRequest:
620 0 : timeout = kSlowCryptoProcessingTime;
621 0 : break;
622 0 : default:
623 0 : timeout = app::kExpectedIMProcessingTime;
624 0 : break;
625 : }
626 :
627 : // Adjust the timeout for our session transport latency, if we have access
628 : // to a session.
629 0 : auto sessionHandle = device->GetSecureSession();
630 0 : if (sessionHandle.HasValue())
631 : {
632 0 : timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout, true /*isFirstMessageOnExchange*/);
633 : }
634 :
635 : // Enforce the spec minimal timeout. Maybe this enforcement should live in
636 : // the DeviceCommissioner?
637 0 : if (timeout < kMinimumCommissioningStepTimeout)
638 : {
639 0 : timeout = kMinimumCommissioningStepTimeout;
640 : }
641 :
642 0 : return MakeOptional(timeout);
643 0 : }
644 :
645 0 : CHIP_ERROR AutoCommissioner::NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac, IdentityProtectionKeySpan ipk,
646 : NodeId adminSubject)
647 : {
648 : // Reuse ICA Cert buffer for temporary store Root Cert.
649 0 : MutableByteSpan rootCert = MutableByteSpan(mICACertBuffer);
650 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcac, rootCert));
651 0 : mParams.SetRootCert(rootCert);
652 :
653 0 : MutableByteSpan noCert = MutableByteSpan(mNOCertBuffer);
654 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(noc, noCert));
655 0 : mParams.SetNoc(noCert);
656 :
657 0 : CommissioningStage nextStage = CommissioningStage::kSendTrustedRootCert;
658 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0,
659 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
660 :
661 : // Trusted root cert has been sent, so we can re-use the icac buffer for the icac.
662 0 : if (!icac.empty())
663 : {
664 0 : MutableByteSpan icaCert = MutableByteSpan(mICACertBuffer);
665 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icac, icaCert));
666 0 : mParams.SetIcac(icaCert);
667 : }
668 : else
669 : {
670 0 : mParams.SetIcac(ByteSpan());
671 : }
672 :
673 0 : mParams.SetIpk(ipk);
674 0 : mParams.SetAdminSubject(adminSubject);
675 :
676 0 : return CHIP_NO_ERROR;
677 : }
678 :
679 0 : void AutoCommissioner::CleanupCommissioning()
680 : {
681 0 : if (IsSecondaryNetworkSupported() && TryingSecondaryNetwork())
682 : {
683 0 : ResetTryingSecondaryNetwork();
684 : }
685 0 : mPAI.Free();
686 0 : mDAC.Free();
687 0 : mCommissioneeDeviceProxy = nullptr;
688 0 : mOperationalDeviceProxy = OperationalDeviceProxy();
689 0 : mDeviceCommissioningInfo = ReadCommissioningInfo();
690 0 : mNeedsDST = false;
691 0 : }
692 :
693 0 : CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report)
694 : {
695 0 : CompletionStatus completionStatus;
696 0 : completionStatus.err = err;
697 0 : if (err != CHIP_NO_ERROR)
698 : {
699 0 : ChipLogError(Controller, "Error on commissioning step '%s': '%s'", StageToString(report.stageCompleted), err.AsString());
700 0 : completionStatus.failedStage = MakeOptional(report.stageCompleted);
701 0 : if (report.Is<AttestationErrorInfo>())
702 : {
703 0 : completionStatus.attestationResult = MakeOptional(report.Get<AttestationErrorInfo>().attestationResult);
704 0 : if ((report.Get<AttestationErrorInfo>().attestationResult ==
705 0 : Credentials::AttestationVerificationResult::kDacProductIdMismatch) ||
706 0 : (report.Get<AttestationErrorInfo>().attestationResult ==
707 : Credentials::AttestationVerificationResult::kDacVendorIdMismatch))
708 : {
709 0 : ChipLogError(Controller,
710 : "Failed device attestation. Device vendor and/or product ID do not match the IDs expected. "
711 : "Verify DAC certificate chain and certification declaration to ensure spec rules followed.");
712 : }
713 :
714 0 : if (report.stageCompleted == CommissioningStage::kAttestationVerification)
715 : {
716 0 : ChipLogError(Controller, "Failed verifying attestation information. Now checking DAC chain revoked status.");
717 : // don't error out until we check for DAC chain revocation status
718 0 : err = CHIP_NO_ERROR;
719 : }
720 : }
721 0 : else if (report.Is<CommissioningErrorInfo>())
722 : {
723 0 : completionStatus.commissioningError = MakeOptional(report.Get<CommissioningErrorInfo>().commissioningError);
724 : }
725 0 : else if (report.Is<NetworkCommissioningStatusInfo>())
726 : {
727 : // This report type is used when an error happens in either NetworkConfig or ConnectNetwork commands
728 : completionStatus.networkCommissioningStatus =
729 0 : MakeOptional(report.Get<NetworkCommissioningStatusInfo>().networkCommissioningStatus);
730 :
731 : // If we are configured to scan networks, then don't error out.
732 : // Instead, allow the app to try another network.
733 0 : if (IsScanNeeded())
734 : {
735 0 : if (completionStatus.err == CHIP_NO_ERROR)
736 : {
737 0 : completionStatus.err = err;
738 : }
739 0 : err = CHIP_NO_ERROR;
740 : // Walk back the completed stage to kScanNetworks.
741 : // This will allow the app to try another network.
742 0 : report.stageCompleted = CommissioningStage::kScanNetworks;
743 : }
744 : }
745 :
746 0 : if (err != CHIP_NO_ERROR && IsSecondaryNetworkSupported() && !TryingSecondaryNetwork() &&
747 0 : completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup &&
748 0 : completionStatus.failedStage.Value() <= kICDSendStayActive)
749 : {
750 : // Primary network failed, disable primary network interface and try secondary network interface.
751 0 : TrySecondaryNetwork();
752 0 : err = CHIP_NO_ERROR;
753 0 : report.stageCompleted = CommissioningStage::kPrimaryOperationalNetworkFailed;
754 : }
755 : }
756 : else
757 : {
758 0 : ChipLogProgress(Controller, "Successfully finished commissioning step '%s'", StageToString(report.stageCompleted));
759 0 : switch (report.stageCompleted)
760 : {
761 0 : case CommissioningStage::kReadCommissioningInfo: {
762 0 : mDeviceCommissioningInfo = report.Get<ReadCommissioningInfo>();
763 :
764 0 : if (!mParams.GetFailsafeTimerSeconds().HasValue() && mDeviceCommissioningInfo.general.recommendedFailsafe > 0)
765 : {
766 0 : mParams.SetFailsafeTimerSeconds(mDeviceCommissioningInfo.general.recommendedFailsafe);
767 : }
768 0 : mParams.SetRemoteVendorId(mDeviceCommissioningInfo.basic.vendorId)
769 0 : .SetRemoteProductId(mDeviceCommissioningInfo.basic.productId)
770 0 : .SetDefaultRegulatoryLocation(mDeviceCommissioningInfo.general.currentRegulatoryLocation)
771 0 : .SetLocationCapability(mDeviceCommissioningInfo.general.locationCapability);
772 : // Don't send DST unless the device says it needs it
773 0 : mNeedsDST = false;
774 :
775 0 : mParams.SetSupportsConcurrentConnection(mDeviceCommissioningInfo.supportsConcurrentConnection);
776 :
777 0 : if (mParams.GetCheckForMatchingFabric())
778 : {
779 0 : NodeId nodeId = mDeviceCommissioningInfo.remoteNodeId;
780 0 : if (nodeId != kUndefinedNodeId)
781 : {
782 0 : mParams.SetRemoteNodeId(nodeId);
783 : }
784 : }
785 :
786 0 : mNeedIcdRegistration = false;
787 0 : if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
788 : {
789 0 : if (mDeviceCommissioningInfo.icd.isLIT && mDeviceCommissioningInfo.icd.checkInProtocolSupport)
790 : {
791 0 : mNeedIcdRegistration = true;
792 0 : ChipLogDetail(Controller, "AutoCommissioner: ICD supports the check-in protocol.");
793 : }
794 0 : else if (mParams.GetICDStayActiveDurationMsec().HasValue())
795 : {
796 0 : ChipLogDetail(Controller, "AutoCommissioner: Clear ICD StayActiveDurationMsec");
797 0 : mParams.ClearICDStayActiveDurationMsec();
798 : }
799 : }
800 :
801 0 : break;
802 : }
803 0 : case CommissioningStage::kConfigureTimeZone:
804 0 : mNeedsDST = report.Get<TimeZoneResponseInfo>().requiresDSTOffsets;
805 0 : break;
806 0 : case CommissioningStage::kSendPAICertificateRequest: {
807 0 : auto reportPAISpan = report.Get<RequestedCertificate>().certificate;
808 :
809 0 : mPAI.CopyFromSpan(reportPAISpan);
810 0 : mParams.SetPAI(mPAI.Span());
811 0 : break;
812 : }
813 0 : case CommissioningStage::kSendDACCertificateRequest: {
814 0 : auto reportDACSpan = report.Get<RequestedCertificate>().certificate;
815 :
816 0 : mDAC.CopyFromSpan(reportDACSpan);
817 0 : mParams.SetDAC(ByteSpan(mDAC.Span()));
818 0 : break;
819 : }
820 0 : case CommissioningStage::kSendAttestationRequest: {
821 0 : auto & elements = report.Get<AttestationResponse>().attestationElements;
822 0 : auto & signature = report.Get<AttestationResponse>().signature;
823 0 : if (elements.size() > sizeof(mAttestationElements))
824 : {
825 0 : ChipLogError(Controller, "AutoCommissioner attestationElements buffer size %u larger than cache size %u",
826 : static_cast<unsigned>(elements.size()), static_cast<unsigned>(sizeof(mAttestationElements)));
827 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
828 : }
829 0 : memcpy(mAttestationElements, elements.data(), elements.size());
830 0 : mAttestationElementsLen = static_cast<uint16_t>(elements.size());
831 0 : mParams.SetAttestationElements(ByteSpan(mAttestationElements, elements.size()));
832 0 : ChipLogDetail(Controller, "AutoCommissioner setting attestationElements buffer size %u/%u",
833 : static_cast<unsigned>(elements.size()),
834 : static_cast<unsigned>(mParams.GetAttestationElements().Value().size()));
835 :
836 0 : if (signature.size() > sizeof(mAttestationSignature))
837 : {
838 0 : ChipLogError(Controller,
839 : "AutoCommissioner attestationSignature buffer size %u larger than "
840 : "cache size %u",
841 : static_cast<unsigned>(signature.size()), static_cast<unsigned>(sizeof(mAttestationSignature)));
842 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
843 : }
844 0 : memcpy(mAttestationSignature, signature.data(), signature.size());
845 0 : mAttestationSignatureLen = static_cast<uint16_t>(signature.size());
846 0 : mParams.SetAttestationSignature(ByteSpan(mAttestationSignature, signature.size()));
847 :
848 : // TODO: Does this need to be done at runtime? Seems like this could be done earlier and we wouldn't need to hold a
849 : // reference to the operational credential delegate here
850 0 : if (mOperationalCredentialsDelegate != nullptr)
851 : {
852 0 : MutableByteSpan nonce(mCSRNonce);
853 0 : ReturnErrorOnFailure(mOperationalCredentialsDelegate->ObtainCsrNonce(nonce));
854 0 : mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));
855 : }
856 0 : break;
857 : }
858 0 : case CommissioningStage::kSendOpCertSigningRequest: {
859 0 : NOCChainGenerationParameters nocParams;
860 0 : nocParams.nocsrElements = report.Get<CSRResponse>().nocsrElements;
861 0 : nocParams.signature = report.Get<CSRResponse>().signature;
862 0 : mParams.SetNOCChainGenerationParameters(nocParams);
863 : }
864 0 : break;
865 0 : case CommissioningStage::kGenerateNOCChain:
866 : // For NOC chain generation, we re-use the buffers. NOCChainGenerated triggers the next stage before
867 : // storing the returned certs, so just return here without triggering the next stage.
868 0 : return NOCChainGenerated(report.Get<NocChain>().noc, report.Get<NocChain>().icac, report.Get<NocChain>().rcac,
869 0 : report.Get<NocChain>().ipk, report.Get<NocChain>().adminSubject);
870 0 : case CommissioningStage::kICDGetRegistrationInfo:
871 : // Noting to do. The ICD registation info is handled elsewhere.
872 0 : break;
873 0 : case CommissioningStage::kICDRegistration:
874 : // Noting to do. DevicePairingDelegate will handle this.
875 0 : break;
876 0 : case CommissioningStage::kFindOperationalForStayActive:
877 : case CommissioningStage::kFindOperationalForCommissioningComplete:
878 0 : mOperationalDeviceProxy = report.Get<OperationalNodeFoundData>().operationalProxy;
879 0 : break;
880 0 : case CommissioningStage::kCleanup:
881 0 : CleanupCommissioning();
882 0 : return CHIP_NO_ERROR;
883 0 : default:
884 0 : break;
885 : }
886 : }
887 :
888 0 : CommissioningStage nextStage = GetNextCommissioningStage(report.stageCompleted, err);
889 0 : if (nextStage == CommissioningStage::kError)
890 : {
891 0 : return CHIP_ERROR_INCORRECT_STATE;
892 : }
893 :
894 : // If GetNextCommissioningStage indicated a failure, don't lose track of
895 : // that. But don't overwrite any existing failures we had hanging
896 : // around.
897 0 : if (completionStatus.err == CHIP_NO_ERROR)
898 : {
899 0 : completionStatus.err = err;
900 : }
901 0 : mParams.SetCompletionStatus(completionStatus);
902 :
903 0 : return PerformStep(nextStage);
904 : }
905 :
906 0 : DeviceProxy * AutoCommissioner::GetDeviceProxyForStep(CommissioningStage nextStage)
907 : {
908 0 : if (nextStage == CommissioningStage::kSendComplete || nextStage == CommissioningStage::kICDSendStayActive ||
909 0 : (nextStage == CommissioningStage::kCleanup && mOperationalDeviceProxy.GetDeviceId() != kUndefinedNodeId))
910 : {
911 0 : return &mOperationalDeviceProxy;
912 : }
913 0 : return mCommissioneeDeviceProxy;
914 : }
915 :
916 0 : CHIP_ERROR AutoCommissioner::PerformStep(CommissioningStage nextStage)
917 : {
918 0 : DeviceProxy * proxy = GetDeviceProxyForStep(nextStage);
919 0 : if (proxy == nullptr)
920 : {
921 0 : ChipLogError(Controller, "Invalid device for commissioning");
922 0 : return CHIP_ERROR_INCORRECT_STATE;
923 : }
924 : // Perform any last minute parameter adjustments before calling the commissioner object
925 0 : switch (nextStage)
926 : {
927 0 : case CommissioningStage::kConfigureTimeZone:
928 0 : if (mParams.GetTimeZone().Value().size() > mDeviceCommissioningInfo.maxTimeZoneSize)
929 : {
930 0 : mParams.SetTimeZone(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>(
931 0 : mParams.GetTimeZone().Value().SubSpan(0, mDeviceCommissioningInfo.maxTimeZoneSize)));
932 : }
933 0 : break;
934 0 : case CommissioningStage::kConfigureDSTOffset:
935 0 : if (mParams.GetDSTOffsets().Value().size() > mDeviceCommissioningInfo.maxDSTSize)
936 : {
937 0 : mParams.SetDSTOffsets(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>(
938 0 : mParams.GetDSTOffsets().Value().SubSpan(0, mDeviceCommissioningInfo.maxDSTSize)));
939 : }
940 0 : break;
941 0 : default:
942 0 : break;
943 : }
944 :
945 0 : mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, GetEndpoint(nextStage),
946 0 : GetCommandTimeout(proxy, nextStage));
947 0 : return CHIP_NO_ERROR;
948 : }
949 :
950 : } // namespace Controller
951 : } // namespace chip
|