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