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

Generated by: LCOV version 2.0-1