Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include <app/server/OnboardingCodesUtil.h>
20 :
21 : #include <algorithm>
22 : #include <inttypes.h>
23 :
24 : #include <lib/support/CodeUtils.h>
25 : #include <lib/support/ScopedBuffer.h>
26 : #include <lib/support/logging/CHIPLogging.h>
27 : #include <platform/CHIPDeviceLayer.h>
28 : #include <platform/CommissionableDataProvider.h>
29 : #include <platform/DeviceInstanceInfoProvider.h>
30 : #include <setup_payload/ManualSetupPayloadGenerator.h>
31 : #include <setup_payload/QRCodeSetupPayloadGenerator.h>
32 :
33 : constexpr char kQrCodeBaseUrl[] = "https://project-chip.github.io/connectedhomeip/qrcode.html";
34 : constexpr char kUrlDataAssignmentPhrase[] = "?data=";
35 : constexpr char kSpecialCharsUnreservedInRfc3986[] = "-._~";
36 :
37 : using namespace ::chip::DeviceLayer;
38 :
39 0 : void PrintOnboardingCodes(chip::RendezvousInformationFlags aRendezvousFlags)
40 : {
41 0 : chip::PayloadContents payload;
42 :
43 0 : CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
44 0 : if (err != CHIP_NO_ERROR)
45 : {
46 0 : ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
47 : }
48 :
49 0 : PrintOnboardingCodes(payload);
50 0 : }
51 :
52 0 : void PrintOnboardingCodes(const chip::PayloadContents & payload)
53 : {
54 : char payloadBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
55 0 : chip::MutableCharSpan qrCode(payloadBuffer);
56 :
57 0 : if (GetQRCode(qrCode, payload) == CHIP_NO_ERROR)
58 : {
59 0 : PrintQrCodeURL(qrCode);
60 : }
61 : else
62 : {
63 0 : ChipLogError(AppServer, "Getting QR code failed!");
64 : }
65 :
66 0 : chip::MutableCharSpan manualPairingCode(payloadBuffer);
67 0 : if (GetManualPairingCode(manualPairingCode, payload) == CHIP_NO_ERROR)
68 : {
69 0 : ChipLogProgress(AppServer, "Manual pairing code: [%s]", manualPairingCode.data());
70 : }
71 : else
72 : {
73 0 : ChipLogError(AppServer, "Getting manual pairing code failed!");
74 : }
75 0 : }
76 :
77 0 : void PrintQrCodeURL(const chip::MutableCharSpan qrCode)
78 : {
79 0 : chip::Platform::ScopedMemoryBuffer<char> qrCodeBuffer;
80 0 : const size_t qrCodeBufferMaxSize = strlen(kQrCodeBaseUrl) + strlen(kUrlDataAssignmentPhrase) + 3 * qrCode.size() + 1;
81 0 : qrCodeBuffer.Alloc(qrCodeBufferMaxSize);
82 :
83 0 : ChipLogProgress(AppServer, "SetupQRCode: [%s]", qrCode.data());
84 0 : if (GetQRCodeUrl(qrCodeBuffer.Get(), qrCodeBufferMaxSize, qrCode) == CHIP_NO_ERROR)
85 : {
86 0 : ChipLogProgress(AppServer, "Copy/paste the below URL in a browser to see the QR Code:");
87 0 : ChipLogProgress(AppServer, "%s", qrCodeBuffer.Get());
88 : }
89 0 : }
90 :
91 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC
92 : void ShareQRCodeOverNFC(chip::RendezvousInformationFlags aRendezvousFlags)
93 : {
94 : // Get QR Code and emulate its content using NFC tag
95 : char payloadBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
96 : chip::MutableCharSpan qrCode(payloadBuffer);
97 :
98 : ReturnOnFailure(GetQRCode(qrCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)));
99 :
100 : ReturnOnFailure(NFCMgr().StartTagEmulation(qrCode.data(), qrCode.size()));
101 : }
102 : #endif
103 :
104 0 : CHIP_ERROR GetPayloadContents(chip::PayloadContents & aPayload, chip::RendezvousInformationFlags aRendezvousFlags)
105 : {
106 0 : CHIP_ERROR err = CHIP_NO_ERROR;
107 0 : aPayload.version = 0;
108 0 : aPayload.rendezvousInformation.SetValue(aRendezvousFlags);
109 :
110 0 : err = GetCommissionableDataProvider()->GetSetupPasscode(aPayload.setUpPINCode);
111 0 : if (err != CHIP_NO_ERROR)
112 : {
113 : #if defined(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE) && CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE
114 0 : ChipLogProgress(AppServer, "*** Using default EXAMPLE passcode %u ***",
115 : static_cast<unsigned>(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE));
116 0 : aPayload.setUpPINCode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE;
117 : #else
118 : ChipLogError(AppServer, "GetCommissionableDataProvider()->GetSetupPasscode() failed: %" CHIP_ERROR_FORMAT, err.Format());
119 : return err;
120 : #endif
121 : }
122 :
123 0 : uint16_t discriminator = 0;
124 0 : err = GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator);
125 0 : if (err != CHIP_NO_ERROR)
126 : {
127 0 : ChipLogError(AppServer, "GetCommissionableDataProvider()->GetSetupDiscriminator() failed: %" CHIP_ERROR_FORMAT,
128 : err.Format());
129 0 : return err;
130 : }
131 0 : aPayload.discriminator.SetLongValue(discriminator);
132 :
133 0 : err = chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(aPayload.vendorID);
134 0 : if (err != CHIP_NO_ERROR)
135 : {
136 0 : ChipLogError(AppServer, "GetDeviceInstanceInfoProvider()->GetVendorId() failed: %" CHIP_ERROR_FORMAT, err.Format());
137 0 : return err;
138 : }
139 :
140 0 : err = chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(aPayload.productID);
141 0 : if (err != CHIP_NO_ERROR)
142 : {
143 0 : ChipLogError(AppServer, "GetDeviceInstanceInfoProvider()->GetProductId() failed: %" CHIP_ERROR_FORMAT, err.Format());
144 0 : return err;
145 : }
146 :
147 0 : return err;
148 : }
149 :
150 0 : CHIP_ERROR GetQRCode(chip::MutableCharSpan & aQRCode, chip::RendezvousInformationFlags aRendezvousFlags)
151 : {
152 0 : chip::PayloadContents payload;
153 :
154 0 : CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
155 0 : if (err != CHIP_NO_ERROR)
156 : {
157 0 : ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
158 0 : return err;
159 : }
160 :
161 0 : return GetQRCode(aQRCode, payload);
162 0 : }
163 :
164 0 : CHIP_ERROR GetQRCode(chip::MutableCharSpan & aQRCode, const chip::PayloadContents & payload)
165 : {
166 0 : CHIP_ERROR err = chip::QRCodeBasicSetupPayloadGenerator(payload).payloadBase38Representation(aQRCode);
167 0 : if (err != CHIP_NO_ERROR)
168 : {
169 0 : ChipLogError(AppServer, "Generating QR Code failed: %" CHIP_ERROR_FORMAT, err.Format());
170 0 : return err;
171 : }
172 :
173 0 : return CHIP_NO_ERROR;
174 : }
175 :
176 0 : CHIP_ERROR GetQRCodeUrl(char * aQRCodeUrl, size_t aUrlMaxSize, const chip::CharSpan & aQRCode)
177 : {
178 0 : VerifyOrReturnError(aQRCodeUrl, CHIP_ERROR_INVALID_ARGUMENT);
179 0 : VerifyOrReturnError(aUrlMaxSize >= (strlen(kQrCodeBaseUrl) + strlen(kUrlDataAssignmentPhrase) + aQRCode.size() + 1),
180 : CHIP_ERROR_BUFFER_TOO_SMALL);
181 :
182 0 : const int writtenDataSize = snprintf(aQRCodeUrl, aUrlMaxSize, "%s%s", kQrCodeBaseUrl, kUrlDataAssignmentPhrase);
183 0 : VerifyOrReturnError((writtenDataSize > 0) && (static_cast<size_t>(writtenDataSize) < aUrlMaxSize),
184 : CHIP_ERROR_INVALID_STRING_LENGTH);
185 :
186 0 : return EncodeQRCodeToUrl(aQRCode.data(), aQRCode.size(), aQRCodeUrl + writtenDataSize,
187 0 : aUrlMaxSize - static_cast<size_t>(writtenDataSize));
188 : }
189 :
190 0 : CHIP_ERROR GetManualPairingCode(chip::MutableCharSpan & aManualPairingCode, chip::RendezvousInformationFlags aRendezvousFlags)
191 : {
192 0 : chip::PayloadContents payload;
193 :
194 0 : CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
195 0 : if (err != CHIP_NO_ERROR)
196 : {
197 0 : ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
198 0 : return err;
199 : }
200 0 : return GetManualPairingCode(aManualPairingCode, payload);
201 0 : }
202 :
203 0 : CHIP_ERROR GetManualPairingCode(chip::MutableCharSpan & aManualPairingCode, const chip::PayloadContents & payload)
204 : {
205 0 : CHIP_ERROR err = chip::ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(aManualPairingCode);
206 0 : if (err != CHIP_NO_ERROR)
207 : {
208 0 : ChipLogError(AppServer, "Generating Manual Pairing Code failed: %" CHIP_ERROR_FORMAT, err.Format());
209 0 : return err;
210 : }
211 :
212 0 : return CHIP_NO_ERROR;
213 : }
214 :
215 0 : static inline bool isCharUnreservedInRfc3986(const char c)
216 : {
217 0 : return isalpha(c) || isdigit(c) || (strchr(kSpecialCharsUnreservedInRfc3986, c) != nullptr);
218 : }
219 :
220 0 : CHIP_ERROR EncodeQRCodeToUrl(const char * aQRCode, size_t aLen, char * aUrl, size_t aMaxSize)
221 : {
222 : static const char upperHexDigits[] = "0123456789ABCDEF";
223 0 : size_t i = 0, j = 0;
224 :
225 0 : VerifyOrReturnError((aQRCode != nullptr) && (aUrl != nullptr), CHIP_ERROR_INVALID_ARGUMENT);
226 :
227 0 : for (i = 0; i < aLen; ++i)
228 : {
229 0 : char c = aQRCode[i];
230 0 : if (isCharUnreservedInRfc3986(c))
231 : {
232 :
233 0 : VerifyOrReturnError((j + 1) < aMaxSize, CHIP_ERROR_BUFFER_TOO_SMALL);
234 :
235 0 : aUrl[j++] = c;
236 : }
237 : else
238 : {
239 :
240 0 : VerifyOrReturnError((j + 3) < aMaxSize, CHIP_ERROR_BUFFER_TOO_SMALL);
241 :
242 0 : aUrl[j++] = '%';
243 0 : aUrl[j++] = upperHexDigits[(c & 0xf0) >> 4];
244 0 : aUrl[j++] = upperHexDigits[(c & 0x0f)];
245 : }
246 : }
247 :
248 0 : aUrl[j] = '\0';
249 :
250 0 : return CHIP_NO_ERROR;
251 : }
|