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
|