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 69 : AutoCommissioner::AutoCommissioner()
37 : {
38 23 : SetCommissioningParameters(CommissioningParameters());
39 23 : }
40 :
41 23 : 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 42 : CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params)
80 : {
81 : // Our logic below assumes that we can modify mParams without affecting params.
82 42 : 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 42 : mParams = params;
90 42 : mParams.ClearExternalBufferDependentValues();
91 :
92 42 : if (params.GetThreadOperationalDataset().HasValue())
93 : {
94 6 : ByteSpan dataset = params.GetThreadOperationalDataset().Value();
95 6 : 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 5 : memmove(mThreadOperationalDataset, dataset.data(), dataset.size());
101 5 : ChipLogProgress(Controller, "Setting thread operational dataset from parameters");
102 5 : mParams.SetThreadOperationalDataset(ByteSpan(mThreadOperationalDataset, dataset.size()));
103 : }
104 :
105 41 : if (params.GetWiFiCredentials().HasValue())
106 : {
107 6 : WiFiCredentials creds = params.GetWiFiCredentials().Value();
108 11 : if (creds.ssid.size() > CommissioningParameters::kMaxSsidLen ||
109 5 : creds.credentials.size() > CommissioningParameters::kMaxCredentialsLen)
110 : {
111 1 : ChipLogError(Controller, "Wifi credentials are too large");
112 1 : return CHIP_ERROR_INVALID_ARGUMENT;
113 : }
114 5 : memmove(mSsid, creds.ssid.data(), creds.ssid.size());
115 5 : memmove(mCredentials, creds.credentials.data(), creds.credentials.size());
116 5 : ChipLogProgress(Controller, "Setting wifi credentials from parameters");
117 15 : mParams.SetWiFiCredentials(
118 10 : WiFiCredentials(ByteSpan(mSsid, creds.ssid.size()), ByteSpan(mCredentials, creds.credentials.size())));
119 : }
120 :
121 40 : 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 39 : 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 38 : ChipLogProgress(Controller, "Setting attestation nonce to random value");
146 38 : Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce));
147 : }
148 38 : mParams.SetAttestationNonce(ByteSpan(mAttestationNonce, sizeof(mAttestationNonce)));
149 :
150 38 : 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 37 : ChipLogProgress(Controller, "Setting CSR nonce to random value");
159 37 : Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce));
160 : }
161 37 : mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));
162 :
163 37 : 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 37 : 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 37 : 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 37 : 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 37 : auto extraReadPaths = params.GetExtraReadPaths();
225 37 : 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 37 : mExtraReadPaths.Free();
248 : }
249 :
250 37 : 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 (!IsSomeNetworkSupported())
277 : {
278 0 : ChipLogError(Controller, "Network setup is needed, but commissionee does not support any network types we know about");
279 0 : lastErr = CHIP_ERROR_INCORRECT_STATE;
280 0 : return CommissioningStage::kCleanup;
281 : }
282 :
283 : enum class NetworkType
284 : {
285 : kWiFi,
286 : kThread,
287 : };
288 :
289 : NetworkType networkToUse;
290 0 : if (TryingSecondaryNetwork())
291 : {
292 : // Try secondary network interface.
293 0 : networkToUse =
294 0 : mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? NetworkType::kThread : NetworkType::kWiFi;
295 : }
296 0 : else if (IsSecondaryNetworkSupported())
297 : {
298 : // Try primary network interface.
299 0 : TryPrimaryNetwork();
300 0 : networkToUse =
301 0 : mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? NetworkType::kWiFi : NetworkType::kThread;
302 : }
303 0 : else if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
304 : {
305 0 : networkToUse = NetworkType::kWiFi;
306 : }
307 0 : else if (mParams.GetThreadOperationalDataset().HasValue() &&
308 0 : mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
309 : {
310 0 : networkToUse = NetworkType::kThread;
311 : }
312 : else
313 : {
314 : // If we have ended up here, then the commissionee does not support trying two different
315 : // network technologies. If it did, then either we would have credentials for one of them
316 : // and take one of those branches, or we would not have credentials for either one and
317 : // IsSecondaryNetworkSupported() would have tested true.
318 : //
319 : // Also, since we got here, we don't (yet) have credentials for the one network technology
320 : // the commissionee supports. Go ahead and just try to use that one network technology,
321 : // which should be the primary network (on the root endpoint).
322 : //
323 : // Note that we don't call TryPrimaryNetwork() here, because that's only used when there
324 : // will be a secondary network to try. Which in this case there isn't.
325 0 : networkToUse =
326 0 : mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? NetworkType::kWiFi : NetworkType::kThread;
327 : }
328 :
329 0 : if (networkToUse == NetworkType::kWiFi)
330 : {
331 0 : if (mParams.GetWiFiCredentials().HasValue())
332 : {
333 : // Just go ahead and set that up.
334 0 : return CommissioningStage::kWiFiNetworkSetup;
335 : }
336 :
337 : // We need credentials but don't have them. We need to ask for those.
338 0 : return CommissioningStage::kRequestWiFiCredentials;
339 : }
340 :
341 : // networkToUse must be kThread here.
342 0 : if (mParams.GetThreadOperationalDataset().HasValue())
343 : {
344 : // Just go ahead and set that up.
345 0 : return CommissioningStage::kThreadNetworkSetup;
346 : }
347 :
348 : // We need credentials but don't have them. We need to ask for those.
349 0 : return CommissioningStage::kRequestThreadCredentials;
350 : }
351 :
352 39 : CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(CommissioningStage currentStage, CHIP_ERROR & lastErr)
353 : {
354 39 : if (mStopCommissioning)
355 : {
356 1 : return CommissioningStage::kCleanup;
357 : }
358 38 : if (lastErr != CHIP_NO_ERROR)
359 : {
360 1 : return CommissioningStage::kCleanup;
361 : }
362 :
363 37 : switch (currentStage)
364 : {
365 1 : case CommissioningStage::kSecurePairing:
366 1 : return CommissioningStage::kReadCommissioningInfo;
367 2 : case CommissioningStage::kReadCommissioningInfo:
368 2 : if (mDeviceCommissioningInfo.general.breadcrumb > 0)
369 : {
370 : // If the breadcrumb is 0, the failsafe was disarmed.
371 : // We failed on network setup or later, the node failsafe has not been re-armed and the breadcrumb has not been reset.
372 : // Per the spec, we restart from after adding the NOC.
373 1 : return GetNextCommissioningStage(CommissioningStage::kSendNOC, lastErr);
374 : }
375 1 : return CommissioningStage::kArmFailsafe;
376 1 : case CommissioningStage::kArmFailsafe:
377 1 : return CommissioningStage::kConfigRegulatory;
378 1 : case CommissioningStage::kConfigRegulatory:
379 1 : return CommissioningStage::kConfigureTCAcknowledgments;
380 2 : case CommissioningStage::kConfigureTCAcknowledgments:
381 2 : if (mDeviceCommissioningInfo.requiresUTC)
382 : {
383 1 : return CommissioningStage::kConfigureUTCTime;
384 : }
385 : else
386 : {
387 : // Time cluster is not supported, move right to DA
388 1 : return CommissioningStage::kSendPAICertificateRequest;
389 : }
390 0 : case CommissioningStage::kConfigureUTCTime:
391 0 : if (mDeviceCommissioningInfo.requiresTimeZone && mParams.GetTimeZone().HasValue())
392 : {
393 0 : return kConfigureTimeZone;
394 : }
395 : else
396 : {
397 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureTimeZone, lastErr);
398 : }
399 0 : case CommissioningStage::kConfigureTimeZone:
400 0 : if (mNeedsDST && mParams.GetDSTOffsets().HasValue())
401 : {
402 0 : return CommissioningStage::kConfigureDSTOffset;
403 : }
404 : else
405 : {
406 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureDSTOffset, lastErr);
407 : }
408 0 : case CommissioningStage::kConfigureDSTOffset:
409 0 : if (mDeviceCommissioningInfo.requiresDefaultNTP && mParams.GetDefaultNTP().HasValue())
410 : {
411 0 : return CommissioningStage::kConfigureDefaultNTP;
412 : }
413 : else
414 : {
415 0 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureDefaultNTP, lastErr);
416 : }
417 1 : case CommissioningStage::kConfigureDefaultNTP:
418 1 : return CommissioningStage::kSendPAICertificateRequest;
419 1 : case CommissioningStage::kSendPAICertificateRequest:
420 1 : return CommissioningStage::kSendDACCertificateRequest;
421 1 : case CommissioningStage::kSendDACCertificateRequest:
422 1 : return CommissioningStage::kSendAttestationRequest;
423 1 : case CommissioningStage::kSendAttestationRequest:
424 1 : return CommissioningStage::kAttestationVerification;
425 1 : case CommissioningStage::kAttestationVerification:
426 1 : return CommissioningStage::kAttestationRevocationCheck;
427 0 : case CommissioningStage::kAttestationRevocationCheck:
428 : #if CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
429 : if (mParams.GetUseJCM().ValueOr(false))
430 : {
431 : return CommissioningStage::kJCMTrustVerification;
432 : }
433 : #endif // CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC
434 0 : return CommissioningStage::kSendOpCertSigningRequest;
435 1 : case CommissioningStage::kJCMTrustVerification:
436 1 : return CommissioningStage::kSendOpCertSigningRequest;
437 1 : case CommissioningStage::kSendOpCertSigningRequest:
438 1 : return CommissioningStage::kValidateCSR;
439 1 : case CommissioningStage::kValidateCSR:
440 1 : return CommissioningStage::kGenerateNOCChain;
441 1 : case CommissioningStage::kGenerateNOCChain:
442 1 : return CommissioningStage::kSendTrustedRootCert;
443 1 : case CommissioningStage::kSendTrustedRootCert:
444 1 : return CommissioningStage::kSendNOC;
445 2 : case CommissioningStage::kSendNOC:
446 2 : if (mDeviceCommissioningInfo.requiresTrustedTimeSource && mParams.GetTrustedTimeSource().HasValue())
447 : {
448 0 : return CommissioningStage::kConfigureTrustedTimeSource;
449 : }
450 : else
451 : {
452 2 : return GetNextCommissioningStageInternal(CommissioningStage::kConfigureTrustedTimeSource, lastErr);
453 : }
454 2 : case CommissioningStage::kConfigureTrustedTimeSource:
455 2 : if (mNeedIcdRegistration)
456 : {
457 0 : if (mParams.GetICDCheckInNodeId().HasValue() && mParams.GetICDMonitoredSubject().HasValue() &&
458 0 : mParams.GetICDSymmetricKey().HasValue())
459 : {
460 0 : return CommissioningStage::kICDRegistration;
461 : }
462 0 : return CommissioningStage::kICDGetRegistrationInfo;
463 : }
464 2 : return GetNextCommissioningStageInternal(CommissioningStage::kICDRegistration, lastErr);
465 1 : case CommissioningStage::kICDGetRegistrationInfo:
466 1 : return CommissioningStage::kICDRegistration;
467 2 : case CommissioningStage::kICDRegistration:
468 : // TODO(cecille): device attestation casues operational cert provisioning to happen, This should be a separate stage.
469 : // For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the
470 : // operational network because the provisioning of certificates will trigger the device to start operational advertising.
471 2 : if (mNeedsNetworkSetup)
472 : {
473 : // if there is a WiFi or a Thread endpoint, then perform scan
474 0 : if (IsScanNeeded())
475 : {
476 : // Perform Scan (kScanNetworks) and collect credentials (kNeedsNetworkCreds) right before configuring network.
477 : // This order of steps allows the workflow to return to collect credentials again if network enablement fails.
478 :
479 : // TODO: This is broken when we have multiple network commissioning endpoints
480 : // We always end up doing the scan on endpoint 0, even if we have disabled that
481 : // endpoint and are trying to use the secondary network commissioning endpoint.
482 : // We should probably have separate stages for "scan Thread" and "scan Wi-Fi", which
483 : // would allow GetEndpoint() to do the right thing. The IsScanNeeded() check should
484 : // also be changed to check the actual endpoint we are going to try to commission
485 : // here, not just "all the endpoints".
486 : //
487 : // See https://github.com/project-chip/connectedhomeip/issues/40755
488 0 : return CommissioningStage::kScanNetworks;
489 : }
490 0 : ChipLogProgress(Controller, "No NetworkScan enabled or WiFi/Thread endpoint not specified, skipping ScanNetworks");
491 :
492 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
493 : }
494 : else
495 : {
496 2 : SetCASEFailsafeTimerIfNeeded();
497 2 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
498 : {
499 0 : return CommissioningStage::kCleanup;
500 : }
501 2 : return CommissioningStage::kEvictPreviousCaseSessions;
502 : }
503 1 : case CommissioningStage::kScanNetworks:
504 : case CommissioningStage::kRequestWiFiCredentials:
505 : case CommissioningStage::kRequestThreadCredentials:
506 1 : return CommissioningStage::kNeedsNetworkCreds;
507 0 : case CommissioningStage::kNeedsNetworkCreds:
508 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
509 1 : case CommissioningStage::kWiFiNetworkSetup:
510 1 : return CommissioningStage::kFailsafeBeforeWiFiEnable;
511 1 : case CommissioningStage::kThreadNetworkSetup:
512 1 : return CommissioningStage::kFailsafeBeforeThreadEnable;
513 1 : case CommissioningStage::kFailsafeBeforeWiFiEnable:
514 1 : return CommissioningStage::kWiFiNetworkEnable;
515 1 : case CommissioningStage::kFailsafeBeforeThreadEnable:
516 1 : return CommissioningStage::kThreadNetworkEnable;
517 0 : case CommissioningStage::kWiFiNetworkEnable:
518 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
519 : {
520 0 : SetCASEFailsafeTimerIfNeeded();
521 0 : return CommissioningStage::kCleanup;
522 : }
523 : else
524 : {
525 0 : SetCASEFailsafeTimerIfNeeded();
526 0 : return CommissioningStage::kEvictPreviousCaseSessions;
527 : }
528 0 : case CommissioningStage::kThreadNetworkEnable:
529 0 : SetCASEFailsafeTimerIfNeeded();
530 0 : if (mParams.GetSkipCommissioningComplete().ValueOr(false))
531 : {
532 0 : return CommissioningStage::kCleanup;
533 : }
534 0 : return CommissioningStage::kEvictPreviousCaseSessions;
535 1 : case CommissioningStage::kEvictPreviousCaseSessions:
536 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
537 : {
538 : // If there is no secure session, means commissioning has been continued after the unpowered phase was completed
539 : // In such case, move on setup the CASE session over operational network
540 : if (!mCommissioneeDeviceProxy->GetSecureSession().HasValue())
541 : {
542 : return CommissioningStage::kFindOperationalForStayActive;
543 : }
544 :
545 : // If the transport is NFC, move to unpowered phase complete to end the first phase of commissioning
546 : auto transportType =
547 : mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType();
548 : return (transportType == Transport::Type::kNfc && mDeviceCommissioningInfo.general.isCommissioningWithoutPower)
549 : ? CommissioningStage::kUnpoweredPhaseComplete
550 : : CommissioningStage::kFindOperationalForStayActive;
551 : }
552 : #else
553 1 : return CommissioningStage::kFindOperationalForStayActive;
554 : #endif
555 0 : case CommissioningStage::kPrimaryOperationalNetworkFailed:
556 0 : if (mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId)
557 : {
558 0 : return CommissioningStage::kRemoveWiFiNetworkConfig;
559 : }
560 : else
561 : {
562 0 : return CommissioningStage::kRemoveThreadNetworkConfig;
563 : }
564 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
565 : case CommissioningStage::kRemoveThreadNetworkConfig:
566 0 : return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
567 1 : case CommissioningStage::kFindOperationalForStayActive:
568 1 : return CommissioningStage::kICDSendStayActive;
569 1 : case CommissioningStage::kICDSendStayActive:
570 1 : return CommissioningStage::kFindOperationalForCommissioningComplete;
571 1 : case CommissioningStage::kFindOperationalForCommissioningComplete:
572 1 : return CommissioningStage::kSendComplete;
573 1 : case CommissioningStage::kSendComplete:
574 1 : return CommissioningStage::kCleanup;
575 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
576 : case CommissioningStage::kUnpoweredPhaseComplete:
577 : return CommissioningStage::kCleanup; // End commissioning (phase 1)
578 : #endif
579 :
580 : // Neither of these have a next stage so return kError;
581 2 : case CommissioningStage::kCleanup:
582 : case CommissioningStage::kError:
583 2 : return CommissioningStage::kError;
584 : }
585 1 : return CommissioningStage::kError;
586 : }
587 :
588 : // No specific actions to take when an error happens since this command can fail and commissioning can still succeed.
589 0 : static void OnFailsafeFailureForCASE(void * context, CHIP_ERROR error)
590 : {
591 0 : ChipLogProgress(Controller, "ExtendFailsafe received failure response: %" CHIP_ERROR_FORMAT, error.Format());
592 0 : }
593 :
594 : // No specific actions to take upon success.
595 : static void
596 0 : OnExtendFailsafeSuccessForCASE(void * context,
597 : const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
598 : {
599 0 : ChipLogProgress(Controller, "ExtendFailsafe received ArmFailSafe response errorCode=%u", to_underlying(data.errorCode));
600 0 : }
601 :
602 2 : void AutoCommissioner::SetCASEFailsafeTimerIfNeeded()
603 : {
604 : // if there is a final fail-safe timer configured then, send it
605 2 : if (mParams.GetCASEFailsafeTimerSeconds().HasValue() && mCommissioneeDeviceProxy != nullptr)
606 : {
607 : // send the command via the PASE session (mCommissioneeDeviceProxy) since the CASE portion of commissioning
608 : // might be done by a different service (ex. PASE is done by a phone app and CASE is done by a Hub).
609 : // Also, we want the CASE failsafe timer to apply for the time it takes the Hub to perform operational discovery,
610 : // CASE establishment, and receipt of the commissioning complete command.
611 : // We know that the mCommissioneeDeviceProxy is still valid at this point since it gets cleared during cleanup
612 : // and SetCASEFailsafeTimerIfNeeded is always called before that stage.
613 : //
614 : // A false return from ExtendArmFailSafe is fine; we don't want to make
615 : // the fail-safe shorter here.
616 0 : mCommissioner->ExtendArmFailSafe(mCommissioneeDeviceProxy, mCommissioner->GetCommissioningStage(),
617 0 : mParams.GetCASEFailsafeTimerSeconds().Value(),
618 0 : GetCommandTimeout(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe),
619 : OnExtendFailsafeSuccessForCASE, OnFailsafeFailureForCASE);
620 : }
621 2 : }
622 :
623 0 : EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage) const
624 : {
625 0 : switch (stage)
626 : {
627 0 : case CommissioningStage::kRequestWiFiCredentials:
628 : case CommissioningStage::kWiFiNetworkSetup:
629 : case CommissioningStage::kWiFiNetworkEnable:
630 0 : return mDeviceCommissioningInfo.network.wifi.endpoint;
631 0 : case CommissioningStage::kRequestThreadCredentials:
632 : case CommissioningStage::kThreadNetworkSetup:
633 : case CommissioningStage::kThreadNetworkEnable:
634 0 : return mDeviceCommissioningInfo.network.thread.endpoint;
635 0 : case CommissioningStage::kRemoveWiFiNetworkConfig:
636 : case CommissioningStage::kRemoveThreadNetworkConfig:
637 0 : return kRootEndpointId;
638 0 : default:
639 0 : return kRootEndpointId;
640 : }
641 : }
642 :
643 0 : CHIP_ERROR AutoCommissioner::StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy)
644 : {
645 0 : if (commissioner == nullptr)
646 : {
647 0 : ChipLogError(Controller, "Invalid DeviceCommissioner");
648 0 : return CHIP_ERROR_INVALID_ARGUMENT;
649 : }
650 :
651 : // Proxy is expected to have a valid secure session before starting to commission. However, in case of continuing
652 : // commissioning post unpowered phase, allow proceeding if the stage is evict secure CASE sessions
653 0 : if (proxy == nullptr ||
654 0 : (!proxy->GetSecureSession().HasValue()
655 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
656 : && commissioner->GetCommissioningStage() != CommissioningStage::kEvictPreviousCaseSessions
657 : #endif
658 : ))
659 : {
660 0 : ChipLogError(Controller, "Device proxy secure session error");
661 0 : return CHIP_ERROR_INVALID_ARGUMENT;
662 : }
663 0 : mStopCommissioning = false;
664 0 : mCommissioner = commissioner;
665 0 : mCommissioneeDeviceProxy = proxy;
666 :
667 : // When commissioning is started after unpowered phase, there will be no secure session since the CASE session first needs to be
668 : // setup over operational network. Hence assume transport is UDP in such case.
669 0 : auto transportType = Transport::Type::kUdp;
670 0 : if (mCommissioneeDeviceProxy->GetSecureSession().HasValue())
671 : {
672 : transportType =
673 0 : mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType();
674 : }
675 :
676 0 : mNeedsNetworkSetup = (transportType == Transport::Type::kBle);
677 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
678 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kNfc);
679 : #endif
680 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
681 0 : mNeedsNetworkSetup = mNeedsNetworkSetup || (transportType == Transport::Type::kWiFiPAF);
682 : #endif
683 0 : CHIP_ERROR err = CHIP_NO_ERROR;
684 0 : CommissioningStage nextStage = GetNextCommissioningStage(commissioner->GetCommissioningStage(), err);
685 :
686 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, GetEndpoint(nextStage),
687 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
688 0 : return CHIP_NO_ERROR;
689 : }
690 :
691 0 : Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(DeviceProxy * device, CommissioningStage stage) const
692 : {
693 : // Network clusters can indicate the time required to connect, so if we are
694 : // connecting, use that time as our "how long it takes to process server
695 : // side" time. Otherwise pick a time that should be enough for the command
696 : // processing: 7s for slow steps that can involve crypto, the default IM
697 : // timeout otherwise.
698 : // TODO: is this a reasonable estimate for the slow-crypto cases?
699 0 : constexpr System::Clock::Timeout kSlowCryptoProcessingTime = System::Clock::Seconds16(7);
700 :
701 : System::Clock::Timeout timeout;
702 0 : switch (stage)
703 : {
704 0 : case CommissioningStage::kWiFiNetworkEnable:
705 0 : ChipLogProgress(Controller, "Setting wifi connection time min = %u",
706 : mDeviceCommissioningInfo.network.wifi.minConnectionTime);
707 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.wifi.minConnectionTime);
708 0 : break;
709 0 : case CommissioningStage::kThreadNetworkEnable:
710 0 : timeout = System::Clock::Seconds16(mDeviceCommissioningInfo.network.thread.minConnectionTime);
711 0 : break;
712 0 : case CommissioningStage::kScanNetworks:
713 : // We're not sure which sort of scan we will do, so just use the larger
714 : // of the timeouts we might have. Note that anything we select here is
715 : // still clamped to be at least kMinimumCommissioningStepTimeout below.
716 0 : timeout = System::Clock::Seconds16(
717 0 : std::max(mDeviceCommissioningInfo.network.wifi.maxScanTime, mDeviceCommissioningInfo.network.thread.maxScanTime));
718 0 : break;
719 0 : case CommissioningStage::kSendNOC:
720 : case CommissioningStage::kSendOpCertSigningRequest:
721 0 : timeout = kSlowCryptoProcessingTime;
722 0 : break;
723 0 : default:
724 0 : timeout = app::kExpectedIMProcessingTime;
725 0 : break;
726 : }
727 :
728 : // Adjust the timeout for our session transport latency, if we have access
729 : // to a session.
730 0 : auto sessionHandle = device->GetSecureSession();
731 0 : if (sessionHandle.HasValue())
732 : {
733 0 : timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout, true /*isFirstMessageOnExchange*/);
734 : }
735 :
736 : // Enforce the spec minimal timeout. Maybe this enforcement should live in
737 : // the DeviceCommissioner?
738 0 : if (timeout < kMinimumCommissioningStepTimeout)
739 : {
740 0 : timeout = kMinimumCommissioningStepTimeout;
741 : }
742 :
743 0 : return MakeOptional(timeout);
744 0 : }
745 :
746 4 : CHIP_ERROR AutoCommissioner::NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac, IdentityProtectionKeySpan ipk,
747 : NodeId adminSubject)
748 : {
749 : // Reuse ICA Cert buffer for temporary store Root Cert.
750 4 : MutableByteSpan rootCert = MutableByteSpan(mICACertBuffer);
751 4 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcac, rootCert));
752 2 : mParams.SetRootCert(rootCert);
753 :
754 2 : MutableByteSpan noCert = MutableByteSpan(mNOCertBuffer);
755 2 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(noc, noCert));
756 0 : mParams.SetNoc(noCert);
757 :
758 0 : CommissioningStage nextStage = CommissioningStage::kSendTrustedRootCert;
759 0 : mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0,
760 0 : GetCommandTimeout(mCommissioneeDeviceProxy, nextStage));
761 :
762 : // Trusted root cert has been sent, so we can re-use the icac buffer for the icac.
763 0 : if (!icac.empty())
764 : {
765 0 : MutableByteSpan icaCert = MutableByteSpan(mICACertBuffer);
766 0 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icac, icaCert));
767 0 : mParams.SetIcac(icaCert);
768 : }
769 : else
770 : {
771 0 : mParams.SetIcac(ByteSpan());
772 : }
773 :
774 0 : mParams.SetIpk(ipk);
775 0 : mParams.SetAdminSubject(adminSubject);
776 :
777 0 : return CHIP_NO_ERROR;
778 : }
779 :
780 0 : void AutoCommissioner::CleanupCommissioning()
781 : {
782 0 : ResetNetworkAttemptType();
783 0 : mPAI.Free();
784 0 : mDAC.Free();
785 0 : mCommissioneeDeviceProxy = nullptr;
786 0 : mOperationalDeviceProxy = OperationalDeviceProxy();
787 0 : mDeviceCommissioningInfo = ReadCommissioningInfo();
788 0 : mNeedsDST = false;
789 0 : }
790 :
791 0 : CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report)
792 : {
793 0 : CompletionStatus completionStatus;
794 0 : completionStatus.err = err;
795 0 : if (err != CHIP_NO_ERROR)
796 : {
797 0 : ChipLogError(Controller, "Error on commissioning step '%s': '%s'", StageToString(report.stageCompleted), err.AsString());
798 0 : completionStatus.failedStage = MakeOptional(report.stageCompleted);
799 0 : if (report.Is<AttestationErrorInfo>())
800 : {
801 0 : completionStatus.attestationResult = MakeOptional(report.Get<AttestationErrorInfo>().attestationResult);
802 0 : if ((report.Get<AttestationErrorInfo>().attestationResult ==
803 0 : Credentials::AttestationVerificationResult::kDacProductIdMismatch) ||
804 0 : (report.Get<AttestationErrorInfo>().attestationResult ==
805 : Credentials::AttestationVerificationResult::kDacVendorIdMismatch))
806 : {
807 0 : ChipLogError(Controller,
808 : "Failed device attestation. Device vendor and/or product ID do not match the IDs expected. "
809 : "Verify DAC certificate chain and certification declaration to ensure spec rules followed.");
810 : }
811 :
812 0 : if (report.stageCompleted == CommissioningStage::kAttestationVerification)
813 : {
814 0 : ChipLogError(Controller, "Failed verifying attestation information. Now checking DAC chain revoked status.");
815 : // don't error out until we check for DAC chain revocation status
816 0 : err = CHIP_NO_ERROR;
817 : }
818 : }
819 0 : else if (report.Is<CommissioningErrorInfo>())
820 : {
821 0 : completionStatus.commissioningError = MakeOptional(report.Get<CommissioningErrorInfo>().commissioningError);
822 : }
823 0 : else if (report.Is<NetworkCommissioningStatusInfo>())
824 : {
825 : // This report type is used when an error happens in either NetworkConfig or ConnectNetwork commands
826 : completionStatus.networkCommissioningStatus =
827 0 : MakeOptional(report.Get<NetworkCommissioningStatusInfo>().networkCommissioningStatus);
828 :
829 : // If we are configured to scan networks, then don't error out.
830 : // Instead, allow the app to try another network.
831 : //
832 : // TODO: This doesn't actually work, because in order to provide credentials someone
833 : // had to SetWiFiCredentials() or SetThreadOperationalDataset() on our params, so
834 : // IsScanNeeded() will no longer test true for that network technology.
835 0 : if (IsScanNeeded())
836 : {
837 0 : if (completionStatus.err == CHIP_NO_ERROR)
838 : {
839 0 : completionStatus.err = err;
840 : }
841 0 : err = CHIP_NO_ERROR;
842 : // Walk back the completed stage to kScanNetworks.
843 : // This will allow the app to try another network.
844 0 : report.stageCompleted = CommissioningStage::kScanNetworks;
845 : }
846 : }
847 :
848 0 : auto isNetworkFailureStage = [](CommissioningStage stage) {
849 : // If we were somewhere between network setup and and trying to finish up commissioning,
850 : // treat that as a network failure stage; trying the secondary network might work
851 : // better.
852 0 : if (stage >= kWiFiNetworkSetup && stage <= kICDSendStayActive)
853 : {
854 0 : return true;
855 : }
856 :
857 : // If we were trying to request credentials and failed (because our delegate does not
858 : // implement that functionality, for example), treat that as a network failure stage and
859 : // try the other network type.
860 0 : if (stage == kRequestThreadCredentials || stage == kRequestWiFiCredentials || stage == kNeedsNetworkCreds)
861 : {
862 0 : return true;
863 : }
864 :
865 0 : return false;
866 : };
867 :
868 0 : if (err != CHIP_NO_ERROR && TryingPrimaryNetwork() && completionStatus.failedStage.HasValue() &&
869 0 : isNetworkFailureStage(completionStatus.failedStage.Value()))
870 : {
871 : // Primary network failed, disable primary network interface and try secondary network interface.
872 0 : TrySecondaryNetwork();
873 0 : err = CHIP_NO_ERROR;
874 0 : report.stageCompleted = CommissioningStage::kPrimaryOperationalNetworkFailed;
875 : }
876 : }
877 : else
878 : {
879 0 : ChipLogProgress(Controller, "Successfully finished commissioning step '%s'", StageToString(report.stageCompleted));
880 0 : switch (report.stageCompleted)
881 : {
882 0 : case CommissioningStage::kReadCommissioningInfo: {
883 0 : mDeviceCommissioningInfo = report.Get<ReadCommissioningInfo>();
884 :
885 0 : if (!mParams.GetFailsafeTimerSeconds().HasValue() && mDeviceCommissioningInfo.general.recommendedFailsafe > 0)
886 : {
887 0 : mParams.SetFailsafeTimerSeconds(mDeviceCommissioningInfo.general.recommendedFailsafe);
888 : }
889 0 : mParams.SetRemoteVendorId(mDeviceCommissioningInfo.basic.vendorId)
890 0 : .SetRemoteProductId(mDeviceCommissioningInfo.basic.productId)
891 0 : .SetDefaultRegulatoryLocation(mDeviceCommissioningInfo.general.currentRegulatoryLocation)
892 0 : .SetLocationCapability(mDeviceCommissioningInfo.general.locationCapability);
893 : // Don't send DST unless the device says it needs it
894 0 : mNeedsDST = false;
895 :
896 0 : mParams.SetSupportsConcurrentConnection(mDeviceCommissioningInfo.supportsConcurrentConnection);
897 :
898 0 : if (mParams.GetCheckForMatchingFabric())
899 : {
900 0 : NodeId nodeId = mDeviceCommissioningInfo.remoteNodeId;
901 0 : if (nodeId != kUndefinedNodeId)
902 : {
903 0 : mParams.SetRemoteNodeId(nodeId);
904 : }
905 : }
906 :
907 0 : mNeedIcdRegistration = false;
908 0 : if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
909 : {
910 0 : if (mDeviceCommissioningInfo.icd.isLIT && mDeviceCommissioningInfo.icd.checkInProtocolSupport)
911 : {
912 0 : mNeedIcdRegistration = true;
913 0 : ChipLogDetail(Controller, "AutoCommissioner: ICD supports the check-in protocol.");
914 : }
915 0 : else if (mParams.GetICDStayActiveDurationMsec().HasValue())
916 : {
917 0 : ChipLogDetail(Controller, "AutoCommissioner: Clear ICD StayActiveDurationMsec");
918 0 : mParams.ClearICDStayActiveDurationMsec();
919 : }
920 : }
921 :
922 0 : break;
923 : }
924 0 : case CommissioningStage::kConfigureTimeZone:
925 0 : mNeedsDST = report.Get<TimeZoneResponseInfo>().requiresDSTOffsets;
926 0 : break;
927 0 : case CommissioningStage::kSendPAICertificateRequest: {
928 0 : auto reportPAISpan = report.Get<RequestedCertificate>().certificate;
929 :
930 0 : mPAI.CopyFromSpan(reportPAISpan);
931 0 : mParams.SetPAI(mPAI.Span());
932 0 : break;
933 : }
934 0 : case CommissioningStage::kSendDACCertificateRequest: {
935 0 : auto reportDACSpan = report.Get<RequestedCertificate>().certificate;
936 :
937 0 : mDAC.CopyFromSpan(reportDACSpan);
938 0 : mParams.SetDAC(ByteSpan(mDAC.Span()));
939 0 : break;
940 : }
941 0 : case CommissioningStage::kSendAttestationRequest: {
942 0 : auto & elements = report.Get<AttestationResponse>().attestationElements;
943 0 : auto & signature = report.Get<AttestationResponse>().signature;
944 0 : if (elements.size() > sizeof(mAttestationElements))
945 : {
946 0 : ChipLogError(Controller, "AutoCommissioner attestationElements buffer size %u larger than cache size %u",
947 : static_cast<unsigned>(elements.size()), static_cast<unsigned>(sizeof(mAttestationElements)));
948 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
949 : }
950 0 : memcpy(mAttestationElements, elements.data(), elements.size());
951 0 : mAttestationElementsLen = static_cast<uint16_t>(elements.size());
952 0 : mParams.SetAttestationElements(ByteSpan(mAttestationElements, elements.size()));
953 0 : ChipLogDetail(Controller, "AutoCommissioner setting attestationElements buffer size %u/%u",
954 : static_cast<unsigned>(elements.size()),
955 : static_cast<unsigned>(mParams.GetAttestationElements().Value().size()));
956 :
957 0 : if (signature.size() > sizeof(mAttestationSignature))
958 : {
959 0 : ChipLogError(Controller,
960 : "AutoCommissioner attestationSignature buffer size %u larger than "
961 : "cache size %u",
962 : static_cast<unsigned>(signature.size()), static_cast<unsigned>(sizeof(mAttestationSignature)));
963 0 : return CHIP_ERROR_MESSAGE_TOO_LONG;
964 : }
965 0 : memcpy(mAttestationSignature, signature.data(), signature.size());
966 0 : mAttestationSignatureLen = static_cast<uint16_t>(signature.size());
967 0 : mParams.SetAttestationSignature(ByteSpan(mAttestationSignature, signature.size()));
968 :
969 : // TODO: Does this need to be done at runtime? Seems like this could be done earlier and we wouldn't need to hold a
970 : // reference to the operational credential delegate here
971 0 : if (mOperationalCredentialsDelegate != nullptr)
972 : {
973 0 : MutableByteSpan nonce(mCSRNonce);
974 0 : ReturnErrorOnFailure(mOperationalCredentialsDelegate->ObtainCsrNonce(nonce));
975 0 : mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));
976 : }
977 0 : break;
978 : }
979 0 : case CommissioningStage::kSendOpCertSigningRequest: {
980 0 : NOCChainGenerationParameters nocParams;
981 0 : nocParams.nocsrElements = report.Get<CSRResponse>().nocsrElements;
982 0 : nocParams.signature = report.Get<CSRResponse>().signature;
983 0 : mParams.SetNOCChainGenerationParameters(nocParams);
984 : }
985 0 : break;
986 0 : case CommissioningStage::kGenerateNOCChain:
987 : // For NOC chain generation, we re-use the buffers. NOCChainGenerated triggers the next stage before
988 : // storing the returned certs, so just return here without triggering the next stage.
989 0 : return NOCChainGenerated(report.Get<NocChain>().noc, report.Get<NocChain>().icac, report.Get<NocChain>().rcac,
990 0 : report.Get<NocChain>().ipk, report.Get<NocChain>().adminSubject);
991 0 : case CommissioningStage::kICDGetRegistrationInfo:
992 : // Noting to do. The ICD registation info is handled elsewhere.
993 0 : break;
994 0 : case CommissioningStage::kICDRegistration:
995 : // Noting to do. DevicePairingDelegate will handle this.
996 0 : break;
997 0 : case CommissioningStage::kFindOperationalForStayActive:
998 : case CommissioningStage::kFindOperationalForCommissioningComplete:
999 0 : mOperationalDeviceProxy = report.Get<OperationalNodeFoundData>().operationalProxy;
1000 0 : break;
1001 0 : case CommissioningStage::kCleanup:
1002 0 : CleanupCommissioning();
1003 0 : return CHIP_NO_ERROR;
1004 0 : default:
1005 0 : break;
1006 : }
1007 : }
1008 :
1009 0 : CommissioningStage nextStage = GetNextCommissioningStage(report.stageCompleted, err);
1010 0 : if (nextStage == CommissioningStage::kError)
1011 : {
1012 0 : return CHIP_ERROR_INCORRECT_STATE;
1013 : }
1014 :
1015 : // If GetNextCommissioningStage indicated a failure, don't lose track of
1016 : // that. But don't overwrite any existing failures we had hanging
1017 : // around.
1018 0 : if (completionStatus.err == CHIP_NO_ERROR)
1019 : {
1020 0 : completionStatus.err = err;
1021 : }
1022 0 : mParams.SetCompletionStatus(completionStatus);
1023 :
1024 0 : return PerformStep(nextStage);
1025 : }
1026 :
1027 0 : DeviceProxy * AutoCommissioner::GetDeviceProxyForStep(CommissioningStage nextStage)
1028 : {
1029 0 : if (nextStage == CommissioningStage::kSendComplete || nextStage == CommissioningStage::kICDSendStayActive ||
1030 0 : (nextStage == CommissioningStage::kCleanup && mOperationalDeviceProxy.GetDeviceId() != kUndefinedNodeId))
1031 : {
1032 0 : return &mOperationalDeviceProxy;
1033 : }
1034 0 : return mCommissioneeDeviceProxy;
1035 : }
1036 :
1037 0 : CHIP_ERROR AutoCommissioner::PerformStep(CommissioningStage nextStage)
1038 : {
1039 0 : DeviceProxy * proxy = GetDeviceProxyForStep(nextStage);
1040 0 : if (proxy == nullptr)
1041 : {
1042 0 : ChipLogError(Controller, "Invalid device for commissioning");
1043 0 : return CHIP_ERROR_INCORRECT_STATE;
1044 : }
1045 : // Perform any last minute parameter adjustments before calling the commissioner object
1046 0 : switch (nextStage)
1047 : {
1048 0 : case CommissioningStage::kConfigureTimeZone:
1049 0 : if (mParams.GetTimeZone().Value().size() > mDeviceCommissioningInfo.maxTimeZoneSize)
1050 : {
1051 0 : mParams.SetTimeZone(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>(
1052 0 : mParams.GetTimeZone().Value().SubSpan(0, mDeviceCommissioningInfo.maxTimeZoneSize)));
1053 : }
1054 0 : break;
1055 0 : case CommissioningStage::kConfigureDSTOffset:
1056 0 : if (mParams.GetDSTOffsets().Value().size() > mDeviceCommissioningInfo.maxDSTSize)
1057 : {
1058 0 : mParams.SetDSTOffsets(app::DataModel::List<app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type>(
1059 0 : mParams.GetDSTOffsets().Value().SubSpan(0, mDeviceCommissioningInfo.maxDSTSize)));
1060 : }
1061 0 : break;
1062 0 : default:
1063 0 : break;
1064 : }
1065 :
1066 0 : mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, GetEndpoint(nextStage),
1067 0 : GetCommandTimeout(proxy, nextStage));
1068 0 : return CHIP_NO_ERROR;
1069 : }
1070 :
1071 : } // namespace Controller
1072 : } // namespace chip
|