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 : * The implementation of the Setup Payload. Currently only needed to
21 : * verify the validity of a Setup Payload
22 : */
23 :
24 : #include "SetupPayload.h"
25 :
26 : #include <crypto/CHIPCryptoPAL.h>
27 : #include <lib/core/CHIPCore.h>
28 : #include <lib/core/CHIPVendorIdentifiers.hpp>
29 : #include <lib/core/TLV.h>
30 : #include <lib/core/TLVData.h>
31 : #include <lib/core/TLVUtilities.h>
32 : #include <lib/support/CodeUtils.h>
33 : #include <utility>
34 :
35 : namespace chip {
36 :
37 : // Check the Setup Payload for validity
38 : //
39 : // `vendor_id` and `product_id` are allowed all of uint16_t
40 51 : bool PayloadContents::isValidQRCodePayload(ValidationMode mode) const
41 : {
42 : // 3-bit value specifying the QR code payload version.
43 51 : VerifyOrReturnValue(version < (1 << kVersionFieldLengthInBits), false);
44 :
45 50 : VerifyOrReturnValue(static_cast<uint8_t>(commissioningFlow) < (1 << kCommissioningFlowFieldLengthInBits), false);
46 :
47 : // Device Commissioning Flow
48 : // Even in ValidationMode::kConsume we can only handle modes that we understand.
49 : // 0: Standard commissioning flow: such a device, when uncommissioned, always enters commissioning mode upon power-up, subject
50 : // to the rules in [ref_Announcement_Commencement]. 1: User-intent commissioning flow: user action required to enter
51 : // commissioning mode. 2: Custom commissioning flow: interaction with a vendor-specified means is needed before commissioning.
52 : // 3: Reserved
53 49 : VerifyOrReturnValue(commissioningFlow == CommissioningFlow::kStandard ||
54 : commissioningFlow == CommissioningFlow::kUserActionRequired ||
55 : commissioningFlow == CommissioningFlow::kCustom,
56 : false);
57 :
58 : // General discriminator validity is enforced by the SetupDiscriminator class, but it can't be short for QR a code.
59 49 : VerifyOrReturnValue(!discriminator.IsShortDiscriminator(), false);
60 :
61 : // RendevouzInformation must be present for a QR code.
62 47 : VerifyOrReturnValue(rendezvousInformation.HasValue(), false);
63 42 : if (mode == ValidationMode::kProduce)
64 : {
65 40 : chip::RendezvousInformationFlags valid(RendezvousInformationFlag::kBLE, RendezvousInformationFlag::kOnNetwork,
66 : RendezvousInformationFlag::kSoftAP, RendezvousInformationFlag::kWiFiPAF);
67 40 : VerifyOrReturnValue(rendezvousInformation.Value().HasOnly(valid), false);
68 : }
69 :
70 41 : return CheckPayloadCommonConstraints();
71 : }
72 :
73 12 : bool PayloadContents::isValidManualCode(ValidationMode mode) const
74 : {
75 : // No additional constraints apply to Manual Pairing Codes.
76 : // (If the payload has a long discriminator it will be converted automatically.)
77 12 : return CheckPayloadCommonConstraints();
78 : }
79 :
80 56 : bool PayloadContents::IsValidSetupPIN(uint32_t setupPIN)
81 : {
82 : // SHALL be restricted to the values 0x0000001 to 0x5F5E0FE (00000001 to 99999998 in decimal), excluding the invalid Passcode
83 : // values.
84 56 : if (setupPIN == kSetupPINCodeUndefinedValue || setupPIN > kSetupPINCodeMaximumValue || setupPIN == 11111111 ||
85 54 : setupPIN == 22222222 || setupPIN == 33333333 || setupPIN == 44444444 || setupPIN == 55555555 || setupPIN == 66666666 ||
86 54 : setupPIN == 77777777 || setupPIN == 88888888 || setupPIN == 12345678 || setupPIN == 87654321)
87 : {
88 2 : return false;
89 : }
90 :
91 54 : return true;
92 : }
93 :
94 53 : bool PayloadContents::CheckPayloadCommonConstraints() const
95 : {
96 : // Validation rules in this method apply to all validation modes.
97 :
98 : // Even in ValidationMode::kConsume we don't understand how to handle any payload version other than 0.
99 53 : VerifyOrReturnValue(version == 0, false);
100 :
101 53 : VerifyOrReturnValue(IsValidSetupPIN(setUpPINCode), false);
102 :
103 : // VendorID must be unspecified (0) or in valid range expected.
104 51 : VerifyOrReturnValue((vendorID == VendorId::Unspecified) || IsVendorIdValidOperationally(vendorID), false);
105 :
106 : // A value of 0x0000 SHALL NOT be assigned to a product since Product ID = 0x0000 is used for these specific cases:
107 : // * To announce an anonymized Product ID as part of device discovery
108 : // * To indicate an OTA software update file applies to multiple Product IDs equally.
109 : // * To avoid confusion when presenting the Onboarding Payload for ECM with multiple nodes
110 : // In these special cases the vendorID must be 0 (Unspecified)
111 51 : VerifyOrReturnValue(productID != 0 || vendorID == VendorId::Unspecified, false);
112 :
113 51 : return true;
114 : }
115 :
116 27 : bool PayloadContents::operator==(const PayloadContents & input) const
117 : {
118 27 : return (this->version == input.version && this->vendorID == input.vendorID && this->productID == input.productID &&
119 54 : this->commissioningFlow == input.commissioningFlow && this->rendezvousInformation == input.rendezvousInformation &&
120 81 : this->discriminator == input.discriminator && this->setUpPINCode == input.setUpPINCode);
121 : }
122 :
123 11 : CHIP_ERROR SetupPayload::addOptionalVendorData(uint8_t tag, std::string data)
124 : {
125 11 : OptionalQRCodeInfo info;
126 11 : info.tag = tag;
127 11 : info.type = optionalQRCodeInfoTypeString;
128 11 : info.data = std::move(data);
129 :
130 11 : return addOptionalVendorData(info);
131 11 : }
132 :
133 6 : CHIP_ERROR SetupPayload::addOptionalVendorData(uint8_t tag, int32_t data)
134 : {
135 6 : OptionalQRCodeInfo info;
136 6 : info.tag = tag;
137 6 : info.type = optionalQRCodeInfoTypeInt32;
138 6 : info.int32 = data;
139 :
140 6 : return addOptionalVendorData(info);
141 6 : }
142 :
143 70 : std::vector<OptionalQRCodeInfo> SetupPayload::getAllOptionalVendorData() const
144 : {
145 70 : std::vector<OptionalQRCodeInfo> returnedOptionalInfo;
146 92 : for (auto & entry : optionalVendorData)
147 : {
148 22 : returnedOptionalInfo.push_back(entry.second);
149 : }
150 70 : return returnedOptionalInfo;
151 : }
152 :
153 4 : CHIP_ERROR SetupPayload::removeOptionalVendorData(uint8_t tag)
154 : {
155 4 : VerifyOrReturnError(optionalVendorData.find(tag) != optionalVendorData.end(), CHIP_ERROR_KEY_NOT_FOUND);
156 2 : optionalVendorData.erase(tag);
157 :
158 2 : return CHIP_NO_ERROR;
159 : }
160 :
161 7 : CHIP_ERROR SetupPayload::addSerialNumber(std::string serialNumber)
162 : {
163 7 : OptionalQRCodeInfoExtension info;
164 7 : info.tag = kSerialNumberTag;
165 7 : info.type = optionalQRCodeInfoTypeString;
166 7 : info.data = std::move(serialNumber);
167 :
168 7 : return addOptionalExtensionData(info);
169 7 : }
170 :
171 2 : CHIP_ERROR SetupPayload::addSerialNumber(uint32_t serialNumber)
172 : {
173 2 : OptionalQRCodeInfoExtension info;
174 2 : info.tag = kSerialNumberTag;
175 2 : info.type = optionalQRCodeInfoTypeUInt32;
176 2 : info.uint32 = serialNumber;
177 :
178 2 : return addOptionalExtensionData(info);
179 2 : }
180 :
181 4 : CHIP_ERROR SetupPayload::getSerialNumber(std::string & outSerialNumber) const
182 : {
183 4 : CHIP_ERROR err = CHIP_NO_ERROR;
184 4 : OptionalQRCodeInfoExtension info;
185 4 : ReturnErrorOnFailure(getOptionalExtensionData(kSerialNumberTag, info));
186 :
187 2 : switch (info.type)
188 : {
189 1 : case (optionalQRCodeInfoTypeString):
190 1 : outSerialNumber = info.data;
191 1 : break;
192 1 : case (optionalQRCodeInfoTypeUInt32):
193 1 : outSerialNumber = std::to_string(info.uint32);
194 1 : break;
195 0 : default:
196 0 : err = CHIP_ERROR_INVALID_ARGUMENT;
197 0 : break;
198 : }
199 :
200 2 : return err;
201 4 : }
202 :
203 3 : CHIP_ERROR SetupPayload::removeSerialNumber()
204 : {
205 3 : VerifyOrReturnError(optionalExtensionData.find(kSerialNumberTag) != optionalExtensionData.end(), CHIP_ERROR_KEY_NOT_FOUND);
206 1 : optionalExtensionData.erase(kSerialNumberTag);
207 :
208 1 : return CHIP_NO_ERROR;
209 : }
210 :
211 1 : CHIP_ERROR SetupPayload::generateRandomSetupPin(uint32_t & setupPINCode)
212 : {
213 1 : uint8_t retries = 0;
214 1 : const uint8_t maxRetries = 10;
215 :
216 : do
217 : {
218 1 : ReturnErrorOnFailure(Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(&setupPINCode), sizeof(setupPINCode)));
219 :
220 : // Passcodes shall be restricted to the values 00000001 to 99999998 in decimal, see 5.1.1.6
221 : // TODO: Consider revising this method to ensure uniform distribution of setup PIN codes
222 1 : setupPINCode = (setupPINCode % kSetupPINCodeMaximumValue) + 1;
223 :
224 : // Make sure that the Generated Setup Pin code is not one of the invalid passcodes/pin codes defined in the
225 : // specification.
226 1 : if (IsValidSetupPIN(setupPINCode))
227 : {
228 1 : return CHIP_NO_ERROR;
229 : }
230 :
231 0 : retries++;
232 : // We got pretty unlucky with the random number generator, Just try again.
233 : // This shouldn't take many retries assuming DRBG_get_bytes is not broken.
234 0 : } while (retries < maxRetries);
235 :
236 0 : return CHIP_ERROR_INTERNAL;
237 : }
238 :
239 21 : CHIP_ERROR SetupPayload::addOptionalVendorData(const OptionalQRCodeInfo & info)
240 : {
241 21 : VerifyOrReturnError(IsVendorTag(info.tag), CHIP_ERROR_INVALID_ARGUMENT);
242 19 : optionalVendorData[info.tag] = info;
243 :
244 19 : return CHIP_NO_ERROR;
245 : }
246 :
247 12 : CHIP_ERROR SetupPayload::addOptionalExtensionData(const OptionalQRCodeInfoExtension & info)
248 : {
249 12 : VerifyOrReturnError(IsCommonTag(info.tag), CHIP_ERROR_INVALID_ARGUMENT);
250 12 : optionalExtensionData[info.tag] = info;
251 :
252 12 : return CHIP_NO_ERROR;
253 : }
254 :
255 4 : CHIP_ERROR SetupPayload::getOptionalVendorData(uint8_t tag, OptionalQRCodeInfo & info) const
256 : {
257 4 : const auto it = optionalVendorData.find(tag);
258 4 : VerifyOrReturnError(it != optionalVendorData.end(), CHIP_ERROR_KEY_NOT_FOUND);
259 4 : info = it->second;
260 :
261 4 : return CHIP_NO_ERROR;
262 : }
263 :
264 7 : CHIP_ERROR SetupPayload::getOptionalExtensionData(uint8_t tag, OptionalQRCodeInfoExtension & info) const
265 : {
266 7 : const auto it = optionalExtensionData.find(tag);
267 7 : VerifyOrReturnError(it != optionalExtensionData.end(), CHIP_ERROR_KEY_NOT_FOUND);
268 5 : info = it->second;
269 5 : return CHIP_NO_ERROR;
270 : }
271 :
272 3 : optionalQRCodeInfoType SetupPayload::getNumericTypeFor(uint8_t tag) const
273 : {
274 3 : optionalQRCodeInfoType elemType = optionalQRCodeInfoTypeUnknown;
275 :
276 3 : if (IsVendorTag(tag))
277 : {
278 2 : elemType = optionalQRCodeInfoTypeInt32;
279 : }
280 1 : else if (tag == kSerialNumberTag)
281 : {
282 1 : elemType = optionalQRCodeInfoTypeUInt32;
283 : }
284 :
285 3 : return elemType;
286 : }
287 :
288 63 : std::vector<OptionalQRCodeInfoExtension> SetupPayload::getAllOptionalExtensionData() const
289 : {
290 63 : std::vector<OptionalQRCodeInfoExtension> returnedOptionalInfo;
291 78 : for (auto & entry : optionalExtensionData)
292 : {
293 15 : returnedOptionalInfo.push_back(entry.second);
294 : }
295 63 : return returnedOptionalInfo;
296 : }
297 :
298 25 : bool SetupPayload::operator==(const SetupPayload & input) const
299 : {
300 25 : std::vector<OptionalQRCodeInfo> inputOptionalVendorData;
301 25 : std::vector<OptionalQRCodeInfoExtension> inputOptionalExtensionData;
302 :
303 25 : VerifyOrReturnError(PayloadContents::operator==(input), false);
304 :
305 24 : inputOptionalVendorData = input.getAllOptionalVendorData();
306 24 : VerifyOrReturnError(optionalVendorData.size() == inputOptionalVendorData.size(), false);
307 :
308 28 : for (const OptionalQRCodeInfo & inputInfo : inputOptionalVendorData)
309 : {
310 4 : OptionalQRCodeInfo info;
311 4 : CHIP_ERROR err = getOptionalVendorData(inputInfo.tag, info);
312 4 : VerifyOrReturnError(err == CHIP_NO_ERROR, false);
313 4 : VerifyOrReturnError(inputInfo.type == info.type, false);
314 4 : VerifyOrReturnError(inputInfo.data == info.data, false);
315 4 : VerifyOrReturnError(inputInfo.int32 == info.int32, false);
316 4 : }
317 :
318 24 : inputOptionalExtensionData = input.getAllOptionalExtensionData();
319 24 : VerifyOrReturnError(optionalExtensionData.size() == inputOptionalExtensionData.size(), false);
320 :
321 27 : for (const OptionalQRCodeInfoExtension & inputInfo : inputOptionalExtensionData)
322 : {
323 3 : OptionalQRCodeInfoExtension info;
324 3 : CHIP_ERROR err = getOptionalExtensionData(inputInfo.tag, info);
325 3 : VerifyOrReturnError(err == CHIP_NO_ERROR, false);
326 3 : VerifyOrReturnError(inputInfo.type == info.type, false);
327 3 : VerifyOrReturnError(inputInfo.data == info.data, false);
328 3 : VerifyOrReturnError(inputInfo.int32 == info.int32, false);
329 3 : VerifyOrReturnError(inputInfo.int64 == info.int64, false);
330 3 : VerifyOrReturnError(inputInfo.uint32 == info.uint32, false);
331 3 : VerifyOrReturnError(inputInfo.uint64 == info.uint64, false);
332 3 : }
333 :
334 24 : return true;
335 25 : }
336 :
337 : } // namespace chip
|