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