Matter SDK Coverage Report
Current view: top level - setup_payload - QRCodeSetupPayloadParser.cpp (source / functions) Coverage Total Hit
Test: SHA:df778ebac263a6e42dae264499a97423d126704b Lines: 87.6 % 194 170
Test Date: 2025-06-01 07:10:48 Functions: 87.5 % 16 14

            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              :  *      This file describes a QRCode Setup Payload parser based on the
      21              :  *      CHIP specification.
      22              :  */
      23              : 
      24              : #include "QRCodeSetupPayloadParser.h"
      25              : #include "Base38Decode.h"
      26              : 
      27              : #include <algorithm>
      28              : #include <string.h>
      29              : #include <vector>
      30              : 
      31              : #include <lib/core/CHIPCore.h>
      32              : #include <lib/core/CHIPError.h>
      33              : #include <lib/core/TLVData.h>
      34              : #include <lib/core/TLVUtilities.h>
      35              : #include <lib/support/CodeUtils.h>
      36              : #include <lib/support/SafeInt.h>
      37              : #include <lib/support/ScopedBuffer.h>
      38              : #include <protocols/Protocols.h>
      39              : 
      40              : namespace chip {
      41              : 
      42              : // Populate numberOfBits into dest from buf starting at startIndex
      43          408 : static CHIP_ERROR readBits(std::vector<uint8_t> buf, size_t & index, uint64_t & dest, size_t numberOfBitsToRead)
      44              : {
      45          408 :     dest = 0;
      46          408 :     if (index + numberOfBitsToRead > buf.size() * 8 || numberOfBitsToRead > sizeof(uint64_t) * 8)
      47              :     {
      48            0 :         ChipLogError(SetupPayload, "Error parsing QR code. startIndex %u numberOfBitsToLoad %u buf_len %u ",
      49              :                      static_cast<unsigned int>(index), static_cast<unsigned int>(numberOfBitsToRead),
      50              :                      static_cast<unsigned int>(buf.size()));
      51            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
      52              :     }
      53              : 
      54          408 :     size_t currentIndex = index;
      55         4704 :     for (size_t bitsRead = 0; bitsRead < numberOfBitsToRead; bitsRead++)
      56              :     {
      57         4296 :         if (buf[currentIndex / 8] & (1 << (currentIndex % 8)))
      58              :         {
      59          652 :             dest |= (1 << bitsRead);
      60              :         }
      61         4296 :         currentIndex++;
      62              :     }
      63          408 :     index += numberOfBitsToRead;
      64          408 :     return CHIP_NO_ERROR;
      65              : }
      66              : 
      67            5 : static CHIP_ERROR openTLVContainer(TLV::ContiguousBufferTLVReader & reader, TLV::TLVType type, TLV::Tag tag,
      68              :                                    TLV::ContiguousBufferTLVReader & containerReader)
      69              : {
      70            5 :     VerifyOrReturnError(reader.GetType() == type, CHIP_ERROR_INVALID_ARGUMENT);
      71            5 :     VerifyOrReturnError(reader.GetTag() == tag, CHIP_ERROR_INVALID_ARGUMENT);
      72            5 :     VerifyOrReturnError(reader.GetLength() == 0, CHIP_ERROR_INVALID_ARGUMENT);
      73              : 
      74            5 :     ReturnErrorOnFailure(reader.OpenContainer(containerReader));
      75              : 
      76            5 :     VerifyOrReturnError(containerReader.GetContainerType() == type, CHIP_ERROR_INVALID_ARGUMENT);
      77            5 :     return CHIP_NO_ERROR;
      78              : }
      79              : 
      80            4 : static CHIP_ERROR retrieveOptionalInfoString(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfo & info)
      81              : {
      82            4 :     Span<const char> data;
      83            4 :     ReturnErrorOnFailure(reader.GetStringView(data));
      84              : 
      85            4 :     info.type = optionalQRCodeInfoTypeString;
      86            4 :     info.data = std::string(data.data(), data.size());
      87              : 
      88            4 :     return CHIP_NO_ERROR;
      89              : }
      90              : 
      91            2 : static CHIP_ERROR retrieveOptionalInfoInt32(TLV::TLVReader & reader, OptionalQRCodeInfo & info)
      92              : {
      93              :     int32_t value;
      94            2 :     ReturnErrorOnFailure(reader.Get(value));
      95              : 
      96            2 :     info.type  = optionalQRCodeInfoTypeInt32;
      97            2 :     info.int32 = value;
      98              : 
      99            2 :     return CHIP_NO_ERROR;
     100              : }
     101              : 
     102            0 : static CHIP_ERROR retrieveOptionalInfoInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
     103              : {
     104              :     int64_t value;
     105            0 :     ReturnErrorOnFailure(reader.Get(value));
     106              : 
     107            0 :     info.type  = optionalQRCodeInfoTypeInt64;
     108            0 :     info.int64 = value;
     109              : 
     110            0 :     return CHIP_NO_ERROR;
     111              : }
     112              : 
     113            1 : static CHIP_ERROR retrieveOptionalInfoUInt32(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
     114              : {
     115              :     uint32_t value;
     116            1 :     ReturnErrorOnFailure(reader.Get(value));
     117              : 
     118            1 :     info.type   = optionalQRCodeInfoTypeUInt32;
     119            1 :     info.uint32 = value;
     120              : 
     121            1 :     return CHIP_NO_ERROR;
     122              : }
     123              : 
     124            0 : static CHIP_ERROR retrieveOptionalInfoUInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
     125              : {
     126              :     uint64_t value;
     127            0 :     ReturnErrorOnFailure(reader.Get(value));
     128              : 
     129            0 :     info.type   = optionalQRCodeInfoTypeUInt64;
     130            0 :     info.uint64 = value;
     131              : 
     132            0 :     return CHIP_NO_ERROR;
     133              : }
     134              : 
     135            6 : static CHIP_ERROR retrieveOptionalInfo(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfo & info,
     136              :                                        optionalQRCodeInfoType type)
     137              : {
     138            6 :     CHIP_ERROR err = CHIP_NO_ERROR;
     139              : 
     140            6 :     if (type == optionalQRCodeInfoTypeString)
     141              :     {
     142            4 :         err = retrieveOptionalInfoString(reader, info);
     143              :     }
     144            2 :     else if (type == optionalQRCodeInfoTypeInt32)
     145              :     {
     146            2 :         err = retrieveOptionalInfoInt32(reader, info);
     147              :     }
     148              :     else
     149              :     {
     150            0 :         err = CHIP_ERROR_INVALID_ARGUMENT;
     151              :     }
     152              : 
     153            6 :     return err;
     154              : }
     155              : 
     156            3 : static CHIP_ERROR retrieveOptionalInfo(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfoExtension & info,
     157              :                                        optionalQRCodeInfoType type)
     158              : {
     159            3 :     CHIP_ERROR err = CHIP_NO_ERROR;
     160              : 
     161            3 :     if (type == optionalQRCodeInfoTypeString || type == optionalQRCodeInfoTypeInt32)
     162              :     {
     163            2 :         err = retrieveOptionalInfo(reader, static_cast<OptionalQRCodeInfo &>(info), type);
     164              :     }
     165            1 :     else if (type == optionalQRCodeInfoTypeInt64)
     166              :     {
     167            0 :         err = retrieveOptionalInfoInt64(reader, info);
     168              :     }
     169            1 :     else if (type == optionalQRCodeInfoTypeUInt32)
     170              :     {
     171            1 :         err = retrieveOptionalInfoUInt32(reader, info);
     172              :     }
     173            0 :     else if (type == optionalQRCodeInfoTypeUInt64)
     174              :     {
     175            0 :         err = retrieveOptionalInfoUInt64(reader, info);
     176              :     }
     177              :     else
     178              :     {
     179            0 :         err = CHIP_ERROR_INVALID_ARGUMENT;
     180              :     }
     181              : 
     182            3 :     return err;
     183              : }
     184              : 
     185            5 : CHIP_ERROR QRCodeSetupPayloadParser::retrieveOptionalInfos(SetupPayload & outPayload, TLV::ContiguousBufferTLVReader & reader)
     186              : {
     187            5 :     CHIP_ERROR err = CHIP_NO_ERROR;
     188           12 :     while (err == CHIP_NO_ERROR)
     189              :     {
     190            7 :         const TLV::TLVType type = reader.GetType();
     191            7 :         if (type != TLV::kTLVType_UTF8String && type != TLV::kTLVType_SignedInteger && type != TLV::kTLVType_UnsignedInteger)
     192              :         {
     193            0 :             err = reader.Next();
     194            0 :             continue;
     195              :         }
     196              : 
     197            7 :         TLV::Tag tag = reader.GetTag();
     198            7 :         VerifyOrReturnError(TLV::IsContextTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
     199            7 :         const uint8_t tagNumber = static_cast<uint8_t>(TLV::TagNumFromTag(tag));
     200              : 
     201            7 :         optionalQRCodeInfoType elemType = optionalQRCodeInfoTypeUnknown;
     202            7 :         if (type == TLV::kTLVType_UTF8String)
     203              :         {
     204            4 :             elemType = optionalQRCodeInfoTypeString;
     205              :         }
     206            7 :         if (type == TLV::kTLVType_SignedInteger || type == TLV::kTLVType_UnsignedInteger)
     207              :         {
     208            3 :             elemType = outPayload.getNumericTypeFor(tagNumber);
     209              :         }
     210              : 
     211            7 :         if (SetupPayload::IsCommonTag(tagNumber))
     212              :         {
     213            3 :             OptionalQRCodeInfoExtension info;
     214            3 :             info.tag = tagNumber;
     215            3 :             ReturnErrorOnFailure(retrieveOptionalInfo(reader, info, elemType));
     216              : 
     217            3 :             ReturnErrorOnFailure(outPayload.addOptionalExtensionData(info));
     218            3 :         }
     219              :         else
     220              :         {
     221            4 :             OptionalQRCodeInfo info;
     222            4 :             info.tag = tagNumber;
     223            4 :             ReturnErrorOnFailure(retrieveOptionalInfo(reader, info, elemType));
     224              : 
     225            4 :             ReturnErrorOnFailure(outPayload.addOptionalVendorData(info));
     226            4 :         }
     227            7 :         err = reader.Next();
     228              :     }
     229            5 :     if (err == CHIP_END_OF_TLV)
     230              :     {
     231            5 :         err = CHIP_NO_ERROR;
     232              :     }
     233              : 
     234            5 :     return err;
     235              : }
     236              : 
     237            5 : CHIP_ERROR QRCodeSetupPayloadParser::parseTLVFields(SetupPayload & outPayload, uint8_t * tlvDataStart, size_t tlvDataLengthInBytes)
     238              : {
     239            5 :     CHIP_ERROR err = CHIP_NO_ERROR;
     240            5 :     if (!CanCastTo<uint32_t>(tlvDataLengthInBytes))
     241              :     {
     242            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     243              :     }
     244            5 :     TLV::ContiguousBufferTLVReader rootReader;
     245            5 :     rootReader.Init(tlvDataStart, tlvDataLengthInBytes);
     246            5 :     ReturnErrorOnFailure(rootReader.Next());
     247              : 
     248            5 :     if (rootReader.GetType() != TLV::kTLVType_Structure)
     249              :     {
     250            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     251              :     }
     252              : 
     253            5 :     TLV::ContiguousBufferTLVReader innerStructureReader;
     254            5 :     ReturnErrorOnFailure(openTLVContainer(rootReader, TLV::kTLVType_Structure, TLV::AnonymousTag(), innerStructureReader));
     255            5 :     ReturnErrorOnFailure(innerStructureReader.Next());
     256            5 :     err = retrieveOptionalInfos(outPayload, innerStructureReader);
     257              : 
     258            5 :     if (err == CHIP_END_OF_TLV)
     259              :     {
     260            0 :         err = CHIP_NO_ERROR;
     261              :     }
     262            5 :     return err;
     263              : }
     264              : 
     265           43 : CHIP_ERROR QRCodeSetupPayloadParser::populateTLV(SetupPayload & outPayload, const std::vector<uint8_t> & buf, size_t & index)
     266              : {
     267           43 :     size_t bitsLeftToRead = (buf.size() * 8) - index;
     268           43 :     size_t tlvBytesLength = (bitsLeftToRead + 7) / 8; // ceil(bitsLeftToRead/8)
     269           43 :     chip::Platform::ScopedMemoryBuffer<uint8_t> tlvArray;
     270              : 
     271           43 :     VerifyOrReturnError(tlvBytesLength != 0, CHIP_NO_ERROR);
     272              : 
     273            5 :     tlvArray.Alloc(tlvBytesLength);
     274            5 :     VerifyOrReturnError(tlvArray, CHIP_ERROR_NO_MEMORY);
     275              : 
     276           69 :     for (size_t i = 0; i < tlvBytesLength; i++)
     277              :     {
     278              :         uint64_t dest;
     279           64 :         readBits(buf, index, dest, 8);
     280           64 :         tlvArray[i] = static_cast<uint8_t>(dest);
     281              :     }
     282              : 
     283            5 :     return parseTLVFields(outPayload, tlvArray.Get(), tlvBytesLength);
     284           43 : }
     285              : 
     286           57 : std::string QRCodeSetupPayloadParser::ExtractPayload(std::string inString)
     287              : {
     288           57 :     std::string chipSegment;
     289           57 :     char delimiter = '%';
     290           57 :     std::vector<size_t> startIndices;
     291           57 :     startIndices.push_back(0);
     292              : 
     293         1391 :     for (size_t i = 0; i < inString.length(); i++)
     294              :     {
     295         1334 :         if (inString[i] == delimiter)
     296              :         {
     297           25 :             startIndices.push_back(i + 1);
     298              :         }
     299              :     }
     300              : 
     301              :     // Find the first string between delimiters that starts with kQRCodePrefix
     302           85 :     for (size_t i = 0; i < startIndices.size(); i++)
     303              :     {
     304           75 :         size_t startIndex   = startIndices[i];
     305           75 :         size_t endIndex     = (i == startIndices.size() - 1 ? std::string::npos : startIndices[i + 1] - 1);
     306           75 :         size_t length       = (endIndex != std::string::npos ? endIndex - startIndex : std::string::npos);
     307           75 :         std::string segment = inString.substr(startIndex, length);
     308              : 
     309              :         // Find a segment that starts with kQRCodePrefix
     310           75 :         if (segment.find(kQRCodePrefix, 0) == 0 && segment.length() > strlen(kQRCodePrefix))
     311              :         {
     312           47 :             chipSegment = segment;
     313           47 :             break;
     314              :         }
     315           75 :     }
     316              : 
     317           57 :     if (chipSegment.length() > 0)
     318              :     {
     319           47 :         return chipSegment.substr(strlen(kQRCodePrefix)); // strip out prefix before returning
     320              :     }
     321              : 
     322           10 :     return chipSegment;
     323           57 : }
     324              : 
     325           26 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload)
     326              : {
     327           26 :     std::string payload = ExtractPayload(mBase38Representation);
     328           26 :     VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
     329              : 
     330           26 :     return populatePayloadFromBase38Data(std::move(payload), outPayload);
     331           26 : }
     332              : 
     333           46 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayloadFromBase38Data(std::string payload, SetupPayload & outPayload)
     334              : {
     335           46 :     std::vector<uint8_t> buf;
     336           46 :     size_t indexToReadFrom = 0;
     337              :     uint64_t dest;
     338              : 
     339           46 :     ReturnErrorOnFailure(base38Decode(payload, buf));
     340              : 
     341           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVersionFieldLengthInBits));
     342              :     static_assert(kVersionFieldLengthInBits <= 8, "Won't fit in uint8_t");
     343           43 :     outPayload.version = static_cast<uint8_t>(dest);
     344              : 
     345           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVendorIDFieldLengthInBits));
     346              :     static_assert(kVendorIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
     347           43 :     outPayload.vendorID = static_cast<uint16_t>(dest);
     348              : 
     349           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kProductIDFieldLengthInBits));
     350              :     static_assert(kProductIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
     351           43 :     outPayload.productID = static_cast<uint16_t>(dest);
     352              : 
     353           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kCommissioningFlowFieldLengthInBits));
     354              :     static_assert(kCommissioningFlowFieldLengthInBits <= std::numeric_limits<std::underlying_type_t<CommissioningFlow>>::digits,
     355              :                   "Won't fit in CommissioningFlow");
     356           43 :     outPayload.commissioningFlow = static_cast<CommissioningFlow>(dest);
     357              : 
     358           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kRendezvousInfoFieldLengthInBits));
     359              :     static_assert(kRendezvousInfoFieldLengthInBits <= 8 * sizeof(RendezvousInformationFlag),
     360              :                   "Won't fit in RendezvousInformationFlags");
     361           43 :     outPayload.rendezvousInformation.SetValue(
     362           43 :         RendezvousInformationFlags().SetRaw(static_cast<std::underlying_type_t<RendezvousInformationFlag>>(dest)));
     363              : 
     364           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPayloadDiscriminatorFieldLengthInBits));
     365              :     static_assert(kPayloadDiscriminatorFieldLengthInBits <= 16, "Won't fit in uint16_t");
     366           43 :     outPayload.discriminator.SetLongValue(static_cast<uint16_t>(dest));
     367              : 
     368           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kSetupPINCodeFieldLengthInBits));
     369              :     static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
     370           43 :     outPayload.setUpPINCode = static_cast<uint32_t>(dest);
     371              : 
     372           43 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits));
     373           43 :     if (dest != 0)
     374              :     {
     375            0 :         ChipLogError(SetupPayload, "Payload padding bits are not all 0: 0x%x", static_cast<unsigned>(dest));
     376            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     377              :     }
     378              : 
     379           43 :     return populateTLV(outPayload, buf, indexToReadFrom);
     380           46 : }
     381              : 
     382           12 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayloads(std::vector<SetupPayload> & outPayloads) const
     383              : {
     384           12 :     constexpr char kPayloadDelimiter = '*';
     385              : 
     386           12 :     std::string payload = ExtractPayload(mBase38Representation);
     387           12 :     VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
     388              : 
     389           10 :     auto chunkCount = std::count(payload.begin(), payload.end(), kPayloadDelimiter) + 1;
     390              : 
     391           10 :     outPayloads.clear();
     392           10 :     outPayloads.reserve(static_cast<std::vector<SetupPayload>::size_type>(chunkCount));
     393              : 
     394           10 :     std::string::size_type chunkStart = 0;
     395              :     do
     396              :     {
     397           20 :         auto chunkEnd = payload.find(kPayloadDelimiter, chunkStart);
     398           20 :         std::string chunk;
     399           20 :         if (chunkEnd == std::string::npos)
     400              :         {
     401           10 :             chunk      = payload.substr(chunkStart);
     402           10 :             chunkStart = std::string::npos;
     403              :         }
     404              :         else
     405              :         {
     406           10 :             chunk = payload.substr(chunkStart, chunkEnd - chunkStart);
     407              : 
     408              :             // Next chunk will start after the delimiter.
     409           10 :             chunkStart = chunkEnd + 1;
     410              :         }
     411              : 
     412           20 :         auto & nextItem = outPayloads.emplace_back();
     413           20 :         ReturnErrorOnFailure(populatePayloadFromBase38Data(chunk, nextItem));
     414              : 
     415           40 :     } while (chunkStart != std::string::npos);
     416              : 
     417           10 :     return CHIP_NO_ERROR;
     418           12 : }
     419              : 
     420              : } // namespace chip
        

Generated by: LCOV version 2.0-1