|
24 | 24 | #import "MTRError_Internal.h"
|
25 | 25 | #import "MTREventTLVValueDecoder_Internal.h"
|
26 | 26 | #import "MTRLogging.h"
|
| 27 | +#import "MTRSetupPayload_Internal.h" |
27 | 28 |
|
28 | 29 | #include "app/ConcreteAttributePath.h"
|
29 | 30 | #include "app/ConcreteCommandPath.h"
|
|
36 | 37 | #include <app/InteractionModelEngine.h>
|
37 | 38 | #include <app/ReadClient.h>
|
38 | 39 | #include <app/util/error-mapping.h>
|
| 40 | +#include <controller/CommissioningWindowOpener.h> |
39 | 41 | #include <controller/ReadInteraction.h>
|
40 | 42 | #include <controller/WriteInteraction.h>
|
| 43 | +#include <crypto/CHIPCryptoPAL.h> |
| 44 | +#include <setup_payload/SetupPayload.h> |
| 45 | +#include <system/SystemClock.h> |
41 | 46 |
|
42 | 47 | #include <memory>
|
43 | 48 |
|
@@ -1256,6 +1261,144 @@ - (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue co
|
1256 | 1261 | PurgeReadClientContainers(self.nodeID, clientQueue, completion);
|
1257 | 1262 | }
|
1258 | 1263 |
|
| 1264 | +namespace { |
| 1265 | +class OpenCommissioningWindowHelper { |
| 1266 | + typedef void (^ResultCallback)(CHIP_ERROR status, const SetupPayload &); |
| 1267 | + |
| 1268 | +public: |
| 1269 | + static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID, |
| 1270 | + System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback); |
| 1271 | + |
| 1272 | +private: |
| 1273 | + OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback); |
| 1274 | + |
| 1275 | + static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload); |
| 1276 | + |
| 1277 | + Controller::CommissioningWindowOpener mOpener; |
| 1278 | + Callback::Callback<Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback; |
| 1279 | + ResultCallback mResultCallback; |
| 1280 | +}; |
| 1281 | + |
| 1282 | +OpenCommissioningWindowHelper::OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback) |
| 1283 | + : mOpener(controller) |
| 1284 | + , mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this) |
| 1285 | + , mResultCallback(callback) |
| 1286 | +{ |
| 1287 | +} |
| 1288 | + |
| 1289 | +CHIP_ERROR OpenCommissioningWindowHelper::OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID, |
| 1290 | + System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback) |
| 1291 | +{ |
| 1292 | + auto * self = new (std::nothrow) OpenCommissioningWindowHelper(controller, callback); |
| 1293 | + if (self == nullptr) { |
| 1294 | + return CHIP_ERROR_NO_MEMORY; |
| 1295 | + } |
| 1296 | + |
| 1297 | + SetupPayload unused; |
| 1298 | + CHIP_ERROR err = self->mOpener.OpenCommissioningWindow(nodeID, timeout, Crypto::kSpake2p_Min_PBKDF_Iterations, discriminator, |
| 1299 | + MakeOptional(setupPIN), NullOptional, &self->mOnOpenCommissioningWindowCallback, unused); |
| 1300 | + if (err != CHIP_NO_ERROR) { |
| 1301 | + delete self; |
| 1302 | + } |
| 1303 | + // Else will clean up when the callback is called. |
| 1304 | + return err; |
| 1305 | +} |
| 1306 | + |
| 1307 | +void OpenCommissioningWindowHelper::OnOpenCommissioningWindowResponse( |
| 1308 | + void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload) |
| 1309 | +{ |
| 1310 | + auto * self = static_cast<OpenCommissioningWindowHelper *>(context); |
| 1311 | + self->mResultCallback(status, payload); |
| 1312 | + delete self; |
| 1313 | +} |
| 1314 | + |
| 1315 | +} // anonymous namespace |
| 1316 | + |
| 1317 | +- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode |
| 1318 | + discriminator:(NSNumber *)discriminator |
| 1319 | + duration:(NSNumber *)duration |
| 1320 | + queue:(dispatch_queue_t)queue |
| 1321 | + completion:(MTRDeviceOpenCommissioningWindowHandler)completion |
| 1322 | +{ |
| 1323 | + if (self.isPASEDevice) { |
| 1324 | + MTR_LOG_ERROR("Can't open a commissioning window over PASE"); |
| 1325 | + dispatch_async(queue, ^{ |
| 1326 | + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]); |
| 1327 | + }); |
| 1328 | + return; |
| 1329 | + } |
| 1330 | + |
| 1331 | + unsigned long long durationVal = [duration unsignedLongLongValue]; |
| 1332 | + if (!CanCastTo<uint16_t>(durationVal)) { |
| 1333 | + MTR_LOG_ERROR("Error: Duration %llu is too large.", durationVal); |
| 1334 | + dispatch_async(queue, ^{ |
| 1335 | + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]); |
| 1336 | + }); |
| 1337 | + return; |
| 1338 | + } |
| 1339 | + |
| 1340 | + unsigned long long discriminatorVal = [discriminator unsignedLongLongValue]; |
| 1341 | + |
| 1342 | + if (discriminatorVal > 0xFFF) { |
| 1343 | + MTR_LOG_ERROR("Error: Discriminator %llu is too large. Max value %d", discriminatorVal, 0xFFF); |
| 1344 | + dispatch_async(queue, ^{ |
| 1345 | + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]); |
| 1346 | + }); |
| 1347 | + return; |
| 1348 | + } |
| 1349 | + |
| 1350 | + unsigned long long passcodeVal = [setupPasscode unsignedLongLongValue]; |
| 1351 | + if (!CanCastTo<uint32_t>(passcodeVal) || !SetupPayload::IsValidSetupPIN(static_cast<uint32_t>(passcodeVal))) { |
| 1352 | + MTR_LOG_ERROR("Error: Setup passcode %llu is not valid", passcodeVal); |
| 1353 | + dispatch_async(queue, ^{ |
| 1354 | + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]); |
| 1355 | + }); |
| 1356 | + return; |
| 1357 | + } |
| 1358 | + |
| 1359 | + [self.deviceController |
| 1360 | + asyncDispatchToMatterQueue:^(Controller::DeviceCommissioner * commissioner) { |
| 1361 | + auto resultCallback = ^(CHIP_ERROR status, const SetupPayload & payload) { |
| 1362 | + if (status != CHIP_NO_ERROR) { |
| 1363 | + dispatch_async(queue, ^{ |
| 1364 | + completion(nil, [MTRError errorForCHIPErrorCode:status]); |
| 1365 | + }); |
| 1366 | + return; |
| 1367 | + } |
| 1368 | + auto * payloadObj = [[MTRSetupPayload alloc] initWithSetupPayload:payload]; |
| 1369 | + if (payloadObj == nil) { |
| 1370 | + dispatch_async(queue, ^{ |
| 1371 | + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_NO_MEMORY]); |
| 1372 | + }); |
| 1373 | + return; |
| 1374 | + } |
| 1375 | + |
| 1376 | + dispatch_async(queue, ^{ |
| 1377 | + completion(payloadObj, nil); |
| 1378 | + }); |
| 1379 | + }; |
| 1380 | + |
| 1381 | + SetupPayload setupPayload; |
| 1382 | + auto errorCode = OpenCommissioningWindowHelper::OpenCommissioningWindow(commissioner, self.nodeID, |
| 1383 | + chip::System::Clock::Seconds16(static_cast<uint16_t>(durationVal)), static_cast<uint16_t>(discriminatorVal), |
| 1384 | + static_cast<uint32_t>(passcodeVal), resultCallback); |
| 1385 | + |
| 1386 | + if (errorCode != CHIP_NO_ERROR) { |
| 1387 | + dispatch_async(queue, ^{ |
| 1388 | + completion(nil, [MTRError errorForCHIPErrorCode:errorCode]); |
| 1389 | + }); |
| 1390 | + return; |
| 1391 | + } |
| 1392 | + |
| 1393 | + // resultCallback will handle things now. |
| 1394 | + } |
| 1395 | + errorHandler:^(NSError * error) { |
| 1396 | + dispatch_async(queue, ^{ |
| 1397 | + completion(nil, error); |
| 1398 | + }); |
| 1399 | + }]; |
| 1400 | +} |
| 1401 | + |
1259 | 1402 | #ifdef DEBUG
|
1260 | 1403 | // This method is for unit testing only
|
1261 | 1404 | - (void)failSubscribers:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion
|
|
0 commit comments