From 5acffb211c396696ce7aed14fe4805e26d4ceec6 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 11 Apr 2023 02:35:07 -0400 Subject: [PATCH] Add API on Darwin to open commissioning windows without explicitly providing a setup code. (#26037) Fixes https://github.com/project-chip/connectedhomeip/issues/22842 --- src/darwin/Framework/CHIP/MTRBaseDevice.h | 16 +++++ src/darwin/Framework/CHIP/MTRBaseDevice.mm | 61 ++++++++++++++----- src/darwin/Framework/CHIP/MTRDevice.h | 16 +++++ src/darwin/Framework/CHIP/MTRDevice.mm | 9 +++ src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm | 11 ++++ 5 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.h b/src/darwin/Framework/CHIP/MTRBaseDevice.h index 4bf87183a7b1d9..a0ff37f5d682c8 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.h +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.h @@ -337,6 +337,22 @@ typedef NS_ENUM(uint8_t, MTRTransportType) { completion:(MTRDeviceOpenCommissioningWindowHandler)completion API_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2)); +/** + * Open a commissioning window on the device, using a random setup passcode. + * + * On success, completion will be called on queue with the MTRSetupPayload that + * can be used to commission the device. + * + * @param discriminator The discriminator to use for the commissionable + * advertisement. + * @param duration Duration, in seconds, during which the commissioning + * window will be open. + */ +- (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion MTR_NEWLY_AVAILABLE; + /** * Reads events from the device. * diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm index ea47734e3facf6..09f5ea582cc9e2 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm @@ -1280,7 +1280,7 @@ - (void)deregisterReportHandlersWithQueue:(dispatch_queue_t)queue completion:(di public: static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID, - System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback); + System::Clock::Seconds16 timeout, uint16_t discriminator, const Optional & setupPIN, ResultCallback callback); private: OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback); @@ -1300,7 +1300,7 @@ static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * control } CHIP_ERROR OpenCommissioningWindowHelper::OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID, - System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback) + System::Clock::Seconds16 timeout, uint16_t discriminator, const Optional & setupPIN, ResultCallback callback) { auto * self = new (std::nothrow) OpenCommissioningWindowHelper(controller, callback); if (self == nullptr) { @@ -1309,7 +1309,7 @@ static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * control SetupPayload unused; CHIP_ERROR err = self->mOpener.OpenCommissioningWindow(nodeID, timeout, Crypto::kSpake2p_Min_PBKDF_Iterations, discriminator, - MakeOptional(setupPIN), NullOptional, &self->mOnOpenCommissioningWindowCallback, unused); + setupPIN, NullOptional, &self->mOnOpenCommissioningWindowCallback, unused); if (err != CHIP_NO_ERROR) { delete self; } @@ -1327,11 +1327,11 @@ static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * control } // anonymous namespace -- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode - discriminator:(NSNumber *)discriminator - duration:(NSNumber *)duration - queue:(dispatch_queue_t)queue - completion:(MTRDeviceOpenCommissioningWindowHandler)completion +- (void)_openCommissioningWindowWithSetupPasscode:(nullable NSNumber *)setupPasscode + discriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion { if (self.isPASEDevice) { MTR_LOG_ERROR("Can't open a commissioning window over PASE"); @@ -1360,13 +1360,17 @@ - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode return; } - unsigned long long passcodeVal = [setupPasscode unsignedLongLongValue]; - if (!CanCastTo(passcodeVal) || !SetupPayload::IsValidSetupPIN(static_cast(passcodeVal))) { - MTR_LOG_ERROR("Error: Setup passcode %llu is not valid", passcodeVal); - dispatch_async(queue, ^{ - completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]); - }); - return; + Optional passcode; + if (setupPasscode != nil) { + unsigned long long passcodeVal = [setupPasscode unsignedLongLongValue]; + if (!CanCastTo(passcodeVal) || !SetupPayload::IsValidSetupPIN(static_cast(passcodeVal))) { + MTR_LOG_ERROR("Error: Setup passcode %llu is not valid", passcodeVal); + dispatch_async(queue, ^{ + completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]); + }); + return; + } + passcode.Emplace(static_cast(passcodeVal)); } [self.deviceController @@ -1394,7 +1398,7 @@ - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode SetupPayload setupPayload; auto errorCode = OpenCommissioningWindowHelper::OpenCommissioningWindow(commissioner, self.nodeID, chip::System::Clock::Seconds16(static_cast(durationVal)), static_cast(discriminatorVal), - static_cast(passcodeVal), resultCallback); + passcode, resultCallback); if (errorCode != CHIP_NO_ERROR) { dispatch_async(queue, ^{ @@ -1412,6 +1416,31 @@ - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode }]; } +- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode + discriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion +{ + [self _openCommissioningWindowWithSetupPasscode:setupPasscode + discriminator:discriminator + duration:duration + queue:queue + completion:completion]; +} + +- (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion +{ + [self _openCommissioningWindowWithSetupPasscode:nil + discriminator:discriminator + duration:duration + queue:queue + completion:completion]; +} + #ifdef DEBUG // This method is for unit testing only - (void)failSubscribers:(dispatch_queue_t)queue completion:(void (^)(void))completion diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h index 6baac86c76990a..6f8bda9703e0bc 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.h +++ b/src/darwin/Framework/CHIP/MTRDevice.h @@ -184,6 +184,22 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) { completion:(MTRDeviceOpenCommissioningWindowHandler)completion API_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2)); +/** + * Open a commissioning window on the device, using a random setup passcode. + * + * On success, completion will be called on queue with the MTRSetupPayload that + * can be used to commission the device. + * + * @param discriminator The discriminator to use for the commissionable + * advertisement. + * @param duration Duration, in seconds, during which the commissioning + * window will be open. + */ +- (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion MTR_NEWLY_AVAILABLE; + @end extern NSString * const MTREventNumberKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5)); diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 30a184f8d37ef9..9d09201390b6b6 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -806,6 +806,15 @@ - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode completion:completion]; } +- (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion +{ + auto * baseDevice = [self newBaseDevice]; + [baseDevice openCommissioningWindowWithDiscriminator:discriminator duration:duration queue:queue completion:completion]; +} + #pragma mark - Cache management // assume lock is held diff --git a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm index cea5e0308e595a..17cd039278d294 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.mm @@ -327,6 +327,17 @@ - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode }); } +- (void)openCommissioningWindowWithDiscriminator:(NSNumber *)discriminator + duration:(NSNumber *)duration + queue:(dispatch_queue_t)queue + completion:(MTRDeviceOpenCommissioningWindowHandler)completion +{ + MTR_LOG_ERROR("MTRDevice doesn't support openCommissioningWindowWithDiscriminator over XPC"); + dispatch_async(queue, ^{ + completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]); + }); +} + - (void)fetchProxyHandleWithQueue:(dispatch_queue_t)queue completion:(MTRFetchProxyHandleCompletion)completion { if (self.controllerID != nil) {