Matter SDK Coverage Report
Current view: top level - setup_payload - QRCodeSetupPayloadParser.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 86.0 % 171 147
Test Date: 2025-01-17 19:00:11 Functions: 85.7 % 14 12

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

Generated by: LCOV version 2.0-1