3 * Copyright (c) 2020 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * This file describes a QRCode Setup Payload parser based on the
24 #include "QRCodeSetupPayloadParser.h"
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>
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)
48 if (index + numberOfBitsToRead > buf.size() * 8 || numberOfBitsToRead > sizeof(uint64_t) * 8)
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;
55 size_t currentIndex = index;
56 for (size_t bitsRead = 0; bitsRead < numberOfBitsToRead; bitsRead++)
58 if (buf[currentIndex / 8] & (1 << (currentIndex % 8)))
60 dest |= (1 << bitsRead);
64 index += numberOfBitsToRead;
68 static CHIP_ERROR openTLVContainer(TLV::TLVReader & reader, TLV::TLVType type, uint64_t tag, TLV::TLVReader & containerReader)
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);
75 err = reader.OpenContainer(containerReader);
78 VerifyOrExit(containerReader.GetContainerType() == type, err = CHIP_ERROR_INVALID_ARGUMENT);
83 static CHIP_ERROR retrieveOptionalInfoString(TLV::TLVReader & reader, OptionalQRCodeInfo & info)
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);
91 err = reader.GetString(value.Get(), valLength + 1);
94 info.type = optionalQRCodeInfoTypeString;
95 info.data = std::string(value.Get());
101 static CHIP_ERROR retrieveOptionalInfoInt32(TLV::TLVReader & reader, OptionalQRCodeInfo & info)
104 CHIP_ERROR err = reader.Get(value);
107 info.type = optionalQRCodeInfoTypeInt32;
114 static CHIP_ERROR retrieveOptionalInfoInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
117 CHIP_ERROR err = reader.Get(value);
120 info.type = optionalQRCodeInfoTypeInt64;
127 static CHIP_ERROR retrieveOptionalInfoUInt32(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
130 CHIP_ERROR err = reader.Get(value);
133 info.type = optionalQRCodeInfoTypeUInt32;
140 static CHIP_ERROR retrieveOptionalInfoUInt64(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info)
143 CHIP_ERROR err = reader.Get(value);
146 info.type = optionalQRCodeInfoTypeUInt64;
153 static CHIP_ERROR retrieveOptionalInfo(TLV::TLVReader & reader, OptionalQRCodeInfo & info, optionalQRCodeInfoType type)
155 CHIP_ERROR err = CHIP_NO_ERROR;
157 if (type == optionalQRCodeInfoTypeString)
159 err = retrieveOptionalInfoString(reader, info);
161 else if (type == optionalQRCodeInfoTypeInt32)
163 err = retrieveOptionalInfoInt32(reader, info);
167 err = CHIP_ERROR_INVALID_ARGUMENT;
173 static CHIP_ERROR retrieveOptionalInfo(TLV::TLVReader & reader, OptionalQRCodeInfoExtension & info, optionalQRCodeInfoType type)
175 CHIP_ERROR err = CHIP_NO_ERROR;
177 if (type == optionalQRCodeInfoTypeString || type == optionalQRCodeInfoTypeInt32)
179 err = retrieveOptionalInfo(reader, static_cast<OptionalQRCodeInfo &>(info), type);
181 else if (type == optionalQRCodeInfoTypeInt64)
183 err = retrieveOptionalInfoInt64(reader, info);
185 else if (type == optionalQRCodeInfoTypeUInt32)
187 err = retrieveOptionalInfoUInt32(reader, info);
189 else if (type == optionalQRCodeInfoTypeUInt64)
191 err = retrieveOptionalInfoUInt64(reader, info);
195 err = CHIP_ERROR_INVALID_ARGUMENT;
201 CHIP_ERROR QRCodeSetupPayloadParser::retrieveOptionalInfos(SetupPayload & outPayload, TLV::TLVReader & reader)
203 CHIP_ERROR err = CHIP_NO_ERROR;
206 while (err == CHIP_NO_ERROR)
208 type = reader.GetType();
209 if (type != TLV::kTLVType_UTF8String && type != TLV::kTLVType_SignedInteger && type != TLV::kTLVType_UnsignedInteger)
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);
218 optionalQRCodeInfoType elemType = optionalQRCodeInfoTypeUnknown;
219 if (type == TLV::kTLVType_UTF8String)
221 elemType = optionalQRCodeInfoTypeString;
223 if (type == TLV::kTLVType_SignedInteger || type == TLV::kTLVType_UnsignedInteger)
225 elemType = outPayload.getNumericTypeFor(tag);
230 OptionalQRCodeInfoExtension info;
232 err = retrieveOptionalInfo(reader, info, elemType);
235 err = outPayload.addOptionalExtensionData(info);
240 OptionalQRCodeInfo info;
242 err = retrieveOptionalInfo(reader, info, elemType);
245 err = outPayload.addOptionalVendorData(info);
250 if (err == CHIP_END_OF_TLV)
259 CHIP_ERROR QRCodeSetupPayloadParser::parseTLVFields(SetupPayload & outPayload, uint8_t * tlvDataStart, size_t tlvDataLengthInBytes)
261 CHIP_ERROR err = CHIP_NO_ERROR;
262 if (!CanCastTo<uint32_t>(tlvDataLengthInBytes))
264 return CHIP_ERROR_INVALID_ARGUMENT;
266 TLV::TLVReader rootReader;
267 rootReader.Init(tlvDataStart, static_cast<uint32_t>(tlvDataLengthInBytes));
268 err = rootReader.Next();
271 if (rootReader.GetType() != TLV::kTLVType_Structure)
273 return CHIP_ERROR_INVALID_ARGUMENT;
276 TLV::TLVReader innerStructureReader;
277 err = openTLVContainer(rootReader, TLV::kTLVType_Structure, TLV::AnonymousTag, innerStructureReader);
279 err = innerStructureReader.Next();
281 err = retrieveOptionalInfos(outPayload, innerStructureReader);
283 if (err == CHIP_END_OF_TLV)
291 CHIP_ERROR QRCodeSetupPayloadParser::populateTLV(SetupPayload & outPayload, const std::vector<uint8_t> & buf, size_t & index)
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;
297 ReturnErrorCodeIf(tlvBytesLength == 0, CHIP_NO_ERROR);
299 tlvArray.Alloc(tlvBytesLength);
300 ReturnErrorCodeIf(!tlvArray, CHIP_ERROR_NO_MEMORY);
302 for (size_t i = 0; i < tlvBytesLength; i++)
305 readBits(buf, index, dest, 8);
306 tlvArray[i] = static_cast<uint8_t>(dest);
309 return parseTLVFields(outPayload, tlvArray.Get(), tlvBytesLength);
312 static std::string extractPayload(std::string inString)
314 std::string chipSegment;
315 char delimiter = '%';
316 std::vector<size_t> startIndices;
317 startIndices.push_back(0);
319 for (size_t i = 0; i < inString.length(); i++)
321 if (inString[i] == delimiter)
323 startIndices.push_back(i + 1);
327 // Find the first string between delimiters that starts with kQRCodePrefix
328 for (size_t i = 0; i < startIndices.size(); i++)
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);
335 // Find a segment that starts with kQRCodePrefix
336 if (segment.find(kQRCodePrefix, 0) == 0 && segment.length() > strlen(kQRCodePrefix))
338 chipSegment = segment;
343 if (chipSegment.length() > 0)
345 return chipSegment.substr(strlen(kQRCodePrefix)); // strip out prefix before returning
351 CHIP_ERROR QRCodeSetupPayloadParser::populatePayload(SetupPayload & outPayload)
353 std::vector<uint8_t> buf;
354 CHIP_ERROR err = CHIP_NO_ERROR;
355 size_t indexToReadFrom = 0;
358 std::string payload = extractPayload(mBase41Representation);
359 VerifyOrExit(payload.length() != 0, err = CHIP_ERROR_INVALID_ARGUMENT);
361 err = base41Decode(payload, buf);
364 err = readBits(buf, indexToReadFrom, dest, kVersionFieldLengthInBits);
366 static_assert(kVersionFieldLengthInBits <= 8, "Won't fit in uint8_t");
367 outPayload.version = static_cast<uint8_t>(dest);
369 err = readBits(buf, indexToReadFrom, dest, kVendorIDFieldLengthInBits);
371 static_assert(kVendorIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
372 outPayload.vendorID = static_cast<uint16_t>(dest);
374 err = readBits(buf, indexToReadFrom, dest, kProductIDFieldLengthInBits);
376 static_assert(kProductIDFieldLengthInBits <= 16, "Won't fit in uint16_t");
377 outPayload.productID = static_cast<uint16_t>(dest);
379 err = readBits(buf, indexToReadFrom, dest, kCustomFlowRequiredFieldLengthInBits);
381 static_assert(kCustomFlowRequiredFieldLengthInBits <= 8, "Won't fit in uint8_t");
382 outPayload.requiresCustomFlow = static_cast<uint8_t>(dest);
384 err = readBits(buf, indexToReadFrom, dest, kRendezvousInfoFieldLengthInBits);
386 outPayload.rendezvousInformation = static_cast<RendezvousInformationFlags>(dest);
388 err = readBits(buf, indexToReadFrom, dest, kPayloadDiscriminatorFieldLengthInBits);
390 static_assert(kPayloadDiscriminatorFieldLengthInBits <= 16, "Won't fit in uint16_t");
391 outPayload.discriminator = static_cast<uint16_t>(dest);
393 err = readBits(buf, indexToReadFrom, dest, kSetupPINCodeFieldLengthInBits);
395 static_assert(kSetupPINCodeFieldLengthInBits <= 32, "Won't fit in uint32_t");
396 outPayload.setUpPINCode = static_cast<uint32_t>(dest);
398 err = readBits(buf, indexToReadFrom, dest, kPaddingFieldLengthInBits);
401 err = populateTLV(outPayload, buf, indexToReadFrom);