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