Line data Source code
1 : /*
2 : * Copyright (c) 2022 Project CHIP Authors
3 : * All rights reserved.
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 : #include <app-common/zap-generated/cluster-objects.h>
19 : #include <controller/CommissioningWindowOpener.h>
20 : #include <lib/core/CHIPSafeCasts.h>
21 : #include <lib/support/CHIPMem.h>
22 : #include <protocols/secure_channel/PASESession.h>
23 : #include <setup_payload/ManualSetupPayloadGenerator.h>
24 : #include <setup_payload/QRCodeSetupPayloadGenerator.h>
25 :
26 : using namespace chip::app::Clusters;
27 : using namespace chip::System::Clock;
28 :
29 : namespace {
30 : // TODO: What should the timed invoke timeout here be?
31 : constexpr uint16_t kTimedInvokeTimeoutMs = 10000;
32 : } // anonymous namespace
33 :
34 : namespace chip {
35 : namespace Controller {
36 :
37 0 : CHIP_ERROR CommissioningWindowOpener::OpenBasicCommissioningWindow(NodeId deviceId, Seconds16 timeout,
38 : Callback::Callback<OnOpenBasicCommissioningWindow> * callback)
39 : {
40 0 : VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
41 0 : mSetupPayload = SetupPayload();
42 :
43 : // Basic commissioning does not use the setup payload.
44 :
45 0 : mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
46 0 : mBasicCommissioningWindowCallback = callback;
47 0 : mCommissioningWindowCallback = nullptr;
48 0 : mNodeId = deviceId;
49 0 : mCommissioningWindowTimeout = timeout;
50 :
51 0 : mNextStep = Step::kOpenCommissioningWindow;
52 0 : return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
53 : }
54 :
55 0 : CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, Seconds16 timeout, uint32_t iteration,
56 : uint16_t discriminator, Optional<uint32_t> setupPIN,
57 : Optional<ByteSpan> salt,
58 : Callback::Callback<OnOpenCommissioningWindow> * callback,
59 : SetupPayload & payload, bool readVIDPIDAttributes)
60 : {
61 0 : VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
62 :
63 0 : VerifyOrReturnError(kSpake2p_Min_PBKDF_Iterations <= iteration && iteration <= kSpake2p_Max_PBKDF_Iterations,
64 : CHIP_ERROR_INVALID_ARGUMENT);
65 0 : VerifyOrReturnError(
66 : !salt.HasValue() ||
67 : (salt.Value().size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.Value().size() <= kSpake2p_Max_PBKDF_Salt_Length),
68 : CHIP_ERROR_INVALID_ARGUMENT);
69 :
70 0 : mSetupPayload = SetupPayload();
71 :
72 0 : if (setupPIN.HasValue())
73 : {
74 0 : if (!SetupPayload::IsValidSetupPIN(setupPIN.Value()))
75 : {
76 0 : return CHIP_ERROR_INVALID_ARGUMENT;
77 : }
78 :
79 0 : mCommissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN;
80 0 : mSetupPayload.setUpPINCode = setupPIN.Value();
81 : }
82 : else
83 : {
84 0 : mCommissioningWindowOption = CommissioningWindowOption::kTokenWithRandomPIN;
85 : }
86 :
87 0 : if (salt.HasValue())
88 : {
89 0 : memcpy(mPBKDFSaltBuffer, salt.Value().data(), salt.Value().size());
90 0 : mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer, salt.Value().size());
91 : }
92 : else
93 : {
94 0 : ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFSaltBuffer, sizeof(mPBKDFSaltBuffer)));
95 0 : mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer);
96 : }
97 :
98 0 : mSetupPayload.version = 0;
99 0 : mSetupPayload.discriminator.SetLongValue(discriminator);
100 0 : mSetupPayload.rendezvousInformation.SetValue(RendezvousInformationFlag::kOnNetwork);
101 :
102 0 : mCommissioningWindowCallback = callback;
103 0 : mBasicCommissioningWindowCallback = nullptr;
104 0 : mNodeId = deviceId;
105 0 : mCommissioningWindowTimeout = timeout;
106 0 : mPBKDFIterations = iteration;
107 :
108 0 : bool randomSetupPIN = !setupPIN.HasValue();
109 0 : ReturnErrorOnFailure(
110 : PASESession::GeneratePASEVerifier(mVerifier, mPBKDFIterations, mPBKDFSalt, randomSetupPIN, mSetupPayload.setUpPINCode));
111 :
112 0 : payload = mSetupPayload;
113 :
114 0 : if (readVIDPIDAttributes)
115 : {
116 0 : mNextStep = Step::kReadVID;
117 : }
118 : else
119 : {
120 0 : mNextStep = Step::kOpenCommissioningWindow;
121 : }
122 :
123 0 : return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
124 : }
125 :
126 0 : CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr,
127 : const SessionHandle & sessionHandle)
128 : {
129 0 : ChipLogProgress(Controller, "OpenCommissioningWindow for device ID 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId));
130 :
131 0 : constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0;
132 :
133 0 : ClusterBase cluster(exchangeMgr, sessionHandle, kAdministratorCommissioningClusterEndpoint);
134 :
135 0 : if (mCommissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode)
136 : {
137 : chip::Spake2pVerifierSerialized serializedVerifier;
138 0 : MutableByteSpan serializedVerifierSpan(serializedVerifier);
139 0 : ReturnErrorOnFailure(mVerifier.Serialize(serializedVerifierSpan));
140 :
141 0 : AdministratorCommissioning::Commands::OpenCommissioningWindow::Type request;
142 0 : request.commissioningTimeout = mCommissioningWindowTimeout.count();
143 0 : request.PAKEPasscodeVerifier = serializedVerifierSpan;
144 0 : request.discriminator = mSetupPayload.discriminator.GetLongValue();
145 0 : request.iterations = mPBKDFIterations;
146 0 : request.salt = mPBKDFSalt;
147 :
148 0 : ReturnErrorOnFailure(cluster.InvokeCommand(request, this, OnOpenCommissioningWindowSuccess,
149 : OnOpenCommissioningWindowFailure, MakeOptional(kTimedInvokeTimeoutMs)));
150 : }
151 : else
152 : {
153 0 : AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Type request;
154 0 : request.commissioningTimeout = mCommissioningWindowTimeout.count();
155 0 : ReturnErrorOnFailure(cluster.InvokeCommand(request, this, OnOpenCommissioningWindowSuccess,
156 : OnOpenCommissioningWindowFailure, MakeOptional(kTimedInvokeTimeoutMs)));
157 : }
158 :
159 0 : return CHIP_NO_ERROR;
160 0 : }
161 :
162 0 : void CommissioningWindowOpener::OnPIDReadResponse(void * context, uint16_t value)
163 : {
164 0 : ChipLogProgress(Controller, "Received PID for the device. Value %d", value);
165 0 : auto * self = static_cast<CommissioningWindowOpener *>(context);
166 0 : self->mSetupPayload.productID = value;
167 :
168 0 : self->mNextStep = Step::kOpenCommissioningWindow;
169 :
170 0 : CHIP_ERROR err = self->mController->GetConnectedDevice(self->mNodeId, &self->mDeviceConnected, &self->mDeviceConnectionFailure);
171 0 : if (err != CHIP_NO_ERROR)
172 : {
173 0 : OnOpenCommissioningWindowFailure(context, err);
174 : }
175 0 : }
176 :
177 0 : void CommissioningWindowOpener::OnVIDReadResponse(void * context, VendorId value)
178 : {
179 0 : ChipLogProgress(Controller, "Received VID for the device. Value %d", to_underlying(value));
180 :
181 0 : auto * self = static_cast<CommissioningWindowOpener *>(context);
182 :
183 0 : self->mSetupPayload.vendorID = value;
184 :
185 0 : self->mNextStep = Step::kReadPID;
186 0 : CHIP_ERROR err = self->mController->GetConnectedDevice(self->mNodeId, &self->mDeviceConnected, &self->mDeviceConnectionFailure);
187 0 : if (err != CHIP_NO_ERROR)
188 : {
189 0 : OnOpenCommissioningWindowFailure(context, err);
190 : }
191 0 : }
192 :
193 0 : void CommissioningWindowOpener::OnVIDPIDReadFailureResponse(void * context, CHIP_ERROR error)
194 : {
195 0 : ChipLogProgress(Controller, "Failed to read VID/PID for the device. error %" CHIP_ERROR_FORMAT, error.Format());
196 0 : OnOpenCommissioningWindowFailure(context, error);
197 0 : }
198 :
199 0 : void CommissioningWindowOpener::OnOpenCommissioningWindowSuccess(void * context, const chip::app::DataModel::NullObjectType &)
200 : {
201 0 : ChipLogProgress(Controller, "Successfully opened pairing window on the device");
202 0 : auto * self = static_cast<CommissioningWindowOpener *>(context);
203 0 : self->mNextStep = Step::kAcceptCommissioningStart;
204 0 : if (self->mCommissioningWindowCallback != nullptr)
205 : {
206 : char payloadBuffer[QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
207 :
208 0 : MutableCharSpan manualCode(payloadBuffer);
209 0 : CHIP_ERROR err = ManualSetupPayloadGenerator(self->mSetupPayload).payloadDecimalStringRepresentation(manualCode);
210 0 : if (err == CHIP_NO_ERROR)
211 : {
212 0 : ChipLogProgress(Controller, "Manual pairing code: [%s]", payloadBuffer);
213 : }
214 : else
215 : {
216 0 : ChipLogError(Controller, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, err.Format());
217 : }
218 :
219 0 : MutableCharSpan QRCode(payloadBuffer);
220 0 : err = QRCodeBasicSetupPayloadGenerator(self->mSetupPayload).payloadBase38Representation(QRCode);
221 0 : if (err == CHIP_NO_ERROR)
222 : {
223 0 : ChipLogProgress(Controller, "SetupQRCode: [%s]", payloadBuffer);
224 : }
225 : else
226 : {
227 0 : ChipLogError(Controller, "Unable to generate QR code for setup payload: %" CHIP_ERROR_FORMAT, err.Format());
228 : }
229 :
230 0 : self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, CHIP_NO_ERROR,
231 0 : self->mSetupPayload);
232 : // Don't touch `self` anymore; it might have been destroyed by the
233 : // callee.
234 : }
235 0 : else if (self->mBasicCommissioningWindowCallback != nullptr)
236 : {
237 0 : self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId,
238 : CHIP_NO_ERROR);
239 : // Don't touch `self` anymore; it might have been destroyed by the
240 : // callee.
241 : }
242 0 : }
243 :
244 0 : void CommissioningWindowOpener::OnOpenCommissioningWindowFailure(void * context, CHIP_ERROR error)
245 : {
246 0 : ChipLogError(Controller, "Failed to open pairing window on the device. Status %" CHIP_ERROR_FORMAT, error.Format());
247 0 : auto * self = static_cast<CommissioningWindowOpener *>(context);
248 0 : self->mNextStep = Step::kAcceptCommissioningStart;
249 0 : if (self->mCommissioningWindowCallback != nullptr)
250 : {
251 0 : self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, error,
252 0 : SetupPayload());
253 : }
254 0 : else if (self->mBasicCommissioningWindowCallback != nullptr)
255 : {
256 0 : self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId, error);
257 : }
258 0 : }
259 :
260 0 : void CommissioningWindowOpener::OnDeviceConnectedCallback(void * context, Messaging::ExchangeManager & exchangeMgr,
261 : const SessionHandle & sessionHandle)
262 : {
263 0 : auto * self = static_cast<CommissioningWindowOpener *>(context);
264 :
265 : #if CHIP_ERROR_LOGGING
266 0 : const char * messageIfError = nullptr;
267 : #endif // CHIP_ERROR_LOGGING
268 0 : CHIP_ERROR err = CHIP_NO_ERROR;
269 :
270 0 : switch (self->mNextStep)
271 : {
272 0 : case Step::kReadVID: {
273 0 : ClusterBase cluster(exchangeMgr, sessionHandle, kRootEndpointId);
274 0 : err = cluster.ReadAttribute<BasicInformation::Attributes::VendorID::TypeInfo>(context, OnVIDReadResponse,
275 : OnVIDPIDReadFailureResponse);
276 : #if CHIP_ERROR_LOGGING
277 0 : messageIfError = "Could not read VID for opening commissioning window";
278 : #endif // CHIP_ERROR_LOGGING
279 0 : break;
280 0 : }
281 0 : case Step::kReadPID: {
282 0 : ClusterBase cluster(exchangeMgr, sessionHandle, kRootEndpointId);
283 0 : err = cluster.ReadAttribute<BasicInformation::Attributes::ProductID::TypeInfo>(context, OnPIDReadResponse,
284 : OnVIDPIDReadFailureResponse);
285 : #if CHIP_ERROR_LOGGING
286 0 : messageIfError = "Could not read PID for opening commissioning window";
287 : #endif // CHIP_ERROR_LOGGING
288 0 : break;
289 0 : }
290 0 : case Step::kOpenCommissioningWindow: {
291 0 : err = self->OpenCommissioningWindowInternal(exchangeMgr, sessionHandle);
292 : #if CHIP_ERROR_LOGGING
293 0 : messageIfError = "Could not connect to open commissioning window";
294 : #endif // CHIP_ERROR_LOGGING
295 0 : break;
296 : }
297 0 : case Step::kAcceptCommissioningStart: {
298 0 : err = CHIP_ERROR_INCORRECT_STATE;
299 : #if CHIP_ERROR_LOGGING
300 0 : messageIfError = "Just got a connected device; how can we be done?";
301 : #endif // CHIP_ERROR_LOGGING
302 0 : break;
303 : }
304 : }
305 :
306 0 : if (err != CHIP_NO_ERROR)
307 : {
308 0 : ChipLogError(Controller, "%s: %" CHIP_ERROR_FORMAT, messageIfError, err.Format());
309 0 : OnOpenCommissioningWindowFailure(context, err);
310 : }
311 0 : }
312 :
313 0 : void CommissioningWindowOpener::OnDeviceConnectionFailureCallback(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
314 : {
315 0 : OnOpenCommissioningWindowFailure(context, error);
316 0 : }
317 :
318 0 : AutoCommissioningWindowOpener::AutoCommissioningWindowOpener(DeviceController * controller) :
319 0 : CommissioningWindowOpener(controller), mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
320 0 : mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
321 0 : {}
322 :
323 0 : CHIP_ERROR AutoCommissioningWindowOpener::OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
324 : Seconds16 timeout)
325 : {
326 : // Not using Platform::New because we want to keep our constructor private.
327 0 : auto * opener = new (std::nothrow) AutoCommissioningWindowOpener(controller);
328 0 : if (opener == nullptr)
329 : {
330 0 : return CHIP_ERROR_NO_MEMORY;
331 : }
332 :
333 0 : CHIP_ERROR err = opener->CommissioningWindowOpener::OpenBasicCommissioningWindow(
334 : deviceId, timeout, &opener->mOnOpenBasicCommissioningWindowCallback);
335 0 : if (err != CHIP_NO_ERROR)
336 : {
337 0 : delete opener;
338 : }
339 : // Else will clean up when the callback is called.
340 0 : return err;
341 : }
342 :
343 0 : CHIP_ERROR AutoCommissioningWindowOpener::OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, Seconds16 timeout,
344 : uint32_t iteration, uint16_t discriminator,
345 : Optional<uint32_t> setupPIN, Optional<ByteSpan> salt,
346 : SetupPayload & payload, bool readVIDPIDAttributes)
347 : {
348 : // Not using Platform::New because we want to keep our constructor private.
349 0 : auto * opener = new (std::nothrow) AutoCommissioningWindowOpener(controller);
350 0 : if (opener == nullptr)
351 : {
352 0 : return CHIP_ERROR_NO_MEMORY;
353 : }
354 :
355 0 : CHIP_ERROR err = opener->CommissioningWindowOpener::OpenCommissioningWindow(
356 : deviceId, timeout, iteration, discriminator, setupPIN, salt, &opener->mOnOpenCommissioningWindowCallback, payload,
357 : readVIDPIDAttributes);
358 0 : if (err != CHIP_NO_ERROR)
359 : {
360 0 : delete opener;
361 : }
362 : // Else will clean up when the callback is called.
363 0 : return err;
364 : }
365 :
366 0 : void AutoCommissioningWindowOpener::OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status,
367 : chip::SetupPayload payload)
368 : {
369 0 : auto * self = static_cast<AutoCommissioningWindowOpener *>(context);
370 0 : delete self;
371 0 : }
372 0 : void AutoCommissioningWindowOpener::OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status)
373 : {
374 0 : auto * self = static_cast<AutoCommissioningWindowOpener *>(context);
375 0 : delete self;
376 0 : }
377 :
378 : } // namespace Controller
379 : } // namespace chip
|