LCOV - code coverage report
Current view: top level - controller - CommissioningWindowOpener.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 0 172 0.0 %
Date: 2024-02-15 08:20:41 Functions: 0 15 0.0 %

          Line data    Source code
       1             : /*
       2             :  *    Copyright (c) 2022 Project CHIP Authors
       3             :  *    All rights reserved.
       4             :  *
       5             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6             :  *    you may not use this file except in compliance with the License.
       7             :  *    You may obtain a copy of the License at
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  *    Unless required by applicable law or agreed to in writing, software
      12             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  *    See the License for the specific language governing permissions and
      15             :  *    limitations under the License.
      16             :  */
      17             : 
      18             : #include <app-common/zap-generated/cluster-objects.h>
      19             : #include <controller/CommissioningWindowOpener.h>
      20             : #include <lib/core/CHIPSafeCasts.h>
      21             : #include <lib/support/CHIPMem.h>
      22             : #include <protocols/secure_channel/PASESession.h>
      23             : #include <setup_payload/ManualSetupPayloadGenerator.h>
      24             : #include <setup_payload/QRCodeSetupPayloadGenerator.h>
      25             : 
      26             : using namespace chip::app::Clusters;
      27             : using namespace chip::System::Clock;
      28             : 
      29             : namespace {
      30             : // TODO: What should the timed invoke timeout here be?
      31             : constexpr uint16_t kTimedInvokeTimeoutMs = 10000;
      32             : } // anonymous namespace
      33             : 
      34             : namespace chip {
      35             : namespace Controller {
      36             : 
      37           0 : CHIP_ERROR CommissioningWindowOpener::OpenBasicCommissioningWindow(NodeId deviceId, Seconds16 timeout,
      38             :                                                                    Callback::Callback<OnOpenBasicCommissioningWindow> * callback)
      39             : {
      40           0 :     VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
      41           0 :     mSetupPayload = SetupPayload();
      42             : 
      43             :     // Basic commissioning does not use the setup payload.
      44             : 
      45           0 :     mCommissioningWindowOption        = CommissioningWindowOption::kOriginalSetupCode;
      46           0 :     mBasicCommissioningWindowCallback = callback;
      47           0 :     mCommissioningWindowCallback      = nullptr;
      48           0 :     mNodeId                           = deviceId;
      49           0 :     mCommissioningWindowTimeout       = timeout;
      50             : 
      51           0 :     mNextStep = Step::kOpenCommissioningWindow;
      52           0 :     return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
      53             : }
      54             : 
      55           0 : CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, Seconds16 timeout, uint32_t iteration,
      56             :                                                               uint16_t discriminator, Optional<uint32_t> setupPIN,
      57             :                                                               Optional<ByteSpan> salt,
      58             :                                                               Callback::Callback<OnOpenCommissioningWindow> * callback,
      59             :                                                               SetupPayload & payload, bool readVIDPIDAttributes)
      60             : {
      61           0 :     VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
      62             : 
      63           0 :     VerifyOrReturnError(kSpake2p_Min_PBKDF_Iterations <= iteration && iteration <= kSpake2p_Max_PBKDF_Iterations,
      64             :                         CHIP_ERROR_INVALID_ARGUMENT);
      65           0 :     VerifyOrReturnError(
      66             :         !salt.HasValue() ||
      67             :             (salt.Value().size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.Value().size() <= kSpake2p_Max_PBKDF_Salt_Length),
      68             :         CHIP_ERROR_INVALID_ARGUMENT);
      69             : 
      70           0 :     mSetupPayload = SetupPayload();
      71             : 
      72           0 :     if (setupPIN.HasValue())
      73             :     {
      74           0 :         if (!SetupPayload::IsValidSetupPIN(setupPIN.Value()))
      75             :         {
      76           0 :             return CHIP_ERROR_INVALID_ARGUMENT;
      77             :         }
      78             : 
      79           0 :         mCommissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN;
      80           0 :         mSetupPayload.setUpPINCode = setupPIN.Value();
      81             :     }
      82             :     else
      83             :     {
      84           0 :         mCommissioningWindowOption = CommissioningWindowOption::kTokenWithRandomPIN;
      85             :     }
      86             : 
      87           0 :     if (salt.HasValue())
      88             :     {
      89           0 :         memcpy(mPBKDFSaltBuffer, salt.Value().data(), salt.Value().size());
      90           0 :         mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer, salt.Value().size());
      91             :     }
      92             :     else
      93             :     {
      94           0 :         ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFSaltBuffer, sizeof(mPBKDFSaltBuffer)));
      95           0 :         mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer);
      96             :     }
      97             : 
      98           0 :     mSetupPayload.version = 0;
      99           0 :     mSetupPayload.discriminator.SetLongValue(discriminator);
     100           0 :     mSetupPayload.rendezvousInformation.SetValue(RendezvousInformationFlag::kOnNetwork);
     101             : 
     102           0 :     mCommissioningWindowCallback      = callback;
     103           0 :     mBasicCommissioningWindowCallback = nullptr;
     104           0 :     mNodeId                           = deviceId;
     105           0 :     mCommissioningWindowTimeout       = timeout;
     106           0 :     mPBKDFIterations                  = iteration;
     107             : 
     108           0 :     bool randomSetupPIN = !setupPIN.HasValue();
     109           0 :     ReturnErrorOnFailure(
     110             :         PASESession::GeneratePASEVerifier(mVerifier, mPBKDFIterations, mPBKDFSalt, randomSetupPIN, mSetupPayload.setUpPINCode));
     111             : 
     112           0 :     payload = mSetupPayload;
     113             : 
     114           0 :     if (readVIDPIDAttributes)
     115             :     {
     116           0 :         mNextStep = Step::kReadVID;
     117             :     }
     118             :     else
     119             :     {
     120           0 :         mNextStep = Step::kOpenCommissioningWindow;
     121             :     }
     122             : 
     123           0 :     return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
     124             : }
     125             : 
     126           0 : CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr,
     127             :                                                                       const SessionHandle & sessionHandle)
     128             : {
     129           0 :     ChipLogProgress(Controller, "OpenCommissioningWindow for device ID 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId));
     130             : 
     131           0 :     constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0;
     132             : 
     133           0 :     ClusterBase cluster(exchangeMgr, sessionHandle, kAdministratorCommissioningClusterEndpoint);
     134             : 
     135           0 :     if (mCommissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode)
     136             :     {
     137             :         chip::Spake2pVerifierSerialized serializedVerifier;
     138           0 :         MutableByteSpan serializedVerifierSpan(serializedVerifier);
     139           0 :         ReturnErrorOnFailure(mVerifier.Serialize(serializedVerifierSpan));
     140             : 
     141           0 :         AdministratorCommissioning::Commands::OpenCommissioningWindow::Type request;
     142           0 :         request.commissioningTimeout = mCommissioningWindowTimeout.count();
     143           0 :         request.PAKEPasscodeVerifier = serializedVerifierSpan;
     144           0 :         request.discriminator        = mSetupPayload.discriminator.GetLongValue();
     145           0 :         request.iterations           = mPBKDFIterations;
     146           0 :         request.salt                 = mPBKDFSalt;
     147             : 
     148           0 :         ReturnErrorOnFailure(cluster.InvokeCommand(request, this, OnOpenCommissioningWindowSuccess,
     149             :                                                    OnOpenCommissioningWindowFailure, MakeOptional(kTimedInvokeTimeoutMs)));
     150             :     }
     151             :     else
     152             :     {
     153           0 :         AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Type request;
     154           0 :         request.commissioningTimeout = mCommissioningWindowTimeout.count();
     155           0 :         ReturnErrorOnFailure(cluster.InvokeCommand(request, this, OnOpenCommissioningWindowSuccess,
     156             :                                                    OnOpenCommissioningWindowFailure, MakeOptional(kTimedInvokeTimeoutMs)));
     157             :     }
     158             : 
     159           0 :     return CHIP_NO_ERROR;
     160           0 : }
     161             : 
     162           0 : void CommissioningWindowOpener::OnPIDReadResponse(void * context, uint16_t value)
     163             : {
     164           0 :     ChipLogProgress(Controller, "Received PID for the device. Value %d", value);
     165           0 :     auto * self                   = static_cast<CommissioningWindowOpener *>(context);
     166           0 :     self->mSetupPayload.productID = value;
     167             : 
     168           0 :     self->mNextStep = Step::kOpenCommissioningWindow;
     169             : 
     170           0 :     CHIP_ERROR err = self->mController->GetConnectedDevice(self->mNodeId, &self->mDeviceConnected, &self->mDeviceConnectionFailure);
     171           0 :     if (err != CHIP_NO_ERROR)
     172             :     {
     173           0 :         OnOpenCommissioningWindowFailure(context, err);
     174             :     }
     175           0 : }
     176             : 
     177           0 : void CommissioningWindowOpener::OnVIDReadResponse(void * context, VendorId value)
     178             : {
     179           0 :     ChipLogProgress(Controller, "Received VID for the device. Value %d", to_underlying(value));
     180             : 
     181           0 :     auto * self = static_cast<CommissioningWindowOpener *>(context);
     182             : 
     183           0 :     self->mSetupPayload.vendorID = value;
     184             : 
     185           0 :     self->mNextStep = Step::kReadPID;
     186           0 :     CHIP_ERROR err = self->mController->GetConnectedDevice(self->mNodeId, &self->mDeviceConnected, &self->mDeviceConnectionFailure);
     187           0 :     if (err != CHIP_NO_ERROR)
     188             :     {
     189           0 :         OnOpenCommissioningWindowFailure(context, err);
     190             :     }
     191           0 : }
     192             : 
     193           0 : void CommissioningWindowOpener::OnVIDPIDReadFailureResponse(void * context, CHIP_ERROR error)
     194             : {
     195           0 :     ChipLogProgress(Controller, "Failed to read VID/PID for the device. error %" CHIP_ERROR_FORMAT, error.Format());
     196           0 :     OnOpenCommissioningWindowFailure(context, error);
     197           0 : }
     198             : 
     199           0 : void CommissioningWindowOpener::OnOpenCommissioningWindowSuccess(void * context, const chip::app::DataModel::NullObjectType &)
     200             : {
     201           0 :     ChipLogProgress(Controller, "Successfully opened pairing window on the device");
     202           0 :     auto * self     = static_cast<CommissioningWindowOpener *>(context);
     203           0 :     self->mNextStep = Step::kAcceptCommissioningStart;
     204           0 :     if (self->mCommissioningWindowCallback != nullptr)
     205             :     {
     206             :         char payloadBuffer[QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
     207             : 
     208           0 :         MutableCharSpan manualCode(payloadBuffer);
     209           0 :         CHIP_ERROR err = ManualSetupPayloadGenerator(self->mSetupPayload).payloadDecimalStringRepresentation(manualCode);
     210           0 :         if (err == CHIP_NO_ERROR)
     211             :         {
     212           0 :             ChipLogProgress(Controller, "Manual pairing code: [%s]", payloadBuffer);
     213             :         }
     214             :         else
     215             :         {
     216           0 :             ChipLogError(Controller, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, err.Format());
     217             :         }
     218             : 
     219           0 :         MutableCharSpan QRCode(payloadBuffer);
     220           0 :         err = QRCodeBasicSetupPayloadGenerator(self->mSetupPayload).payloadBase38Representation(QRCode);
     221           0 :         if (err == CHIP_NO_ERROR)
     222             :         {
     223           0 :             ChipLogProgress(Controller, "SetupQRCode: [%s]", payloadBuffer);
     224             :         }
     225             :         else
     226             :         {
     227           0 :             ChipLogError(Controller, "Unable to generate QR code for setup payload: %" CHIP_ERROR_FORMAT, err.Format());
     228             :         }
     229             : 
     230           0 :         self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, CHIP_NO_ERROR,
     231           0 :                                                   self->mSetupPayload);
     232             :         // Don't touch `self` anymore; it might have been destroyed by the
     233             :         // callee.
     234             :     }
     235           0 :     else if (self->mBasicCommissioningWindowCallback != nullptr)
     236             :     {
     237           0 :         self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId,
     238             :                                                        CHIP_NO_ERROR);
     239             :         // Don't touch `self` anymore; it might have been destroyed by the
     240             :         // callee.
     241             :     }
     242           0 : }
     243             : 
     244           0 : void CommissioningWindowOpener::OnOpenCommissioningWindowFailure(void * context, CHIP_ERROR error)
     245             : {
     246           0 :     ChipLogError(Controller, "Failed to open pairing window on the device. Status %" CHIP_ERROR_FORMAT, error.Format());
     247           0 :     auto * self     = static_cast<CommissioningWindowOpener *>(context);
     248           0 :     self->mNextStep = Step::kAcceptCommissioningStart;
     249           0 :     if (self->mCommissioningWindowCallback != nullptr)
     250             :     {
     251           0 :         self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, error,
     252           0 :                                                   SetupPayload());
     253             :     }
     254           0 :     else if (self->mBasicCommissioningWindowCallback != nullptr)
     255             :     {
     256           0 :         self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId, error);
     257             :     }
     258           0 : }
     259             : 
     260           0 : void CommissioningWindowOpener::OnDeviceConnectedCallback(void * context, Messaging::ExchangeManager & exchangeMgr,
     261             :                                                           const SessionHandle & sessionHandle)
     262             : {
     263           0 :     auto * self = static_cast<CommissioningWindowOpener *>(context);
     264             : 
     265             : #if CHIP_ERROR_LOGGING
     266           0 :     const char * messageIfError = nullptr;
     267             : #endif // CHIP_ERROR_LOGGING
     268           0 :     CHIP_ERROR err = CHIP_NO_ERROR;
     269             : 
     270           0 :     switch (self->mNextStep)
     271             :     {
     272           0 :     case Step::kReadVID: {
     273           0 :         ClusterBase cluster(exchangeMgr, sessionHandle, kRootEndpointId);
     274           0 :         err = cluster.ReadAttribute<BasicInformation::Attributes::VendorID::TypeInfo>(context, OnVIDReadResponse,
     275             :                                                                                       OnVIDPIDReadFailureResponse);
     276             : #if CHIP_ERROR_LOGGING
     277           0 :         messageIfError = "Could not read VID for opening commissioning window";
     278             : #endif // CHIP_ERROR_LOGGING
     279           0 :         break;
     280           0 :     }
     281           0 :     case Step::kReadPID: {
     282           0 :         ClusterBase cluster(exchangeMgr, sessionHandle, kRootEndpointId);
     283           0 :         err = cluster.ReadAttribute<BasicInformation::Attributes::ProductID::TypeInfo>(context, OnPIDReadResponse,
     284             :                                                                                        OnVIDPIDReadFailureResponse);
     285             : #if CHIP_ERROR_LOGGING
     286           0 :         messageIfError = "Could not read PID for opening commissioning window";
     287             : #endif // CHIP_ERROR_LOGGING
     288           0 :         break;
     289           0 :     }
     290           0 :     case Step::kOpenCommissioningWindow: {
     291           0 :         err = self->OpenCommissioningWindowInternal(exchangeMgr, sessionHandle);
     292             : #if CHIP_ERROR_LOGGING
     293           0 :         messageIfError = "Could not connect to open commissioning window";
     294             : #endif // CHIP_ERROR_LOGGING
     295           0 :         break;
     296             :     }
     297           0 :     case Step::kAcceptCommissioningStart: {
     298           0 :         err = CHIP_ERROR_INCORRECT_STATE;
     299             : #if CHIP_ERROR_LOGGING
     300           0 :         messageIfError = "Just got a connected device; how can we be done?";
     301             : #endif // CHIP_ERROR_LOGGING
     302           0 :         break;
     303             :     }
     304             :     }
     305             : 
     306           0 :     if (err != CHIP_NO_ERROR)
     307             :     {
     308           0 :         ChipLogError(Controller, "%s: %" CHIP_ERROR_FORMAT, messageIfError, err.Format());
     309           0 :         OnOpenCommissioningWindowFailure(context, err);
     310             :     }
     311           0 : }
     312             : 
     313           0 : void CommissioningWindowOpener::OnDeviceConnectionFailureCallback(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
     314             : {
     315           0 :     OnOpenCommissioningWindowFailure(context, error);
     316           0 : }
     317             : 
     318           0 : AutoCommissioningWindowOpener::AutoCommissioningWindowOpener(DeviceController * controller) :
     319           0 :     CommissioningWindowOpener(controller), mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
     320           0 :     mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
     321           0 : {}
     322             : 
     323           0 : CHIP_ERROR AutoCommissioningWindowOpener::OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
     324             :                                                                        Seconds16 timeout)
     325             : {
     326             :     // Not using Platform::New because we want to keep our constructor private.
     327           0 :     auto * opener = new (std::nothrow) AutoCommissioningWindowOpener(controller);
     328           0 :     if (opener == nullptr)
     329             :     {
     330           0 :         return CHIP_ERROR_NO_MEMORY;
     331             :     }
     332             : 
     333           0 :     CHIP_ERROR err = opener->CommissioningWindowOpener::OpenBasicCommissioningWindow(
     334             :         deviceId, timeout, &opener->mOnOpenBasicCommissioningWindowCallback);
     335           0 :     if (err != CHIP_NO_ERROR)
     336             :     {
     337           0 :         delete opener;
     338             :     }
     339             :     // Else will clean up when the callback is called.
     340           0 :     return err;
     341             : }
     342             : 
     343           0 : CHIP_ERROR AutoCommissioningWindowOpener::OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, Seconds16 timeout,
     344             :                                                                   uint32_t iteration, uint16_t discriminator,
     345             :                                                                   Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
     346             :                                                                   SetupPayload & payload, bool readVIDPIDAttributes)
     347             : {
     348             :     // Not using Platform::New because we want to keep our constructor private.
     349           0 :     auto * opener = new (std::nothrow) AutoCommissioningWindowOpener(controller);
     350           0 :     if (opener == nullptr)
     351             :     {
     352           0 :         return CHIP_ERROR_NO_MEMORY;
     353             :     }
     354             : 
     355           0 :     CHIP_ERROR err = opener->CommissioningWindowOpener::OpenCommissioningWindow(
     356             :         deviceId, timeout, iteration, discriminator, setupPIN, salt, &opener->mOnOpenCommissioningWindowCallback, payload,
     357             :         readVIDPIDAttributes);
     358           0 :     if (err != CHIP_NO_ERROR)
     359             :     {
     360           0 :         delete opener;
     361             :     }
     362             :     // Else will clean up when the callback is called.
     363           0 :     return err;
     364             : }
     365             : 
     366           0 : void AutoCommissioningWindowOpener::OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status,
     367             :                                                                       chip::SetupPayload payload)
     368             : {
     369           0 :     auto * self = static_cast<AutoCommissioningWindowOpener *>(context);
     370           0 :     delete self;
     371           0 : }
     372           0 : void AutoCommissioningWindowOpener::OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status)
     373             : {
     374           0 :     auto * self = static_cast<AutoCommissioningWindowOpener *>(context);
     375           0 :     delete self;
     376           0 : }
     377             : 
     378             : } // namespace Controller
     379             : } // namespace chip

Generated by: LCOV version 1.14