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 <algorithm>
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 408 : static CHIP_ERROR readBits(std::vector<uint8_t> buf, size_t & index, uint64_t & dest, size_t numberOfBitsToRead)
44 : {
45 408 : dest = 0;
46 408 : 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 408 : size_t currentIndex = index;
55 4704 : for (size_t bitsRead = 0; bitsRead < numberOfBitsToRead; bitsRead++)
56 : {
57 4296 : if (buf[currentIndex / 8] & (1 << (currentIndex % 8)))
58 : {
59 652 : dest |= (1 << bitsRead);
60 : }
61 4296 : currentIndex++;
62 : }
63 408 : index += numberOfBitsToRead;
64 408 : 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 43 : CHIP_ERROR QRCodeSetupPayloadParser::populateTLV(SetupPayload & outPayload, const std::vector<uint8_t> & buf, size_t & index)
266 : {
267 43 : size_t bitsLeftToRead = (buf.size() * 8) - index;
268 43 : size_t tlvBytesLength = (bitsLeftToRead + 7) / 8; // ceil(bitsLeftToRead/8)
269 43 : chip::Platform::ScopedMemoryBuffer<uint8_t> tlvArray;
270 :
271 43 : VerifyOrReturnError(tlvBytesLength != 0, CHIP_NO_ERROR);
272 :
273 5 : tlvArray.Alloc(tlvBytesLength);
274 5 : VerifyOrReturnError(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 43 : }
285 :
286 57 : std::string QRCodeSetupPayloadParser::ExtractPayload(std::string inString)
287 : {
288 57 : std::string chipSegment;
289 57 : char delimiter = '%';
290 57 : std::vector<size_t> startIndices;
291 57 : startIndices.push_back(0);
292 :
293 1391 : for (size_t i = 0; i < inString.length(); i++)
294 : {
295 1334 : if (inString[i] == delimiter)
296 : {
297 25 : startIndices.push_back(i + 1);
298 : }
299 : }
300 :
301 : // Find the first string between delimiters that starts with kQRCodePrefix
302 85 : for (size_t i = 0; i < startIndices.size(); i++)
303 : {
304 75 : size_t startIndex = startIndices[i];
305 75 : size_t endIndex = (i == startIndices.size() - 1 ? std::string::npos : startIndices[i + 1] - 1);
306 75 : size_t length = (endIndex != std::string::npos ? endIndex - startIndex : std::string::npos);
307 75 : std::string segment = inString.substr(startIndex, length);
308 :
309 : // Find a segment that starts with kQRCodePrefix
310 75 : if (segment.find(kQRCodePrefix, 0) == 0 && segment.length() > strlen(kQRCodePrefix))
311 : {
312 47 : chipSegment = segment;
313 47 : break;
314 : }
315 75 : }
316 :
317 57 : if (chipSegment.length() > 0)
318 : {
319 47 : return chipSegment.substr(strlen(kQRCodePrefix)); // strip out prefix before returning
320 : }
321 :
322 10 : return chipSegment;
323 57 : }
324 :
325 26 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload)
326 : {
327 26 : std::string payload = ExtractPayload(mBase38Representation);
328 26 : VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
329 :
330 26 : return populatePayloadFromBase38Data(std::move(payload), outPayload);
331 26 : }
332 :
333 46 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayloadFromBase38Data(std::string payload, SetupPayload & outPayload)
334 : {
335 46 : std::vector<uint8_t> buf;
336 46 : size_t indexToReadFrom = 0;
337 : uint64_t dest;
338 :
339 46 : ReturnErrorOnFailure(base38Decode(payload, buf));
340 :
341 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVersionFieldLengthInBits));
342 : static_assert(kVersionFieldLengthInBits <= 8, "Won't fit in uint8_t");
343 43 : outPayload.version = static_cast<uint8_t>(dest);
344 :
345 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVendorIDFieldLengthInBits));
346 : static_assert(kVendorIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
347 43 : outPayload.vendorID = static_cast<uint16_t>(dest);
348 :
349 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kProductIDFieldLengthInBits));
350 : static_assert(kProductIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
351 43 : outPayload.productID = static_cast<uint16_t>(dest);
352 :
353 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kCommissioningFlowFieldLengthInBits));
354 : static_assert(kCommissioningFlowFieldLengthInBits <= std::numeric_limits<std::underlying_type_t<CommissioningFlow>>::digits,
355 : "Won't fit in CommissioningFlow");
356 43 : outPayload.commissioningFlow = static_cast<CommissioningFlow>(dest);
357 :
358 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kRendezvousInfoFieldLengthInBits));
359 : static_assert(kRendezvousInfoFieldLengthInBits <= 8 * sizeof(RendezvousInformationFlag),
360 : "Won't fit in RendezvousInformationFlags");
361 43 : outPayload.rendezvousInformation.SetValue(
362 43 : RendezvousInformationFlags().SetRaw(static_cast<std::underlying_type_t<RendezvousInformationFlag>>(dest)));
363 :
364 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPayloadDiscriminatorFieldLengthInBits));
365 : static_assert(kPayloadDiscriminatorFieldLengthInBits <= 16, "Won't fit in uint16_t");
366 43 : outPayload.discriminator.SetLongValue(static_cast<uint16_t>(dest));
367 :
368 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kSetupPINCodeFieldLengthInBits));
369 : static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
370 43 : outPayload.setUpPINCode = static_cast<uint32_t>(dest);
371 :
372 43 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits));
373 43 : if (dest != 0)
374 : {
375 0 : ChipLogError(SetupPayload, "Payload padding bits are not all 0: 0x%x", static_cast<unsigned>(dest));
376 0 : return CHIP_ERROR_INVALID_ARGUMENT;
377 : }
378 :
379 43 : return populateTLV(outPayload, buf, indexToReadFrom);
380 46 : }
381 :
382 12 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayloads(std::vector<SetupPayload> & outPayloads) const
383 : {
384 12 : constexpr char kPayloadDelimiter = '*';
385 :
386 12 : std::string payload = ExtractPayload(mBase38Representation);
387 12 : VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
388 :
389 10 : auto chunkCount = std::count(payload.begin(), payload.end(), kPayloadDelimiter) + 1;
390 :
391 10 : outPayloads.clear();
392 10 : outPayloads.reserve(static_cast<std::vector<SetupPayload>::size_type>(chunkCount));
393 :
394 10 : std::string::size_type chunkStart = 0;
395 : do
396 : {
397 20 : auto chunkEnd = payload.find(kPayloadDelimiter, chunkStart);
398 20 : std::string chunk;
399 20 : if (chunkEnd == std::string::npos)
400 : {
401 10 : chunk = payload.substr(chunkStart);
402 10 : chunkStart = std::string::npos;
403 : }
404 : else
405 : {
406 10 : chunk = payload.substr(chunkStart, chunkEnd - chunkStart);
407 :
408 : // Next chunk will start after the delimiter.
409 10 : chunkStart = chunkEnd + 1;
410 : }
411 :
412 20 : auto & nextItem = outPayloads.emplace_back();
413 20 : ReturnErrorOnFailure(populatePayloadFromBase38Data(chunk, nextItem));
414 :
415 40 : } while (chunkStart != std::string::npos);
416 :
417 10 : return CHIP_NO_ERROR;
418 12 : }
419 :
420 : } // namespace chip
|