diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp new file mode 100644 index 00000000000000..b7ebb6f8e0cf07 --- /dev/null +++ b/src/controller/AutoCommissioner.cpp @@ -0,0 +1,96 @@ +/* + * + * 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 + +#include + +namespace chip { +namespace Controller { + +CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStage currentStage) +{ + switch (currentStage) + { + case CommissioningStage::kSecurePairing: + return CommissioningStage::kArmFailsafe; + case CommissioningStage::kArmFailsafe: + return CommissioningStage::kConfigRegulatory; + case CommissioningStage::kConfigRegulatory: + return CommissioningStage::kDeviceAttestation; + case CommissioningStage::kDeviceAttestation: + // TODO(cecille): device attestation casues operational cert provisioinging to happen, This should be a separate stage. + // For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the + // operational network because the provisioning of certificates will trigger the device to start operational advertising. +#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD + return CommissioningStage::kFindOperational; // TODO : once case is working, need to add stages to find and reconnect + // here. +#else + return CommissioningStage::kSendComplete; +#endif + case CommissioningStage::kFindOperational: + return CommissioningStage::kSendComplete; + case CommissioningStage::kSendComplete: + return CommissioningStage::kCleanup; + + // Currently unimplemented. + case CommissioningStage::kConfigACL: + case CommissioningStage::kNetworkSetup: + case CommissioningStage::kNetworkEnable: + case CommissioningStage::kScanNetworks: + case CommissioningStage::kCheckCertificates: + return CommissioningStage::kError; + // Neither of these have a next stage so return kError; + case CommissioningStage::kCleanup: + case CommissioningStage::kError: + return CommissioningStage::kError; + } + return CommissioningStage::kError; +} + +void AutoCommissioner::StartCommissioning(CommissioneeDeviceProxy * proxy) +{ + // TODO: check that there is no commissioning in progress currently. + mCommissioneeDeviceProxy = proxy; + mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe, mParams, this); +} + +void AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) +{ + + if (report.stageCompleted == CommissioningStage::kFindOperational) + { + mOperationalDeviceProxy = report.OperationalNodeFoundData.operationalProxy; + } + CommissioningStage nextStage = GetNextCommissioningStage(report.stageCompleted); + DeviceProxy * proxy = mCommissioneeDeviceProxy; + if (nextStage == CommissioningStage::kSendComplete || nextStage == CommissioningStage::kCleanup) + { + proxy = mOperationalDeviceProxy; + } + + if (proxy == nullptr) + { + ChipLogError(Controller, "Invalid device for commissioning"); + return; + } + mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this); +} + +} // namespace Controller +} // namespace chip diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h new file mode 100644 index 00000000000000..1c8877685612e4 --- /dev/null +++ b/src/controller/AutoCommissioner.h @@ -0,0 +1,46 @@ +/* + * + * 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 + +namespace chip { +namespace Controller { + +class DeviceCommissioner; + +class AutoCommissioner : public CommissioningDelegate +{ +public: + AutoCommissioner(DeviceCommissioner * commissioner) : mCommissioner(commissioner) {} + void SetCommissioningParameters(CommissioningParameters & params) { mParams = params; } + void StartCommissioning(CommissioneeDeviceProxy * proxy); + + void CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) override; + +private: + CommissioningStage GetNextCommissioningStage(CommissioningStage currentStage); + DeviceCommissioner * mCommissioner; + CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr; + OperationalDeviceProxy * mOperationalDeviceProxy = nullptr; + CommissioningParameters mParams = CommissioningParameters(); +}; + +} // namespace Controller +} // namespace chip diff --git a/src/controller/BUILD.gn b/src/controller/BUILD.gn index 6339f2dfddd7a2..38dd3540fd9cd2 100644 --- a/src/controller/BUILD.gn +++ b/src/controller/BUILD.gn @@ -34,6 +34,8 @@ static_library("controller") { if (chip_controller) { sources += [ "AbstractDnssdDiscoveryController.cpp", + "AutoCommissioner.cpp", + "AutoCommissioner.h", "CHIPCommissionableNodeController.cpp", "CHIPCommissionableNodeController.h", "CHIPDeviceController.cpp", diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 7512e74129de7a..1480d8fca7467d 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -608,7 +608,7 @@ DeviceCommissioner::DeviceCommissioner() : mOnAttestationFailureCallback(OnAttestationFailureResponse, this), mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddNOCFailureResponse, this), mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), - mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this), mSetUpCodePairer(this) + mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this), mSetUpCodePairer(this), mAutoCommissioner(this) { mPairingDelegate = nullptr; mPairedDevicesUpdated = false; @@ -858,7 +858,6 @@ CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, Re // Immediately persist the updated mNextKeyID value // TODO maybe remove FreeRendezvousSession() since mNextKeyID is always persisted immediately PersistNextKeyId(); - mCommissioningStage = kSecurePairing; exit: if (err != CHIP_NO_ERROR) @@ -872,7 +871,6 @@ CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, Re if (device != nullptr) { ReleaseCommissioneeDevice(device); - mDeviceBeingCommissioned = nullptr; } } @@ -895,6 +893,8 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId, CommissioningPa return CHIP_ERROR_INCORRECT_STATE; } // If the CSRNonce is passed in, using that else using a random one.. + // TODO(cecille): Once the commissioning stages are separated, this can be removed from the device and moved down into the + // approprirate commissioning step. if (params.HasCSRNonce()) { ReturnErrorOnFailure(device->SetCSRNonce(params.GetCSRNonce().Value())); @@ -920,9 +920,11 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId, CommissioningPa mSystemState->SystemLayer()->StartTimer(chip::System::Clock::Milliseconds32(kSessionEstablishmentTimeout), OnSessionEstablishmentTimeoutCallback, this); + + mAutoCommissioner.SetCommissioningParameters(params); if (device->IsSecureConnected()) { - AdvanceCommissioningStage(CHIP_NO_ERROR); + mAutoCommissioner.StartCommissioning(device); } else { @@ -1031,7 +1033,7 @@ void DeviceCommissioner::OnSessionEstablished() } else { - AdvanceCommissioningStage(CHIP_NO_ERROR); + mAutoCommissioner.StartCommissioning(mDeviceBeingCommissioned); } } else @@ -1515,7 +1517,7 @@ CHIP_ERROR DeviceCommissioner::OnOperationalCredentialsProvisioningCompletion(Co } if (mIsIPRendezvous) { - AdvanceCommissioningStage(CHIP_NO_ERROR); + CommissioningStageComplete(CHIP_NO_ERROR); } else { @@ -1619,7 +1621,7 @@ void BasicSuccess(void * context, uint16_t val) { ChipLogProgress(Controller, "Received success response 0x%x\n", val); DeviceCommissioner * commissioner = static_cast(context); - commissioner->AdvanceCommissioningStage(CHIP_NO_ERROR); + commissioner->CommissioningStageComplete(CHIP_NO_ERROR); } void BasicFailure(void * context, uint8_t status) @@ -1629,6 +1631,17 @@ void BasicFailure(void * context, uint8_t status) commissioner->OnSessionEstablishmentError(static_cast(status)); } +void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err) +{ + if (mCommissioningDelegate == nullptr) + { + return; + } + CommissioningDelegate::CommissioningReport report; + report.stageCompleted = mCommissioningStage; + mCommissioningDelegate->CommissioningStepFinished(err, report); +} + #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD void DeviceCommissioner::OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) { @@ -1675,8 +1688,13 @@ void DeviceCommissioner::OnDeviceConnectedFn(void * context, OperationalDevicePr { if (commissioner->mCommissioningStage == CommissioningStage::kFindOperational) { - commissioner->mDeviceOperational = device; - commissioner->AdvanceCommissioningStage(CHIP_NO_ERROR); + if (commissioner->mCommissioningDelegate != nullptr) + { + CommissioningDelegate::CommissioningReport report; + report.stageCompleted = CommissioningStage::kFindOperational; + report.OperationalNodeFoundData.operationalProxy = device; + commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); + } } else { @@ -1703,84 +1721,25 @@ void DeviceCommissioner::OnDeviceConnectionFailureFn(void * context, NodeId devi commissioner->mPairingDelegate->OnCommissioningComplete(deviceId, error); } -CommissioningStage DeviceCommissioner::GetNextCommissioningStage() -{ - switch (mCommissioningStage) - { - case CommissioningStage::kSecurePairing: - return CommissioningStage::kArmFailsafe; - case CommissioningStage::kArmFailsafe: - return CommissioningStage::kConfigRegulatory; - case CommissioningStage::kConfigRegulatory: - return CommissioningStage::kDeviceAttestation; - case CommissioningStage::kDeviceAttestation: - // TODO(cecille): device attestation casues operational cert provisioinging to happen, This should be a separate stage. - // For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the - // operational network because the provisioning of certificates will trigger the device to start operational advertising. -#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD - return CommissioningStage::kFindOperational; // TODO : once case is working, need to add stages to find and reconnect - // here. -#else - return CommissioningStage::kSendComplete; -#endif - case CommissioningStage::kFindOperational: - return CommissioningStage::kSendComplete; - case CommissioningStage::kSendComplete: - return CommissioningStage::kCleanup; - - // Currently unimplemented. - case CommissioningStage::kConfigACL: - case CommissioningStage::kNetworkSetup: - case CommissioningStage::kNetworkEnable: - case CommissioningStage::kScanNetworks: - case CommissioningStage::kCheckCertificates: - return CommissioningStage::kError; - // Neither of these have a next stage so return kError; - case CommissioningStage::kCleanup: - case CommissioningStage::kError: - return CommissioningStage::kError; - } - return CommissioningStage::kError; -} - -void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) +void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, CommissioningStage step, CommissioningParameters & params, + CommissioningDelegate * delegate) { // For now, we ignore errors coming in from the device since not all commissioning clusters are implemented on the device // side. - CommissioningStage nextStage = GetNextCommissioningStage(); - if (nextStage == CommissioningStage::kError) - { - return; - } - if (!mIsIPRendezvous) { return; } - if (nextStage == CommissioningStage::kSendComplete || nextStage == CommissioningStage::kCleanup) - { - if (mDeviceOperational == nullptr) - { - ChipLogError(Controller, "Invalid operational device for commissioning"); - return; - } - } - else - { - if (mDeviceBeingCommissioned == nullptr) - { - ChipLogError(Controller, "Invalid commissionee device for commissioning"); - return; - } - } + mCommissioningStage = step; + mCommissioningDelegate = delegate; // TODO(cecille): We probably want something better than this for breadcrumbs. - uint64_t breadcrumb = static_cast(nextStage); + uint64_t breadcrumb = static_cast(step); // TODO(cecille): This should be customized per command. constexpr uint32_t kCommandTimeoutMs = 3000; - switch (nextStage) + switch (step) { case CommissioningStage::kArmFailsafe: { // TODO(cecille): This is NOT the right way to do this - we should consider attaching an im delegate per command or @@ -1789,7 +1748,7 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) // TODO(cecille): Find a way to enumerate the clusters here. GeneralCommissioningCluster genCom; // TODO: should get the endpoint information from the descriptor cluster. - genCom.Associate(mDeviceBeingCommissioned, 0); + genCom.Associate(proxy, 0); // TODO(cecille): Make this a parameter uint16_t commissioningExpirySeconds = 60; genCom.ArmFailSafe(mSuccess.Cancel(), mFailure.Cancel(), commissioningExpirySeconds, breadcrumb, kCommandTimeoutMs); @@ -1832,18 +1791,21 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) chip::CharSpan countryCode(countryCodeStr, actualCountryCodeSize); GeneralCommissioningCluster genCom; - genCom.Associate(mDeviceBeingCommissioned, 0); + genCom.Associate(proxy, 0); genCom.SetRegulatoryConfig(mSuccess.Cancel(), mFailure.Cancel(), regulatoryLocation, countryCode, breadcrumb, kCommandTimeoutMs); } break; case CommissioningStage::kDeviceAttestation: { ChipLogProgress(Controller, "Exchanging vendor certificates"); - CHIP_ERROR status = SendCertificateChainRequestCommand(mDeviceBeingCommissioned, CertificateType::kPAI); + // TODO(cecille): Remove the certificates from the CommissioneeDeviceProxy and take from the commissioning parameters. + CHIP_ERROR status = + SendCertificateChainRequestCommand(reinterpret_cast(proxy), CertificateType::kPAI); if (status != CHIP_NO_ERROR) { - ChipLogError(Controller, "Failed in sending 'Certificate Chain Request' command to the device: err %s", ErrorStr(err)); - OnSessionEstablishmentError(err); + ChipLogError(Controller, "Failed in sending 'Certificate Chain Request' command to the device: err %s", + ErrorStr(status)); + OnSessionEstablishmentError(status); return; } } @@ -1852,11 +1814,14 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) ChipLogProgress(Controller, "Exchanging certificates"); // TODO(cecille): Once this is implemented through the clusters, it should be moved to the proper stage and the callback // should advance the commissioning stage - CHIP_ERROR status = SendOperationalCertificateSigningRequestCommand(mDeviceBeingCommissioned); + // TODO(cecille): The pointer re-interpret here is ugly. Once these steps are moved into the commissioning state machine, + // the commissioning state machine can hold the parameters instead of the CommissioneeDeviceProxy and we can use the base + // class here. + CHIP_ERROR status = SendOperationalCertificateSigningRequestCommand(reinterpret_cast(proxy)); if (status != CHIP_NO_ERROR) { - ChipLogError(Controller, "Failed in sending 'CSR Request' command to the device: err %s", ErrorStr(err)); - OnSessionEstablishmentError(err); + ChipLogError(Controller, "Failed in sending 'CSR Request' command to the device: err %s", ErrorStr(status)); + OnSessionEstablishmentError(status); return; } } @@ -1876,7 +1841,7 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) } break; case CommissioningStage::kFindOperational: { - PeerId peerId = PeerId().SetCompressedFabricId(GetCompressedFabricId()).SetNodeId(mDeviceBeingCommissioned->GetDeviceId()); + PeerId peerId = PeerId().SetCompressedFabricId(GetCompressedFabricId()).SetNodeId(proxy->GetDeviceId()); RendezvousCleanup(CHIP_NO_ERROR); #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD ChipLogProgress(Controller, "Finding node on operational network"); @@ -1887,7 +1852,7 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) case CommissioningStage::kSendComplete: { ChipLogProgress(Controller, "Calling commissioning complete"); GeneralCommissioningCluster genCom; - genCom.Associate(mDeviceOperational, 0); + genCom.Associate(proxy, 0); genCom.CommissioningComplete(mSuccess.Cancel(), mFailure.Cancel()); } break; @@ -1895,15 +1860,13 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) ChipLogProgress(Controller, "Rendezvous cleanup"); if (mPairingDelegate != nullptr) { - mPairingDelegate->OnCommissioningComplete(mDeviceOperational->GetDeviceId(), CHIP_NO_ERROR); + mPairingDelegate->OnCommissioningComplete(proxy->GetDeviceId(), CHIP_NO_ERROR); } - mDeviceOperational = nullptr; break; case CommissioningStage::kSecurePairing: case CommissioningStage::kError: break; } - mCommissioningStage = nextStage; } } // namespace Controller diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index a632beb2c833ec..cfcb0cb8861e5f 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -36,8 +36,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -115,26 +117,6 @@ struct ControllerInitParams FabricId fabricId = kUndefinedFabricId; }; -enum CommissioningStage : uint8_t -{ - kError, - kSecurePairing, - kArmFailsafe, - // kConfigTime, // NOT YET IMPLEMENTED - // kConfigTimeZone, // NOT YET IMPLEMENTED - // kConfigDST, // NOT YET IMPLEMENTED - kConfigRegulatory, - kDeviceAttestation, - kCheckCertificates, - kConfigACL, - kNetworkSetup, - kScanNetworks, // optional stage if network setup fails (not yet implemented) - kNetworkEnable, - kFindOperational, - kSendComplete, - kCleanup, -}; - class DLL_EXPORT DevicePairingDelegate { public: @@ -576,7 +558,10 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, void RendezvousCleanup(CHIP_ERROR status); - void AdvanceCommissioningStage(CHIP_ERROR err); + void PerformCommissioningStep(DeviceProxy * device, CommissioningStage step, CommissioningParameters & params, + CommissioningDelegate * delegate); + + void CommissioningStageComplete(CHIP_ERROR err); #if CONFIG_NETWORK_LAYER_BLE /** @@ -664,7 +649,6 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, DevicePairingDelegate * mPairingDelegate; CommissioneeDeviceProxy * mDeviceBeingCommissioned = nullptr; - OperationalDeviceProxy * mDeviceOperational = nullptr; Credentials::CertificateType mCertificateTypeBeingRequested = Credentials::CertificateType::kUnknown; @@ -801,7 +785,6 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Callback::Callback mSuccess; Callback::Callback mFailure; - CommissioningStage GetNextCommissioningStage(); static CHIP_ERROR ConvertFromNodeOperationalCertStatus(uint8_t err); Callback::Callback mCertificateChainResponseCallback; @@ -820,6 +803,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Callback::Callback mDeviceNOCChainCallback; SetUpCodePairer mSetUpCodePairer; + AutoCommissioner mAutoCommissioner; + CommissioningDelegate * mCommissioningDelegate = nullptr; }; } // namespace Controller diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h new file mode 100644 index 00000000000000..55a1e82509a996 --- /dev/null +++ b/src/controller/CommissioningDelegate.h @@ -0,0 +1,66 @@ +/* + * + * 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 + +namespace chip { +namespace Controller { + +enum CommissioningStage : uint8_t +{ + kError, + kSecurePairing, + kArmFailsafe, + // kConfigTime, // NOT YET IMPLEMENTED + // kConfigTimeZone, // NOT YET IMPLEMENTED + // kConfigDST, // NOT YET IMPLEMENTED + kConfigRegulatory, + kDeviceAttestation, + kCheckCertificates, + kConfigACL, + kNetworkSetup, + kScanNetworks, // optional stage if network setup fails (not yet implemented) + kNetworkEnable, + kFindOperational, + kSendComplete, + kCleanup, +}; + +class CommissioningDelegate +{ +public: + virtual ~CommissioningDelegate(){}; + struct CommissioningReport + { + CommissioningStage stageCompleted; + // TODO: Add other things the delegate needs to know. + union + { + struct + { + OperationalDeviceProxy * operationalProxy; + } OperationalNodeFoundData; + }; + }; + virtual void CommissioningStepFinished(CHIP_ERROR err, CommissioningReport report) = 0; +}; + +} // namespace Controller +} // namespace chip