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 <string.h>
28 : #include <vector>
29 :
30 : #include <lib/core/CHIPCore.h>
31 : #include <lib/core/CHIPError.h>
32 : #include <lib/core/TLVData.h>
33 : #include <lib/core/TLVUtilities.h>
34 : #include <lib/support/CodeUtils.h>
35 : #include <lib/support/SafeInt.h>
36 : #include <lib/support/ScopedBuffer.h>
37 : #include <protocols/Protocols.h>
38 :
39 : namespace chip {
40 :
41 : // Populate numberOfBits into dest from buf starting at startIndex
42 248 : static CHIP_ERROR readBits(std::vector<uint8_t> buf, size_t & index, uint64_t & dest, size_t numberOfBitsToRead)
43 : {
44 248 : dest = 0;
45 248 : if (index + numberOfBitsToRead > buf.size() * 8 || numberOfBitsToRead > sizeof(uint64_t) * 8)
46 : {
47 0 : ChipLogError(SetupPayload, "Error parsing QR code. startIndex %u numberOfBitsToLoad %u buf_len %u ",
48 : static_cast<unsigned int>(index), static_cast<unsigned int>(numberOfBitsToRead),
49 : static_cast<unsigned int>(buf.size()));
50 0 : return CHIP_ERROR_INVALID_ARGUMENT;
51 : }
52 :
53 248 : size_t currentIndex = index;
54 2784 : for (size_t bitsRead = 0; bitsRead < numberOfBitsToRead; bitsRead++)
55 : {
56 2536 : if (buf[currentIndex / 8] & (1 << (currentIndex % 8)))
57 : {
58 414 : dest |= (1 << bitsRead);
59 : }
60 2536 : currentIndex++;
61 : }
62 248 : index += numberOfBitsToRead;
63 248 : return CHIP_NO_ERROR;
64 : }
65 :
66 5 : static CHIP_ERROR openTLVContainer(TLV::ContiguousBufferTLVReader & reader, TLV::TLVType type, TLV::Tag tag,
67 : TLV::ContiguousBufferTLVReader & containerReader)
68 : {
69 5 : VerifyOrReturnError(reader.GetType() == type, CHIP_ERROR_INVALID_ARGUMENT);
70 5 : VerifyOrReturnError(reader.GetTag() == tag, CHIP_ERROR_INVALID_ARGUMENT);
71 5 : VerifyOrReturnError(reader.GetLength() == 0, CHIP_ERROR_INVALID_ARGUMENT);
72 :
73 5 : ReturnErrorOnFailure(reader.OpenContainer(containerReader));
74 :
75 5 : VerifyOrReturnError(containerReader.GetContainerType() == type, CHIP_ERROR_INVALID_ARGUMENT);
76 5 : return CHIP_NO_ERROR;
77 : }
78 :
79 4 : static CHIP_ERROR retrieveOptionalInfoString(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfo & info)
80 : {
81 4 : Span<const char> data;
82 4 : ReturnErrorOnFailure(reader.GetStringView(data));
83 :
84 4 : info.type = optionalQRCodeInfoTypeString;
85 4 : info.data = std::string(data.data(), data.size());
86 :
87 4 : return CHIP_NO_ERROR;
88 : }
89 :
90 2 : static CHIP_ERROR retrieveOptionalInfoInt32(TLV::TLVReader & reader, OptionalQRCodeInfo & info)
91 : {
92 : int32_t value;
93 2 : ReturnErrorOnFailure(reader.Get(value));
94 :
95 2 : info.type = optionalQRCodeInfoTypeInt32;
96 2 : info.int32 = value;
97 :
98 2 : return CHIP_NO_ERROR;
99 : }
100 :
101 0 : static CHIP_ERROR retrieveOptionalInfoInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
102 : {
103 : int64_t value;
104 0 : ReturnErrorOnFailure(reader.Get(value));
105 :
106 0 : info.type = optionalQRCodeInfoTypeInt64;
107 0 : info.int64 = value;
108 :
109 0 : return CHIP_NO_ERROR;
110 : }
111 :
112 1 : static CHIP_ERROR retrieveOptionalInfoUInt32(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
113 : {
114 : uint32_t value;
115 1 : ReturnErrorOnFailure(reader.Get(value));
116 :
117 1 : info.type = optionalQRCodeInfoTypeUInt32;
118 1 : info.uint32 = value;
119 :
120 1 : return CHIP_NO_ERROR;
121 : }
122 :
123 0 : static CHIP_ERROR retrieveOptionalInfoUInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
124 : {
125 : uint64_t value;
126 0 : ReturnErrorOnFailure(reader.Get(value));
127 :
128 0 : info.type = optionalQRCodeInfoTypeUInt64;
129 0 : info.uint64 = value;
130 :
131 0 : return CHIP_NO_ERROR;
132 : }
133 :
134 6 : static CHIP_ERROR retrieveOptionalInfo(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfo & info,
135 : optionalQRCodeInfoType type)
136 : {
137 6 : CHIP_ERROR err = CHIP_NO_ERROR;
138 :
139 6 : if (type == optionalQRCodeInfoTypeString)
140 : {
141 4 : err = retrieveOptionalInfoString(reader, info);
142 : }
143 2 : else if (type == optionalQRCodeInfoTypeInt32)
144 : {
145 2 : err = retrieveOptionalInfoInt32(reader, info);
146 : }
147 : else
148 : {
149 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
150 : }
151 :
152 6 : return err;
153 : }
154 :
155 3 : static CHIP_ERROR retrieveOptionalInfo(TLV::ContiguousBufferTLVReader & reader, OptionalQRCodeInfoExtension & info,
156 : optionalQRCodeInfoType type)
157 : {
158 3 : CHIP_ERROR err = CHIP_NO_ERROR;
159 :
160 3 : if (type == optionalQRCodeInfoTypeString || type == optionalQRCodeInfoTypeInt32)
161 : {
162 2 : err = retrieveOptionalInfo(reader, static_cast<OptionalQRCodeInfo &>(info), type);
163 : }
164 1 : else if (type == optionalQRCodeInfoTypeInt64)
165 : {
166 0 : err = retrieveOptionalInfoInt64(reader, info);
167 : }
168 1 : else if (type == optionalQRCodeInfoTypeUInt32)
169 : {
170 1 : err = retrieveOptionalInfoUInt32(reader, info);
171 : }
172 0 : else if (type == optionalQRCodeInfoTypeUInt64)
173 : {
174 0 : err = retrieveOptionalInfoUInt64(reader, info);
175 : }
176 : else
177 : {
178 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
179 : }
180 :
181 3 : return err;
182 : }
183 :
184 5 : CHIP_ERROR QRCodeSetupPayloadParser::retrieveOptionalInfos(SetupPayload & outPayload, TLV::ContiguousBufferTLVReader & reader)
185 : {
186 5 : CHIP_ERROR err = CHIP_NO_ERROR;
187 12 : while (err == CHIP_NO_ERROR)
188 : {
189 7 : const TLV::TLVType type = reader.GetType();
190 7 : if (type != TLV::kTLVType_UTF8String && type != TLV::kTLVType_SignedInteger && type != TLV::kTLVType_UnsignedInteger)
191 : {
192 0 : err = reader.Next();
193 0 : continue;
194 : }
195 :
196 7 : TLV::Tag tag = reader.GetTag();
197 7 : VerifyOrReturnError(TLV::IsContextTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
198 7 : const uint8_t tagNumber = static_cast<uint8_t>(TLV::TagNumFromTag(tag));
199 :
200 7 : optionalQRCodeInfoType elemType = optionalQRCodeInfoTypeUnknown;
201 7 : if (type == TLV::kTLVType_UTF8String)
202 : {
203 4 : elemType = optionalQRCodeInfoTypeString;
204 : }
205 7 : if (type == TLV::kTLVType_SignedInteger || type == TLV::kTLVType_UnsignedInteger)
206 : {
207 3 : elemType = outPayload.getNumericTypeFor(tagNumber);
208 : }
209 :
210 7 : if (SetupPayload::IsCommonTag(tagNumber))
211 : {
212 3 : OptionalQRCodeInfoExtension info;
213 3 : info.tag = tagNumber;
214 3 : ReturnErrorOnFailure(retrieveOptionalInfo(reader, info, elemType));
215 :
216 3 : ReturnErrorOnFailure(outPayload.addOptionalExtensionData(info));
217 3 : }
218 : else
219 : {
220 4 : OptionalQRCodeInfo info;
221 4 : info.tag = tagNumber;
222 4 : ReturnErrorOnFailure(retrieveOptionalInfo(reader, info, elemType));
223 :
224 4 : ReturnErrorOnFailure(outPayload.addOptionalVendorData(info));
225 4 : }
226 7 : err = reader.Next();
227 : }
228 5 : if (err == CHIP_END_OF_TLV)
229 : {
230 5 : err = CHIP_NO_ERROR;
231 : }
232 :
233 5 : return err;
234 : }
235 :
236 5 : CHIP_ERROR QRCodeSetupPayloadParser::parseTLVFields(SetupPayload & outPayload, uint8_t * tlvDataStart, size_t tlvDataLengthInBytes)
237 : {
238 5 : CHIP_ERROR err = CHIP_NO_ERROR;
239 5 : if (!CanCastTo<uint32_t>(tlvDataLengthInBytes))
240 : {
241 0 : return CHIP_ERROR_INVALID_ARGUMENT;
242 : }
243 5 : TLV::ContiguousBufferTLVReader rootReader;
244 5 : rootReader.Init(tlvDataStart, tlvDataLengthInBytes);
245 5 : ReturnErrorOnFailure(rootReader.Next());
246 :
247 5 : if (rootReader.GetType() != TLV::kTLVType_Structure)
248 : {
249 0 : return CHIP_ERROR_INVALID_ARGUMENT;
250 : }
251 :
252 5 : TLV::ContiguousBufferTLVReader innerStructureReader;
253 5 : ReturnErrorOnFailure(openTLVContainer(rootReader, TLV::kTLVType_Structure, TLV::AnonymousTag(), innerStructureReader));
254 5 : ReturnErrorOnFailure(innerStructureReader.Next());
255 5 : err = retrieveOptionalInfos(outPayload, innerStructureReader);
256 :
257 5 : if (err == CHIP_END_OF_TLV)
258 : {
259 0 : err = CHIP_NO_ERROR;
260 : }
261 5 : return err;
262 : }
263 :
264 23 : CHIP_ERROR QRCodeSetupPayloadParser::populateTLV(SetupPayload & outPayload, const std::vector<uint8_t> & buf, size_t & index)
265 : {
266 23 : size_t bitsLeftToRead = (buf.size() * 8) - index;
267 23 : size_t tlvBytesLength = (bitsLeftToRead + 7) / 8; // ceil(bitsLeftToRead/8)
268 23 : chip::Platform::ScopedMemoryBuffer<uint8_t> tlvArray;
269 :
270 23 : VerifyOrReturnError(tlvBytesLength != 0, CHIP_NO_ERROR);
271 :
272 5 : tlvArray.Alloc(tlvBytesLength);
273 5 : VerifyOrReturnError(tlvArray, CHIP_ERROR_NO_MEMORY);
274 :
275 69 : for (size_t i = 0; i < tlvBytesLength; i++)
276 : {
277 : uint64_t dest;
278 64 : readBits(buf, index, dest, 8);
279 64 : tlvArray[i] = static_cast<uint8_t>(dest);
280 : }
281 :
282 5 : return parseTLVFields(outPayload, tlvArray.Get(), tlvBytesLength);
283 23 : }
284 :
285 42 : std::string QRCodeSetupPayloadParser::ExtractPayload(std::string inString)
286 : {
287 42 : std::string chipSegment;
288 42 : char delimiter = '%';
289 42 : std::vector<size_t> startIndices;
290 42 : startIndices.push_back(0);
291 :
292 803 : for (size_t i = 0; i < inString.length(); i++)
293 : {
294 761 : if (inString[i] == delimiter)
295 : {
296 19 : startIndices.push_back(i + 1);
297 : }
298 : }
299 :
300 : // Find the first string between delimiters that starts with kQRCodePrefix
301 64 : for (size_t i = 0; i < startIndices.size(); i++)
302 : {
303 56 : size_t startIndex = startIndices[i];
304 56 : size_t endIndex = (i == startIndices.size() - 1 ? std::string::npos : startIndices[i + 1] - 1);
305 56 : size_t length = (endIndex != std::string::npos ? endIndex - startIndex : std::string::npos);
306 56 : std::string segment = inString.substr(startIndex, length);
307 :
308 : // Find a segment that starts with kQRCodePrefix
309 56 : if (segment.find(kQRCodePrefix, 0) == 0 && segment.length() > strlen(kQRCodePrefix))
310 : {
311 34 : chipSegment = segment;
312 34 : break;
313 : }
314 56 : }
315 :
316 42 : if (chipSegment.length() > 0)
317 : {
318 34 : return chipSegment.substr(strlen(kQRCodePrefix)); // strip out prefix before returning
319 : }
320 :
321 8 : return chipSegment;
322 42 : }
323 :
324 25 : CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload)
325 : {
326 25 : std::vector<uint8_t> buf;
327 25 : size_t indexToReadFrom = 0;
328 : uint64_t dest;
329 :
330 25 : std::string payload = ExtractPayload(mBase38Representation);
331 25 : VerifyOrReturnError(payload.length() != 0, CHIP_ERROR_INVALID_ARGUMENT);
332 :
333 25 : ReturnErrorOnFailure(base38Decode(payload, buf));
334 :
335 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVersionFieldLengthInBits));
336 : static_assert(kVersionFieldLengthInBits <= 8, "Won't fit in uint8_t");
337 23 : outPayload.version = static_cast<uint8_t>(dest);
338 :
339 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kVendorIDFieldLengthInBits));
340 : static_assert(kVendorIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
341 23 : outPayload.vendorID = static_cast<uint16_t>(dest);
342 :
343 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kProductIDFieldLengthInBits));
344 : static_assert(kProductIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
345 23 : outPayload.productID = static_cast<uint16_t>(dest);
346 :
347 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kCommissioningFlowFieldLengthInBits));
348 : static_assert(kCommissioningFlowFieldLengthInBits <= std::numeric_limits<std::underlying_type_t<CommissioningFlow>>::digits,
349 : "Won't fit in CommissioningFlow");
350 23 : outPayload.commissioningFlow = static_cast<CommissioningFlow>(dest);
351 :
352 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kRendezvousInfoFieldLengthInBits));
353 : static_assert(kRendezvousInfoFieldLengthInBits <= 8 * sizeof(RendezvousInformationFlag),
354 : "Won't fit in RendezvousInformationFlags");
355 23 : outPayload.rendezvousInformation.SetValue(
356 23 : RendezvousInformationFlags().SetRaw(static_cast<std::underlying_type_t<RendezvousInformationFlag>>(dest)));
357 :
358 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPayloadDiscriminatorFieldLengthInBits));
359 : static_assert(kPayloadDiscriminatorFieldLengthInBits <= 16, "Won't fit in uint16_t");
360 23 : outPayload.discriminator.SetLongValue(static_cast<uint16_t>(dest));
361 :
362 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kSetupPINCodeFieldLengthInBits));
363 : static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
364 23 : outPayload.setUpPINCode = static_cast<uint32_t>(dest);
365 :
366 23 : ReturnErrorOnFailure(readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits));
367 23 : if (dest != 0)
368 : {
369 0 : ChipLogError(SetupPayload, "Payload padding bits are not all 0: 0x%x", static_cast<unsigned>(dest));
370 0 : return CHIP_ERROR_INVALID_ARGUMENT;
371 : }
372 :
373 23 : return populateTLV(outPayload, buf, indexToReadFrom);
374 25 : }
375 :
376 : } // namespace chip
|