Fixes in tv-casting-app to allow setting custom Setup Passcodes and backgrounding the app on iOS (#24046)
* tv-casting-app: Making getDiscoveredCommissioner API synchronous
* iOS MatterTvCastingBridge: Generate spake2pSalt (and verifier) if required in CommissionableDataProviderImpl
* tv-casting-app/darwin: Stopping/restarting Matter server when app becomes inactive/active. Also, disabling BLE
* Addressing cliffamzn@'s feedback
diff --git a/examples/tv-casting-app/android/args.gni b/examples/tv-casting-app/android/args.gni
index 802f54f..e3a20c5 100644
--- a/examples/tv-casting-app/android/args.gni
+++ b/examples/tv-casting-app/android/args.gni
@@ -29,3 +29,5 @@
chip_enable_additional_data_advertising = true
chip_enable_rotating_device_id = true
+
+chip_config_network_layer_ble = false
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
index d85d5a6..ef0579e 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
@@ -26,6 +26,7 @@
3CCB8742286A593700771BAD /* ConversionUtils.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3CCB873C286A593700771BAD /* ConversionUtils.hpp */; };
3CCB8743286A593700771BAD /* CastingServerBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3CCB873D286A593700771BAD /* CastingServerBridge.mm */; };
3CCB8744286A593700771BAD /* ConversionUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3CCB873E286A593700771BAD /* ConversionUtils.mm */; };
+ 3CE868F42946D76200FCB92B /* CommissionableDataProviderImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3CE868F32946D76200FCB92B /* CommissionableDataProviderImpl.mm */; };
3CF8532728E37F1000F07B9F /* MatterError.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3CF8532628E37F1000F07B9F /* MatterError.mm */; };
/* End PBXBuildFile section */
@@ -59,6 +60,7 @@
3CCB873C286A593700771BAD /* ConversionUtils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ConversionUtils.hpp; sourceTree = "<group>"; };
3CCB873D286A593700771BAD /* CastingServerBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CastingServerBridge.mm; sourceTree = "<group>"; };
3CCB873E286A593700771BAD /* ConversionUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConversionUtils.mm; sourceTree = "<group>"; };
+ 3CE868F32946D76200FCB92B /* CommissionableDataProviderImpl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CommissionableDataProviderImpl.mm; sourceTree = "<group>"; };
3CF8532528E37ED800F07B9F /* MatterError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MatterError.h; sourceTree = "<group>"; };
3CF8532628E37F1000F07B9F /* MatterError.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MatterError.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -125,6 +127,7 @@
3C26AC8B2926FE0C00BA6881 /* DeviceAttestationCredentialsProviderImpl.hpp */,
3C26AC8F2927008900BA6881 /* DeviceAttestationCredentialsProviderImpl.mm */,
3C0D9CDF2920A30C00D3332B /* CommissionableDataProviderImpl.hpp */,
+ 3CE868F32946D76200FCB92B /* CommissionableDataProviderImpl.mm */,
);
path = MatterTvCastingBridge;
sourceTree = "<group>";
@@ -241,6 +244,7 @@
3CCB8744286A593700771BAD /* ConversionUtils.mm in Sources */,
3C4E53B028E4F28100F293E8 /* MediaPlaybackTypes.mm in Sources */,
3C66FBFC290327BB00B63FE7 /* AppParameters.mm in Sources */,
+ 3CE868F42946D76200FCB92B /* CommissionableDataProviderImpl.mm in Sources */,
3C26AC9329282B8100BA6881 /* DeviceAttestationCredentialsHolder.m in Sources */,
3C26AC902927008900BA6881 /* DeviceAttestationCredentialsProviderImpl.mm in Sources */,
3CCB873F286A593700771BAD /* DiscoveredNodeData.mm in Sources */,
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.h
index 9297223..9e445e2 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.h
@@ -31,9 +31,9 @@
@property uint32_t spake2pIterationCount;
-@property NSData * spake2pSalt;
+@property NSData * spake2pSaltBase64;
-@property NSData * spake2pVerifier;
+@property NSData * spake2pVerifierBase64;
@property DeviceAttestationCredentialsHolder * deviceAttestationCredentials;
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h
index 8c8f8c8..8f1d7d1 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h
@@ -54,7 +54,7 @@
@param clientQueue Queue to dispatch the call to the discoveredCommissionerHandler on
- @param discoveredCommissionerHandler Handler to call after a discovered commissioner has been retrieved
+ @param discoveredCommissionerHandler Handler called synchronously after a discovered commissioner has been retrieved
*/
- (void)getDiscoveredCommissioner:(int)index
clientQueue:(dispatch_queue_t _Nonnull)clientQueue
@@ -184,6 +184,16 @@
*/
- (void)disconnect:(dispatch_queue_t _Nonnull)clientQueue requestSentHandler:(nullable void (^)())requestSentHandler;
+/**
+ @brief Start the Matter server and reconnect to a previously connected Video Player (if any)
+ */
+- (void)startMatterServer;
+
+/**
+ @brief Stop the Matter server
+ */
+- (void)stopMatterServer;
+
/*!
@brief Send a ContentLauncher:LaunchURL request to a TV
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm
index d2620f6..cd86084 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm
@@ -38,10 +38,14 @@
@property OnboardingPayload * _Nonnull onboardingPayload;
-@property chip::DeviceLayer::CommissionableDataProviderImpl * commissionableDataProvider;
+@property CommissionableDataProviderImpl * commissionableDataProvider;
@property chip::Credentials::DeviceAttestationCredentialsProvider * deviceAttestationCredentialsProvider;
+@property chip::CommonCaseDeviceServerInitParams * serverInitParams;
+
+@property TargetVideoPlayerInfo * previouslyConnectedVideoPlayer;
+
// queue used to serialize all work performed by the CastingServerBridge
@property (atomic) dispatch_queue_t chipWorkQueue;
@@ -111,10 +115,15 @@
ChipLogProgress(AppServer, "CastingServerBridge().initApp() called");
CHIP_ERROR err = CHIP_NO_ERROR;
- _commissionableDataProvider = new chip::DeviceLayer::CommissionableDataProviderImpl();
+ _commissionableDataProvider = new CommissionableDataProviderImpl();
_deviceAttestationCredentialsProvider = chip::Credentials::Examples::GetExampleDACProvider();
+
_appParameters = appParameters;
AppParams cppAppParams;
+ uint32_t setupPasscode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE;
+ uint16_t setupDiscriminator = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR;
+ uint32_t spake2pIterationCount;
+ chip::ByteSpan spake2pSaltSpan, spake2pVerifierSpan;
if (_appParameters != nil) {
err = [ConversionUtils convertToCppAppParamsInfoFrom:_appParameters outAppParams:cppAppParams];
if (err != CHIP_NO_ERROR) {
@@ -123,22 +132,31 @@
}
// set fields in commissionableDataProvider
- _commissionableDataProvider->SetSpake2pIterationCount(_appParameters.spake2pIterationCount);
- if (_appParameters.spake2pSalt != nil) {
- chip::ByteSpan spake2pSaltSpan
- = chip::ByteSpan(static_cast<const uint8_t *>(_appParameters.spake2pSalt.bytes), _appParameters.spake2pSalt.length);
- _commissionableDataProvider->SetSpake2pSalt(spake2pSaltSpan);
- }
-
- if (_appParameters.spake2pVerifier != nil) {
- chip::ByteSpan spake2pVerifierSpan = chip::ByteSpan(
- static_cast<const uint8_t *>(_appParameters.spake2pVerifier.bytes), _appParameters.spake2pVerifier.length);
- _commissionableDataProvider->SetSpake2pSalt(spake2pVerifierSpan);
- }
-
if (_appParameters.onboardingPayload != nil) {
- _commissionableDataProvider->SetSetupPasscode(_appParameters.onboardingPayload.setupPasscode);
- _commissionableDataProvider->SetSetupDiscriminator(_appParameters.onboardingPayload.setupDiscriminator);
+ setupPasscode = _appParameters.onboardingPayload.setupPasscode > 0 ? _appParameters.onboardingPayload.setupPasscode
+ : CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE;
+ setupDiscriminator = _appParameters.onboardingPayload.setupDiscriminator > 0
+ ? _appParameters.onboardingPayload.setupDiscriminator
+ : CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR;
+ }
+ spake2pIterationCount = _appParameters.spake2pIterationCount;
+ if (_appParameters.spake2pSaltBase64 != nil) {
+ spake2pSaltSpan = chip::ByteSpan(
+ static_cast<const uint8_t *>(_appParameters.spake2pSaltBase64.bytes), _appParameters.spake2pSaltBase64.length);
+ }
+
+ if (_appParameters.spake2pVerifierBase64 != nil) {
+ chip::ByteSpan spake2pVerifierSpan
+ = chip::ByteSpan(static_cast<const uint8_t *>(_appParameters.spake2pVerifierBase64.bytes),
+ _appParameters.spake2pVerifierBase64.length);
+ }
+
+ err = _commissionableDataProvider->Initialize(_appParameters.spake2pVerifierBase64 != nil ? &spake2pVerifierSpan : nil,
+ _appParameters.spake2pSaltBase64 != nil ? &spake2pSaltSpan : nil, spake2pIterationCount, setupPasscode,
+ setupDiscriminator);
+ if (err != CHIP_NO_ERROR) {
+ ChipLogError(AppServer, "Failed to initialize CommissionableDataProvider: %s", ErrorStr(err));
+ return;
}
if (_appParameters.deviceAttestationCredentials != nil) {
@@ -182,8 +200,6 @@
}
chip::DeviceLayer::SetCommissionableDataProvider(_commissionableDataProvider);
- uint32_t setupPasscode = 0;
- uint16_t setupDiscriminator = 0;
_commissionableDataProvider->GetSetupPasscode(setupPasscode);
_commissionableDataProvider->GetSetupDiscriminator(setupDiscriminator);
_onboardingPayload = [[OnboardingPayload alloc] initWithSetupPasscode:setupPasscode setupDiscriminator:setupDiscriminator];
@@ -199,14 +215,14 @@
}
// init app Server
- static chip::CommonCaseDeviceServerInitParams initParams;
- err = initParams.InitializeStaticResourcesBeforeServerInit();
+ _serverInitParams = new chip::CommonCaseDeviceServerInitParams();
+ err = _serverInitParams->InitializeStaticResourcesBeforeServerInit();
if (err != CHIP_NO_ERROR) {
ChipLogError(AppServer, "InitializeStaticResourcesBeforeServerInit failed: %s", ErrorStr(err));
return;
}
- err = chip::Server::GetInstance().Init(initParams);
+ err = chip::Server::GetInstance().Init(*_serverInitParams);
if (err != CHIP_NO_ERROR) {
ChipLogError(AppServer, "chip::Server init failed: %s", ErrorStr(err));
return;
@@ -261,7 +277,7 @@
{
ChipLogProgress(AppServer, "CastingServerBridge().getDiscoveredCommissioner() called");
- dispatch_async(_chipWorkQueue, ^{
+ dispatch_sync(_chipWorkQueue, ^{
chip::Optional<TargetVideoPlayerInfo *> associatedConnectableVideoPlayer;
DiscoveredNodeData * commissioner = nil;
const chip::Dnssd::DiscoveredNodeData * cppDiscoveredNodeData
@@ -275,7 +291,7 @@
}
}
- dispatch_async(clientQueue, ^{
+ dispatch_sync(clientQueue, ^{
discoveredCommissionerHandler(commissioner);
});
});
@@ -407,10 +423,9 @@
ChipLogProgress(AppServer, "CastingServerBridge().getActiveTargetVideoPlayers() called");
dispatch_async(_chipWorkQueue, ^{
- NSMutableArray * videoPlayers = nil;
+ NSMutableArray * videoPlayers = [NSMutableArray new];
TargetVideoPlayerInfo * cppTargetVideoPlayerInfo = CastingServer::GetInstance()->GetActiveTargetVideoPlayer();
- if (cppTargetVideoPlayerInfo != nullptr) {
- videoPlayers = [NSMutableArray new];
+ if (cppTargetVideoPlayerInfo != nullptr && cppTargetVideoPlayerInfo->IsInitialized()) {
videoPlayers[0] = [ConversionUtils convertToObjCVideoPlayerFrom:cppTargetVideoPlayerInfo];
}
@@ -495,6 +510,79 @@
});
}
+- (void)startMatterServer
+{
+ ChipLogProgress(AppServer, "CastingServerBridge().startMatterServer() called");
+
+ dispatch_sync(_chipWorkQueue, ^{
+ // Initialize the Matter server
+ CHIP_ERROR err = chip::Server::GetInstance().Init(*self->_serverInitParams);
+ if (err != CHIP_NO_ERROR) {
+ ChipLogError(AppServer, "chip::Server init failed: %s", ErrorStr(err));
+ return;
+ }
+
+ // Now reconnect to the VideoPlayer the casting app was previously connected to (if any)
+ if (self->_previouslyConnectedVideoPlayer != nil) {
+ ChipLogProgress(
+ AppServer, "CastingServerBridge().startMatterServer() reconnecting to previously connected VideoPlayer...");
+ err = CastingServer::GetInstance()->VerifyOrEstablishConnection(
+ *(self->_previouslyConnectedVideoPlayer), [](TargetVideoPlayerInfo * cppTargetVideoPlayerInfo) {},
+ [](CHIP_ERROR err) {}, [](TargetEndpointInfo * cppTargetEndpointInfo) {});
+ }
+ });
+}
+
+- (void)stopMatterServer
+{
+ ChipLogProgress(AppServer, "CastingServerBridge().stopMatterServer() called");
+
+ dispatch_sync(_chipWorkQueue, ^{
+ // capture pointer to previouslyConnectedVideoPlayer, to be deleted
+ TargetVideoPlayerInfo * videoPlayerForDeletion
+ = self->_previouslyConnectedVideoPlayer == nil ? nil : self->_previouslyConnectedVideoPlayer;
+
+ // On shutting down the Matter server, the casting app will be automatically disconnected from any Video Players it was
+ // connected to. Save the VideoPlayer that the casting app was targetting and connected to, so we can reconnect to it on
+ // re-starting the Matter server.
+ TargetVideoPlayerInfo * currentTargetVideoPlayerInfo = CastingServer::GetInstance()->GetActiveTargetVideoPlayer();
+ if (currentTargetVideoPlayerInfo != nil && currentTargetVideoPlayerInfo->IsInitialized()
+ && currentTargetVideoPlayerInfo->GetOperationalDeviceProxy() != nil) {
+ self->_previouslyConnectedVideoPlayer = new TargetVideoPlayerInfo();
+ self->_previouslyConnectedVideoPlayer->Initialize(currentTargetVideoPlayerInfo->GetNodeId(),
+ currentTargetVideoPlayerInfo->GetFabricIndex(), nullptr, nullptr, currentTargetVideoPlayerInfo->GetVendorId(),
+ currentTargetVideoPlayerInfo->GetProductId(), currentTargetVideoPlayerInfo->GetDeviceType(),
+ currentTargetVideoPlayerInfo->GetDeviceName(), currentTargetVideoPlayerInfo->GetNumIPs(),
+ const_cast<chip::Inet::IPAddress *>(currentTargetVideoPlayerInfo->GetIpAddresses()));
+
+ TargetEndpointInfo * prevEndpoints = self->_previouslyConnectedVideoPlayer->GetEndpoints();
+ if (prevEndpoints != nullptr) {
+ for (size_t i = 0; i < kMaxNumberOfEndpoints; i++) {
+ prevEndpoints[i].Reset();
+ }
+ }
+ TargetEndpointInfo * currentEndpoints = currentTargetVideoPlayerInfo->GetEndpoints();
+ for (size_t i = 0; i < kMaxNumberOfEndpoints && currentEndpoints[i].IsInitialized(); i++) {
+ prevEndpoints[i].Initialize(currentEndpoints[i].GetEndpointId());
+ chip::ClusterId * currentClusters = currentEndpoints[i].GetClusters();
+ for (size_t j = 0; j < kMaxNumberOfClustersPerEndpoint && currentClusters[j] != chip::kInvalidClusterId; j++) {
+ prevEndpoints[i].AddCluster(currentClusters[j]);
+ }
+ }
+ } else {
+ self->_previouslyConnectedVideoPlayer = nil;
+ }
+
+ // Now shutdown the Matter server
+ chip::Server::GetInstance().Shutdown();
+
+ // Delete the old previouslyConnectedVideoPlayer, if non-nil
+ if (videoPlayerForDeletion != nil) {
+ delete videoPlayerForDeletion;
+ }
+ });
+}
+
- (void)disconnect:(dispatch_queue_t _Nonnull)clientQueue requestSentHandler:(nullable void (^)())requestSentHandler
{
ChipLogProgress(AppServer, "CastingServerBridge().disconnect() called");
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.hpp b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.hpp
index b05ce4a..979c7c7 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.hpp
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.hpp
@@ -1,6 +1,7 @@
-/**
+/*
*
- * Copyright (c) 2020-2022 Project CHIP Authors
+ * Copyright (c) 2022 Project CHIP Authors
+ * All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,166 +16,47 @@
* limitations under the License.
*/
-#ifndef CommissionableDataProviderImpl_h
-#define CommissionableDataProviderImpl_h
-
#pragma once
#include <lib/core/CHIPError.h>
-#include <lib/support/Span.h>
+#include <lib/core/Optional.h>
#include <platform/CommissionableDataProvider.h>
+#include <stdint.h>
+#include <vector>
-namespace chip {
-namespace DeviceLayer {
-
-class CommissionableDataProviderImpl : public CommissionableDataProvider
+class CommissionableDataProviderImpl : public chip::DeviceLayer::CommissionableDataProvider
{
public:
- CommissionableDataProviderImpl() {}
-
- CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override
- {
- if (mSetupPasscode > 0)
- {
- setupPasscode = mSetupPasscode;
- }
- else
- {
- setupPasscode = kDefaultTestPasscode;
- }
-
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override
- {
- mSetupPasscode = setupPasscode;
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override
- {
- if (mSetupDiscriminator > 0)
- {
- setupDiscriminator = mSetupDiscriminator;
- }
- else
- {
- setupDiscriminator = kDefaultTestDiscriminator;
- }
-
- return CHIP_NO_ERROR;
- }
-
+ CHIP_ERROR Initialize(chip::ByteSpan * spake2pVerifierBase64, chip::ByteSpan * spake2pSaltBase64,
+ uint32_t spake2pIterationCount, uint32_t setupPasscode, uint16_t discriminator);
+ CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override;
CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override
{
- mSetupDiscriminator = setupDiscriminator;
- return CHIP_NO_ERROR;
+ // We don't support overriding the discriminator post-init (it is deprecated!)
+ return CHIP_ERROR_NOT_IMPLEMENTED;
}
-
- CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override
+ CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override;
+ CHIP_ERROR GetSpake2pSalt(chip::MutableByteSpan & saltBuf) override;
+ CHIP_ERROR GetSpake2pVerifier(chip::MutableByteSpan & verifierBuf, size_t & outVerifierLen) override;
+ CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override;
+ CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override
{
- if (mSpake2pIterationCount > 0)
- {
- iterationCount = mSpake2pIterationCount;
- }
- else
- {
- iterationCount = kDefaultTestVerifierIterationCount;
- }
-
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR SetSpake2pIterationCount(uint32_t iterationCount)
- {
- mSpake2pIterationCount = iterationCount;
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR GetSpake2pSalt(MutableByteSpan & saltBuf) override
- {
- size_t saltLen = mSpake2pSalt.data() == nullptr ? sizeof(kDefaultTestVerifierSalt) : mSpake2pSalt.size();
- if (saltBuf.size() < saltLen)
- {
- return CHIP_ERROR_BUFFER_TOO_SMALL;
- }
-
- memcpy(saltBuf.data(), mSpake2pSalt.data() == nullptr ? kDefaultTestVerifierSalt : mSpake2pSalt.data(), saltLen);
- saltBuf.reduce_size(saltLen);
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR SetSpake2pSalt(ByteSpan saltBuf)
- {
- size_t saltLen = saltBuf.size();
- if (chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length < saltLen)
- {
- return CHIP_ERROR_BUFFER_TOO_SMALL;
- }
-
- mSpake2pSalt = MutableByteSpan(mSpake2pSaltBuf, chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length);
-
- memcpy(mSpake2pSalt.data(), saltBuf.data(), saltLen);
- mSpake2pSalt.reduce_size(saltLen);
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & outVerifierLen) override
- {
- outVerifierLen = mSpake2pVerifier.data() == nullptr ? sizeof(kDefaultTestVerifier) : mSpake2pVerifier.size();
- if (verifierBuf.size() < outVerifierLen)
- {
- return CHIP_ERROR_BUFFER_TOO_SMALL;
- }
- memcpy(verifierBuf.data(), mSpake2pVerifier.data() == nullptr ? kDefaultTestVerifier : mSpake2pVerifier.data(),
- outVerifierLen);
- verifierBuf.reduce_size(outVerifierLen);
- return CHIP_NO_ERROR;
- }
-
- CHIP_ERROR SetSpake2pVerifier(MutableByteSpan verifierBuf)
- {
- size_t inVerifierBufLen = verifierBuf.size();
- if (chip::Crypto::kSpake2p_VerifierSerialized_Length < inVerifierBufLen)
- {
- return CHIP_ERROR_BUFFER_TOO_SMALL;
- }
-
- mSpake2pVerifier = MutableByteSpan(mSpake2pSaltBuf, chip::Crypto::kSpake2p_VerifierSerialized_Length);
-
- memcpy(mSpake2pVerifier.data(), verifierBuf.data(), inVerifierBufLen);
- mSpake2pVerifier.reduce_size(inVerifierBufLen);
- return CHIP_NO_ERROR;
+ // We don't support overriding the passcode post-init (it is deprecated!)
+ return CHIP_ERROR_NOT_IMPLEMENTED;
}
private:
- static constexpr uint32_t kDefaultTestPasscode = 20202021;
- static constexpr uint16_t kDefaultTestDiscriminator = 3840;
- static constexpr uint32_t kDefaultTestVerifierIterationCount = 1000;
- static constexpr uint8_t kDefaultTestVerifierSalt[16] = {
- 0x53, 0x50, 0x41, 0x4b, 0x45, 0x32, 0x50, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x53, 0x61, 0x6c, 0x74,
- };
- static constexpr uint8_t kDefaultTestVerifier[97] = {
- 0xb9, 0x61, 0x70, 0xaa, 0xe8, 0x03, 0x34, 0x68, 0x84, 0x72, 0x4f, 0xe9, 0xa3, 0xb2, 0x87, 0xc3, 0x03, 0x30, 0xc2, 0xa6,
- 0x60, 0x37, 0x5d, 0x17, 0xbb, 0x20, 0x5a, 0x8c, 0xf1, 0xae, 0xcb, 0x35, 0x04, 0x57, 0xf8, 0xab, 0x79, 0xee, 0x25, 0x3a,
- 0xb6, 0xa8, 0xe4, 0x6b, 0xb0, 0x9e, 0x54, 0x3a, 0xe4, 0x22, 0x73, 0x6d, 0xe5, 0x01, 0xe3, 0xdb, 0x37, 0xd4, 0x41, 0xfe,
- 0x34, 0x49, 0x20, 0xd0, 0x95, 0x48, 0xe4, 0xc1, 0x82, 0x40, 0x63, 0x0c, 0x4f, 0xf4, 0x91, 0x3c, 0x53, 0x51, 0x38, 0x39,
- 0xb7, 0xc0, 0x7f, 0xcc, 0x06, 0x27, 0xa1, 0xb8, 0x57, 0x3a, 0x14, 0x9f, 0xcd, 0x1f, 0xa4, 0x66, 0xcf,
- };
-
- uint32_t mSetupPasscode = 0;
- uint16_t mSetupDiscriminator = 0;
- uint32_t mSpake2pIterationCount = 0;
-
- uint8_t mSpake2pSaltBuf[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length];
- MutableByteSpan mSpake2pSalt;
-
- MutableByteSpan mSpake2pVerifier;
- uint8_t mSpake2pVerifierBuf[chip::Crypto::kSpake2p_VerifierSerialized_Length];
+ friend CommissionableDataProviderImpl & CommissionableDataProviderMgrImpl();
+ static CommissionableDataProviderImpl sInstance;
+ bool mFirstUpdated = false;
+ std::vector<uint8_t> mSerializedPaseVerifier;
+ std::vector<uint8_t> mPaseSalt;
+ uint32_t mPaseIterationCount = 0;
+ chip::Optional<uint32_t> mSetupPasscode;
+ uint16_t mDiscriminator = 0;
};
-} // namespace DeviceLayer
-} // namespace chip
-
-#endif /* CommissionableDataProviderImpl_h */
+inline CommissionableDataProviderImpl & CommissionableDataProviderMgrImpl()
+{
+ return CommissionableDataProviderImpl::sInstance;
+}
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.mm
new file mode 100644
index 0000000..c1e7c6e
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.mm
@@ -0,0 +1,205 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CommissionableDataProviderImpl.hpp"
+
+#include <cstdint>
+#include <string.h>
+
+#include <crypto/CHIPCryptoPAL.h>
+#include <lib/support/Base64.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/Span.h>
+#include <lib/support/logging/CHIPLogging.h>
+#include <platform/CHIPDeviceConfig.h>
+
+using namespace chip;
+using namespace chip::Crypto;
+
+namespace {
+
+CHIP_ERROR GeneratePaseSalt(std::vector<uint8_t> & spake2pSaltVector)
+{
+ constexpr size_t kSaltLen = kSpake2p_Max_PBKDF_Salt_Length;
+ spake2pSaltVector.resize(kSaltLen);
+ return DRBG_get_bytes(spake2pSaltVector.data(), spake2pSaltVector.size());
+}
+
+} // namespace
+
+CommissionableDataProviderImpl CommissionableDataProviderImpl::sInstance;
+
+CHIP_ERROR CommissionableDataProviderImpl::Initialize(chip::ByteSpan * spake2pVerifierBase64, chip::ByteSpan * spake2pSaltBase64,
+ uint32_t spake2pIterationCount, uint32_t setupPasscode, uint16_t discriminator)
+{
+ VerifyOrReturnLogError(discriminator <= chip::kMaxDiscriminatorValue, CHIP_ERROR_INVALID_ARGUMENT);
+
+ if (spake2pIterationCount == 0) {
+ spake2pIterationCount = CHIP_DEVICE_CONFIG_USE_TEST_SPAKE2P_ITERATION_COUNT;
+ }
+ VerifyOrReturnLogError(
+ static_cast<uint32_t>(spake2pIterationCount) >= kSpake2p_Min_PBKDF_Iterations, CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnLogError(
+ static_cast<uint32_t>(spake2pIterationCount) <= kSpake2p_Max_PBKDF_Iterations, CHIP_ERROR_INVALID_ARGUMENT);
+
+ const bool havePaseVerifier = (spake2pVerifierBase64 != nullptr);
+ const bool havePaseSalt = (spake2pSaltBase64 != nullptr);
+ VerifyOrReturnLogError(!havePaseVerifier || (havePaseVerifier && havePaseSalt), CHIP_ERROR_INVALID_ARGUMENT);
+
+ CHIP_ERROR err;
+ // read verifier from paramter if provided
+ Spake2pVerifier providedVerifier;
+ std::vector<uint8_t> serializedSpake2pVerifier(kSpake2p_VerifierSerialized_Length);
+ if (havePaseVerifier) {
+ size_t maxBase64Size = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length);
+ VerifyOrReturnLogError(static_cast<unsigned>(spake2pVerifierBase64->size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT);
+
+ size_t decodedLen = chip::Base64Decode32(reinterpret_cast<const char *>(spake2pVerifierBase64->data()),
+ static_cast<uint32_t>(spake2pVerifierBase64->size()), reinterpret_cast<uint8_t *>(serializedSpake2pVerifier.data()));
+ VerifyOrReturnLogError(decodedLen == chip::Crypto::kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INVALID_ARGUMENT);
+
+ chip::MutableByteSpan verifierSpan { serializedSpake2pVerifier.data(), decodedLen };
+ err = providedVerifier.Deserialize(verifierSpan);
+ VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
+
+ ChipLogProgress(Support, "Got externally provided verifier, using it.");
+ }
+
+ // read salt from paramter if provided or generate one
+ std::vector<uint8_t> spake2pSalt(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length);
+ if (!havePaseSalt) {
+ ChipLogProgress(Support, "CommissionableDataProviderImpl didn't get a PASE salt, generating one.");
+ err = GeneratePaseSalt(spake2pSalt);
+ VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
+ } else {
+ size_t maxBase64Size = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length);
+ VerifyOrReturnLogError(static_cast<unsigned>(spake2pSaltBase64->size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT);
+
+ size_t decodedLen = chip::Base64Decode32(reinterpret_cast<const char *>(spake2pSaltBase64->data()),
+ static_cast<uint32_t>(spake2pSaltBase64->size()), reinterpret_cast<uint8_t *>(spake2pSalt.data()));
+ VerifyOrReturnLogError(decodedLen >= chip::Crypto::kSpake2p_Min_PBKDF_Salt_Length
+ && decodedLen <= chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length,
+ CHIP_ERROR_INVALID_ARGUMENT);
+ spake2pSalt.resize(decodedLen);
+ }
+
+ // generate verifier from passcode if provided
+ const bool havePasscode = (setupPasscode > kMinSetupPasscode && setupPasscode < kMaxSetupPasscode);
+ Spake2pVerifier passcodeVerifier;
+ std::vector<uint8_t> serializedPasscodeVerifier(kSpake2p_VerifierSerialized_Length);
+ chip::MutableByteSpan saltSpan { spake2pSalt.data(), spake2pSalt.size() };
+ if (havePasscode) {
+ uint32_t u32SetupPasscode = static_cast<uint32_t>(setupPasscode);
+ err = passcodeVerifier.Generate(spake2pIterationCount, saltSpan, u32SetupPasscode);
+ VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
+
+ chip::MutableByteSpan verifierSpan { serializedPasscodeVerifier.data(), serializedPasscodeVerifier.size() };
+ err = passcodeVerifier.Serialize(verifierSpan);
+ VerifyOrReturnLogError(err == CHIP_NO_ERROR, err);
+ }
+
+ // Make sure we actually have a verifier
+ VerifyOrReturnLogError(havePasscode || havePaseVerifier, CHIP_ERROR_INVALID_ARGUMENT);
+
+ // If both passcode and external verifier were provided, validate they match, otherwise
+ // it's ambiguous.
+ if (havePasscode && havePaseVerifier) {
+ VerifyOrReturnLogError(serializedPasscodeVerifier == serializedSpake2pVerifier, CHIP_ERROR_INVALID_ARGUMENT);
+ ChipLogProgress(Support, "Validated externally provided passcode matches the one generated from provided passcode.");
+ }
+
+ // External PASE verifier takes precedence when present (even though it is identical to passcode-based
+ // one when the latter is present).
+ if (havePaseVerifier) {
+ mSerializedPaseVerifier = std::move(serializedSpake2pVerifier);
+ } else {
+ mSerializedPaseVerifier = std::move(serializedPasscodeVerifier);
+ }
+ mDiscriminator = discriminator;
+ mPaseSalt = std::move(spake2pSalt);
+ mPaseIterationCount = spake2pIterationCount;
+ if (havePasscode) {
+ mSetupPasscode.SetValue(setupPasscode);
+ }
+
+ // Set to global CommissionableDataProvider once success first time
+ if (!mFirstUpdated) {
+ DeviceLayer::SetCommissionableDataProvider(this);
+ }
+ mFirstUpdated = true;
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CommissionableDataProviderImpl::GetSetupDiscriminator(uint16_t & setupDiscriminator)
+{
+ VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
+ setupDiscriminator = mDiscriminator;
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pIterationCount(uint32_t & iterationCount)
+{
+ ChipLogProgress(AppServer, "CommissionableDataProviderImpl::GetSpake2pIterationCount called");
+ VerifyOrReturnLogError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
+ iterationCount = mPaseIterationCount;
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pSalt(chip::MutableByteSpan & saltBuf)
+{
+ ChipLogProgress(AppServer, "CommissionableDataProviderImpl::GetSpake2pSalt called");
+ VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
+
+ VerifyOrReturnError(saltBuf.size() >= kSpake2p_Max_PBKDF_Salt_Length, CHIP_ERROR_BUFFER_TOO_SMALL);
+ memcpy(saltBuf.data(), mPaseSalt.data(), mPaseSalt.size());
+ saltBuf.reduce_size(mPaseSalt.size());
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CommissionableDataProviderImpl::GetSpake2pVerifier(chip::MutableByteSpan & verifierBuf, size_t & outVerifierLen)
+{
+ ChipLogProgress(AppServer, "CommissionableDataProviderImpl::GetSpake2pVerifier called");
+ VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
+
+ // By now, serialized verifier from Init should be correct size
+ VerifyOrReturnError(mSerializedPaseVerifier.size() == kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INTERNAL);
+
+ outVerifierLen = mSerializedPaseVerifier.size();
+ VerifyOrReturnError(verifierBuf.size() >= outVerifierLen, CHIP_ERROR_BUFFER_TOO_SMALL);
+ memcpy(verifierBuf.data(), mSerializedPaseVerifier.data(), mSerializedPaseVerifier.size());
+ verifierBuf.reduce_size(mSerializedPaseVerifier.size());
+
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR CommissionableDataProviderImpl::GetSetupPasscode(uint32_t & setupPasscode)
+{
+ ChipLogProgress(AppServer, "CommissionableDataProviderImpl::GetSetupPasscode called");
+ VerifyOrReturnError(mFirstUpdated, CHIP_ERROR_INCORRECT_STATE);
+
+ // Pretend not implemented if we don't have a passcode value externally set
+ if (!mSetupPasscode.HasValue()) {
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+ }
+
+ setupPasscode = mSetupPasscode.Value();
+ ChipLogProgress(AppServer, "CommissionableDataProviderImpl::GetSetupPasscode returning value %d", setupPasscode);
+ return CHIP_NO_ERROR;
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift
index daef2e7..322f1a1 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift
@@ -22,6 +22,8 @@
struct TvCastingApp: App {
let Log = Logger(subsystem: "com.matter.casting",
category: "TvCastingApp")
+ @State
+ var firstAppActivation: Bool = true
var body: some Scene {
WindowGroup {
@@ -49,6 +51,36 @@
})
}
})
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.willResignActiveNotification")
+ if let castingServerBridge = CastingServerBridge.getSharedInstance()
+ {
+ castingServerBridge.stopMatterServer()
+ }
+ }
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.didBecomeActiveNotification")
+ if(!firstAppActivation)
+ {
+ if let castingServerBridge = CastingServerBridge.getSharedInstance()
+ {
+ castingServerBridge.startMatterServer()
+ }
+ }
+ firstAppActivation = false
+ }
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.didEnterBackgroundNotification")
+ }
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.didFinishLaunchingNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.didFinishLaunchingNotification")
+ }
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.willEnterForegroundNotification")
+ }
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)) { _ in
+ self.Log.info("TvCastingApp: UIApplication.willTerminateNotification")
+ }
}
}
}
diff --git a/examples/tv-casting-app/darwin/args.gni b/examples/tv-casting-app/darwin/args.gni
index 1171f4d..6435338 100644
--- a/examples/tv-casting-app/darwin/args.gni
+++ b/examples/tv-casting-app/darwin/args.gni
@@ -29,3 +29,5 @@
chip_enable_additional_data_advertising = true
chip_enable_rotating_device_id = true
+
+chip_config_network_layer_ble = false
diff --git a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h
index b8daebc..e11b968 100644
--- a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h
+++ b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h
@@ -47,6 +47,10 @@
#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_NAME 1
+#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021
+
+#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00
+
#define CHIP_DEVICE_CONFIG_DEVICE_NAME "Test TV casting app"
#define CHIP_DEVICE_CONFIG_ENABLE_PAIRING_AUTOSTART 0