From 52588140859a28faf23de9eed5f5176f4f6da437 Mon Sep 17 00:00:00 2001 From: Sharad Binjola <31142146+sharadb-amazon@users.noreply.github.com> Date: Fri, 23 Dec 2022 10:49:39 -0800 Subject: [PATCH] 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 --- examples/tv-casting-app/android/args.gni | 2 + .../project.pbxproj | 4 + .../MatterTvCastingBridge/AppParameters.h | 4 +- .../CastingServerBridge.h | 12 +- .../CastingServerBridge.mm | 136 ++++++++++-- .../CommissionableDataProviderImpl.hpp | 180 +++------------ .../CommissionableDataProviderImpl.mm | 205 ++++++++++++++++++ .../TvCasting/TvCasting/TvCastingApp.swift | 32 +++ examples/tv-casting-app/darwin/args.gni | 2 + .../include/CHIPProjectAppConfig.h | 4 + 10 files changed, 405 insertions(+), 176 deletions(-) create mode 100644 examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CommissionableDataProviderImpl.mm diff --git a/examples/tv-casting-app/android/args.gni b/examples/tv-casting-app/android/args.gni index 802f54f8b49484..e3a20c5055dd91 100644 --- a/examples/tv-casting-app/android/args.gni +++ b/examples/tv-casting-app/android/args.gni @@ -29,3 +29,5 @@ chip_build_libshell = true 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 d85d5a6f2d4f91..ef0579eca8eb50 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 = ""; }; 3CCB873D286A593700771BAD /* CastingServerBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CastingServerBridge.mm; sourceTree = ""; }; 3CCB873E286A593700771BAD /* ConversionUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConversionUtils.mm; sourceTree = ""; }; + 3CE868F32946D76200FCB92B /* CommissionableDataProviderImpl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CommissionableDataProviderImpl.mm; sourceTree = ""; }; 3CF8532528E37ED800F07B9F /* MatterError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MatterError.h; sourceTree = ""; }; 3CF8532628E37F1000F07B9F /* MatterError.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MatterError.mm; sourceTree = ""; }; /* End PBXFileReference section */ @@ -125,6 +127,7 @@ 3C26AC8B2926FE0C00BA6881 /* DeviceAttestationCredentialsProviderImpl.hpp */, 3C26AC8F2927008900BA6881 /* DeviceAttestationCredentialsProviderImpl.mm */, 3C0D9CDF2920A30C00D3332B /* CommissionableDataProviderImpl.hpp */, + 3CE868F32946D76200FCB92B /* CommissionableDataProviderImpl.mm */, ); path = MatterTvCastingBridge; sourceTree = ""; @@ -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 929722387a7388..9e445e25fa63d7 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 8c8f8c83e71849..8f1d7d17bf95ac 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 d2620f6ca69e01..cd860847c4b300 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 @@ @interface CastingServerBridge () @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 @@ - (void)initApp:(AppParameters * _Nullable)appParameters 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 @@ - (void)initApp:(AppParameters * _Nullable)appParameters } // set fields in commissionableDataProvider - _commissionableDataProvider->SetSpake2pIterationCount(_appParameters.spake2pIterationCount); - if (_appParameters.spake2pSalt != nil) { - chip::ByteSpan spake2pSaltSpan - = chip::ByteSpan(static_cast(_appParameters.spake2pSalt.bytes), _appParameters.spake2pSalt.length); - _commissionableDataProvider->SetSpake2pSalt(spake2pSaltSpan); + if (_appParameters.onboardingPayload != nil) { + 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(_appParameters.spake2pSaltBase64.bytes), _appParameters.spake2pSaltBase64.length); } - if (_appParameters.spake2pVerifier != nil) { - chip::ByteSpan spake2pVerifierSpan = chip::ByteSpan( - static_cast(_appParameters.spake2pVerifier.bytes), _appParameters.spake2pVerifier.length); - _commissionableDataProvider->SetSpake2pSalt(spake2pVerifierSpan); + if (_appParameters.spake2pVerifierBase64 != nil) { + chip::ByteSpan spake2pVerifierSpan + = chip::ByteSpan(static_cast(_appParameters.spake2pVerifierBase64.bytes), + _appParameters.spake2pVerifierBase64.length); } - if (_appParameters.onboardingPayload != nil) { - _commissionableDataProvider->SetSetupPasscode(_appParameters.onboardingPayload.setupPasscode); - _commissionableDataProvider->SetSetupDiscriminator(_appParameters.onboardingPayload.setupDiscriminator); + 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 @@ - (void)initApp:(AppParameters * _Nullable)appParameters } 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 @@ - (void)initApp:(AppParameters * _Nullable)appParameters } // 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 @@ - (void)getDiscoveredCommissioner:(int)index { ChipLogProgress(AppServer, "CastingServerBridge().getDiscoveredCommissioner() called"); - dispatch_async(_chipWorkQueue, ^{ + dispatch_sync(_chipWorkQueue, ^{ chip::Optional associatedConnectableVideoPlayer; DiscoveredNodeData * commissioner = nil; const chip::Dnssd::DiscoveredNodeData * cppDiscoveredNodeData @@ -275,7 +291,7 @@ - (void)getDiscoveredCommissioner:(int)index } } - dispatch_async(clientQueue, ^{ + dispatch_sync(clientQueue, ^{ discoveredCommissionerHandler(commissioner); }); }); @@ -407,10 +423,9 @@ - (void)getActiveTargetVideoPlayers:(dispatch_queue_t _Nonnull)clientQueue 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)shutdownAllSubscriptions:(dispatch_queue_t _Nonnull)clientQueue requestS }); } +- (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(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 b05ce4aa89a4ef..979c7c72f5e057 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 -#include +#include #include +#include +#include -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; - } - - CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) 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; + // We don't support overriding the discriminator post-init (it is deprecated!) + return CHIP_ERROR_NOT_IMPLEMENTED; } - - 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) + 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 { - 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 mSerializedPaseVerifier; + std::vector mPaseSalt; + uint32_t mPaseIterationCount = 0; + chip::Optional 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 00000000000000..c1e7c6ef9b6120 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::Crypto; + +namespace { + +CHIP_ERROR GeneratePaseSalt(std::vector & 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(spake2pIterationCount) >= kSpake2p_Min_PBKDF_Iterations, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnLogError( + static_cast(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 serializedSpake2pVerifier(kSpake2p_VerifierSerialized_Length); + if (havePaseVerifier) { + size_t maxBase64Size = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length); + VerifyOrReturnLogError(static_cast(spake2pVerifierBase64->size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT); + + size_t decodedLen = chip::Base64Decode32(reinterpret_cast(spake2pVerifierBase64->data()), + static_cast(spake2pVerifierBase64->size()), reinterpret_cast(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 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(spake2pSaltBase64->size()) <= maxBase64Size, CHIP_ERROR_INVALID_ARGUMENT); + + size_t decodedLen = chip::Base64Decode32(reinterpret_cast(spake2pSaltBase64->data()), + static_cast(spake2pSaltBase64->size()), reinterpret_cast(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 serializedPasscodeVerifier(kSpake2p_VerifierSerialized_Length); + chip::MutableByteSpan saltSpan { spake2pSalt.data(), spake2pSalt.size() }; + if (havePasscode) { + uint32_t u32SetupPasscode = static_cast(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 daef2e730d5e3a..322f1a1108d15b 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 @@ import os.log 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 @@ struct TvCastingApp: App { }) } }) + .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 1171f4d8398a89..64353389c5285f 100644 --- a/examples/tv-casting-app/darwin/args.gni +++ b/examples/tv-casting-app/darwin/args.gni @@ -29,3 +29,5 @@ chip_build_libshell = true 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 b8daebc0a86f5e..e11b96824bcd5a 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