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