LCOV - code coverage report
Current view: top level - setup_payload - QRCodeSetupPayloadParser.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 147 171 86.0 %
Date: 2024-02-15 08:20:41 Functions: 12 14 85.7 %

          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 <memory>
      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         208 : static CHIP_ERROR readBits(std::vector<uint8_t> buf, size_t & index, uint64_t & dest, size_t numberOfBitsToRead)
      44             : {
      45         208 :     dest = 0;
      46         208 :     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         208 :     size_t currentIndex = index;
      55        2304 :     for (size_t bitsRead = 0; bitsRead < numberOfBitsToRead; bitsRead++)
      56             :     {
      57        2096 :         if (buf[currentIndex / 8] & (1 << (currentIndex % 8)))
      58             :         {
      59         375 :             dest |= (1 << bitsRead);
      60             :         }
      61        2096 :         currentIndex++;
      62             :     }
      63         208 :     index += numberOfBitsToRead;
      64         208 :     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          18 : CHIP_ERROR QRCodeSetupPayloadParser::populateTLV(SetupPayload & outPayload, const std::vector<uint8_t> & buf, size_t & index)
     266             : {
     267          18 :     size_t bitsLeftToRead = (buf.size() * 8) - index;
     268          18 :     size_t tlvBytesLength = (bitsLeftToRead + 7) / 8; // ceil(bitsLeftToRead/8)
     269          18 :     chip::Platform::ScopedMemoryBuffer<uint8_t> tlvArray;
     270             : 
     271          18 :     ReturnErrorCodeIf(tlvBytesLength == 0, CHIP_NO_ERROR);
     272             : 
     273           5 :     tlvArray.Alloc(tlvBytesLength);
     274           5 :     ReturnErrorCodeIf(!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          18 : }
     285             : 
     286          37 : std::string QRCodeSetupPayloadParser::ExtractPayload(std::string inString)
     287             : {
     288          37 :     std::string chipSegment;
     289          37 :     char delimiter = '%';
     290          37 :     std::vector<size_t> startIndices;
     291          37 :     startIndices.push_back(0);
     292             : 
     293         688 :     for (size_t i = 0; i < inString.length(); i++)
     294             :     {
     295         651 :         if (inString[i] == delimiter)
     296             :         {
     297          19 :             startIndices.push_back(i + 1);
     298             :         }
     299             :     }
     300             : 
     301             :     // Find the first string between delimiters that starts with kQRCodePrefix
     302          59 :     for (size_t i = 0; i < startIndices.size(); i++)
     303             :     {
     304          51 :         size_t startIndex   = startIndices[i];
     305          51 :         size_t endIndex     = (i == startIndices.size() - 1 ? std::string::npos : startIndices[i + 1] - 1);
     306          51 :         size_t length       = (endIndex != std::string::npos ? endIndex - startIndex : std::string::npos);
     307          51 :         std::string segment = inString.substr(startIndex, length);
     308             : 
     309             :         // Find a segment that starts with kQRCodePrefix
     310          51 :         if (segment.find(kQRCodePrefix, 0) == 0 && segment.length() > strlen(kQRCodePrefix))
     311             :         {
     312          29 :             chipSegment = segment;
     313          29 :             break;
     314             :         }
     315          51 :     }
     316             : 
     317          37 :     if (chipSegment.length() > 0)
     318             :     {
     319          29 :         return chipSegment.substr(strlen(kQRCodePrefix)); // strip out prefix before returning
     320             :     }
     321             : 
     322           8 :     return chipSegment;
     323          37 : }
     324             : 
     325          20 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload)
     326             : {
     327          20 :     std::vector<uint8_t> buf;
     328          20 :     size_t indexToReadFrom = 0;
     329             :     uint64_t dest;
     330             : 
     331          20 :     std::string payload = ExtractPayload(mBase38Representation);
     332          20 :     VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
     333             : 
     334          20 :     ReturnErrorOnFailure(base38Decode(payload, buf));
     335             : 
     336          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVersionFieldLengthInBits));
     337             :     static_assert(kVersionFieldLengthInBits <= 8, "Won't fit in uint8_t");
     338          18 :     outPayload.version = static_cast<uint8_t>(dest);
     339             : 
     340          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVendorIDFieldLengthInBits));
     341             :     static_assert(kVendorIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
     342          18 :     outPayload.vendorID = static_cast<uint16_t>(dest);
     343             : 
     344          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kProductIDFieldLengthInBits));
     345             :     static_assert(kProductIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
     346          18 :     outPayload.productID = static_cast<uint16_t>(dest);
     347             : 
     348          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kCommissioningFlowFieldLengthInBits));
     349             :     static_assert(kCommissioningFlowFieldLengthInBits <= std::numeric_limits<std::underlying_type_t<CommissioningFlow>>::digits,
     350             :                   "Won't fit in CommissioningFlow");
     351          18 :     outPayload.commissioningFlow = static_cast<CommissioningFlow>(dest);
     352             : 
     353          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kRendezvousInfoFieldLengthInBits));
     354             :     static_assert(kRendezvousInfoFieldLengthInBits <= 8 * sizeof(RendezvousInformationFlag),
     355             :                   "Won't fit in RendezvousInformationFlags");
     356          18 :     outPayload.rendezvousInformation.SetValue(
     357          18 :         RendezvousInformationFlags().SetRaw(static_cast<std::underlying_type_t<RendezvousInformationFlag>>(dest)));
     358             : 
     359          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPayloadDiscriminatorFieldLengthInBits));
     360             :     static_assert(kPayloadDiscriminatorFieldLengthInBits <= 16, "Won't fit in uint16_t");
     361          18 :     outPayload.discriminator.SetLongValue(static_cast<uint16_t>(dest));
     362             : 
     363          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kSetupPINCodeFieldLengthInBits));
     364             :     static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
     365          18 :     outPayload.setUpPINCode = static_cast<uint32_t>(dest);
     366             : 
     367          18 :     ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits));
     368          18 :     if (dest != 0)
     369             :     {
     370           0 :         ChipLogError(SetupPayload, "Payload padding bits are not all 0: 0x%x", static_cast<unsigned>(dest));
     371           0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     372             :     }
     373             : 
     374          18 :     return populateTLV(outPayload, buf, indexToReadFrom);
     375          20 : }
     376             : 
     377             : } // namespace chip

Generated by: LCOV version 1.14