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