Matter SDK Coverage Report
Current view: top level - controller - AutoCommissioner.cpp (source / functions) Coverage Total Hit
Test: SHA:704d97f9c619242ad76fcf75aeabc67802fa72d4 Lines: 34.8 % 523 182
Test Date: 2026-05-18 07:37:39 Functions: 40.9 % 22 9

            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(&params != &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
        

Generated by: LCOV version 2.0-1