diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index f8c0af9df1bad5..211ba98f5851c1 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -72,6 +72,7 @@ static_library("chip-tool-utils") { "${chip_root}/src/app/tests/suites/commands/log", "${chip_root}/src/app/tests/suites/commands/system", "${chip_root}/src/app/tests/suites/pics", + "${chip_root}/src/commissioner", "${chip_root}/src/controller/data_model", "${chip_root}/src/lib", "${chip_root}/src/platform", diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 3fd71824945e8e..3ba78b8d4e4149 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -157,7 +157,7 @@ chip::FabricId CHIPCommand::CurrentCommissionerId() return id; } -chip::Controller::DeviceCommissioner & CHIPCommand::CurrentCommissioner() +chip::Controller::DeviceController & CHIPCommand::CurrentCommissioner() { auto item = mCommissioners.find(GetIdentity()); return *item->second.get(); @@ -212,7 +212,7 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId f commissionerParams.operationalCredentialsDelegate = mCredIssuerCmds->GetCredentialIssuer(); commissionerParams.controllerVendorId = chip::VendorId::TestVendor1; - ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, *(commissioner.get()))); + ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupController(commissionerParams, *(commissioner.get()))); mCommissioners[key] = std::move(commissioner); return CHIP_NO_ERROR; diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index 85f7f5d7b30c0a..68cfd49770f8c0 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -45,7 +45,7 @@ class CHIPCommand : public Command { public: using ChipDevice = ::chip::DeviceProxy; - using ChipDeviceCommissioner = ::chip::Controller::DeviceCommissioner; + using ChipDeviceCommissioner = ::chip::Controller::DeviceController; using ChipDeviceController = ::chip::Controller::DeviceController; using IPAddress = ::chip::Inet::IPAddress; using NodeId = ::chip::NodeId; diff --git a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp index fd116833696027..03a18a4f52a979 100644 --- a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp +++ b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.cpp @@ -17,18 +17,26 @@ */ #include "DiscoverCommissionablesCommand.h" +#include #include using namespace ::chip; CHIP_ERROR DiscoverCommissionablesCommand::RunCommand() { - CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this); + ReturnErrorOnFailure( + mDnsResolver.Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState()->UDPEndPointManager())); + mDnsResolver.SetResolverDelegate(this); Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, (uint64_t) 0); - return CurrentCommissioner().DiscoverCommissionableNodes(filter); + ReturnErrorOnFailure(mDnsResolver.FindCommissionableNodes(filter)); + return CHIP_NO_ERROR; } -void DiscoverCommissionablesCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) +void DiscoverCommissionablesCommand::OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) { nodeData.LogDetail(); } + +void DiscoverCommissionablesCommand::OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) {} + +void DiscoverCommissionablesCommand::OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) {} diff --git a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.h b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.h index 1183ec53caaa08..7666dca537169d 100644 --- a/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.h +++ b/examples/chip-tool/commands/discover/DiscoverCommissionablesCommand.h @@ -20,16 +20,21 @@ #include "../common/CHIPCommand.h" -class DiscoverCommissionablesCommand : public CHIPCommand, public chip::Controller::DeviceDiscoveryDelegate +class DiscoverCommissionablesCommand : public CHIPCommand, public chip::Dnssd::ResolverDelegate { public: DiscoverCommissionablesCommand(CredentialIssuerCommands * credsIssuerConfig) : CHIPCommand("commissionables", credsIssuerConfig) {} - /////////// DeviceDiscoveryDelegate Interface ///////// - void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) override; + /////////// ResolverDelegate Interface ///////// + void OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) override; + void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override; + void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override; /////////// CHIPCommand Interface ///////// CHIP_ERROR RunCommand() override; chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(30); } + +private: + chip::Dnssd::ResolverProxy mDnsResolver; }; diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index c568bf45f5d08f..cfd09dce28c91f 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.cpp +++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp @@ -18,6 +18,7 @@ #include "PairingCommand.h" #include "platform/PlatformManager.h" +#include #include #include #include @@ -29,10 +30,10 @@ using namespace ::chip; using namespace ::chip::Controller; +using Commissionee = chip::Commissioner::Commissionee; CHIP_ERROR PairingCommand::RunCommand() { - CurrentCommissioner().RegisterPairingDelegate(this); return RunInternal(mNodeId); } @@ -85,20 +86,51 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() CHIP_ERROR PairingCommand::PairWithQRCode(NodeId remoteId) { - return CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload); + auto stateMachine = Platform::MakeShared(); + VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY); + auto onSuccess = [this, stateMachine](Commissionee & commissionee) { OnCommissioningComplete(*stateMachine.get()); }; + auto onFailure = [this, stateMachine](Commissionee & commissionee) { OnCommissioningFailure(*stateMachine.get()); }; + stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), mCredIssuerCmds->GetCredentialIssuer(), + CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword); + CHIP_ERROR err = stateMachine.get()->Commission(mOnboardingPayload, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + stateMachine.get()->Shutdown(); + } + return err; } CHIP_ERROR PairingCommand::PairWithManualCode(NodeId remoteId) { - return CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload); + auto stateMachine = Platform::MakeShared(); + VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY); + auto onSuccess = [this, stateMachine](Commissionee & commissionee) { OnCommissioningComplete(*stateMachine.get()); }; + auto onFailure = [this, stateMachine](Commissionee & commissionee) { OnCommissioningFailure(*stateMachine.get()); }; + stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), mCredIssuerCmds->GetCredentialIssuer(), + CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword); + CHIP_ERROR err = stateMachine.get()->Commission(mOnboardingPayload, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + stateMachine.get()->Shutdown(); + } + return err; } CHIP_ERROR PairingCommand::Pair(NodeId remoteId, PeerAddress address) { - RendezvousParameters params = - RendezvousParameters().SetSetupPINCode(mSetupPINCode).SetDiscriminator(mDiscriminator).SetPeerAddress(address); - CommissioningParameters commissioningParams = GetCommissioningParameters(); - return CurrentCommissioner().PairDevice(remoteId, params, commissioningParams); + auto stateMachine = Platform::MakeShared(); + VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY); + auto onSuccess = [this, stateMachine](Commissionee & commissionee) { OnCommissioningComplete(*stateMachine.get()); }; + auto onFailure = [this, stateMachine](Commissionee & commissionee) { OnCommissioningFailure(*stateMachine.get()); }; + stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), mCredIssuerCmds->GetCredentialIssuer(), + CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword); + CHIP_ERROR err = + stateMachine.get()->Commission(chip::RendezvousInformationFlag::kNone, mDiscriminator, mSetupPINCode, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + stateMachine.get()->Shutdown(); + } + return err; } CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId) @@ -109,7 +141,18 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId) case chip::Dnssd::DiscoveryFilterType::kNone: break; case chip::Dnssd::DiscoveryFilterType::kShortDiscriminator: - case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator: + case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator: { + auto stateMachine = Platform::MakeShared(); + VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY); + auto onSuccess = [this, stateMachine](Commissionee & commissionee) { OnCommissioningComplete(*stateMachine.get()); }; + auto onFailure = [this, stateMachine](Commissionee & commissionee) { OnCommissioningFailure(*stateMachine.get()); }; + stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), mCredIssuerCmds->GetCredentialIssuer(), + CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword); + CHIP_ERROR err = + stateMachine.get()->Commission(chip::RendezvousInformationFlag::kOnNetwork, static_cast(mDiscoveryFilterCode), + mSetupPINCode, onSuccess, onFailure); + return err; + } case chip::Dnssd::DiscoveryFilterType::kCompressedFabricId: case chip::Dnssd::DiscoveryFilterType::kVendorId: case chip::Dnssd::DiscoveryFilterType::kDeviceType: @@ -126,84 +169,50 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId) break; } - CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this); - return CurrentCommissioner().DiscoverCommissionableNodes(filter); + ReturnErrorOnFailure( + mDnsResolver.Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState()->UDPEndPointManager())); + mDnsResolver.SetResolverDelegate(this); + ReturnErrorOnFailure(mDnsResolver.FindCommissionableNodes(filter)); + return CHIP_NO_ERROR; } CHIP_ERROR PairingCommand::Unpair(NodeId remoteId) { - CHIP_ERROR err = CurrentCommissioner().UnpairDevice(remoteId); + CurrentCommissioner().ReleaseOperationalDevice(remoteId); + CHIP_ERROR err = CHIP_NO_ERROR; SetCommandExitStatus(err); return err; } -void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status) -{ - switch (status) - { - case DevicePairingDelegate::Status::SecurePairingSuccess: - ChipLogProgress(chipTool, "Secure Pairing Success"); - break; - case DevicePairingDelegate::Status::SecurePairingFailed: - ChipLogError(chipTool, "Secure Pairing Failed"); - break; - } -} - -void PairingCommand::OnPairingComplete(CHIP_ERROR err) -{ - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(chipTool, "Pairing Success"); - } - else - { - ChipLogProgress(chipTool, "Pairing Failure: %s", ErrorStr(err)); - } - - if (err != CHIP_NO_ERROR) - { - SetCommandExitStatus(err); - } -} - -void PairingCommand::OnPairingDeleted(CHIP_ERROR err) +void PairingCommand::OnCommissioningComplete(CommissioningStateMachine & stateMachine) { + CHIP_ERROR err = stateMachine.GrabCommissionee(CurrentCommissioner()); if (err == CHIP_NO_ERROR) { - ChipLogProgress(chipTool, "Pairing Deleted Success"); + ChipLogProgress(chipTool, "Device commissioning completed with success"); } else { - ChipLogProgress(chipTool, "Pairing Deleted Failure: %s", ErrorStr(err)); + ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err)); } - + stateMachine.Shutdown(); SetCommandExitStatus(err); } -void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err) +void PairingCommand::OnCommissioningFailure(CommissioningStateMachine & stateMachine) { - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(chipTool, "Device commissioning completed with success"); - } - else - { - ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err)); - } - - SetCommandExitStatus(err); + ChipLogProgress(chipTool, "Device commissioning Failure"); + SetCommandExitStatus(CHIP_ERROR_INTERNAL); } -void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) +void PairingCommand::OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) { const uint16_t port = nodeData.port; char buf[chip::Inet::IPAddress::kMaxStringLength]; nodeData.ipAddress[0].ToString(buf); ChipLogProgress(chipTool, "Discovered Device: %s:%u", buf, port); - // Stop Mdns discovery. Is it the right method ? - CurrentCommissioner().RegisterDeviceDiscoveryDelegate(nullptr); + mDnsResolver.Shutdown(); Inet::InterfaceId interfaceId = nodeData.ipAddress[0].IsIPv6LinkLocal() ? nodeData.interfaceId[0] : Inet::InterfaceId::Null(); PeerAddress peerAddress = PeerAddress::UDP(nodeData.ipAddress[0], port, interfaceId); @@ -213,3 +222,7 @@ void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & SetCommandExitStatus(err); } } + +void PairingCommand::OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) {} + +void PairingCommand::OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) {} diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h index 94d55969e9a0ea..2c84ebfe8f7a30 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.h +++ b/examples/chip-tool/commands/pairing/PairingCommand.h @@ -20,6 +20,7 @@ #include "../common/CHIPCommand.h" #include +#include #include #include @@ -45,10 +46,10 @@ enum class PairingNetworkType Ethernet, }; -class PairingCommand : public CHIPCommand, - public chip::Controller::DevicePairingDelegate, - public chip::Controller::DeviceDiscoveryDelegate +class PairingCommand : public CHIPCommand, public chip::Dnssd::ResolverDelegate { + using CommissioningStateMachine = chip::Commissioner::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine; + public: PairingCommand(const char * commandName, PairingMode mode, PairingNetworkType networkType, CredentialIssuerCommands * credIssuerCmds, @@ -134,14 +135,10 @@ class PairingCommand : public CHIPCommand, CHIP_ERROR RunCommand() override; chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(120); } - /////////// DevicePairingDelegate Interface ///////// - void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override; - void OnPairingComplete(CHIP_ERROR error) override; - void OnPairingDeleted(CHIP_ERROR error) override; - void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override; - - /////////// DeviceDiscoveryDelegate Interface ///////// - void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) override; + /////////// ResolverDelegate Interface ///////// + void OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) override; + void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override; + void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override; private: CHIP_ERROR RunInternal(NodeId remoteId); @@ -152,6 +149,8 @@ class PairingCommand : public CHIPCommand, CHIP_ERROR PairWithCode(NodeId remoteId, chip::SetupPayload payload); CHIP_ERROR Unpair(NodeId remoteId); chip::Controller::CommissioningParameters GetCommissioningParameters(); + void OnCommissioningComplete(CommissioningStateMachine & stateMachine); + void OnCommissioningFailure(CommissioningStateMachine & stateMachine); const PairingMode mPairingMode; const PairingNetworkType mNetworkType; @@ -167,6 +166,7 @@ class PairingCommand : public CHIPCommand, char * mOnboardingPayload; uint64_t mDiscoveryFilterCode; char * mDiscoveryFilterInstanceName; + chip::Dnssd::ResolverProxy mDnsResolver; chip::CommissioneeDeviceProxy * mDevice; chip::EndpointId mEndpointId = 0; diff --git a/src/commissioner/BUILD.gn b/src/commissioner/BUILD.gn new file mode 100644 index 00000000000000..982f2657ec1d27 --- /dev/null +++ b/src/commissioner/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright (c) 2020-2021 Project CHIP Authors +# +# 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. + +import("//build_overrides/chip.gni") +import("//build_overrides/nlassert.gni") +import("${chip_root}/src/crypto/crypto.gni") +import("${chip_root}/src/platform/device.gni") + +static_library("commissioner") { + output_name = "libCommissioner" + + sources = [ + "Commissionee.h", + "Discoverer.cpp", + "Discoverer.h", + "Events.h", + "ExampleCommissioningStateMachine.cpp", + "ExampleCommissioningStateMachine.h", + "States.h", + ] + + public_deps = [ + "${chip_root}/src/controller", + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/dnssd", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + "${chip_root}/src/setup_payload", + ] +} diff --git a/src/commissioner/Commissionee.h b/src/commissioner/Commissionee.h new file mode 100644 index 00000000000000..eb199d85001670 --- /dev/null +++ b/src/commissioner/Commissionee.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2020-2021 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace Commissioner { + +using namespace Controller; + +struct Commissionee +{ + DeviceControllerSystemState * mSystemState = nullptr; + chip::SessionHolder mPaseSession; + chip::SessionHolder mCaseSession; + chip::Optional mCommissionableNodeAddress; + chip::Optional mOperationalAddress; + chip::Optional mMrpConfig; + chip::Optional mOperationalId; +#if CONFIG_NETWORK_LAYER_BLE + chip::Optional mBleConnection; +#endif + + void Init(DeviceControllerSystemState * systemState) + { + if (mSystemState) + { + mSystemState->Release(); + } + mSystemState = systemState; + if (mSystemState) + { + mSystemState->Retain(); + } + } + +#if CONFIG_NETWORK_LAYER_BLE + CHIP_ERROR OpenBle() + { + VerifyOrReturnError(mSystemState && mBleConnection.HasValue(), CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(mSystemState->BleLayer()->NewBleConnectionByObject(mBleConnection.Value())); + return CHIP_NO_ERROR; + } + + CHIP_ERROR CloseBle() + { + VerifyOrReturnError(mSystemState && mBleConnection.HasValue(), CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(mSystemState->BleLayer()->CloseBleConnection(mBleConnection.Value())); + mBleConnection.ClearValue(); + return CHIP_NO_ERROR; + } +#endif // CONFIG_NETWORK_LAYER_BLE + + CHIP_ERROR ClosePase() + { + ReturnErrorOnFailure(ValidatePaseState()); + this->mSystemState->SessionMgr()->ExpirePairing(mPaseSession.Get()); + mPaseSession.Release(); +#if CONFIG_NETWORK_LAYER_BLE + ReturnErrorOnFailure(CloseBle()); +#endif + return CHIP_NO_ERROR; + } + + CHIP_ERROR CloseCase() + { + ReturnErrorOnFailure(ValidateCaseState()); + this->mSystemState->SessionMgr()->ExpirePairing(mCaseSession.Get()); + mCaseSession.Release(); + return CHIP_NO_ERROR; + } + + void Shutdown() + { + ClosePase(); + CloseCase(); + if (mSystemState != nullptr) + { + mSystemState->Release(); + mSystemState = nullptr; + } + } + + template + CHIP_ERROR PaseRead(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, + typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, + typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb) + { + ReturnErrorOnFailure(ValidatePaseState()); + return ReadAttribute(mSystemState->ExchangeMgr(), mPaseSession.Get(), endpointId, clusterId, + attributeId, onSuccessCb, onErrorCb); + } + + template + CHIP_ERROR PaseInvoke(EndpointId endpointId, const RequestObjectT & requestCommandData, + typename TypedCommandCallback::OnSuccessCallbackType onSuccessCb, + typename TypedCommandCallback::OnErrorCallbackType onErrorCb) + { + ReturnErrorOnFailure(ValidatePaseState()); + return InvokeCommandRequest(mSystemState->ExchangeMgr(), mPaseSession.Get(), endpointId, requestCommandData, onSuccessCb, + onErrorCb); + } + + template + CHIP_ERROR CaseRead(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, + typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, + typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb) + { + ReturnErrorOnFailure(ValidateCaseState()); + return ReadAttribute(mSystemState->ExchangeMgr(), mCaseSession.Get(), clusterId, attributeId, onSuccessCb, onErrorCb); + } + + template + CHIP_ERROR CaseInvoke(EndpointId endpointId, const RequestObjectT & requestCommandData, + typename TypedCommandCallback::OnSuccessCallbackType onSuccessCb, + typename TypedCommandCallback::OnErrorCallbackType onErrorCb) + { + ReturnErrorOnFailure(ValidateCaseState()); + return InvokeCommandRequest(mSystemState->ExchangeMgr(), mCaseSession.Get(), endpointId, requestCommandData, onSuccessCb, + onErrorCb); + } + +private: + CHIP_ERROR ValidatePaseState() + { + if (!mPaseSession || mSystemState == nullptr) + { + return CHIP_ERROR_INCORRECT_STATE; + } + return CHIP_NO_ERROR; + } + + CHIP_ERROR ValidateCaseState() + { + if (!mCaseSession || mSystemState == nullptr) + { + return CHIP_ERROR_INCORRECT_STATE; + } + return CHIP_NO_ERROR; + } +}; + +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/Discoverer.cpp b/src/commissioner/Discoverer.cpp new file mode 100644 index 00000000000000..a16760d9478a74 --- /dev/null +++ b/src/commissioner/Discoverer.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2020-2021 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 + +namespace chip { +namespace Commissioner { +namespace CommissionableNodeDiscoverer { + +#if CONFIG_NETWORK_LAYER_BLE +BleDiscoverer::BleDiscoverer(Controller::DeviceControllerSystemState & systemState, Delegate ** delegate) : + Joinable(delegate), mSystemState(systemState) +{ + mSystemState.Retain(); +} + +BleDiscoverer::~BleDiscoverer() +{ + mSystemState.Release(); +} + +void BleDiscoverer::Shutdown() +{ + if (InProgress()) + { + VerifyOrReturn(mSystemState.BleLayer()->CancelBleIncompleteConnection() == CHIP_NO_ERROR); + ReleaseShutdownToken(); + } +} + +CHIP_ERROR BleDiscoverer::StartBleDiscovery(SetupPayload & payload, Platform::SharedPtr token) +{ + VerifyOrReturnError(token.get() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorOnFailure(mSystemState.BleLayer()->NewBleConnectionByDiscriminator(payload.discriminator, this, + OnBleDiscoverySuccess, OnBleDiscoveryError)); + RetainShutdownToken(token); + return CHIP_NO_ERROR; +} + +void BleDiscoverer::TallyBleDiscovery(BLE_CONNECTION_OBJECT connection) +{ + mBleConnection.SetValue(connection); + (*mDelegate)->OnDiscovery(); + ReleaseShutdownToken(); +} + +void BleDiscoverer::TallyBleFailure() +{ + (*mDelegate)->OnDiscovery(); + ReleaseShutdownToken(); +} + +CHIP_ERROR BleDiscoverer::GetNextBleCandidate(Commissionee & commissionee) +{ + VerifyOrReturnError(mBleConnection.HasValue(), CHIP_ERROR_NOT_FOUND); + commissionee.CloseBle(); // close if connection currently exists; failure is a no-op + commissionee.mBleConnection = mBleConnection; + commissionee.mCommissionableNodeAddress.SetValue(Transport::PeerAddress::BLE()); + commissionee.mMrpConfig.ClearValue(); + ReturnErrorOnFailure(commissionee.OpenBle()); + mBleConnection.ClearValue(); + ChipLogProgress(Controller, "Commissionable node candidate identified via BLE"); + return CHIP_NO_ERROR; +} + +void BleDiscoverer::OnBleDiscoverySuccess(void * context, BLE_CONNECTION_OBJECT connection) +{ + BleDiscoverer * instance = static_cast(context); + instance->TallyBleDiscovery(connection); +} + +void BleDiscoverer::OnBleDiscoveryError(void * context, CHIP_ERROR err) +{ + ChipLogError(Controller, "BLE Commissionable Node Discovery failed: %s", ErrorStr(err)); + BleDiscoverer * instance = static_cast(context); + instance->TallyBleFailure(); +} + +#endif // CONFIG_NETWORK_LAYER_BLE + +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD +DnssdDiscoverer::DnssdDiscoverer(Controller::DeviceControllerSystemState & systemState, Delegate ** delegate) : + Joinable(delegate), mSystemState(systemState) +{ + mSystemState.Retain(); +} + +DnssdDiscoverer::~DnssdDiscoverer() +{ + mSystemState.Release(); +} + +void DnssdDiscoverer::Shutdown() +{ + if (InProgress()) + { + mDNSResolver.Shutdown(); + // TODO: Setting the instance delegate to nullptr is a workaround for + // #13227. If we do not do this, minimal mdns can call back into + // our allocated delegate proxy after it is freed. + chip::Dnssd::Resolver::Instance().SetResolverDelegate(nullptr); + ReleaseShutdownToken(); + } +} + +CHIP_ERROR DnssdDiscoverer::StartDnssdDiscovery(SetupPayload & payload, Platform::SharedPtr token) +{ + VerifyOrReturnError(token.get() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + Dnssd::DiscoveryFilter filter; + filter.type = payload.isShortDiscriminator ? Dnssd::DiscoveryFilterType::kShortDiscriminator + : Dnssd::DiscoveryFilterType::kLongDiscriminator; + filter.code = payload.isShortDiscriminator ? (payload.discriminator >> 8) & 0xF : payload.discriminator; + ReturnErrorOnFailure(this->mDNSResolver.Init(mSystemState.UDPEndPointManager())); + this->mDNSResolver.SetResolverDelegate(this); + this->mDeviceDiscoveryDelegate = this; + ReturnErrorOnFailure(this->mDNSResolver.FindCommissionableNodes(filter)); + RetainShutdownToken(token); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DnssdDiscoverer::GetNextDnssdCandidate(Commissionee & commissionee) +{ + ReturnErrorOnFailure(GetValidRecord()); + ReturnErrorOnFailure(GetAddress(commissionee.mCommissionableNodeAddress, commissionee.mMrpConfig)); +#if CHIP_PROGRESS_LOGGING + char addressStr[Transport::PeerAddress::kMaxToStringSize]; + commissionee.mCommissionableNodeAddress.Value().ToString(addressStr); + ChipLogProgress(Controller, "Commissionable node candidate identified at %s", addressStr); +#endif // CHIP_PROGRESS_LOGGING + ++mIpIdx; + return CHIP_NO_ERROR; +} + +CHIP_ERROR DnssdDiscoverer::GetValidRecord() +{ + while (mNodeIdx < GetDiscoveredNodes().size() && GetDiscoveredNodes().data()[mNodeIdx].IsValid()) + { + if (mIpIdx < GetDiscoveredNodes().data()[mNodeIdx].numIPs) + { + return CHIP_NO_ERROR; + } + ++mNodeIdx; + mIpIdx = 0; + } + if (mNodeIdx >= GetDiscoveredNodes().size()) + { + Shutdown(); // If mNodes is full, no point searching further. + } + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR DnssdDiscoverer::GetAddress(chip::Optional & address, + chip::Optional & mrpConfig) +{ + if (mNodeIdx >= GetDiscoveredNodes().size() || mIpIdx >= GetDiscoveredNodes().data()[mNodeIdx].numIPs) + { + return CHIP_ERROR_NOT_FOUND; + } + Dnssd::DiscoveredNodeData & node = GetDiscoveredNodes().data()[mNodeIdx]; + Inet::InterfaceId interfaceId = node.ipAddress[mIpIdx].IsIPv6LinkLocal() ? node.interfaceId[mIpIdx] : Inet::InterfaceId::Null(); +#if 0 + // TODO: TCP support + if (node.supportsTcp) + { + address.SetValue(Transport::PeerAddress::TCP(node.ipAddress[mIpIdx], node.port, interfaceId)); + mrpConfig.SetValue(node.GetMRPConfig()); + } + else +#endif + { + address.SetValue(Transport::PeerAddress::UDP(node.ipAddress[mIpIdx], node.port, interfaceId)); + mrpConfig.SetValue(node.GetMRPConfig()); + } + return CHIP_NO_ERROR; +} + +#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD + +Discoverer::Discoverer(Controller::DeviceControllerSystemState & systemState, Platform::SharedPtr payload, + Delegate * delegate) : + Joinable(&mDelegate), + mDelegate(delegate), mPayload(payload) +#if CONFIG_NETWORK_LAYER_BLE + , + mBleDiscoverer(systemState, &mDelegate) +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + , + mDnssdDiscoverer(systemState, &mDelegate) +#endif +{} + +CHIP_ERROR Discoverer::Init() +{ + RetainShutdownToken(Platform::MakeShared(&mDelegate)); + return InProgress() ? CHIP_NO_ERROR : CHIP_ERROR_NO_MEMORY; +} + +void Discoverer::Shutdown() +{ +#if CONFIG_NETWORK_LAYER_BLE + mBleDiscoverer.Shutdown(); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + mDnssdDiscoverer.Shutdown(); +#endif + ReleaseShutdownToken(); +} + +void Discoverer::SetDelegate(Delegate * delegate) +{ + mDelegate = delegate; +} + +CHIP_ERROR Discoverer::Discover() +{ + CHIP_ERROR err = CHIP_ERROR_NOT_IMPLEMENTED; +#if CONFIG_NETWORK_LAYER_BLE + bool searchAllOver = this->mPayload.get()->rendezvousInformation == RendezvousInformationFlag::kNone; + if (searchAllOver || this->mPayload.get()->rendezvousInformation == RendezvousInformationFlag::kBLE) + { + SuccessOrExit(err = mBleDiscoverer.StartBleDiscovery(*mPayload.get(), GetShutdownToken())); + } +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + // We always want to search on-network because any node that has + // already been commissioned will use on-network regardless of + // onboarding payload contents. + SuccessOrExit(err = mDnssdDiscoverer.StartDnssdDiscovery(*mPayload.get(), GetShutdownToken())); +#endif +exit: + return err; +} + +CHIP_ERROR Discoverer::GetNextCandidate(Commissionee & commissionee) +{ +#if CONFIG_NETWORK_LAYER_BLE + VerifyOrReturnError(mBleDiscoverer.GetNextBleCandidate(commissionee) != CHIP_NO_ERROR, CHIP_NO_ERROR); +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + VerifyOrReturnError(mDnssdDiscoverer.GetNextDnssdCandidate(commissionee) != CHIP_NO_ERROR, CHIP_NO_ERROR); +#endif + // CHIP_ERROR_NOT_FOUND is a special signal to the caller that no + // candidates were found, but discovery is still in progress. + return InProgress() ? CHIP_ERROR_NOT_FOUND : CHIP_ERROR_INTERNAL; +} + +} // namespace CommissionableNodeDiscoverer +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/Discoverer.h b/src/commissioner/Discoverer.h new file mode 100644 index 00000000000000..d3ea42f2915d27 --- /dev/null +++ b/src/commissioner/Discoverer.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020-2021 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace Commissioner { +namespace CommissionableNodeDiscoverer { + +struct Delegate +{ + virtual ~Delegate() = default; + virtual void OnDiscovery() = 0; + virtual void OnDiscovererShutdown() = 0; +}; + +struct ShutdownToken +{ + ShutdownToken(Delegate ** delegate) : mDelegate(delegate) {} + ~ShutdownToken() { (*mDelegate)->OnDiscovererShutdown(); } + +private: + Delegate ** mDelegate; +}; + +struct Joinable +{ + Joinable(Delegate ** delegate) : mDelegate(delegate) {} + +protected: + bool InProgress() { return mShutdownToken.get() != nullptr ? true : false; } + void RetainShutdownToken(Platform::SharedPtr token) { mShutdownToken = token; } + void ReleaseShutdownToken() { mShutdownToken = std::shared_ptr(nullptr); } + Platform::SharedPtr GetShutdownToken() { return mShutdownToken; } + + Delegate ** mDelegate; + +private: + Platform::SharedPtr mShutdownToken; +}; + +#if CONFIG_NETWORK_LAYER_BLE +class BleDiscoverer : Joinable +{ +public: + BleDiscoverer(Controller::DeviceControllerSystemState & systemState, Delegate ** delegate); + ~BleDiscoverer(); + + void Shutdown(); + CHIP_ERROR StartBleDiscovery(SetupPayload & payload, Platform::SharedPtr token); + CHIP_ERROR GetNextBleCandidate(Commissionee & commissionee); + void TallyBleDiscovery(BLE_CONNECTION_OBJECT connection); + void TallyBleFailure(); + +private: + static void OnBleDiscoverySuccess(void * context, BLE_CONNECTION_OBJECT connection); + static void OnBleDiscoveryError(void * context, CHIP_ERROR err); + Controller::DeviceControllerSystemState & mSystemState; + chip::Optional mBleConnection; +}; +#endif // CONFIG_NETWORK_LAYER_BLE + +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD +class DnssdDiscoverer : Controller::AbstractDnssdDiscoveryController, Controller::DeviceDiscoveryDelegate, Joinable +{ +public: + DnssdDiscoverer(Controller::DeviceControllerSystemState & systemState, Delegate ** delegate); + ~DnssdDiscoverer(); + + void Shutdown(); + CHIP_ERROR StartDnssdDiscovery(SetupPayload & payload, Platform::SharedPtr token); + CHIP_ERROR GetNextDnssdCandidate(Commissionee & commissionee); + +private: + DiscoveredNodeList GetDiscoveredNodes() { return DiscoveredNodeList(mNodes); } + void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & node) { (*mDelegate)->OnDiscovery(); } + void OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeData) {} + void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) {} + + CHIP_ERROR GetValidRecord(); + CHIP_ERROR GetAddress(chip::Optional & address, + chip::Optional & mrpConfig); + + Controller::DeviceControllerSystemState & mSystemState; + Dnssd::DiscoveredNodeData mNodes[CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES]; + size_t mNodeIdx = 0; + int mIpIdx = 0; +}; +#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD + +class Discoverer : Joinable +{ +public: + Discoverer(Controller::DeviceControllerSystemState & systemState, Platform::SharedPtr payload, + Delegate * delegate); + + CHIP_ERROR Init(); + void Shutdown(); + void SetDelegate(Delegate * delegate); + CHIP_ERROR Discover(); + CHIP_ERROR GetNextCandidate(Commissionee & commissionee); + +private: + Delegate * mDelegate; + Platform::SharedPtr mPayload; +#if CONFIG_NETWORK_LAYER_BLE + BleDiscoverer mBleDiscoverer; +#endif +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + DnssdDiscoverer mDnssdDiscoverer; +#endif +}; + +} // namespace CommissionableNodeDiscoverer +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/Events.h b/src/commissioner/Events.h new file mode 100644 index 00000000000000..f418dffa317933 --- /dev/null +++ b/src/commissioner/Events.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2020-2021 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Commissioner { +namespace Events { + +constexpr size_t kMaxAttestationElementsLength = 900; // RESP_MAX +constexpr size_t kMaxNocsrElementsLength = 900; // RESP_MAX +constexpr size_t kMaxNetworkIdLength = 32; + +struct SharedBuffer +{ + const ByteSpan Get() const { return ByteSpan(mData.get(), mLen); } + MutableByteSpan GetMutable() { return MutableByteSpan(mData.get(), mCapacity); }; + CHIP_ERROR Set(const ByteSpan buffer) + { + if (mCapacity < buffer.size()) + { + ReturnErrorOnFailure(Allocate(buffer.size())); + } + memmove(mData.get(), buffer.data(), buffer.size()); + mLen = buffer.size(); + return CHIP_NO_ERROR; + } + void Clear() + { + mData = std::shared_ptr(nullptr); + mCapacity = 0; + mLen = 0; + } + CHIP_ERROR Allocate(size_t size) + { + mData = std::shared_ptr(static_cast(chip::Platform::MemoryAlloc(size)), chip::Platform::MemoryFree); + if (mData.get() == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + mLen = 0; + mCapacity = size; + return CHIP_NO_ERROR; + } + +private: + Platform::SharedPtr mData; + size_t mCapacity = 0; + size_t mLen = 0; +}; + +struct SharedString +{ + const char * Get() const { return mData.get(); } + CHIP_ERROR Set(const char * cStr) + { + if (cStr == nullptr) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + const size_t len = sizeof(*cStr) * (strlen(cStr) + 1); + if (mCapacity < len) + { + ReturnErrorOnFailure(Allocate(len)); + } + memmove(mData.get(), cStr, len); + mLen = len; + return CHIP_NO_ERROR; + } + void Clear() + { + mData = std::shared_ptr(nullptr); + mCapacity = 0; + mLen = 0; + } + CHIP_ERROR Allocate(size_t len) + { + mData = std::shared_ptr(static_cast(chip::Platform::MemoryAlloc(len)), chip::Platform::MemoryFree); + if (mData.get() == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + mLen = 0; + mCapacity = len; + return CHIP_NO_ERROR; + } + +private: + Platform::SharedPtr mData; + size_t mCapacity = 0; + size_t mLen = 0; +}; + +template +struct CapacityBoundSharedBuffer : public SharedBuffer +{ + CHIP_ERROR Allocate(size_t size) + { + if (size > MaxSize) + { + return CHIP_ERROR_NO_MEMORY; + } + return SharedBuffer::Allocate(size); + } + CHIP_ERROR Set(const ByteSpan buffer) + { + if (buffer.size() > MaxSize) + { + return CHIP_ERROR_NO_MEMORY; + } + return SharedBuffer::Set(buffer); + } +}; + +template +struct CapacityBoundStaticBuffer +{ + const ByteSpan Get() const { return ByteSpan(mBuffer, mBuffer.Length()); } + void Clear() { mBuffer.SetLength(0); }; + CHIP_ERROR Set(ByteSpan buffer) + { + ReturnErrorOnFailure(mBuffer.SetLength(buffer.size())); + memmove(mBuffer, buffer.data(), buffer.size()); + return CHIP_NO_ERROR; + } + +private: + Crypto::CapacityBoundBuffer mBuffer; +}; + +// Events +struct Success +{ +}; + +struct Failure +{ +}; + +struct Await +{ +}; + +struct OnboardingPayload +{ + const char * mPayload; +}; + +struct NetworkId : CapacityBoundStaticBuffer +{ + CHIP_ERROR FromThreadOperationalDataset(ByteSpan opaqueDataset) + { + Thread::OperationalDataset parsed; + ReturnErrorOnFailure(parsed.Init(opaqueDataset)); + uint8_t extendedPanId[Thread::kSizeExtendedPanId]; + ReturnErrorOnFailure(parsed.GetExtendedPanId(extendedPanId)); + ReturnErrorOnFailure(Set(ByteSpan(extendedPanId))); + return CHIP_NO_ERROR; + } +}; + +class AttestationBase +{ +public: + AttestationBase(ByteSpan nonce) : mNonce(nonce) {} + AttestationBase() = delete; + + class CapacityBoundNonce + { + public: + CapacityBoundNonce(ByteSpan nonce) { Set(nonce); } + CapacityBoundNonce() = delete; + + const ByteSpan Get() const { return ByteSpan(mNonce, mNonce.Length()); } + void Clear() { mNonce.SetLength(0); }; + void Set(ByteSpan nonce) + { + mNonce.SetLength(std::min(nonce.size(), mNonce.Capacity())); + memmove(mNonce, nonce.data(), mNonce.Length()); + } + + private: + Crypto::CapacityBoundBuffer mNonce; + }; + + auto Nonce() { return &mNonce; } + auto Challenge() { return &mChallenge; } + auto Signature() { return &mSignature; } + auto Pai() { return &mPai; } + auto Dac() { return &mDac; } + +private: + CapacityBoundNonce mNonce; + CapacityBoundStaticBuffer mChallenge; + CapacityBoundSharedBuffer mSignature; + CapacityBoundSharedBuffer mPai; + CapacityBoundSharedBuffer mDac; +}; + +struct AttestationInformation : public AttestationBase +{ + AttestationInformation(ByteSpan nonce) : AttestationBase(nonce) {} + auto AttestationElements() { return &mAttestationElements; } + +private: + CapacityBoundSharedBuffer mAttestationElements; +}; + +struct NocsrInformation : public AttestationBase +{ + NocsrInformation(AttestationInformation attestationInformation, ByteSpan nonce) : AttestationBase(attestationInformation) + { + Nonce()->Set(nonce); + Signature()->Clear(); // Don't retain signature from AttestationBase. + } + + auto NocsrElements() { return &mNocsrElements; } + +private: + CapacityBoundSharedBuffer mNocsrElements; +}; + +struct OperationalCredentials +{ + auto Rcac() { return &mRcac; } + auto Icac() { return &mIcac; } + auto Noc() { return &mNoc; } + auto Ipk() { return &mIpk; } + chip::NodeId mAdminSubject = kUndefinedNodeId; + uint16_t mAdminVendorId = kUndefinedVendorId; + + CHIP_ERROR GetRootPublicKey(Crypto::P256PublicKey & rootPublicKey) + { + Credentials::P256PublicKeySpan rootPublicKeySpan; + ReturnErrorOnFailure(Credentials::ExtractPublicKeyFromChipCert(this->Rcac()->Get(), rootPublicKeySpan)); + rootPublicKey = Crypto::P256PublicKey(rootPublicKeySpan); + return CHIP_NO_ERROR; + } + + CHIP_ERROR GetFabricAndNodeId(FabricId & fabricId, NodeId & nodeId) + { + ReturnErrorOnFailure(Credentials::ExtractNodeIdFabricIdFromOpCert(this->Noc()->Get(), &nodeId, &fabricId)); + return CHIP_NO_ERROR; + } + + CHIP_ERROR GetCompressedFabricId(CompressedFabricId & compressedFabricId) + { + Crypto::P256PublicKey rootPublicKey; + FabricId fabricId; + NodeId unused; + ReturnErrorOnFailure(GetRootPublicKey(rootPublicKey)); + ReturnErrorOnFailure(GetFabricAndNodeId(fabricId, unused)); + ReturnErrorOnFailure(GetCompressedFabricId(rootPublicKey, fabricId, compressedFabricId)); + return CHIP_NO_ERROR; + } + + CHIP_ERROR GetOperationalId(PeerId & operationalId) + { + CompressedFabricId compressedFabricId; + Crypto::P256PublicKey rootPublicKey; + FabricId fabricId; + NodeId nodeId; + ReturnErrorOnFailure(GetRootPublicKey(rootPublicKey)); + ReturnErrorOnFailure(GetFabricAndNodeId(fabricId, nodeId)); + ReturnErrorOnFailure(GetCompressedFabricId(rootPublicKey, fabricId, compressedFabricId)); + operationalId.SetCompressedFabricId(compressedFabricId); + operationalId.SetNodeId(nodeId); + return CHIP_NO_ERROR; + } + +private: + static CHIP_ERROR GetCompressedFabricId(Crypto::P256PublicKey & rootPublicKey, FabricId fabricId, + CompressedFabricId & compressedFabricId) + { + uint8_t allocated[sizeof(uint64_t)]; + MutableByteSpan span(allocated); + ReturnErrorOnFailure(GenerateCompressedFabricId(rootPublicKey, fabricId, span)); + // Decode compressed fabric ID accounting for endianness, as GenerateCompressedFabricId() + // returns a binary buffer and is agnostic of usage of the output as an integer type. + compressedFabricId = Encoding::BigEndian::Get64(allocated); + return CHIP_NO_ERROR; + } + + CapacityBoundSharedBuffer mRcac; + CapacityBoundSharedBuffer mIcac; + CapacityBoundSharedBuffer mNoc; + CapacityBoundStaticBuffer mIpk; +}; + +} // namespace Events +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/ExampleCommissioningStateMachine.cpp b/src/commissioner/ExampleCommissioningStateMachine.cpp new file mode 100644 index 00000000000000..678fed68cd6ae6 --- /dev/null +++ b/src/commissioner/ExampleCommissioningStateMachine.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021 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 + +namespace chip { +namespace Commissioner { +namespace ExampleCommissioningStateMachine { + +void StateFactory::Init(OpCredsIssuer * issuer, FabricIndex fabricIndex, NodeId nodeId, ByteSpan operationalDataset, ByteSpan ssid, + ByteSpan wiFiCredentials) +{ + mIssuer = issuer; + mFabricIndex = fabricIndex; + mNodeId = nodeId; + mOperationalDataset = operationalDataset; + mSsid = ssid; + mWiFiCredentials = wiFiCredentials; +} + +void StateFactory::SetCallbacks(OnSuccess onSuccess, OnFailure onFailure) +{ + mOnSuccess = onSuccess; + mOnFailure = onFailure; +} + +chip::StateMachine::Optional Transitions::operator()(const State & state, const Event & event) +{ + if (state.Is() && event.Is()) + { + return mFactory.CreateParsingOnboardingPayload(event.Get()); + } + else if ((state.Is() || state.Is()) && event.Is()) + { + CHIP_ERROR err = mCtx.ScheduleTimeout(kCommissionableDiscoveryTimeout); + return err == CHIP_NO_ERROR ? mFactory.CreateCommissionableNodeDiscovery(event.Get()) + : mFactory.CreateFailed(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateAbortingCommissionableDiscovery(state.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInitiatingPase(state.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateFailed(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateAwaitingCommissionableDiscovery(state.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateFinishingPase(state.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreatePaseComplete(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateAbortingCommissionableDiscovery(state.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInitiatingPase(state.Get()); + } + else if (state.Is() && event.Is()) + { + this->mCtx.CancelTimeout(); + return mFactory.CreatePaseComplete(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingArmFailSafe(event.Get()); + } + else if (state.Is() && (event.Is() || event.Is())) // TODO: current devices fail this + { + return mFactory.CreateFailSafeArmed(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingAttestationRequest(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingDacCertificateChainRequest(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingPaiCertificateChainRequest(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateCapturingAttestationChallenge(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateAttestationVerification(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateAttestationVerified(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingOpCSRRequest(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateOpCSRResponseReceived(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateSigningCertificates(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateCertificatesSigned(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingAddTrustedRootCertificate(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingAddNOC(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateOpCredsWritten(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateReadingNetworkFeatureMap(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateNetworkEnabled(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateNetworkFeatureMapRead(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingAddOrUpdateWiFiNetwork(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingAddOrUpdateThreadNetwork(event.Get()); + } + else if ((state.Is() || state.Is()) && event.Is()) + { + return mFactory.CreateNetworkAdded(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingConnectNetwork(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateNetworkEnabled(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateOperationalDiscovery(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInitiatingCase(event.Get()); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateCaseComplete(); + } + else if (state.Is() && event.Is()) + { + return mFactory.CreateInvokingCommissioningComplete(); + } + else if (state.Is() && + (event.Is() || event.Is())) // TODO: current devices fail this + { + return mFactory.CreateCommissioningComplete(); + } + else if (event.Is()) + { + this->mCtx.CancelTimeout(); + return mFactory.CreateFailed(); + } + else if (event.Is()) + { + this->mCtx.CancelTimeout(); + return mFactory.CreateIdle(); + } + else + { + return {}; + } +} + +void ExampleCommissioningStateMachine::Init(SystemState * systemState, OpCredsIssuer * issuer, FabricIndex fabricIndex, + NodeId nodeId, ByteSpan operationalDataset, ByteSpan ssid, ByteSpan wiFiCredentials) +{ + mTransitions.mFactory.Init(issuer, fabricIndex, nodeId, operationalDataset, ssid, wiFiCredentials); + mCommissionee.Init(systemState); +} + +void ExampleCommissioningStateMachine::Init(SystemState * systemState, OpCredsIssuer * issuer, FabricIndex fabricIndex, + NodeId nodeId) +{ + Init(systemState, issuer, fabricIndex, nodeId, ByteSpan{}, ByteSpan{}, ByteSpan{}); +} + +void ExampleCommissioningStateMachine::Shutdown() +{ + this->Dispatch(Event::Create()); + mCommissionee.Shutdown(); +} + +CHIP_ERROR ExampleCommissioningStateMachine::Commission(Event event, OnSuccess onSuccess, OnFailure onFailure) +{ + if (!GetState().Is()) + { + return CHIP_ERROR_INCORRECT_STATE; + } + mTransitions.mFactory.SetCallbacks(onSuccess, onFailure); + this->Dispatch(event); + return GetState().Is() ? CHIP_ERROR_INCORRECT_STATE : CHIP_NO_ERROR; +} + +CHIP_ERROR ExampleCommissioningStateMachine::Commission(const char * onboardingPayload, OnSuccess onSuccess, OnFailure onFailure) +{ + return Commission(Event::Create(OnboardingPayload{ onboardingPayload }), onSuccess, onFailure); +} + +CHIP_ERROR ExampleCommissioningStateMachine::Commission(chip::SetupPayload & onboardingPayload, OnSuccess onSuccess, + OnFailure onFailure) +{ + auto allocatedPayload = Platform::MakeShared(onboardingPayload); + if (allocatedPayload.get() == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + return Commission(Event::Create(allocatedPayload), onSuccess, onFailure); +} + +CHIP_ERROR ExampleCommissioningStateMachine::Commission(RendezvousInformationFlags flags, uint16_t discriminator, + uint32_t setUpPINCode, OnSuccess onSuccess, OnFailure onFailure) +{ + chip::SetupPayload onboardingPayload; + onboardingPayload.rendezvousInformation = flags; + onboardingPayload.discriminator = discriminator; + onboardingPayload.setUpPINCode = setUpPINCode; + return Commission(onboardingPayload, onSuccess, onFailure); +} + +CHIP_ERROR ExampleCommissioningStateMachine::GrabCommissionee(chip::Controller::DeviceController & controller) +{ + VerifyOrReturnError(mCommissionee.mOperationalAddress.HasValue(), CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mCommissionee.mOperationalId.HasValue(), CHIP_ERROR_INCORRECT_STATE); + controller.EmplaceOperationalDevice(mCommissionee.mOperationalAddress.Value(), mCommissionee.mMrpConfig, + mCommissionee.mOperationalId.Value(), mCommissionee.mCaseSession); + mCommissionee.mCaseSession.Release(); + return CHIP_NO_ERROR; +} + +void ExampleCommissioningStateMachine::DispatchTimeout(System::Layer * aLayer, void * appState) +{ + ExampleCommissioningStateMachine * instance = static_cast(appState); + instance->Dispatch(Event::Create()); +} + +CHIP_ERROR ExampleCommissioningStateMachine::ScheduleTimeout(System::Clock::Timeout aDelay) +{ + VerifyOrReturnError(mCommissionee.mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(mCommissionee.mSystemState->SystemLayer()->StartTimer(aDelay, DispatchTimeout, this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExampleCommissioningStateMachine::CancelTimeout() +{ + VerifyOrReturnError(mCommissionee.mSystemState != nullptr, CHIP_ERROR_INCORRECT_STATE); + mCommissionee.mSystemState->SystemLayer()->CancelTimer(DispatchTimeout, this); + return CHIP_NO_ERROR; +} + +} // namespace ExampleCommissioningStateMachine +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/ExampleCommissioningStateMachine.h b/src/commissioner/ExampleCommissioningStateMachine.h new file mode 100644 index 00000000000000..809854fb651f80 --- /dev/null +++ b/src/commissioner/ExampleCommissioningStateMachine.h @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace Commissioner { +namespace ExampleCommissioningStateMachine { + +constexpr System::Clock::Timeout kCommissionableDiscoveryTimeout = System::Clock::Seconds16(30); +class Timer +{ +public: + virtual ~Timer() = default; + virtual CHIP_ERROR ScheduleTimeout(System::Clock::Timeout aDelay) = 0; + virtual CHIP_ERROR CancelTimeout() = 0; +}; + +using SystemState = chip::Controller::DeviceControllerSystemState; +using Commissionee = chip::Commissioner::Commissionee; +using OpCredsIssuer = chip::Controller::OperationalCredentialsDelegate; +using OnSuccess = std::function; +using OnFailure = std::function; + +// SDK Events +using Success = chip::Commissioner::Events::Success; +using Failure = chip::Commissioner::Events::Failure; +using Await = chip::Commissioner::Events::Await; +using OnboardingPayload = chip::Commissioner::Events::OnboardingPayload; +using ParsedPayload = chip::Platform::SharedPtr; +using ArmFailSafe = chip::app::Clusters::GeneralCommissioning::Commands::ArmFailSafe::Type; +using AttestationInformation = chip::Commissioner::Events::AttestationInformation; +using NocsrInformation = chip::Commissioner::Events::NocsrInformation; +using OperationalCredentials = chip::Commissioner::Events::OperationalCredentials; +using NetworkFeatureMap = app::Clusters::NetworkCommissioning::Attributes::FeatureMap::TypeInfo::DecodableType; +using AddOrUpdateWiFiNetwork = app::Clusters::NetworkCommissioning::Commands::AddOrUpdateWiFiNetwork::Type; +using AddOrUpdateThreadNetwork = app::Clusters::NetworkCommissioning::Commands::AddOrUpdateThreadNetwork::Type; +using NetworkId = chip::Commissioner::Events::NetworkId; +using OperationalRecord = chip::Platform::SharedPtr; + +// App Events +struct Timeout +{ +}; +struct InitiateNetworkConfiguration +{ +}; +struct SkipNetworkConfiguration +{ +}; +struct InitiateOperationalDiscovery +{ +}; +struct InvokeCommissioningComplete +{ +}; +struct Shutdown +{ +}; + +// Variant Event definition +using Event = chip::Variant; + +class Context : public chip::StateMachine::Context, public Timer +{ +}; + +// SDK States +using ParsingOnboardingPayload = chip::Commissioner::States::ParsingOnboardingPayload; +using CommissionableNodeDiscovery = chip::Commissioner::States::CommissionableNodeDiscovery; +using AwaitingCommissionableDiscovery = chip::Commissioner::States::AwaitingCommissionableDiscovery; +using InitiatingPase = chip::Commissioner::States::PasscodeAuthenticatedSessionEstablishment; +using InvokingArmFailSafe = chip::Commissioner::States::InvokingArmFailSafe; +using InvokingAttestationRequest = chip::Commissioner::States::InvokingAttestationRequest; +using InvokingDacCertificateChainRequest = chip::Commissioner::States::InvokingDacCertificateChainRequest; +using InvokingPaiCertificateChainRequest = chip::Commissioner::States::InvokingPaiCertificateChainRequest; +using CapturingAttestationChallenge = chip::Commissioner::States::CapturingAttestationChallenge; +using InvokingOpCSRRequest = chip::Commissioner::States::InvokingOpCSRRequest; +using InvokingAddTrustedRootCertificate = chip::Commissioner::States::InvokingAddTrustedRootCertificate; +using InvokingAddNOC = chip::Commissioner::States::InvokingAddNOC; +using ReadingNetworkFeatureMap = chip::Commissioner::States::ReadingNetworkCommissioningClusterFeatureMap; +using InvokingAddOrUpdateWiFiNetwork = chip::Commissioner::States::InvokingAddOrUpdateWiFiNetwork; +using InvokingAddOrUpdateThreadNetwork = chip::Commissioner::States::InvokingAddOrUpdateThreadNetwork; +using InvokingConnectNetwork = chip::Commissioner::States::InvokingConnectNetwork; +using OperationalDiscovery = chip::Commissioner::States::OperationalDiscovery; +using InitiatingCase = chip::Commissioner::States::CertificateAuthenticatedSessionEstablishment; +using InvokingCommissioningComplete = chip::Commissioner::States::InvokingCommissioningComplete; + +// App-Specific States + +struct Idle : chip::Commissioner::States::Base +{ + Idle(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "Idle") {} +}; + +struct AbortingCommissionableDiscovery : CommissionableNodeDiscovery +{ + AbortingCommissionableDiscovery(const CommissionableNodeDiscovery & previous) : CommissionableNodeDiscovery(previous) + { + this->mName = "AbortingCommissionableDiscovery"; + } + void Enter() + { + this->mDiscoverer.get()->SetDelegate(this); + this->mDiscoverer.get()->Shutdown(); + } + +private: + void OnDiscovery() {} + void OnDiscovererShutdown() { this->mCtx.Dispatch(Event::Create()); } +}; + +struct FinishingPase : InitiatingPase +{ + FinishingPase(const InitiatingPase & previous) : InitiatingPase(previous) { this->mName = "FinishingPase"; } + + void Enter() { this->mPairing.get()->SetDelegate(this); } + void OnSessionEstablishmentError(CHIP_ERROR error) override + { +#if CONFIG_NETWORK_LAYER_BLE + this->mCommissionee.CloseBle(); +#endif + this->mCtx.Dispatch(Event::Create()); + } +}; + +struct PaseComplete : chip::Commissioner::States::Base +{ + PaseComplete(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "PaseComplete") {} + void Enter() { this->mCtx.Dispatch(Event::Create(ArmFailSafe{ 60 })); } +}; + +struct FailSafeArmed : chip::Commissioner::States::Base +{ + FailSafeArmed(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "FailSafeArmed") {} + void Enter() + { + uint8_t attestationNonce[chip::kAttestationNonceLength]; + chip::Crypto::DRBG_get_bytes(attestationNonce, sizeof(attestationNonce)); + this->mCtx.Dispatch(Event::Create(chip::ByteSpan(attestationNonce))); + } +}; + +struct AttestationVerification : chip::Commissioner::States::Base +{ + AttestationVerification(Context & ctx, Commissionee & commissionee, AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "AttestationVerification"), mAttestationInformation(attestationInformation) + {} + void Enter() + { + auto testingRootStore = chip::Credentials::GetTestAttestationTrustStore(); + auto verifier = chip::Credentials::GetDefaultDACVerifier(testingRootStore); + auto result = verifier->VerifyAttestationInformation( + mAttestationInformation.AttestationElements()->Get(), mAttestationInformation.Challenge()->Get(), + mAttestationInformation.Signature()->Get(), mAttestationInformation.Pai()->Get(), mAttestationInformation.Dac()->Get(), + mAttestationInformation.Nonce()->Get()); + if (result == chip::Credentials::AttestationVerificationResult::kSuccess) + { + this->mCtx.Dispatch(Event::Create(mAttestationInformation)); + } + else + { + ChipLogError(Controller, + "Failed in verifying 'Attestation Information' command received from the device: err %hu. Look at " + "AttestationVerificationResult enum to understand the errors", + static_cast(result)); + // Go look at AttestationVerificationResult enum in src/credentials/DeviceAttestationVerifier.h to understand the + // errors. + this->mCtx.Dispatch(Event::Create()); + } + } + +private: + AttestationInformation mAttestationInformation; +}; + +struct AttestationVerified : chip::Commissioner::States::Base +{ + AttestationVerified(Context & ctx, Commissionee & commissionee, AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "AttestationVerified"), mAttestationInformation(attestationInformation) + {} + void Enter() + { + uint8_t csrNonce[kOpCSRNonceLength]; + chip::Crypto::DRBG_get_bytes(csrNonce, sizeof(csrNonce)); + this->mCtx.Dispatch(Event::Create(mAttestationInformation, chip::ByteSpan(csrNonce))); + } + +private: + AttestationInformation mAttestationInformation; +}; + +struct OpCSRResponseReceived : chip::Commissioner::States::Base +{ + OpCSRResponseReceived(Context & ctx, Commissionee & commissionee, NocsrInformation & nocsrInformation) : + Base(ctx, commissionee, "OpCSRResponseReceived"), mNocsrInformation(nocsrInformation) + {} + void Enter() { this->mCtx.Dispatch(Event::Create(mNocsrInformation)); } + +private: + NocsrInformation mNocsrInformation; +}; + +struct SigningCertificates : chip::Commissioner::States::Base +{ + SigningCertificates(Context & ctx, Commissionee & commissionee, NocsrInformation & nocsrInformation, OpCredsIssuer * issuer, + FabricIndex fabricIndex, NodeId nodeId) : + Base(ctx, commissionee, "SigningCertificates"), + mNocsrInformation(nocsrInformation), mIssuer(issuer), mFabricIndex(fabricIndex), mNodeId(nodeId) + {} + void Enter() + { + // Note that OperationalCredentialsDelegate is just used here for + // convenience. The exmaple app can use any signer interface it + // chooses, and from SigningCertificates state context, + // everything needed for end-to-end attestation, IPK and admin subject + // assignment is accessible. + chip::Callback::Callback callback(OnDeviceNOCChainGeneration, this); + FabricInfo * fabric = this->mCommissionee.mSystemState->Fabrics()->FindFabricWithIndex(mFabricIndex); + if (fabric == nullptr) + { + this->mCtx.Dispatch(Event::Create()); + return; + } + mIssuer->SetFabricIdForNextNOCRequest(fabric->GetFabricId()); + mIssuer->SetNodeIdForNextNOCRequest(mNodeId); + CHIP_ERROR err = + mIssuer->GenerateNOCChain(mNocsrInformation.NocsrElements()->Get(), mNocsrInformation.Signature()->Get(), + mNocsrInformation.Dac()->Get(), mNocsrInformation.Pai()->Get(), chip::ByteSpan(), &callback); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(Event::Create()); + } + } + void Dispatch(Event event) { this->mCtx.Dispatch(event); } + +private: + static void OnDeviceNOCChainGeneration(void * context, CHIP_ERROR err, const chip::ByteSpan & derNoc, + const chip::ByteSpan & derIcac, const chip::ByteSpan & derRcac) + { + + ChipLogProgress(Controller, "Received callback from the CA for NOC Chain generation. Status %s", ErrorStr(err)); + SigningCertificates * state = static_cast(context); + OperationalCredentials opCreds; + SuccessOrExit(err); + + { + chip::MutableByteSpan chipRcac; + SuccessOrExit(err = opCreds.Rcac()->Allocate(chip::Credentials::kMaxCHIPCertLength)); + chipRcac = opCreds.Rcac()->GetMutable(); + SuccessOrExit(err = chip::Credentials::ConvertX509CertToChipCert(derRcac, chipRcac)); + SuccessOrExit(err = opCreds.Rcac()->Set(chipRcac)); + } + if (!derIcac.empty()) + { + chip::MutableByteSpan chipIcac; + SuccessOrExit(err = opCreds.Icac()->Allocate(chip::Credentials::kMaxCHIPCertLength)); + chipIcac = opCreds.Icac()->GetMutable(); + SuccessOrExit(err = chip::Credentials::ConvertX509CertToChipCert(derIcac, chipIcac)); + SuccessOrExit(err = opCreds.Icac()->Set(chipIcac)); + } + { + chip::MutableByteSpan chipNoc; + SuccessOrExit(err = opCreds.Noc()->Allocate(chip::Credentials::kMaxCHIPCertLength)); + chipNoc = opCreds.Noc()->GetMutable(); + SuccessOrExit(err = chip::Credentials::ConvertX509CertToChipCert(derNoc, chipNoc)); + SuccessOrExit(err = opCreds.Noc()->Set(chipNoc)); + } + exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed in generating device's operational credentials. Error %s", ErrorStr(err)); + state->Dispatch(Event::Create()); + } + else + { + state->Dispatch(Event::Create(opCreds)); + } + } + + NocsrInformation mNocsrInformation; + OpCredsIssuer * mIssuer; + FabricIndex mFabricIndex; + NodeId mNodeId; +}; + +struct CertificatesSigned : chip::Commissioner::States::Base +{ + CertificatesSigned(Context & ctx, Commissionee & commissionee, OperationalCredentials & opCreds) : + Base(ctx, commissionee, "CertificatesSigned"), mOpCreds(opCreds) + {} + void Enter() { this->mCtx.Dispatch(Event::Create(mOpCreds)); } + +private: + OperationalCredentials mOpCreds; +}; + +struct OpCredsWritten : chip::Commissioner::States::Base +{ + OpCredsWritten(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "OpCredsWritten") {} + void Enter() + { + if (!this->mCommissionee.mCommissionableNodeAddress.HasValue()) + { + this->mCtx.Dispatch(Event::Create()); + } + else if (this->mCommissionee.mCommissionableNodeAddress.Value().GetTransportType() == Transport::Type::kTcp || + this->mCommissionee.mCommissionableNodeAddress.Value().GetTransportType() == Transport::Type::kUdp) + { + this->mCtx.Dispatch(Event::Create()); + } + else + { + this->mCtx.Dispatch(Event::Create()); + } + }; +}; + +struct NetworkFeatureMapRead : chip::Commissioner::States::Base +{ + NetworkFeatureMapRead(Context & ctx, Commissionee & commissionee, NetworkFeatureMap featureMap, ByteSpan operationalDataset, + ByteSpan ssid, ByteSpan wiFiCredentials) : + Base(ctx, commissionee, "NetworkFeatureMapRead"), + mFeatureMap(featureMap), mOperationalDataset(operationalDataset), mSsid(ssid), mWiFiCredentials(wiFiCredentials) + {} + void Enter() + { + // TODO: currently, devices don't correctly report the neworking + // commissioning cluster feature map attribute. But when they do, we + // should key network configuration on this. For now, just log it. + ChipLogDetail(Controller, "Network Feature Map = 0x%08" PRIX32, mFeatureMap); + if (mSsid.size()) + { + this->mCtx.Dispatch(Event::Create(AddOrUpdateWiFiNetwork{ mSsid, mWiFiCredentials })); + } + else if (mOperationalDataset.size()) + { + this->mCtx.Dispatch(Event::Create(AddOrUpdateThreadNetwork{ mOperationalDataset })); + } + else + { + // We should only arrive here if the commissionee was not located on + // an IP network. + this->mCtx.Dispatch(Event::Create()); + } + }; + +private: + NetworkFeatureMap mFeatureMap; + ByteSpan mOperationalDataset; + ByteSpan mSsid; + ByteSpan mWiFiCredentials; +}; + +struct NetworkAdded : chip::Commissioner::States::Base +{ + NetworkAdded(Context & ctx, Commissionee & commissionee, NetworkId networkId) : + Base(ctx, commissionee, "NetworkAdded"), mNetworkId(networkId) + {} + void Enter() { this->mCtx.Dispatch(Event::Create(mNetworkId)); } + +private: + NetworkId mNetworkId; +}; + +struct NetworkEnabled : chip::Commissioner::States::Base + +{ + NetworkEnabled(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "NetworkEnabled") {} + void Enter() { this->mCtx.Dispatch(Event::Create()); } +}; + +struct CaseComplete : chip::Commissioner::States::Base +{ + CaseComplete(Context & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "CaseComplete") {} + void Enter() { this->mCtx.Dispatch(Event::Create()); } +}; + +struct CommissioningComplete : chip::Commissioner::States::Base +{ + CommissioningComplete(Context & ctx, Commissionee & commissionee, OnSuccess onSuccess) : + Base(ctx, commissionee, "CommissioningComplete"), mOnSuccess(onSuccess) + {} + void Enter() + { + if (mOnSuccess != nullptr) + { + mOnSuccess(this->mCommissionee); + } + } + +private: + OnSuccess mOnSuccess; +}; + +struct Failed : chip::Commissioner::States::Base + +{ + Failed(Context & ctx, Commissionee & commissionee, OnFailure onFailure) : + Base(ctx, commissionee, "Failed"), mOnFailure(onFailure) + {} + void Enter() + { + if (mOnFailure != nullptr) + { + mOnFailure(this->mCommissionee); + } + } + +private: + OnFailure mOnFailure; +}; + +// Variant State definition +using State = chip::StateMachine::VariantState< + Idle, ParsingOnboardingPayload, CommissionableNodeDiscovery, AwaitingCommissionableDiscovery, AbortingCommissionableDiscovery, + InitiatingPase, FinishingPase, PaseComplete, InvokingArmFailSafe, FailSafeArmed, InvokingAttestationRequest, + InvokingDacCertificateChainRequest, InvokingPaiCertificateChainRequest, CapturingAttestationChallenge, AttestationVerification, + AttestationVerified, InvokingOpCSRRequest, OpCSRResponseReceived, SigningCertificates, CertificatesSigned, + InvokingAddTrustedRootCertificate, InvokingAddNOC, OpCredsWritten, ReadingNetworkFeatureMap, NetworkFeatureMapRead, + InvokingAddOrUpdateWiFiNetwork, InvokingAddOrUpdateThreadNetwork, NetworkAdded, InvokingConnectNetwork, NetworkEnabled, + OperationalDiscovery, InitiatingCase, CaseComplete, InvokingCommissioningComplete, CommissioningComplete, Failed>; + +class StateFactory +{ +public: + StateFactory(Context & ctx, Commissionee & commissionee) : mCtx(ctx), mCommissionee(commissionee) {} + void Init(OpCredsIssuer * issuer, FabricIndex fabricIndex, NodeId nodeId, ByteSpan operationalDataset, ByteSpan ssid, + ByteSpan wiFiCredentials); + void SetCallbacks(OnSuccess onSuccess, OnFailure onFailure); + + // clang-format off + auto CreateIdle() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateParsingOnboardingPayload(OnboardingPayload payload) + { + return State::Create(mCtx, mCommissionee, payload); + } + auto CreateCommissionableNodeDiscovery(ParsedPayload payload) + { + return State::Create(mCtx, mCommissionee, payload); + } + auto CreateAwaitingCommissionableDiscovery(const CommissionableNodeDiscovery & previous) + { + return State::Create(previous); + } + auto CreateAbortingCommissionableDiscovery(const CommissionableNodeDiscovery & previous) + { + return State::Create(previous); + } + auto CreateInitiatingPase(const CommissionableNodeDiscovery & previous) + { + return State::Create(previous); + } + auto CreateFinishingPase(const InitiatingPase & previous) + { + return State::Create(previous); + } + auto CreatePaseComplete() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateInvokingArmFailSafe(ArmFailSafe request) + { + return State::Create(mCtx, mCommissionee, request); + } + auto CreateFailSafeArmed() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateInvokingAttestationRequest(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateInvokingDacCertificateChainRequest(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateInvokingPaiCertificateChainRequest(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateCapturingAttestationChallenge(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateAttestationVerification(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateAttestationVerified(AttestationInformation attestationInformation) + { + return State::Create(mCtx, mCommissionee, attestationInformation); + } + auto CreateInvokingOpCSRRequest(NocsrInformation nocsrInformation) + { + return State::Create(mCtx, mCommissionee, nocsrInformation); + } + auto CreateOpCSRResponseReceived(NocsrInformation nocsrInformation) + { + return State::Create(mCtx, mCommissionee, nocsrInformation); + } + auto CreateSigningCertificates(NocsrInformation nocsrInformation) + { + return State::Create(mCtx, mCommissionee, nocsrInformation, mIssuer, mFabricIndex, mNodeId); + } + auto CreateCertificatesSigned(OperationalCredentials opCreds) + { + return State::Create(mCtx, mCommissionee, opCreds); + } + auto CreateInvokingAddTrustedRootCertificate(OperationalCredentials opCreds) + { + return State::Create(mCtx, mCommissionee, opCreds); + } + auto CreateInvokingAddNOC(OperationalCredentials opCreds) + { + return State::Create(mCtx, mCommissionee, opCreds); + } + auto CreateOpCredsWritten() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateReadingNetworkFeatureMap() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateNetworkFeatureMapRead(NetworkFeatureMap featureMap) + { + return State::Create(mCtx, mCommissionee, featureMap, mOperationalDataset, mSsid, mWiFiCredentials); + } + auto CreateInvokingAddOrUpdateWiFiNetwork(AddOrUpdateWiFiNetwork request) + { + return State::Create(mCtx, mCommissionee, request); + } + auto CreateInvokingAddOrUpdateThreadNetwork(AddOrUpdateThreadNetwork request) + { + return State::Create(mCtx, mCommissionee, request); + } + auto CreateNetworkAdded(NetworkId networkId) + { + return State::Create(mCtx, mCommissionee, networkId); + } + auto CreateInvokingConnectNetwork(NetworkId networkId) + { + return State::Create(mCtx, mCommissionee, networkId); + } + auto CreateNetworkEnabled() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateOperationalDiscovery() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateInitiatingCase(OperationalRecord record) + { + return State::Create(mCtx, mCommissionee, record); + } + auto CreateCaseComplete() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateInvokingCommissioningComplete() + { + return State::Create(mCtx, mCommissionee); + } + auto CreateCommissioningComplete() + { + return State::Create(mCtx, mCommissionee, mOnSuccess); + } + auto CreateFailed() + { + return State::Create(mCtx, mCommissionee, mOnFailure); + } + // clang-format on + +private: + Context & mCtx; + Commissionee & mCommissionee; + OpCredsIssuer * mIssuer; + FabricIndex mFabricIndex = kUndefinedFabricIndex; + NodeId mNodeId = kUndefinedNodeId; + ByteSpan mOperationalDataset; + ByteSpan mSsid; + ByteSpan mWiFiCredentials; + OnSuccess mOnSuccess; + OnFailure mOnFailure; +}; + +struct Transitions +{ + Transitions(Context & ctx, Commissionee & commissionee) : mFactory(ctx, commissionee), mCtx(ctx) {} + chip::StateMachine::Optional operator()(const State & state, const Event & event); + State GetInitState() { return mFactory.CreateIdle(); } + StateFactory mFactory; + Context & mCtx; +}; + +class ExampleCommissioningStateMachine : public chip::StateMachine::StateMachine +{ + +public: + ExampleCommissioningStateMachine() : + chip::StateMachine::StateMachine(mTransitions), mTransitions(*this, mCommissionee) + {} + void Init(SystemState * systemState, OpCredsIssuer * issuer, FabricIndex fabricIndex, NodeId nodeId); + void Init(SystemState * systemState, OpCredsIssuer * issuer, FabricIndex fabricIndex, NodeId nodeId, + ByteSpan operationalDataset, ByteSpan ssid, ByteSpan wiFiCredentials); + void Shutdown(); + CHIP_ERROR Commission(const char * onboardingPayload, OnSuccess onSuccess, OnFailure onFailure); + CHIP_ERROR Commission(chip::SetupPayload & onboardingPayload, OnSuccess onSuccess, OnFailure onFailure); + CHIP_ERROR Commission(chip::RendezvousInformationFlags flags, uint16_t discriminator, uint32_t setUpPINCode, + OnSuccess onSuccess, OnFailure onFailure); + CHIP_ERROR GrabCommissionee(chip::Controller::DeviceController & controller); + CHIP_ERROR ScheduleTimeout(System::Clock::Timeout aDelay); + CHIP_ERROR CancelTimeout(); + +private: + CHIP_ERROR Commission(Event event, OnSuccess onSuccess, OnFailure onFailure); + static void DispatchTimeout(System::Layer * aLayer, void * appState); + Transitions mTransitions; + Commissionee mCommissionee; +}; + +} // namespace ExampleCommissioningStateMachine +} // namespace Commissioner +} // namespace chip diff --git a/src/commissioner/States.h b/src/commissioner/States.h new file mode 100644 index 00000000000000..897d38fb958957 --- /dev/null +++ b/src/commissioner/States.h @@ -0,0 +1,870 @@ +/* + * Copyright (c) 2020-2021 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Commissioner { +namespace States { + +constexpr uint16_t kCommissioningEndpoint = 0; // TODO: should get the endpoint information from the descriptor cluster. + +template +struct Base +{ + Base(TContext & ctx, Commissionee & commissionee, const char * name) : mCtx(ctx), mCommissionee(commissionee), mName(name) {} + void LogTransition(const char * previous) { ChipLogDetail(Controller, "%s -> %s", previous, GetName()); } + const char * GetName() { return mName; } + void Enter() {} + void Exit() {} + +protected: + TContext & mCtx; + Commissionee & mCommissionee; + const char * mName; +}; + +template +struct ParsingOnboardingPayload : Base +{ + ParsingOnboardingPayload(TContext & ctx, Commissionee & commissionee, Events::OnboardingPayload & payload) : + Base(ctx, commissionee, "ParsingOnboardingPayload"), mPayload(payload) + {} + + void Enter() + { + CHIP_ERROR err; + bool isQRCode = strncmp(mPayload.mPayload, kQRCodePrefix, strlen(kQRCodePrefix)) == 0; + Platform::SharedPtr parsed = Platform::MakeShared(); + VerifyOrExit(parsed.get() != nullptr, err = CHIP_ERROR_NO_MEMORY); + if (isQRCode) + { + SuccessOrExit(err = chip::QRCodeSetupPayloadParser(mPayload.mPayload).populatePayload(*parsed.get())); + } + else + { + SuccessOrExit(err = chip::ManualSetupPayloadParser(mPayload.mPayload).populatePayload(*parsed.get())); + } + exit: + if (err == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create>(parsed)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::OnboardingPayload mPayload; +}; + +template +struct CommissionableNodeDiscovery : Base, CommissionableNodeDiscoverer::Delegate +{ + CommissionableNodeDiscovery(TContext & ctx, Commissionee & commissionee, Platform::SharedPtr payload) : + Base(ctx, commissionee, "CommissionableNodeDiscovery"), mPayload(payload) + {} + + void Enter() + { + CHIP_ERROR err = CHIP_NO_ERROR; + mDiscoverer = + Platform::MakeShared(*this->mCommissionee.mSystemState, mPayload, this); + VerifyOrExit(mDiscoverer.get() != nullptr, err = CHIP_ERROR_NO_MEMORY); + SuccessOrExit(err = mDiscoverer.get()->Init()); + SuccessOrExit(err = mDiscoverer.get()->Discover()); + exit: + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + void OnDiscovery() override { this->mCtx.Dispatch(TContext::Event::template Create()); } + void OnDiscovererShutdown() override {} + + Platform::SharedPtr mPayload; + Platform::SharedPtr mDiscoverer; +}; + +template +struct AwaitingCommissionableDiscovery : CommissionableNodeDiscovery +{ + AwaitingCommissionableDiscovery(const CommissionableNodeDiscovery & previous) : + CommissionableNodeDiscovery(previous) + { + this->mName = "AwaitingCommissionableDiscovery"; + } + void Enter() { this->mDiscoverer.get()->SetDelegate(this); } + +protected: + void OnDiscovery() override { this->mCtx.Dispatch(TContext::Event::template Create()); } + void OnDiscovererShutdown() override {} +}; + +template +struct PasscodeAuthenticatedSessionEstablishment : CommissionableNodeDiscovery, SessionEstablishmentDelegate +{ + PasscodeAuthenticatedSessionEstablishment(const CommissionableNodeDiscovery & previous) : + CommissionableNodeDiscovery(previous) + { + this->mName = "PasscodeAuthenticatedSessionEstablishment"; + } + + void Enter() + { + this->mDiscoverer.get()->SetDelegate(this); + while ((mStatus = this->mDiscoverer.get()->GetNextCandidate(this->mCommissionee)) == CHIP_NO_ERROR) + { + VerifyOrReturn((mStatus = TryPase()) != CHIP_NO_ERROR); + } + if (mStatus == CHIP_ERROR_NOT_FOUND) + { + // CHIP_ERROR_NOT_FOUND from the discoverer means we should keep looking. + // This is not ncessarily a failure. + this->mCtx.Dispatch(TContext::Event::template Create()); + } + else + { + this->mDiscoverer.get()->Shutdown(); + } + } + + void ShutdownDiscoverer() { this->mDiscoverer.get()->Shutdown(); } + +protected: + CHIP_ERROR TryPase() + { + CHIP_ERROR err = CHIP_NO_ERROR; + uint16_t sessionId = 0; + Messaging::ExchangeContext * exchange = nullptr; + Optional session; + + mPairing = Platform::MakeShared(); + VerifyOrExit(mPairing.get() != nullptr, err = CHIP_ERROR_NO_MEMORY); + session = this->mCommissionee.mSystemState->SessionMgr()->CreateUnauthenticatedSession( + this->mCommissionee.mCommissionableNodeAddress.Value(), this->mCommissionee.mMrpConfig.ValueOr(gDefaultMRPConfig)); + VerifyOrExit(session.HasValue(), err = CHIP_ERROR_NO_MEMORY); + exchange = this->mCommissionee.mSystemState->ExchangeMgr()->NewContext(session.Value(), this->mPairing.get()); + VerifyOrExit(exchange != nullptr, err = CHIP_ERROR_INTERNAL); + SuccessOrExit(err = this->mCommissionee.mSystemState->GetSessionIDAllocator()->Allocate(sessionId)); + SuccessOrExit(err = mPairing.get()->Pair(this->mCommissionee.mCommissionableNodeAddress.Value(), + this->mPayload.get()->setUpPINCode, sessionId, this->mCommissionee.mMrpConfig, + exchange, this)); + exit: + return err; + } + + void OnSessionEstablishmentError(CHIP_ERROR error) override + { +#if CONFIG_NETWORK_LAYER_BLE + this->mCommissionee.CloseBle(); +#endif + Enter(); // try next candidate, if any exists + } + + void OnSessionEstablished() override + { + mStatus = this->mCommissionee.mSystemState->SessionMgr()->NewPairing( + this->mCommissionee.mPaseSession, Optional::Value(mPairing.get()->GetPeerAddress()), + kUndefinedNodeId, mPairing.get(), CryptoContext::SessionRole::kInitiator, kUndefinedFabricId); + if (mStatus != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed in setting up secure channel: err %s", ErrorStr(mStatus)); + } + else + { + ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake"); + } + // We can't dustruct the PASESession object until the stack unwinds + // because the exchange isn't yet closed, the exchange has a pointer + // to the PASESession object, and ACK for the secure_channel Status + // Report hasn't been sent yet. Therefore, schedule dispatch, and + // implicit destruction, for the next iteration of the event loop. + this->mCommissionee.mSystemState->SystemLayer()->ScheduleWork(Dispose, this); + } + + static void Dispose(System::Layer * layer, void * context) + { + auto state = static_cast(context); + state->ShutdownDiscoverer(); + } + + void OnDiscovery() override {} + void OnDiscovererShutdown() override + { + if (mStatus == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + + Platform::SharedPtr mPairing; + CHIP_ERROR mStatus = CHIP_NO_ERROR; +}; + +template +struct InvokingArmFailSafe : Base +{ + using ArmFailSafe = app::Clusters::GeneralCommissioning::Commands::ArmFailSafe::Type; + using Response = ArmFailSafe::ResponseType; + InvokingArmFailSafe(TContext & ctx, Commissionee & commissionee, ArmFailSafe request) : + Base(ctx, commissionee, "InvokingArmFailSafe"), mRequest(request) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = + this->mCommissionee.template PaseInvoke(kCommissioningEndpoint, mRequest, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + ArmFailSafe mRequest; +}; + +template +struct InvokingAttestationRequest : Base +{ + using AttestationRequest = app::Clusters::OperationalCredentials::Commands::AttestationRequest::Type; + using Response = AttestationRequest::ResponseType; + InvokingAttestationRequest(TContext & ctx, Commissionee & commissionee, + Events::AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "InvokingAttestationRequest"), + mAttestationInformation(attestationInformation) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + CHIP_ERROR err = CHIP_NO_ERROR; + SuccessOrExit(err = mAttestationInformation.AttestationElements()->Set(response.attestationElements)); + SuccessOrExit(err = mAttestationInformation.Signature()->Set(response.signature)); + exit: + if (err == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mAttestationInformation)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, AttestationRequest{ mAttestationInformation.Nonce()->Get() }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::AttestationInformation mAttestationInformation; +}; + +template +struct InvokingDacCertificateChainRequest : Base +{ + using CertificateChainRequest = app::Clusters::OperationalCredentials::Commands::CertificateChainRequest::Type; + using Response = CertificateChainRequest::ResponseType; + InvokingDacCertificateChainRequest(TContext & ctx, Commissionee & commissionee, + Events::AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "InvokingDacCertificateChainRequest"), + mAttestationInformation(attestationInformation) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + if (mAttestationInformation.Dac()->Set(response.certificate) == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mAttestationInformation)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, CertificateChainRequest{ Credentials::CertificateType::kDAC }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::AttestationInformation mAttestationInformation; +}; + +template +struct InvokingPaiCertificateChainRequest : Base +{ + using CertificateChainRequest = app::Clusters::OperationalCredentials::Commands::CertificateChainRequest::Type; + using Response = CertificateChainRequest::ResponseType; + InvokingPaiCertificateChainRequest(TContext & ctx, Commissionee & commissionee, + Events::AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "InvokingPaiCertificateChainRequest"), + mAttestationInformation(attestationInformation) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + if (mAttestationInformation.Pai()->Set(response.certificate) == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mAttestationInformation)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, CertificateChainRequest{ Credentials::CertificateType::kPAI }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::AttestationInformation mAttestationInformation; +}; + +template +struct CapturingAttestationChallenge : Base +{ + CapturingAttestationChallenge(TContext & ctx, Commissionee & commissionee, + Events::AttestationInformation & attestationInformation) : + Base(ctx, commissionee, "CapturingAttestationChallenge"), + mAttestationInformation(attestationInformation) + {} + void Enter() + { + const ByteSpan challenge = this->mCommissionee.mSystemState->SessionMgr() + ->GetSecureSession(this->mCommissionee.mPaseSession.Get()) + ->GetCryptoContext() + .GetAttestationChallenge(); + if (mAttestationInformation.Challenge()->Set(challenge) == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mAttestationInformation)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::AttestationInformation mAttestationInformation; +}; + +template +struct InvokingOpCSRRequest : Base +{ + using OpCSRRequest = app::Clusters::OperationalCredentials::Commands::OpCSRRequest::Type; + using Response = OpCSRRequest::ResponseType; + InvokingOpCSRRequest(TContext & ctx, Commissionee & commissionee, Events::NocsrInformation nocsrInformation) : + Base(ctx, commissionee, "InvokingOpCSRRequest"), mNocsrInformation(nocsrInformation) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + CHIP_ERROR err; + SuccessOrExit(err = mNocsrInformation.NocsrElements()->Set(response.NOCSRElements)); + SuccessOrExit(err = mNocsrInformation.Signature()->Set(response.attestationSignature)); + exit: + if (err == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mNocsrInformation)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, OpCSRRequest{ mNocsrInformation.Nonce()->Get() }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::NocsrInformation mNocsrInformation; +}; + +template +struct InvokingAddTrustedRootCertificate : Base +{ + using AddTrustedRootCertificate = app::Clusters::OperationalCredentials::Commands::AddTrustedRootCertificate::Type; + using Response = AddTrustedRootCertificate::ResponseType; + InvokingAddTrustedRootCertificate(TContext & ctx, Commissionee & commissionee, Events::OperationalCredentials opCreds) : + Base(ctx, commissionee, "InvokingAddTrustedRootCertificate"), mOpCreds(opCreds) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create(mOpCreds)); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, AddTrustedRootCertificate{ mOpCreds.Rcac()->Get() }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::OperationalCredentials mOpCreds; +}; + +template +struct InvokingAddNOC : Base +{ + using AddNOC = app::Clusters::OperationalCredentials::Commands::AddNOC::Type; + using Response = AddNOC::ResponseType; + InvokingAddNOC(TContext & ctx, Commissionee & commissionee, Events::OperationalCredentials opCreds) : + Base(ctx, commissionee, "InvokingAddNOC"), mOpCreds(opCreds) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + PeerId operationalId; + CHIP_ERROR err; + SuccessOrExit(err = mOpCreds.GetOperationalId(operationalId)); + this->mCommissionee.mOperationalId.SetValue(operationalId); + ChipLogProgress(Controller, "AddNOC succeeded for node %016" PRIX64 "-%016" PRIX64, + this->mCommissionee.mOperationalId.Value().GetCompressedFabricId(), + this->mCommissionee.mOperationalId.Value().GetNodeId()); + exit: + if (err == CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create(mOpCreds)); + } + else + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + AddNOC request = { + .NOCValue = mOpCreds.Noc()->Get(), + .ICACValue = mOpCreds.Icac()->Get().size() ? Optional(mOpCreds.Icac()->Get()) : Optional{}, + .IPKValue = mOpCreds.Ipk()->Get(), + .caseAdminNode = mOpCreds.mAdminSubject, + .adminVendorId = mOpCreds.mAdminVendorId, + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke(kCommissioningEndpoint, request, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::OperationalCredentials mOpCreds; +}; + +template +struct ReadingNetworkCommissioningClusterFeatureMap : Base +{ + ClusterId clusterId = app::Clusters::NetworkCommissioning::Id; + AttributeId attributeId = app::Clusters::NetworkCommissioning::Attributes::FeatureMap::Id; + using FeatureMap = app::Clusters::NetworkCommissioning::Attributes::FeatureMap::TypeInfo::DecodableType; + ReadingNetworkCommissioningClusterFeatureMap(TContext & ctx, Commissionee & commissionee) : + Base(ctx, commissionee, "ReadingNetworkCommissioningClusterFeatureMap") + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteAttributePath & commandPath, const FeatureMap & aData) { + this->mCtx.Dispatch(TContext::Event::template Create(aData)); + }; + + auto onFailure = [this](const app::ConcreteAttributePath * commandPath, app::StatusIB status, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + + CHIP_ERROR err = + this->mCommissionee.template PaseRead(kCommissioningEndpoint, clusterId, attributeId, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } +}; + +template +struct InvokingAddOrUpdateWiFiNetwork : Base +{ + using AddOrUpdateWiFiNetwork = app::Clusters::NetworkCommissioning::Commands::AddOrUpdateWiFiNetwork::Type; + using Response = AddOrUpdateWiFiNetwork::ResponseType; + InvokingAddOrUpdateWiFiNetwork(TContext & ctx, Commissionee & commissionee, AddOrUpdateWiFiNetwork request) : + Base(ctx, commissionee, "InvokingAddOrUpdateWiFiNetwork"), mRequest(request) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create(mNetworkId)); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err; + SuccessOrExit(err = mNetworkId.Set(mRequest.ssid)); // deep-copy network ID for next state + SuccessOrExit(err = this->mCommissionee.template PaseInvoke(kCommissioningEndpoint, mRequest, + onSuccess, onFailure)); + exit: + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + AddOrUpdateWiFiNetwork mRequest; + Events::NetworkId mNetworkId; +}; + +template +struct InvokingAddOrUpdateThreadNetwork : Base +{ + using AddOrUpdateThreadNetwork = app::Clusters::NetworkCommissioning::Commands::AddOrUpdateThreadNetwork::Type; + using Response = AddOrUpdateThreadNetwork::ResponseType; + InvokingAddOrUpdateThreadNetwork(TContext & ctx, Commissionee & commissionee, AddOrUpdateThreadNetwork request) : + Base(ctx, commissionee, "InvokingAddOrUpdateThreadNetwork"), mRequest(request) + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create(mNetworkId)); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err; + SuccessOrExit( + err = mNetworkId.FromThreadOperationalDataset(mRequest.operationalDataset)); // deep-copy ext pan ID for next state + SuccessOrExit(err = this->mCommissionee.template PaseInvoke(kCommissioningEndpoint, mRequest, + onSuccess, onFailure)); + exit: + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + AddOrUpdateThreadNetwork mRequest; + Events::NetworkId mNetworkId; +}; + +template +struct InvokingConnectNetwork : Base +{ + using ConnectNetwork = app::Clusters::NetworkCommissioning::Commands::ConnectNetwork::Type; + using Response = ConnectNetwork::ResponseType; + InvokingConnectNetwork(TContext & ctx, Commissionee & commissionee, Events::NetworkId networkId) : + Base(ctx, commissionee, "InvokingConnectNetwork"), mNetworkId(networkId) + {} + + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.template PaseInvoke( + kCommissioningEndpoint, ConnectNetwork{ mNetworkId.Get() }, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + +protected: + Events::NetworkId mNetworkId; +}; + +template +struct OperationalDiscovery : Base, chip::Dnssd::ResolverDelegate +{ + OperationalDiscovery(TContext & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "OperationalDiscovery") {} + + void Enter() + { +#if !CHIP_DEVICE_CONFIG_ENABLE_DNSSD + this->mCtx.Dispatch(TContext::Event::template Create()); + } +#else + CHIP_ERROR err; + SuccessOrExit(err = mDnsResolver.Init(this->mCommissionee.mSystemState->UDPEndPointManager())); + mDnsResolver.SetResolverDelegate(this); + SuccessOrExit(err = mDnsResolver.ResolveNodeId(this->mCommissionee.mOperationalId.Value(), Inet::IPAddressType::kAny)); + exit: + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + + void Exit() + { + mDnsResolver.Shutdown(); + // TODO: Setting the instance delegate to nullptr is a workaround for + // #13227. If we do not do this, minimal mdns can call back into + // our allocated delegate proxy after it is freed. + chip::Dnssd::Resolver::Instance().SetResolverDelegate(nullptr); + } + +protected: + void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override {} + + void OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeData) override + { + CHIP_ERROR err = CHIP_NO_ERROR; + auto record = Platform::MakeShared(nodeData); + VerifyOrExit(record.get() != nullptr, err = CHIP_ERROR_NO_MEMORY); + this->mCtx.Dispatch(TContext::Event::template Create(record)); + exit: + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } + + void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override + { + ChipLogError(Controller, "Operational discovery failed for node %016" PRIX64 "-%016" PRIX64, + this->mCommissionee.mOperationalId.Value().GetCompressedFabricId(), + this->mCommissionee.mOperationalId.Value().GetNodeId()); + this->mCtx.Dispatch(TContext::Event::template Create()); + } + + Dnssd::ResolverProxy mDnsResolver; +#endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD +}; + +template +struct CertificateAuthenticatedSessionEstablishment : Base, SessionEstablishmentDelegate +{ + CertificateAuthenticatedSessionEstablishment(TContext & ctx, Commissionee & commissionee, + Platform::SharedPtr record) : + Base(ctx, commissionee, "CertificateAuthenticatedSessionEstablishment"), + mRecord(record) + {} + + void Enter() + { + while (GetNextCandidate() == CHIP_NO_ERROR) + { + VerifyOrReturn(TryCase() != CHIP_NO_ERROR); + } + this->mCtx.Dispatch(TContext::Event::template Create()); + } + + void DispatchStatus() + { + auto status = + mSuccess ? TContext::Event::template Create() : TContext::Event::template Create(); + this->mCtx.Dispatch(status); + } + +protected: + CHIP_ERROR GetNextCandidate() + { + if (mIdx >= mRecord.get()->mNumIPs) + { + return CHIP_ERROR_NOT_FOUND; + } + Inet::InterfaceId interfaceId = + mRecord.get()->mAddress[mIdx].IsIPv6LinkLocal() ? mRecord.get()->mInterfaceId : Inet::InterfaceId::Null(); +#if 0 + // TODO: TCP support + if (mRecord.get()->supportsTcp) + { + this->mCommissionee.mOperationalAddress.SetValue(Transport::PeerAddress::TCP(mRecord.get()->mAddress[mIdx], mRecord.get()->mPort, interfaceId)); + this->mCommissionee.mMrpConfig.ClearValue(); + } + else +#endif + { + this->mCommissionee.mOperationalAddress.SetValue( + Transport::PeerAddress::UDP(mRecord.get()->mAddress[mIdx], mRecord.get()->mPort, interfaceId)); + this->mCommissionee.mMrpConfig.SetValue(mRecord.get()->GetMRPConfig()); + } +#if CHIP_PROGRESS_LOGGING + char addressStr[Transport::PeerAddress::kMaxToStringSize]; + this->mCommissionee.mOperationalAddress.Value().ToString(addressStr); + ChipLogProgress(Controller, "Operational identity discovered at %s", addressStr); +#endif // CHIP_PROGRESS_LOGGING + ++mIdx; + return CHIP_NO_ERROR; + } + + CHIP_ERROR TryCase() + { + CHIP_ERROR err = CHIP_NO_ERROR; + uint16_t sessionId = 0; + Messaging::ExchangeContext * exchange = nullptr; + Optional session; + FabricInfo * fabric; + + fabric = this->mCommissionee.mSystemState->Fabrics()->FindFabricWithCompressedId( + this->mCommissionee.mOperationalId.Value().GetCompressedFabricId()); + VerifyOrExit(fabric != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + mPairing = Platform::MakeShared(); + VerifyOrExit(mPairing.get() != nullptr, err = CHIP_ERROR_NO_MEMORY); + session = this->mCommissionee.mSystemState->SessionMgr()->CreateUnauthenticatedSession( + this->mCommissionee.mOperationalAddress.Value(), this->mCommissionee.mMrpConfig.ValueOr(gDefaultMRPConfig)); + VerifyOrExit(session.HasValue(), err = CHIP_ERROR_NO_MEMORY); + exchange = this->mCommissionee.mSystemState->ExchangeMgr()->NewContext(session.Value(), this->mPairing.get()); + VerifyOrExit(exchange != nullptr, err = CHIP_ERROR_INTERNAL); + SuccessOrExit(err = this->mCommissionee.mSystemState->GetSessionIDAllocator()->Allocate(sessionId)); + SuccessOrExit(err = mPairing.get()->EstablishSession(this->mCommissionee.mOperationalAddress.Value(), fabric, + this->mCommissionee.mOperationalId.Value().GetNodeId(), sessionId, + exchange, this, this->mCommissionee.mMrpConfig)); + exit: + return err; + } + + void OnSessionEstablishmentError(CHIP_ERROR error) override + { + Enter(); // try next candidate, if any exists + } + + void OnSessionEstablished() override + { + CHIP_ERROR err = this->mCommissionee.mSystemState->SessionMgr()->NewPairing( + this->mCommissionee.mCaseSession, this->mCommissionee.mOperationalAddress, + this->mCommissionee.mOperationalId.Value().GetNodeId(), mPairing.get(), CryptoContext::SessionRole::kInitiator, + kUndefinedFabricId); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed in setting up secure channel: err %s", ErrorStr(err)); + } + else + { + ChipLogDetail(Controller, "Remote device completed SPAKE2+ handshake"); + mSuccess = true; + } + // We can't dustruct the CASESession object until the stack unwinds + // because the exchange isn't yet closed, the exchange has a pointer + // to the CASESession object, and ACK for the secure_channel Status + // Report hasn't been sent yet. Therefore, schedule dispatch, and + // implicit destruction, for the next iteration of the event loop. + this->mCommissionee.mSystemState->SystemLayer()->ScheduleWork(Dispose, this); + } + + static void Dispose(System::Layer * layer, void * context) + { + auto state = static_cast(context); + state->DispatchStatus(); + } + + Platform::SharedPtr mRecord; + Platform::SharedPtr mPairing; + size_t mIdx = 0; + bool mSuccess = false; +}; + +template +struct InvokingCommissioningComplete : Base +{ + using CommissioningComplete = app::Clusters::GeneralCommissioning::Commands::CommissioningComplete::Type; + using Response = CommissioningComplete::ResponseType; + InvokingCommissioningComplete(TContext & ctx, Commissionee & commissionee) : + Base(ctx, commissionee, "InvokingCommissioningComplete") + {} + void Enter() + { + auto onSuccess = [this](const app::ConcreteCommandPath & commandPath, const app::StatusIB & aStatus, + const Response & response) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + auto onFailure = [this](const app::StatusIB & aStatus, CHIP_ERROR aError) { + this->mCtx.Dispatch(TContext::Event::template Create()); + }; + CHIP_ERROR err = this->mCommissionee.CaseInvoke(kCommissioningEndpoint, CommissioningComplete{}, onSuccess, onFailure); + if (err != CHIP_NO_ERROR) + { + this->mCtx.Dispatch(TContext::Event::template Create()); + } + } +}; + +template +struct Failed : Base +{ + Failed(TContext & ctx, Commissionee & commissionee) : Base(ctx, commissionee, "Failed") {} +}; + +} // namespace States +} // namespace Commissioner +} // namespace chip diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 435a5a84022407..190e6de024b192 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -338,6 +338,7 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate * @brief Get the raw Fabric ID assigned to the device. */ uint64_t GetFabricId() const { return mFabricId; } + FabricIndex GetFabricIndex() const { return mFabricIndex; } /** * @brief Get the Node ID of this instance. @@ -347,6 +348,8 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate void ReleaseOperationalDevice(NodeId remoteDeviceId); OperationalCredentialsDelegate * GetOperationalCredentialsDelegate() { return mOperationalCredentialsDelegate; } + void EmplaceOperationalDevice(const Transport::PeerAddress & addr, const Optional & config, + const PeerId & peerId, SessionHolder & session); protected: enum class State @@ -405,9 +408,6 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate DiscoveredNodeList GetDiscoveredNodes() override { return DiscoveredNodeList(mCommissionableNodes); } #endif // CHIP_DEVICE_CONFIG_ENABLE_DNSSD - void EmplaceOperationalDevice(const Transport::PeerAddress & addr, const Optional & config, - const PeerId & peerId, SessionHolder & session); - private: void ReleaseOperationalDevice(OperationalDeviceProxy * device); diff --git a/src/controller/CHIPDeviceControllerFactory.cpp b/src/controller/CHIPDeviceControllerFactory.cpp index 68c9ddbdd72e75..bbe2cbd9ea8119 100644 --- a/src/controller/CHIPDeviceControllerFactory.cpp +++ b/src/controller/CHIPDeviceControllerFactory.cpp @@ -156,6 +156,11 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params) return CHIP_NO_ERROR; } +DeviceControllerSystemState * DeviceControllerFactory::GetSystemState() +{ + return mSystemState; +} + void DeviceControllerFactory::PopulateInitParams(ControllerInitParams & controllerParams, const SetupParams & params) { #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD diff --git a/src/controller/CHIPDeviceControllerFactory.h b/src/controller/CHIPDeviceControllerFactory.h index ca2667c68619dd..aea0d82a67eb93 100644 --- a/src/controller/CHIPDeviceControllerFactory.h +++ b/src/controller/CHIPDeviceControllerFactory.h @@ -92,6 +92,7 @@ class DeviceControllerFactory CHIP_ERROR Init(FactoryInitParams params); CHIP_ERROR SetupController(SetupParams params, DeviceController & controller); CHIP_ERROR SetupCommissioner(SetupParams params, DeviceCommissioner & commissioner); + DeviceControllerSystemState * GetSystemState(); // ----- IO ----- /** diff --git a/src/lib/dnssd/Resolver.h b/src/lib/dnssd/Resolver.h index 86020e551b0264..8cd03fb70d0b74 100644 --- a/src/lib/dnssd/Resolver.h +++ b/src/lib/dnssd/Resolver.h @@ -111,6 +111,11 @@ struct DiscoveredNodeData bool supportsTcp; Optional mrpRetryIntervalIdle; Optional mrpRetryIntervalActive; + ReliableMessageProtocolConfig GetMRPConfig() const + { + return ReliableMessageProtocolConfig(mrpRetryIntervalIdle.ValueOr(gDefaultMRPConfig.mIdleRetransTimeout), + mrpRetryIntervalActive.ValueOr(gDefaultMRPConfig.mActiveRetransTimeout)); + } uint16_t port; int numIPs; Inet::InterfaceId interfaceId[kMaxIPAddresses]; diff --git a/src/lib/support/StateMachine.h b/src/lib/support/StateMachine.h index 7a9e255b440719..9266ebf7837aa5 100644 --- a/src/lib/support/StateMachine.h +++ b/src/lib/support/StateMachine.h @@ -136,6 +136,12 @@ class Context * @param evt a variant holding an Event for the State Machine. */ virtual void Dispatch(const TEvent & evt) = 0; + + /** + * Provide access to the Event variant type so we can invoke the static + * Variant Create method. + */ + using Event = TEvent; }; /** @@ -208,8 +214,8 @@ class Context * @tparam TEvent a variant holding the Events. * @tparam TTransitions an object that implements the () operator for transitions. */ -template -class StateMachine : public Context +template +class StateMachine : public TContext { public: StateMachine(TTransitions & tr) : mCurrentState(tr.GetInitState()), mTransitions(tr), mSequence(0) {} diff --git a/src/lib/support/tests/TestStateMachine.cpp b/src/lib/support/tests/TestStateMachine.cpp index 47baef33737049..e0e84a49c2b363 100644 --- a/src/lib/support/tests/TestStateMachine.cpp +++ b/src/lib/support/tests/TestStateMachine.cpp @@ -155,7 +155,7 @@ class SimpleStateMachine { public: Transitions mTransitions; - chip::StateMachine::StateMachine mStateMachine; + chip::StateMachine::StateMachine, State, Event, Transitions> mStateMachine; SimpleStateMachine() : mTransitions(mStateMachine), mStateMachine(mTransitions) {} ~SimpleStateMachine() {} diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index 9d46995548e165..f6504fe80a2b84 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -193,17 +193,21 @@ CHIP_ERROR PASESession::FromSerializable(const PASESessionSerializable & seriali return CHIP_NO_ERROR; } -CHIP_ERROR PASESession::Init(uint16_t mySessionId, uint32_t setupCode, SessionEstablishmentDelegate * delegate) +CHIP_ERROR PASESession::SetDelegate(SessionEstablishmentDelegate * delegate) { VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mDelegate = delegate; + return CHIP_NO_ERROR; +} +CHIP_ERROR PASESession::Init(uint16_t mySessionId, uint32_t setupCode, SessionEstablishmentDelegate * delegate) +{ // Reset any state maintained by PASESession object (in case it's being reused for pairing) Clear(); ReturnErrorOnFailure(mCommissioningHash.Begin()); ReturnErrorOnFailure(mCommissioningHash.AddData(ByteSpan{ Uint8::from_const_char(kSpake2pContext), strlen(kSpake2pContext) })); - - mDelegate = delegate; + ReturnErrorOnFailure(SetDelegate(delegate)); ChipLogDetail(SecureChannel, "Assigned local session key ID %d", mySessionId); SetLocalSessionId(mySessionId); diff --git a/src/protocols/secure_channel/PASESession.h b/src/protocols/secure_channel/PASESession.h index f8229bd6bb2ad4..94454579a09e4c 100644 --- a/src/protocols/secure_channel/PASESession.h +++ b/src/protocols/secure_channel/PASESession.h @@ -228,6 +228,14 @@ class DLL_EXPORT PASESession : public Messaging::ExchangeDelegate, public Pairin */ void OnResponseTimeout(Messaging::ExchangeContext * ec) override; + /** + * @brief + * Set a new delegate for the PASESession to callback to. + * + * @param delegate Callback object + */ + CHIP_ERROR SetDelegate(SessionEstablishmentDelegate * delegate); + Messaging::ExchangeMessageDispatch & GetMessageDispatch() override { return SessionEstablishmentExchangeDispatch::Instance(); } private: diff --git a/src/protocols/secure_channel/RendezvousParameters.h b/src/protocols/secure_channel/RendezvousParameters.h index 1c823d69a03855..cc0d2199e3e234 100644 --- a/src/protocols/secure_channel/RendezvousParameters.h +++ b/src/protocols/secure_channel/RendezvousParameters.h @@ -54,6 +54,13 @@ class RendezvousParameters return *this; } + ReliableMessageProtocolConfig GetMRPConfig() const { return mMRPConfig; } + RendezvousParameters & SetMRPConfig(ReliableMessageProtocolConfig config) + { + mMRPConfig = config; + return *this; + } + bool HasDiscriminator() const { return mDiscriminator <= kMaxRendezvousDiscriminatorValue; } uint16_t GetDiscriminator() const { return mDiscriminator; } RendezvousParameters & SetDiscriminator(uint16_t discriminator) @@ -92,9 +99,10 @@ class RendezvousParameters #endif // CONFIG_NETWORK_LAYER_BLE private: - Transport::PeerAddress mPeerAddress; ///< the peer node address - uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code - uint16_t mDiscriminator = UINT16_MAX; ///< the target peripheral discriminator + Transport::PeerAddress mPeerAddress; ///< the peer node address + ReliableMessageProtocolConfig mMRPConfig = gDefaultMRPConfig; ///< MRP parameters for the peer + uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code + uint16_t mDiscriminator = UINT16_MAX; ///< the target peripheral discriminator PASEVerifier mPASEVerifier; bool mHasPASEVerifier = false;