Matter SDK Coverage Report
Current view: top level - setup_payload - SetupPayload.cpp (source / functions) Coverage Total Hit
Test: SHA:df778ebac263a6e42dae264499a97423d126704b Lines: 96.4 % 166 160
Test Date: 2025-06-01 07:10:48 Functions: 100.0 % 22 22

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020 Project CHIP Authors
       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              : /**
      19              :  *    @file
      20              :  *      The implementation of the Setup Payload. Currently only needed to
      21              :  *      verify the validity of a Setup Payload
      22              :  */
      23              : 
      24              : #include "SetupPayload.h"
      25              : 
      26              : #include <crypto/CHIPCryptoPAL.h>
      27              : #include <lib/core/CHIPCore.h>
      28              : #include <lib/core/CHIPVendorIdentifiers.hpp>
      29              : #include <lib/core/TLV.h>
      30              : #include <lib/core/TLVData.h>
      31              : #include <lib/core/TLVUtilities.h>
      32              : #include <lib/support/CodeUtils.h>
      33              : #include <setup_payload/ManualSetupPayloadParser.h>
      34              : #include <setup_payload/QRCodeSetupPayloadParser.h>
      35              : #include <utility>
      36              : 
      37              : namespace chip {
      38              : 
      39              : // Check the Setup Payload for validity
      40              : //
      41              : // `vendor_id` and `product_id` are allowed all of uint16_t
      42           61 : bool PayloadContents::isValidQRCodePayload(ValidationMode mode) const
      43              : {
      44              :     // 3-bit value specifying the QR code payload version.
      45           61 :     VerifyOrReturnValue(version < (1 << kVersionFieldLengthInBits), false);
      46              : 
      47           60 :     VerifyOrReturnValue(static_cast<uint8_t>(commissioningFlow) < (1 << kCommissioningFlowFieldLengthInBits), false);
      48              : 
      49              :     // Device Commissioning Flow
      50              :     // Even in ValidationMode::kConsume we can only handle modes that we understand.
      51              :     // 0: Standard commissioning flow: such a device, when uncommissioned, always enters commissioning mode upon power-up, subject
      52              :     // to the rules in [ref_Announcement_Commencement]. 1: User-intent commissioning flow: user action required to enter
      53              :     // commissioning mode. 2: Custom commissioning flow: interaction with a vendor-specified means is needed before commissioning.
      54              :     // 3: Reserved
      55           59 :     VerifyOrReturnValue(commissioningFlow == CommissioningFlow::kStandard ||
      56              :                             commissioningFlow == CommissioningFlow::kUserActionRequired ||
      57              :                             commissioningFlow == CommissioningFlow::kCustom,
      58              :                         false);
      59              : 
      60              :     // General discriminator validity is enforced by the SetupDiscriminator class, but it can't be short for QR a code.
      61           59 :     VerifyOrReturnValue(!discriminator.IsShortDiscriminator(), false);
      62              : 
      63              :     // RendevouzInformation must be present for a QR code.
      64           57 :     VerifyOrReturnValue(rendezvousInformation.HasValue(), false);
      65           51 :     if (mode == ValidationMode::kProduce)
      66              :     {
      67           49 :         chip::RendezvousInformationFlags valid(RendezvousInformationFlag::kBLE, RendezvousInformationFlag::kOnNetwork,
      68              :                                                RendezvousInformationFlag::kSoftAP, RendezvousInformationFlag::kWiFiPAF);
      69           49 :         VerifyOrReturnValue(rendezvousInformation.Value().HasOnly(valid), false);
      70              :     }
      71              : 
      72           50 :     return CheckPayloadCommonConstraints();
      73              : }
      74              : 
      75           14 : bool PayloadContents::isValidManualCode(ValidationMode mode) const
      76              : {
      77              :     // No additional constraints apply to Manual Pairing Codes.
      78              :     // (If the payload has a long discriminator it will be converted automatically.)
      79           14 :     return CheckPayloadCommonConstraints();
      80              : }
      81              : 
      82           67 : bool PayloadContents::IsValidSetupPIN(uint32_t setupPIN)
      83              : {
      84              :     // SHALL be restricted to the values 0x0000001 to 0x5F5E0FE (00000001 to 99999998 in decimal), excluding the invalid Passcode
      85              :     // values.
      86           67 :     if (setupPIN == kSetupPINCodeUndefinedValue || setupPIN > kSetupPINCodeMaximumValue || setupPIN == 11111111 ||
      87           61 :         setupPIN == 22222222 || setupPIN == 33333333 || setupPIN == 44444444 || setupPIN == 55555555 || setupPIN == 66666666 ||
      88           61 :         setupPIN == 77777777 || setupPIN == 88888888 || setupPIN == 12345678 || setupPIN == 87654321)
      89              :     {
      90            6 :         return false;
      91              :     }
      92              : 
      93           61 :     return true;
      94              : }
      95              : 
      96           64 : bool PayloadContents::CheckPayloadCommonConstraints() const
      97              : {
      98              :     // Validation rules in this method apply to all validation modes.
      99              : 
     100              :     // Even in ValidationMode::kConsume we don't understand how to handle any payload version other than 0.
     101           64 :     VerifyOrReturnValue(version == 0, false);
     102              : 
     103           64 :     VerifyOrReturnValue(IsValidSetupPIN(setUpPINCode), false);
     104              : 
     105              :     // VendorID must be unspecified (0) or in valid range expected.
     106           58 :     VerifyOrReturnValue((vendorID == VendorId::Unspecified) || IsVendorIdValidOperationally(vendorID), false);
     107              : 
     108              :     // A value of 0x0000 SHALL NOT be assigned to a product since Product ID = 0x0000 is used for these specific cases:
     109              :     //  * To announce an anonymized Product ID as part of device discovery
     110              :     //  * To indicate an OTA software update file applies to multiple Product IDs equally.
     111              :     //  * To avoid confusion when presenting the Onboarding Payload for ECM with multiple nodes
     112              :     // In these special cases the vendorID must be 0 (Unspecified)
     113           58 :     VerifyOrReturnValue(productID != 0 || vendorID == VendorId::Unspecified, false);
     114              : 
     115           58 :     return true;
     116              : }
     117              : 
     118           37 : bool PayloadContents::operator==(const PayloadContents & input) const
     119              : {
     120           37 :     return (this->version == input.version && this->vendorID == input.vendorID && this->productID == input.productID &&
     121           74 :             this->commissioningFlow == input.commissioningFlow && this->rendezvousInformation == input.rendezvousInformation &&
     122          111 :             this->discriminator == input.discriminator && this->setUpPINCode == input.setUpPINCode);
     123              : }
     124              : 
     125           11 : CHIP_ERROR SetupPayload::addOptionalVendorData(uint8_t tag, std::string data)
     126              : {
     127           11 :     OptionalQRCodeInfo info;
     128           11 :     info.tag  = tag;
     129           11 :     info.type = optionalQRCodeInfoTypeString;
     130           11 :     info.data = std::move(data);
     131              : 
     132           11 :     return addOptionalVendorData(info);
     133           11 : }
     134              : 
     135            6 : CHIP_ERROR SetupPayload::addOptionalVendorData(uint8_t tag, int32_t data)
     136              : {
     137            6 :     OptionalQRCodeInfo info;
     138            6 :     info.tag   = tag;
     139            6 :     info.type  = optionalQRCodeInfoTypeInt32;
     140            6 :     info.int32 = data;
     141              : 
     142            6 :     return addOptionalVendorData(info);
     143            6 : }
     144              : 
     145           80 : std::vector<OptionalQRCodeInfo> SetupPayload::getAllOptionalVendorData() const
     146              : {
     147           80 :     std::vector<OptionalQRCodeInfo> returnedOptionalInfo;
     148          102 :     for (auto & entry : optionalVendorData)
     149              :     {
     150           22 :         returnedOptionalInfo.push_back(entry.second);
     151              :     }
     152           80 :     return returnedOptionalInfo;
     153              : }
     154              : 
     155            4 : CHIP_ERROR SetupPayload::removeOptionalVendorData(uint8_t tag)
     156              : {
     157            4 :     VerifyOrReturnError(optionalVendorData.find(tag) != optionalVendorData.end(), CHIP_ERROR_KEY_NOT_FOUND);
     158            2 :     optionalVendorData.erase(tag);
     159              : 
     160            2 :     return CHIP_NO_ERROR;
     161              : }
     162              : 
     163            7 : CHIP_ERROR SetupPayload::addSerialNumber(std::string serialNumber)
     164              : {
     165            7 :     OptionalQRCodeInfoExtension info;
     166            7 :     info.tag  = kSerialNumberTag;
     167            7 :     info.type = optionalQRCodeInfoTypeString;
     168            7 :     info.data = std::move(serialNumber);
     169              : 
     170            7 :     return addOptionalExtensionData(info);
     171            7 : }
     172              : 
     173            2 : CHIP_ERROR SetupPayload::addSerialNumber(uint32_t serialNumber)
     174              : {
     175            2 :     OptionalQRCodeInfoExtension info;
     176            2 :     info.tag    = kSerialNumberTag;
     177            2 :     info.type   = optionalQRCodeInfoTypeUInt32;
     178            2 :     info.uint32 = serialNumber;
     179              : 
     180            2 :     return addOptionalExtensionData(info);
     181            2 : }
     182              : 
     183            4 : CHIP_ERROR SetupPayload::getSerialNumber(std::string & outSerialNumber) const
     184              : {
     185            4 :     CHIP_ERROR err = CHIP_NO_ERROR;
     186            4 :     OptionalQRCodeInfoExtension info;
     187            4 :     ReturnErrorOnFailure(getOptionalExtensionData(kSerialNumberTag, info));
     188              : 
     189            2 :     switch (info.type)
     190              :     {
     191            1 :     case (optionalQRCodeInfoTypeString):
     192            1 :         outSerialNumber = info.data;
     193            1 :         break;
     194            1 :     case (optionalQRCodeInfoTypeUInt32):
     195            1 :         outSerialNumber = std::to_string(info.uint32);
     196            1 :         break;
     197            0 :     default:
     198            0 :         err = CHIP_ERROR_INVALID_ARGUMENT;
     199            0 :         break;
     200              :     }
     201              : 
     202            2 :     return err;
     203            4 : }
     204              : 
     205            3 : CHIP_ERROR SetupPayload::removeSerialNumber()
     206              : {
     207            3 :     VerifyOrReturnError(optionalExtensionData.find(kSerialNumberTag) != optionalExtensionData.end(), CHIP_ERROR_KEY_NOT_FOUND);
     208            1 :     optionalExtensionData.erase(kSerialNumberTag);
     209              : 
     210            1 :     return CHIP_NO_ERROR;
     211              : }
     212              : 
     213            1 : CHIP_ERROR SetupPayload::generateRandomSetupPin(uint32_t & setupPINCode)
     214              : {
     215            1 :     uint8_t retries          = 0;
     216            1 :     const uint8_t maxRetries = 10;
     217              : 
     218              :     do
     219              :     {
     220            1 :         ReturnErrorOnFailure(Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(&setupPINCode), sizeof(setupPINCode)));
     221              : 
     222              :         // Passcodes shall be restricted to the values 00000001 to 99999998 in decimal, see 5.1.1.6
     223              :         // TODO: Consider revising this method to ensure uniform distribution of setup PIN codes
     224            1 :         setupPINCode = (setupPINCode % kSetupPINCodeMaximumValue) + 1;
     225              : 
     226              :         // Make sure that the Generated Setup Pin code is not one of the invalid passcodes/pin codes defined in the
     227              :         // specification.
     228            1 :         if (IsValidSetupPIN(setupPINCode))
     229              :         {
     230            1 :             return CHIP_NO_ERROR;
     231              :         }
     232              : 
     233            0 :         retries++;
     234              :         // We got pretty unlucky with the random number generator, Just try again.
     235              :         // This shouldn't take many retries assuming DRBG_get_bytes is not broken.
     236            0 :     } while (retries < maxRetries);
     237              : 
     238            0 :     return CHIP_ERROR_INTERNAL;
     239              : }
     240              : 
     241           21 : CHIP_ERROR SetupPayload::addOptionalVendorData(const OptionalQRCodeInfo & info)
     242              : {
     243           21 :     VerifyOrReturnError(IsVendorTag(info.tag), CHIP_ERROR_INVALID_ARGUMENT);
     244           19 :     optionalVendorData[info.tag] = info;
     245              : 
     246           19 :     return CHIP_NO_ERROR;
     247              : }
     248              : 
     249           12 : CHIP_ERROR SetupPayload::addOptionalExtensionData(const OptionalQRCodeInfoExtension & info)
     250              : {
     251           12 :     VerifyOrReturnError(IsCommonTag(info.tag), CHIP_ERROR_INVALID_ARGUMENT);
     252           12 :     optionalExtensionData[info.tag] = info;
     253              : 
     254           12 :     return CHIP_NO_ERROR;
     255              : }
     256              : 
     257            4 : CHIP_ERROR SetupPayload::getOptionalVendorData(uint8_t tag, OptionalQRCodeInfo & info) const
     258              : {
     259            4 :     const auto it = optionalVendorData.find(tag);
     260            4 :     VerifyOrReturnError(it != optionalVendorData.end(), CHIP_ERROR_KEY_NOT_FOUND);
     261            4 :     info = it->second;
     262              : 
     263            4 :     return CHIP_NO_ERROR;
     264              : }
     265              : 
     266            7 : CHIP_ERROR SetupPayload::getOptionalExtensionData(uint8_t tag, OptionalQRCodeInfoExtension & info) const
     267              : {
     268            7 :     const auto it = optionalExtensionData.find(tag);
     269            7 :     VerifyOrReturnError(it != optionalExtensionData.end(), CHIP_ERROR_KEY_NOT_FOUND);
     270            5 :     info = it->second;
     271            5 :     return CHIP_NO_ERROR;
     272              : }
     273              : 
     274            3 : optionalQRCodeInfoType SetupPayload::getNumericTypeFor(uint8_t tag) const
     275              : {
     276            3 :     optionalQRCodeInfoType elemType = optionalQRCodeInfoTypeUnknown;
     277              : 
     278            3 :     if (IsVendorTag(tag))
     279              :     {
     280            2 :         elemType = optionalQRCodeInfoTypeInt32;
     281              :     }
     282            1 :     else if (tag == kSerialNumberTag)
     283              :     {
     284            1 :         elemType = optionalQRCodeInfoTypeUInt32;
     285              :     }
     286              : 
     287            3 :     return elemType;
     288              : }
     289              : 
     290           73 : std::vector<OptionalQRCodeInfoExtension> SetupPayload::getAllOptionalExtensionData() const
     291              : {
     292           73 :     std::vector<OptionalQRCodeInfoExtension> returnedOptionalInfo;
     293           88 :     for (auto & entry : optionalExtensionData)
     294              :     {
     295           15 :         returnedOptionalInfo.push_back(entry.second);
     296              :     }
     297           73 :     return returnedOptionalInfo;
     298              : }
     299              : 
     300           35 : bool SetupPayload::operator==(const SetupPayload & input) const
     301              : {
     302           35 :     std::vector<OptionalQRCodeInfo> inputOptionalVendorData;
     303           35 :     std::vector<OptionalQRCodeInfoExtension> inputOptionalExtensionData;
     304              : 
     305           35 :     VerifyOrReturnError(PayloadContents::operator==(input), false);
     306              : 
     307           34 :     inputOptionalVendorData = input.getAllOptionalVendorData();
     308           34 :     VerifyOrReturnError(optionalVendorData.size() == inputOptionalVendorData.size(), false);
     309              : 
     310           38 :     for (const OptionalQRCodeInfo & inputInfo : inputOptionalVendorData)
     311              :     {
     312            4 :         OptionalQRCodeInfo info;
     313            4 :         CHIP_ERROR err = getOptionalVendorData(inputInfo.tag, info);
     314            4 :         VerifyOrReturnError(err == CHIP_NO_ERROR, false);
     315            4 :         VerifyOrReturnError(inputInfo.type == info.type, false);
     316            4 :         VerifyOrReturnError(inputInfo.data == info.data, false);
     317            4 :         VerifyOrReturnError(inputInfo.int32 == info.int32, false);
     318            4 :     }
     319              : 
     320           34 :     inputOptionalExtensionData = input.getAllOptionalExtensionData();
     321           34 :     VerifyOrReturnError(optionalExtensionData.size() == inputOptionalExtensionData.size(), false);
     322              : 
     323           37 :     for (const OptionalQRCodeInfoExtension & inputInfo : inputOptionalExtensionData)
     324              :     {
     325            3 :         OptionalQRCodeInfoExtension info;
     326            3 :         CHIP_ERROR err = getOptionalExtensionData(inputInfo.tag, info);
     327            3 :         VerifyOrReturnError(err == CHIP_NO_ERROR, false);
     328            3 :         VerifyOrReturnError(inputInfo.type == info.type, false);
     329            3 :         VerifyOrReturnError(inputInfo.data == info.data, false);
     330            3 :         VerifyOrReturnError(inputInfo.int32 == info.int32, false);
     331            3 :         VerifyOrReturnError(inputInfo.int64 == info.int64, false);
     332            3 :         VerifyOrReturnError(inputInfo.uint32 == info.uint32, false);
     333            3 :         VerifyOrReturnError(inputInfo.uint64 == info.uint64, false);
     334            3 :     }
     335              : 
     336           34 :     return true;
     337           35 : }
     338              : 
     339            9 : CHIP_ERROR SetupPayload::FromStringRepresentation(std::string stringRepresentation, std::vector<SetupPayload> & outPayloads)
     340              : {
     341              :     // We're going to assume that in practice all these allocations are small
     342              :     // enough that allocation failure will not happen.  If that ever turns out
     343              :     // to not be the case, we may need to figure out how to handle that.
     344              : 
     345              :     // std::string::starts_with is C++20, sadly.
     346            9 :     bool isQRCode = (stringRepresentation.rfind(kQRCodePrefix, 0) == 0);
     347            9 :     if (!isQRCode)
     348              :     {
     349            4 :         outPayloads.clear();
     350            4 :         auto & payload = outPayloads.emplace_back();
     351            4 :         ReturnErrorOnFailure(ManualSetupPayloadParser(stringRepresentation).populatePayload(payload));
     352            2 :         VerifyOrReturnError(payload.isValidManualCode(), CHIP_ERROR_INVALID_ARGUMENT);
     353            1 :         return CHIP_NO_ERROR;
     354              :     }
     355              : 
     356            5 :     ReturnErrorOnFailure(QRCodeSetupPayloadParser(stringRepresentation).populatePayloads(outPayloads));
     357              : 
     358           11 :     for (auto & entry : outPayloads)
     359              :     {
     360            9 :         VerifyOrReturnError(entry.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
     361              :     }
     362            2 :     return CHIP_NO_ERROR;
     363              : }
     364              : 
     365              : } // namespace chip
        

Generated by: LCOV version 2.0-1