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 implements a QRCode Setup Payload generator in accordance
21 : * with the CHIP specification.
22 : *
23 : */
24 :
25 : #include "QRCodeSetupPayloadGenerator.h"
26 : #include "Base38Encode.h"
27 :
28 : #include <lib/core/CHIPCore.h>
29 : #include <lib/core/TLV.h>
30 : #include <lib/core/TLVData.h>
31 : #include <lib/core/TLVDebug.h>
32 : #include <lib/core/TLVUtilities.h>
33 : #include <lib/support/CodeUtils.h>
34 : #include <lib/support/SafeInt.h>
35 : #include <lib/support/ScopedBuffer.h>
36 : #include <protocols/Protocols.h>
37 :
38 : #include <stdlib.h>
39 : #include <string.h>
40 :
41 : namespace chip {
42 :
43 : // Populates numberOfBits starting from LSB of input into bits, which is assumed to be zero-initialized
44 336 : static CHIP_ERROR populateBits(uint8_t * bits, size_t & offset, uint64_t input, size_t numberOfBits,
45 : size_t totalPayloadDataSizeInBits)
46 : {
47 336 : VerifyOrReturnError(offset + numberOfBits <= totalPayloadDataSizeInBits, CHIP_ERROR_INVALID_ARGUMENT);
48 336 : VerifyOrReturnError(input < 1u << numberOfBits, CHIP_ERROR_INVALID_ARGUMENT);
49 :
50 336 : size_t index = offset;
51 336 : offset += numberOfBits;
52 1785 : while (input != 0)
53 : {
54 1449 : if (input & 1)
55 : {
56 603 : const uint8_t mask = static_cast<uint8_t>(1 << index % 8);
57 603 : bits[index / 8] = static_cast<uint8_t>(bits[index / 8] | mask);
58 : }
59 1449 : index++;
60 1449 : input >>= 1;
61 : }
62 336 : return CHIP_NO_ERROR;
63 : }
64 :
65 26 : static CHIP_ERROR populateTLVBits(uint8_t * bits, size_t & offset, const uint8_t * tlvBuf, size_t tlvBufSizeInBytes,
66 : size_t totalPayloadDataSizeInBits)
67 : {
68 154 : for (size_t i = 0; i < tlvBufSizeInBytes; i++)
69 : {
70 128 : const uint8_t value = tlvBuf[i];
71 128 : ReturnErrorOnFailure(populateBits(bits, offset, value, 8, totalPayloadDataSizeInBits));
72 : }
73 26 : return CHIP_NO_ERROR;
74 : }
75 :
76 14 : CHIP_ERROR writeTag(TLV::TLVWriter & writer, TLV::Tag tag, OptionalQRCodeInfo & info)
77 : {
78 14 : CHIP_ERROR err = CHIP_NO_ERROR;
79 :
80 14 : if (info.type == optionalQRCodeInfoTypeString)
81 : {
82 10 : err = writer.PutString(tag, info.data.c_str());
83 : }
84 4 : else if (info.type == optionalQRCodeInfoTypeInt32)
85 : {
86 4 : err = writer.Put(tag, info.int32);
87 : }
88 : else
89 : {
90 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
91 : }
92 :
93 14 : return err;
94 : }
95 :
96 7 : CHIP_ERROR writeTag(TLV::TLVWriter & writer, TLV::Tag tag, OptionalQRCodeInfoExtension & info)
97 : {
98 7 : CHIP_ERROR err = CHIP_NO_ERROR;
99 :
100 7 : if (info.type == optionalQRCodeInfoTypeString || info.type == optionalQRCodeInfoTypeInt32)
101 : {
102 6 : err = writeTag(writer, tag, static_cast<OptionalQRCodeInfo &>(info));
103 : }
104 1 : else if (info.type == optionalQRCodeInfoTypeInt64)
105 : {
106 0 : err = writer.Put(tag, info.int64);
107 : }
108 1 : else if (info.type == optionalQRCodeInfoTypeUInt32)
109 : {
110 1 : err = writer.Put(tag, info.uint32);
111 : }
112 0 : else if (info.type == optionalQRCodeInfoTypeUInt64)
113 : {
114 0 : err = writer.Put(tag, info.uint64);
115 : }
116 : else
117 : {
118 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
119 : }
120 :
121 7 : return err;
122 : }
123 :
124 29 : CHIP_ERROR QRCodeSetupPayloadGenerator::generateTLVFromOptionalData(SetupPayload & outPayload, uint8_t * tlvDataStart,
125 : uint32_t maxLen, size_t & tlvDataLengthInBytes)
126 : {
127 29 : std::vector<OptionalQRCodeInfo> optionalData = outPayload.getAllOptionalVendorData();
128 29 : std::vector<OptionalQRCodeInfoExtension> optionalExtensionData = outPayload.getAllOptionalExtensionData();
129 29 : VerifyOrReturnError(!optionalData.empty() || !optionalExtensionData.empty(), CHIP_NO_ERROR);
130 :
131 12 : TLV::TLVWriter rootWriter;
132 12 : rootWriter.Init(tlvDataStart, maxLen);
133 :
134 12 : TLV::TLVWriter innerStructureWriter;
135 :
136 12 : ReturnErrorOnFailure(rootWriter.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, innerStructureWriter));
137 :
138 17 : for (OptionalQRCodeInfo info : optionalData)
139 : {
140 8 : ReturnErrorOnFailure(writeTag(innerStructureWriter, TLV::ContextTag(info.tag), info));
141 8 : }
142 :
143 16 : for (OptionalQRCodeInfoExtension info : optionalExtensionData)
144 : {
145 7 : ReturnErrorOnFailure(writeTag(innerStructureWriter, TLV::ContextTag(info.tag), info));
146 7 : }
147 :
148 9 : ReturnErrorOnFailure(rootWriter.CloseContainer(innerStructureWriter));
149 :
150 9 : ReturnErrorOnFailure(rootWriter.Finalize());
151 :
152 9 : tlvDataLengthInBytes = rootWriter.GetLengthWritten();
153 :
154 9 : return CHIP_NO_ERROR;
155 29 : }
156 :
157 26 : static CHIP_ERROR generateBitSet(PayloadContents & payload, MutableByteSpan & bits, uint8_t * tlvDataStart,
158 : size_t tlvDataLengthInBytes)
159 : {
160 26 : size_t offset = 0;
161 26 : size_t totalPayloadSizeInBits = kTotalPayloadDataSizeInBits + (tlvDataLengthInBytes * 8);
162 26 : VerifyOrReturnError(bits.size() * 8 >= totalPayloadSizeInBits, CHIP_ERROR_BUFFER_TOO_SMALL);
163 :
164 26 : ReturnErrorOnFailure(
165 : populateBits(bits.data(), offset, payload.version, kVersionFieldLengthInBits, kTotalPayloadDataSizeInBits));
166 26 : ReturnErrorOnFailure(
167 : populateBits(bits.data(), offset, payload.vendorID, kVendorIDFieldLengthInBits, kTotalPayloadDataSizeInBits));
168 26 : ReturnErrorOnFailure(
169 : populateBits(bits.data(), offset, payload.productID, kProductIDFieldLengthInBits, kTotalPayloadDataSizeInBits));
170 26 : ReturnErrorOnFailure(populateBits(bits.data(), offset, static_cast<uint64_t>(payload.commissioningFlow),
171 : kCommissioningFlowFieldLengthInBits, kTotalPayloadDataSizeInBits));
172 26 : VerifyOrReturnError(payload.rendezvousInformation.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
173 26 : ReturnErrorOnFailure(populateBits(bits.data(), offset, payload.rendezvousInformation.Value().Raw(),
174 : kRendezvousInfoFieldLengthInBits, kTotalPayloadDataSizeInBits));
175 26 : ReturnErrorOnFailure(populateBits(bits.data(), offset, payload.discriminator.GetLongValue(),
176 : kPayloadDiscriminatorFieldLengthInBits, kTotalPayloadDataSizeInBits));
177 26 : ReturnErrorOnFailure(
178 : populateBits(bits.data(), offset, payload.setUpPINCode, kSetupPINCodeFieldLengthInBits, kTotalPayloadDataSizeInBits));
179 26 : ReturnErrorOnFailure(populateBits(bits.data(), offset, 0, kPaddingFieldLengthInBits, kTotalPayloadDataSizeInBits));
180 26 : ReturnErrorOnFailure(populateTLVBits(bits.data(), offset, tlvDataStart, tlvDataLengthInBytes, totalPayloadSizeInBits));
181 :
182 26 : return CHIP_NO_ERROR;
183 : }
184 :
185 26 : static CHIP_ERROR payloadBase38RepresentationWithTLV(PayloadContents & payload, MutableCharSpan & outBuffer, MutableByteSpan bits,
186 : uint8_t * tlvDataStart, size_t tlvDataLengthInBytes)
187 : {
188 26 : memset(bits.data(), 0, bits.size());
189 26 : ReturnErrorOnFailure(generateBitSet(payload, bits, tlvDataStart, tlvDataLengthInBytes));
190 :
191 26 : CHIP_ERROR err = CHIP_NO_ERROR;
192 26 : size_t prefixLen = strlen(kQRCodePrefix);
193 :
194 26 : if (outBuffer.size() < prefixLen + 1)
195 : {
196 0 : err = CHIP_ERROR_BUFFER_TOO_SMALL;
197 : }
198 : else
199 : {
200 26 : MutableCharSpan subSpan = outBuffer.SubSpan(prefixLen, outBuffer.size() - prefixLen);
201 26 : memcpy(outBuffer.data(), kQRCodePrefix, prefixLen);
202 26 : err = base38Encode(bits, subSpan);
203 : // Reduce output span size to be the size of written data
204 26 : outBuffer.reduce_size(subSpan.size() + prefixLen);
205 : }
206 :
207 26 : return err;
208 : }
209 :
210 6 : CHIP_ERROR QRCodeSetupPayloadGenerator::payloadBase38Representation(std::string & base38Representation)
211 : {
212 : // 6.1.2.2. Table: Packed Binary Data Structure
213 : // The TLV Data should be 0 length if TLV is not included.
214 6 : return payloadBase38Representation(base38Representation, nullptr, 0);
215 : }
216 :
217 3 : CHIP_ERROR QRCodeSetupPayloadGenerator::payloadBase38RepresentationWithAutoTLVBuffer(std::string & base38Representation)
218 : {
219 : // Estimate the size of the needed buffer.
220 3 : size_t estimate = 0;
221 :
222 4 : auto dataItemSizeEstimate = [](const OptionalQRCodeInfo & item) {
223 : // Each data item needs a control byte and a context tag.
224 4 : size_t size = 2;
225 :
226 4 : if (item.type == optionalQRCodeInfoTypeString)
227 : {
228 : // We'll need to encode the string length and then the string data.
229 : // Length is at most 8 bytes.
230 3 : size += 8;
231 3 : size += item.data.size();
232 : }
233 : else
234 : {
235 : // Integer. Assume it might need up to 8 bytes, for simplicity.
236 1 : size += 8;
237 : }
238 4 : return size;
239 : };
240 :
241 3 : auto vendorData = mPayload.getAllOptionalVendorData();
242 5 : for (auto & data : vendorData)
243 : {
244 2 : estimate += dataItemSizeEstimate(data);
245 : }
246 :
247 3 : auto extensionData = mPayload.getAllOptionalExtensionData();
248 5 : for (auto & data : extensionData)
249 : {
250 2 : estimate += dataItemSizeEstimate(data);
251 : }
252 :
253 3 : estimate = TLV::EstimateStructOverhead(estimate);
254 :
255 3 : VerifyOrReturnError(CanCastTo<uint32_t>(estimate), CHIP_ERROR_NO_MEMORY);
256 :
257 3 : Platform::ScopedMemoryBuffer<uint8_t> buf;
258 3 : VerifyOrReturnError(buf.Alloc(estimate), CHIP_ERROR_NO_MEMORY);
259 :
260 3 : return payloadBase38Representation(base38Representation, buf.Get(), static_cast<uint32_t>(estimate));
261 3 : }
262 :
263 29 : CHIP_ERROR QRCodeSetupPayloadGenerator::payloadBase38Representation(std::string & base38Representation, uint8_t * tlvDataStart,
264 : uint32_t tlvDataStartSize)
265 : {
266 29 : size_t tlvDataLengthInBytes = 0;
267 :
268 29 : VerifyOrReturnError(mAllowInvalidPayload || mPayload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
269 29 : ReturnErrorOnFailure(generateTLVFromOptionalData(mPayload, tlvDataStart, tlvDataStartSize, tlvDataLengthInBytes));
270 :
271 26 : std::vector<uint8_t> bits(kTotalPayloadDataSizeInBytes + tlvDataLengthInBytes);
272 26 : MutableByteSpan bitsSpan(bits.data(), bits.capacity());
273 26 : std::vector<char> buffer(base38EncodedLength(bits.capacity()) + strlen(kQRCodePrefix));
274 26 : MutableCharSpan bufferSpan(buffer.data(), buffer.capacity());
275 :
276 26 : ReturnErrorOnFailure(payloadBase38RepresentationWithTLV(mPayload, bufferSpan, bitsSpan, tlvDataStart, tlvDataLengthInBytes));
277 :
278 26 : base38Representation.assign(bufferSpan.data());
279 26 : return CHIP_NO_ERROR;
280 26 : }
281 :
282 0 : CHIP_ERROR QRCodeBasicSetupPayloadGenerator::payloadBase38Representation(MutableCharSpan & outBuffer)
283 : {
284 : uint8_t bits[kTotalPayloadDataSizeInBytes];
285 0 : VerifyOrReturnError(mPayload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
286 :
287 0 : return payloadBase38RepresentationWithTLV(mPayload, outBuffer, MutableByteSpan(bits), nullptr, 0);
288 : }
289 :
290 : } // namespace chip
|