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