LCOV - code coverage report
Current view: top level - setup_payload - ManualSetupPayloadParser.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 83 90 92.2 %
Date: 2024-02-15 08:20:41 Functions: 5 5 100.0 %

          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 Manul Setup Payload parser based on the
      21             :  *      CHIP specification.
      22             :  */
      23             : 
      24             : #include "ManualSetupPayloadParser.h"
      25             : 
      26             : #include <lib/support/SafeInt.h>
      27             : #include <lib/support/logging/CHIPLogging.h>
      28             : #include <lib/support/verhoeff/Verhoeff.h>
      29             : 
      30             : #include <string>
      31             : #include <vector>
      32             : 
      33             : namespace chip {
      34             : 
      35          29 : CHIP_ERROR ManualSetupPayloadParser::CheckDecimalStringValidity(std::string decimalString,
      36             :                                                                 std::string & decimalStringWithoutCheckDigit)
      37             : {
      38          29 :     if (decimalString.length() < 2)
      39             :     {
      40           3 :         ChipLogError(SetupPayload, "Failed decoding base10. Input was empty. %u",
      41             :                      static_cast<unsigned int>(decimalString.length()));
      42           3 :         return CHIP_ERROR_INVALID_STRING_LENGTH;
      43             :     }
      44          26 :     std::string repWithoutCheckChar = decimalString.substr(0, decimalString.length() - 1);
      45          26 :     char checkChar                  = decimalString.back();
      46             : 
      47          26 :     if (!Verhoeff10::ValidateCheckChar(checkChar, repWithoutCheckChar.c_str()))
      48             :     {
      49           1 :         return CHIP_ERROR_INTEGRITY_CHECK_FAILED;
      50             :     }
      51          25 :     decimalStringWithoutCheckDigit = repWithoutCheckChar;
      52          25 :     return CHIP_NO_ERROR;
      53          26 : }
      54             : 
      55          27 : CHIP_ERROR ManualSetupPayloadParser::CheckCodeLengthValidity(const std::string & decimalString, bool isLongCode)
      56             : {
      57          27 :     size_t expectedCharLength = isLongCode ? kManualSetupLongCodeCharLength : kManualSetupShortCodeCharLength;
      58          27 :     if (decimalString.length() != expectedCharLength)
      59             :     {
      60           7 :         ChipLogError(SetupPayload, "Failed decoding base10. Input length %u was not expected length %u",
      61             :                      static_cast<unsigned int>(decimalString.length()), static_cast<unsigned int>(expectedCharLength));
      62           7 :         return CHIP_ERROR_INVALID_STRING_LENGTH;
      63             :     }
      64          20 :     return CHIP_NO_ERROR;
      65             : }
      66             : 
      67          98 : CHIP_ERROR ManualSetupPayloadParser::ToNumber(const std::string & decimalString, uint32_t & dest)
      68             : {
      69          98 :     uint32_t number = 0;
      70         462 :     for (char c : decimalString)
      71             :     {
      72         367 :         if (!isdigit(c))
      73             :         {
      74           3 :             ChipLogError(SetupPayload, "Failed decoding base10. Character was invalid %c", c);
      75           3 :             return CHIP_ERROR_INVALID_INTEGER_VALUE;
      76             :         }
      77         364 :         number *= 10;
      78         364 :         number += static_cast<uint32_t>(c - '0');
      79             :     }
      80          95 :     dest = number;
      81          95 :     return CHIP_NO_ERROR;
      82             : }
      83             : 
      84             : // Populate numberOfChars into dest from decimalString starting at startIndex (least significant digit = left-most digit)
      85          96 : CHIP_ERROR ManualSetupPayloadParser::ReadDigitsFromDecimalString(const std::string & decimalString, size_t & index, uint32_t & dest,
      86             :                                                                  size_t numberOfCharsToRead)
      87             : {
      88          96 :     if (decimalString.length() < numberOfCharsToRead || (numberOfCharsToRead + index > decimalString.length()))
      89             :     {
      90           4 :         ChipLogError(SetupPayload, "Failed decoding base10. Input was too short. %u",
      91             :                      static_cast<unsigned int>(decimalString.length()));
      92           4 :         return CHIP_ERROR_INVALID_STRING_LENGTH;
      93             :     }
      94             : 
      95          92 :     std::string decimalSubstring = decimalString.substr(index, numberOfCharsToRead);
      96          92 :     index += numberOfCharsToRead;
      97          92 :     return ToNumber(decimalSubstring, dest);
      98          92 : }
      99             : 
     100          25 : CHIP_ERROR ManualSetupPayloadParser::populatePayload(SetupPayload & outPayload)
     101             : {
     102          25 :     CHIP_ERROR result = CHIP_NO_ERROR;
     103          25 :     SetupPayload payload;
     104          25 :     std::string representationWithoutCheckDigit;
     105             : 
     106          25 :     result = CheckDecimalStringValidity(mDecimalStringRepresentation, representationWithoutCheckDigit);
     107          25 :     if (result != CHIP_NO_ERROR)
     108             :     {
     109           2 :         return result;
     110             :     }
     111             : 
     112          23 :     size_t stringOffset = 0;
     113             :     uint32_t chunk1, chunk2, chunk3;
     114             : 
     115          23 :     result = ReadDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, chunk1, kManualSetupCodeChunk1CharLength);
     116          23 :     if (result != CHIP_NO_ERROR)
     117             :     {
     118           0 :         return result;
     119             :     }
     120             : 
     121          23 :     result = ReadDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, chunk2, kManualSetupCodeChunk2CharLength);
     122          23 :     if (result != CHIP_NO_ERROR)
     123             :     {
     124           2 :         return result;
     125             :     }
     126             : 
     127          21 :     result = ReadDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, chunk3, kManualSetupCodeChunk3CharLength);
     128          21 :     if (result != CHIP_NO_ERROR)
     129             :     {
     130           0 :         return result;
     131             :     }
     132             : 
     133             :     // First digit of '8' or '9' would be invalid for v1 and would indicate new format (e.g. version 2)
     134          21 :     if (chunk1 == 8 || chunk1 == 9)
     135             :     {
     136           0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     137             :     }
     138             : 
     139          21 :     bool isLongCode = ((chunk1 >> kManualSetupChunk1VidPidPresentBitPos) & 1) == 1;
     140          21 :     result          = CheckCodeLengthValidity(representationWithoutCheckDigit, isLongCode);
     141          21 :     if (result != CHIP_NO_ERROR)
     142             :     {
     143           3 :         return result;
     144             :     }
     145             : 
     146          18 :     constexpr uint32_t kDiscriminatorMsbitsMask = (1 << kManualSetupChunk1DiscriminatorMsbitsLength) - 1;
     147          18 :     constexpr uint32_t kDiscriminatorLsbitsMask = (1 << kManualSetupChunk2DiscriminatorLsbitsLength) - 1;
     148             : 
     149          18 :     uint32_t discriminator = ((chunk2 >> kManualSetupChunk2DiscriminatorLsbitsPos) & kDiscriminatorLsbitsMask);
     150          18 :     discriminator |= ((chunk1 >> kManualSetupChunk1DiscriminatorMsbitsPos) & kDiscriminatorMsbitsMask)
     151          18 :         << kManualSetupChunk2DiscriminatorLsbitsLength;
     152             : 
     153          18 :     constexpr uint32_t kPincodeMsbitsMask = (1 << kManualSetupChunk3PINCodeMsbitsLength) - 1;
     154          18 :     constexpr uint32_t kPincodeLsbitsMask = (1 << kManualSetupChunk2PINCodeLsbitsLength) - 1;
     155             : 
     156          18 :     uint32_t setUpPINCode = ((chunk2 >> kManualSetupChunk2PINCodeLsbitsPos) & kPincodeLsbitsMask);
     157          18 :     setUpPINCode |= ((chunk3 >> kManualSetupChunk3PINCodeMsbitsPos) & kPincodeMsbitsMask) << kManualSetupChunk2PINCodeLsbitsLength;
     158             : 
     159          18 :     if (setUpPINCode == 0)
     160             :     {
     161           1 :         ChipLogError(SetupPayload, "Failed decoding base10. SetUpPINCode was 0.");
     162           1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     163             :     }
     164             : 
     165          17 :     if (isLongCode)
     166             :     {
     167             :         uint32_t vendorID;
     168          10 :         result =
     169          10 :             ReadDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, vendorID, kManualSetupVendorIdCharLength);
     170          10 :         if (result != CHIP_NO_ERROR)
     171             :         {
     172           0 :             return result;
     173             :         }
     174             : 
     175             :         uint32_t productID;
     176          10 :         result =
     177          10 :             ReadDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, productID, kManualSetupProductIdCharLength);
     178          10 :         if (result != CHIP_NO_ERROR)
     179             :         {
     180           0 :             return result;
     181             :         }
     182             :         // Need to do dynamic checks, because we are reading 5 chars, so could
     183             :         // have 99,999 here or something.
     184          10 :         if (!CanCastTo<uint16_t>(vendorID))
     185             :         {
     186           0 :             return CHIP_ERROR_INVALID_INTEGER_VALUE;
     187             :         }
     188          10 :         outPayload.vendorID = static_cast<uint16_t>(vendorID);
     189          10 :         if (!CanCastTo<uint16_t>(productID))
     190             :         {
     191           0 :             return CHIP_ERROR_INVALID_INTEGER_VALUE;
     192             :         }
     193          10 :         outPayload.productID = static_cast<uint16_t>(productID);
     194             :     }
     195          17 :     outPayload.commissioningFlow = isLongCode ? CommissioningFlow::kCustom : CommissioningFlow::kStandard;
     196             :     static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
     197          17 :     outPayload.setUpPINCode = static_cast<uint32_t>(setUpPINCode);
     198             :     static_assert(kManualSetupDiscriminatorFieldLengthInBits <= 8, "Won't fit in uint8_t");
     199          17 :     outPayload.discriminator.SetShortValue(static_cast<uint8_t>(discriminator));
     200             : 
     201          17 :     return result;
     202          25 : }
     203             : 
     204             : } // namespace chip

Generated by: LCOV version 1.14