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