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